📄 rndwin32.c
字号:
}
}
clFree( "slowPollWinNT", buffer );
/* If we got enough data, we can leave now without having to try
for a Win32-level performance information query */
if( noResults > 15 )
{
static const int quality = 100;
checkPollExit();
krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_SETATTRIBUTE,
( void * ) &quality,
CRYPT_IATTRIBUTE_ENTROPY_QUALITY );
return;
}
}
}
checkPollExit();
/* 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 );
checkPollExit();
/* 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, in
some cases 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. MS knowledge base article Q178887 covers
this type of problem, it's typically caused by an external driver or
other program that adds its own values under the
HKEY_PERFORMANCE_DATA key. The NT kernel, via Advapi32.dll, calls the
required external module to map in the data inside an SEH try/except
block, so problems in the module's collect function don't pop up until
after it has finished, so the fault appears to occur in Advapi32.dll.
There may be problems in the NT kernel as well though, 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).
OTOH this could be coming from the external module calling back into
the kernel, which eventually causes the problem described above.
Possibly as an extension of the problem that the waitSemaphore() call
above works around, running two instances of cryptlib (e.g. two
applications that use it) under NT4 can result in one of them hanging
in the RegQueryValueEx() call. This happens only under NT4 and is
hard to reproduce in any consistent manner.
One workaround that helps a bit is to read the registry as a remote
(rather than local) registry, it's possible that the use of a network
RPC call isolates the calling app from the problem in that whatever
service handles the RPC is taking the hit and not affecting the
calling app. Since this would require another round of extensive
testing to verify and the NT native API call is working fine, we'll
stick with the native API call for now.
Some versions of NT4 had a problem where the amount of data returned
was mis-reported and would never settle down, because of this the code
below includes a safety-catch that bails out after 10 attempts have
been made, this results in no data being returned but at does ensure
that the thread will terminate.
In addition to these problems the code in RegQueryValueEx() that
estimates the amount of memory required to return the performance
counter information isn't very accurate (it's much worse than the
"slightly-inaccurate" level that the MS docs warn about, it's usually
wildly off) 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.
Even worse than the registry-based performance counters is the
performance data helper (PDH) shim that tries to make the counters
look like the old Win16 API (which is also used by Win95). Under NT
this can consume tens of MB of memory and huge amounts of CPU time
while it gathers its data, and even running once can still consume
about 1/2MB of memory */
pPerfData = ( PPERF_DATA_BLOCK ) clAlloc( "slowPollNT", cbPerfData );
while( pPerfData != NULL && iterations++ < 10 )
{
dwSize = cbPerfData;
status = RegQueryValueEx( HKEY_PERFORMANCE_DATA, "Global", NULL,
NULL, ( LPBYTE ) pPerfData, &dwSize );
if( status == ERROR_SUCCESS )
{
if( !memcmp( pPerfData->Signature, L"PERF", 8 ) )
{
static const int quality = 100;
int status;
setMessageData( &msgData, pPerfData, dwSize );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
IMESSAGE_SETATTRIBUTE_S, &msgData,
CRYPT_IATTRIBUTE_ENTROPY );
if( cryptStatusOK( status ) )
krnlSendMessage( SYSTEM_OBJECT_HANDLE,
IMESSAGE_SETATTRIBUTE,
( void * ) &quality,
CRYPT_IATTRIBUTE_ENTROPY_QUALITY );
}
clFree( "slowPollWinNT", pPerfData );
pPerfData = NULL;
}
else
if( status == ERROR_MORE_DATA )
{
cbPerfData += PERFORMANCE_BUFFER_STEP;
pPerfData = ( PPERF_DATA_BLOCK ) realloc( pPerfData, cbPerfData );
}
}
/* 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 that provide performance data
can't be removed or changed while the handle remains active */
RegCloseKey( HKEY_PERFORMANCE_DATA );
}
/* Perform a thread-safe slow poll for Windows NT */
unsigned __stdcall threadSafeSlowPollWinNT( void *dummy )
{
#if 0
typedef BOOL ( WINAPI *CREATERESTRICTEDTOKEN )( HANDLE ExistingTokenHandle,
DWORD Flags, DWORD DisableSidCount,
PSID_AND_ATTRIBUTES SidsToDisable,
DWORD DeletePrivilegeCount,
PLUID_AND_ATTRIBUTES PrivilegesToDelete,
DWORD RestrictedSidCount,
PSID_AND_ATTRIBUTES SidsToRestrict,
PHANDLE NewTokenHandle );
static CREATERESTRICTEDTOKEN pCreateRestrictedToken = NULL;
static BOOLEAN isInited = FALSE;
#endif /* 0 */
UNUSED( dummy );
/* If the poll performed any kind of active operation like the Unix one
rather than just basic data reads it'd probably be a good idea to drop
privileges before we begin, something that can be performed by the
following code */
#if 0
if( !isInited )
{
OSVERSIONINFO osvi = { sizeof( osvi ) };
/* Since CreateRestrictedToken() is a Win2K function we can only use
it on a post-NT4 system, and have to bind it at runtime */
GetVersionEx( &osvi );
if( osvi.dwMajorVersion > 4 )
{
const HINSTANCE hAdvAPI32 = GetModuleHandle( "AdvAPI32.dll" );
pCreateRestrictedToken = ( CREATERESTRICTEDTOKEN ) \
GetProcAddress( hAdvAPI32, "CreateRestrictedToken" );
}
isInited = TRUE;
}
if( pCreateRestrictedToken != NULL )
{
HANDLE hToken, hNewToken;
ImpersonateSelf( SecurityImpersonation );
OpenThreadToken( GetCurrentThread(),
TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | \
TOKEN_QUERY | TOKEN_ADJUST_DEFAULT | \
TOKEN_IMPERSONATE, TRUE, &hToken );
CreateRestrictedToken( hToken, DISABLE_MAX_PRIVILEGE, 0, NULL, 0, NULL,
0, NULL, &hNewToken );
SetThreadToken( &hThread, hNewToken );
}
#endif /* 0 */
slowPollWinNT();
#if 0
if( pCreateRestrictedToken != NULL )
RevertToSelf();
#endif /* 0 */
_endthreadex( 0 );
return( 0 );
}
/* Perform a generic slow poll. This starts the OS-specific poll in a
separate thread */
void slowPoll( void )
{
checkPollExit();
/* Read data from the various hardware sources */
readPIIIRng();
readMBMData();
/* Start a threaded slow poll. If a slow poll is already running, we
just return since there isn't much point in running two of them at the
same time */
if( hThread )
return;
if( isWin95 )
hThread = ( HANDLE ) _beginthreadex( NULL, 0, &threadSafeSlowPollWin95,
NULL, 0, &threadID );
else
{
/* In theory since the thread is gathering info used (eventually)
for crypto keys we could set an ACL on the thread to make it
explicit that no-one else can mess with it:
void *aclInfo = initACLInfo( THREAD_ALL_ACCESS );
hThread = ( HANDLE ) _beginthreadex( getACLInfo( aclInfo ),
0, &threadSafeSlowPollWinNT,
NULL, 0, &threadID );
freeACLInfo( aclInfo );
However, although this is supposed to be the default access for
threads anyway, when used from a service (running under the
LocalSystem account) under Win2K SP4 and up, the thread creation
fails with error = 22 (invalid parameter). Presumably MS patched
some security hole or other in SP4, which causes the thread
creation to fail. Because of this problem, we don't set an ACL for
the thread */
hThread = ( HANDLE ) _beginthreadex( NULL, 0,
&threadSafeSlowPollWinNT,
NULL, 0, &threadID );
}
assert( hThread );
}
/* Wait for the randomness gathering to finish. Anything that requires the
gatherer process to have completed gathering entropy should call
waitforRandomCompletion(), which will block until the background process
completes */
void waitforRandomCompletion( const BOOLEAN force )
{
/* If there's not polling thread running, there's nothing to do */
if( !hThread )
return;
/* If this is a forced shutdown, tell the polling thread to exit */
if( force )
{
enterMutex( MUTEX_RANDOMPOLLING );
exitNow = TRUE;
exitMutex( MUTEX_RANDOMPOLLING );
/* Wait for the polling thread to terminate. Since this is a forced
shutdown, we only wait a fixed amount of time (2s) before we bail
out */
WaitForSingleObject( hThread, 2000 );
CloseHandle( hThread );
hThread = NULL;
return;
}
/* Sign the system object over to the polling thread to allow it to
update the entropy data */
krnlReleaseSystemObject( ( THREAD_HANDLE ) threadID );
/* Wait for the polling thread to terminate */
WaitForSingleObject( hThread, INFINITE );
CloseHandle( hThread );
hThread = NULL;
/* Return the system object to the calling thread */
krnlReacquireSystemObject();
}
/* Initialise and clean up any auxiliary randomness-related objects */
void initRandomPolling( void )
{
/* Reset the various module and object handles and status info and
initialise the PIII/P4 hardware RNG interface if it's present */
hAdvAPI32 = hNetAPI32 = hThread = NULL;
exitNow = FALSE;
initPIIIRng();
}
void endRandomPolling( void )
{
assert( hThread == NULL );
if( hNetAPI32 )
{
FreeLibrary( hNetAPI32 );
hNetAPI32 = NULL;
}
if( hProv != NULL )
{
pCryptReleaseContext( hProv, 0 );
hProv = NULL;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -