📄 ssh2_chn.c
字号:
assert( channelNo >= 0 );
assert( maxPacketSize >= 1024 && maxPacketSize <= 0x100000L );
assert( isReadPtr( type, typeLen ) );
/* Make sure that this channel doesn't already exist */
if( findChannelInfo( sessionInfoPtr, channelNo ) != NULL )
retExt( sessionInfoPtr, CRYPT_ERROR_DUPLICATE,
"Attempt to add duplicate channel %d", channelNo );
/* SSH channels are allocated unique IDs for tracking by cryptlib,
since (at least in theory) the SSH-level channel IDs may repeat.
If the initial (not-yet-initialised) channel ID matches the
UNUSED_CHANNEL_ID magic value, we initialise it to one past that
value */
if( sshInfo->channelIndex <= UNUSED_CHANNEL_ID )
sshInfo->channelIndex = UNUSED_CHANNEL_ID + 1;
/* Make sure that we haven't exceeded the maximum number of channels */
for( attributeListPtr = sessionInfoPtr->attributeList;
attributeListPtr != NULL; attributeListPtr = attributeListPtr->next )
if( attributeListPtr->attribute == CRYPT_SESSINFO_SSH_CHANNEL )
channelCount++;
if( channelCount > SSH_MAX_CHANNELS )
retExt( sessionInfoPtr, CRYPT_ERROR_OVERFLOW,
"Maximum number (%d) of SSH channels reached",
SSH_MAX_CHANNELS );
/* Initialise the info for the new channel and create it */
memset( &channelInfo, 0, sizeof( SSH_CHANNEL_INFO ) );
channelInfo.channelID = sshInfo->channelIndex++;
channelInfo.readChannelNo = channelInfo.writeChannelNo = channelNo;
channelInfo.maxPacketSize = maxPacketSize;
copyAttributeData( channelInfo.type, &channelInfo.typeLen,
type, typeLen, TRUE );
if( arg1 != NULL )
copyAttributeData( channelInfo.arg1, &channelInfo.arg1Len,
arg1, arg1Len, TRUE );
status = addSessionAttributeEx( &sessionInfoPtr->attributeList,
CRYPT_SESSINFO_SSH_CHANNEL,
&channelInfo, sizeof( SSH_CHANNEL_INFO ),
accessFunction,
ATTR_FLAG_MULTIVALUED | \
ATTR_FLAG_COMPOSITE );
if( cryptStatusError( status ) )
return( status );
/* Select the newly-created channel. We have to select it using the
special-case indicator of CHANNEL_NONE since we can't normally
select an inactive channel */
return( selectChannel( sessionInfoPtr, channelNo, CHANNEL_NONE ) );
}
int createChannel( SESSION_INFO *sessionInfoPtr )
{
SSH_INFO *sshInfo = sessionInfoPtr->sessionSSH;
long channelNo;
/* Create a channel with a new, unused channel number */
while( findChannelInfo( sessionInfoPtr, \
sshInfo->nextChannelNo ) != NULL )
sshInfo->nextChannelNo++;
channelNo = sshInfo->nextChannelNo++;
return( addChannel( sessionInfoPtr, channelNo,
sessionInfoPtr->sendBufSize - EXTRA_PACKET_SIZE,
"session", 7, NULL, 0 ) );
}
int deleteChannel( SESSION_INFO *sessionInfoPtr, const long channelNo,
const CHANNEL_TYPE channelType,
const BOOLEAN deleteLastChannel )
{
SSH_INFO *sshInfo = sessionInfoPtr->sessionSSH;
SSH_CHANNEL_INFO *channelInfoPtr;
ATTRIBUTE_LIST *attributeListPtr;
int channelID;
/* If we can't delete the last remaining channel (it has to be done
explicitly via a session close) and there are less than two active
channels left, we can't do anything */
if( !deleteLastChannel && \
!isChannelActive( sessionInfoPtr, UNUSED_CHANNEL_ID, 2 ) )
return( CRYPT_ERROR_PERMISSION );
/* Locate the channel info */
attributeListPtr = findChannelAttr( sessionInfoPtr, channelNo );
if( attributeListPtr == NULL )
return( isChannelActive( sessionInfoPtr, UNUSED_CHANNEL_ID, 1 ) ? \
CRYPT_ERROR_NOTFOUND : OK_SPECIAL );
channelInfoPtr = attributeListPtr->value;
channelID = channelInfoPtr->channelID;
/* Delete the channel entry. If we're only closing the write side we
mark the channel as closed for write but leave the overall channel
open */
if( channelType == CHANNEL_WRITE )
{
channelInfoPtr->flags |= CHANNEL_FLAG_WRITECLOSED;
if( channelID == sshInfo->currWriteChannel )
sshInfo->currWriteChannel = UNUSED_CHANNEL_ID;
return( isChannelActive( sessionInfoPtr, \
channelInfoPtr->channelID, 1 ) ? \
CRYPT_OK : OK_SPECIAL );
}
deleteSessionAttribute( &sessionInfoPtr->attributeList,
attributeListPtr );
/* If we've deleted the current channel, select a null channel until a
new one is created/selected */
if( channelID == sshInfo->currReadChannel )
sshInfo->currReadChannel = UNUSED_CHANNEL_ID;
if( channelID == sshInfo->currWriteChannel )
sshInfo->currWriteChannel = UNUSED_CHANNEL_ID;
/* We've deleted an open channel, check if there are any channels left
and if not let the caller know */
return( isChannelActive( sessionInfoPtr, UNUSED_CHANNEL_ID, 1 ) ? \
CRYPT_OK : OK_SPECIAL );
}
#if 0
int deleteChannelAddr( SESSION_INFO *sessionInfoPtr, const char *addrInfo,
const int addrInfoLen )
{
const SSH_CHANNEL_INFO *channelInfoPtr;
channelInfoPtr = findChannelInfoAddr( sessionInfoPtr, addrInfo,
addrInfoLen );
if( channelInfoPtr == NULL )
return( CRYPT_ERROR_NOTFOUND );
/* We've found the entry that it corresponds to, clear it. This doesn't
actually delete the entire channel, but merely deletes the forwarding.
See the note in ssh2_msg.c for why this is currently unused */
memset( channelInfoPtr->arg1, 0, CRYPT_MAX_TEXTSIZE );
channelInfoPtr->arg1Len = 0;
return( CRYPT_OK );
}
#endif /* 0 */
/****************************************************************************
* *
* Enqueue/Send Channel Messages *
* *
****************************************************************************/
/* Enqueue a response to a request, to be sent at the next available
opportunity. This is required because we may be in the middle of
assembling or sending a data packet when we need to send the response,
so the response has to be deferred until after the data packet has been
completed and sent */
int enqueueResponse( SESSION_INFO *sessionInfoPtr, const int type,
const int noParams, const long channelNo,
const int param1, const int param2, const int param3 )
{
SSH_RESPONSE_INFO *respPtr = &sessionInfoPtr->sessionSSH->response;
STREAM stream;
/* If there's already a response enqueued, we can't enqueue another one
until it's been sent */
if( respPtr->type != 0 )
{
assert( NOTREACHED );
return( CRYPT_ERROR_OVERFLOW );
}
respPtr->type = type;
sMemOpen( &stream, respPtr->data, SSH_MAX_RESPONSESIZE );
if( noParams > 0 )
writeUint32( &stream, channelNo );
if( noParams > 1 )
writeUint32( &stream, param1 );
if( noParams > 2 )
writeUint32( &stream, param2 );
if( noParams > 3 )
writeUint32( &stream, param3 );
assert( sStatusOK( &stream ) );
respPtr->dataLen = stell( &stream );
sMemDisconnect( &stream );
return( CRYPT_OK );
}
/* Assemble a packet for and send a previously enqueued response */
int sendEnqueuedResponse( SESSION_INFO *sessionInfoPtr, const int offset )
{
SSH_RESPONSE_INFO *respPtr = &sessionInfoPtr->sessionSSH->response;
STREAM stream;
int sendBufOffset = ( offset == CRYPT_UNUSED ) ? \
sessionInfoPtr->sendBufPos : offset;
int status;
assert( sendBufOffset >= 0 );
/* If there's an incomplete packet in the process of being assembled in
the send buffer, we can't do anything */
if( !sessionInfoPtr->partialWrite && \
( sendBufOffset > sessionInfoPtr->sendBufStartOfs ) )
return( CRYPT_OK );
/* Either the send buffer's empty or it contains a completed packet in
the process of being written, if there's not enough room for the
enqueued response we can't do anything */
if( sendBufOffset + ( 32 + CRYPT_MAX_HASHSIZE + CRYPT_MAX_IVSIZE ) > \
sessionInfoPtr->sendBufSize )
return( CRYPT_OK );
assert( ( sendBufOffset <= sessionInfoPtr->sendBufStartOfs ) || \
( sessionInfoPtr->partialWrite && \
sendBufOffset + ( 32 + CRYPT_MAX_HASHSIZE + CRYPT_MAX_IVSIZE ) < \
sessionInfoPtr->sendBufSize ) );
/* If there's nothing in the send buffer, set the start offset to zero.
We have to do this because it's pre-adjusted to accomodate the header
for a payload data packet, since we're assembling our own packet in
the buffer there's no need for this additional header room */
if( sendBufOffset == sessionInfoPtr->sendBufStartOfs )
sessionInfoPtr->sendBufPos = sendBufOffset = 0;
/* Assemble the response as a new packet at the end of any existing
data */
sMemOpen( &stream, sessionInfoPtr->sendBuffer + sendBufOffset,
sessionInfoPtr->sendBufSize - sendBufOffset );
swrite( &stream, "\x00\x00\x00\x00\x00", SSH2_HEADER_SIZE );
status = sputc( &stream, respPtr->type );
if( respPtr->dataLen > 0 )
/* Some responses can consist purely of an ID byte */
status = swrite( &stream, respPtr->data, respPtr->dataLen );
if( cryptStatusOK( status ) )
status = wrapPacketSSH2( sessionInfoPtr, &stream, 0 );
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
/* If we're only assembling the data and the caller is taking care of
sending the assembled packet, we're done */
if( offset != CRYPT_UNUSED )
return( CRYPT_OK );
/* We've sent (or at least assembled) the response, clear the enqueued
data */
memset( respPtr, 0, sizeof( SSH_RESPONSE_INFO ) );
/* Try and write the response */
if( sessionInfoPtr->flags & SESSION_ISOPEN )
{
int dummy;
/* We're in the data transfer phase, use the standard data-flush
mechanism to try and get the data out. We set the partial-write
flag because what we've just added is pre-packaged data that
doesn't have to go through the data-payload encoding process */
sessionInfoPtr->sendBufPos += stell( &stream );
sessionInfoPtr->partialWrite = TRUE;
status = putSessionData( sessionInfoPtr, NULL, 0, &dummy );
}
else
/* We're still in the handshake phase, we can send the packet
directly */
status = sendPacketSSH2( sessionInfoPtr, &stream, TRUE );
sMemDisconnect( &stream );
return( status );
}
/* Enqueue channel control data ready to be sent, and try and send it if
possible */
int enqueueChannelData( SESSION_INFO *sessionInfoPtr, const int type,
const long channelNo, const int param )
{
int status;
status = enqueueResponse( sessionInfoPtr, type, 2, channelNo, param,
CRYPT_UNUSED, CRYPT_UNUSED );
return( cryptStatusOK( status ) ? \
sendEnqueuedResponse( sessionInfoPtr, CRYPT_UNUSED ) : status );
}
/* Append enqueued channel control data to existing channel payload data
without trying to send it (the data send is being piggybacked on a
payload data send and will be handled by the caller) */
int appendChannelData( SESSION_INFO *sessionInfoPtr, const int offset )
{
assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
assert( offset >= 0 && offset < sessionInfoPtr->sendBufSize );
return( sendEnqueuedResponse( sessionInfoPtr, offset ) );
}
#endif /* USE_SSH2 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -