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

📄 ssh2_msg.c

📁 cryptlib安全工具包
💻 C
📖 第 1 页 / 共 5 页
字号:
		{
		/* 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 */
			retExt( CRYPT_ERROR_BADDATA,
					( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 
					  "Invalid channel open channel type '%s'", 
					  sanitiseString( typeString, CRYPT_MAX_TEXTSIZE, 
									  typeLen ) ) );
			}
		isPortForwarding = TRUE;
		}
	channelNo = readUint32( stream );
	readUint32( stream );			/* Skip window size */
	status = maxPacketSize = readUint32( stream );
	if( cryptStatusError( status ) )
		retExt( status, 
				( status, SESSION_ERRINFO, "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( CRYPT_ERROR_BADDATA,
				( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 
				  "Invalid channel open maximum packet size %ld",
				  maxPacketSize ) );
		}
	if( isPortForwarding )
		{
		/* Get the source and destination host information */
		status = getAddressAndPort( sessionInfoPtr, stream,
									arg1String, CRYPT_MAX_TEXTSIZE, 
									&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( !isServer( sessionInfoPtr ) )
		{
		sendOpenResponseFailed( sessionInfoPtr, channelNo );
		retExt( CRYPT_ERROR_PERMISSION,
				( CRYPT_ERROR_PERMISSION, SESSION_ERRINFO, 
				  "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( status,
				( status, SESSION_ERRINFO, 
				  "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 ) )
		{
		/* Since we're already in an error state, we can't do much more if 
		   the cleanup from the failed operation fails */
		( void ) deleteChannel( sessionInfoPtr, channelNo, CHANNEL_BOTH, 
								TRUE );
		return( status );
		}

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

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

/* Send a channel close notification.  Returns OK_SPECIAL if the last
   channel is being closed */

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

	/* Delete the channel.  If we've deleted the last active channel,
	   deleteChannel() will return OK_SPECIAL to let us know that there are
	   no more channels left to close */
	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 );

	/* 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 */
	disableErrorReporting( sessionInfoPtr );
	status = sendEnqueuedResponse( sessionInfoPtr, CRYPT_UNUSED );
	enableErrorReporting( sessionInfoPtr );

	/* If it's the last channel, let the caller know (this overrides any
	   possible error return status, since we're about to close the 
	   connection there's not much that we can do with an error anyway) */
	return( lastChannel ? OK_SPECIAL : 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
			   long waiting for the rehandshake to complete before sending
			   new data because the lack of WINDOW_ADJUSTs (in an
			   implementation that sends these with almost every packet, as
			   most do) will screw up flow control and lead to deadlock.
			   This problem got so bad that as of 2.4.0 the ssh.com
			   implementation would detect OpenSSH (the other main
			   implementation at the time) and disable the rehandshake when
			   it was talking to it, but it may not do this for other
			   implementations.

			   To avoid falling into this hole, or at least to fail
			   obviously when the two sides can't agree on how to handle the
			   layering mismatch problem, we report a rehandshake request as
			   an error.  Trying to handle it properly results in hard-to-
			   diagnose errors (it depends on what the layers are doing at
			   the time of the problem), typically some bad-packet error
			   when the other side tries to interpret a connection-layer
			   packet as part of the rehandshake, or when the two sides
			   disagree on when to switch keys and it decrypts with the
			   wrong keys and gets a garbled packet type */
			retExt( CRYPT_ERROR_BADDATA,
					( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 
					  "Unexpected KEXINIT request received" ) );

		case SSH2_MSG_CHANNEL_DATA:
		case SSH2_MSG_CHANNEL_EXTENDED_DATA:
		case SSH2_MSG_CHANNEL_REQUEST:
		case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
		case SSH2_MSG_CHANNEL_EOF:
		case SSH2_MSG_CHANNEL_CLOSE:
			/* All channel-specific messages end up here */
			channelNo = readUint32( stream );
			if( cryptStatusError( channelNo ) )
				{
				/* We can't send an error response to a channel request at
				   this point both because we haven't got to the response-
				   required flag yet and because SSH doesn't provide a
				   mechanism for returning an error response without an
				   accompanying channel number.  The best that we can do is
				   to quietly ignore the packet */
				retExt( CRYPT_ERROR_BADDATA,
						( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 
						  "Invalid channel-specific packet type %d",
						  sshInfo->packetType ) );
				}
			if( channelNo != getCurrentChannelNo( sessionInfoPtr, \
												  CHANNEL_READ ) )
				{
				/* It's a request on something other than the current
				   channel, try and select the new channel */
				status = selectChannel( sessionInfoPtr, channelNo,
										CHANNEL_READ );
				if( cryptStatusError( status ) )
					{
					/* As before for error handling */
					retExt( CRYPT_ERROR_BADDATA,
							( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 
							  "Invalid channel number %ld in "
							  "channel-specific packet type %d, current "
							  "channel is %ld", channelNo,
							  sshInfo->packetType, prevChannelNo ) );
					}
				}
			break;

		default:
			{
			BYTE buffer[ 16 + 8 ];

			/* We got something unexpected, throw an exception in the debug
			   version and let the caller know the details */
			assert( DEBUG_WARN );
			status = sread( stream, buffer, 8 );
			if( cryptStatusError( status ) )
				{
				/* There's not enough data present to dump the start of the
				   packet, provide a more generic response */
				retExt( CRYPT_ERROR_BADDATA,
						( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 
						  "Unexpected control packet type %d received",
						  sshInfo->packetType ) );
				}
			retExt( CRYPT_ERROR_BADDATA,
					( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 
					  "Unexpected control packet type %d received, "
					  "beginning %02X %02X %02X %02X %02X %02X %02X %02X",
					  sshInfo->packetType,
					  buffer[ 0 ], buffer[ 1 ], buffer[ 2 ], buffer[ 3 ],
					  buffer[ 4 ], buffer[ 5 ], buffer[ 6 ], buffer[ 7 ] ) );
			}
		}

	/* From here on we're processing a channel-specific message that applies
	   to the currently selected channel */
	switch( sshInfo->packetType )
		{
		case SSH2_MSG_CHANNEL_DATA:
		case SSH2_MSG_CHANNEL_EXTENDED_DATA:
			{

⌨️ 快捷键说明

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