Tor  0.4.6.0-alpha-dev
readpassphrase.c
1 /* $OpenBSD: readpassphrase.c,v 1.22 2010/01/13 10:20:54 dtucker Exp $ */
2 
3 /*
4  * Copyright (c) 2000-2002, 2007 Todd C. Miller <Todd.Miller@courtesan.com>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  *
18  * Sponsored in part by the Defense Advanced Research Projects
19  * Agency (DARPA) and Air Force Research Laboratory, Air Force
20  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
21  */
22 
23 /* OPENBSD ORIGINAL: lib/libc/gen/readpassphrase.c */
24 
25 #include "orconfig.h"
26 
27 #ifndef HAVE_READPASSPHRASE
28 
29 #include <termios.h>
30 #include <signal.h>
31 #include <ctype.h>
32 #include <fcntl.h>
33 #include "ext/tor_readpassphrase.h"
34 #include <errno.h>
35 #include <string.h>
36 #include <unistd.h>
37 
38 #ifndef _PATH_TTY
39 # define _PATH_TTY "/dev/tty"
40 #endif
41 
42 #ifdef TCSASOFT
43 # define _T_FLUSH (TCSAFLUSH|TCSASOFT)
44 #else
45 # define _T_FLUSH (TCSAFLUSH)
46 #endif
47 
48 /* SunOS 4.x which lacks _POSIX_VDISABLE, but has VDISABLE */
49 #if !defined(_POSIX_VDISABLE) && defined(VDISABLE)
50 # define _POSIX_VDISABLE VDISABLE
51 #endif
52 
53 #ifndef _NSIG
54 # ifdef NSIG
55 # define _NSIG NSIG
56 # else
57 # define _NSIG 128
58 # endif
59 #endif
60 
61 static volatile sig_atomic_t signo[_NSIG];
62 
63 static void handler(int);
64 
65 char *
66 readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags)
67 {
68  ssize_t bytes_written = 0;
69  ssize_t nr;
70  int input, output, save_errno, i, need_restart;
71  char ch, *p, *end;
72  struct termios term, oterm;
73  struct sigaction sa, savealrm, saveint, savehup, savequit, saveterm;
74  struct sigaction savetstp, savettin, savettou, savepipe;
75 
76  /* I suppose we could alloc on demand in this case (XXX). */
77  if (bufsiz == 0) {
78  errno = EINVAL;
79  return(NULL);
80  }
81 
82 restart:
83  for (i = 0; i < _NSIG; i++)
84  signo[i] = 0;
85  nr = -1;
86  save_errno = 0;
87  need_restart = 0;
88  /*
89  * Read and write to /dev/tty if available. If not, read from
90  * stdin and write to stderr unless a tty is required.
91  */
92  if ((flags & RPP_STDIN) ||
93  (input = output = open(_PATH_TTY, O_RDWR)) == -1) {
94  if (flags & RPP_REQUIRE_TTY) {
95  errno = ENOTTY;
96  return(NULL);
97  }
98  input = STDIN_FILENO;
99  output = STDERR_FILENO;
100  }
101 
102  /*
103  * Catch signals that would otherwise cause the user to end
104  * up with echo turned off in the shell. Don't worry about
105  * things like SIGXCPU and SIGVTALRM for now.
106  */
107  sigemptyset(&sa.sa_mask);
108  sa.sa_flags = 0; /* don't restart system calls */
109  sa.sa_handler = handler;
110  (void)sigaction(SIGALRM, &sa, &savealrm);
111  (void)sigaction(SIGHUP, &sa, &savehup);
112  (void)sigaction(SIGINT, &sa, &saveint);
113  (void)sigaction(SIGPIPE, &sa, &savepipe);
114  (void)sigaction(SIGQUIT, &sa, &savequit);
115  (void)sigaction(SIGTERM, &sa, &saveterm);
116  (void)sigaction(SIGTSTP, &sa, &savetstp);
117  (void)sigaction(SIGTTIN, &sa, &savettin);
118  (void)sigaction(SIGTTOU, &sa, &savettou);
119 
120  /* Turn off echo if possible. */
121  if (input != STDIN_FILENO && tcgetattr(input, &oterm) == 0) {
122  memcpy(&term, &oterm, sizeof(term));
123  if (!(flags & RPP_ECHO_ON))
124  term.c_lflag &= ~(ECHO | ECHONL);
125 #ifdef VSTATUS
126  if (term.c_cc[VSTATUS] != _POSIX_VDISABLE)
127  term.c_cc[VSTATUS] = _POSIX_VDISABLE;
128 #endif
129  (void)tcsetattr(input, _T_FLUSH, &term);
130  } else {
131  memset(&term, 0, sizeof(term));
132  term.c_lflag |= ECHO;
133  memset(&oterm, 0, sizeof(oterm));
134  oterm.c_lflag |= ECHO;
135  }
136 
137  /* No I/O if we are already backgrounded. */
138  if (signo[SIGTTOU] != 1 && signo[SIGTTIN] != 1) {
139  if (!(flags & RPP_STDIN))
140  bytes_written = write(output, prompt, strlen(prompt));
141  end = buf + bufsiz - 1;
142  p = buf;
143  while ((nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r') {
144  if (p < end) {
145 #if 0
146  if ((flags & RPP_SEVENBIT))
147  ch &= 0x7f;
148  if (isalpha(ch)) {
149  if ((flags & RPP_FORCELOWER))
150  ch = (char)tolower(ch);
151  if ((flags & RPP_FORCEUPPER))
152  ch = (char)toupper(ch);
153  }
154 #endif
155  *p++ = ch;
156  }
157  }
158  *p = '\0';
159  save_errno = errno;
160  if (!(term.c_lflag & ECHO))
161  bytes_written = write(output, "\n", 1);
162  }
163 
164  (void) bytes_written;
165 
166  /* Restore old terminal settings and signals. */
167  if (memcmp(&term, &oterm, sizeof(term)) != 0) {
168  while (tcsetattr(input, _T_FLUSH, &oterm) == -1 &&
169  errno == EINTR)
170  continue;
171  }
172  (void)sigaction(SIGALRM, &savealrm, NULL);
173  (void)sigaction(SIGHUP, &savehup, NULL);
174  (void)sigaction(SIGINT, &saveint, NULL);
175  (void)sigaction(SIGQUIT, &savequit, NULL);
176  (void)sigaction(SIGPIPE, &savepipe, NULL);
177  (void)sigaction(SIGTERM, &saveterm, NULL);
178  (void)sigaction(SIGTSTP, &savetstp, NULL);
179  (void)sigaction(SIGTTIN, &savettin, NULL);
180  (void)sigaction(SIGTTOU, &savettou, NULL);
181  if (input != STDIN_FILENO)
182  (void)close(input);
183 
184  /*
185  * If we were interrupted by a signal, resend it to ourselves
186  * now that we have restored the signal handlers.
187  */
188  for (i = 0; i < _NSIG; i++) {
189  if (signo[i]) {
190  kill(getpid(), i);
191  switch (i) {
192  case SIGTSTP:
193  case SIGTTIN:
194  case SIGTTOU:
195  need_restart = 1;
196  }
197  }
198  }
199  if (need_restart)
200  goto restart;
201 
202  if (save_errno)
203  errno = save_errno;
204  return(nr == -1 ? NULL : buf);
205 }
206 
207 #if 0
208 char *
209 getpass(const char *prompt)
210 {
211  static char buf[_PASSWORD_LEN + 1];
212 
213  return(readpassphrase(prompt, buf, sizeof(buf), RPP_ECHO_OFF));
214 }
215 #endif
216 
217 static void handler(int s)
218 {
219 
220  signo[s] = 1;
221 }
222 #endif /* HAVE_READPASSPHRASE */