📄 ssl_rw.c
字号:
}
/* MAC the payload */
if( sessionInfoPtr->version == SSL_MINOR_VERSION_SSL )
length = macDataSSL( sessionInfoPtr, bufPtr, payloadLength,
*headerPtr, FALSE, FALSE );
else
length = macDataTLS( sessionInfoPtr, bufPtr, payloadLength,
*headerPtr, FALSE, FALSE );
if( cryptStatusError( length ) )
return( length );
/* If it's TLS 1.1 or newer and we're using a block cipher, adjust for
the explicit IV that precedes the data */
if( sslInfo->ivSize > 0 )
{
assert( sessionInfoPtr->sendBufStartOfs >= \
SSL_HEADER_SIZE + sslInfo->ivSize );
bufPtr -= sslInfo->ivSize;
length += sslInfo->ivSize;
}
/* Encrypt the payload */
length = encryptData( sessionInfoPtr, bufPtr, length );
if( cryptStatusError( length ) )
return( length );
/* Insert the final packet payload length into the packet header. We do
this both for convenience and 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 */
headerPtr += ID_SIZE + VERSIONINFO_SIZE;
mputWord( headerPtr, length );
/* 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 )
{
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( NOTREACHED );
return( sGetStatus( stream ) );
}
/* Update the length field at the start of the packet if necessary */
if( !sendOnly )
completePacketStreamSSL( stream, 0 );
/* Send the packet to the peer */
status = swrite( &sessionInfoPtr->stream,
sMemBufPtr( stream ) - stell( stream ),
stell( stream ) );
if( cryptStatusError( status ) )
{
sNetGetErrorInfo( &sessionInfoPtr->stream,
sessionInfoPtr->errorMessage,
&sessionInfoPtr->errorCode );
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 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 )
{
const static struct {
const int type;
const char *message;
const int cryptlibError;
} alertInfo[] = {
{ SSL_ALERT_CLOSE_NOTIFY, "Close notify", CRYPT_ERROR_COMPLETE },
{ SSL_ALERT_UNEXPECTED_MESSAGE, "Unexpected message", CRYPT_ERROR_FAILED },
{ SSL_ALERT_BAD_RECORD_MAC, "Bad record MAC", CRYPT_ERROR_SIGNATURE },
{ TLS_ALERT_DECRYPTION_FAILED, "Decryption failed", CRYPT_ERROR_WRONGKEY },
{ TLS_ALERT_RECORD_OVERFLOW, "Record overflow", CRYPT_ERROR_OVERFLOW },
{ SSL_ALERT_DECOMPRESSION_FAILURE, "Decompression failure", CRYPT_ERROR_FAILED },
{ SSL_ALERT_HANDSHAKE_FAILURE, "Handshake failure", CRYPT_ERROR_FAILED },
{ SSL_ALERT_NO_CERTIFICATE, "No certificate", CRYPT_ERROR_PERMISSION },
{ SSL_ALERT_BAD_CERTIFICATE, "Bad certificate", CRYPT_ERROR_INVALID },
{ SSL_ALERT_UNSUPPORTED_CERTIFICATE, "Unsupported certificate", CRYPT_ERROR_INVALID },
{ SSL_ALERT_CERTIFICATE_REVOKED, "Certificate revoked", CRYPT_ERROR_INVALID },
{ SSL_ALERT_CERTIFICATE_EXPIRED, "Certificate expired", CRYPT_ERROR_INVALID },
{ SSL_ALERT_CERTIFICATE_UNKNOWN, "Certificate unknown", CRYPT_ERROR_INVALID },
{ SSL_ALERT_ILLEGAL_PARAMETER, "Illegal parameter", CRYPT_ERROR_FAILED },
{ TLS_ALERT_UNKNOWN_CA, "Unknown CA", CRYPT_ERROR_INVALID },
{ TLS_ALERT_ACCESS_DENIED, "Access denied", CRYPT_ERROR_PERMISSION },
{ TLS_ALERT_DECODE_ERROR, "Decode error", CRYPT_ERROR_FAILED },
{ TLS_ALERT_DECRYPT_ERROR, "Decrypt error", CRYPT_ERROR_WRONGKEY },
{ TLS_ALERT_EXPORT_RESTRICTION, "Export restriction", CRYPT_ERROR_FAILED },
{ TLS_ALERT_PROTOCOL_VERSION, "Protocol version", CRYPT_ERROR_NOTAVAIL },
{ TLS_ALERT_INSUFFICIENT_SECURITY, "Insufficient security", CRYPT_ERROR_NOSECURE },
{ TLS_ALERT_INTERNAL_ERROR, "Internal error", CRYPT_ERROR_FAILED },
{ TLS_ALERT_USER_CANCELLED, "User cancelled", CRYPT_ERROR_FAILED },
{ TLS_ALERT_NO_RENEGOTIATION, "No renegotiation", CRYPT_ERROR_FAILED },
{ TLS_ALERT_UNSUPPORTED_EXTENSION, "Unsupported extension", CRYPT_ERROR_NOTAVAIL },
{ TLS_ALERT_CERTIFICATE_UNOBTAINABLE, "Certificate unobtainable", CRYPT_ERROR_NOTFOUND },
{ TLS_ALERT_UNRECOGNIZED_NAME, "Unrecognized name", CRYPT_ERROR_FAILED },
{ TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE, "Bad certificate status response", CRYPT_ERROR_FAILED },
{ TLS_ALERT_BAD_CERTIFICATE_HASH_VALUE, "Bad certificate hash value", CRYPT_ERROR_FAILED },
{ TLS_ALERT_UNKNOWN_PSK_IDENTITY, "Unknown PSK identity", CRYPT_ERROR_NOTFOUND },
{ CRYPT_ERROR, NULL }
};
STREAM stream;
BYTE buffer[ 256 + 8 ];
int length, type, i, status;
/* Process the alert packet header */
sMemConnect( &stream, header, headerLength );
status = length = checkPacketHeader( sessionInfoPtr, &stream,
SSL_MSG_ALERT, ALERTINFO_SIZE );
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( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid alert message" );
/* Read and process the alert packet */
status = sread( &sessionInfoPtr->stream, buffer, length );
if( cryptStatusError( status ) )
{
sNetGetErrorInfo( &sessionInfoPtr->stream,
sessionInfoPtr->errorMessage,
&sessionInfoPtr->errorCode );
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( sessionInfoPtr, CRYPT_ERROR_TIMEOUT,
"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 */
sMemConnect( &stream, buffer, length );
status = unwrapPacketSSL( sessionInfoPtr, &stream, SSL_MSG_ALERT );
sMemDisconnect( &stream );
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( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid alert message level %d", buffer[ 0 ] );
sessionInfoPtr->errorCode = type = buffer[ 1 ];
for( i = 0; alertInfo[ i ].type != CRYPT_ERROR && \
alertInfo[ i ].type != type; i++ );
if( alertInfo[ i ].type == CRYPT_ERROR )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Unknown alert message type %d at alert level %d",
type, buffer[ 0 ] );
strcpy( sessionInfoPtr->errorMessage,
( sessionInfoPtr->version == SSL_MINOR_VERSION_SSL ) ? \
"Received SSL alert message: " : \
"Received TLS alert message: " );
strcat( sessionInfoPtr->errorMessage, alertInfo[ i ].message );
return( alertInfo[ i ].cryptlibError );
}
/* Send a close alert, with appropriate protection if necessary */
static void sendAlert( SESSION_INFO *sessionInfoPtr,
const int alertLevel, const int alertType,
const BOOLEAN alertReceived )
{
STREAM stream;
int length, status = CRYPT_OK;
/* 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 */
openPacketStreamSSL( &stream, sessionInfoPtr, CRYPT_USE_DEFAULT,
SSL_MSG_ALERT );
sputc( &stream, alertLevel );
sputc( &stream, alertType );
if( sessionInfoPtr->flags & SESSION_ISSECURE_WRITE )
{
status = wrapPacketSSL( sessionInfoPtr, &stream, 0 );
assert( status != CRYPT_ERROR_PERMISSION );
}
else
completePacketStreamSSL( &stream, 0 );
length = stell( &stream );
sMemDisconnect( &stream );
/* Send the alert */
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 */
readPacketSSL( sessionInfoPtr, NULL, 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 + -