⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ssh2_msg.c

📁 cryptlib安全工具包
💻 C
📖 第 1 页 / 共 5 页
字号:
			const int streamPos = stell( stream );
			const BOOLEAN hasWindowBug = \
				( sessionInfoPtr->protocolFlags & SSH_PFLAG_WINDOWBUG );
			long length;
			int windowCount;

			/* Get the payload length and make sure that it's
			   (approximately) valid.  More exact checking will be done
			   by the caller (which is why we reset the stream position
			   to allow it to be re-read), all that we're really interested
			   in here is that the length is approximately valid for window-
			   adjust calculation purposes */
			length = readUint32( stream );
			sseek( stream, streamPos );
			if( length < 0 || length > sessionInfoPtr->receiveBufSize )
				{
				retExt( CRYPT_ERROR_BADDATA,
						( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 
						  "Invalid data packet payload length %ld, should "
						  "be 0...%d", length, 
						  sessionInfoPtr->receiveBufSize ) );
				}

			/* These are messages that consume window space, adjust the data
			   window and communicate changes to the other side if necessary.
			   See the comment in sendChannelOpen() for the reason for the
			   window size handling */
			status = getChannelExtAttribute( sessionInfoPtr,
											 SSH_ATTRIBUTE_WINDOWCOUNT,
											 NULL, 0, &windowCount );
			if( cryptStatusError( status ) )
				retIntError();
			windowCount += length;
			if( windowCount < 0 || \
				windowCount > MAX_WINDOW_SIZE - \
							  sessionInfoPtr->sendBufSize || \
				hasWindowBug )
				{
				/* Send the window adjust to the remote system:

					byte	SSH2_MSG_CHANNEL_WINDOW_ADJUST
					uint32	channel
					uint32	bytes_to_add

				   Unfortunately the error status we return from a failed
				   window adjust is going to come as a complete surprise to
				   the caller because we're supposed to be processing a read 
				   and not a write at this point, the write is only required 
				   by SSH's braindamaged flow-control handling */
				status = enqueueChannelData( sessionInfoPtr,
									SSH2_MSG_CHANNEL_WINDOW_ADJUST,
									channelNo, hasWindowBug ? \
										length : MAX_WINDOW_SIZE );
				if( cryptStatusError( status ) )
					{
					retExt( status,
							( status, SESSION_ERRINFO, 
							  "Error sending SSH window adjust for data "
							  "flow control" ) );
					}

				/* We've reset the window, start again from zero */
				windowCount = 0;
				}
			status = setChannelExtAttribute( sessionInfoPtr,
											 SSH_ATTRIBUTE_WINDOWCOUNT,
											 NULL, windowCount );
			if( cryptStatusError( status ) )
				retIntError();

			/* If it's a standard data packet, we're done */
			if( sshInfo->packetType == SSH2_MSG_CHANNEL_DATA )
				return( CRYPT_OK );

			/* The extended data message is used for out-of-band data sent
			   over a channel, specifically output sent to stderr from a
			   shell command.  What to do with this is somewhat uncertain,
			   the only possible action that we could take apart from just
			   ignoring it is to convert it back to in-band data.  However,
			   something running a shell command may not expect to get
			   anything returned in this manner (see the comment for the
			   port-forwarding channel open in the client-side channel-open
			   code for more on this), so for now we just ignore it and
			   assume that the user will rely on results sent as in-band
			   data.  This should be fairly safe since this message type
			   seems to be rarely (if ever) used, so apps will function
			   without it */
			return( OK_SPECIAL );
			}

		case SSH2_MSG_CHANNEL_REQUEST:
			status = processChannelRequest( sessionInfoPtr, stream,
											prevChannelNo );
			if( cryptStatusError( status ) && status != OK_SPECIAL )
				return( status );
			return( OK_SPECIAL );

		case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
			/* Another noop-equivalent (but a very performance-affecting
			   one) */
			return( OK_SPECIAL );

		case SSH2_MSG_CHANNEL_EOF:
			/* According to the SSH docs the EOF packet is mostly a courtesy
			   notification, however many implementations seem to use a
			   channel EOF in place of a close before sending a disconnect
			   message */
			return( OK_SPECIAL );

		case SSH2_MSG_CHANNEL_CLOSE:
			/* The peer has closed their side of the channel, if our side
			   isn't already closed (in other words if this message isn't
			   a response to a close that we sent), close our side as well */
			if( getChannelStatus( sessionInfoPtr, channelNo ) == CHANNEL_BOTH )
				status = sendChannelClose( sessionInfoPtr, channelNo,
										   CHANNEL_BOTH, TRUE );
			else
				{
				/* We've already closed our side of the channel, delete it */
				status = deleteChannel( sessionInfoPtr, channelNo,
										CHANNEL_BOTH, TRUE );
				}

			/* If this wasn't the last channel, we're done */
			if( status != OK_SPECIAL )
				return( OK_SPECIAL );

			/* We've closed the last channel, indicate that the overall
			   connection is now closed.  This behaviour isn't mentioned in
			   the spec, but it seems to be the standard way of handling
			   things, particularly for the most common case where
			   channel == session */
			sessionInfoPtr->flags |= SESSION_SENDCLOSED;
			retExt( CRYPT_ERROR_COMPLETE,
					( CRYPT_ERROR_COMPLETE, SESSION_ERRINFO, 
					  "Remote system closed last remaining SSH channel" ) );
		}

	retIntError();
	}

