📄 ssl_cli.c
字号:
/****************************************************************************
* *
* cryptlib SSL v3/TLS Client Management *
* Copyright Peter Gutmann 1998-2003 *
* *
****************************************************************************/
#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL )
#include "crypt.h"
#include "session.h"
#include "ssl.h"
#elif defined( INC_CHILD )
#include "../crypt.h"
#include "../session/session.h"
#include "../session/ssl.h"
#else
#include "crypt.h"
#include "session/session.h"
#include "session/ssl.h"
#endif /* Compiler-specific includes */
#ifdef USE_SSL
/****************************************************************************
* *
* Utility Functions *
* *
****************************************************************************/
/* Most of the SSL packets have fixed formats, so we can construct them by
copying in a constant template and setting up the variable fields. The
following templates are for various packet types */
#define SERVERHELLODONE_TEMPLATE_SIZE 4
#define NOCERTALERT_TEMPLATE_SIZE 7
#define NOCERT_TEMPLATE_SIZE 7
static const FAR_BSS BYTE serverHelloDoneTemplate[] = {
SSL_HAND_SERVER_HELLODONE, /* ID */
0, 0, 0 /* Length */
};
static const FAR_BSS BYTE noCertAlertSSLTemplate[] = {
SSL_MSG_ALERT, /* ID */
SSL_MAJOR_VERSION, SSL_MINOR_VERSION_SSL,/* Version */
0, 2, /* Length */
SSL_ALERTLEVEL_WARNING, SSL_ALERT_NO_CERTIFICATE
};
static const FAR_BSS BYTE noCertTLSTemplate[] = {
SSL_HAND_CERTIFICATE, /* ID */
0, 0, 3, /* Length */
0, 0, 0 /* Cert list length */
};
/****************************************************************************
* *
* Client-side Connect Functions *
* *
****************************************************************************/
/* Perform the initial part of the handshake with the server */
int beginClientHandshake( SESSION_INFO *sessionInfoPtr,
SSL_HANDSHAKE_INFO *handshakeInfo )
{
RESOURCE_DATA msgData;
BYTE *bufPtr, *bufMarkPtr, *lengthPtr;
BOOLEAN resumedSession = FALSE;
int length, sessionIDlength, cipherSuite, status;
/* Build the client hello packet:
byte ID = 1
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 ]
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 */
setMessageData( &msgData, handshakeInfo->clientNonce, SSL_NONCE_SIZE );
krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_GETATTRIBUTE_S,
&msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE );
bufPtr = sessionInfoPtr->sendBuffer + sessionInfoPtr->sendBufStartOfs;
*bufPtr++ = SSL_HAND_CLIENT_HELLO;
*bufPtr++ = 0;
lengthPtr = bufPtr; /* Low 16 bits of length */
bufPtr += LENGTH_SIZE - 1;
*bufPtr++ = SSL_MAJOR_VERSION;
*bufPtr++ = handshakeInfo->clientOfferedVersion = \
sessionInfoPtr->version;
memcpy( bufPtr, handshakeInfo->clientNonce, SSL_NONCE_SIZE );
bufPtr += SSL_NONCE_SIZE;
if( sessionInfoPtr->userNameLength > 0 )
{
/* 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 */
*bufPtr++ = SESSIONID_SIZE;
memset( bufPtr, 0, SESSIONID_SIZE );
memcpy( bufPtr, sessionInfoPtr->userName,
min( sessionInfoPtr->userNameLength, SESSIONID_SIZE ) );
bufPtr += SESSIONID_SIZE;
}
else
*bufPtr++ = '\0'; /* No session ID */
bufMarkPtr = bufPtr;
bufPtr += UINT16_SIZE; /* Leave room for length */
if( algoAvailable( CRYPT_ALGO_3DES ) )
{ mputWord( bufPtr, SSL_RSA_WITH_3DES_EDE_CBC_SHA ); }
if( algoAvailable( CRYPT_ALGO_AES ) )
{
mputWord( bufPtr, TLS_RSA_WITH_AES_128_CBC_SHA );
mputWord( bufPtr, TLS_RSA_WITH_AES_256_CBC_SHA );
}
if( algoAvailable( CRYPT_ALGO_IDEA ) )
{ mputWord( bufPtr, SSL_RSA_WITH_IDEA_CBC_SHA ); }
if( algoAvailable( CRYPT_ALGO_RC4 ) )
{
mputWord( bufPtr, SSL_RSA_WITH_RC4_128_SHA );
mputWord( bufPtr, SSL_RSA_WITH_RC4_128_MD5 );
}
if( algoAvailable( CRYPT_ALGO_DES ) )
{ mputWord( bufPtr, SSL_RSA_WITH_DES_CBC_SHA ); }
mputWord( bufPtr, SSL_RSA_EXPORT_WITH_RC4_40_MD5 ); /* Canary for broken servers */
mputWord( bufMarkPtr, bufPtr - ( bufMarkPtr + UINT16_SIZE ) );
*bufPtr++ = 1; /* No compression */
*bufPtr++ = 0;
#if 0 /* TLS extension test code. Since no known 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 rarely the case we leave the following disabled
by default */
mputWord( bufPtr, ID_SIZE + UINT16_SIZE + 1 );
*bufPtr++ = TLS_EXT_MAX_FRAGMENT_LENTH;
mputWord( bufPtr, 1 );
*bufPtr++ = 3;
#endif /* 0 */
length = bufPtr - \
( sessionInfoPtr->sendBuffer + sessionInfoPtr->sendBufStartOfs );
mputWord( lengthPtr, length - ( ID_SIZE + LENGTH_SIZE ) );
wrapHandshakePacket( sessionInfoPtr->sendBuffer, length,
sessionInfoPtr->version );
/* Send the client hello to the server and read back and process the
server's data (server hello, cert or key mgt. packets, and server
done). We perform the dual MAC'ing of the client hello in between the
network ops where it's effectively free */
status = swrite( &sessionInfoPtr->stream, sessionInfoPtr->sendBuffer,
sessionInfoPtr->sendBufStartOfs + length );
if( cryptStatusError( status ) )
{
sNetGetErrorInfo( &sessionInfoPtr->stream,
sessionInfoPtr->errorMessage,
&sessionInfoPtr->errorCode );
return( status );
}
dualMacData( handshakeInfo, sessionInfoPtr->sendBuffer + \
sessionInfoPtr->sendBufStartOfs, length );
status = readPacketSSL( sessionInfoPtr, handshakeInfo,
SSL_MSG_HANDSHAKE );
if( cryptStatusError( status ) )
return( status );
/* Process the server hello:
byte ID = 2
uint24 len
byte[2] version = { 0x03, 0x0n }
uint32 time | Server nonce
byte[28] nonce |
byte sessIDlen
byte sessID
uint16 suite
byte copr = 0 */
bufPtr = sessionInfoPtr->receiveBuffer;
length = checkPacketHeader( sessionInfoPtr, &bufPtr,
SSL_HAND_SERVER_HELLO,
VERSIONINFO_SIZE + SSL_NONCE_SIZE + 1 + \
UINT16_SIZE + 1, SSL_MAJOR_VERSION );
if( cryptStatusError( length ) )
return( length );
status = processVersionInfo( sessionInfoPtr, *bufPtr++ );
if( cryptStatusError( status ) )
return( status );
memcpy( handshakeInfo->serverNonce, bufPtr, SSL_NONCE_SIZE );
bufPtr += SSL_NONCE_SIZE;
sessionIDlength = *bufPtr++;
if( sessionIDlength < 0 || sessionIDlength > MAX_SESSIONID_SIZE )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid session ID length %d", sessionIDlength );
if( length != VERSIONINFO_SIZE + SSL_NONCE_SIZE + \
( 1 + sessionIDlength ) + UINT16_SIZE + 1 )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid header data length %d", length );
if( sessionIDlength == SESSIONID_SIZE )
{
BYTE sessionID[ SESSIONID_SIZE ];
/* There's a session ID present, check to make sure that it matches
the one we sent */
memset( sessionID, 0, SESSIONID_SIZE );
memcpy( sessionID, sessionInfoPtr->userName,
min( sessionInfoPtr->userNameLength, SESSIONID_SIZE ) );
if( !memcmp( bufPtr, sessionID, SESSIONID_SIZE ) )
{
/* It's a resumed session, remember the session ID */
memcpy( handshakeInfo->sessionID, sessionID, SESSIONID_SIZE );
handshakeInfo->sessionIDlength = SESSIONID_SIZE;
resumedSession = TRUE;
/* Create the master secret from the user-supplied password */
status = createSharedMasterSecret( handshakeInfo->premasterSecret,
sessionInfoPtr );
if( cryptStatusError( status ) )
retExt( sessionInfoPtr, status,
"Couldn't create SSL master secret from shared "
"secret/password value" );
}
}
bufPtr += sessionIDlength;
cipherSuite = mgetWord( bufPtr );
if( cipherSuite == SSL_RSA_EXPORT_WITH_RC4_40_MD5 )
/* If we got back our method-of-last-resort cipher suite, the server
is incapable of handling non-crippled crypto. Veni, vidi, volo in
domum redire */
retExt( sessionInfoPtr, CRYPT_ERROR_NOSECURE,
"Server rejected attempt to connect using non-crippled "
"encryption" );
status = initCiphersuiteInfo( sessionInfoPtr, handshakeInfo,
cipherSuite );
if( cryptStatusError( status ) )
return( status );
if( *bufPtr++ )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid compression algorithm suite %02X",
bufPtr[ -1 ] );
return( resumedSession ? OK_SPECIAL : CRYPT_OK );
}
/* Exchange keys with the server */
int exchangeClientKeys( SESSION_INFO *sessionInfoPtr,
SSL_HANDSHAKE_INFO *handshakeInfo )
{
MECHANISM_WRAP_INFO mechanismInfo;
MESSAGE_CREATEOBJECT_INFO createInfo;
RESOURCE_DATA msgData;
BYTE certFingerprint[ CRYPT_MAX_HASHSIZE ];
BYTE *bufPtr = sessionInfoPtr->receiveBuffer + \
sessionInfoPtr->receiveBufPos, *lengthPtr;
BOOLEAN needClientCert = FALSE;
int length, chainLength, algorithm, status;
/* Process the server cert chain:
byte ID = 0x0B
uint24 len
uint24 certLen | 1...n certs ordered
byte[] cert | leaf -> root */
if( sessionInfoPtr->receiveBufPos >= sessionInfoPtr->receiveBufEnd )
{
status = readPacketSSL( sessionInfoPtr, handshakeInfo,
SSL_MSG_HANDSHAKE );
if( cryptStatusError( status ) )
return( status );
bufPtr = sessionInfoPtr->receiveBuffer;
}
length = checkPacketHeader( sessionInfoPtr, &bufPtr,
SSL_HAND_CERTIFICATE, 64, 0 );
if( cryptStatusError( length ) )
return( length );
chainLength = mgetWord( bufPtr );
if( chainLength < 64 || chainLength != length - LENGTH_SIZE )
retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
"Invalid server cert chain length %d", chainLength );
/* Import the cert chain and get information on it. This isn't a true
cert chain (in the sense of being degenerate PKCS #7 SignedData) but
a special-case SSL-encoded cert chain */
setMessageCreateObjectIndirectInfo( &createInfo, bufPtr, chainLength,
CRYPT_ICERTTYPE_SSL_CERTCHAIN );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
IMESSAGE_DEV_CREATEOBJECT_INDIRECT, &createInfo,
OBJECT_TYPE_CERTIFICATE );
if( cryptStatusError( status ) )
{
/* There are sufficient numbers of broken certs around that if we
run into a problem importing one we provide a custom error
message telling the user to try again with a reduced compliance
level */
if( status == CRYPT_ERROR_BADDATA || status == CRYPT_ERROR_INVALID )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -