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

📄 ssh2_msg.c

📁 cryptlib安全工具包
💻 C
📖 第 1 页 / 共 5 页
字号:
/****************************************************************************
*																			*
*					cryptlib SSHv2 Control Message Management				*
*						Copyright Peter Gutmann 1998-2008					*
*																			*
****************************************************************************/

#include <stdio.h>
#if defined( INC_ALL )
  #include "crypt.h"
  #include "misc_rw.h"
  #include "session.h"
  #include "ssh.h"
#else
  #include "crypt.h"
  #include "misc/misc_rw.h"
  #include "session/session.h"
  #include "session/ssh.h"
#endif /* Compiler-specific includes */

#ifdef USE_SSH

/****************************************************************************
*																			*
*								Utility Functions							*
*																			*
****************************************************************************/

/* Read host name/address and port information and format it into string
   form for the caller */

static int readAddressAndPort( SESSION_INFO *sessionInfoPtr, STREAM *stream,
							   char *hostInfo, const int hostInfoMaxLen, 
							   int *hostInfoLen )
	{
	BYTE stringBuffer[ CRYPT_MAX_TEXTSIZE + 8 ];
	char portBuffer[ 16 + 8 ];
	int stringLength, port, portLength, status;

	/* Clear return value */
	*hostInfo = '\0';
	*hostInfoLen = 0;

	/* Get the host and port:

		string	host
		uint32	port */
	status = readString32( stream, stringBuffer, CRYPT_MAX_TEXTSIZE - 4,
						   &stringLength );
	if( cryptStatusError( status ) || \
		stringLength <= 0 || stringLength > CRYPT_MAX_TEXTSIZE - 4 )
		retExt( CRYPT_ERROR_BADDATA,
				( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 
				  "Invalid host name value" ) );
	status = port = readUint32( stream );
	if( cryptStatusError( status ) || port <= 0 || port >= 65535L )
		retExt( CRYPT_ERROR_BADDATA,
				( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 
				  "Invalid port number value" ) );

	/* Convert the info into string form for the caller to process */
	portLength = sprintf_s( portBuffer, 8, ":%d", port );
	memcpy( hostInfo, stringBuffer, stringLength );
	if( stringLength + portLength <= hostInfoMaxLen )
		{
		memcpy( hostInfo + stringLength, portBuffer, portLength );
		stringLength += portLength;
		}
	*hostInfoLen = stringLength;

	return( CRYPT_OK );
	}

/* Add or clear host name/address and port information */

static int getAddressAndPort( SESSION_INFO *sessionInfoPtr, STREAM *stream,
							  char *hostInfo, const int hostInfoMaxLen,
							  int *hostInfoLen )
	{
	int status;

	/* Read the address and port info */
	status = readAddressAndPort( sessionInfoPtr, stream, hostInfo,
								 hostInfoMaxLen, hostInfoLen );
	if( cryptStatusError( status ) )
		return( status );
	if( getChannelStatusAddr( sessionInfoPtr, hostInfo, \
							  *hostInfoLen ) != CHANNEL_NONE )
		{
		/* We're adding new forwarding info, if it already exists this is
		   an error */
		retExt( CRYPT_ERROR_DUPLICATE,
				( CRYPT_ERROR_DUPLICATE, SESSION_ERRINFO, 
				  "Received duplicate request for existing host/port %s",
				  sanitiseString( hostInfo, *hostInfoLen, *hostInfoLen ) ) );
		}

	return( CRYPT_OK );
	}

static int clearAddressAndPort( SESSION_INFO *sessionInfoPtr, STREAM *stream )
	{
#if 0	/* This is a somewhat special-case function in that it does't apply
		   to an open channel but to a past request for forwarding that
		   exists outside of the normal attribute space.  Until this type of
		   functionality is explicitly requested by users, we don't handle
		   this special-case non-attribute data setting */
	SSH_CHANNEL_INFO *channelInfoPtr;
	char hostInfo[ CRYPT_MAX_TEXTSIZE + 8 ];
	int hostInfoLen, status;

	/* Read the address and port info */
	status = readAddressAndPort( sessionInfoPtr, stream, hostInfo, 
								 hostInfoMaxLen, &hostInfoLen );
	if( cryptStatusError( status ) )
		return( status );
	return( deleteChannelAddr( sessionInfoPtr, addrInfo, addrInfoLen ) );
#else
	return( CRYPT_OK );
#endif /* 0 */
	}

