From 8d44ce910ddb00d6a9c18b912f2b027f822e5dc1 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 17 Jun 2015 10:19:40 -0400 Subject: [PATCH 1/4] Add readpassphrase.c in src/ext This is taken verbatim from openssh 6.8p1, which appears to have lightly tweaked it from the openbsd version. --- LICENSE | 19 ++++ src/ext/README | 3 + src/ext/include.am | 1 + src/ext/readpassphrase.c | 213 +++++++++++++++++++++++++++++++++++++++ src/ext/readpassphrase.h | 44 ++++++++ 5 files changed, 280 insertions(+) create mode 100644 src/ext/readpassphrase.c create mode 100644 src/ext/readpassphrase.h diff --git a/LICENSE b/LICENSE index 48602c13c6..68c9b23374 100644 --- a/LICENSE +++ b/LICENSE @@ -249,7 +249,26 @@ As a special exception to the GNU General Public License, if you distribute this file as part of a program that contains a configuration script generated by Autoconf, you may include it under the same distribution terms that you use for the rest of that program. +=============================================================================== +src/ext/readpassphrase.[ch] are distributed under this license: + Copyright (c) 2000-2002, 2007 Todd C. Miller + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + Sponsored in part by the Defense Advanced Research Projects + Agency (DARPA) and Air Force Research Laboratory, Air Force + Materiel Command, USAF, under agreement number F39502-99-1-0512. =============================================================================== If you got Tor as a static binary with OpenSSL included, then you should know: diff --git a/src/ext/README b/src/ext/README index 616716e099..b52c74d627 100644 --- a/src/ext/README +++ b/src/ext/README @@ -60,4 +60,7 @@ ed25519/ref10/* Daniel Bernsten's portable ref10 implementation of ed25519. Public domain. +readpassphrase.[ch] + Portable readpassphrase implementation from OpenSSH portable, version + 6.8p1. diff --git a/src/ext/include.am b/src/ext/include.am index 576fd4efb8..f0bfbb791f 100644 --- a/src/ext/include.am +++ b/src/ext/include.am @@ -7,6 +7,7 @@ EXTHEADERS = \ src/ext/ht.h \ src/ext/eventdns.h \ src/ext/tinytest.h \ + src/ext/readpassphrase.h \ src/ext/strlcat.c \ src/ext/strlcpy.c \ src/ext/tinytest_macros.h \ diff --git a/src/ext/readpassphrase.c b/src/ext/readpassphrase.c new file mode 100644 index 0000000000..d63cdf2f0e --- /dev/null +++ b/src/ext/readpassphrase.c @@ -0,0 +1,213 @@ +/* $OpenBSD: readpassphrase.c,v 1.22 2010/01/13 10:20:54 dtucker Exp $ */ + +/* + * Copyright (c) 2000-2002, 2007 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ + +/* OPENBSD ORIGINAL: lib/libc/gen/readpassphrase.c */ + +#include "includes.h" + +#ifndef HAVE_READPASSPHRASE + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef TCSASOFT +# define _T_FLUSH (TCSAFLUSH|TCSASOFT) +#else +# define _T_FLUSH (TCSAFLUSH) +#endif + +/* SunOS 4.x which lacks _POSIX_VDISABLE, but has VDISABLE */ +#if !defined(_POSIX_VDISABLE) && defined(VDISABLE) +# define _POSIX_VDISABLE VDISABLE +#endif + +#ifndef _NSIG +# ifdef NSIG +# define _NSIG NSIG +# else +# define _NSIG 128 +# endif +#endif + +static volatile sig_atomic_t signo[_NSIG]; + +static void handler(int); + +char * +readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags) +{ + ssize_t nr; + int input, output, save_errno, i, need_restart; + char ch, *p, *end; + struct termios term, oterm; + struct sigaction sa, savealrm, saveint, savehup, savequit, saveterm; + struct sigaction savetstp, savettin, savettou, savepipe; + + /* I suppose we could alloc on demand in this case (XXX). */ + if (bufsiz == 0) { + errno = EINVAL; + return(NULL); + } + +restart: + for (i = 0; i < _NSIG; i++) + signo[i] = 0; + nr = -1; + save_errno = 0; + need_restart = 0; + /* + * Read and write to /dev/tty if available. If not, read from + * stdin and write to stderr unless a tty is required. + */ + if ((flags & RPP_STDIN) || + (input = output = open(_PATH_TTY, O_RDWR)) == -1) { + if (flags & RPP_REQUIRE_TTY) { + errno = ENOTTY; + return(NULL); + } + input = STDIN_FILENO; + output = STDERR_FILENO; + } + + /* + * Catch signals that would otherwise cause the user to end + * up with echo turned off in the shell. Don't worry about + * things like SIGXCPU and SIGVTALRM for now. + */ + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; /* don't restart system calls */ + sa.sa_handler = handler; + (void)sigaction(SIGALRM, &sa, &savealrm); + (void)sigaction(SIGHUP, &sa, &savehup); + (void)sigaction(SIGINT, &sa, &saveint); + (void)sigaction(SIGPIPE, &sa, &savepipe); + (void)sigaction(SIGQUIT, &sa, &savequit); + (void)sigaction(SIGTERM, &sa, &saveterm); + (void)sigaction(SIGTSTP, &sa, &savetstp); + (void)sigaction(SIGTTIN, &sa, &savettin); + (void)sigaction(SIGTTOU, &sa, &savettou); + + /* Turn off echo if possible. */ + if (input != STDIN_FILENO && tcgetattr(input, &oterm) == 0) { + memcpy(&term, &oterm, sizeof(term)); + if (!(flags & RPP_ECHO_ON)) + term.c_lflag &= ~(ECHO | ECHONL); +#ifdef VSTATUS + if (term.c_cc[VSTATUS] != _POSIX_VDISABLE) + term.c_cc[VSTATUS] = _POSIX_VDISABLE; +#endif + (void)tcsetattr(input, _T_FLUSH, &term); + } else { + memset(&term, 0, sizeof(term)); + term.c_lflag |= ECHO; + memset(&oterm, 0, sizeof(oterm)); + oterm.c_lflag |= ECHO; + } + + /* No I/O if we are already backgrounded. */ + if (signo[SIGTTOU] != 1 && signo[SIGTTIN] != 1) { + if (!(flags & RPP_STDIN)) + (void)write(output, prompt, strlen(prompt)); + end = buf + bufsiz - 1; + p = buf; + while ((nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r') { + if (p < end) { + if ((flags & RPP_SEVENBIT)) + ch &= 0x7f; + if (isalpha(ch)) { + if ((flags & RPP_FORCELOWER)) + ch = (char)tolower(ch); + if ((flags & RPP_FORCEUPPER)) + ch = (char)toupper(ch); + } + *p++ = ch; + } + } + *p = '\0'; + save_errno = errno; + if (!(term.c_lflag & ECHO)) + (void)write(output, "\n", 1); + } + + /* Restore old terminal settings and signals. */ + if (memcmp(&term, &oterm, sizeof(term)) != 0) { + while (tcsetattr(input, _T_FLUSH, &oterm) == -1 && + errno == EINTR) + continue; + } + (void)sigaction(SIGALRM, &savealrm, NULL); + (void)sigaction(SIGHUP, &savehup, NULL); + (void)sigaction(SIGINT, &saveint, NULL); + (void)sigaction(SIGQUIT, &savequit, NULL); + (void)sigaction(SIGPIPE, &savepipe, NULL); + (void)sigaction(SIGTERM, &saveterm, NULL); + (void)sigaction(SIGTSTP, &savetstp, NULL); + (void)sigaction(SIGTTIN, &savettin, NULL); + (void)sigaction(SIGTTOU, &savettou, NULL); + if (input != STDIN_FILENO) + (void)close(input); + + /* + * If we were interrupted by a signal, resend it to ourselves + * now that we have restored the signal handlers. + */ + for (i = 0; i < _NSIG; i++) { + if (signo[i]) { + kill(getpid(), i); + switch (i) { + case SIGTSTP: + case SIGTTIN: + case SIGTTOU: + need_restart = 1; + } + } + } + if (need_restart) + goto restart; + + if (save_errno) + errno = save_errno; + return(nr == -1 ? NULL : buf); +} + +#if 0 +char * +getpass(const char *prompt) +{ + static char buf[_PASSWORD_LEN + 1]; + + return(readpassphrase(prompt, buf, sizeof(buf), RPP_ECHO_OFF)); +} +#endif + +static void handler(int s) +{ + + signo[s] = 1; +} +#endif /* HAVE_READPASSPHRASE */ diff --git a/src/ext/readpassphrase.h b/src/ext/readpassphrase.h new file mode 100644 index 0000000000..5fd7c5d77a --- /dev/null +++ b/src/ext/readpassphrase.h @@ -0,0 +1,44 @@ +/* $OpenBSD: readpassphrase.h,v 1.5 2003/06/17 21:56:23 millert Exp $ */ + +/* + * Copyright (c) 2000, 2002 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ + +/* OPENBSD ORIGINAL: include/readpassphrase.h */ + +#ifndef _READPASSPHRASE_H_ +#define _READPASSPHRASE_H_ + +#include "includes.h" + +#ifndef HAVE_READPASSPHRASE + +#define RPP_ECHO_OFF 0x00 /* Turn off echo (default). */ +#define RPP_ECHO_ON 0x01 /* Leave echo on. */ +#define RPP_REQUIRE_TTY 0x02 /* Fail if there is no tty. */ +#define RPP_FORCELOWER 0x04 /* Force input to lower case. */ +#define RPP_FORCEUPPER 0x08 /* Force input to upper case. */ +#define RPP_SEVENBIT 0x10 /* Strip the high bit from input. */ +#define RPP_STDIN 0x20 /* Read from stdin, not /dev/tty */ + +char * readpassphrase(const char *, char *, size_t, int); + +#endif /* HAVE_READPASSPHRASE */ + +#endif /* !_READPASSPHRASE_H_ */ From b9b658e727520d947e6c14475ee22e8606e9f062 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 17 Jun 2015 10:41:22 -0400 Subject: [PATCH 2/4] Add the openssh 6.8p1 readpassphrase implementation This way glibc users don't have to fall back to getpass. Windows users are still out of luck --- configure.ac | 2 ++ src/common/compat.c | 2 ++ src/common/include.am | 9 ++++++++- src/ext/readpassphrase.c | 13 ++++++++++--- src/ext/readpassphrase.h | 4 +++- 5 files changed, 25 insertions(+), 5 deletions(-) diff --git a/configure.ac b/configure.ac index 195e56fc93..23d4c98f5d 100644 --- a/configure.ac +++ b/configure.ac @@ -423,6 +423,8 @@ if test "$bwin32" != true; then AC_CHECK_FUNCS(pthread_create) fi +AM_CONDITIONAL(BUILD_READPASSPHRASE_C, test x$ac_cv_func_readpassphrase = xno && test $bwin32 = false) + dnl ------------------------------------------------------ dnl Where do you live, libevent? And how do we call you? diff --git a/src/common/compat.c b/src/common/compat.c index 701027523e..a931f0a229 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -69,6 +69,8 @@ #endif #ifdef HAVE_READPASSPHRASE_H #include +#elif !defined(_WIN32) +#include "readpassphrase.h" #endif #ifndef HAVE_GETTIMEOFDAY diff --git a/src/common/include.am b/src/common/include.am index b782310663..789f4003c9 100644 --- a/src/common/include.am +++ b/src/common/include.am @@ -51,6 +51,12 @@ if THREADS_WIN32 threads_impl_source=src/common/compat_winthreads.c endif +if BUILD_READPASSPHRASE_C +readpassphrase_source=src/ext/readpassphrase.c +else +readpassphrase_source= +endif + LIBOR_A_SOURCES = \ src/common/address.c \ src/common/backtrace.c \ @@ -67,7 +73,8 @@ LIBOR_A_SOURCES = \ src/ext/csiphash.c \ src/ext/trunnel/trunnel.c \ $(libor_extra_source) \ - $(threads_impl_source) + $(threads_impl_source) \ + $(readpassphrase_source) src/common/log.o: micro-revision.i diff --git a/src/ext/readpassphrase.c b/src/ext/readpassphrase.c index d63cdf2f0e..7bdecaf637 100644 --- a/src/ext/readpassphrase.c +++ b/src/ext/readpassphrase.c @@ -22,7 +22,7 @@ /* OPENBSD ORIGINAL: lib/libc/gen/readpassphrase.c */ -#include "includes.h" +#include "orconfig.h" #ifndef HAVE_READPASSPHRASE @@ -35,6 +35,10 @@ #include #include +#ifndef _PATH_TTY +# define _PATH_TTY "/dev/tty" +#endif + #ifdef TCSASOFT # define _T_FLUSH (TCSAFLUSH|TCSASOFT) #else @@ -61,6 +65,7 @@ static void handler(int); char * readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags) { + ssize_t bytes_written = 0; ssize_t nr; int input, output, save_errno, i, need_restart; char ch, *p, *end; @@ -132,7 +137,7 @@ restart: /* No I/O if we are already backgrounded. */ if (signo[SIGTTOU] != 1 && signo[SIGTTIN] != 1) { if (!(flags & RPP_STDIN)) - (void)write(output, prompt, strlen(prompt)); + bytes_written = write(output, prompt, strlen(prompt)); end = buf + bufsiz - 1; p = buf; while ((nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r') { @@ -151,9 +156,11 @@ restart: *p = '\0'; save_errno = errno; if (!(term.c_lflag & ECHO)) - (void)write(output, "\n", 1); + bytes_written = write(output, "\n", 1); } + (void) bytes_written; + /* Restore old terminal settings and signals. */ if (memcmp(&term, &oterm, sizeof(term)) != 0) { while (tcsetattr(input, _T_FLUSH, &oterm) == -1 && diff --git a/src/ext/readpassphrase.h b/src/ext/readpassphrase.h index 5fd7c5d77a..83ae1f20a8 100644 --- a/src/ext/readpassphrase.h +++ b/src/ext/readpassphrase.h @@ -25,7 +25,7 @@ #ifndef _READPASSPHRASE_H_ #define _READPASSPHRASE_H_ -#include "includes.h" +#include "orconfig.h" #ifndef HAVE_READPASSPHRASE @@ -39,6 +39,8 @@ char * readpassphrase(const char *, char *, size_t, int); +#define HAVE_READPASSPHRASE + #endif /* HAVE_READPASSPHRASE */ #endif /* !_READPASSPHRASE_H_ */ From a64f2d167eab4baefb4fe2471fd2e554a132d37f Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 17 Jun 2015 11:22:31 -0400 Subject: [PATCH 3/4] Add a getpass implementation for windows that won't totally suck The logic here is inspired by Python's win_getpass(), which I'm assuming is better than nothing. --- src/common/compat.c | 65 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/src/common/compat.c b/src/common/compat.c index a931f0a229..4a35183f38 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -71,6 +71,8 @@ #include #elif !defined(_WIN32) #include "readpassphrase.h" +#else +#include #endif #ifndef HAVE_GETTIMEOFDAY @@ -3248,16 +3250,77 @@ tor_sleep_msec(int msec) #endif /** Emit the password prompt prompt, then read up to buflen - * characters of passphrase into output. */ + * bytes of passphrase into output. Return the number of bytes in + * the passphrase, excluding terminating NUL. + */ ssize_t tor_getpass(const char *prompt, char *output, size_t buflen) { tor_assert(buflen <= SSIZE_MAX); + tor_assert(buflen >= 1); #if defined(HAVE_READPASSPHRASE) char *pwd = readpassphrase(prompt, output, buflen, RPP_ECHO_OFF); if (pwd == NULL) return -1; return strlen(pwd); +#elif defined(_WIN32) + int r = -1; + while (*prompt) { + _putch(*prompt++); + } + + tor_assert(buflen <= INT_MAX); + wchar_t *buf = tor_calloc(buflen, sizeof(wchar_t)); + + wchar_t *ptr = buf, *lastch = buf + buflen - 1; + while (ptr < lastch) { + wint_t ch = _getwch(); + switch (ch) { + case '\r': + case '\n': + case WEOF: + goto done_reading; + case 3: + goto done; /* Can't actually read ctrl-c this way. */ + case '\b': + if (ptr > buf) + --ptr; + continue; + case 0: + case 0xe0: + ch = _getwch(); /* Ignore; this is a function or arrow key */ + break; + default: + *ptr++ = ch; + break; + } + } + done_reading: + ; + +#ifndef WC_ERR_INVALID_CHARS +#define WC_ERR_INVALID_CHARS 0x80 +#endif + + /* Now convert it to UTF-8 */ + r = WideCharToMultiByte(CP_UTF8, + WC_NO_BEST_FIT_CHARS|WC_ERR_INVALID_CHARS, + buf, (int)(ptr-buf), + output, (int)(buflen-1), + NULL, NULL); + if (r <= 0) { + r = -1; + goto done; + } + + tor_assert(r < (int)buflen); + + output[r] = 0; + + done: + SecureZeroMemory(buf, sizeof(wchar_t)*buflen); + tor_free(buf); + return r; #elif defined(HAVE_GETPASS) /* XXX We shouldn't actually use this; it's deprecated to hell and back */ memset(output, 0, buflen); From 272229ff5d3828eb2f51b93a0079a8e86d1ec5af Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 17 Jun 2015 11:52:54 -0400 Subject: [PATCH 4/4] Remove janky getpass implementations --- src/common/compat.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/common/compat.c b/src/common/compat.c index 4a35183f38..6e46f277f8 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -3321,22 +3321,7 @@ tor_getpass(const char *prompt, char *output, size_t buflen) SecureZeroMemory(buf, sizeof(wchar_t)*buflen); tor_free(buf); return r; -#elif defined(HAVE_GETPASS) - /* XXX We shouldn't actually use this; it's deprecated to hell and back */ - memset(output, 0, buflen); - char *pwd = getpass(prompt); - if (pwd == NULL) - return -1; - ssize_t len = (ssize_t)strlen(pwd); - strlcpy(output, pwd, buflen); - memset(pwd, 0, len); - return len; #else - /* XXX This is even worse. */ - puts(prompt); - ssize_t n = read(STDIN_FILENO, output, buflen); - if (n < 0) - return -1; - return n; +#error "No implementation for tor_getpass found!" #endif }