winmisc.c 15 KB

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