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

📄 ssh1.c

📁 cryptlib是功能强大的安全工具集。允许开发人员快速在自己的软件中集成加密和认证服务。
💻 C
📖 第 1 页 / 共 5 页
字号:

/* Exchange keys with the server */

static int exchangeClientKeys( SESSION_INFO *sessionInfoPtr, 
							   SSH_HANDSHAKE_INFO *handshakeInfo )
	{
	MECHANISM_WRAP_INFO mechanismInfo;
	RESOURCE_DATA msgData;
	BYTE buffer[ CRYPT_MAX_PKCSIZE ];
	BYTE *bufPtr = sessionInfoPtr->sendBuffer;
	int length, dataLength, value, i, status;

	/* Output the start of the session key packet:

		byte		cipher_type
		byte[8]		cookie
		mpint		double_enc_sessionkey
		uint32		protocol_flags */
	switch( sessionInfoPtr->cryptAlgo )
		{
		case CRYPT_ALGO_BLOWFISH:
			value = SSH1_CIPHER_BLOWFISH;
			break;
		case CRYPT_ALGO_DES:
			value = SSH1_CIPHER_DES;
			break;
		case CRYPT_ALGO_IDEA:
			value = SSH1_CIPHER_IDEA;
			break;
		case CRYPT_ALGO_RC4:
			value = SSH1_CIPHER_RC4;
			break;
		default:
			assert( NOTREACHED );
			return( CRYPT_ERROR_NOTAVAIL );
		}
	*bufPtr++ = value;
	memcpy( bufPtr, handshakeInfo->cookie, SSH1_COOKIE_SIZE );
	bufPtr += SSH1_COOKIE_SIZE;

	/* Generate the session ID and secure state information and XOR the 
	   secure state with the session ID */
	generateSessionID( handshakeInfo );
	setMessageData( &msgData, handshakeInfo->secretValue, SSH1_SECRET_SIZE );
	status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_GETATTRIBUTE_S,
							  &msgData, CRYPT_IATTRIBUTE_RANDOM );
	if( cryptStatusError( status ) )
		return( status );
	handshakeInfo->secretValueLength = SSH1_SECRET_SIZE;
	for( i = 0; i < SSH1_SESSIONID_SIZE; i++ )
		handshakeInfo->secretValue[ i ] ^= handshakeInfo->sessionID[ i ];

	/* Export the secure state information in double-encrypted form, 
	   encrypted first with the server key, then with the host key */
	setMechanismWrapInfo( &mechanismInfo, buffer, CRYPT_MAX_PKCSIZE, 
						  handshakeInfo->secretValue, SSH1_SECRET_SIZE, 
						  CRYPT_UNUSED, handshakeInfo->iServerCryptContext, 
						  CRYPT_UNUSED );
	status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_EXPORT, 
							  &mechanismInfo, MECHANISM_ENC_PKCS1_RAW );
	if( cryptStatusError( status ) )
		return( status );
	length = mechanismInfo.wrappedDataLength;
	setMechanismWrapInfo( &mechanismInfo, 
						  bufPtr + SSH1_MPI_LENGTH_SIZE, CRYPT_MAX_PKCSIZE, 
						  buffer, length, CRYPT_UNUSED, 
						  sessionInfoPtr->iKeyexCryptContext, CRYPT_UNUSED );
	status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_EXPORT, 
							  &mechanismInfo, MECHANISM_ENC_PKCS1_RAW );
	if( cryptStatusError( status ) )
		return( status );
	length = bytesToBits( mechanismInfo.wrappedDataLength );
	mputWord( bufPtr, length );
	bufPtr += mechanismInfo.wrappedDataLength;
	clearMechanismInfo( &mechanismInfo );

	/* XOR the state with the session ID to recover the actual state */
	for( i = 0; i < SSH1_SESSIONID_SIZE; i++ )
		handshakeInfo->secretValue[ i ] ^= handshakeInfo->sessionID[ i ];

	/* Write the various flags */
	mputLong( bufPtr, 0 );		/* Protocol flags */

	/* Move the data up in the buffer to allow for the variable-length
	   padding and send it to the server */
	dataLength = bufPtr - sessionInfoPtr->sendBuffer;
	memmove( sessionInfoPtr->sendBuffer + LENGTH_SIZE + \
			 getPadLength( dataLength ) + ID_SIZE,
			 sessionInfoPtr->sendBuffer, dataLength );
	return( sendPacketSsh1( sessionInfoPtr, SSH1_CMSG_SESSION_KEY,
							dataLength, CRYPT_UNUSED ) );
	}

/* Complete the handshake with the server */

static int completeClientHandshake( SESSION_INFO *sessionInfoPtr, 
									SSH_HANDSHAKE_INFO *handshakeInfo )
	{
	const ATTRIBUTE_LIST *userNamePtr = \
				findSessionAttribute( sessionInfoPtr->attributeList,
									  CRYPT_SESSINFO_USERNAME );
	const ATTRIBUTE_LIST *passwordPtr = \
				findSessionAttribute( sessionInfoPtr->attributeList,
									  CRYPT_SESSINFO_PASSWORD );
	BYTE *bufPtr;
	int padLength, modulusLength, length, status;

	/* Set up the security information required for the session */
	status = initSecurityInfoSSH1( sessionInfoPtr, handshakeInfo );
	if( cryptStatusError( status ) )
		return( status );

	/* Read back the server ack and send the user name:
		string		username */
	status = readPacketSSH1( sessionInfoPtr, SSH1_SMSG_SUCCESS );
	if( cryptStatusError( status ) )
		return( status );
	padLength = getPadLength( LENGTH_SIZE + userNamePtr->valueLength );
	bufPtr = sessionInfoPtr->sendBuffer + LENGTH_SIZE + padLength + ID_SIZE;
	encodeString( bufPtr, userNamePtr->value, userNamePtr->valueLength );
	status = sendPacketSsh1( sessionInfoPtr, SSH1_CMSG_USER,
							 LENGTH_SIZE + userNamePtr->valueLength, 
							 CRYPT_UNUSED );
	if( cryptStatusError( status ) )
		return( status );

	/* Read back the server ack and send the authentication information if 
	   required.  This information is optional, if the server returns a 
	   failure packet (converted to an OK_SPECIAL return status) it means 
	   authentication is required, otherwise it isn't and we're already 
	   logged in */
	status = readPacketSSH1( sessionInfoPtr, SSH1_MSG_SPECIAL_USEROPT );
	if( status == OK_SPECIAL )
		{
		/* If there's a password present, we're using password-based 
		   authentication:
			string		password */
		if( passwordPtr != NULL )
			{
			int maxLen, packetType, i;

			/* Since SSHv1 sends the packet length in the clear and uses
			   implicit-length padding, it reveals the length of the 
			   encrypted password to an observer.  To get around this, we
			   send a series of packets of length 4...maxLen to the server,
			   one of which is the password, the rest are SSH_MSG_IGNOREs.
			   It's still possible for an attacker who can perform very
			   precise timing measurements to determine which one is the
			   password based on server response time, but it's a lot less
			   problematic than with a single packet.  Unfortunately, it's
			   also possible for an attacker to determine the password packet 
			   by checking which one generates the password response, unless
			   the server somehow knows how many packets are coming reads 
			   them all in a loop and only then sends a response, which 
			   means that this defence isn't as effective as it seems */
			status = CRYPT_OK;
			for( maxLen = 16; maxLen <= passwordPtr->valueLength;
				 maxLen <<= 1 );
			for( i = min( 4, passwordPtr->valueLength ); 
				 i < maxLen && cryptStatusOK( status ); i++ )
				{
				padLength = getPadLength( LENGTH_SIZE + i );
				bufPtr = sessionInfoPtr->sendBuffer + LENGTH_SIZE + \
													  padLength + ID_SIZE;
				if( i == passwordPtr->valueLength )
					{
					encodeString( bufPtr, passwordPtr->value, 
								  passwordPtr->valueLength );
					packetType = SSH1_CMSG_AUTH_PASSWORD;
					}
				else
					{
					RESOURCE_DATA msgData;

					setMessageData( &msgData, sessionInfoPtr->receiveBuffer, 
									i );
					krnlSendMessage( SYSTEM_OBJECT_HANDLE, 
									 IMESSAGE_GETATTRIBUTE_S, &msgData, 
									 CRYPT_IATTRIBUTE_RANDOM_NONCE );
					encodeString( bufPtr, sessionInfoPtr->receiveBuffer, i );
					packetType = SSH1_MSG_IGNORE;
					}
				status = sendPacketSsh1( sessionInfoPtr, packetType,
										 LENGTH_SIZE + i, CRYPT_UNUSED );
				if( cryptStatusOK( status ) && \
					packetType == SSH1_CMSG_AUTH_PASSWORD )
					status = readPacketSSH1( sessionInfoPtr, 
											 SSH1_MSG_SPECIAL_PWOPT );
				}
			}
		else
			{
			MECHANISM_WRAP_INFO mechanismInfo;
			RESOURCE_DATA msgData;
			BYTE challenge[ SSH1_CHALLENGE_SIZE ], response[ SSH1_RESPONSE_SIZE ];
			BYTE modulusBuffer[ ( SSH1_MPI_LENGTH_SIZE + CRYPT_MAX_PKCSIZE ) * 2 ];
			BYTE *modulusPtr;

			/* We're using RSA authentication, initially we send just the user's
			   public-key info:

		        mpint	identity_public_modulus

			   First we get the modulus used to identify the client's key.
			   For no adequately explored reason this is only the modulus and
			   not the usual exponent+modulus combination, which means that
			   there's no way for a cryptlib server to identify the public 
			   key from it, although at a pinch we could try with e=17, 257, 
			   or F4 */
			setMessageData( &msgData, modulusBuffer, 
							( SSH1_MPI_LENGTH_SIZE + CRYPT_MAX_PKCSIZE ) * 2 );
			status = krnlSendMessage( sessionInfoPtr->privateKey, 
									  IMESSAGE_GETATTRIBUTE_S, &msgData, 
									  CRYPT_IATTRIBUTE_KEY_SSH1 );
			if( cryptStatusError( status ) )
				return( status );

			/* Skip the key size and exponent to get to the modulus.  We
			   don't have to perform safety checks here since the data is
			   coming from within cryptlib */
			modulusPtr = modulusBuffer + sizeof( LENGTH_SIZE );
			length = mgetWord( modulusPtr );			/* Exponent */
			length = bitsToBytes( length );
			modulusPtr += length;
			modulusLength = mgetWord( modulusPtr );		/* Modulus */
			modulusLength = bitsToBytes( modulusLength );
			length = SSH1_MPI_LENGTH_SIZE + modulusLength;
			modulusPtr -= SSH1_MPI_LENGTH_SIZE;

			/* Send the modulus to the server */
			padLength = getPadLength( length );
			bufPtr = sessionInfoPtr->sendBuffer + LENGTH_SIZE + \
												  padLength + ID_SIZE;
			memcpy( bufPtr, modulusPtr, length );
			status = sendPacketSsh1( sessionInfoPtr, SSH1_CMSG_AUTH_RSA, 
									 length, CRYPT_UNUSED );
			if( cryptStatusOK( status ) )
				status = readPacketSSH1( sessionInfoPtr, 
										 SSH1_MSG_SPECIAL_RSAOPT );
			if( cryptStatusError( status ) )
				return( status );

			/* The server recognises our key (no mean feat, considering that
			   e could be anything) and has sent an RSA challenge, decrypt 
			   it:

				mpint	encrypted_challenge */
			bufPtr = sessionInfoPtr->receiveBuffer;
			length = mgetWord( bufPtr );
			length = bitsToBytes( length );
			if( length < modulusLength - 8 || length > modulusLength )
				retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
						"Invalid encrypted challenge length %d for modulus "
						"length %d", length, modulusLength );
			setMechanismWrapInfo( &mechanismInfo, bufPtr, length, 
								  challenge, SSH1_CHALLENGE_SIZE, CRYPT_UNUSED, 
								  sessionInfoPtr->privateKey, CRYPT_UNUSED );
			status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
									  IMESSAGE_DEV_IMPORT, &mechanismInfo, 
									  MECHANISM_ENC_PKCS1_RAW );
			clearMechanismInfo( &mechanismInfo );
			if( cryptStatusError( status ) )
				return( status );

			/* Send the response to the challenge:

				byte[16]	MD5 of decrypted challenge

			   Since this completes the authentication, we expect to see 
			   either a success or failure packet once we're done */
			generateChallengeResponse( response, handshakeInfo, challenge );
			padLength = getPadLength( SSH1_RESPONSE_SIZE );
			bufPtr = sessionInfoPtr->sendBuffer + LENGTH_SIZE + 
												  padLength + ID_SIZE;
			memcpy( bufPtr, response, SSH1_RESPONSE_SIZE );
			status = sendPacketSsh1( sessionInfoPtr, 
									 SSH1_CMSG_AUTH_RSA_RESPONSE, 
									 SSH1_RESPONSE_SIZE, CRYPT_UNUSED );
			if( cryptStatusOK( status ) )
				status = readPacketSSH1( sessionInfoPtr, 
										 SSH1_MSG_SPECIAL_PWOPT );
			}
		}
	if( cryptStatusError( status ) )
		return( status );

	/* Tell the server to adjust its maximum packet size if required:

		uint32		packet_size */
	if( sessionInfoPtr->sendBufSize < EXTRA_PACKET_SIZE + MAX_PACKET_SIZE )
		{
		const int maxLength = sessionInfoPtr->sendBufSize - \
							  EXTRA_PACKET_SIZE;

		padLength = getPadLength( LENGTH_SIZE );
		bufPtr = sessionInfoPtr->sendBuffer + LENGTH_SIZE + padLength + ID_SIZE; 
		mputLong( bufPtr, maxLength );
		status = sendPacketSsh1( sessionInfoPtr, SSH1_CMSG_MAX_PACKET_SIZE,
								 LENGTH_SIZE, CRYPT_UNUSED );
		if( cryptStatusOK( status ) )
			status = readPacketSSH1( sessionInfoPtr, SSH1_SMSG_SUCCESS );
		if( cryptStatusError( status ) )
			return( status );
		}

	/* Request a pty from the server:

		string		TERM environment variable = "vt100"
		uint32		rows = 24
		uint32		cols = 80
		uint32		pixel_width = 0
		uint32		pixel_height = 0
		byte		tty_mode_info = 0 */
	padLength = getPadLength( ( LENGTH_SIZE + 5 ) + ( UINT_SIZE * 4 ) + 1 );
	bufPtr = sessionInfoPtr->sendBuffer + LENGTH_SIZE + padLength + ID_SIZE; 
	bufPtr += encodeString( bufPtr, "vt100", 0 );	/* Generic terminal type */
	mputLong( bufPtr, 24 );
	mputLong( bufPtr, 80 );			/* 24 x 80 */
	mputLong( bufPtr, 0 );
	mputLong( bufPtr, 0 );			/* No graphics capabilities */
	*bufPtr = 0;					/* No special TTY modes */
	status = sendPacketSsh1( sessionInfoPtr, SSH1_CMSG_REQUEST_PTY,
							 ( LENGTH_SIZE + 5 ) + ( UINT_SIZE * 4 ) + 1, 
							 CRYPT_UNUSED );
	if( cryptStatusOK( status ) )
		status = readPacketSSH1( sessionInfoPtr, SSH1_SMSG_SUCCESS );
	if( cryptStatusError( status ) )
		return( status );

	/* Tell the server to create a shell for us.  This moves the server into
	   the interactive session mode, if we're talking to a standard Unix 
	   server implementing a remote shell we could read the stdout data 
	   response from starting the shell but this may not be the case so we 
	   leave the response for the user to process explicitly */
	return( sendPacketSsh1( sessionInfoPtr, SSH1_CMSG_EXEC_SHELL, 0, 
							CRYPT_UNUSED ) );

⌨️ 快捷键说明

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