1 /*
2 * Platform-independent routines shared between all PuTTY programs.
3 *
4 * This file contains functions that use the kind of infrastructure
5 * like conf.c that tends to only live in the main applications, or
6 * that do things that only something like a main PuTTY application
7 * would need. So standalone test programs should generally be able to
8 * avoid linking against it.
9 *
10 * More standalone functions that depend on nothing but the C library
11 * live in utils.c.
12 */
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <stdarg.h>
17 #include <limits.h>
18 #include <ctype.h>
19 #include <assert.h>
20
21 #include "defs.h"
22 #include "putty.h"
23 #include "misc.h"
24
25 #define BASE64_CHARS_NOEQ \
26 "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/"
27 #define BASE64_CHARS_ALL BASE64_CHARS_NOEQ "="
28
seat_connection_fatal(Seat * seat,const char * fmt,...)29 void seat_connection_fatal(Seat *seat, const char *fmt, ...)
30 {
31 va_list ap;
32 char *msg;
33
34 va_start(ap, fmt);
35 msg = dupvprintf(fmt, ap);
36 va_end(ap);
37
38 seat->vt->connection_fatal(seat, msg);
39 sfree(msg); /* if we return */
40 }
41
new_prompts(void)42 prompts_t *new_prompts(void)
43 {
44 prompts_t *p = snew(prompts_t);
45 p->prompts = NULL;
46 p->n_prompts = p->prompts_size = 0;
47 p->data = NULL;
48 p->to_server = true; /* to be on the safe side */
49 p->name = p->instruction = NULL;
50 p->name_reqd = p->instr_reqd = false;
51 return p;
52 }
add_prompt(prompts_t * p,char * promptstr,bool echo)53 void add_prompt(prompts_t *p, char *promptstr, bool echo)
54 {
55 prompt_t *pr = snew(prompt_t);
56 pr->prompt = promptstr;
57 pr->echo = echo;
58 pr->result = strbuf_new_nm();
59 sgrowarray(p->prompts, p->prompts_size, p->n_prompts);
60 p->prompts[p->n_prompts++] = pr;
61 }
prompt_set_result(prompt_t * pr,const char * newstr)62 void prompt_set_result(prompt_t *pr, const char *newstr)
63 {
64 strbuf_clear(pr->result);
65 put_datapl(pr->result, ptrlen_from_asciz(newstr));
66 }
prompt_get_result_ref(prompt_t * pr)67 const char *prompt_get_result_ref(prompt_t *pr)
68 {
69 return pr->result->s;
70 }
prompt_get_result(prompt_t * pr)71 char *prompt_get_result(prompt_t *pr)
72 {
73 return dupstr(pr->result->s);
74 }
free_prompts(prompts_t * p)75 void free_prompts(prompts_t *p)
76 {
77 size_t i;
78 for (i=0; i < p->n_prompts; i++) {
79 prompt_t *pr = p->prompts[i];
80 strbuf_free(pr->result);
81 sfree(pr->prompt);
82 sfree(pr);
83 }
84 sfree(p->prompts);
85 sfree(p->name);
86 sfree(p->instruction);
87 sfree(p);
88 }
89
90 /*
91 * Determine whether or not a Conf represents a session which can
92 * sensibly be launched right now.
93 */
conf_launchable(Conf * conf)94 bool conf_launchable(Conf *conf)
95 {
96 if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
97 return conf_get_str(conf, CONF_serline)[0] != 0;
98 else
99 return conf_get_str(conf, CONF_host)[0] != 0;
100 }
101
conf_dest(Conf * conf)102 char const *conf_dest(Conf *conf)
103 {
104 if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
105 return conf_get_str(conf, CONF_serline);
106 else
107 return conf_get_str(conf, CONF_host);
108 }
109
110 /*
111 * Validate a manual host key specification (either entered in the
112 * GUI, or via -hostkey). If valid, we return true, and update 'key'
113 * to contain a canonicalised version of the key string in 'key'
114 * (which is guaranteed to take up at most as much space as the
115 * original version), suitable for putting into the Conf. If not
116 * valid, we return false.
117 */
validate_manual_hostkey(char * key)118 bool validate_manual_hostkey(char *key)
119 {
120 char *p, *q, *r, *s;
121
122 /*
123 * Step through the string word by word, looking for a word that's
124 * in one of the formats we like.
125 */
126 p = key;
127 while ((p += strspn(p, " \t"))[0]) {
128 q = p;
129 p += strcspn(p, " \t");
130 if (*p) *p++ = '\0';
131
132 /*
133 * Now q is our word.
134 */
135
136 if (strstartswith(q, "SHA256:")) {
137 /* Test for a valid SHA256 key fingerprint. */
138 r = q + 7;
139 if (strlen(r) == 43 && r[strspn(r, BASE64_CHARS_NOEQ)] == 0)
140 return true;
141 }
142
143 r = q;
144 if (strstartswith(r, "MD5:"))
145 r += 4;
146 if (strlen(r) == 16*3 - 1 &&
147 r[strspn(r, "0123456789abcdefABCDEF:")] == 0) {
148 /*
149 * Test for a valid MD5 key fingerprint. Check the colons
150 * are in the right places, and if so, return the same
151 * fingerprint canonicalised into lowercase.
152 */
153 int i;
154 for (i = 0; i < 16; i++)
155 if (r[3*i] == ':' || r[3*i+1] == ':')
156 goto not_fingerprint; /* sorry */
157 for (i = 0; i < 15; i++)
158 if (r[3*i+2] != ':')
159 goto not_fingerprint; /* sorry */
160 for (i = 0; i < 16*3 - 1; i++)
161 key[i] = tolower(r[i]);
162 key[16*3 - 1] = '\0';
163 return true;
164 }
165 not_fingerprint:;
166
167 /*
168 * Before we check for a public-key blob, trim newlines out of
169 * the middle of the word, in case someone's managed to paste
170 * in a public-key blob _with_ them.
171 */
172 for (r = s = q; *r; r++)
173 if (*r != '\n' && *r != '\r')
174 *s++ = *r;
175 *s = '\0';
176
177 if (strlen(q) % 4 == 0 && strlen(q) > 2*4 &&
178 q[strspn(q, BASE64_CHARS_ALL)] == 0) {
179 /*
180 * Might be a base64-encoded SSH-2 public key blob. Check
181 * that it starts with a sensible algorithm string. No
182 * canonicalisation is necessary for this string type.
183 *
184 * The algorithm string must be at most 64 characters long
185 * (RFC 4251 section 6).
186 */
187 unsigned char decoded[6];
188 unsigned alglen;
189 int minlen;
190 int len = 0;
191
192 len += base64_decode_atom(q, decoded+len);
193 if (len < 3)
194 goto not_ssh2_blob; /* sorry */
195 len += base64_decode_atom(q+4, decoded+len);
196 if (len < 4)
197 goto not_ssh2_blob; /* sorry */
198
199 alglen = GET_32BIT_MSB_FIRST(decoded);
200 if (alglen > 64)
201 goto not_ssh2_blob; /* sorry */
202
203 minlen = ((alglen + 4) + 2) / 3;
204 if (strlen(q) < minlen)
205 goto not_ssh2_blob; /* sorry */
206
207 strcpy(key, q);
208 return true;
209 }
210 not_ssh2_blob:;
211 }
212
213 return false;
214 }
215
buildinfo(const char * newline)216 char *buildinfo(const char *newline)
217 {
218 strbuf *buf = strbuf_new();
219
220 strbuf_catf(buf, "Build platform: %d-bit %s",
221 (int)(CHAR_BIT * sizeof(void *)),
222 BUILDINFO_PLATFORM);
223
224 #ifdef __clang_version__
225 #define FOUND_COMPILER
226 strbuf_catf(buf, "%sCompiler: clang %s", newline, __clang_version__);
227 #elif defined __GNUC__ && defined __VERSION__
228 #define FOUND_COMPILER
229 strbuf_catf(buf, "%sCompiler: gcc %s", newline, __VERSION__);
230 #endif
231
232 #if defined _MSC_VER
233 #ifndef FOUND_COMPILER
234 #define FOUND_COMPILER
235 strbuf_catf(buf, "%sCompiler: ", newline);
236 #else
237 strbuf_catf(buf, ", emulating ");
238 #endif
239 strbuf_catf(buf, "Visual Studio");
240
241 #if 0
242 /*
243 * List of _MSC_VER values and their translations taken from
244 * https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros
245 *
246 * The pointless #if 0 branch containing this comment is there so
247 * that every real clause can start with #elif and there's no
248 * anomalous first clause. That way the patch looks nicer when you
249 * add extra ones.
250 */
251 #elif _MSC_VER == 1928 && _MSC_FULL_VER >= 192829500
252 /*
253 * 16.9 and 16.8 have the same _MSC_VER value, and have to be
254 * distinguished by _MSC_FULL_VER. As of 2021-03-04 that is not
255 * mentioned on the above page, but see e.g.
256 * https://developercommunity.visualstudio.com/t/the-169-cc-compiler-still-uses-the-same-version-nu/1335194#T-N1337120
257 * which says that 16.9 builds will have versions starting at
258 * 19.28.29500.* and going up. Hence, 19 28 29500 is what we
259 * compare _MSC_FULL_VER against above.
260 */
261 strbuf_catf(buf, " 2019 (16.9)");
262 #elif _MSC_VER == 1928
263 strbuf_catf(buf, " 2019 (16.8)");
264 #elif _MSC_VER == 1927
265 strbuf_catf(buf, " 2019 (16.7)");
266 #elif _MSC_VER == 1926
267 strbuf_catf(buf, " 2019 (16.6)");
268 #elif _MSC_VER == 1925
269 strbuf_catf(buf, " 2019 (16.5)");
270 #elif _MSC_VER == 1924
271 strbuf_catf(buf, " 2019 (16.4)");
272 #elif _MSC_VER == 1923
273 strbuf_catf(buf, " 2019 (16.3)");
274 #elif _MSC_VER == 1922
275 strbuf_catf(buf, " 2019 (16.2)");
276 #elif _MSC_VER == 1921
277 strbuf_catf(buf, " 2019 (16.1)");
278 #elif _MSC_VER == 1920
279 strbuf_catf(buf, " 2019 (16.0)");
280 #elif _MSC_VER == 1916
281 strbuf_catf(buf, " 2017 version 15.9");
282 #elif _MSC_VER == 1915
283 strbuf_catf(buf, " 2017 version 15.8");
284 #elif _MSC_VER == 1914
285 strbuf_catf(buf, " 2017 version 15.7");
286 #elif _MSC_VER == 1913
287 strbuf_catf(buf, " 2017 version 15.6");
288 #elif _MSC_VER == 1912
289 strbuf_catf(buf, " 2017 version 15.5");
290 #elif _MSC_VER == 1911
291 strbuf_catf(buf, " 2017 version 15.3");
292 #elif _MSC_VER == 1910
293 strbuf_catf(buf, " 2017 RTW (15.0)");
294 #elif _MSC_VER == 1900
295 strbuf_catf(buf, " 2015 (14.0)");
296 #elif _MSC_VER == 1800
297 strbuf_catf(buf, " 2013 (12.0)");
298 #elif _MSC_VER == 1700
299 strbuf_catf(buf, " 2012 (11.0)");
300 #elif _MSC_VER == 1600
301 strbuf_catf(buf, " 2010 (10.0)");
302 #elif _MSC_VER == 1500
303 strbuf_catf(buf, " 2008 (9.0)");
304 #elif _MSC_VER == 1400
305 strbuf_catf(buf, " 2005 (8.0)");
306 #elif _MSC_VER == 1310
307 strbuf_catf(buf, " .NET 2003 (7.1)");
308 #elif _MSC_VER == 1300
309 strbuf_catf(buf, " .NET 2002 (7.0)");
310 #elif _MSC_VER == 1200
311 strbuf_catf(buf, " 6.0");
312 #else
313 strbuf_catf(buf, ", unrecognised version");
314 #endif
315 strbuf_catf(buf, ", _MSC_VER=%d", (int)_MSC_VER);
316 #endif
317
318 #ifdef BUILDINFO_GTK
319 {
320 char *gtk_buildinfo = buildinfo_gtk_version();
321 if (gtk_buildinfo) {
322 strbuf_catf(buf, "%sCompiled against GTK version %s",
323 newline, gtk_buildinfo);
324 sfree(gtk_buildinfo);
325 }
326 }
327 #endif
328 #if defined _WINDOWS
329 {
330 int echm = has_embedded_chm();
331 if (echm >= 0)
332 strbuf_catf(buf, "%sEmbedded HTML Help file: %s", newline,
333 echm ? "yes" : "no");
334 }
335 #endif
336
337 #if defined _WINDOWS && defined MINEFIELD
338 strbuf_catf(buf, "%sBuild option: MINEFIELD", newline);
339 #endif
340 #ifdef NO_SECURITY
341 strbuf_catf(buf, "%sBuild option: NO_SECURITY", newline);
342 #endif
343 #ifdef NO_SECUREZEROMEMORY
344 strbuf_catf(buf, "%sBuild option: NO_SECUREZEROMEMORY", newline);
345 #endif
346 #ifdef NO_IPV6
347 strbuf_catf(buf, "%sBuild option: NO_IPV6", newline);
348 #endif
349 #ifdef NO_GSSAPI
350 strbuf_catf(buf, "%sBuild option: NO_GSSAPI", newline);
351 #endif
352 #ifdef STATIC_GSSAPI
353 strbuf_catf(buf, "%sBuild option: STATIC_GSSAPI", newline);
354 #endif
355 #ifdef UNPROTECT
356 strbuf_catf(buf, "%sBuild option: UNPROTECT", newline);
357 #endif
358 #ifdef FUZZING
359 strbuf_catf(buf, "%sBuild option: FUZZING", newline);
360 #endif
361 #ifdef DEBUG
362 strbuf_catf(buf, "%sBuild option: DEBUG", newline);
363 #endif
364
365 strbuf_catf(buf, "%sSource commit: %s", newline, commitid);
366
367 return strbuf_to_str(buf);
368 }
369
nullseat_output(Seat * seat,bool is_stderr,const void * data,size_t len)370 size_t nullseat_output(
371 Seat *seat, bool is_stderr, const void *data, size_t len) { return 0; }
nullseat_eof(Seat * seat)372 bool nullseat_eof(Seat *seat) { return true; }
nullseat_get_userpass_input(Seat * seat,prompts_t * p,bufchain * input)373 int nullseat_get_userpass_input(
374 Seat *seat, prompts_t *p, bufchain *input) { return 0; }
nullseat_notify_remote_exit(Seat * seat)375 void nullseat_notify_remote_exit(Seat *seat) {}
nullseat_connection_fatal(Seat * seat,const char * message)376 void nullseat_connection_fatal(Seat *seat, const char *message) {}
nullseat_update_specials_menu(Seat * seat)377 void nullseat_update_specials_menu(Seat *seat) {}
nullseat_get_ttymode(Seat * seat,const char * mode)378 char *nullseat_get_ttymode(Seat *seat, const char *mode) { return NULL; }
nullseat_set_busy_status(Seat * seat,BusyStatus status)379 void nullseat_set_busy_status(Seat *seat, BusyStatus status) {}
nullseat_verify_ssh_host_key(Seat * seat,const char * host,int port,const char * keytype,char * keystr,const char * keydisp,char ** key_fingerprints,void (* callback)(void * ctx,int result),void * ctx)380 int nullseat_verify_ssh_host_key(
381 Seat *seat, const char *host, int port, const char *keytype,
382 char *keystr, const char *keydisp, char **key_fingerprints,
383 void (*callback)(void *ctx, int result), void *ctx) { return 0; }
nullseat_confirm_weak_crypto_primitive(Seat * seat,const char * algtype,const char * algname,void (* callback)(void * ctx,int result),void * ctx)384 int nullseat_confirm_weak_crypto_primitive(
385 Seat *seat, const char *algtype, const char *algname,
386 void (*callback)(void *ctx, int result), void *ctx) { return 0; }
nullseat_confirm_weak_cached_hostkey(Seat * seat,const char * algname,const char * betteralgs,void (* callback)(void * ctx,int result),void * ctx)387 int nullseat_confirm_weak_cached_hostkey(
388 Seat *seat, const char *algname, const char *betteralgs,
389 void (*callback)(void *ctx, int result), void *ctx) { return 0; }
nullseat_is_never_utf8(Seat * seat)390 bool nullseat_is_never_utf8(Seat *seat) { return false; }
nullseat_is_always_utf8(Seat * seat)391 bool nullseat_is_always_utf8(Seat *seat) { return true; }
nullseat_echoedit_update(Seat * seat,bool echoing,bool editing)392 void nullseat_echoedit_update(Seat *seat, bool echoing, bool editing) {}
nullseat_get_x_display(Seat * seat)393 const char *nullseat_get_x_display(Seat *seat) { return NULL; }
nullseat_get_windowid(Seat * seat,long * id_out)394 bool nullseat_get_windowid(Seat *seat, long *id_out) { return false; }
nullseat_get_window_pixel_size(Seat * seat,int * width,int * height)395 bool nullseat_get_window_pixel_size(
396 Seat *seat, int *width, int *height) { return false; }
nullseat_stripctrl_new(Seat * seat,BinarySink * bs_out,SeatInteractionContext sic)397 StripCtrlChars *nullseat_stripctrl_new(
398 Seat *seat, BinarySink *bs_out, SeatInteractionContext sic) {return NULL;}
nullseat_set_trust_status(Seat * seat,bool tr)399 bool nullseat_set_trust_status(Seat *seat, bool tr) { return false; }
nullseat_set_trust_status_vacuously(Seat * seat,bool tr)400 bool nullseat_set_trust_status_vacuously(Seat *seat, bool tr) { return true; }
nullseat_verbose_no(Seat * seat)401 bool nullseat_verbose_no(Seat *seat) { return false; }
nullseat_verbose_yes(Seat * seat)402 bool nullseat_verbose_yes(Seat *seat) { return true; }
nullseat_interactive_no(Seat * seat)403 bool nullseat_interactive_no(Seat *seat) { return false; }
nullseat_interactive_yes(Seat * seat)404 bool nullseat_interactive_yes(Seat *seat) { return true; }
nullseat_get_cursor_position(Seat * seat,int * x,int * y)405 bool nullseat_get_cursor_position(Seat *seat, int *x, int *y) { return false; }
406
null_lp_verbose_no(LogPolicy * lp)407 bool null_lp_verbose_no(LogPolicy *lp) { return false; }
null_lp_verbose_yes(LogPolicy * lp)408 bool null_lp_verbose_yes(LogPolicy *lp) { return true; }
409
sk_free_peer_info(SocketPeerInfo * pi)410 void sk_free_peer_info(SocketPeerInfo *pi)
411 {
412 if (pi) {
413 sfree((char *)pi->addr_text);
414 sfree((char *)pi->log_text);
415 sfree(pi);
416 }
417 }
418
out_of_memory(void)419 void out_of_memory(void)
420 {
421 modalfatalbox("Out of memory");
422 }
423