Introduce a sane interface function, from_backend(), for backends to
[u/mdw/putty] / plink.c
diff --git a/plink.c b/plink.c
index 62394c0..26a1729 100644 (file)
--- a/plink.c
+++ b/plink.c
@@ -11,6 +11,8 @@
 
 #define PUTTY_DO_GLOBALS                      /* actually _define_ globals */
 #include "putty.h"
+#include "winstuff.h"
+#include "storage.h"
 
 void fatalbox (char *p, ...) {
     va_list ap;
@@ -35,13 +37,88 @@ void connection_fatal (char *p, ...) {
 
 static char *password = NULL;
 
-/*
- * Stubs for linking with other modules.
- */
-void write_clip (void *data, int len, int must_deselect) { }
-void term_deselect(void) { }
+void logevent(char *string) { }
+
+void verify_ssh_host_key(char *host, int port, char *keytype,
+                         char *keystr, char *fingerprint) {
+    int ret;
+    HANDLE hin;
+    DWORD savemode, i;
+
+    static const char absentmsg[] =
+        "The server's host key is not cached in the registry. You\n"
+        "have no guarantee that the server is the computer you\n"
+        "think it is.\n"
+        "The server's key fingerprint is:\n"
+        "%s\n"
+        "If you trust this host, enter \"y\" to add the key to\n"
+        "PuTTY's cache and carry on connecting.\n"
+        "If you do not trust this host, enter \"n\" to abandon the\n"
+        "connection.\n"
+        "Continue connecting? (y/n) ";
+
+    static const char wrongmsg[] =
+        "WARNING - POTENTIAL SECURITY BREACH!\n"
+        "The server's host key does not match the one PuTTY has\n"
+        "cached in the registry. This means that either the\n"
+        "server administrator has changed the host key, or you\n"
+        "have actually connected to another computer pretending\n"
+        "to be the server.\n"
+        "The new key fingerprint is:\n"
+        "%s\n"
+        "If you were expecting this change and trust the new key,\n"
+        "enter \"y\" to update PuTTY's cache and continue connecting.\n"
+        "If you want to carry on connecting but without updating\n"
+        "the cache, enter \"n\".\n"
+        "If you want to abandon the connection completely, press\n"
+        "Return to cancel. Pressing Return is the ONLY guaranteed\n"
+        "safe choice.\n"
+        "Update cached key? (y/n, Return cancels connection) ";
+
+    static const char abandoned[] = "Connection abandoned.\n";
+
+    char line[32];
+
+    /*
+     * Verify the key against the registry.
+     */
+    ret = verify_host_key(host, port, keytype, keystr);
+
+    if (ret == 0)                      /* success - key matched OK */
+        return;
 
-HANDLE outhandle;
+    if (ret == 2)                      /* key was different */
+        fprintf(stderr, wrongmsg, fingerprint);
+    if (ret == 1)                      /* key was absent */
+        fprintf(stderr, absentmsg, fingerprint);
+
+    hin = GetStdHandle(STD_INPUT_HANDLE);
+    GetConsoleMode(hin, &savemode);
+    SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |
+                         ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));
+    ReadFile(hin, line, sizeof(line)-1, &i, NULL);
+    SetConsoleMode(hin, savemode);
+
+    if (ret == 2) {                    /* key was different */
+        if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') {
+            if (line[0] == 'y' || line[0] == 'Y')
+                store_host_key(host, port, keytype, keystr);
+        } else {
+            fprintf(stderr, abandoned);
+            exit(0);
+        }
+    }
+    if (ret == 1) {                    /* key was absent */
+        if (line[0] == 'y' || line[0] == 'Y')
+            store_host_key(host, port, keytype, keystr);
+        else {
+            fprintf(stderr, abandoned);
+            exit(0);
+        }
+    }
+}
+
+HANDLE outhandle, errhandle;
 DWORD orig_console_mode;
 
 void begin_session(void) {
@@ -51,17 +128,17 @@ void begin_session(void) {
         SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), orig_console_mode);
 }
 
-void term_out(void)
-{
-    int reap;
+void from_backend(int is_stderr, char *data, int len) {
+    int pos;
     DWORD ret;
-    reap = 0;
-    while (reap < inbuf_head) {
-        if (!WriteFile(outhandle, inbuf+reap, inbuf_head-reap, &ret, NULL))
+    HANDLE h = (is_stderr ? errhandle : outhandle);
+
+    pos = 0;
+    while (pos < len) {
+        if (!WriteFile(h, data+pos, len-pos, &ret, NULL))
             return;                    /* give up in panic */
-        reap += ret;
+        pos += ret;
     }
-    inbuf_head = 0;
 }
 
 struct input_data {
@@ -112,7 +189,7 @@ static int get_password(const char *prompt, char *str, int maxlen)
     return 1;
 }
 
-static int WINAPI stdin_read_thread(void *param) {
+static DWORD WINAPI stdin_read_thread(void *param) {
     struct input_data *idata = (struct input_data *)param;
     HANDLE inhandle;
 
@@ -162,7 +239,7 @@ int main(int argc, char **argv) {
     /*
      * Process the command line.
      */
-    do_defaults(NULL);
+    do_defaults(NULL, &cfg);
     default_protocol = cfg.protocol;
     default_port = cfg.port;
     {
@@ -270,7 +347,7 @@ int main(int argc, char **argv) {
                         /*
                          * One string.
                          */
-                        do_defaults (p);
+                        do_defaults (p, &cfg);
                         if (cfg.host[0] == '\0') {
                             /* No settings for this host; use defaults */
                             strncpy(cfg.host, p, sizeof(cfg.host)-1);
@@ -373,6 +450,7 @@ int main(int argc, char **argv) {
     GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &orig_console_mode);
     SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_PROCESSED_INPUT);
     outhandle = GetStdHandle(STD_OUTPUT_HANDLE);
+    errhandle = GetStdHandle(STD_ERROR_HANDLE);
 
     /*
      * Now we must send the back end oodles of stuff.
@@ -427,7 +505,6 @@ int main(int argc, char **argv) {
                     break;
                 }
             }
-            term_out();
         } else if (n == 1) {
             if (idata.len > 0) {
                 back->send(idata.buffer, idata.len);
@@ -435,6 +512,8 @@ int main(int argc, char **argv) {
                 back->special(TS_EOF);
             }
         }
+        if (back->socket() == INVALID_SOCKET)
+            break;                 /* we closed the connection */
     }
     WSACleanup();
     return 0;