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