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

📄 ssl.c

📁 cryptlib是功能强大的安全工具集。允许开发人员快速在自己的软件中集成加密和认证服务。
💻 C
📖 第 1 页 / 共 4 页
字号:
				static const int fragmentTbl[] = \
						{ 0, 512, 1024, 2048, 4096, 8192, 16384 };

				/* Response: If frag-size == 3...5, send same to peer.  
				   Note that we also allow a frag-size value of 5, which 
				   isn't specified in the standard but should probably be 
				   present since it would otherwise result in a missing 
				   value between 4096 and the default of 16384:
				   
					byte		fragmentLength */
				value = sgetc( stream );
				if( cryptStatusError( value ) || \
					extLen != 1 || value < 1 || value > 5 )
					retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
							"Invalid TLS fragment length extension" );
/*				sessionInfoPtr->maxPacketSize = fragmentTbl[ value ]; */
				break;
				}

			case TLS_EXT_CLIENT_CERTIFICATE_URL:
				/* Response: Ignore.  This dangerous extension allows a 
				   client to direct a server to grope around in arbitrary 
				   external (and untrusted) URLs trying to locate certs,
				   provinding a convenient mechanism for bounce attacks
				   and all manner of similar firewall/trusted-host 
				   subversion problems:

					byte		chainType
					uint16		urlAndHashList
						uint16	urlLen
						byte[]	url
						byte	hashPresent
						byte[20] hash	- If hashPresent flag set */
				if( extLen < 1 + UINT16_SIZE + \
							 UINT16_SIZE + MIN_URL_SIZE + 1 || \
					cryptStatusError( sSkip( stream, extLen ) ) )
					retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
							"Invalid TLS client certificate URL extension" );
				break;

			case TLS_EXT_TRUSTED_CA_KEYS:
				/* Response: Ignore.  This allows a client to specify which
				   CA certs it trusts, and by extension which server certs
				   it trusts.  God knows what actual problem this is 
				   intended to solve:

					uint16		caList
						byte	idType
						... */
				if( extLen < UINT16_SIZE + 1 || \
					cryptStatusError( sSkip( stream, extLen ) ) )
					retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
							"Invalid TLS trusted CA extension" );
				break;

			case TLS_EXT_TRUNCATED_HMAC:
				/* Truncate the HMAC to a nonstandard 80 bits (rather than 
				   the de facto IPsec cargo-cult standard of 96 bits) */
				if( extLen != 0 )
					retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
							"Invalid TLS truncated HMAC extension" );
				break;

			case TLS_EXT_STATUS_REQUEST:
				/* Response: Ignore - another bounce-attack enabler, this 
				   time on both the server and an OCSP responder:

					byte	statusType
					... */
				if( extLen < 1 || \
					cryptStatusError( sSkip( stream, extLen ) ) )
					retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
							"Invalid TLS status request extension" );
				break;

			default:
				/* Default: Ignore the extension */
				if( cryptStatusError( sSkip( stream, extLen ) ) )
					retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
							"Invalid TLS extension data for extension "
							"type %d", type );
			}
		}

	return( CRYPT_OK );
	}

/* Process a session ID */

