winmisc.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676
  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. const char *filename_to_str(const Filename *fn)
  31. {
  32. return fn->path;
  33. }
  34. int filename_equal(const Filename *f1, const Filename *f2)
  35. {
  36. return !strcmp(f1->path, f2->path);
  37. }
  38. int filename_is_null(const Filename *fn)
  39. {
  40. return !*fn->path;
  41. }
  42. void filename_free(Filename *fn)
  43. {
  44. sfree(fn->path);
  45. sfree(fn);
  46. }
  47. void filename_serialise(BinarySink *bs, const Filename *f)
  48. {
  49. put_asciz(bs, f->path);
  50. }
  51. Filename *filename_deserialise(BinarySource *src)
  52. {
  53. return filename_from_str(get_asciz(src));
  54. }
  55. char filename_char_sanitise(char c)
  56. {
  57. if (strchr("<>:\"/\\|?*", c))
  58. return '.';
  59. return c;
  60. }
  61. #ifdef MPEXT
  62. FILE * mp_wfopen(const char *filename, const char *mode)
  63. {
  64. size_t len = strlen(filename);
  65. wchar_t * wfilename = snewn(len * 10, wchar_t);
  66. size_t wlen = MultiByteToWideChar(CP_UTF8, 0, filename, -1, wfilename, len * 10);
  67. FILE * file;
  68. if (wlen <= 0)
  69. {
  70. file = NULL;
  71. }
  72. else
  73. {
  74. wchar_t wmode[3];
  75. memset(wmode, 0, sizeof(wmode));
  76. wmode[0] = (wchar_t)mode[0];
  77. if (mode[0] != '\0')
  78. {
  79. wmode[1] = (wchar_t)mode[1];
  80. if (mode[1] != '\0')
  81. {
  82. assert(mode[2] == '\0');
  83. }
  84. }
  85. file = _wfopen(wfilename, wmode);
  86. }
  87. sfree(wfilename);
  88. return file;
  89. }
  90. #endif
  91. #ifndef NO_SECUREZEROMEMORY
  92. /*
  93. * Windows implementation of smemclr (see misc.c) using SecureZeroMemory.
  94. */
  95. void smemclr(void *b, size_t n) {
  96. if (b && n > 0)
  97. SecureZeroMemory(b, n);
  98. }
  99. #endif
  100. char *get_username(void)
  101. {
  102. DWORD namelen;
  103. char *user;
  104. int got_username = FALSE;
  105. DECL_WINDOWS_FUNCTION(static, BOOLEAN, GetUserNameExA,
  106. (EXTENDED_NAME_FORMAT, LPSTR, PULONG));
  107. {
  108. static int tried_usernameex = FALSE;
  109. if (!tried_usernameex) {
  110. /* Not available on Win9x, so load dynamically */
  111. HMODULE secur32 = load_system32_dll("secur32.dll");
  112. /* If MIT Kerberos is installed, the following call to
  113. GET_WINDOWS_FUNCTION makes Windows implicitly load
  114. sspicli.dll WITHOUT proper path sanitizing, so better
  115. load it properly before */
  116. HMODULE sspicli = load_system32_dll("sspicli.dll");
  117. (void)sspicli; /* squash compiler warning about unused variable */
  118. GET_WINDOWS_FUNCTION(secur32, GetUserNameExA);
  119. tried_usernameex = TRUE;
  120. }
  121. }
  122. if (p_GetUserNameExA) {
  123. /*
  124. * If available, use the principal -- this avoids the problem
  125. * that the local username is case-insensitive but Kerberos
  126. * usernames are case-sensitive.
  127. */
  128. /* Get the length */
  129. namelen = 0;
  130. (void) p_GetUserNameExA(NameUserPrincipal, NULL, &namelen);
  131. user = snewn(namelen, char);
  132. got_username = p_GetUserNameExA(NameUserPrincipal, user, &namelen);
  133. if (got_username) {
  134. char *p = strchr(user, '@');
  135. if (p) *p = 0;
  136. } else {
  137. sfree(user);
  138. }
  139. }
  140. if (!got_username) {
  141. /* Fall back to local user name */
  142. namelen = 0;
  143. if (GetUserName(NULL, &namelen) == FALSE) {
  144. /*
  145. * Apparently this doesn't work at least on Windows XP SP2.
  146. * Thus assume a maximum of 256. It will fail again if it
  147. * doesn't fit.
  148. */
  149. namelen = 256;
  150. }
  151. user = snewn(namelen, char);
  152. got_username = GetUserName(user, &namelen);
  153. if (!got_username) {
  154. sfree(user);
  155. }
  156. }
  157. return got_username ? user : NULL;
  158. }
  159. void dll_hijacking_protection(void)
  160. {
  161. /*
  162. * If the OS provides it, call SetDefaultDllDirectories() to
  163. * prevent DLLs from being loaded from the directory containing
  164. * our own binary, and instead only load from system32.
  165. *
  166. * This is a protection against hijacking attacks, if someone runs
  167. * PuTTY directly from their web browser's download directory
  168. * having previously been enticed into clicking on an unwise link
  169. * that downloaded a malicious DLL to the same directory under one
  170. * of various magic names that seem to be things that standard
  171. * Windows DLLs delegate to.
  172. *
  173. * It shouldn't break deliberate loading of user-provided DLLs
  174. * such as GSSAPI providers, because those are specified by their
  175. * full pathname by the user-provided configuration.
  176. */
  177. static HMODULE kernel32_module;
  178. DECL_WINDOWS_FUNCTION(static, BOOL, SetDefaultDllDirectories, (DWORD));
  179. if (!kernel32_module) {
  180. kernel32_module = load_system32_dll("kernel32.dll");
  181. #if (defined _MSC_VER && _MSC_VER < 1900) || defined COVERITY || defined MPEXT
  182. /* For older Visual Studio, and also for the system I
  183. * currently use for Coveritying the Windows code, this
  184. * function isn't available in the header files to
  185. * type-check */
  186. GET_WINDOWS_FUNCTION_NO_TYPECHECK(
  187. kernel32_module, SetDefaultDllDirectories);
  188. #else
  189. GET_WINDOWS_FUNCTION(kernel32_module, SetDefaultDllDirectories);
  190. #endif
  191. }
  192. if (p_SetDefaultDllDirectories) {
  193. /* LOAD_LIBRARY_SEARCH_SYSTEM32 and explicitly specified
  194. * directories only */
  195. p_SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32 |
  196. LOAD_LIBRARY_SEARCH_USER_DIRS);
  197. }
  198. }
  199. void init_winver(void)
  200. {
  201. OSVERSIONINFO osVersion;
  202. static HMODULE kernel32_module;
  203. DECL_WINDOWS_FUNCTION(static, BOOL, GetVersionExA, (LPOSVERSIONINFO));
  204. if (!kernel32_module) {
  205. kernel32_module = load_system32_dll("kernel32.dll");
  206. /* Deliberately don't type-check this function, because that
  207. * would involve using its declaration in a header file which
  208. * triggers a deprecation warning. I know it's deprecated (see
  209. * below) and don't need telling. */
  210. GET_WINDOWS_FUNCTION_NO_TYPECHECK(kernel32_module, GetVersionExA);
  211. }
  212. ZeroMemory(&osVersion, sizeof(osVersion));
  213. osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
  214. if (p_GetVersionExA && p_GetVersionExA(&osVersion)) {
  215. osMajorVersion = osVersion.dwMajorVersion;
  216. osMinorVersion = osVersion.dwMinorVersion;
  217. osPlatformId = osVersion.dwPlatformId;
  218. } else {
  219. /*
  220. * GetVersionEx is deprecated, so allow for it perhaps going
  221. * away in future API versions. If it's not there, simply
  222. * assume that's because Windows is too _new_, so fill in the
  223. * variables we care about to a value that will always compare
  224. * higher than any given test threshold.
  225. *
  226. * Normally we should be checking against the presence of a
  227. * specific function if possible in any case.
  228. */
  229. osMajorVersion = osMinorVersion = UINT_MAX; /* a very high number */
  230. osPlatformId = VER_PLATFORM_WIN32_NT; /* not Win32s or Win95-like */
  231. }
  232. }
  233. #ifdef MPEXT
  234. static char *sysdir = NULL;
  235. void win_misc_cleanup()
  236. {
  237. sfree(sysdir);
  238. }
  239. #endif
  240. HMODULE load_system32_dll(const char *libname)
  241. {
  242. /*
  243. * Wrapper function to load a DLL out of c:\windows\system32
  244. * without going through the full DLL search path. (Hence no
  245. * attack is possible by placing a substitute DLL earlier on that
  246. * path.)
  247. */
  248. #ifndef MPEXT
  249. static char *sysdir = NULL;
  250. #endif
  251. char *fullpath;
  252. HMODULE ret;
  253. if (!sysdir) {
  254. int size = 0, len;
  255. do {
  256. size = 3*size/2 + 512;
  257. sysdir = sresize(sysdir, size, char);
  258. len = GetSystemDirectory(sysdir, size);
  259. } while (len >= size);
  260. }
  261. fullpath = dupcat(sysdir, "\\", libname, NULL);
  262. ret = LoadLibrary(fullpath);
  263. sfree(fullpath);
  264. return ret;
  265. }
  266. /*
  267. * A tree234 containing mappings from system error codes to strings.
  268. */
  269. struct errstring {
  270. int error;
  271. char *text;
  272. };
  273. static int errstring_find(void *av, void *bv)
  274. {
  275. int *a = (int *)av;
  276. struct errstring *b = (struct errstring *)bv;
  277. if (*a < b->error)
  278. return -1;
  279. if (*a > b->error)
  280. return +1;
  281. return 0;
  282. }
  283. static int errstring_compare(void *av, void *bv)
  284. {
  285. struct errstring *a = (struct errstring *)av;
  286. return errstring_find(&a->error, bv);
  287. }
  288. static tree234 *errstrings = NULL;
  289. const char *win_strerror(int error)
  290. {
  291. struct errstring *es;
  292. if (!errstrings)
  293. errstrings = newtree234(errstring_compare);
  294. es = find234(errstrings, &error, errstring_find);
  295. if (!es) {
  296. char msgtext[65536]; /* maximum size for FormatMessage is 64K */
  297. es = snew(struct errstring);
  298. es->error = error;
  299. if (!FormatMessage((FORMAT_MESSAGE_FROM_SYSTEM |
  300. FORMAT_MESSAGE_IGNORE_INSERTS), NULL, error,
  301. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  302. msgtext, lenof(msgtext)-1, NULL)) {
  303. sprintf(msgtext,
  304. "(unable to format: FormatMessage returned %u)",
  305. (unsigned int)GetLastError());
  306. } else {
  307. int len = strlen(msgtext);
  308. if (len > 0 && msgtext[len-1] == '\n')
  309. msgtext[len-1] = '\0';
  310. }
  311. es->text = dupprintf("Error %d: %s", error, msgtext);
  312. add234(errstrings, es);
  313. }
  314. return es->text;
  315. }
  316. #ifdef DEBUG
  317. static FILE *debug_fp = NULL;
  318. static HANDLE debug_hdl = INVALID_HANDLE_VALUE;
  319. static int debug_got_console = 0;
  320. void dputs(const char *buf)
  321. {
  322. DWORD dw;
  323. if (!debug_got_console) {
  324. if (AllocConsole()) {
  325. debug_got_console = 1;
  326. debug_hdl = GetStdHandle(STD_OUTPUT_HANDLE);
  327. }
  328. }
  329. if (!debug_fp) {
  330. debug_fp = fopen("debug.log", "w");
  331. }
  332. if (debug_hdl != INVALID_HANDLE_VALUE) {
  333. WriteFile(debug_hdl, buf, strlen(buf), &dw, NULL);
  334. }
  335. fputs(buf, debug_fp);
  336. fflush(debug_fp);
  337. }
  338. #endif
  339. #ifdef MINEFIELD
  340. /*
  341. * Minefield - a Windows equivalent for Electric Fence
  342. */
  343. #define PAGESIZE 4096
  344. /*
  345. * Design:
  346. *
  347. * We start by reserving as much virtual address space as Windows
  348. * will sensibly (or not sensibly) let us have. We flag it all as
  349. * invalid memory.
  350. *
  351. * Any allocation attempt is satisfied by committing one or more
  352. * pages, with an uncommitted page on either side. The returned
  353. * memory region is jammed up against the _end_ of the pages.
  354. *
  355. * Freeing anything causes instantaneous decommitment of the pages
  356. * involved, so stale pointers are caught as soon as possible.
  357. */
  358. static int minefield_initialised = 0;
  359. static void *minefield_region = NULL;
  360. static long minefield_size = 0;
  361. static long minefield_npages = 0;
  362. static long minefield_curpos = 0;
  363. static unsigned short *minefield_admin = NULL;
  364. static void *minefield_pages = NULL;
  365. static void minefield_admin_hide(int hide)
  366. {
  367. int access = hide ? PAGE_NOACCESS : PAGE_READWRITE;
  368. VirtualProtect(minefield_admin, minefield_npages * 2, access, NULL);
  369. }
  370. static void minefield_init(void)
  371. {
  372. int size;
  373. int admin_size;
  374. int i;
  375. for (size = 0x40000000; size > 0; size = ((size >> 3) * 7) & ~0xFFF) {
  376. minefield_region = VirtualAlloc(NULL, size,
  377. MEM_RESERVE, PAGE_NOACCESS);
  378. if (minefield_region)
  379. break;
  380. }
  381. minefield_size = size;
  382. /*
  383. * Firstly, allocate a section of that to be the admin block.
  384. * We'll need a two-byte field for each page.
  385. */
  386. minefield_admin = minefield_region;
  387. minefield_npages = minefield_size / PAGESIZE;
  388. admin_size = (minefield_npages * 2 + PAGESIZE - 1) & ~(PAGESIZE - 1);
  389. minefield_npages = (minefield_size - admin_size) / PAGESIZE;
  390. minefield_pages = (char *) minefield_region + admin_size;
  391. /*
  392. * Commit the admin region.
  393. */
  394. VirtualAlloc(minefield_admin, minefield_npages * 2,
  395. MEM_COMMIT, PAGE_READWRITE);
  396. /*
  397. * Mark all pages as unused (0xFFFF).
  398. */
  399. for (i = 0; i < minefield_npages; i++)
  400. minefield_admin[i] = 0xFFFF;
  401. /*
  402. * Hide the admin region.
  403. */
  404. minefield_admin_hide(1);
  405. minefield_initialised = 1;
  406. }
  407. static void minefield_bomb(void)
  408. {
  409. div(1, *(int *) minefield_pages);
  410. }
  411. static void *minefield_alloc(int size)
  412. {
  413. int npages;
  414. int pos, lim, region_end, region_start;
  415. int start;
  416. int i;
  417. npages = (size + PAGESIZE - 1) / PAGESIZE;
  418. minefield_admin_hide(0);
  419. /*
  420. * Search from current position until we find a contiguous
  421. * bunch of npages+2 unused pages.
  422. */
  423. pos = minefield_curpos;
  424. lim = minefield_npages;
  425. while (1) {
  426. /* Skip over used pages. */
  427. while (pos < lim && minefield_admin[pos] != 0xFFFF)
  428. pos++;
  429. /* Count unused pages. */
  430. start = pos;
  431. while (pos < lim && pos - start < npages + 2 &&
  432. minefield_admin[pos] == 0xFFFF)
  433. pos++;
  434. if (pos - start == npages + 2)
  435. break;
  436. /* If we've reached the limit, reset the limit or stop. */
  437. if (pos >= lim) {
  438. if (lim == minefield_npages) {
  439. /* go round and start again at zero */
  440. lim = minefield_curpos;
  441. pos = 0;
  442. } else {
  443. minefield_admin_hide(1);
  444. return NULL;
  445. }
  446. }
  447. }
  448. minefield_curpos = pos - 1;
  449. /*
  450. * We have npages+2 unused pages starting at start. We leave
  451. * the first and last of these alone and use the rest.
  452. */
  453. region_end = (start + npages + 1) * PAGESIZE;
  454. region_start = region_end - size;
  455. /* FIXME: could align here if we wanted */
  456. /*
  457. * Update the admin region.
  458. */
  459. for (i = start + 2; i < start + npages + 1; i++)
  460. minefield_admin[i] = 0xFFFE; /* used but no region starts here */
  461. minefield_admin[start + 1] = region_start % PAGESIZE;
  462. minefield_admin_hide(1);
  463. VirtualAlloc((char *) minefield_pages + region_start, size,
  464. MEM_COMMIT, PAGE_READWRITE);
  465. return (char *) minefield_pages + region_start;
  466. }
  467. static void minefield_free(void *ptr)
  468. {
  469. int region_start, i, j;
  470. minefield_admin_hide(0);
  471. region_start = (char *) ptr - (char *) minefield_pages;
  472. i = region_start / PAGESIZE;
  473. if (i < 0 || i >= minefield_npages ||
  474. minefield_admin[i] != region_start % PAGESIZE)
  475. minefield_bomb();
  476. for (j = i; j < minefield_npages && minefield_admin[j] != 0xFFFF; j++) {
  477. minefield_admin[j] = 0xFFFF;
  478. }
  479. VirtualFree(ptr, j * PAGESIZE - region_start, MEM_DECOMMIT);
  480. minefield_admin_hide(1);
  481. }
  482. static int minefield_get_size(void *ptr)
  483. {
  484. int region_start, i, j;
  485. minefield_admin_hide(0);
  486. region_start = (char *) ptr - (char *) minefield_pages;
  487. i = region_start / PAGESIZE;
  488. if (i < 0 || i >= minefield_npages ||
  489. minefield_admin[i] != region_start % PAGESIZE)
  490. minefield_bomb();
  491. for (j = i; j < minefield_npages && minefield_admin[j] != 0xFFFF; j++);
  492. minefield_admin_hide(1);
  493. return j * PAGESIZE - region_start;
  494. }
  495. void *minefield_c_malloc(size_t size)
  496. {
  497. if (!minefield_initialised)
  498. minefield_init();
  499. return minefield_alloc(size);
  500. }
  501. void minefield_c_free(void *p)
  502. {
  503. if (!minefield_initialised)
  504. minefield_init();
  505. minefield_free(p);
  506. }
  507. /*
  508. * realloc _always_ moves the chunk, for rapid detection of code
  509. * that assumes it won't.
  510. */
  511. void *minefield_c_realloc(void *p, size_t size)
  512. {
  513. size_t oldsize;
  514. void *q;
  515. if (!minefield_initialised)
  516. minefield_init();
  517. q = minefield_alloc(size);
  518. oldsize = minefield_get_size(p);
  519. memcpy(q, p, (oldsize < size ? oldsize : size));
  520. minefield_free(p);
  521. return q;
  522. }
  523. #endif /* MINEFIELD */
  524. FontSpec *fontspec_new(const char *name,
  525. int bold, int height, int charset)
  526. {
  527. FontSpec *f = snew(FontSpec);
  528. f->name = dupstr(name);
  529. f->isbold = bold;
  530. f->height = height;
  531. f->charset = charset;
  532. return f;
  533. }
  534. FontSpec *fontspec_copy(const FontSpec *f)
  535. {
  536. return fontspec_new(f->name, f->isbold, f->height, f->charset);
  537. }
  538. void fontspec_free(FontSpec *f)
  539. {
  540. sfree(f->name);
  541. sfree(f);
  542. }
  543. void fontspec_serialise(BinarySink *bs, FontSpec *f)
  544. {
  545. put_asciz(bs, f->name);
  546. put_uint32(bs, f->isbold);
  547. put_uint32(bs, f->height);
  548. put_uint32(bs, f->charset);
  549. }
  550. FontSpec *fontspec_deserialise(BinarySource *src)
  551. {
  552. const char *name = get_asciz(src);
  553. unsigned isbold = get_uint32(src);
  554. unsigned height = get_uint32(src);
  555. unsigned charset = get_uint32(src);
  556. return fontspec_new(name, isbold, height, charset);
  557. }
  558. int open_for_write_would_lose_data(const Filename *fn)
  559. {
  560. WIN32_FILE_ATTRIBUTE_DATA attrs;
  561. if (!GetFileAttributesEx(fn->path, GetFileExInfoStandard, &attrs)) {
  562. /*
  563. * Generally, if we don't identify a specific reason why we
  564. * should return true from this function, we return false, and
  565. * let the subsequent attempt to open the file for real give a
  566. * more useful error message.
  567. */
  568. return FALSE;
  569. }
  570. if (attrs.dwFileAttributes & (FILE_ATTRIBUTE_DEVICE |
  571. FILE_ATTRIBUTE_DIRECTORY)) {
  572. /*
  573. * File is something other than an ordinary disk file, so
  574. * opening it for writing will not cause truncation. (It may
  575. * not _succeed_ either, but that's not our problem here!)
  576. */
  577. return FALSE;
  578. }
  579. if (attrs.nFileSizeHigh == 0 && attrs.nFileSizeLow == 0) {
  580. /*
  581. * File is zero-length (or may be a named pipe, which
  582. * dwFileAttributes can't tell apart from a regular file), so
  583. * opening it for writing won't truncate any data away because
  584. * there's nothing to truncate anyway.
  585. */
  586. return FALSE;
  587. }
  588. return TRUE;
  589. }