Create the long-awaited console.c, and move the common routines out
[u/mdw/putty] / console.c
1 /*
2 * console.c: various interactive-prompt routines shared between
3 * the console PuTTY tools
4 */
5
6 #include <windows.h>
7
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <stdarg.h>
11
12 #include "putty.h"
13 #include "storage.h"
14 #include "ssh.h"
15
16 int console_batch_mode = FALSE;
17
18 void verify_ssh_host_key(char *host, int port, char *keytype,
19 char *keystr, char *fingerprint)
20 {
21 int ret;
22 HANDLE hin;
23 DWORD savemode, i;
24
25 static const char absentmsg_batch[] =
26 "The server's host key is not cached in the registry. You\n"
27 "have no guarantee that the server is the computer you\n"
28 "think it is.\n"
29 "The server's key fingerprint is:\n"
30 "%s\n"
31 "Connection abandoned.\n";
32 static const char absentmsg[] =
33 "The server's host key is not cached in the registry. You\n"
34 "have no guarantee that the server is the computer you\n"
35 "think it is.\n"
36 "The server's key fingerprint is:\n"
37 "%s\n"
38 "If you trust this host, enter \"y\" to add the key to\n"
39 "PuTTY's cache and carry on connecting.\n"
40 "If you want to carry on connecting just once, without\n"
41 "adding the key to the cache, enter \"n\".\n"
42 "If you do not trust this host, press Return to abandon the\n"
43 "connection.\n"
44 "Store key in cache? (y/n) ";
45
46 static const char wrongmsg_batch[] =
47 "WARNING - POTENTIAL SECURITY BREACH!\n"
48 "The server's host key does not match the one PuTTY has\n"
49 "cached in the registry. This means that either the\n"
50 "server administrator has changed the host key, or you\n"
51 "have actually connected to another computer pretending\n"
52 "to be the server.\n"
53 "The new key fingerprint is:\n"
54 "%s\n"
55 "Connection abandoned.\n";
56 static const char wrongmsg[] =
57 "WARNING - POTENTIAL SECURITY BREACH!\n"
58 "The server's host key does not match the one PuTTY has\n"
59 "cached in the registry. This means that either the\n"
60 "server administrator has changed the host key, or you\n"
61 "have actually connected to another computer pretending\n"
62 "to be the server.\n"
63 "The new key fingerprint is:\n"
64 "%s\n"
65 "If you were expecting this change and trust the new key,\n"
66 "enter \"y\" to update PuTTY's cache and continue connecting.\n"
67 "If you want to carry on connecting but without updating\n"
68 "the cache, enter \"n\".\n"
69 "If you want to abandon the connection completely, press\n"
70 "Return to cancel. Pressing Return is the ONLY guaranteed\n"
71 "safe choice.\n"
72 "Update cached key? (y/n, Return cancels connection) ";
73
74 static const char abandoned[] = "Connection abandoned.\n";
75
76 char line[32];
77
78 /*
79 * Verify the key against the registry.
80 */
81 ret = verify_host_key(host, port, keytype, keystr);
82
83 if (ret == 0) /* success - key matched OK */
84 return;
85
86 if (ret == 2) { /* key was different */
87 if (console_batch_mode) {
88 fprintf(stderr, wrongmsg_batch, fingerprint);
89 exit(1);
90 }
91 fprintf(stderr, wrongmsg, fingerprint);
92 fflush(stderr);
93 }
94 if (ret == 1) { /* key was absent */
95 if (console_batch_mode) {
96 fprintf(stderr, absentmsg_batch, fingerprint);
97 exit(1);
98 }
99 fprintf(stderr, absentmsg, fingerprint);
100 fflush(stderr);
101 }
102
103 hin = GetStdHandle(STD_INPUT_HANDLE);
104 GetConsoleMode(hin, &savemode);
105 SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |
106 ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));
107 ReadFile(hin, line, sizeof(line) - 1, &i, NULL);
108 SetConsoleMode(hin, savemode);
109
110 if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') {
111 if (line[0] == 'y' || line[0] == 'Y')
112 store_host_key(host, port, keytype, keystr);
113 } else {
114 fprintf(stderr, abandoned);
115 exit(0);
116 }
117 }
118
119 /*
120 * Ask whether the selected cipher is acceptable (since it was
121 * below the configured 'warn' threshold).
122 * cs: 0 = both ways, 1 = client->server, 2 = server->client
123 */
124 void askcipher(char *ciphername, int cs)
125 {
126 HANDLE hin;
127 DWORD savemode, i;
128
129 static const char msg[] =
130 "The first %scipher supported by the server is\n"
131 "%s, which is below the configured warning threshold.\n"
132 "Continue with connection? (y/n) ";
133 static const char msg_batch[] =
134 "The first %scipher supported by the server is\n"
135 "%s, which is below the configured warning threshold.\n"
136 "Connection abandoned.\n";
137 static const char abandoned[] = "Connection abandoned.\n";
138
139 char line[32];
140
141 if (console_batch_mode) {
142 fprintf(stderr, msg_batch,
143 (cs == 0) ? "" :
144 (cs == 1) ? "client-to-server " : "server-to-client ",
145 ciphername);
146 exit(1);
147 }
148
149 fprintf(stderr, msg,
150 (cs == 0) ? "" :
151 (cs == 1) ? "client-to-server " : "server-to-client ",
152 ciphername);
153 fflush(stderr);
154
155 hin = GetStdHandle(STD_INPUT_HANDLE);
156 GetConsoleMode(hin, &savemode);
157 SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |
158 ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));
159 ReadFile(hin, line, sizeof(line) - 1, &i, NULL);
160 SetConsoleMode(hin, savemode);
161
162 if (line[0] == 'y' || line[0] == 'Y') {
163 return;
164 } else {
165 fprintf(stderr, abandoned);
166 exit(0);
167 }
168 }
169
170 /*
171 * Ask whether to wipe a session log file before writing to it.
172 * Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
173 */
174 int askappend(char *filename)
175 {
176 HANDLE hin;
177 DWORD savemode, i;
178
179 static const char msgtemplate[] =
180 "The session log file \"%.*s\" already exists.\n"
181 "You can overwrite it with a new session log,\n"
182 "append your session log to the end of it,\n"
183 "or disable session logging for this session.\n"
184 "Enter \"y\" to wipe the file, \"n\" to append to it,\n"
185 "or just press Return to disable logging.\n"
186 "Wipe the log file? (y/n, Return cancels logging) ";
187
188 static const char msgtemplate_batch[] =
189 "The session log file \"%.*s\" already exists.\n"
190 "Logging will not be enabled.\n";
191
192 char line[32];
193
194 if (console_batch_mode) {
195 fprintf(stderr, msgtemplate_batch, FILENAME_MAX, filename);
196 fflush(stderr);
197 return 0;
198 }
199 fprintf(stderr, msgtemplate, FILENAME_MAX, filename);
200 fflush(stderr);
201
202 hin = GetStdHandle(STD_INPUT_HANDLE);
203 GetConsoleMode(hin, &savemode);
204 SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |
205 ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));
206 ReadFile(hin, line, sizeof(line) - 1, &i, NULL);
207 SetConsoleMode(hin, savemode);
208
209 if (line[0] == 'y' || line[0] == 'Y')
210 return 2;
211 else if (line[0] == 'n' || line[0] == 'N')
212 return 1;
213 else
214 return 0;
215 }
216
217 /*
218 * Warn about the obsolescent key file format.
219 */
220 void old_keyfile_warning(void)
221 {
222 static const char message[] =
223 "You are loading an SSH 2 private key which has an\n"
224 "old version of the file format. This means your key\n"
225 "file is not fully tamperproof. Future versions of\n"
226 "PuTTY may stop supporting this private key format,\n"
227 "so we recommend you convert your key to the new\n"
228 "format.\n"
229 "\n"
230 "Once the key is loaded into PuTTYgen, you can perform\n"
231 "this conversion simply by saving it again.\n";
232
233 fputs(message, stderr);
234 }
235
236 void logevent(char *string)
237 {
238 }
239
240 char *console_password = NULL;
241
242 int console_get_line(const char *prompt, char *str,
243 int maxlen, int is_pw)
244 {
245 HANDLE hin, hout;
246 DWORD savemode, newmode, i;
247
248 if (is_pw && console_password) {
249 static int tried_once = 0;
250
251 if (tried_once) {
252 return 0;
253 } else {
254 strncpy(str, console_password, maxlen);
255 str[maxlen - 1] = '\0';
256 tried_once = 1;
257 return 1;
258 }
259 }
260
261 if (console_batch_mode) {
262 if (maxlen > 0)
263 str[0] = '\0';
264 } else {
265 hin = GetStdHandle(STD_INPUT_HANDLE);
266 hout = GetStdHandle(STD_OUTPUT_HANDLE);
267 if (hin == INVALID_HANDLE_VALUE || hout == INVALID_HANDLE_VALUE) {
268 fprintf(stderr, "Cannot get standard input/output handles\n");
269 exit(1);
270 }
271
272 GetConsoleMode(hin, &savemode);
273 newmode = savemode | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT;
274 if (is_pw)
275 newmode &= ~ENABLE_ECHO_INPUT;
276 else
277 newmode |= ENABLE_ECHO_INPUT;
278 SetConsoleMode(hin, newmode);
279
280 WriteFile(hout, prompt, strlen(prompt), &i, NULL);
281 ReadFile(hin, str, maxlen - 1, &i, NULL);
282
283 SetConsoleMode(hin, savemode);
284
285 if ((int) i > maxlen)
286 i = maxlen - 1;
287 else
288 i = i - 2;
289 str[i] = '\0';
290
291 if (is_pw)
292 WriteFile(hout, "\r\n", 2, &i, NULL);
293
294 }
295 return 1;
296 }