changeset 452:4cab61369879

Prevent invalid packets being sent during key-exchange, instead queue them until afterwards. This could sometimes terminate connections after 8 hours if (for example) a new TCP forwarded connection was sent at the KEX timeout.
author Matt Johnston <matt@ucc.asn.au>
date Fri, 27 Jul 2007 17:13:42 +0000
parents f2aa5aeea619
children 29953de278ae
files common-session.c packet.c packet.h session.h
diffstat 4 files changed, 84 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/common-session.c	Wed Jul 25 16:20:57 2007 +0000
+++ b/common-session.c	Fri Jul 27 17:13:42 2007 +0000
@@ -80,9 +80,12 @@
 	initqueue(&ses.writequeue);
 
 	ses.requirenext = SSH_MSG_KEXINIT;
-	ses.dataallowed = 0; /* don't send data yet, we'll wait until after kex */
+	ses.dataallowed = 1; /* we can send data until we actually 
+							send the SSH_MSG_KEXINIT */
 	ses.ignorenext = 0;
 	ses.lastpacket = 0;
+	ses.reply_queue_head = NULL;
+	ses.reply_queue_tail = NULL;
 
 	/* set all the algos to none */
 	ses.keys = (struct key_context*)m_malloc(sizeof(struct key_context));
@@ -192,6 +195,10 @@
 				process_packet();
 			}
 		}
+		
+		/* if required, flush out any queued reply packets that
+		were being held up during a KEX */
+		maybe_flush_reply_queue();
 
 		/* process pipes etc for the channels, ses.dataallowed == 0
 		 * during rekeying ) */
--- a/packet.c	Wed Jul 25 16:20:57 2007 +0000
+++ b/packet.c	Fri Jul 27 17:13:42 2007 +0000
@@ -403,7 +403,60 @@
 #endif
 
 
+/* returns 1 if the packet is a valid type during kex (see 7.1 of rfc4253) */
+static int packet_is_okay_kex(unsigned char type) {
+	if (type >= SSH_MSG_USERAUTH_REQUEST) {
+		return 0;
+	}
+	if (type == SSH_MSG_SERVICE_REQUEST || type == SSH_MSG_SERVICE_ACCEPT) {
+		return 0;
+	}
+	if (type == SSH_MSG_KEXINIT) {
+		/* XXX should this die horribly if !dataallowed ?? */
+		return 0;
+	}
+	return 1;
+}
 
+static void enqueue_reply_packet() {
+	struct packetlist * new_item = NULL;
+	new_item = m_malloc(sizeof(struct packetlist));
+	new_item->next = NULL;
+	
+	new_item->payload = buf_newcopy(ses.writepayload);
+	buf_setpos(ses.writepayload, 0);
+	buf_setlen(ses.writepayload, 0);
+	
+	if (ses.reply_queue_tail) {
+		ses.reply_queue_tail->next = new_item;
+	} else {
+		ses.reply_queue_head = new_item;
+		ses.reply_queue_tail = new_item;
+	}
+	TRACE(("leave enqueue_reply_packet"))
+}
+
+void maybe_flush_reply_queue() {
+	struct packetlist *tmp_item = NULL, *curr_item = NULL;
+	if (!ses.dataallowed)
+	{
+		TRACE(("maybe_empty_reply_queue - no data allowed"))
+		return;
+	}
+		
+	for (curr_item = ses.reply_queue_head; curr_item; ) {
+		CHECKCLEARTOWRITE();
+		buf_putbytes(ses.writepayload,
+			curr_item->payload->data, curr_item->payload->len);
+			
+		buf_free(curr_item->payload);
+		tmp_item = curr_item;
+		curr_item = curr_item->next;
+		m_free(tmp_item);
+		encrypt_packet();
+	}
+	ses.reply_queue_head = ses.reply_queue_tail = NULL;
+}
 	
 /* encrypt the writepayload, putting into writebuf, ready for write_packet()
  * to put on the wire */
@@ -413,9 +466,20 @@
 	unsigned char blocksize, macsize;
 	buffer * writebuf; /* the packet which will go on the wire */
 	buffer * clearwritebuf; /* unencrypted, possibly compressed */
+	unsigned char type;
 	
+	type = ses.writepayload->data[0];
 	TRACE(("enter encrypt_packet()"))
-	TRACE(("encrypt_packet type is %d", ses.writepayload->data[0]))
+	TRACE(("encrypt_packet type is %d", type))
+	
+	if (!ses.dataallowed && !packet_is_okay_kex(type)) {
+		/* During key exchange only particular packets are allowed.
+			Since this type isn't OK we just enqueue it to send 
+			after the KEX, see maybe_flush_reply_queue */
+		enqueue_reply_packet();
+		return;
+	}
+		
 	blocksize = ses.keys->trans_algo_crypt->blocksize;
 	macsize = ses.keys->trans_algo_mac->hashsize;
 
--- a/packet.h	Wed Jul 25 16:20:57 2007 +0000
+++ b/packet.h	Fri Jul 27 17:13:42 2007 +0000
@@ -35,6 +35,7 @@
 
 void process_packet();
 
+void maybe_flush_reply_queue();
 typedef struct PacketType {
 	unsigned char type; /* SSH_MSG_FOO */
 	void (*handler)();
--- a/session.h	Wed Jul 25 16:20:57 2007 +0000
+++ b/session.h	Fri Jul 27 17:13:42 2007 +0000
@@ -81,6 +81,12 @@
 
 };
 
+struct packetlist;
+struct packetlist {
+	struct packetlist *next;
+	buffer * payload;
+};
+
 struct sshsession {
 
 	/* Is it a client or server? */
@@ -137,6 +143,10 @@
 	buffer* kexhashbuf; /* session hash buffer calculated from various packets*/
 	buffer* transkexinit; /* the kexinit packet we send should be kept so we
 							 can add it to the hash when generating keys */
+							
+	/* a list of queued replies that should be sent after a KEX has
+	   concluded (ie, while dataallowed was unset)*/
+	struct packetlist *reply_queue_head, *reply_queue_tail;
 
 	algo_type*(*buf_match_algo)(buffer*buf, algo_type localalgos[],
 			int *goodguess); /* The function to use to choose which algorithm