]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - windows/wincons.c
d7b95704174fb11a14a5e1e934d1d076bcf4e7cf
[PuTTY.git] / windows / wincons.c
1 /*
2  * wincons.c - various interactive-prompt routines shared between
3  * the Windows console PuTTY tools
4  */
5
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <stdarg.h>
9
10 #include "putty.h"
11 #include "storage.h"
12 #include "ssh.h"
13
14 int console_batch_mode = FALSE;
15
16 static void *console_logctx = NULL;
17
18 /*
19  * Clean up and exit.
20  */
21 void cleanup_exit(int code)
22 {
23     /*
24      * Clean up.
25      */
26     sk_cleanup();
27
28     random_save_seed();
29 #ifdef MSCRYPTOAPI
30     crypto_wrapup();
31 #endif
32
33     exit(code);
34 }
35
36 void set_busy_status(void *frontend, int status)
37 {
38 }
39
40 void notify_remote_exit(void *frontend)
41 {
42 }
43
44 void timer_change_notify(unsigned long next)
45 {
46 }
47
48 int verify_ssh_host_key(void *frontend, char *host, int port,
49                         const char *keytype, char *keystr, char *fingerprint,
50                         void (*callback)(void *ctx, int result), void *ctx)
51 {
52     int ret;
53     HANDLE hin;
54     DWORD savemode, i;
55
56     static const char absentmsg_batch[] =
57         "The server's host key is not cached in the registry. You\n"
58         "have no guarantee that the server is the computer you\n"
59         "think it is.\n"
60         "The server's %s key fingerprint is:\n"
61         "%s\n"
62         "Connection abandoned.\n";
63     static const char absentmsg[] =
64         "The server's host key is not cached in the registry. You\n"
65         "have no guarantee that the server is the computer you\n"
66         "think it is.\n"
67         "The server's %s key fingerprint is:\n"
68         "%s\n"
69         "If you trust this host, enter \"y\" to add the key to\n"
70         "PuTTY's cache and carry on connecting.\n"
71         "If you want to carry on connecting just once, without\n"
72         "adding the key to the cache, enter \"n\".\n"
73         "If you do not trust this host, press Return to abandon the\n"
74         "connection.\n"
75         "Store key in cache? (y/n) ";
76
77     static const char wrongmsg_batch[] =
78         "WARNING - POTENTIAL SECURITY BREACH!\n"
79         "The server's host key does not match the one PuTTY has\n"
80         "cached in the registry. This means that either the\n"
81         "server administrator has changed the host key, or you\n"
82         "have actually connected to another computer pretending\n"
83         "to be the server.\n"
84         "The new %s key fingerprint is:\n"
85         "%s\n"
86         "Connection abandoned.\n";
87     static const char wrongmsg[] =
88         "WARNING - POTENTIAL SECURITY BREACH!\n"
89         "The server's host key does not match the one PuTTY has\n"
90         "cached in the registry. This means that either the\n"
91         "server administrator has changed the host key, or you\n"
92         "have actually connected to another computer pretending\n"
93         "to be the server.\n"
94         "The new %s key fingerprint is:\n"
95         "%s\n"
96         "If you were expecting this change and trust the new key,\n"
97         "enter \"y\" to update PuTTY's cache and continue connecting.\n"
98         "If you want to carry on connecting but without updating\n"
99         "the cache, enter \"n\".\n"
100         "If you want to abandon the connection completely, press\n"
101         "Return to cancel. Pressing Return is the ONLY guaranteed\n"
102         "safe choice.\n"
103         "Update cached key? (y/n, Return cancels connection) ";
104
105     static const char abandoned[] = "Connection abandoned.\n";
106
107     char line[32];
108
109     /*
110      * Verify the key against the registry.
111      */
112     ret = verify_host_key(host, port, keytype, keystr);
113
114     if (ret == 0)                      /* success - key matched OK */
115         return 1;
116
117     if (ret == 2) {                    /* key was different */
118         if (console_batch_mode) {
119             fprintf(stderr, wrongmsg_batch, keytype, fingerprint);
120             return 0;
121         }
122         fprintf(stderr, wrongmsg, keytype, fingerprint);
123         fflush(stderr);
124     }
125     if (ret == 1) {                    /* key was absent */
126         if (console_batch_mode) {
127             fprintf(stderr, absentmsg_batch, keytype, fingerprint);
128             return 0;
129         }
130         fprintf(stderr, absentmsg, keytype, fingerprint);
131         fflush(stderr);
132     }
133
134     hin = GetStdHandle(STD_INPUT_HANDLE);
135     GetConsoleMode(hin, &savemode);
136     SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |
137                          ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));
138     ReadFile(hin, line, sizeof(line) - 1, &i, NULL);
139     SetConsoleMode(hin, savemode);
140
141     if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') {
142         if (line[0] == 'y' || line[0] == 'Y')
143             store_host_key(host, port, keytype, keystr);
144         return 1;
145     } else {
146         fprintf(stderr, abandoned);
147         return 0;
148     }
149 }
150
151 void update_specials_menu(void *frontend)
152 {
153 }
154
155 /*
156  * Ask whether the selected algorithm is acceptable (since it was
157  * below the configured 'warn' threshold).
158  */
159 int askalg(void *frontend, const char *algtype, const char *algname,
160            void (*callback)(void *ctx, int result), void *ctx)
161 {
162     HANDLE hin;
163     DWORD savemode, i;
164
165     static const char msg[] =
166         "The first %s supported by the server is\n"
167         "%s, which is below the configured warning threshold.\n"
168         "Continue with connection? (y/n) ";
169     static const char msg_batch[] =
170         "The first %s supported by the server is\n"
171         "%s, which is below the configured warning threshold.\n"
172         "Connection abandoned.\n";
173     static const char abandoned[] = "Connection abandoned.\n";
174
175     char line[32];
176
177     if (console_batch_mode) {
178         fprintf(stderr, msg_batch, algtype, algname);
179         return 0;
180     }
181
182     fprintf(stderr, msg, algtype, algname);
183     fflush(stderr);
184
185     hin = GetStdHandle(STD_INPUT_HANDLE);
186     GetConsoleMode(hin, &savemode);
187     SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |
188                          ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));
189     ReadFile(hin, line, sizeof(line) - 1, &i, NULL);
190     SetConsoleMode(hin, savemode);
191
192     if (line[0] == 'y' || line[0] == 'Y') {
193         return 1;
194     } else {
195         fprintf(stderr, abandoned);
196         return 0;
197     }
198 }
199
200 int askhk(void *frontend, const char *algname, const char *betteralgs,
201           void (*callback)(void *ctx, int result), void *ctx)
202 {
203     HANDLE hin;
204     DWORD savemode, i;
205
206     static const char msg[] =
207         "The first host key type we have stored for this server\n"
208         "is %s, which is below the configured warning threshold.\n"
209         "The server also provides the following types of host key\n"
210         "above the threshold, which we do not have stored:\n"
211         "%s\n"
212         "Continue with connection? (y/n) ";
213     static const char msg_batch[] =
214         "The first host key type we have stored for this server\n"
215         "is %s, which is below the configured warning threshold.\n"
216         "The server also provides the following types of host key\n"
217         "above the threshold, which we do not have stored:\n"
218         "%s\n"
219         "Connection abandoned.\n";
220     static const char abandoned[] = "Connection abandoned.\n";
221
222     char line[32];
223
224     if (console_batch_mode) {
225         fprintf(stderr, msg_batch, algname, betteralgs);
226         return 0;
227     }
228
229     fprintf(stderr, msg, algname, betteralgs);
230     fflush(stderr);
231
232     hin = GetStdHandle(STD_INPUT_HANDLE);
233     GetConsoleMode(hin, &savemode);
234     SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |
235                          ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));
236     ReadFile(hin, line, sizeof(line) - 1, &i, NULL);
237     SetConsoleMode(hin, savemode);
238
239     if (line[0] == 'y' || line[0] == 'Y') {
240         return 1;
241     } else {
242         fprintf(stderr, abandoned);
243         return 0;
244     }
245 }
246
247 /*
248  * Ask whether to wipe a session log file before writing to it.
249  * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
250  */
251 int askappend(void *frontend, Filename *filename,
252               void (*callback)(void *ctx, int result), void *ctx)
253 {
254     HANDLE hin;
255     DWORD savemode, i;
256
257     static const char msgtemplate[] =
258         "The session log file \"%.*s\" already exists.\n"
259         "You can overwrite it with a new session log,\n"
260         "append your session log to the end of it,\n"
261         "or disable session logging for this session.\n"
262         "Enter \"y\" to wipe the file, \"n\" to append to it,\n"
263         "or just press Return to disable logging.\n"
264         "Wipe the log file? (y/n, Return cancels logging) ";
265
266     static const char msgtemplate_batch[] =
267         "The session log file \"%.*s\" already exists.\n"
268         "Logging will not be enabled.\n";
269
270     char line[32];
271
272     if (console_batch_mode) {
273         fprintf(stderr, msgtemplate_batch, FILENAME_MAX, filename->path);
274         fflush(stderr);
275         return 0;
276     }
277     fprintf(stderr, msgtemplate, FILENAME_MAX, filename->path);
278     fflush(stderr);
279
280     hin = GetStdHandle(STD_INPUT_HANDLE);
281     GetConsoleMode(hin, &savemode);
282     SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |
283                          ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));
284     ReadFile(hin, line, sizeof(line) - 1, &i, NULL);
285     SetConsoleMode(hin, savemode);
286
287     if (line[0] == 'y' || line[0] == 'Y')
288         return 2;
289     else if (line[0] == 'n' || line[0] == 'N')
290         return 1;
291     else
292         return 0;
293 }
294
295 /*
296  * Warn about the obsolescent key file format.
297  * 
298  * Uniquely among these functions, this one does _not_ expect a
299  * frontend handle. This means that if PuTTY is ported to a
300  * platform which requires frontend handles, this function will be
301  * an anomaly. Fortunately, the problem it addresses will not have
302  * been present on that platform, so it can plausibly be
303  * implemented as an empty function.
304  */
305 void old_keyfile_warning(void)
306 {
307     static const char message[] =
308         "You are loading an SSH-2 private key which has an\n"
309         "old version of the file format. This means your key\n"
310         "file is not fully tamperproof. Future versions of\n"
311         "PuTTY may stop supporting this private key format,\n"
312         "so we recommend you convert your key to the new\n"
313         "format.\n"
314         "\n"
315         "Once the key is loaded into PuTTYgen, you can perform\n"
316         "this conversion simply by saving it again.\n";
317
318     fputs(message, stderr);
319 }
320
321 /*
322  * Display the fingerprints of the PGP Master Keys to the user.
323  */
324 void pgp_fingerprints(void)
325 {
326     fputs("These are the fingerprints of the PuTTY PGP Master Keys. They can\n"
327           "be used to establish a trust path from this executable to another\n"
328           "one. See the manual for more information.\n"
329           "(Note: these fingerprints have nothing to do with SSH!)\n"
330           "\n"
331           "PuTTY Master Key as of 2015 (RSA, 4096-bit):\n"
332           "  " PGP_MASTER_KEY_FP "\n\n"
333           "Original PuTTY Master Key (RSA, 1024-bit):\n"
334           "  " PGP_RSA_MASTER_KEY_FP "\n"
335           "Original PuTTY Master Key (DSA, 1024-bit):\n"
336           "  " PGP_DSA_MASTER_KEY_FP "\n", stdout);
337 }
338
339 void console_provide_logctx(void *logctx)
340 {
341     console_logctx = logctx;
342 }
343
344 void logevent(void *frontend, const char *string)
345 {
346     log_eventlog(console_logctx, string);
347 }
348
349 static void console_data_untrusted(HANDLE hout, const char *data, int len)
350 {
351     DWORD dummy;
352     /* FIXME: control-character filtering */
353     WriteFile(hout, data, len, &dummy, NULL);
354 }
355
356 int console_get_userpass_input(prompts_t *p,
357                                const unsigned char *in, int inlen)
358 {
359     HANDLE hin, hout;
360     size_t curr_prompt;
361
362     /*
363      * Zero all the results, in case we abort half-way through.
364      */
365     {
366         int i;
367         for (i = 0; i < (int)p->n_prompts; i++)
368             prompt_set_result(p->prompts[i], "");
369     }
370
371     /*
372      * The prompts_t might contain a message to be displayed but no
373      * actual prompt. More usually, though, it will contain
374      * questions that the user needs to answer, in which case we
375      * need to ensure that we're able to get the answers.
376      */
377     if (p->n_prompts) {
378         if (console_batch_mode)
379             return 0;
380         hin = GetStdHandle(STD_INPUT_HANDLE);
381         if (hin == INVALID_HANDLE_VALUE) {
382             fprintf(stderr, "Cannot get standard input handle\n");
383             cleanup_exit(1);
384         }
385     }
386
387     /*
388      * And if we have anything to print, we need standard output.
389      */
390     if ((p->name_reqd && p->name) || p->instruction || p->n_prompts) {
391         hout = GetStdHandle(STD_OUTPUT_HANDLE);
392         if (hout == INVALID_HANDLE_VALUE) {
393             fprintf(stderr, "Cannot get standard output handle\n");
394             cleanup_exit(1);
395         }
396     }
397
398     /*
399      * Preamble.
400      */
401     /* We only print the `name' caption if we have to... */
402     if (p->name_reqd && p->name) {
403         size_t l = strlen(p->name);
404         console_data_untrusted(hout, p->name, l);
405         if (p->name[l-1] != '\n')
406             console_data_untrusted(hout, "\n", 1);
407     }
408     /* ...but we always print any `instruction'. */
409     if (p->instruction) {
410         size_t l = strlen(p->instruction);
411         console_data_untrusted(hout, p->instruction, l);
412         if (p->instruction[l-1] != '\n')
413             console_data_untrusted(hout, "\n", 1);
414     }
415
416     for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) {
417
418         DWORD savemode, newmode;
419         int len;
420         prompt_t *pr = p->prompts[curr_prompt];
421
422         GetConsoleMode(hin, &savemode);
423         newmode = savemode | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT;
424         if (!pr->echo)
425             newmode &= ~ENABLE_ECHO_INPUT;
426         else
427             newmode |= ENABLE_ECHO_INPUT;
428         SetConsoleMode(hin, newmode);
429
430         console_data_untrusted(hout, pr->prompt, strlen(pr->prompt));
431
432         len = 0;
433         while (1) {
434             DWORD ret = 0;
435             BOOL r;
436
437             prompt_ensure_result_size(pr, len * 5 / 4 + 512);
438
439             r = ReadFile(hin, pr->result + len, pr->resultsize - len - 1,
440                          &ret, NULL);
441
442             if (!r || ret == 0) {
443                 len = -1;
444                 break;
445             }
446             len += ret;
447             if (pr->result[len - 1] == '\n') {
448                 len--;
449                 if (pr->result[len - 1] == '\r')
450                     len--;
451                 break;
452             }
453         }
454
455         SetConsoleMode(hin, savemode);
456
457         if (!pr->echo) {
458             DWORD dummy;
459             WriteFile(hout, "\r\n", 2, &dummy, NULL);
460         }
461
462         if (len < 0) {
463             return 0;                  /* failure due to read error */
464         }
465
466         pr->result[len] = '\0';
467     }
468
469     return 1; /* success */
470 }
471
472 void frontend_keypress(void *handle)
473 {
474     /*
475      * This is nothing but a stub, in console code.
476      */
477     return;
478 }