winmisc.c 14 KB

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