winmisc.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  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. char *fullpath;
  201. HMODULE ret;
  202. if (!sysdir) {
  203. int size = 0, len;
  204. do {
  205. size = 3*size/2 + 512;
  206. sysdir = sresize(sysdir, size, char);
  207. len = GetSystemDirectory(sysdir, size);
  208. } while (len >= size);
  209. }
  210. fullpath = dupcat(sysdir, "\\", libname, NULL);
  211. ret = LoadLibrary(fullpath);
  212. sfree(fullpath);
  213. return ret;
  214. }
  215. /*
  216. * A tree234 containing mappings from system error codes to strings.
  217. */
  218. struct errstring {
  219. int error;
  220. char *text;
  221. };
  222. static int errstring_find(void *av, void *bv)
  223. {
  224. int *a = (int *)av;
  225. struct errstring *b = (struct errstring *)bv;
  226. if (*a < b->error)
  227. return -1;
  228. if (*a > b->error)
  229. return +1;
  230. return 0;
  231. }
  232. static int errstring_compare(void *av, void *bv)
  233. {
  234. struct errstring *a = (struct errstring *)av;
  235. return errstring_find(&a->error, bv);
  236. }
  237. static tree234 *errstrings = NULL;
  238. const char *win_strerror(int error)
  239. {
  240. struct errstring *es;
  241. if (!errstrings)
  242. errstrings = newtree234(errstring_compare);
  243. es = find234(errstrings, &error, errstring_find);
  244. if (!es) {
  245. char msgtext[65536]; /* maximum size for FormatMessage is 64K */
  246. es = snew(struct errstring);
  247. es->error = error;
  248. if (!FormatMessage((FORMAT_MESSAGE_FROM_SYSTEM |
  249. FORMAT_MESSAGE_IGNORE_INSERTS), NULL, error,
  250. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  251. msgtext, lenof(msgtext)-1, NULL)) {
  252. sprintf(msgtext,
  253. "(unable to format: FormatMessage returned %u)",
  254. (unsigned int)GetLastError());
  255. } else {
  256. int len = strlen(msgtext);
  257. if (len > 0 && msgtext[len-1] == '\n')
  258. msgtext[len-1] = '\0';
  259. }
  260. es->text = dupprintf("Error %d: %s", error, msgtext);
  261. add234(errstrings, es);
  262. }
  263. return es->text;
  264. }
  265. FontSpec *fontspec_new(const char *name, bool bold, int height, int charset)
  266. {
  267. FontSpec *f = snew(FontSpec);
  268. f->name = dupstr(name);
  269. f->isbold = bold;
  270. f->height = height;
  271. f->charset = charset;
  272. return f;
  273. }
  274. FontSpec *fontspec_copy(const FontSpec *f)
  275. {
  276. return fontspec_new(f->name, f->isbold, f->height, f->charset);
  277. }
  278. void fontspec_free(FontSpec *f)
  279. {
  280. sfree(f->name);
  281. sfree(f);
  282. }
  283. void fontspec_serialise(BinarySink *bs, FontSpec *f)
  284. {
  285. put_asciz(bs, f->name);
  286. put_uint32(bs, f->isbold);
  287. put_uint32(bs, f->height);
  288. put_uint32(bs, f->charset);
  289. }
  290. FontSpec *fontspec_deserialise(BinarySource *src)
  291. {
  292. const char *name = get_asciz(src);
  293. unsigned isbold = get_uint32(src);
  294. unsigned height = get_uint32(src);
  295. unsigned charset = get_uint32(src);
  296. return fontspec_new(name, isbold, height, charset);
  297. }
  298. bool open_for_write_would_lose_data(const Filename *fn)
  299. {
  300. WIN32_FILE_ATTRIBUTE_DATA attrs;
  301. if (!GetFileAttributesEx(fn->path, GetFileExInfoStandard, &attrs)) {
  302. /*
  303. * Generally, if we don't identify a specific reason why we
  304. * should return true from this function, we return false, and
  305. * let the subsequent attempt to open the file for real give a
  306. * more useful error message.
  307. */
  308. return false;
  309. }
  310. if (attrs.dwFileAttributes & (FILE_ATTRIBUTE_DEVICE |
  311. FILE_ATTRIBUTE_DIRECTORY)) {
  312. /*
  313. * File is something other than an ordinary disk file, so
  314. * opening it for writing will not cause truncation. (It may
  315. * not _succeed_ either, but that's not our problem here!)
  316. */
  317. return false;
  318. }
  319. if (attrs.nFileSizeHigh == 0 && attrs.nFileSizeLow == 0) {
  320. /*
  321. * File is zero-length (or may be a named pipe, which
  322. * dwFileAttributes can't tell apart from a regular file), so
  323. * opening it for writing won't truncate any data away because
  324. * there's nothing to truncate anyway.
  325. */
  326. return false;
  327. }
  328. return true;
  329. }
  330. void escape_registry_key(const char *in, strbuf *out)
  331. {
  332. bool candot = false;
  333. static const char hex[16] = "0123456789ABCDEF";
  334. while (*in) {
  335. if (*in == ' ' || *in == '\\' || *in == '*' || *in == '?' ||
  336. *in == '%' || *in < ' ' || *in > '~' || (*in == '.'
  337. && !candot)) {
  338. put_byte(out, '%');
  339. put_byte(out, hex[((unsigned char) *in) >> 4]);
  340. put_byte(out, hex[((unsigned char) *in) & 15]);
  341. } else
  342. put_byte(out, *in);
  343. in++;
  344. candot = true;
  345. }
  346. }
  347. void unescape_registry_key(const char *in, strbuf *out)
  348. {
  349. while (*in) {
  350. if (*in == '%' && in[1] && in[2]) {
  351. int i, j;
  352. i = in[1] - '0';
  353. i -= (i > 9 ? 7 : 0);
  354. j = in[2] - '0';
  355. j -= (j > 9 ? 7 : 0);
  356. put_byte(out, (i << 4) + j);
  357. in += 3;
  358. } else {
  359. put_byte(out, *in++);
  360. }
  361. }
  362. }