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

📄 ssh2_cli.c

📁 cryptlib安全工具包
💻 C
📖 第 1 页 / 共 4 页
字号:
	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 */
	status = writeUint32( &stream, 0 );	/* Reserved */
	if( cryptStatusOK( status ) )
		{
		status = streamBookmarkComplete( &stream, &clientHelloPtr, 
										 &clientHelloLength, 
										 clientHelloLength );
		}
	if( cryptStatusOK( status ) )
		status = wrapPacketSSH2( sessionInfoPtr, &stream, 0, FALSE, TRUE );
	if( cryptStatusError( status ) )
		{
		sMemDisconnect( &stream );
		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 */
	status = hashAsString( handshakeInfo->iExchangeHashcontext, 
						   clientHelloPtr, clientHelloLength );
	if( cryptStatusOK( status ) )
		status = hashAsString( handshakeInfo->iExchangeHashcontext,
							   sessionInfoPtr->receiveBuffer, 
							   serverHelloLength );
	if( cryptStatusError( status ) )
		{
		sMemDisconnect( &stream );
		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 ) )
			{
			/* processDHE() has already disconnected the stream */
			return( status );
			}
		}

	/*	...
		byte	type = SSH2_MSG_KEXDH_INIT / SSH2_MSG_KEXDH_GEX_INIT
		mpint	y */
	status = continuePacketStreamSSH( &stream, \
						( handshakeInfo->requestedServerKeySize > 0 ) ? \
							SSH2_MSG_KEXDH_GEX_INIT : SSH2_MSG_KEXDH_INIT,
						&packetOffset );
	if( cryptStatusOK( status ) )
		{
		streamBookmarkSet( &stream, keyexLength );
		status = writeInteger32( &stream, keyAgreeParams.publicValue,
								 keyAgreeParams.publicValueLen );
		}
	if( cryptStatusOK( status ) )
		status = streamBookmarkComplete( &stream, &keyexPtr, &keyexLength, 
										 keyexLength );
	if( cryptStatusOK( status ) )
		status = wrapPacketSSH2( sessionInfoPtr, &stream, packetOffset, 
								 FALSE, TRUE );
	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 = DUMMY_INIT;
	STREAM stream;
	MESSAGE_DATA msgData;
	void *keyPtr = DUMMY_INIT_PTR, *keyBlobPtr = DUMMY_INIT_PTR;
	void *sigPtr = DUMMY_INIT_PTR;
	int keyLength = DUMMY_INIT, keyBlobLength, sigLength, length;
	int dummy, 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 */
	status = length = \
		readHSPacketSSH2( sessionInfoPtr,
						  ( handshakeInfo->requestedServerKeySize > 0 ) ? \
							SSH2_MSG_KEXDH_GEX_REPLY : SSH2_MSG_KEXDH_REPLY,
						  ID_SIZE + LENGTH_SIZE + sizeofString32( "", 6 ) + \
							sizeofString32( "", 1 ) + \
							sizeofString32( "", MIN_PKCSIZE ) + \
							sizeofString32( "", MIN_PKCSIZE ) + \
							LENGTH_SIZE + sizeofString32( "", 6 ) + 40 );
	if( cryptStatusError( status ) )
		return( status );
	sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
	status = sgetc( &stream );			/* Skip packet type */
	if( !cryptStatusError( status ) )
		{
		streamBookmarkSet( &stream, keyLength );
		status = readUint32( &stream );	/* Server key data size */
		}
	if( !cryptStatusError( status ) )
		{
		status = readAlgoString( &stream, handshakeInfo->algoStringPubkeyTbl,
								 handshakeInfo->algoStringPubkeyTblNoEntries,
								 &pubkeyAlgo, TRUE, SESSION_ERRINFO );
		}
	if( cryptStatusError( status ) )
		{
		sMemDisconnect( &stream );
		return( status );
		}
	if( pubkeyAlgo != handshakeInfo->pubkeyAlgo )
		{
		sMemDisconnect( &stream );
		retExt( CRYPT_ERROR_BADDATA,
				( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 
				  "Invalid DH phase 2 public key algorithm %d, expected %d",
				  pubkeyAlgo, handshakeInfo->pubkeyAlgo ) );
		}
	streamBookmarkSet( &stream, keyBlobLength );
	if( pubkeyAlgo == CRYPT_ALGO_RSA )
		{
		/* RSA e, n */
		readInteger32( &stream, NULL, &dummy, 1, CRYPT_MAX_PKCSIZE );
		status = readInteger32Checked( &stream, NULL, &dummy, 
									   MIN_PKCSIZE, CRYPT_MAX_PKCSIZE );
		}
	else
		{
		/* DSA p, q, g, y */
		status = readInteger32Checked( &stream, NULL, &dummy, 
									   MIN_PKCSIZE, CRYPT_MAX_PKCSIZE );
		if( cryptStatusOK( status ) )
			{
			readInteger32( &stream, NULL, &dummy, 1, CRYPT_MAX_PKCSIZE );
			readInteger32( &stream, NULL, &dummy, 1, CRYPT_MAX_PKCSIZE );
			status = readInteger32Checked( &stream, NULL, &dummy, 
										   MIN_PKCSIZE, CRYPT_MAX_PKCSIZE );
			}
		}
	if( cryptStatusOK( status ) )
		status = streamBookmarkComplete( &stream, &keyBlobPtr, 
										 &keyBlobLength, keyBlobLength );
	if( cryptStatusOK( status ) )
		status = streamBookmarkComplete( &stream, &keyPtr, &keyLength, 
										 keyLength );
	if( cryptStatusError( status ) )
		{
		sMemDisconnect( &stream );

		/* Some misconfigured servers may use very short keys, we perform
		   a special-case check for these and return a more specific message
		   than the generic bad-data */
		if( status == CRYPT_ERROR_NOSECURE )
			{
			retExt( CRYPT_ERROR_NOSECURE,
					( CRYPT_ERROR_NOSECURE, SESSION_ERRINFO, 
					  "Insecure key used in key exchange" ) );
			}

		retExt( CRYPT_ERROR_BADDATA,
				( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 
				  "Invalid DH phase 2 packet" ) );
		}
	setMessageData( &msgData, keyPtr, keyLength );
	status = krnlSendMessage( sessionInfoPtr->iKeyexAuthContext,
							  IMESSAGE_SETATTRIBUTE_S, &msgData,
							  CRYPT_IATTRIBUTE_KEY_SSH );
	if( cryptStatusError( status ) )
		{
		sMemDisconnect( &stream );
		retExt( cryptArgError( status ) ? \
				CRYPT_ERROR_BADDATA : status,
				( cryptArgError( status ) ? \
				  CRYPT_ERROR_BADDATA : status, SESSION_ERRINFO, 
				  "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,
							  CRYPT_MAX_PKCSIZE + 16,
							  &handshakeInfo->serverKeyexValueLength );
	if( cryptStatusError( status ) || \
		!isValidDHsize( handshakeInfo->clientKeyexValueLength,
						handshakeInfo->serverKeySize, LENGTH_SIZE ) )
		{
		sMemDisconnect( &stream );
		retExt( CRYPT_ERROR_BADDATA,
				( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 
				  "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, sigLength );
	status = length = readUint32( &stream );
	if( !cryptStatusError( status ) )
		status = sSkip( &stream, length );
	if( cryptStatusOK( status ) )
		status = streamBookmarkComplete( &stream, &sigPtr, &sigLength, 
										 sigLength );
	sMemDisconnect( &stream );
	if( cryptStatusError( status ) )
		{
		retExt( CRYPT_ERROR_BADDATA,
				( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 
				  "Invalid DH phase 2 packet signature data" ) );
		}

	/* 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 ) ) )
		{
		int headerSize = DUMMY_INIT;

		/* Rewrite the signature to fix up the overall length at the start 
		   and insert the algorithm name and signature length.  We can 
		   safely reuse the receive buffer for this because the start 
		   contains the server key/certificate and DH keyex value, which is 
		   far longer than the 12 bytes of header plus signature that we'll
		   be writing there */
		sMemOpen( &stream, sessionInfoPtr->receiveBuffer,
				  LENGTH_SIZE + sizeofString32( "ssh-dsa", 6 ) + sigLength );
		writeUint32( &stream, sizeofString32( "ssh-dsa", 6 ) );
		writeAlgoString( &stream, CRYPT_ALGO_DSA );
		status = swrite( &stream, sigPtr, sigLength );
		if( cryptStatusOK( status ) )
			headerSize = stell( &stream );
		sMemDisconnect( &stream );
		if( cryptStatusError( status ) )
			return( status );

		/* 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 = iCryptCheckSignature( sigPtr, sigLength, CRYPT_IFORMAT_SSH,
								   sessionInfoPtr->iKeyexAuthContext,
								   handshakeInfo->iExchangeHashcontext, 
								   CRYPT_UNUSED, NULL );
	if( cryptStatusError( status ) )
		retExt( status, 
				( status, SESSION_ERRINFO, 
				  "Bad handshake data signature" ) );

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

	return( CRYPT_OK );
	}

/* Complete the handshake with the server */

⌨️ 快捷键说明

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