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