aboutsummaryrefslogtreecommitdiffstats
path: root/src/passphrase.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/passphrase.c287
1 files changed, 180 insertions, 107 deletions
diff --git a/src/passphrase.c b/src/passphrase.c
index 9c42c0f..1430a47 100644
--- a/src/passphrase.c
+++ b/src/passphrase.c
@@ -18,26 +18,33 @@
*/
#include <stdlib.h>
#include <stdio.h>
-#include <termios.h>
#include <unistd.h>
-#include <signal.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/wait.h>
#define PASSPHRASE_USE_DEPRECATED
#include "passphrase.h"
#include "passphrase_helper.h"
-#define START_PASSPHRASE_LIMIT 32
-
+#ifndef START_PASSPHRASE_LIMIT
+# define START_PASSPHRASE_LIMIT 32
+#endif
+#ifndef DEFAULT_PASSPHRASE_METER
+# define DEFAULT_PASSPHRASE_METER "passcheck"
+#endif
-#if !defined(PASSPHRASE_ECHO) || defined(PASSPHRASE_MOVE)
-/**
- * The original TTY settings
- */
-static struct termios saved_stty;
-#endif /* !PASSPHRASE_ECHO || PASSPHRASE_MOVE */
+#ifdef PASSPHRASE_METER
+struct passcheck_state
+{
+ int pipe_rw[2];
+ pid_t pid;
+ int flags;
+};
+#endif /* PASSPHRASE_METER */
@@ -58,6 +65,136 @@ static char* xrealloc(char* array, size_t cur_size, size_t new_size)
#endif /* !PASSPHRASE_REALLOC */
+#ifdef PASSPHRASE_METER
+static void passcheck_start(struct passcheck_state* state, int flags)
+{
+ const char* command;
+ int pipe_rw[2] = { -1, -1 };
+ int exec_rw[2] = { -1, -1 };
+ pid_t pid;
+ ssize_t n;
+ int i = 0;
+
+ state->pid = -1;
+ state->flags = (flags & PASSPHRASE_READ_NEW) ? (flags ^ PASSPHRASE_READ_NEW) : 0;
+ if (state->flags == 0)
+ return;
+
+ command = getenv("LIBPASSPHRASE_METER");
+ if (!command || !*command)
+ command = DEFAULT_PASSPHRASE_METER;
+
+ xpipe(state->pipe_rw);
+ xpipe(pipe_rw);
+ xpipe(exec_rw);
+ /* ‘Their integer values shall be the two lowest available at the time of the pipe() call’ [man 3p pipe]
+ * This guarantees (unless the application is doing something stupid) that the file desriptors
+ * in exec_rw[1] is not stdin, stdout, stderr, or 0 (required by FD_CLOEXEC to take affect), assuming
+ * stdin, stdout, and stderr are 0, 1, and 2, respectively, as specified in `man 3p stdin`. */
+
+ if (fcntl(exec_rw[1], F_SETFD, FD_CLOEXEC) == -1)
+ goto fail;
+
+ pid = fork();
+ if (pid == -1)
+ {
+ state->flags = 0;
+ goto fail;
+ }
+
+ close(exec_rw[!!pid]), exec_rw[!!pid] = -1;
+ close(state->pipe_rw[!!pid]), state->pipe_rw[!!pid] = -1;
+ close(pipe_rw[!pid]), pipe_rw[!pid] = -1;
+ state->pipe_rw[!!pid] = pipe_rw[!!pid], pipe_rw[!!pid] = -1;
+
+ if (pid == 0)
+ {
+ gid_t gid = getgid(), egid = getegid();
+ uid_t uid = getuid(), euid = geteuid();
+
+ if ((state->pipe_rw[0] != STDIN_FILENO) && (state->pipe_rw[1] == STDIN_FILENO))
+ {
+ pipe_rw[1] = dup(state->pipe_rw[1]);
+ if (pipe_rw[1] == -1)
+ goto child_fail;
+ state->pipe_rw[1] = pipe_rw[1], pipe_rw[1] = -1;
+ }
+ for (i = 0; i <= 1; i++)
+ if (state->pipe_rw[i] != i)
+ {
+ close(i);
+ pipe_rw[i] = dup2(state->pipe_rw[i], i);
+ if (pipe_rw[i] == -1)
+ goto child_fail;
+ close(state->pipe_rw[i]), state->pipe_rw[i] = pipe_rw[i];
+ }
+
+ if (egid != gid)
+ if (setregid(gid, gid) && gid)
+ goto child_fail;
+ if (euid != uid)
+ if (setreuid(uid, uid) && uid)
+ goto child_fail;
+
+ execlp(command, command, "-r", NULL);
+ child_fail:
+ n = write(exec_rw[1], &i, sizeof(i));
+ _exit(!!n);
+ }
+
+ rewait:
+ n = read(exec_rw[0], &i, sizeof(i));
+ if ((n < 0) && (errno == EINTR))
+ goto rewait;
+ if (n)
+ {
+ rereap:
+ if ((waitpid(pid, &i, 0) == -1) && (errno == EINTR))
+ goto rereap;
+ goto fail;
+ }
+
+ close(exec_rw[0]);
+ state->pid = pid;
+ return;
+ fail:
+ if (state->pipe_rw[0] >= 0) close(state->pipe_rw[0]);
+ if (state->pipe_rw[1] >= 0) close(state->pipe_rw[1]);
+ if (pipe_rw[0] >= 0) close(pipe_rw[0]);
+ if (pipe_rw[1] >= 0) close(pipe_rw[1]);
+ if (exec_rw[0] >= 0) close(exec_rw[0]);
+ if (exec_rw[1] >= 0) close(exec_rw[1]);
+ state->pid = -1;
+ state->flags = 0;
+}
+
+static void passcheck_stop(struct passcheck_state* state)
+{
+ int _status;
+
+ if (state->flags == 0)
+ return;
+
+ close(state->pipe_rw[0]);
+ close(state->pipe_rw[1]);
+
+rereap:
+ if ((waitpid(state->pid, &_status, 0) == -1) && (errno == EINTR))
+ goto rereap;
+
+ /* TODO cleanup */
+}
+
+static void passcheck_update(struct passcheck_state* state, char* passphrase, size_t len)
+{
+ if (state->flags == 0)
+ return;
+
+ /* TODO */
+}
+#endif /* PASSPHRASE_METER */
+
+
static int fdgetc(int fd)
{
unsigned char c;
@@ -117,17 +254,6 @@ static int get_key(int c, int fdin)
/**
* Reads the passphrase from stdin
*
- * @return The passphrase, should be wiped and `free`:ed, `NULL` on error
- */
-char* passphrase_read(void)
-{
- return passphrase_read2(STDIN_FILENO, 0);
-}
-
-
-/**
- * Reads the passphrase from stdin
- *
* @param fdin File descriptor for input
* @param flags Settings, a combination of the constants:
* * PASSPHRASE_READ_EXISTING
@@ -157,11 +283,17 @@ char* passphrase_read2(int fdin, int flags)
#ifdef PASSPHRASE_MOVE
int cc;
#endif
- (void) flags;
+#ifdef PASSPHRASE_METER
+ struct passcheck_state passcheck;
+#endif /* PASSPHRASE_METER */
if (rc == NULL)
return NULL;
+#ifdef PASSPHRASE_METER
+ passcheck_start(&passcheck, flags);
+#endif /* PASSPHRASE_METER */
+
#ifdef PASSPHRASE_TEXT
xprintf("%s%zn", PASSPHRASE_TEXT_EMPTY, &printed_len);
if (printed_len)
@@ -174,8 +306,16 @@ char* passphrase_read2(int fdin, int flags)
for (;;)
{
c = fdgetc(fdin);
- if ((c < 0) || (c == '\n')) break;
- if (c == 0) continue;
+ if ((c < 0) || (c == '\n'))
+ {
+#ifdef PASSPHRASE_METER
+ passcheck_stop(&passcheck);
+ xflush();
+#endif /* PASSPHRASE_METER */
+ break;
+ }
+ if (c == 0)
+ continue;
#if defined(PASSPHRASE_MOVE)
cc = get_key(c, fdin);
@@ -215,6 +355,11 @@ char* passphrase_read2(int fdin, int flags)
continue;
erase_prev();
print_erase();
+
+#ifdef PASSPHRASE_METER
+ passcheck_update(&passcheck, rc, len);
+#endif /* PASSPHRASE_METER */
+
xflush();
# ifdef DEBUG
goto debug;
@@ -228,6 +373,10 @@ char* passphrase_read2(int fdin, int flags)
append_char();
#endif /* PASSPHRASE_MOVE, PASSPHRASE_STAR || PASSPHRASE_TEXT */
+#ifdef PASSPHRASE_METER
+ passcheck_update(&passcheck, rc, len);
+#endif /* PASSPHRASE_METER */
+
xflush();
if (len == size)
{
@@ -264,96 +413,20 @@ char* passphrase_read2(int fdin, int flags)
fprintf(stderr, "\n");
#endif /* !PASSPHRASE_ECHO || PASSPHRASE_MOVE */
return rc;
-}
-
-
-#ifdef __GNUC__
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Wsuggest-attribute=const"
-# pragma GCC diagnostic ignored "-Wsuggest-attribute=pure"
-#endif
-/* Must positively absolutely not be flagged as possible to optimise away as it depends on configurations,
- and programs that uses this library must not be forced to be recompiled if the library is reconfigured. */
-
-
-/**
- * Used to make sure that `passphrase_wipe` is not optimised away even within this library
- */
-volatile sig_atomic_t passphrase_wipe_volatile________________ = 1;
-
-/**
- * Forcable write NUL characters to a passphrase
- *
- * @param ptr The password to wipe
- * @param n The number of characters to wipe
- */
-#ifdef __GNUC__
-__attribute__((optimize("-O0")))
-#endif
-void passphrase_wipe(volatile char* ptr, size_t n)
-{
- size_t i;
- for (i = 0; (i < n) && passphrase_wipe_volatile________________; i++)
- *(ptr + i) = 0;
-}
-
-
-/**
- * Disable echoing and do anything else to the terminal settnings `passphrase_read` requires
- */
-void passphrase_disable_echo(void)
-{
- passphrase_disable_echo1(STDIN_FILENO);
-}
-
-
-/**
- * Undo the actions of `passphrase_disable_echo`
- */
-void passphrase_reenable_echo(void)
-{
- passphrase_reenable_echo1(STDIN_FILENO);
-}
-
-/**
- * Disable echoing and do anything else to the terminal settnings `passphrase_read2` requires
- *
- * @param fdin File descriptor for input
- */
-void passphrase_disable_echo1(int fdin)
-{
-#if !defined(PASSPHRASE_ECHO) || defined(PASSPHRASE_MOVE)
- struct termios stty;
- tcgetattr(fdin, &stty);
- saved_stty = stty;
- stty.c_lflag &= (tcflag_t)~ECHO;
-# if defined(PASSPHRASE_STAR) || defined(PASSPHRASE_TEXT) || defined(PASSPHRASE_MOVE)
- stty.c_lflag &= (tcflag_t)~ICANON;
-# endif /* PASSPHRASE_STAR || PASSPHRASE_TEXT || PASSPHRASE_MOVE */
- tcsetattr(fdin, TCSAFLUSH, &stty);
-#else /* !PASSPHRASE_ECHO || PASSPHRASE_MOVE */
- (void) fdin;
-#endif /* !PASSPHRASE_ECHO || PASSPHRASE_MOVE */
+#ifndef PASSPHRASE_METER
+ (void) flags;
+#endif /* !PASSPHRASE_METER */
}
/**
- * Undo the actions of `passphrase_disable_echo1`
+ * Reads the passphrase from stdin
*
- * @param fdin File descriptor for input
+ * @return The passphrase, should be wiped and `free`:ed, `NULL` on error
*/
-void passphrase_reenable_echo1(int fdin)
+char* passphrase_read(void)
{
-#if !defined(PASSPHRASE_ECHO) || defined(PASSPHRASE_MOVE)
- tcsetattr(fdin, TCSAFLUSH, &saved_stty);
-#else /* !PASSPHRASE_ECHO || !PASSPHRASE_MOVE */
- (void) fdin;
-#endif /* !PASSPHRASE_ECHO || !PASSPHRASE_MOVE */
+ return passphrase_read2(STDIN_FILENO, 0);
}
-
-#ifdef __GNUC__
-# pragma GCC diagnostic pop
-#endif
-