📄 ssh2_msg.c
字号:
status = wrapPacketSSH2( sessionInfoPtr, stream, 0, FALSE, TRUE );
return( status );
}
static int createSessionOpenRequest( SESSION_INFO *sessionInfoPtr,
STREAM *stream )
{
const long channelNo = getCurrentChannelNo( sessionInfoPtr,
CHANNEL_WRITE );
BYTE typeString[ CRYPT_MAX_TEXTSIZE + 8 ];
int typeLen, packetOffset, status;
/* If the caller has requested the use of a custom subsystem (and at the
moment the only one that's likely to be used is SFTP), request this
from the server by modifying the channel that we've just opened to
run the subsystem */
status = getChannelAttributeString( sessionInfoPtr,
CRYPT_SESSINFO_SSH_CHANNEL_TYPE,
typeString, CRYPT_MAX_TEXTSIZE,
&typeLen );
if( cryptStatusError( status ) )
return( status );
if( !strCompare( typeString, "subsystem", 9 ) )
{
BYTE arg1String[ CRYPT_MAX_TEXTSIZE + 8 ];
int arg1Len;
/* Get the subsystem type */
status = getChannelAttributeString( sessionInfoPtr,
CRYPT_SESSINFO_SSH_CHANNEL_ARG1,
arg1String, CRYPT_MAX_TEXTSIZE,
&arg1Len );
if( cryptStatusError( status ) )
return( status );
/* byte type = SSH2_MSG_CHANNEL_REQUEST
uint32 recipient_channel
string request_name = "subsystem"
boolean want_reply = FALSE
string subsystem_name */
status = openPacketStreamSSH( stream, sessionInfoPtr,
CRYPT_USE_DEFAULT,
SSH2_MSG_CHANNEL_REQUEST );
if( cryptStatusError( status ) )
return( status );
writeUint32( stream, channelNo );
writeString32( stream, "subsystem", 9 );
sputc( stream, 0 );
status = writeString32( stream, arg1String, arg1Len );
if( cryptStatusOK( status ) )
status = wrapPacketSSH2( sessionInfoPtr, stream, 0, FALSE, TRUE );
return( status );
}
/* If the caller has requested the use of remote command execution (i.e.
an rexec rather than the usual SSH rsh), run the command directly
without going via a pty */
if( !strCompare( typeString, "exec", 4 ) )
{
BYTE arg1String[ CRYPT_MAX_TEXTSIZE + 8 ];
int arg1Len;
/* Get the command to execute */
status = getChannelAttributeString( sessionInfoPtr,
CRYPT_SESSINFO_SSH_CHANNEL_ARG1,
arg1String, CRYPT_MAX_TEXTSIZE,
&arg1Len );
if( cryptStatusError( status ) )
return( status );
/* byte type = SSH2_MSG_CHANNEL_REQUEST
uint32 recipient_channel
string request_name = "exec"
boolean want_reply = FALSE
string command */
status = openPacketStreamSSH( stream, sessionInfoPtr,
CRYPT_USE_DEFAULT,
SSH2_MSG_CHANNEL_REQUEST );
if( cryptStatusError( status ) )
return( status );
writeUint32( stream, channelNo );
writeString32( stream, "exec", 4 );
sputc( stream, 0 );
status = writeString32( stream, arg1String, arg1Len );
if( cryptStatusOK( status ) )
status = wrapPacketSSH2( sessionInfoPtr, stream, 0, FALSE, TRUE );
return( status );
}
/* It's a standard channel open:
byte type = SSH2_MSG_CHANNEL_REQUEST
uint32 recipient_channel
string request_name = "pty-req"
boolean want_reply = FALSE
string TERM_environment_variable = "xterm"
uint32 cols = 80
uint32 rows = 48
uint32 pixel_width = 0
uint32 pixel_height = 0
string tty_mode_info = ""
... */
status = openPacketStreamSSH( stream, sessionInfoPtr, CRYPT_USE_DEFAULT,
SSH2_MSG_CHANNEL_REQUEST );
if( cryptStatusError( status ) )
return( status );
writeUint32( stream, channelNo );
writeString32( stream, "pty-req", 7 );
sputc( stream, 0 ); /* No reply */
writeString32( stream, "xterm", 5 );/* Generic */
writeUint32( stream, 80 );
writeUint32( stream, 48 ); /* 48 x 80 (we're past 24 x 80) */
writeUint32( stream, 0 );
writeUint32( stream, 0 ); /* No graphics capabilities */
status = writeUint32( stream, 0 ); /* No special TTY modes */
if( cryptStatusOK( status ) )
status = wrapPacketSSH2( sessionInfoPtr, stream, 0, FALSE, TRUE );
if( cryptStatusError( status ) )
return( status );
/* ...
byte type = SSH2_MSG_CHANNEL_REQUEST
uint32 recipient_channel
string request_name = "shell"
boolean want_reply = FALSE
This final request, once sent, moves the server into interactive
session mode */
status = continuePacketStreamSSH( stream, SSH2_MSG_CHANNEL_REQUEST,
&packetOffset );
if( cryptStatusError( status ) )
return( status );
writeUint32( stream, channelNo );
writeString32( stream, "shell", 5 );
status = sputc( stream, 0 ); /* No reply */
if( cryptStatusOK( status ) )
status = wrapPacketSSH2( sessionInfoPtr, stream, packetOffset,
FALSE, TRUE );
return( status );
}
/* Send a channel open */
int sendChannelOpen( SESSION_INFO *sessionInfoPtr )
{
STREAM stream;
OPENREQUEST_TYPE requestType;
const long channelNo = getCurrentChannelNo( sessionInfoPtr,
CHANNEL_READ );
long currentChannelNo;
int length, value, status;
/* Make sure that there's channel data available to activate and
that it doesn't correspond to an already-active channel */
if( channelNo == UNUSED_CHANNEL_NO )
{
retExt( CRYPT_ERROR_NOTINITED,
( CRYPT_ERROR_NOTINITED, SESSION_ERRINFO,
"No current channel information available to activate "
"channel" ) );
}
status = getChannelAttribute( sessionInfoPtr,
CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE,
&value );
if( cryptStatusError( status ) || value )
{
retExt( CRYPT_ERROR_INITED,
( CRYPT_ERROR_INITED, SESSION_ERRINFO,
"Current channel has already been activated" ) );
}
/* Create a request for the appropriate type of service */
status = createOpenRequest( sessionInfoPtr, &stream, &requestType );
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
/* If it's a request-only message that doesn't open a channel,send it
and exit */
if( requestType == OPENREQUEST_STANDALONE )
{
status = sendPacketSSH2( sessionInfoPtr, &stream, TRUE );
sMemDisconnect( &stream );
return( status );
}
/* Send the open request to the server. 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 just ignore the returned values,
which seems to work for all deployed servers */
status = sendPacketSSH2( sessionInfoPtr, &stream, TRUE );
sMemDisconnect( &stream );
if( cryptStatusError( status ) )
return( status );
/* Wait for the server's ack of the channel open request:
byte SSH_MSG_CHANNEL_OPEN_CONFIRMATION
uint32 recipient_channel
uint32 sender_channel
uint32 initial_window_size
uint32 maximum_packet_size
... */
status = length = \
readHSPacketSSH2( sessionInfoPtr, SSH2_MSG_SPECIAL_CHANNEL,
ID_SIZE + UINT32_SIZE + UINT32_SIZE + \
UINT32_SIZE + UINT32_SIZE );
if( cryptStatusError( status ) )
return( status );
sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
if( sgetc( &stream ) == SSH2_MSG_CHANNEL_OPEN_FAILURE )
{
ERROR_INFO *errorInfo = &sessionInfoPtr->errorInfo;
BYTE stringBuffer[ CRYPT_MAX_TEXTSIZE + 8 ];
int stringLen;
/* The channel open failed, tell the caller why:
byte SSH_MSG_CHANNEL_OPEN_FAILURE
uint32 recipient_channel
uint32 reason_code
string additional_text */
readUint32( &stream ); /* Skip channel number */
errorInfo->errorCode = readUint32( &stream );
status = readString32( &stream, stringBuffer, CRYPT_MAX_TEXTSIZE,
&stringLen );
if( cryptStatusError( status ) || \
stringLen <= 0 || stringLen > CRYPT_MAX_TEXTSIZE )
{
/* No error message, the best that we can do is give the reason
code as part of the message */
retExt( CRYPT_ERROR_OPEN,
( CRYPT_ERROR_OPEN, SESSION_ERRINFO,
"Channel open failed, reason code %d",
errorInfo->errorCode ) );
}
retExt( CRYPT_ERROR_OPEN,
( CRYPT_ERROR_OPEN, SESSION_ERRINFO,
"Channel open failed, error message '%s'",
sanitiseString( stringBuffer, CRYPT_MAX_TEXTSIZE,
stringLen ) ) );
}
currentChannelNo = readUint32( &stream );
if( currentChannelNo != channelNo )
{
sMemDisconnect( &stream );
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Invalid channel number %ld in channel open confirmation, "
"should be %ld", currentChannelNo, channelNo ) );
}
currentChannelNo = readUint32( &stream );
sMemDisconnect( &stream );
/* 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 ) && currentChannelNo != channelNo )
{
/* It's unclear why anyone would want to use different channel
numbers for different directions since it's the same channel that
the data is moving across, but Cisco do it anyway */
status = setChannelExtAttribute( sessionInfoPtr,
SSH_ATTRIBUTE_ALTCHANNELNO,
NULL, currentChannelNo );
}
if( cryptStatusOK( status ) )
status = selectChannel( sessionInfoPtr, channelNo, CHANNEL_BOTH );
if( cryptStatusError( status ) )
return( status );
if( requestType == OPENREQUEST_CHANNELONLY )
{
/* If we're just opening a new channel in an existing session, we're
done */
return( CRYPT_OK );
}
assert( requestType == OPENREQUEST_SESSION );
/* It's a session open request that requires additional messages to do
anything useful, create and send the extra packets */
status = createSessionOpenRequest( sessionInfoPtr, &stream );
if( cryptStatusOK( status ) )
status = sendPacketSSH2( sessionInfoPtr, &stream, TRUE );
sMemDisconnect( &stream );
return( status );
}
/****************************************************************************
* *
* Server-side Channel Management *
* *
****************************************************************************/
/* SSH identifies channel requests using awkward string-based identifiers,
to make these easier to work with we map them to integer values */
typedef enum { REQUEST_NONE, REQUEST_SUBSYSTEM, REQUEST_SHELL, REQUEST_EXEC,
REQUEST_PORTFORWARD, REQUEST_PORTFORWARD_CANCEL, REQUEST_PTY,
REQUEST_NOOP, REQUEST_DISALLOWED } REQUEST_TYPE;
#define REQUEST_FLAG_NONE 0x00/* No request flag */
#define REQUEST_FLAG_TERMINAL 0x01/* Request ends negotiation */
typedef struct {
const char FAR_BSS *requestName;/* String form of request type */
const REQUEST_TYPE requestType; /* Integer form of request type */
const int flags; /* Request flags */
} REQUEST_TYPE_INFO;
/* Process a global or channel request */
static int sendRequestResponse( SESSION_INFO *sessionInfoPtr,
const long channelNo,
const BOOLEAN isChannelRequest,
const BOOLEAN isSuccessful )
{
int status;
/* Indicate that the request succeeded/was denied:
byte type = SSH2_MSG_CHANNEL/GLOBAL_SUCCESS/FAILURE
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -