| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521 |
- /*
- * winmisc.c: miscellaneous Windows-specific things
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <limits.h>
- #include "putty.h"
- #ifndef SECURITY_WIN32
- #define SECURITY_WIN32
- #endif
- #include <security.h>
- #ifdef MPEXT
- #include <assert.h>
- #endif
- DWORD osMajorVersion, osMinorVersion, osPlatformId;
- char *platform_get_x_display(void) {
- /* We may as well check for DISPLAY in case it's useful. */
- return dupstr(getenv("DISPLAY"));
- }
- Filename *filename_from_str(const char *str)
- {
- Filename *ret = snew(Filename);
- ret->path = dupstr(str);
- return ret;
- }
- Filename *filename_copy(const Filename *fn)
- {
- return filename_from_str(fn->path);
- }
- const char *filename_to_str(const Filename *fn)
- {
- return fn->path;
- }
- bool filename_equal(const Filename *f1, const Filename *f2)
- {
- return !strcmp(f1->path, f2->path);
- }
- bool filename_is_null(const Filename *fn)
- {
- return !*fn->path;
- }
- void filename_free(Filename *fn)
- {
- sfree(fn->path);
- sfree(fn);
- }
- void filename_serialise(BinarySink *bs, const Filename *f)
- {
- put_asciz(bs, f->path);
- }
- Filename *filename_deserialise(BinarySource *src)
- {
- return filename_from_str(get_asciz(src));
- }
- char filename_char_sanitise(char c)
- {
- if (strchr("<>:\"/\\|?*", c))
- return '.';
- return c;
- }
- #ifdef MPEXT
- FILE * mp_wfopen(const char *filename, const char *mode)
- {
- size_t len = strlen(filename);
- wchar_t * wfilename = snewn(len * 10, wchar_t);
- size_t wlen = MultiByteToWideChar(CP_UTF8, 0, filename, -1, wfilename, len * 10);
- FILE * file;
- if (wlen <= 0)
- {
- file = NULL;
- }
- else
- {
- wchar_t wmode[3];
- memset(wmode, 0, sizeof(wmode));
- wmode[0] = (wchar_t)mode[0];
- if (mode[0] != '\0')
- {
- wmode[1] = (wchar_t)mode[1];
- if (mode[1] != '\0')
- {
- assert(mode[2] == '\0');
- }
- }
- file = _wfopen(wfilename, wmode);
- }
- sfree(wfilename);
- return file;
- }
- #endif
- char *get_username(void)
- {
- DWORD namelen;
- char *user;
- bool got_username = false;
- DECL_WINDOWS_FUNCTION(static, BOOLEAN, GetUserNameExA,
- (EXTENDED_NAME_FORMAT, LPSTR, PULONG));
- {
- static bool tried_usernameex = false;
- if (!tried_usernameex) {
- /* Not available on Win9x, so load dynamically */
- HMODULE secur32 = load_system32_dll("secur32.dll");
- /* If MIT Kerberos is installed, the following call to
- GET_WINDOWS_FUNCTION makes Windows implicitly load
- sspicli.dll WITHOUT proper path sanitizing, so better
- load it properly before */
- HMODULE sspicli = load_system32_dll("sspicli.dll");
- (void)sspicli; /* squash compiler warning about unused variable */
- GET_WINDOWS_FUNCTION(secur32, GetUserNameExA);
- tried_usernameex = true;
- }
- }
- if (p_GetUserNameExA) {
- /*
- * If available, use the principal -- this avoids the problem
- * that the local username is case-insensitive but Kerberos
- * usernames are case-sensitive.
- */
- /* Get the length */
- namelen = 0;
- (void) p_GetUserNameExA(NameUserPrincipal, NULL, &namelen);
- user = snewn(namelen, char);
- got_username = p_GetUserNameExA(NameUserPrincipal, user, &namelen);
- if (got_username) {
- char *p = strchr(user, '@');
- if (p) *p = 0;
- } else {
- sfree(user);
- }
- }
- if (!got_username) {
- /* Fall back to local user name */
- namelen = 0;
- if (!GetUserName(NULL, &namelen)) {
- /*
- * Apparently this doesn't work at least on Windows XP SP2.
- * Thus assume a maximum of 256. It will fail again if it
- * doesn't fit.
- */
- namelen = 256;
- }
- user = snewn(namelen, char);
- got_username = GetUserName(user, &namelen);
- if (!got_username) {
- sfree(user);
- }
- }
- return got_username ? user : NULL;
- }
- void dll_hijacking_protection(void)
- {
- /*
- * If the OS provides it, call SetDefaultDllDirectories() to
- * prevent DLLs from being loaded from the directory containing
- * our own binary, and instead only load from system32.
- *
- * This is a protection against hijacking attacks, if someone runs
- * PuTTY directly from their web browser's download directory
- * having previously been enticed into clicking on an unwise link
- * that downloaded a malicious DLL to the same directory under one
- * of various magic names that seem to be things that standard
- * Windows DLLs delegate to.
- *
- * It shouldn't break deliberate loading of user-provided DLLs
- * such as GSSAPI providers, because those are specified by their
- * full pathname by the user-provided configuration.
- */
- static HMODULE kernel32_module;
- DECL_WINDOWS_FUNCTION(static, BOOL, SetDefaultDllDirectories, (DWORD));
- if (!kernel32_module) {
- kernel32_module = load_system32_dll("kernel32.dll");
- #if (defined _MSC_VER && _MSC_VER < 1900) || defined COVERITY || defined MPEXT
- /* For older Visual Studio, and also for the system I
- * currently use for Coveritying the Windows code, this
- * function isn't available in the header files to
- * type-check */
- GET_WINDOWS_FUNCTION_NO_TYPECHECK(
- kernel32_module, SetDefaultDllDirectories);
- #else
- GET_WINDOWS_FUNCTION(kernel32_module, SetDefaultDllDirectories);
- #endif
- }
- if (p_SetDefaultDllDirectories) {
- /* LOAD_LIBRARY_SEARCH_SYSTEM32 and explicitly specified
- * directories only */
- p_SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32 |
- LOAD_LIBRARY_SEARCH_USER_DIRS);
- }
- }
- void init_winver(void)
- {
- OSVERSIONINFO osVersion;
- static HMODULE kernel32_module;
- DECL_WINDOWS_FUNCTION(static, BOOL, GetVersionExA, (LPOSVERSIONINFO));
- if (!kernel32_module) {
- kernel32_module = load_system32_dll("kernel32.dll");
- /* Deliberately don't type-check this function, because that
- * would involve using its declaration in a header file which
- * triggers a deprecation warning. I know it's deprecated (see
- * below) and don't need telling. */
- GET_WINDOWS_FUNCTION_NO_TYPECHECK(kernel32_module, GetVersionExA);
- }
- ZeroMemory(&osVersion, sizeof(osVersion));
- osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
- if (p_GetVersionExA && p_GetVersionExA(&osVersion)) {
- osMajorVersion = osVersion.dwMajorVersion;
- osMinorVersion = osVersion.dwMinorVersion;
- osPlatformId = osVersion.dwPlatformId;
- } else {
- /*
- * GetVersionEx is deprecated, so allow for it perhaps going
- * away in future API versions. If it's not there, simply
- * assume that's because Windows is too _new_, so fill in the
- * variables we care about to a value that will always compare
- * higher than any given test threshold.
- *
- * Normally we should be checking against the presence of a
- * specific function if possible in any case.
- */
- osMajorVersion = osMinorVersion = UINT_MAX; /* a very high number */
- osPlatformId = VER_PLATFORM_WIN32_NT; /* not Win32s or Win95-like */
- }
- }
- #ifdef MPEXT
- static char *sysdir = NULL;
- void win_misc_cleanup()
- {
- sfree(sysdir);
- }
- #endif
- HMODULE load_system32_dll(const char *libname)
- {
- /*
- * Wrapper function to load a DLL out of c:\windows\system32
- * without going through the full DLL search path. (Hence no
- * attack is possible by placing a substitute DLL earlier on that
- * path.)
- */
- #ifndef MPEXT
- static char *sysdir = NULL;
- #endif
- static size_t sysdirsize = 0;
- char *fullpath;
- HMODULE ret;
- if (!sysdir) {
- size_t len;
- while ((len = GetSystemDirectory(sysdir, sysdirsize)) >= sysdirsize)
- sgrowarray(sysdir, sysdirsize, len);
- }
- fullpath = dupcat(sysdir, "\\", libname, NULL);
- ret = LoadLibrary(fullpath);
- sfree(fullpath);
- return ret;
- }
- /*
- * A tree234 containing mappings from system error codes to strings.
- */
- struct errstring {
- int error;
- char *text;
- };
- static int errstring_find(void *av, void *bv)
- {
- int *a = (int *)av;
- struct errstring *b = (struct errstring *)bv;
- if (*a < b->error)
- return -1;
- if (*a > b->error)
- return +1;
- return 0;
- }
- static int errstring_compare(void *av, void *bv)
- {
- struct errstring *a = (struct errstring *)av;
- return errstring_find(&a->error, bv);
- }
- static tree234 *errstrings = NULL;
- const char *win_strerror(int error)
- {
- struct errstring *es;
- if (!errstrings)
- errstrings = newtree234(errstring_compare);
- es = find234(errstrings, &error, errstring_find);
- if (!es) {
- char msgtext[65536]; /* maximum size for FormatMessage is 64K */
- es = snew(struct errstring);
- es->error = error;
- if (!FormatMessage((FORMAT_MESSAGE_FROM_SYSTEM |
- FORMAT_MESSAGE_IGNORE_INSERTS), NULL, error,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- msgtext, lenof(msgtext)-1, NULL)) {
- sprintf(msgtext,
- "(unable to format: FormatMessage returned %u)",
- (unsigned int)GetLastError());
- } else {
- int len = strlen(msgtext);
- if (len > 0 && msgtext[len-1] == '\n')
- msgtext[len-1] = '\0';
- }
- es->text = dupprintf("Error %d: %s", error, msgtext);
- add234(errstrings, es);
- }
- return es->text;
- }
- FontSpec *fontspec_new(const char *name, bool bold, int height, int charset)
- {
- FontSpec *f = snew(FontSpec);
- f->name = dupstr(name);
- f->isbold = bold;
- f->height = height;
- f->charset = charset;
- return f;
- }
- FontSpec *fontspec_copy(const FontSpec *f)
- {
- return fontspec_new(f->name, f->isbold, f->height, f->charset);
- }
- void fontspec_free(FontSpec *f)
- {
- sfree(f->name);
- sfree(f);
- }
- void fontspec_serialise(BinarySink *bs, FontSpec *f)
- {
- put_asciz(bs, f->name);
- put_uint32(bs, f->isbold);
- put_uint32(bs, f->height);
- put_uint32(bs, f->charset);
- }
- FontSpec *fontspec_deserialise(BinarySource *src)
- {
- const char *name = get_asciz(src);
- unsigned isbold = get_uint32(src);
- unsigned height = get_uint32(src);
- unsigned charset = get_uint32(src);
- return fontspec_new(name, isbold, height, charset);
- }
- bool open_for_write_would_lose_data(const Filename *fn)
- {
- WIN32_FILE_ATTRIBUTE_DATA attrs;
- if (!GetFileAttributesEx(fn->path, GetFileExInfoStandard, &attrs)) {
- /*
- * Generally, if we don't identify a specific reason why we
- * should return true from this function, we return false, and
- * let the subsequent attempt to open the file for real give a
- * more useful error message.
- */
- return false;
- }
- if (attrs.dwFileAttributes & (FILE_ATTRIBUTE_DEVICE |
- FILE_ATTRIBUTE_DIRECTORY)) {
- /*
- * File is something other than an ordinary disk file, so
- * opening it for writing will not cause truncation. (It may
- * not _succeed_ either, but that's not our problem here!)
- */
- return false;
- }
- if (attrs.nFileSizeHigh == 0 && attrs.nFileSizeLow == 0) {
- /*
- * File is zero-length (or may be a named pipe, which
- * dwFileAttributes can't tell apart from a regular file), so
- * opening it for writing won't truncate any data away because
- * there's nothing to truncate anyway.
- */
- return false;
- }
- return true;
- }
- void escape_registry_key(const char *in, strbuf *out)
- {
- bool candot = false;
- static const char hex[16] = "0123456789ABCDEF";
- while (*in) {
- if (*in == ' ' || *in == '\\' || *in == '*' || *in == '?' ||
- *in == '%' || *in < ' ' || *in > '~' || (*in == '.'
- && !candot)) {
- put_byte(out, '%');
- put_byte(out, hex[((unsigned char) *in) >> 4]);
- put_byte(out, hex[((unsigned char) *in) & 15]);
- } else
- put_byte(out, *in);
- in++;
- candot = true;
- }
- }
- void unescape_registry_key(const char *in, strbuf *out)
- {
- while (*in) {
- if (*in == '%' && in[1] && in[2]) {
- int i, j;
- i = in[1] - '0';
- i -= (i > 9 ? 7 : 0);
- j = in[2] - '0';
- j -= (j > 9 ? 7 : 0);
- put_byte(out, (i << 4) + j);
- in += 3;
- } else {
- put_byte(out, *in++);
- }
- }
- }
- #ifdef DEBUG
- static FILE *debug_fp = NULL;
- static HANDLE debug_hdl = INVALID_HANDLE_VALUE;
- static int debug_got_console = 0;
- void dputs(const char *buf)
- {
- DWORD dw;
- if (!debug_got_console) {
- if (AllocConsole()) {
- debug_got_console = 1;
- debug_hdl = GetStdHandle(STD_OUTPUT_HANDLE);
- }
- }
- if (!debug_fp) {
- debug_fp = fopen("debug.log", "w");
- }
- if (debug_hdl != INVALID_HANDLE_VALUE) {
- WriteFile(debug_hdl, buf, strlen(buf), &dw, NULL);
- }
- fputs(buf, debug_fp);
- fflush(debug_fp);
- }
- #endif
- char *registry_get_string(HKEY root, const char *path, const char *leaf)
- {
- HKEY key = root;
- bool need_close_key = false;
- char *toret = NULL, *str = NULL;
- if (path) {
- if (RegCreateKey(key, path, &key) != ERROR_SUCCESS)
- goto out;
- need_close_key = true;
- }
- { // WINSCP
- DWORD type, size;
- if (RegQueryValueEx(key, leaf, 0, &type, NULL, &size) != ERROR_SUCCESS)
- goto out;
- if (type != REG_SZ)
- goto out;
- str = snewn(size + 1, char);
- { // WINSCP
- DWORD size_got = size;
- if (RegQueryValueEx(key, leaf, 0, &type, (LPBYTE)str,
- &size_got) != ERROR_SUCCESS)
- goto out;
- if (type != REG_SZ || size_got > size)
- goto out;
- str[size_got] = '\0';
- toret = str;
- str = NULL;
- out:
- if (need_close_key)
- RegCloseKey(key);
- sfree(str);
- return toret;
- } // WINSCP
- } // WINSCP
- }
|