/****************************************************************************
*																			*
*							Client-side Channel Management					*
*																			*
****************************************************************************/

/* Create a request for the appropriate type of service, either encrypted-
   telnet, SFTP (or more generically a subsystem), or port forwarding.
   There are several different port-forwarding mechanisms that we can use.
   A global request of type "tcpip-forward" requests forwarding of a remote
   port to the local system, specifying the remote port to be forwarded but
   without actually opening a session/channel, it's merely q request for
   future forwarding.  When a connection arrives on the remote port for
   which forwarding has been requested, the remote system opens a channel of
   type "forwarded-tcpip" to the local system.  To open a connection from a
   locally-forwarded port to a port on the remote system, the local system
   opens a channel of type "direct-tcpip" to the remote system:

	Pkt		Name			Arg1			Arg2		Comment
	---		----			----			----		-------
	open	"session"									Followed by pty-req
														or subsys
	open	"fded-tcpip"	remote_info (in)			Server -> client in
														response.to tcpip-fd
	open	"direct-tcpip"	remote_info		local_info	Client -> server, currently
														local_info = 127.0.0.1
	global	"tcpip-fd"		remote_info (out)			Request for remote
														forwarding

   Once we've opened a standard session, we need to follow it with either a
   pty-request + shell request or a subsystem request:

	Pkt		Name			Arg1			Arg2		Comment
	---		----			----			----		-------
	channel	"pty-req"
	channel "subsystem"		name

   In theory we could bundle the channel open + pty-request + shell request
   into a single packet group to save round-trips, but the packets sent after
   the channel open require the use of the receive-channel number supplied by
   the remote system.  This is usually the same as the send channel that we
   specify, but for some unknown reason Cisco use different send and receive
   channel numbers, requiring that we wait for the response to the channel-
   open before we send any subsequent packets, adding another RTT to the
   exchange */

typedef enum { OPENREQUEST_NONE, OPENREQUEST_STANDALONE,
			   OPENREQUEST_CHANNELONLY, OPENREQUEST_SESSION } OPENREQUEST_TYPE;

