📄 ssh2_chn.c
字号:
const int maxPacketSize, const void *type,
const int typeLen, const void *arg1, const int arg1Len )
{
ATTRIBUTE_LIST *attributeListPtr;
SSH_INFO *sshInfo = sessionInfoPtr->sessionSSH;
SSH_CHANNEL_INFO channelInfo;
int channelCount = 0, iterationCount = 0, status;
assert( channelNo >= 0 );
assert( maxPacketSize >= 1024 && maxPacketSize <= 0x100000L );
assert( isReadPtr( type, typeLen ) );
assert( ( arg1 == NULL && arg1Len == 0 ) ||
isReadPtr( arg1, arg1Len ) );
/* Make sure that this channel doesn't already exist */
if( findChannelInfo( sessionInfoPtr, channelNo ) != NULL )
{
retExt( CRYPT_ERROR_DUPLICATE,
( CRYPT_ERROR_DUPLICATE, SESSION_ERRINFO,
"Attempt to add duplicate channel %ld", 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 && \
iterationCount++ < FAILSAFE_ITERATIONS_MAX;
attributeListPtr = attributeListPtr->next )
{
if( attributeListPtr->attributeID == CRYPT_SESSINFO_SSH_CHANNEL )
channelCount++;
}
if( iterationCount >= FAILSAFE_ITERATIONS_MAX )
retIntError();
if( channelCount > SSH_MAX_CHANNELS )
{
retExt( CRYPT_ERROR_OVERFLOW,
( CRYPT_ERROR_OVERFLOW, SESSION_ERRINFO,
"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;
status = attributeCopyParams( channelInfo.type, CRYPT_MAX_TEXTSIZE,
&channelInfo.typeLen, type, typeLen );
if( cryptStatusOK( status ) && arg1 != NULL )
status = attributeCopyParams( channelInfo.arg1, CRYPT_MAX_TEXTSIZE,
&channelInfo.arg1Len, arg1, arg1Len );
if( cryptStatusOK( status ) )
status = addSessionInfoComposite( &sessionInfoPtr->attributeList,
CRYPT_SESSINFO_SSH_CHANNEL, accessFunction,
&channelInfo, sizeof( SSH_CHANNEL_INFO ),
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;
int iterationCount = 0;
/* Find an unused channel number. Since the peer can request the
creation of arbitrary-numbered channels, we have to be careful to
ensure that we don't clash with any existing peer-requested channel
numbers when we create our own channel */
while( findChannelInfo( sessionInfoPtr, \
sshInfo->nextChannelNo ) != NULL && \
iterationCount++ < FAILSAFE_ITERATIONS_MED )
sshInfo->nextChannelNo++;
if( iterationCount >= FAILSAFE_ITERATIONS_MED )
retIntError();
/* Create a channel with the new channel number */
return( addChannel( sessionInfoPtr, sshInfo->nextChannelNo++,
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 )
{
assert( !( channelInfoPtr->flags & CHANNEL_FLAG_WRITECLOSED ) );
channelInfoPtr->flags |= CHANNEL_FLAG_WRITECLOSED;
if( channelID == sshInfo->currWriteChannel )
sshInfo->currWriteChannel = UNUSED_CHANNEL_ID;
return( isChannelActive( sessionInfoPtr, \
channelInfoPtr->channelID, 1 ) ? \
CRYPT_OK : OK_SPECIAL );
}
deleteSessionInfo( &sessionInfoPtr->attributeList,
&sessionInfoPtr->attributeListCurrent,
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;
int status = CRYPT_OK;
/* If there's already a response enqueued, we can't enqueue another one
until it's been sent */
if( respPtr->type != 0 )
retIntError();
respPtr->type = type;
sMemOpen( &stream, respPtr->data, SSH_MAX_RESPONSESIZE );
if( noParams > 0 )
status = writeUint32( &stream, channelNo );
if( noParams > 1 )
status = writeUint32( &stream, param1 );
if( noParams > 2 )
status = writeUint32( &stream, param2 );
if( noParams > 3 )
status = writeUint32( &stream, param3 );
ENSURES( cryptStatusOK( status ) );
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, FALSE, TRUE );
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_SSH */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -