📄 ssl_svr.c
字号:
/****************************************************************************
* *
* cryptlib SSL v3/TLS Server Management *
* Copyright Peter Gutmann 1998-2005 *
* *
****************************************************************************/
#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL )
#include "crypt.h"
#include "misc_rw.h"
#include "session.h"
#include "ssl.h"
#elif defined( INC_CHILD )
#include "../crypt.h"
#include "../misc/misc_rw.h"
#include "session.h"
#include "ssl.h"
#else
#include "crypt.h"
#include "misc/misc_rw.h"
#include "session/session.h"
#include "session/ssl.h"
#endif /* Compiler-specific includes */
#ifdef USE_SSL
/****************************************************************************
* *
* Session Cache *
* *
****************************************************************************/
/* Session cache data and index information */
typedef BYTE SESSIONCACHE_DATA[ SSL_SECRET_SIZE ];
typedef struct {
/* Identification information: The checksum and hash of the session ID */
int checkValue;
BYTE hashValue[ 20 ];
/* Misc info */
time_t timeStamp; /* Time entry was added to the cache */
int uniqueID; /* Unique ID for this entry */
BOOLEAN fixedEntry; /* Whether entry was added manually */
} SESSIONCACHE_INDEX;
/* A template used to initialise session cache entries */
static const SESSIONCACHE_INDEX SESSIONCACHE_INDEX_TEMPLATE = \
{ 0, { 0 }, 0, 0, 0 };
/* The action to perform on the cache */
typedef enum { CACHE_ACTION_NONE, CACHE_ACTION_PRESENCECHECK,
CACHE_ACTION_LOOKUP, CACHE_ACTION_ADD,
CACHE_ACTION_LAST } CACHE_ACTION;
/* Session cache information */
static SESSIONCACHE_INDEX *sessionCacheIndex;
static SESSIONCACHE_DATA *sessionCacheData;
static int sessionCacheLastEntry;
static int sesionCacheUniqueID;
/* Hash data */
static void hashData( BYTE *hash, const void *data, const int dataLength )
{
static HASHFUNCTION hashFunction = NULL;
/* Get the hash algorithm information if necessary */
if( hashFunction == NULL )
getHashParameters( CRYPT_ALGO_SHA, &hashFunction, NULL );
/* Hash the data */
hashFunction( NULL, hash, ( BYTE * ) data, dataLength, HASH_ALL );
}
/* Handle the session cache. This function currently uses a straightforward
linear search with entries clustered towards the start of the cache.
Although this may seem somewhat suboptimal, since cryptlib isn't a high-
performance server the cache will rarely contain more than a handful of
entries (if any). In any case a quick scan through a small number of
integers is probably still faster than the complex in-memory database
lookup schemes used by many servers, and is also required to handle things
like cache LRU management */
static int handleSessionCache( const void *sessionID,
const int sessionIDlength, void *masterKey,
const BOOLEAN isFixedEntry,
const CACHE_ACTION cacheAction )
{
BYTE hashValue[ 20 ];
BOOLEAN dataHashed = FALSE;
const time_t currentTime = getTime();
time_t oldestTime = currentTime;
const int checkValue = checksumData( sessionID, sessionIDlength );
int nextFreeEntry = CRYPT_ERROR, lastUsedEntry = 0, oldestEntry = 0;
int cachePos, uniqueID = 0, i;
assert( isReadPtr( sessionID, sessionIDlength ) && sessionIDlength >= 8 );
assert( ( cacheAction == CACHE_ACTION_PRESENCECHECK && masterKey == NULL ) || \
( cacheAction == CACHE_ACTION_LOOKUP && masterKey != NULL ) || \
( cacheAction == CACHE_ACTION_ADD && masterKey != NULL ) );
assert( isWritePtr( sessionCacheIndex,
SESSIONCACHE_SIZE * sizeof( SESSIONCACHE_INDEX ) ) );
assert( isWritePtr( sessionCacheData,
SESSIONCACHE_SIZE * sizeof( SESSIONCACHE_DATA ) ) );
/* If there's something wrong with the time, we can't perform (time-
based) cache management */
if( currentTime < MIN_TIME_VALUE )
return( 0 );
krnlEnterMutex( MUTEX_SESSIONCACHE );
for( i = 0; i < sessionCacheLastEntry; i++ )
{
SESSIONCACHE_INDEX *sessionCacheInfo = &sessionCacheIndex[ i ];
/* If this entry has expired, delete it */
if( sessionCacheInfo->timeStamp + SESSIONCACHE_TIMEOUT < currentTime )
{
sessionCacheIndex[ i ] = SESSIONCACHE_INDEX_TEMPLATE;
zeroise( sessionCacheData[ i ], sizeof( SESSIONCACHE_DATA ) );
}
/* Check for a free entry and the oldest non-free entry. We could
perform an early-out once we find a free entry, but this would
prevent any following expired entries from being deleted */
if( sessionCacheInfo->timeStamp <= 0 )
{
/* We've found a free entry, remember it for future use if
required and continue */
if( nextFreeEntry == CRYPT_ERROR )
nextFreeEntry = i;
continue;
}
lastUsedEntry = i;
if( sessionCacheInfo->timeStamp < oldestTime )
{
/* We've found an older entry than the current oldest entry,
remember it */
oldestTime = sessionCacheInfo->timeStamp;
oldestEntry = i;
}
/* Perform a quick check using a checksum of the name to weed out
most entries */
if( sessionCacheInfo->checkValue == checkValue )
{
if( !dataHashed )
{
hashData( hashValue, sessionID, sessionIDlength );
dataHashed = TRUE;
}
if( !memcmp( sessionCacheInfo->hashValue, hashValue, 20 ) )
{
uniqueID = sessionCacheInfo->uniqueID;
/* We've found a matching entry in the cache, if we're
looking for an existing entry return its data */
if( cacheAction == CACHE_ACTION_LOOKUP )
{
memcpy( masterKey, sessionCacheData[ i ], SSL_SECRET_SIZE );
sessionCacheInfo->timeStamp = currentTime;
#if 0 /* Old PSK mechanism */
if( sessionCacheInfo->fixedEntry )
/* Indicate that this entry corresponds to a fixed
entry that was added manually rather than a true
resumed session */
uniqueID = -uniqueID;
#endif /* 0 */
}
krnlExitMutex( MUTEX_SESSIONCACHE );
return( uniqueID );
}
}
}
/* If the total number of entries has shrunk due to old entries expiring,
reduce the overall cache size */
if( lastUsedEntry + 1 < sessionCacheLastEntry )
sessionCacheLastEntry = lastUsedEntry + 1;
/* No match found, if we're adding a new entry, add it at the
appropriate location */
if( cacheAction == CACHE_ACTION_ADD )
{
if( !dataHashed )
hashData( hashValue, sessionID, sessionIDlength );
cachePos = ( nextFreeEntry > 0 ) ? nextFreeEntry : \
( sessionCacheLastEntry >= SESSIONCACHE_SIZE ) ? \
oldestEntry : sessionCacheLastEntry++;
assert( cachePos >= 0 && cachePos < SESSIONCACHE_SIZE );
sessionCacheIndex[ cachePos ].checkValue = checkValue;
memcpy( sessionCacheIndex[ cachePos ].hashValue, hashValue, 20 );
sessionCacheIndex[ cachePos ].timeStamp = currentTime;
sessionCacheIndex[ cachePos ].uniqueID = uniqueID = \
sesionCacheUniqueID++;
#if 0 /* Old PSK mechanism */
sessionCacheIndex[ cachePos ].fixedEntry = isFixedEntry;
#endif /* 0 */
memcpy( sessionCacheData[ cachePos ], masterKey, SSL_SECRET_SIZE );
}
krnlExitMutex( MUTEX_SESSIONCACHE );
return( uniqueID );
}
/* Add and delete entries to/from the session cache. These are just wrappers
for the local cache-access function, for use by external code */
int findSessionCacheEntryID( const void *sessionID,
const int sessionIDlength )
{
return( handleSessionCache( sessionID, sessionIDlength, NULL,
FALSE, CACHE_ACTION_PRESENCECHECK ) );
}
static int findSessionCacheEntry( const void *sessionID,
const int sessionIDlength,
void *masterSecret,
int *masterSecretLength )
{
const int resumedSessionID = \
handleSessionCache( sessionID, sessionIDlength, masterSecret,
FALSE, CACHE_ACTION_LOOKUP );
*masterSecretLength = ( resumedSessionID != 0 ) ? SSL_SECRET_SIZE : 0;
return( resumedSessionID );
}
int addSessionCacheEntry( const void *sessionID, const int sessionIDlength,
const void *masterSecret,
const int masterSecretLength,
const BOOLEAN isFixedEntry )
{
assert( isReadPtr( masterSecret, masterSecretLength ) && \
masterSecretLength == SSL_SECRET_SIZE );
/* If we're not doing resumes (or the ID is suspiciously short), don't
try and update the session cache */
if( sessionIDlength < 8 )
return( 0 );
/* Add the entry to the cache */
return( handleSessionCache( sessionID, sessionIDlength,
( void * ) masterSecret, isFixedEntry,
CACHE_ACTION_ADD ) );
}
void deleteSessionCacheEntry( const int uniqueID )
{
int i;
krnlEnterMutex( MUTEX_SESSIONCACHE );
/* Search the cache for the entry with the given ID */
for( i = 0; i < sessionCacheLastEntry; i++ )
{
SESSIONCACHE_INDEX *sessionCacheInfo = &sessionCacheIndex[ i ];
/* If we've found the entry we're after, clear it and exit */
if( sessionCacheInfo->uniqueID == uniqueID )
{
sessionCacheIndex[ i ] = SESSIONCACHE_INDEX_TEMPLATE;
zeroise( sessionCacheData[ i ], sizeof( SESSIONCACHE_DATA ) );
break;
}
}
krnlExitMutex( MUTEX_SESSIONCACHE );
}
/* Initialise and shut down the session cache */
int initSessionCache( void )
{
int i, status;
krnlEnterMutex( MUTEX_SESSIONCACHE );
/* Initialise the session cache */
if( ( sessionCacheIndex = clAlloc( "initSessionCache", \
SESSIONCACHE_SIZE * sizeof( SESSIONCACHE_INDEX ) ) ) == NULL )
return( CRYPT_ERROR_MEMORY );
status = krnlMemalloc( ( void ** ) &sessionCacheData,
SESSIONCACHE_SIZE * sizeof( SESSIONCACHE_DATA ) );
if( cryptStatusError( status ) )
{
clFree( "initSessionCache", sessionCacheIndex );
return( status );
}
for( i = 0; i < SESSIONCACHE_SIZE; i++ )
sessionCacheIndex[ i ] = SESSIONCACHE_INDEX_TEMPLATE;
memset( sessionCacheData, 0, SESSIONCACHE_SIZE * \
sizeof( SESSIONCACHE_DATA ) );
sessionCacheLastEntry = 0;
sesionCacheUniqueID = 1;
krnlExitMutex( MUTEX_SESSIONCACHE );
return( CRYPT_OK );
}
void endSessionCache( void )
{
int i;
krnlEnterMutex( MUTEX_SESSIONCACHE );
/* Clear and free the session cache */
krnlMemfree( ( void ** ) &sessionCacheData );
for( i = 0; i < SESSIONCACHE_SIZE; i++ )
sessionCacheIndex[ i ] = SESSIONCACHE_INDEX_TEMPLATE;
clFree( "endSessionCache", sessionCacheIndex );
krnlExitMutex( MUTEX_SESSIONCACHE );
}
/****************************************************************************
* *
* Legacy SSLv2 Functions *
* *
****************************************************************************/
/* Process an SSLv2 client hello:
uint16 suiteLen
uint16 sessIDlen
uint16 nonceLen
uint24[] suites
byte[] sessID
byte[] nonce
The v2 type and version have already been processed in readPacketSSL()
since this information, which is moved into the header in v3, is part of
the body in v2. What's left for the v2 hello is the remainder of the
payload */
static int processHelloSSLv2( SESSION_INFO *sessionInfoPtr,
SSL_HANDSHAKE_INFO *handshakeInfo,
STREAM *stream, int *resumedSessionID )
{
int suiteLength, sessionIDlength, nonceLength, status;
/* Clear return values */
*resumedSessionID = 0;
/* Read the SSLv2 hello */
suiteLength = readUint16( stream );
sessionIDlength = readUint16( stream );
nonceLength = readUint16( stream );
if( suiteLength < 3 || ( suiteLength % 3 ) != 0 || \
sessionIDlength < 0 || sessionIDlength > MAX_SESSIONID_SIZE || \
nonceLength < 16 || nonceLength > SSL_NONCE_SIZE )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid legacy SSLv2 hello packet" );
status = processCipherSuite( sessionInfoPtr, handshakeInfo, stream,
suiteLength / 3 );
if( cryptStatusError( status ) )
return( status );
if( sessionIDlength > 0 )
sSkip( stream, sessionIDlength );
return( sread( stream, handshakeInfo->clientNonce + \
SSL_NONCE_SIZE - nonceLength, nonceLength ) );
}
/****************************************************************************
* *
* Server-side Connect Functions *
* *
****************************************************************************/
/* Perform the initial part of the handshake with the client */
int beginServerHandshake( SESSION_INFO *sessionInfoPtr,
SSL_HANDSHAKE_INFO *handshakeInfo )
{
STREAM *stream = &handshakeInfo->stream;
RESOURCE_DATA msgData;
int length, resumedSessionID = 0, packetOffset, status;
/* Read the hello packet from the client */
length = readPacketSSL( sessionInfoPtr, handshakeInfo,
SSL_MSG_FIRST_HANDSHAKE );
if( cryptStatusError( length ) )
return( length );
/* Process the client hello. Although this should be a v3 hello,
Netscape always sends a v2 hello (even if SSLv2 is disabled) and
in any case both MSIE and Mozilla still have SSLv2 enabled by
default (!!) so we have to process both types */
sMemConnect( stream, sessionInfoPtr->receiveBuffer, length );
if( handshakeInfo->isSSLv2 )
status = processHelloSSLv2( sessionInfoPtr, handshakeInfo,
stream, &resumedSessionID );
else
status = processHelloSSL( sessionInfoPtr, handshakeInfo, stream,
TRUE );
sMemDisconnect( stream );
if( cryptStatusError( status ) && status != OK_SPECIAL )
return( status );
/* Handle session resumption */
if( status == OK_SPECIAL && \
( resumedSessionID = \
findSessionCacheEntry( handshakeInfo->sessionID,
handshakeInfo->sessionIDlength,
handshakeInfo->premasterSecret,
&handshakeInfo->premasterSecretSize ) ) != 0 )
{
#if 0 /* Old PSK mechanism */
/* It's a resumed session, if it's a fixed entry that was added
manually store the session ID as the user name */
if( resumedSessionID < 0 )
{
for( length = handshakeInfo->sessionIDlength; \
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -