readpass.c revision 255767
1249259Sdim/* $OpenBSD: readpass.c,v 1.49 2013/05/17 00:13:14 djm Exp $ */ 2249259Sdim/* 3249259Sdim * Copyright (c) 2001 Markus Friedl. All rights reserved. 4249259Sdim * 5249259Sdim * Redistribution and use in source and binary forms, with or without 6249259Sdim * modification, are permitted provided that the following conditions 7249259Sdim * are met: 8249259Sdim * 1. Redistributions of source code must retain the above copyright 9249259Sdim * notice, this list of conditions and the following disclaimer. 10249259Sdim * 2. Redistributions in binary form must reproduce the above copyright 11249259Sdim * notice, this list of conditions and the following disclaimer in the 12249259Sdim * documentation and/or other materials provided with the distribution. 13249259Sdim * 14249259Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15249259Sdim * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16249259Sdim * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17249259Sdim * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18263508Sdim * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19249259Sdim * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20249259Sdim * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21249259Sdim * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22249259Sdim * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23249259Sdim * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24249259Sdim */ 25249259Sdim 26249259Sdim#include "includes.h" 27249259Sdim 28249259Sdim#include <sys/types.h> 29249259Sdim#include <sys/wait.h> 30249259Sdim 31249259Sdim#include <errno.h> 32249259Sdim#include <fcntl.h> 33249259Sdim#ifdef HAVE_PATHS_H 34249259Sdim# include <paths.h> 35249259Sdim#endif 36249259Sdim#include <signal.h> 37249259Sdim#include <stdarg.h> 38249259Sdim#include <stdio.h> 39249259Sdim#include <stdlib.h> 40249259Sdim#include <string.h> 41249259Sdim#include <unistd.h> 42249259Sdim 43249259Sdim#include "xmalloc.h" 44263508Sdim#include "misc.h" 45263508Sdim#include "pathnames.h" 46263508Sdim#include "log.h" 47263508Sdim#include "ssh.h" 48263508Sdim#include "uidswap.h" 49249259Sdim 50249259Sdimstatic char * 51263508Sdimssh_askpass(char *askpass, const char *msg) 52263508Sdim{ 53249259Sdim pid_t pid, ret; 54249259Sdim size_t len; 55249259Sdim char *pass; 56249259Sdim int p[2], status; 57249259Sdim char buf[1024]; 58249259Sdim void (*osigchld)(int); 59249259Sdim 60249259Sdim if (fflush(stdout) != 0) 61249259Sdim error("ssh_askpass: fflush: %s", strerror(errno)); 62249259Sdim if (askpass == NULL) 63263508Sdim fatal("internal error: askpass undefined"); 64263508Sdim if (pipe(p) < 0) { 65263508Sdim error("ssh_askpass: pipe: %s", strerror(errno)); 66263508Sdim return NULL; 67249259Sdim } 68263508Sdim osigchld = signal(SIGCHLD, SIG_DFL); 69249259Sdim if ((pid = fork()) < 0) { 70249259Sdim error("ssh_askpass: fork: %s", strerror(errno)); 71249259Sdim signal(SIGCHLD, osigchld); 72249259Sdim return NULL; 73249259Sdim } 74249259Sdim if (pid == 0) { 75263508Sdim permanently_drop_suid(getuid()); 76263508Sdim close(p[0]); 77263508Sdim if (dup2(p[1], STDOUT_FILENO) < 0) 78263508Sdim fatal("ssh_askpass: dup2: %s", strerror(errno)); 79263508Sdim execlp(askpass, askpass, msg, (char *) 0); 80263508Sdim fatal("ssh_askpass: exec(%s): %s", askpass, strerror(errno)); 81249259Sdim } 82249259Sdim close(p[1]); 83249259Sdim 84249259Sdim len = 0; 85249259Sdim do { 86249259Sdim ssize_t r = read(p[0], buf + len, sizeof(buf) - 1 - len); 87249259Sdim 88249259Sdim if (r == -1 && errno == EINTR) 89249259Sdim continue; 90249259Sdim if (r <= 0) 91249259Sdim break; 92249259Sdim len += r; 93249259Sdim } while (sizeof(buf) - 1 - len > 0); 94249259Sdim buf[len] = '\0'; 95249259Sdim 96249259Sdim close(p[0]); 97263508Sdim while ((ret = waitpid(pid, &status, 0)) < 0) 98263508Sdim if (errno != EINTR) 99263508Sdim break; 100263508Sdim signal(SIGCHLD, osigchld); 101263508Sdim if (ret == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) { 102263508Sdim memset(buf, 0, sizeof(buf)); 103263508Sdim return NULL; 104263508Sdim } 105263508Sdim 106263508Sdim buf[strcspn(buf, "\r\n")] = '\0'; 107249259Sdim pass = xstrdup(buf); 108249259Sdim memset(buf, 0, sizeof(buf)); 109249259Sdim return pass; 110263508Sdim} 111263508Sdim 112249259Sdim/* 113249259Sdim * Reads a passphrase from /dev/tty with echo turned off/on. Returns the 114249259Sdim * passphrase (allocated with xmalloc). Exits if EOF is encountered. If 115249259Sdim * RP_ALLOW_STDIN is set, the passphrase will be read from stdin if no 116249259Sdim * tty is available 117249259Sdim */ 118249259Sdimchar * 119249259Sdimread_passphrase(const char *prompt, int flags) 120249259Sdim{ 121249259Sdim char *askpass = NULL, *ret, buf[1024]; 122263508Sdim int rppflags, use_askpass = 0, ttyfd; 123263508Sdim 124263508Sdim rppflags = (flags & RP_ECHO) ? RPP_ECHO_ON : RPP_ECHO_OFF; 125263508Sdim if (flags & RP_USE_ASKPASS) 126263508Sdim use_askpass = 1; 127263508Sdim else if (flags & RP_ALLOW_STDIN) { 128263508Sdim if (!isatty(STDIN_FILENO)) { 129263508Sdim debug("read_passphrase: stdin is not a tty"); 130263508Sdim use_askpass = 1; 131249259Sdim } 132249259Sdim } else { 133249259Sdim rppflags |= RPP_REQUIRE_TTY; 134249259Sdim ttyfd = open(_PATH_TTY, O_RDWR); 135249259Sdim if (ttyfd >= 0) 136249259Sdim close(ttyfd); 137249259Sdim else { 138249259Sdim debug("read_passphrase: can't open %s: %s", _PATH_TTY, 139249259Sdim strerror(errno)); 140249259Sdim use_askpass = 1; 141249259Sdim } 142249259Sdim } 143249259Sdim 144249259Sdim if ((flags & RP_USE_ASKPASS) && getenv("DISPLAY") == NULL) 145249259Sdim return (flags & RP_ALLOW_EOF) ? NULL : xstrdup(""); 146249259Sdim 147249259Sdim if (use_askpass && getenv("DISPLAY")) { 148249259Sdim if (getenv(SSH_ASKPASS_ENV)) 149249259Sdim askpass = getenv(SSH_ASKPASS_ENV); 150249259Sdim else 151249259Sdim askpass = _PATH_SSH_ASKPASS_DEFAULT; 152249259Sdim if ((ret = ssh_askpass(askpass, prompt)) == NULL) 153249259Sdim if (!(flags & RP_ALLOW_EOF)) 154249259Sdim return xstrdup(""); 155249259Sdim return ret; 156249259Sdim } 157263508Sdim 158263508Sdim if (readpassphrase(prompt, buf, sizeof buf, rppflags) == NULL) { 159249259Sdim if (flags & RP_ALLOW_EOF) 160249259Sdim return NULL; 161249259Sdim return xstrdup(""); 162249259Sdim } 163263508Sdim 164263508Sdim ret = xstrdup(buf); 165249259Sdim memset(buf, 'x', sizeof buf); 166249259Sdim return ret; 167249259Sdim} 168249259Sdim 169249259Sdimint 170249259Sdimask_permission(const char *fmt, ...) 171249259Sdim{ 172249259Sdim va_list args; 173249259Sdim char *p, prompt[1024]; 174249259Sdim int allowed = 0; 175249259Sdim 176249259Sdim va_start(args, fmt); 177249259Sdim vsnprintf(prompt, sizeof(prompt), fmt, args); 178249259Sdim va_end(args); 179249259Sdim 180249259Sdim p = read_passphrase(prompt, RP_USE_ASKPASS|RP_ALLOW_EOF); 181249259Sdim if (p != NULL) { 182249259Sdim /* 183249259Sdim * Accept empty responses and responses consisting 184249259Sdim * of the word "yes" as affirmative. 185249259Sdim */ 186249259Sdim if (*p == '\0' || *p == '\n' || 187249259Sdim strcasecmp(p, "yes") == 0) 188249259Sdim allowed = 1; 189249259Sdim free(p); 190249259Sdim } 191249259Sdim 192249259Sdim return (allowed); 193249259Sdim} 194249259Sdim