📄 rndunix.c
字号:
void ( *disp )( int );
const int pageSize = getPageSize();
#if defined( __hpux )
size_t maxFD = 0;
#else
int maxFD = 0;
#endif /* OS-specific brokenness */
int bufPos, i, usefulness = 0;
/* Make sure we don't start more than one slow poll at a time */
enterMutex( MUTEX_RANDOMPOLLING );
if( gathererProcess )
{
exitMutex( MUTEX_RANDOMPOLLING );
return;
}
/* Some systems provide further info which we can grab before we start
the slow poll */
getDevRandomData();
if( access( "/proc/interrupts", R_OK ) )
getProcFSdata();
getEGDdata();
#ifdef USE_KSTAT
getKstatData();
#endif /* USE_KSTAT */
#ifdef USE_PROC
getProcData();
#endif /* USE_PROC */
/* Reset the SIGC(H)LD handler to the system default. This is necessary
because if the program which cryptlib is a part of installs its own
SIGC(H)LD handler, it will end up reaping the cryptlib children before
cryptlib can. As a result, my_pclose() will call waitpid() on a
process which has already been reaped by the installed handler and
return an error, so the read data won't be added to the randomness
pool.
There are two types of SIGC(H)LD naming, the SysV SIGCLD and the
BSD/Posix SIGCHLD, so we need to handle either possibility */
#ifdef SIGCLD
disp = signal( SIGCLD, SIG_DFL );
#else
disp = signal( SIGCHLD, SIG_DFL );
#endif /* SIGCLD */
if( disp != SIG_DFL && disp != SIG_IGN )
{
/* The user has installed their own SIGC(H)LD handler, warn them that
they need to take special steps to manage this problem. This is
ugly, but it's better than leaving a difficult-to-find problem
where the user's SIGC(H)LD handling is pre-empted by cryptlib */
printf( "Conflicting SIGC(H)LD handling detected in randomness "
"polling code,\nfile " __FILE__ ", line %d.\n", __LINE__ );
}
/* Set up the shared memory */
gathererBufSize = ( SHARED_BUFSIZE / pageSize ) * ( pageSize + 1 );
if( ( gathererMemID = shmget( IPC_PRIVATE, gathererBufSize,
IPC_CREAT | 0600 ) ) == -1 || \
( gathererBuffer = ( BYTE * ) shmat( gathererMemID,
NULL, 0 ) ) == ( BYTE * ) -1 )
{
if( gathererMemID != -1 )
shmctl( gathererMemID, IPC_RMID, NULL );
exitMutex( MUTEX_RANDOMPOLLING );
return; /* Something broke */
}
/* At this point we have a possible race condition since we need to set
the gatherer PID value inside the mutex but messing with mutexes
across a fork() is somewhat messy. To resolve this, we set the PID
to a nonzero value (which marks it as busy) and exit the mutex, then
overwrite it with the real PID (also nonzero) from the fork */
gathererProcess = -1;
exitMutex( MUTEX_RANDOMPOLLING );
/* Fork off the gatherer, the parent process returns to the caller */
if( ( gathererProcess = fork() ) || ( gathererProcess == -1 ) )
{
/* If the fork() failed, clear the gatherer process value to make
sure we're not locked out of retrying the poll later */
if( gathererProcess == -1 )
{
enterMutex( MUTEX_RANDOMPOLLING );
gathererProcess = 0;
exitMutex( MUTEX_RANDOMPOLLING );
}
return; /* Error/parent process returns */
}
fclose( stderr ); /* Arrghh!! It's Stuart code!! */
/* Fire up each randomness source */
FD_ZERO( &fds );
for( i = 0; dataSources[ i ].path != NULL; i++ )
{
/* Since popen() is a fairly heavy function, we check to see whether
the executable exists before we try to run it */
if( access( dataSources[ i ].path, X_OK ) )
{
#ifdef DEBUG_RANDOM
printf( "%s not present%s\n", dataSources[ i ].path,
dataSources[ i ].hasAlternative ? ", has alternatives" : "" );
#endif /* DEBUG_RANDOM */
dataSources[ i ].pipe = NULL;
}
else
dataSources[ i ].pipe = my_popen( &dataSources[ i ] );
if( dataSources[ i ].pipe != NULL )
{
dataSources[ i ].pipeFD = fileno( dataSources[ i ].pipe );
if( dataSources[ i ].pipeFD > maxFD )
maxFD = dataSources[ i ].pipeFD;
fcntl( dataSources[ i ].pipeFD, F_SETFL, O_NONBLOCK );
FD_SET( dataSources[ i ].pipeFD, &fds );
dataSources[ i ].length = 0;
/* If there are alternatives for this command, don't try and
execute them */
while( dataSources[ i ].hasAlternative )
{
#ifdef DEBUG_RANDOM
printf( "Skipping %s\n", dataSources[ i + 1 ].path );
#endif /* DEBUG_RANDOM */
i++;
}
}
}
gathererInfo = ( GATHERER_INFO * ) gathererBuffer;
bufPos = sizeof( GATHERER_INFO ); /* Start of buf.has status info */
/* Suck all the data we can get from each of the sources */
moreSources = TRUE;
startTime = time( NULL );
while( moreSources && bufPos <= gathererBufSize )
{
/* Wait for data to become available from any of the sources, with a
timeout of 10 seconds. This adds even more randomness since data
becomes available in a nondeterministic fashion. Kudos to HP's QA
department for managing to ship a select() which breaks its own
prototype */
tv.tv_sec = 10;
tv.tv_usec = 0;
#if defined( __hpux ) && ( OS_VERSION == 9 || OS_VERSION == 0 )
if( select( maxFD + 1, ( int * ) &fds, NULL, NULL, &tv ) == -1 )
#else
if( select( maxFD + 1, &fds, NULL, NULL, &tv ) == -1 )
#endif /* __hpux */
break;
/* One of the sources has data available, read it into the buffer */
for( i = 0; dataSources[ i ].path != NULL; i++ )
if( dataSources[ i ].pipe != NULL && \
FD_ISSET( dataSources[ i ].pipeFD, &fds ) )
{
size_t noBytes;
if( ( noBytes = fread( gathererBuffer + bufPos, 1,
gathererBufSize - bufPos,
dataSources[ i ].pipe ) ) == 0 )
{
if( my_pclose( &dataSources[ i ] ) == 0 )
{
int total = 0;
/* Try and estimate how much entropy we're getting
from a data source */
if( dataSources[ i ].usefulness )
if( dataSources[ i ].usefulness < 0 )
total = ( dataSources[ i ].length + 999 ) / \
-dataSources[ i ].usefulness;
else
total = dataSources[ i ].length / \
dataSources[ i ].usefulness;
#ifdef DEBUG_RANDOM
printf( "%s %s contributed %d bytes (compressed), "
"usefulness = %d\n", dataSources[ i ].path,
( dataSources[ i ].arg != NULL ) ? \
dataSources[ i ].arg : "",
dataSources[ i ].length, total );
#endif /* DEBUG_RANDOM */
usefulness += total;
}
dataSources[ i ].pipe = NULL;
}
else
{
int currPos = bufPos;
int endPos = bufPos + noBytes;
/* Run-length compress the input byte sequence */
while( currPos < endPos )
{
int ch = gathererBuffer[ currPos ];
/* If it's a single byte, just copy it over */
if( ch != gathererBuffer[ currPos + 1 ] )
{
gathererBuffer[ bufPos++ ] = ch;
currPos++;
}
else
{
int count = 0;
/* It's a run of repeated bytes, replace them with
the byte count mod 256 */
while( ( ch == gathererBuffer[ currPos ] ) && \
currPos < endPos )
{
count++;
currPos++;
}
gathererBuffer[ bufPos++ ] = count;
noBytes -= count - 1;
}
}
/* Remember the number of (compressed) bytes of input we
obtained */
dataSources[ i ].length += noBytes;
}
}
/* Check if there is more input available on any of the sources */
moreSources = FALSE;
FD_ZERO( &fds );
for( i = 0; dataSources[ i ].path != NULL; i++ )
if( dataSources[ i ].pipe != NULL )
{
FD_SET( dataSources[ i ].pipeFD, &fds );
moreSources = TRUE;
}
/* If we've gone over our time limit, kill anything still hanging
around and exit. This prevents problems with input from blocked
sources */
if( time( NULL ) > startTime + SLOWPOLL_TIMEOUT )
{
for( i = 0; dataSources[ i ].path != NULL; i++ )
if( dataSources[ i ].pipe != NULL )
{
#ifdef DEBUG_RANDOM
printf( "Aborting read of %s due to timeout.\n",
dataSources[ i ].path );
#endif /* DEBUG_RANDOM */
fclose( dataSources[ i ].pipe );
kill( dataSources[ i ].pid, SIGKILL );
dataSources[ i ].pipe = NULL;
dataSources[ i ].pid = 0;
}
moreSources = FALSE;
#ifdef DEBUG_RANDOM
puts( "Poll timed out, probably due to blocked data source." );
#endif /* DEBUG_RANDOM */
}
}
gathererInfo->usefulness = usefulness;
gathererInfo->noBytes = bufPos;
#ifdef DEBUG_RANDOM
printf( "Got %d bytes, usefulness = %d\n", bufPos, usefulness );
#endif /* DEBUG_RANDOM */
/* "Thou child of the daemon, ... wilt thou not cease...?" -- Acts 13:10 */
exit( 0 );
}
/* 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( void )
{
enterMutex( MUTEX_RANDOMPOLLING );
if( gathererProcess )
{
RESOURCE_DATA msgData;
GATHERER_INFO *gathererInfo = ( GATHERER_INFO * ) gathererBuffer;
int quality, status;
/* Wait for the gathering process to finish, add the randomness it's
gathered, and detach the shared memory */
waitpid( gathererProcess, &status, 0 ); /* Should prob.check status */
quality = gathererInfo->usefulness * 5; /* 0-20 -> 0-100 */
setResourceData( &msgData, gathererBuffer, gathererInfo->noBytes );
krnlSendMessage( SYSTEM_OBJECT_HANDLE, RESOURCE_IMESSAGE_SETATTRIBUTE_S,
&msgData, CRYPT_IATTRIBUTE_RANDOM );
krnlSendMessage( SYSTEM_OBJECT_HANDLE, RESOURCE_IMESSAGE_SETATTRIBUTE,
&quality, CRYPT_IATTRIBUTE_RANDOM_QUALITY );
zeroise( gathererBuffer, gathererBufSize );
shmdt( gathererBuffer );
shmctl( gathererMemID, IPC_RMID, NULL );
gathererProcess = 0;
}
exitMutex( MUTEX_RANDOMPOLLING );
}
/* Check whether we've forked and we're the child. The mechanism used varies
depending on whether we're running in a single-threaded or multithreaded
environment, for single-threaded we check whether the pid has changed
since the last check, for multithreaded environments this isn't reliable
since some systems have per-thread pid's so we need to use
pthread_atfork() as a trigger to set the pid-changed flag.
Under Aches, calling pthread_atfork() with any combination of arguments or
circumstances produces a segfault, so we undefine USE_THREADS to force the
use of the getpid()-based fork detection */
#if defined( USE_THREADS ) && defined( _AIX )
#undef USE_THREADS
#endif /* USE_THREADS && _AIX */
#ifdef USE_THREADS
static BOOLEAN forked = FALSE;
BOOLEAN checkForked( void )
{
BOOLEAN hasForked;
/* Read the forked-t flag in a thread-safe manner */
enterMutex( MUTEX_RANDOMPOLLING );
hasForked = forked;
forked = FALSE;
exitMutex( MUTEX_RANDOMPOLLING );
return( hasForked );
}
void setForked( void )
{
/* Set the forked-t flag in a thread-safe manner */
enterMutex( MUTEX_RANDOMPOLLING );
forked = TRUE;
exitMutex( MUTEX_RANDOMPOLLING );
}
#else
BOOLEAN checkForked( void )
{
static pid_t originalPID = -1;
/* Set the initial PID if necessary */
if( originalPID == -1 )
originalPID = getpid();
/* If the pid has changed we've forked and we're the child, remember the
new pid */
if( getpid() != originalPID )
{
originalPID = getpid();
return( TRUE );
}
return( FALSE );
}
#endif /* USE_THREADS */
/* Initialise and clean up any auxiliary randomness-related objects */
void initRandomPolling( void )
{
/* If it's multithreaded code, we need to ensure that we're signalled if
another thread calls fork(). Hardcoding in the Posix function name at
this point is safe because it also works for Solaris threads. We set
the forked flag in both the child and the parent to ensure that both
sides remix the pool thoroughly */
#ifdef USE_THREADS
pthread_atfork( NULL, setForked, setForked );
#endif /* USE_THREADS */
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -