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