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

📄 ssh2.c

📁 cryptlib是功能强大的安全工具集。允许开发人员快速在自己的软件中集成加密和认证服务。
💻 C
📖 第 1 页 / 共 2 页
字号:
/****************************************************************************
*																			*
*						cryptlib SSHv2 Session Management					*
*						Copyright Peter Gutmann 1998-2004					*
*																			*
****************************************************************************/

#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL )
  #include "crypt.h"
  #include "misc_rw.h"
  #include "session.h"
  #include "ssh.h"
#elif defined( INC_CHILD )
  #include "../crypt.h"
  #include "../misc/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_SSH2

/* Tables mapping SSHv2 algorithm names to cryptlib algorithm IDs, in 
   preferred algorithm order.  See the comment in ssh2_svr.c for the reason
   behind the difference in encryption algorithm tables for client and 
   server */

static const FAR_BSS ALGO_STRING_INFO algoStringKeyexTbl[] = {
	{ "diffie-hellman-group-exchange-sha1", CRYPT_PSEUDOALGO_DHE },
	{ "diffie-hellman-group1-sha1", CRYPT_ALGO_DH },
	{ NULL, CRYPT_ALGO_NONE }
	};

static const FAR_BSS ALGO_STRING_INFO algoStringCoprTbl[] = {
	{ "none", CRYPT_PSEUDOALGO_COPR },
	{ NULL, CRYPT_ALGO_NONE }
	};

static const FAR_BSS ALGO_STRING_INFO algoStringPubkeyTbl[] = {
	{ "ssh-rsa", CRYPT_ALGO_RSA },
	{ "ssh-dss", CRYPT_ALGO_DSA },
	{ NULL, CRYPT_ALGO_NONE }
	};

static const FAR_BSS ALGO_STRING_INFO algoStringEncrTblClient[] = {
	{ "3des-cbc", CRYPT_ALGO_3DES },
	{ "aes128-cbc", CRYPT_ALGO_AES },
	{ "blowfish-cbc", CRYPT_ALGO_BLOWFISH },
	{ "cast128-cbc", CRYPT_ALGO_CAST },
	{ "idea-cbc", CRYPT_ALGO_IDEA },
	{ "arcfour", CRYPT_ALGO_RC4 },
	{ NULL, CRYPT_ALGO_NONE }
	};
static const FAR_BSS ALGO_STRING_INFO algoStringEncrTblServer[] = {
	{ "3des-cbc", CRYPT_ALGO_3DES },
	{ "blowfish-cbc", CRYPT_ALGO_BLOWFISH },
	{ "cast128-cbc", CRYPT_ALGO_CAST },
	{ "idea-cbc", CRYPT_ALGO_IDEA },
	{ "arcfour", CRYPT_ALGO_RC4 },
	{ NULL, CRYPT_ALGO_NONE }
	};

static const FAR_BSS ALGO_STRING_INFO algoStringMACTbl[] = {
	{ "hmac-sha1", CRYPT_ALGO_HMAC_SHA },
	{ "hmac-md5", CRYPT_ALGO_HMAC_MD5 },
	{ NULL, CRYPT_ALGO_NONE }
	};

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

/* Convert an SSHv2 algorithm list to a cryptlib ID in preferred-algorithm
   order.  For some bizarre reason the algorithm information is communicated
   as a comma-delimited list (in an otherwise binary protocol), so we have
   to unpack and pack them into this cumbersome format alongside just
   choosing which algorithm to use.  In addition, the algorithm selection
   mechanism differs depending on whether we're the client or server, and 
   what set of algorithms we're matching.  Unlike SSL, which uses the 
   offered-suites/chosen-suites mechanism, in SSHv2 both sides offer a 
   selection of cipher suites and the server chooses the first one that 
   appears on both it and the client's list, with special-case handling for
   the keyex and signature algorithms if the match isn't the first one on 
   the list.  This means that the client can choose as it pleases from the 
   server's list if it waits for the server hello (see the comment in the 
   client/server hello handling code on the annoying nature of this portion 
   of the SSHv2 handshake), but the server has to perform a complex double-
   match of its own vs.the client's list.  The cases that we need to handle 
   are:

	get the first matching algorithm, used by the server to match the client.

	get the first matching algorithm and warn if it isn't the first one on 
		the list of possible algorithms, used by the server to match the 
		client for the keyex and public-key algorithms.

	get the best matching algorithm (that is, the one corresponding to the
		strongest crypto mechanism), used by the client to match the server.

   This is a sufficiently complex and screwball function that we need to 
   define a composite structure to pass all of the control information in 
   and out */

