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

📄 ssh1.c

📁 cryptlib安全工具包
💻 C
📖 第 1 页 / 共 5 页
字号:

static int processKeyFingerprint( SESSION_INFO *sessionInfoPtr,
								  const void *n, const int nLength,
								  const void *e, int eLength )
	{
	HASHFUNCTION hashFunction;
	HASHINFO hashInfo;
	const ATTRIBUTE_LIST *attributeListPtr = \
				findSessionInfo( sessionInfoPtr->attributeList,
								 CRYPT_SESSINFO_SERVER_FINGERPRINT );
	BYTE fingerPrint[ CRYPT_MAX_HASHSIZE + 8 ];
	int hashSize;

	getHashParameters( CRYPT_ALGO_MD5, &hashFunction, &hashSize );
	hashFunction( hashInfo, NULL, n, nLength, HASH_START );
	hashFunction( hashInfo, fingerPrint, e, eLength, HASH_END );
	if( attributeListPtr == NULL )
		/* Remember the value for the caller */
		return( addSessionInfo( &sessionInfoPtr->attributeList,
								CRYPT_SESSINFO_SERVER_FINGERPRINT,
								fingerPrint, hashSize ) );

	/* There's an existing fingerprint value, make sure that it matches what
	   we just calculated */
	if( attributeListPtr->valueLength != hashSize || \
		memcmp( attributeListPtr->value, fingerPrint, hashSize ) )
		retExt( sessionInfoPtr, CRYPT_ERROR_WRONGKEY,
				"Server key fingerprint doesn't match requested "
				"fingerprint" );
	return( CRYPT_OK );
	}

/* Generate a response to an RSA authentication challenge */

static void generateChallengeResponse( BYTE *response,
									   const SSH_HANDSHAKE_INFO *handshakeInfo,
									   const BYTE *challenge )
	{
	HASHFUNCTION hashFunction;
	HASHINFO hashInfo;

	/* Hash the session ID and challenge:
		hash( sessionID || challenge ) */
	getHashParameters( CRYPT_ALGO_MD5, &hashFunction, NULL );
	hashFunction( hashInfo, NULL, ( BYTE * ) handshakeInfo->sessionID,
				  handshakeInfo->sessionIDlength, HASH_START );
	hashFunction( hashInfo, response, ( BYTE * ) challenge,
				  SSH1_CHALLENGE_SIZE, HASH_END );
	}

/* Process the public key data.  The preceding key length value isn't useful
   because it contains the nominal key size in bits rather than the size of
   the following data, so we have to dig into the data to find out how much
   there is.  In addition we need to take a copy of the key modulus since
   it's needed later for calculating the session ID */

static int processPublickeyData( SSH_HANDSHAKE_INFO *handshakeInfo,
								 const void *data, const int dataLength,
								 const BOOLEAN isServerKey,
								 SESSION_INFO *sessionInfoPtr )
	{
	BYTE *dataPtr = ( BYTE * ) data, *ePtr;
	int nominalLength, eLength, nLength;

	nominalLength = ( int ) mgetLong( dataPtr );
	nominalLength = bitsToBytes( nominalLength );
	if( nominalLength < bitsToBytes( MIN_PKCSIZE_BITS ) || \
		nominalLength > CRYPT_MAX_PKCSIZE )
		retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
				"Invalid public key size %d", nominalLength );
	eLength = mgetWord( dataPtr );
	eLength = bitsToBytes( eLength );
	if( LENGTH_SIZE + SSH1_MPI_LENGTH_SIZE + eLength + \
					  SSH1_MPI_LENGTH_SIZE + nominalLength > dataLength )
		retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
				"Invalid exponent size %d for key size %d", eLength,
				nominalLength );
	ePtr = dataPtr;
	dataPtr += eLength;
	nLength = mgetWord( dataPtr );
	nLength = bitsToBytes( nLength );
	if( nLength != nominalLength )
		retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
				"Public key size %d doesn't match modulus size %d",
				nominalLength, nLength );
	if( isServerKey )
		{
		memcpy( handshakeInfo->serverModulus, dataPtr, nLength );
		handshakeInfo->serverModulusLength = nLength;
		}
	else
		{
		memcpy( handshakeInfo->hostModulus, dataPtr, nLength );
		handshakeInfo->hostModulusLength = nLength;
		}
	if( sessionInfoPtr != NULL )
		{
		int status;

		status = processKeyFingerprint( sessionInfoPtr, dataPtr, nLength,
										ePtr, eLength );
		if( cryptStatusError( status ) )
			return( status );
		}

	return( LENGTH_SIZE + SSH1_MPI_LENGTH_SIZE + eLength + \
						  SSH1_MPI_LENGTH_SIZE + nLength );
	}

/* Set up the security information required for the session */

static int initSecurityInfoSSH1( SESSION_INFO *sessionInfoPtr,
								 SSH_HANDSHAKE_INFO *handshakeInfo )
	{
	MESSAGE_DATA msgData;
	int keySize, ivSize, status;

	/* Create the security contexts required for the session */
	status = initSecurityContextsSSH( sessionInfoPtr );
	if( cryptStatusError( status ) )
		return( status );
	if( sessionInfoPtr->cryptAlgo == CRYPT_ALGO_BLOWFISH )
		/* For Blowfish the session key size doesn't match the default
		   Blowfish key size so we explicitly specify its length */
		keySize = SSH1_SECRET_SIZE;
	else
		krnlSendMessage( sessionInfoPtr->iCryptInContext,
						 IMESSAGE_GETATTRIBUTE, &keySize,
						 CRYPT_CTXINFO_KEYSIZE );
	if( krnlSendMessage( sessionInfoPtr->iCryptInContext,
						 IMESSAGE_GETATTRIBUTE, &ivSize,
						 CRYPT_CTXINFO_IVSIZE ) == CRYPT_ERROR_NOTAVAIL )
		/* It's a stream cipher */
		ivSize = 0;

	/* Load the keys.  For RC4, which is IV-less, the session key is split
	   into two parts, with the first part being the receive key and the
	   second part being the send key.  For other algorithms, the entire
	   session key is used for both send and receive contexts, leading to
	   a simple attack on the first data block since the initial IV is all
	   zeroes */
	setMessageData( &msgData, ( sessionInfoPtr->cryptAlgo == CRYPT_ALGO_RC4 ) ? \
					handshakeInfo->secretValue + 16 : handshakeInfo->secretValue,
					keySize );
	status = krnlSendMessage( sessionInfoPtr->iCryptOutContext,
							  IMESSAGE_SETATTRIBUTE_S, &msgData,
							  CRYPT_CTXINFO_KEY );
	if( cryptStatusOK( status ) )
		{
		setMessageData( &msgData, handshakeInfo->secretValue, keySize );
		status = krnlSendMessage( sessionInfoPtr->iCryptInContext,
								  IMESSAGE_SETATTRIBUTE_S, &msgData,
								  CRYPT_CTXINFO_KEY );
		}
	if( cryptStatusOK( status ) && ivSize > 0 )
		{
		static const char iv[ CRYPT_MAX_IVSIZE ] = { 0 };

		setMessageData( &msgData, ( void * ) iv, ivSize );
		krnlSendMessage( sessionInfoPtr->iCryptOutContext,
						 IMESSAGE_SETATTRIBUTE_S, &msgData, CRYPT_CTXINFO_IV );
		setMessageData( &msgData, ( void * ) iv, ivSize );
		krnlSendMessage( sessionInfoPtr->iCryptInContext,
						 IMESSAGE_SETATTRIBUTE_S, &msgData, CRYPT_CTXINFO_IV );
		}
	if( cryptStatusError( status ) )
		return( status );

	/* If we're talking to a cryptlib peer, set up the MAC context which is
	   used instead of a CRC32.  The key we use for this is taken from the
	   end of the SSH secret data, which isn't used for any cipher except
	   Blowfish */
	if( sessionInfoPtr->flags & SESSION_ISCRYPTLIB )
		{
		setMessageData( &msgData,
				handshakeInfo->secretValue + ( SSH1_SECRET_SIZE - 16 ), 16 );
		status = krnlSendMessage( sessionInfoPtr->iAuthInContext,
								  IMESSAGE_SETATTRIBUTE_S, &msgData,
								  CRYPT_CTXINFO_KEY );
		if( cryptStatusError( status ) )
			return( status );
		}

	/* We've set up the security info, from now on all data is encrypted */
	sessionInfoPtr->flags |= SESSION_ISSECURE_READ | SESSION_ISSECURE_WRITE;

	return( CRYPT_OK );
	}

/* Read an SSH packet */

static int decryptPayload( SESSION_INFO *sessionInfoPtr, BYTE *buffer,
						   const int length )
	{
	int status;

	/* Decrypt the payload, with handling for SSH's Blowfish endianness bug.
	   This may not be a true bug but more a problem in the spec, since the
	   original was rather vague about the endianness of the byte -> long
	   conversion */
	if( sessionInfoPtr->cryptAlgo == CRYPT_ALGO_BLOWFISH )
		longReverse( ( unsigned long * ) buffer, length );
	status = krnlSendMessage( sessionInfoPtr->iCryptInContext,
							  IMESSAGE_CTX_DECRYPT, buffer, length );
	if( sessionInfoPtr->cryptAlgo == CRYPT_ALGO_BLOWFISH )
		longReverse( ( unsigned long * ) buffer, length );
	return( status );
	}

static BOOLEAN checksumPayload( SESSION_INFO *sessionInfoPtr,
								const BYTE *buffer, const int length )
	{
	const int dataLength = length - SSH1_CRC_SIZE;	/* CRC isn't part of payload */
	BYTE *bufPtr = ( BYTE * ) buffer + dataLength;
	unsigned long crc32, storedCrc32;

	/* Calculate the checksum over the padding, type, and data and make sure
	   that it matches the transmitted value */
	if( ( sessionInfoPtr->flags & ( SESSION_ISCRYPTLIB | SESSION_ISSECURE_READ ) ) == \
								  ( SESSION_ISCRYPTLIB | SESSION_ISSECURE_READ ) )
		crc32 = calculateTruncatedMAC( sessionInfoPtr->iAuthInContext,
									   buffer, dataLength );
	else
		crc32 = calculateCRC( buffer, dataLength );
	storedCrc32 = mgetLong( bufPtr );
	return( ( crc32 == storedCrc32 ) ? TRUE : FALSE );
	}

static int getDisconnectInfoSSH1( SESSION_INFO *sessionInfoPtr, BYTE *bufPtr )
	{
	int length;

	/* Server is disconnecting, find out why */
	length = mgetLong( bufPtr );
	if( length > MAX_ERRMSG_SIZE - 32 )
		retExt( sessionInfoPtr, CRYPT_ERROR_OVERFLOW,
				"Invalid error information size %d", length );
	strlcpy_s( sessionInfoPtr->errorMessage, MAX_ERRMSG_SIZE,
			   "Received SSHv1 server message: " );
	memcpy( sessionInfoPtr->errorMessage + 31, bufPtr, length );
	sessionInfoPtr->errorMessage[ 31 + length ] = '\0';

	return( CRYPT_ERROR_READ );
	}

