📄 ssl.c
字号:
static const int fragmentTbl[] = \
{ 0, 512, 1024, 2048, 4096, 8192, 16384 };
/* Response: If frag-size == 3...5, send same to peer.
Note that we also allow a frag-size value of 5, which
isn't specified in the standard but should probably be
present since it would otherwise result in a missing
value between 4096 and the default of 16384:
byte fragmentLength */
value = sgetc( stream );
if( cryptStatusError( value ) || \
extLen != 1 || value < 1 || value > 5 )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid TLS fragment length extension" );
/* sessionInfoPtr->maxPacketSize = fragmentTbl[ value ]; */
break;
}
case TLS_EXT_CLIENT_CERTIFICATE_URL:
/* Response: Ignore. This dangerous extension allows a
client to direct a server to grope around in arbitrary
external (and untrusted) URLs trying to locate certs,
provinding a convenient mechanism for bounce attacks
and all manner of similar firewall/trusted-host
subversion problems:
byte chainType
uint16 urlAndHashList
uint16 urlLen
byte[] url
byte hashPresent
byte[20] hash - If hashPresent flag set */
if( extLen < 1 + UINT16_SIZE + \
UINT16_SIZE + MIN_URL_SIZE + 1 || \
cryptStatusError( sSkip( stream, extLen ) ) )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid TLS client certificate URL extension" );
break;
case TLS_EXT_TRUSTED_CA_KEYS:
/* Response: Ignore. This allows a client to specify which
CA certs it trusts, and by extension which server certs
it trusts. God knows what actual problem this is
intended to solve:
uint16 caList
byte idType
... */
if( extLen < UINT16_SIZE + 1 || \
cryptStatusError( sSkip( stream, extLen ) ) )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid TLS trusted CA extension" );
break;
case TLS_EXT_TRUNCATED_HMAC:
/* Truncate the HMAC to a nonstandard 80 bits (rather than
the de facto IPsec cargo-cult standard of 96 bits) */
if( extLen != 0 )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid TLS truncated HMAC extension" );
break;
case TLS_EXT_STATUS_REQUEST:
/* Response: Ignore - another bounce-attack enabler, this
time on both the server and an OCSP responder:
byte statusType
... */
if( extLen < 1 || \
cryptStatusError( sSkip( stream, extLen ) ) )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid TLS status request extension" );
break;
default:
/* Default: Ignore the extension */
if( cryptStatusError( sSkip( stream, extLen ) ) )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid TLS extension data for extension "
"type %d", type );
}
}
return( CRYPT_OK );
}
/* Process a session ID */
static int processSessionID( SESSION_INFO *sessionInfoPtr,
SSL_HANDSHAKE_INFO *handshakeInfo,
STREAM *stream )
{
BYTE sessionID[ SESSIONID_SIZE + 8 ];
const int sessionIDlength = sgetc( stream );
int status;
/* Get the session ID info and if it's not one of ours, skip it */
if( cryptStatusError( sessionIDlength ) || \
sessionIDlength < 0 || sessionIDlength > MAX_SESSIONID_SIZE )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA, "Invalid session ID" );
if( sessionIDlength != SESSIONID_SIZE )
{
if( sessionIDlength > 0 )
{
status = sSkip( stream, sessionIDlength );
if( cryptStatusError( status ) )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid session ID" );
}
return( CRYPT_OK );
}
/* There's a session ID present, check to make sure that it matches our
expectations. If we're the server the the size is right for it to
(potentially) be one of ours, if we're the client we check to see
whether it matches what we sent */
status = sread( stream, sessionID, SESSIONID_SIZE );
if( cryptStatusError( status ) )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA, "Invalid session ID" );
if( !( sessionInfoPtr->flags & SESSION_ISSERVER ) )
{
const ATTRIBUTE_LIST *userNamePtr;
BYTE formattedSessionID[ SESSIONID_SIZE + 8 ];
/* If the returned session ID matches the one that we sent,
it's a resumed session */
if( ( userNamePtr = \
findSessionAttribute( sessionInfoPtr->attributeList,
CRYPT_SESSINFO_USERNAME ) ) == NULL )
/* There's no user name present, it can't be a resumed
session */
return( CRYPT_OK );
memset( formattedSessionID, 0, SESSIONID_SIZE );
memcpy( formattedSessionID, userNamePtr->value,
min( userNamePtr->valueLength, SESSIONID_SIZE ) );
if( memcmp( formattedSessionID, sessionID, SESSIONID_SIZE ) )
/* The user name doesn't match the returned ID, it's not a
resumed session */
return( CRYPT_OK );
}
/* It's a resumed session, remember the details and let the caller
know */
memcpy( handshakeInfo->sessionID, sessionID, SESSIONID_SIZE );
handshakeInfo->sessionIDlength = SESSIONID_SIZE;
return( OK_SPECIAL );
}
/* Process the client/server hello:
byte ID = SSL_HAND_CLIENT_HELLO / SSL_HAND_SERVER_HELLO
uint24 len
byte[2] version = { 0x03, 0x0n }
uint32 time | Client/server nonce
byte[28] nonce |
byte sessIDlen | May receive nonzero len +
byte[] sessID | <len> bytes data
Client Server
uint16 suiteLen -
uint16[] suites uint16 suite
byte coprLen = 1 -
byte copr = 0 byte copr = 0 */
int processHelloSSL( SESSION_INFO *sessionInfoPtr,
SSL_HANDSHAKE_INFO *handshakeInfo,
STREAM *stream, const BOOLEAN isServer )
{
BOOLEAN resumedSession = FALSE;
int endPos, length, suiteLength = 1, i, status;
/* Check the header and version info */
if( isServer )
length = checkHSPacketHeader( sessionInfoPtr, stream,
SSL_HAND_CLIENT_HELLO,
VERSIONINFO_SIZE + SSL_NONCE_SIZE + \
1 + ( UINT16_SIZE * 2 ) + 1 + 1 );
else
length = checkHSPacketHeader( sessionInfoPtr, stream,
SSL_HAND_SERVER_HELLO,
VERSIONINFO_SIZE + SSL_NONCE_SIZE + \
1 + UINT16_SIZE + 1 );
if( cryptStatusError( length ) )
return( length );
endPos = stell( stream ) + length;
status = processVersionInfo( sessionInfoPtr, stream,
isServer ? \
&handshakeInfo->clientOfferedVersion : \
NULL );
if( cryptStatusError( status ) )
return( status );
/* Process the nonce and session ID */
sread( stream, isServer ? handshakeInfo->clientNonce : \
handshakeInfo->serverNonce, SSL_NONCE_SIZE );
status = processSessionID( sessionInfoPtr, handshakeInfo, stream );
if( status == OK_SPECIAL )
resumedSession = TRUE;
else
if( cryptStatusError( status ) )
return( status );
/* Process the cipher suite information */
if( isServer )
{
/* If we're reading the client hello, the packet contains a
selection of suites preceded by a suite count */
suiteLength = readUint16( stream );
if( cryptStatusError( suiteLength ) || \
suiteLength < UINT16_SIZE || ( suiteLength % UINT16_SIZE ) != 0 )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid cipher suite information" );
suiteLength /= UINT16_SIZE;
}
status = processCipherSuite( sessionInfoPtr, handshakeInfo, stream,
suiteLength );
if( cryptStatusError( status ) )
return( status );
/* Process the compression suite information */
if( isServer )
{
/* If we're reading the client hello, the packet contains a
selection of suites preceded by a suite count */
suiteLength = sgetc( stream );
if( cryptStatusError( suiteLength ) || suiteLength < 1 )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid compression suite information" );
}
for( i = 0; i < suiteLength; i++ )
{
if( sgetc( stream ) != 0 )
status = CRYPT_ERROR_BADDATA;
}
if( cryptStatusError( status ) )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid compression algorithm information" );
/* If there's extra data present at the end of the packet, check for TLS
extension data */
if( endPos - stell( stream ) > 0 )
{
status = processExtensions( sessionInfoPtr, stream,
endPos - stell( stream ) );
if( cryptStatusError( status ) )
return( status );
handshakeInfo->hasExtensions = TRUE;
}
return( resumedSession ? OK_SPECIAL : CRYPT_OK );
}
/****************************************************************************
* *
* Certificate-handling Functions *
* *
****************************************************************************/
/* Read/write an SSL cert chain:
byte ID = SSL_HAND_CERTIFICATE
uint24 len
uint24 certListLen
uint24 certLen | 1...n certs ordered
byte[] cert | leaf -> root */
int readSSLCertChain( SESSION_INFO *sessionInfoPtr,
SSL_HANDSHAKE_INFO *handshakeInfo, STREAM *stream,
CRYPT_CERTIFICATE *iCertChain,
const BOOLEAN isServer )
{
CRYPT_ALGO_TYPE algorithm;
const ATTRIBUTE_LIST *fingerprintPtr = \
findSessionAttribute( sessionInfoPtr->attributeList,
CRYPT_SESSINFO_SERVER_FINGERPRINT );
RESOURCE_DATA msgData;
BYTE certFingerprint[ CRYPT_MAX_HASHSIZE ];
const char *peerTypeName = isServer ? "Client" : "Server";
int chainLength, length, status;
/* Clear return value */
*iCertChain = CRYPT_ERROR;
/* Make sure that the packet header is in order */
length = checkHSPacketHeader( sessionInfoPtr, stream,
SSL_HAND_CERTIFICATE, isServer ? \
0 : LENGTH_SIZE + MIN_CERTSIZE );
if( cryptStatusError( length ) )
return( length );
if( isServer && length < LENGTH_SIZE + MIN_CERTSIZE )
{
/* There is a special case in which a too-short cert packet is valid
and that's where it constitutes the TLS equivalent of an SSL
no-certs alert. SSLv3 sent an SSL_ALERT_NO_CERTIFICATE alert to
indicate that the client doesn't have a cert, which is handled by
the readPacketSSL() call. TLS changed this to send an empty cert
packet instead, supposedly because it lead to implementation
problems (presumably it's necessary to create a state machine-
based implementation to reproduce these problems, whatever they
are). The TLS 1.0 spec is ambiguous as to what constitutes an
empty packet, it could be either a packet with a length of zero
or a packet containing a zero-length cert list so we check for
both. TLS 1.1 fixed this to say that that certListLen entry has
a length of zero. To report this condition we fake the error
indicators for consistency with the status obtained from an SSLv3
no-cert alert */
if( length == 0 || length == LENGTH_SIZE )
{
sessionInfoPtr->errorCode = SSL_ALERT_NO_CERTIFICATE;
retExt( sessionInfoPtr, CRYPT_ERROR_PERMISSION,
"Received TLS alert message: No certificate" );
}
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid certificate chain" );
}
chainLength = readUint24( stream );
if( cryptStatusError( chainLength ) || \
chainLength < MIN_CERTSIZE || chainLength != length - LENGTH_SIZE )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid cert chain length %d, should be %d",
chainLength, length - LENGTH_SIZE );
/* Import the cert chain and get information on it. This isn't a true
cert chain (in the sense of being degenerate PKCS #7 SignedData) but
a special-case SSL-encoded cert chain */
status = importCertFromStream( stream, iCertChain, chainLength,
CRYPT_ICERTTYPE_SSL_CERTCHAIN );
if( cryptStatusError( status ) )
{
/* There are sufficient numbers of broken certs around that if we
run into a problem importing one we provide a custom error
message telling the user to try again with a reduced compliance
level */
if( status == CRYPT_ERROR_BADDATA || status == CRYPT_ERROR_INVALID )
retExt( sessionInfoPtr, status,
"%s provided a broken/invalid certificate, try again "
"with a reduced level of certificate compliance "
"checking", peerTypeName );
retExt( sessionInfoPtr, status, "Invalid certificate chain" );
}
status = krnlSendMessage( *iCertChain, IMESSAGE_GETATTRIBUTE,
&algorithm, CRYPT_CTXINFO_ALGO );
if( cryptStatusOK( status ) )
{
setMessageData( &msgData, certFingerprint, CRYPT_MAX_HASHSIZE );
status = krnlSendMessage( *iCertChain, IMESSAGE_GETATTRIBUTE_S,
&msgData,
( fingerprintPtr != NULL && \
fingerprintPtr->valueLength == 16 ) ? \
CRYPT_CERTINFO_FINGERPRINT_MD5 : \
CRYPT_CERTINFO_FINGERPRINT_SHA );
}
if( cryptStatusError( status ) )
{
krnlSendNotifier( *iCertChain, IMESSAGE_DECREFCOUNT );
return( status );
}
if( !isServer && algorithm != handshakeInfo->authAlgo )
{
krnlSendNotifier( *iCertChain, IMESSAGE_DECREFCOUNT );
retExt( sessionInfoPtr, CRYPT_ERROR_WRONGKEY,
"%s key algorithm %d doesn't match negotiated algorithm %d",
peerTypeName, algorithm, handshakeInfo->authAlgo );
}
/* Either compare the cert fingerprint to a supplied one or save it for
the caller to examine */
if( fingerprintPtr != NULL )
{
/* The caller has supplied a cert fingerprint, compare it to the
received cert's fingerprint to make sure that we're talking to
the right system */
if( fingerprintPtr->valueLength != msgData.length || \
memcmp( fingerprintPtr->value, certFingerprint, msgData.length ) )
{
krnlSendNotifier( *iCertChain, IMESSAGE_DECREFCOUNT );
retExt( sessionInfoPtr, CRYPT_ERROR_WRONGKEY,
"%s key didn't match key fingerprint", peerTypeName );
}
}
else
/* Remember the cert fingerprint in case the caller wants to check
it. We don't worry if the add fails, it's a minor thing and not
worth aborting the handshake for */
addSessionAttribute( &sessionInfoPtr->attributeList,
CRYPT_SESSINFO_SERVER_FINGERPRINT,
certFingerprint, msgData.length );
/* Make sure that we can perform the required operation using the key
that we've been given. For a client key we need signing capability,
for a server key using DH key agreement we also need signing
capability to authenticate the DH parameters, and for a server key
using RSA key transport we need encryption capability. This
operation also performs a variety of additional checks alongside the
obvious one, so it's a good general health check before we go any
further */
status = krnlSendMessage( *iCertChain, IMESSAGE_CHECK, NULL,
isServer || isKeyxAlgo( algorithm ) ? \
MESSAGE_CHECK_PKC_SIGCHECK : \
MESSAGE_CHECK_PKC_ENCRYPT );
if( cryptStatusError( status ) )
{
krnlSendNotifier( *iCertChain, IMESSAGE_DECREFCOUNT );
retExt( sessionInfoPtr, CRYPT_ERROR_WRONGKEY,
"%s provided a key incapable of being used for %s",
peerTypeName,
isServer ? "client authentication" : \
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -