]> asedeno.scripts.mit.edu Git - PuTTY_svn.git/blob - unix/uxcons.c
Simon suggests a better solution to valgrind's complaining about
[PuTTY_svn.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 /*
22  * Clean up and exit.
23  */
24 void cleanup_exit(int code)
25 {
26     /*
27      * Clean up.
28      */
29     sk_cleanup();
30     random_save_seed();
31     exit(code);
32 }
33
34 void set_busy_status(void *frontend, int status)
35 {
36 }
37
38 void update_specials_menu(void *frontend)
39 {
40 }
41
42 void notify_remote_exit(void *frontend)
43 {
44 }
45
46 void timer_change_notify(long next)
47 {
48 }
49
50 void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
51                          char *keystr, char *fingerprint)
52 {
53     int ret;
54
55     static const char absentmsg_batch[] =
56         "The server's host key is not cached. You have no guarantee\n"
57         "that the server is the computer you think it is.\n"
58         "The server's %s key fingerprint is:\n"
59         "%s\n"
60         "Connection abandoned.\n";
61     static const char absentmsg[] =
62         "The server's host key is not cached. You have no guarantee\n"
63         "that the server is the computer you think it is.\n"
64         "The server's %s key fingerprint is:\n"
65         "%s\n"
66         "If you trust this host, enter \"y\" to add the key to\n"
67         "PuTTY's cache and carry on connecting.\n"
68         "If you want to carry on connecting just once, without\n"
69         "adding the key to the cache, enter \"n\".\n"
70         "If you do not trust this host, press Return to abandon the\n"
71         "connection.\n"
72         "Store key in cache? (y/n) ";
73
74     static const char wrongmsg_batch[] =
75         "WARNING - POTENTIAL SECURITY BREACH!\n"
76         "The server's host key does not match the one PuTTY has\n"
77         "cached. This means that either the server administrator\n"
78         "has changed the host key, or you have actually connected\n"
79         "to another computer pretending to be the server.\n"
80         "The new %s key fingerprint is:\n"
81         "%s\n"
82         "Connection abandoned.\n";
83     static const char wrongmsg[] =
84         "WARNING - POTENTIAL SECURITY BREACH!\n"
85         "The server's host key does not match the one PuTTY has\n"
86         "cached. This means that either the server administrator\n"
87         "has changed the host key, or you have actually connected\n"
88         "to another computer pretending 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.
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     {
130         struct termios oldmode, newmode;
131         tcgetattr(0, &oldmode);
132         newmode = oldmode;
133         newmode.c_lflag |= ECHO | ISIG | ICANON;
134         tcsetattr(0, TCSANOW, &newmode);
135         line[0] = '\0';
136         read(0, line, sizeof(line) - 1);
137         tcsetattr(0, TCSANOW, &oldmode);
138     }
139
140     if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') {
141         if (line[0] == 'y' || line[0] == 'Y')
142             store_host_key(host, port, keytype, keystr);
143     } else {
144         fprintf(stderr, abandoned);
145         cleanup_exit(0);
146     }
147 }
148
149 /*
150  * Ask whether the selected algorithm is acceptable (since it was
151  * below the configured 'warn' threshold).
152  */
153 void askalg(void *frontend, const char *algtype, const char *algname)
154 {
155     static const char msg[] =
156         "The first %s supported by the server is\n"
157         "%s, which is below the configured warning threshold.\n"
158         "Continue with connection? (y/n) ";
159     static const char msg_batch[] =
160         "The first %s supported by the server is\n"
161         "%s, which is below the configured warning threshold.\n"
162         "Connection abandoned.\n";
163     static const char abandoned[] = "Connection abandoned.\n";
164
165     char line[32];
166
167     if (console_batch_mode) {
168         fprintf(stderr, msg_batch, algtype, algname);
169         cleanup_exit(1);
170     }
171
172     fprintf(stderr, msg, algtype, algname);
173     fflush(stderr);
174
175     {
176         struct termios oldmode, newmode;
177         tcgetattr(0, &oldmode);
178         newmode = oldmode;
179         newmode.c_lflag |= ECHO | ISIG | ICANON;
180         tcsetattr(0, TCSANOW, &newmode);
181         line[0] = '\0';
182         read(0, line, sizeof(line) - 1);
183         tcsetattr(0, TCSANOW, &oldmode);
184     }
185
186     if (line[0] == 'y' || line[0] == 'Y') {
187         return;
188     } else {
189         fprintf(stderr, abandoned);
190         cleanup_exit(0);
191     }
192 }
193
194 /*
195  * Ask whether to wipe a session log file before writing to it.
196  * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
197  */
198 int askappend(void *frontend, Filename filename)
199 {
200     static const char msgtemplate[] =
201         "The session log file \"%.*s\" already exists.\n"
202         "You can overwrite it with a new session log,\n"
203         "append your session log to the end of it,\n"
204         "or disable session logging for this session.\n"
205         "Enter \"y\" to wipe the file, \"n\" to append to it,\n"
206         "or just press Return to disable logging.\n"
207         "Wipe the log file? (y/n, Return cancels logging) ";
208
209     static const char msgtemplate_batch[] =
210         "The session log file \"%.*s\" already exists.\n"
211         "Logging will not be enabled.\n";
212
213     char line[32];
214
215     if (console_batch_mode) {
216         fprintf(stderr, msgtemplate_batch, FILENAME_MAX, filename.path);
217         fflush(stderr);
218         return 0;
219     }
220     fprintf(stderr, msgtemplate, FILENAME_MAX, filename.path);
221     fflush(stderr);
222
223     {
224         struct termios oldmode, newmode;
225         tcgetattr(0, &oldmode);
226         newmode = oldmode;
227         newmode.c_lflag |= ECHO | ISIG | ICANON;
228         tcsetattr(0, TCSANOW, &newmode);
229         line[0] = '\0';
230         read(0, line, sizeof(line) - 1);
231         tcsetattr(0, TCSANOW, &oldmode);
232     }
233
234     if (line[0] == 'y' || line[0] == 'Y')
235         return 2;
236     else if (line[0] == 'n' || line[0] == 'N')
237         return 1;
238     else
239         return 0;
240 }
241
242 /*
243  * Warn about the obsolescent key file format.
244  * 
245  * Uniquely among these functions, this one does _not_ expect a
246  * frontend handle. This means that if PuTTY is ported to a
247  * platform which requires frontend handles, this function will be
248  * an anomaly. Fortunately, the problem it addresses will not have
249  * been present on that platform, so it can plausibly be
250  * implemented as an empty function.
251  */
252 void old_keyfile_warning(void)
253 {
254     static const char message[] =
255         "You are loading an SSH 2 private key which has an\n"
256         "old version of the file format. This means your key\n"
257         "file is not fully tamperproof. Future versions of\n"
258         "PuTTY may stop supporting this private key format,\n"
259         "so we recommend you convert your key to the new\n"
260         "format.\n"
261         "\n"
262         "Once the key is loaded into PuTTYgen, you can perform\n"
263         "this conversion simply by saving it again.\n";
264
265     fputs(message, stderr);
266 }
267
268 void console_provide_logctx(void *logctx)
269 {
270     console_logctx = logctx;
271 }
272
273 void logevent(void *frontend, const char *string)
274 {
275     if (console_logctx)
276         log_eventlog(console_logctx, string);
277 }
278
279 int console_get_line(const char *prompt, char *str,
280                      int maxlen, int is_pw)
281 {
282     struct termios oldmode, newmode;
283     int i;
284
285     if (console_batch_mode) {
286         if (maxlen > 0)
287             str[0] = '\0';
288         return 0;
289     } else {
290         tcgetattr(0, &oldmode);
291         newmode = oldmode;
292         newmode.c_lflag |= ISIG | ICANON;
293         if (is_pw)
294             newmode.c_lflag &= ~ECHO;
295         else
296             newmode.c_lflag |= ECHO;
297         tcsetattr(0, TCSANOW, &newmode);
298
299         fputs(prompt, stdout);
300         fflush(stdout);
301         i = read(0, str, maxlen - 1);
302
303         tcsetattr(0, TCSANOW, &oldmode);
304
305         if (i > 0 && str[i-1] == '\n')
306             i--;
307         str[i] = '\0';
308
309         if (is_pw)
310             fputs("\n", stdout);
311
312         return 1;
313     }
314 }
315
316 void frontend_keypress(void *handle)
317 {
318     /*
319      * This is nothing but a stub, in console code.
320      */
321     return;
322 }
323
324 int is_interactive(void)
325 {
326     return isatty(0);
327 }
328
329 /*
330  * X11-forwarding-related things suitable for console.
331  */
332
333 const char platform_x11_best_transport[] = "unix";
334
335 char *platform_get_x_display(void) {
336     return dupstr(getenv("DISPLAY"));
337 }