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