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