]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - unix/uxcons.c
Turn 'Filename' into a dynamically allocated type with no arbitrary
[PuTTY.git] / unix / uxcons.c
1 /*
2  * uxcons.c: various interactive-prompt routines shared between the
3  * Unix console PuTTY tools
4  */
5
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <stdarg.h>
9 #include <assert.h>
10
11 #include <termios.h>
12 #include <unistd.h>
13 #include <fcntl.h>
14
15 #include "putty.h"
16 #include "storage.h"
17 #include "ssh.h"
18
19 int console_batch_mode = FALSE;
20
21 static void *console_logctx = NULL;
22
23 static struct termios orig_termios_stderr;
24 static int stderr_is_a_tty;
25
26 void stderr_tty_init()
27 {
28     /* Ensure that if stderr is a tty, we can get it back to a sane state. */
29     if ((flags & FLAG_STDERR_TTY) && isatty(STDERR_FILENO)) {
30         stderr_is_a_tty = TRUE;
31         tcgetattr(STDERR_FILENO, &orig_termios_stderr);
32     }
33 }
34
35 void premsg(struct termios *cf)
36 {
37     if (stderr_is_a_tty) {
38         tcgetattr(STDERR_FILENO, cf);
39         tcsetattr(STDERR_FILENO, TCSADRAIN, &orig_termios_stderr);
40     }
41 }
42 void postmsg(struct termios *cf)
43 {
44     if (stderr_is_a_tty)
45         tcsetattr(STDERR_FILENO, TCSADRAIN, cf);
46 }
47
48 /*
49  * Clean up and exit.
50  */
51 void cleanup_exit(int code)
52 {
53     /*
54      * Clean up.
55      */
56     sk_cleanup();
57     random_save_seed();
58     exit(code);
59 }
60
61 void set_busy_status(void *frontend, int status)
62 {
63 }
64
65 void update_specials_menu(void *frontend)
66 {
67 }
68
69 void notify_remote_exit(void *frontend)
70 {
71 }
72
73 void timer_change_notify(long next)
74 {
75 }
76
77 int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
78                         char *keystr, char *fingerprint,
79                         void (*callback)(void *ctx, int result), void *ctx)
80 {
81     int ret;
82
83     static const char absentmsg_batch[] =
84         "The server's host key is not cached. You have no guarantee\n"
85         "that the server is the computer you think it is.\n"
86         "The server's %s key fingerprint is:\n"
87         "%s\n"
88         "Connection abandoned.\n";
89     static const char absentmsg[] =
90         "The server's host key is not cached. You have no guarantee\n"
91         "that the server is the computer you think it is.\n"
92         "The server's %s key fingerprint is:\n"
93         "%s\n"
94         "If you trust this host, enter \"y\" to add the key to\n"
95         "PuTTY's cache and carry on connecting.\n"
96         "If you want to carry on connecting just once, without\n"
97         "adding the key to the cache, enter \"n\".\n"
98         "If you do not trust this host, press Return to abandon the\n"
99         "connection.\n"
100         "Store key in cache? (y/n) ";
101
102     static const char wrongmsg_batch[] =
103         "WARNING - POTENTIAL SECURITY BREACH!\n"
104         "The server's host key does not match the one PuTTY has\n"
105         "cached. This means that either the server administrator\n"
106         "has changed the host key, or you have actually connected\n"
107         "to another computer pretending to be the server.\n"
108         "The new %s key fingerprint is:\n"
109         "%s\n"
110         "Connection abandoned.\n";
111     static const char wrongmsg[] =
112         "WARNING - POTENTIAL SECURITY BREACH!\n"
113         "The server's host key does not match the one PuTTY has\n"
114         "cached. This means that either the server administrator\n"
115         "has changed the host key, or you have actually connected\n"
116         "to another computer pretending to be the server.\n"
117         "The new %s key fingerprint is:\n"
118         "%s\n"
119         "If you were expecting this change and trust the new key,\n"
120         "enter \"y\" to update PuTTY's cache and continue connecting.\n"
121         "If you want to carry on connecting but without updating\n"
122         "the cache, enter \"n\".\n"
123         "If you want to abandon the connection completely, press\n"
124         "Return to cancel. Pressing Return is the ONLY guaranteed\n"
125         "safe choice.\n"
126         "Update cached key? (y/n, Return cancels connection) ";
127
128     static const char abandoned[] = "Connection abandoned.\n";
129
130     char line[32];
131     struct termios cf;
132
133     /*
134      * Verify the key.
135      */
136     ret = verify_host_key(host, port, keytype, keystr);
137
138     if (ret == 0)                      /* success - key matched OK */
139         return 1;
140
141     premsg(&cf);
142     if (ret == 2) {                    /* key was different */
143         if (console_batch_mode) {
144             fprintf(stderr, wrongmsg_batch, keytype, fingerprint);
145             return 0;
146         }
147         fprintf(stderr, wrongmsg, keytype, fingerprint);
148         fflush(stderr);
149     }
150     if (ret == 1) {                    /* key was absent */
151         if (console_batch_mode) {
152             fprintf(stderr, absentmsg_batch, keytype, fingerprint);
153             return 0;
154         }
155         fprintf(stderr, absentmsg, keytype, fingerprint);
156         fflush(stderr);
157     }
158
159     {
160         struct termios oldmode, newmode;
161         tcgetattr(0, &oldmode);
162         newmode = oldmode;
163         newmode.c_lflag |= ECHO | ISIG | ICANON;
164         tcsetattr(0, TCSANOW, &newmode);
165         line[0] = '\0';
166         if (read(0, line, sizeof(line) - 1) <= 0)
167             /* handled below */;
168         tcsetattr(0, TCSANOW, &oldmode);
169     }
170
171     if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') {
172         if (line[0] == 'y' || line[0] == 'Y')
173             store_host_key(host, port, keytype, keystr);
174         postmsg(&cf);
175         return 1;
176     } else {
177         fprintf(stderr, abandoned);
178         postmsg(&cf);
179         return 0;
180     }
181 }
182
183 /*
184  * Ask whether the selected algorithm is acceptable (since it was
185  * below the configured 'warn' threshold).
186  */
187 int askalg(void *frontend, const char *algtype, const char *algname,
188            void (*callback)(void *ctx, int result), void *ctx)
189 {
190     static const char msg[] =
191         "The first %s supported by the server is\n"
192         "%s, which is below the configured warning threshold.\n"
193         "Continue with connection? (y/n) ";
194     static const char msg_batch[] =
195         "The first %s supported by the server is\n"
196         "%s, which is below the configured warning threshold.\n"
197         "Connection abandoned.\n";
198     static const char abandoned[] = "Connection abandoned.\n";
199
200     char line[32];
201     struct termios cf;
202
203     premsg(&cf);
204     if (console_batch_mode) {
205         fprintf(stderr, msg_batch, algtype, algname);
206         return 0;
207     }
208
209     fprintf(stderr, msg, algtype, algname);
210     fflush(stderr);
211
212     {
213         struct termios oldmode, newmode;
214         tcgetattr(0, &oldmode);
215         newmode = oldmode;
216         newmode.c_lflag |= ECHO | ISIG | ICANON;
217         tcsetattr(0, TCSANOW, &newmode);
218         line[0] = '\0';
219         if (read(0, line, sizeof(line) - 1) <= 0)
220             /* handled below */;
221         tcsetattr(0, TCSANOW, &oldmode);
222     }
223
224     if (line[0] == 'y' || line[0] == 'Y') {
225         postmsg(&cf);
226         return 1;
227     } else {
228         fprintf(stderr, abandoned);
229         postmsg(&cf);
230         return 0;
231     }
232 }
233
234 /*
235  * Ask whether to wipe a session log file before writing to it.
236  * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
237  */
238 int askappend(void *frontend, Filename *filename,
239               void (*callback)(void *ctx, int result), void *ctx)
240 {
241     static const char msgtemplate[] =
242         "The session log file \"%.*s\" already exists.\n"
243         "You can overwrite it with a new session log,\n"
244         "append your session log to the end of it,\n"
245         "or disable session logging for this session.\n"
246         "Enter \"y\" to wipe the file, \"n\" to append to it,\n"
247         "or just press Return to disable logging.\n"
248         "Wipe the log file? (y/n, Return cancels logging) ";
249
250     static const char msgtemplate_batch[] =
251         "The session log file \"%.*s\" already exists.\n"
252         "Logging will not be enabled.\n";
253
254     char line[32];
255     struct termios cf;
256
257     premsg(&cf);
258     if (console_batch_mode) {
259         fprintf(stderr, msgtemplate_batch, FILENAME_MAX, filename->path);
260         fflush(stderr);
261         return 0;
262     }
263     fprintf(stderr, msgtemplate, FILENAME_MAX, filename->path);
264     fflush(stderr);
265
266     {
267         struct termios oldmode, newmode;
268         tcgetattr(0, &oldmode);
269         newmode = oldmode;
270         newmode.c_lflag |= ECHO | ISIG | ICANON;
271         tcsetattr(0, TCSANOW, &newmode);
272         line[0] = '\0';
273         if (read(0, line, sizeof(line) - 1) <= 0)
274             /* handled below */;
275         tcsetattr(0, TCSANOW, &oldmode);
276     }
277
278     postmsg(&cf);
279     if (line[0] == 'y' || line[0] == 'Y')
280         return 2;
281     else if (line[0] == 'n' || line[0] == 'N')
282         return 1;
283     else
284         return 0;
285 }
286
287 /*
288  * Warn about the obsolescent key file format.
289  * 
290  * Uniquely among these functions, this one does _not_ expect a
291  * frontend handle. This means that if PuTTY is ported to a
292  * platform which requires frontend handles, this function will be
293  * an anomaly. Fortunately, the problem it addresses will not have
294  * been present on that platform, so it can plausibly be
295  * implemented as an empty function.
296  */
297 void old_keyfile_warning(void)
298 {
299     static const char message[] =
300         "You are loading an SSH-2 private key which has an\n"
301         "old version of the file format. This means your key\n"
302         "file is not fully tamperproof. Future versions of\n"
303         "PuTTY may stop supporting this private key format,\n"
304         "so we recommend you convert your key to the new\n"
305         "format.\n"
306         "\n"
307         "Once the key is loaded into PuTTYgen, you can perform\n"
308         "this conversion simply by saving it again.\n";
309
310     struct termios cf;
311     premsg(&cf);
312     fputs(message, stderr);
313     postmsg(&cf);
314 }
315
316 void console_provide_logctx(void *logctx)
317 {
318     console_logctx = logctx;
319 }
320
321 void logevent(void *frontend, const char *string)
322 {
323     struct termios cf;
324     premsg(&cf);
325     if (console_logctx)
326         log_eventlog(console_logctx, string);
327     postmsg(&cf);
328 }
329
330 /*
331  * Special functions to read and print to the console for password
332  * prompts and the like. Uses /dev/tty or stdin/stderr, in that order
333  * of preference; also sanitises escape sequences out of the text, on
334  * the basis that it might have been sent by a hostile SSH server
335  * doing malicious keyboard-interactive.
336  */
337 static void console_open(FILE **outfp, int *infd)
338 {
339     int fd;
340
341     if ((fd = open("/dev/tty", O_RDWR)) >= 0) {
342         *infd = fd;
343         *outfp = fdopen(*infd, "w");
344     } else {
345         *infd = 0;
346         *outfp = stderr;
347     }
348 }
349 static void console_close(FILE *outfp, int infd)
350 {
351     if (outfp != stderr)
352         fclose(outfp);             /* will automatically close infd too */
353 }
354
355 static void console_prompt_text(FILE *outfp, const char *data, int len)
356 {
357     int i;
358
359     for (i = 0; i < len; i++)
360         if ((data[i] & 0x60) || (data[i] == '\n'))
361             fputc(data[i], outfp);
362     fflush(outfp);
363 }
364
365 int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen)
366 {
367     size_t curr_prompt;
368     FILE *outfp = NULL;
369     int infd;
370
371     /*
372      * Zero all the results, in case we abort half-way through.
373      */
374     {
375         int i;
376         for (i = 0; i < p->n_prompts; i++)
377             memset(p->prompts[i]->result, 0, p->prompts[i]->result_len);
378     }
379
380     if (p->n_prompts && console_batch_mode)
381         return 0;
382
383     console_open(&outfp, &infd);
384
385     /*
386      * Preamble.
387      */
388     /* We only print the `name' caption if we have to... */
389     if (p->name_reqd && p->name) {
390         size_t l = strlen(p->name);
391         console_prompt_text(outfp, p->name, l);
392         if (p->name[l-1] != '\n')
393             console_prompt_text(outfp, "\n", 1);
394     }
395     /* ...but we always print any `instruction'. */
396     if (p->instruction) {
397         size_t l = strlen(p->instruction);
398         console_prompt_text(outfp, p->instruction, l);
399         if (p->instruction[l-1] != '\n')
400             console_prompt_text(outfp, "\n", 1);
401     }
402
403     for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) {
404
405         struct termios oldmode, newmode;
406         int i;
407         prompt_t *pr = p->prompts[curr_prompt];
408
409         tcgetattr(infd, &oldmode);
410         newmode = oldmode;
411         newmode.c_lflag |= ISIG | ICANON;
412         if (!pr->echo)
413             newmode.c_lflag &= ~ECHO;
414         else
415             newmode.c_lflag |= ECHO;
416         tcsetattr(infd, TCSANOW, &newmode);
417
418         console_prompt_text(outfp, pr->prompt, strlen(pr->prompt));
419
420         i = read(infd, pr->result, pr->result_len - 1);
421
422         tcsetattr(infd, TCSANOW, &oldmode);
423
424         if (i > 0 && pr->result[i-1] == '\n')
425             i--;
426         pr->result[i] = '\0';
427
428         if (!pr->echo)
429             console_prompt_text(outfp, "\n", 1);
430
431     }
432
433     console_close(outfp, infd);
434
435     return 1; /* success */
436 }
437
438 void frontend_keypress(void *handle)
439 {
440     /*
441      * This is nothing but a stub, in console code.
442      */
443     return;
444 }
445
446 int is_interactive(void)
447 {
448     return isatty(0);
449 }
450
451 /*
452  * X11-forwarding-related things suitable for console.
453  */
454
455 char *platform_get_x_display(void) {
456     return dupstr(getenv("DISPLAY"));
457 }