static int createOpenRequest( SESSION_INFO *sessionInfoPtr, STREAM *stream,
							  OPENREQUEST_TYPE *requestType )
	{
	const long channelNo = getCurrentChannelNo( sessionInfoPtr,
												CHANNEL_WRITE );
	const int maxPacketSize = sessionInfoPtr->sendBufSize - \
							  EXTRA_PACKET_SIZE;
	URL_INFO urlInfo = { DUMMY_INIT };
	BYTE typeString[ CRYPT_MAX_TEXTSIZE + 8 ];
	BYTE arg1String[ CRYPT_MAX_TEXTSIZE + 8 ];
	BOOLEAN isPortForward = FALSE, isSubsystem = FALSE, isExec = FALSE;
	int typeLen, arg1Len = DUMMY_INIT, status;

	/* Clear return value */
	*requestType = OPENREQUEST_NONE;

	/* Get the information that's needed for the channel we're about to
	   create */
	status = getChannelAttributeString( sessionInfoPtr,
										CRYPT_SESSINFO_SSH_CHANNEL_TYPE,
										typeString, CRYPT_MAX_TEXTSIZE, 
										&typeLen );
	if( cryptStatusError( status ) )
		{
		retExt( status,
				( status, SESSION_ERRINFO, 
				  "Missing channel type for channel activation" ) );
		}
	if( !strCompare( typeString, "subsystem", 9 ) )
		isSubsystem = TRUE;
	if( !strCompare( typeString, "direct-tcpip", 12 ) || \
		!strCompare( typeString, "forwarded-tcpip", 15 ) )
		isPortForward = TRUE;
	if( !strCompare( typeString, "exec", 4 ) )
		isExec = TRUE;
	if( isPortForward || isSubsystem || isExec )
		{
		status = getChannelAttributeString( sessionInfoPtr,
											CRYPT_SESSINFO_SSH_CHANNEL_ARG1,
											arg1String, CRYPT_MAX_TEXTSIZE, 
											&arg1Len );
		if( cryptStatusError( status ) )
			{
			retExt( status,
					( status, SESSION_ERRINFO, 
					  "Missing channel argument (%s) for channel "
					  "activation", isPortForward ? "host name/port" : \
								  isExec ? "command" : "subsystem name" ) );
			}
		}

	/* If we know that the argument is a URL (rather than a subsystem name 
	   or command), check its validity */
	if( isPortForward )
		{
		status = sNetParseURL( &urlInfo, arg1String, arg1Len, URL_TYPE_SSH );
		if( cryptStatusError( status ) )
			{
			retExt( status,
					( status, SESSION_ERRINFO, 
					  "Invalid channel argument (host name/port) for "
					  "channel activation" ) );
			}
		}

	/* Set the request type to tell the caller what to do after they've
	   sent the initial channel open */
	*requestType = isPortForward ? OPENREQUEST_CHANNELONLY : \
								   OPENREQUEST_SESSION;

#if 0	/* Request forwarding of a port from the remote system to the local
		   one.  Once a connection arrives on the remote port it'll open a
		   channel to the local system of type "forwarded-tcpip".  Since
		   this isn't a normal channel open, we return a special status to
		   let the caller know that there's nothing further to do */
	if( "tcpip-forward" )
		{
		URL_INFO urlInfo;

		*requestType = OPENREQUEST_STANDALONE;

		/*	...
			byte	type = SSH_MSG_GLOBAL_REQUEST
			string	request_name = "tcpip-forward"
			boolean	want_reply = FALSE
			string	remote_address_to_bind (e.g. "0.0.0.0")
			uint32	remote_port_to_bind

		   Since this is a special-case request-only message, we let the
		   caller know that they don't have to proceed further with the
		   channel-open */
		status = continuePacketStreamSSH( stream, SSH2_MSG_GLOBAL_REQUEST,
										  &packetOffset );
		if( cryptStatusError( status ) )
			return( status );
		writeString32( stream, "tcpip-forward", 13 );
		sputc( stream, 0 );
		writeString32( stream, urlInfo.host, urlInfo.hostLen );
		writeUint32( stream, urlInfo.port );
		return( wrapPacketSSH2( sessionInfoPtr, stream, packetOffset ) );
		}
#endif /* 0 */

	/* Send a channel open:

		byte	type = SSH2_MSG_CHANNEL_OPEN
		string	channel_type
		uint32	sender_channel
		uint32	initial_window_size = MAX_WINDOW_SIZE
		uint32	max_packet_size = bufSize
		...

	   The use of security protocol-level flow control when there's already
	   a far better, heavily analysed and field-tested network protocol-
	   level flow control mechanism is just stupid.  All it does is create
	   a performance handbrake where throughput can be reduced by as much as
	   an order of magnitude due to SSH's "flow-control" getting in the way
	   (Putty even has an FAQ entry "Why is SFTP so much slower than scp?",
	   for which the correct answer should be "It's the SSH-level flow-
	   control braindamage").  For this reason cryptlib always advertises a
	   maximum window size (effectively disabling the SSH-level flow
	   control) and lets the network stack and network hardware take care of
	   flow control, as they should */
	status = openPacketStreamSSH( stream, sessionInfoPtr, CRYPT_USE_DEFAULT,
								  SSH2_MSG_CHANNEL_OPEN );
	if( cryptStatusError( status ) )
		return( status );
	if( isSubsystem || isExec )
		{
		/* A subsystem is an additional layer on top of the standard
		   channel, so we have to open the channel first and then add the
		   subsystem later via a channel request rather than opening it
		   directly.  An exec is a special case that works like the default
		   type of operation, "shell", but doesn't go via a pty */
		writeString32( stream, "session", 7 );
		}
	else
		writeString32( stream, typeString, typeLen );
	writeUint32( stream, channelNo );
	writeUint32( stream, MAX_WINDOW_SIZE );
	status = writeUint32( stream, maxPacketSize );
	if( isPortForward )
		{
		/* The caller has requested a port-forwarding channel open, continue
		   the basic channel-open packet with port-forwarding info:

			...
			string	remote_host_to_connect
			uint32	rempte_port_to_connect
			string	local_originator_IP_address
			uint32	local_originator_port */
		writeString32( stream, urlInfo.host, urlInfo.hostLen );
		writeUint32( stream, urlInfo.port );
		writeString32( stream, "127.0.0.1", 9 );
		status = writeUint32( stream, 22 );
		}
	if( cryptStatusOK( status ) )

⌨️ 快捷键说明

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