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