diff options
Diffstat (limited to 'readpass.c')
-rw-r--r-- | readpass.c | 119 |
1 files changed, 107 insertions, 12 deletions
diff --git a/readpass.c b/readpass.c index 7e52cae9c..974d67f0b 100644 --- a/readpass.c +++ b/readpass.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: readpass.c,v 1.54 2019/06/28 13:35:04 deraadt Exp $ */ | 1 | /* $OpenBSD: readpass.c,v 1.61 2020/01/23 07:10:22 dtucker Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2001 Markus Friedl. All rights reserved. | 3 | * Copyright (c) 2001 Markus Friedl. All rights reserved. |
4 | * | 4 | * |
@@ -48,7 +48,7 @@ | |||
48 | #include "uidswap.h" | 48 | #include "uidswap.h" |
49 | 49 | ||
50 | static char * | 50 | static char * |
51 | ssh_askpass(char *askpass, const char *msg) | 51 | ssh_askpass(char *askpass, const char *msg, const char *env_hint) |
52 | { | 52 | { |
53 | pid_t pid, ret; | 53 | pid_t pid, ret; |
54 | size_t len; | 54 | size_t len; |
@@ -58,25 +58,27 @@ ssh_askpass(char *askpass, const char *msg) | |||
58 | void (*osigchld)(int); | 58 | void (*osigchld)(int); |
59 | 59 | ||
60 | if (fflush(stdout) != 0) | 60 | if (fflush(stdout) != 0) |
61 | error("ssh_askpass: fflush: %s", strerror(errno)); | 61 | error("%s: fflush: %s", __func__, strerror(errno)); |
62 | if (askpass == NULL) | 62 | if (askpass == NULL) |
63 | fatal("internal error: askpass undefined"); | 63 | fatal("internal error: askpass undefined"); |
64 | if (pipe(p) == -1) { | 64 | if (pipe(p) == -1) { |
65 | error("ssh_askpass: pipe: %s", strerror(errno)); | 65 | error("%s: pipe: %s", __func__, strerror(errno)); |
66 | return NULL; | 66 | return NULL; |
67 | } | 67 | } |
68 | osigchld = signal(SIGCHLD, SIG_DFL); | 68 | osigchld = ssh_signal(SIGCHLD, SIG_DFL); |
69 | if ((pid = fork()) == -1) { | 69 | if ((pid = fork()) == -1) { |
70 | error("ssh_askpass: fork: %s", strerror(errno)); | 70 | error("%s: fork: %s", __func__, strerror(errno)); |
71 | signal(SIGCHLD, osigchld); | 71 | ssh_signal(SIGCHLD, osigchld); |
72 | return NULL; | 72 | return NULL; |
73 | } | 73 | } |
74 | if (pid == 0) { | 74 | if (pid == 0) { |
75 | close(p[0]); | 75 | close(p[0]); |
76 | if (dup2(p[1], STDOUT_FILENO) == -1) | 76 | if (dup2(p[1], STDOUT_FILENO) == -1) |
77 | fatal("ssh_askpass: dup2: %s", strerror(errno)); | 77 | fatal("%s: dup2: %s", __func__, strerror(errno)); |
78 | if (env_hint != NULL) | ||
79 | setenv("SSH_ASKPASS_PROMPT", env_hint, 1); | ||
78 | execlp(askpass, askpass, msg, (char *)NULL); | 80 | execlp(askpass, askpass, msg, (char *)NULL); |
79 | fatal("ssh_askpass: exec(%s): %s", askpass, strerror(errno)); | 81 | fatal("%s: exec(%s): %s", __func__, askpass, strerror(errno)); |
80 | } | 82 | } |
81 | close(p[1]); | 83 | close(p[1]); |
82 | 84 | ||
@@ -96,7 +98,7 @@ ssh_askpass(char *askpass, const char *msg) | |||
96 | while ((ret = waitpid(pid, &status, 0)) == -1) | 98 | while ((ret = waitpid(pid, &status, 0)) == -1) |
97 | if (errno != EINTR) | 99 | if (errno != EINTR) |
98 | break; | 100 | break; |
99 | signal(SIGCHLD, osigchld); | 101 | ssh_signal(SIGCHLD, osigchld); |
100 | if (ret == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) { | 102 | if (ret == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) { |
101 | explicit_bzero(buf, sizeof(buf)); | 103 | explicit_bzero(buf, sizeof(buf)); |
102 | return NULL; | 104 | return NULL; |
@@ -108,6 +110,9 @@ ssh_askpass(char *askpass, const char *msg) | |||
108 | return pass; | 110 | return pass; |
109 | } | 111 | } |
110 | 112 | ||
113 | /* private/internal read_passphrase flags */ | ||
114 | #define RP_ASK_PERMISSION 0x8000 /* pass hint to askpass for confirm UI */ | ||
115 | |||
111 | /* | 116 | /* |
112 | * Reads a passphrase from /dev/tty with echo turned off/on. Returns the | 117 | * Reads a passphrase from /dev/tty with echo turned off/on. Returns the |
113 | * passphrase (allocated with xmalloc). Exits if EOF is encountered. If | 118 | * passphrase (allocated with xmalloc). Exits if EOF is encountered. If |
@@ -119,6 +124,7 @@ read_passphrase(const char *prompt, int flags) | |||
119 | { | 124 | { |
120 | char cr = '\r', *askpass = NULL, *ret, buf[1024]; | 125 | char cr = '\r', *askpass = NULL, *ret, buf[1024]; |
121 | int rppflags, use_askpass = 0, ttyfd; | 126 | int rppflags, use_askpass = 0, ttyfd; |
127 | const char *askpass_hint = NULL; | ||
122 | 128 | ||
123 | rppflags = (flags & RP_ECHO) ? RPP_ECHO_ON : RPP_ECHO_OFF; | 129 | rppflags = (flags & RP_ECHO) ? RPP_ECHO_ON : RPP_ECHO_OFF; |
124 | if (flags & RP_USE_ASKPASS) | 130 | if (flags & RP_USE_ASKPASS) |
@@ -155,7 +161,9 @@ read_passphrase(const char *prompt, int flags) | |||
155 | askpass = getenv(SSH_ASKPASS_ENV); | 161 | askpass = getenv(SSH_ASKPASS_ENV); |
156 | else | 162 | else |
157 | askpass = _PATH_SSH_ASKPASS_DEFAULT; | 163 | askpass = _PATH_SSH_ASKPASS_DEFAULT; |
158 | if ((ret = ssh_askpass(askpass, prompt)) == NULL) | 164 | if ((flags & RP_ASK_PERMISSION) != 0) |
165 | askpass_hint = "confirm"; | ||
166 | if ((ret = ssh_askpass(askpass, prompt, askpass_hint)) == NULL) | ||
159 | if (!(flags & RP_ALLOW_EOF)) | 167 | if (!(flags & RP_ALLOW_EOF)) |
160 | return xstrdup(""); | 168 | return xstrdup(""); |
161 | return ret; | 169 | return ret; |
@@ -183,7 +191,8 @@ ask_permission(const char *fmt, ...) | |||
183 | vsnprintf(prompt, sizeof(prompt), fmt, args); | 191 | vsnprintf(prompt, sizeof(prompt), fmt, args); |
184 | va_end(args); | 192 | va_end(args); |
185 | 193 | ||
186 | p = read_passphrase(prompt, RP_USE_ASKPASS|RP_ALLOW_EOF); | 194 | p = read_passphrase(prompt, |
195 | RP_USE_ASKPASS|RP_ALLOW_EOF|RP_ASK_PERMISSION); | ||
187 | if (p != NULL) { | 196 | if (p != NULL) { |
188 | /* | 197 | /* |
189 | * Accept empty responses and responses consisting | 198 | * Accept empty responses and responses consisting |
@@ -197,3 +206,89 @@ ask_permission(const char *fmt, ...) | |||
197 | 206 | ||
198 | return (allowed); | 207 | return (allowed); |
199 | } | 208 | } |
209 | |||
210 | struct notifier_ctx { | ||
211 | pid_t pid; | ||
212 | void (*osigchld)(int); | ||
213 | }; | ||
214 | |||
215 | struct notifier_ctx * | ||
216 | notify_start(int force_askpass, const char *fmt, ...) | ||
217 | { | ||
218 | va_list args; | ||
219 | char *prompt = NULL; | ||
220 | int devnull; | ||
221 | pid_t pid; | ||
222 | void (*osigchld)(int); | ||
223 | const char *askpass; | ||
224 | struct notifier_ctx *ret; | ||
225 | |||
226 | va_start(args, fmt); | ||
227 | xvasprintf(&prompt, fmt, args); | ||
228 | va_end(args); | ||
229 | |||
230 | if (fflush(NULL) != 0) | ||
231 | error("%s: fflush: %s", __func__, strerror(errno)); | ||
232 | if (!force_askpass && isatty(STDERR_FILENO)) { | ||
233 | (void)write(STDERR_FILENO, "\r", 1); | ||
234 | (void)write(STDERR_FILENO, prompt, strlen(prompt)); | ||
235 | (void)write(STDERR_FILENO, "\r\n", 2); | ||
236 | free(prompt); | ||
237 | return NULL; | ||
238 | } | ||
239 | if ((askpass = getenv("SSH_ASKPASS")) == NULL) | ||
240 | askpass = _PATH_SSH_ASKPASS_DEFAULT; | ||
241 | if (getenv("DISPLAY") == NULL || *askpass == '\0') { | ||
242 | debug3("%s: cannot notify", __func__); | ||
243 | free(prompt); | ||
244 | return NULL; | ||
245 | } | ||
246 | osigchld = ssh_signal(SIGCHLD, SIG_DFL); | ||
247 | if ((pid = fork()) == -1) { | ||
248 | error("%s: fork: %s", __func__, strerror(errno)); | ||
249 | ssh_signal(SIGCHLD, osigchld); | ||
250 | free(prompt); | ||
251 | return NULL; | ||
252 | } | ||
253 | if (pid == 0) { | ||
254 | if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) | ||
255 | fatal("%s: open %s", __func__, strerror(errno)); | ||
256 | if (dup2(devnull, STDIN_FILENO) == -1 || | ||
257 | dup2(devnull, STDOUT_FILENO) == -1) | ||
258 | fatal("%s: dup2: %s", __func__, strerror(errno)); | ||
259 | closefrom(STDERR_FILENO + 1); | ||
260 | setenv("SSH_ASKPASS_PROMPT", "none", 1); /* hint to UI */ | ||
261 | execlp(askpass, askpass, prompt, (char *)NULL); | ||
262 | error("%s: exec(%s): %s", __func__, askpass, strerror(errno)); | ||
263 | _exit(1); | ||
264 | /* NOTREACHED */ | ||
265 | } | ||
266 | if ((ret = calloc(1, sizeof(*ret))) == NULL) { | ||
267 | kill(pid, SIGTERM); | ||
268 | fatal("%s: calloc failed", __func__); | ||
269 | } | ||
270 | ret->pid = pid; | ||
271 | ret->osigchld = osigchld; | ||
272 | free(prompt); | ||
273 | return ret; | ||
274 | } | ||
275 | |||
276 | void | ||
277 | notify_complete(struct notifier_ctx *ctx) | ||
278 | { | ||
279 | int ret; | ||
280 | |||
281 | if (ctx == NULL || ctx->pid <= 0) { | ||
282 | free(ctx); | ||
283 | return; | ||
284 | } | ||
285 | kill(ctx->pid, SIGTERM); | ||
286 | while ((ret = waitpid(ctx->pid, NULL, 0)) == -1) { | ||
287 | if (errno != EINTR) | ||
288 | break; | ||
289 | } | ||
290 | if (ret == -1) | ||
291 | fatal("%s: waitpid: %s", __func__, strerror(errno)); | ||
292 | ssh_signal(SIGCHLD, ctx->osigchld); | ||
293 | free(ctx); | ||
294 | } | ||