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

📄 ssh2_msg.c

📁 cryptlib是功能强大的安全工具集。允许开发人员快速在自己的软件中集成加密和认证服务。
💻 C
📖 第 1 页 / 共 4 页
字号:
				}
			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,
										&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 );

			/* 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 ) );
			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, &typeLen, CRYPT_MAX_TEXTSIZE );
	if( cryptStatusError( status ) || \
		typeLen <= 0 || typeLen > CRYPT_MAX_TEXTSIZE )
		retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
				"Invalid channel open channel type" );
	if( typeLen != 7 || strCompare( typeString, "session", 7 ) )
		{
		/* It's not a normal channel open, see if the caller is trying to
		   do port forwarding */
		if( typeLen != 12 || strCompare( typeString, "direct-tcpip", 12 ) )
			{
			/* It's something else, report it as an error */
			typeString[ typeLen ] = '\0';
			retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
					"Invalid channel open channel type '%'", typeString );
			}
		isPortForwarding = TRUE;
		}
	channelNo = readUint32( stream );
	readUint32( stream );			/* Skip window size */
	status = maxPacketSize = readUint32( stream );
	if( cryptStatusError( status ) )
		retExt( sessionInfoPtr, status, "Invalid channel open packet" );
	if( maxPacketSize < 1024 || maxPacketSize > 0x100000L )
		{
		/* General sanity check to make sure that the packet size is in the 
		   range 1K ... 16MB.  We've finally got valid packet data so we can
		   send error responses from now on */
		sendOpenResponseFailed( sessionInfoPtr, channelNo );
		retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
				"Invalid channel open maximum packet size %d", 
				maxPacketSize );
		}
	if( isPortForwarding )
		{
		/* Get the source and destination host information */
		status = getAddressAndPort( sessionInfoPtr, stream,
									arg1String, &arg1Len );
		if( cryptStatusError( status ) )
			{
			sendOpenResponseFailed( sessionInfoPtr, channelNo );
			return( status );
			}
		arg1Ptr = arg1String;
		}
	maxPacketSize = min( maxPacketSize, \
						 sessionInfoPtr->receiveBufSize - EXTRA_PACKET_SIZE );

	/* If this is the client, opening a new channel by the server isn't 
	   permitted */
	if( !( sessionInfoPtr->flags & SESSION_ISSERVER ) )
		{
		sendOpenResponseFailed( sessionInfoPtr, channelNo );
		retExt( sessionInfoPtr, CRYPT_ERROR_PERMISSION,
				"Server attempted to a open channel to the client" );
		}

	/* Add the new channel */
	status = addChannel( sessionInfoPtr, channelNo, maxPacketSize, 
						 typeString, typeLen, arg1Ptr, arg1Len );
	if( cryptStatusError( status ) )
		{
		sendOpenResponseFailed( sessionInfoPtr, channelNo );
		retExt( sessionInfoPtr, status,
				"Couldn't add new channel %ld", channelNo );
		}

	/* Send back the open confirmation:

		byte	type = SSH2_MSG_CHANNEL_OPEN_CONFIRMATION
		uint32	recipient_channel = prev. sender_channel
		uint32	sender_channel
		uint32	initial_window_size = MAX_WINDOW_SIZE
		uint32	max_packet_size = bufSize

	   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 try and comply with a client's request for smaller data 
	   quantities, but also return a smaller-than-requested data size value 
	   if they ask for too much data.

	   See the comments in the client-side channel-open code for the reason 
	   for the window size */
	status = enqueueResponse( sessionInfoPtr, 
							  SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, 4, 
							  channelNo, channelNo, 
							  MAX_WINDOW_SIZE, maxPacketSize );
	if( cryptStatusOK( status ) )
		status = sendEnqueuedResponse( sessionInfoPtr, CRYPT_UNUSED );
	if( cryptStatusError( status ) )
		{
		deleteChannel( sessionInfoPtr, channelNo, CHANNEL_BOTH, TRUE );
		return( status );
		}

	/* The channel has been successfully created, mark it as active and 
	   select it for future exchanges */
	setChannelExtAttribute( sessionInfoPtr, SSH_ATTRIBUTE_ACTIVE,
							NULL, TRUE );
	return( selectChannel( sessionInfoPtr, channelNo, CHANNEL_BOTH ) );
	}

/****************************************************************************
*																			*
*							General Channel Management						*
*																			*
****************************************************************************/

/* Send a channel close notification */

static int sendChannelClose( SESSION_INFO *sessionInfoPtr,
							 const long channelNo,
							 const CHANNEL_TYPE channelType,
							 const BOOLEAN closeLastChannel )
	{
	BOOLEAN lastChannel = FALSE;
	int status;

	/* Delete the channel */
	status = deleteChannel( sessionInfoPtr, channelNo, channelType,
							closeLastChannel  );
	if( status == OK_SPECIAL )
		lastChannel = TRUE;

	/* Prepare the channel-close notification:

		byte		SSH2_MSG_CHANNEL_CLOSE
		uint32		channel_no */
	status = enqueueResponse( sessionInfoPtr, SSH2_MSG_CHANNEL_CLOSE, 1, 
							  channelNo, CRYPT_UNUSED, CRYPT_UNUSED, 
							  CRYPT_UNUSED );
	if( cryptStatusError( status ) )
		return( status );

	/* If it's the last channel, don't try and send the close, since this
	   will be sent as part of the session shutdown process */
	if( lastChannel )
		return( OK_SPECIAL );
	
	/* We can't safely use anything that ends up at sendPacketSSH2() at this 
	   point since we may be closing the connection in response to a link 
	   error, in which case the error returned from the packet send would 
	   overwrite the actual error information.  Because of this we send the
	   response with the no-report-error flag set to suppress reporting of
	   network errors during the send */
	sessionInfoPtr->flags |= SESSION_NOREPORTERROR;
	status = sendEnqueuedResponse( sessionInfoPtr, CRYPT_UNUSED );
	sessionInfoPtr->flags &= ~SESSION_NOREPORTERROR;
	return( status );
	}

/* Process a channel control message.  Returns OK_SPECIAL to tell the caller
   to try again with the next packet */

int processChannelControlMessage( SESSION_INFO *sessionInfoPtr, 
								  STREAM *stream )
	{
	SSH_INFO *sshInfo = sessionInfoPtr->sessionSSH;
	const long prevChannelNo = \
				getCurrentChannelNo( sessionInfoPtr, CHANNEL_READ );
	long channelNo;
	int status;

	/* See what we've got.  SSHv2 has a pile of noop-equivalents that we 
	   have to handle as well as the obvious no-ops.  We can also get global 
	   and channel requests for assorted reasons and a constant stream of 
	   window adjust messages to implement the SSH performance handbrake */
	switch( sshInfo->packetType )
		{
		case SSH2_MSG_GLOBAL_REQUEST:
			status = processChannelRequest( sessionInfoPtr, stream, 
											CRYPT_UNUSED );
			if( cryptStatusError( status ) && status != OK_SPECIAL )
				return( status );
			return( OK_SPECIAL );

		case SSH2_MSG_CHANNEL_OPEN:
			status = processChannelOpen( sessionInfoPtr, stream );
			if( cryptStatusError( status ) )
				return( status );

			/* Tell the caller that they have to process the new channel 
			   info before they can continue */
			return( CRYPT_ENVELOPE_RESOURCE );

		case SSH2_MSG_IGNORE:
		case SSH2_MSG_DEBUG:
			/* Nothing to see here, move along, move along:

				byte	SSH2_MSG_IGNORE
				string	data

				byte	SSH2_MSG_DEBUG
				boolean	always_display
				string	message
				string	language_tag */
			return( OK_SPECIAL );

		case SSH2_MSG_DISCONNECT:
			/* This only really seems to be used during the handshake phase, 
			   once a channel is open it (and the session as a whole) is 
			   disconnected with a channel EOF/close, but we handle it here
			   just in case */
			return( getDisconnectInfo( sessionInfoPtr, stream ) );

		case SSH2_MSG_KEXINIT:
			/* The SSH spec is extremely vague about the sequencing of 
			   operations during a rehandshake.  Unlike SSL, there is no 
			   real indication of what happens to the connection-layer 
			   transfers while a transport-layer rehandshake is in progress.  
			   Also unlike SSL, we can't refuse a rehandshake by ignoring 
			   the request, so once we've fallen we can't get up any more.  
			   This is most obvious with ssh.com's server, which starting
			   with version 2.3.0 would do a rehandshake every hour (for a 
			   basic encrypted telnet session, while a high-volume IPsec 
			   link can run for hours before it feels the need to do this).  
			   To make things even messier, neither side can block for too 

⌨️ 快捷键说明

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