2021f40ebf5a30d2ca441d67b0ad910ff9552c2f
[u/mdw/putty] / 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 (p->n_prompts && 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 char *platform_get_x_display(void) {
420 return dupstr(getenv("DISPLAY"));
421 }