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

📄 ssh2_cli.c

📁 老外写的加密库cryptlib(版本3.1)
💻 C
📖 第 1 页 / 共 3 页
字号:
/****************************************************************************
*																			*
*						cryptlib SSHv2 Session Management					*
*						Copyright Peter Gutmann 1998-2003					*
*																			*
****************************************************************************/

#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL )
  #include "crypt.h"
  #include "session.h"
  #include "ssh.h"
#elif defined( INC_CHILD )
  #include "../crypt.h"
  #include "../session/session.h"
  #include "../session/ssh.h"
#else
  #include "crypt.h"
  #include "session/session.h"
  #include "session/ssh.h"
#endif /* Compiler-specific includes */

#ifdef USE_SSH2

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

/* Generate/check an SSHv2 key fingerprint.  This is simply an MD5 hash of
   the server's key/certificate data */

static int processKeyFingerprint( SESSION_INFO *sessionInfoPtr,
								  const void *keyData,
								  const int keyDataLength )
	{
	HASHFUNCTION hashFunction;
	BYTE fingerPrint[ CRYPT_MAX_HASHSIZE ];
	int hashSize;

	getHashParameters( CRYPT_ALGO_MD5, &hashFunction, &hashSize );
	hashFunction( NULL, fingerPrint, keyData, keyDataLength, HASH_ALL );
	if( sessionInfoPtr->keyFingerprintSize > 0 )
		{
		/* In the unlikely event that the user has passed us an SHA-1
		   fingerprint (which isn't allowed by the spec, but no doubt
		   someone out there's using it based on the fact that the SSH
		   architecture draft suggests an SHA-1 fingerprint while the SSH
		   fingerprint draft requires an MD5 fingerprint), calculate that
		   instead */
		if( sessionInfoPtr->keyFingerprintSize == 20 )
			{
			getHashParameters( CRYPT_ALGO_SHA, &hashFunction, &hashSize );
			hashFunction( NULL, fingerPrint, keyData, keyDataLength,
						  HASH_ALL );
			}

		/* There's an existing fingerprint value, make sure that it matches
		   what we just calculated */
		if( memcmp( sessionInfoPtr->keyFingerprint, fingerPrint, hashSize ) )
			retExt( sessionInfoPtr, CRYPT_ERROR_WRONGKEY,
					"Server key fingerprint doesn't match requested "
					"fingerprint" );
		}
	else
		{
		/* Remember the value for the caller */
		memcpy( sessionInfoPtr->keyFingerprint, fingerPrint, hashSize );
		sessionInfoPtr->keyFingerprintSize = hashSize;
		}
	return( CRYPT_OK );
	}

/* Create a request for the appropriate type of service, either encrypted-
   telnet, SFTP, or port forwarding */

static int createOpenRequest( SESSION_INFO *sessionInfoPtr, 
							  BYTE *buffer )
	{
	BYTE *bufPtr = buffer + SSH2_HEADER_SIZE;
	int length, status;

	/* If the user has requested the use of a custom subsystem (and at the
	   moment the only one that's likely to be used is SFTP), request this 
	   from the server */
	if( sessionInfoPtr->sshSubsystemLength > 0 )
		{
		/*	...
			byte	type = SSH2_MSG_CHANNEL_REQUEST
			uint32	recipient_channel = 0
			string	request_name = "subsystem"
			boolean	want_reply = FALSE
			string	subsystem_name */
		*bufPtr++ = SSH2_MSG_CHANNEL_REQUEST;
		mputLong( bufPtr, 0 );
		bufPtr += encodeString( bufPtr, "subsystem", 0 );
		*bufPtr++ = 0;
		bufPtr += encodeString( bufPtr, sessionInfoPtr->sshSubsystem,
								sessionInfoPtr->sshSubsystemLength );
		return( wrapPacket( sessionInfoPtr, buffer,
							bufPtr - ( buffer + SSH2_HEADER_SIZE ) ) );
		}

	/* If the user has requested port-forwarding, request this from the 
	   server */
	if( sessionInfoPtr->sshPortForwardLength > 0 )
		{
		URL_INFO urlInfo;

		/*	...
			byte	type = SSH_MSG_GLOBAL_REQUEST
			string	request_name = "tcpip-forward"
			boolean	want_reply
			string	address_to_bind (e.g. "0.0.0.0")
			uint32	port_to_bind

		  The exact details of what we should send at this stage of the 
		  handshake are a bit unclear.  Most implementations go through the 
		  standard channel open process to provide a general control channel 
		  and then specify the port-forwarding in addition to this (see 
		  processChannelOpen() for the hoops we have to jump through to 
		  handle this).  This double-open can cause problems with some 
		  applications hanging off the tunnel because they may see the 
		  output from opening the channel and starting a shell as tunnelled 
		  data and get confused by it.  The safest option seems to be to 
		  only open the forwarded channel, without opening a (mostly 
		  redundant) control channel */
		sNetParseURL( &urlInfo, sessionInfoPtr->sshPortForward, 
					  sessionInfoPtr->sshPortForwardLength );
		bufPtr = buffer + SSH2_HEADER_SIZE;
		*bufPtr++ = SSH2_MSG_GLOBAL_REQUEST;
		bufPtr += encodeString( bufPtr, "tcpip-forward", 0 );
		*bufPtr++ = 0;
		bufPtr += encodeString( bufPtr, urlInfo.host, urlInfo.hostLen );
		mputLong( bufPtr, urlInfo.port );
		return( wrapPacket( sessionInfoPtr, buffer,
							bufPtr - ( buffer + SSH2_HEADER_SIZE ) ) );
		}

	/* It's a standard channel open:
		...
		byte	type = SSH2_MSG_CHANNEL_REQUEST
		uint32	recipient_channel = 0
		string	request_name = "pty-req"
		boolean	want_reply = FALSE
		string	TERM_environment_variable = "vt100"
		uint32	cols = 80
		uint32	rows = 24
		uint32	pixel_width = 0
		uint32	pixel_height = 0
		string	tty_mode_info = ""
		... */
	*bufPtr++ = SSH2_MSG_CHANNEL_REQUEST;
	mputLong( bufPtr, 0 );
	bufPtr += encodeString( bufPtr, "pty-req", 0 );
	*bufPtr++ = 0;
	bufPtr += encodeString( bufPtr, "vt100", 0 );/* Generic */
	mputLong( bufPtr, 80 );
	mputLong( bufPtr, 24 );				/* 24 x 80 */
	mputLong( bufPtr, 0 );
	mputLong( bufPtr, 0 );				/* No graphics capabilities */
	bufPtr += encodeString( bufPtr, "", 0 );/* No special TTY modes */
	status = length = wrapPacket( sessionInfoPtr, buffer,
								  bufPtr - ( buffer + SSH2_HEADER_SIZE ) );
	if( cryptStatusError( status ) )
		return( status );

	/*	...
		byte	type = SSH2_MSG_CHANNEL_REQUEST
		uint32	recipient_channel = 0
		string	request_name = "shell"
		boolean	want_reply = FALSE

	   This final request, once sent, moves the server into interactive 
	   session mode, if we're talking to a standard Unix server implementing 
	   a remote shell we could read the stdout data response from starting 
	   the shell but this may not be the case so we leave the response for 
	   the user to process explicitly */
	bufPtr = buffer + length + SSH2_HEADER_SIZE;
	*bufPtr++ = SSH2_MSG_CHANNEL_REQUEST;
	mputLong( bufPtr, 0 );
	bufPtr += encodeString( bufPtr, "shell", 0 );
	*bufPtr++ = 0;
	status = wrapPacket( sessionInfoPtr, buffer + length,
						 bufPtr - ( buffer + length + SSH2_HEADER_SIZE ) );
	return( cryptStatusError( status ) ? status : length + status );
	}

/****************************************************************************
*																			*
*						Client-side Connect Functions						*
*																			*
****************************************************************************/

/* Perform the initial part of the handshake with the server */

static int beginClientHandshake( SESSION_INFO *sessionInfoPtr,
								 SSH_HANDSHAKE_INFO *handshakeInfo )
	{
	MESSAGE_CREATEOBJECT_INFO createInfo;
	KEYAGREE_PARAMS keyAgreeParams;
	RESOURCE_DATA msgData;
	BYTE *bufPtr;
	int length, length2, serverKeyexLength, clientKeyexLength;
	int mpiLength, status;

	/* The higher-level code has already read the server session info, send
	   back our own version info (SSHv2 uses a CR and LF as terminator,
	   which differs from SSHv1) */
	length = strlen( SSH2_ID_STRING );
	memcpy( sessionInfoPtr->sendBuffer, SSH2_ID_STRING "\r\n", length + 2 );
	status = swrite( &sessionInfoPtr->stream, sessionInfoPtr->sendBuffer,
					 length + 2 );
	if( cryptStatusError( status ) )
		{
		sNetGetErrorInfo( &sessionInfoPtr->stream,
						  sessionInfoPtr->errorMessage,
						  &sessionInfoPtr->errorCode );
		return( status );
		}

	/* SSHv2 hashes parts of the handshake messages for integrity-protection
	   purposes, so we hash the ID strings (first our client string, then the
	   server string that we read previously) encoded as SSH string values */
	hashAsString( handshakeInfo->iExchangeHashcontext, SSH2_ID_STRING,
				  length );
	hashAsString( handshakeInfo->iExchangeHashcontext,
				  sessionInfoPtr->receiveBuffer,
				  strlen( sessionInfoPtr->receiveBuffer ) );

	/* While we wait for the server to digest our version info and send
	   back its response, we can create the context with the DH key and
	   perform phase 1 of the DH key agreement process */
	status = initDHcontext( &handshakeInfo->iServerCryptContext,
							&handshakeInfo->serverKeySize, NULL, 0,
							CRYPT_USE_DEFAULT );
	if( cryptStatusOK( status ) )
		{
		memset( &keyAgreeParams, 0, sizeof( KEYAGREE_PARAMS ) );
		status = krnlSendMessage( handshakeInfo->iServerCryptContext,
								  IMESSAGE_CTX_ENCRYPT, &keyAgreeParams,
								  sizeof( KEYAGREE_PARAMS ) );
		}
	if( cryptStatusError( status ) )
		return( status );

	/* Process the server hello */
	status = processHello( sessionInfoPtr, handshakeInfo, 
						   &serverKeyexLength, FALSE );
	if( cryptStatusError( status ) )
		return( status );

	/* Build the client hello and DH phase 1 keyex packet:

		byte		type = SSH2_MSG_KEXINIT
		byte[16]	cookie
		string		keyex algorithms = DH
		string		pubkey algorithms
		string		client_crypto algorithms
		string		server_crypto algorithms
		string		client_mac algorithms
		string		server_mac algorithms
		string		client_compression algorithms = "none"
		string		server_compression algorithms = "none"
		string		client_language = {}
		string		server_language = {}
		boolean		first_keyex_packet_follows = FALSE
		uint32		reserved = 0
		...

	   The SSH spec leaves the order in which things happen ambiguous, in 
	   order to save a whole round trip it has provisions for both sides
	   shouting at each other and then a complex interlock process where
	   bits of the initial exchange can be discarded and retried if necessary.
	   This is ugly and error-prone, so what we do is wait for the server
	   hello (already done earlier), choose known-good algorithms, and then
	   send the client hello immediately followed by the client keyex.  
	   Since we wait for the server to speak first, we can choose parameters 
	   that are accepted the first time.  In theory this means that we can 
	   set keyex_follows to true (since a correct keyex packet always 
	   follows the hello), however because of the nondeterministic initial 
	   exchange the spec requires that a (guessed) keyex be discarded by the 
	   server if the hello doesn't match (even if the keyex does):
	   
		svr: hello
		client: matched hello, keyex
		svr: (discard keyex)

	   To avoid this problem, we set keyex_follows to false to make it clear
	   to the server that the keyex is the real thing and shouldn't be 
	   discarded */
	bufPtr = sessionInfoPtr->sendBuffer + SSH2_HEADER_SIZE;
	*bufPtr++ = SSH2_MSG_KEXINIT;
	setMessageData( &msgData, bufPtr, SSH2_COOKIE_SIZE );
	krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_GETATTRIBUTE_S,
					 &msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE );
	bufPtr += SSH2_COOKIE_SIZE;
	if( handshakeInfo->requestedServerKeySize > 0 )
		bufPtr += encodeString( bufPtr, "diffie-hellman-group-exchange-sha1", 
								0 );
	else
		putAlgoID( &bufPtr, CRYPT_ALGO_DH );
	putAlgoID( &bufPtr, handshakeInfo->pubkeyAlgo );
	putAlgoID( &bufPtr, sessionInfoPtr->cryptAlgo );
	putAlgoID( &bufPtr, sessionInfoPtr->cryptAlgo );
	putAlgoID( &bufPtr, sessionInfoPtr->integrityAlgo );
	putAlgoID( &bufPtr, sessionInfoPtr->integrityAlgo );
	putAlgoID( &bufPtr, CRYPT_ALGO_NONE );
	putAlgoID( &bufPtr, CRYPT_ALGO_NONE );
	mputLong( bufPtr, 0 );	/* No language tag */
	mputLong( bufPtr, 0 );
	*bufPtr++ = 0;			/* Tell the server not to discard the packet */
	mputLong( bufPtr, 0 );	/* Reserved */
	clientKeyexLength = ( int ) ( bufPtr - \
							( sessionInfoPtr->sendBuffer + SSH2_HEADER_SIZE ) );
	status = length = wrapPacket( sessionInfoPtr,
							sessionInfoPtr->sendBuffer, clientKeyexLength );
	if( cryptStatusError( status ) )
		return( status );

	/* Hash the client and server hello messages.  We have to do this now 
	   (rather than deferring it until we're waiting on network traffic from
	   the server) because they may get overwritten by the keyex negotiation 
	   if we're using a non-builtin DH key value */
	hashAsString( handshakeInfo->iExchangeHashcontext,
				  sessionInfoPtr->sendBuffer + SSH2_HEADER_SIZE,

⌨️ 快捷键说明

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