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

📄 ssh2_msg.c

📁 cryptlib是功能强大的安全工具集。允许开发人员快速在自己的软件中集成加密和认证服务。
💻 C
📖 第 1 页 / 共 4 页
字号:
			   long waiting for the rehandshake to complete before sending 
			   new data because the lack of WINDOW_ADJUSTs (in an 
			   implementation that sends these with almost every packet, as 
			   most do) will screw up flow control and lead to deadlock.
			   This problem got so bad that as of 2.4.0 the ssh.com 
			   implementation would detect OpenSSH (the other main
			   implementation at the time) and disable the rehandshake when
			   it was talking to it, but it may not do this for other
			   implementations.

			   To avoid falling into this hole, or at least to fail 
			   obviously when the two sides can't agree on how to handle the 
			   layering mismatch problem, we report a rehandshake request as 
			   an error.  Trying to handle it properly results in hard-to-
			   diagnose errors (it depends on what the layers are doing at 
			   the time of the problem), typically some bad-packet error 
			   when the other side tries to interpret a connection-layer 
			   packet as part of the rehandshake, or when the two sides 
			   disagree on when to switch keys and it decrypts with the 
			   wrong keys and gets a garbled packet type */
			retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
					"Unexpected KEXINIT request received" );

		case SSH2_MSG_CHANNEL_DATA:
		case SSH2_MSG_CHANNEL_EXTENDED_DATA:
		case SSH2_MSG_CHANNEL_REQUEST:
		case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
		case SSH2_MSG_CHANNEL_EOF:
		case SSH2_MSG_CHANNEL_CLOSE:
			/* All channel-specific messages end up here */
			channelNo = readUint32( stream );
			if( cryptStatusError( channelNo ) )
				/* We can't send an error response to a channel request at
				   this point both because we haven't got to the response-
				   required flag yet and because SSH doesn't provide a 
				   mechanism for returning an error response without an
				   accompanying channel number.  The best that we can do is
				   to quietly ignore the packet */
				retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
						"Invalid channel-specific packet type %d",
						sshInfo->packetType );
			if( channelNo != getCurrentChannelNo( sessionInfoPtr, \
												  CHANNEL_READ ) )
				{
				/* It's a request on something other than the current 
				   channel, try and select the new channel */
				status = selectChannel( sessionInfoPtr, channelNo, 
										CHANNEL_READ );
				if( cryptStatusError( status ) )
					{
					/* As before for error handling */
					retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
							"Invalid channel number %ld in channel-specific "
							"packet type %d, current channel "
							"is %ld", channelNo, 
							sshInfo->packetType, prevChannelNo );
					}
				}
			break;

		default:
			{
			BYTE buffer[ 16 ];

			/* We got something unexpected, throw an exception in the debug 
			   version and let the caller know the details */
			assert( NOTREACHED );
			status = sread( stream, buffer, 8 );
			if( cryptStatusError( status ) )
				/* There's not enough data present to dump the start of the
				   packet, provide a more generic response */
				retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
						"Unexpected control packet type %d received",
						sshInfo->packetType );
			retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
					"Unexpected control packet type %d received, beginning "
					"%02X %02X %02X %02X %02X %02X %02X %02X",
					sshInfo->packetType, 
					buffer[ 0 ], buffer[ 1 ], buffer[ 2 ], buffer[ 3 ],
					buffer[ 4 ], buffer[ 5 ], buffer[ 6 ], buffer[ 7 ] );
			}
		}

	/* From here on we're processing a channel-specific message that applies 
	   to the currently selected channel */
	switch( sshInfo->packetType )
		{
		case SSH2_MSG_CHANNEL_DATA:
		case SSH2_MSG_CHANNEL_EXTENDED_DATA:
			{
			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( sessionInfoPtr, CRYPT_ERROR_BADDATA,
						"Invalid data packet payload length %d, 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 */
			getChannelExtAttribute( sessionInfoPtr, 
									SSH_ATTRIBUTE_WINDOWCOUNT,
									NULL, &windowCount );
			windowCount += length;
			if( 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

				   We ignore any possible error code from the packet send 
				   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 */
				enqueueChannelData( sessionInfoPtr, 
									SSH2_MSG_CHANNEL_WINDOW_ADJUST, 
									channelNo, hasWindowBug ? \
										length : MAX_WINDOW_SIZE );

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

			/* 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( sessionInfoPtr, CRYPT_ERROR_COMPLETE,
					"Remote system closed last remaining SSH channel" );
		}

	assert( NOTREACHED );
	return( CRYPT_ERROR );	/* Get rid of compiler warning */
	}

/* Close a channel */

int closeChannel( SESSION_INFO *sessionInfoPtr,
				  const BOOLEAN closeLastChannel )
	{
	READSTATE_INFO readInfo;
	const int currWriteChannelNo = \
				getCurrentChannelNo( sessionInfoPtr, CHANNEL_WRITE );
	int 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( !closeLastChannel && currWriteChannelNo == UNUSED_CHANNEL_NO )
		retExt( sessionInfoPtr, CRYPT_ERROR_NOTINITED,
				"No current channel information available to close "
				"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 )
		{
		status = enqueueResponse( sessionInfoPtr, SSH2_MSG_DISCONNECT, 3, 
								  SSH2_DISCONNECT_CONNECTION_LOST, 0, 0, 
								  CRYPT_UNUSED );
		if( cryptStatusOK( status ) )
			sendEnqueuedResponse( sessionInfoPtr, CRYPT_UNUSED );
		sessionInfoPtr->flags |= SESSION_SENDCLOSED;
		sNetDisconnect( &sessionInfoPtr->stream );
		return( CRYPT_OK );
		}

	/* Close the write side of the channel, 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, closeLastChannel );
	if( status != OK_SPECIAL )
		{
		/* If this is the last remaining channel, we similarly can't close
		   it */
		if( status == CRYPT_ERROR_PERMISSION )
			retExt( sessionInfoPtr, CRYPT_ERROR_PERMISSION,
					"Cannot close last remaining channel without closing "
					"the overall session" );

		return( CRYPT_OK );
		}

	/* It's the last open channel, close down the session */
	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.  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) */
	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_SSH2 */

⌨️ 快捷键说明

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