📄 ssl_rw.c
字号:
/****************************************************************************
* *
* cryptlib SSL v3/TLS Session Read/Write Routines *
* Copyright Peter Gutmann 1998-2008 *
* *
****************************************************************************/
#if defined( INC_ALL )
#include "crypt.h"
#include "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
/****************************************************************************
* *
* Legacy SSLv2 Functions *
* *
****************************************************************************/
#if 0 /* 28/01/08 Disabled since it's now finally removed in MSIE and
Firefox */
/* Handle a legacy SSLv2 client hello:
uint16 length code = { 0x80, len }
byte type = SSL_HAND_CLIENT_HELLO
byte[2] vers = { 0x03, 0x0n } */
static int handleSSLv2Header( SESSION_INFO *sessionInfoPtr,
SSL_HANDSHAKE_INFO *handshakeInfo,
const BYTE *bufPtr )
{
STREAM stream;
int length, value, status;
assert( bufPtr[ 0 ] == SSL_MSG_V2HANDSHAKE );
/* Make sure that the length is in order. Beyond the header we need at
least the three 16-bit field lengths, one 24-bit cipher suite, and at
least 16 bytes of nonce */
bufPtr++; /* Skip SSLv2 length ID, already checked by caller */
length = *bufPtr++;
if( length < ID_SIZE + VERSIONINFO_SIZE + \
( UINT16_SIZE * 3 ) + 3 + 16 || \
length > sessionInfoPtr->receiveBufSize )
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Invalid legacy SSLv2 hello packet length %d", length ) );
}
/* Due to the different ordering of header fields in SSLv2, the type and
version is regarded as part of the payload that needs to be
hashed, rather than the header as for SSLv3 */
sMemConnect( &stream, bufPtr, ID_SIZE + VERSIONINFO_SIZE );
status = dualMacDataRead( handshakeInfo, &stream );
if( cryptStatusError( status ) )
retIntError();
value = sgetc( &stream );
if( value != SSL_HAND_CLIENT_HELLO )
{
sMemDisconnect( &stream );
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Unexpected legacy SSLv2 packet type %d, should be %d",
value, SSL_HAND_CLIENT_HELLO ) );
}
status = processVersionInfo( sessionInfoPtr, &stream,
&handshakeInfo->clientOfferedVersion );
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
length -= stell( &stream );
sMemDisconnect( &stream );
/* Read the packet payload */
status = sread( &sessionInfoPtr->stream, sessionInfoPtr->receiveBuffer,
length );
if( cryptStatusError( status ) )
{
sNetGetErrorInfo( &sessionInfoPtr->stream,
&sessionInfoPtr->errorInfo );
return( status );
}
if( status < length )
{
/* If we timed out during the handshake phase, treat it as a hard
timeout error */
retExt( CRYPT_ERROR_TIMEOUT,
( CRYPT_ERROR_TIMEOUT, SESSION_ERRINFO,
"Timeout during legacy SSLv2 hello packet read, only got "
"%d of %d bytes", status, length ) );
}
sessionInfoPtr->receiveBufPos = 0;
sessionInfoPtr->receiveBufEnd = length;
sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
status = dualMacDataRead( handshakeInfo, &stream );
sMemDisconnect( &stream );
if( cryptStatusError( status ) )
retIntError();
/* SSLv2 puts the version info in the header, so we set the SSLv2 flag
in the handshake info to ensure that it doesn't get confused with a
normal SSL packet type */
handshakeInfo->isSSLv2 = TRUE;
return( length );
}
#endif /* 0 */
/****************************************************************************
* *
* Read Packet Utility Functions *
* *
****************************************************************************/
/* Process version information */
int processVersionInfo( SESSION_INFO *sessionInfoPtr, STREAM *stream,
int *clientVersion )
{
int version;
assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( clientVersion == NULL || \
isWritePtr( clientVersion, sizeof( int ) ) );
/* Clear return value */
if( clientVersion != NULL )
*clientVersion = CRYPT_ERROR;
/* Check the major version number */
version = sgetc( stream );
if( version != SSL_MAJOR_VERSION )
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Invalid major version number %d, should be 3", version ) );
}
/* Check the minor version number. If we've already got the version
established, make sure that it matches the existing one, otherwise
determine which version we'll be using */
version = sgetc( stream );
if( clientVersion == NULL )
{
if( version != sessionInfoPtr->version )
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Invalid version number 3.%d, should be 3.%d",
version, sessionInfoPtr->version ) );
}
return( CRYPT_OK );
}
switch( version )
{
case SSL_MINOR_VERSION_SSL:
/* If the other side can't do TLS, fall back to SSL */
if( sessionInfoPtr->version >= SSL_MINOR_VERSION_TLS )
sessionInfoPtr->version = SSL_MINOR_VERSION_SSL;
break;
case SSL_MINOR_VERSION_TLS:
/* If the other side can't do TLS 1.1, fall back to TLS 1.0 */
if( sessionInfoPtr->version >= SSL_MINOR_VERSION_TLS11 )
sessionInfoPtr->version = SSL_MINOR_VERSION_TLS;
break;
case SSL_MINOR_VERSION_TLS11:
/* If the other side can't do TLS 1.2, fall back to TLS 1.1 */
if( sessionInfoPtr->version >= SSL_MINOR_VERSION_TLS12 )
sessionInfoPtr->version = SSL_MINOR_VERSION_TLS11;
break;
case SSL_MINOR_VERSION_TLS12:
/* If the other side can't do post-TLS 1.2, fall back to
TLS 1.2 */
if( sessionInfoPtr->version > SSL_MINOR_VERSION_TLS12 )
sessionInfoPtr->version = SSL_MINOR_VERSION_TLS12;
break;
default:
/* If we're the server and the client has offered a vaguely
sensible version, fall back to the highest version that we
support */
if( isServer( sessionInfoPtr ) && version <= 5 )
{
sessionInfoPtr->version = SSL_MINOR_VERSION_TLS11;
break;
}
/* It's nothing that we can handle */
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Invalid protocol version 3.%d", version ) );
}
*clientVersion = version;
return( CRYPT_OK );
}
/* Check that the header of an SSL packet is in order:
byte type
byte[2] vers = { 0x03, 0x0n }
uint16 length
[ byte[] iv - TLS 1.1 ]
If this is the initial hello packet we request a dummy version info read
since the peer's version isn't known yet at this point. The actual
version info is taken from the hello packet data, not from the SSL
wrapper */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
static int checkPacketHeader( INOUT SESSION_INFO *sessionInfoPtr,
INOUT STREAM *stream,
OUT int *packetLength, const int packetType,
const int minLength, const int maxLength )
{
SSL_INFO *sslInfo = sessionInfoPtr->sessionSSL;
const int expectedPacketType = \
( packetType == SSL_MSG_FIRST_HANDSHAKE ) ? \
SSL_MSG_HANDSHAKE : packetType;
int value, length, status;
assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( ( packetType >= SSL_MSG_FIRST && packetType <= SSL_MSG_LAST ) || \
( packetType == SSL_MSG_FIRST_HANDSHAKE ) );
assert( ( packetType == SSL_MSG_APPLICATION_DATA && minLength == 0 ) || \
( minLength > 0 ) );
assert( isWritePtr( packetLength, sizeof( int ) ) );
/* Clear return value */
*packetLength = 0;
/* Check the packet type */
value = sgetc( stream );
if( value != expectedPacketType )
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Unexpected packet type %d, expected %d",
value, expectedPacketType ) );
}
status = processVersionInfo( sessionInfoPtr, stream,
( packetType == SSL_MSG_FIRST_HANDSHAKE ) ? &value : NULL );
if( cryptStatusError( status ) )
return( status );
/* Check the packet length */
length = readUint16( stream );
if( sessionInfoPtr->flags & SESSION_ISSECURE_READ )
{
if( length < sslInfo->ivSize + minLength + \
sessionInfoPtr->authBlocksize || \
length > sslInfo->ivSize + MAX_PACKET_SIZE + \
sessionInfoPtr->authBlocksize + 256 || \
length > maxLength )
status = CRYPT_ERROR_BADDATA;
}
else
{
if( length < minLength || length > MAX_PACKET_SIZE || \
length > maxLength )
status = CRYPT_ERROR_BADDATA;
}
if( cryptStatusError( status ) )
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Invalid packet length %d for packet type %d",
length, packetType ) );
}
/* Load the TLS 1.1 explicit IV if necessary */
if( ( sessionInfoPtr->flags & SESSION_ISSECURE_READ ) && \
sslInfo->ivSize > 0 )
{
int ivLength;
status = loadExplicitIV( sessionInfoPtr, stream, &ivLength );
if( cryptStatusError( status ) )
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Error loading TLS explicit IV" ) );
}
length -= ivLength;
if( length < minLength + sessionInfoPtr->authBlocksize || \
length > maxLength )
retIntError();
}
*packetLength = length;
return( CRYPT_OK );
}
/* Check that the header of an SSL packet and SSL handshake packet is in
order */
int checkPacketHeaderSSL( SESSION_INFO *sessionInfoPtr, STREAM *stream,
int *packetLength )
{
return( checkPacketHeader( sessionInfoPtr, stream, packetLength,
SSL_MSG_APPLICATION_DATA, 0,
sessionInfoPtr->receiveBufSize ) );
}
int checkHSPacketHeader( SESSION_INFO *sessionInfoPtr, STREAM *stream,
int *packetLength, const int packetType,
const int minSize )
{
int type, length;
assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( packetType >= SSL_HAND_FIRST && packetType <= SSL_HAND_LAST );
assert( minSize >= 0 ); /* May be zero for change cipherspec */
assert( isWritePtr( packetLength, sizeof( int ) ) );
/* Clear return value */
*packetLength = 0;
/* byte ID = type
uint24 length */
type = sgetc( stream );
if( type != packetType )
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Invalid handshake packet type %d, expected %d",
type, packetType ) );
}
length = readUint24( stream );
if( length < minSize || length > MAX_PACKET_SIZE || \
length > sMemDataLeft( stream ) )
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Invalid length %d for handshake packet type %d",
length, type ) );
}
*packetLength = length;
return( CRYPT_OK );
}
/****************************************************************************
* *
* Read/Unwrap a Packet *
* *
****************************************************************************/
/* Unwrap an SSL data packet:
data
|----------- MAC'd
v======================= Encrypted
+-----+-----+-----------+-----+-----+
| hdr |(IV) | data | MAC | pad |
+-----+-----+-----------+-----+-----+
|<---- dataMaxLen ----->|
|<- dLen -->|
This decrypts the data, removes the padding, checks and removes the MAC,
and returns the payload length. Processing of the header and IV have
already been performed during the packet header read */
int unwrapPacketSSL( SESSION_INFO *sessionInfoPtr, void *data,
const int dataMaxLength, int *dataLength,
const int packetType )
{
BOOLEAN badDecrypt = FALSE;
int length, payloadLength, status;
assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) && \
sessionInfoPtr->flags & SESSION_ISSECURE_READ );
assert( isWritePtr( data, dataMaxLength ) );
assert( isWritePtr( dataLength, sizeof( int ) ) );
assert( dataMaxLength >= sessionInfoPtr->authBlocksize && \
dataMaxLength <= MAX_PACKET_SIZE + sessionInfoPtr->authBlocksize + \
256 );
/* Sanity-check the state */
if( dataMaxLength < sessionInfoPtr->authBlocksize || \
dataMaxLength > MAX_PACKET_SIZE + sessionInfoPtr->authBlocksize + 256 )
retIntError();
/* Clear return value */
*dataLength = 0;
/* Make sure that the length is a multiple of the block cipher size */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -