]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - unix/uxcons.c
Giant const-correctness patch of doom!
[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(unsigned long next)
74 {
75 }
76
77 int verify_ssh_host_key(void *frontend, char *host, int port,
78                         const char *keytype, 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     if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE))
325         premsg(&cf);
326     if (console_logctx)
327         log_eventlog(console_logctx, string);
328     if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE))
329         postmsg(&cf);
330 }
331
332 /*
333  * Special functions to read and print to the console for password
334  * prompts and the like. Uses /dev/tty or stdin/stderr, in that order
335  * of preference; also sanitises escape sequences out of the text, on
336  * the basis that it might have been sent by a hostile SSH server
337  * doing malicious keyboard-interactive.
338  */
339 static void console_open(FILE **outfp, int *infd)
340 {
341     int fd;
342
343     if ((fd = open("/dev/tty", O_RDWR)) >= 0) {
344         *infd = fd;
345         *outfp = fdopen(*infd, "w");
346     } else {
347         *infd = 0;
348         *outfp = stderr;
349     }
350 }
351 static void console_close(FILE *outfp, int infd)
352 {
353     if (outfp != stderr)
354         fclose(outfp);             /* will automatically close infd too */
355 }
356
357 static void console_prompt_text(FILE *outfp, const char *data, int len)
358 {
359     int i;
360
361     for (i = 0; i < len; i++)
362         if ((data[i] & 0x60) || (data[i] == '\n'))
363             fputc(data[i], outfp);
364     fflush(outfp);
365 }
366
367 int console_get_userpass_input(prompts_t *p, const unsigned char *in,
368                                int inlen)
369 {
370     size_t curr_prompt;
371     FILE *outfp = NULL;
372     int infd;
373
374     /*
375      * Zero all the results, in case we abort half-way through.
376      */
377     {
378         int i;
379         for (i = 0; i < p->n_prompts; i++)
380             prompt_set_result(p->prompts[i], "");
381     }
382
383     if (p->n_prompts && console_batch_mode)
384         return 0;
385
386     console_open(&outfp, &infd);
387
388     /*
389      * Preamble.
390      */
391     /* We only print the `name' caption if we have to... */
392     if (p->name_reqd && p->name) {
393         size_t l = strlen(p->name);
394         console_prompt_text(outfp, p->name, l);
395         if (p->name[l-1] != '\n')
396             console_prompt_text(outfp, "\n", 1);
397     }
398     /* ...but we always print any `instruction'. */
399     if (p->instruction) {
400         size_t l = strlen(p->instruction);
401         console_prompt_text(outfp, p->instruction, l);
402         if (p->instruction[l-1] != '\n')
403             console_prompt_text(outfp, "\n", 1);
404     }
405
406     for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) {
407
408         struct termios oldmode, newmode;
409         int len;
410         prompt_t *pr = p->prompts[curr_prompt];
411
412         tcgetattr(infd, &oldmode);
413         newmode = oldmode;
414         newmode.c_lflag |= ISIG | ICANON;
415         if (!pr->echo)
416             newmode.c_lflag &= ~ECHO;
417         else
418             newmode.c_lflag |= ECHO;
419         tcsetattr(infd, TCSANOW, &newmode);
420
421         console_prompt_text(outfp, pr->prompt, strlen(pr->prompt));
422
423         len = 0;
424         while (1) {
425             int ret;
426
427             prompt_ensure_result_size(pr, len * 5 / 4 + 512);
428             ret = read(infd, pr->result + len, pr->resultsize - len - 1);
429             if (ret <= 0) {
430                 len = -1;
431                 break;
432             }
433             len += ret;
434             if (pr->result[len - 1] == '\n') {
435                 len--;
436                 break;
437             }
438         }
439
440         tcsetattr(infd, TCSANOW, &oldmode);
441
442         if (!pr->echo)
443             console_prompt_text(outfp, "\n", 1);
444
445         if (len < 0) {
446             console_close(outfp, infd);
447             return 0;                  /* failure due to read error */
448         }
449
450         pr->result[len] = '\0';
451     }
452
453     console_close(outfp, infd);
454
455     return 1; /* success */
456 }
457
458 void frontend_keypress(void *handle)
459 {
460     /*
461      * This is nothing but a stub, in console code.
462      */
463     return;
464 }
465
466 int is_interactive(void)
467 {
468     return isatty(0);
469 }
470
471 /*
472  * X11-forwarding-related things suitable for console.
473  */
474
475 char *platform_get_x_display(void) {
476     return dupstr(getenv("DISPLAY"));
477 }