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