readpassphrase.c revision 294691
1268642Spfg/* $OpenBSD: readpassphrase.c,v 1.24 2013/11/24 23:51:29 deraadt Exp $ */ 291913Sgreen 386669Sgreen/* 4215236Sdelphij * Copyright (c) 2000-2002, 2007, 2010 5215236Sdelphij * Todd C. Miller <Todd.Miller@courtesan.com> 686669Sgreen * 7215236Sdelphij * Permission to use, copy, modify, and distribute this software for any 8215236Sdelphij * purpose with or without fee is hereby granted, provided that the above 9215236Sdelphij * copyright notice and this permission notice appear in all copies. 1086669Sgreen * 11215236Sdelphij * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12215236Sdelphij * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13215236Sdelphij * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14215236Sdelphij * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15215236Sdelphij * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16215236Sdelphij * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17215236Sdelphij * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18215236Sdelphij * 19215236Sdelphij * Sponsored in part by the Defense Advanced Research Projects 20215236Sdelphij * Agency (DARPA) and Air Force Research Laboratory, Air Force 21215236Sdelphij * Materiel Command, USAF, under agreement number F39502-99-1-0512. 2286669Sgreen */ 2386669Sgreen 2486750Sfjoe#include <sys/cdefs.h> 2586669Sgreen__FBSDID("$FreeBSD: head/lib/libc/gen/readpassphrase.c 294691 2016-01-24 22:20:13Z sobomax $"); 2686669Sgreen 2786733Sgreen#include "namespace.h" 2886669Sgreen#include <ctype.h> 2986669Sgreen#include <errno.h> 3086669Sgreen#include <fcntl.h> 3186669Sgreen#include <paths.h> 3286669Sgreen#include <pwd.h> 3386669Sgreen#include <signal.h> 3486669Sgreen#include <string.h> 3586669Sgreen#include <termios.h> 3686669Sgreen#include <unistd.h> 3786669Sgreen#include <readpassphrase.h> 3886733Sgreen#include "un-namespace.h" 39287292Skib#include "libc_private.h" 4086669Sgreen 41215236Sdelphijstatic volatile sig_atomic_t signo[NSIG]; 4291913Sgreen 4391913Sgreenstatic void handler(int); 4491913Sgreen 4586669Sgreenchar * 4691913Sgreenreadpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags) 4786669Sgreen{ 4891913Sgreen ssize_t nr; 49294691Ssobomax int input, output, save_errno, i, need_restart, input_is_tty; 5091913Sgreen char ch, *p, *end; 5186669Sgreen struct termios term, oterm; 52215236Sdelphij struct sigaction sa, savealrm, saveint, savehup, savequit, saveterm; 53215236Sdelphij struct sigaction savetstp, savettin, savettou, savepipe; 5486669Sgreen 5586669Sgreen /* I suppose we could alloc on demand in this case (XXX). */ 5686669Sgreen if (bufsiz == 0) { 5786669Sgreen errno = EINVAL; 5886669Sgreen return(NULL); 5986669Sgreen } 6086669Sgreen 6191913Sgreenrestart: 62215236Sdelphij for (i = 0; i < NSIG; i++) 63215236Sdelphij signo[i] = 0; 64215236Sdelphij nr = -1; 65215236Sdelphij save_errno = 0; 66215236Sdelphij need_restart = 0; 6786669Sgreen /* 6886669Sgreen * Read and write to /dev/tty if available. If not, read from 6986669Sgreen * stdin and write to stderr unless a tty is required. 7086669Sgreen */ 71294691Ssobomax input_is_tty = 0; 72294691Ssobomax if (!(flags & RPP_STDIN)) { 73294691Ssobomax input = output = _open(_PATH_TTY, O_RDWR | O_CLOEXEC); 74294691Ssobomax if (input == -1) { 75294691Ssobomax if (flags & RPP_REQUIRE_TTY) { 76294691Ssobomax errno = ENOTTY; 77294691Ssobomax return(NULL); 78294691Ssobomax } 79294691Ssobomax input = STDIN_FILENO; 80294691Ssobomax output = STDERR_FILENO; 81294691Ssobomax } else { 82294691Ssobomax input_is_tty = 1; 8386669Sgreen } 84294691Ssobomax } else { 8586669Sgreen input = STDIN_FILENO; 8686669Sgreen output = STDERR_FILENO; 8786669Sgreen } 8886669Sgreen 8986669Sgreen /* 90215236Sdelphij * Turn off echo if possible. 91215236Sdelphij * If we are using a tty but are not the foreground pgrp this will 92215236Sdelphij * generate SIGTTOU, so do it *before* installing the signal handlers. 93215236Sdelphij */ 94294691Ssobomax if (input_is_tty && tcgetattr(input, &oterm) == 0) { 95215236Sdelphij memcpy(&term, &oterm, sizeof(term)); 96215236Sdelphij if (!(flags & RPP_ECHO_ON)) 97215236Sdelphij term.c_lflag &= ~(ECHO | ECHONL); 98215236Sdelphij if (term.c_cc[VSTATUS] != _POSIX_VDISABLE) 99215236Sdelphij term.c_cc[VSTATUS] = _POSIX_VDISABLE; 100215236Sdelphij (void)tcsetattr(input, TCSAFLUSH|TCSASOFT, &term); 101215236Sdelphij } else { 102215236Sdelphij memset(&term, 0, sizeof(term)); 103215236Sdelphij term.c_lflag |= ECHO; 104215236Sdelphij memset(&oterm, 0, sizeof(oterm)); 105215236Sdelphij oterm.c_lflag |= ECHO; 106215236Sdelphij } 107215236Sdelphij 108215236Sdelphij /* 10991913Sgreen * Catch signals that would otherwise cause the user to end 11091913Sgreen * up with echo turned off in the shell. Don't worry about 111215236Sdelphij * things like SIGXCPU and SIGVTALRM for now. 11286669Sgreen */ 11391913Sgreen sigemptyset(&sa.sa_mask); 11491913Sgreen sa.sa_flags = 0; /* don't restart system calls */ 11591913Sgreen sa.sa_handler = handler; 116287292Skib (void)__libc_sigaction(SIGALRM, &sa, &savealrm); 117287292Skib (void)__libc_sigaction(SIGHUP, &sa, &savehup); 118287292Skib (void)__libc_sigaction(SIGINT, &sa, &saveint); 119287292Skib (void)__libc_sigaction(SIGPIPE, &sa, &savepipe); 120287292Skib (void)__libc_sigaction(SIGQUIT, &sa, &savequit); 121287292Skib (void)__libc_sigaction(SIGTERM, &sa, &saveterm); 122287292Skib (void)__libc_sigaction(SIGTSTP, &sa, &savetstp); 123287292Skib (void)__libc_sigaction(SIGTTIN, &sa, &savettin); 124287292Skib (void)__libc_sigaction(SIGTTOU, &sa, &savettou); 12586669Sgreen 126215236Sdelphij if (!(flags & RPP_STDIN)) 127215236Sdelphij (void)_write(output, prompt, strlen(prompt)); 12886669Sgreen end = buf + bufsiz - 1; 129215236Sdelphij p = buf; 130215236Sdelphij while ((nr = _read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r') { 13186669Sgreen if (p < end) { 13286669Sgreen if ((flags & RPP_SEVENBIT)) 13386669Sgreen ch &= 0x7f; 134268642Spfg if (isalpha((unsigned char)ch)) { 13586669Sgreen if ((flags & RPP_FORCELOWER)) 136268642Spfg ch = (char)tolower((unsigned char)ch); 13786669Sgreen if ((flags & RPP_FORCEUPPER)) 138268642Spfg ch = (char)toupper((unsigned char)ch); 13986669Sgreen } 14086669Sgreen *p++ = ch; 14186669Sgreen } 14286669Sgreen } 14386669Sgreen *p = '\0'; 14491913Sgreen save_errno = errno; 14586669Sgreen if (!(term.c_lflag & ECHO)) 14686732Sgreen (void)_write(output, "\n", 1); 14786669Sgreen 14891913Sgreen /* Restore old terminal settings and signals. */ 149215236Sdelphij if (memcmp(&term, &oterm, sizeof(term)) != 0) { 150215236Sdelphij while (tcsetattr(input, TCSAFLUSH|TCSASOFT, &oterm) == -1 && 151215236Sdelphij errno == EINTR && !signo[SIGTTOU]) 152215236Sdelphij continue; 153215236Sdelphij } 154287292Skib (void)__libc_sigaction(SIGALRM, &savealrm, NULL); 155287292Skib (void)__libc_sigaction(SIGHUP, &savehup, NULL); 156287292Skib (void)__libc_sigaction(SIGINT, &saveint, NULL); 157287292Skib (void)__libc_sigaction(SIGQUIT, &savequit, NULL); 158287292Skib (void)__libc_sigaction(SIGPIPE, &savepipe, NULL); 159287292Skib (void)__libc_sigaction(SIGTERM, &saveterm, NULL); 160287292Skib (void)__libc_sigaction(SIGTSTP, &savetstp, NULL); 161287292Skib (void)__libc_sigaction(SIGTTIN, &savettin, NULL); 162287292Skib (void)__libc_sigaction(SIGTTOU, &savettou, NULL); 163294691Ssobomax if (input_is_tty) 16486732Sgreen (void)_close(input); 16591913Sgreen 16691913Sgreen /* 16791913Sgreen * If we were interrupted by a signal, resend it to ourselves 16891913Sgreen * now that we have restored the signal handlers. 16991913Sgreen */ 170215236Sdelphij for (i = 0; i < NSIG; i++) { 171215236Sdelphij if (signo[i]) { 172215236Sdelphij kill(getpid(), i); 173215236Sdelphij switch (i) { 174215236Sdelphij case SIGTSTP: 175215236Sdelphij case SIGTTIN: 176215236Sdelphij case SIGTTOU: 177215236Sdelphij need_restart = 1; 178215236Sdelphij } 17991913Sgreen } 18091913Sgreen } 181215236Sdelphij if (need_restart) 182215236Sdelphij goto restart; 18391913Sgreen 184215236Sdelphij if (save_errno) 185215236Sdelphij errno = save_errno; 18691913Sgreen return(nr == -1 ? NULL : buf); 18786669Sgreen} 18886669Sgreen 18986669Sgreenchar * 19091913Sgreengetpass(const char *prompt) 19186669Sgreen{ 19286669Sgreen static char buf[_PASSWORD_LEN + 1]; 19386669Sgreen 19491922Sgreen if (readpassphrase(prompt, buf, sizeof(buf), RPP_ECHO_OFF) == NULL) 19591922Sgreen buf[0] = '\0'; 19691922Sgreen return(buf); 19786669Sgreen} 19891913Sgreen 19991913Sgreenstatic void handler(int s) 20091913Sgreen{ 20191913Sgreen 202215236Sdelphij signo[s] = 1; 20391913Sgreen} 204