⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ssl_cli.c

📁 cryptlib安全工具包
💻 C
📖 第 1 页 / 共 2 页
字号:
/****************************************************************************
*																			*
*					cryptlib SSL v3/TLS Client Management					*
*					   Copyright Peter Gutmann 1998-2008					*
*																			*
****************************************************************************/

#if defined( INC_ALL )
  #include "crypt.h"
  #include "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 (in fact it won't even load such a
   key), 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 )
	{
	typedef struct {
		const CRYPT_ALGO_TYPE cryptAlgo;
		const int cipherSuite;
		const BOOLEAN isPSK;
		} CIPHERSUITE_INFO;
	const static CIPHERSUITE_INFO cipherSuiteList[] = {
		{ CRYPT_ALGO_3DES, TLS_PSK_WITH_3DES_EDE_CBC_SHA, TRUE },
		{ CRYPT_ALGO_AES, TLS_PSK_WITH_AES_256_CBC_SHA, TRUE },
		{ CRYPT_ALGO_AES, TLS_PSK_WITH_AES_128_CBC_SHA, TRUE },
		{ CRYPT_ALGO_RC4, TLS_PSK_WITH_RC4_128_SHA, TRUE },
#ifdef PREFER_DH_SUITES
		{ CRYPT_ALGO_3DES, TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, FALSE },
		{ CRYPT_ALGO_AES, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, FALSE },
		{ CRYPT_ALGO_AES, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, FALSE },
		{ CRYPT_ALGO_3DES, SSL_RSA_WITH_3DES_EDE_CBC_SHA, FALSE },
		{ CRYPT_ALGO_AES, TLS_RSA_WITH_AES_256_CBC_SHA, FALSE },
		{ CRYPT_ALGO_AES, TLS_RSA_WITH_AES_128_CBC_SHA, FALSE },
#else
		{ CRYPT_ALGO_3DES, SSL_RSA_WITH_3DES_EDE_CBC_SHA, FALSE },
		{ CRYPT_ALGO_AES, TLS_RSA_WITH_AES_256_CBC_SHA, FALSE },
		{ CRYPT_ALGO_AES, TLS_RSA_WITH_AES_128_CBC_SHA, FALSE },
		{ CRYPT_ALGO_3DES, TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, FALSE },
		{ CRYPT_ALGO_AES, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, FALSE },
		{ CRYPT_ALGO_AES, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, FALSE },
#endif /* PREFER_DH_SUITES */
		{ CRYPT_ALGO_IDEA, SSL_RSA_WITH_IDEA_CBC_SHA, FALSE },
		{ CRYPT_ALGO_RC4, SSL_RSA_WITH_RC4_128_SHA, FALSE },
		{ CRYPT_ALGO_RC4, SSL_RSA_WITH_RC4_128_MD5, FALSE },
		{ CRYPT_ALGO_DES, SSL_RSA_WITH_DES_CBC_SHA, FALSE },
		{ CRYPT_ALGO_DES, TLS_DHE_RSA_WITH_DES_CBC_SHA, FALSE },
		{ CRYPT_ALGO_SHA1, SSL_RSA_EXPORT_WITH_RC4_40_MD5, FALSE },	/* Canary */
		{ CRYPT_ALGO_NONE, SSL_NULL_WITH_NULL, FALSE },
		{ CRYPT_ALGO_NONE, SSL_NULL_WITH_NULL, FALSE }
		};
	int availableSuites[ 32 + 8 ], cipherSuiteCount = 0, suiteIndex = 0;
	int 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 && \
		   suiteIndex < FAILSAFE_ARRAYSIZE( cipherSuiteList, CIPHERSUITE_INFO ) )
		{
		const CRYPT_ALGO_TYPE cryptAlgo = \
								cipherSuiteList[ suiteIndex ].cryptAlgo;

		if( !usePSK && cipherSuiteList[ suiteIndex ].isPSK )
			{
			/* 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 < FAILSAFE_ARRAYSIZE( cipherSuiteList, \
													 CIPHERSUITE_INFO ) )
				suiteIndex++;
			ENSURES( suiteIndex < FAILSAFE_ARRAYSIZE( cipherSuiteList, \
													  CIPHERSUITE_INFO ) );
			continue;
			}
		while( cipherSuiteList[ suiteIndex ].cryptAlgo == cryptAlgo && \
			   cipherSuiteCount < 32 && \
			   suiteIndex < FAILSAFE_ARRAYSIZE( cipherSuiteList, CIPHERSUITE_INFO ) )
			{
			availableSuites[ cipherSuiteCount++ ] = \
						cipherSuiteList[ suiteIndex++ ].cipherSuite;
			}
		if( suiteIndex >= FAILSAFE_ARRAYSIZE( cipherSuiteList, CIPHERSUITE_INFO ) )
			retIntError();
		}
	if( suiteIndex >= FAILSAFE_ARRAYSIZE( cipherSuiteList, CIPHERSUITE_INFO ) )
		retIntError();
	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 (according to SecuritySpace, the 
   majority of all web site certs are invalid for one reason or another) 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 )
	{
	MESSAGE_DATA msgData;
	char hostName[ MAX_URL_SIZE + 8 ];
	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( status,
				( status, SESSION_ERRINFO, 
				  "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( CRYPT_ERROR_BADDATA,
						( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 
						  "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( CRYPT_ERROR_BADDATA,
					( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 
					  "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( CRYPT_ERROR_BADDATA,
				( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 
				  "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( CRYPT_ERROR_BADDATA,
				( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 
				  "Server name doesn't match name in server certificate" ) );
		}
	if( strCompare( hostName + splatPos + 1,
					sessionInfoPtr->serverName + serverNameLength - postSplatLen,
					postSplatLen ) )
		{
		retExt( CRYPT_ERROR_BADDATA,
				( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 
				  "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 = \
				findSessionInfo( sessionInfoPtr->attributeList,
								 CRYPT_SESSINFO_USERNAME );
#endif /* 0 */
	MESSAGE_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 ] */
	status = openPacketStreamSSL( stream, sessionInfoPtr, CRYPT_USE_DEFAULT,
								  SSL_MSG_HANDSHAKE );
	if( cryptStatusError( status ) )
		return( status );
	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,
						  findSessionInfo( sessionInfoPtr->attributeList,
										   CRYPT_SESSINFO_USERNAME ) ? \
						  TRUE : FALSE );
	sputc( stream, 1 );		/* No compression */
	sputc( stream, 0 );
