winmisc.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  1. /*
  2. * winmisc.c: miscellaneous Windows-specific things
  3. */
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <limits.h>
  7. #include "putty.h"
  8. #ifndef SECURITY_WIN32
  9. #define SECURITY_WIN32
  10. #endif
  11. #include <security.h>
  12. #ifdef MPEXT
  13. #include <assert.h>
  14. #endif
  15. DWORD osMajorVersion, osMinorVersion, osPlatformId;
  16. char *platform_get_x_display(void) {
  17. /* We may as well check for DISPLAY in case it's useful. */
  18. return dupstr(getenv("DISPLAY"));
  19. }
  20. Filename *filename_from_str(const char *str)
  21. {
  22. Filename *ret = snew(Filename);
  23. ret->path = dupstr(str);
  24. return ret;
  25. }
  26. Filename *filename_copy(const Filename *fn)
  27. {
  28. return filename_from_str(fn->path);
  29. }
  30. #ifdef WINSCP
  31. const char* in_memory_key_data(const Filename *fn)
  32. {
  33. const char* result = fn->path;
  34. if (result[0] != '@')
  35. {
  36. result = NULL;
  37. }
  38. else
  39. {
  40. int len;
  41. result++;
  42. len = strlen(result);
  43. if (((len % 2) != 0) ||
  44. ((len / 2) < MAX_PATH))
  45. {
  46. result = NULL;
  47. }
  48. else
  49. {
  50. int i;
  51. for (i = 0; (result != NULL) && (i < len); i++)
  52. {
  53. if (!isxdigit(result[i]))
  54. {
  55. result = NULL;
  56. }
  57. }
  58. }
  59. }
  60. return result;
  61. }
  62. #endif
  63. const char *filename_to_str(const Filename *fn)
  64. {
  65. #ifdef WINSCP
  66. if (in_memory_key_data(fn) != NULL) return "in-memory";
  67. #endif
  68. return fn->path;
  69. }
  70. bool filename_equal(const Filename *f1, const Filename *f2)
  71. {
  72. return !strcmp(f1->path, f2->path);
  73. }
  74. bool filename_is_null(const Filename *fn)
  75. {
  76. return !*fn->path;
  77. }
  78. void filename_free(Filename *fn)
  79. {
  80. sfree(fn->path);
  81. sfree(fn);
  82. }
  83. void filename_serialise(BinarySink *bs, const Filename *f)
  84. {
  85. put_asciz(bs, f->path);
  86. }
  87. Filename *filename_deserialise(BinarySource *src)
  88. {
  89. return filename_from_str(get_asciz(src));
  90. }
  91. char filename_char_sanitise(char c)
  92. {
  93. if (strchr("<>:\"/\\|?*", c))
  94. return '.';
  95. return c;
  96. }
  97. #ifdef MPEXT
  98. FILE * mp_wfopen(const char *filename, const char *mode)
  99. {
  100. size_t len = strlen(filename);
  101. wchar_t * wfilename = snewn(len * 10, wchar_t);
  102. size_t wlen = MultiByteToWideChar(CP_UTF8, 0, filename, -1, wfilename, len * 10);
  103. FILE * file;
  104. if (wlen <= 0)
  105. {
  106. file = NULL;
  107. }
  108. else
  109. {
  110. wchar_t wmode[3];
  111. memset(wmode, 0, sizeof(wmode));
  112. wmode[0] = (wchar_t)mode[0];
  113. if (mode[0] != '\0')
  114. {
  115. wmode[1] = (wchar_t)mode[1];
  116. if (mode[1] != '\0')
  117. {
  118. assert(mode[2] == '\0');
  119. }
  120. }
  121. file = _wfopen(wfilename, wmode);
  122. }
  123. sfree(wfilename);
  124. return file;
  125. }
  126. #endif
  127. char *get_username(void)
  128. {
  129. DWORD namelen;
  130. char *user;
  131. bool got_username = false;
  132. DECL_WINDOWS_FUNCTION(static, BOOLEAN, GetUserNameExA,
  133. (EXTENDED_NAME_FORMAT, LPSTR, PULONG));
  134. {
  135. static bool tried_usernameex = false;
  136. if (!tried_usernameex) {
  137. /* Not available on Win9x, so load dynamically */
  138. HMODULE secur32 = load_system32_dll("secur32.dll");
  139. /* If MIT Kerberos is installed, the following call to
  140. GET_WINDOWS_FUNCTION makes Windows implicitly load
  141. sspicli.dll WITHOUT proper path sanitizing, so better
  142. load it properly before */
  143. HMODULE sspicli = load_system32_dll("sspicli.dll");
  144. (void)sspicli; /* squash compiler warning about unused variable */
  145. GET_WINDOWS_FUNCTION(secur32, GetUserNameExA);
  146. tried_usernameex = true;
  147. }
  148. }
  149. if (p_GetUserNameExA) {
  150. /*
  151. * If available, use the principal -- this avoids the problem
  152. * that the local username is case-insensitive but Kerberos
  153. * usernames are case-sensitive.
  154. */
  155. /* Get the length */
  156. namelen = 0;
  157. (void) p_GetUserNameExA(NameUserPrincipal, NULL, &namelen);
  158. user = snewn(namelen, char);
  159. got_username = p_GetUserNameExA(NameUserPrincipal, user, &namelen);
  160. if (got_username) {
  161. char *p = strchr(user, '@');
  162. if (p) *p = 0;
  163. } else {
  164. sfree(user);
  165. }
  166. }
  167. if (!got_username) {
  168. /* Fall back to local user name */
  169. namelen = 0;
  170. if (!GetUserName(NULL, &namelen)) {
  171. /*
  172. * Apparently this doesn't work at least on Windows XP SP2.
  173. * Thus assume a maximum of 256. It will fail again if it
  174. * doesn't fit.
  175. */
  176. namelen = 256;
  177. }
  178. user = snewn(namelen, char);
  179. got_username = GetUserName(user, &namelen);
  180. if (!got_username) {
  181. sfree(user);
  182. }
  183. }
  184. return got_username ? user : NULL;
  185. }
  186. void dll_hijacking_protection(void)
  187. {
  188. /*
  189. * If the OS provides it, call SetDefaultDllDirectories() to
  190. * prevent DLLs from being loaded from the directory containing
  191. * our own binary, and instead only load from system32.
  192. *
  193. * This is a protection against hijacking attacks, if someone runs
  194. * PuTTY directly from their web browser's download directory
  195. * having previously been enticed into clicking on an unwise link
  196. * that downloaded a malicious DLL to the same directory under one
  197. * of various magic names that seem to be things that standard
  198. * Windows DLLs delegate to.
  199. *
  200. * It shouldn't break deliberate loading of user-provided DLLs
  201. * such as GSSAPI providers, because those are specified by their
  202. * full pathname by the user-provided configuration.
  203. */
  204. static HMODULE kernel32_module;
  205. DECL_WINDOWS_FUNCTION(static, BOOL, SetDefaultDllDirectories, (DWORD));
  206. if (!kernel32_module) {
  207. kernel32_module = load_system32_dll("kernel32.dll");
  208. #if (defined _MSC_VER && _MSC_VER < 1900) || defined WINSCP
  209. /* For older Visual Studio, this function isn't available in
  210. * the header files to type-check */
  211. GET_WINDOWS_FUNCTION_NO_TYPECHECK(
  212. kernel32_module, SetDefaultDllDirectories);
  213. #else
  214. GET_WINDOWS_FUNCTION(kernel32_module, SetDefaultDllDirectories);
  215. #endif
  216. }
  217. if (p_SetDefaultDllDirectories) {
  218. /* LOAD_LIBRARY_SEARCH_SYSTEM32 and explicitly specified
  219. * directories only */
  220. p_SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32 |
  221. LOAD_LIBRARY_SEARCH_USER_DIRS);
  222. }
  223. }
  224. void init_winver(void)
  225. {
  226. OSVERSIONINFO osVersion;
  227. static HMODULE kernel32_module;
  228. DECL_WINDOWS_FUNCTION(static, BOOL, GetVersionExA, (LPOSVERSIONINFO));
  229. if (!kernel32_module) {
  230. kernel32_module = load_system32_dll("kernel32.dll");
  231. /* Deliberately don't type-check this function, because that
  232. * would involve using its declaration in a header file which
  233. * triggers a deprecation warning. I know it's deprecated (see
  234. * below) and don't need telling. */
  235. GET_WINDOWS_FUNCTION_NO_TYPECHECK(kernel32_module, GetVersionExA);
  236. }
  237. ZeroMemory(&osVersion, sizeof(osVersion));
  238. osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
  239. if (p_GetVersionExA && p_GetVersionExA(&osVersion)) {
  240. osMajorVersion = osVersion.dwMajorVersion;
  241. osMinorVersion = osVersion.dwMinorVersion;
  242. osPlatformId = osVersion.dwPlatformId;
  243. } else {
  244. /*
  245. * GetVersionEx is deprecated, so allow for it perhaps going
  246. * away in future API versions. If it's not there, simply
  247. * assume that's because Windows is too _new_, so fill in the
  248. * variables we care about to a value that will always compare
  249. * higher than any given test threshold.
  250. *
  251. * Normally we should be checking against the presence of a
  252. * specific function if possible in any case.
  253. */
  254. osMajorVersion = osMinorVersion = UINT_MAX; /* a very high number */
  255. osPlatformId = VER_PLATFORM_WIN32_NT; /* not Win32s or Win95-like */
  256. }
  257. }
  258. #ifdef MPEXT
  259. static char *sysdir = NULL;
  260. void win_misc_cleanup()
  261. {
  262. sfree(sysdir);
  263. }
  264. #endif
  265. HMODULE load_system32_dll(const char *libname)
  266. {
  267. /*
  268. * Wrapper function to load a DLL out of c:\windows\system32
  269. * without going through the full DLL search path. (Hence no
  270. * attack is possible by placing a substitute DLL earlier on that
  271. * path.)
  272. */
  273. #ifndef MPEXT
  274. static char *sysdir = NULL;
  275. #endif
  276. static size_t sysdirsize = 0;
  277. char *fullpath;
  278. HMODULE ret;
  279. if (!sysdir) {
  280. size_t len;
  281. while ((len = GetSystemDirectory(sysdir, sysdirsize)) >= sysdirsize)
  282. sgrowarray(sysdir, sysdirsize, len);
  283. }
  284. fullpath = dupcat(sysdir, "\\", libname);
  285. ret = LoadLibrary(fullpath);
  286. sfree(fullpath);
  287. return ret;
  288. }
  289. /*
  290. * A tree234 containing mappings from system error codes to strings.
  291. */
  292. struct errstring {
  293. int error;
  294. char *text;
  295. };
  296. static int errstring_find(void *av, void *bv)
  297. {
  298. int *a = (int *)av;
  299. struct errstring *b = (struct errstring *)bv;
  300. if (*a < b->error)
  301. return -1;
  302. if (*a > b->error)
  303. return +1;
  304. return 0;
  305. }
  306. static int errstring_compare(void *av, void *bv)
  307. {
  308. struct errstring *a = (struct errstring *)av;
  309. return errstring_find(&a->error, bv);
  310. }
  311. static tree234 *errstrings = NULL;
  312. const char *win_strerror(int error)
  313. {
  314. struct errstring *es;
  315. if (!errstrings)
  316. errstrings = newtree234(errstring_compare);
  317. es = find234(errstrings, &error, errstring_find);
  318. if (!es) {
  319. char msgtext[65536]; /* maximum size for FormatMessage is 64K */
  320. es = snew(struct errstring);
  321. es->error = error;
  322. if (!FormatMessage((FORMAT_MESSAGE_FROM_SYSTEM |
  323. FORMAT_MESSAGE_IGNORE_INSERTS), NULL, error,
  324. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  325. msgtext, lenof(msgtext)-1, NULL)) {
  326. sprintf(msgtext,
  327. "(unable to format: FormatMessage returned %u)",
  328. (unsigned int)GetLastError());
  329. } else {
  330. int len = strlen(msgtext);
  331. if (len > 0 && msgtext[len-1] == '\n')
  332. msgtext[len-1] = '\0';
  333. }
  334. es->text = dupprintf("Error %d: %s", error, msgtext);
  335. add234(errstrings, es);
  336. }
  337. return es->text;
  338. }
  339. FontSpec *fontspec_new(const char *name, bool bold, int height, int charset)
  340. {
  341. FontSpec *f = snew(FontSpec);
  342. f->name = dupstr(name);
  343. f->isbold = bold;
  344. f->height = height;
  345. f->charset = charset;
  346. return f;
  347. }
  348. FontSpec *fontspec_copy(const FontSpec *f)
  349. {
  350. return fontspec_new(f->name, f->isbold, f->height, f->charset);
  351. }
  352. void fontspec_free(FontSpec *f)
  353. {
  354. sfree(f->name);
  355. sfree(f);
  356. }
  357. void fontspec_serialise(BinarySink *bs, FontSpec *f)
  358. {
  359. put_asciz(bs, f->name);
  360. put_uint32(bs, f->isbold);
  361. put_uint32(bs, f->height);
  362. put_uint32(bs, f->charset);
  363. }
  364. FontSpec *fontspec_deserialise(BinarySource *src)
  365. {
  366. const char *name = get_asciz(src);
  367. unsigned isbold = get_uint32(src);
  368. unsigned height = get_uint32(src);
  369. unsigned charset = get_uint32(src);
  370. return fontspec_new(name, isbold, height, charset);
  371. }
  372. bool open_for_write_would_lose_data(const Filename *fn)
  373. {
  374. WIN32_FILE_ATTRIBUTE_DATA attrs;
  375. if (!GetFileAttributesEx(fn->path, GetFileExInfoStandard, &attrs)) {
  376. /*
  377. * Generally, if we don't identify a specific reason why we
  378. * should return true from this function, we return false, and
  379. * let the subsequent attempt to open the file for real give a
  380. * more useful error message.
  381. */
  382. return false;
  383. }
  384. if (attrs.dwFileAttributes & (FILE_ATTRIBUTE_DEVICE |
  385. FILE_ATTRIBUTE_DIRECTORY)) {
  386. /*
  387. * File is something other than an ordinary disk file, so
  388. * opening it for writing will not cause truncation. (It may
  389. * not _succeed_ either, but that's not our problem here!)
  390. */
  391. return false;
  392. }
  393. if (attrs.nFileSizeHigh == 0 && attrs.nFileSizeLow == 0) {
  394. /*
  395. * File is zero-length (or may be a named pipe, which
  396. * dwFileAttributes can't tell apart from a regular file), so
  397. * opening it for writing won't truncate any data away because
  398. * there's nothing to truncate anyway.
  399. */
  400. return false;
  401. }
  402. return true;
  403. }
  404. void escape_registry_key(const char *in, strbuf *out)
  405. {
  406. bool candot = false;
  407. static const char hex[16] = "0123456789ABCDEF";
  408. while (*in) {
  409. if (*in == ' ' || *in == '\\' || *in == '*' || *in == '?' ||
  410. *in == '%' || *in < ' ' || *in > '~' || (*in == '.'
  411. && !candot)) {
  412. put_byte(out, '%');
  413. put_byte(out, hex[((unsigned char) *in) >> 4]);
  414. put_byte(out, hex[((unsigned char) *in) & 15]);
  415. } else
  416. put_byte(out, *in);
  417. in++;
  418. candot = true;
  419. }
  420. }
  421. void unescape_registry_key(const char *in, strbuf *out)
  422. {
  423. while (*in) {
  424. if (*in == '%' && in[1] && in[2]) {
  425. int i, j;
  426. i = in[1] - '0';
  427. i -= (i > 9 ? 7 : 0);
  428. j = in[2] - '0';
  429. j -= (j > 9 ? 7 : 0);
  430. put_byte(out, (i << 4) + j);
  431. in += 3;
  432. } else {
  433. put_byte(out, *in++);
  434. }
  435. }
  436. }
  437. #ifdef DEBUG
  438. static FILE *debug_fp = NULL;
  439. static HANDLE debug_hdl = INVALID_HANDLE_VALUE;
  440. static int debug_got_console = 0;
  441. void dputs(const char *buf)
  442. {
  443. DWORD dw;
  444. if (!debug_got_console) {
  445. if (AllocConsole()) {
  446. debug_got_console = 1;
  447. debug_hdl = GetStdHandle(STD_OUTPUT_HANDLE);
  448. }
  449. }
  450. if (!debug_fp) {
  451. debug_fp = fopen("debug.log", "w");
  452. }
  453. if (debug_hdl != INVALID_HANDLE_VALUE) {
  454. WriteFile(debug_hdl, buf, strlen(buf), &dw, NULL);
  455. }
  456. fputs(buf, debug_fp);
  457. fflush(debug_fp);
  458. }
  459. #endif
  460. char *registry_get_string(HKEY root, const char *path, const char *leaf)
  461. {
  462. HKEY key = root;
  463. bool need_close_key = false;
  464. char *toret = NULL, *str = NULL;
  465. if (path) {
  466. if (RegCreateKey(key, path, &key) != ERROR_SUCCESS)
  467. goto out;
  468. need_close_key = true;
  469. }
  470. { // WINSCP
  471. DWORD type, size;
  472. if (RegQueryValueEx(key, leaf, 0, &type, NULL, &size) != ERROR_SUCCESS)
  473. goto out;
  474. if (type != REG_SZ)
  475. goto out;
  476. str = snewn(size + 1, char);
  477. { // WINSCP
  478. DWORD size_got = size;
  479. if (RegQueryValueEx(key, leaf, 0, &type, (LPBYTE)str,
  480. &size_got) != ERROR_SUCCESS)
  481. goto out;
  482. if (type != REG_SZ || size_got > size)
  483. goto out;
  484. str[size_got] = '\0';
  485. toret = str;
  486. str = NULL;
  487. out:
  488. if (need_close_key)
  489. RegCloseKey(key);
  490. sfree(str);
  491. return toret;
  492. } // WINSCP
  493. } // WINSCP
  494. }