source: filezilla/trunk/fuentes/src/putty/windows/winmisc.c @ 130

Last change on this file since 130 was 130, checked in by jrpelegrina, 3 years ago

First release to xenial

File size: 12.7 KB
Line 
1/*
2 * winmisc.c: miscellaneous Windows-specific things
3 */
4
5#include <stdio.h>
6#include <stdlib.h>
7#include "putty.h"
8#ifndef SECURITY_WIN32
9#define SECURITY_WIN32
10#endif
11#include <security.h>
12
13OSVERSIONINFO osVersion;
14
15char *platform_get_x_display(void) {
16    /* We may as well check for DISPLAY in case it's useful. */
17    return dupstr(getenv("DISPLAY"));
18}
19
20Filename *filename_from_str(const char *str)
21{
22    Filename *ret = snew(Filename);
23    ret->path = dupstr(str);
24    return ret;
25}
26
27Filename *filename_copy(const Filename *fn)
28{
29    return filename_from_str(fn->path);
30}
31
32const char *filename_to_str(const Filename *fn)
33{
34    return fn->path;
35}
36
37int filename_equal(const Filename *f1, const Filename *f2)
38{
39    return !strcmp(f1->path, f2->path);
40}
41
42int filename_is_null(const Filename *fn)
43{
44    return !*fn->path;
45}
46
47void filename_free(Filename *fn)
48{
49    sfree(fn->path);
50    sfree(fn);
51}
52
53int filename_serialise(const Filename *f, void *vdata)
54{
55    char *data = (char *)vdata;
56    int len = strlen(f->path) + 1;     /* include trailing NUL */
57    if (data) {
58        strcpy(data, f->path);
59    }
60    return len;
61}
62Filename *filename_deserialise(void *vdata, int maxsize, int *used)
63{
64    char *data = (char *)vdata;
65    char *end;
66    end = memchr(data, '\0', maxsize);
67    if (!end)
68        return NULL;
69    end++;
70    *used = end - data;
71    return filename_from_str(data);
72}
73
74char filename_char_sanitise(char c)
75{
76    if (strchr("<>:\"/\\|?*", c))
77        return '.';
78    return c;
79}
80
81#ifndef NO_SECUREZEROMEMORY
82/*
83 * Windows implementation of smemclr (see misc.c) using SecureZeroMemory.
84 */
85void smemclr(void *b, size_t n) {
86    if (b && n > 0)
87        SecureZeroMemory(b, n);
88}
89#endif
90
91char *get_username(void)
92{
93    DWORD namelen;
94    char *user;
95    int got_username = FALSE;
96    DECL_WINDOWS_FUNCTION(static, BOOLEAN, GetUserNameExA,
97                          (EXTENDED_NAME_FORMAT, LPSTR, PULONG));
98
99    {
100        static int tried_usernameex = FALSE;
101        if (!tried_usernameex) {
102            /* Not available on Win9x, so load dynamically */
103            HMODULE secur32 = load_system32_dll("secur32.dll");
104            GET_WINDOWS_FUNCTION(secur32, GetUserNameExA);
105            tried_usernameex = TRUE;
106        }
107    }
108
109    if (p_GetUserNameExA) {
110        /*
111         * If available, use the principal -- this avoids the problem
112         * that the local username is case-insensitive but Kerberos
113         * usernames are case-sensitive.
114         */
115
116        /* Get the length */
117        namelen = 0;
118        (void) p_GetUserNameExA(NameUserPrincipal, NULL, &namelen);
119
120        user = snewn(namelen, char);
121        got_username = p_GetUserNameExA(NameUserPrincipal, user, &namelen);
122        if (got_username) {
123            char *p = strchr(user, '@');
124            if (p) *p = 0;
125        } else {
126            sfree(user);
127        }
128    }
129
130    if (!got_username) {
131        /* Fall back to local user name */
132        namelen = 0;
133        if (GetUserName(NULL, &namelen) == FALSE) {
134            /*
135             * Apparently this doesn't work at least on Windows XP SP2.
136             * Thus assume a maximum of 256. It will fail again if it
137             * doesn't fit.
138             */
139            namelen = 256;
140        }
141
142        user = snewn(namelen, char);
143        got_username = GetUserName(user, &namelen);
144        if (!got_username) {
145            sfree(user);
146        }
147    }
148
149    return got_username ? user : NULL;
150}
151
152BOOL init_winver(void)
153{
154    ZeroMemory(&osVersion, sizeof(osVersion));
155    osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
156    return GetVersionEx ( (OSVERSIONINFO *) &osVersion);
157}
158
159HMODULE load_system32_dll(const char *libname)
160{
161    /*
162     * Wrapper function to load a DLL out of c:\windows\system32
163     * without going through the full DLL search path. (Hence no
164     * attack is possible by placing a substitute DLL earlier on that
165     * path.)
166     */
167    static char *sysdir = NULL;
168    char *fullpath;
169    HMODULE ret;
170
171    if (!sysdir) {
172        int size = 0, len;
173        do {
174            size = 3*size/2 + 512;
175            sysdir = sresize(sysdir, size, char);
176            len = GetSystemDirectory(sysdir, size);
177        } while (len >= size);
178    }
179
180    fullpath = dupcat(sysdir, "\\", libname, NULL);
181    ret = LoadLibrary(fullpath);
182    sfree(fullpath);
183    return ret;
184}
185
186/*
187 * A tree234 containing mappings from system error codes to strings.
188 */
189
190struct errstring {
191    int error;
192    char *text;
193};
194
195static int errstring_find(void *av, void *bv)
196{
197    int *a = (int *)av;
198    struct errstring *b = (struct errstring *)bv;
199    if (*a < b->error)
200        return -1;
201    if (*a > b->error)
202        return +1;
203    return 0;
204}
205static int errstring_compare(void *av, void *bv)
206{
207    struct errstring *a = (struct errstring *)av;
208    return errstring_find(&a->error, bv);
209}
210
211static tree234 *errstrings = NULL;
212
213const char *win_strerror(int error)
214{
215    struct errstring *es;
216
217    if (!errstrings)
218        errstrings = newtree234(errstring_compare);
219
220    es = find234(errstrings, &error, errstring_find);
221
222    if (!es) {
223        char msgtext[65536]; /* maximum size for FormatMessage is 64K */
224
225        es = snew(struct errstring);
226        es->error = error;
227        if (!FormatMessage((FORMAT_MESSAGE_FROM_SYSTEM |
228                            FORMAT_MESSAGE_IGNORE_INSERTS), NULL, error,
229                           MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
230                           msgtext, lenof(msgtext)-1, NULL)) {
231            sprintf(msgtext,
232                    "(unable to format: FormatMessage returned %u)",
233                    (unsigned int)GetLastError());
234        } else {
235            int len = strlen(msgtext);
236            if (len > 0 && msgtext[len-1] == '\n')
237                msgtext[len-1] = '\0';
238        }
239        es->text = dupprintf("Error %d: %s", error, msgtext);
240        add234(errstrings, es);
241    }
242
243    return es->text;
244}
245
246#ifdef DEBUG
247static FILE *debug_fp = NULL;
248static HANDLE debug_hdl = INVALID_HANDLE_VALUE;
249static int debug_got_console = 0;
250
251void dputs(const char *buf)
252{
253    DWORD dw;
254
255    if (!debug_got_console) {
256        if (AllocConsole()) {
257            debug_got_console = 1;
258            debug_hdl = GetStdHandle(STD_OUTPUT_HANDLE);
259        }
260    }
261    if (!debug_fp) {
262        debug_fp = fopen("debug.log", "w");
263    }
264
265    if (debug_hdl != INVALID_HANDLE_VALUE) {
266        WriteFile(debug_hdl, buf, strlen(buf), &dw, NULL);
267    }
268    fputs(buf, debug_fp);
269    fflush(debug_fp);
270}
271#endif
272
273#ifdef MINEFIELD
274/*
275 * Minefield - a Windows equivalent for Electric Fence
276 */
277
278#define PAGESIZE 4096
279
280/*
281 * Design:
282 *
283 * We start by reserving as much virtual address space as Windows
284 * will sensibly (or not sensibly) let us have. We flag it all as
285 * invalid memory.
286 *
287 * Any allocation attempt is satisfied by committing one or more
288 * pages, with an uncommitted page on either side. The returned
289 * memory region is jammed up against the _end_ of the pages.
290 *
291 * Freeing anything causes instantaneous decommitment of the pages
292 * involved, so stale pointers are caught as soon as possible.
293 */
294
295static int minefield_initialised = 0;
296static void *minefield_region = NULL;
297static long minefield_size = 0;
298static long minefield_npages = 0;
299static long minefield_curpos = 0;
300static unsigned short *minefield_admin = NULL;
301static void *minefield_pages = NULL;
302
303static void minefield_admin_hide(int hide)
304{
305    int access = hide ? PAGE_NOACCESS : PAGE_READWRITE;
306    VirtualProtect(minefield_admin, minefield_npages * 2, access, NULL);
307}
308
309static void minefield_init(void)
310{
311    int size;
312    int admin_size;
313    int i;
314
315    for (size = 0x40000000; size > 0; size = ((size >> 3) * 7) & ~0xFFF) {
316        minefield_region = VirtualAlloc(NULL, size,
317                                        MEM_RESERVE, PAGE_NOACCESS);
318        if (minefield_region)
319            break;
320    }
321    minefield_size = size;
322
323    /*
324     * Firstly, allocate a section of that to be the admin block.
325     * We'll need a two-byte field for each page.
326     */
327    minefield_admin = minefield_region;
328    minefield_npages = minefield_size / PAGESIZE;
329    admin_size = (minefield_npages * 2 + PAGESIZE - 1) & ~(PAGESIZE - 1);
330    minefield_npages = (minefield_size - admin_size) / PAGESIZE;
331    minefield_pages = (char *) minefield_region + admin_size;
332
333    /*
334     * Commit the admin region.
335     */
336    VirtualAlloc(minefield_admin, minefield_npages * 2,
337                 MEM_COMMIT, PAGE_READWRITE);
338
339    /*
340     * Mark all pages as unused (0xFFFF).
341     */
342    for (i = 0; i < minefield_npages; i++)
343        minefield_admin[i] = 0xFFFF;
344
345    /*
346     * Hide the admin region.
347     */
348    minefield_admin_hide(1);
349
350    minefield_initialised = 1;
351}
352
353static void minefield_bomb(void)
354{
355    div(1, *(int *) minefield_pages);
356}
357
358static void *minefield_alloc(int size)
359{
360    int npages;
361    int pos, lim, region_end, region_start;
362    int start;
363    int i;
364
365    npages = (size + PAGESIZE - 1) / PAGESIZE;
366
367    minefield_admin_hide(0);
368
369    /*
370     * Search from current position until we find a contiguous
371     * bunch of npages+2 unused pages.
372     */
373    pos = minefield_curpos;
374    lim = minefield_npages;
375    while (1) {
376        /* Skip over used pages. */
377        while (pos < lim && minefield_admin[pos] != 0xFFFF)
378            pos++;
379        /* Count unused pages. */
380        start = pos;
381        while (pos < lim && pos - start < npages + 2 &&
382               minefield_admin[pos] == 0xFFFF)
383            pos++;
384        if (pos - start == npages + 2)
385            break;
386        /* If we've reached the limit, reset the limit or stop. */
387        if (pos >= lim) {
388            if (lim == minefield_npages) {
389                /* go round and start again at zero */
390                lim = minefield_curpos;
391                pos = 0;
392            } else {
393                minefield_admin_hide(1);
394                return NULL;
395            }
396        }
397    }
398
399    minefield_curpos = pos - 1;
400
401    /*
402     * We have npages+2 unused pages starting at start. We leave
403     * the first and last of these alone and use the rest.
404     */
405    region_end = (start + npages + 1) * PAGESIZE;
406    region_start = region_end - size;
407    /* FIXME: could align here if we wanted */
408
409    /*
410     * Update the admin region.
411     */
412    for (i = start + 2; i < start + npages + 1; i++)
413        minefield_admin[i] = 0xFFFE;   /* used but no region starts here */
414    minefield_admin[start + 1] = region_start % PAGESIZE;
415
416    minefield_admin_hide(1);
417
418    VirtualAlloc((char *) minefield_pages + region_start, size,
419                 MEM_COMMIT, PAGE_READWRITE);
420    return (char *) minefield_pages + region_start;
421}
422
423static void minefield_free(void *ptr)
424{
425    int region_start, i, j;
426
427    minefield_admin_hide(0);
428
429    region_start = (char *) ptr - (char *) minefield_pages;
430    i = region_start / PAGESIZE;
431    if (i < 0 || i >= minefield_npages ||
432        minefield_admin[i] != region_start % PAGESIZE)
433        minefield_bomb();
434    for (j = i; j < minefield_npages && minefield_admin[j] != 0xFFFF; j++) {
435        minefield_admin[j] = 0xFFFF;
436    }
437
438    VirtualFree(ptr, j * PAGESIZE - region_start, MEM_DECOMMIT);
439
440    minefield_admin_hide(1);
441}
442
443static int minefield_get_size(void *ptr)
444{
445    int region_start, i, j;
446
447    minefield_admin_hide(0);
448
449    region_start = (char *) ptr - (char *) minefield_pages;
450    i = region_start / PAGESIZE;
451    if (i < 0 || i >= minefield_npages ||
452        minefield_admin[i] != region_start % PAGESIZE)
453        minefield_bomb();
454    for (j = i; j < minefield_npages && minefield_admin[j] != 0xFFFF; j++);
455
456    minefield_admin_hide(1);
457
458    return j * PAGESIZE - region_start;
459}
460
461void *minefield_c_malloc(size_t size)
462{
463    if (!minefield_initialised)
464        minefield_init();
465    return minefield_alloc(size);
466}
467
468void minefield_c_free(void *p)
469{
470    if (!minefield_initialised)
471        minefield_init();
472    minefield_free(p);
473}
474
475/*
476 * realloc _always_ moves the chunk, for rapid detection of code
477 * that assumes it won't.
478 */
479void *minefield_c_realloc(void *p, size_t size)
480{
481    size_t oldsize;
482    void *q;
483    if (!minefield_initialised)
484        minefield_init();
485    q = minefield_alloc(size);
486    oldsize = minefield_get_size(p);
487    memcpy(q, p, (oldsize < size ? oldsize : size));
488    minefield_free(p);
489    return q;
490}
491
492#endif                          /* MINEFIELD */
493
494FontSpec *fontspec_new(const char *name,
495                        int bold, int height, int charset)
496{
497    FontSpec *f = snew(FontSpec);
498    f->name = dupstr(name);
499    f->isbold = bold;
500    f->height = height;
501    f->charset = charset;
502    return f;
503}
504FontSpec *fontspec_copy(const FontSpec *f)
505{
506    return fontspec_new(f->name, f->isbold, f->height, f->charset);
507}
508void fontspec_free(FontSpec *f)
509{
510    sfree(f->name);
511    sfree(f);
512}
513int fontspec_serialise(FontSpec *f, void *vdata)
514{
515    char *data = (char *)vdata;
516    int len = strlen(f->name) + 1;     /* include trailing NUL */
517    if (data) {
518        strcpy(data, f->name);
519        PUT_32BIT_MSB_FIRST(data + len, f->isbold);
520        PUT_32BIT_MSB_FIRST(data + len + 4, f->height);
521        PUT_32BIT_MSB_FIRST(data + len + 8, f->charset);
522    }
523    return len + 12;                   /* also include three 4-byte ints */
524}
525FontSpec *fontspec_deserialise(void *vdata, int maxsize, int *used)
526{
527    char *data = (char *)vdata;
528    char *end;
529    if (maxsize < 13)
530        return NULL;
531    end = memchr(data, '\0', maxsize-12);
532    if (!end)
533        return NULL;
534    end++;
535    *used = end - data + 12;
536    return fontspec_new(data,
537                        GET_32BIT_MSB_FIRST(end),
538                        GET_32BIT_MSB_FIRST(end + 4),
539                        GET_32BIT_MSB_FIRST(end + 8));
540}
Note: See TracBrowser for help on using the repository browser.