winmisc.c 16 KB

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