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

📄 ssh2_msg.c

📁 cryptlib是功能强大的安全工具集。允许开发人员快速在自己的软件中集成加密和认证服务。
💻 C
📖 第 1 页 / 共 4 页
字号:
	/* It's a standard channel open:

		byte	type = SSH2_MSG_CHANNEL_REQUEST
		uint32	recipient_channel
		string	request_name = "pty-req"
		boolean	want_reply = FALSE
		string	TERM_environment_variable = "xterm"
		uint32	cols = 80
		uint32	rows = 48
		uint32	pixel_width = 0
		uint32	pixel_height = 0
		string	tty_mode_info = ""
		... */
	openPacketStreamSSH( stream, sessionInfoPtr, CRYPT_USE_DEFAULT, 
						 SSH2_MSG_CHANNEL_REQUEST );
	writeUint32( stream, channelNo );
	writeString32( stream, "pty-req", 0 );
	sputc( stream, 0 );					/* No reply */
	writeString32( stream, "xterm", 0 );/* Generic */
	writeUint32( stream, 80 );
	writeUint32( stream, 48 );			/* 48 x 80 (we're past 24 x 80) */
	writeUint32( stream, 0 );
	writeUint32( stream, 0 );			/* No graphics capabilities */
	writeUint32( stream, 0 );			/* No special TTY modes */
	status = wrapPacketSSH2( sessionInfoPtr, stream, 0 );
	if( cryptStatusError( status ) )
		return( status );

	/*	...
		byte	type = SSH2_MSG_CHANNEL_REQUEST
		uint32	recipient_channel
		string	request_name = "shell"
		boolean	want_reply = FALSE

	   This final request, once sent, moves the server into interactive 
	   session mode */
	packetOffset = continuePacketStreamSSH( stream, 
											SSH2_MSG_CHANNEL_REQUEST );
	writeUint32( stream, channelNo );
	writeString32( stream, "shell", 0 );
	sputc( stream, 0 );					/* No reply */
	return( wrapPacketSSH2( sessionInfoPtr, stream, packetOffset ) );
	}

/* Send a channel open */

int sendChannelOpen( SESSION_INFO *sessionInfoPtr )
	{
	STREAM stream;
	OPENREQUEST_TYPE requestType;
	const long channelNo = getCurrentChannelNo( sessionInfoPtr,
												CHANNEL_READ );
	long currentChannelNo;
	int length, value, status;

	/* Make sure that there's channel data available to activate and
	   that it doesn't correspond to an already-active channel */
	if( channelNo == UNUSED_CHANNEL_NO )
		retExt( sessionInfoPtr, CRYPT_ERROR_NOTINITED,
				"No current channel information available to activate "
				"channel" );
	status = getChannelAttribute( sessionInfoPtr, 
								  CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE,
								  NULL, &value );
	if( cryptStatusError( status ) || value )
		retExt( sessionInfoPtr, CRYPT_ERROR_INITED,
				"Current channel has already been activated" );

	/* Create a request for the appropriate type of service */
	status = createOpenRequest( sessionInfoPtr, &stream, &requestType );
	if( cryptStatusError( status ) )
		{
		sMemDisconnect( &stream );
		return( status );
		}

	/* If it's a request-only message that doesn't open a channel,send it 
	   and exit */
	if( requestType == OPENREQUEST_STANDALONE )
		{
		status = sendPacketSSH2( sessionInfoPtr, &stream, TRUE );
		sMemDisconnect( &stream );
		return( status );
		}

	/* Send the open request to the server.  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, &stream, TRUE );
	sMemDisconnect( &stream );
	if( cryptStatusError( status ) )
		return( status );

	/* Wait for the server's ack of the channel open request:

		byte	SSH_MSG_CHANNEL_OPEN_CONFIRMATION
		uint32	recipient_channel
		uint32	sender_channel
		uint32	initial_window_size
		uint32	maximum_packet_size
		... */
	length = readPacketSSH2( sessionInfoPtr, SSH2_MSG_SPECIAL_CHANNEL,
							 ID_SIZE + UINT32_SIZE + UINT32_SIZE + \
								UINT32_SIZE + UINT32_SIZE );
	if( cryptStatusError( length ) )
		return( length );
	sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
	if( sgetc( &stream ) == SSH2_MSG_CHANNEL_OPEN_FAILURE )
		{
		BYTE stringBuffer[ CRYPT_MAX_TEXTSIZE + 8 ];
		int stringLen;

		/* The channel open failed, tell the caller why:

			byte	SSH_MSG_CHANNEL_OPEN_FAILURE
			uint32	recipient_channel
			uint32	reason_code
			string	additional_text */
		readUint32( &stream );		/* Skip channel number */
		sessionInfoPtr->errorCode = readUint32( &stream );
		status = readString32( &stream, stringBuffer, &stringLen, 
							   CRYPT_MAX_TEXTSIZE );
		if( cryptStatusError( status ) || \
			stringLen <= 0 || stringLen > CRYPT_MAX_TEXTSIZE )
			/* No error message, the best that we can do is give the reason
			   code as part of the message */
			retExt( sessionInfoPtr, CRYPT_ERROR_OPEN,
					"Channel open failed, reason code %ld", 
					sessionInfoPtr->errorCode );
		stringBuffer[ stringLen ] = '\0';
		retExt( sessionInfoPtr, CRYPT_ERROR_OPEN,
				"Channel open failed, error message '%s'", 
				sanitiseString( stringBuffer ) );
		}
	currentChannelNo = readUint32( &stream );
	if( currentChannelNo != channelNo )
		{
		sMemDisconnect( &stream );
		retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
				"Invalid channel number %ld in channel open confirmation, "
				"should be %ld", currentChannelNo, channelNo );
		}
	currentChannelNo = readUint32( &stream );
	sMemDisconnect( &stream );

	/* The channel has been successfully created, mark it as active and 
	   select it for future exchanges */
	setChannelExtAttribute( sessionInfoPtr, SSH_ATTRIBUTE_ACTIVE,
							NULL, TRUE );
	if( currentChannelNo != channelNo )
	/* It's unclear why anyone would use different channel numbers for 
	   different directions, since it's the same channel that the data is 
	   moving across.  All (known) implementations use the same value in 
	   both directions, just in case anyone doesn't we throw an exception in 
	   the debug version */
		setChannelExtAttribute( sessionInfoPtr, SSH_ATTRIBUTE_ALTCHANNELNO, 
								 NULL, currentChannelNo );
	status = selectChannel( sessionInfoPtr, channelNo, CHANNEL_BOTH );
	if( ( requestType == OPENREQUEST_CHANNELONLY ) || \
		cryptStatusError( status ) )
		return( status );
	assert( requestType == OPENREQUEST_SESSION );

	/* It's a session open request that requires additional messages to do 
	   anything useful, create and send the extra packets */
	status = createSessionOpenRequest( sessionInfoPtr, &stream );
	if( cryptStatusOK( status ) )
		status = sendPacketSSH2( sessionInfoPtr, &stream, TRUE );
	sMemDisconnect( &stream );
	return( status );
	}

/****************************************************************************
*																			*
*							Server-side Channel Management					*
*																			*
****************************************************************************/

/* SSH identifies channel requests using awkward string-based identifiers,
   to make these easier to work with we map them to integer values */

typedef enum { REQUEST_NONE, REQUEST_SUBSYSTEM, REQUEST_SHELL, REQUEST_EXEC, 
			   REQUEST_PORTFORWARD, REQUEST_PORTFORWARD_CANCEL, REQUEST_PTY,
			   REQUEST_NOOP, REQUEST_DISALLOWED } REQUEST_TYPE;

#define REQUEST_FLAG_NONE		0x00/* No request flag */
#define REQUEST_FLAG_TERMINAL	0x01/* Request ends negotiation */