static int processSessionID( SESSION_INFO *sessionInfoPtr, 
							 SSL_HANDSHAKE_INFO *handshakeInfo, 
							 STREAM *stream )
	{
	BYTE sessionID[ SESSIONID_SIZE + 8 ];
	const int sessionIDlength = sgetc( stream );
	int status;

	/* Get the session ID info and if it's not one of ours, skip it */
	if( cryptStatusError( sessionIDlength ) || \
		sessionIDlength < 0 || sessionIDlength > MAX_SESSIONID_SIZE )
		retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA, "Invalid session ID" );
	if( sessionIDlength != SESSIONID_SIZE )
		{
		if( sessionIDlength > 0 )
			{
			status = sSkip( stream, sessionIDlength );
			if( cryptStatusError( status ) )
				retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
						"Invalid session ID" );
			}
		return( CRYPT_OK );
		}

	/* There's a session ID present, check to make sure that it matches our 
	   expectations.  If we're the server the the size is right for it to
	   (potentially) be one of ours, if we're the client we check to see 
	   whether it matches what we sent */
	status = sread( stream, sessionID, SESSIONID_SIZE );
	if( cryptStatusError( status ) )
		retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA, "Invalid session ID" );
	if( !( sessionInfoPtr->flags & SESSION_ISSERVER ) )
		{
		const ATTRIBUTE_LIST *userNamePtr;
		BYTE formattedSessionID[ SESSIONID_SIZE + 8 ];

		/* If the returned session ID matches the one that we sent,
		   it's a resumed session */
		if( ( userNamePtr = \
				findSessionAttribute( sessionInfoPtr->attributeList,
									  CRYPT_SESSINFO_USERNAME ) ) == NULL )
			/* There's no user name present, it can't be a resumed 
			   session */
			return( CRYPT_OK );
		memset( formattedSessionID, 0, SESSIONID_SIZE );
		memcpy( formattedSessionID, userNamePtr->value, 
				min( userNamePtr->valueLength, SESSIONID_SIZE ) );
		if( memcmp( formattedSessionID, sessionID, SESSIONID_SIZE ) )
			/* The user name doesn't match the returned ID, it's not a 
			   resumed session */
			return( CRYPT_OK );
		}

	/* It's a resumed session, remember the details and let the caller 
	   know */
	memcpy( handshakeInfo->sessionID, sessionID, SESSIONID_SIZE );
	handshakeInfo->sessionIDlength = SESSIONID_SIZE;
	return( OK_SPECIAL );
	}

/* Process the client/server hello:

	byte		ID = SSL_HAND_CLIENT_HELLO / SSL_HAND_SERVER_HELLO
	uint24		len
	byte[2]		version = { 0x03, 0x0n }
	uint32		time		| Client/server nonce
	byte[28]	nonce		|
	byte		sessIDlen	| May receive nonzero len +
	byte[]		sessID		|	<len> bytes data

		Client						Server
	uint16		suiteLen		-
	uint16[]	suites			uint16		suite
	byte		coprLen = 1		-
	byte		copr = 0		byte		copr = 0 */

int processHelloSSL( SESSION_INFO *sessionInfoPtr, 
					 SSL_HANDSHAKE_INFO *handshakeInfo, 
					 STREAM *stream, const BOOLEAN isServer )
	{
	BOOLEAN resumedSession = FALSE;
	int endPos, length, suiteLength = 1, i, status;

	/* Check the header and version info */
	if( isServer )
		length = checkHSPacketHeader( sessionInfoPtr, stream,
									  SSL_HAND_CLIENT_HELLO,
									  VERSIONINFO_SIZE + SSL_NONCE_SIZE + \
										1 + ( UINT16_SIZE * 2 ) + 1 + 1 );
	else
		length = checkHSPacketHeader( sessionInfoPtr, stream,
									  SSL_HAND_SERVER_HELLO,
									  VERSIONINFO_SIZE + SSL_NONCE_SIZE + \
										1 + UINT16_SIZE + 1 );
	if( cryptStatusError( length ) )
		return( length );
	endPos = stell( stream ) + length;
	status = processVersionInfo( sessionInfoPtr, stream, 
								 isServer ? \
									&handshakeInfo->clientOfferedVersion : \
									NULL );
	if( cryptStatusError( status ) )
		return( status );

	/* Process the nonce and session ID */
	sread( stream, isServer ? handshakeInfo->clientNonce : \
							  handshakeInfo->serverNonce, SSL_NONCE_SIZE );
	status = processSessionID( sessionInfoPtr, handshakeInfo, stream );
	if( status == OK_SPECIAL )
		resumedSession = TRUE;
	else
		if( cryptStatusError( status ) )
			return( status );

	/* Process the cipher suite information */
	if( isServer )
		{
		/* If we're reading the client hello, the packet contains a 
		   selection of suites preceded by a suite count */
		suiteLength = readUint16( stream );
		if( cryptStatusError( suiteLength ) || \
			suiteLength < UINT16_SIZE || ( suiteLength % UINT16_SIZE ) != 0 )
			retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
					"Invalid cipher suite information" );
		suiteLength /= UINT16_SIZE;
		}
	status = processCipherSuite( sessionInfoPtr, handshakeInfo, stream, 
								 suiteLength );
	if( cryptStatusError( status ) )
		return( status );

	/* Process the compression suite information */
	if( isServer )
		{
		/* If we're reading the client hello, the packet contains a 
		   selection of suites preceded by a suite count */
		suiteLength = sgetc( stream );
		if( cryptStatusError( suiteLength ) || suiteLength < 1 )
			retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
					"Invalid compression suite information" );
		}
	for( i = 0; i < suiteLength; i++ )
		{
		if( sgetc( stream ) != 0 )
			status = CRYPT_ERROR_BADDATA;
		}
	if( cryptStatusError( status ) )
		retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
				"Invalid compression algorithm information" );

	/* If there's extra data present at the end of the packet, check for TLS 
	   extension data */
	if( endPos - stell( stream ) > 0 )
		{
		status = processExtensions( sessionInfoPtr, stream, 
									endPos - stell( stream ) );
		if( cryptStatusError( status ) )
			return( status );
		handshakeInfo->hasExtensions = TRUE;
		}

	return( resumedSession ? OK_SPECIAL : CRYPT_OK );
	}

/****************************************************************************
*																			*
*							Certificate-handling Functions					*
*																			*
****************************************************************************/

/* Read/write an SSL cert chain:

	byte		ID = SSL_HAND_CERTIFICATE
	uint24		len
	uint24		certListLen
	uint24		certLen			| 1...n certs ordered
	byte[]		cert			|   leaf -> root */

int readSSLCertChain( SESSION_INFO *sessionInfoPtr, 
					  SSL_HANDSHAKE_INFO *handshakeInfo, STREAM *stream,
					  CRYPT_CERTIFICATE *iCertChain, 
					  const BOOLEAN isServer )
	{
	CRYPT_ALGO_TYPE algorithm;
	const ATTRIBUTE_LIST *fingerprintPtr = \
				findSessionAttribute( sessionInfoPtr->attributeList,
									  CRYPT_SESSINFO_SERVER_FINGERPRINT );
	RESOURCE_DATA msgData;
	BYTE certFingerprint[ CRYPT_MAX_HASHSIZE ];
	const char *peerTypeName = isServer ? "Client" : "Server";
	int chainLength, length, status;

	/* Clear return value */
	*iCertChain = CRYPT_ERROR;

	/* Make sure that the packet header is in order */
	length = checkHSPacketHeader( sessionInfoPtr, stream,
								  SSL_HAND_CERTIFICATE, isServer ? \
									0 : LENGTH_SIZE + MIN_CERTSIZE );
	if( cryptStatusError( length ) )
		return( length );
	if( isServer && length < LENGTH_SIZE + MIN_CERTSIZE )
		{
		/* There is a special case in which a too-short cert packet is valid
		   and that's where it constitutes the TLS equivalent of an SSL
		   no-certs alert.  SSLv3 sent an SSL_ALERT_NO_CERTIFICATE alert to 
		   indicate that the client doesn't have a cert, which is handled by 
		   the readPacketSSL() call.  TLS changed this to send an empty cert 
		   packet instead, supposedly because it lead to implementation 
		   problems (presumably it's necessary to create a state machine-
		   based implementation to reproduce these problems, whatever they 
		   are).  The TLS 1.0 spec is ambiguous as to what constitutes an 
		   empty packet, it could be either a packet with a length of zero 
		   or a packet containing a zero-length cert list so we check for 
		   both.  TLS 1.1 fixed this to say that that certListLen entry has
		   a length of zero.  To report this condition we fake the error 
		   indicators for consistency with the status obtained from an SSLv3 
		   no-cert alert */
		if( length == 0 || length == LENGTH_SIZE )
			{
			sessionInfoPtr->errorCode = SSL_ALERT_NO_CERTIFICATE;
			retExt( sessionInfoPtr, CRYPT_ERROR_PERMISSION,
					"Received TLS alert message: No certificate" );
			}
		retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA, 
				"Invalid certificate chain" );
		}
	chainLength = readUint24( stream );
	if( cryptStatusError( chainLength ) || \
		chainLength < MIN_CERTSIZE || chainLength != length - LENGTH_SIZE )
		retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
				"Invalid cert chain length %d, should be %d", 
				chainLength, length - LENGTH_SIZE );

	/* 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 */
	status = importCertFromStream( stream, iCertChain, chainLength,
								   CRYPT_ICERTTYPE_SSL_CERTCHAIN );
	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 )
			retExt( sessionInfoPtr, status, 
					"%s provided a broken/invalid certificate, try again "
					"with a reduced level of certificate compliance "
					"checking", peerTypeName );
		retExt( sessionInfoPtr, status, "Invalid certificate chain" );
		}
	status = krnlSendMessage( *iCertChain, IMESSAGE_GETATTRIBUTE, 
							  &algorithm, CRYPT_CTXINFO_ALGO );
	if( cryptStatusOK( status ) )
		{
		setMessageData( &msgData, certFingerprint, CRYPT_MAX_HASHSIZE );
		status = krnlSendMessage( *iCertChain, IMESSAGE_GETATTRIBUTE_S, 
								  &msgData, 
								  ( fingerprintPtr != NULL && \
									fingerprintPtr->valueLength == 16 ) ? \
									CRYPT_CERTINFO_FINGERPRINT_MD5 : \
									CRYPT_CERTINFO_FINGERPRINT_SHA );
		}
	if( cryptStatusError( status ) )
		{
		krnlSendNotifier( *iCertChain, IMESSAGE_DECREFCOUNT );
		return( status );
		}
	if( !isServer && algorithm != handshakeInfo->authAlgo )
		{
		krnlSendNotifier( *iCertChain, IMESSAGE_DECREFCOUNT );
		retExt( sessionInfoPtr, CRYPT_ERROR_WRONGKEY,
				"%s key algorithm %d doesn't match negotiated algorithm %d", 
				peerTypeName, algorithm, handshakeInfo->authAlgo );
		}

	/* Either compare the cert fingerprint to a supplied one or save it for 
	   the caller to examine */
	if( fingerprintPtr != NULL )
		{
		/* The caller has supplied a cert fingerprint, compare it to the
		   received cert's fingerprint to make sure that we're talking to 
		   the right system */
		if( fingerprintPtr->valueLength != msgData.length || \
			memcmp( fingerprintPtr->value, certFingerprint, msgData.length ) )
			{
			krnlSendNotifier( *iCertChain, IMESSAGE_DECREFCOUNT );
			retExt( sessionInfoPtr, CRYPT_ERROR_WRONGKEY,
					"%s key didn't match key fingerprint", peerTypeName );
			}
		}
	else
		/* Remember the cert fingerprint in case the caller wants to check 
		   it.  We don't worry if the add fails, it's a minor thing and not
		   worth aborting the handshake for */
		addSessionAttribute( &sessionInfoPtr->attributeList,
							 CRYPT_SESSINFO_SERVER_FINGERPRINT, 
							 certFingerprint, msgData.length );

	/* Make sure that we can perform the required operation using the key 
	   that we've been given.  For a client key we need signing capability,
	   for a server key using DH key agreement we also need signing 
	   capability to authenticate the DH parameters, and for a server key
	   using RSA key transport we need encryption capability.  This 
	   operation also performs a variety of additional checks alongside the 
	   obvious one, so it's a good general health check before we go any 
	   further */
	status = krnlSendMessage( *iCertChain, IMESSAGE_CHECK, NULL,
							  isServer || isKeyxAlgo( algorithm ) ? \
								MESSAGE_CHECK_PKC_SIGCHECK : \
								MESSAGE_CHECK_PKC_ENCRYPT );
	if( cryptStatusError( status ) )
		{
		krnlSendNotifier( *iCertChain, IMESSAGE_DECREFCOUNT );
		retExt( sessionInfoPtr, CRYPT_ERROR_WRONGKEY,
				"%s provided a key incapable of being used for %s",
				peerTypeName, 
				isServer ? "client authentication" : \

⌨️ 快捷键说明

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