winmisc.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  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 COVERITY || defined MPEXT
  173. /* For older Visual Studio, and also for the system I
  174. * currently use for Coveritying the Windows code, this
  175. * function isn't available in the header files to
  176. * type-check */
  177. GET_WINDOWS_FUNCTION_NO_TYPECHECK(
  178. kernel32_module, SetDefaultDllDirectories);
  179. #else
  180. GET_WINDOWS_FUNCTION(kernel32_module, SetDefaultDllDirectories);
  181. #endif
  182. }
  183. if (p_SetDefaultDllDirectories) {
  184. /* LOAD_LIBRARY_SEARCH_SYSTEM32 and explicitly specified
  185. * directories only */
  186. p_SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32 |
  187. LOAD_LIBRARY_SEARCH_USER_DIRS);
  188. }
  189. }
  190. void init_winver(void)
  191. {
  192. OSVERSIONINFO osVersion;
  193. static HMODULE kernel32_module;
  194. DECL_WINDOWS_FUNCTION(static, BOOL, GetVersionExA, (LPOSVERSIONINFO));
  195. if (!kernel32_module) {
  196. kernel32_module = load_system32_dll("kernel32.dll");
  197. /* Deliberately don't type-check this function, because that
  198. * would involve using its declaration in a header file which
  199. * triggers a deprecation warning. I know it's deprecated (see
  200. * below) and don't need telling. */
  201. GET_WINDOWS_FUNCTION_NO_TYPECHECK(kernel32_module, GetVersionExA);
  202. }
  203. ZeroMemory(&osVersion, sizeof(osVersion));
  204. osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
  205. if (p_GetVersionExA && p_GetVersionExA(&osVersion)) {
  206. osMajorVersion = osVersion.dwMajorVersion;
  207. osMinorVersion = osVersion.dwMinorVersion;
  208. osPlatformId = osVersion.dwPlatformId;
  209. } else {
  210. /*
  211. * GetVersionEx is deprecated, so allow for it perhaps going
  212. * away in future API versions. If it's not there, simply
  213. * assume that's because Windows is too _new_, so fill in the
  214. * variables we care about to a value that will always compare
  215. * higher than any given test threshold.
  216. *
  217. * Normally we should be checking against the presence of a
  218. * specific function if possible in any case.
  219. */
  220. osMajorVersion = osMinorVersion = UINT_MAX; /* a very high number */
  221. osPlatformId = VER_PLATFORM_WIN32_NT; /* not Win32s or Win95-like */
  222. }
  223. }
  224. #ifdef MPEXT
  225. static char *sysdir = NULL;
  226. void win_misc_cleanup()
  227. {
  228. sfree(sysdir);
  229. }
  230. #endif
  231. HMODULE load_system32_dll(const char *libname)
  232. {
  233. /*
  234. * Wrapper function to load a DLL out of c:\windows\system32
  235. * without going through the full DLL search path. (Hence no
  236. * attack is possible by placing a substitute DLL earlier on that
  237. * path.)
  238. */
  239. #ifndef MPEXT
  240. static char *sysdir = NULL;
  241. #endif
  242. static size_t sysdirsize = 0;
  243. char *fullpath;
  244. HMODULE ret;
  245. if (!sysdir) {
  246. size_t len;
  247. while ((len = GetSystemDirectory(sysdir, sysdirsize)) >= sysdirsize)
  248. sgrowarray(sysdir, sysdirsize, len);
  249. }
  250. fullpath = dupcat(sysdir, "\\", libname, NULL);
  251. ret = LoadLibrary(fullpath);
  252. sfree(fullpath);
  253. return ret;
  254. }
  255. /*
  256. * A tree234 containing mappings from system error codes to strings.
  257. */
  258. struct errstring {
  259. int error;
  260. char *text;
  261. };
  262. static int errstring_find(void *av, void *bv)
  263. {
  264. int *a = (int *)av;
  265. struct errstring *b = (struct errstring *)bv;
  266. if (*a < b->error)
  267. return -1;
  268. if (*a > b->error)
  269. return +1;
  270. return 0;
  271. }
  272. static int errstring_compare(void *av, void *bv)
  273. {
  274. struct errstring *a = (struct errstring *)av;
  275. return errstring_find(&a->error, bv);
  276. }
  277. static tree234 *errstrings = NULL;
  278. const char *win_strerror(int error)
  279. {
  280. struct errstring *es;
  281. if (!errstrings)
  282. errstrings = newtree234(errstring_compare);
  283. es = find234(errstrings, &error, errstring_find);
  284. if (!es) {
  285. char msgtext[65536]; /* maximum size for FormatMessage is 64K */
  286. es = snew(struct errstring);
  287. es->error = error;
  288. if (!FormatMessage((FORMAT_MESSAGE_FROM_SYSTEM |
  289. FORMAT_MESSAGE_IGNORE_INSERTS), NULL, error,
  290. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  291. msgtext, lenof(msgtext)-1, NULL)) {
  292. sprintf(msgtext,
  293. "(unable to format: FormatMessage returned %u)",
  294. (unsigned int)GetLastError());
  295. } else {
  296. int len = strlen(msgtext);
  297. if (len > 0 && msgtext[len-1] == '\n')
  298. msgtext[len-1] = '\0';
  299. }
  300. es->text = dupprintf("Error %d: %s", error, msgtext);
  301. add234(errstrings, es);
  302. }
  303. return es->text;
  304. }
  305. FontSpec *fontspec_new(const char *name, bool bold, int height, int charset)
  306. {
  307. FontSpec *f = snew(FontSpec);
  308. f->name = dupstr(name);
  309. f->isbold = bold;
  310. f->height = height;
  311. f->charset = charset;
  312. return f;
  313. }
  314. FontSpec *fontspec_copy(const FontSpec *f)
  315. {
  316. return fontspec_new(f->name, f->isbold, f->height, f->charset);
  317. }
  318. void fontspec_free(FontSpec *f)
  319. {
  320. sfree(f->name);
  321. sfree(f);
  322. }
  323. void fontspec_serialise(BinarySink *bs, FontSpec *f)
  324. {
  325. put_asciz(bs, f->name);
  326. put_uint32(bs, f->isbold);
  327. put_uint32(bs, f->height);
  328. put_uint32(bs, f->charset);
  329. }
  330. FontSpec *fontspec_deserialise(BinarySource *src)
  331. {
  332. const char *name = get_asciz(src);
  333. unsigned isbold = get_uint32(src);
  334. unsigned height = get_uint32(src);
  335. unsigned charset = get_uint32(src);
  336. return fontspec_new(name, isbold, height, charset);
  337. }
  338. bool open_for_write_would_lose_data(const Filename *fn)
  339. {
  340. WIN32_FILE_ATTRIBUTE_DATA attrs;
  341. if (!GetFileAttributesEx(fn->path, GetFileExInfoStandard, &attrs)) {
  342. /*
  343. * Generally, if we don't identify a specific reason why we
  344. * should return true from this function, we return false, and
  345. * let the subsequent attempt to open the file for real give a
  346. * more useful error message.
  347. */
  348. return false;
  349. }
  350. if (attrs.dwFileAttributes & (FILE_ATTRIBUTE_DEVICE |
  351. FILE_ATTRIBUTE_DIRECTORY)) {
  352. /*
  353. * File is something other than an ordinary disk file, so
  354. * opening it for writing will not cause truncation. (It may
  355. * not _succeed_ either, but that's not our problem here!)
  356. */
  357. return false;
  358. }
  359. if (attrs.nFileSizeHigh == 0 && attrs.nFileSizeLow == 0) {
  360. /*
  361. * File is zero-length (or may be a named pipe, which
  362. * dwFileAttributes can't tell apart from a regular file), so
  363. * opening it for writing won't truncate any data away because
  364. * there's nothing to truncate anyway.
  365. */
  366. return false;
  367. }
  368. return true;
  369. }
  370. void escape_registry_key(const char *in, strbuf *out)
  371. {
  372. bool candot = false;
  373. static const char hex[16] = "0123456789ABCDEF";
  374. while (*in) {
  375. if (*in == ' ' || *in == '\\' || *in == '*' || *in == '?' ||
  376. *in == '%' || *in < ' ' || *in > '~' || (*in == '.'
  377. && !candot)) {
  378. put_byte(out, '%');
  379. put_byte(out, hex[((unsigned char) *in) >> 4]);
  380. put_byte(out, hex[((unsigned char) *in) & 15]);
  381. } else
  382. put_byte(out, *in);
  383. in++;
  384. candot = true;
  385. }
  386. }
  387. void unescape_registry_key(const char *in, strbuf *out)
  388. {
  389. while (*in) {
  390. if (*in == '%' && in[1] && in[2]) {
  391. int i, j;
  392. i = in[1] - '0';
  393. i -= (i > 9 ? 7 : 0);
  394. j = in[2] - '0';
  395. j -= (j > 9 ? 7 : 0);
  396. put_byte(out, (i << 4) + j);
  397. in += 3;
  398. } else {
  399. put_byte(out, *in++);
  400. }
  401. }
  402. }
  403. #ifdef DEBUG
  404. static FILE *debug_fp = NULL;
  405. static HANDLE debug_hdl = INVALID_HANDLE_VALUE;
  406. static int debug_got_console = 0;
  407. void dputs(const char *buf)
  408. {
  409. DWORD dw;
  410. if (!debug_got_console) {
  411. if (AllocConsole()) {
  412. debug_got_console = 1;
  413. debug_hdl = GetStdHandle(STD_OUTPUT_HANDLE);
  414. }
  415. }
  416. if (!debug_fp) {
  417. debug_fp = fopen("debug.log", "w");
  418. }
  419. if (debug_hdl != INVALID_HANDLE_VALUE) {
  420. WriteFile(debug_hdl, buf, strlen(buf), &dw, NULL);
  421. }
  422. fputs(buf, debug_fp);
  423. fflush(debug_fp);
  424. }
  425. #endif
  426. char *registry_get_string(HKEY root, const char *path, const char *leaf)
  427. {
  428. HKEY key = root;
  429. bool need_close_key = false;
  430. char *toret = NULL, *str = NULL;
  431. if (path) {
  432. if (RegCreateKey(key, path, &key) != ERROR_SUCCESS)
  433. goto out;
  434. need_close_key = true;
  435. }
  436. { // WINSCP
  437. DWORD type, size;
  438. if (RegQueryValueEx(key, leaf, 0, &type, NULL, &size) != ERROR_SUCCESS)
  439. goto out;
  440. if (type != REG_SZ)
  441. goto out;
  442. str = snewn(size + 1, char);
  443. { // WINSCP
  444. DWORD size_got = size;
  445. if (RegQueryValueEx(key, leaf, 0, &type, (LPBYTE)str,
  446. &size_got) != ERROR_SUCCESS)
  447. goto out;
  448. if (type != REG_SZ || size_got > size)
  449. goto out;
  450. str[size_got] = '\0';
  451. toret = str;
  452. str = NULL;
  453. out:
  454. if (need_close_key)
  455. RegCloseKey(key);
  456. sfree(str);
  457. return toret;
  458. } // WINSCP
  459. } // WINSCP
  460. }