📄 ssl_rw.c
字号:
if( sessionInfoPtr->cryptBlocksize > 1 && \
( dataMaxLength % sessionInfoPtr->cryptBlocksize ) )
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Invalid encrypted packet length %d relative to cipher "
"block size %d for packet type %d", dataMaxLength,
sessionInfoPtr->cryptBlocksize, packetType ) );
}
/* Decrypt the packet in the buffer. We allow zero-length blocks (once
the padding is stripped) because some versions of OpenSSL send these
as a kludge to work around pre-TLS 1.1 chosen-IV attacks */
status = decryptData( sessionInfoPtr, data, dataMaxLength, &length );
if( cryptStatusError( status ) )
{
/* If there's a padding error, don't exit immediately but record
that there was a problem for after we've done the MAC'ing.
Delaying the error reporting until then helps prevent timing
attacks of the kind described by Brice Canvel, Alain Hiltgen,
Serge Vaudenay, and Martin Vuagnoux in "Password Interception
in a SSL/TLS Channel", Crypto'03, LNCS No.2729, p.583. These
are close to impossible in most cases because we delay sending
the close notify over a much longer period than the MAC vs.non-
MAC time difference and because it requires repeatedly connecting
with a fixed-format secret such as a password at the same location
in the packet (which MS Outlook does however manage to do), but
we take this step anyway just to be safe */
if( status == CRYPT_ERROR_BADDATA )
{
badDecrypt = TRUE;
length = dataMaxLength;
}
else
return( status );
}
payloadLength = length - sessionInfoPtr->authBlocksize;
if( payloadLength < 0 || payloadLength > MAX_PACKET_SIZE )
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Invalid packet payload length %d for packet type %d",
payloadLength, packetType ) );
}
/* MAC the decrypted data. The badDecrypt flag suppresses the reporting
of a MAC error due to an earlier bad decrypt, which has already been
reported by decryptData() */
if( sessionInfoPtr->version == SSL_MINOR_VERSION_SSL )
status = checkMacSSL( sessionInfoPtr, data, length, payloadLength,
packetType, badDecrypt );
else
status = checkMacTLS( sessionInfoPtr, data, length, payloadLength,
packetType, badDecrypt );
if( badDecrypt )
{
/* Report the delayed decrypt error, held to this point to make
timing attacks more difficult */
return( CRYPT_ERROR_BADDATA );
}
if( cryptStatusError( status ) )
return( status );
*dataLength = payloadLength;
return( CRYPT_OK );
}
/* Read an SSL handshake packet. Since the data transfer phase has its own
read/write code we can perform some special-case handling based on this */
int readHSPacketSSL( SESSION_INFO *sessionInfoPtr,
SSL_HANDSHAKE_INFO *handshakeInfo, int *packetLength,
const int packetType )
{
STREAM stream;
BYTE headerBuffer[ SSL_HEADER_SIZE + CRYPT_MAX_IVSIZE + 8 ];
int bytesToRead, length, status;
assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
assert( ( handshakeInfo == NULL ) || \
isWritePtr( handshakeInfo, sizeof( SSL_HANDSHAKE_INFO ) ) );
assert( isWritePtr( packetLength, sizeof( int ) ) );
assert( ( packetType >= SSL_MSG_FIRST && packetType <= SSL_MSG_LAST ) || \
( packetType == SSL_MSG_FIRST_HANDSHAKE ) );
assert( sessionInfoPtr->receiveBufStartOfs >= SSL_HEADER_SIZE && \
sessionInfoPtr->receiveBufStartOfs < \
SSL_HEADER_SIZE + CRYPT_MAX_IVSIZE );
/* Sanity-check the state */
if( sessionInfoPtr->receiveBufStartOfs < SSL_HEADER_SIZE || \
sessionInfoPtr->receiveBufStartOfs >= \
SSL_HEADER_SIZE + CRYPT_MAX_IVSIZE )
retIntError();
/* Clear return value */
*packetLength = 0;
/* Read and process the header */
status = readFixedHeaderAtomic( sessionInfoPtr, headerBuffer,
sessionInfoPtr->receiveBufStartOfs );
if( cryptStatusError( status ) )
return( status );
/* Check for an SSL alert message */
if( headerBuffer[ 0 ] == SSL_MSG_ALERT )
return( processAlert( sessionInfoPtr, headerBuffer,
sessionInfoPtr->receiveBufStartOfs ) );
/* Decode and process the SSL packet header */
if( packetType == SSL_MSG_FIRST_HANDSHAKE && \
headerBuffer[ 0 ] == SSL_MSG_V2HANDSHAKE )
{
#if 0 /* 28/01/08 Disabled since it's now finally been removed from MSIE
and Firefox */
/* It's an SSLv2 handshake, handle it specially */
return( handleSSLv2Header( sessionInfoPtr, handshakeInfo,
headerBuffer ) );
#else
retExt( CRYPT_ERROR_NOSECURE,
( CRYPT_ERROR_NOSECURE, SESSION_ERRINFO,
"Client sent obsolete handshake for the insecure SSLv2 "
"protocol" ) );
#endif /* 0 */
}
sMemConnect( &stream, headerBuffer, sessionInfoPtr->receiveBufStartOfs );
status = checkPacketHeader( sessionInfoPtr, &stream, &bytesToRead,
packetType,
( packetType == SSL_MSG_CHANGE_CIPHER_SPEC ) ? \
1 : MIN_PACKET_SIZE,
sessionInfoPtr->receiveBufSize );
sMemDisconnect( &stream );
if( cryptStatusError( status ) )
return( status );
/* Read the payload packet(s) */
status = length = \
sread( &sessionInfoPtr->stream, sessionInfoPtr->receiveBuffer,
bytesToRead );
if( cryptStatusError( status ) )
{
sNetGetErrorInfo( &sessionInfoPtr->stream,
&sessionInfoPtr->errorInfo );
return( status );
}
if( length < bytesToRead )
{
/* If we timed out during the handshake phase, treat it as a hard
timeout error */
retExt( CRYPT_ERROR_TIMEOUT,
( CRYPT_ERROR_TIMEOUT, SESSION_ERRINFO,
"Timed out reading packet data for packet type %d, only "
"got %d of %d bytes", packetType, length, bytesToRead ) );
}
sessionInfoPtr->receiveBufPos = 0;
sessionInfoPtr->receiveBufEnd = length;
if( handshakeInfo != NULL )
{
sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
status = dualMacDataRead( handshakeInfo, &stream );
sMemDisconnect( &stream );
if( cryptStatusError( status ) )
return( status );
}
*packetLength = length;
return( CRYPT_OK );
}
/* Read the next handshake stream packet */
int refreshHSStream( SESSION_INFO *sessionInfoPtr,
SSL_HANDSHAKE_INFO *handshakeInfo )
{
STREAM *stream = &handshakeInfo->stream;
int length, status;
assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
assert( isWritePtr( handshakeInfo, sizeof( SSL_HANDSHAKE_INFO ) ) );
/* If there's still data present in the stream, there's nothing left
to do */
if( sMemDataLeft( stream ) > 0 )
return( CRYPT_OK );
/* Refill the stream */
sMemDisconnect( stream );
status = readHSPacketSSL( sessionInfoPtr, handshakeInfo, &length,
SSL_MSG_HANDSHAKE );
if( cryptStatusError( status ) )
return( status );
assert( length > 0 );
sMemConnect( stream, sessionInfoPtr->receiveBuffer, length );
return( CRYPT_OK );
}
/****************************************************************************
* *
* Write Packet Utility Functions *
* *
****************************************************************************/
/* Open and complete an SSL packet:
offset packetEndOfs
| |
v v
+---+---+---+----+--------------------------+
|ID |Ver|Len|(IV)| |
+---+---+---+----+--------------------------+
An initial openXXX() starts a new packet at the start of a stream and
continueXXX() adds another packet after an existing one, or (for the
xxxHSXXX() variants) adds a handshake sub-packet within an existing
packet. The continueXXX() operations return the start offset of the new
packet within the stream, openXXX() always starts at the start of the SSL
send buffer so the start offset is an implied 0. completeXXX() then goes
back to the given offset and deposits the appropriate length value in the
header that was written earlier. So typical usage would be:
// Change-cipher-spec packet
openPacketStreamSSL( CRYPT_USE_DEFAULT, SSL_MSG_CHANGE_CIPHER_SPEC );
write( stream, ... );
completePacketStreamSSL( stream, 0 );
// Finished handshake sub-packet within a handshake packet
continuePacketStreamSSL( SSL_MSG_HANDSHAKE );
offset = continueHSPacketStream( SSL_HAND_FINISHED );
write( stream, ... );
completeHSPacketStream( stream, offset );
// (Packet stream is completed by wrapPacketSSL())
Errors are propagated and caught at the completeXXX() stage */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int openPacketStream( INOUT STREAM *stream,
const SESSION_INFO *sessionInfoPtr,
const int packetType )
{
SSL_INFO *sslInfo = sessionInfoPtr->sessionSSL;
int status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
assert( packetType >= SSL_MSG_FIRST && packetType <= SSL_MSG_LAST );
/* Write the packet header:
byte ID = packetType
byte[2] version = { 0x03, 0x0n }
uint16 len = 0 (placeholder)
[ byte[] iv - TLS 1.1 only ] */
sputc( stream, packetType );
sputc( stream, SSL_MAJOR_VERSION );
sputc( stream, sessionInfoPtr->version );
status = writeUint16( stream, 0 ); /* Placeholder */
if( ( sessionInfoPtr->flags & SESSION_ISSECURE_WRITE ) && \
sslInfo->ivSize > 0 )
{
MESSAGE_DATA msgData;
BYTE iv[ CRYPT_MAX_IVSIZE + 8 ];
setMessageData( &msgData, iv, sslInfo->ivSize );
krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_GETATTRIBUTE_S,
&msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE );
status = swrite( stream, iv, sslInfo->ivSize );
}
return( status );
}
int openPacketStreamSSL( STREAM *stream, const SESSION_INFO *sessionInfoPtr,
const int bufferSize, const int packetType )
{
const int streamSize = ( bufferSize == CRYPT_USE_DEFAULT ) ? \
sessionInfoPtr->sendBufSize - EXTRA_PACKET_SIZE : \
bufferSize + sessionInfoPtr->sendBufStartOfs;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) && \
isWritePtr( sessionInfoPtr->sendBuffer, streamSize ) );
assert( bufferSize == CRYPT_USE_DEFAULT || \
( packetType == SSL_MSG_APPLICATION_DATA && bufferSize == 0 ) || \
bufferSize > 0 );
/* When wrapping up data packets we only write the implicit-
length header so the buffer size is zero */
assert( packetType >= SSL_MSG_FIRST && packetType <= SSL_MSG_LAST );
/* Sanity-check the state */
if( streamSize < sessionInfoPtr->sendBufStartOfs || \
streamSize > sessionInfoPtr->sendBufSize - EXTRA_PACKET_SIZE )
retIntError();
/* Create the stream */
sMemOpen( stream, sessionInfoPtr->sendBuffer, streamSize );
return( openPacketStream( stream, sessionInfoPtr, packetType ) );
}
int continuePacketStreamSSL( STREAM *stream,
const SESSION_INFO *sessionInfoPtr,
const int packetType )
{
const int offset = stell( stream );
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( stell( stream ) >= SSL_HEADER_SIZE );
assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
assert( packetType >= SSL_MSG_FIRST && packetType <= SSL_MSG_LAST );
/* We don't have to check the return value of the continue/open since
it's implicitly communicated via the stream state */
( void ) openPacketStream( stream, sessionInfoPtr, packetType );
return( offset );
}
int completePacketStreamSSL( STREAM *stream, const int offset )
{
const int packetEndOffset = stell( stream );
int status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( ( offset == 0 || offset >= SSL_HEADER_SIZE ) && \
offset <= packetEndOffset - ( ID_SIZE + VERSIONINFO_SIZE ) );
/* Sanity-check the state */
if( ( offset != 0 && offset < SSL_HEADER_SIZE ) || \
offset > packetEndOffset - ( ID_SIZE + VERSIONINFO_SIZE ) )
retIntError();
/* Update the length field at the start of the packet */
sseek( stream, offset + ID_SIZE + VERSIONINFO_SIZE );
status = writeUint16( stream, ( packetEndOffset - offset ) - \
SSL_HEADER_SIZE );
sseek( stream, packetEndOffset );
return( status );
}
/* Start and complete a handshake packet within an SSL packet. Since this
continues an existing packet stream that's been opened using
openPacketStreamSSL(), it's denoted as continueXXX() rather than
openXXX() */
int continueHSPacketStream( STREAM *stream, const int packetType )
{
const int offset = stell( stream );
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( packetType >= SSL_HAND_FIRST && packetType <= SSL_HAND_LAST );
/* Write the handshake packet header:
byte ID = packetType
uint24 len = 0 (placeholder) */
sputc( stream, packetType );
writeUint24( stream, 0 ); /* Placeholder */
return( offset );
}
int completeHSPacketStream( STREAM *stream, const int offset )
{
const int packetEndOffset = stell( stream );
int status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( offset >= SSL_HEADER_SIZE && \
offset <= packetEndOffset - ( ID_SIZE + LENGTH_SIZE ) );
/* HELLO_DONE has size zero so ofs == pEO - HDR_SIZE */
/* Sanity-check the state */
if( offset < SSL_HEADER_SIZE || \
offset > packetEndOffset - ( ID_SIZE + LENGTH_SIZE ) )
retIntError();
/* Update the length field at the start of the packet */
sseek( stream, offset + ID_SIZE );
status = writeUint24( stream, packetEndOffset - \
( offset + ID_SIZE + LENGTH_SIZE ) );
sseek( stream, packetEndOffset );
return( status );
}
/****************************************************************************
* *
* Write/wrap a Packet *
* *
****************************************************************************/
/* Wrap an SSL data packet:
sendBuffer hdrPtr dataPtr
| | |------------------- MAC'd
v v v================================ Encrypted
+-------+-----+-----+-------------------+-----+-----+
|///////| hdr | IV | data | MAC | pad |
+-------+-----+-----+-------------------+-----+-----+
^<--------->|<- payloadLength ->^ |
| | <-------- bMaxLen -|---------->
offset sBufStartOfs stell( stream )
This MACs the data, adds the IV if necessary, pads and encrypts, and
updates the header */
int wrapPacketSSL( SESSION_INFO *sessionInfoPtr, STREAM *stream,
const int offset )
{
SSL_INFO *sslInfo = sessionInfoPtr->sessionSSL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -