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

📄 ssh2_rw.c

📁 cryptlib安全工具包
💻 C
📖 第 1 页 / 共 3 页
字号:
/****************************************************************************
*																			*
*					cryptlib SSHv2 Session Read/Write Routines				*
*						Copyright Peter Gutmann 1998-2008					*
*																			*
****************************************************************************/

#if defined( INC_ALL )
  #include "crypt.h"
  #include "misc_rw.h"
  #include "session.h"
  #include "ssh.h"
#else
  #include "crypt.h"
  #include "misc/misc_rw.h"
  #include "session/session.h"
  #include "session/ssh.h"
#endif /* Compiler-specific includes */

#ifdef USE_SSH

/****************************************************************************
*																			*
*								Utility Functions							*
*																			*
****************************************************************************/

/* Processing handshake data can run into a number of special-case 
   conditions due to buggy SSH implementations, we handle these in a special
   function to avoid cluttering up the main packet-read code */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
static int checkHandshakePacketStatus( INOUT SESSION_INFO *sessionInfoPtr,
									   const int headerStatus,
									   IN_BUFFER( headerLength ) \
									   const BYTE *header, const int headerLength,
									   const int expectedType )
	{
	assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
	assert( headerStatus == CRYPT_ERROR_READ || cryptStatusOK( headerStatus ) );
	assert( isReadPtr( header, headerLength ) );
	assert( expectedType >= SSH2_MSG_DISCONNECT && \
			expectedType <= SSH2_MSG_SPECIAL_REQUEST );

	/* If the other side has simply dropped the connection, see if we can 
	   get further details on what went wrong */
	if( headerStatus == CRYPT_ERROR_READ )
		{
		/* Some servers just close the connection in response to a bad 
		   password rather than returning an error, if it looks like this 
		   has occurred we return a more informative error than the low-
		   level networking one */
		if( !isServer( sessionInfoPtr ) && \
			( expectedType == SSH2_MSG_SPECIAL_USERAUTH || \
			  expectedType == SSH2_MSG_SPECIAL_USERAUTH_PAM ) )
			{
			retExt( headerStatus,
					( headerStatus, SESSION_ERRINFO, 
					  "Remote server has closed the connection, possibly "
					  "in response to an incorrect password or other "
					  "authentication value" ) );
			}

		/* Some versions of CuteFTP simply drop the connection with no
		   diagnostics or error information when they get the phase 2 keyex
		   packet, the best that we can do is tell the user to hassle the
		   CuteFTP vendor about this */
		if( isServer( sessionInfoPtr ) && \
			( sessionInfoPtr->protocolFlags & SSH_PFLAG_CUTEFTP ) && \
			expectedType == SSH2_MSG_NEWKEYS )
			{
			retExt( headerStatus,
					( headerStatus, SESSION_ERRINFO, 
					  "CuteFTP client has aborted the handshake due to a "
					  "CuteFTP bug, please contact the CuteFTP vendor" ) );
			}

		return( CRYPT_OK );
		}

	assert( cryptStatusOK( headerStatus ) );

	/* 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 */
	if( ( sessionInfoPtr->protocolFlags & SSH_PFLAG_TEXTDIAGS ) && \
		header[ 0 ] == 'F' && \
		( !memcmp( header, "FATAL: ", 7 ) || \
		  !memcmp( header, "FATAL ERROR:", 12 ) ) )
		{
		BYTE *bufPtr;
		const int maxLength = min( MAX_ERRMSG_SIZE - 128, 
								   sessionInfoPtr->receiveBufSize - 128 );
		int length;

		/* Copy across what we've got so far.  Since this is a fatal error,
		   we use the receive buffer to contain the data since we don't need
		   it for any further processing */
		memcpy( sessionInfoPtr->receiveBuffer, header, 
				MIN_PACKET_SIZE );

		/* Read the rest of the error message */
		for( length = MIN_PACKET_SIZE; length < maxLength; length++ )
			{
			const int ch = sgetc( &sessionInfoPtr->stream );

			if( cryptStatusError( ch ) || ch == '\n' || ch == '\r' )
				break;
			sessionInfoPtr->receiveBuffer[ length ] = ch;
			}

		/* Remove trailing garbage.  We check for CR and LF even though 
		   they're excluded by the loop above because they may have been read
		   as part of the initial read of MIN_PACKET_SIZE bytes */
		for( bufPtr = sessionInfoPtr->receiveBuffer; length > 0; length-- )
			{
			const int ch = bufPtr[ length - 1 ];

			if( ch != '\r' && ch != '\n' && ch != '\t' && ch != ' ' )
				break;
			}
		bufPtr[ 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 try and perform a standard shutdown */
		sessionInfoPtr->flags |= SESSION_SENDCLOSED;
		retExt( CRYPT_ERROR_BADDATA,
				( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 
				  "Remote SSH software has crashed, diagnostic was: '%s'",
				  sanitiseString( sessionInfoPtr->receiveBuffer, 
				  MAX_ERRMSG_SIZE - 64, length ) ) );
		}

	/* No buggy behaviour detected */
	return( CRYPT_OK );
	}

/****************************************************************************
*																			*
*								Read/Unwrap a Packet						*
*																			*
****************************************************************************/

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

int getDisconnectInfo( SESSION_INFO *sessionInfoPtr, STREAM *stream )
	{
	typedef struct {
		const int sshStatus, cryptlibStatus;
		} ERRORMAP_INFO;
	static const ERRORMAP_INFO FAR_BSS errorMap[] = {
		/* A mapping of SSH error codes that have cryptlib equivalents to
		   the equivalent cryptlib codes.  If there's no mapping available,
		   we use a default of CRYPT_ERROR_READ */
		{ 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 }, { CRYPT_ERROR, CRYPT_ERROR_READ }
		};
	ERROR_INFO *errorInfo = &sessionInfoPtr->errorInfo;
	char errorString[ MAX_ERRMSG_SIZE + 8 ];
	int errorCode, length, i, status;

	assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
	assert( isWritePtr( stream, sizeof( STREAM ) ) );

	/* Peer is disconnecting, find out why:

	  [	byte	SSH2_MSG_DISCONNECT ]
		uint32	reason
		string	description
		string	language_tag */
	errorCode = readUint32( stream );
	if( cryptStatusError( errorCode ) )
		{
		retExt( CRYPT_ERROR_BADDATA,
				( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 
				  "Invalid disconnect status information in disconnect "
				  "message" ) );
		}
	errorInfo->errorCode = errorCode;
	status = readString32( stream, errorString, MAX_ERRMSG_SIZE - 64, 
						   &length );
	if( cryptStatusOK( status ) )
		sanitiseString( errorString, MAX_ERRMSG_SIZE - 64, length );
	else
		{
		memcpy( errorString, "<No details available>", 22 + 1 );
		}

	/* Try and map the SSH status to an equivalent cryptlib one */
	for( i = 0; errorMap[ i ].sshStatus != CRYPT_ERROR && \
				i < FAILSAFE_ARRAYSIZE( errorMap, ERRORMAP_INFO ); i++ )
		{
		if( errorMap[ i ].sshStatus == errorInfo->errorCode )
			break;
		}
	if( i >= FAILSAFE_ARRAYSIZE( errorMap, ERRORMAP_INFO ) )
		retIntError();
	retExt( errorMap[ i ].cryptlibStatus,
			( errorMap[ i ].cryptlibStatus, SESSION_ERRINFO, 
			  "Received disconnect message: %s", errorString ) );
	}

/* Read, decrypt if necessary, and check the start of a packet header */

int readPacketHeaderSSH2( SESSION_INFO *sessionInfoPtr,
						  const int expectedType, long *packetLength,
						  int *packetExtraLength,
						  READSTATE_INFO *readInfo )
	{
	SSH_INFO *sshInfo = sessionInfoPtr->sessionSSH;
	STREAM stream;
	BYTE headerBuffer[ MIN_PACKET_SIZE + 8 ];
	const BOOLEAN isHandshake = ( readInfo == NULL ) ? TRUE : FALSE;
	BYTE *headerBufPtr = isHandshake ? headerBuffer : sshInfo->headerBuffer;
	long length;
	int extraLength = 0, status = CRYPT_OK;

	assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
	assert( expectedType >= SSH2_MSG_DISCONNECT && \
			expectedType <= SSH2_MSG_SPECIAL_REQUEST );
	assert( isWritePtr( packetLength, sizeof( long ) ) );
	assert( isWritePtr( packetExtraLength, sizeof( int ) ) );
	assert( readInfo == NULL || \
			isWritePtr( readInfo, sizeof( READSTATE_INFO ) ) );

	/* Clear return values */
	*packetLength = 0;
	*packetExtraLength = 0;

	assert( CRYPT_MAX_IVSIZE >= MIN_PACKET_SIZE );
			/* Packet header is a single cipher block */

	/* SSHv2 encrypts everything but the MAC (including the packet length)
	   so we need to speculatively read ahead for the minimum packet size
	   and decrypt that in order to figure out what to do */
	if( isHandshake )
		{
		int localStatus;

		/* Processing handshake data can run into a number of special-case
		   conditions due to buggy SSH implementations, to handle these we
		   check the return code as well as the returned data to see if we
		   need to process it specially */
		status = readFixedHeaderAtomic( sessionInfoPtr, headerBufPtr, 
										MIN_PACKET_SIZE );
		if( status == CRYPT_ERROR_READ || cryptStatusOK( status ) )
			{
			localStatus = checkHandshakePacketStatus( sessionInfoPtr, 
									status, headerBufPtr, MIN_PACKET_SIZE, 
									expectedType );
			if( cryptStatusError( localStatus ) )
				status = localStatus;
			}
		}
	else
		{
		status = readFixedHeader( sessionInfoPtr, headerBufPtr, 
								  MIN_PACKET_SIZE );
		}
	if( cryptStatusError( status ) )
		return( status );

	/* If we're in the data-processing stage (i.e. it's a post-handshake
	   data packet read), exception conditions need to be handled specially
	   if they occur */
	if( !isHandshake )
		{
		/* Since data errors are always fatal, when we're in the data-
		   processing stage we make all errors fatal until we've finished
		   handling the header */
		*readInfo = READINFO_FATAL;
		}

	/* Decrypt the header if necessary */
	if( sessionInfoPtr->flags & SESSION_ISSECURE_READ )
		{
		status = krnlSendMessage( sessionInfoPtr->iCryptInContext,
								  IMESSAGE_CTX_DECRYPT, headerBufPtr,
								  MIN_PACKET_SIZE );
		if( cryptStatusError( status ) )
			return( status );
		}

	/* Process the packet header.  The dual minimum-length checks actually
	   simplify to the following:

		Non-secure mode: length < SSH2_HEADER_REMAINDER_SIZE (extraLength = 0).
			In this case there's no MAC being used, so all that we need to
			guarantee is that the packet is at least as long as the
			(remaining) data that we've already read.

⌨️ 快捷键说明

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