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