📄 ssl_rw.c
字号:
STREAM lengthStream;
const int payloadLength = stell( stream ) - \
( offset + sessionInfoPtr->sendBufStartOfs );
const int bufMaxLen = payloadLength + sMemDataLeft( stream );
BYTE lengthBuffer[ UINT16_SIZE + 8 ];
BYTE *dataPtr, *headerPtr;
int length, status;
assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
assert( sessionInfoPtr->flags & SESSION_ISSECURE_WRITE );
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( sStatusOK( stream ) );
assert( offset >= 0 && \
offset <= stell( stream ) - \
( payloadLength + sessionInfoPtr->sendBufStartOfs ) );
assert( payloadLength >= 0 && payloadLength <= MAX_PACKET_SIZE && \
payloadLength < sessionInfoPtr->sendBufSize - \
( sessionInfoPtr->sendBufStartOfs + sslInfo->ivSize ) );
/* Sanity-check the state */
if( offset < 0 || \
offset > stell( stream ) - \
( payloadLength + sessionInfoPtr->sendBufStartOfs ) || \
payloadLength < 0 || payloadLength > MAX_PACKET_SIZE || \
payloadLength >= sessionInfoPtr->sendBufSize - \
( sessionInfoPtr->sendBufStartOfs + sslInfo->ivSize ) )
retIntError();
/* Get pointers into the data stream for the crypto processing */
status = sMemGetDataBlockAbs( stream, offset, ( void ** ) &headerPtr,
bufMaxLen );
if( cryptStatusError( status ) )
return( status );
dataPtr = headerPtr + SSL_HEADER_SIZE + sslInfo->ivSize;
assert( *headerPtr >= SSL_MSG_FIRST && *headerPtr <= SSL_MSG_LAST );
/* MAC the payload */
if( sessionInfoPtr->version == SSL_MINOR_VERSION_SSL )
status = createMacSSL( sessionInfoPtr, dataPtr, bufMaxLen, &length,
payloadLength, *headerPtr );
else
status = createMacTLS( sessionInfoPtr, dataPtr, bufMaxLen, &length,
payloadLength, *headerPtr );
if( cryptStatusError( status ) )
return( status );
/* If it's TLS 1.1 or newer and we're using a block cipher, adjust for
the explicit IV that precedes the data. We know that the resulting
values are within bounds because dataPtr = headerPtr + hdr + IV */
if( sslInfo->ivSize > 0 )
{
assert( sessionInfoPtr->sendBufStartOfs >= \
SSL_HEADER_SIZE + sslInfo->ivSize );
dataPtr -= sslInfo->ivSize;
assert( dataPtr > headerPtr );
length += sslInfo->ivSize;
if( length > bufMaxLen )
retIntError();
}
/* Pad and encrypt the payload */
status = encryptData( sessionInfoPtr, dataPtr, bufMaxLen, &length,
length );
if( cryptStatusError( status ) )
return( status );
/* Insert the final packet payload length into the packet header. We
directly copy the data in because the stream may have been opened in
read-only mode if we're using it to write pre-assembled packet data
that's been passed in by the caller */
sMemOpen( &lengthStream, lengthBuffer, UINT16_SIZE );
status = writeUint16( &lengthStream, length );
sMemDisconnect( &lengthStream );
memcpy( headerPtr + ID_SIZE + VERSIONINFO_SIZE, lengthBuffer,
UINT16_SIZE );
if( cryptStatusError( status ) )
return( status );
/* Sync the stream info to match the new payload size */
return( sSkip( stream, length - ( sslInfo->ivSize + payloadLength ) ) );
}
/* Wrap up and send an SSL packet */
int sendPacketSSL( SESSION_INFO *sessionInfoPtr, STREAM *stream,
const BOOLEAN sendOnly )
{
const int length = stell( stream );
void *dataPtr;
int status;
assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( sStatusOK( stream ) );
assert( stell( stream ) >= SSL_HEADER_SIZE );
/* Safety check to make sure that the stream is OK */
if( !sStatusOK( stream ) )
{
assert( DEBUG_WARN );
return( sGetStatus( stream ) );
}
/* Update the length field at the start of the packet if necessary */
if( !sendOnly )
{
status = completePacketStreamSSL( stream, 0 );
if( cryptStatusError( status ) )
return( status );
}
/* Send the packet to the peer */
status = sMemGetDataBlockAbs( stream, 0, &dataPtr, length );
if( cryptStatusOK( status ) )
status = swrite( &sessionInfoPtr->stream, dataPtr, length );
if( cryptStatusError( status ) )
{
sNetGetErrorInfo( &sessionInfoPtr->stream,
&sessionInfoPtr->errorInfo );
return( status );
}
return( CRYPT_OK ); /* swrite() returns a byte count */
}
/****************************************************************************
* *
* Send/Receive SSL Alerts *
* *
****************************************************************************/
/* Process an alert packet. IIS often just drops the connection rather than
sending an alert when it encounters a problem (although we try and work
around some of the known problems, e.g. by sending a canary in the client
hello to force IIS to at least send back something rather than just
dropping the connection, see ssl_cli.c), so when communicating with IIS
the only error indication that we sometimes get will be a "Connection
closed by remote host" rather than an SSL-level error message. In
addition when it encounters an unknown cert MSIE will complete the
handshake and then close the connection (via a proper close alert in this
case rather than just closing the connection), wait while the user clicks
OK several times, and then restart the connection via an SSL resume.
Netscape in contrast just hopes that the session won't time out while
waiting for the user to click OK. As a result, cryptlib sees a closed
connection and aborts the session setup process, requiring a second call
to the session setup to continue with the resumed session */
int processAlert( SESSION_INFO *sessionInfoPtr, const void *header,
const int headerLength )
{
typedef struct {
const int type;
const char *message;
const int messageLength;
const int cryptlibError;
} ALERT_INFO;
const static ALERT_INFO alertInfo[] = {
{ SSL_ALERT_CLOSE_NOTIFY, "Close notify", 12, CRYPT_ERROR_COMPLETE },
{ SSL_ALERT_UNEXPECTED_MESSAGE, "Unexpected message", 18, CRYPT_ERROR_FAILED },
{ SSL_ALERT_BAD_RECORD_MAC, "Bad record MAC", 14, CRYPT_ERROR_SIGNATURE },
{ TLS_ALERT_DECRYPTION_FAILED, "Decryption failed", 17, CRYPT_ERROR_WRONGKEY },
{ TLS_ALERT_RECORD_OVERFLOW, "Record overflow", 15, CRYPT_ERROR_OVERFLOW },
{ SSL_ALERT_DECOMPRESSION_FAILURE, "Decompression failure", 21, CRYPT_ERROR_FAILED },
{ SSL_ALERT_HANDSHAKE_FAILURE, "Handshake failure", 17, CRYPT_ERROR_FAILED },
{ SSL_ALERT_NO_CERTIFICATE, "No certificate", 14, CRYPT_ERROR_PERMISSION },
{ SSL_ALERT_BAD_CERTIFICATE, "Bad certificate", 15, CRYPT_ERROR_INVALID },
{ SSL_ALERT_UNSUPPORTED_CERTIFICATE, "Unsupported certificate", 23, CRYPT_ERROR_INVALID },
{ SSL_ALERT_CERTIFICATE_REVOKED, "Certificate revoked", 19, CRYPT_ERROR_INVALID },
{ SSL_ALERT_CERTIFICATE_EXPIRED, "Certificate expired", 19, CRYPT_ERROR_INVALID },
{ SSL_ALERT_CERTIFICATE_UNKNOWN, "Certificate unknown", 19, CRYPT_ERROR_INVALID },
{ SSL_ALERT_ILLEGAL_PARAMETER, "Illegal parameter", 17, CRYPT_ERROR_FAILED },
{ TLS_ALERT_UNKNOWN_CA, "Unknown CA", 10, CRYPT_ERROR_INVALID },
{ TLS_ALERT_ACCESS_DENIED, "Access denied", 13, CRYPT_ERROR_PERMISSION },
{ TLS_ALERT_DECODE_ERROR, "Decode error", 12, CRYPT_ERROR_FAILED },
{ TLS_ALERT_DECRYPT_ERROR, "Decrypt error", 13, CRYPT_ERROR_WRONGKEY },
{ TLS_ALERT_EXPORT_RESTRICTION, "Export restriction", 18, CRYPT_ERROR_FAILED },
{ TLS_ALERT_PROTOCOL_VERSION, "Protocol version", 16, CRYPT_ERROR_NOTAVAIL },
{ TLS_ALERT_INSUFFICIENT_SECURITY, "Insufficient security", 21, CRYPT_ERROR_NOSECURE },
{ TLS_ALERT_INTERNAL_ERROR, "Internal error", 14, CRYPT_ERROR_FAILED },
{ TLS_ALERT_USER_CANCELLED, "User cancelled", 14, CRYPT_ERROR_FAILED },
{ TLS_ALERT_NO_RENEGOTIATION, "No renegotiation", 16, CRYPT_ERROR_FAILED },
{ TLS_ALERT_UNSUPPORTED_EXTENSION, "Unsupported extension", 21, CRYPT_ERROR_NOTAVAIL },
{ TLS_ALERT_CERTIFICATE_UNOBTAINABLE, "Certificate unobtainable", 24, CRYPT_ERROR_NOTFOUND },
{ TLS_ALERT_UNRECOGNIZED_NAME, "Unrecognized name", 17, CRYPT_ERROR_FAILED },
{ TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE, "Bad certificate status response", 31, CRYPT_ERROR_FAILED },
{ TLS_ALERT_BAD_CERTIFICATE_HASH_VALUE, "Bad certificate hash value", 26, CRYPT_ERROR_FAILED },
{ TLS_ALERT_UNKNOWN_PSK_IDENTITY, "Unknown PSK identity", 20, CRYPT_ERROR_NOTFOUND },
{ CRYPT_ERROR, NULL }, { CRYPT_ERROR, NULL }
};
ERROR_INFO *errorInfo = &sessionInfoPtr->errorInfo;
STREAM stream;
BYTE buffer[ 256 + 8 ];
int length, type, i, status;
assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
assert( isReadPtr( header, headerLength ) );
/* Process the alert packet header */
sMemConnect( &stream, header, headerLength );
status = checkPacketHeader( sessionInfoPtr, &stream, &length,
SSL_MSG_ALERT, ALERTINFO_SIZE,
sessionInfoPtr->receiveBufSize );
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
if( sessionInfoPtr->flags & SESSION_ISSECURE_READ )
{
if( length < ALERTINFO_SIZE || length > 256 )
status = CRYPT_ERROR_BADDATA;
}
else
{
if( length != ALERTINFO_SIZE )
status = CRYPT_ERROR_BADDATA;
}
sMemDisconnect( &stream );
if( cryptStatusError( status ) )
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Invalid alert message length %d", length ) );
}
/* Read and process the alert packet */
status = sread( &sessionInfoPtr->stream, buffer, length );
if( cryptStatusError( status ) )
{
sNetGetErrorInfo( &sessionInfoPtr->stream,
&sessionInfoPtr->errorInfo );
return( status );
}
if( status < length )
{
/* If we timed out before we could get all of the alert data, bail
out without trying to perform any further processing. We're
about to shut down the session anyway so there's no point in
potentially stalling for ages trying to find a lost byte */
sendCloseAlert( sessionInfoPtr, TRUE );
sessionInfoPtr->flags |= SESSION_SENDCLOSED;
retExt( CRYPT_ERROR_TIMEOUT,
( CRYPT_ERROR_TIMEOUT, SESSION_ERRINFO,
"Timed out reading alert message, only got %d of %d "
"bytes", status, length ) );
}
sessionInfoPtr->receiveBufEnd = length;
if( ( sessionInfoPtr->flags & SESSION_ISSECURE_READ ) && \
( length > ALERTINFO_SIZE || \
isStreamCipher( sessionInfoPtr->cryptAlgo ) ) )
{
/* We only try and decrypt if the alert info is big enough to be
encrypted, i.e. it contains the fixed-size data + padding. This
situation can occur if there's an error moving from the non-
secure to the secure state. However, if it's a stream cipher the
ciphertext and plaintext are the same size so we always have to
try the decryption */
status = unwrapPacketSSL( sessionInfoPtr, buffer, length, &length,
SSL_MSG_ALERT );
if( cryptStatusError( status ) )
{
sendCloseAlert( sessionInfoPtr, TRUE );
sessionInfoPtr->flags |= SESSION_SENDCLOSED;
return( status );
}
}
/* Tell the other side that we're going away */
sendCloseAlert( sessionInfoPtr, TRUE );
sessionInfoPtr->flags |= SESSION_SENDCLOSED;
/* Process the alert info. In theory we should also make the session
non-resumable if the other side goes away without sending a close
alert, but this leads to too many problems with non-resumable
sessions if we do it. For example many protocols do their own end-of-
data indication (e.g. "Connection: close" in HTTP and BYE in SMTP)
and so don't bother with a close alert. In other cases
implementations just drop the connection without sending a close
alert, carried over from many early Unix protocols that used a
connection close to signify end-of-data, which has caused problems
ever since for newer protocols that want to keep the connection open.
Other implementations still send their alert but then immediately
close the connection. Because of this haphazard approach to closing
connections, many implementations allow a session to be resumed even
if no close alert is sent. In order to be compatible with this
behaviour, we do the same (thus perpetuating the problem). If
necessary this can be fixed by calling deleteSessionCacheEntry() if
the connection is closed without a close alert having been sent */
if( buffer[ 0 ] != SSL_ALERTLEVEL_WARNING && \
buffer[ 0 ] != SSL_ALERTLEVEL_FATAL )
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Invalid alert message level %d", buffer[ 0 ] ) );
}
errorInfo->errorCode = type = buffer[ 1 ];
for( i = 0; alertInfo[ i ].type != CRYPT_ERROR && \
alertInfo[ i ].type != type && \
i < FAILSAFE_ARRAYSIZE( alertInfo, ALERT_INFO ); i++ );
if( i >= FAILSAFE_ARRAYSIZE( alertInfo, ALERT_INFO ) )
retIntError();
if( alertInfo[ i ].type == CRYPT_ERROR )
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Unknown alert message type %d at alert level %d",
type, buffer[ 0 ] ) );
}
retExtStr( alertInfo[ i ].cryptlibError,
( alertInfo[ i ].cryptlibError, SESSION_ERRINFO,
alertInfo[ i ].message, alertInfo[ i ].messageLength,
( sessionInfoPtr->version == SSL_MINOR_VERSION_SSL ) ? \
"Received SSL alert message: " : \
"Received TLS alert message: " ) );
}
/* Send a close alert, with appropriate protection if necessary */
STDC_NONNULL_ARG( ( 1 ) ) \
static void sendAlert( INOUT SESSION_INFO *sessionInfoPtr,
const int alertLevel, const int alertType,
const BOOLEAN alertReceived )
{
STREAM stream;
int length = DUMMY_INIT, status;
assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
assert( alertLevel == SSL_ALERTLEVEL_WARNING || \
alertLevel == SSL_ALERTLEVEL_FATAL );
assert( alertType >= SSL_ALERT_FIRST && \
alertType <= SSL_ALERT_LAST );
/* Make sure that we only send a single alert. Normally we do this
automatically on shutdown, but we may have already sent it earlier
as part of an error-handler */
if( sessionInfoPtr->protocolFlags & SSL_PFLAG_ALERTSENT )
return;
sessionInfoPtr->protocolFlags |= SSL_PFLAG_ALERTSENT;
/* Create the alert. We can't really do much with errors at this point,
although we can throw an exception in the debug version to draw
attention to the fact that there's a problem. The one error type
that we don't complain about is an access permission problem, which
can occur when cryptlib is shutting down, for example when the
current thread is blocked waiting for network traffic and another
thread shuts cryptlib down */
status = openPacketStreamSSL( &stream, sessionInfoPtr,
CRYPT_USE_DEFAULT, SSL_MSG_ALERT );
if( cryptStatusOK( status ) )
{
sputc( &stream, alertLevel );
sputc( &stream, alertType );
if( sessionInfoPtr->flags & SESSION_ISSECURE_WRITE )
{
status = wrapPacketSSL( sessionInfoPtr, &stream, 0 );
assert( cryptStatusOK( status ) || \
status == CRYPT_ERROR_PERMISSION );
}
else
{
status = completePacketStreamSSL( &stream, 0 );
}
if( cryptStatusOK( status ) )
length = stell( &stream );
sMemDisconnect( &stream );
}
/* Fall through with status passed on to the following code */
/* Send the alert. Note that we don't exit on an error status in the
previous operation (for the reasons given in the comment earlier)
since we can at least perform a clean shutdown even if the creation
of the close alert fails */
if( cryptStatusOK( status ) )
status = sendCloseNotification( sessionInfoPtr,
sessionInfoPtr->sendBuffer, length );
else
status = sendCloseNotification( sessionInfoPtr, NULL, 0 );
if( cryptStatusError( status ) || alertReceived )
return;
/* Read back the other side's close alert acknowledgement. Again, since
we're closing down the session anyway there's not much that we can do
in response to an error */
( void ) readHSPacketSSL( sessionInfoPtr, NULL, &length,
SSL_MSG_ALERT );
}
void sendCloseAlert( SESSION_INFO *sessionInfoPtr,
const BOOLEAN alertReceived )
{
sendAlert( sessionInfoPtr, SSL_ALERTLEVEL_WARNING,
SSL_ALERT_CLOSE_NOTIFY, alertReceived );
}
void sendHandshakeFailAlert( SESSION_INFO *sessionInfoPtr )
{
/* We set the alertReceived flag to true when sending a handshake
failure alert to avoid waiting to get back an ack, since this
alert type isn't acknowledged by the other side */
sendAlert( sessionInfoPtr, SSL_ALERTLEVEL_FATAL,
SSL_ALERT_HANDSHAKE_FAILURE, TRUE );
}
#endif /* USE_SSL */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -