📄 ssh2_msg.c
字号:
[ uint32 channel_no - For channel reqs ] */
if( isChannelRequest )
status = enqueueResponse( sessionInfoPtr,
isSuccessful ? SSH2_MSG_CHANNEL_SUCCESS : \
SSH2_MSG_CHANNEL_FAILURE, 1,
( channelNo == CRYPT_USE_DEFAULT ) ? \
getCurrentChannelNo( sessionInfoPtr, CHANNEL_READ ) : \
channelNo,
CRYPT_UNUSED, CRYPT_UNUSED, CRYPT_UNUSED );
else
status = enqueueResponse( sessionInfoPtr,
isSuccessful ? SSH2_MSG_GLOBAL_SUCCESS : \
SSH2_MSG_GLOBAL_FAILURE, 0,
CRYPT_UNUSED, CRYPT_UNUSED, CRYPT_UNUSED,
CRYPT_UNUSED );
return( cryptStatusOK( status ) ? \
sendEnqueuedResponse( sessionInfoPtr, CRYPT_UNUSED ) : status );
}
static int processChannelRequest( SESSION_INFO *sessionInfoPtr,
STREAM *stream, const long prevChannelNo )
{
static const REQUEST_TYPE_INFO FAR_BSS requestInfo[] = {
/* Channel/session-creation requests, only permitted on the server-
side */
{ "subsystem", REQUEST_SUBSYSTEM, REQUEST_FLAG_TERMINAL },
{ "tcpip-forward", REQUEST_PORTFORWARD, REQUEST_FLAG_NONE },
{ "cancel-tcpip-forward", REQUEST_PORTFORWARD_CANCEL, REQUEST_FLAG_NONE },
{ "shell", REQUEST_SHELL, REQUEST_FLAG_TERMINAL },
{ "exec", REQUEST_EXEC, REQUEST_FLAG_TERMINAL },
{ "pty-req", REQUEST_PTY, REQUEST_FLAG_NONE },
/* No-op requests */
{ "env", REQUEST_NOOP, REQUEST_FLAG_NONE },
{ "exit-signal", REQUEST_NOOP, REQUEST_FLAG_NONE },
{ "exit-status", REQUEST_NOOP, REQUEST_FLAG_NONE },
{ "signal", REQUEST_NOOP, REQUEST_FLAG_NONE },
{ "xon-xoff", REQUEST_NOOP, REQUEST_FLAG_NONE },
{ "window-change", REQUEST_NOOP, REQUEST_FLAG_NONE },
/* Disallowed requests */
{ "x11-req", REQUEST_DISALLOWED, REQUEST_FLAG_NONE },
{ NULL, REQUEST_NONE, REQUEST_FLAG_NONE },
{ NULL, REQUEST_NONE, REQUEST_FLAG_NONE }
};
SSH_INFO *sshInfo = sessionInfoPtr->sessionSSH;
const BOOLEAN isChannelRequest = \
( sshInfo->packetType == SSH2_MSG_CHANNEL_REQUEST );
REQUEST_TYPE requestType = REQUEST_DISALLOWED;
BYTE stringBuffer[ CRYPT_MAX_TEXTSIZE + 8 ];
BOOLEAN wantReply, requestOK = FALSE, requestIsTerminal = FALSE;
int stringLength, i, status;
assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
assert( isWritePtr( stream, sizeof( STREAM ) ) );
/* Process the channel/global request (the type and channel number
have already been read by the caller):
[ byte type = SSH2_MSG_CHANNEL_REQUEST / SSH2_MSG_GLOBAL_REQUEST ]
[ uint32 recipient_channel - For channel reqs ]
string request_type
boolean want_reply
[...]
If there's an error at this point we can't send back a response
because one or both of the channel number and the want_reply flag
aren't available yet. The consensus among SSH implementors was that
not doing anything if the request packet is invalid is preferable to
sending back a response with a placeholder channel number, or a
response when want_reply could have been false had it been able to
be decoded */
status = readString32( stream, stringBuffer, CRYPT_MAX_TEXTSIZE,
&stringLength );
if( cryptStatusError( status ) || \
stringLength <= 0 || stringLength > CRYPT_MAX_TEXTSIZE || \
cryptStatusError( wantReply = sgetc( stream ) ) )
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Invalid %s request packet type",
isChannelRequest ? "channel" : "global" ) );
}
/* Try and identify the request type */
for( i = 0; requestInfo[ i ].requestName != NULL && \
i < FAILSAFE_ARRAYSIZE( requestInfo, REQUEST_TYPE_INFO );
i++ )
{
if( stringLength == strlen( requestInfo[ i ].requestName ) && \
!memcmp( stringBuffer, requestInfo[ i ].requestName,
stringLength ) )
{
requestType = requestInfo[ i ].requestType;
requestOK = ( requestType != REQUEST_DISALLOWED ) ? \
TRUE : FALSE;
requestIsTerminal = \
( requestInfo[ i ].flags & REQUEST_FLAG_TERMINAL ) ? \
TRUE : FALSE;
break;
}
}
if( i >= FAILSAFE_ARRAYSIZE( requestInfo, REQUEST_TYPE_INFO ) )
retIntError();
/* If it's an explicitly disallowed request type or if we're the client
and it's anything other than a no-op request (for example a request
to execute a command or perform port forwarding), it isn't
permitted */
if( !requestOK || \
( !isServer( sessionInfoPtr ) && ( requestType != REQUEST_NOOP ) ) )
{
/* If the other side doesn't want a response to their request, we're
done */
if( !wantReply )
return( CRYPT_OK );
/* Send a request-denied response to the other side's request */
status = sendRequestResponse( sessionInfoPtr, prevChannelNo,
isChannelRequest, FALSE );
if( isChannelRequest )
{
int localStatus;
/* The request failed, go back to the previous channel */
localStatus = selectChannel( sessionInfoPtr, prevChannelNo,
CHANNEL_READ );
if( cryptStatusOK( status ) )
status = localStatus;
}
return( status );
}
assert( requestOK && \
( isServer( sessionInfoPtr ) || \
( requestType == REQUEST_NOOP ) ) );
/* Process the request. 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 */
switch( requestType )
{
case REQUEST_SUBSYSTEM:
/* We're being asked for a subsystem, record the type:
[...]
string subsystem_name */
status = readString32( stream, stringBuffer, CRYPT_MAX_TEXTSIZE,
&stringLength );
if( cryptStatusError( status ) || \
stringLength <= 0 || stringLength > CRYPT_MAX_TEXTSIZE )
requestOK = FALSE;
else
{
/* The handling of subsystems is somewhat awkward, instead
of opening a subsystem channel SSH first opens a standard
session channel and then layers a subsystem on top of it.
Because of this we have to replace the standard channel
type with a new subsystem channel-type as well as recording
the subsystem type */
status = setChannelAttributeString( sessionInfoPtr,
CRYPT_SESSINFO_SSH_CHANNEL_TYPE,
"subsystem", 9 );
if( cryptStatusOK( status ) )
{
status = \
setChannelAttributeString( sessionInfoPtr,
CRYPT_SESSINFO_SSH_CHANNEL_ARG1,
stringBuffer,
stringLength );
}
if( cryptStatusError( status ) )
retIntError();
}
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,
CRYPT_MAX_TEXTSIZE, &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 );
int iterationCount = 0;
/* 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 ) && \
iterationCount++ < FAILSAFE_ITERATIONS_MED );
if( iterationCount >= FAILSAFE_ITERATIONS_MED )
retIntError();
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, CRYPT_MAX_TEXTSIZE,
&typeLen );
if( cryptStatusError( status ) || \
typeLen <= 0 || typeLen > CRYPT_MAX_TEXTSIZE )
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Invalid channel open channel type" ) );
}
if( typeLen != 7 || strCompare( typeString, "session", 7 ) )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -