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

📄 ssh2_msg.c

📁 cryptlib安全工具包
💻 C
📖 第 1 页 / 共 5 页
字号:
	  [	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 REQUEST_TYPE_INFO FAR_BSS 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 },
		{ 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, CRYPT_MAX_TEXTSIZE, 
						   &stringLength );
	if( cryptStatusError( status ) || \
		stringLength <= 0 || stringLength > CRYPT_MAX_TEXTSIZE  || \
		cryptStatusError( wantReply = sgetc( stream ) ) )
		{
		retExt( CRYPT_ERROR_BADDATA, 
				( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 
				  "Invalid %s request packet type",
				  isChannelRequest ? "channel" : "global" ) );
		}

	/* Try and identify the request type */
	for( i = 0; requestInfo[ i ].requestName != NULL && \
				i < FAILSAFE_ARRAYSIZE( requestInfo, REQUEST_TYPE_INFO ); 
		 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( i >= FAILSAFE_ARRAYSIZE( requestInfo, REQUEST_TYPE_INFO ) )
		retIntError();

	/* 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 || \
		( !isServer( sessionInfoPtr ) && ( requestType != REQUEST_NOOP ) ) )
		{
		/* If the other side doesn't want a response to their request, we're 
		   done */
		if( !wantReply )
			return( CRYPT_OK );

		/* Send a request-denied response to the other side's request */
		status = sendRequestResponse( sessionInfoPtr, prevChannelNo,
									  isChannelRequest, FALSE );
		if( isChannelRequest )
			{
			int localStatus;

			/* The request failed, go back to the previous channel */
			localStatus = selectChannel( sessionInfoPtr, prevChannelNo, 
										 CHANNEL_READ );
			if( cryptStatusOK( status ) )
				status = localStatus;
			}
		return( status );
		}

	assert( requestOK && \
			( isServer( sessionInfoPtr ) || \
			  ( 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, CRYPT_MAX_TEXTSIZE,
								   &stringLength );
			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 */
				status = setChannelAttributeString( sessionInfoPtr,
													CRYPT_SESSINFO_SSH_CHANNEL_TYPE,
													"subsystem", 9 );
				if( cryptStatusOK( status ) )
					{
					status = \
						setChannelAttributeString( sessionInfoPtr,
												   CRYPT_SESSINFO_SSH_CHANNEL_ARG1,
												   stringBuffer, 
												   stringLength );
					}
				if( cryptStatusError( status ) )
					retIntError();
				}
			break;

		case REQUEST_SHELL:
		case REQUEST_EXEC:
		case REQUEST_PTY:
		case REQUEST_NOOP:
			/* Generic requests containing extra information that we're not
			   interested in */
			break;

		case REQUEST_PORTFORWARD:
			/* We're being asked for port forwarding, get the address and
			   port information:

				[...]
				string	local_address_to_bind (e.g. "0.0.0.0")
				uint32	local_port_to_bind */
			status = getAddressAndPort( sessionInfoPtr, stream, stringBuffer,
										CRYPT_MAX_TEXTSIZE, &stringLength );
			if( cryptStatusError( status ) )
				requestOK = FALSE;
			else
				{
#if 0			/* This is a global request that doesn't apply to any
				   channel, which makes it rather hard to deal with since
				   we can't associate it with anything that the user can
				   work with.  For now we leave it until there's actual
				   user demand for it */
				setChannelAttribute( sessionInfoPtr,
									 CRYPT_SESSINFO_SSH_CHANNEL_ARG1,
									 stringBuffer, stringLength );
#endif /* 0 */
				}
			break;

		case REQUEST_PORTFORWARD_CANCEL:
			{
			const int offset = stell( stream );
			int iterationCount = 0;

			/* Check that this is a request to close a port for which
			   forwarding was actually requested.  Since there could be
			   multiple channels open on the forwarded port, we keep looking
			   for other channels open on this port until we've cleared them
			   all.  The spec is silent about what happens to open channels
			   when the forwarding is cancelled, but from reading between
			   the lines (new channel-open requests can be received until
			   the forwarding is cancelled) it appears that the channels
			   remain active until the channel itself is closed */
			requestOK = FALSE;
			do
				{
				sseek( stream, offset );
				status = clearAddressAndPort( sessionInfoPtr, stream );
				if( cryptStatusOK( status ) )
					requestOK = TRUE;
				}
			while( cryptStatusOK( status ) && \
				   iterationCount++ < FAILSAFE_ITERATIONS_MED );
			if( iterationCount >= FAILSAFE_ITERATIONS_MED )
				retIntError();
			break;
			}

		case REQUEST_DISALLOWED:
		default:
			/* Anything else we don't allow.  This should already be handled
			   via the default status setting of FALSE, but we make it
			   explicit here */
			requestOK = FALSE;
			break;
		}

	/* Acknowledge the request if necessary */
	if( wantReply )
		{
		status = sendRequestResponse( sessionInfoPtr, prevChannelNo,
									  isChannelRequest, requestOK );
		if( isChannelRequest && \
			( cryptStatusError( status ) || !requestOK ) )
			{
			/* The request failed, go back to the previous channel */
			status = selectChannel( sessionInfoPtr, prevChannelNo,
									CHANNEL_READ );
			}
		if( cryptStatusError( status ) )
			return( status );
		}
	return( requestIsTerminal ? OK_SPECIAL : CRYPT_OK );
	}

/* Process a channel open.  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 */

static int sendOpenResponseFailed( SESSION_INFO *sessionInfoPtr,
								   const long channelNo )
	{
	int status;

	/* Indicate that the request was denied:

		byte	SSH2_MSG_CHANNEL_OPEN_FAILURE
		uint32	recipient_channel
		uint32	reason_code = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED
		string	additional_text = ""
		string	language_tag = ""

	   We always send the same reason code to avoid giving away anything
	   to an attacker */
	status = enqueueResponse( sessionInfoPtr,
							  SSH2_MSG_CHANNEL_OPEN_FAILURE, 4,
							  channelNo,
							  SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
							  0, 0 );
	if( cryptStatusOK( status ) )
		status = sendEnqueuedResponse( sessionInfoPtr, CRYPT_UNUSED );
	return( status );
	}

int processChannelOpen( SESSION_INFO *sessionInfoPtr, STREAM *stream )
	{
	BYTE typeString[ CRYPT_MAX_TEXTSIZE + 8 ];
	BYTE arg1String[ CRYPT_MAX_TEXTSIZE + 8 ], *arg1Ptr = NULL;
	BOOLEAN isPortForwarding = FALSE;
	long channelNo, maxPacketSize;
	int typeLen, arg1Len = 0, status;

	/* Read the channel open request (the type has already been read by the
	   caller):

	  [	byte	type = SSH2_MSG_CHANNEL_OPEN ]
		string	channel_type = "session" | "direct-tcpip"
		uint32	sender_channel
		uint32	initial_window_size
		uint32	max_packet_size
	  [ string	host_to_connect		- For port-forwarding
		uint32	port_to_connect
		string	originator_IP_address
		uint32	originator_port ]

	   As for global/channel requests in processChannelOpen(), we can't
	   return an error indication if we encounter a problem too early in the
	   packet, see the comment for that function for further details */
	status = readString32( stream, typeString, CRYPT_MAX_TEXTSIZE, 
						   &typeLen );
	if( cryptStatusError( status ) || \
		typeLen <= 0 || typeLen > CRYPT_MAX_TEXTSIZE )
		{
		retExt( CRYPT_ERROR_BADDATA,
				( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 
				  "Invalid channel open channel type" ) );
		}
	if( typeLen != 7 || strCompare( typeString, "session", 7 ) )

⌨️ 快捷键说明

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