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

📄 ssh2_cli.c

📁 cryptlib是功能强大的安全工具集。允许开发人员快速在自己的软件中集成加密和认证服务。
💻 C
📖 第 1 页 / 共 4 页
字号:
					  IMESSAGE_DECREFCOUNT );
	handshakeInfo->iExchangeHashcontext = CRYPT_ERROR;

	return( CRYPT_OK );
	}

/* Complete the handshake with the server */

static int completeClientHandshake( SESSION_INFO *sessionInfoPtr,
									SSH_HANDSHAKE_INFO *handshakeInfo )
	{
	const ATTRIBUTE_LIST *userNamePtr = \
				findSessionAttribute( sessionInfoPtr->attributeList,
									  CRYPT_SESSINFO_USERNAME );
	const ATTRIBUTE_LIST *passwordPtr = \
				findSessionAttribute( sessionInfoPtr->attributeList,
									  CRYPT_SESSINFO_PASSWORD );
	STREAM stream;
	BYTE stringBuffer[ CRYPT_MAX_TEXTSIZE + 8 ];
	void *signedDataPtr;
	int signedDataLength, stringLength, length, packetOffset, status;

	/* Set up the security information required for the session */
	status = initSecurityInfo( sessionInfoPtr, handshakeInfo );
	if( cryptStatusError( status ) )
		return( status );

	/* Wait for the server's change cipherspec message.  From this point
	   on the read channel is in the secure state */
	status = readPacketSSH2( sessionInfoPtr, SSH2_MSG_NEWKEYS, ID_SIZE );
	if( cryptStatusError( status ) )
		return( status );
	sessionInfoPtr->flags |= SESSION_ISSECURE_READ;

	/* Build our change cipherspec message and request authentication with
	   the server:

		byte	type = SSH2_MSG_NEWKEYS
		... 
	
	   After this point the write channel is also in the secure state */
	openPacketStreamSSH( &stream, sessionInfoPtr, CRYPT_USE_DEFAULT, 
						 SSH2_MSG_NEWKEYS );
	status = wrapPacketSSH2( sessionInfoPtr, &stream, 0 );
	if( cryptStatusError( status ) )
		{
		sMemDisconnect( &stream );
		return( status );
		}
	sessionInfoPtr->flags |= SESSION_ISSECURE_WRITE;

	/*	...
		byte	type = SSH2_MSG_SERVICE_REQUEST
		string	service_name = "ssh-userauth" */
	packetOffset = continuePacketStreamSSH( &stream, 
											SSH2_MSG_SERVICE_REQUEST );
	writeString32( &stream, "ssh-userauth", 0 );
	status = wrapPacketSSH2( sessionInfoPtr, &stream, packetOffset );
	if( cryptStatusError( status ) )
		{
		sMemDisconnect( &stream );
		return( status );
		}

	/* Send the whole mess to the server.  For some reason SSHv2 requires 
	   the use of two authentication messages, an "I'm about to 
	   authenticate" packet and an "I'm authenticating" packet, so we have 
	   to perform the authentication in two parts.  SSL at this point uses a 
	   Finished message in which the client and server do a mutual proof-of-
	   possession of encryption and MAC keys via a pipeline-stalling message 
	   that prevents any further (sensitive) data from being exchanged until 
	   the PoP has concluded (the SSL Finished also authenticates the 
	   handshake messages).  The signed exchange hash from the server proves 
	   to the client that the server knows the master secret, but not 
	   necessarily that the client and server share encryption and MAC keys.  
	   Without this mutual PoP, the client could potentially end up sending 
	   passwords to the server using an incorrect (and potentially weak) key 
	   if it's messed up and derived the key incorrectly.  Although mutual 
	   PoP isn't a design goal of the SSH handshake, we do it anyway (as far 
	   as we can without a proper Finished message), although this introduces 
	   a pipeline stall at this point 

	   The spec in fact says that after a key exchange with implicit server
	   authentication the client has to wait for the server to send a 
	   service-accept packet before continuing, however it never explains 
	   what implicit (and, by extension, explicit) server authentication 
	   actually are.  This text is a leftover from an extremely early SSH 
	   draft in which the only keyex mechanism was "double-encrypting-sha", 
	   a mechanism that required a pipeline stall at this point because the 
	   client wasn't able to authenticate the server until it received the 
	   first encrypted/MAC'ed message from it.  To extricate ourselves from 
	   the confusion due to the missing definition we could define "implicit 
	   authentication" to be "Something completely different from what we're 
	   doing here", which means that we could send the two packets together 
	   without having to wait for the server, but it's probably better to 
	   use SSL-tyle Finished semantics at this point even if it adds an 
	   extra RTT delay */
	status = sendPacketSSH2( sessionInfoPtr, &stream, TRUE );
	sMemDisconnect( &stream );
	if( cryptStatusOK( status ) )
		status = length = readPacketSSH2( sessionInfoPtr, 
									SSH2_MSG_SERVICE_ACCEPT, 
									ID_SIZE + sizeofString32( "", 8 ) );
	if( cryptStatusError( status ) )
		return( status );
	sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
	sgetc( &stream );		/* Skip packet type */
	status = readString32( &stream, stringBuffer, &stringLength, 
						   CRYPT_MAX_TEXTSIZE );
	sMemDisconnect( &stream );
	if( cryptStatusError( status ) || \
		stringLength != 12 || memcmp( stringBuffer, "ssh-userauth", 12 ) )
		/* More of a sanity check than anything else, the MAC should have
		   caught any keying problems */
		retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
				"Invalid service accept packet" );

	/*	byte	type = SSH2_MSG_USERAUTH_REQUEST
		string	user_name
		string	service_name = "ssh-connection"
		... 

	   The way in which we handle authentication here isn't totally 
	   appropriate since we assume that the user knows the appropriate form
	   of authentication to use.  If they're ambiguous and supply both a
	   password and a private key and the server only accepts PKC-based
	   authentication, we'll always preferentially choose password-based
	   authentication.  The way around this is to send an auth-request with
	   a method-type of "none" to see what the server wants, but the only 
	   thing cryptlib can do (since it's non-interactive during the
	   handshake phase) is disconnect, tell the user what went wrong, and try
	   again.  The current mechanism does this anyway, so we don't gain much
	   except extra RTT delays by adding this question-and-answer facility */
	openPacketStreamSSH( &stream, sessionInfoPtr, CRYPT_USE_DEFAULT, 
						 SSH2_MSG_USERAUTH_REQUEST );
	streamBookmarkSetFullPacket( &stream, signedDataPtr, signedDataLength );
	writeString32( &stream, userNamePtr->value, userNamePtr->valueLength );
	writeString32( &stream, "ssh-connection", 0 );
	if( passwordPtr != NULL )
		{
		/*	...
			string	method-name = "password"
			boolean	FALSE
			string	password */
		writeString32( &stream, "password", 0 );
		sputc( &stream, 0 );
		status = writeString32( &stream, passwordPtr->value,
								passwordPtr->valueLength );
		}
	else
		{
		CRYPT_ALGO_TYPE pkcAlgo;
		MESSAGE_CREATEOBJECT_INFO createInfo;
		int sigLength;

		krnlSendMessage( sessionInfoPtr->privateKey, IMESSAGE_GETATTRIBUTE,
						 &pkcAlgo, CRYPT_CTXINFO_ALGO );

		/*	...
			string	method-name = "publickey"
			boolean	TRUE
			string		"ssh-rsa"	"ssh-dss"
			string		[ client key/certificate ]
				string	"ssh-rsa"	"ssh-dss"
				mpint	e			p
				mpint	n			q
				mpint				g
				mpint				y
			string		[ client signature ]
				string	"ssh-rsa"	"ssh-dss"
				string	signature	signature.
		
		   Note the doubled-up algorithm name, the spec first requires that
		   the public-key auth packet send the algorithm name and then
		   includes it a second time as part of the client key info */
		writeString32( &stream, "publickey", 0 );
		sputc( &stream, 1 );
		writeAlgoString( &stream, pkcAlgo );
		status = exportAttributeToStream( &stream, 
										  sessionInfoPtr->privateKey,
										  CRYPT_IATTRIBUTE_KEY_SSH2,
										  CRYPT_USE_DEFAULT );
		if( cryptStatusError( status ) )
			{
			sMemDisconnect( &stream );
			return( status );
			}
		streamBookmarkComplete( &stream, signedDataLength );

		/* Hash the authentication request data:

			string		exchange hash
			[ user_auth_request packet payload up to signature start ] */
		setMessageCreateObjectInfo( &createInfo, CRYPT_ALGO_SHA );
		status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
								  IMESSAGE_DEV_CREATEOBJECT, &createInfo,
								  OBJECT_TYPE_CONTEXT );
		if( cryptStatusError( status ) )
			{
			sMemDisconnect( &stream );
			return( status );
			}
		if( sessionInfoPtr->protocolFlags & SSH_PFLAG_NOHASHLENGTH )
			/* Some implementations erroneously omit the length when hashing
			    the exchange hash */
			krnlSendMessage( createInfo.cryptHandle, IMESSAGE_CTX_HASH,
							 handshakeInfo->sessionID,
							 handshakeInfo->sessionIDlength );
		else
			hashAsString( createInfo.cryptHandle, handshakeInfo->sessionID,
						  handshakeInfo->sessionIDlength );
		krnlSendMessage( createInfo.cryptHandle, IMESSAGE_CTX_HASH, 
						 signedDataPtr, signedDataLength );
		status = krnlSendMessage( createInfo.cryptHandle, IMESSAGE_CTX_HASH,
								  "", 0 );
		if( cryptStatusError( status ) )
			{
			sMemDisconnect( &stream );
			krnlSendNotifier( createInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
			return( status );
			}

		/* Sign the hash.  The reason for the min() part of the expression 
		   is that iCryptCreateSignatureEx() gets suspicious of very large 
		   buffer sizes, for example when the user has specified the use of 
		   a 1MB send buffer */
		status = iCryptCreateSignatureEx( sMemBufPtr( &stream ), &sigLength, 
						min( sMemDataLeft( &stream ), 16384 ),
						CRYPT_IFORMAT_SSH, sessionInfoPtr->privateKey,
						createInfo.cryptHandle, CRYPT_UNUSED, CRYPT_UNUSED );
		if( cryptStatusOK( status ) )
			status = sSkip( &stream, sigLength );
		krnlSendNotifier( createInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
		}
	if( cryptStatusError( status ) )
		{
		sMemDisconnect( &stream );
		return( status );
		}

	/* Send the authentication info to the server */
	status = wrapPacketSSH2( sessionInfoPtr, &stream, 0 );
	if( cryptStatusOK( status ) )
		status = sendPacketSSH2( sessionInfoPtr, &stream, TRUE );
	sMemDisconnect( &stream );
	if( cryptStatusError( status ) )
		return( status );

	/* Wait for the server's ack of the authentication */
	status = length = readPacketSSH2( sessionInfoPtr, 
									  SSH2_MSG_SPECIAL_USERAUTH, ID_SIZE );
	if( !cryptStatusError( status ) )
		{
		int type;

		sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
		type = sgetc( &stream );
		sMemDisconnect( &stream );
		if( type == SSH2_MSG_USERAUTH_FAILURE )
			/* The authentication failed, provide more specific details for 
			   the caller, with an optional fallback to PAM authentication
			   if the server requested it */
			status = reportAuthFailure( sessionInfoPtr, length, FALSE );
		}
	if( cryptStatusError( status ) )
		return( status );

	/* We've finally made it through all of the formalities (post proelia 
	   praemia), create (if necessary) and open a channel */
	if( getCurrentChannelNo( sessionInfoPtr, \
							 CHANNEL_READ ) == UNUSED_CHANNEL_NO )
		{
		/* The user hasn't specified any channel details, create a
		   channel of the default type */
		status = createChannel( sessionInfoPtr );
		if( cryptStatusError( status ) )
			return( status );
		}
	return( sendChannelOpen( sessionInfoPtr ) );
	}

/****************************************************************************
*																			*
*							Session Access Routines							*
*																			*
****************************************************************************/

void initSSH2clientProcessing( SESSION_INFO *sessionInfoPtr,
							   SSH_HANDSHAKE_INFO *handshakeInfo )
	{
	UNUSED( sessionInfoPtr );

	handshakeInfo->beginHandshake = beginClientHandshake;
	handshakeInfo->exchangeKeys = exchangeClientKeys;
	handshakeInfo->completeHandshake = completeClientHandshake;
	}
#endif /* USE_SSH2 */

⌨️ 快捷键说明

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