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

📄 ssl_rw.c

📁 cryptlib安全工具包
💻 C
📖 第 1 页 / 共 3 页
字号:
	STREAM lengthStream;
	const int payloadLength = stell( stream ) - \
							  ( offset + sessionInfoPtr->sendBufStartOfs );
	const int bufMaxLen = payloadLength + sMemDataLeft( stream );
	BYTE lengthBuffer[ UINT16_SIZE + 8 ];
	BYTE *dataPtr, *headerPtr;
	int length, status;

	assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
	assert( sessionInfoPtr->flags & SESSION_ISSECURE_WRITE );
	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( sStatusOK( stream ) );
	assert( offset >= 0 && \
			offset <= stell( stream ) - \
					  ( payloadLength + sessionInfoPtr->sendBufStartOfs ) );
	assert( payloadLength >= 0 && payloadLength <= MAX_PACKET_SIZE && \
			payloadLength < sessionInfoPtr->sendBufSize - \
							( sessionInfoPtr->sendBufStartOfs + sslInfo->ivSize ) );

	/* Sanity-check the state */
	if( offset < 0 || \
		offset > stell( stream ) - \
				 ( payloadLength + sessionInfoPtr->sendBufStartOfs ) || \
		payloadLength < 0 || payloadLength > MAX_PACKET_SIZE || \
		payloadLength >= sessionInfoPtr->sendBufSize - \
						 ( sessionInfoPtr->sendBufStartOfs + sslInfo->ivSize ) )
		retIntError();

	/* Get pointers into the data stream for the crypto processing */
	status = sMemGetDataBlockAbs( stream, offset, ( void ** ) &headerPtr, 
								  bufMaxLen );
	if( cryptStatusError( status ) )
		return( status );
	dataPtr = headerPtr + SSL_HEADER_SIZE + sslInfo->ivSize;
	assert( *headerPtr >= SSL_MSG_FIRST && *headerPtr <= SSL_MSG_LAST );

	/* MAC the payload */
	if( sessionInfoPtr->version == SSL_MINOR_VERSION_SSL )
		status = createMacSSL( sessionInfoPtr, dataPtr, bufMaxLen, &length, 
							   payloadLength, *headerPtr );
	else
		status = createMacTLS( sessionInfoPtr, dataPtr, bufMaxLen, &length,
							   payloadLength, *headerPtr );
	if( cryptStatusError( status ) )
		return( status );

	/* If it's TLS 1.1 or newer and we're using a block cipher, adjust for 
	   the explicit IV that precedes the data.  We know that the resulting
	   values are within bounds because dataPtr = headerPtr + hdr + IV */
	if( sslInfo->ivSize > 0 )
		{
		assert( sessionInfoPtr->sendBufStartOfs >= \
				SSL_HEADER_SIZE + sslInfo->ivSize ); 

		dataPtr -= sslInfo->ivSize;
		assert( dataPtr > headerPtr );
		length += sslInfo->ivSize;
		if( length > bufMaxLen )
			retIntError();
		}

	/* Pad and encrypt the payload */
	status = encryptData( sessionInfoPtr, dataPtr, bufMaxLen, &length, 
						  length );
	if( cryptStatusError( status ) )
		return( status );

	/* Insert the final packet payload length into the packet header.  We 
	   directly copy the data in because the stream may have been opened in 
	   read-only mode if we're using it to write pre-assembled packet data 
	   that's been passed in by the caller */
	sMemOpen( &lengthStream, lengthBuffer, UINT16_SIZE );
	status = writeUint16( &lengthStream, length );
	sMemDisconnect( &lengthStream );
	memcpy( headerPtr + ID_SIZE + VERSIONINFO_SIZE, lengthBuffer, 
			UINT16_SIZE );
	if( cryptStatusError( status ) )
		return( status );

	/* Sync the stream info to match the new payload size */
	return( sSkip( stream, length - ( sslInfo->ivSize + payloadLength ) ) );
	}

/* Wrap up and send an SSL packet */

int sendPacketSSL( SESSION_INFO *sessionInfoPtr, STREAM *stream,
				   const BOOLEAN sendOnly )
	{
	const int length = stell( stream );
	void *dataPtr;
	int status;

	assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( sStatusOK( stream ) );
	assert( stell( stream ) >= SSL_HEADER_SIZE );

	/* Safety check to make sure that the stream is OK */
	if( !sStatusOK( stream ) )
		{
		assert( DEBUG_WARN );
		return( sGetStatus( stream ) );
		}

	/* Update the length field at the start of the packet if necessary */
	if( !sendOnly )
		{
		status = completePacketStreamSSL( stream, 0 );
		if( cryptStatusError( status ) )
			return( status );
		}

	/* Send the packet to the peer */
	status = sMemGetDataBlockAbs( stream, 0, &dataPtr, length );
	if( cryptStatusOK( status ) )
		status = swrite( &sessionInfoPtr->stream, dataPtr, length );
	if( cryptStatusError( status ) )
		{
		sNetGetErrorInfo( &sessionInfoPtr->stream,
						  &sessionInfoPtr->errorInfo );
		return( status );
		}
	return( CRYPT_OK );	/* swrite() returns a byte count */
	}

/****************************************************************************
*																			*
*							Send/Receive SSL Alerts							*
*																			*
****************************************************************************/

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

int processAlert( SESSION_INFO *sessionInfoPtr, const void *header, 
				  const int headerLength )
	{
	typedef struct {
		const int type;
		const char *message;
		const int messageLength;
		const int cryptlibError;
		} ALERT_INFO;
	const static ALERT_INFO alertInfo[] = {
		{ SSL_ALERT_CLOSE_NOTIFY, "Close notify", 12, CRYPT_ERROR_COMPLETE },
		{ SSL_ALERT_UNEXPECTED_MESSAGE, "Unexpected message", 18, CRYPT_ERROR_FAILED },
		{ SSL_ALERT_BAD_RECORD_MAC, "Bad record MAC", 14, CRYPT_ERROR_SIGNATURE },
		{ TLS_ALERT_DECRYPTION_FAILED, "Decryption failed", 17, CRYPT_ERROR_WRONGKEY },
		{ TLS_ALERT_RECORD_OVERFLOW, "Record overflow", 15, CRYPT_ERROR_OVERFLOW },
		{ SSL_ALERT_DECOMPRESSION_FAILURE, "Decompression failure", 21, CRYPT_ERROR_FAILED },
		{ SSL_ALERT_HANDSHAKE_FAILURE, "Handshake failure", 17, CRYPT_ERROR_FAILED },
		{ SSL_ALERT_NO_CERTIFICATE, "No certificate", 14, CRYPT_ERROR_PERMISSION },
		{ SSL_ALERT_BAD_CERTIFICATE, "Bad certificate", 15, CRYPT_ERROR_INVALID },
		{ SSL_ALERT_UNSUPPORTED_CERTIFICATE, "Unsupported certificate", 23, CRYPT_ERROR_INVALID },
		{ SSL_ALERT_CERTIFICATE_REVOKED, "Certificate revoked", 19, CRYPT_ERROR_INVALID },
		{ SSL_ALERT_CERTIFICATE_EXPIRED, "Certificate expired", 19, CRYPT_ERROR_INVALID },
		{ SSL_ALERT_CERTIFICATE_UNKNOWN, "Certificate unknown", 19, CRYPT_ERROR_INVALID },
		{ SSL_ALERT_ILLEGAL_PARAMETER, "Illegal parameter", 17, CRYPT_ERROR_FAILED },
		{ TLS_ALERT_UNKNOWN_CA, "Unknown CA", 10, CRYPT_ERROR_INVALID },
		{ TLS_ALERT_ACCESS_DENIED, "Access denied", 13, CRYPT_ERROR_PERMISSION },
		{ TLS_ALERT_DECODE_ERROR, "Decode error", 12, CRYPT_ERROR_FAILED },
		{ TLS_ALERT_DECRYPT_ERROR, "Decrypt error", 13, CRYPT_ERROR_WRONGKEY },
		{ TLS_ALERT_EXPORT_RESTRICTION, "Export restriction", 18, CRYPT_ERROR_FAILED },
		{ TLS_ALERT_PROTOCOL_VERSION, "Protocol version", 16, CRYPT_ERROR_NOTAVAIL },
		{ TLS_ALERT_INSUFFICIENT_SECURITY, "Insufficient security", 21, CRYPT_ERROR_NOSECURE },
		{ TLS_ALERT_INTERNAL_ERROR, "Internal error", 14, CRYPT_ERROR_FAILED },
		{ TLS_ALERT_USER_CANCELLED, "User cancelled", 14, CRYPT_ERROR_FAILED },
		{ TLS_ALERT_NO_RENEGOTIATION, "No renegotiation", 16, CRYPT_ERROR_FAILED },
		{ TLS_ALERT_UNSUPPORTED_EXTENSION, "Unsupported extension", 21, CRYPT_ERROR_NOTAVAIL },
		{ TLS_ALERT_CERTIFICATE_UNOBTAINABLE, "Certificate unobtainable", 24, CRYPT_ERROR_NOTFOUND },
		{ TLS_ALERT_UNRECOGNIZED_NAME, "Unrecognized name", 17, CRYPT_ERROR_FAILED },
		{ TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE, "Bad certificate status response", 31, CRYPT_ERROR_FAILED },
		{ TLS_ALERT_BAD_CERTIFICATE_HASH_VALUE, "Bad certificate hash value", 26, CRYPT_ERROR_FAILED },
		{ TLS_ALERT_UNKNOWN_PSK_IDENTITY, "Unknown PSK identity", 20, CRYPT_ERROR_NOTFOUND },
 		{ CRYPT_ERROR, NULL }, { CRYPT_ERROR, NULL }
		};
	ERROR_INFO *errorInfo = &sessionInfoPtr->errorInfo;
	STREAM stream;
	BYTE buffer[ 256 + 8 ];
	int length, type, i, status;

	assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
	assert( isReadPtr( header, headerLength ) );

	/* Process the alert packet header */
	sMemConnect( &stream, header, headerLength );
	status = checkPacketHeader( sessionInfoPtr, &stream, &length, 
								SSL_MSG_ALERT, ALERTINFO_SIZE,
								sessionInfoPtr->receiveBufSize );
	if( cryptStatusError( status ) )
		{
		sMemDisconnect( &stream );
		return( status );
		}
	if( sessionInfoPtr->flags & SESSION_ISSECURE_READ )
		{
		if( length < ALERTINFO_SIZE || length > 256 )
			status = CRYPT_ERROR_BADDATA;
		}
	else
		{
		if( length != ALERTINFO_SIZE )
			status = CRYPT_ERROR_BADDATA;
		}
	sMemDisconnect( &stream );
	if( cryptStatusError( status ) )
		{
		retExt( CRYPT_ERROR_BADDATA,
				( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 
				  "Invalid alert message length %d", length ) );
		}

	/* Read and process the alert packet */
	status = sread( &sessionInfoPtr->stream, buffer, length );
	if( cryptStatusError( status ) )
		{
		sNetGetErrorInfo( &sessionInfoPtr->stream,
						  &sessionInfoPtr->errorInfo );
		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 shut down 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( CRYPT_ERROR_TIMEOUT, 
				( CRYPT_ERROR_TIMEOUT, SESSION_ERRINFO, 
				  "Timed out reading alert message, only got %d of %d "
				  "bytes", status, length ) );
		}
	sessionInfoPtr->receiveBufEnd = length;
	if( ( sessionInfoPtr->flags & SESSION_ISSECURE_READ ) && \
		( 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 non-
		   secure to the secure 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 = unwrapPacketSSL( sessionInfoPtr, buffer, length, &length, 
								  SSL_MSG_ALERT );
		if( cryptStatusError( status ) )
			{
			sendCloseAlert( sessionInfoPtr, TRUE );
			sessionInfoPtr->flags |= SESSION_SENDCLOSED;
			return( status );
			}
		}

	/* Tell the other side that we're going away */
	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.  
	   Other implementations still send their alert but 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 having been sent */
	if( buffer[ 0 ] != SSL_ALERTLEVEL_WARNING && \
		buffer[ 0 ] != SSL_ALERTLEVEL_FATAL )
		{
		retExt( CRYPT_ERROR_BADDATA,
				( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 
				  "Invalid alert message level %d", buffer[ 0 ] ) );
		}
	errorInfo->errorCode = type = buffer[ 1 ];
	for( i = 0; alertInfo[ i ].type != CRYPT_ERROR && \
				alertInfo[ i ].type != type && \
				i < FAILSAFE_ARRAYSIZE( alertInfo, ALERT_INFO ); i++ );
	if( i >= FAILSAFE_ARRAYSIZE( alertInfo, ALERT_INFO ) )
		retIntError();
	if( alertInfo[ i ].type == CRYPT_ERROR )
		{
		retExt( CRYPT_ERROR_BADDATA,
				( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 
				  "Unknown alert message type %d at alert level %d", 
				  type, buffer[ 0 ] ) );
		}
	retExtStr( alertInfo[ i ].cryptlibError,
			   ( alertInfo[ i ].cryptlibError, SESSION_ERRINFO, 
				 alertInfo[ i ].message, alertInfo[ i ].messageLength,
				 ( sessionInfoPtr->version == SSL_MINOR_VERSION_SSL ) ? \
					"Received SSL alert message: " : \
					"Received TLS alert message: " ) );
	}

/* Send a close alert, with appropriate protection if necessary */

STDC_NONNULL_ARG( ( 1 ) ) \
static void sendAlert( INOUT SESSION_INFO *sessionInfoPtr, 
					   const int alertLevel, const int alertType,
					   const BOOLEAN alertReceived )
	{
	STREAM stream;
	int length = DUMMY_INIT, status;

	assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
	assert( alertLevel == SSL_ALERTLEVEL_WARNING || \
			alertLevel == SSL_ALERTLEVEL_FATAL );
	assert( alertType >= SSL_ALERT_FIRST && \
			alertType <= SSL_ALERT_LAST );

	/* Make sure that we only send a single 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;

	/* Create the alert.  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 */
	status = openPacketStreamSSL( &stream, sessionInfoPtr, 
								  CRYPT_USE_DEFAULT, SSL_MSG_ALERT );
	if( cryptStatusOK( status ) )
		{
		sputc( &stream, alertLevel );
		sputc( &stream, alertType );
		if( sessionInfoPtr->flags & SESSION_ISSECURE_WRITE )
			{
			status = wrapPacketSSL( sessionInfoPtr, &stream, 0 );
			assert( cryptStatusOK( status ) || \
					status == CRYPT_ERROR_PERMISSION );
			}
		else
			{
			status = completePacketStreamSSL( &stream, 0 );
			}
		if( cryptStatusOK( status ) )
			length = stell( &stream );
		sMemDisconnect( &stream );
		}
	/* Fall through with status passed on to the following code */

	/* Send the alert.  Note that we don't exit on an error status in the
	   previous operation (for the reasons given in the comment earlier) 
	   since we can at least perform a clean shutdown even if the creation
	   of the close alert fails */
	if( cryptStatusOK( status ) )
		status = sendCloseNotification( sessionInfoPtr, 
										sessionInfoPtr->sendBuffer, length );
	else
		status = sendCloseNotification( sessionInfoPtr, NULL, 0 );
	if( cryptStatusError( status ) || alertReceived )
		return;

	/* Read back the other side's close alert acknowledgement.  Again, since 
	   we're closing down the session anyway there's not much that we can do 
	   in response to an error */
	( void ) readHSPacketSSL( sessionInfoPtr, NULL, &length, 
							  SSL_MSG_ALERT );
	}

void sendCloseAlert( SESSION_INFO *sessionInfoPtr, 
					 const BOOLEAN alertReceived )
	{
	sendAlert( sessionInfoPtr, SSL_ALERTLEVEL_WARNING, 
			   SSL_ALERT_CLOSE_NOTIFY, alertReceived );
	}

void sendHandshakeFailAlert( SESSION_INFO *sessionInfoPtr )
	{
	/* We set the alertReceived flag to true when sending a handshake
	   failure alert to avoid waiting to get back an ack, since this 
	   alert type isn't acknowledged by the other side */
	sendAlert( sessionInfoPtr, SSL_ALERTLEVEL_FATAL, 
			   SSL_ALERT_HANDSHAKE_FAILURE, TRUE );
	}
#endif /* USE_SSL */

⌨️ 快捷键说明

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