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

📄 ssh2_svr.c

📁 cryptlib是功能强大的安全工具集。允许开发人员快速在自己的软件中集成加密和认证服务。
💻 C
📖 第 1 页 / 共 3 页
字号:
/****************************************************************************
*																			*
*						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

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

/* SSHv2 algorithm names sent to the client, in preferred algorithm order.  
   Since we have a fixed algorithm for our public key (determined by the key
   type), we only send a single value for this that's evaluated at runtime,
   so there's no list for this defined.
   
   Note that these lists must match the algoStringXXXTbl values in ssh2.c */

static const FAR_BSS CRYPT_ALGO_TYPE algoKeyexList[] = {
	CRYPT_PSEUDOALGO_DHE, CRYPT_ALGO_DH, CRYPT_ALGO_NONE };
static const FAR_BSS CRYPT_ALGO_TYPE algoEncrList[] = {
	/* We can't list AES as an option because the peer can pick up anything
	   it wants from the list as its preferred choice, which means that if 
	   we're talking to any non-cryptlib implementation they always go for 
	   AES even though it doesn't currently have the provenance of 3DES.
	   Once AES passes the five-year test this option can be enabled */
	CRYPT_ALGO_3DES, /*CRYPT_ALGO_AES,*/ CRYPT_ALGO_BLOWFISH, 
	CRYPT_ALGO_CAST, CRYPT_ALGO_IDEA, CRYPT_ALGO_RC4, CRYPT_ALGO_NONE };
static const FAR_BSS CRYPT_ALGO_TYPE algoMACList[] = { 
	CRYPT_ALGO_HMAC_SHA, CRYPT_ALGO_HMAC_MD5, CRYPT_ALGO_NONE };
static const FAR_BSS CRYPT_ALGO_TYPE algoStringUserauthentList[] = {
	CRYPT_PSEUDOALGO_PASSWORD, CRYPT_ALGO_NONE };

/* Encode a list of available algorithms */

static int writeAlgoList( STREAM *stream, const CRYPT_ALGO_TYPE *algoList )
	{
	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 },
		{ "password", CRYPT_PSEUDOALGO_PASSWORD },
		{ "none", CRYPT_ALGO_NONE },
		};
	const char *availableAlgos[ 16 ];
	int noAlgos = 0, length = 0, algoIndex, status;

	/* Walk down the list of algorithms remembering the encoded name of each 
	   one that's available for use */
	for( algoIndex = 0; algoList[ algoIndex ] != CRYPT_ALGO_NONE && \
						algoIndex < 16; algoIndex++ )
		if( algoAvailable( algoList[ algoIndex ] ) || \
			isPseudoAlgo( algoList[ algoIndex ] ) )
			{
			int i;

			for( i = 0; algoStringMapTbl[ i ].algo != CRYPT_ALGO_NONE && \
						algoStringMapTbl[ i ].algo != algoList[ algoIndex ]; i++ );
			assert( algoStringMapTbl[ i ].algo != CRYPT_ALGO_NONE );
			assert( noAlgos < 16 );
			availableAlgos[ noAlgos++ ] = algoStringMapTbl[ i ].name;
			length += strlen( algoStringMapTbl[ i ].name );
			if( noAlgos > 1 )
				length++;			/* Room for comma delimiter */
			}

	/* Encode the list of available algorithms into a comma-separated string */
	status = writeUint32( stream, length );
	for( algoIndex = 0; \
		 cryptStatusOK( status ) && algoIndex < noAlgos; algoIndex++ )
		{
		if( algoIndex > 0 )
			sputc( stream, ',' );	/* Add comma delimiter */
		status = swrite( stream, availableAlgos[ algoIndex ], 
						 strlen( availableAlgos[ algoIndex ] ) );
		}
	return( status );
	}

/* Handle an ephemeral DH key exchange */

static int processDHE( SESSION_INFO *sessionInfoPtr,
					   SSH_HANDSHAKE_INFO *handshakeInfo )
	{
	STREAM stream;
	BYTE *keyPtr;
	void *keyexInfoPtr;
	const int offset = LENGTH_SIZE + sizeofString32( "ssh-dh", 6 );
	int keyPos, keyLength, keyexInfoLength, length, type, status;

	/* Get the keyex key request from the client:

		byte	type = SSH2_MSG_KEXDH_GEX_REQUEST_OLD
		uint32	n (bits)

	   or:
	
		byte	type = SSH2_MSG_KEXDH_GEX_REQUEST_NEW
		uint32	min (bits)
		uint32	n (bits)
		uint32	max (bits)

	   Portions of the the request info are hashed later as part of the 
	   exchange hash, so we have to save a copy for then.  We save the 
	   original encoded form, because some clients send non-integral lengths 
	   that don't survive the conversion from bits to bytes */
	length = readPacketSSH2( sessionInfoPtr, 
							 SSH2_MSG_KEXDH_GEX_REQUEST_OLD, 
							 ID_SIZE + UINT32_SIZE );
	if( cryptStatusError( length ) )
		return( length );
	sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
	type = sgetc( &stream );
	streamBookmarkSet( &stream, keyexInfoPtr, keyexInfoLength );
	if( type == SSH2_MSG_KEXDH_GEX_REQUEST_NEW )
		{
		/* It's a { min_length, length, max_length } sequence, save a copy 
		   and get the length value */
		readUint32( &stream );
		keyLength = readUint32( &stream );
		status = readUint32( &stream );
		}
	else
		/* It's a straight length, save a copy and get the length value */
		status = keyLength = readUint32( &stream );
	streamBookmarkComplete( &stream, keyexInfoLength );
	sMemDisconnect( &stream );
	if( cryptStatusError( status ) )
		retExt( sessionInfoPtr, status,
				"Invalid ephemeral DH key data request packet" );
	if( keyLength < MIN_PKCSIZE_BITS || \
		keyLength > bytesToBits( CRYPT_MAX_PKCSIZE ) )
		retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
				"Client requested invalid ephemeral DH key size %d bits",
				keyLength );
	memcpy( handshakeInfo->encodedReqKeySizes, keyexInfoPtr, 
			keyexInfoLength );
	handshakeInfo->encodedReqKeySizesLength = keyexInfoLength;
	handshakeInfo->requestedServerKeySize = bitsToBytes( keyLength );

	/* If the requested key size differs too much from the built-in default 
	   one, destroy the existing default DH key and load a new one of the 
	   appropriate size.  Things get quite confusing here because the spec 
	   is a schizophrenic mix of two different documents, one that specifies 
	   the behaviour for the original message format which uses a single 
	   length value and a second one that specifies the behaviour for the 
	   { min, n, max } combination.  The range option was added as an 
	   attempted fix for implementations that couldn't handle the single 
	   size option, but the real problem is that the server knows what key 
	   sizes are appropriate but the client has to make the choice, without 
	   any knowledge of what the server can actually handle.  Because of 
	   this the spec (in its n-only mindset, which also applies to the 
	   min/n/max version since it's the same document) contains assorted 
	   weasel-words that allow the server to choose any key size it feels 
	   like if the client sends a range indication that's inappropriate.  
	   Although the spec ends up saying that the server can do anything it 
	   feels like ("The server should return the smallest group it knows 
	   that is larger than the size the client requested.  If the server 
	   does not know a group that is larger than the client request, then it 
	   SHOULD return the largest group it knows"), we use a least-upper-
	   bound interpretation of the above, mostly because we store a range of 
	   fixed keys of different sizes and can always find something 
	   reasonably close to any (sensible) requested length */
	if( handshakeInfo->requestedServerKeySize < \
										SSH2_DEFAULT_KEYSIZE - 16 || \
		handshakeInfo->requestedServerKeySize > \
										SSH2_DEFAULT_KEYSIZE + 16 )
		{
		krnlSendNotifier( handshakeInfo->iServerCryptContext, 
						  IMESSAGE_DECREFCOUNT );
		status = initDHcontextSSH( &handshakeInfo->iServerCryptContext,
								   &handshakeInfo->serverKeySize, NULL, 0,
								   handshakeInfo->requestedServerKeySize );
		if( cryptStatusError( status ) )
			return( status );
		}

	/* Send the DH key values to the client:

		byte	type = SSH2_MSG_KEXDH_GEX_GROUP
		mpint	p
		mpint	g
		
	   Since this phase of the key negotiation exchanges raw key components 
	   rather than the standard SSH public-key format, we have to rewrite 
	   the public key before we can send it to the client */
	openPacketStreamSSH( &stream, sessionInfoPtr, CRYPT_USE_DEFAULT, 
						 SSH2_MSG_KEXDH_GEX_GROUP );
	streamBookmarkSet( &stream, keyPtr, keyPos );
	status = exportAttributeToStream( &stream, 
									  handshakeInfo->iServerCryptContext,
									  CRYPT_IATTRIBUTE_KEY_SSH2,
									  CRYPT_USE_DEFAULT );
	if( cryptStatusOK( status ) )
		{
		keyLength = keyPos;
		streamBookmarkComplete( &stream, keyLength );
		}
	if( cryptStatusError( status ) )
		return( status );
	memmove( keyPtr, keyPtr + offset, keyLength - offset );
	sseek( &stream, keyPos + keyLength );
	status = sendPacketSSH2( sessionInfoPtr, &stream, FALSE );
	sMemDisconnect( &stream );
	return( status );
	}


/* Handle user authentication:

	byte	type = SSH2_MSG_USERAUTH_REQUEST
	string	user_name
	string	service_name = "ssh-connection"
	string	method_name = "none" | "password"
	[ boolean	FALSE ]
	[ string	password ]

   The client can send a method-type of "none" to indicate that it'd like 
   the server to return a list of allowed authentication types, if we get a 
   packet of this kind we return our allowed types list.

   Unlike SSHv1, SSHv2 properly identifies public keys, however because of 
   its complexity (several more states added to the state machine because of 
   SSHv2's propensity for carrying out any negotiation it performs in lots 
   of little bits and pieces) we don't support this form of authentication 
   until someone specifically requests it */

static int processUserAuth( SESSION_INFO *sessionInfoPtr,
							SSH_HANDSHAKE_INFO *handshakeInfo )
	{
	STREAM stream;
	BYTE stringBuffer[ CRYPT_MAX_TEXTSIZE + 8 ];
	int length, stringLength, status;

	/* Get the userAuth packet from the client */
	length = readPacketSSH2( sessionInfoPtr, 
							 SSH2_MSG_USERAUTH_REQUEST, 
							 ID_SIZE + sizeofString32( "", 1 ) + \
							 sizeofString32( "", 8 ) + \
							 sizeofString32( "", 4 ) );
	if( cryptStatusError( length ) )
		return( length );
	sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
	sgetc( &stream );		/* Skip packet type */

	/* Process the user name */
	status = readString32( &stream, stringBuffer, &stringLength,
						   CRYPT_MAX_TEXTSIZE );
	if( cryptStatusError( status ) || \
		stringLength <= 0 || stringLength > CRYPT_MAX_TEXTSIZE )
		{
		sMemDisconnect( &stream );
		retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
				"Invalid user auth user name" );
		}
	updateSessionAttribute( &sessionInfoPtr->attributeList,

⌨️ 快捷键说明

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