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

📄 ssh2_rw.c

📁 cryptlib安全工具包
💻 C
📖 第 1 页 / 共 3 页
字号:
		Secure mode: length < ID_SIZE + PADLENGTH_SIZE +
			SSH2_MIN_PADLENGTH_SIZE.  In this case there's an (implicit) MAC
			present so the packet (length + extraLength) will always be
			larger than the (remaining) data that we've already read.  For
			this case we need to check that the data payload is at least as
			long as the minimum-length packet */
	sMemConnect( &stream, headerBufPtr, MIN_PACKET_SIZE );
	length = readUint32( &stream );
	assert( SSH2_HEADER_REMAINDER_SIZE == MIN_PACKET_SIZE - LENGTH_SIZE );
	if( sessionInfoPtr->flags & SESSION_ISSECURE_READ )
		{
		/* The MAC size isn't included in the packet length so we have to
		   add it manually */
		extraLength = sessionInfoPtr->authBlocksize;
		}
	if( cryptStatusError( length ) || \
		length + extraLength < SSH2_HEADER_REMAINDER_SIZE || \
		length < ID_SIZE + PADLENGTH_SIZE + SSH2_MIN_PADLENGTH_SIZE || \
		length + extraLength >= sessionInfoPtr->receiveBufSize )
		{
		sMemDisconnect( &stream );
		retExt( CRYPT_ERROR_BADDATA,
				( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 
				  "Invalid packet length %ld, should be %d...%d", 
				  cryptStatusError( length ) ? 0 : length,
				  ID_SIZE + PADLENGTH_SIZE + SSH2_MIN_PADLENGTH_SIZE,
				  sessionInfoPtr->receiveBufSize - extraLength ) );
		}
	assert( ( isHandshake && sessionInfoPtr->receiveBufPos == 0 ) || \
			!isHandshake );
	status = sread( &stream, sessionInfoPtr->receiveBuffer + \
							 sessionInfoPtr->receiveBufPos, 
					SSH2_HEADER_REMAINDER_SIZE );
	sMemDisconnect( &stream );
	if( cryptStatusError( status ) )
		return( status );

	*packetLength = length;
	*packetExtraLength = extraLength;
	return( CRYPT_OK );
	}

/* Read an SSHv2 handshake packet.  This function is only used during the 
   handshake phase (the data transfer phase has its own read/write code) so 
   we can perform some special-case handling based on this.  In particular 
   we know that packets will always be read into the start of the receive 
   buffer so we don't have to perform special buffer-space-remaining 
   calculations */

int readHSPacketSSH2( SESSION_INFO *sessionInfoPtr, int expectedType,
					  const int minPacketSize )
	{
	SSH_INFO *sshInfo = sessionInfoPtr->sessionSSH;
	long length;
	int padLength = 0, packetType, minPacketLength = minPacketSize;
	int iterationCount = 0, status;

	assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
	assert( expectedType >= SSH2_MSG_DISCONNECT && \
			expectedType <= SSH2_MSG_SPECIAL_REQUEST );
	assert( minPacketSize >= 1 && minPacketSize < 1024 );

	/* Alongside the expected handshake packets the server can send us all 
	   sorts of no-op messages, ranging from explicit no-ops 
	   (SSH2_MSG_IGNORE) through to general chattiness (SSH2_MSG_DEBUG, 
	   SSH2_MSG_USERAUTH_BANNER).  Because we can receive any quantity of 
	   these at any time, we have to run the receive code in a (bounds-
	   checked) loop to strip them out */
	do
		{
		int extraLength;

		/* Read the SSHv2 handshake packet header:

			uint32		length (excluding MAC size)
			byte		padLen
		  [	byte		type - checked but not removed ]
			byte[]		data
			byte[]		padding
			byte[]		MAC

		  The reason why the length and pad length precede the packet type
		  and other information is that these two fields are part of the
		  SSHv2 transport layer while the type and payload are seen as part
		  of the connection layer, although the different RFCs tend to mix
		  them up quite thoroughly */
		assert( sessionInfoPtr->receiveBufPos == 0 && \
				sessionInfoPtr->receiveBufEnd == 0 );
		status = readPacketHeaderSSH2( sessionInfoPtr, expectedType, &length,
									   &extraLength, NULL );
		if( cryptStatusError( status ) )
			return( status );
		assert( length + extraLength >= SSH2_HEADER_REMAINDER_SIZE && \
				length + extraLength < sessionInfoPtr->receiveBufSize );
				/* Guaranteed by readPacketHeaderSSH2() */

		/* Read the remainder of the handshake-packet message.  The change 
		   cipherspec message has length 0 so we only perform the read if 
		   there's packet data present */
		if( length + extraLength > SSH2_HEADER_REMAINDER_SIZE )
			{
			const long remainingLength = length + extraLength - \
										 SSH2_HEADER_REMAINDER_SIZE;

			/* Because this code is called conditionally, we can't make the
			   read part of the fixed-header read but have to do independent
			   handling of shortfalls due to read timeouts */
			status = sread( &sessionInfoPtr->stream,
							sessionInfoPtr->receiveBuffer + \
								SSH2_HEADER_REMAINDER_SIZE,
							remainingLength );
			if( cryptStatusError( status ) )
				{
				sNetGetErrorInfo( &sessionInfoPtr->stream,
								  &sessionInfoPtr->errorInfo );
				return( status );
				}
			if( status != remainingLength )
				{
				retExt( CRYPT_ERROR_TIMEOUT,
						( CRYPT_ERROR_TIMEOUT, SESSION_ERRINFO, 
						  "Timeout during handshake packet remainder read, "
						  "only got %d of %ld bytes", status,
						  remainingLength ) );
				}
			}

		/* Decrypt and MAC the packet if required */
		if( sessionInfoPtr->flags & SESSION_ISSECURE_READ )
			{
			/* Decrypt the remainder of the packet except for the MAC.
			   Sometimes the payload can be zero-length, so we have to check
			   for this before we try the decrypt */
			if( length > SSH2_HEADER_REMAINDER_SIZE )
				{
				status = krnlSendMessage( sessionInfoPtr->iCryptInContext,
										  IMESSAGE_CTX_DECRYPT,
										  sessionInfoPtr->receiveBuffer + \
											SSH2_HEADER_REMAINDER_SIZE,
										  length - SSH2_HEADER_REMAINDER_SIZE );
				if( cryptStatusError( status ) )
					return( status );
				}

			/* MAC the decrypted payload */
			status = checkMacSSH( sessionInfoPtr->iAuthInContext,
								  sshInfo->readSeqNo,
								  sessionInfoPtr->receiveBuffer, 
								  length + extraLength, length, 0, MAC_ALL, 
								  extraLength );
			if( cryptStatusError( status ) )
				{
				/* If we're expecting a service control packet after a change
				   cipherspec 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 */
				if( expectedType == SSH2_MSG_SERVICE_REQUEST || \
					expectedType == SSH2_MSG_SERVICE_ACCEPT )
					{
					retExt( CRYPT_ERROR_WRONGKEY,
							( CRYPT_ERROR_WRONGKEY, SESSION_ERRINFO, 
							  "Bad message MAC for handshake packet type "
							  "%d, length %ld, probably due to an "
							  "incorrect key being used to generate the "
							  "MAC", sessionInfoPtr->receiveBuffer[ 1 ], 
							  length ) );
					}
				retExt( CRYPT_ERROR_BADDATA,
						( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 
						  "Bad message MAC for handshake packet type %d, "
						  "length %ld", sessionInfoPtr->receiveBuffer[ 1 ],
						  length ) );
				}
			}
		padLength = sessionInfoPtr->receiveBuffer[ 0 ];
		packetType = sessionInfoPtr->receiveBuffer[ 1 ];
		sshInfo->readSeqNo++;
		}
	while( ( packetType == SSH2_MSG_IGNORE || \
			 packetType == SSH2_MSG_DEBUG || \
			 packetType == SSH2_MSG_USERAUTH_BANNER ) && \
		   ( iterationCount++ < FAILSAFE_ITERATIONS_SMALL ) );
	if( iterationCount >= FAILSAFE_ITERATIONS_SMALL )
		{
		/* We have to be a bit careful here in case this is a strange
		   implementation that sends large numbers of no-op packets as cover
		   traffic.  Complaining after FAILSAFE_ITERATIONS_SMALL consecutive 
		   no-ops seems to be a safe tradeoff between catching DoS's and 
		   handling cover traffic */
		retExt( CRYPT_ERROR_OVERFLOW,
				( CRYPT_ERROR_OVERFLOW, SESSION_ERRINFO, 
				  "Peer sent an excessive number of consecutive no-op "
				  "packets, it may be stuck in a loop" ) );
		}
	sshInfo->packetType = packetType;

	/* Adjust the length to account for the fixed-size fields, remember
	   where the data starts, and make sure that there's some payload
	   present (there should always be at least one byte, the packet type) */
	length -= PADLENGTH_SIZE + padLength;
	if( packetType == SSH2_MSG_DISCONNECT )
		{
		/* If we're expecting a standard data packet and we get a disconnect
		   packet due to an error, the length can be less than the expected
		   mimimum length, so we adjust the length to the minimum packet 
		   length of a disconnect packet */
		minPacketLength = ID_SIZE + UINT32_SIZE + \
						  sizeofString32( "", 1 ) + sizeofString32( "", 0 );
		}
	if( length < minPacketLength || \
		length > sessionInfoPtr->receiveBufSize - PADLENGTH_SIZE )
		{
		retExt( CRYPT_ERROR_BADDATA,
				( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 
				  "Invalid length %ld for handshake packet type %d, should "
				  "be %d...%d", length, packetType, minPacketLength,
				  sessionInfoPtr->receiveBufSize - PADLENGTH_SIZE ) );
		}

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

	/* If the other side has gone away, report the details */
	if( packetType == SSH2_MSG_DISCONNECT )
		{
		STREAM stream;

		sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
		assert( sPeek( &stream ) == SSH2_MSG_DISCONNECT );
		status = sgetc( &stream );	/* Skip packet type */
		if( !cryptStatusError( status ) )
			status = getDisconnectInfo( sessionInfoPtr, &stream );
		sMemDisconnect( &stream );
		return( status );
		}

	/* Make sure that we either got what we asked for or one of the allowed
	   special-case packets */
	switch( expectedType )
		{
		case SSH2_MSG_SPECIAL_USERAUTH:
			/* If we're reading a response to a user authentication message
			   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 */
			expectedType = ( packetType == SSH2_MSG_USERAUTH_FAILURE ) ? \
								SSH2_MSG_USERAUTH_FAILURE : \
								SSH2_MSG_USERAUTH_SUCCESS;
			break;

		case SSH2_MSG_SPECIAL_USERAUTH_PAM:
			/* PAM authentication can go through multiple iterations of back-
			   and-forth negotiation, for this case an info-request is also
			   a valid response, otherwise the responses are as for
			   SSH2_MSG_SPECIAL_USERAUTH */
			expectedType = ( packetType == SSH2_MSG_USERAUTH_INFO_REQUEST ) ? \
								SSH2_MSG_USERAUTH_INFO_REQUEST : \
						   ( packetType == SSH2_MSG_USERAUTH_FAILURE ) ? \
								SSH2_MSG_USERAUTH_FAILURE : \
								SSH2_MSG_USERAUTH_SUCCESS;
			break;

		case SSH2_MSG_SPECIAL_CHANNEL:
			/* If we're reading a response to a channel open message then
			   getting a failure response is valid (even if it's not what
			   we're expecting) since it's an indication that the channel
			   open (for example a port-forwarding operation) failed rather
			   than that there was some general type of failure */
			expectedType = ( packetType == SSH2_MSG_CHANNEL_OPEN_FAILURE ) ? \
								SSH2_MSG_CHANNEL_OPEN_FAILURE : \
								SSH2_MSG_CHANNEL_OPEN_CONFIRMATION;
			break;

		case SSH2_MSG_SPECIAL_REQUEST:
			/* If we're at the end of the handshake phase we can get either
			   a global or a channel request to tell us what to do next */
			if( packetType != SSH2_MSG_GLOBAL_REQUEST && \
				packetType != SSH2_MSG_CHANNEL_REQUEST )
				{
				retExt( CRYPT_ERROR_BADDATA,
						( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 
						  "Invalid handshake packet type %d, expected "
						  "global or channel request", packetType ) );
				}
			expectedType = packetType;
			break;

		case SSH2_MSG_KEXDH_GEX_REQUEST_OLD:
			/* The ephemeral DH key exchange spec was changed halfway
			   through to try and work around problems with key negotiation,
			   because of this we can see two different types of ephemeral
			   DH request, although they're functionally identical */
			if( packetType == SSH2_MSG_KEXDH_GEX_REQUEST_NEW )

⌨️ 快捷键说明

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