readpassphrase.c revision 287292
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 287292 2015-08-29 14:25:01Z kib $"); 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; 49215236Sdelphij int input, output, save_errno, i, need_restart; 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 */ 71215236Sdelphij if ((flags & RPP_STDIN) || 72241046Sjilles (input = output = _open(_PATH_TTY, O_RDWR | O_CLOEXEC)) == -1) { 7386669Sgreen if (flags & RPP_REQUIRE_TTY) { 7486669Sgreen errno = ENOTTY; 7586669Sgreen return(NULL); 7686669Sgreen } 7786669Sgreen input = STDIN_FILENO; 7886669Sgreen output = STDERR_FILENO; 7986669Sgreen } 8086669Sgreen 8186669Sgreen /* 82215236Sdelphij * Turn off echo if possible. 83215236Sdelphij * If we are using a tty but are not the foreground pgrp this will 84215236Sdelphij * generate SIGTTOU, so do it *before* installing the signal handlers. 85215236Sdelphij */ 86215236Sdelphij if (input != STDIN_FILENO && tcgetattr(input, &oterm) == 0) { 87215236Sdelphij memcpy(&term, &oterm, sizeof(term)); 88215236Sdelphij if (!(flags & RPP_ECHO_ON)) 89215236Sdelphij term.c_lflag &= ~(ECHO | ECHONL); 90215236Sdelphij if (term.c_cc[VSTATUS] != _POSIX_VDISABLE) 91215236Sdelphij term.c_cc[VSTATUS] = _POSIX_VDISABLE; 92215236Sdelphij (void)tcsetattr(input, TCSAFLUSH|TCSASOFT, &term); 93215236Sdelphij } else { 94215236Sdelphij memset(&term, 0, sizeof(term)); 95215236Sdelphij term.c_lflag |= ECHO; 96215236Sdelphij memset(&oterm, 0, sizeof(oterm)); 97215236Sdelphij oterm.c_lflag |= ECHO; 98215236Sdelphij } 99215236Sdelphij 100215236Sdelphij /* 10191913Sgreen * Catch signals that would otherwise cause the user to end 10291913Sgreen * up with echo turned off in the shell. Don't worry about 103215236Sdelphij * things like SIGXCPU and SIGVTALRM for now. 10486669Sgreen */ 10591913Sgreen sigemptyset(&sa.sa_mask); 10691913Sgreen sa.sa_flags = 0; /* don't restart system calls */ 10791913Sgreen sa.sa_handler = handler; 108287292Skib (void)__libc_sigaction(SIGALRM, &sa, &savealrm); 109287292Skib (void)__libc_sigaction(SIGHUP, &sa, &savehup); 110287292Skib (void)__libc_sigaction(SIGINT, &sa, &saveint); 111287292Skib (void)__libc_sigaction(SIGPIPE, &sa, &savepipe); 112287292Skib (void)__libc_sigaction(SIGQUIT, &sa, &savequit); 113287292Skib (void)__libc_sigaction(SIGTERM, &sa, &saveterm); 114287292Skib (void)__libc_sigaction(SIGTSTP, &sa, &savetstp); 115287292Skib (void)__libc_sigaction(SIGTTIN, &sa, &savettin); 116287292Skib (void)__libc_sigaction(SIGTTOU, &sa, &savettou); 11786669Sgreen 118215236Sdelphij if (!(flags & RPP_STDIN)) 119215236Sdelphij (void)_write(output, prompt, strlen(prompt)); 12086669Sgreen end = buf + bufsiz - 1; 121215236Sdelphij p = buf; 122215236Sdelphij while ((nr = _read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r') { 12386669Sgreen if (p < end) { 12486669Sgreen if ((flags & RPP_SEVENBIT)) 12586669Sgreen ch &= 0x7f; 126268642Spfg if (isalpha((unsigned char)ch)) { 12786669Sgreen if ((flags & RPP_FORCELOWER)) 128268642Spfg ch = (char)tolower((unsigned char)ch); 12986669Sgreen if ((flags & RPP_FORCEUPPER)) 130268642Spfg ch = (char)toupper((unsigned char)ch); 13186669Sgreen } 13286669Sgreen *p++ = ch; 13386669Sgreen } 13486669Sgreen } 13586669Sgreen *p = '\0'; 13691913Sgreen save_errno = errno; 13786669Sgreen if (!(term.c_lflag & ECHO)) 13886732Sgreen (void)_write(output, "\n", 1); 13986669Sgreen 14091913Sgreen /* Restore old terminal settings and signals. */ 141215236Sdelphij if (memcmp(&term, &oterm, sizeof(term)) != 0) { 142215236Sdelphij while (tcsetattr(input, TCSAFLUSH|TCSASOFT, &oterm) == -1 && 143215236Sdelphij errno == EINTR && !signo[SIGTTOU]) 144215236Sdelphij continue; 145215236Sdelphij } 146287292Skib (void)__libc_sigaction(SIGALRM, &savealrm, NULL); 147287292Skib (void)__libc_sigaction(SIGHUP, &savehup, NULL); 148287292Skib (void)__libc_sigaction(SIGINT, &saveint, NULL); 149287292Skib (void)__libc_sigaction(SIGQUIT, &savequit, NULL); 150287292Skib (void)__libc_sigaction(SIGPIPE, &savepipe, NULL); 151287292Skib (void)__libc_sigaction(SIGTERM, &saveterm, NULL); 152287292Skib (void)__libc_sigaction(SIGTSTP, &savetstp, NULL); 153287292Skib (void)__libc_sigaction(SIGTTIN, &savettin, NULL); 154287292Skib (void)__libc_sigaction(SIGTTOU, &savettou, NULL); 15586669Sgreen if (input != STDIN_FILENO) 15686732Sgreen (void)_close(input); 15791913Sgreen 15891913Sgreen /* 15991913Sgreen * If we were interrupted by a signal, resend it to ourselves 16091913Sgreen * now that we have restored the signal handlers. 16191913Sgreen */ 162215236Sdelphij for (i = 0; i < NSIG; i++) { 163215236Sdelphij if (signo[i]) { 164215236Sdelphij kill(getpid(), i); 165215236Sdelphij switch (i) { 166215236Sdelphij case SIGTSTP: 167215236Sdelphij case SIGTTIN: 168215236Sdelphij case SIGTTOU: 169215236Sdelphij need_restart = 1; 170215236Sdelphij } 17191913Sgreen } 17291913Sgreen } 173215236Sdelphij if (need_restart) 174215236Sdelphij goto restart; 17591913Sgreen 176215236Sdelphij if (save_errno) 177215236Sdelphij errno = save_errno; 17891913Sgreen return(nr == -1 ? NULL : buf); 17986669Sgreen} 18086669Sgreen 18186669Sgreenchar * 18291913Sgreengetpass(const char *prompt) 18386669Sgreen{ 18486669Sgreen static char buf[_PASSWORD_LEN + 1]; 18586669Sgreen 18691922Sgreen if (readpassphrase(prompt, buf, sizeof(buf), RPP_ECHO_OFF) == NULL) 18791922Sgreen buf[0] = '\0'; 18891922Sgreen return(buf); 18986669Sgreen} 19091913Sgreen 19191913Sgreenstatic void handler(int s) 19291913Sgreen{ 19391913Sgreen 194215236Sdelphij signo[s] = 1; 19591913Sgreen} 196