⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 rndunix.c

📁 老外写的加密库cryptlib(版本3.1)
💻 C
📖 第 1 页 / 共 4 页
字号:
		   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 + -