📄 ssh2_msg.c
字号:
}
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 + -