X-Git-Url: http://andersk.mit.edu/gitweb/openssh.git/blobdiff_plain/6ae2364d36eaebbbc830a417d5a01ebf3cc84e5f..13b90bdd9190a9d55c110cad33483b1a5df1f3ea:/readpass.c diff --git a/readpass.c b/readpass.c index 3bfec82c..bd144c2e 100644 --- a/readpass.c +++ b/readpass.c @@ -1,6 +1,6 @@ +/* $OpenBSD: readpass.c,v 1.47 2006/08/03 03:34:42 deraadt Exp $ */ /* - * Copyright (c) 1988, 1993 - * The Regents of the University of California. All rights reserved. + * Copyright (c) 2001 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,110 +10,179 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" -RCSID("$Id$"); + +#include +#include + +#include +#include +#ifdef HAVE_PATHS_H +# include +#endif +#include +#include +#include +#include +#include #include "xmalloc.h" +#include "misc.h" +#include "pathnames.h" +#include "log.h" #include "ssh.h" +#include "uidswap.h" -volatile int intr; - -void -intcatch() +static char * +ssh_askpass(char *askpass, const char *msg) { - intr = 1; + pid_t pid; + size_t len; + char *pass; + int p[2], status, ret; + char buf[1024]; + + if (fflush(stdout) != 0) + error("ssh_askpass: fflush: %s", strerror(errno)); + if (askpass == NULL) + fatal("internal error: askpass undefined"); + if (pipe(p) < 0) { + error("ssh_askpass: pipe: %s", strerror(errno)); + return NULL; + } + if ((pid = fork()) < 0) { + error("ssh_askpass: fork: %s", strerror(errno)); + return NULL; + } + if (pid == 0) { + permanently_drop_suid(getuid()); + close(p[0]); + if (dup2(p[1], STDOUT_FILENO) < 0) + fatal("ssh_askpass: dup2: %s", strerror(errno)); + execlp(askpass, askpass, msg, (char *) 0); + fatal("ssh_askpass: exec(%s): %s", askpass, strerror(errno)); + } + close(p[1]); + + len = ret = 0; + do { + ret = read(p[0], buf + len, sizeof(buf) - 1 - len); + if (ret == -1 && errno == EINTR) + continue; + if (ret <= 0) + break; + len += ret; + } while (sizeof(buf) - 1 - len > 0); + buf[len] = '\0'; + + close(p[0]); + while (waitpid(pid, &status, 0) < 0) + if (errno != EINTR) + break; + + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + memset(buf, 0, sizeof(buf)); + return NULL; + } + + buf[strcspn(buf, "\r\n")] = '\0'; + pass = xstrdup(buf); + memset(buf, 0, sizeof(buf)); + return pass; } /* - * Reads a passphrase from /dev/tty with echo turned off. Returns the - * passphrase (allocated with xmalloc), being very careful to ensure that - * no other userland buffer is storing the password. + * Reads a passphrase from /dev/tty with echo turned off/on. Returns the + * passphrase (allocated with xmalloc). Exits if EOF is encountered. If + * RP_ALLOW_STDIN is set, the passphrase will be read from stdin if no + * tty is available */ char * -read_passphrase(const char *prompt, int from_stdin) +read_passphrase(const char *prompt, int flags) { - char buf[1024], *p, ch; - struct termios tio, saved_tio; - sigset_t oset, nset; - struct sigaction sa, osa; - int input, output, echo = 0; - - if (from_stdin) { - input = STDIN_FILENO; - output = STDERR_FILENO; - } else - input = output = open("/dev/tty", O_RDWR); - - if (input == -1) - fatal("You have no controlling tty. Cannot read passphrase.\n"); - - /* block signals, get terminal modes and turn off echo */ - sigemptyset(&nset); - sigaddset(&nset, SIGTSTP); - (void) sigprocmask(SIG_BLOCK, &nset, &oset); - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = intcatch; - (void) sigaction(SIGINT, &sa, &osa); - - intr = 0; - - if (tcgetattr(input, &saved_tio) == 0 && (saved_tio.c_lflag & ECHO)) { - echo = 1; - tio = saved_tio; - tio.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); - (void) tcsetattr(input, TCSANOW, &tio); + char *askpass = NULL, *ret, buf[1024]; + int rppflags, use_askpass = 0, ttyfd; + + rppflags = (flags & RP_ECHO) ? RPP_ECHO_ON : RPP_ECHO_OFF; + if (flags & RP_USE_ASKPASS) + use_askpass = 1; + else if (flags & RP_ALLOW_STDIN) { + if (!isatty(STDIN_FILENO)) { + debug("read_passphrase: stdin is not a tty"); + use_askpass = 1; + } + } else { + rppflags |= RPP_REQUIRE_TTY; + ttyfd = open(_PATH_TTY, O_RDWR); + if (ttyfd >= 0) + close(ttyfd); + else { + debug("read_passphrase: can't open %s: %s", _PATH_TTY, + strerror(errno)); + use_askpass = 1; + } } - fflush(stdout); + if ((flags & RP_USE_ASKPASS) && getenv("DISPLAY") == NULL) + return (flags & RP_ALLOW_EOF) ? NULL : xstrdup(""); - (void)write(output, prompt, strlen(prompt)); - for (p = buf; read(input, &ch, 1) == 1 && ch != '\n';) { - if (intr) - break; - if (p < buf + sizeof(buf) - 1) - *p++ = ch; + if (use_askpass && getenv("DISPLAY")) { + if (getenv(SSH_ASKPASS_ENV)) + askpass = getenv(SSH_ASKPASS_ENV); + else + askpass = _PATH_SSH_ASKPASS_DEFAULT; + if ((ret = ssh_askpass(askpass, prompt)) == NULL) + if (!(flags & RP_ALLOW_EOF)) + return xstrdup(""); + return ret; } - *p = '\0'; - if (!intr) - (void)write(output, "\n", 1); - - /* restore terminal modes and allow signals */ - if (echo) - tcsetattr(input, TCSANOW, &saved_tio); - (void) sigprocmask(SIG_SETMASK, &oset, NULL); - (void) sigaction(SIGINT, &osa, NULL); - - if (intr) { - kill(getpid(), SIGINT); - sigemptyset(&nset); - /* XXX tty has not neccessarily drained by now? */ - sigsuspend(&nset); + + if (readpassphrase(prompt, buf, sizeof buf, rppflags) == NULL) { + if (flags & RP_ALLOW_EOF) + return NULL; + return xstrdup(""); } - if (!from_stdin) - (void)close(input); - p = xstrdup(buf); - memset(buf, 0, sizeof(buf)); - return (p); + ret = xstrdup(buf); + memset(buf, 'x', sizeof buf); + return ret; +} + +int +ask_permission(const char *fmt, ...) +{ + va_list args; + char *p, prompt[1024]; + int allowed = 0; + + va_start(args, fmt); + vsnprintf(prompt, sizeof(prompt), fmt, args); + va_end(args); + + p = read_passphrase(prompt, RP_USE_ASKPASS|RP_ALLOW_EOF); + if (p != NULL) { + /* + * Accept empty responses and responses consisting + * of the word "yes" as affirmative. + */ + if (*p == '\0' || *p == '\n' || + strcasecmp(p, "yes") == 0) + allowed = 1; + xfree(p); + } + + return (allowed); }