#if 0	/* TLS extension test code.  Since few clients/servers 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 */
	status = completeHSPacketStream( stream, packetOffset );
	if( cryptStatusOK( status ) )
		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 */
	status = dualMacDataWrite( handshakeInfo, stream );
	sMemDisconnect( stream );
	if( cryptStatusError( status ) )
		return( status );

	/* Process the server hello.  The server usually sends us a session ID,
	   indicated by a return status of OK_SPECIAL, but we don't do anything
	   further with it since we won't be resuming this session */
	status = readHSPacketSSL( sessionInfoPtr, handshakeInfo, &length,
							  SSL_MSG_FIRST_HANDSHAKE );
	if( cryptStatusError( status ) )
		return( status );
	sMemConnect( stream, sessionInfoPtr->receiveBuffer, length );
	status = processHelloSSL( sessionInfoPtr, handshakeInfo, stream, FALSE );
	if( cryptStatusError( status ) && status != OK_SPECIAL )
		{
		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, length, status;

	/* Process the optional server supplemental data:

		byte		ID = SSL_HAND_SUPPLEMENTAL_DATA
		uint24		len
		uint16		type
		uint16		len
		byte[]		value */
	status = refreshHSStream( sessionInfoPtr, handshakeInfo );
	if( cryptStatusError( status ) )
		return( status );
	if( sPeek( stream ) == SSL_HAND_SUPPLEMENTAL_DATA )
		{
		int type;

		status = checkHSPacketHeader( sessionInfoPtr, stream, &length,
									  SSL_HAND_SUPPLEMENTAL_DATA, 
									  UINT16_SIZE + UINT16_SIZE + 1 );
		if( cryptStatusError( status ) )
			{
			sMemDisconnect( stream );
			return( status );
			}
		type = readUint16( stream );
		if( cryptStatusError( type ) )
			{
			sMemDisconnect( stream );
			retExt( CRYPT_ERROR_BADDATA,
					( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 
					  "Invalid supplemental data type %04X", type ) );
			}
		length = readUint16( stream );
		if( cryptStatusError( length ) || \
			( length > 0 && cryptStatusError( sSkip( stream, length ) ) ) )
			{
			sMemDisconnect( stream );
			retExt( CRYPT_ERROR_BADDATA,
					( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -