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

📄 ssh2_cli.c

📁 cryptlib是功能强大的安全工具集。允许开发人员快速在自己的软件中集成加密和认证服务。
💻 C
📖 第 1 页 / 共 4 页
字号:
	   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 */
	openPacketStreamSSH( &stream, sessionInfoPtr, CRYPT_USE_DEFAULT, 
						 SSH2_MSG_KEXINIT );
	streamBookmarkSetFullPacket( &stream, clientHelloPtr, clientHelloLength );
	exportAttributeToStream( &stream, SYSTEM_OBJECT_HANDLE,
							 CRYPT_IATTRIBUTE_RANDOM_NONCE, 
							 SSH2_COOKIE_SIZE );
	writeAlgoString( &stream,  ( handshakeInfo->requestedServerKeySize > 0 ) ? \
					 CRYPT_PSEUDOALGO_DHE : CRYPT_ALGO_DH );
	writeAlgoString( &stream, handshakeInfo->pubkeyAlgo );
	writeAlgoString( &stream, sessionInfoPtr->cryptAlgo );
	writeAlgoString( &stream, sessionInfoPtr->cryptAlgo );
	writeAlgoString( &stream, sessionInfoPtr->integrityAlgo );
	writeAlgoString( &stream, sessionInfoPtr->integrityAlgo );
	writeAlgoString( &stream, CRYPT_PSEUDOALGO_COPR );
	writeAlgoString( &stream, CRYPT_PSEUDOALGO_COPR );
	writeUint32( &stream, 0 );	/* No language tag */
	writeUint32( &stream, 0 );
	sputc( &stream, 0 );		/* Tell the server not to discard the packet */
	writeUint32( &stream, 0 );	/* Reserved */
	streamBookmarkComplete( &stream, clientHelloLength );
	status = wrapPacketSSH2( sessionInfoPtr, &stream, 0 );
	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 
	   data if we're using a non-builtin DH key value */
	hashAsString( handshakeInfo->iExchangeHashcontext, clientHelloPtr, 
				  clientHelloLength );
	status = hashAsString( handshakeInfo->iExchangeHashcontext,
						   sessionInfoPtr->receiveBuffer, serverHelloLength );
	if( cryptStatusError( status ) )
		return( status );

	/* If we're using a non-builtin DH key value, request the keyex key from 
	   the server.  This requires disconnecting and re-connecting the stream
	   since it exchanges further data with the server, so if there's an 
	   error return we don't disconnect the stream before we exit */
	if( handshakeInfo->requestedServerKeySize > 0 )
		{
		status = processDHE( sessionInfoPtr, handshakeInfo, &stream, 
							 &keyAgreeParams );
		if( cryptStatusError( status ) )
			return( status );
		}

	/*	...
		byte	type = SSH2_MSG_KEXDH_INIT / SSH2_MSG_KEXDH_GEX_INIT
		mpint	y */
	packetOffset = continuePacketStreamSSH( &stream, \
						( handshakeInfo->requestedServerKeySize > 0 ) ? \
							SSH2_MSG_KEXDH_GEX_INIT : SSH2_MSG_KEXDH_INIT );
	streamBookmarkSet( &stream, keyexPtr, keyexLength );
	writeInteger32( &stream, keyAgreeParams.publicValue,
							 keyAgreeParams.publicValueLen );
	streamBookmarkComplete( &stream, keyexLength );
	status = wrapPacketSSH2( sessionInfoPtr, &stream, packetOffset );
	if( cryptStatusOK( status ) )
		/* Send the whole mess to the server.  Since SSH, unlike SSL, 
		   requires that each packet in a multi-packet group be wrapped as a 
		   separate packet, we have to first assemble the packets via 
		   wrapPacket() and then send them in a group via sendPacket() with 
		   the send-only flag set */
		status = sendPacketSSH2( sessionInfoPtr, &stream, TRUE );
	sMemDisconnect( &stream );
	if( cryptStatusError( status ) )
		return( status );

	/* Save the MPI-encoded client DH keyex value for later, when we need to
	   hash it */
	memcpy( handshakeInfo->clientKeyexValue, keyexPtr, keyexLength );
	handshakeInfo->clientKeyexValueLength = keyexLength;

	/* Set up PKC info while we wait for the server to process our 
	   response */
	setMessageCreateObjectInfo( &createInfo, handshakeInfo->pubkeyAlgo );
	status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
							  IMESSAGE_DEV_CREATEOBJECT, &createInfo,
							  OBJECT_TYPE_CONTEXT );
	if( cryptStatusOK( status ) )
		sessionInfoPtr->iKeyexAuthContext = createInfo.cryptHandle;
	return( status );
	}

/* Exchange keys with the server */

static int exchangeClientKeys( SESSION_INFO *sessionInfoPtr,
							   SSH_HANDSHAKE_INFO *handshakeInfo )
	{
	CRYPT_ALGO_TYPE pubkeyAlgo;
	STREAM stream;
	RESOURCE_DATA msgData;
	void *keyPtr, *keyBlobPtr, *sigPtr;
	int keyLength, keyBlobLength, sigLength, length, status;

	/* Process the DH phase 2 keyex packet:

		byte		type = SSH2_MSG_KEXDH_REPLY / SSH2_MSG_KEXDH_GEX_REPLY
		string		[ server key/certificate ]
			string	"ssh-rsa"	"ssh-dss"
			mpint	e			p
			mpint	n			q
			mpint				g
			mpint				y
		mpint		y'
		string		[ signature of handshake data ]
			string	"ssh-rsa"	"ssh-dss"
			string	signature	signature

	   First, we read and hash the server key/certificate.  Since this is
	   already encoded as an SSH string, we can hash it directly */
	length = readPacketSSH2( sessionInfoPtr, 
					( handshakeInfo->requestedServerKeySize > 0 ) ? \
					SSH2_MSG_KEXDH_GEX_REPLY : SSH2_MSG_KEXDH_REPLY,
					ID_SIZE + LENGTH_SIZE + sizeofString32( "", 6 ) + \
					sizeofString32( "", 1 ) + \
					sizeofString32( "", bitsToBytes( MIN_PKCSIZE_BITS ) ) + \
					sizeofString32( "", bitsToBytes( MIN_PKCSIZE_BITS ) ) + \
					LENGTH_SIZE + sizeofString32( "", 6 ) + 40 );
	if( cryptStatusError( length ) )
		return( length );
	sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
	sgetc( &stream );		/* Skip packet type */
	streamBookmarkSet( &stream, keyPtr, keyLength );
	readUint32( &stream );	/* Server key data size */
	status = readAlgoString( &stream, handshakeInfo->algoStringPubkeyTbl,
							 &pubkeyAlgo, TRUE, sessionInfoPtr );
	if( cryptStatusError( status ) )
		{
		sMemDisconnect( &stream );
		return( status );
		}
	if( pubkeyAlgo != handshakeInfo->pubkeyAlgo )
		{
		sMemDisconnect( &stream );
		retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
				"Invalid DH phase 2 public key algorithm %d, expected %d",
				pubkeyAlgo, handshakeInfo->pubkeyAlgo );
		}
	streamBookmarkSet( &stream, keyBlobPtr, keyBlobLength );
	if( pubkeyAlgo == CRYPT_ALGO_RSA )
		{
		/* RSA e, n */
		readInteger32( &stream, NULL, NULL, 1, CRYPT_MAX_PKCSIZE );
		status = readInteger32( &stream, NULL, NULL, 
								bitsToBytes( MIN_PKCSIZE_BITS ), 
								CRYPT_MAX_PKCSIZE );
		}
	else
		{
		/* DSA p, q, g, y */
		readInteger32( &stream, NULL, NULL, bitsToBytes( MIN_PKCSIZE_BITS ), 
					   CRYPT_MAX_PKCSIZE );
		readInteger32( &stream, NULL, NULL, 1, CRYPT_MAX_PKCSIZE );
		readInteger32( &stream, NULL, NULL, 1, CRYPT_MAX_PKCSIZE );
		status = readInteger32( &stream, NULL, NULL, 
								bitsToBytes( MIN_PKCSIZE_BITS ), 
								CRYPT_MAX_PKCSIZE );
		}
	streamBookmarkComplete( &stream, keyBlobLength );
	streamBookmarkComplete( &stream, keyLength );
	if( cryptStatusError( status ) )
		{
		sMemDisconnect( &stream );
		retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
				"Invalid DH phase 2 packet" );
		}
	setMessageData( &msgData, keyPtr, keyLength );
	status = krnlSendMessage( sessionInfoPtr->iKeyexAuthContext,
							  IMESSAGE_SETATTRIBUTE_S, &msgData,
							  CRYPT_IATTRIBUTE_KEY_SSH2 );
	if( cryptStatusError( status ) )
		{
		sMemDisconnect( &stream );
		retExt( sessionInfoPtr, cryptArgError( status ) ? \
				CRYPT_ERROR_BADDATA : status, 
				"Invalid server key/certificate" );
		}
	status = krnlSendMessage( handshakeInfo->iExchangeHashcontext,
							  IMESSAGE_CTX_HASH, keyPtr, keyLength );
	if( cryptStatusOK( status ) )
		/* The fingerprint is computed from the "key blob", which is 
		   different from the server key.  The server key is the full key, 
		   while the "key blob" is only the raw key components (e, n for 
		   RSA, p, q, g, y for DSA).  Note that, as with the old PGP 2.x key 
		   hash mechanism, this allows key spoofing (although it isn't quite 
		   as bad as the PGP 2.x key fingerprint mechanism) since it doesn't 
		   hash an indication of the key type or format */
		status = processKeyFingerprint( sessionInfoPtr,
										keyBlobPtr, keyBlobLength );
	if( cryptStatusError( status ) )
		{
		sMemDisconnect( &stream );
		return( status );
		}

	/* Read the server DH keyex value and complete the DH key agreement */
	status = readRawObject32( &stream, handshakeInfo->serverKeyexValue,
							  &handshakeInfo->serverKeyexValueLength,
							  sizeof( handshakeInfo->serverKeyexValue ) );
	if( cryptStatusError( status ) || \
		!isValidDHsize( handshakeInfo->clientKeyexValueLength,
						handshakeInfo->serverKeySize, LENGTH_SIZE ) )
		{
		sMemDisconnect( &stream );
		retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
				"Invalid DH phase 2 keyex value" );
		}
	status = completeKeyex( sessionInfoPtr, handshakeInfo, FALSE );
	if( cryptStatusError( status ) )
		{
		sMemDisconnect( &stream );
		return( status );
		}

	/* Prepare to process the handshake packet signature */
	streamBookmarkSet( &stream, sigPtr, sigLength );
	status = readUint32( &stream );
	if( !cryptStatusError( status ) )
		status = sSkip( &stream, status );
	if( cryptStatusError( status ) )
		{
		sMemDisconnect( &stream );
		retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
				"Invalid DH phase 2 packet signature data" );
		}
	streamBookmarkComplete( &stream, sigLength );
	sMemDisconnect( &stream );

	/* Some implementations incorrectly format the signature packet, 
	   omitting the algorithm name and signature blob length for DSA sigs 
	   (that is, they just encode two 20-byte values instead of a properly-
	   formatted signature):

				Right							Wrong
		string		[ signature data ]		string	[ nothing ]
			string	"ssh-dss"
			string	signature						signature

	   If we're talking to one of these versions, we check to see whether 
	   the packet is correctly formatted (that is, that it has the 
	   algorithm-type string present as required) and if it isn't present 
	   rewrite it into the correct form so that we can verify the signature.  
	   This check requires that the signature format be one of the SSHv2 
	   standard types, but since we can't (by definition) handle proprietary 
	   formats this isn't a problem */
	if( ( sessionInfoPtr->protocolFlags & SSH_PFLAG_SIGFORMAT ) && \
		( pubkeyAlgo == CRYPT_ALGO_DSA ) && \
		( memcmp( ( BYTE * ) sigPtr + LENGTH_SIZE + LENGTH_SIZE, 
				  "ssh-dss", 7 ) && \
		  memcmp( ( BYTE * ) sigPtr + LENGTH_SIZE + LENGTH_SIZE, 
				  "x509v3-sign-dss", 15 ) && \
		  memcmp( ( BYTE * ) sigPtr + LENGTH_SIZE + LENGTH_SIZE, 
				  "spki-sign-dss", 13 ) && \
		  memcmp( ( BYTE * ) sigPtr + LENGTH_SIZE + LENGTH_SIZE, 
				  "pgp-sign-dss", 12 ) ) )
		{
		void *headerEndPtr;
		int headerSize;

		/* Rewrite the signature to fix up the overall length at the start and 
		   insert the algorithm name and signature length */
		sMemOpen( &stream, sessionInfoPtr->receiveBuffer, 
				  LENGTH_SIZE + sizeofString32( "ssh-dsa", 6 ) );	
		writeUint32( &stream, sizeofString32( "ssh-dsa", 6 ) );
		writeAlgoString( &stream, CRYPT_ALGO_DSA );
		headerSize = stell( &stream );
		headerEndPtr = sMemBufPtr( &stream );
		sMemDisconnect( &stream );

		/* Move the signature data down so that it follows the newly-created
		   header */
		memmove( headerEndPtr, sigPtr, sigLength );

		/* The rewritten signature is now at the start of the buffer, update 
		   the sig. pointer and size to accomodate the added header */
		sigPtr = sessionInfoPtr->receiveBuffer;
		sigLength += headerSize;
		}

	/* Finally, verify the server's signature on the exchange hash */
	status = iCryptCheckSignatureEx( sigPtr, sigLength, CRYPT_IFORMAT_SSH,
							sessionInfoPtr->iKeyexAuthContext,
							handshakeInfo->iExchangeHashcontext, NULL );
	if( cryptStatusError( status ) )
		retExt( sessionInfoPtr, status, "Bad handshake data signature" );

	/* We don't need the hash context any more, get rid of it */
	krnlSendNotifier( handshakeInfo->iExchangeHashcontext,

⌨️ 快捷键说明

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