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