diff options
Diffstat (limited to 'readpass.c')
-rw-r--r-- | readpass.c | 143 |
1 files changed, 122 insertions, 21 deletions
@@ -1,4 +1,4 @@ -/* $OpenBSD: readpass.c,v 1.50 2014/02/02 03:44:31 djm Exp $ */ +/* $OpenBSD: readpass.c,v 1.61 2020/01/23 07:10:22 dtucker Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * @@ -48,7 +48,7 @@ #include "uidswap.h" static char * -ssh_askpass(char *askpass, const char *msg) +ssh_askpass(char *askpass, const char *msg, const char *env_hint) { pid_t pid, ret; size_t len; @@ -58,26 +58,27 @@ ssh_askpass(char *askpass, const char *msg) void (*osigchld)(int); if (fflush(stdout) != 0) - error("ssh_askpass: fflush: %s", strerror(errno)); + error("%s: fflush: %s", __func__, strerror(errno)); if (askpass == NULL) fatal("internal error: askpass undefined"); - if (pipe(p) < 0) { - error("ssh_askpass: pipe: %s", strerror(errno)); + if (pipe(p) == -1) { + error("%s: pipe: %s", __func__, strerror(errno)); return NULL; } - osigchld = signal(SIGCHLD, SIG_DFL); - if ((pid = fork()) < 0) { - error("ssh_askpass: fork: %s", strerror(errno)); - signal(SIGCHLD, osigchld); + osigchld = ssh_signal(SIGCHLD, SIG_DFL); + if ((pid = fork()) == -1) { + error("%s: fork: %s", __func__, strerror(errno)); + ssh_signal(SIGCHLD, osigchld); 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)); + if (dup2(p[1], STDOUT_FILENO) == -1) + fatal("%s: dup2: %s", __func__, strerror(errno)); + if (env_hint != NULL) + setenv("SSH_ASKPASS_PROMPT", env_hint, 1); + execlp(askpass, askpass, msg, (char *)NULL); + fatal("%s: exec(%s): %s", __func__, askpass, strerror(errno)); } close(p[1]); @@ -94,10 +95,10 @@ ssh_askpass(char *askpass, const char *msg) buf[len] = '\0'; close(p[0]); - while ((ret = waitpid(pid, &status, 0)) < 0) + while ((ret = waitpid(pid, &status, 0)) == -1) if (errno != EINTR) break; - signal(SIGCHLD, osigchld); + ssh_signal(SIGCHLD, osigchld); if (ret == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) { explicit_bzero(buf, sizeof(buf)); return NULL; @@ -109,6 +110,9 @@ ssh_askpass(char *askpass, const char *msg) return pass; } +/* private/internal read_passphrase flags */ +#define RP_ASK_PERMISSION 0x8000 /* pass hint to askpass for confirm UI */ + /* * Reads a passphrase from /dev/tty with echo turned off/on. Returns the * passphrase (allocated with xmalloc). Exits if EOF is encountered. If @@ -118,8 +122,9 @@ ssh_askpass(char *askpass, const char *msg) char * read_passphrase(const char *prompt, int flags) { - char *askpass = NULL, *ret, buf[1024]; + char cr = '\r', *askpass = NULL, *ret, buf[1024]; int rppflags, use_askpass = 0, ttyfd; + const char *askpass_hint = NULL; rppflags = (flags & RP_ECHO) ? RPP_ECHO_ON : RPP_ECHO_OFF; if (flags & RP_USE_ASKPASS) @@ -132,9 +137,16 @@ read_passphrase(const char *prompt, int flags) } else { rppflags |= RPP_REQUIRE_TTY; ttyfd = open(_PATH_TTY, O_RDWR); - if (ttyfd >= 0) + if (ttyfd >= 0) { + /* + * If we're on a tty, ensure that show the prompt at + * the beginning of the line. This will hopefully + * clobber any password characters the user has + * optimistically typed before echo is disabled. + */ + (void)write(ttyfd, &cr, 1); close(ttyfd); - else { + } else { debug("read_passphrase: can't open %s: %s", _PATH_TTY, strerror(errno)); use_askpass = 1; @@ -149,7 +161,9 @@ read_passphrase(const char *prompt, int flags) askpass = getenv(SSH_ASKPASS_ENV); else askpass = _PATH_SSH_ASKPASS_DEFAULT; - if ((ret = ssh_askpass(askpass, prompt)) == NULL) + if ((flags & RP_ASK_PERMISSION) != 0) + askpass_hint = "confirm"; + if ((ret = ssh_askpass(askpass, prompt, askpass_hint)) == NULL) if (!(flags & RP_ALLOW_EOF)) return xstrdup(""); return ret; @@ -177,7 +191,8 @@ ask_permission(const char *fmt, ...) vsnprintf(prompt, sizeof(prompt), fmt, args); va_end(args); - p = read_passphrase(prompt, RP_USE_ASKPASS|RP_ALLOW_EOF); + p = read_passphrase(prompt, + RP_USE_ASKPASS|RP_ALLOW_EOF|RP_ASK_PERMISSION); if (p != NULL) { /* * Accept empty responses and responses consisting @@ -191,3 +206,89 @@ ask_permission(const char *fmt, ...) return (allowed); } + +struct notifier_ctx { + pid_t pid; + void (*osigchld)(int); +}; + +struct notifier_ctx * +notify_start(int force_askpass, const char *fmt, ...) +{ + va_list args; + char *prompt = NULL; + int devnull; + pid_t pid; + void (*osigchld)(int); + const char *askpass; + struct notifier_ctx *ret; + + va_start(args, fmt); + xvasprintf(&prompt, fmt, args); + va_end(args); + + if (fflush(NULL) != 0) + error("%s: fflush: %s", __func__, strerror(errno)); + if (!force_askpass && isatty(STDERR_FILENO)) { + (void)write(STDERR_FILENO, "\r", 1); + (void)write(STDERR_FILENO, prompt, strlen(prompt)); + (void)write(STDERR_FILENO, "\r\n", 2); + free(prompt); + return NULL; + } + if ((askpass = getenv("SSH_ASKPASS")) == NULL) + askpass = _PATH_SSH_ASKPASS_DEFAULT; + if (getenv("DISPLAY") == NULL || *askpass == '\0') { + debug3("%s: cannot notify", __func__); + free(prompt); + return NULL; + } + osigchld = ssh_signal(SIGCHLD, SIG_DFL); + if ((pid = fork()) == -1) { + error("%s: fork: %s", __func__, strerror(errno)); + ssh_signal(SIGCHLD, osigchld); + free(prompt); + return NULL; + } + if (pid == 0) { + if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) + fatal("%s: open %s", __func__, strerror(errno)); + if (dup2(devnull, STDIN_FILENO) == -1 || + dup2(devnull, STDOUT_FILENO) == -1) + fatal("%s: dup2: %s", __func__, strerror(errno)); + closefrom(STDERR_FILENO + 1); + setenv("SSH_ASKPASS_PROMPT", "none", 1); /* hint to UI */ + execlp(askpass, askpass, prompt, (char *)NULL); + error("%s: exec(%s): %s", __func__, askpass, strerror(errno)); + _exit(1); + /* NOTREACHED */ + } + if ((ret = calloc(1, sizeof(*ret))) == NULL) { + kill(pid, SIGTERM); + fatal("%s: calloc failed", __func__); + } + ret->pid = pid; + ret->osigchld = osigchld; + free(prompt); + return ret; +} + +void +notify_complete(struct notifier_ctx *ctx) +{ + int ret; + + if (ctx == NULL || ctx->pid <= 0) { + free(ctx); + return; + } + kill(ctx->pid, SIGTERM); + while ((ret = waitpid(ctx->pid, NULL, 0)) == -1) { + if (errno != EINTR) + break; + } + if (ret == -1) + fatal("%s: waitpid: %s", __func__, strerror(errno)); + ssh_signal(SIGCHLD, ctx->osigchld); + free(ctx); +} |