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