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 49295367Sdes#ifndef _NSIG 50295367Sdes# ifdef NSIG 51295367Sdes# define _NSIG NSIG 52295367Sdes# else 53295367Sdes# define _NSIG 128 54295367Sdes# endif 55295367Sdes#endif 56295367Sdes 57204917Sdesstatic volatile sig_atomic_t signo[_NSIG]; 5898937Sdes 5998937Sdesstatic void handler(int); 6098937Sdes 6198937Sdeschar * 6298937Sdesreadpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags) 6398937Sdes{ 6498937Sdes ssize_t nr; 65204917Sdes int input, output, save_errno, i, need_restart; 6698937Sdes char ch, *p, *end; 6798937Sdes struct termios term, oterm; 68106121Sdes struct sigaction sa, savealrm, saveint, savehup, savequit, saveterm; 69106121Sdes struct sigaction savetstp, savettin, savettou, savepipe; 7098937Sdes 7198937Sdes /* I suppose we could alloc on demand in this case (XXX). */ 7298937Sdes if (bufsiz == 0) { 7398937Sdes errno = EINVAL; 7498937Sdes return(NULL); 7598937Sdes } 7698937Sdes 7798937Sdesrestart: 78204917Sdes for (i = 0; i < _NSIG; i++) 79204917Sdes signo[i] = 0; 80204917Sdes nr = -1; 81204917Sdes save_errno = 0; 82204917Sdes need_restart = 0; 8398937Sdes /* 8498937Sdes * Read and write to /dev/tty if available. If not, read from 8598937Sdes * stdin and write to stderr unless a tty is required. 8698937Sdes */ 87106121Sdes if ((flags & RPP_STDIN) || 88106121Sdes (input = output = open(_PATH_TTY, O_RDWR)) == -1) { 8998937Sdes if (flags & RPP_REQUIRE_TTY) { 9098937Sdes errno = ENOTTY; 9198937Sdes return(NULL); 9298937Sdes } 9398937Sdes input = STDIN_FILENO; 9498937Sdes output = STDERR_FILENO; 9598937Sdes } 9698937Sdes 9798937Sdes /* 9898937Sdes * Catch signals that would otherwise cause the user to end 9998937Sdes * up with echo turned off in the shell. Don't worry about 100106121Sdes * things like SIGXCPU and SIGVTALRM for now. 10198937Sdes */ 10298937Sdes sigemptyset(&sa.sa_mask); 10398937Sdes sa.sa_flags = 0; /* don't restart system calls */ 10498937Sdes sa.sa_handler = handler; 105106121Sdes (void)sigaction(SIGALRM, &sa, &savealrm); 106106121Sdes (void)sigaction(SIGHUP, &sa, &savehup); 10798937Sdes (void)sigaction(SIGINT, &sa, &saveint); 108106121Sdes (void)sigaction(SIGPIPE, &sa, &savepipe); 10998937Sdes (void)sigaction(SIGQUIT, &sa, &savequit); 11098937Sdes (void)sigaction(SIGTERM, &sa, &saveterm); 11198937Sdes (void)sigaction(SIGTSTP, &sa, &savetstp); 11298937Sdes (void)sigaction(SIGTTIN, &sa, &savettin); 11398937Sdes (void)sigaction(SIGTTOU, &sa, &savettou); 11498937Sdes 11598937Sdes /* Turn off echo if possible. */ 116106121Sdes if (input != STDIN_FILENO && tcgetattr(input, &oterm) == 0) { 11798937Sdes memcpy(&term, &oterm, sizeof(term)); 11898937Sdes if (!(flags & RPP_ECHO_ON)) 11998937Sdes term.c_lflag &= ~(ECHO | ECHONL); 12098937Sdes#ifdef VSTATUS 12198937Sdes if (term.c_cc[VSTATUS] != _POSIX_VDISABLE) 12298937Sdes term.c_cc[VSTATUS] = _POSIX_VDISABLE; 12398937Sdes#endif 12498937Sdes (void)tcsetattr(input, _T_FLUSH, &term); 12598937Sdes } else { 12698937Sdes memset(&term, 0, sizeof(term)); 127106121Sdes term.c_lflag |= ECHO; 12898937Sdes memset(&oterm, 0, sizeof(oterm)); 129106121Sdes oterm.c_lflag |= ECHO; 13098937Sdes } 13198937Sdes 132204917Sdes /* No I/O if we are already backgrounded. */ 133204917Sdes if (signo[SIGTTOU] != 1 && signo[SIGTTIN] != 1) { 134204917Sdes if (!(flags & RPP_STDIN)) 135204917Sdes (void)write(output, prompt, strlen(prompt)); 136204917Sdes end = buf + bufsiz - 1; 137204917Sdes p = buf; 138204917Sdes while ((nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r') { 139204917Sdes if (p < end) { 140204917Sdes if ((flags & RPP_SEVENBIT)) 141204917Sdes ch &= 0x7f; 142204917Sdes if (isalpha(ch)) { 143204917Sdes if ((flags & RPP_FORCELOWER)) 144204917Sdes ch = (char)tolower(ch); 145204917Sdes if ((flags & RPP_FORCEUPPER)) 146204917Sdes ch = (char)toupper(ch); 147204917Sdes } 148204917Sdes *p++ = ch; 14998937Sdes } 15098937Sdes } 151204917Sdes *p = '\0'; 152204917Sdes save_errno = errno; 153204917Sdes if (!(term.c_lflag & ECHO)) 154204917Sdes (void)write(output, "\n", 1); 15598937Sdes } 15698937Sdes 15798937Sdes /* Restore old terminal settings and signals. */ 158147001Sdes if (memcmp(&term, &oterm, sizeof(term)) != 0) { 159147001Sdes while (tcsetattr(input, _T_FLUSH, &oterm) == -1 && 160147001Sdes errno == EINTR) 161147001Sdes continue; 162147001Sdes } 163106121Sdes (void)sigaction(SIGALRM, &savealrm, NULL); 164106121Sdes (void)sigaction(SIGHUP, &savehup, NULL); 16598937Sdes (void)sigaction(SIGINT, &saveint, NULL); 16698937Sdes (void)sigaction(SIGQUIT, &savequit, NULL); 167106121Sdes (void)sigaction(SIGPIPE, &savepipe, NULL); 16898937Sdes (void)sigaction(SIGTERM, &saveterm, NULL); 16998937Sdes (void)sigaction(SIGTSTP, &savetstp, NULL); 17098937Sdes (void)sigaction(SIGTTIN, &savettin, NULL); 171204917Sdes (void)sigaction(SIGTTOU, &savettou, NULL); 17298937Sdes if (input != STDIN_FILENO) 17398937Sdes (void)close(input); 17498937Sdes 17598937Sdes /* 17698937Sdes * If we were interrupted by a signal, resend it to ourselves 17798937Sdes * now that we have restored the signal handlers. 17898937Sdes */ 179204917Sdes for (i = 0; i < _NSIG; i++) { 180204917Sdes if (signo[i]) { 181204917Sdes kill(getpid(), i); 182204917Sdes switch (i) { 183204917Sdes case SIGTSTP: 184204917Sdes case SIGTTIN: 185204917Sdes case SIGTTOU: 186204917Sdes need_restart = 1; 187204917Sdes } 18898937Sdes } 18998937Sdes } 190204917Sdes if (need_restart) 191204917Sdes goto restart; 19298937Sdes 193204917Sdes if (save_errno) 194204917Sdes errno = save_errno; 19598937Sdes return(nr == -1 ? NULL : buf); 19698937Sdes} 197204917Sdes 19898937Sdes#if 0 19998937Sdeschar * 20098937Sdesgetpass(const char *prompt) 20198937Sdes{ 20298937Sdes static char buf[_PASSWORD_LEN + 1]; 20398937Sdes 20498937Sdes return(readpassphrase(prompt, buf, sizeof(buf), RPP_ECHO_OFF)); 20598937Sdes} 20698937Sdes#endif 20798937Sdes 20898937Sdesstatic void handler(int s) 20998937Sdes{ 210124208Sdes 211204917Sdes signo[s] = 1; 21298937Sdes} 21398937Sdes#endif /* HAVE_READPASSPHRASE */ 214