summaryrefslogtreecommitdiff
path: root/readpass.c
diff options
context:
space:
mode:
authorAlistair Delva <adelva@google.com>2020-08-20 16:14:23 -0700
committerAlistair Delva <adelva@google.com>2020-08-20 16:53:18 -0700
commitd9da10d147d633fdb6ec65e17ff4b8447419d83e (patch)
tree8f93e8fdc2907f141e0924910bfec26669819f0b /readpass.c
parent22246b08952d746a7cc5a292570636cf4277598f (diff)
parentecb2c02d994b3e21994f31a70ff911667c262f1f (diff)
Merge upstream-master into master
Commit ecb2c02d994b3e21994f31a70ff911667c262f1f upstream This nearly (but not quite) corresponds to V_8_3_P1; subsequent cherry-picks will correct this. Bug: 162492243 Change-Id: I3c079d86435b7c25aefff4538dc89a3002b1e25b
Diffstat (limited to 'readpass.c')
-rw-r--r--readpass.c143
1 files changed, 122 insertions, 21 deletions
diff --git a/readpass.c b/readpass.c
index 869d8642..974d67f0 100644
--- a/readpass.c
+++ b/readpass.c
@@ -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);
+}