📄 ssl_cli.c
字号:
/****************************************************************************
* *
* cryptlib SSL v3/TLS Client Management *
* Copyright Peter Gutmann 1998-2004 *
* *
****************************************************************************/
#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL )
#include "crypt.h"
#include "misc_rw.h"
#include "session.h"
#include "ssl.h"
#elif defined( INC_CHILD )
#include "../crypt.h"
#include "../misc/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
/****************************************************************************
* *
* Utility Functions *
* *
****************************************************************************/
/* Encode a list of available algorithms. Some buggy older versions of IIS
that only support crippled crypto drop the connection when they see a
client hello advertising strong crypto, rather than sending an alert as
they should. To work around this, we advertise a dummy cipher suite
SSL_RSA_EXPORT_WITH_RC4_40_MD5 as a canary to force IIS to send back a
response that we can then turn into an error message. The need to do
this is somewhat unfortunate since it will appear to an observer that
cryptlib will use crippled crypto, but there's no other way to detect the
buggy IIS apart from completely restarting the session activation at the
session level with crippled-crypto advertised in the restarted session */
static int writeCipherSuiteList( STREAM *stream, const BOOLEAN usePSK )
{
const static struct {
const CRYPT_ALGO_TYPE cryptAlgo;
const int cipherSuite;
} cipherSuiteList[] = {
{ CRYPT_ALGO_3DES, TLS_PSK_WITH_3DES_EDE_CBC_SHA },
{ CRYPT_ALGO_AES, TLS_PSK_WITH_AES_256_CBC_SHA },
{ CRYPT_ALGO_AES, TLS_PSK_WITH_AES_128_CBC_SHA },
{ CRYPT_ALGO_RC4, TLS_PSK_WITH_RC4_128_SHA },
#ifdef PREFER_DH_SUITES
{ CRYPT_ALGO_3DES, TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA },
{ CRYPT_ALGO_AES, TLS_DHE_RSA_WITH_AES_256_CBC_SHA },
{ CRYPT_ALGO_AES, TLS_DHE_RSA_WITH_AES_128_CBC_SHA },
{ CRYPT_ALGO_3DES, SSL_RSA_WITH_3DES_EDE_CBC_SHA },
{ CRYPT_ALGO_AES, TLS_RSA_WITH_AES_256_CBC_SHA },
{ CRYPT_ALGO_AES, TLS_RSA_WITH_AES_128_CBC_SHA },
#else
{ CRYPT_ALGO_3DES, SSL_RSA_WITH_3DES_EDE_CBC_SHA },
{ CRYPT_ALGO_AES, TLS_RSA_WITH_AES_256_CBC_SHA },
{ CRYPT_ALGO_AES, TLS_RSA_WITH_AES_128_CBC_SHA },
{ CRYPT_ALGO_3DES, TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA },
{ CRYPT_ALGO_AES, TLS_DHE_RSA_WITH_AES_256_CBC_SHA },
{ CRYPT_ALGO_AES, TLS_DHE_RSA_WITH_AES_128_CBC_SHA },
#endif /* PREFER_DH_SUITES */
{ CRYPT_ALGO_IDEA, SSL_RSA_WITH_IDEA_CBC_SHA },
{ CRYPT_ALGO_RC4, SSL_RSA_WITH_RC4_128_SHA },
{ CRYPT_ALGO_RC4, SSL_RSA_WITH_RC4_128_MD5 },
{ CRYPT_ALGO_DES, SSL_RSA_WITH_DES_CBC_SHA },
{ CRYPT_ALGO_DES, TLS_DHE_RSA_WITH_DES_CBC_SHA },
{ CRYPT_ALGO_SHA, SSL_RSA_EXPORT_WITH_RC4_40_MD5 }, /* Canary */
{ CRYPT_ALGO_NONE, SSL_NULL_WITH_NULL }
};
int availableSuites[ 32 ], cipherSuiteCount = 0, suiteIndex = 0, status;
/* Walk down the list of algorithms (and the corresponding cipher
suites) remembering each one that's available for use */
while( cipherSuiteList[ suiteIndex ].cryptAlgo != CRYPT_ALGO_NONE && \
cipherSuiteCount < 32 )
{
const CRYPT_ALGO_TYPE cryptAlgo = \
cipherSuiteList[ suiteIndex ].cryptAlgo;
if( !usePSK && \
cipherSuiteList[ suiteIndex ].cipherSuite >= TLS_PSK_WITH_RC4_128_SHA )
{
/* It's a PSK suite but we're not using a PSK handshake, skip
it */
suiteIndex++;
continue;
}
if( !algoAvailable( cipherSuiteList[ suiteIndex ].cryptAlgo ) )
{
while( cipherSuiteList[ suiteIndex ].cryptAlgo == cryptAlgo )
suiteIndex++;
continue;
}
while( cipherSuiteList[ suiteIndex ].cryptAlgo == cryptAlgo && \
cipherSuiteCount < 32 )
availableSuites[ cipherSuiteCount++ ] = \
cipherSuiteList[ suiteIndex++ ].cipherSuite;
}
assert( cipherSuiteCount < 32 );
/* Encode the list of available cipher suites */
status = writeUint16( stream, cipherSuiteCount * UINT16_SIZE );
for( suiteIndex = 0; \
cryptStatusOK( status ) && suiteIndex < cipherSuiteCount; \
suiteIndex++ )
status = writeUint16( stream, availableSuites[ suiteIndex ] );
return( status );
}
/* Make sure that the server URL matches the value in the returned
certificate. This code isn't currently called because it's not certain
what the best way is to report this to the user is, and more importantly
because there are quite a few servers out there where the server name
doesn't match what's in the cert but for which the user will just click
"OK" anyway even if we can tunnel a warning indication back to them, so
we leave it to the caller to perform whatever checking and take whatever
action they consider necessary */
#if 0
static int checkURL( SESSION_INFO *sessionInfoPtr )
{
RESOURCE_DATA msgData;
char hostName[ MAX_URL_SIZE ];
const int serverNameLength = strlen( sessionInfoPtr->serverName );
int hostNameLength, splatPos = CRYPT_ERROR, postSplatLen, i, status;
/* Read the server name specification from the server's cert */
setMessageData( &msgData, hostName, MAX_URL_SIZE );
status = krnlSendMessage( sessionInfoPtr->iKeyexCryptContext,
IMESSAGE_GETATTRIBUTE_S, &msgData,
CRYPT_CERTINFO_DNSNAME );
if( cryptStatusError( status ) )
status = krnlSendMessage( sessionInfoPtr->iKeyexCryptContext,
IMESSAGE_GETATTRIBUTE_S, &msgData,
CRYPT_CERTINFO_COMMONNAME );
if( cryptStatusError( status ) )
retExt( sessionInfoPtr, status,
"Couldn't read server name from server certificate" );
hostNameLength = msgData.length;
/* Look for a splat in the host name spec */
for( i = 0; i < hostNameLength; i++ )
if( hostName[ i ] == '*' )
{
if( splatPos != CRYPT_ERROR )
/* Can't have more than one splat in a host name */
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Server name in certificate contains more than one "
"wildcard" );
splatPos = i;
}
/* If there's no wildcarding, perform a direct match */
if( splatPos == CRYPT_ERROR )
{
if( hostNameLength != serverNameLength || \
strCompare( hostName, sessionInfoPtr->serverName,
serverNameLength ) )
/* Host doesn't match the name in the cert */
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Server name doesn't match name in server certificate" );
return( CRYPT_OK );
}
/* Determine how much to match before and after the splat */
postSplatLen = hostNameLength - splatPos - 1;
if( postSplatLen + splatPos > serverNameLength )
/* The fixed name spec text is longer than the server name, a match
can't be possible */
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Server name doesn't match name in server certificate" );
/* Check that the pre- and post-splat URL components match */
if( splatPos > 0 && \
strCompare( hostName, sessionInfoPtr->serverName, splatPos ) )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Server name doesn't match name in server certificate" );
if( strCompare( hostName + splatPos + 1,
sessionInfoPtr->serverName + serverNameLength - postSplatLen,
postSplatLen ) )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Server name doesn't match name in server certificate" );
return( CRYPT_OK );
}
#endif /* 0 */
/****************************************************************************
* *
* Client-side Connect Functions *
* *
****************************************************************************/
/* Perform the initial part of the handshake with the server */
int beginClientHandshake( SESSION_INFO *sessionInfoPtr,
SSL_HANDSHAKE_INFO *handshakeInfo )
{
STREAM *stream = &handshakeInfo->stream;
#if 0 /* Old PSK mechanism */
const ATTRIBUTE_LIST *attributeListPtr = \
findSessionAttribute( sessionInfoPtr->attributeList,
CRYPT_SESSINFO_USERNAME );
#endif /* 0 */
RESOURCE_DATA msgData;
int packetOffset, length, status;
/* Get the nonce that's used to randomise all crypto ops */
setMessageData( &msgData, handshakeInfo->clientNonce, SSL_NONCE_SIZE );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_GETATTRIBUTE_S,
&msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE );
if( cryptStatusError( status ) )
return( status );
/* Build the client hello packet:
byte ID = SSL_HAND_CLIENT_HELLO
uint24 len
byte[2] version = { 0x03, 0x0n }
uint32 time | Client nonce
byte[28] nonce |
byte sessIDlen
byte[] sessID
uint16 suiteLen
uint16[] suite
byte coprLen = 1
byte[] copr = { 0x00 }
[ uint16 extListLen | RFC 3546
byte extType
uint16 extLen
byte[] extData ] */
openPacketStreamSSL( stream, sessionInfoPtr, CRYPT_USE_DEFAULT,
SSL_MSG_HANDSHAKE );
packetOffset = continueHSPacketStream( stream, SSL_HAND_CLIENT_HELLO );
sputc( stream, SSL_MAJOR_VERSION );
sputc( stream, sessionInfoPtr->version );
handshakeInfo->clientOfferedVersion = sessionInfoPtr->version;
swrite( stream, handshakeInfo->clientNonce, SSL_NONCE_SIZE );
#if 0 /* Old PSK mechanism */
if( attributeListPtr != NULL )
{
BYTE buffer[ SESSIONID_SIZE + 8 ];
/* If there's a user name present, we're "resuming" a session based
on a shared secret, send the user name as the session ID */
sputc( stream, SESSIONID_SIZE );
memset( buffer, 0, SESSIONID_SIZE );
memcpy( buffer, attributeListPtr->value,
min( attributeListPtr->valueLength, SESSIONID_SIZE ) );
swrite( stream, buffer, SESSIONID_SIZE );
}
else
sputc( stream, 0 ); /* No session ID */
#else
sputc( stream, 0 ); /* No session ID */
#endif /* 0 */
writeCipherSuiteList( stream,
findSessionAttribute( sessionInfoPtr->attributeList,
CRYPT_SESSINFO_USERNAME ) ? \
TRUE : FALSE );
sputc( stream, 1 ); /* No compression */
sputc( stream, 0 );
#if 0 /* TLS extension test code. Since almost no clients/servers (except
maybe some obscure bits of code embedded in cellphones) do this,
we have to fake it ourselves for testing purpose. In addition
the RFC rather optimistically expects implementations to handle
the presence of unexpected data at the end of the hello packet,
since this is often not the case (quite a few servers fail the
handshake if extension data is present) we leave the following
disabled by default */
writeUint16( stream, UINT16_SIZE + UINT16_SIZE + 1 );
writeUint16( stream, TLS_EXT_MAX_FRAGMENT_LENTH );
writeUint16( stream, 1 );
sputc( stream, 3 );
#endif /* 0 */
completeHSPacketStream( stream, packetOffset );
status = sendPacketSSL( sessionInfoPtr, stream, FALSE );
if( cryptStatusError( status ) )
{
sMemDisconnect( stream );
return( status );
}
/* Perform the dual MAC'ing of the client hello in between the network
ops where it's effectively free */
dualMacData( handshakeInfo, stream, FALSE );
sMemDisconnect( stream );
/* Process the server hello */
length = readPacketSSL( sessionInfoPtr, handshakeInfo,
SSL_MSG_FIRST_HANDSHAKE );
if( cryptStatusError( length ) )
return( length );
sMemConnect( stream, sessionInfoPtr->receiveBuffer, length );
status = processHelloSSL( sessionInfoPtr, handshakeInfo, stream, FALSE );
#if 0 /* Old PSK mechanism */
if( status == OK_SPECIAL )
{
/* It's a (pseudo-)resumed session using a pre-shared secret key,
there's no more packets to read, disconnect the stream and create
the master secret from the user-supplied password */
sMemDisconnect( stream );
status = createSharedMasterSecret( handshakeInfo->premasterSecret,
&handshakeInfo->premasterSecretSize,
sessionInfoPtr );
if( cryptStatusError( status ) )
retExt( sessionInfoPtr, status,
"Couldn't create SSL master secret from shared "
"secret/password value" );
return( OK_SPECIAL );
}
#endif /* 0 */
if( cryptStatusError( status ) )
{
sMemDisconnect( stream );
return( status );
}
return( CRYPT_OK );
}
/* Exchange keys with the server */
int exchangeClientKeys( SESSION_INFO *sessionInfoPtr,
SSL_HANDSHAKE_INFO *handshakeInfo )
{
STREAM *stream = &handshakeInfo->stream;
BOOLEAN needClientCert = FALSE;
int packetOffset, status;
/* Process the optional server cert chain:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -