2 * uxcons.c: various interactive-prompt routines shared between the
3 * Unix console PuTTY tools
15 #include <sys/select.h>
21 int console_batch_mode = FALSE;
23 static void *console_logctx = NULL;
25 static struct termios orig_termios_stderr;
26 static int stderr_is_a_tty;
28 void stderr_tty_init()
30 /* Ensure that if stderr is a tty, we can get it back to a sane state. */
31 if ((flags & FLAG_STDERR_TTY) && isatty(STDERR_FILENO)) {
32 stderr_is_a_tty = TRUE;
33 tcgetattr(STDERR_FILENO, &orig_termios_stderr);
37 void premsg(struct termios *cf)
39 if (stderr_is_a_tty) {
40 tcgetattr(STDERR_FILENO, cf);
41 tcsetattr(STDERR_FILENO, TCSADRAIN, &orig_termios_stderr);
44 void postmsg(struct termios *cf)
47 tcsetattr(STDERR_FILENO, TCSADRAIN, cf);
53 void cleanup_exit(int code)
63 void set_busy_status(void *frontend, int status)
67 void update_specials_menu(void *frontend)
71 void notify_remote_exit(void *frontend)
75 void timer_change_notify(unsigned long next)
80 * Wrapper around Unix read(2), suitable for use on a file descriptor
81 * that's been set into nonblocking mode. Handles EAGAIN/EWOULDBLOCK
82 * by means of doing a one-fd select and then trying again; all other
83 * errors (including errors from select) are returned to the caller.
85 static int block_and_read(int fd, void *buf, size_t len)
89 while ((ret = read(fd, buf, len)) < 0 && (
94 (errno == EWOULDBLOCK) ||
101 ret = select(fd+1, &rfds, NULL, NULL, NULL);
105 assert(FD_ISSET(fd, &rfds));
111 int verify_ssh_host_key(void *frontend, char *host, int port,
112 const char *keytype, char *keystr, char *fingerprint,
113 void (*callback)(void *ctx, int result), void *ctx)
117 static const char absentmsg_batch[] =
118 "The server's host key is not cached. You have no guarantee\n"
119 "that the server is the computer you think it is.\n"
120 "The server's %s key fingerprint is:\n"
122 "Connection abandoned.\n";
123 static const char absentmsg[] =
124 "The server's host key is not cached. You have no guarantee\n"
125 "that the server is the computer you think it is.\n"
126 "The server's %s key fingerprint is:\n"
128 "If you trust this host, enter \"y\" to add the key to\n"
129 "PuTTY's cache and carry on connecting.\n"
130 "If you want to carry on connecting just once, without\n"
131 "adding the key to the cache, enter \"n\".\n"
132 "If you do not trust this host, press Return to abandon the\n"
134 "Store key in cache? (y/n) ";
136 static const char wrongmsg_batch[] =
137 "WARNING - POTENTIAL SECURITY BREACH!\n"
138 "The server's host key does not match the one PuTTY has\n"
139 "cached. This means that either the server administrator\n"
140 "has changed the host key, or you have actually connected\n"
141 "to another computer pretending to be the server.\n"
142 "The new %s key fingerprint is:\n"
144 "Connection abandoned.\n";
145 static const char wrongmsg[] =
146 "WARNING - POTENTIAL SECURITY BREACH!\n"
147 "The server's host key does not match the one PuTTY has\n"
148 "cached. This means that either the server administrator\n"
149 "has changed the host key, or you have actually connected\n"
150 "to another computer pretending to be the server.\n"
151 "The new %s key fingerprint is:\n"
153 "If you were expecting this change and trust the new key,\n"
154 "enter \"y\" to update PuTTY's cache and continue connecting.\n"
155 "If you want to carry on connecting but without updating\n"
156 "the cache, enter \"n\".\n"
157 "If you want to abandon the connection completely, press\n"
158 "Return to cancel. Pressing Return is the ONLY guaranteed\n"
160 "Update cached key? (y/n, Return cancels connection) ";
162 static const char abandoned[] = "Connection abandoned.\n";
170 ret = verify_host_key(host, port, keytype, keystr);
172 if (ret == 0) /* success - key matched OK */
176 if (ret == 2) { /* key was different */
177 if (console_batch_mode) {
178 fprintf(stderr, wrongmsg_batch, keytype, fingerprint);
181 fprintf(stderr, wrongmsg, keytype, fingerprint);
184 if (ret == 1) { /* key was absent */
185 if (console_batch_mode) {
186 fprintf(stderr, absentmsg_batch, keytype, fingerprint);
189 fprintf(stderr, absentmsg, keytype, fingerprint);
194 struct termios oldmode, newmode;
195 tcgetattr(0, &oldmode);
197 newmode.c_lflag |= ECHO | ISIG | ICANON;
198 tcsetattr(0, TCSANOW, &newmode);
200 if (block_and_read(0, line, sizeof(line) - 1) <= 0)
202 tcsetattr(0, TCSANOW, &oldmode);
205 if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') {
206 if (line[0] == 'y' || line[0] == 'Y')
207 store_host_key(host, port, keytype, keystr);
211 fprintf(stderr, abandoned);
218 * Ask whether the selected algorithm is acceptable (since it was
219 * below the configured 'warn' threshold).
221 int askalg(void *frontend, const char *algtype, const char *algname,
222 void (*callback)(void *ctx, int result), void *ctx)
224 static const char msg[] =
225 "The first %s supported by the server is\n"
226 "%s, which is below the configured warning threshold.\n"
227 "Continue with connection? (y/n) ";
228 static const char msg_batch[] =
229 "The first %s supported by the server is\n"
230 "%s, which is below the configured warning threshold.\n"
231 "Connection abandoned.\n";
232 static const char abandoned[] = "Connection abandoned.\n";
238 if (console_batch_mode) {
239 fprintf(stderr, msg_batch, algtype, algname);
243 fprintf(stderr, msg, algtype, algname);
247 struct termios oldmode, newmode;
248 tcgetattr(0, &oldmode);
250 newmode.c_lflag |= ECHO | ISIG | ICANON;
251 tcsetattr(0, TCSANOW, &newmode);
253 if (block_and_read(0, line, sizeof(line) - 1) <= 0)
255 tcsetattr(0, TCSANOW, &oldmode);
258 if (line[0] == 'y' || line[0] == 'Y') {
262 fprintf(stderr, abandoned);
268 int askhk(void *frontend, const char *algname, const char *betteralgs,
269 void (*callback)(void *ctx, int result), void *ctx)
271 static const char msg[] =
272 "The first host key type we have stored for this server\n"
273 "is %s, which is below the configured warning threshold.\n"
274 "The server also provides the following types of host key\n"
275 "above the threshold, which we do not have stored:\n"
277 "Continue with connection? (y/n) ";
278 static const char msg_batch[] =
279 "The first host key type we have stored for this server\n"
280 "is %s, which is below the configured warning threshold.\n"
281 "The server also provides the following types of host key\n"
282 "above the threshold, which we do not have stored:\n"
284 "Connection abandoned.\n";
285 static const char abandoned[] = "Connection abandoned.\n";
291 if (console_batch_mode) {
292 fprintf(stderr, msg_batch, algname, betteralgs);
296 fprintf(stderr, msg, algname, betteralgs);
300 struct termios oldmode, newmode;
301 tcgetattr(0, &oldmode);
303 newmode.c_lflag |= ECHO | ISIG | ICANON;
304 tcsetattr(0, TCSANOW, &newmode);
306 if (block_and_read(0, line, sizeof(line) - 1) <= 0)
308 tcsetattr(0, TCSANOW, &oldmode);
311 if (line[0] == 'y' || line[0] == 'Y') {
315 fprintf(stderr, abandoned);
322 * Ask whether to wipe a session log file before writing to it.
323 * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
325 int askappend(void *frontend, Filename *filename,
326 void (*callback)(void *ctx, int result), void *ctx)
328 static const char msgtemplate[] =
329 "The session log file \"%.*s\" already exists.\n"
330 "You can overwrite it with a new session log,\n"
331 "append your session log to the end of it,\n"
332 "or disable session logging for this session.\n"
333 "Enter \"y\" to wipe the file, \"n\" to append to it,\n"
334 "or just press Return to disable logging.\n"
335 "Wipe the log file? (y/n, Return cancels logging) ";
337 static const char msgtemplate_batch[] =
338 "The session log file \"%.*s\" already exists.\n"
339 "Logging will not be enabled.\n";
345 if (console_batch_mode) {
346 fprintf(stderr, msgtemplate_batch, FILENAME_MAX, filename->path);
350 fprintf(stderr, msgtemplate, FILENAME_MAX, filename->path);
354 struct termios oldmode, newmode;
355 tcgetattr(0, &oldmode);
357 newmode.c_lflag |= ECHO | ISIG | ICANON;
358 tcsetattr(0, TCSANOW, &newmode);
360 if (block_and_read(0, line, sizeof(line) - 1) <= 0)
362 tcsetattr(0, TCSANOW, &oldmode);
366 if (line[0] == 'y' || line[0] == 'Y')
368 else if (line[0] == 'n' || line[0] == 'N')
375 * Warn about the obsolescent key file format.
377 * Uniquely among these functions, this one does _not_ expect a
378 * frontend handle. This means that if PuTTY is ported to a
379 * platform which requires frontend handles, this function will be
380 * an anomaly. Fortunately, the problem it addresses will not have
381 * been present on that platform, so it can plausibly be
382 * implemented as an empty function.
384 void old_keyfile_warning(void)
386 static const char message[] =
387 "You are loading an SSH-2 private key which has an\n"
388 "old version of the file format. This means your key\n"
389 "file is not fully tamperproof. Future versions of\n"
390 "PuTTY may stop supporting this private key format,\n"
391 "so we recommend you convert your key to the new\n"
394 "Once the key is loaded into PuTTYgen, you can perform\n"
395 "this conversion simply by saving it again.\n";
399 fputs(message, stderr);
403 void console_provide_logctx(void *logctx)
405 console_logctx = logctx;
408 void logevent(void *frontend, const char *string)
411 if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE))
414 log_eventlog(console_logctx, string);
415 if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE))
420 * Special functions to read and print to the console for password
421 * prompts and the like. Uses /dev/tty or stdin/stderr, in that order
422 * of preference; also sanitises escape sequences out of the text, on
423 * the basis that it might have been sent by a hostile SSH server
424 * doing malicious keyboard-interactive.
426 static void console_open(FILE **outfp, int *infd)
430 if ((fd = open("/dev/tty", O_RDWR)) >= 0) {
432 *outfp = fdopen(*infd, "w");
438 static void console_close(FILE *outfp, int infd)
441 fclose(outfp); /* will automatically close infd too */
444 static void console_prompt_text(FILE *outfp, const char *data, int len)
448 for (i = 0; i < len; i++)
449 if ((data[i] & 0x60) || (data[i] == '\n'))
450 fputc(data[i], outfp);
454 int console_get_userpass_input(prompts_t *p, const unsigned char *in,
462 * Zero all the results, in case we abort half-way through.
466 for (i = 0; i < p->n_prompts; i++)
467 prompt_set_result(p->prompts[i], "");
470 if (p->n_prompts && console_batch_mode)
473 console_open(&outfp, &infd);
478 /* We only print the `name' caption if we have to... */
479 if (p->name_reqd && p->name) {
480 size_t l = strlen(p->name);
481 console_prompt_text(outfp, p->name, l);
482 if (p->name[l-1] != '\n')
483 console_prompt_text(outfp, "\n", 1);
485 /* ...but we always print any `instruction'. */
486 if (p->instruction) {
487 size_t l = strlen(p->instruction);
488 console_prompt_text(outfp, p->instruction, l);
489 if (p->instruction[l-1] != '\n')
490 console_prompt_text(outfp, "\n", 1);
493 for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) {
495 struct termios oldmode, newmode;
497 prompt_t *pr = p->prompts[curr_prompt];
499 tcgetattr(infd, &oldmode);
501 newmode.c_lflag |= ISIG | ICANON;
503 newmode.c_lflag &= ~ECHO;
505 newmode.c_lflag |= ECHO;
506 tcsetattr(infd, TCSANOW, &newmode);
508 console_prompt_text(outfp, pr->prompt, strlen(pr->prompt));
514 prompt_ensure_result_size(pr, len * 5 / 4 + 512);
515 ret = read(infd, pr->result + len, pr->resultsize - len - 1);
521 if (pr->result[len - 1] == '\n') {
527 tcsetattr(infd, TCSANOW, &oldmode);
530 console_prompt_text(outfp, "\n", 1);
533 console_close(outfp, infd);
534 return 0; /* failure due to read error */
537 pr->result[len] = '\0';
540 console_close(outfp, infd);
542 return 1; /* success */
545 void frontend_keypress(void *handle)
548 * This is nothing but a stub, in console code.
553 int is_interactive(void)
559 * X11-forwarding-related things suitable for console.
562 char *platform_get_x_display(void) {
563 return dupstr(getenv("DISPLAY"));