📄 ssl.c
字号:
/* Add the length and type at the start */
*dataPtr++ = SSL_MSG_HANDSHAKE;
*dataPtr++ = SSL_MAJOR_VERSION;
*dataPtr++ = protocolVersion;
mputWord( dataPtr, length );
}
/* Send a close alert, with appropriate protection if necessary */
static void sendCloseAlert( SESSION_INFO *sessionInfoPtr,
const BOOLEAN alertReceived )
{
int status;
/* Make sure that we only send a single close 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;
/* Send a close alert to tell the other side that we're going away */
if( !( sessionInfoPtr->flags & SESSION_ISSECURE ) )
status = swrite( &sessionInfoPtr->stream,
closeAlertTemplate[ sessionInfoPtr->version ],
CLOSEALERT_TEMPLATE_SIZE );
else
{
BYTE buffer[ 256 ];
buffer[ sessionInfoPtr->sendBufStartOfs ] = SSL_ALERTLEVEL_WARNING;
buffer[ sessionInfoPtr->sendBufStartOfs + 1 ] = SSL_ALERT_CLOSE_NOTIFY;
status = wrapData( sessionInfoPtr, buffer, 2, SSL_MSG_ALERT );
if( !cryptStatusError( status ) )
status = swrite( &sessionInfoPtr->stream, buffer,
sessionInfoPtr->sendBufStartOfs + status );
else
/* 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 */
if( status != CRYPT_ERROR_PERMISSION )
assert( NOTREACHED );
}
if( cryptStatusError( status ) || alertReceived )
return;
/* Close the send side of the connection if it's a cryptlib-internal
socket and (try and) read the response from the other side. The
former is needed by some implementations that want to see a FIN
before they react to a shutdown notification, the latter to clear the
line in case it's a persistent connection. If it's a user-managed
socket, we can't perform the partial close since this would affect the
state of the socket as seen by the user, since the need to see the FIN
is fairly rare we choose this as the less problematic of the two
options */
if( sessionInfoPtr->networkSocket == CRYPT_ERROR )
sioctl( &sessionInfoPtr->stream, STREAM_IOCTL_CLOSESENDCHANNEL, NULL, 0 );
readPacketSSL( sessionInfoPtr, NULL, SSL_MSG_ALERT );
}
/* Send a handshake failure alert. This doesn't need any protection since
it's always sent during the handshake phase */
static void sendHandshakeFailAlert( SESSION_INFO *sessionInfoPtr )
{
/* Make sure that we only send a single alert. Normally we send a close
alert automatically on shutdown, but we may have already sent one
earlier as part of an error-handler */
if( sessionInfoPtr->protocolFlags & SSL_PFLAG_ALERTSENT )
return;
sessionInfoPtr->protocolFlags |= SSL_PFLAG_ALERTSENT;
/* Send the appropriate handshake failure alert */
swrite( &sessionInfoPtr->stream,
handshakeFailAlertTemplate[ sessionInfoPtr->version ],
HANDSHAKEFAILALERT_TEMPLATE_SIZE );
}
/* 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 */
static int processAlert( SESSION_INFO *sessionInfoPtr, const int length )
{
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 },
{ CRYPT_ERROR, NULL }
};
BYTE buffer[ 256 ];
int type, i, status;
assert( length > 0 && length < 256 ); /* Range already checked by caller */
/* Get the alert packet and tell the other side that we're going away */
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 close 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, got %d of %d bytes",
status, length );
}
sessionInfoPtr->receiveBufEnd = length;
if( sessionInfoPtr->flags & SESSION_ISSECURE && \
( 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
unencrypted to the encrypted 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 = unwrapData( sessionInfoPtr, buffer, length, SSL_MSG_ALERT );
if( cryptStatusError( status ) )
{
sessionInfoPtr->flags |= SESSION_SENDCLOSED;
return( status );
}
}
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.
Others still send their alert and 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 being sent */
if( buffer[ 0 ] != SSL_ALERTLEVEL_WARNING && \
buffer[ 0 ] != SSL_ALERTLEVEL_FATAL )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid SSL alert level 0x%02X", 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", type );
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 );
}
/* Read an SSL packet. The readPacketSSL() portion is only used during the
handshake phase (the data transfer phase has its own read/write code) so
we can perform some special-case handling based on this */
static int readPacketHeader( SESSION_INFO *sessionInfoPtr, BOOLEAN *isFatal )
{
BYTE *bufPtr = sessionInfoPtr->receiveBuffer + \
sessionInfoPtr->receiveBufEnd;
int status;
/* Read the SSL packet header data */
status = readFixedHeader( sessionInfoPtr,
sessionInfoPtr->receiveBufStartOfs );
if( status <= 0 )
return( status );
assert( status == sessionInfoPtr->receiveBufStartOfs );
/* Check for an SSL alert message */
if( bufPtr[ 0 ] == SSL_MSG_ALERT )
{
int length, ch;
if( isFatal != NULL )
*isFatal = TRUE;
bufPtr += ID_SIZE;
if( *bufPtr++ != SSL_MAJOR_VERSION )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid SSL major version number 0x%02X in alert "
"message", bufPtr[ -1 ] );
ch = *bufPtr++;
if( ch < SSL_MINOR_VERSION_SSL || ch > SSL_MINOR_VERSION_TLS11 )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid SSL minor version number 0x%02X in alert "
"message", ch );
length = mgetWord( bufPtr );
if( sessionInfoPtr->flags & SESSION_ISSECURE )
{
if( length < ALERTINFO_SIZE || length > 128 )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid encrypted alert info size %d", length );
/* If we're using explicit IVs, the first block constitutes the
IV. Load it into the context */
if( sessionInfoPtr->protocolFlags & SSL_PFLAG_EXPLICITIV )
{
RESOURCE_DATA msgData;
setMessageData( &msgData, bufPtr,
sessionInfoPtr->cryptBlocksize );
krnlSendMessage( sessionInfoPtr->iCryptInContext,
IMESSAGE_SETATTRIBUTE_S, &msgData,
CRYPT_CTXINFO_IV );
length -= sessionInfoPtr->cryptBlocksize;
}
}
else
if( length != ALERTINFO_SIZE )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid alert info size %d, should be %d",
length, ALERTINFO_SIZE );
return( processAlert( sessionInfoPtr, length ) );
}
return( status );
}
int readPacketSSL( SESSION_INFO *sessionInfoPtr,
SSL_HANDSHAKE_INFO *handshakeInfo, const int packetType )
{
BYTE *bufPtr = sessionInfoPtr->receiveBuffer + \
sessionInfoPtr->receiveBufEnd;
BOOLEAN isV2handshake = FALSE;
int totalLength, effectiveTotalLength, type, version, status;
/* Read and process the header. We don't have to check for status == 0
(meaning no data was read) at this point since all reads during the
handshake phase are blocking reads */
status = readPacketHeader( sessionInfoPtr, NULL );
if( cryptStatusError( status ) )
return( status );
/* Decode the SSL packet header:
SSLv3/TLS SSLv2
byte type uint16 length code = { 0x80, len }
byte[2] vers = { 0x03, 0x0n } byte type = 1
uint16 length byte[2] vers = { 0x03, 0x0n }
[ byte[] iv - TLS 1.1 ]
If the expected packet type is SSL_MSG_SPECIAL_HANDSHAKE the actual
type can be either an SSLv2 or SSLv3/TLS handshake, so we have to
check for either type being present */
type = *bufPtr++;
if( packetType == SSL_MSG_SPECIAL_HANDSHAKE )
{
if( type == SSL_MSG_V2HANDSHAKE )
{
/* It's an SSLv2 handshake from Netscape, handle it specially */
isV2handshake = TRUE;
totalLength = *bufPtr++;
if( handshakeInfo != NULL )
/* 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 */
dualMacData( handshakeInfo, bufPtr, 3 );
if( *bufPtr++ != SSL_HAND_CLIENT_HELLO )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Unknown SSLv2 hello message type %d, should be %d",
bufPtr[ -1 ], SSL_HAND_CLIENT_HELLO );
totalLength -= ID_SIZE + VERSIONINFO_SIZE;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -