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

📄 ssl.c

📁 老外写的加密库cryptlib(版本3.1)
💻 C
📖 第 1 页 / 共 5 页
字号:

	/* 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 + -