winmisc.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620
  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. GET_WINDOWS_FUNCTION(secur32, GetUserNameExA);
  124. tried_usernameex = TRUE;
  125. }
  126. }
  127. if (p_GetUserNameExA) {
  128. /*
  129. * If available, use the principal -- this avoids the problem
  130. * that the local username is case-insensitive but Kerberos
  131. * usernames are case-sensitive.
  132. */
  133. /* Get the length */
  134. namelen = 0;
  135. (void) p_GetUserNameExA(NameUserPrincipal, NULL, &namelen);
  136. user = snewn(namelen, char);
  137. got_username = p_GetUserNameExA(NameUserPrincipal, user, &namelen);
  138. if (got_username) {
  139. char *p = strchr(user, '@');
  140. if (p) *p = 0;
  141. } else {
  142. sfree(user);
  143. }
  144. }
  145. if (!got_username) {
  146. /* Fall back to local user name */
  147. namelen = 0;
  148. if (GetUserName(NULL, &namelen) == FALSE) {
  149. /*
  150. * Apparently this doesn't work at least on Windows XP SP2.
  151. * Thus assume a maximum of 256. It will fail again if it
  152. * doesn't fit.
  153. */
  154. namelen = 256;
  155. }
  156. user = snewn(namelen, char);
  157. got_username = GetUserName(user, &namelen);
  158. if (!got_username) {
  159. sfree(user);
  160. }
  161. }
  162. return got_username ? user : NULL;
  163. }
  164. void dll_hijacking_protection(void)
  165. {
  166. /*
  167. * If the OS provides it, call SetDefaultDllDirectories() to
  168. * prevent DLLs from being loaded from the directory containing
  169. * our own binary, and instead only load from system32.
  170. *
  171. * This is a protection against hijacking attacks, if someone runs
  172. * PuTTY directly from their web browser's download directory
  173. * having previously been enticed into clicking on an unwise link
  174. * that downloaded a malicious DLL to the same directory under one
  175. * of various magic names that seem to be things that standard
  176. * Windows DLLs delegate to.
  177. *
  178. * It shouldn't break deliberate loading of user-provided DLLs
  179. * such as GSSAPI providers, because those are specified by their
  180. * full pathname by the user-provided configuration.
  181. */
  182. static HMODULE kernel32_module;
  183. DECL_WINDOWS_FUNCTION(static, BOOL, SetDefaultDllDirectories, (DWORD));
  184. if (!kernel32_module) {
  185. kernel32_module = load_system32_dll("kernel32.dll");
  186. GET_WINDOWS_FUNCTION(kernel32_module, SetDefaultDllDirectories);
  187. }
  188. if (p_SetDefaultDllDirectories) {
  189. /* LOAD_LIBRARY_SEARCH_SYSTEM32 only */
  190. p_SetDefaultDllDirectories(0x800);
  191. }
  192. }
  193. BOOL init_winver(void)
  194. {
  195. ZeroMemory(&osVersion, sizeof(osVersion));
  196. osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
  197. return GetVersionEx ( (OSVERSIONINFO *) &osVersion);
  198. }
  199. #ifdef MPEXT
  200. static char *sysdir = NULL;
  201. void win_misc_cleanup()
  202. {
  203. sfree(sysdir);
  204. }
  205. #endif
  206. HMODULE load_system32_dll(const char *libname)
  207. {
  208. /*
  209. * Wrapper function to load a DLL out of c:\windows\system32
  210. * without going through the full DLL search path. (Hence no
  211. * attack is possible by placing a substitute DLL earlier on that
  212. * path.)
  213. */
  214. #ifndef MPEXT
  215. static char *sysdir = NULL;
  216. #endif
  217. char *fullpath;
  218. HMODULE ret;
  219. if (!sysdir) {
  220. int size = 0, len;
  221. do {
  222. size = 3*size/2 + 512;
  223. sysdir = sresize(sysdir, size, char);
  224. len = GetSystemDirectory(sysdir, size);
  225. } while (len >= size);
  226. }
  227. fullpath = dupcat(sysdir, "\\", libname, NULL);
  228. ret = LoadLibrary(fullpath);
  229. sfree(fullpath);
  230. return ret;
  231. }
  232. /*
  233. * A tree234 containing mappings from system error codes to strings.
  234. */
  235. struct errstring {
  236. int error;
  237. char *text;
  238. };
  239. static int errstring_find(void *av, void *bv)
  240. {
  241. int *a = (int *)av;
  242. struct errstring *b = (struct errstring *)bv;
  243. if (*a < b->error)
  244. return -1;
  245. if (*a > b->error)
  246. return +1;
  247. return 0;
  248. }
  249. static int errstring_compare(void *av, void *bv)
  250. {
  251. struct errstring *a = (struct errstring *)av;
  252. return errstring_find(&a->error, bv);
  253. }
  254. static tree234 *errstrings = NULL;
  255. const char *win_strerror(int error)
  256. {
  257. struct errstring *es;
  258. if (!errstrings)
  259. errstrings = newtree234(errstring_compare);
  260. es = find234(errstrings, &error, errstring_find);
  261. if (!es) {
  262. char msgtext[65536]; /* maximum size for FormatMessage is 64K */
  263. es = snew(struct errstring);
  264. es->error = error;
  265. if (!FormatMessage((FORMAT_MESSAGE_FROM_SYSTEM |
  266. FORMAT_MESSAGE_IGNORE_INSERTS), NULL, error,
  267. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  268. msgtext, lenof(msgtext)-1, NULL)) {
  269. sprintf(msgtext,
  270. "(unable to format: FormatMessage returned %u)",
  271. (unsigned int)GetLastError());
  272. } else {
  273. int len = strlen(msgtext);
  274. if (len > 0 && msgtext[len-1] == '\n')
  275. msgtext[len-1] = '\0';
  276. }
  277. es->text = dupprintf("Error %d: %s", error, msgtext);
  278. add234(errstrings, es);
  279. }
  280. return es->text;
  281. }
  282. #ifdef DEBUG
  283. static FILE *debug_fp = NULL;
  284. static HANDLE debug_hdl = INVALID_HANDLE_VALUE;
  285. static int debug_got_console = 0;
  286. void dputs(const char *buf)
  287. {
  288. DWORD dw;
  289. if (!debug_got_console) {
  290. if (AllocConsole()) {
  291. debug_got_console = 1;
  292. debug_hdl = GetStdHandle(STD_OUTPUT_HANDLE);
  293. }
  294. }
  295. if (!debug_fp) {
  296. debug_fp = fopen("debug.log", "w");
  297. }
  298. if (debug_hdl != INVALID_HANDLE_VALUE) {
  299. WriteFile(debug_hdl, buf, strlen(buf), &dw, NULL);
  300. }
  301. fputs(buf, debug_fp);
  302. fflush(debug_fp);
  303. }
  304. #endif
  305. #ifdef MINEFIELD
  306. /*
  307. * Minefield - a Windows equivalent for Electric Fence
  308. */
  309. #define PAGESIZE 4096
  310. /*
  311. * Design:
  312. *
  313. * We start by reserving as much virtual address space as Windows
  314. * will sensibly (or not sensibly) let us have. We flag it all as
  315. * invalid memory.
  316. *
  317. * Any allocation attempt is satisfied by committing one or more
  318. * pages, with an uncommitted page on either side. The returned
  319. * memory region is jammed up against the _end_ of the pages.
  320. *
  321. * Freeing anything causes instantaneous decommitment of the pages
  322. * involved, so stale pointers are caught as soon as possible.
  323. */
  324. static int minefield_initialised = 0;
  325. static void *minefield_region = NULL;
  326. static long minefield_size = 0;
  327. static long minefield_npages = 0;
  328. static long minefield_curpos = 0;
  329. static unsigned short *minefield_admin = NULL;
  330. static void *minefield_pages = NULL;
  331. static void minefield_admin_hide(int hide)
  332. {
  333. int access = hide ? PAGE_NOACCESS : PAGE_READWRITE;
  334. VirtualProtect(minefield_admin, minefield_npages * 2, access, NULL);
  335. }
  336. static void minefield_init(void)
  337. {
  338. int size;
  339. int admin_size;
  340. int i;
  341. for (size = 0x40000000; size > 0; size = ((size >> 3) * 7) & ~0xFFF) {
  342. minefield_region = VirtualAlloc(NULL, size,
  343. MEM_RESERVE, PAGE_NOACCESS);
  344. if (minefield_region)
  345. break;
  346. }
  347. minefield_size = size;
  348. /*
  349. * Firstly, allocate a section of that to be the admin block.
  350. * We'll need a two-byte field for each page.
  351. */
  352. minefield_admin = minefield_region;
  353. minefield_npages = minefield_size / PAGESIZE;
  354. admin_size = (minefield_npages * 2 + PAGESIZE - 1) & ~(PAGESIZE - 1);
  355. minefield_npages = (minefield_size - admin_size) / PAGESIZE;
  356. minefield_pages = (char *) minefield_region + admin_size;
  357. /*
  358. * Commit the admin region.
  359. */
  360. VirtualAlloc(minefield_admin, minefield_npages * 2,
  361. MEM_COMMIT, PAGE_READWRITE);
  362. /*
  363. * Mark all pages as unused (0xFFFF).
  364. */
  365. for (i = 0; i < minefield_npages; i++)
  366. minefield_admin[i] = 0xFFFF;
  367. /*
  368. * Hide the admin region.
  369. */
  370. minefield_admin_hide(1);
  371. minefield_initialised = 1;
  372. }
  373. static void minefield_bomb(void)
  374. {
  375. div(1, *(int *) minefield_pages);
  376. }
  377. static void *minefield_alloc(int size)
  378. {
  379. int npages;
  380. int pos, lim, region_end, region_start;
  381. int start;
  382. int i;
  383. npages = (size + PAGESIZE - 1) / PAGESIZE;
  384. minefield_admin_hide(0);
  385. /*
  386. * Search from current position until we find a contiguous
  387. * bunch of npages+2 unused pages.
  388. */
  389. pos = minefield_curpos;
  390. lim = minefield_npages;
  391. while (1) {
  392. /* Skip over used pages. */
  393. while (pos < lim && minefield_admin[pos] != 0xFFFF)
  394. pos++;
  395. /* Count unused pages. */
  396. start = pos;
  397. while (pos < lim && pos - start < npages + 2 &&
  398. minefield_admin[pos] == 0xFFFF)
  399. pos++;
  400. if (pos - start == npages + 2)
  401. break;
  402. /* If we've reached the limit, reset the limit or stop. */
  403. if (pos >= lim) {
  404. if (lim == minefield_npages) {
  405. /* go round and start again at zero */
  406. lim = minefield_curpos;
  407. pos = 0;
  408. } else {
  409. minefield_admin_hide(1);
  410. return NULL;
  411. }
  412. }
  413. }
  414. minefield_curpos = pos - 1;
  415. /*
  416. * We have npages+2 unused pages starting at start. We leave
  417. * the first and last of these alone and use the rest.
  418. */
  419. region_end = (start + npages + 1) * PAGESIZE;
  420. region_start = region_end - size;
  421. /* FIXME: could align here if we wanted */
  422. /*
  423. * Update the admin region.
  424. */
  425. for (i = start + 2; i < start + npages + 1; i++)
  426. minefield_admin[i] = 0xFFFE; /* used but no region starts here */
  427. minefield_admin[start + 1] = region_start % PAGESIZE;
  428. minefield_admin_hide(1);
  429. VirtualAlloc((char *) minefield_pages + region_start, size,
  430. MEM_COMMIT, PAGE_READWRITE);
  431. return (char *) minefield_pages + region_start;
  432. }
  433. static void minefield_free(void *ptr)
  434. {
  435. int region_start, i, j;
  436. minefield_admin_hide(0);
  437. region_start = (char *) ptr - (char *) minefield_pages;
  438. i = region_start / PAGESIZE;
  439. if (i < 0 || i >= minefield_npages ||
  440. minefield_admin[i] != region_start % PAGESIZE)
  441. minefield_bomb();
  442. for (j = i; j < minefield_npages && minefield_admin[j] != 0xFFFF; j++) {
  443. minefield_admin[j] = 0xFFFF;
  444. }
  445. VirtualFree(ptr, j * PAGESIZE - region_start, MEM_DECOMMIT);
  446. minefield_admin_hide(1);
  447. }
  448. static int minefield_get_size(void *ptr)
  449. {
  450. int region_start, i, j;
  451. minefield_admin_hide(0);
  452. region_start = (char *) ptr - (char *) minefield_pages;
  453. i = region_start / PAGESIZE;
  454. if (i < 0 || i >= minefield_npages ||
  455. minefield_admin[i] != region_start % PAGESIZE)
  456. minefield_bomb();
  457. for (j = i; j < minefield_npages && minefield_admin[j] != 0xFFFF; j++);
  458. minefield_admin_hide(1);
  459. return j * PAGESIZE - region_start;
  460. }
  461. void *minefield_c_malloc(size_t size)
  462. {
  463. if (!minefield_initialised)
  464. minefield_init();
  465. return minefield_alloc(size);
  466. }
  467. void minefield_c_free(void *p)
  468. {
  469. if (!minefield_initialised)
  470. minefield_init();
  471. minefield_free(p);
  472. }
  473. /*
  474. * realloc _always_ moves the chunk, for rapid detection of code
  475. * that assumes it won't.
  476. */
  477. void *minefield_c_realloc(void *p, size_t size)
  478. {
  479. size_t oldsize;
  480. void *q;
  481. if (!minefield_initialised)
  482. minefield_init();
  483. q = minefield_alloc(size);
  484. oldsize = minefield_get_size(p);
  485. memcpy(q, p, (oldsize < size ? oldsize : size));
  486. minefield_free(p);
  487. return q;
  488. }
  489. #endif /* MINEFIELD */
  490. FontSpec *fontspec_new(const char *name,
  491. int bold, int height, int charset)
  492. {
  493. FontSpec *f = snew(FontSpec);
  494. f->name = dupstr(name);
  495. f->isbold = bold;
  496. f->height = height;
  497. f->charset = charset;
  498. return f;
  499. }
  500. FontSpec *fontspec_copy(const FontSpec *f)
  501. {
  502. return fontspec_new(f->name, f->isbold, f->height, f->charset);
  503. }
  504. void fontspec_free(FontSpec *f)
  505. {
  506. sfree(f->name);
  507. sfree(f);
  508. }
  509. int fontspec_serialise(FontSpec *f, void *vdata)
  510. {
  511. char *data = (char *)vdata;
  512. int len = strlen(f->name) + 1; /* include trailing NUL */
  513. if (data) {
  514. strcpy(data, f->name);
  515. PUT_32BIT_MSB_FIRST(data + len, f->isbold);
  516. PUT_32BIT_MSB_FIRST(data + len + 4, f->height);
  517. PUT_32BIT_MSB_FIRST(data + len + 8, f->charset);
  518. }
  519. return len + 12; /* also include three 4-byte ints */
  520. }
  521. FontSpec *fontspec_deserialise(void *vdata, int maxsize, int *used)
  522. {
  523. char *data = (char *)vdata;
  524. char *end;
  525. if (maxsize < 13)
  526. return NULL;
  527. end = memchr(data, '\0', maxsize-12);
  528. if (!end)
  529. return NULL;
  530. end++;
  531. *used = end - data + 12;
  532. return fontspec_new(data,
  533. GET_32BIT_MSB_FIRST(end),
  534. GET_32BIT_MSB_FIRST(end + 4),
  535. GET_32BIT_MSB_FIRST(end + 8));
  536. }