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

📄 ssh2_cli.c

📁 老外写的加密库cryptlib(版本3.1)
💻 C
📖 第 1 页 / 共 3 页
字号:
	*bufPtr++ = SSH2_MSG_NEWKEYS;
	status = totalLength = wrapPacket( sessionInfoPtr,
									   sessionInfoPtr->sendBuffer, ID_SIZE );
	if( cryptStatusError( status ) )
		return( status );

	/* We've sent the change cipherspec message, from now on all data is
	   encrypted and MAC'ed */
	sessionInfoPtr->flags |= SESSION_ISSECURE;

	/*	...
		byte	type = SSH2_MSG_SERVICE_REQUEST
		string	service_name = "ssh-userauth" */
	bufPtr = sessionInfoPtr->sendBuffer + totalLength + SSH2_HEADER_SIZE;
	*bufPtr++ = SSH2_MSG_SERVICE_REQUEST;
	length = encodeString( bufPtr, "ssh-userauth", 0 );
	status = length = wrapPacket( sessionInfoPtr,
								  sessionInfoPtr->sendBuffer + totalLength,
								  ID_SIZE + length );
	if( cryptStatusError( status ) )
		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.  In theory 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, totalLength + length, TRUE );
	if( cryptStatusOK( status ) )
		status = readPacketSSH2( sessionInfoPtr, SSH2_MSG_SERVICE_ACCEPT );
	if( cryptStatusError( status ) )
		return( status );

	/*	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, this will 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
	   by adding this question-and-answer facility */
	bufPtr = sessionInfoPtr->sendBuffer + SSH2_HEADER_SIZE;
	*bufPtr++ = SSH2_MSG_USERAUTH_REQUEST;
	bufPtr += encodeString( bufPtr, sessionInfoPtr->userName,
							sessionInfoPtr->userNameLength );
	bufPtr += encodeString( bufPtr, "ssh-connection", 0 );
	if( sessionInfoPtr->passwordLength > 0 )
		{
		/*	...
			string	method-name = "password"
			boolean	FALSE
			string	password */
		bufPtr += encodeString( bufPtr, "password", 0 );
		*bufPtr++ = 0;
		bufPtr += encodeString( bufPtr, sessionInfoPtr->password,
								sessionInfoPtr->passwordLength );
		}
	else
		{
		MESSAGE_CREATEOBJECT_INFO createInfo;
		RESOURCE_DATA msgData;
		int sigLength;

		/*	...
			string	method-name = "publickey"
			boolean	TRUE
			string		client 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 */
		bufPtr += encodeString( bufPtr, "publickey", 0 );
		*bufPtr++ = 1;
		setMessageData( &msgData, bufPtr,
						LENGTH_SIZE + ( LENGTH_SIZE + 7 ) + \
						( ( LENGTH_SIZE + CRYPT_MAX_PKCSIZE ) * 4 ) );
		status = krnlSendMessage( sessionInfoPtr->privateKey,
								  IMESSAGE_GETATTRIBUTE_S, &msgData,
								  CRYPT_IATTRIBUTE_KEY_SSH2 );
		if( cryptStatusError( status ) )
			return( status );
		bufPtr += msgData.length;

		/* Sign 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 ) )
			return( status );
		if( !( sessionInfoPtr->protocolFlags & SSH_PFLAG_NOHASHLENGTH ) )
			{
			BYTE header[ 8 ], *headerPtr = header;

			/* Some implementations erroneously omit the length when hashing
			    the exchange hash */
			mputLong( headerPtr, handshakeInfo->sessionIDlength );
			krnlSendMessage( createInfo.cryptHandle, IMESSAGE_CTX_HASH,
							 header, LENGTH_SIZE );
			}
		krnlSendMessage( createInfo.cryptHandle, IMESSAGE_CTX_HASH,
						 handshakeInfo->sessionID,
						 handshakeInfo->sessionIDlength );
		krnlSendMessage( createInfo.cryptHandle, IMESSAGE_CTX_HASH,
						 sessionInfoPtr->sendBuffer + SSH2_HEADER_SIZE,
						 bufPtr - ( sessionInfoPtr->sendBuffer + \
									SSH2_HEADER_SIZE ) );
		status = krnlSendMessage( createInfo.cryptHandle, IMESSAGE_CTX_HASH,
								  "", 0 );
		if( cryptStatusError( status ) )
			{
			krnlSendNotifier( createInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
			return( status );
			}
		status = iCryptCreateSignatureEx( bufPtr, &sigLength,
						sessionInfoPtr->sendBufSize - \
							( ( bufPtr - sessionInfoPtr->sendBuffer ) + 128 ),
						CRYPT_IFORMAT_SSH, sessionInfoPtr->privateKey,
						createInfo.cryptHandle, CRYPT_UNUSED, CRYPT_UNUSED );
		krnlSendNotifier( createInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
		if( cryptStatusError( status ) )
			return( status );
		bufPtr += sigLength;
		}
	status = length = wrapPacket( sessionInfoPtr,
								  sessionInfoPtr->sendBuffer,
								  bufPtr - ( sessionInfoPtr->sendBuffer + \
											 SSH2_HEADER_SIZE ) );
	if( cryptStatusError( status ) )
		return( status );

	/* Send the authentication info to the server and wait for the server's 
	   ack of the authentication */
	status = sendPacketSSH2( sessionInfoPtr, length, TRUE );
	if( cryptStatusOK( status ) )
		status = readPacketSSH2( sessionInfoPtr, SSH2_MSG_SPECIAL_USERAUTH );
	if( status == CRYPT_ERROR_WRONGKEY )
		{
		CRYPT_ALGO_TYPE authentAlgo;
		int stringLength;

		/* The authentication failed, pick apart the response to see if we 
		   can return more meaningful error info:

			byte	type = SSH2_MSG_USERAUTH_FAILURE
			string	available_auth_types
			boolean	partial_success

		  God knows how the partial_success flag is really meant to be 
		  applied (there are a whole pile of odd conditions surrounding 
		  changed passwords and similar issues), according to the spec it 
		  means that the authentication was successful, however the packet 
		  type indicates that the authentication failed and something else is 
		  needed.  This whole section of the protocol winds up in an 
		  extremely complex state machine with all sorts of special-case 
		  conditionsm, several of which require manual intervention by the
		  user.  It's easiest to not even try and handle this stuff */
		bufPtr = sessionInfoPtr->receiveBuffer + ID_SIZE;
		length = ( int ) mgetLong( bufPtr );
		stringLength = getAlgoID( handshakeInfo->algoStringUserauthentTbl, 
								  &authentAlgo, CRYPT_ALGO_DES, 
								  sessionInfoPtr->receiveBuffer + ID_SIZE, 
								  length + UINT_SIZE, sessionInfoPtr );
		if( cryptStatusError( stringLength ) )
			{
			/* If the problem is due to lack of a compatible algorithm, make
			   the error message a bit more specific to tell the user that we
			   got through most of the handshake but failed at the 
			   authentication stage */
			if( stringLength == CRYPT_ERROR_NOTAVAIL )
				retExt( sessionInfoPtr, CRYPT_ERROR_NOTAVAIL,
						"Remote system supports neither password nor "
						"public-key authentication" );
			return( stringLength );
			}
		if( sessionInfoPtr->passwordLength > 0 )
			{
			/* If we used a password and the server wants a PKC, report the 
			   error as a missing private key.  RSA in this case is a 
			   placeholder that means "any public-key algorithm", it could
			   just as well end up as DSA once we send the data */
			if( authentAlgo == CRYPT_ALGO_RSA )
				{
				setErrorInfo( sessionInfoPtr, CRYPT_SESSINFO_PRIVATEKEY,
							  CRYPT_ERRTYPE_ATTR_ABSENT );
				retExt( sessionInfoPtr, CRYPT_ERROR_NOTINITED,
						"Server requested public-key authentication but "
						"only a password was available" );
				}
			}
		else
			/* If we used a PKC and the server wants a password, report the 
			   error as a missing password.  DES in this case is a 
			   placeholder for passwords, since there's no cryptlib ID for
			   them */
			if( authentAlgo == CRYPT_ALGO_DES )
				{
				setErrorInfo( sessionInfoPtr, CRYPT_SESSINFO_PASSWORD,
							  CRYPT_ERRTYPE_ATTR_ABSENT );
				retExt( sessionInfoPtr, CRYPT_ERROR_NOTINITED,
						"Server requested password authentication but "
						"only a public/private key was available" );
				}
		}
	if( cryptStatusError( status ) )
		return( status );

	/* We've finally made it through all the formalities (post proelia 
	   praemia), open a channel of the requested type:

		byte	type = SSH2_MSG_CHANNEL_OPEN
		string	channel_type = "session"
		uint32	sender_channel = 0
		uint32	initial_window_size = MAX_WINDOW_SIZE
		uint32	max_packet_size = bufSize
		...

	   The use of security protocol-level flow control when there's already
	   a far better, heavily analysed and field-tested network protocol-
	   level flow control mechanism is just stupid.  All it does is create
	   performance problems where throughput can be reduced by as much as an
	   order of magnitude due to SSH's "flow-control" getting in the way 
	   (Putty even has an FAQ entry "Why is SFTP so much slower than scp?", 
	   for which the correct answer should be "It's the SSH-level flow-
	   control braindamage").  For this reason cryptlib always advertises a 
	   maximum window size (effectively disabling the SSH-level flow 
	   control) and lets the network stack and network hardware take care of 
	   flow control, as it should */
	bufPtr = sessionInfoPtr->sendBuffer + SSH2_HEADER_SIZE;
	*bufPtr++ = SSH2_MSG_CHANNEL_OPEN;
	bufPtr += encodeString( bufPtr, "session", 0 );
	mputLong( bufPtr, 0 );
	length = sessionInfoPtr->sendBufSize - EXTRA_PACKET_SIZE;
	mputLong( bufPtr, MAX_WINDOW_SIZE );
	mputLong( bufPtr, length );
	status = totalLength = wrapPacket( sessionInfoPtr,
							sessionInfoPtr->sendBuffer,
							bufPtr - ( sessionInfoPtr->sendBuffer + \
									   SSH2_HEADER_SIZE ) );
	if( cryptStatusError( status ) )
		return( status );

	/* Create a request for the appropriate type of service, either 
	   encrypted-telnet, SFTP, or port forwarding */
	status = createOpenRequest( sessionInfoPtr, 
								sessionInfoPtr->sendBuffer + totalLength );
	if( cryptStatusError( status ) )
		return( status );
	totalLength += status;

	/* Send the whole mess to the server, again as separate packets, and wait
	   for the server's ack of the channel open request and channel request.
	   As with the authentication, we have to send two packets to do the work
	   of one.

	   The SSHv2 spec doesn't really explain the semantics of the server's
	   response to the channel open command, in particular whether the
	   returned data size parameters are merely a confirmation of the
	   client's requested values or whether the server is allowed to further
	   modify them to suit its own requirements (or perhaps one is for send
	   and the other for receive?).  In the absence of any further guidance,
	   we just ignore the returned values, which seems to work for all
	   deployed servers */
	status = sendPacketSSH2( sessionInfoPtr, totalLength, TRUE );
	if( cryptStatusOK( status ) )
		status = readPacketSSH2( sessionInfoPtr,
								 SSH2_MSG_CHANNEL_OPEN_CONFIRMATION );
	return( cryptStatusError( status ) ? status : CRYPT_OK );
	}

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

void initSSH2clientProcessing( SESSION_INFO *sessionInfoPtr,
							   SSH_HANDSHAKE_INFO *handshakeInfo )
	{
	handshakeInfo->beginHandshake = beginClientHandshake;
	handshakeInfo->exchangeKeys = exchangeClientKeys;
	handshakeInfo->completeHandshake = completeClientHandshake;
	}
#endif /* SSH2 */

⌨️ 快捷键说明

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