winmisc.c 17 KB

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