mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-23 10:29:58 +01:00
970 lines
32 KiB
C
970 lines
32 KiB
C
|
/* rndw32.c - W32 entropy gatherer
|
||
|
* Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
|
||
|
* Copyright Peter Gutmann, Matt Thomlinson and Blake Coverett 1996-1999
|
||
|
*
|
||
|
* This file is part of GnuPG.
|
||
|
*
|
||
|
* GnuPG is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation; either version 2 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* GnuPG is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program; if not, write to the Free Software
|
||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||
|
*
|
||
|
*************************************************************************
|
||
|
* The code here is based on code from Cryptlib 3.0 beta by Peter Gutmann.
|
||
|
* Source file misc/rndwin32.c "Win32 Randomness-Gathering Code" with this
|
||
|
* copyright notice:
|
||
|
*
|
||
|
* This module is part of the cryptlib continuously seeded pseudorandom
|
||
|
* number generator. For usage conditions, see lib_rand.c
|
||
|
*
|
||
|
* [Here is the notice from lib_rand.c, which is now called dev_sys.c]
|
||
|
*
|
||
|
* This module and the misc/rnd*.c modules represent the cryptlib
|
||
|
* continuously seeded pseudorandom number generator (CSPRNG) as described in
|
||
|
* my 1998 Usenix Security Symposium paper "The generation of random numbers
|
||
|
* for cryptographic purposes".
|
||
|
*
|
||
|
* The CSPRNG code is copyright Peter Gutmann (and various others) 1996,
|
||
|
* 1997, 1998, 1999, all rights reserved. Redistribution of the CSPRNG
|
||
|
* modules and use in source and binary forms, with or without modification,
|
||
|
* are permitted provided that the following conditions are met:
|
||
|
*
|
||
|
* 1. Redistributions of source code must retain the above copyright notice
|
||
|
* and this permission notice in its entirety.
|
||
|
*
|
||
|
* 2. Redistributions in binary form must reproduce the copyright notice in
|
||
|
* the documentation and/or other materials provided with the distribution.
|
||
|
*
|
||
|
* 3. A copy of any bugfixes or enhancements made must be provided to the
|
||
|
* author, <pgut001@cs.auckland.ac.nz> to allow them to be added to the
|
||
|
* baseline version of the code.
|
||
|
*
|
||
|
* ALTERNATIVELY, the code may be distributed under the terms of the GNU
|
||
|
* General Public License, version 2 or any later version published by the
|
||
|
* Free Software Foundation, in which case the provisions of the GNU GPL are
|
||
|
* required INSTEAD OF the above restrictions.
|
||
|
*
|
||
|
* Although not required under the terms of the GPL, it would still be nice if
|
||
|
* you could make any changes available to the author to allow a consistent
|
||
|
* code base to be maintained
|
||
|
*************************************************************************
|
||
|
*/
|
||
|
|
||
|
#include <config.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <assert.h>
|
||
|
#include <errno.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include <windows.h>
|
||
|
#ifdef __CYGWIN32__
|
||
|
# include <winioctl.h>
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#include "types.h"
|
||
|
#include "util.h"
|
||
|
#include "dynload.h"
|
||
|
|
||
|
/* We do not use the netropy DLL anymore because a standalone program is
|
||
|
* easier to maintain and */
|
||
|
/*#define USE_ENTROPY_DLL*/
|
||
|
|
||
|
|
||
|
|
||
|
#ifdef IS_MODULE
|
||
|
#define _(a) (a)
|
||
|
#else
|
||
|
#include "i18n.h"
|
||
|
#endif
|
||
|
|
||
|
|
||
|
static int debug_me;
|
||
|
|
||
|
#ifdef USE_ENTROPY_DLL
|
||
|
|
||
|
#define WIN32_SLOW_SEEDER 0
|
||
|
#define WIN32_FAST_SEEDER 1
|
||
|
|
||
|
#define PCP_SUCCESS 0
|
||
|
#define PCP_NULL_POINTER 1
|
||
|
#define PCP_SEEDER_FAILED 2
|
||
|
#define PCP_SEEDER_NO_MEM 3
|
||
|
#define PCP_SEEDER_TOO_SMALL 4
|
||
|
#define PCP_DLL_LOAD_FAILED 5
|
||
|
#define PCP_UNKNOWN_PLATFORM 6
|
||
|
#define PCP_ERROR_VERSION 7
|
||
|
#define PCP_DLL_FUNC 8
|
||
|
#define PCP_UNKNOWN_SEEDER_TYPE 9
|
||
|
|
||
|
|
||
|
/****************
|
||
|
* We sometimes get a SEEDER_TOO_SMALL error, in which case we increment
|
||
|
* the internal buffer by SEEDER_INC_CHUNK until we reach MAX_SEEDER_SIZE
|
||
|
* MAX_SEEDER_SIZE is used as an arbitrary limit to protect against
|
||
|
* bugs in Winseed.
|
||
|
*/
|
||
|
#define MAX_SEEDER_SIZE 500000
|
||
|
#define SEEDER_INC_CHUNK 50000
|
||
|
|
||
|
|
||
|
typedef void *WIN32_SEEDER;
|
||
|
|
||
|
static WIN32_SEEDER (WINAPI *create_instance)( byte type, unsigned int *reason);
|
||
|
static void (WINAPI *delete_instance)( WIN32_SEEDER that );
|
||
|
static unsigned int (WINAPI *get_internal_seed_size)( WIN32_SEEDER that );
|
||
|
static void (WINAPI *set_internal_seed_size)( WIN32_SEEDER that,
|
||
|
unsigned int new_size);
|
||
|
static unsigned int (WINAPI *get_expected_seed_size)( WIN32_SEEDER that);
|
||
|
static unsigned int (WINAPI *get_seed)( WIN32_SEEDER that, byte *buffer,
|
||
|
unsigned int *desired_length);
|
||
|
|
||
|
static WIN32_SEEDER slow_seeder, fast_seeder;
|
||
|
static byte *entropy_buffer;
|
||
|
static size_t entropy_buffer_size;
|
||
|
|
||
|
/****************
|
||
|
* Load and initialize the winseed DLL
|
||
|
* NOTE: winseed is not part of the GnuPG distribution. It should be available
|
||
|
* at the GNU crypto FTP server site.
|
||
|
* We do not load the DLL on demand to have a better control over the
|
||
|
* location of the library.
|
||
|
*/
|
||
|
static void
|
||
|
load_and_init_winseed( void )
|
||
|
{
|
||
|
HANDLE hInstance;
|
||
|
void *addr;
|
||
|
unsigned int reason = 0;
|
||
|
unsigned int n1, n2;
|
||
|
const char *dllname;
|
||
|
|
||
|
dllname = read_w32_registry_string( "HKEY_LOCAL_MACHINE",
|
||
|
"Software\\GNU\\GnuPG",
|
||
|
"EntropyDLL" );
|
||
|
if( !dllname )
|
||
|
dllname = "c:/gnupg/entropy.dll";
|
||
|
|
||
|
hInstance = LoadLibrary( dllname );
|
||
|
if( !hInstance )
|
||
|
goto failure;
|
||
|
if( !(addr = GetProcAddress( hInstance, "WS_create_instance" )) )
|
||
|
goto failure;
|
||
|
create_instance = addr;
|
||
|
if( !(addr = GetProcAddress( hInstance, "WS_delete_instance" )) )
|
||
|
goto failure;
|
||
|
delete_instance = addr;
|
||
|
if( !(addr = GetProcAddress( hInstance, "WS_get_internal_seed_size" )) )
|
||
|
goto failure;
|
||
|
get_internal_seed_size = addr;
|
||
|
if( !(addr = GetProcAddress( hInstance, "WS_set_internal_seed_size" )) )
|
||
|
goto failure;
|
||
|
set_internal_seed_size = addr;
|
||
|
if( !(addr = GetProcAddress( hInstance, "WS_get_expected_seed_size" )) )
|
||
|
goto failure;
|
||
|
get_expected_seed_size = addr;
|
||
|
if( !(addr = GetProcAddress( hInstance, "WS_get_seed" )) )
|
||
|
goto failure;
|
||
|
get_seed = addr;
|
||
|
|
||
|
/* we have all the functions - init the system */
|
||
|
slow_seeder = create_instance( WIN32_SLOW_SEEDER, &reason);
|
||
|
if( !slow_seeder ) {
|
||
|
g10_log_fatal("error creating winseed slow seeder: rc=%u\n", reason );
|
||
|
goto failure;
|
||
|
}
|
||
|
fast_seeder = create_instance( WIN32_FAST_SEEDER, &reason);
|
||
|
if( !fast_seeder ) {
|
||
|
g10_log_fatal("error creating winseed fast seeder: rc=%u\n", reason );
|
||
|
goto failure;
|
||
|
}
|
||
|
n1 = get_internal_seed_size( slow_seeder );
|
||
|
/*g10_log_info("slow buffer size=%u\n", n1);*/
|
||
|
n2 = get_internal_seed_size( fast_seeder );
|
||
|
/*g10_log_info("fast buffer size=%u\n", n2);*/
|
||
|
|
||
|
entropy_buffer_size = n1 > n2? n1: n2;
|
||
|
entropy_buffer = m_alloc( entropy_buffer_size );
|
||
|
/*g10_log_info("using a buffer of size=%u\n", entropy_buffer_size );*/
|
||
|
|
||
|
return;
|
||
|
|
||
|
failure:
|
||
|
g10_log_fatal("error loading winseed DLL `%s'\n", dllname );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/* Note: we always use the highest level.
|
||
|
* TO boost the performance we may want to add some
|
||
|
* additional code for level 1
|
||
|
*/
|
||
|
static int
|
||
|
gather_random( void (*add)(const void*, size_t, int), int requester,
|
||
|
size_t length, int level )
|
||
|
{
|
||
|
unsigned int result;
|
||
|
unsigned int nbytes;
|
||
|
|
||
|
if( !level )
|
||
|
return 0;
|
||
|
|
||
|
if( !slow_seeder )
|
||
|
load_and_init_winseed();
|
||
|
|
||
|
/* Our estimation on how much entropy we should use is very vague.
|
||
|
* Winseed delivers some amount of entropy on each slow poll and
|
||
|
* we add it to our random pool. Depending on the required quality
|
||
|
* level we adjust the requested length so that for higher quality
|
||
|
* we make sure to add more entropy to our pool. However, as we don't
|
||
|
* like to waste any entropy collected by winseed, we always add
|
||
|
* at least everything we got from winseed.
|
||
|
*/
|
||
|
if( level > 1 )
|
||
|
length *= 100;
|
||
|
else if( level > 0 )
|
||
|
length *= 10;
|
||
|
|
||
|
for(;;) {
|
||
|
nbytes = entropy_buffer_size;
|
||
|
result = get_seed( slow_seeder, entropy_buffer, &nbytes);
|
||
|
if( result == PCP_SEEDER_TOO_SMALL ) {
|
||
|
unsigned int n1 = get_internal_seed_size( slow_seeder );
|
||
|
|
||
|
if( n1 > MAX_SEEDER_SIZE ) {
|
||
|
g10_log_fatal("rndw32: internal seeder problem (size=%u)\n",
|
||
|
n1);
|
||
|
return -1; /* actually never reached */
|
||
|
}
|
||
|
n1 += SEEDER_INC_CHUNK;
|
||
|
set_internal_seed_size( slow_seeder, n1 );
|
||
|
if( n1 > entropy_buffer_size ) {
|
||
|
entropy_buffer_size = n1;
|
||
|
entropy_buffer = m_realloc( entropy_buffer,
|
||
|
entropy_buffer_size );
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
|
||
|
if( result ) {
|
||
|
g10_log_fatal("rndw32: get_seed(slow) failed: rc=%u\n", result);
|
||
|
return -1; /* actually never reached */
|
||
|
}
|
||
|
/*g10_log_info("rndw32: slow poll level %d, need %u, got %u\n",
|
||
|
level, (unsigned int)length, (unsigned int)nbytes );*/
|
||
|
(*add)( entropy_buffer, nbytes, requester );
|
||
|
if( length <= nbytes )
|
||
|
return 0; /* okay */
|
||
|
length -= nbytes;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
gather_random_fast( void (*add)(const void*, size_t, int), int requester )
|
||
|
{
|
||
|
unsigned int result;
|
||
|
unsigned int nbytes;
|
||
|
|
||
|
if( !fast_seeder )
|
||
|
load_and_init_winseed();
|
||
|
|
||
|
/* winseed delivers a constant ammount of entropy for a fast
|
||
|
* poll. We can simply use this and add it to the pool; no need
|
||
|
* a loop like it is used in the slow poll */
|
||
|
nbytes = entropy_buffer_size;
|
||
|
result = get_seed( fast_seeder, entropy_buffer, &nbytes);
|
||
|
if( result ) {
|
||
|
g10_log_fatal("rndw32: get_seed(fast) failed: rc=%u\n", result);
|
||
|
return -1; /* actually never reached */
|
||
|
}
|
||
|
/*g10_log_info("rndw32: fast poll got %u\n", (unsigned int)nbytes );*/
|
||
|
(*add)( entropy_buffer, nbytes, requester );
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#else /* !USE_ENTROPY_DLL */
|
||
|
/* This is the new code which does not require the entropy.dll */
|
||
|
|
||
|
/*
|
||
|
* Definitions which are missing from the current GNU Windows32Api
|
||
|
*/
|
||
|
|
||
|
#ifndef TH32CS_SNAPHEAPLIST
|
||
|
#define TH32CS_SNAPHEAPLIST 1
|
||
|
#define TH32CS_SNAPPROCESS 2
|
||
|
#define TH32CS_SNAPTHREAD 4
|
||
|
#define TH32CS_SNAPMODULE 8
|
||
|
#define TH32CS_SNAPALL (1|2|4|8)
|
||
|
#define TH32CS_INHERIT 0x80000000
|
||
|
#endif /*TH32CS_SNAPHEAPLIST*/
|
||
|
|
||
|
#ifndef IOCTL_DISK_PERFORMANCE
|
||
|
#define IOCTL_DISK_PERFORMANCE 0x00070020
|
||
|
#endif
|
||
|
#ifndef VER_PLATFORM_WIN32_WINDOWS
|
||
|
#define VER_PLATFORM_WIN32_WINDOWS 1
|
||
|
#endif
|
||
|
|
||
|
typedef struct {
|
||
|
DWORD dwSize;
|
||
|
DWORD th32ProcessID;
|
||
|
DWORD th32HeapID;
|
||
|
DWORD dwFlags;
|
||
|
} HEAPLIST32;
|
||
|
|
||
|
typedef struct {
|
||
|
DWORD dwSize;
|
||
|
HANDLE hHandle;
|
||
|
DWORD dwAddress;
|
||
|
DWORD dwBlockSize;
|
||
|
DWORD dwFlags;
|
||
|
DWORD dwLockCount;
|
||
|
DWORD dwResvd;
|
||
|
DWORD th32ProcessID;
|
||
|
DWORD th32HeapID;
|
||
|
} HEAPENTRY32;
|
||
|
|
||
|
typedef struct {
|
||
|
DWORD dwSize;
|
||
|
DWORD cntUsage;
|
||
|
DWORD th32ProcessID;
|
||
|
DWORD th32DefaultHeapID;
|
||
|
DWORD th32ModuleID;
|
||
|
DWORD cntThreads;
|
||
|
DWORD th32ParentProcessID;
|
||
|
LONG pcPriClassBase;
|
||
|
DWORD dwFlags;
|
||
|
char szExeFile[260];
|
||
|
} PROCESSENTRY32;
|
||
|
|
||
|
typedef struct {
|
||
|
DWORD dwSize;
|
||
|
DWORD cntUsage;
|
||
|
DWORD th32ThreadID;
|
||
|
DWORD th32OwnerProcessID;
|
||
|
LONG tpBasePri;
|
||
|
LONG tpDeltaPri;
|
||
|
DWORD dwFlags;
|
||
|
} THREADENTRY32;
|
||
|
|
||
|
typedef struct {
|
||
|
DWORD dwSize;
|
||
|
DWORD th32ModuleID;
|
||
|
DWORD th32ProcessID;
|
||
|
DWORD GlblcntUsage;
|
||
|
DWORD ProccntUsage;
|
||
|
BYTE *modBaseAddr;
|
||
|
DWORD modBaseSize;
|
||
|
HMODULE hModule;
|
||
|
char szModule[256];
|
||
|
char szExePath[260];
|
||
|
} MODULEENTRY32;
|
||
|
|
||
|
|
||
|
|
||
|
/* Type definitions for function pointers to call Toolhelp32 functions
|
||
|
* used with the windows95 gatherer */
|
||
|
typedef BOOL (WINAPI * MODULEWALK) (HANDLE hSnapshot, MODULEENTRY32 *lpme);
|
||
|
typedef BOOL (WINAPI * THREADWALK) (HANDLE hSnapshot, THREADENTRY32 *lpte);
|
||
|
typedef BOOL (WINAPI * PROCESSWALK) (HANDLE hSnapshot, PROCESSENTRY32 *lppe);
|
||
|
typedef BOOL (WINAPI * HEAPLISTWALK) (HANDLE hSnapshot, HEAPLIST32 *lphl);
|
||
|
typedef BOOL (WINAPI * HEAPFIRST) (HEAPENTRY32 *lphe, DWORD th32ProcessID,
|
||
|
DWORD th32HeapID);
|
||
|
typedef BOOL (WINAPI * HEAPNEXT) (HEAPENTRY32 *lphe);
|
||
|
typedef HANDLE (WINAPI * CREATESNAPSHOT) (DWORD dwFlags, DWORD th32ProcessID);
|
||
|
|
||
|
/* Type definitions for function pointers to call NetAPI32 functions */
|
||
|
typedef DWORD (WINAPI * NETSTATISTICSGET) (LPWSTR szServer, LPWSTR szService,
|
||
|
DWORD dwLevel, DWORD dwOptions,
|
||
|
LPBYTE * lpBuffer);
|
||
|
typedef DWORD (WINAPI * NETAPIBUFFERSIZE) (LPVOID lpBuffer, LPDWORD cbBuffer);
|
||
|
typedef DWORD (WINAPI * NETAPIBUFFERFREE) (LPVOID lpBuffer);
|
||
|
|
||
|
|
||
|
/* When we query the performance counters, we allocate an initial buffer and
|
||
|
* then reallocate it as required until RegQueryValueEx() stops returning
|
||
|
* ERROR_MORE_DATA. The following values define the initial buffer size and
|
||
|
* step size by which the buffer is increased
|
||
|
*/
|
||
|
#define PERFORMANCE_BUFFER_SIZE 65536 /* Start at 64K */
|
||
|
#define PERFORMANCE_BUFFER_STEP 16384 /* Step by 16K */
|
||
|
|
||
|
|
||
|
static void
|
||
|
slow_gatherer_windows95( void (*add)(const void*, size_t, int), int requester )
|
||
|
{
|
||
|
static CREATESNAPSHOT pCreateToolhelp32Snapshot = NULL;
|
||
|
static MODULEWALK pModule32First = NULL;
|
||
|
static MODULEWALK pModule32Next = NULL;
|
||
|
static PROCESSWALK pProcess32First = NULL;
|
||
|
static PROCESSWALK pProcess32Next = NULL;
|
||
|
static THREADWALK pThread32First = NULL;
|
||
|
static THREADWALK pThread32Next = NULL;
|
||
|
static HEAPLISTWALK pHeap32ListFirst = NULL;
|
||
|
static HEAPLISTWALK pHeap32ListNext = NULL;
|
||
|
static HEAPFIRST pHeap32First = NULL;
|
||
|
static HEAPNEXT pHeap32Next = NULL;
|
||
|
HANDLE hSnapshot;
|
||
|
|
||
|
|
||
|
/* initialize the Toolhelp32 function pointers */
|
||
|
if ( !pCreateToolhelp32Snapshot ) {
|
||
|
HANDLE hKernel;
|
||
|
|
||
|
if ( debug_me )
|
||
|
log_debug ("rndw32#slow_gatherer_95: init toolkit\n" );
|
||
|
|
||
|
/* Obtain the module handle of the kernel to retrieve the addresses
|
||
|
* of the Toolhelp32 functions */
|
||
|
if ( ( !(hKernel = GetModuleHandle ("KERNEL32.DLL"))) ) {
|
||
|
g10_log_fatal ( "rndw32: can't get module handle\n" );
|
||
|
}
|
||
|
|
||
|
/* Now get pointers to the functions */
|
||
|
pCreateToolhelp32Snapshot = (CREATESNAPSHOT) GetProcAddress (hKernel,
|
||
|
"CreateToolhelp32Snapshot");
|
||
|
pModule32First = (MODULEWALK) GetProcAddress (hKernel, "Module32First");
|
||
|
pModule32Next = (MODULEWALK) GetProcAddress (hKernel, "Module32Next");
|
||
|
pProcess32First = (PROCESSWALK) GetProcAddress (hKernel,
|
||
|
"Process32First");
|
||
|
pProcess32Next = (PROCESSWALK) GetProcAddress (hKernel,
|
||
|
"Process32Next");
|
||
|
pThread32First = (THREADWALK) GetProcAddress (hKernel, "Thread32First");
|
||
|
pThread32Next = (THREADWALK) GetProcAddress (hKernel, "Thread32Next");
|
||
|
pHeap32ListFirst = (HEAPLISTWALK) GetProcAddress (hKernel,
|
||
|
"Heap32ListFirst");
|
||
|
pHeap32ListNext = (HEAPLISTWALK) GetProcAddress (hKernel,
|
||
|
"Heap32ListNext");
|
||
|
pHeap32First = (HEAPFIRST) GetProcAddress (hKernel, "Heap32First");
|
||
|
pHeap32Next = (HEAPNEXT) GetProcAddress (hKernel, "Heap32Next");
|
||
|
|
||
|
if ( !pCreateToolhelp32Snapshot
|
||
|
|| !pModule32First || !pModule32Next
|
||
|
|| !pProcess32First || !pProcess32Next
|
||
|
|| !pThread32First || !pThread32Next
|
||
|
|| !pHeap32ListFirst || !pHeap32ListNext
|
||
|
|| !pHeap32First || !pHeap32Next ) {
|
||
|
g10_log_fatal ( "rndw32: failed to get a toolhep function\n" );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Take a snapshot of everything we can get to which is currently
|
||
|
* in the system */
|
||
|
if ( !(hSnapshot = pCreateToolhelp32Snapshot (TH32CS_SNAPALL, 0)) ) {
|
||
|
g10_log_fatal ( "rndw32: failed to take a toolhelp snapshot\n" );
|
||
|
}
|
||
|
|
||
|
/* Walk through the local heap */
|
||
|
{ HEAPLIST32 hl32;
|
||
|
hl32.dwSize = sizeof (HEAPLIST32);
|
||
|
if (pHeap32ListFirst (hSnapshot, &hl32)) {
|
||
|
if ( debug_me )
|
||
|
log_debug ("rndw32#slow_gatherer_95: walk heap\n" );
|
||
|
do {
|
||
|
HEAPENTRY32 he32;
|
||
|
|
||
|
/* First add the information from the basic Heaplist32 struct */
|
||
|
(*add) ( &hl32, sizeof (hl32), requester );
|
||
|
|
||
|
/* Now walk through the heap blocks getting information
|
||
|
* on each of them */
|
||
|
he32.dwSize = sizeof (HEAPENTRY32);
|
||
|
if (pHeap32First (&he32, hl32.th32ProcessID, hl32.th32HeapID)){
|
||
|
do {
|
||
|
(*add) ( &he32, sizeof (he32), requester );
|
||
|
} while (pHeap32Next (&he32));
|
||
|
}
|
||
|
} while (pHeap32ListNext (hSnapshot, &hl32));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Walk through all processes */
|
||
|
{ PROCESSENTRY32 pe32;
|
||
|
pe32.dwSize = sizeof (PROCESSENTRY32);
|
||
|
if (pProcess32First (hSnapshot, &pe32)) {
|
||
|
if ( debug_me )
|
||
|
log_debug ("rndw32#slow_gatherer_95: walk processes\n" );
|
||
|
do {
|
||
|
(*add) ( &pe32, sizeof (pe32), requester );
|
||
|
} while (pProcess32Next (hSnapshot, &pe32));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Walk through all threads */
|
||
|
{ THREADENTRY32 te32;
|
||
|
te32.dwSize = sizeof (THREADENTRY32);
|
||
|
if (pThread32First (hSnapshot, &te32)) {
|
||
|
if ( debug_me )
|
||
|
log_debug ("rndw32#slow_gatherer_95: walk threads\n" );
|
||
|
do {
|
||
|
(*add) ( &te32, sizeof (te32), requester );
|
||
|
} while (pThread32Next (hSnapshot, &te32));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Walk through all modules associated with the process */
|
||
|
{ MODULEENTRY32 me32;
|
||
|
me32.dwSize = sizeof (MODULEENTRY32);
|
||
|
if (pModule32First (hSnapshot, &me32)) {
|
||
|
if ( debug_me )
|
||
|
log_debug ("rndw32#slow_gatherer_95: walk modules\n" );
|
||
|
do {
|
||
|
(*add) ( &me32, sizeof (me32), requester );
|
||
|
} while (pModule32Next (hSnapshot, &me32));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CloseHandle (hSnapshot);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
static void
|
||
|
slow_gatherer_windowsNT( void (*add)(const void*, size_t, int), int requester )
|
||
|
{
|
||
|
static int is_initialized = 0;
|
||
|
static NETSTATISTICSGET pNetStatisticsGet = NULL;
|
||
|
static NETAPIBUFFERSIZE pNetApiBufferSize = NULL;
|
||
|
static NETAPIBUFFERFREE pNetApiBufferFree = NULL;
|
||
|
static int is_workstation = 1;
|
||
|
|
||
|
static int cbPerfData = PERFORMANCE_BUFFER_SIZE;
|
||
|
PERF_DATA_BLOCK *pPerfData;
|
||
|
HANDLE hDevice, hNetAPI32 = NULL;
|
||
|
DWORD dwSize, status;
|
||
|
int nDrive;
|
||
|
|
||
|
if ( !is_initialized ) {
|
||
|
HKEY hKey;
|
||
|
|
||
|
if ( debug_me )
|
||
|
log_debug ("rndw32#slow_gatherer_nt: init toolkit\n" );
|
||
|
/* Find out whether this is an NT server or workstation if necessary */
|
||
|
if (RegOpenKeyEx (HKEY_LOCAL_MACHINE,
|
||
|
"SYSTEM\\CurrentControlSet\\Control\\ProductOptions",
|
||
|
0, KEY_READ, &hKey) == ERROR_SUCCESS) {
|
||
|
BYTE szValue[32];
|
||
|
dwSize = sizeof (szValue);
|
||
|
|
||
|
if ( debug_me )
|
||
|
log_debug ("rndw32#slow_gatherer_nt: check product options\n" );
|
||
|
status = RegQueryValueEx (hKey, "ProductType", 0, NULL,
|
||
|
szValue, &dwSize);
|
||
|
if (status == ERROR_SUCCESS
|
||
|
&& ascii_strcasecmp (szValue, "WinNT")) {
|
||
|
/* Note: There are (at least) three cases for ProductType:
|
||
|
* WinNT = NT Workstation, ServerNT = NT Server, LanmanNT =
|
||
|
* NT Server acting as a Domain Controller */
|
||
|
is_workstation = 0;
|
||
|
if ( debug_me )
|
||
|
log_debug ("rndw32: this is a NT server\n");
|
||
|
}
|
||
|
RegCloseKey (hKey);
|
||
|
}
|
||
|
|
||
|
/* Initialize the NetAPI32 function pointers if necessary */
|
||
|
if ( (hNetAPI32 = LoadLibrary ("NETAPI32.DLL")) ) {
|
||
|
if ( debug_me )
|
||
|
log_debug ("rndw32#slow_gatherer_nt: netapi32 loaded\n" );
|
||
|
pNetStatisticsGet = (NETSTATISTICSGET) GetProcAddress (hNetAPI32,
|
||
|
"NetStatisticsGet");
|
||
|
pNetApiBufferSize = (NETAPIBUFFERSIZE) GetProcAddress (hNetAPI32,
|
||
|
"NetApiBufferSize");
|
||
|
pNetApiBufferFree = (NETAPIBUFFERFREE) GetProcAddress (hNetAPI32,
|
||
|
"NetApiBufferFree");
|
||
|
|
||
|
if ( !pNetStatisticsGet
|
||
|
|| !pNetApiBufferSize || !pNetApiBufferFree ) {
|
||
|
FreeLibrary (hNetAPI32);
|
||
|
hNetAPI32 = NULL;
|
||
|
g10_log_debug ("rndw32: No NETAPI found\n" );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
is_initialized = 1;
|
||
|
}
|
||
|
|
||
|
/* Get network statistics. Note: Both NT Workstation and NT Server by
|
||
|
* default will be running both the workstation and server services. The
|
||
|
* heuristic below is probably useful though on the assumption that the
|
||
|
* majority of the network traffic will be via the appropriate service.
|
||
|
* In any case the network statistics return almost no randomness */
|
||
|
{ LPBYTE lpBuffer;
|
||
|
if (hNetAPI32 && !pNetStatisticsGet (NULL,
|
||
|
is_workstation ? L"LanmanWorkstation" :
|
||
|
L"LanmanServer", 0, 0, &lpBuffer) ) {
|
||
|
if ( debug_me )
|
||
|
log_debug ("rndw32#slow_gatherer_nt: get netstats\n" );
|
||
|
pNetApiBufferSize (lpBuffer, &dwSize);
|
||
|
(*add) ( lpBuffer, dwSize,requester );
|
||
|
pNetApiBufferFree (lpBuffer);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Get disk I/O statistics for all the hard drives */
|
||
|
for (nDrive = 0;; nDrive++) {
|
||
|
DISK_PERFORMANCE diskPerformance;
|
||
|
char szDevice[50];
|
||
|
|
||
|
/* Check whether we can access this device */
|
||
|
sprintf (szDevice, "\\\\.\\PhysicalDrive%d", nDrive);
|
||
|
hDevice = CreateFile (szDevice, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||
|
NULL, OPEN_EXISTING, 0, NULL);
|
||
|
if (hDevice == INVALID_HANDLE_VALUE)
|
||
|
break;
|
||
|
|
||
|
/* Note: This only works if you have turned on the disk performance
|
||
|
* counters with 'diskperf -y'. These counters are off by default */
|
||
|
if (DeviceIoControl (hDevice, IOCTL_DISK_PERFORMANCE, NULL, 0,
|
||
|
&diskPerformance, sizeof (DISK_PERFORMANCE),
|
||
|
&dwSize, NULL))
|
||
|
{
|
||
|
if ( debug_me )
|
||
|
log_debug ("rndw32#slow_gatherer_nt: iostats drive %d\n",
|
||
|
nDrive );
|
||
|
(*add) ( &diskPerformance, dwSize, requester );
|
||
|
}
|
||
|
else {
|
||
|
log_info ("NOTE: you should run 'diskperf -y' "
|
||
|
"to enable the disk statistics\n");
|
||
|
}
|
||
|
CloseHandle (hDevice);
|
||
|
}
|
||
|
|
||
|
#if 0 /* we don't need this in GnuPG */
|
||
|
/* Wait for any async keyset driver binding to complete. You may be
|
||
|
* wondering what this call is doing here... the reason it's necessary is
|
||
|
* because RegQueryValueEx() will hang indefinitely if the async driver
|
||
|
* bind is in progress. The problem occurs in the dynamic loading and
|
||
|
* linking of driver DLL's, which work as follows:
|
||
|
*
|
||
|
* hDriver = LoadLibrary( DRIVERNAME );
|
||
|
* pFunction1 = ( TYPE_FUNC1 ) GetProcAddress( hDriver, NAME_FUNC1 );
|
||
|
* pFunction2 = ( TYPE_FUNC1 ) GetProcAddress( hDriver, NAME_FUNC2 );
|
||
|
*
|
||
|
* If RegQueryValueEx() is called while the GetProcAddress()'s are in
|
||
|
* progress, it will hang indefinitely. This is probably due to some
|
||
|
* synchronisation problem in the NT kernel where the GetProcAddress()
|
||
|
* calls affect something like a module reference count or function
|
||
|
* reference count while RegQueryValueEx() is trying to take a snapshot
|
||
|
* of the statistics, which include the reference counts. Because of
|
||
|
* this, we have to wait until any async driver bind has completed
|
||
|
* before we can call RegQueryValueEx() */
|
||
|
waitSemaphore (SEMAPHORE_DRIVERBIND);
|
||
|
#endif
|
||
|
|
||
|
/* Get information from the system performance counters. This can take
|
||
|
* a few seconds to do. In some environments the call to
|
||
|
* RegQueryValueEx() can produce an access violation at some random time
|
||
|
* in the future, adding a short delay after the following code block
|
||
|
* makes the problem go away. This problem is extremely difficult to
|
||
|
* reproduce, I haven't been able to get it to occur despite running it
|
||
|
* on a number of machines. The best explanation for the problem is that
|
||
|
* on the machine where it did occur, it was caused by an external driver
|
||
|
* or other program which adds its own values under the
|
||
|
* HKEY_PERFORMANCE_DATA key. The NT kernel calls the required external
|
||
|
* modules to map in the data, if there's a synchronisation problem the
|
||
|
* external module would write its data at an inappropriate moment,
|
||
|
* causing the access violation. A low-level memory checker indicated
|
||
|
* that ExpandEnvironmentStrings() in KERNEL32.DLL, called an
|
||
|
* interminable number of calls down inside RegQueryValueEx(), was
|
||
|
* overwriting memory (it wrote twice the allocated size of a buffer to a
|
||
|
* buffer allocated by the NT kernel). This may be what's causing the
|
||
|
* problem, but since it's in the kernel there isn't much which can be
|
||
|
* done.
|
||
|
*
|
||
|
* In addition to these problems the code in RegQueryValueEx() which
|
||
|
* estimates the amount of memory required to return the performance
|
||
|
* counter information isn't very accurate, since it always returns a
|
||
|
* worst-case estimate which is usually nowhere near the actual amount
|
||
|
* required. For example it may report that 128K of memory is required,
|
||
|
* but only return 64K of data */
|
||
|
{ pPerfData = m_alloc (cbPerfData);
|
||
|
for (;;) {
|
||
|
dwSize = cbPerfData;
|
||
|
if ( debug_me )
|
||
|
log_debug ("rndw32#slow_gatherer_nt: get perf data\n" );
|
||
|
status = RegQueryValueEx (HKEY_PERFORMANCE_DATA, "Global", NULL,
|
||
|
NULL, (LPBYTE) pPerfData, &dwSize);
|
||
|
if (status == ERROR_SUCCESS) {
|
||
|
if (!memcmp (pPerfData->Signature, L"PERF", 8)) {
|
||
|
(*add) ( pPerfData, dwSize, requester );
|
||
|
}
|
||
|
else
|
||
|
g10_log_debug ( "rndw32: no PERF signature\n");
|
||
|
break;
|
||
|
}
|
||
|
else if (status == ERROR_MORE_DATA) {
|
||
|
cbPerfData += PERFORMANCE_BUFFER_STEP;
|
||
|
pPerfData = m_realloc (pPerfData, cbPerfData);
|
||
|
}
|
||
|
else {
|
||
|
g10_log_debug ( "rndw32: get performance data problem\n");
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
m_free (pPerfData);
|
||
|
}
|
||
|
/* Although this isn't documented in the Win32 API docs, it's necessary
|
||
|
to explicitly close the HKEY_PERFORMANCE_DATA key after use (it's
|
||
|
implicitly opened on the first call to RegQueryValueEx()). If this
|
||
|
isn't done then any system components which provide performance data
|
||
|
can't be removed or changed while the handle remains active */
|
||
|
RegCloseKey (HKEY_PERFORMANCE_DATA);
|
||
|
}
|
||
|
|
||
|
|
||
|
static int
|
||
|
gather_random( void (*add)(const void*, size_t, int), int requester,
|
||
|
size_t length, int level )
|
||
|
{
|
||
|
static int is_initialized;
|
||
|
static int is_windowsNT, has_toolhelp;
|
||
|
|
||
|
|
||
|
if( !level )
|
||
|
return 0;
|
||
|
/* We don't differentiate between level 1 and 2 here because
|
||
|
* there is no nternal entropy pool as a scary resource. It may
|
||
|
* all work slower, but because our entropy source will never
|
||
|
* block but deliver some not easy to measure entropy, we assume level 2
|
||
|
*/
|
||
|
|
||
|
|
||
|
if ( !is_initialized ) {
|
||
|
OSVERSIONINFO osvi = { sizeof( osvi ) };
|
||
|
DWORD platform;
|
||
|
|
||
|
GetVersionEx( &osvi );
|
||
|
platform = osvi.dwPlatformId;
|
||
|
is_windowsNT = platform == VER_PLATFORM_WIN32_NT;
|
||
|
has_toolhelp = (platform == VER_PLATFORM_WIN32_WINDOWS
|
||
|
|| (is_windowsNT && osvi.dwMajorVersion >= 5));
|
||
|
|
||
|
if ( platform == VER_PLATFORM_WIN32s ) {
|
||
|
g10_log_fatal("can't run on a W32s platform\n" );
|
||
|
}
|
||
|
is_initialized = 1;
|
||
|
if ( debug_me )
|
||
|
log_debug ("rndw32#gather_random: platform=%d\n", (int)platform );
|
||
|
}
|
||
|
|
||
|
|
||
|
if ( debug_me )
|
||
|
log_debug ("rndw32#gather_random: req=%d len=%u lvl=%d\n",
|
||
|
requester, (unsigned int)length, level );
|
||
|
|
||
|
if ( has_toolhelp ) {
|
||
|
slow_gatherer_windows95 ( add, requester );
|
||
|
}
|
||
|
if ( is_windowsNT ) {
|
||
|
slow_gatherer_windowsNT ( add, requester );
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
static int
|
||
|
gather_random_fast( void (*add)(const void*, size_t, int), int requester )
|
||
|
{
|
||
|
static int addedFixedItems = 0;
|
||
|
|
||
|
if ( debug_me )
|
||
|
log_debug ("rndw32#gather_random_fast: req=%d\n", requester );
|
||
|
|
||
|
/* Get various basic pieces of system information: Handle of active
|
||
|
* window, handle of window with mouse capture, handle of clipboard owner
|
||
|
* handle of start of clpboard viewer list, pseudohandle of current
|
||
|
* process, current process ID, pseudohandle of current thread, current
|
||
|
* thread ID, handle of desktop window, handle of window with keyboard
|
||
|
* focus, whether system queue has any events, cursor position for last
|
||
|
* message, 1 ms time for last message, handle of window with clipboard
|
||
|
* open, handle of process heap, handle of procs window station, types of
|
||
|
* events in input queue, and milliseconds since Windows was started */
|
||
|
{ byte buffer[20*sizeof(ulong)], *bufptr;
|
||
|
bufptr = buffer;
|
||
|
#define ADD(f) do { ulong along = (ulong)(f); \
|
||
|
memcpy (bufptr, &along, sizeof (along) ); \
|
||
|
bufptr += sizeof (along); } while (0)
|
||
|
ADD ( GetActiveWindow ());
|
||
|
ADD ( GetCapture ());
|
||
|
ADD ( GetClipboardOwner ());
|
||
|
ADD ( GetClipboardViewer ());
|
||
|
ADD ( GetCurrentProcess ());
|
||
|
ADD ( GetCurrentProcessId ());
|
||
|
ADD ( GetCurrentThread ());
|
||
|
ADD ( GetCurrentThreadId ());
|
||
|
ADD ( GetDesktopWindow ());
|
||
|
ADD ( GetFocus ());
|
||
|
ADD ( GetInputState ());
|
||
|
ADD ( GetMessagePos ());
|
||
|
ADD ( GetMessageTime ());
|
||
|
ADD ( GetOpenClipboardWindow ());
|
||
|
ADD ( GetProcessHeap ());
|
||
|
ADD ( GetProcessWindowStation ());
|
||
|
ADD ( GetQueueStatus (QS_ALLEVENTS));
|
||
|
ADD ( GetTickCount ());
|
||
|
|
||
|
assert ( bufptr-buffer < sizeof (buffer) );
|
||
|
(*add) ( buffer, bufptr-buffer, requester );
|
||
|
#undef ADD
|
||
|
}
|
||
|
|
||
|
/* Get multiword system information: Current caret position, current
|
||
|
* mouse cursor position */
|
||
|
{ POINT point;
|
||
|
GetCaretPos (&point);
|
||
|
(*add) ( &point, sizeof (point), requester );
|
||
|
GetCursorPos (&point);
|
||
|
(*add) ( &point, sizeof (point), requester );
|
||
|
}
|
||
|
|
||
|
/* Get percent of memory in use, bytes of physical memory, bytes of free
|
||
|
* physical memory, bytes in paging file, free bytes in paging file, user
|
||
|
* bytes of address space, and free user bytes */
|
||
|
{ MEMORYSTATUS memoryStatus;
|
||
|
memoryStatus.dwLength = sizeof (MEMORYSTATUS);
|
||
|
GlobalMemoryStatus (&memoryStatus);
|
||
|
(*add) ( &memoryStatus, sizeof (memoryStatus), requester );
|
||
|
}
|
||
|
|
||
|
/* Get thread and process creation time, exit time, time in kernel mode,
|
||
|
and time in user mode in 100ns intervals */
|
||
|
{ HANDLE handle;
|
||
|
FILETIME creationTime, exitTime, kernelTime, userTime;
|
||
|
DWORD minimumWorkingSetSize, maximumWorkingSetSize;
|
||
|
|
||
|
handle = GetCurrentThread ();
|
||
|
GetThreadTimes (handle, &creationTime, &exitTime,
|
||
|
&kernelTime, &userTime);
|
||
|
(*add) ( &creationTime, sizeof (creationTime), requester );
|
||
|
(*add) ( &exitTime, sizeof (exitTime), requester );
|
||
|
(*add) ( &kernelTime, sizeof (kernelTime), requester );
|
||
|
(*add) ( &userTime, sizeof (userTime), requester );
|
||
|
|
||
|
handle = GetCurrentProcess ();
|
||
|
GetProcessTimes (handle, &creationTime, &exitTime,
|
||
|
&kernelTime, &userTime);
|
||
|
(*add) ( &creationTime, sizeof (creationTime), requester );
|
||
|
(*add) ( &exitTime, sizeof (exitTime), requester );
|
||
|
(*add) ( &kernelTime, sizeof (kernelTime), requester );
|
||
|
(*add) ( &userTime, sizeof (userTime), requester );
|
||
|
|
||
|
/* Get the minimum and maximum working set size for the current process */
|
||
|
GetProcessWorkingSetSize (handle, &minimumWorkingSetSize,
|
||
|
&maximumWorkingSetSize);
|
||
|
(*add) ( &minimumWorkingSetSize,
|
||
|
sizeof (&minimumWorkingSetSize), requester );
|
||
|
(*add) ( &maximumWorkingSetSize,
|
||
|
sizeof (&maximumWorkingSetSize), requester );
|
||
|
}
|
||
|
|
||
|
|
||
|
/* The following are fixed for the lifetime of the process so we only
|
||
|
* add them once */
|
||
|
if (!addedFixedItems) {
|
||
|
STARTUPINFO startupInfo;
|
||
|
|
||
|
/* Get name of desktop, console window title, new window position and
|
||
|
* size, window flags, and handles for stdin, stdout, and stderr */
|
||
|
startupInfo.cb = sizeof (STARTUPINFO);
|
||
|
GetStartupInfo (&startupInfo);
|
||
|
(*add) ( &startupInfo, sizeof (STARTUPINFO), requester );
|
||
|
addedFixedItems = 1;
|
||
|
}
|
||
|
|
||
|
/* The performance of QPC varies depending on the architecture it's
|
||
|
* running on and on the OS. Under NT it reads the CPU's 64-bit timestamp
|
||
|
* counter (at least on a Pentium and newer '486's, it hasn't been tested
|
||
|
* on anything without a TSC), under Win95 it reads the 1.193180 MHz PIC
|
||
|
* timer. There are vague mumblings in the docs that it may fail if the
|
||
|
* appropriate hardware isn't available (possibly '386's or MIPS machines
|
||
|
* running NT), but who's going to run NT on a '386? */
|
||
|
{ LARGE_INTEGER performanceCount;
|
||
|
if (QueryPerformanceCounter (&performanceCount)) {
|
||
|
if ( debug_me )
|
||
|
log_debug ("rndw32#gather_random_fast: perf data\n");
|
||
|
(*add) (&performanceCount, sizeof (&performanceCount), requester);
|
||
|
}
|
||
|
else { /* Millisecond accuracy at best... */
|
||
|
DWORD aword = GetTickCount ();
|
||
|
(*add) (&aword, sizeof (aword), requester );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
#endif /* !USE_ENTROPY_DLL */
|
||
|
|
||
|
|
||
|
#ifndef IS_MODULE
|
||
|
static
|
||
|
#endif
|
||
|
const char * const gnupgext_version = "RNDW32 ($Revision$)";
|
||
|
|
||
|
static struct {
|
||
|
int class;
|
||
|
int version;
|
||
|
void *func;
|
||
|
} func_table[] = {
|
||
|
{ 40, 1, gather_random },
|
||
|
{ 41, 1, gather_random_fast },
|
||
|
};
|
||
|
|
||
|
|
||
|
#ifndef IS_MODULE
|
||
|
static
|
||
|
#endif
|
||
|
void *
|
||
|
gnupgext_enum_func( int what, int *sequence, int *class, int *vers )
|
||
|
{
|
||
|
void *ret;
|
||
|
int i = *sequence;
|
||
|
|
||
|
debug_me = !!getenv("DEBUG_RNDW32");
|
||
|
|
||
|
do {
|
||
|
if ( i >= DIM(func_table) || i < 0 ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
*class = func_table[i].class;
|
||
|
*vers = func_table[i].version;
|
||
|
ret = func_table[i].func;
|
||
|
i++;
|
||
|
} while ( what && what != *class );
|
||
|
|
||
|
*sequence = i;
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
#ifndef IS_MODULE
|
||
|
void
|
||
|
rndw32_constructor(void)
|
||
|
{
|
||
|
register_internal_cipher_extension( gnupgext_version,
|
||
|
gnupgext_enum_func );
|
||
|
}
|
||
|
#endif
|
||
|
|