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

📄 ssh2.c

📁 cryptlib是功能强大的安全工具集。允许开发人员快速在自己的软件中集成加密和认证服务。
💻 C
📖 第 1 页 / 共 2 页
字号:
	/* Read the keyex algorithm info */
	if( isServer )
		{
		setAlgoIDInfo( &algoIDInfo, algoStringKeyexTbl, CRYPT_PSEUDOALGO_DHE, 
					   GETALGO_FIRST_MATCH_WARN );
		}
	else
		{
		setAlgoIDInfo( &algoIDInfo, algoStringKeyexTbl, CRYPT_ALGO_NONE, 
					   GETALGO_BEST_MATCH );
		}
	status = readAlgoStringEx( &stream, &algoIDInfo, sessionInfoPtr );
	if( cryptStatusError( status ) )
		{
		sMemDisconnect( &stream );
		return( status );
		}
	if( algoIDInfo.prefAlgoMismatch )
		/* We didn't get a match for our first choice, remember that we have
		   to discard any guessed keyex that may follow */
		preferredAlgoMismatch = TRUE;
	if( algoIDInfo.algo == CRYPT_PSEUDOALGO_DHE )
		/* If we're using ephemeral rather than static DH keys, we need to 
		   negotiate the keyex key before we can perform the exchange */
		handshakeInfo->requestedServerKeySize = SSH2_DEFAULT_KEYSIZE;

	/* Read the pubkey (signature) algorithm info */
	if( isServer )
		{
		setAlgoIDInfo( &algoIDInfo, handshakeInfo->algoStringPubkeyTbl, 
					   handshakeInfo->pubkeyAlgo, GETALGO_FIRST_MATCH_WARN );
		}
	else
		{
		setAlgoIDInfo( &algoIDInfo, handshakeInfo->algoStringPubkeyTbl, 
					   CRYPT_ALGO_NONE, GETALGO_BEST_MATCH );
		}
	status = readAlgoStringEx( &stream, &algoIDInfo, sessionInfoPtr );
	if( cryptStatusError( status ) )
		{
		sMemDisconnect( &stream );
		return( status );
		}
	if( !isServer )
		handshakeInfo->pubkeyAlgo = algoIDInfo.algo;
	if( algoIDInfo.prefAlgoMismatch )
		/* We didn't get a match for our first choice, remember that we have
		   to discard any guessed keyex that may follow */
		preferredAlgoMismatch = TRUE;

	/* Read the encryption and MAC algorithm info */
	status = readAlgoStringPair( &stream,
						( sessionInfoPtr->flags & SESSION_ISSERVER ) ? \
						  algoStringEncrTblServer : algoStringEncrTblClient, 
						&sessionInfoPtr->cryptAlgo, isServer, 
						sessionInfoPtr );
	if( cryptStatusOK( status ) )
		status = readAlgoStringPair( &stream, algoStringMACTbl,
									 &sessionInfoPtr->integrityAlgo, 
									 isServer, sessionInfoPtr );
	if( cryptStatusError( status ) )
		{
		sMemDisconnect( &stream );
		return( status );
		}

	/* Read the remaining algorithm info.  The final reserved value should
	   always be zero, but we don't specifically check for this since at 
	   some point in the future it may become non-zero */
	status = readAlgoStringPair( &stream, algoStringCoprTbl, NULL, 
								 isServer, sessionInfoPtr );
	if( cryptStatusOK( status ) )
		status = readUniversal32( &stream );
	if( cryptStatusOK( status ) )
		status = readUniversal32( &stream );
	if( cryptStatusOK( status ) )
		{
		if( sgetc( &stream ) )
			guessedKeyex = TRUE;
		status = readUint32( &stream );	/* Reserved value */
		}
	if( cryptStatusError( status ) )
		retExt( sessionInfoPtr, status, 
				"Invalid hello packet compression algorithm/language string/"
				"trailer" );

	/* If there's a guessed keyex following this packet and we didn't match 
	   the first-choice keyex/pubkey algorithm, tell the caller to skip it */
	if( guessedKeyex && preferredAlgoMismatch )
		return( OK_SPECIAL );

	return( CRYPT_OK );
	}

/****************************************************************************
*																			*
*								Get/Put Data Functions						*
*																			*
****************************************************************************/

/* Read data over the SSHv2 link */

static int readHeaderFunction( SESSION_INFO *sessionInfoPtr,
							   READSTATE_INFO *readInfo )
	{
	SSH_INFO *sshInfo = sessionInfoPtr->sessionSSH;
	BYTE *bufPtr = sessionInfoPtr->receiveBuffer + \
				   sessionInfoPtr->receiveBufPos;
	long length;
	int extraLength, removedDataLength = ( ID_SIZE + PADLENGTH_SIZE );
	int status;

	/* Clear return value */
	*readInfo = READINFO_NONE;

	/* Make sure that there's room left to handle the speculative read */
	if( sessionInfoPtr->receiveBufPos >= \
		sessionInfoPtr->receiveBufSize - 128 )
		return( 0 );

	/* Try and read the header data from the remote system */
	assert( sessionInfoPtr->receiveBufPos == sessionInfoPtr->receiveBufEnd );
	status = readPacketHeaderSSH2( sessionInfoPtr, SSH2_MSG_CHANNEL_DATA,
								   &length, &extraLength, readInfo );
	if( cryptStatusError( status ) )
		return( ( status == OK_SPECIAL ) ? CRYPT_OK : status );
	assert( length >= ID_SIZE + PADLENGTH_SIZE + SSH2_MIN_PADLENGTH_SIZE );
	status = macPayload( sessionInfoPtr->iAuthInContext, sshInfo->readSeqNo, 
						 bufPtr, MIN_PACKET_SIZE - LENGTH_SIZE, length, 
						 MAC_START, sessionInfoPtr->authBlocksize, TRUE );
	if( cryptStatusError( status ) )
		/* We don't return an extended status at this point because we 
		   haven't completed message MAC calculation/check yet, so any 
		   errors will be cryptlib-internal ones */
		return( status );

	/* Extract fixed information (the pad length and packet type) */
	sshInfo->padLength = bufPtr[ 0 ];
	sshInfo->packetType = bufPtr[ 1 ];

	/* If it's channel data, strip the encapsulation, which allows us to
	   process the payload directly without having to move it around in
	   the buffer */
	if( sshInfo->packetType == SSH2_MSG_CHANNEL_DATA )
		{
		STREAM stream;
		long payloadLength;

		/* Process the channel header and make sure that the payload length 
		   matches the packet length */
		sMemConnect( &stream, bufPtr, SSH2_HEADER_REMAINDER_SIZE );
		sSkip( &stream, ID_SIZE + PADLENGTH_SIZE );
		status = processChannelControlMessage( sessionInfoPtr, &stream );
		if( cryptStatusError( status ) )
			{
			sMemDisconnect( &stream );
			return( status );
			}
		payloadLength = readUint32( &stream );
		removedDataLength = stell( &stream );
		sMemDisconnect( &stream );
		if( payloadLength != length - ( removedDataLength + \
										sshInfo->padLength ) )
			retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
					"Invalid data packet payload length %ld, should be %ld",
					payloadLength, 
					length - ( removedDataLength + sshInfo->padLength ) );
		}

	/* Move the remainder down to the start of the buffer.  The general idea 
	   is to remove all of the header data so that only the payload remains 
	   in the buffer, avoiding the need to move it down afterwards.  This is 
	   complicated by the fact that (unlike SSL) all of the data (including 
	   the header) is encrypted and MAC'ed, so we can't just read that 
	   separately but have to process it as part of the payload, remove it, 
	   and remember anything that's left for later */
	assert( SSH2_HEADER_REMAINDER_SIZE - removedDataLength > 0 );
	memmove( bufPtr, bufPtr + removedDataLength, 
			 SSH2_HEADER_REMAINDER_SIZE - removedDataLength );

	/* Determine how much data we'll be expecting, adjusted for the fixed 
	   information that we've removed and the (implicitly present) MAC data */
	sessionInfoPtr->pendingPacketLength = \
			sessionInfoPtr->pendingPacketRemaining = \
					( length + extraLength ) - removedDataLength;

	/* Indicate that we got some payload as part of the header */
	*readInfo = READINFO_HEADERPAYLOAD;
	return( SSH2_HEADER_REMAINDER_SIZE - removedDataLength );
	}

