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