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

📄 ssh1.c

📁 cryptlib是功能强大的安全工具集。允许开发人员快速在自己的软件中集成加密和认证服务。
💻 C
📖 第 1 页 / 共 5 页
字号:
		/* 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 );
		}
	if( expectedType == SSH1_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 == SSH1_SMSG_FAILURE )
			retExt( sessionInfoPtr, CRYPT_ERROR_WRONGKEY,
					"Server indicated incorrect password was used" );
		expectedType = SSH1_SMSG_SUCCESS;
		}
	if( expectedType == SSH1_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 == SSH1_SMSG_FAILURE )
			retExt( sessionInfoPtr, CRYPT_ERROR_WRONGKEY,
					"Server indicated incorrect RSA key was used" );
		expectedType = SSH1_SMSG_AUTH_RSA_CHALLENGE;
		}
	if( expectedType == SSH1_MSG_SPECIAL_ANY )
		{
		/* If we're expecting any kind of data, save the type at the start
		   of the buffer.  The length increment means that we move one byte 
		   of data too much down, but this isn't a big deal since the 
		   returned data length excludes it, and it's not processed anyway - 
		   this packet pseudo-type is only used to eat client or server
		   chattiness at the end of the handshake */
		*bufPtr++ = packetType;
		length += ID_SIZE;
		}
	else
		if( packetType != expectedType )
			retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
					"Invalid packet type 0x%02X, expected 0x%02X", 
					packetType, expectedType );

	/* Move the data down in the buffer to get rid of the padding.  This 
	   isn't as inefficient as it seems since it's only used for short
	   handshake messages */
	memmove( bufPtr, sessionInfoPtr->receiveBuffer + padLength + ID_SIZE, 
			 length );

	return( length );
	}

/* Send an SSHv1 packet.  SSHv1 uses an awkward variable-length padding at 
   the start, when we're sending short control packets we can pre-calculate 
   the data start position or memmove() it into place, with longer payload 
   data quantities we can no longer easily do this so we build the header 
   backwards from the start of the data in the buffer.  Because of this we 
   perform all operations on the data relative to a variable start position 
   given by the parameter delta */

static int sendPacketSsh1( SESSION_INFO *sessionInfoPtr, 
						   const int packetType, const int dataLength,
						   const int delta )
	{
	RESOURCE_DATA msgData;
	BYTE *bufStartPtr = sessionInfoPtr->sendBuffer + \
						( ( delta != CRYPT_UNUSED ) ? delta : 0 );
	BYTE *bufPtr = bufStartPtr;
	unsigned long crc32;
	const int length = ID_SIZE + dataLength + SSH1_CRC_SIZE;
	const int padLength = getPadLength( dataLength );
	int status;

	/* Add the SSH packet header:

		uint32		length
		byte[]		padding, 8 - ( length & 7 ) bytes
		byte		type
		byte[]		data
		uint32		crc32	- Calculated over padding, type, and data */
	mputLong( bufPtr, ( long ) length );
	setMessageData( &msgData, bufPtr, padLength );
	krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_GETATTRIBUTE_S, 
					 &msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE );
	bufPtr[ padLength ] = packetType;
	if( ( sessionInfoPtr->flags & ( SESSION_ISCRYPTLIB | SESSION_ISSECURE_WRITE ) ) == \
								  ( SESSION_ISCRYPTLIB | SESSION_ISSECURE_WRITE ) )
		crc32 = calculateTruncatedMAC( sessionInfoPtr->iAuthInContext,
									   bufPtr, 
									   padLength + ID_SIZE + dataLength );
	else
		crc32 = calculateCRC( bufPtr, padLength + ID_SIZE + dataLength );
	bufPtr += padLength + ID_SIZE + dataLength;
	mputLong( bufPtr, crc32 );
	if( sessionInfoPtr->flags & SESSION_ISSECURE_WRITE )
		{
		/* Encrypt the payload with handling for SSH's Blowfish
		   endianness bug */
		if( sessionInfoPtr->cryptAlgo == CRYPT_ALGO_BLOWFISH )
			longReverse( ( unsigned long * ) ( bufStartPtr + LENGTH_SIZE ),
						 padLength + length );
		status = krnlSendMessage( sessionInfoPtr->iCryptOutContext,
								  IMESSAGE_CTX_ENCRYPT, 
								  bufStartPtr + LENGTH_SIZE, 
								  padLength + length );
		if( sessionInfoPtr->cryptAlgo == CRYPT_ALGO_BLOWFISH )
			longReverse( ( unsigned long * ) ( bufStartPtr + LENGTH_SIZE ),
						 padLength + length );
		if( cryptStatusError( status ) )
			return( status );
		}
	if( delta != CRYPT_UNUSED )
		{
		/* If we're sending payload data the send is interruptible, it's the 
		   caller's responsibility to handle this.  Since the caller expects
		   to write data from the start of the buffer, we have to move it
		   down to line it up correctly.  This is somewhat inefficient, but
		   since SSHv1 is virtually extinct it's not worth going to a lot of
		   effort to work around this (the same applies to other SSHv1 
		   issues like the Blowfish endianness problem and the weird 3DES
		   mode) */
		if( delta )
			memmove( sessionInfoPtr->sendBuffer, bufStartPtr,
					 LENGTH_SIZE + padLength + length );
		return( LENGTH_SIZE + padLength + length );
		}
	status = swrite( &sessionInfoPtr->stream, bufStartPtr, 
					 LENGTH_SIZE + padLength + length );
	if( cryptStatusError( status ) )
		{
		sNetGetErrorInfo( &sessionInfoPtr->stream, 
						  sessionInfoPtr->errorMessage,
						  &sessionInfoPtr->errorCode );
		return( status );
		}
	return( CRYPT_OK );	/* swrite() returns a byte count */
	}

/****************************************************************************
*																			*
*							Client-side Connect Functions					*
*																			*
****************************************************************************/

/* Perform the initial part of the handshake with the server */

static int beginClientHandshake( SESSION_INFO *sessionInfoPtr, 
								 SSH_HANDSHAKE_INFO *handshakeInfo )
	{
	MESSAGE_CREATEOBJECT_INFO createInfo;
	RESOURCE_DATA msgData;
	BYTE *bufPtr;
	const BOOLEAN hasPassword = \
			( findSessionAttribute( sessionInfoPtr->attributeList,
								    CRYPT_SESSINFO_PASSWORD ) != NULL ) ? \
			TRUE : FALSE;
	const BOOLEAN hasPrivkey = \
			( findSessionAttribute( sessionInfoPtr->attributeList,
								    CRYPT_SESSINFO_PRIVATEKEY ) != NULL ) ? \
			TRUE : FALSE;
	BOOLEAN rsaOK, pwOK;
	int hostKeyLength, serverKeyLength, keyDataLength, length, value, status;

	/* The higher-level code has already read the server session info, send 
	   back our own version info (SSHv1 uses only a LF as terminator).  For 
	   SSHv1 we use the lowest common denominator of our version (1.5, 
	   described in the only existing spec for SSHv1) and whatever the 
	   server can handle */
	strcpy( sessionInfoPtr->sendBuffer, SSH1_ID_STRING "\n" );
	if( sessionInfoPtr->receiveBuffer[ 2 ] < \
							SSH1_ID_STRING[ SSH_ID_SIZE + 2 ] )
		sessionInfoPtr->sendBuffer[ SSH_ID_SIZE + 2 ] = \
								sessionInfoPtr->receiveBuffer[ 2 ];
	status = swrite( &sessionInfoPtr->stream, sessionInfoPtr->sendBuffer, 
					 strlen( sessionInfoPtr->sendBuffer ) );
	if( cryptStatusError( status ) )
		{
		sNetGetErrorInfo( &sessionInfoPtr->stream, 
						  sessionInfoPtr->errorMessage,
						  &sessionInfoPtr->errorCode );
		return( status );
		}

	/* Create the contexts to hold the server and host keys */
	setMessageCreateObjectInfo( &createInfo, CRYPT_ALGO_RSA );
	status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_CREATEOBJECT, 
							  &createInfo, OBJECT_TYPE_CONTEXT );
	if( cryptStatusError( status ) )
		return( status );
	handshakeInfo->iServerCryptContext = createInfo.cryptHandle;
	setMessageCreateObjectInfo( &createInfo, CRYPT_ALGO_RSA );
	status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_CREATEOBJECT,
							  &createInfo, OBJECT_TYPE_CONTEXT );
	if( cryptStatusError( status ) )
		return( status );
	sessionInfoPtr->iKeyexCryptContext = createInfo.cryptHandle;

	/* If the peer is using cryptlib, we use HMAC-SHA instead of CRC32 */
	if( sessionInfoPtr->flags & SESSION_ISCRYPTLIB )
		{
		setMessageCreateObjectInfo( &createInfo, CRYPT_ALGO_HMAC_SHA );
		status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
								  IMESSAGE_DEV_CREATEOBJECT, &createInfo, 
								  OBJECT_TYPE_CONTEXT );
		if( cryptStatusError( status ) )
			return( status );
		sessionInfoPtr->iAuthInContext = createInfo.cryptHandle;
		}

	/* Process the server public key packet:

		byte[8]		cookie
		uint32		keysize_bits		- Usually 768 bits
		mpint		serverkey_exponent
		mpint		serverkey_modulus
		uint32		keysize_bits		- Usually 1024 bits
		mpint		hostkey_exponent
		mpint		hostkey_modulus
		uint32		protocol_flags		- Not used
		uint32		offered_ciphers
		uint32		offered_authent */
	length = readPacketSSH1( sessionInfoPtr, SSH1_SMSG_PUBLIC_KEY );
	if( cryptStatusError( length ) )
		return( length );
	bufPtr = sessionInfoPtr->receiveBuffer;
	memcpy( handshakeInfo->cookie, bufPtr, SSH1_COOKIE_SIZE );
	bufPtr += SSH1_COOKIE_SIZE;
	length -= SSH1_COOKIE_SIZE;
	keyDataLength = status = processPublickeyData( handshakeInfo, bufPtr, 
												   length, TRUE, NULL );
	if( cryptStatusError( status ) )
		return( status );
	setMessageData( &msgData, bufPtr, keyDataLength );
	status = krnlSendMessage( handshakeInfo->iServerCryptContext, 
							  IMESSAGE_SETATTRIBUTE_S, &msgData, 
							  CRYPT_IATTRIBUTE_KEY_SSH1 );
	if( cryptStatusError( status ) )
		return( status );
	serverKeyLength = mgetLong( bufPtr );
	bufPtr += keyDataLength - LENGTH_SIZE;
	length -= keyDataLength;
	keyDataLength = status = processPublickeyData( handshakeInfo, bufPtr, 
												   length, FALSE, 
												   sessionInfoPtr );
	if( cryptStatusError( status ) )
		return( status );
	setMessageData( &msgData, bufPtr, keyDataLength );
	status = krnlSendMessage( sessionInfoPtr->iKeyexCryptContext, 
							  IMESSAGE_SETATTRIBUTE_S, &msgData, 
							  CRYPT_IATTRIBUTE_KEY_SSH1 );
	if( cryptStatusError( status ) )
		return( status );
	hostKeyLength = mgetLong( bufPtr );
	bufPtr += keyDataLength - LENGTH_SIZE + UINT_SIZE;	/* Skip protocol flags */
	length -= keyDataLength + UINT_SIZE;
	if( length != UINT_SIZE + UINT_SIZE )
		retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
				"Invalid length %d, should be %d", length, 
				UINT_SIZE + UINT_SIZE );
	value = ( int ) mgetLong( bufPtr );		/* Offered ciphers */
	sessionInfoPtr->cryptAlgo = maskToAlgoID( value );
	if( sessionInfoPtr->cryptAlgo == CRYPT_ALGO_NONE )
		retExt( sessionInfoPtr, CRYPT_ERROR_NOTAVAIL,
				"No crypto algorithm compatible with the remote system "
				"could be found" );
	value = ( int ) mgetLong( bufPtr );		/* Offered authentication */
	pwOK = hasPassword && ( value & ( 1 << SSH1_AUTH_PASSWORD ) );
	rsaOK = hasPrivkey && ( value & ( 1 << SSH1_AUTH_RSA ) );
	if( !pwOK )
		{
		/* If neither RSA nor password authentication is possible, we can't 
		   authenticate ourselves */
		if( !rsaOK )
			{
			if( value & ( 1 << SSH1_AUTH_PASSWORD ) )
				{
				setErrorInfo( sessionInfoPtr, CRYPT_SESSINFO_PASSWORD,
							  CRYPT_ERRTYPE_ATTR_ABSENT );
				retExt( sessionInfoPtr, CRYPT_ERROR_NOTINITED,
						"Server requested password authentication but no "
						"password was available" );
				}
			setErrorInfo( sessionInfoPtr, CRYPT_SESSINFO_PRIVATEKEY,
						  CRYPT_ERRTYPE_ATTR_ABSENT );
			retExt( sessionInfoPtr, CRYPT_ERROR_NOTINITED,
					"Server requested public-key authentication but no "
					"key was available" );
			}

		/* Either the client or the server won't do passwords, turn it off 
		   explicitly at the client in case it's the server */
		if( hasPassword )
			{
			ATTRIBUTE_LIST *attributeListPtr = ( ATTRIBUTE_LIST * ) \
					findSessionAttribute( sessionInfoPtr->attributeList,
										  CRYPT_SESSINFO_PASSWORD );

			deleteSessionAttribute( &sessionInfoPtr->attributeList,
									attributeListPtr );
			}
		}

	/* Although in theory the server key has to fit inside the host key, the 
	   spec is vague enough to allow either of the keys to be larger (and at 
	   least one SSH implementation has them the wrong way around), requiring 
	   that the two be swapped before they can be used (which makes you 
	   wonder why there's any distinction, since the two must be 
	   interchangeable in order for this to work) */
	if( hostKeyLength < serverKeyLength )
		{
		int temp;

		/* Swap the two keys around */
		temp = sessionInfoPtr->iKeyexCryptContext;
		sessionInfoPtr->iKeyexCryptContext = \
								handshakeInfo->iServerCryptContext;
		handshakeInfo->iServerCryptContext = temp;
		temp = hostKeyLength;
		hostKeyLength = serverKeyLength;
		serverKeyLength = temp;
		}

	/* Make sure that the smaller of the two keys will fit inside the 
	   larger */
	if( hostKeyLength < serverKeyLength + 128 )
		retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA, 
				"Invalid host vs.server key lengths %d:%d bytes", 
				hostKeyLength, serverKeyLength );

	return( CRYPT_OK );
	}

⌨️ 快捷键说明

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