typedef struct { 
	const char *requestName;		/* String form of request type */
	const REQUEST_TYPE requestType;	/* Integer form of request type */
	const int flags;				/* Request flags */
	} REQUEST_TYPE_INFO;

/* Process a global or channel request */

static int sendRequestResponse( SESSION_INFO *sessionInfoPtr,
								const long channelNo,
								const BOOLEAN isChannelRequest,
								const BOOLEAN isSuccessful )
	{
	int status;

	/* Indicate that the request succeeded/was denied:

		byte	type = SSH2_MSG_CHANNEL/GLOBAL_SUCCESS/FAILURE
	  [	uint32	channel_no		- For channel reqs ] */
	if( isChannelRequest )
		status = enqueueResponse( sessionInfoPtr, 
					isSuccessful ? SSH2_MSG_CHANNEL_SUCCESS : \
								   SSH2_MSG_CHANNEL_FAILURE, 1,
					( channelNo == CRYPT_USE_DEFAULT ) ? \
						getCurrentChannelNo( sessionInfoPtr, CHANNEL_READ ) : \
						channelNo,
					CRYPT_UNUSED, CRYPT_UNUSED, CRYPT_UNUSED );
	else
		status = enqueueResponse( sessionInfoPtr, 
					isSuccessful ? SSH2_MSG_GLOBAL_SUCCESS : \
								   SSH2_MSG_GLOBAL_FAILURE, 0,
					CRYPT_UNUSED, CRYPT_UNUSED, CRYPT_UNUSED, 
					CRYPT_UNUSED );
	return( cryptStatusOK( status ) ? \
			sendEnqueuedResponse( sessionInfoPtr, CRYPT_UNUSED ) : status );
	}

static int processChannelRequest( SESSION_INFO *sessionInfoPtr, 
								  STREAM *stream, const long prevChannelNo )
	{
	static const FAR_BSS REQUEST_TYPE_INFO requestInfo[] = {
		/* Channel/session-creation requests, only permitted on the server-
		   side */
		{ "subsystem", REQUEST_SUBSYSTEM, REQUEST_FLAG_TERMINAL },
		{ "tcpip-forward", REQUEST_PORTFORWARD, REQUEST_FLAG_NONE },
		{ "cancel-tcpip-forward", REQUEST_PORTFORWARD_CANCEL, REQUEST_FLAG_NONE },
		{ "shell", REQUEST_SHELL, REQUEST_FLAG_TERMINAL }, 
		{ "exec", REQUEST_EXEC, REQUEST_FLAG_TERMINAL },
		{ "pty-req", REQUEST_PTY, REQUEST_FLAG_NONE },

		/* No-op requests */
		{ "env", REQUEST_NOOP, REQUEST_FLAG_NONE },
		{ "exit-signal", REQUEST_NOOP, REQUEST_FLAG_NONE },
		{ "exit-status", REQUEST_NOOP, REQUEST_FLAG_NONE },
		{ "signal", REQUEST_NOOP, REQUEST_FLAG_NONE },
		{ "xon-xoff", REQUEST_NOOP, REQUEST_FLAG_NONE },
		{ "window-change", REQUEST_NOOP, REQUEST_FLAG_NONE },

		/* Disallowed requests */
		{ "x11-req", REQUEST_DISALLOWED, REQUEST_FLAG_NONE },
		{ NULL, REQUEST_NONE, REQUEST_FLAG_NONE }
		};
	SSH_INFO *sshInfo = sessionInfoPtr->sessionSSH;
	const BOOLEAN isChannelRequest = \
			( sshInfo->packetType == SSH2_MSG_CHANNEL_REQUEST );
	REQUEST_TYPE requestType = REQUEST_DISALLOWED;
	BYTE stringBuffer[ CRYPT_MAX_TEXTSIZE + 8 ];
	BOOLEAN wantReply, requestOK = FALSE, requestIsTerminal = FALSE;
	int stringLength, i, status;

	assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
	assert( isWritePtr( stream, sizeof( STREAM ) ) );

	/* Process the channel/global request (the type and channel number
	   have already been read by the caller):

	  [	byte	type = SSH2_MSG_CHANNEL_REQUEST / SSH2_MSG_GLOBAL_REQUEST ]
	  [	uint32	recipient_channel	- For channel reqs ]
		string	request_type
		boolean	want_reply
		[...]

	   If there's an error at this point we can't send back a response 
	   because one or both of the channel number and the want_reply flag
	   aren't available yet.  The consensus among SSH implementors was that
	   not doing anything if the request packet is invalid is preferable to
	   sending back a response with a placeholder channel number, or a 
	   response when want_reply could have been false had it been able to
	   be decoded */
	status = readString32( stream, stringBuffer, &stringLength, 
						   CRYPT_MAX_TEXTSIZE );
	if( cryptStatusError( status ) || \
		stringLength <= 0 || stringLength > CRYPT_MAX_TEXTSIZE  || \
		cryptStatusError( wantReply = sgetc( stream ) ) )
		retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
				"Invalid %s request packet type",
				isChannelRequest ? "channel" : "global" );

	/* Try and identify the request type */
	for( i = 0; requestInfo[ i ].requestName != NULL; i++ )
		if( stringLength == strlen( requestInfo[ i ].requestName ) && \
			!memcmp( stringBuffer, requestInfo[ i ].requestName, 
					 stringLength ) )
			{
			requestType = requestInfo[ i ].requestType;
			requestOK = ( requestType != REQUEST_DISALLOWED ) ? \
						TRUE : FALSE;
			requestIsTerminal = \
					( requestInfo[ i ].flags & REQUEST_FLAG_TERMINAL ) ? \
					TRUE : FALSE;
			break;
			}

	/* If it's an explicitly disallowed request type or if we're the client 
	   and it's anything other than a no-op request (for example a request 
	   to execute a command or perform port forwarding), it isn't 
	   permitted */
	if( !requestOK || \
		( !( sessionInfoPtr->flags & SESSION_ISSERVER ) && \
		  ( requestType != REQUEST_NOOP ) ) )
		{
		if( wantReply )
			{
			status = sendRequestResponse( sessionInfoPtr, prevChannelNo,
										  isChannelRequest, FALSE );
			if( isChannelRequest )
				/* The request failed, go back to the previous channel */
				selectChannel( sessionInfoPtr, prevChannelNo, CHANNEL_READ );
			}
		return( status );
		}

	assert( requestOK && \
			( ( sessionInfoPtr->flags & SESSION_ISSERVER ) || \
			  ( requestType == REQUEST_NOOP ) ) );

	/* Process the request.  Since these are administrative messages that 
	   aren't visible to the caller, we don't bail out if we encounter a 
	   problem, we just deny the request */
	switch( requestType )
		{
		case REQUEST_SUBSYSTEM:
			/* We're being asked for a subsystem, record the type:

				[...]
				string	subsystem_name */
			status = readString32( stream, stringBuffer, &stringLength, 
								   CRYPT_MAX_TEXTSIZE );
			if( cryptStatusError( status ) || \
				stringLength <= 0 || stringLength > CRYPT_MAX_TEXTSIZE )
				requestOK = FALSE;
			else
				{
				/* The handling of subsystems is somewhat awkward, instead
				   of opening a subsystem channel SSH first opens a standard
				   session channel and then layers a subsystem on top of it.
				   Because of this we have to replace the standard channel 
				   type with a new subsystem channel-type as well as recording
				   the subsystem type */
				setChannelAttribute( sessionInfoPtr, 
									 CRYPT_SESSINFO_SSH_CHANNEL_TYPE,
									 "subsystem", 9 );
				setChannelAttribute( sessionInfoPtr, 
									 CRYPT_SESSINFO_SSH_CHANNEL_ARG1,
									 stringBuffer, stringLength );

⌨️ 快捷键说明

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