+++ /dev/null
-/*\r
- * uxcons.c: various interactive-prompt routines shared between the\r
- * Unix console PuTTY tools\r
- */\r
-\r
-#include <stdio.h>\r
-#include <stdlib.h>\r
-#include <stdarg.h>\r
-#include <assert.h>\r
-#include <termios.h>\r
-#include <unistd.h>\r
-\r
-#include "putty.h"\r
-#include "storage.h"\r
-#include "ssh.h"\r
-\r
-int console_batch_mode = FALSE;\r
-\r
-static void *console_logctx = NULL;\r
-\r
-static struct termios orig_termios_stderr;\r
-static int stderr_is_a_tty;\r
-\r
-void stderr_tty_init()\r
-{\r
- /* Ensure that if stderr is a tty, we can get it back to a sane state. */\r
- if ((flags & FLAG_STDERR_TTY) && isatty(STDERR_FILENO)) {\r
- stderr_is_a_tty = TRUE;\r
- tcgetattr(STDERR_FILENO, &orig_termios_stderr);\r
- }\r
-}\r
-\r
-void premsg(struct termios *cf)\r
-{\r
- if (stderr_is_a_tty) {\r
- tcgetattr(STDERR_FILENO, cf);\r
- tcsetattr(STDERR_FILENO, TCSADRAIN, &orig_termios_stderr);\r
- }\r
-}\r
-void postmsg(struct termios *cf)\r
-{\r
- if (stderr_is_a_tty)\r
- tcsetattr(STDERR_FILENO, TCSADRAIN, cf);\r
-}\r
-\r
-/*\r
- * Clean up and exit.\r
- */\r
-void cleanup_exit(int code)\r
-{\r
- /*\r
- * Clean up.\r
- */\r
- sk_cleanup();\r
- random_save_seed();\r
- exit(code);\r
-}\r
-\r
-void set_busy_status(void *frontend, int status)\r
-{\r
-}\r
-\r
-void update_specials_menu(void *frontend)\r
-{\r
-}\r
-\r
-void notify_remote_exit(void *frontend)\r
-{\r
-}\r
-\r
-void timer_change_notify(long next)\r
-{\r
-}\r
-\r
-int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,\r
- char *keystr, char *fingerprint,\r
- void (*callback)(void *ctx, int result), void *ctx)\r
-{\r
- int ret;\r
-\r
- static const char absentmsg_batch[] =\r
- "The server's host key is not cached. You have no guarantee\n"\r
- "that the server is the computer you think it is.\n"\r
- "The server's %s key fingerprint is:\n"\r
- "%s\n"\r
- "Connection abandoned.\n";\r
- static const char absentmsg[] =\r
- "The server's host key is not cached. You have no guarantee\n"\r
- "that the server is the computer you think it is.\n"\r
- "The server's %s key fingerprint is:\n"\r
- "%s\n"\r
- "If you trust this host, enter \"y\" to add the key to\n"\r
- "PuTTY's cache and carry on connecting.\n"\r
- "If you want to carry on connecting just once, without\n"\r
- "adding the key to the cache, enter \"n\".\n"\r
- "If you do not trust this host, press Return to abandon the\n"\r
- "connection.\n"\r
- "Store key in cache? (y/n) ";\r
-\r
- static const char wrongmsg_batch[] =\r
- "WARNING - POTENTIAL SECURITY BREACH!\n"\r
- "The server's host key does not match the one PuTTY has\n"\r
- "cached. This means that either the server administrator\n"\r
- "has changed the host key, or you have actually connected\n"\r
- "to another computer pretending to be the server.\n"\r
- "The new %s key fingerprint is:\n"\r
- "%s\n"\r
- "Connection abandoned.\n";\r
- static const char wrongmsg[] =\r
- "WARNING - POTENTIAL SECURITY BREACH!\n"\r
- "The server's host key does not match the one PuTTY has\n"\r
- "cached. This means that either the server administrator\n"\r
- "has changed the host key, or you have actually connected\n"\r
- "to another computer pretending to be the server.\n"\r
- "The new %s key fingerprint is:\n"\r
- "%s\n"\r
- "If you were expecting this change and trust the new key,\n"\r
- "enter \"y\" to update PuTTY's cache and continue connecting.\n"\r
- "If you want to carry on connecting but without updating\n"\r
- "the cache, enter \"n\".\n"\r
- "If you want to abandon the connection completely, press\n"\r
- "Return to cancel. Pressing Return is the ONLY guaranteed\n"\r
- "safe choice.\n"\r
- "Update cached key? (y/n, Return cancels connection) ";\r
-\r
- static const char abandoned[] = "Connection abandoned.\n";\r
-\r
- char line[32];\r
- struct termios cf;\r
-\r
- /*\r
- * Verify the key.\r
- */\r
- ret = verify_host_key(host, port, keytype, keystr);\r
-\r
- if (ret == 0) /* success - key matched OK */\r
- return 1;\r
-\r
- premsg(&cf);\r
- if (ret == 2) { /* key was different */\r
- if (console_batch_mode) {\r
- fprintf(stderr, wrongmsg_batch, keytype, fingerprint);\r
- return 0;\r
- }\r
- fprintf(stderr, wrongmsg, keytype, fingerprint);\r
- fflush(stderr);\r
- }\r
- if (ret == 1) { /* key was absent */\r
- if (console_batch_mode) {\r
- fprintf(stderr, absentmsg_batch, keytype, fingerprint);\r
- return 0;\r
- }\r
- fprintf(stderr, absentmsg, keytype, fingerprint);\r
- fflush(stderr);\r
- }\r
-\r
- {\r
- struct termios oldmode, newmode;\r
- tcgetattr(0, &oldmode);\r
- newmode = oldmode;\r
- newmode.c_lflag |= ECHO | ISIG | ICANON;\r
- tcsetattr(0, TCSANOW, &newmode);\r
- line[0] = '\0';\r
- if (read(0, line, sizeof(line) - 1) <= 0)\r
- /* handled below */;\r
- tcsetattr(0, TCSANOW, &oldmode);\r
- }\r
-\r
- if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') {\r
- if (line[0] == 'y' || line[0] == 'Y')\r
- store_host_key(host, port, keytype, keystr);\r
- postmsg(&cf);\r
- return 1;\r
- } else {\r
- fprintf(stderr, abandoned);\r
- postmsg(&cf);\r
- return 0;\r
- }\r
-}\r
-\r
-/*\r
- * Ask whether the selected algorithm is acceptable (since it was\r
- * below the configured 'warn' threshold).\r
- */\r
-int askalg(void *frontend, const char *algtype, const char *algname,\r
- void (*callback)(void *ctx, int result), void *ctx)\r
-{\r
- static const char msg[] =\r
- "The first %s supported by the server is\n"\r
- "%s, which is below the configured warning threshold.\n"\r
- "Continue with connection? (y/n) ";\r
- static const char msg_batch[] =\r
- "The first %s supported by the server is\n"\r
- "%s, which is below the configured warning threshold.\n"\r
- "Connection abandoned.\n";\r
- static const char abandoned[] = "Connection abandoned.\n";\r
-\r
- char line[32];\r
- struct termios cf;\r
-\r
- premsg(&cf);\r
- if (console_batch_mode) {\r
- fprintf(stderr, msg_batch, algtype, algname);\r
- return 0;\r
- }\r
-\r
- fprintf(stderr, msg, algtype, algname);\r
- fflush(stderr);\r
-\r
- {\r
- struct termios oldmode, newmode;\r
- tcgetattr(0, &oldmode);\r
- newmode = oldmode;\r
- newmode.c_lflag |= ECHO | ISIG | ICANON;\r
- tcsetattr(0, TCSANOW, &newmode);\r
- line[0] = '\0';\r
- if (read(0, line, sizeof(line) - 1) <= 0)\r
- /* handled below */;\r
- tcsetattr(0, TCSANOW, &oldmode);\r
- }\r
-\r
- if (line[0] == 'y' || line[0] == 'Y') {\r
- postmsg(&cf);\r
- return 1;\r
- } else {\r
- fprintf(stderr, abandoned);\r
- postmsg(&cf);\r
- return 0;\r
- }\r
-}\r
-\r
-/*\r
- * Ask whether to wipe a session log file before writing to it.\r
- * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).\r
- */\r
-int askappend(void *frontend, Filename filename,\r
- void (*callback)(void *ctx, int result), void *ctx)\r
-{\r
- static const char msgtemplate[] =\r
- "The session log file \"%.*s\" already exists.\n"\r
- "You can overwrite it with a new session log,\n"\r
- "append your session log to the end of it,\n"\r
- "or disable session logging for this session.\n"\r
- "Enter \"y\" to wipe the file, \"n\" to append to it,\n"\r
- "or just press Return to disable logging.\n"\r
- "Wipe the log file? (y/n, Return cancels logging) ";\r
-\r
- static const char msgtemplate_batch[] =\r
- "The session log file \"%.*s\" already exists.\n"\r
- "Logging will not be enabled.\n";\r
-\r
- char line[32];\r
- struct termios cf;\r
-\r
- premsg(&cf);\r
- if (console_batch_mode) {\r
- fprintf(stderr, msgtemplate_batch, FILENAME_MAX, filename.path);\r
- fflush(stderr);\r
- return 0;\r
- }\r
- fprintf(stderr, msgtemplate, FILENAME_MAX, filename.path);\r
- fflush(stderr);\r
-\r
- {\r
- struct termios oldmode, newmode;\r
- tcgetattr(0, &oldmode);\r
- newmode = oldmode;\r
- newmode.c_lflag |= ECHO | ISIG | ICANON;\r
- tcsetattr(0, TCSANOW, &newmode);\r
- line[0] = '\0';\r
- if (read(0, line, sizeof(line) - 1) <= 0)\r
- /* handled below */;\r
- tcsetattr(0, TCSANOW, &oldmode);\r
- }\r
-\r
- postmsg(&cf);\r
- if (line[0] == 'y' || line[0] == 'Y')\r
- return 2;\r
- else if (line[0] == 'n' || line[0] == 'N')\r
- return 1;\r
- else\r
- return 0;\r
-}\r
-\r
-/*\r
- * Warn about the obsolescent key file format.\r
- * \r
- * Uniquely among these functions, this one does _not_ expect a\r
- * frontend handle. This means that if PuTTY is ported to a\r
- * platform which requires frontend handles, this function will be\r
- * an anomaly. Fortunately, the problem it addresses will not have\r
- * been present on that platform, so it can plausibly be\r
- * implemented as an empty function.\r
- */\r
-void old_keyfile_warning(void)\r
-{\r
- static const char message[] =\r
- "You are loading an SSH-2 private key which has an\n"\r
- "old version of the file format. This means your key\n"\r
- "file is not fully tamperproof. Future versions of\n"\r
- "PuTTY may stop supporting this private key format,\n"\r
- "so we recommend you convert your key to the new\n"\r
- "format.\n"\r
- "\n"\r
- "Once the key is loaded into PuTTYgen, you can perform\n"\r
- "this conversion simply by saving it again.\n";\r
-\r
- struct termios cf;\r
- premsg(&cf);\r
- fputs(message, stderr);\r
- postmsg(&cf);\r
-}\r
-\r
-void console_provide_logctx(void *logctx)\r
-{\r
- console_logctx = logctx;\r
-}\r
-\r
-void logevent(void *frontend, const char *string)\r
-{\r
- struct termios cf;\r
- premsg(&cf);\r
- if (console_logctx)\r
- log_eventlog(console_logctx, string);\r
- postmsg(&cf);\r
-}\r
-\r
-/*\r
- * Special function to print text to the console for password\r
- * prompts and the like. Uses /dev/tty or stderr, in that order of\r
- * preference; also sanitises escape sequences out of the text, on\r
- * the basis that it might have been sent by a hostile SSH server\r
- * doing malicious keyboard-interactive.\r
- */\r
-static void console_prompt_text(FILE **confp, const char *data, int len)\r
-{\r
- int i;\r
-\r
- if (!*confp) {\r
- if ((*confp = fopen("/dev/tty", "w")) == NULL)\r
- *confp = stderr;\r
- }\r
-\r
- for (i = 0; i < len; i++)\r
- if ((data[i] & 0x60) || (data[i] == '\n'))\r
- fputc(data[i], *confp);\r
- fflush(*confp);\r
-}\r
-\r
-int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen)\r
-{\r
- size_t curr_prompt;\r
- FILE *confp = NULL;\r
-\r
- /*\r
- * Zero all the results, in case we abort half-way through.\r
- */\r
- {\r
- int i;\r
- for (i = 0; i < p->n_prompts; i++)\r
- memset(p->prompts[i]->result, 0, p->prompts[i]->result_len);\r
- }\r
-\r
- if (p->n_prompts && console_batch_mode)\r
- return 0;\r
-\r
- /*\r
- * Preamble.\r
- */\r
- /* We only print the `name' caption if we have to... */\r
- if (p->name_reqd && p->name) {\r
- size_t l = strlen(p->name);\r
- console_prompt_text(&confp, p->name, l);\r
- if (p->name[l-1] != '\n')\r
- console_prompt_text(&confp, "\n", 1);\r
- }\r
- /* ...but we always print any `instruction'. */\r
- if (p->instruction) {\r
- size_t l = strlen(p->instruction);\r
- console_prompt_text(&confp, p->instruction, l);\r
- if (p->instruction[l-1] != '\n')\r
- console_prompt_text(&confp, "\n", 1);\r
- }\r
-\r
- for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) {\r
-\r
- struct termios oldmode, newmode;\r
- int i;\r
- prompt_t *pr = p->prompts[curr_prompt];\r
-\r
- tcgetattr(0, &oldmode);\r
- newmode = oldmode;\r
- newmode.c_lflag |= ISIG | ICANON;\r
- if (!pr->echo)\r
- newmode.c_lflag &= ~ECHO;\r
- else\r
- newmode.c_lflag |= ECHO;\r
- tcsetattr(0, TCSANOW, &newmode);\r
-\r
- console_prompt_text(&confp, pr->prompt, strlen(pr->prompt));\r
-\r
- i = read(0, pr->result, pr->result_len - 1);\r
-\r
- tcsetattr(0, TCSANOW, &oldmode);\r
-\r
- if (i > 0 && pr->result[i-1] == '\n')\r
- i--;\r
- pr->result[i] = '\0';\r
-\r
- if (!pr->echo)\r
- console_prompt_text(&confp, "\n", 1);\r
-\r
- }\r
-\r
- if (confp && confp != stderr)\r
- fclose(confp);\r
-\r
- return 1; /* success */\r
-}\r
-\r
-void frontend_keypress(void *handle)\r
-{\r
- /*\r
- * This is nothing but a stub, in console code.\r
- */\r
- return;\r
-}\r
-\r
-int is_interactive(void)\r
-{\r
- return isatty(0);\r
-}\r
-\r
-/*\r
- * X11-forwarding-related things suitable for console.\r
- */\r
-\r
-char *platform_get_x_display(void) {\r
- return dupstr(getenv("DISPLAY"));\r
-}\r