]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - windows/wincons.c
757f753b418566eb9b7abd7fc33c392605e7b0ea
[PuTTY.git] / windows / wincons.c
1 /*
2  * console.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 verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
37                          char *keystr, char *fingerprint)
38 {
39     int ret;
40     HANDLE hin;
41     DWORD savemode, i;
42
43     static const char absentmsg_batch[] =
44         "The server's host key is not cached in the registry. You\n"
45         "have no guarantee that the server is the computer you\n"
46         "think it is.\n"
47         "The server's %s key fingerprint is:\n"
48         "%s\n"
49         "Connection abandoned.\n";
50     static const char absentmsg[] =
51         "The server's host key is not cached in the registry. You\n"
52         "have no guarantee that the server is the computer you\n"
53         "think it is.\n"
54         "The server's %s key fingerprint is:\n"
55         "%s\n"
56         "If you trust this host, enter \"y\" to add the key to\n"
57         "PuTTY's cache and carry on connecting.\n"
58         "If you want to carry on connecting just once, without\n"
59         "adding the key to the cache, enter \"n\".\n"
60         "If you do not trust this host, press Return to abandon the\n"
61         "connection.\n"
62         "Store key in cache? (y/n) ";
63
64     static const char wrongmsg_batch[] =
65         "WARNING - POTENTIAL SECURITY BREACH!\n"
66         "The server's host key does not match the one PuTTY has\n"
67         "cached in the registry. This means that either the\n"
68         "server administrator has changed the host key, or you\n"
69         "have actually connected to another computer pretending\n"
70         "to be the server.\n"
71         "The new %s key fingerprint is:\n"
72         "%s\n"
73         "Connection abandoned.\n";
74     static const char wrongmsg[] =
75         "WARNING - POTENTIAL SECURITY BREACH!\n"
76         "The server's host key does not match the one PuTTY has\n"
77         "cached in the registry. This means that either the\n"
78         "server administrator has changed the host key, or you\n"
79         "have actually connected to another computer pretending\n"
80         "to be the server.\n"
81         "The new %s key fingerprint is:\n"
82         "%s\n"
83         "If you were expecting this change and trust the new key,\n"
84         "enter \"y\" to update PuTTY's cache and continue connecting.\n"
85         "If you want to carry on connecting but without updating\n"
86         "the cache, enter \"n\".\n"
87         "If you want to abandon the connection completely, press\n"
88         "Return to cancel. Pressing Return is the ONLY guaranteed\n"
89         "safe choice.\n"
90         "Update cached key? (y/n, Return cancels connection) ";
91
92     static const char abandoned[] = "Connection abandoned.\n";
93
94     char line[32];
95
96     /*
97      * Verify the key against the registry.
98      */
99     ret = verify_host_key(host, port, keytype, keystr);
100
101     if (ret == 0)                      /* success - key matched OK */
102         return;
103
104     if (ret == 2) {                    /* key was different */
105         if (console_batch_mode) {
106             fprintf(stderr, wrongmsg_batch, keytype, fingerprint);
107             cleanup_exit(1);
108         }
109         fprintf(stderr, wrongmsg, keytype, fingerprint);
110         fflush(stderr);
111     }
112     if (ret == 1) {                    /* key was absent */
113         if (console_batch_mode) {
114             fprintf(stderr, absentmsg_batch, keytype, fingerprint);
115             cleanup_exit(1);
116         }
117         fprintf(stderr, absentmsg, keytype, fingerprint);
118         fflush(stderr);
119     }
120
121     hin = GetStdHandle(STD_INPUT_HANDLE);
122     GetConsoleMode(hin, &savemode);
123     SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |
124                          ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));
125     ReadFile(hin, line, sizeof(line) - 1, &i, NULL);
126     SetConsoleMode(hin, savemode);
127
128     if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') {
129         if (line[0] == 'y' || line[0] == 'Y')
130             store_host_key(host, port, keytype, keystr);
131     } else {
132         fprintf(stderr, abandoned);
133         cleanup_exit(0);
134     }
135 }
136
137 void update_specials_menu(void *frontend)
138 {
139 }
140
141 /*
142  * Ask whether the selected cipher is acceptable (since it was
143  * below the configured 'warn' threshold).
144  * cs: 0 = both ways, 1 = client->server, 2 = server->client
145  */
146 void askcipher(void *frontend, char *ciphername, int cs)
147 {
148     HANDLE hin;
149     DWORD savemode, i;
150
151     static const char msg[] =
152         "The first %scipher supported by the server is\n"
153         "%s, which is below the configured warning threshold.\n"
154         "Continue with connection? (y/n) ";
155     static const char msg_batch[] =
156         "The first %scipher supported by the server is\n"
157         "%s, which is below the configured warning threshold.\n"
158         "Connection abandoned.\n";
159     static const char abandoned[] = "Connection abandoned.\n";
160
161     char line[32];
162
163     if (console_batch_mode) {
164         fprintf(stderr, msg_batch,
165                 (cs == 0) ? "" :
166                 (cs == 1) ? "client-to-server " : "server-to-client ",
167                 ciphername);
168         cleanup_exit(1);
169     }
170
171     fprintf(stderr, msg,
172             (cs == 0) ? "" :
173             (cs == 1) ? "client-to-server " : "server-to-client ",
174             ciphername);
175     fflush(stderr);
176
177     hin = GetStdHandle(STD_INPUT_HANDLE);
178     GetConsoleMode(hin, &savemode);
179     SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |
180                          ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));
181     ReadFile(hin, line, sizeof(line) - 1, &i, NULL);
182     SetConsoleMode(hin, savemode);
183
184     if (line[0] == 'y' || line[0] == 'Y') {
185         return;
186     } else {
187         fprintf(stderr, abandoned);
188         cleanup_exit(0);
189     }
190 }
191
192 /*
193  * Ask whether to wipe a session log file before writing to it.
194  * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
195  */
196 int askappend(void *frontend, Filename filename)
197 {
198     HANDLE hin;
199     DWORD savemode, i;
200
201     static const char msgtemplate[] =
202         "The session log file \"%.*s\" already exists.\n"
203         "You can overwrite it with a new session log,\n"
204         "append your session log to the end of it,\n"
205         "or disable session logging for this session.\n"
206         "Enter \"y\" to wipe the file, \"n\" to append to it,\n"
207         "or just press Return to disable logging.\n"
208         "Wipe the log file? (y/n, Return cancels logging) ";
209
210     static const char msgtemplate_batch[] =
211         "The session log file \"%.*s\" already exists.\n"
212         "Logging will not be enabled.\n";
213
214     char line[32];
215
216     if (console_batch_mode) {
217         fprintf(stderr, msgtemplate_batch, FILENAME_MAX, filename.path);
218         fflush(stderr);
219         return 0;
220     }
221     fprintf(stderr, msgtemplate, FILENAME_MAX, filename.path);
222     fflush(stderr);
223
224     hin = GetStdHandle(STD_INPUT_HANDLE);
225     GetConsoleMode(hin, &savemode);
226     SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |
227                          ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));
228     ReadFile(hin, line, sizeof(line) - 1, &i, NULL);
229     SetConsoleMode(hin, savemode);
230
231     if (line[0] == 'y' || line[0] == 'Y')
232         return 2;
233     else if (line[0] == 'n' || line[0] == 'N')
234         return 1;
235     else
236         return 0;
237 }
238
239 /*
240  * Warn about the obsolescent key file format.
241  * 
242  * Uniquely among these functions, this one does _not_ expect a
243  * frontend handle. This means that if PuTTY is ported to a
244  * platform which requires frontend handles, this function will be
245  * an anomaly. Fortunately, the problem it addresses will not have
246  * been present on that platform, so it can plausibly be
247  * implemented as an empty function.
248  */
249 void old_keyfile_warning(void)
250 {
251     static const char message[] =
252         "You are loading an SSH 2 private key which has an\n"
253         "old version of the file format. This means your key\n"
254         "file is not fully tamperproof. Future versions of\n"
255         "PuTTY may stop supporting this private key format,\n"
256         "so we recommend you convert your key to the new\n"
257         "format.\n"
258         "\n"
259         "Once the key is loaded into PuTTYgen, you can perform\n"
260         "this conversion simply by saving it again.\n";
261
262     fputs(message, stderr);
263 }
264
265 void console_provide_logctx(void *logctx)
266 {
267     console_logctx = logctx;
268 }
269
270 void logevent(void *frontend, const char *string)
271 {
272     if (console_logctx)
273         log_eventlog(console_logctx, string);
274 }
275
276 int console_get_line(const char *prompt, char *str,
277                             int maxlen, int is_pw)
278 {
279     HANDLE hin, hout;
280     DWORD savemode, newmode, i;
281
282     if (console_batch_mode) {
283         if (maxlen > 0)
284             str[0] = '\0';
285         return 0;
286     } else {
287         hin = GetStdHandle(STD_INPUT_HANDLE);
288         hout = GetStdHandle(STD_OUTPUT_HANDLE);
289         if (hin == INVALID_HANDLE_VALUE || hout == INVALID_HANDLE_VALUE) {
290             fprintf(stderr, "Cannot get standard input/output handles\n");
291             cleanup_exit(1);
292         }
293
294         GetConsoleMode(hin, &savemode);
295         newmode = savemode | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT;
296         if (is_pw)
297             newmode &= ~ENABLE_ECHO_INPUT;
298         else
299             newmode |= ENABLE_ECHO_INPUT;
300         SetConsoleMode(hin, newmode);
301
302         WriteFile(hout, prompt, strlen(prompt), &i, NULL);
303         ReadFile(hin, str, maxlen - 1, &i, NULL);
304
305         SetConsoleMode(hin, savemode);
306
307         if ((int) i > maxlen)
308             i = maxlen - 1;
309         else
310             i = i - 2;
311         str[i] = '\0';
312
313         if (is_pw)
314             WriteFile(hout, "\r\n", 2, &i, NULL);
315
316         return 1;
317     }
318 }
319
320 void frontend_keypress(void *handle)
321 {
322     /*
323      * This is nothing but a stub, in console code.
324      */
325     return;
326 }