static int processBodyFunction( SESSION_INFO *sessionInfoPtr,
								READSTATE_INFO *readInfo )
	{
	SSH_INFO *sshInfo = sessionInfoPtr->sessionSSH;
	BYTE *bufPtr = sessionInfoPtr->receiveBuffer + \
				   sessionInfoPtr->receiveBufPos;
	long length = ( sessionInfoPtr->pendingPacketLength - \
					sessionInfoPtr->pendingPacketPartialLength ) - \
				  sessionInfoPtr->authBlocksize;
	int status;

	/* All errors processing the payload are fatal */
	*readInfo = READINFO_FATAL;

	/* Decrypt the packet in the buffer and MAC the payload.  The length may
	   be zero if the entire message fits into the fixed-length portion, e.g.
	   for channel-close messages that only contain a channel number */
	if( length > 0 )
		{
		status = krnlSendMessage( sessionInfoPtr->iCryptInContext,
						IMESSAGE_CTX_DECRYPT,
						bufPtr + sessionInfoPtr->pendingPacketPartialLength,
						length );
		if( cryptStatusError( status ) )
			return( status );
		}
	status = macPayload( sessionInfoPtr->iAuthInContext, 0,
						 bufPtr + sessionInfoPtr->pendingPacketPartialLength,
						 length, 0, MAC_END, sessionInfoPtr->authBlocksize,
						 TRUE );
	if( cryptStatusError( status ) )
		retExt( sessionInfoPtr, CRYPT_ERROR_SIGNATURE, 
				"Bad message MAC for packet type %d, length %d",
				sshInfo->packetType, 
				sessionInfoPtr->pendingPacketPartialLength + length );

	/* Strip the padding and MAC and update the state information */
	length = sessionInfoPtr->pendingPacketLength - \
			 ( sshInfo->padLength + sessionInfoPtr->authBlocksize );
	sshInfo->readSeqNo++;

	/* If it's not plain data (which was handled at the readHeaderFunction()
	   stage), handle it as a control message */
	if( sshInfo->packetType != SSH2_MSG_CHANNEL_DATA )
		{
		STREAM stream;

		/* Process the control message and reset the receive buffer 
		   indicators to clear it */
		sMemConnect( &stream, bufPtr, length );
		status = processChannelControlMessage( sessionInfoPtr, &stream );
		sMemDisconnect( &stream );
		sessionInfoPtr->receiveBufEnd = sessionInfoPtr->receiveBufPos;
		sessionInfoPtr->pendingPacketLength = 0;
		if( cryptStatusError( status ) )
			{
			/* If we got an OK_SPECIAL status, the packet was handled 
			   internally and we can try again.  If it was a message that
			   the user has to respond to, it's also not a fatal error
			   condition and they can continue afterwards */
			if( status == OK_SPECIAL || status == CRYPT_ENVELOPE_RESOURCE )
				*readInfo = READINFO_NOOP;
			return( status );
			}
		}

	sessionInfoPtr->receiveBufEnd = sessionInfoPtr->receiveBufPos + length;
	sessionInfoPtr->receiveBufPos = sessionInfoPtr->receiveBufEnd;
	sessionInfoPtr->pendingPacketLength = 0;

	*readInfo = READINFO_NONE;
	return( length );
	}

/* Write data over the SSHv2 link */

static int preparePacketFunction( SESSION_INFO *sessionInfoPtr )
	{
	SSH_INFO *sshInfo = sessionInfoPtr->sessionSSH;
	STREAM stream;
	const int dataLength = sessionInfoPtr->sendBufPos - \
						   ( SSH2_HEADER_SIZE + SSH2_PAYLOAD_HEADER_SIZE );
	int length, status;

	assert( !( sessionInfoPtr->flags & SESSION_SENDCLOSED ) );

	/* Wrap up the payload ready for sending:

		byte		SSH2_MSG_CHANNEL_DATA
		uint32		channel_no
		string		data

	   Since this is wrapping in-place data, we first open a write stream to
	   add the header, then open a read stream covering the full buffer in
	   preparation for wrapping the packet */
	openPacketStreamSSH( &stream, sessionInfoPtr, SSH2_PAYLOAD_HEADER_SIZE, 
						 SSH2_MSG_CHANNEL_DATA );
	writeUint32( &stream, getCurrentChannelNo( sessionInfoPtr, \
											   CHANNEL_WRITE ) );
	writeUint32( &stream, dataLength );
	assert( sStatusOK( &stream ) );
	sMemDisconnect( &stream );
	sMemConnect( &stream, sessionInfoPtr->sendBuffer, 
				 sessionInfoPtr->sendBufSize );
	sSkip( &stream, SSH2_HEADER_SIZE + SSH2_PAYLOAD_HEADER_SIZE + \
					dataLength );
	status = wrapPacketSSH2( sessionInfoPtr, &stream, 0 );
	length = stell( &stream );
	sMemDisconnect( &stream );
	if( cryptStatusError( status ) )
		return( status );

	/* If there's control data enqueued to be written, try and append it to
	   the existing data to be sent */
	if( sshInfo->response.type > 0 )
		{
		int length2;

		length2 = appendChannelData( sessionInfoPtr, length );
		if( !cryptStatusError( length2 ) )
			length += length2;
		}

	return( length );
	}

