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

📄 ssh2.c

📁 cryptlib安全工具包
💻 C
📖 第 1 页 / 共 3 页
字号:
/****************************************************************************
*																			*
*						cryptlib SSHv2 Session Management					*
*						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

/* 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 ALGO_STRING_INFO FAR_BSS algoStringKeyexTbl[] = {
	{ "diffie-hellman-group-exchange-sha1", 34, CRYPT_PSEUDOALGO_DHE },
	{ "diffie-hellman-group1-sha1", 26, CRYPT_ALGO_DH },
	{ NULL, 0, CRYPT_ALGO_NONE }, { NULL, 0, CRYPT_ALGO_NONE }
	};

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

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

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

static const ALGO_STRING_INFO FAR_BSS algoStringMACTbl[] = {
	{ "hmac-sha1", 9, CRYPT_ALGO_HMAC_SHA },
	{ "hmac-md5", 8, CRYPT_ALGO_HMAC_MD5 },
	{ NULL, 0, CRYPT_ALGO_NONE }, { NULL, 0, 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 */
	int noAlgoInfoEntries;
	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, algoStrInfoEntries, prefAlgo, getType ) \
	{ \
	memset( ( algoIDInfo ), 0, sizeof( ALGOID_INFO ) ); \
	( algoIDInfo )->algoInfo = ( algoStrInfo ); \
	( algoIDInfo )->noAlgoInfoEntries = ( algoStrInfoEntries ); \
	( algoIDInfo )->preferredAlgo = ( prefAlgo ); \
	( algoIDInfo )->getAlgoType = ( getType ); \
	}

static int readAlgoStringEx( STREAM *stream, ALGOID_INFO *algoIDInfo,
							 INOUT ERROR_INFO *errorInfo )
	{
	BOOLEAN foundMatch = FALSE;
	void *string = DUMMY_INIT_PTR;
	int stringPos, stringLen, substringLen, algoIndex = 999;
	int iterationCount = 0, status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isWritePtr( algoIDInfo, sizeof( ALGOID_INFO ) ) );
	assert( isReadPtr( algoIDInfo->algoInfo, \
					   sizeof( ALGO_STRING_INFO ) * \
							algoIDInfo->noAlgoInfoEntries ) );
	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 data and make sure that it's valid */
	status = stringLen = readUint32( stream );
	if( !cryptStatusError( status ) && stringLen < SSH2_MIN_ALGOID_SIZE )
		status = CRYPT_ERROR_BADDATA;	/* Quick-rej. for too-short strings */
	if( !cryptStatusError( status ) )
		status = sMemGetDataBlock( stream, &string, stringLen );
	if( cryptStatusOK( status ) )
		status = sSkip( stream, stringLen );
	if( cryptStatusError( status ) )
		{
		retExt( CRYPT_ERROR_BADDATA,
				( CRYPT_ERROR_BADDATA, errorInfo, 
				  "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 && \
			iterationCount++ < FAILSAFE_ITERATIONS_LARGE; 
		 stringPos += substringLen + 1 )
		{
		const BYTE *stringPtr = string;
		int currentAlgoIndex;

		/* Find the length of the next algorithm name */
		for( substringLen = stringPos; \
			 substringLen < stringLen && stringPtr[ substringLen ] != ','; \
			 substringLen++ );
		substringLen -= stringPos;
		if( substringLen < SSH2_MIN_ALGOID_SIZE || \
			substringLen > CRYPT_MAX_TEXTSIZE )
			{
			/* Empty or too-short algorithm name (or excessively long one), 
			   continue */
			continue;
			}

		/* Check whether it's something that we can handle */
		for( currentAlgoIndex = 0; 
			 algoIDInfo->algoInfo[ currentAlgoIndex ].name != NULL && \
				currentAlgoIndex < algoIDInfo->noAlgoInfoEntries && \
				currentAlgoIndex < FAILSAFE_ITERATIONS_MED;
			 currentAlgoIndex++ )
			{
			if( substringLen == algoIDInfo->algoInfo[ currentAlgoIndex ].nameLen && \
				!memcmp( algoIDInfo->algoInfo[ currentAlgoIndex ].name,
						 stringPtr + stringPos, substringLen ) )
				break;
			}
		if( currentAlgoIndex >= algoIDInfo->noAlgoInfoEntries || \
			currentAlgoIndex >= FAILSAFE_ITERATIONS_MED )
			retIntError();
		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:
				retIntError();
			}
		}
	if( iterationCount >= FAILSAFE_ITERATIONS_LARGE )
		retIntError();
	if( algoIndex > 50 )
		{
		char algoString[ 256 + 8 ];
		const int algoStringLen = min( stringLen, \
									   min( MAX_ERRMSG_SIZE - 80, 255 ) );

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

	/* 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,
					const int noAlgoStringEntries, CRYPT_ALGO_TYPE *algo, 
					const BOOLEAN useFirstMatch, 
					INOUT ERROR_INFO *errorInfo )
	{
	ALGOID_INFO algoIDInfo;
	int status;

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

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

	setAlgoIDInfo( &algoIDInfo, algoInfo, noAlgoStringEntries, 
				   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 */

⌨️ 快捷键说明

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