📄 rndunix.c
字号:
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 */
fprintf( stderr, "cryptlib: Conflicting SIGC(H)LD handling detected "
"in randomness polling code,\nfile " __FILE__ ", line %d. "
"See the source code for more\ninformation.\n", __LINE__ );
abort();
}
#endif /* 1 */
/* 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() ) != 0 )
{
/* 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 */
}
/* General housekeeping: Make sure that we can never dump core, and close
all inherited file descriptors. We need to do this because if we
don't and the calling app has FILE *'s open, these will be flushed
when we call exit() in the child and again when the parent writes to
them or closes them, resulting in everything that was present in the
FILE * buffer at the time of the fork() being written twice. An
alternative solution would be to call _exit() instead if exit() below,
but this is somewhat system-dependant and therefore a bit risky to
use.
In addition to this we should in theory call cryptEnd() since we don't
need any cryptlib objects beyond this point and it'd be a good idea to
clean them up to get rid of sensitive data held in memory. However in
some cases when using a crypto device or network interface or similar
item the shutdown in the child will also shut down the parent item
because cryptlib objects are reference-counted but external items
aren't (they're beyond the control of cryptlib). Even destroying just
contexts with keys loaded isn't possible because they may be tied to a
device that will propagate the shutdown from the child to the parent
via the device.
In general the child will be short-lived, and the use in its further
children of vfork() or the fact that many modern fork()s have copy-on-
write semantics even if no vfork() is available will mean that
cryptlib memory is never copied to the child and further children. It
would, however, be better if there were some way to perform a neutron-
bomb type shutdown that only zeroises senstive information while
leaving structures intact */
setrlimit( RLIMIT_CORE, &rl );
for( fd = getdtablesize() - 1; fd > STDOUT_FILENO; fd-- )
close( fd );
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 = getTime();
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() that breaks its own
prototype */
tv.tv_sec = 10;
tv.tv_usec = 0;
#if defined( __hpux ) && ( OSVERSION == 9 || OSVERSION == 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 ) )
usefulness += getEntropySourceData( &dataSources[ i ],
gathererBuffer + bufPos,
gathererBufSize - bufPos,
&bufPos );
/* Check if there's 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( getTime() > 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( const BOOLEAN force )
{
enterMutex( MUTEX_RANDOMPOLLING );
if( gathererProcess )
{
RESOURCE_DATA msgData;
GATHERER_INFO *gathererInfo = ( GATHERER_INFO * ) gathererBuffer;
int quality, status;
/* If this is a forced shutdown, be fairly assertive with the
gathering process */
if( force )
{
/* Politely ask the the gatherer to shut down and (try and)
yield our timeslice a few times so that the shutdown can
take effect. This is unfortunately somewhat implementation-
dependant in that in some cases it'll only yield the current
thread's timeslice, or if it's a high-priority thread it'll
be scheduled again before any lower-priority threads get to
run */
kill( gathererProcess, SIGTERM );
sched_yield();
sched_yield();
sched_yield(); /* Well, sync is done three times too... */
/* If the gatherer is still running, ask again, less
politely this time */
#if 1
if( kill( gathererProcess, 0 ) != -1 || errno != ESRCH )
#else
if( getpgid( pid ) > 0 || errno == EPERM )
#endif /* 1 */
kill( gathererProcess, SIGKILL );
}
/* Wait for the gathering process to finish, add the randomness it's
gathered, and detach and delete the shared memory (the latter is
necessary because otherwise the unused ID hangs around until the
process terminates). We don't check for errors at this point
(except in the debug version) since this is an invisible internal
routine for which we can't easily recover from problems. Any
problems are caught at a higher level by the randomness-quality
checking */
waitpid( gathererProcess, &status, 0 );
if( gathererInfo->noBytes > 0 && !force )
{
quality = min( gathererInfo->usefulness * 5, 100 ); /* 0-20 -> 0-100 */
setMessageData( &msgData, gathererBuffer, gathererInfo->noBytes );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
IMESSAGE_SETATTRIBUTE_S,
&msgData, CRYPT_IATTRIBUTE_ENTROPY );
assert( cryptStatusOK( status ) );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
IMESSAGE_SETATTRIBUTE,
&quality, CRYPT_IATTRIBUTE_ENTROPY_QUALITY );
assert( cryptStatusOK( status ) );
}
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. In addition some other
environments don't support the call, so we exclude those as well. FreeBSD
is a particular pain because of its highly confusing use of -RELEASE,
-STABLE, and -CURRENT while maintaining the same version, it's present in
5.x-CURRENT but not 5.x-RELEASE or -STABLE, so we have to exclude it for
all 5.x to be safe */
#if defined( USE_THREADS ) && ( defined( _AIX ) || defined( __MVS__ ) || \
defined( _MPRAS ) || defined( __APPLE__ ) || \
( defined( __FreeBSD__ ) && OSVERSION <= 5 ) )
#undef USE_THREADS
#endif /* USE_THREADS && OSes without pthread_atfork() */
#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 + -