/* Close a channel */

int closeChannel( SESSION_INFO *sessionInfoPtr,
				  const BOOLEAN closeAllChannels )
	{
	READSTATE_INFO readInfo;
	const int currWriteChannelNo = \
				getCurrentChannelNo( sessionInfoPtr, CHANNEL_WRITE );
	int noChannels = 1, status;

	/* If we've already sent the final channel-close message in response to
	   getting a final close notification from the peer, all that's left
	   to do is to disconnect the session */
	if( sessionInfoPtr->flags & SESSION_SENDCLOSED )
		{
		sNetDisconnect( &sessionInfoPtr->stream );
		return( CRYPT_OK );
		}

	/* Normally we can keep closing open channels until we hit the last one
	   whereupon we close the overall session, however if we're closing a
	   single identified channel we can't automatically close the whole
	   session as a side-effect of closing the single channel */
	if( !closeAllChannels && currWriteChannelNo == UNUSED_CHANNEL_NO )
		{
		retExt( CRYPT_ERROR_NOTINITED,
				( CRYPT_ERROR_NOTINITED, SESSION_ERRINFO, 
				  "No channel information available to close the current "
				  "channel" ) );
		}

	/* If there's no channel open, close the session with a session
	   disconnect rather than a channel close:

		byte		SSH2_MSG_DISCONNECT
		uint32		reason_code = SSH2_DISCONNECT_CONNECTION_LOST
		string		description = ""
		string		language_tag = ""

	   The spec doesn't explain what the reason codes actually mean, but
	   SSH2_DISCONNECT_CONNECTION_LOST seems to be the least inappropriate
	   disconnect reason at this point */
	if( currWriteChannelNo == UNUSED_CHANNEL_NO )
		{
		/* Since we're closing the session, there's not much that we can do
		   in the case of an error because the very next operation is to 
		   shut down the network session, so we don't do anything if the 
		   send fails */
		status = enqueueResponse( sessionInfoPtr, SSH2_MSG_DISCONNECT, 3,
								  SSH2_DISCONNECT_CONNECTION_LOST, 0, 0,
								  CRYPT_UNUSED );
		if( cryptStatusOK( status ) )
			( void ) sendEnqueuedResponse( sessionInfoPtr, CRYPT_UNUSED );
		sessionInfoPtr->flags |= SESSION_SENDCLOSED;
		sNetDisconnect( &sessionInfoPtr->stream );
		return( CRYPT_OK );
		}

	/* Close one or all channels */
	if( closeAllChannels )
		{
		int iterationCount = 0;

		/* Get the first available channel (which must succeed, since we
		   just checked it above) and close each successive channel in
		   turn */
		status = selectChannel( sessionInfoPtr, CRYPT_USE_DEFAULT,
								CHANNEL_WRITE );
		for( noChannels = 0; 
			 cryptStatusOK( status ) && \
				cryptStatusOK( selectChannel( sessionInfoPtr, CRYPT_USE_DEFAULT,
											  CHANNEL_WRITE ) ) && \
				iterationCount++ < FAILSAFE_ITERATIONS_MED; 
			 noChannels++ )
			{
			status = sendChannelClose( sessionInfoPtr,
						getCurrentChannelNo( sessionInfoPtr, CHANNEL_WRITE ),
						CHANNEL_WRITE, TRUE );
			}
		if( iterationCount >= FAILSAFE_ITERATIONS_MED )
			retIntError();
		}
	else
		{
		/* We're just closing one channel, close the write side.  The
		   complete close will be done when the other side acknowledges our
		   close.  If this isn't the last open channel, the response to our
		   close will be handled as part of normal packet processing and
		   we're done */
		status = sendChannelClose( sessionInfoPtr, currWriteChannelNo,
								   CHANNEL_WRITE, FALSE );
		if( status != OK_SPECIAL )
			{
			/* If this is the last remaining channel, we similarly can't
			   close it */
			if( status == CRYPT_ERROR_PERMISSION )
				retExt( CRYPT_ERROR_PERMISSION,
						( CRYPT_ERROR_PERMISSION, SESSION_ERRINFO, 
						  "Cannot close last remaining channel without "
						  "closing the overall session" ) );

			return( CRYPT_OK );
			}
		}

	/* It's the last open channel, flush through the remaining data */
	status = sendCloseNotification( sessionInfoPtr, NULL, 0 );
	if( cryptStatusError( status ) || \
		( sessionInfoPtr->protocolFlags & SESSION_SENDCLOSED ) )
		{
		/* There's a problem at the network level or the other side has
		   already closed the session, close the network link and exit */
		sNetDisconnect( &sessionInfoPtr->stream );
		return( CRYPT_OK );
		}

	/* If there's not enough room in the receive buffer to read at least 1K
	   of packet data, we can't try anything further */
	if( sessionInfoPtr->receiveBufSize - sessionInfoPtr->receiveBufEnd < \
		min( sessionInfoPtr->pendingPacketRemaining, 1024 ) )
		{
		sNetDisconnect( &sessionInfoPtr->stream );
		return( CRYPT_OK );
		}

	/* Read back the other side's channel close(s).  This is somewhat messy
	   since the other side could decide that it still wants to send us
	   arbitrary amounts of data (the spec is rather vague about how urgent
	   a channel close is, the general idea among implementors seems to be
	   that you should let output drain before you close your side, but
	   if you're in the middle of sending a 2GB file that's a lot of output
	   to drain).  This can also be complicated by implementation-specific
	   quirks, for example OpenSSH may hang more or less indefinitely if
	   there's output coming from a background process on the server.  This
	   is because of a rather obscure race condition that would occur if it
	   exited immediately in which the SSH server gets the SIGCHLD from the
	   (local) background process exiting before it's written all of its
	   data to the (local) pipe connecting it to the SSH server, so it
	   closes the (remote) SSH channel/connection before the last piece of
	   data comes over the (local) pipe.  Because the server won't close the
	   (remote) SSH connection until it's certain that the (local) process
	   has written all of its data, and it'll never get the EOF over the
	   pipe, it hangs forever.  This is a piece of Unix plumbing arcana that
	   doesn't really concern us, so again just exiting after a short wait
	   is the best response.

	   Since we're about to shut down the session anyway, we try
	   to read a basic channel close ack from the other side, if there's
	   anything more than that we drop it.  This is complicated somewhat by
	   the fact that what we're doing here is something that's normally
	   handled by the high-level read code in sess_rw.c.  What we implement
	   here is the absolute minimum needed to clear the stream
	   (sendCloseNotification() has set the necessary (small) nonzero
	   timeout for us) */
	while( noChannels-- > 0 )	/* Range-checked earlier */
		{
		status = sessionInfoPtr->readHeaderFunction( sessionInfoPtr, &readInfo );
		if( !cryptStatusError( status ) )
			{
			/* Adjust the packet info for the packet header data that was
			   just read */
			sessionInfoPtr->receiveBufEnd += status;
			sessionInfoPtr->pendingPacketPartialLength = status;
			sessionInfoPtr->pendingPacketRemaining -= status;
			if( sessionInfoPtr->pendingPacketRemaining <= 512 )
				{
				const int bytesLeft = sessionInfoPtr->receiveBufSize - \
									  sessionInfoPtr->receiveBufEnd;

				/* We got a packet and it's probably the channel close ack,
				   read it */
				status = sread( &sessionInfoPtr->stream,
								sessionInfoPtr->receiveBuffer + \
									sessionInfoPtr->receiveBufEnd,
								min( sessionInfoPtr->pendingPacketRemaining, \
									 bytesLeft ) );
				}
			}
		}
	sNetDisconnect( &sessionInfoPtr->stream );
	return( CRYPT_OK );
	}
#endif /* USE_SSH */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -