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

📄 ssh.c

📁 提供了很多种加密算法和CA认证及相关服务如CMP、OCSP等的开发
💻 C
📖 第 1 页 / 共 5 页
字号:
		case SSH_CIPHER_BLOWFISH:
			if( algoAvailable( CRYPT_ALGO_BLOWFISH ) )
				return( CRYPT_ALGO_BLOWFISH );
			break;
		}

	return( CRYPT_ALGO_NONE );
	}

static long getAlgorithmMask( void )
	{
	long value = CRYPT_ALGO_DES;

	if( algoAvailable( CRYPT_ALGO_BLOWFISH ) )
		value |= 1 << SSH_CIPHER_BLOWFISH;
	if( algoAvailable( CRYPT_ALGO_IDEA ) )
		value |= 1 << SSH_CIPHER_IDEA;
	if( algoAvailable( CRYPT_ALGO_RC4 ) )
		value |= 1 << SSH_CIPHER_RC4;

	return( value );
	}

/* Generate an SSH session ID */

static void generateSessionID( SSH_HANDSHAKE_INFO *handshakeInfo )
	{
	HASHFUNCTION hashFunction;
	BYTE hashInfo[ MAX_HASHINFO_SIZE ];
	int hashSize;

	/* Get the hash algorithm information and hash the server key modulus, 
	   host key modulus, and cookie.  The SSH documentation and source code
	   are quite confusing on this issue, giving the key components to be 
	   hashed multiple names (server key, host key, session key, public key, 
	   etc etc).  The correct order is:
		hash( host modulus || server modulus || cookie ) */
	getHashParameters( CRYPT_ALGO_MD5, &hashFunction, &hashSize );
	hashFunction( hashInfo, NULL, handshakeInfo->hostModulus, 
				  handshakeInfo->hostModulusLength, HASH_START );
	hashFunction( hashInfo, NULL, handshakeInfo->serverModulus, 
				  handshakeInfo->serverModulusLength, HASH_CONTINUE );
	hashFunction( hashInfo, handshakeInfo->sessionID,
				  handshakeInfo->cookie, SSH_COOKIE_SIZE, HASH_END );
	handshakeInfo->sessionIDlength = SSH_SESSIONID_SIZE;
	}

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

static void generateChallengeResponse( BYTE *response, 
									   const SSH_HANDSHAKE_INFO *handshakeInfo,
									   const BYTE *challenge )
	{
	HASHFUNCTION hashFunction;
	BYTE hashInfo[ MAX_HASHINFO_SIZE ];
	int hashSize;

	/* Hash the session ID and challenge:
		hash( sessionID || challenge ) */
	getHashParameters( CRYPT_ALGO_MD5, &hashFunction, &hashSize );
	hashFunction( hashInfo, NULL, ( BYTE * ) handshakeInfo->sessionID, 
				  handshakeInfo->sessionIDlength, HASH_START );
	hashFunction( hashInfo, response, ( BYTE * ) challenge,
				  SSH_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 poke 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( const void *data, 
								 SSH_HANDSHAKE_INFO *handshakeInfo, 
								 const BOOLEAN isServerKey )
	{
	BYTE *dataPtr = ( BYTE * ) data;
	int eLength, nLength;

	eLength = mgetBWord( dataPtr );
	dataPtr += bitsToBytes( eLength );
	nLength = mgetBWord( dataPtr );
	nLength = bitsToBytes( nLength );
	if( isServerKey )
		{
		memcpy( handshakeInfo->serverModulus, dataPtr, nLength );
		handshakeInfo->serverModulusLength = nLength;
		}
	else
		{
		memcpy( handshakeInfo->hostModulus, dataPtr, nLength );
		handshakeInfo->hostModulusLength = nLength;
		}
	
	return( MPI_LENGTH_SIZE + bitsToBytes( eLength ) + \
			MPI_LENGTH_SIZE + nLength );
	}

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

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

	/* Create the security contexts required for the session */
	status = initSecurityContexts( 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 = SSH_SECRET_SIZE;
	else
		krnlSendMessage( sessionInfoPtr->iCryptInContext,
						 RESOURCE_IMESSAGE_GETATTRIBUTE, &keySize,
						 CRYPT_CTXINFO_KEYSIZE );
	if( krnlSendMessage( sessionInfoPtr->iCryptInContext,
						 RESOURCE_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 */
	setResourceData( &msgData, ( sessionInfoPtr->cryptAlgo == CRYPT_ALGO_RC4 ) ? \
					 handshakeInfo->secretValue + 16 : handshakeInfo->secretValue,
					 keySize );
	status = krnlSendMessage( sessionInfoPtr->iCryptOutContext, 
							  RESOURCE_IMESSAGE_SETATTRIBUTE_S, 
							  &msgData, CRYPT_CTXINFO_KEY );
	if( cryptStatusOK( status ) )
		{
		setResourceData( &msgData, handshakeInfo->secretValue, keySize );
		status = krnlSendMessage( sessionInfoPtr->iCryptInContext, 
								  RESOURCE_IMESSAGE_SETATTRIBUTE_S, 
								  &msgData, CRYPT_CTXINFO_KEY );
		}
	if( cryptStatusOK( status ) && ivSize )
		{
		static const char iv[ CRYPT_MAX_IVSIZE ] = { 0 };

		setResourceData( &msgData, ( void * ) iv, ivSize );
		krnlSendMessage( sessionInfoPtr->iCryptOutContext, 
						 RESOURCE_IMESSAGE_SETATTRIBUTE_S, 
						 &msgData, CRYPT_CTXINFO_IV );
		setResourceData( &msgData, ( void * ) iv, ivSize );
		krnlSendMessage( sessionInfoPtr->iCryptInContext, 
						 RESOURCE_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 )
		{
		setResourceData( &msgData, 
				handshakeInfo->secretValue + ( SSH_SECRET_SIZE - 16 ), 16 );
		status = krnlSendMessage( sessionInfoPtr->iAuthInContext, 
								  RESOURCE_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;

	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 */
	if( sessionInfoPtr->cryptAlgo == CRYPT_ALGO_BLOWFISH )
		longReverse( ( LONG * ) buffer, length );
	status = krnlSendMessage( sessionInfoPtr->iCryptInContext,
							  RESOURCE_IMESSAGE_CTX_DECRYPT, 
							  buffer, length );
	if( sessionInfoPtr->cryptAlgo == CRYPT_ALGO_BLOWFISH )
		longReverse( ( LONG * ) buffer, length );
	return( status );
	}

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

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

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

	/* Server is disconnecting, find out why */
	length = mgetBLong( bufPtr );
	if( length > MAX_ERRMSG_SIZE - 30 )
		return( CRYPT_ERROR_OVERFLOW );
	strcpy( sessionInfoPtr->errorMessage, "Received SSH server message: " );
	memcpy( sessionInfoPtr->errorMessage + 29, bufPtr, length );
	sessionInfoPtr->errorMessage[ 29 + length ] = '\0';

	return( CRYPT_ERROR_READ );
	}

static int readPacket1( SESSION_INFO *sessionInfoPtr, int expectedType )
	{
	long length;
	int padLength, packetType;

	/* Alongside the expected packets the server can send us all sorts of nop
	   messages, ranging from explicit nops (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
		{
		BYTE *bufPtr = sessionInfoPtr->receiveBuffer;
		int status;

		/* Read the SSHv1 packet header:
			uint32		length
			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 */
		status = sread( &sessionInfoPtr->stream, 
						sessionInfoPtr->receiveBuffer, LENGTH_SIZE );
		if( cryptStatusError( status ) )
			{
			sNetGetErrorInfo( &sessionInfoPtr->stream, 
							  sessionInfoPtr->errorMessage,
							  &sessionInfoPtr->errorCode );
			return( status );
			}
		length = mgetBLong( bufPtr );
		if( length < SSH_HEADER_SIZE || \
			length > sessionInfoPtr->receiveBufSize - 8 )
			return( CRYPT_ERROR_BADDATA );
		padLength = 8 - ( length & 7 );
		status = sread( &sessionInfoPtr->stream, 
						sessionInfoPtr->receiveBuffer, padLength + length );
		if( cryptStatusError( status ) )
			{
			sNetGetErrorInfo( &sessionInfoPtr->stream, 
							  sessionInfoPtr->errorMessage,
							  &sessionInfoPtr->errorCode );
			return( status );
			}
		if( sessionInfoPtr->flags & SESSION_ISSECURE )
			{
			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 */
			return( ( expectedType == SSH_SMSG_SUCCESS ) ? 
					CRYPT_ERROR_WRONGKEY : CRYPT_ERROR_BADDATA );
		packetType = sessionInfoPtr->receiveBuffer[ padLength ];
		}
	while( packetType == SSH_MSG_IGNORE || packetType == SSH_MSG_DEBUG );

	/* Make sure we either got what we asked for or one of the allowed 
	   special-case packets */
	if( packetType == SSH_MSG_DISCONNECT )
		return( getDisconnectInfo1( sessionInfoPtr, 
					sessionInfoPtr->receiveBuffer + padLength + ID_SIZE ) );
	if( expectedType == SSH_MSG_SPECIAL_USEROPT )
		{
		/* Sending an SSH_CMSG_USER can result in an SSH_SMSG_FAILURE if the 
		   user needs some form of authentiction to log on, so we have to 
		   filter this and convert it into a TRUE/FALSE value to let the 
		   caller know whether they have to send a password or not */
		if( packetType == SSH_SMSG_SUCCESS )
			return( FALSE );
		if( packetType == SSH_SMSG_FAILURE )
			return( TRUE );
		}
	if( expectedType == SSH_MSG_SPECIAL_PWOPT )
		{
		/* If we're reading a response to a password then getting a failure
		   response is valid (even if it's not what we're expecting) since
		   it's an indication that an incorrect password was used rather than
		   that there was some general type of failure */
		if( packetType == SSH_SMSG_FAILURE )
			return( CRYPT_ERROR_WRONGKEY );
		expectedType = SSH_SMSG_SUCCESS;
		}
	if( expectedType == SSH_MSG_SPECIAL_RSAOPT )
		{
		/* If we're reading a response to an RSA key ID then getting a 
		   failure response is valid (even if it's not what we're expecting) 
		   since it's an indication that an incorrect key was used rather 
		   than that there was some general type of failure */
		if( packetType == SSH_SMSG_FAILURE )
			return( CRYPT_ERROR_WRONGKEY );
		expectedType = SSH_SMSG_AUTH_RSA_CHALLENGE;
		}
	if( expectedType == SSH_MSG_SPECIAL_ANY )
		{

⌨️ 快捷键说明

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