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) ||
104 ret = select(fd+1, &rfds, NULL, NULL, NULL);
108 assert(FD_ISSET(fd, &rfds));
114 int verify_ssh_host_key(void *frontend, char *host, int port,
115 const char *keytype, char *keystr, char *fingerprint,
116 void (*callback)(void *ctx, int result), void *ctx)
120 static const char absentmsg_batch[] =
121 "The server's host key is not cached. You have no guarantee\n"
122 "that the server is the computer you think it is.\n"
123 "The server's %s key fingerprint is:\n"
125 "Connection abandoned.\n";
126 static const char absentmsg[] =
127 "The server's host key is not cached. You have no guarantee\n"
128 "that the server is the computer you think it is.\n"
129 "The server's %s key fingerprint is:\n"
131 "If you trust this host, enter \"y\" to add the key to\n"
132 "PuTTY's cache and carry on connecting.\n"
133 "If you want to carry on connecting just once, without\n"
134 "adding the key to the cache, enter \"n\".\n"
135 "If you do not trust this host, press Return to abandon the\n"
137 "Store key in cache? (y/n) ";
139 static const char wrongmsg_batch[] =
140 "WARNING - POTENTIAL SECURITY BREACH!\n"
141 "The server's host key does not match the one PuTTY has\n"
142 "cached. This means that either the server administrator\n"
143 "has changed the host key, or you have actually connected\n"
144 "to another computer pretending to be the server.\n"
145 "The new %s key fingerprint is:\n"
147 "Connection abandoned.\n";
148 static const char wrongmsg[] =
149 "WARNING - POTENTIAL SECURITY BREACH!\n"
150 "The server's host key does not match the one PuTTY has\n"
151 "cached. This means that either the server administrator\n"
152 "has changed the host key, or you have actually connected\n"
153 "to another computer pretending to be the server.\n"
154 "The new %s key fingerprint is:\n"
156 "If you were expecting this change and trust the new key,\n"
157 "enter \"y\" to update PuTTY's cache and continue connecting.\n"
158 "If you want to carry on connecting but without updating\n"
159 "the cache, enter \"n\".\n"
160 "If you want to abandon the connection completely, press\n"
161 "Return to cancel. Pressing Return is the ONLY guaranteed\n"
163 "Update cached key? (y/n, Return cancels connection) ";
165 static const char abandoned[] = "Connection abandoned.\n";
173 ret = verify_host_key(host, port, keytype, keystr);
175 if (ret == 0) /* success - key matched OK */
179 if (ret == 2) { /* key was different */
180 if (console_batch_mode) {
181 fprintf(stderr, wrongmsg_batch, keytype, fingerprint);
184 fprintf(stderr, wrongmsg, keytype, fingerprint);
187 if (ret == 1) { /* key was absent */
188 if (console_batch_mode) {
189 fprintf(stderr, absentmsg_batch, keytype, fingerprint);
192 fprintf(stderr, absentmsg, keytype, fingerprint);
197 struct termios oldmode, newmode;
198 tcgetattr(0, &oldmode);
200 newmode.c_lflag |= ECHO | ISIG | ICANON;
201 tcsetattr(0, TCSANOW, &newmode);
203 if (block_and_read(0, line, sizeof(line) - 1) <= 0)
205 tcsetattr(0, TCSANOW, &oldmode);
208 if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') {
209 if (line[0] == 'y' || line[0] == 'Y')
210 store_host_key(host, port, keytype, keystr);
214 fprintf(stderr, abandoned);
221 * Ask whether the selected algorithm is acceptable (since it was
222 * below the configured 'warn' threshold).
224 int askalg(void *frontend, const char *algtype, const char *algname,
225 void (*callback)(void *ctx, int result), void *ctx)
227 static const char msg[] =
228 "The first %s supported by the server is\n"
229 "%s, which is below the configured warning threshold.\n"
230 "Continue with connection? (y/n) ";
231 static const char msg_batch[] =
232 "The first %s supported by the server is\n"
233 "%s, which is below the configured warning threshold.\n"
234 "Connection abandoned.\n";
235 static const char abandoned[] = "Connection abandoned.\n";
241 if (console_batch_mode) {
242 fprintf(stderr, msg_batch, algtype, algname);
246 fprintf(stderr, msg, algtype, algname);
250 struct termios oldmode, newmode;
251 tcgetattr(0, &oldmode);
253 newmode.c_lflag |= ECHO | ISIG | ICANON;
254 tcsetattr(0, TCSANOW, &newmode);
256 if (block_and_read(0, line, sizeof(line) - 1) <= 0)
258 tcsetattr(0, TCSANOW, &oldmode);
261 if (line[0] == 'y' || line[0] == 'Y') {
265 fprintf(stderr, abandoned);
271 int askhk(void *frontend, const char *algname, const char *betteralgs,
272 void (*callback)(void *ctx, int result), void *ctx)
274 static const char msg[] =
275 "The first host key type we have stored for this server\n"
276 "is %s, which is below the configured warning threshold.\n"
277 "The server also provides the following types of host key\n"
278 "above the threshold, which we do not have stored:\n"
280 "Continue with connection? (y/n) ";
281 static const char msg_batch[] =
282 "The first host key type we have stored for this server\n"
283 "is %s, which is below the configured warning threshold.\n"
284 "The server also provides the following types of host key\n"
285 "above the threshold, which we do not have stored:\n"
287 "Connection abandoned.\n";
288 static const char abandoned[] = "Connection abandoned.\n";
294 if (console_batch_mode) {
295 fprintf(stderr, msg_batch, algname, betteralgs);
299 fprintf(stderr, msg, algname, betteralgs);
303 struct termios oldmode, newmode;
304 tcgetattr(0, &oldmode);
306 newmode.c_lflag |= ECHO | ISIG | ICANON;
307 tcsetattr(0, TCSANOW, &newmode);
309 if (block_and_read(0, line, sizeof(line) - 1) <= 0)
311 tcsetattr(0, TCSANOW, &oldmode);
314 if (line[0] == 'y' || line[0] == 'Y') {
318 fprintf(stderr, abandoned);
325 * Ask whether to wipe a session log file before writing to it.
326 * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
328 int askappend(void *frontend, Filename *filename,
329 void (*callback)(void *ctx, int result), void *ctx)
331 static const char msgtemplate[] =
332 "The session log file \"%.*s\" already exists.\n"
333 "You can overwrite it with a new session log,\n"
334 "append your session log to the end of it,\n"
335 "or disable session logging for this session.\n"
336 "Enter \"y\" to wipe the file, \"n\" to append to it,\n"
337 "or just press Return to disable logging.\n"
338 "Wipe the log file? (y/n, Return cancels logging) ";
340 static const char msgtemplate_batch[] =
341 "The session log file \"%.*s\" already exists.\n"
342 "Logging will not be enabled.\n";
348 if (console_batch_mode) {
349 fprintf(stderr, msgtemplate_batch, FILENAME_MAX, filename->path);
353 fprintf(stderr, msgtemplate, FILENAME_MAX, filename->path);
357 struct termios oldmode, newmode;
358 tcgetattr(0, &oldmode);
360 newmode.c_lflag |= ECHO | ISIG | ICANON;
361 tcsetattr(0, TCSANOW, &newmode);
363 if (block_and_read(0, line, sizeof(line) - 1) <= 0)
365 tcsetattr(0, TCSANOW, &oldmode);
369 if (line[0] == 'y' || line[0] == 'Y')
371 else if (line[0] == 'n' || line[0] == 'N')
378 * Warn about the obsolescent key file format.
380 * Uniquely among these functions, this one does _not_ expect a
381 * frontend handle. This means that if PuTTY is ported to a
382 * platform which requires frontend handles, this function will be
383 * an anomaly. Fortunately, the problem it addresses will not have
384 * been present on that platform, so it can plausibly be
385 * implemented as an empty function.
387 void old_keyfile_warning(void)
389 static const char message[] =
390 "You are loading an SSH-2 private key which has an\n"
391 "old version of the file format. This means your key\n"
392 "file is not fully tamperproof. Future versions of\n"
393 "PuTTY may stop supporting this private key format,\n"
394 "so we recommend you convert your key to the new\n"
397 "Once the key is loaded into PuTTYgen, you can perform\n"
398 "this conversion simply by saving it again.\n";
402 fputs(message, stderr);
406 void console_provide_logctx(void *logctx)
408 console_logctx = logctx;
411 void logevent(void *frontend, const char *string)
414 if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE))
417 log_eventlog(console_logctx, string);
418 if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE))
423 * Special functions to read and print to the console for password
424 * prompts and the like. Uses /dev/tty or stdin/stderr, in that order
425 * of preference; also sanitises escape sequences out of the text, on
426 * the basis that it might have been sent by a hostile SSH server
427 * doing malicious keyboard-interactive.
429 static void console_open(FILE **outfp, int *infd)
433 if ((fd = open("/dev/tty", O_RDWR)) >= 0) {
435 *outfp = fdopen(*infd, "w");
441 static void console_close(FILE *outfp, int infd)
444 fclose(outfp); /* will automatically close infd too */
447 static void console_prompt_text(FILE *outfp, const char *data, int len)
451 for (i = 0; i < len; i++)
452 if ((data[i] & 0x60) || (data[i] == '\n'))
453 fputc(data[i], outfp);
457 int console_get_userpass_input(prompts_t *p, const unsigned char *in,
465 * Zero all the results, in case we abort half-way through.
469 for (i = 0; i < p->n_prompts; i++)
470 prompt_set_result(p->prompts[i], "");
473 if (p->n_prompts && console_batch_mode)
476 console_open(&outfp, &infd);
481 /* We only print the `name' caption if we have to... */
482 if (p->name_reqd && p->name) {
483 size_t l = strlen(p->name);
484 console_prompt_text(outfp, p->name, l);
485 if (p->name[l-1] != '\n')
486 console_prompt_text(outfp, "\n", 1);
488 /* ...but we always print any `instruction'. */
489 if (p->instruction) {
490 size_t l = strlen(p->instruction);
491 console_prompt_text(outfp, p->instruction, l);
492 if (p->instruction[l-1] != '\n')
493 console_prompt_text(outfp, "\n", 1);
496 for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) {
498 struct termios oldmode, newmode;
500 prompt_t *pr = p->prompts[curr_prompt];
502 tcgetattr(infd, &oldmode);
504 newmode.c_lflag |= ISIG | ICANON;
506 newmode.c_lflag &= ~ECHO;
508 newmode.c_lflag |= ECHO;
509 tcsetattr(infd, TCSANOW, &newmode);
511 console_prompt_text(outfp, pr->prompt, strlen(pr->prompt));
517 prompt_ensure_result_size(pr, len * 5 / 4 + 512);
518 ret = read(infd, pr->result + len, pr->resultsize - len - 1);
524 if (pr->result[len - 1] == '\n') {
530 tcsetattr(infd, TCSANOW, &oldmode);
533 console_prompt_text(outfp, "\n", 1);
536 console_close(outfp, infd);
537 return 0; /* failure due to read error */
540 pr->result[len] = '\0';
543 console_close(outfp, infd);
545 return 1; /* success */
548 void frontend_keypress(void *handle)
551 * This is nothing but a stub, in console code.
556 int is_interactive(void)
562 * X11-forwarding-related things suitable for console.
565 char *platform_get_x_display(void) {
566 return dupstr(getenv("DISPLAY"));