typedef enum {
	GETALGO_NONE,			/* No match action */
	GETALGO_FIRST_MATCH,	/* Get first matching algorithm */
	GETALGO_FIRST_MATCH_WARN,/* Get first matching algo, warn if not first */
	GETALGO_BEST_MATCH,		/* Get best matching algorithm */
	GETALGO_LAST			/* Last possible match action */
	} GETALGO_TYPE;

typedef struct {
	const ALGO_STRING_INFO *algoInfo;/* Algorithm selection info */
	CRYPT_ALGO_TYPE preferredAlgo;	/* Preferred algo for first-match */
	GETALGO_TYPE getAlgoType;		/* Type of match to perform */
	CRYPT_ALGO_TYPE algo;			/* Matched algorithm */
	BOOLEAN prefAlgoMismatch;		/* First match != preferredAlgo */
	} ALGOID_INFO;

#define setAlgoIDInfo( algoIDInfo, algoStrInfo, prefAlgo, getType ) \
	{ \
	memset( ( algoIDInfo ), 0, sizeof( ALGOID_INFO ) ); \
	( algoIDInfo )->algoInfo = ( algoStrInfo ); \
	( algoIDInfo )->preferredAlgo = ( prefAlgo ); \
	( algoIDInfo )->getAlgoType = ( getType ); \
	}

static int readAlgoStringEx( STREAM *stream, ALGOID_INFO *algoIDInfo, 
							 void *errorInfo )
	{
	BOOLEAN foundMatch = FALSE;
	const char *string;
	int stringPos, stringLen, substringLen, algoIndex = 999, status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isWritePtr( algoIDInfo, sizeof( ALGOID_INFO ) ) );
	assert( isReadPtr( algoIDInfo->algoInfo, sizeof( ALGO_STRING_INFO ) ) );
	assert( ( algoIDInfo->getAlgoType == GETALGO_BEST_MATCH && \
			  algoIDInfo->preferredAlgo == CRYPT_ALGO_NONE ) || \
			( algoIDInfo->getAlgoType == GETALGO_FIRST_MATCH ) ||
			( algoIDInfo->getAlgoType == GETALGO_FIRST_MATCH_WARN && \
			  ( algoIDInfo->preferredAlgo > CRYPT_ALGO_NONE && \
				algoIDInfo->preferredAlgo < CRYPT_ALGO_LAST ) ) );

	/* Get the string length and make sure that it's valid */
	status = stringLen = readUint32( stream );
	if( !cryptStatusError( status ) )
		{
		string = sMemBufPtr( stream );
		status = sSkip( stream, stringLen );
		if( cryptStatusOK( status  ) && stringLen < SSH2_MIN_ALGOID_SIZE )
			/* Quick-reject for too-short strings */
			status = CRYPT_ERROR_BADDATA;
		}
	if( cryptStatusError( status ) )
		retExt( errorInfo, CRYPT_ERROR_BADDATA,
				"Invalid algorithm ID string" );

	/* Walk down the string looking for a recognised algorithm.  Since our
	   preference may not match the other side's preferences, we have to walk
	   down the entire list to find our preferred choice:

		"algo1,algo2,algo3,algoN"
			   ^   ^		   ^
			   |substrLen	   |
		  stringPos			stringLen */
	for( stringPos = 0; stringPos < stringLen && !foundMatch; \
		 stringPos += substringLen + 1 )
		{
		int currentAlgoIndex;

		/* Find the length of the next algorithm name */
		for( substringLen = stringPos; \
			 substringLen < stringLen && string[ substringLen ] != ','; \
			 substringLen++ );
		substringLen -= stringPos;
		if( substringLen < SSH2_MIN_ALGOID_SIZE )
			continue;	/* Empty or too-short algorithm name, continue */

		/* Check whether it's something that we can handle */
		for( currentAlgoIndex = 0; \
			 algoIDInfo->algoInfo[ currentAlgoIndex ].name != NULL; \
			 currentAlgoIndex++ )
			if( substringLen == strlen( algoIDInfo->algoInfo[ currentAlgoIndex ].name ) && \
				!memcmp( algoIDInfo->algoInfo[ currentAlgoIndex ].name, 
						 string + stringPos, substringLen ) )
				break;
		if( algoIDInfo->algoInfo[ currentAlgoIndex ].name == NULL || \
			( !isPseudoAlgo( algoIDInfo->algoInfo[ currentAlgoIndex ].algo ) && \
			  !algoAvailable( algoIDInfo->algoInfo[ currentAlgoIndex ].algo ) ) )
			{
			/* No match or the matched algorithm isn't available in this 
			   build, if we have to match the first algorithm on the list
			   remember to warn the caller, then move on to the next name */
			if( algoIDInfo->getAlgoType == GETALGO_FIRST_MATCH_WARN )
				algoIDInfo->prefAlgoMismatch = TRUE;
			continue;
			}

		switch( algoIDInfo->getAlgoType )
			{
			case GETALGO_BEST_MATCH:
				/* If we're looking for the best (highest-ranked algorithm) 
				   match, see whether the current match ranks higher than 
				   the existing one */
				if( currentAlgoIndex < algoIndex )
					{
					algoIndex = currentAlgoIndex;
					if( algoIndex <= 0 )
						foundMatch = TRUE;	/* Gruener werd's net */
					}
				break;

			case GETALGO_FIRST_MATCH:
				/* If we've found an acceptable algorithm, remember it and 
				   exit */
				if( algoIDInfo->preferredAlgo == CRYPT_ALGO_NONE || \
					algoIDInfo->preferredAlgo == \
							algoIDInfo->algoInfo[ currentAlgoIndex ].algo )
					{
					algoIndex = currentAlgoIndex;
					foundMatch = TRUE;
					}
				break;

			case GETALGO_FIRST_MATCH_WARN:
				/* If we found the algorithm that we're after, remember it 
				   and exit */
				if( algoIDInfo->preferredAlgo != \
							algoIDInfo->algoInfo[ currentAlgoIndex ].algo )
					/* We didn't match the first algorithm on the list, warn 
					   the caller */
					algoIDInfo->prefAlgoMismatch = TRUE;
				algoIndex = currentAlgoIndex;
				foundMatch = TRUE;
				break;

			default:
				assert( NOTREACHED );
			}
		}
	if( algoIndex > 50 )
		{
		char algoString[ 256 ];

		/* We couldn't find anything to use, tell the caller what was 
		   available */
		if( stringLen > min( MAX_ERRMSG_SIZE - 80, 255 ) )
			stringLen = min( MAX_ERRMSG_SIZE - 80, 255 );
		memcpy( algoString, string, stringLen );
		algoString[ stringLen ] = '\0';
		retExt( errorInfo, CRYPT_ERROR_NOTAVAIL,
				"No algorithm compatible with the remote system's selection "
				"was found : %s", sanitiseString( algoString ) );
		}

	/* We found a more-preferred algorithm than the default, go with that */
	algoIDInfo->algo = algoIDInfo->algoInfo[ algoIndex ].algo;
	return( CRYPT_OK );
	}

int readAlgoString( STREAM *stream, const ALGO_STRING_INFO *algoInfo, 
					CRYPT_ALGO_TYPE *algo, const BOOLEAN useFirstMatch, 
					void *errorInfo )
	{
	ALGOID_INFO algoIDInfo;
	int status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isReadPtr( algoInfo, sizeof( ALGO_STRING_INFO ) ) );
	assert( isWritePtr( algo, sizeof( CRYPT_ALGO_TYPE ) ) );

	/* Clear return value */
	*algo = CRYPT_ALGO_NONE;

	setAlgoIDInfo( &algoIDInfo, algoInfo, CRYPT_ALGO_NONE, 
				   useFirstMatch ? GETALGO_FIRST_MATCH : \
								   GETALGO_BEST_MATCH );
	status = readAlgoStringEx( stream, &algoIDInfo, errorInfo );
	if( cryptStatusOK( status ) )
		*algo = algoIDInfo.algo;
	return( status );
	}

/* Algorithms used to protect data packets are used in pairs, one for 
   incoming and the other for outgoing data.  To keep things simple we 
   always force these to be the same, first reading the algorithm for one 
   direction and then making sure that the one for the other direction 
   matches this.  All implementations seem to do this anyway, many aren't 
   even capable of supporting asymmetric algorithm choices */

static int readAlgoStringPair( STREAM *stream, const ALGO_STRING_INFO *algoInfo, 
							   CRYPT_ALGO_TYPE *algo, const BOOLEAN isServer, 
							   void *errorInfo )
	{
	CRYPT_ALGO_TYPE pairPreferredAlgo;
	ALGOID_INFO algoIDInfo;
	int status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isReadPtr( algoInfo, sizeof( ALGO_STRING_INFO ) ) );

	/* Clear return value */
	if( algo != NULL )
		*algo = CRYPT_ALGO_NONE;

	/* Get the first algorithm */
	setAlgoIDInfo( &algoIDInfo, algoInfo, CRYPT_ALGO_NONE,
				   isServer ? GETALGO_FIRST_MATCH : GETALGO_BEST_MATCH );
	status = readAlgoStringEx( stream, &algoIDInfo, errorInfo );
	if( cryptStatusError( status ) )
		return( status );
	pairPreferredAlgo = algoIDInfo.algo;

	/* Get the matched second algorithm */
	setAlgoIDInfo( &algoIDInfo, algoInfo, pairPreferredAlgo, 
				   GETALGO_FIRST_MATCH );
	status = readAlgoStringEx( stream, &algoIDInfo, errorInfo );
	if( cryptStatusError( status ) )
		return( status );
	if( pairPreferredAlgo != algoIDInfo.algo )
		retExt( errorInfo, CRYPT_ERROR_BADDATA,
				"Client algorithm %d doesn't match server algorithm %d in "
				"algorithm pair", pairPreferredAlgo, algoIDInfo.algo );
	if( algo != NULL )
		*algo = algoIDInfo.algo;
	return( status );
	}

/* Convert a cryptlib algorithm ID to an SSHv2 algorithm name */

int writeAlgoString( STREAM *stream, const CRYPT_ALGO_TYPE algo )
	{
	static const FAR_BSS ALGO_STRING_INFO algoStringMapTbl[] = {
		{ "ssh-rsa", CRYPT_ALGO_RSA },
		{ "ssh-dss", CRYPT_ALGO_DSA },
		{ "3des-cbc", CRYPT_ALGO_3DES },
		{ "aes128-cbc", CRYPT_ALGO_AES },
		{ "blowfish-cbc", CRYPT_ALGO_BLOWFISH },
		{ "cast128-cbc", CRYPT_ALGO_CAST },
		{ "idea-cbc", CRYPT_ALGO_IDEA },
		{ "arcfour", CRYPT_ALGO_RC4 },
		{ "diffie-hellman-group-exchange-sha1", CRYPT_PSEUDOALGO_DHE },
		{ "diffie-hellman-group1-sha1", CRYPT_ALGO_DH },
		{ "hmac-sha1", CRYPT_ALGO_HMAC_SHA },
		{ "hmac-md5", CRYPT_ALGO_HMAC_MD5 },
		{ "none", CRYPT_PSEUDOALGO_COPR },
		{ "none", CRYPT_ALGO_LAST }		/* Catch-all */
		};
	int i;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( algo >= CRYPT_ALGO_NONE && algo < CRYPT_ALGO_LAST );

	/* Locate the name for this algorithm and encode it as an SSH string */
	for( i = 0; algoStringMapTbl[ i ].algo != CRYPT_ALGO_LAST && \
				algoStringMapTbl[ i ].algo != algo; i++ );
	assert( algoStringMapTbl[ i ].algo != CRYPT_ALGO_LAST );
	return( writeString32( stream, algoStringMapTbl[ i ].name, 0 ) );
	}

/****************************************************************************
*																			*
*							Miscellaneous Functions							*
*																			*
****************************************************************************/

/* Process a client/server hello packet */

int processHelloSSH( SESSION_INFO *sessionInfoPtr, 
					 SSH_HANDSHAKE_INFO *handshakeInfo, int *keyexLength,
					 const BOOLEAN isServer )
	{
	STREAM stream;
	ALGOID_INFO algoIDInfo;
	BOOLEAN preferredAlgoMismatch = FALSE, guessedKeyex = FALSE;
	int length, status;

	/* Process the client/server hello:

		byte		type = SSH2_MSG_KEXINIT
		byte[16]	cookie
		string		keyex algorithms
		string		pubkey algorithms
		string		client_crypto algorithms
		string		server_crypto algorithms
		string		client_mac algorithms
		string		server_mac algorithms
		string		client_compression algorithms
		string		server_compression algorithms
		string		client_language
		string		server_language
		boolean		first_keyex_packet_follows
		uint32		reserved

	   The cookie isn't explicitly processed as with SSHv1 since SSHv2
	   hashes the entire hello message */
	length = readPacketSSH2( sessionInfoPtr, SSH2_MSG_KEXINIT, 128 );
	if( cryptStatusError( length ) )
		return( length );
	*keyexLength = length;
	sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
	sSkip( &stream, ID_SIZE + SSH2_COOKIE_SIZE );

⌨️ 快捷键说明

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