1 /* rndw32.c - W32 entropy gatherer
2 * Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
3 * Copyright Peter Gutmann, Matt Thomlinson and Blake Coverett 1996-1999
4 *
5 * This file is part of GnuPG.
6 *
7 * GnuPG is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * GnuPG is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, see <http://www.gnu.org/licenses/>.
19 *
20 *************************************************************************
21 * The code here is based on code from Cryptlib 3.0 beta by Peter Gutmann.
22 * Source file misc/rndwin32.c "Win32 Randomness-Gathering Code" with this
23 * copyright notice:
24 *
25 * This module is part of the cryptlib continuously seeded pseudorandom
26 * number generator. For usage conditions, see lib_rand.c
27 *
28 * [Here is the notice from lib_rand.c, which is now called dev_sys.c]
29 *
30 * This module and the misc/rnd*.c modules represent the cryptlib
31 * continuously seeded pseudorandom number generator (CSPRNG) as described in
32 * my 1998 Usenix Security Symposium paper "The generation of random numbers
33 * for cryptographic purposes".
34 *
35 * The CSPRNG code is copyright Peter Gutmann (and various others) 1996,
36 * 1997, 1998, 1999, all rights reserved. Redistribution of the CSPRNG
37 * modules and use in source and binary forms, with or without modification,
38 * are permitted provided that the following conditions are met:
39 *
40 * 1. Redistributions of source code must retain the above copyright notice
41 * and this permission notice in its entirety.
42 *
43 * 2. Redistributions in binary form must reproduce the copyright notice in
44 * the documentation and/or other materials provided with the distribution.
45 *
46 * 3. A copy of any bugfixes or enhancements made must be provided to the
47 * author, <pgut001@cs.auckland.ac.nz> to allow them to be added to the
48 * baseline version of the code.
49 *
50 * ALTERNATIVELY, the code may be distributed under the terms of the GNU
51 * General Public License, version 2 or any later version published by the
52 * Free Software Foundation, in which case the provisions of the GNU GPL are
53 * required INSTEAD OF the above restrictions.
54 *
55 * Although not required under the terms of the GPL, it would still be nice if
56 * you could make any changes available to the author to allow a consistent
57 * code base to be maintained
58 *************************************************************************
59 */
60
61 #include <config.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <assert.h>
65 #include <errno.h>
66 #include <string.h>
67
68 #include <windows.h>
69
70
71 #include "types.h"
72 #include "util.h"
73 #include "algorithms.h"
74
75 #include "i18n.h"
76
77
78 static int debug_me;
79
80 /*
81 * Definitions which are missing from the current GNU Windows32Api
82 */
83
84 #ifndef TH32CS_SNAPHEAPLIST
85 #define TH32CS_SNAPHEAPLIST 1
86 #define TH32CS_SNAPPROCESS 2
87 #define TH32CS_SNAPTHREAD 4
88 #define TH32CS_SNAPMODULE 8
89 #define TH32CS_SNAPALL (1|2|4|8)
90 #define TH32CS_INHERIT 0x80000000
91 #endif /*TH32CS_SNAPHEAPLIST*/
92
93 #ifndef IOCTL_DISK_PERFORMANCE
94 #define IOCTL_DISK_PERFORMANCE 0x00070020
95 #endif
96 #ifndef VER_PLATFORM_WIN32_WINDOWS
97 #define VER_PLATFORM_WIN32_WINDOWS 1
98 #endif
99
100 /* This used to be (6*8+5*4+8*2), but Peter Gutmann figured a larger
101 value in a newer release. So we use a far larger value. */
102 #define SIZEOF_DISK_PERFORMANCE_STRUCT 256
103
104
105 typedef struct {
106 DWORD dwSize;
107 DWORD th32ProcessID;
108 DWORD th32HeapID;
109 DWORD dwFlags;
110 } HEAPLIST32;
111
112 typedef struct {
113 DWORD dwSize;
114 HANDLE hHandle;
115 DWORD dwAddress;
116 DWORD dwBlockSize;
117 DWORD dwFlags;
118 DWORD dwLockCount;
119 DWORD dwResvd;
120 DWORD th32ProcessID;
121 DWORD th32HeapID;
122 } HEAPENTRY32;
123
124 typedef struct {
125 DWORD dwSize;
126 DWORD cntUsage;
127 DWORD th32ProcessID;
128 DWORD th32DefaultHeapID;
129 DWORD th32ModuleID;
130 DWORD cntThreads;
131 DWORD th32ParentProcessID;
132 LONG pcPriClassBase;
133 DWORD dwFlags;
134 char szExeFile[260];
135 } PROCESSENTRY32;
136
137 typedef struct {
138 DWORD dwSize;
139 DWORD cntUsage;
140 DWORD th32ThreadID;
141 DWORD th32OwnerProcessID;
142 LONG tpBasePri;
143 LONG tpDeltaPri;
144 DWORD dwFlags;
145 } THREADENTRY32;
146
147 typedef struct {
148 DWORD dwSize;
149 DWORD th32ModuleID;
150 DWORD th32ProcessID;
151 DWORD GlblcntUsage;
152 DWORD ProccntUsage;
153 BYTE *modBaseAddr;
154 DWORD modBaseSize;
155 HMODULE hModule;
156 char szModule[256];
157 char szExePath[260];
158 } MODULEENTRY32;
159
160
161
162 /* Type definitions for function pointers to call Toolhelp32 functions
163 * used with the windows95 gatherer */
164 typedef BOOL (WINAPI * MODULEWALK) (HANDLE hSnapshot, MODULEENTRY32 *lpme);
165 typedef BOOL (WINAPI * THREADWALK) (HANDLE hSnapshot, THREADENTRY32 *lpte);
166 typedef BOOL (WINAPI * PROCESSWALK) (HANDLE hSnapshot, PROCESSENTRY32 *lppe);
167 typedef BOOL (WINAPI * HEAPLISTWALK) (HANDLE hSnapshot, HEAPLIST32 *lphl);
168 typedef BOOL (WINAPI * HEAPFIRST) (HEAPENTRY32 *lphe, DWORD th32ProcessID,
169 DWORD th32HeapID);
170 typedef BOOL (WINAPI * HEAPNEXT) (HEAPENTRY32 *lphe);
171 typedef HANDLE (WINAPI * CREATESNAPSHOT) (DWORD dwFlags, DWORD th32ProcessID);
172
173 /* Type definitions for function pointers to call NetAPI32 functions */
174 typedef DWORD (WINAPI * NETSTATISTICSGET) (LPWSTR szServer, LPWSTR szService,
175 DWORD dwLevel, DWORD dwOptions,
176 LPBYTE * lpBuffer);
177 typedef DWORD (WINAPI * NETAPIBUFFERSIZE) (LPVOID lpBuffer, LPDWORD cbBuffer);
178 typedef DWORD (WINAPI * NETAPIBUFFERFREE) (LPVOID lpBuffer);
179
180
181 /* When we query the performance counters, we allocate an initial buffer and
182 * then reallocate it as required until RegQueryValueEx() stops returning
183 * ERROR_MORE_DATA. The following values define the initial buffer size and
184 * step size by which the buffer is increased
185 */
186 #define PERFORMANCE_BUFFER_SIZE 65536 /* Start at 64K */
187 #define PERFORMANCE_BUFFER_STEP 16384 /* Step by 16K */
188
189
190 static void
slow_gatherer_windows95(void (* add)(const void *,size_t,int),int requester)191 slow_gatherer_windows95( void (*add)(const void*, size_t, int), int requester )
192 {
193 static CREATESNAPSHOT pCreateToolhelp32Snapshot = NULL;
194 static MODULEWALK pModule32First = NULL;
195 static MODULEWALK pModule32Next = NULL;
196 static PROCESSWALK pProcess32First = NULL;
197 static PROCESSWALK pProcess32Next = NULL;
198 static THREADWALK pThread32First = NULL;
199 static THREADWALK pThread32Next = NULL;
200 static HEAPLISTWALK pHeap32ListFirst = NULL;
201 static HEAPLISTWALK pHeap32ListNext = NULL;
202 static HEAPFIRST pHeap32First = NULL;
203 static HEAPNEXT pHeap32Next = NULL;
204 HANDLE hSnapshot;
205
206
207 /* initialize the Toolhelp32 function pointers */
208 if ( !pCreateToolhelp32Snapshot ) {
209 HANDLE hKernel;
210
211 if ( debug_me )
212 log_debug ("rndw32#slow_gatherer_95: init toolkit\n" );
213
214 /* Obtain the module handle of the kernel to retrieve the addresses
215 * of the Toolhelp32 functions */
216 if ( ( !(hKernel = GetModuleHandle ("KERNEL32.DLL"))) ) {
217 g10_log_fatal ( "rndw32: can't get module handle\n" );
218 }
219
220 /* Now get pointers to the functions */
221 pCreateToolhelp32Snapshot = (CREATESNAPSHOT) GetProcAddress (hKernel,
222 "CreateToolhelp32Snapshot");
223 pModule32First = (MODULEWALK) GetProcAddress (hKernel, "Module32First");
224 pModule32Next = (MODULEWALK) GetProcAddress (hKernel, "Module32Next");
225 pProcess32First = (PROCESSWALK) GetProcAddress (hKernel,
226 "Process32First");
227 pProcess32Next = (PROCESSWALK) GetProcAddress (hKernel,
228 "Process32Next");
229 pThread32First = (THREADWALK) GetProcAddress (hKernel, "Thread32First");
230 pThread32Next = (THREADWALK) GetProcAddress (hKernel, "Thread32Next");
231 pHeap32ListFirst = (HEAPLISTWALK) GetProcAddress (hKernel,
232 "Heap32ListFirst");
233 pHeap32ListNext = (HEAPLISTWALK) GetProcAddress (hKernel,
234 "Heap32ListNext");
235 pHeap32First = (HEAPFIRST) GetProcAddress (hKernel, "Heap32First");
236 pHeap32Next = (HEAPNEXT) GetProcAddress (hKernel, "Heap32Next");
237
238 if ( !pCreateToolhelp32Snapshot
239 || !pModule32First || !pModule32Next
240 || !pProcess32First || !pProcess32Next
241 || !pThread32First || !pThread32Next
242 || !pHeap32ListFirst || !pHeap32ListNext
243 || !pHeap32First || !pHeap32Next ) {
244 g10_log_fatal ( "rndw32: failed to get a toolhep function\n" );
245 }
246 }
247
248 /* Take a snapshot of everything we can get to which is currently
249 * in the system */
250 if ( !(hSnapshot = pCreateToolhelp32Snapshot (TH32CS_SNAPALL, 0)) ) {
251 g10_log_fatal ( "rndw32: failed to take a toolhelp snapshot\n" );
252 }
253
254 /* Walk through the local heap */
255 { HEAPLIST32 hl32;
256 hl32.dwSize = sizeof (HEAPLIST32);
257 if (pHeap32ListFirst (hSnapshot, &hl32)) {
258 if ( debug_me )
259 log_debug ("rndw32#slow_gatherer_95: walk heap\n" );
260 do {
261 HEAPENTRY32 he32;
262
263 /* First add the information from the basic Heaplist32 struct */
264 (*add) ( &hl32, sizeof (hl32), requester );
265
266 /* Now walk through the heap blocks getting information
267 * on each of them */
268 he32.dwSize = sizeof (HEAPENTRY32);
269 if (pHeap32First (&he32, hl32.th32ProcessID, hl32.th32HeapID)){
270 do {
271 (*add) ( &he32, sizeof (he32), requester );
272 } while (pHeap32Next (&he32));
273 }
274 } while (pHeap32ListNext (hSnapshot, &hl32));
275 }
276 }
277
278
279 /* Walk through all processes */
280 { PROCESSENTRY32 pe32;
281 pe32.dwSize = sizeof (PROCESSENTRY32);
282 if (pProcess32First (hSnapshot, &pe32)) {
283 if ( debug_me )
284 log_debug ("rndw32#slow_gatherer_95: walk processes\n" );
285 do {
286 (*add) ( &pe32, sizeof (pe32), requester );
287 } while (pProcess32Next (hSnapshot, &pe32));
288 }
289 }
290
291 /* Walk through all threads */
292 { THREADENTRY32 te32;
293 te32.dwSize = sizeof (THREADENTRY32);
294 if (pThread32First (hSnapshot, &te32)) {
295 if ( debug_me )
296 log_debug ("rndw32#slow_gatherer_95: walk threads\n" );
297 do {
298 (*add) ( &te32, sizeof (te32), requester );
299 } while (pThread32Next (hSnapshot, &te32));
300 }
301 }
302
303 /* Walk through all modules associated with the process */
304 { MODULEENTRY32 me32;
305 me32.dwSize = sizeof (MODULEENTRY32);
306 if (pModule32First (hSnapshot, &me32)) {
307 if ( debug_me )
308 log_debug ("rndw32#slow_gatherer_95: walk modules\n" );
309 do {
310 (*add) ( &me32, sizeof (me32), requester );
311 } while (pModule32Next (hSnapshot, &me32));
312 }
313 }
314
315 CloseHandle (hSnapshot);
316 }
317
318
319
320 static void
slow_gatherer_windowsNT(void (* add)(const void *,size_t,int),int requester)321 slow_gatherer_windowsNT( void (*add)(const void*, size_t, int), int requester )
322 {
323 static int is_initialized = 0;
324 static NETSTATISTICSGET pNetStatisticsGet = NULL;
325 static NETAPIBUFFERSIZE pNetApiBufferSize = NULL;
326 static NETAPIBUFFERFREE pNetApiBufferFree = NULL;
327 static int is_workstation = 1;
328
329 static int cbPerfData = PERFORMANCE_BUFFER_SIZE;
330 PERF_DATA_BLOCK *pPerfData;
331 HANDLE hDevice, hNetAPI32 = NULL;
332 DWORD dwSize, status;
333 int nDrive;
334
335 if ( !is_initialized ) {
336 HKEY hKey;
337
338 if ( debug_me )
339 log_debug ("rndw32#slow_gatherer_nt: init toolkit\n" );
340 /* Find out whether this is an NT server or workstation if necessary */
341 if (RegOpenKeyEx (HKEY_LOCAL_MACHINE,
342 "SYSTEM\\CurrentControlSet\\Control\\ProductOptions",
343 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
344 BYTE szValue[32];
345 dwSize = sizeof (szValue);
346
347 if ( debug_me )
348 log_debug ("rndw32#slow_gatherer_nt: check product options\n" );
349 status = RegQueryValueEx (hKey, "ProductType", 0, NULL,
350 szValue, &dwSize);
351 if (status == ERROR_SUCCESS
352 && ascii_strcasecmp (szValue, "WinNT")) {
353 /* Note: There are (at least) three cases for ProductType:
354 * WinNT = NT Workstation, ServerNT = NT Server, LanmanNT =
355 * NT Server acting as a Domain Controller */
356 is_workstation = 0;
357 if ( debug_me )
358 log_debug ("rndw32: this is a NT server\n");
359 }
360 RegCloseKey (hKey);
361 }
362
363 /* Initialize the NetAPI32 function pointers if necessary */
364 if ( (hNetAPI32 = LoadLibrary ("NETAPI32.DLL")) ) {
365 if ( debug_me )
366 log_debug ("rndw32#slow_gatherer_nt: netapi32 loaded\n" );
367 pNetStatisticsGet = (NETSTATISTICSGET) GetProcAddress (hNetAPI32,
368 "NetStatisticsGet");
369 pNetApiBufferSize = (NETAPIBUFFERSIZE) GetProcAddress (hNetAPI32,
370 "NetApiBufferSize");
371 pNetApiBufferFree = (NETAPIBUFFERFREE) GetProcAddress (hNetAPI32,
372 "NetApiBufferFree");
373
374 if ( !pNetStatisticsGet
375 || !pNetApiBufferSize || !pNetApiBufferFree ) {
376 FreeLibrary (hNetAPI32);
377 hNetAPI32 = NULL;
378 g10_log_debug ("rndw32: No NETAPI found\n" );
379 }
380 }
381
382 is_initialized = 1;
383 }
384
385 /* Get network statistics. Note: Both NT Workstation and NT Server by
386 * default will be running both the workstation and server services. The
387 * heuristic below is probably useful though on the assumption that the
388 * majority of the network traffic will be via the appropriate service.
389 * In any case the network statistics return almost no randomness */
390 { LPBYTE lpBuffer;
391 if (hNetAPI32 && !pNetStatisticsGet (NULL,
392 is_workstation ? L"LanmanWorkstation" :
393 L"LanmanServer", 0, 0, &lpBuffer) ) {
394 if ( debug_me )
395 log_debug ("rndw32#slow_gatherer_nt: get netstats\n" );
396 pNetApiBufferSize (lpBuffer, &dwSize);
397 (*add) ( lpBuffer, dwSize,requester );
398 pNetApiBufferFree (lpBuffer);
399 }
400 }
401
402 /* Get disk I/O statistics for all the hard drives */
403 for (nDrive = 0;; nDrive++) {
404 char diskPerformance[SIZEOF_DISK_PERFORMANCE_STRUCT];
405 char szDevice[50];
406
407 /* Check whether we can access this device */
408 sprintf (szDevice, "\\\\.\\PhysicalDrive%d", nDrive);
409 hDevice = CreateFile (szDevice, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
410 NULL, OPEN_EXISTING, 0, NULL);
411 if (hDevice == INVALID_HANDLE_VALUE)
412 break;
413
414 /* Note: This only works if you have turned on the disk performance
415 * counters with 'diskperf -y'. These counters are off by default */
416 if (DeviceIoControl (hDevice, IOCTL_DISK_PERFORMANCE, NULL, 0,
417 diskPerformance, SIZEOF_DISK_PERFORMANCE_STRUCT,
418 &dwSize, NULL))
419 {
420 if ( debug_me )
421 log_debug ("rndw32#slow_gatherer_nt: iostats drive %d\n",
422 nDrive );
423 (*add) (diskPerformance, dwSize, requester );
424 }
425 else {
426 log_info ("NOTE: you should run 'diskperf -y' "
427 "to enable the disk statistics\n");
428 }
429 CloseHandle (hDevice);
430 }
431
432 #if 0 /* we don't need this in GnuPG */
433 /* Wait for any async keyset driver binding to complete. You may be
434 * wondering what this call is doing here... the reason it's necessary is
435 * because RegQueryValueEx() will hang indefinitely if the async driver
436 * bind is in progress. The problem occurs in the dynamic loading and
437 * linking of driver DLL's, which work as follows:
438 *
439 * hDriver = LoadLibrary( DRIVERNAME );
440 * pFunction1 = ( TYPE_FUNC1 ) GetProcAddress( hDriver, NAME_FUNC1 );
441 * pFunction2 = ( TYPE_FUNC1 ) GetProcAddress( hDriver, NAME_FUNC2 );
442 *
443 * If RegQueryValueEx() is called while the GetProcAddress()'s are in
444 * progress, it will hang indefinitely. This is probably due to some
445 * synchronisation problem in the NT kernel where the GetProcAddress()
446 * calls affect something like a module reference count or function
447 * reference count while RegQueryValueEx() is trying to take a snapshot
448 * of the statistics, which include the reference counts. Because of
449 * this, we have to wait until any async driver bind has completed
450 * before we can call RegQueryValueEx() */
451 waitSemaphore (SEMAPHORE_DRIVERBIND);
452 #endif
453
454 /* Get information from the system performance counters. This can take
455 * a few seconds to do. In some environments the call to
456 * RegQueryValueEx() can produce an access violation at some random time
457 * in the future, adding a short delay after the following code block
458 * makes the problem go away. This problem is extremely difficult to
459 * reproduce, I haven't been able to get it to occur despite running it
460 * on a number of machines. The best explanation for the problem is that
461 * on the machine where it did occur, it was caused by an external driver
462 * or other program which adds its own values under the
463 * HKEY_PERFORMANCE_DATA key. The NT kernel calls the required external
464 * modules to map in the data, if there's a synchronisation problem the
465 * external module would write its data at an inappropriate moment,
466 * causing the access violation. A low-level memory checker indicated
467 * that ExpandEnvironmentStrings() in KERNEL32.DLL, called an
468 * interminable number of calls down inside RegQueryValueEx(), was
469 * overwriting memory (it wrote twice the allocated size of a buffer to a
470 * buffer allocated by the NT kernel). This may be what's causing the
471 * problem, but since it's in the kernel there isn't much which can be
472 * done.
473 *
474 * In addition to these problems the code in RegQueryValueEx() which
475 * estimates the amount of memory required to return the performance
476 * counter information isn't very accurate, since it always returns a
477 * worst-case estimate which is usually nowhere near the actual amount
478 * required. For example it may report that 128K of memory is required,
479 * but only return 64K of data */
480 if (getenv("GNUPG_RNDW32_NOPERF"))
481 {
482 static int shown;
483
484 if (!shown)
485 {
486 shown = 1;
487 g10_log_info ("note: get performance data has been disabled\n");
488 }
489 }
490 else
491 { pPerfData = xmalloc (cbPerfData);
492 for (;;) {
493 dwSize = cbPerfData;
494 if ( debug_me )
495 log_debug ("rndw32#slow_gatherer_nt: get perf data\n" );
496 status = RegQueryValueEx (HKEY_PERFORMANCE_DATA, "Global", NULL,
497 NULL, (LPBYTE) pPerfData, &dwSize);
498 if (status == ERROR_SUCCESS) {
499 if (!memcmp (pPerfData->Signature, L"PERF", 8)) {
500 (*add) ( pPerfData, dwSize, requester );
501 }
502 else
503 g10_log_debug ( "rndw32: no PERF signature\n");
504 break;
505 }
506 else if (status == ERROR_MORE_DATA) {
507 cbPerfData += PERFORMANCE_BUFFER_STEP;
508 pPerfData = xrealloc (pPerfData, cbPerfData);
509 }
510 else {
511 g10_log_debug ( "rndw32: get performance data problem\n");
512 break;
513 }
514 }
515 xfree (pPerfData);
516 }
517 /* Although this isn't documented in the Win32 API docs, it's necessary
518 to explicitly close the HKEY_PERFORMANCE_DATA key after use (it's
519 implicitly opened on the first call to RegQueryValueEx()). If this
520 isn't done then any system components which provide performance data
521 can't be removed or changed while the handle remains active */
522 RegCloseKey (HKEY_PERFORMANCE_DATA);
523 }
524
525
526 int
rndw32_gather_random(void (* add)(const void *,size_t,int),int requester,size_t length,int level)527 rndw32_gather_random (void (*add)(const void*, size_t, int), int requester,
528 size_t length, int level )
529 {
530 static int is_initialized;
531 static int is_windowsNT, has_toolhelp;
532
533
534 if( !level )
535 return 0;
536 /* We don't differentiate between level 1 and 2 here because
537 * there is no internal entropy pool as a scarce resource. It may
538 * all work slower, but because our entropy source will never
539 * block but deliver some not easy to measure entropy, we assume level 2
540 */
541
542
543 if ( !is_initialized ) {
544 OSVERSIONINFO osvi = { sizeof( osvi ) };
545 DWORD platform;
546
547 GetVersionEx( &osvi );
548 platform = osvi.dwPlatformId;
549 is_windowsNT = platform == VER_PLATFORM_WIN32_NT;
550 has_toolhelp = (platform == VER_PLATFORM_WIN32_WINDOWS
551 || (is_windowsNT && osvi.dwMajorVersion >= 5));
552
553 if ( platform == VER_PLATFORM_WIN32s ) {
554 g10_log_fatal("can't run on a W32s platform\n" );
555 }
556 is_initialized = 1;
557 if ( debug_me )
558 log_debug ("rndw32#gather_random: platform=%d\n", (int)platform );
559 }
560
561
562 if ( debug_me )
563 log_debug ("rndw32#gather_random: req=%d len=%u lvl=%d\n",
564 requester, (unsigned int)length, level );
565
566 if ( has_toolhelp ) {
567 slow_gatherer_windows95 ( add, requester );
568 }
569 if ( is_windowsNT ) {
570 slow_gatherer_windowsNT ( add, requester );
571 }
572
573 return 0;
574 }
575
576
577
578 int
rndw32_gather_random_fast(void (* add)(const void *,size_t,int),int requester)579 rndw32_gather_random_fast( void (*add)(const void*, size_t, int), int requester )
580 {
581 static int addedFixedItems = 0;
582
583 if ( debug_me )
584 log_debug ("rndw32#gather_random_fast: req=%d\n", requester );
585
586 /* Get various basic pieces of system information: Handle of active
587 * window, handle of window with mouse capture, handle of clipboard owner
588 * handle of start of clpboard viewer list, pseudohandle of current
589 * process, current process ID, pseudohandle of current thread, current
590 * thread ID, handle of desktop window, handle of window with keyboard
591 * focus, whether system queue has any events, cursor position for last
592 * message, 1 ms time for last message, handle of window with clipboard
593 * open, handle of process heap, handle of procs window station, types of
594 * events in input queue, and milliseconds since Windows was started */
595 { byte buffer[20*sizeof(ulong)], *bufptr;
596 bufptr = buffer;
597 #define ADD(f) do { ulong along = (ulong)(f); \
598 memcpy (bufptr, &along, sizeof (along) ); \
599 bufptr += sizeof (along); } while (0)
600 ADD ( GetActiveWindow ());
601 ADD ( GetCapture ());
602 ADD ( GetClipboardOwner ());
603 ADD ( GetClipboardViewer ());
604 ADD ( GetCurrentProcess ());
605 ADD ( GetCurrentProcessId ());
606 ADD ( GetCurrentThread ());
607 ADD ( GetCurrentThreadId ());
608 ADD ( GetDesktopWindow ());
609 ADD ( GetFocus ());
610 ADD ( GetInputState ());
611 ADD ( GetMessagePos ());
612 ADD ( GetMessageTime ());
613 ADD ( GetOpenClipboardWindow ());
614 ADD ( GetProcessHeap ());
615 ADD ( GetProcessWindowStation ());
616 ADD ( GetQueueStatus (QS_ALLEVENTS));
617 ADD ( GetTickCount ());
618
619 assert ( bufptr-buffer < sizeof (buffer) );
620 (*add) ( buffer, bufptr-buffer, requester );
621 #undef ADD
622 }
623
624 /* Get multiword system information: Current caret position, current
625 * mouse cursor position */
626 { POINT point;
627 GetCaretPos (&point);
628 (*add) ( &point, sizeof (point), requester );
629 GetCursorPos (&point);
630 (*add) ( &point, sizeof (point), requester );
631 }
632
633 /* Get percent of memory in use, bytes of physical memory, bytes of free
634 * physical memory, bytes in paging file, free bytes in paging file, user
635 * bytes of address space, and free user bytes */
636 { MEMORYSTATUS memoryStatus;
637 memoryStatus.dwLength = sizeof (MEMORYSTATUS);
638 GlobalMemoryStatus (&memoryStatus);
639 (*add) ( &memoryStatus, sizeof (memoryStatus), requester );
640 }
641
642 /* Get thread and process creation time, exit time, time in kernel mode,
643 and time in user mode in 100ns intervals */
644 { HANDLE handle;
645 FILETIME creationTime, exitTime, kernelTime, userTime;
646 DWORD minimumWorkingSetSize, maximumWorkingSetSize;
647
648 handle = GetCurrentThread ();
649 GetThreadTimes (handle, &creationTime, &exitTime,
650 &kernelTime, &userTime);
651 (*add) ( &creationTime, sizeof (creationTime), requester );
652 (*add) ( &exitTime, sizeof (exitTime), requester );
653 (*add) ( &kernelTime, sizeof (kernelTime), requester );
654 (*add) ( &userTime, sizeof (userTime), requester );
655
656 handle = GetCurrentProcess ();
657 GetProcessTimes (handle, &creationTime, &exitTime,
658 &kernelTime, &userTime);
659 (*add) ( &creationTime, sizeof (creationTime), requester );
660 (*add) ( &exitTime, sizeof (exitTime), requester );
661 (*add) ( &kernelTime, sizeof (kernelTime), requester );
662 (*add) ( &userTime, sizeof (userTime), requester );
663
664 /* Get the minimum and maximum working set size for the
665 current process */
666 GetProcessWorkingSetSize (handle, &minimumWorkingSetSize,
667 &maximumWorkingSetSize);
668 (*add) ( &minimumWorkingSetSize,
669 sizeof (minimumWorkingSetSize), requester );
670 (*add) ( &maximumWorkingSetSize,
671 sizeof (maximumWorkingSetSize), requester );
672 }
673
674
675 /* The following are fixed for the lifetime of the process so we only
676 * add them once */
677 if (!addedFixedItems) {
678 STARTUPINFO startupInfo;
679
680 /* Get name of desktop, console window title, new window position and
681 * size, window flags, and handles for stdin, stdout, and stderr */
682 startupInfo.cb = sizeof (STARTUPINFO);
683 GetStartupInfo (&startupInfo);
684 (*add) ( &startupInfo, sizeof (STARTUPINFO), requester );
685 addedFixedItems = 1;
686 }
687
688 /* The performance of QPC varies depending on the architecture it's
689 * running on and on the OS. Under NT it reads the CPU's 64-bit timestamp
690 * counter (at least on a Pentium and newer '486's, it hasn't been tested
691 * on anything without a TSC), under Win95 it reads the 1.193180 MHz PIC
692 * timer. There are vague mumblings in the docs that it may fail if the
693 * appropriate hardware isn't available (possibly '386's or MIPS machines
694 * running NT), but who's going to run NT on a '386? */
695 { LARGE_INTEGER performanceCount;
696 if (QueryPerformanceCounter (&performanceCount)) {
697 if ( debug_me )
698 log_debug ("rndw32#gather_random_fast: perf data\n");
699 (*add) (&performanceCount, sizeof (performanceCount), requester);
700 }
701 else { /* Millisecond accuracy at best... */
702 DWORD aword = GetTickCount ();
703 (*add) (&aword, sizeof (aword), requester );
704 }
705 }
706
707 return 0;
708 }
709