📄 cryptlib.c
字号:
{
if( initLevel >= 2 )
{
endTrustInfo();
destroyObjects();
}
if( initLevel >= 1 )
endInternalFunctions();
endInitialisation( FALSE );
return( status );
}
/* Unlock the initialisation state */
endInitialisation( TRUE );
return( CRYPT_OK );
}
C_RET cryptInit( void )
{
return( initCrypt() );
}
C_RET cryptEnd( void )
{
int status;
/* If we've already been shut down, don't do anything */
if( !beginInitialisation( FALSE ) )
return( CRYPT_OK );
/* Signal the networking layer that we're about to shut down. This is
necessary before we can close down any objects which are blocked
waiting on network I/O */
signalShutdownNetworking();
/* Clean up all resources. The order in which the cleanup is performed
is based on dependencies of one layer of services upon lower layers:
trustInfo -> cert/context objects
random -> device objects
device objects -> drivers
and everything relies on the kernel itself which is shut down by the
endInternalFunctions() call */
endTrustInfo();
status = destroyObjects();
unbindDrivers();
endInternalFunctions();
/* Unlock the initialisation state */
endInitialisation( FALSE );
/* If the Win32 version is being compiled as a static .LIB, we need to
perform the cleanup here. Note that in this form the library is no
longer thread-safe */
#if defined( __WIN32__ ) && defined( STATIC_LIB )
/* Delete the library initialisation lock */
deleteGlobalResourceLock( initialisation );
#endif /* __WIN32__ && STATIC_LIB */
return( status );
}
/****************************************************************************
* *
* Client/Server Interface Routines *
* *
****************************************************************************/
/* If we're running in our own address space (either in another VM or on
separate hardware), we need to have some sort of client/server mechanism
to communicate with processes running in the applications address space.
The following section implements the server-side interface for various
environments */
#ifdef USE_CLIENT_SERVER
/* Prototypes for functions in cryptsvr.c. Currently this is all done
locally (cryptsvr_client calls cryptsvr_server directly), someone else
can fight with these daemons... */
#if defined( __UNIX__ )
#include <sys/un.h>
#define DAEMON_NAME "cryptd"
#define DAEMON_SOCKET_NAME "/dev/crypt"
#define DAEMON_NO_THREADS 10
/* Per-thread main function */
static MUTEX acceptMutex; /* Mutex for accept() */
static int sockfd; /* Socket for accept() */
static BOOLEAN doShutdown = FALSE; /* Signal for threads to shut down */
static int activeThreads = 0; /* No.of currently active threads */
THREADFUNC_DEFINE( threadedMain, dummy )
{
while( TRUE )
{
int connfd;
/* Some implementations don't handle threads blocking in accept() too
well, and in any case managing the thundering herd in user space
is a lot more efficient than doing it in the kernel, so we
explicitly manage locking ourselves with a mutex.
If we've been told to shut down, we don't try the accept() but
just drop through to the shutdown check afterwards. This
decrements the activeThreads counter, the last thread out turns
off the lights. The way the shutdown works is that the accept()
fails (due to the socket being closed) and the thread falls out of
the accept lock/unlock, at which point either it passes into the
shutdown lock/unlock and exits or (rarely) it gets preempted and
the next thread passes through the accept lock/unlock. In the
most extreme case the accept mutex pileup moves down to the exit
mutex, but in either case all threads eventually terminate. The
only time the daemon might shut down improperly is if a thread is
in the middle of a long-running keygen and keeps everything else
active. There isn't really any clean way to handle this, and in
any case if the system is about to shut down there probably won't
be anything left running to pick up the pieces */
MUTEX_LOCK( &acceptMutex );
if( !doShutdown )
connfd = accept( sockfd, NULL, 0 );
MUTEX_UNLOCK( &acceptMutex );
if( doShutdown )
{
MUTEX_LOCK( &acceptMutex );
activeThreads--;
if( !activeThreads )
cryptEnd();
MUTEX_UNLOCK( &acceptMutex );
THREAD_EXIT();
}
if( connfd == -1 )
{
/* If we got zapped by a signal, continue where we left off */
if( errno == EINTR )
continue;
/* If we got caught by a RST for an established connection before
accept() got called, the connection will be aborted, in which
case we just continue */
if( errno == ECONNABORTED )
continue;
/* ... */
}
/* Get the request type and make sure it's valid */
/* ... */
/* Dispatch the request */
status = dispatchRequest( request.UserDefined, request.RequestID );
/* Clean up */
close( connfd );
}
}
/* Set up the daemon and fire up the thread pool */
void sigTermFunction( int dummy )
{
/* Signal all active threads to die and close the socket, which forces
accept() to fail, guaranteeing that a thread doesn't remain blocked
in the call */
doShutdown = TRUE;
close( socket );
}
int main( int argc, char *argv[] )
{
THREAD threadPool[ DAEMON_NO_THREADS ];
const struct rlimit rl = { 0, 0 };
struct sockaddr_un sockAddr;
struct timeval tv;
char *socketName, *errorString = NULL;
int fd, status;
/* Start logging our status */
openlog( DAEMON_NAME, 0, LOG_DAEMON );
syslog( LOG_INFO, DAEMON_NAME "started" );
/* Check that everything is OK */
if( argc > 2 )
errorString = "usage: " DAEMON_NAME " <server socket pathname>";
else
{
socketName = ( argc == 2 ) ? argv[ 1 ] : DAEMON_SOCKET_NAME;
if( strlen( socketName > 100 )
errorString = DAEMON_NAME ": Socket pathname too long";
else
if( access( socketName, F_OK )
errorString = DAEMON_NAME ": Socket already exists";
}
if( errorString != NULL )
{
syslog( LOG_ERR, errorString );
closelog();
exit( EXIT_FAILURE );
}
/* Turn ourselves into a daemon by forking a new process and killing its
parent. After this sequence of operations, we're a daemon owned by
init */
if( ( status = fork() ) < 0 )
{
syslog( LOG_ERR, "%m" );
closelog();
exit( EXIT_FAILURE );
}
if( status )
exit( EXIT_SUCCESS ); /* Exit if we're the parent */
#if 1
/* Create a new session with ourselves as the session leader and no
controlling TTY, ignore SIGHUP, and fork again. This is necessary
because when a session leader without a controlling terminal opens a
terminal device, it gets assigned as its controlling TTY. By forking
a second time, we make sure the child is no longer a session leader.
The reason we need to ignore SIGHUP is because when the first-level
child (the session leader) exits, the second-level child (just another
process in the session) will be SIGHUP'd */
setsid();
signal( SIGHUP, SIG_IGN );
if( ( status = fork() ) != 0 )
exit( EXIT_SUCCESS );
#else
/* Detach ourselves from the controlling TTY to avoid interruptions and
move into our own process group to avoid mass murders */
fd = open( "/dev/tty", O_RDWR );
ioctl( fd, TIOCNOTTY, 0 );
close( fd );
setpgrp( 0, getpid() );
#endif /* 1 */
/* Close all inherited file descriptors */
for( fd = getdtablesize() - 1; fd >= 0; fd-- )
close( fd );
/* Move to a (safe) standard directory, set our umask to make sure our
files are kept private (although the cryptlib streams module does this
anyway), and point the stdin, stdout, and stderr streams to the null
device in case library routines try and do any I/O */
chdir( "/tmp" );
umask( 0177 ); /* Owner RW access only */
fd = open( "/dev/null", O_RDWR ); /* stdin = 0 */
dup( fd ); /* stdout = 1 */
dup( fd ); /* stderr = 2 */
/* Make sure we can never dump core (we really, *really* don't want to do
this) */
setrlimit( RLIMIT_CORE, &rl );
/* Go catatonic */
signal( SIG_IGN, SIGHUP );
/* Create a domain socket and wait for connections */
memset( sockAddr, 0, sizeof( struct sockaddr_un ) );
strcpy( sockAddr.sun_path, socketName );
status = sockfd = socket( AF_LOCAL, SOCK_STREAM, 0 );
if( status != -1 )
status = bind( sockfd, ( SA * ) &sockAddr, SUN_LEN( &sockAddr ) );
if( status != -1 )
status = listen( sockfd, 5 );
if( status == -1 )
{
syslog( LOG_ERR, "%m" );
closelog();
exit( EXIT_FAILURE );
}
/* Set the socket timeout to 5 seconds to make sure we don't block
forever if a client hangs */
tv.tv_sec = 5;
tv.tv_usec = 0;
setsockopt( sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv,
sizeof( struct timeval ) );
setsockopt( sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv,
sizeof( struct timeval ) );
/* Initialise the crypto code */
status = cryptInitEx();
if( cryptStatusError( status ) )
{
syslog( LOG_ERR, "Crypto initialisation failed" );
closelog();
exit( EXIT_FAILURE );
}
/* Make sure that if we get killed by init, we shut down cleanly */
signal( sigTermFunction, SIGTERM );
/* Start up the thread pool. We hold the accept() mutex while we're
doing this to ensure that it's an all-or-nothing start, in other
words that there are no threads accepting commands while there's
still a chance that the init could be aborted */
MUTEX_INIT( &acceptMutex );
MUTEX_LOCK( &acceptMutex );
for( i = 0; i < DAEMON_NO_THREADS; i++ )
{
status = THREAD_CREATE( &threadMain, NULL );
if( THREAD_STATUS( status ) != CRYPT_OK )
break;
activeThreads++;
}
if( cryptStatusError( status ) )
{
/* Signal any threads which got started to terminate immediately */
doShutdown = TRUE;
close( socket );
MUTEX_UNLOCK (&acceptMutex );
syslog( LOG_ERR, "Thread pool initialisation failed" );
closelog();
exit( EXIT_FAILURE );
}
MUTEX_UNLOCK (&acceptMutex );
/* We're ready to talk, make the socket path accessible to others (the
umask will have made it inaccessible, which is fine since we don't
want anyone poking messages at us while we're initialising) */
chmod( socketName, 0666 );
/* Everything is done by the threads, so we just twiddle our thumbs */
while( TRUE )
pause();
/* Clean up */
MUTEX_DESTROY( &acceptMutex );
exit( EXIT_SUCCESS );
}
#elif defined( __WINDOWS__ )
#define SERVICE_NAME "cryptd"
#define SERVICE_DISPLAY_NAME "cryptlib Server"
#define SERVICE_PATH "%SystemRoot%\\System32\\cryptd.exe"
SERVICE_STATUS serviceStatus;
SERVICE_STATUS_HANDLE hServiceStatus;
/* Service control handler */
void WINAPI Handler( DWORD fdwControl )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -