1204917Sdes/* $OpenBSD: readpassphrase.c,v 1.22 2010/01/13 10:20:54 dtucker Exp $ */ 2126274Sdes 398937Sdes/* 4204917Sdes * Copyright (c) 2000-2002, 2007 Todd C. Miller <Todd.Miller@courtesan.com> 598937Sdes * 6124208Sdes * Permission to use, copy, modify, and distribute this software for any 7124208Sdes * purpose with or without fee is hereby granted, provided that the above 8124208Sdes * copyright notice and this permission notice appear in all copies. 998937Sdes * 10124208Sdes * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11124208Sdes * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12124208Sdes * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13124208Sdes * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14124208Sdes * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15124208Sdes * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16124208Sdes * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17124208Sdes * 18124208Sdes * Sponsored in part by the Defense Advanced Research Projects 19124208Sdes * Agency (DARPA) and Air Force Research Laboratory, Air Force 20124208Sdes * Materiel Command, USAF, under agreement number F39502-99-1-0512. 2198937Sdes */ 2298937Sdes 23157016Sdes/* OPENBSD ORIGINAL: lib/libc/gen/readpassphrase.c */ 2498937Sdes 2598937Sdes#include "includes.h" 2698937Sdes 2798937Sdes#ifndef HAVE_READPASSPHRASE 2898937Sdes 2998937Sdes#include <termios.h> 30162852Sdes#include <signal.h> 31162852Sdes#include <ctype.h> 32162852Sdes#include <fcntl.h> 3398937Sdes#include <readpassphrase.h> 34162852Sdes#include <errno.h> 35162852Sdes#include <string.h> 36162852Sdes#include <unistd.h> 3798937Sdes 3898937Sdes#ifdef TCSASOFT 3998937Sdes# define _T_FLUSH (TCSAFLUSH|TCSASOFT) 4098937Sdes#else 4198937Sdes# define _T_FLUSH (TCSAFLUSH) 4298937Sdes#endif 4398937Sdes 4498937Sdes/* SunOS 4.x which lacks _POSIX_VDISABLE, but has VDISABLE */ 4598937Sdes#if !defined(_POSIX_VDISABLE) && defined(VDISABLE) 4698937Sdes# define _POSIX_VDISABLE VDISABLE 4798937Sdes#endif 4898937Sdes 49204917Sdesstatic volatile sig_atomic_t signo[_NSIG]; 5098937Sdes 5198937Sdesstatic void handler(int); 5298937Sdes 5398937Sdeschar * 5498937Sdesreadpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags) 5598937Sdes{ 5698937Sdes ssize_t nr; 57204917Sdes int input, output, save_errno, i, need_restart; 5898937Sdes char ch, *p, *end; 5998937Sdes struct termios term, oterm; 60106121Sdes struct sigaction sa, savealrm, saveint, savehup, savequit, saveterm; 61106121Sdes struct sigaction savetstp, savettin, savettou, savepipe; 6298937Sdes 6398937Sdes /* I suppose we could alloc on demand in this case (XXX). */ 6498937Sdes if (bufsiz == 0) { 6598937Sdes errno = EINVAL; 6698937Sdes return(NULL); 6798937Sdes } 6898937Sdes 6998937Sdesrestart: 70204917Sdes for (i = 0; i < _NSIG; i++) 71204917Sdes signo[i] = 0; 72204917Sdes nr = -1; 73204917Sdes save_errno = 0; 74204917Sdes need_restart = 0; 7598937Sdes /* 7698937Sdes * Read and write to /dev/tty if available. If not, read from 7798937Sdes * stdin and write to stderr unless a tty is required. 7898937Sdes */ 79106121Sdes if ((flags & RPP_STDIN) || 80106121Sdes (input = output = open(_PATH_TTY, O_RDWR)) == -1) { 8198937Sdes if (flags & RPP_REQUIRE_TTY) { 8298937Sdes errno = ENOTTY; 8398937Sdes return(NULL); 8498937Sdes } 8598937Sdes input = STDIN_FILENO; 8698937Sdes output = STDERR_FILENO; 8798937Sdes } 8898937Sdes 8998937Sdes /* 9098937Sdes * Catch signals that would otherwise cause the user to end 9198937Sdes * up with echo turned off in the shell. Don't worry about 92106121Sdes * things like SIGXCPU and SIGVTALRM for now. 9398937Sdes */ 9498937Sdes sigemptyset(&sa.sa_mask); 9598937Sdes sa.sa_flags = 0; /* don't restart system calls */ 9698937Sdes sa.sa_handler = handler; 97106121Sdes (void)sigaction(SIGALRM, &sa, &savealrm); 98106121Sdes (void)sigaction(SIGHUP, &sa, &savehup); 9998937Sdes (void)sigaction(SIGINT, &sa, &saveint); 100106121Sdes (void)sigaction(SIGPIPE, &sa, &savepipe); 10198937Sdes (void)sigaction(SIGQUIT, &sa, &savequit); 10298937Sdes (void)sigaction(SIGTERM, &sa, &saveterm); 10398937Sdes (void)sigaction(SIGTSTP, &sa, &savetstp); 10498937Sdes (void)sigaction(SIGTTIN, &sa, &savettin); 10598937Sdes (void)sigaction(SIGTTOU, &sa, &savettou); 10698937Sdes 10798937Sdes /* Turn off echo if possible. */ 108106121Sdes if (input != STDIN_FILENO && tcgetattr(input, &oterm) == 0) { 10998937Sdes memcpy(&term, &oterm, sizeof(term)); 11098937Sdes if (!(flags & RPP_ECHO_ON)) 11198937Sdes term.c_lflag &= ~(ECHO | ECHONL); 11298937Sdes#ifdef VSTATUS 11398937Sdes if (term.c_cc[VSTATUS] != _POSIX_VDISABLE) 11498937Sdes term.c_cc[VSTATUS] = _POSIX_VDISABLE; 11598937Sdes#endif 11698937Sdes (void)tcsetattr(input, _T_FLUSH, &term); 11798937Sdes } else { 11898937Sdes memset(&term, 0, sizeof(term)); 119106121Sdes term.c_lflag |= ECHO; 12098937Sdes memset(&oterm, 0, sizeof(oterm)); 121106121Sdes oterm.c_lflag |= ECHO; 12298937Sdes } 12398937Sdes 124204917Sdes /* No I/O if we are already backgrounded. */ 125204917Sdes if (signo[SIGTTOU] != 1 && signo[SIGTTIN] != 1) { 126204917Sdes if (!(flags & RPP_STDIN)) 127204917Sdes (void)write(output, prompt, strlen(prompt)); 128204917Sdes end = buf + bufsiz - 1; 129204917Sdes p = buf; 130204917Sdes while ((nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r') { 131204917Sdes if (p < end) { 132204917Sdes if ((flags & RPP_SEVENBIT)) 133204917Sdes ch &= 0x7f; 134204917Sdes if (isalpha(ch)) { 135204917Sdes if ((flags & RPP_FORCELOWER)) 136204917Sdes ch = (char)tolower(ch); 137204917Sdes if ((flags & RPP_FORCEUPPER)) 138204917Sdes ch = (char)toupper(ch); 139204917Sdes } 140204917Sdes *p++ = ch; 14198937Sdes } 14298937Sdes } 143204917Sdes *p = '\0'; 144204917Sdes save_errno = errno; 145204917Sdes if (!(term.c_lflag & ECHO)) 146204917Sdes (void)write(output, "\n", 1); 14798937Sdes } 14898937Sdes 14998937Sdes /* Restore old terminal settings and signals. */ 150147001Sdes if (memcmp(&term, &oterm, sizeof(term)) != 0) { 151147001Sdes while (tcsetattr(input, _T_FLUSH, &oterm) == -1 && 152147001Sdes errno == EINTR) 153147001Sdes continue; 154147001Sdes } 155106121Sdes (void)sigaction(SIGALRM, &savealrm, NULL); 156106121Sdes (void)sigaction(SIGHUP, &savehup, NULL); 15798937Sdes (void)sigaction(SIGINT, &saveint, NULL); 15898937Sdes (void)sigaction(SIGQUIT, &savequit, NULL); 159106121Sdes (void)sigaction(SIGPIPE, &savepipe, NULL); 16098937Sdes (void)sigaction(SIGTERM, &saveterm, NULL); 16198937Sdes (void)sigaction(SIGTSTP, &savetstp, NULL); 16298937Sdes (void)sigaction(SIGTTIN, &savettin, NULL); 163204917Sdes (void)sigaction(SIGTTOU, &savettou, NULL); 16498937Sdes if (input != STDIN_FILENO) 16598937Sdes (void)close(input); 16698937Sdes 16798937Sdes /* 16898937Sdes * If we were interrupted by a signal, resend it to ourselves 16998937Sdes * now that we have restored the signal handlers. 17098937Sdes */ 171204917Sdes for (i = 0; i < _NSIG; i++) { 172204917Sdes if (signo[i]) { 173204917Sdes kill(getpid(), i); 174204917Sdes switch (i) { 175204917Sdes case SIGTSTP: 176204917Sdes case SIGTTIN: 177204917Sdes case SIGTTOU: 178204917Sdes need_restart = 1; 179204917Sdes } 18098937Sdes } 18198937Sdes } 182204917Sdes if (need_restart) 183204917Sdes goto restart; 18498937Sdes 185204917Sdes if (save_errno) 186204917Sdes errno = save_errno; 18798937Sdes return(nr == -1 ? NULL : buf); 18898937Sdes} 189204917Sdes 19098937Sdes#if 0 19198937Sdeschar * 19298937Sdesgetpass(const char *prompt) 19398937Sdes{ 19498937Sdes static char buf[_PASSWORD_LEN + 1]; 19598937Sdes 19698937Sdes return(readpassphrase(prompt, buf, sizeof(buf), RPP_ECHO_OFF)); 19798937Sdes} 19898937Sdes#endif 19998937Sdes 20098937Sdesstatic void handler(int s) 20198937Sdes{ 202124208Sdes 203204917Sdes signo[s] = 1; 20498937Sdes} 20598937Sdes#endif /* HAVE_READPASSPHRASE */ 206