/* Close a previously-opened SSH session */

static void shutdownFunction( SESSION_INFO *sessionInfoPtr )
	{
	/* If we haven't entered the secure state yet (i.e. we're still in the
	   middle of the handshake), this is an abnormal termination, send a
	   disconnect indication:

		byte		SSH2_MSG_DISCONNECT
		uint32		reason_code = SSH2_DISCONNECT_PROTOCOL_ERROR
		string		description = "Handshake failed"
		string		language_tag = "" */
	if( !( sessionInfoPtr->flags & SESSION_ISSECURE_WRITE ) )
		{
		STREAM stream;
		int status;

		openPacketStreamSSH( &stream, sessionInfoPtr, CRYPT_USE_DEFAULT, 
							 SSH2_MSG_DISCONNECT );
		writeUint32( &stream, SSH2_DISCONNECT_PROTOCOL_ERROR );
		writeString32( &stream, "Handshake failed", 16 );
		writeUint32( &stream, 0 );	/* No language tag */
		status = wrapPacketSSH2( sessionInfoPtr, &stream, 0 );
		if( cryptStatusOK( status ) )
			{
			const int length = stell( &stream );

			sendCloseNotification( sessionInfoPtr, 
								   sMemBufPtr( &stream ) - length,
								   length );
			}
		sMemDisconnect( &stream );
		sNetDisconnect( &sessionInfoPtr->stream );
		return;
		}

	/* Close the channel */
	closeChannel( sessionInfoPtr, TRUE );
	}

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

void initSSH2processing( SESSION_INFO *sessionInfoPtr,
						 SSH_HANDSHAKE_INFO *handshakeInfo,
						 const BOOLEAN isServer )
	{
	static const PROTOCOL_INFO protocolInfo = {
		/* General session information */
		FALSE,						/* Request-response protocol */
		SESSION_NONE,				/* Flags */
		SSH_PORT,					/* SSH port */
		SESSION_NEEDS_USERID |		/* Client attributes */
			SESSION_NEEDS_PASSWORD | \
			SESSION_NEEDS_KEYORPASSWORD | \
			SESSION_NEEDS_PRIVKEYSIGN,
				/* The client private key is optional, but if present it has 
				   to be signature-capable */
		SESSION_NEEDS_PRIVATEKEY |	/* Server attributes */
			SESSION_NEEDS_PRIVKEYSIGN,
#ifdef USE_SSH1
		2, 1, 2,					/* Version 2 */
#else
		2, 2, 2,					/* Version 2 */
#endif /* USE_SSH1 */
		NULL, NULL,					/* Content-type */

		/* Protocol-specific information */
		EXTRA_PACKET_SIZE + \
			DEFAULT_PACKET_SIZE,	/* Send/receive buffer size */
		SSH2_HEADER_SIZE + \
			SSH2_PAYLOAD_HEADER_SIZE,/* Payload data start */
		DEFAULT_PACKET_SIZE			/* (Default) maximum packet size */
		};

	sessionInfoPtr->protocolInfo = &protocolInfo;
	sessionInfoPtr->readHeaderFunction = readHeaderFunction;
	sessionInfoPtr->processBodyFunction = processBodyFunction;
	sessionInfoPtr->preparePacketFunction = preparePacketFunction;
	sessionInfoPtr->shutdownFunction = shutdownFunction;
	if( handshakeInfo != NULL )
		{
		if( isServer )
			initSSH2serverProcessing( sessionInfoPtr, handshakeInfo );
		else
			initSSH2clientProcessing( sessionInfoPtr, handshakeInfo );

		handshakeInfo->algoStringPubkeyTbl = algoStringPubkeyTbl;
		}
	}
#endif /* USE_SSH2 */

⌨️ 快捷键说明

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