From 8fb6e77a618ce1a8151fab27445e0c3cc0241134 Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Sat, 5 Dec 2015 11:13:46 +0100 Subject: beginning of issue #2 on github MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/echoes.c | 90 +++++++++++++++ src/passphrase.c | 287 ++++++++++++++++++++++++++++++------------------ src/passphrase.h | 11 +- src/passphrase_helper.h | 11 ++ src/test.c | 3 +- src/wipe.c | 75 +++++++++++++ 6 files changed, 366 insertions(+), 111 deletions(-) create mode 100644 src/echoes.c create mode 100644 src/wipe.c (limited to 'src') diff --git a/src/echoes.c b/src/echoes.c new file mode 100644 index 0000000..e1a0e63 --- /dev/null +++ b/src/echoes.c @@ -0,0 +1,90 @@ +/** + * libpassphrase – Personalisable library for TTY passphrase reading + * + * Copyright © 2013, 2014, 2015 Mattias Andrée (maandree@member.fsf.org) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include + +#define PASSPHRASE_USE_DEPRECATED +#include "passphrase.h" +#include "passphrase_helper.h" + + + +#if !defined(PASSPHRASE_ECHO) || defined(PASSPHRASE_MOVE) +/** + * The original TTY settings + */ +static struct termios saved_stty; +#endif /* !PASSPHRASE_ECHO || PASSPHRASE_MOVE */ + + + +/** + * 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 */ +} + + +/** + * Undo the actions of `passphrase_disable_echo1` + * + * @param fdin File descriptor for input + */ +void passphrase_reenable_echo1(int fdin) +{ +#if !defined(PASSPHRASE_ECHO) || defined(PASSPHRASE_MOVE) + tcsetattr(fdin, TCSAFLUSH, &saved_stty); +#else /* !PASSPHRASE_ECHO || !PASSPHRASE_MOVE */ + (void) fdin; +#endif /* !PASSPHRASE_ECHO || !PASSPHRASE_MOVE */ +} + 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 #include -#include #include -#include +#include +#include +#include #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; @@ -114,17 +251,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 * @@ -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 - diff --git a/src/passphrase.h b/src/passphrase.h index d24e66e..24ee71b 100644 --- a/src/passphrase.h +++ b/src/passphrase.h @@ -93,12 +93,19 @@ char* passphrase_read(void); char* passphrase_read2(int, int); /** - * Forcable write NUL characters to a passphrase + * Forcefully write NUL characters to a passphrase * * @param ptr The password to wipe * @param n The number of characters to wipe */ -void passphrase_wipe(volatile char*, size_t); +void passphrase_wipe(char*, size_t); + +/** + * Forcefully write NUL characters to a passphrase + * + * @param ptr The password to wipe + */ +void passphrase_wipe1(char*); /** * Disable echoing and do anything else to the terminal settnings `passphrase_read` requires diff --git a/src/passphrase_helper.h b/src/passphrase_helper.h index 792498e..b8abb3a 100644 --- a/src/passphrase_helper.h +++ b/src/passphrase_helper.h @@ -379,6 +379,17 @@ #endif +#define xpipe(pair) \ + do { \ + if (pipe(pair)) \ + { \ + pair[0] = pair[1] = -1; \ + goto fail; \ + } \ + } while (0) + + + #endif diff --git a/src/test.c b/src/test.c index 36e9afc..a962bd5 100644 --- a/src/test.c +++ b/src/test.c @@ -19,7 +19,6 @@ #include "passphrase.h" #include #include -#include #include #include @@ -67,7 +66,7 @@ int main(int argc, char** argv) printf("You entered: %s\n", passphrase); /* Wipe and free the passphrase */ - passphrase_wipe(passphrase, strlen(passphrase)); + passphrase_wipe1(passphrase); free(passphrase); /* Stop hiding user input */ diff --git a/src/wipe.c b/src/wipe.c new file mode 100644 index 0000000..ca67eb1 --- /dev/null +++ b/src/wipe.c @@ -0,0 +1,75 @@ +/** + * libpassphrase – Personalisable library for TTY passphrase reading + * + * Copyright © 2013, 2014, 2015 Mattias Andrée (maandree@member.fsf.org) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +#define PASSPHRASE_USE_DEPRECATED +#include "passphrase.h" +#include "passphrase_helper.h" + + + +#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. */ + + +/** + * `memset`, except calls to it cannot be removed by the compiler. + */ +void* (*volatile passphrase_explicit_memset________________)(void*, int, size_t) = memset; + + +/** + * Forcefully 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(char* ptr, size_t n) +{ + + passphrase_explicit_memset________________(ptr, 0, n); +} + +/** + * Forcefully write NUL characters to a passphrase + * + * @param ptr The password to wipe + */ +#ifdef __GNUC__ +__attribute__((optimize("-O0"))) +#endif +void passphrase_wipe1(char* ptr) +{ + + passphrase_explicit_memset________________(ptr, 0, strlen(ptr)); +} + + +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif + -- cgit v1.2.3-70-g09d2