2 * uxcons.c: various interactive-prompt routines shared between the
3 * Unix console PuTTY tools
16 #ifndef HAVE_NO_SYS_SELECT_H
17 #include <sys/select.h>
24 int console_batch_mode = FALSE;
26 static void *console_logctx = NULL;
28 static struct termios orig_termios_stderr;
29 static int stderr_is_a_tty;
31 void stderr_tty_init()
33 /* Ensure that if stderr is a tty, we can get it back to a sane state. */
34 if ((flags & FLAG_STDERR_TTY) && isatty(STDERR_FILENO)) {
35 stderr_is_a_tty = TRUE;
36 tcgetattr(STDERR_FILENO, &orig_termios_stderr);
40 void premsg(struct termios *cf)
42 if (stderr_is_a_tty) {
43 tcgetattr(STDERR_FILENO, cf);
44 tcsetattr(STDERR_FILENO, TCSADRAIN, &orig_termios_stderr);
47 void postmsg(struct termios *cf)
50 tcsetattr(STDERR_FILENO, TCSADRAIN, cf);
56 void cleanup_exit(int code)
66 void set_busy_status(void *frontend, int status)
70 void update_specials_menu(void *frontend)
74 void notify_remote_exit(void *frontend)
78 void timer_change_notify(unsigned long next)
83 * Wrapper around Unix read(2), suitable for use on a file descriptor
84 * that's been set into nonblocking mode. Handles EAGAIN/EWOULDBLOCK
85 * by means of doing a one-fd select and then trying again; all other
86 * errors (including errors from select) are returned to the caller.
88 static int block_and_read(int fd, void *buf, size_t len)
92 while ((ret = read(fd, buf, len)) < 0 && (
97 (errno == EWOULDBLOCK) ||
105 ret = select(fd+1, &rfds, NULL, NULL, NULL);
106 } while (ret < 0 && errno == EINTR);
110 assert(FD_ISSET(fd, &rfds));
116 int verify_ssh_host_key(void *frontend, char *host, int port,
117 const char *keytype, char *keystr, char *fingerprint,
118 void (*callback)(void *ctx, int result), void *ctx)
122 static const char absentmsg_batch[] =
123 "The server's host key is not cached. You have no guarantee\n"
124 "that the server is the computer you think it is.\n"
125 "The server's %s key fingerprint is:\n"
127 "Connection abandoned.\n";
128 static const char absentmsg[] =
129 "The server's host key is not cached. You have no guarantee\n"
130 "that the server is the computer you think it is.\n"
131 "The server's %s key fingerprint is:\n"
133 "If you trust this host, enter \"y\" to add the key to\n"
134 "PuTTY's cache and carry on connecting.\n"
135 "If you want to carry on connecting just once, without\n"
136 "adding the key to the cache, enter \"n\".\n"
137 "If you do not trust this host, press Return to abandon the\n"
139 "Store key in cache? (y/n) ";
141 static const char wrongmsg_batch[] =
142 "WARNING - POTENTIAL SECURITY BREACH!\n"
143 "The server's host key does not match the one PuTTY has\n"
144 "cached. This means that either the server administrator\n"
145 "has changed the host key, or you have actually connected\n"
146 "to another computer pretending to be the server.\n"
147 "The new %s key fingerprint is:\n"
149 "Connection abandoned.\n";
150 static const char wrongmsg[] =
151 "WARNING - POTENTIAL SECURITY BREACH!\n"
152 "The server's host key does not match the one PuTTY has\n"
153 "cached. This means that either the server administrator\n"
154 "has changed the host key, or you have actually connected\n"
155 "to another computer pretending to be the server.\n"
156 "The new %s key fingerprint is:\n"
158 "If you were expecting this change and trust the new key,\n"
159 "enter \"y\" to update PuTTY's cache and continue connecting.\n"
160 "If you want to carry on connecting but without updating\n"
161 "the cache, enter \"n\".\n"
162 "If you want to abandon the connection completely, press\n"
163 "Return to cancel. Pressing Return is the ONLY guaranteed\n"
165 "Update cached key? (y/n, Return cancels connection) ";
167 static const char abandoned[] = "Connection abandoned.\n";
175 ret = verify_host_key(host, port, keytype, keystr);
177 if (ret == 0) /* success - key matched OK */
181 if (ret == 2) { /* key was different */
182 if (console_batch_mode) {
183 fprintf(stderr, wrongmsg_batch, keytype, fingerprint);
186 fprintf(stderr, wrongmsg, keytype, fingerprint);
189 if (ret == 1) { /* key was absent */
190 if (console_batch_mode) {
191 fprintf(stderr, absentmsg_batch, keytype, fingerprint);
194 fprintf(stderr, absentmsg, keytype, fingerprint);
199 struct termios oldmode, newmode;
200 tcgetattr(0, &oldmode);
202 newmode.c_lflag |= ECHO | ISIG | ICANON;
203 tcsetattr(0, TCSANOW, &newmode);
205 if (block_and_read(0, line, sizeof(line) - 1) <= 0)
207 tcsetattr(0, TCSANOW, &oldmode);
210 if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') {
211 if (line[0] == 'y' || line[0] == 'Y')
212 store_host_key(host, port, keytype, keystr);
216 fprintf(stderr, abandoned);
223 * Ask whether the selected algorithm is acceptable (since it was
224 * below the configured 'warn' threshold).
226 int askalg(void *frontend, const char *algtype, const char *algname,
227 void (*callback)(void *ctx, int result), void *ctx)
229 static const char msg[] =
230 "The first %s supported by the server is\n"
231 "%s, which is below the configured warning threshold.\n"
232 "Continue with connection? (y/n) ";
233 static const char msg_batch[] =
234 "The first %s supported by the server is\n"
235 "%s, which is below the configured warning threshold.\n"
236 "Connection abandoned.\n";
237 static const char abandoned[] = "Connection abandoned.\n";
243 if (console_batch_mode) {
244 fprintf(stderr, msg_batch, algtype, algname);
248 fprintf(stderr, msg, algtype, algname);
252 struct termios oldmode, newmode;
253 tcgetattr(0, &oldmode);
255 newmode.c_lflag |= ECHO | ISIG | ICANON;
256 tcsetattr(0, TCSANOW, &newmode);
258 if (block_and_read(0, line, sizeof(line) - 1) <= 0)
260 tcsetattr(0, TCSANOW, &oldmode);
263 if (line[0] == 'y' || line[0] == 'Y') {
267 fprintf(stderr, abandoned);
273 int askhk(void *frontend, const char *algname, const char *betteralgs,
274 void (*callback)(void *ctx, int result), void *ctx)
276 static const char msg[] =
277 "The first host key type we have stored for this server\n"
278 "is %s, which is below the configured warning threshold.\n"
279 "The server also provides the following types of host key\n"
280 "above the threshold, which we do not have stored:\n"
282 "Continue with connection? (y/n) ";
283 static const char msg_batch[] =
284 "The first host key type we have stored for this server\n"
285 "is %s, which is below the configured warning threshold.\n"
286 "The server also provides the following types of host key\n"
287 "above the threshold, which we do not have stored:\n"
289 "Connection abandoned.\n";
290 static const char abandoned[] = "Connection abandoned.\n";
296 if (console_batch_mode) {
297 fprintf(stderr, msg_batch, algname, betteralgs);
301 fprintf(stderr, msg, algname, betteralgs);
305 struct termios oldmode, newmode;
306 tcgetattr(0, &oldmode);
308 newmode.c_lflag |= ECHO | ISIG | ICANON;
309 tcsetattr(0, TCSANOW, &newmode);
311 if (block_and_read(0, line, sizeof(line) - 1) <= 0)
313 tcsetattr(0, TCSANOW, &oldmode);
316 if (line[0] == 'y' || line[0] == 'Y') {
320 fprintf(stderr, abandoned);
327 * Ask whether to wipe a session log file before writing to it.
328 * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
330 int askappend(void *frontend, Filename *filename,
331 void (*callback)(void *ctx, int result), void *ctx)
333 static const char msgtemplate[] =
334 "The session log file \"%.*s\" already exists.\n"
335 "You can overwrite it with a new session log,\n"
336 "append your session log to the end of it,\n"
337 "or disable session logging for this session.\n"
338 "Enter \"y\" to wipe the file, \"n\" to append to it,\n"
339 "or just press Return to disable logging.\n"
340 "Wipe the log file? (y/n, Return cancels logging) ";
342 static const char msgtemplate_batch[] =
343 "The session log file \"%.*s\" already exists.\n"
344 "Logging will not be enabled.\n";
350 if (console_batch_mode) {
351 fprintf(stderr, msgtemplate_batch, FILENAME_MAX, filename->path);
355 fprintf(stderr, msgtemplate, FILENAME_MAX, filename->path);
359 struct termios oldmode, newmode;
360 tcgetattr(0, &oldmode);
362 newmode.c_lflag |= ECHO | ISIG | ICANON;
363 tcsetattr(0, TCSANOW, &newmode);
365 if (block_and_read(0, line, sizeof(line) - 1) <= 0)
367 tcsetattr(0, TCSANOW, &oldmode);
371 if (line[0] == 'y' || line[0] == 'Y')
373 else if (line[0] == 'n' || line[0] == 'N')
380 * Warn about the obsolescent key file format.
382 * Uniquely among these functions, this one does _not_ expect a
383 * frontend handle. This means that if PuTTY is ported to a
384 * platform which requires frontend handles, this function will be
385 * an anomaly. Fortunately, the problem it addresses will not have
386 * been present on that platform, so it can plausibly be
387 * implemented as an empty function.
389 void old_keyfile_warning(void)
391 static const char message[] =
392 "You are loading an SSH-2 private key which has an\n"
393 "old version of the file format. This means your key\n"
394 "file is not fully tamperproof. Future versions of\n"
395 "PuTTY may stop supporting this private key format,\n"
396 "so we recommend you convert your key to the new\n"
399 "Once the key is loaded into PuTTYgen, you can perform\n"
400 "this conversion simply by saving it again.\n";
404 fputs(message, stderr);
408 void console_provide_logctx(void *logctx)
410 console_logctx = logctx;
413 void logevent(void *frontend, const char *string)
416 if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE))
419 log_eventlog(console_logctx, string);
420 if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE))
425 * Special functions to read and print to the console for password
426 * prompts and the like. Uses /dev/tty or stdin/stderr, in that order
427 * of preference; also sanitises escape sequences out of the text, on
428 * the basis that it might have been sent by a hostile SSH server
429 * doing malicious keyboard-interactive.
431 static void console_open(FILE **outfp, int *infd)
435 if ((fd = open("/dev/tty", O_RDWR)) >= 0) {
437 *outfp = fdopen(*infd, "w");
443 static void console_close(FILE *outfp, int infd)
446 fclose(outfp); /* will automatically close infd too */
449 static void console_prompt_text(FILE *outfp, const char *data, int len)
453 for (i = 0; i < len; i++)
454 if ((data[i] & 0x60) || (data[i] == '\n'))
455 fputc(data[i], outfp);
459 int console_get_userpass_input(prompts_t *p, const unsigned char *in,
467 * Zero all the results, in case we abort half-way through.
471 for (i = 0; i < p->n_prompts; i++)
472 prompt_set_result(p->prompts[i], "");
475 if (p->n_prompts && console_batch_mode)
478 console_open(&outfp, &infd);
483 /* We only print the `name' caption if we have to... */
484 if (p->name_reqd && p->name) {
485 size_t l = strlen(p->name);
486 console_prompt_text(outfp, p->name, l);
487 if (p->name[l-1] != '\n')
488 console_prompt_text(outfp, "\n", 1);
490 /* ...but we always print any `instruction'. */
491 if (p->instruction) {
492 size_t l = strlen(p->instruction);
493 console_prompt_text(outfp, p->instruction, l);
494 if (p->instruction[l-1] != '\n')
495 console_prompt_text(outfp, "\n", 1);
498 for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) {
500 struct termios oldmode, newmode;
502 prompt_t *pr = p->prompts[curr_prompt];
504 tcgetattr(infd, &oldmode);
506 newmode.c_lflag |= ISIG | ICANON;
508 newmode.c_lflag &= ~ECHO;
510 newmode.c_lflag |= ECHO;
511 tcsetattr(infd, TCSANOW, &newmode);
513 console_prompt_text(outfp, pr->prompt, strlen(pr->prompt));
519 prompt_ensure_result_size(pr, len * 5 / 4 + 512);
520 ret = read(infd, pr->result + len, pr->resultsize - len - 1);
526 if (pr->result[len - 1] == '\n') {
532 tcsetattr(infd, TCSANOW, &oldmode);
535 console_prompt_text(outfp, "\n", 1);
538 console_close(outfp, infd);
539 return 0; /* failure due to read error */
542 pr->result[len] = '\0';
545 console_close(outfp, infd);
547 return 1; /* success */
550 void frontend_keypress(void *handle)
553 * This is nothing but a stub, in console code.
558 int is_interactive(void)
564 * X11-forwarding-related things suitable for console.
567 char *platform_get_x_display(void) {
568 return dupstr(getenv("DISPLAY"));