📄 unix.c
字号:
/* Fire up each randomness source */
FD_ZERO( &fds );
for( i = 0; dataSources[ i ].path != NULL && \
i < FAILSAFE_ARRAYSIZE( dataSources, DATA_SOURCE_INFO ); i++ )
{
/* Check for the end-of-lightweight-sources marker */
if( dataSources[ i ].path[ 0 ] == '\0' )
{
/* If we're only polling lightweight sources because we've
already obtained entropy from additional sources,we're
done */
if( existingEntropy >= 50 )
{
#ifdef DEBUG_RANDOM
puts( __FILE__ ": All lightweight sources polled, exiting "
"without polling\nheavyweight ones." );
#endif /* DEBUG_RANDOM */
break;
}
/* We're polling all sources, continue with the heavyweight
ones */
continue;
}
/* Since popen() is a fairly heavyweight 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( __FILE__ ": %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 )
continue;
if( fileno( dataSources[ i ].pipe ) >= FD_SETSIZE )
{
/* The fd is larger than what can be fitted into an fd_set, don't
try and use it. This can happen if the calling app opens a
large number of files, since most FD_SET() macros don't
perform any safety checks this can cause segfaults and other
problems if we don't perform the check ourselves */
fclose( dataSources[ i ].pipe );
dataSources[ i ].pipe = NULL;
continue;
}
/* Set up the data source information */
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 */
iterationCount = 0;
while( dataSources[ i ].hasAlternative && \
i < FAILSAFE_ARRAYSIZE( dataSources, DATA_SOURCE_INFO ) && \
iterationCount++ < FAILSAFE_ITERATIONS_MED )
{
#ifdef DEBUG_RANDOM
printf( __FILE__ ": Skipping %s.\n", dataSources[ i + 1 ].path );
#endif /* DEBUG_RANDOM */
i++;
}
ENSURES_EXIT( iterationCount < FAILSAFE_ITERATIONS_MED );
/* i is checked as part of the loop control */
}
ENSURES_EXIT( i < FAILSAFE_ARRAYSIZE( dataSources, DATA_SOURCE_INFO ) );
gathererInfo = ( GATHERER_INFO * ) gathererBuffer;
bufPos = sizeof( GATHERER_INFO ); /* Start of buf.has status info */
/* Suck up all of the data that we can get from each of the sources */
status = setMonoTimer( &timerInfo, SLOWPOLL_TIMEOUT );
ENSURES_EXIT( cryptStatusOK( status ) );
for( moreSources = TRUE, iterationCount = 0;
moreSources && bufPos < gathererBufSize && \
iterationCount < FAILSAFE_ITERATIONS_MAX;
iterationCount++ )
{
/* 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 < FAILSAFE_ARRAYSIZE( dataSources, DATA_SOURCE_INFO );
i++ )
{
if( dataSources[ i ].pipe != NULL && \
FD_ISSET( dataSources[ i ].pipeFD, &fds ) )
usefulness += getEntropySourceData( &dataSources[ i ],
gathererBuffer + bufPos,
gathererBufSize - bufPos,
&bufPos );
}
ENSURES_EXIT( i < FAILSAFE_ARRAYSIZE( dataSources, DATA_SOURCE_INFO ) );
/* 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 < FAILSAFE_ARRAYSIZE( dataSources, DATA_SOURCE_INFO );
i++ )
{
if( dataSources[ i ].pipe != NULL )
{
FD_SET( dataSources[ i ].pipeFD, &fds );
moreSources = TRUE;
}
}
ENSURES_EXIT( i < FAILSAFE_ARRAYSIZE( dataSources, DATA_SOURCE_INFO ) );
/* If we've gone over our time limit, kill anything still hanging
around and exit. This prevents problems with input from blocked
sources */
if( checkMonoTimerExpired( &timerInfo ) )
{
for( i = 0; dataSources[ i ].path != NULL && \
i < FAILSAFE_ARRAYSIZE( dataSources, DATA_SOURCE_INFO );
i++ )
{
if( dataSources[ i ].pipe != NULL )
{
#ifdef DEBUG_RANDOM
printf( __FILE__ ": 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;
}
}
ENSURES_EXIT( i < FAILSAFE_ARRAYSIZE( dataSources, DATA_SOURCE_INFO ) );
moreSources = FALSE;
#ifdef DEBUG_RANDOM
puts( __FILE__ ": Poll timed out, probably due to blocked data "
"source." );
#endif /* DEBUG_RANDOM */
}
}
ENSURES_EXIT( iterationCount < FAILSAFE_ITERATIONS_MAX );
gathererInfo->usefulness = usefulness;
gathererInfo->noBytes = bufPos;
#ifdef DEBUG_RANDOM
printf( __FILE__ ": 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 );
}
/* Unix slow poll. If a few of the randomness sources create a large amount
of output then the slowPoll() stops once the buffer has been filled (but
before all of the randomness sources have been sucked dry) so that the
'usefulness' factor remains below the threshold. For this reason the
gatherer buffer has to be fairly sizeable on moderately loaded systems.
An alternative strategy, suggested by Massimo Squillace, is to use a
chunk of shared memory protected by a semaphore, with the first
sizeof( int ) bytes at the start serving as a high-water mark. The
process forks and waitpid()'s for the child's pid. The child forks all
the entropy-gatherers and exits, allowing the parent to resume execution.
The child's children are inherited by init (double-fork paradigm), when
each one is finished it takes the semaphore, writes data to the shared
memory segment at the given offset, updates the offset, releases the
semaphore again, and exits, to be reaped by init.
The parent monitors the shared memory offset and when enough data is
available takes the semaphore, grabs the data, and releases the shared
memory area and semaphore. If any children are still running they'll get
errors when they try to access the semaphore or shared memory and
silently exit.
This approach has the advantage that all of the forked processes are
managed by init rather than having the parent have to wait for them, but
the disadvantage that the end-of-job handling is rather less rigorous.
An additional disadvantage is that the existing code has had a lot of
real-world testing and adaptation to system-specific quirks, which would
have to be repeated for any new version */
#define SHARED_BUFSIZE 49152 /* Usually about 25K are filled */
void slowPoll( void )
{
struct sigaction act;
const int pageSize = getSysVar( SYSVAR_PAGESIZE );
int extraEntropy = 0;
/* Make sure that we don't start more than one slow poll at a time. The
gathererProcess value may be positive (a PID) or -1 (error), so we
compare it to the specific value 0 (= not-used) in the check */
lockPollingMutex();
if( gathererProcess != 0 )
{
unlockPollingMutex();
return;
}
/* The popen()-level slow poll is the screen-scraping interface of last
resort that we use only if we can't get the entropy in any other
way. If the system provides entropy from alternate sources, we don't
have have to try the screen-scraping slow poll (a number of these
additional sources, things like procfs and kstats, duplicate the
sources polled in the slow poll anyway, so we're not adding much by
polling these extra sources if we've already got the data directly) */
extraEntropy += getDevRandomData();
if( !access( "/proc/interrupts", R_OK ) )
extraEntropy += getProcFSdata();
extraEntropy += getEGDdata();
#ifdef USE_KSTAT
extraEntropy += getKstatData();
#endif /* USE_KSTAT */
#ifdef USE_PROC
extraEntropy += getProcData();
#endif /* USE_PROC */
#ifdef DEBUG_RANDOM
printf( __FILE__ ": Got %d additional entropy from direct sources.\n",
extraEntropy );
if( extraEntropy >= 100 )
puts( " (Skipping full slowpoll since sufficient entropy is "
"available)." );
#endif /* DEBUG_RANDOM */
if( extraEntropy >= 100 )
{
/* We got enough entropy from the additional sources, we don't
have to go through with the full (heavyweight) poll */
unlockPollingMutex();
return;
}
/* QNX 4.x doesn't have SYSV shared memory, so we can't go beyond this
point, all that we can do is warn the user that they'll have to use
the entropy mechanisms for embedded systems (without proper entropy
sources) */
#if defined( __QNX__ ) && OSVERSION <= 4
fprintf( stderr, "cryptlib: QNX 4.x doesn't contain the OS mechanisms "
"required to provide\n system entropy sources that "
"can be used for key generation. In\n order to use "
"cryptlib in this environment, you need to apply the\n"
" randomness mechanisms for embedded systems "
"described in the\n cryptlib manual.\n" );
abort();
#else
/* Reset the SIGCHLD handler to the system default. This is necessary
because if the program that cryptlib is a part of installs its own
SIGCHLD handler, it will end up reaping the cryptlib children before
cryptlib can. As a result, my_pclose() will call waitpid() on a
process that has already been reaped by the installed handler and
return an error, so the read data won't be added to the randomness
pool */
memset( &act, 0, sizeof( act ) );
act.sa_handler = SIG_DFL;
sigemptyset( &act.sa_mask );
if( sigaction( SIGCHLD, &act, &gathererOldHandler ) < 0 )
{
/* This assumes that stderr is open, i.e. that we're not a daemon
(this should be the case at least during the development/debugging
stage) */
fprintf( stderr, "cryptlib: sigaction() failed, errno = %d, "
"file " __FILE__ ", line %d.\n", errno, __LINE__ );
abort();
}
/* Check for handler override */
if( gathererOldHandler.sa_handler != SIG_DFL && \
gathererOldHandler.sa_handler != SIG_IGN )
{
#ifdef DEBUG_CONFLICTS
/* We overwrote the caller's handler, warn them about this */
fprintf( stderr, "cryptlib: Conflicting SIGCHLD handling detected "
"in randomness polling code,\nfile " __FILE__ ", line %d. "
"See the source code for more\ninformation.\n", __LINE__ );
#endif /* DEBUG_CONFLICTS */
}
/* 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 )
{
/* There was a problem obtaining the shared memory, warn the user
and exit */
#ifdef _CRAY
if( errno == ENOSYS )
/* Unicos supports shmget/shmat, but older Crays don't implement
it and return ENOSYS */
fprintf( stderr, "cryptlib: SYSV shared memory required for "
"random number gathering isn't\n supported on this "
"type of Cray hardware (ENOSYS),\n file " __FILE__
", line %d.\n", __LINE__ );
#endif /* Cray */
#ifdef DEBUG_CONFLICTS
fprintf( stderr, "cryptlib: shmget()/shmat() failed, errno = %d, "
"file " __FILE__ ", line %d.\n", errno, __LINE__ );
#endif /* DEBUG_CONFLICTS */
if( gathererMemID != -1 )
shmctl( gathererMemID, IPC_RMID, NULL );
if( gathererOldHandler.sa_handler != SIG_DFL )
sigaction( SIGCHLD, &gathererOldHandler, NULL );
unlockPollingMutex();
return; /* Something broke */
}
/* At this point we have a possible race condition since we need to set
the gatherer PID
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -