📄 unix.c
字号:
close( procFD );
}
}
zeroise( buffer, 1024 );
if( procCount < 5 )
return( 0 );
/* Produce an estimate of the data's value. We require that we get at
least 5 entries and give them a maximum value of 50 to ensure that
some data is still coming from other sources */
quality = min( procCount * 3, 50 );
krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_SETATTRIBUTE,
( void * ) &quality, CRYPT_IATTRIBUTE_ENTROPY_QUALITY );
return( quality );
}
/* Get data from an entropy source */
static int getEntropySourceData( struct RI *dataSource, BYTE *bufPtr,
const int bufSize, int *bufPos )
{
int bufReadPos = 0, bufWritePos = 0;
size_t noBytes;
/* Try and get more data from the source. If we get zero bytes, the
source has sent us all it has */
if( ( noBytes = fread( bufPtr, 1, bufSize, dataSource->pipe ) ) <= 0 )
{
struct rusage rusage;
int total = 0;
/* If there's a problem, exit */
if( my_pclose( dataSource, &rusage ) != 0 )
return( 0 );
/* Try and estimate how much entropy we're getting from a data
source */
if( dataSource->usefulness != 0 )
{
if( dataSource->usefulness < 0 )
/* Absolute rating, 1024 / -n */
total = 1025 / -dataSource->usefulness;
else
/* Relative rating, 1024 * n */
total = dataSource->length / dataSource->usefulness;
}
#ifdef DEBUG_RANDOM
printf( "rndunix: %s %s contributed %d bytes (compressed), "
"usefulness = %d.\n", dataSource->path,
( dataSource->arg != NULL ) ? dataSource->arg : "",
dataSource->length, total );
#endif /* DEBUG_RANDOM */
/* Copy in the last bit of entropy data, the resource usage of the
popen()ed child */
if( sizeof( struct rusage ) < bufSize )
{
memcpy( bufPtr, &rusage, sizeof( struct rusage ) );
*bufPos += sizeof( struct rusage );
}
return( total );
}
/* Run-length compress the input byte sequence */
while( bufReadPos < noBytes )
{
const int ch = bufPtr[ bufReadPos ];
/* If it's a single byte or we're at the end of the buffer, just
copy it over */
if( bufReadPos >= bufSize - 1 || ch != bufPtr[ bufReadPos + 1 ] )
{
bufPtr[ bufWritePos++ ] = ch;
bufReadPos++;
}
else
{
int count = 0;
/* It's a run of repeated bytes, replace them with the byte
count mod 256 */
while( bufReadPos < noBytes && ( ch == bufPtr[ bufReadPos ] ) )
{
count++;
bufReadPos++;
}
bufPtr[ bufWritePos++ ] = count;
}
}
/* Remember the number of (compressed) bytes of input that we obtained */
*bufPos += bufWritePos;
dataSource->length += noBytes;
return( 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 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, which would have to be repeated for any new version */
#define SHARED_BUFSIZE 49152 /* Usually about 25K are filled */
#define SLOWPOLL_TIMEOUT 30 /* Time out after 30 seconds */
void slowPoll( void )
{
GATHERER_INFO *gathererInfo;
BOOLEAN moreSources;
struct timeval tv;
struct rlimit rl = { 0, 0 };
struct sigaction act;
fd_set fds;
time_t startTime;
#if defined( _CRAY ) || defined( __hpux ) || defined( _M_XENIX ) || \
defined( __aux )
#if defined( _SC_PAGESIZE )
const int pageSize = sysconf( _SC_PAGESIZE );
#elif defined( _SC_PAGE_SIZE )
const int pageSize = sysconf( _SC_PAGE_SIZE );
#else
const int pageSize = 4096;
#endif /* Systems without getpagesize() */
#else
const int pageSize = getpagesize();
#endif /* Unix variant-specific brokenness */
#if defined( __hpux )
size_t maxFD = 0;
#else
int maxFD = 0;
#endif /* OS-specific brokenness */
int extraEntropy = 0, usefulness = 0;
int fd, bufPos, i, value;
/* Make sure we don't start more than one slow poll at a time */
lockPollingMutex();
if( gathererProcess )
{
unlockPollingMutex();
return;
}
/* Some systems provide further info that we can grab before we start
the slow poll. If this is sufficient to satisfy the polling
requirements, we don't have to try the full 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( "rndunix: 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 */
#if !( defined( __QNX__ ) && OSVERSION <= 4 )
/* 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 = %s, line = %d.\n", errno, __FILE__, __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 = %s, line = "
"%d.\n", __FILE__, __LINE__ );
#endif /* Cray */
#ifdef DEBUG_CONFLICTS
fprintf( stderr, "cryptlib: shmget()/shmat() failed, errno = %d, "
"file = %s, line = %d.\n", errno, __FILE__, __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 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;
unlockPollingMutex();
/* Fork off the gatherer, the parent process returns to the caller */
if( ( gathererProcess = fork() ) != 0 )
{
/* If the fork() failed, clean up and reset the gatherer PID to make
sure that we're not locked out of retrying the poll later */
if( gathererProcess == -1 )
{
#ifdef DEBUG_CONFLICTS
fprintf( stderr, "cryptlib: fork() failed, errno = %d, "
"file = %s, line = %d.\n", errno, __FILE__, __LINE__ );
#endif /* DEBUG_CONFLICTS */
lockPollingMutex();
shmctl( gathererMemID, IPC_RMID, NULL );
sigaction( SIGCHLD, &gathererOldHandler, NULL );
gathererProcess = 0;
unlockPollingMutex();
}
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 while cryptlib objects are reference-counted the
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++ )
{
/* Check for the end-of-lightweight-sources marker */
if( dataSources[ i ].path[ 0 ] == '\0' )
{
/* If we're only polling lightweight sources because we've
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -