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

📄 ssh2.c

📁 老外写的加密库cryptlib(版本3.1)
💻 C
📖 第 1 页 / 共 5 页
字号:
					 dataLength );
	if( macType == MAC_END || macType == MAC_ALL )
		{
		RESOURCE_DATA msgData;
		BYTE macBuffer[ CRYPT_MAX_HASHSIZE ];

		krnlSendMessage( iMacContext, IMESSAGE_CTX_HASH, "", 0 );
		setMessageData( &msgData, macBuffer, CRYPT_MAX_HASHSIZE );
		status = krnlSendMessage( iMacContext, IMESSAGE_GETATTRIBUTE_S,
								  &msgData, CRYPT_CTXINFO_HASHVALUE );
		if( cryptStatusError( status ) || \
			memcmp( macBuffer, data + dataLength, msgData.length ) )
			return( FALSE );
		}

	return( TRUE );
	}

/* Get the reason why the peer closed the connection */

static int getDisconnectInfo( SESSION_INFO *sessionInfoPtr, BYTE *bufPtr )
	{
	static const FAR_BSS struct {
		const int sshStatus, cryptlibStatus;
		} errorMap[] = {
		{ SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT, CRYPT_ERROR_PERMISSION },
		{ SSH2_DISCONNECT_MAC_ERROR, CRYPT_ERROR_SIGNATURE },
		{ SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE, CRYPT_ERROR_NOTAVAIL },
		{ SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED, CRYPT_ERROR_NOTAVAIL },
		{ SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE, CRYPT_ERROR_WRONGKEY },
		{ CRYPT_ERROR, CRYPT_ERROR_READ }
		};
	int length, i;

	/* Server is disconnecting, find out why */
	bufPtr++;				/* Skip packet type */
	sessionInfoPtr->errorCode = mgetLong( bufPtr );
	length = mgetLong( bufPtr );
	if( length < 0 || length > MAX_ERRMSG_SIZE - 32 )
		retExt( sessionInfoPtr, CRYPT_ERROR_OVERFLOW,
				"Invalid error information size %d", length );
	strcpy( sessionInfoPtr->errorMessage, "Received SSHv2 server message: " );
	if( length <= 0 )
		strcat( sessionInfoPtr->errorMessage, "<None>" );
	else
		{
		memcpy( sessionInfoPtr->errorMessage + 31, bufPtr, length );
		sessionInfoPtr->errorMessage[ 31 + length ] = '\0';
		}

	/* Try and map the SSH status to an equivalent cryptlib code */
	for( i = 0; errorMap[ i ].sshStatus != CRYPT_ERROR; i++ )
		if( errorMap[ i ].sshStatus == sessionInfoPtr->errorCode )
			break;
	return( errorMap[ i ].cryptlibStatus );
	}

/* Read an SSHv2 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 */

int readPacketSSH2( SESSION_INFO *sessionInfoPtr, int expectedType )
	{
	BYTE *dataStartPtr;
	long length;
	int padLength = 0, packetType;

	/* Alongside the expected 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 loop to strip them out */
	do
		{
		BYTE *bufPtr = sessionInfoPtr->receiveBuffer;
		int extraLength = 0, status;

		/* Read the SSHv2 packet header:

			uint32		length
			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.

		  SSHv2 encrypts everything (including the length) so we need to
		  speculatively read ahead for the minimum packet size and decrypt
		  that in order to figure out what to do */
		assert( sessionInfoPtr->receiveBufEnd == 0 );
		status = readFixedHeader( sessionInfoPtr, MIN_PACKET_SIZE );
		if( cryptStatusError( status ) )
			return( status );
		assert( status == MIN_PACKET_SIZE );
		if( ( sessionInfoPtr->protocolFlags & SSH_PFLAG_TEXTDIAGS ) && \
			sessionInfoPtr->receiveBuffer[ 0 ] == 'F' && \
			( !memcmp( sessionInfoPtr->receiveBuffer, "FATAL: ", 7 ) || \
			  !memcmp( sessionInfoPtr->receiveBuffer, "FATAL ERROR:", 12 ) ) )
			{
			/* Versions of SSH derived from the original SSH code base can
			   sometimes dump raw text strings (that is, strings not 
			   encapsulated in SSH packets such as error packets) onto the 
			   connection if something unexpected occurs.  Normally this 
			   would result in a bad data or MAC error since they decrypt to
			   garbage, so we try and catch them here */
			dataStartPtr = sessionInfoPtr->receiveBuffer + MIN_PACKET_SIZE;
			for( length = 0; 
				 length < MAX_ERRMSG_SIZE - ( MIN_PACKET_SIZE + 64 ); 
				 length++ )
				{
				status = sread( &sessionInfoPtr->stream, 
								dataStartPtr + length, 1 );
				if( cryptStatusError( status ) || \
					dataStartPtr[ length ] == '\n' )
					break;
				}
			while( length > 0 && \
				   ( dataStartPtr[ length - 1 ] == '\r' || \
				     dataStartPtr[ length - 1 ] == '\n' ) )
				length--;
			dataStartPtr[ length ] = '\0';

			/* Report the error as a problem with the remote software.  
			   Since the other side has bailed out, we mark the channel as 
			   closed to prevent any attempt to perform proper shutdown */
			sessionInfoPtr->flags |= SESSION_SENDCLOSED;
			sessionInfoPtr->protocolFlags |= SSH_PFLAG_CHANNELCLOSED;
			retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
					"Remote SSH software has crashed, diagnostic was '%s'",
					sessionInfoPtr->receiveBuffer );
			}
		if( sessionInfoPtr->flags & SESSION_ISSECURE )
			{
			status = krnlSendMessage( sessionInfoPtr->iCryptInContext,
									  IMESSAGE_CTX_DECRYPT,
									  sessionInfoPtr->receiveBuffer,
									  MIN_PACKET_SIZE );
			if( cryptStatusError( status ) )
				return( status );
			}
		length = mgetLong( bufPtr );
		assert( SSH2_HEADER_REMAINDER_SIZE == MIN_PACKET_SIZE - LENGTH_SIZE );
		if( sessionInfoPtr->flags & SESSION_ISSECURE )
			/* The MAC size isn't included in the packet length so we have to
			   add it manually */
			extraLength = sessionInfoPtr->authBlocksize;
		if( length + extraLength < SSH2_HEADER_REMAINDER_SIZE || \
			length + extraLength >= sessionInfoPtr->receiveBufSize )
			retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
					"Invalid packet length %d, extra length %d", length,
					extraLength );
		memmove( sessionInfoPtr->receiveBuffer,
				 sessionInfoPtr->receiveBuffer + LENGTH_SIZE,
				 SSH2_HEADER_REMAINDER_SIZE );
		if( length + extraLength > SSH2_HEADER_REMAINDER_SIZE )
			{
			const long remainingLength = length + extraLength - \
										 SSH2_HEADER_REMAINDER_SIZE;

			/* The change cipherspec message has length 0, so we only
			   perform the read if there's packet data present.  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->errorMessage,
								  &sessionInfoPtr->errorCode );
				return( status );
				}
			if( status != remainingLength )
				retExt( sessionInfoPtr, CRYPT_ERROR_TIMEOUT,
						"Timeout during packet remainder read, only got %d "
						"of %d bytes", status, remainingLength );
			}
		if( sessionInfoPtr->flags & SESSION_ISSECURE )
			{
			/* Decrypt the remainder of the packet except for the MAC */
			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 */
			if( !macPayload( sessionInfoPtr->iAuthInContext,
							 sessionInfoPtr->readSeqNo,
							 sessionInfoPtr->receiveBuffer, length, 0,
							 MAC_ALL ) )
				{
				/* 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( sessionInfoPtr, CRYPT_ERROR_WRONGKEY,
							"Bad message MAC, probably due to an incorrect "
							"key being used to generate the MAC" );
				retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
						"Bad message MAC" );
				}
			}
		padLength = sessionInfoPtr->receiveBuffer[ 0 ];
		packetType = sessionInfoPtr->receiveBuffer[ 1 ];
		sessionInfoPtr->readSeqNo++;
		}
	while( packetType == SSH2_MSG_IGNORE || packetType == SSH2_MSG_DEBUG || \
		   packetType == SSH2_MSG_USERAUTH_BANNER );
	sessionInfoPtr->sshPacketType = packetType;

	/* Adjust the length to account for the fixed-size fields and remember
	   where the data starts */
	dataStartPtr = sessionInfoPtr->receiveBuffer + PADLENGTH_SIZE;
	length -= PADLENGTH_SIZE + padLength;

	/* Make sure that we either got what we asked for or one of the allowed
	   special-case packets */
	if( packetType == SSH2_MSG_DISCONNECT )
		return( getDisconnectInfo( sessionInfoPtr, dataStartPtr ) );
	if( expectedType == 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:

			byte	type = SSH2_MSG_USERAUTH_FAILURE
			string	allowed_authent
			boolean	partial_success = FALSE */
		if( packetType == SSH2_MSG_USERAUTH_FAILURE )
			{
			BYTE *bufPtr = dataStartPtr;
			long stringLength;

			if( length < ID_SIZE + ( LENGTH_SIZE + 1 ) + BOOLEAN_SIZE )
				retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
						"Invalid user auth response length %d", length );
			bufPtr++;		/* Skip packet type */
			stringLength = mgetLong( bufPtr );
			if( length != ID_SIZE + LENGTH_SIZE + stringLength + BOOLEAN_SIZE )
				retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
						"Invalid user auth response length %d, string length "
						"%d", length, stringLength );

			/* If the returned information can fit into an error message,
			   return it to the caller */
			if( stringLength < MAX_ERRMSG_SIZE - 70 )
				{
				strcpy( sessionInfoPtr->errorMessage,
						"Received SSHv2 server message: Permitted "
						"authentication types are " );
				memcpy( sessionInfoPtr->errorMessage + 66, bufPtr, 
						stringLength );
				sessionInfoPtr->errorMessage[ 66 + stringLength ] = '\0';
				}
			memmove( sessionInfoPtr->receiveBuffer, dataStartPtr, length );
			return( CRYPT_ERROR_WRONGKEY );
			}
		expectedType = SSH2_MSG_USERAUTH_SUCCESS;
		}
	if( expectedType == 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( sessionInfoPtr, CRYPT_ERROR_BADDATA,
					"Invalid packet type %d, expected global or channel "
					"request", packetType );
		expectedType = packetType;
		}
	if( expectedType == SSH2_MSG_KEXDH_GEX_REQUEST && \
		packetType == SSH2_MSG_KEXDH_GEX_REQUEST_NEW )
		/* 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 */
		expectedType = packetType;
	if( packetType != expectedType )
		retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
				"Invalid packet type %d, expected %d", packetType,
				expectedType );

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

/* Send an SSHv2 packet.  During the handshake phase we may be sending
   multiple packets at once, however unlike SSL, SSH requires that each
   packet in a multi-packet group be individually wrapped so we have to
   provide a facility for separately wrapping and sending packets to handle
   this */

int wrapPacket( SESSION_INFO *sessionInfoPtr, BYTE *bufPtr,
				const int dataLength )
	{
	const BYTE *bufStartPtr = bufPtr;
	const int length = LENGTH_SIZE + PADLENGTH_SIZE + dataLength;
	const int padBlockSize = max( sessionInfoPtr->cryptBlocksize, 8 );
	int padLength, status;

	/* Evaludate the number of padding bytes that we need to add to a packet 
	   to make it a multiple of the cipher block size long, with a minimum 
	   padding size of SSH2_MIN_PADLENGTH_SIZE bytes.  Note that this padding 
	   is required even when there's no encryption being applied, although we 
	   set the padding to all zeroes in this case */
	if( bufPtr[ LENGTH_SIZE + PADLENGTH_SIZE ] == SSH2_MSG_USERAUTH_REQUEST )
		{
		/* It's a user-authentication packet that (probably) contains a
		   password, make it fixed-length to hide the length information */
		for( padLength = 256; 
			 ( length + SSH2_MIN_PADLENGTH_SIZE ) > padLength; 
			 padLength += 256 );
		padLength -= length;
		}
	else
		padLength = roundUp( length + SSH2_MIN_PADLENGTH_SIZE, 

⌨️ 快捷键说明

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