| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636 |
- /*
- * winmisc.c: miscellaneous Windows-specific things
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include "putty.h"
- #ifndef SECURITY_WIN32
- #define SECURITY_WIN32
- #endif
- #include <security.h>
- #ifdef MPEXT
- #include <assert.h>
- #endif
- OSVERSIONINFO osVersion;
- 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;
- }
- int filename_equal(const Filename *f1, const Filename *f2)
- {
- return !strcmp(f1->path, f2->path);
- }
- int filename_is_null(const Filename *fn)
- {
- return !*fn->path;
- }
- void filename_free(Filename *fn)
- {
- sfree(fn->path);
- sfree(fn);
- }
- int filename_serialise(const Filename *f, void *vdata)
- {
- char *data = (char *)vdata;
- int len = strlen(f->path) + 1; /* include trailing NUL */
- if (data) {
- strcpy(data, f->path);
- }
- return len;
- }
- Filename *filename_deserialise(void *vdata, int maxsize, int *used)
- {
- char *data = (char *)vdata;
- char *end;
- end = memchr(data, '\0', maxsize);
- if (!end)
- return NULL;
- end++;
- *used = end - data;
- return filename_from_str(data);
- }
- 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
- #ifndef NO_SECUREZEROMEMORY
- /*
- * Windows implementation of smemclr (see misc.c) using SecureZeroMemory.
- */
- void smemclr(void *b, size_t n) {
- if (b && n > 0)
- SecureZeroMemory(b, n);
- }
- #endif
- char *get_username(void)
- {
- DWORD namelen;
- char *user;
- int got_username = FALSE;
- DECL_WINDOWS_FUNCTION(static, BOOLEAN, GetUserNameExA,
- (EXTENDED_NAME_FORMAT, LPSTR, PULONG));
- {
- static int 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");
- 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) == FALSE) {
- /*
- * 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);
- }
- }
- BOOL init_winver(void)
- {
- ZeroMemory(&osVersion, sizeof(osVersion));
- osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
- return GetVersionEx ( (OSVERSIONINFO *) &osVersion);
- }
- #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
- char *fullpath;
- HMODULE ret;
- if (!sysdir) {
- int size = 0, len;
- do {
- size = 3*size/2 + 512;
- sysdir = sresize(sysdir, size, char);
- len = GetSystemDirectory(sysdir, size);
- } while (len >= size);
- }
- 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;
- }
- #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
- #ifdef MINEFIELD
- /*
- * Minefield - a Windows equivalent for Electric Fence
- */
- #define PAGESIZE 4096
- /*
- * Design:
- *
- * We start by reserving as much virtual address space as Windows
- * will sensibly (or not sensibly) let us have. We flag it all as
- * invalid memory.
- *
- * Any allocation attempt is satisfied by committing one or more
- * pages, with an uncommitted page on either side. The returned
- * memory region is jammed up against the _end_ of the pages.
- *
- * Freeing anything causes instantaneous decommitment of the pages
- * involved, so stale pointers are caught as soon as possible.
- */
- static int minefield_initialised = 0;
- static void *minefield_region = NULL;
- static long minefield_size = 0;
- static long minefield_npages = 0;
- static long minefield_curpos = 0;
- static unsigned short *minefield_admin = NULL;
- static void *minefield_pages = NULL;
- static void minefield_admin_hide(int hide)
- {
- int access = hide ? PAGE_NOACCESS : PAGE_READWRITE;
- VirtualProtect(minefield_admin, minefield_npages * 2, access, NULL);
- }
- static void minefield_init(void)
- {
- int size;
- int admin_size;
- int i;
- for (size = 0x40000000; size > 0; size = ((size >> 3) * 7) & ~0xFFF) {
- minefield_region = VirtualAlloc(NULL, size,
- MEM_RESERVE, PAGE_NOACCESS);
- if (minefield_region)
- break;
- }
- minefield_size = size;
- /*
- * Firstly, allocate a section of that to be the admin block.
- * We'll need a two-byte field for each page.
- */
- minefield_admin = minefield_region;
- minefield_npages = minefield_size / PAGESIZE;
- admin_size = (minefield_npages * 2 + PAGESIZE - 1) & ~(PAGESIZE - 1);
- minefield_npages = (minefield_size - admin_size) / PAGESIZE;
- minefield_pages = (char *) minefield_region + admin_size;
- /*
- * Commit the admin region.
- */
- VirtualAlloc(minefield_admin, minefield_npages * 2,
- MEM_COMMIT, PAGE_READWRITE);
- /*
- * Mark all pages as unused (0xFFFF).
- */
- for (i = 0; i < minefield_npages; i++)
- minefield_admin[i] = 0xFFFF;
- /*
- * Hide the admin region.
- */
- minefield_admin_hide(1);
- minefield_initialised = 1;
- }
- static void minefield_bomb(void)
- {
- div(1, *(int *) minefield_pages);
- }
- static void *minefield_alloc(int size)
- {
- int npages;
- int pos, lim, region_end, region_start;
- int start;
- int i;
- npages = (size + PAGESIZE - 1) / PAGESIZE;
- minefield_admin_hide(0);
- /*
- * Search from current position until we find a contiguous
- * bunch of npages+2 unused pages.
- */
- pos = minefield_curpos;
- lim = minefield_npages;
- while (1) {
- /* Skip over used pages. */
- while (pos < lim && minefield_admin[pos] != 0xFFFF)
- pos++;
- /* Count unused pages. */
- start = pos;
- while (pos < lim && pos - start < npages + 2 &&
- minefield_admin[pos] == 0xFFFF)
- pos++;
- if (pos - start == npages + 2)
- break;
- /* If we've reached the limit, reset the limit or stop. */
- if (pos >= lim) {
- if (lim == minefield_npages) {
- /* go round and start again at zero */
- lim = minefield_curpos;
- pos = 0;
- } else {
- minefield_admin_hide(1);
- return NULL;
- }
- }
- }
- minefield_curpos = pos - 1;
- /*
- * We have npages+2 unused pages starting at start. We leave
- * the first and last of these alone and use the rest.
- */
- region_end = (start + npages + 1) * PAGESIZE;
- region_start = region_end - size;
- /* FIXME: could align here if we wanted */
- /*
- * Update the admin region.
- */
- for (i = start + 2; i < start + npages + 1; i++)
- minefield_admin[i] = 0xFFFE; /* used but no region starts here */
- minefield_admin[start + 1] = region_start % PAGESIZE;
- minefield_admin_hide(1);
- VirtualAlloc((char *) minefield_pages + region_start, size,
- MEM_COMMIT, PAGE_READWRITE);
- return (char *) minefield_pages + region_start;
- }
- static void minefield_free(void *ptr)
- {
- int region_start, i, j;
- minefield_admin_hide(0);
- region_start = (char *) ptr - (char *) minefield_pages;
- i = region_start / PAGESIZE;
- if (i < 0 || i >= minefield_npages ||
- minefield_admin[i] != region_start % PAGESIZE)
- minefield_bomb();
- for (j = i; j < minefield_npages && minefield_admin[j] != 0xFFFF; j++) {
- minefield_admin[j] = 0xFFFF;
- }
- VirtualFree(ptr, j * PAGESIZE - region_start, MEM_DECOMMIT);
- minefield_admin_hide(1);
- }
- static int minefield_get_size(void *ptr)
- {
- int region_start, i, j;
- minefield_admin_hide(0);
- region_start = (char *) ptr - (char *) minefield_pages;
- i = region_start / PAGESIZE;
- if (i < 0 || i >= minefield_npages ||
- minefield_admin[i] != region_start % PAGESIZE)
- minefield_bomb();
- for (j = i; j < minefield_npages && minefield_admin[j] != 0xFFFF; j++);
- minefield_admin_hide(1);
- return j * PAGESIZE - region_start;
- }
- void *minefield_c_malloc(size_t size)
- {
- if (!minefield_initialised)
- minefield_init();
- return minefield_alloc(size);
- }
- void minefield_c_free(void *p)
- {
- if (!minefield_initialised)
- minefield_init();
- minefield_free(p);
- }
- /*
- * realloc _always_ moves the chunk, for rapid detection of code
- * that assumes it won't.
- */
- void *minefield_c_realloc(void *p, size_t size)
- {
- size_t oldsize;
- void *q;
- if (!minefield_initialised)
- minefield_init();
- q = minefield_alloc(size);
- oldsize = minefield_get_size(p);
- memcpy(q, p, (oldsize < size ? oldsize : size));
- minefield_free(p);
- return q;
- }
- #endif /* MINEFIELD */
- FontSpec *fontspec_new(const char *name,
- int 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);
- }
- int fontspec_serialise(FontSpec *f, void *vdata)
- {
- char *data = (char *)vdata;
- int len = strlen(f->name) + 1; /* include trailing NUL */
- if (data) {
- strcpy(data, f->name);
- PUT_32BIT_MSB_FIRST(data + len, f->isbold);
- PUT_32BIT_MSB_FIRST(data + len + 4, f->height);
- PUT_32BIT_MSB_FIRST(data + len + 8, f->charset);
- }
- return len + 12; /* also include three 4-byte ints */
- }
- FontSpec *fontspec_deserialise(void *vdata, int maxsize, int *used)
- {
- char *data = (char *)vdata;
- char *end;
- if (maxsize < 13)
- return NULL;
- end = memchr(data, '\0', maxsize-12);
- if (!end)
- return NULL;
- end++;
- *used = end - data + 12;
- return fontspec_new(data,
- GET_32BIT_MSB_FIRST(end),
- GET_32BIT_MSB_FIRST(end + 4),
- GET_32BIT_MSB_FIRST(end + 8));
- }
|