static int readPacketSSH1( SESSION_INFO *sessionInfoPtr, int expectedType )
	{
	BYTE *bufPtr = sessionInfoPtr->receiveBuffer;
	long length;
	int padLength, packetType, iterationCount = 0;

	/* Alongside the expected packets the server can also send us all sorts
	   of no-op messages, ranging from explicit no-ops (SSH_MSG_IGNORE)
	   through to general chattiness (SSH_MSG_DEBUG).  Because we can
	   receive any quantity of these at any time, we have to run the receive
	   code in a loop to strip them out */
	do
		{
		const BYTE *lengthPtr = bufPtr;
		int status;

		/* Read the SSHv1 packet header:

			uint32		length (excluding padding)
			byte[]		padding
			byte		type
			byte[]		data
			uint32		crc32

		  The padding length is implicitly calculated as
		  8 - ( length & 7 ) bytes, and the CRC is calculated over the
		  padding, type, and data */
		assert( sessionInfoPtr->receiveBufEnd == 0 );
		status = readFixedHeader( sessionInfoPtr, LENGTH_SIZE );
		if( cryptStatusError( status ) )
			return( status );
		assert( status == LENGTH_SIZE );
		length = mgetLong( lengthPtr );
		padLength = 8 - ( length & 7 );
		if( length < SSH1_HEADER_SIZE || \
			length + padLength >= sessionInfoPtr->receiveBufSize )
			retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
					"Invalid packet length %d", length );
		status = sread( &sessionInfoPtr->stream,
						sessionInfoPtr->receiveBuffer, padLength + length );
		if( cryptStatusError( status ) )
			{
			sNetGetErrorInfo( &sessionInfoPtr->stream,
							  &sessionInfoPtr->errorInfo );
			return( status );
			}
		if( status != padLength + length )
			retExt( sessionInfoPtr, CRYPT_ERROR_TIMEOUT,
					"Timeout during packet remainder read, only got %d of "
					"%d bytes", status, padLength + length );
		if( sessionInfoPtr->flags & SESSION_ISSECURE_READ )
			{
			status = decryptPayload( sessionInfoPtr,
									 sessionInfoPtr->receiveBuffer,
									 padLength + length );
			if( cryptStatusError( status ) )
				return( status );
			}
		if( !checksumPayload( sessionInfoPtr, sessionInfoPtr->receiveBuffer,
							  padLength + length ) )
			/* If we're expecting a success packet after a key exchange or an
			   immediate post key-exchange packet and don't get it then it's
			   more likely that the problem is due to the wrong key being
			   used than data corruption, so we return a wrong key error
			   instead of bad data */
			retExt( sessionInfoPtr, ( expectedType == SSH1_SMSG_SUCCESS ) ? \
						CRYPT_ERROR_WRONGKEY : CRYPT_ERROR_BADDATA,
					"Bad message checksum" );
		packetType = sessionInfoPtr->receiveBuffer[ padLength ];
		}
	while( ( packetType == SSH1_MSG_IGNORE || \
			 packetType == SSH1_MSG_DEBUG ) && iterationCount++ < 1000 );
	if( iterationCount >= 1000 )
		retExt( sessionInfoPtr, CRYPT_ERROR_OVERFLOW,
				"Peer sent excessive number of no-op packets" );
	length -= ID_SIZE + UINT_SIZE;	/* Remove fixed fields */

	/* Make sure we either got what we asked for or one of the allowed
	   special-case packets */
	if( packetType == SSH1_MSG_DISCONNECT )
		return( getDisconnectInfoSSH1( sessionInfoPtr,
					sessionInfoPtr->receiveBuffer + padLength + ID_SIZE ) );
	if( expectedType == SSH1_MSG_SPECIAL_USEROPT )
		{
		/* Sending an SSH1_CMSG_USER can result in an SSH1_SMSG_FAILURE if the
		   user needs some form of authentiction to log on, so we have to
		   filter this and convert it into a CRYPT_OK/OK_SPECIAL value to
		   let the caller know whether they have to send a password or not */
		if( packetType == SSH1_SMSG_SUCCESS )
			return( CRYPT_OK );
		if( packetType == SSH1_SMSG_FAILURE )
			return( OK_SPECIAL );

⌨️ 快捷键说明

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