📄 ssl_svr.c
字号:
/****************************************************************************
* *
* cryptlib SSL v3/TLS Server Management *
* Copyright Peter Gutmann 1998-2003 *
* *
****************************************************************************/
#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL )
#include "crypt.h"
#include "session.h"
#include "ssl.h"
#elif defined( INC_CHILD )
#include "../crypt.h"
#include "../session/session.h"
#include "../session/ssl.h"
#else
#include "crypt.h"
#include "session/session.h"
#include "session/ssl.h"
#endif /* Compiler-specific includes */
#ifdef USE_SSL
/****************************************************************************
* *
* Utility Functions *
* *
****************************************************************************/
/* Many of the SSL packets have fixed formats, so we can construct them by
copying in a constant template and setting up the variable fields. The
following templates are for various packet types */
#define SERVERHELLODONE_TEMPLATE_SIZE 4
#define SERVERCERTREQUEST_TEMPLATE_SIZE 13
static const FAR_BSS BYTE serverHelloDoneTemplate[] = {
SSL_HAND_SERVER_HELLODONE, /* ID */
0, 0, 0 /* Length */
};
static const FAR_BSS BYTE serverCertRequestTemplate[] = {
SSL_HAND_SERVER_CERTREQUEST, /* ID */
0, 0, 9, /* Length */
2, /* Cert type length */
1, 2, /* RSA, DSA */
0, 4, /* CA name list length */
0, 2, /* CA name length */
0x30, 0x00 /* CA name */
};
/* Choose the best cipher suite from a list of suites */
static int chooseCipherSuite( const BYTE *suitePtr, const int noSuites,
const BOOLEAN isV2 )
{
int suite = SSL_NULL_WITH_NULL, i;
for( i = 0; i < noSuites; i++ )
{
int ch, currentSuite;
/* Get the cipher suite info. If it's a v2 suite (the high byte is
nonzero), skip it and continue */
if( isV2 )
ch = *suitePtr++;
currentSuite = mgetWord( suitePtr );
if( isV2 && ch )
continue;
#if 0 /* When resuming a cached session, the client is required to offer
as one of its suites the original suite that was used. There is
no good reason for this requirement (it's probable that the spec
is intending that there be at least one cipher suite, and that if
there's only one it should really be the one originally
negotiated), and it complicates implementation of shared-secret
key sessions, so we don't perform this check */
/* If we have to match a specific suite and this isn't it,
continue */
if( requiredSuite > 0 && requiredSuite != currentSuite )
continue;
#endif /* 0 */
/* Pick out the best suite available. The order is 3DES, AES, IDEA,
RC4/128, DES */
switch( currentSuite )
{
case SSL_RSA_WITH_3DES_EDE_CBC_SHA:
if( algoAvailable( CRYPT_ALGO_3DES ) )
suite = currentSuite;
break;
case TLS_RSA_WITH_AES_128_CBC_SHA:
case TLS_RSA_WITH_AES_256_CBC_SHA:
if( suite != SSL_RSA_WITH_3DES_EDE_CBC_SHA && \
algoAvailable( CRYPT_ALGO_AES ) )
suite = currentSuite;
break;
case SSL_RSA_WITH_IDEA_CBC_SHA:
if( suite != SSL_RSA_WITH_3DES_EDE_CBC_SHA && \
suite != TLS_RSA_WITH_AES_128_CBC_SHA && \
suite != TLS_RSA_WITH_AES_256_CBC_SHA && \
algoAvailable( CRYPT_ALGO_IDEA ) )
suite = currentSuite;
break;
case SSL_RSA_WITH_RC4_128_MD5:
case SSL_RSA_WITH_RC4_128_SHA:
if( ( suite == SSL_NULL_WITH_NULL || \
suite == SSL_RSA_WITH_DES_CBC_SHA ) && \
algoAvailable( CRYPT_ALGO_RC4 ) )
suite = currentSuite;
break;
case SSL_RSA_WITH_DES_CBC_SHA:
if( suite == SSL_NULL_WITH_NULL && \
algoAvailable( CRYPT_ALGO_DES ) )
suite = currentSuite;
break;
}
}
return( suite );
}
/* Process TLS extensions */
static int processExtensions( SESSION_INFO *sessionInfoPtr,
const BYTE *extListPtr, const int extListLen )
{
int length = extListLen;
/* Process the extensions */
while( length > ID_SIZE + UINT16_SIZE )
{
int type, extLen, value;
/* Get the next extension */
type = *extListPtr++;
extLen = mgetWord( extListPtr );
if( length < ID_SIZE + UINT16_SIZE + extLen || extLen < 1 )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid TLS extension length %d, total length %d",
extLen, length );
length -= ID_SIZE + UINT16_SIZE;
/* Process the extension data. The internal structure of some of
these things shows that they were created by ASN.1 people... */
switch( type )
{
case TLS_EXT_SERVER_NAME:
{
int listLen;
/* Response: Send zero-length reply to client */
listLen = mgetWord( extListPtr );
if( extLen != listLen + UINT16_SIZE )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid host name TLS extension data "
"length %d", listLen );
/* Parsing of further SEQUENCE OF SEQUENCE data omitted */
break;
}
case TLS_EXT_MAX_FRAGMENT_LENTH:
/* Response: If frag-size == 3 or 4, send same to client */
value = *extListPtr++;
if( extLen != 1 || value < 1 || value > 4 )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid fragment length TLS extension data "
"%02X, length %d", value, extLen );
break;
case TLS_EXT_TRUNCATED_HMAC:
break;
/* Default: Ignore the extension */
}
/* Move on to the next extension */
extListPtr += extLen;
length -= extLen;
}
/* Make sure that we consumed all of the data */
if( length != 0 )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Extraneous TLS extension data %d bytes", length );
return( CRYPT_OK );
}
/****************************************************************************
* *
* 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 expiryTime; /* Time entry expires from 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;
int cachePos, uniqueID = 0, i;
assert( sessionID != NULL && 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 );
enterMutex( MUTEX_SESSIONCACHE );
for( i = 0; i < sessionCacheLastEntry; i++ )
{
SESSIONCACHE_INDEX *sessionCacheInfo = &sessionCacheIndex[ i ];
/* If this entry has expired, delete it */
if( sessionCacheInfo->expiryTime < 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->expiryTime <= 0 )
{
if( nextFreeEntry == CRYPT_ERROR )
nextFreeEntry = i;
continue;
}
lastUsedEntry = i;
if( sessionCacheInfo->expiryTime < oldestTime )
{
oldestTime = sessionCacheInfo->expiryTime;
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->expiryTime = \
currentTime + SESSIONCACHE_TIMEOUT;
if( sessionCacheInfo->fixedEntry )
/* Indicate that this entry corresponds to a fixed
entry that was added manually rather than a true
resumed session */
uniqueID = -uniqueID;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -