From 049fcf1ac572e1a13dc1281c26bbf3512c533fb7 Mon Sep 17 00:00:00 2001
From: Matt Johnston <matt@ucc.asn.au>
Date: Mon, 29 Sep 2008 02:23:04 +0000
Subject: [PATCH] Add support for zlib@openssh.com delayed compression. Are
 still advertising 'zlib' for the server, need to allow delayed-only as an
 option

--HG--
extra : convert_revision : 319df675cc3c9b35a10b7d8357c94f33fdab1a46
---
 cli-auth.c    |  2 ++
 common-algo.c |  1 +
 common-kex.c  | 23 +++++++++++++++++++----
 dbclient.1    |  1 +
 kex.h         |  5 +++++
 packet.c      | 13 +++++++------
 session.h     |  2 ++
 svr-auth.c    |  2 ++
 sysoptions.h  |  1 +
 9 files changed, 40 insertions(+), 10 deletions(-)

diff --git a/cli-auth.c b/cli-auth.c
index 10245387..731d7696 100644
--- a/cli-auth.c
+++ b/cli-auth.c
@@ -229,6 +229,8 @@ void recv_msg_userauth_failure() {
 
 void recv_msg_userauth_success() {
 	TRACE(("received msg_userauth_success"))
+	/* Note: in delayed-zlib mode, setting authdone here 
+	 * will enable compression in the transport layer */
 	ses.authstate.authdone = 1;
 	cli_ses.state = USERAUTH_SUCCESS_RCVD;
 	cli_ses.lastauthtype = AUTH_TYPE_NONE;
diff --git a/common-algo.c b/common-algo.c
index 21ac96a4..d38c5e55 100644
--- a/common-algo.c
+++ b/common-algo.c
@@ -124,6 +124,7 @@ algo_type sshhashes[] = {
 algo_type sshcompress[] = {
 #ifndef DISABLE_ZLIB
 	{"zlib", DROPBEAR_COMP_ZLIB, NULL, 1},
+	{"zlib@openssh.com", DROPBEAR_COMP_ZLIB_DELAY, NULL, 1},
 #endif
 	{"none", DROPBEAR_COMP_NONE, NULL, 1},
 	{NULL, 0, NULL, 0}
diff --git a/common-kex.c b/common-kex.c
index e9c655df..80eb2a16 100644
--- a/common-kex.c
+++ b/common-kex.c
@@ -331,12 +331,26 @@ void gen_new_keys() {
 }
 
 #ifndef DISABLE_ZLIB
+
+int is_compress_trans() {
+	return ses.keys->trans_algo_comp == DROPBEAR_COMP_ZLIB
+		|| (ses.authstate.authdone
+			&& ses.keys->trans_algo_comp == DROPBEAR_COMP_ZLIB_DELAY);
+}
+
+int is_compress_recv() {
+	return ses.keys->recv_algo_comp == DROPBEAR_COMP_ZLIB
+		|| (ses.authstate.authdone
+			&& ses.keys->recv_algo_comp == DROPBEAR_COMP_ZLIB_DELAY);
+}
+
 /* Set up new zlib compression streams, close the old ones. Only
  * called from gen_new_keys() */
 static void gen_new_zstreams() {
 
 	/* create new zstreams */
-	if (ses.newkeys->recv_algo_comp == DROPBEAR_COMP_ZLIB) {
+	if (ses.newkeys->recv_algo_comp == DROPBEAR_COMP_ZLIB
+			|| ses.newkeys->recv_algo_comp == DROPBEAR_COMP_ZLIB_DELAY) {
 		ses.newkeys->recv_zstream = (z_streamp)m_malloc(sizeof(z_stream));
 		ses.newkeys->recv_zstream->zalloc = Z_NULL;
 		ses.newkeys->recv_zstream->zfree = Z_NULL;
@@ -348,7 +362,8 @@ static void gen_new_zstreams() {
 		ses.newkeys->recv_zstream = NULL;
 	}
 
-	if (ses.newkeys->trans_algo_comp == DROPBEAR_COMP_ZLIB) {
+	if (ses.newkeys->trans_algo_comp == DROPBEAR_COMP_ZLIB
+			|| ses.newkeys->trans_algo_comp == DROPBEAR_COMP_ZLIB_DELAY) {
 		ses.newkeys->trans_zstream = (z_streamp)m_malloc(sizeof(z_stream));
 		ses.newkeys->trans_zstream->zalloc = Z_NULL;
 		ses.newkeys->trans_zstream->zfree = Z_NULL;
@@ -360,7 +375,7 @@ static void gen_new_zstreams() {
 	} else {
 		ses.newkeys->trans_zstream = NULL;
 	}
-	
+
 	/* clean up old keys */
 	if (ses.keys->recv_zstream != NULL) {
 		if (inflateEnd(ses.keys->recv_zstream) == Z_STREAM_ERROR) {
@@ -377,7 +392,7 @@ static void gen_new_zstreams() {
 		m_free(ses.keys->trans_zstream);
 	}
 }
-#endif
+#endif /* DISABLE_ZLIB */
 
 
 /* Executed upon receiving a kexinit message from the client to initiate
diff --git a/dbclient.1 b/dbclient.1
index 033672d0..934e34ac 100644
--- a/dbclient.1
+++ b/dbclient.1
@@ -10,6 +10,7 @@ dbclient \- lightweight SSH2 client
 .I l\fR:\fIh\fR:\fIr\fR] [\-l
 .IR user ]
 .I host
+.RI [ command ]
 .SH DESCRIPTION
 .B dbclient
 is a SSH 2 client designed to be small enough to be used in small memory
diff --git a/kex.h b/kex.h
index d3dd1874..a3bdc7a7 100644
--- a/kex.h
+++ b/kex.h
@@ -37,6 +37,11 @@ void gen_kexdh_vals(mp_int *dh_pub, mp_int *dh_priv);
 void kexdh_comb_key(mp_int *dh_pub_us, mp_int *dh_priv, mp_int *dh_pub_them,
 		sign_key *hostkey);
 
+#ifndef DISABLE_ZLIB
+int is_compress_trans();
+int is_compress_recv();
+#endif
+
 void recv_msg_kexdh_init(); /* server */
 
 void send_msg_kexdh_init(); /* client */
diff --git a/packet.c b/packet.c
index 30f47586..5ba99c30 100644
--- a/packet.c
+++ b/packet.c
@@ -290,10 +290,9 @@ void decrypt_packet() {
 	buf_setpos(ses.decryptreadbuf, PACKET_PAYLOAD_OFF);
 
 #ifndef DISABLE_ZLIB
-	if (ses.keys->recv_algo_comp == DROPBEAR_COMP_ZLIB) {
+	if (is_compress_recv()) {
 		/* decompress */
 		ses.payload = buf_decompress(ses.decryptreadbuf, len);
-
 	} else 
 #endif
 	{
@@ -469,6 +468,7 @@ void encrypt_packet() {
 	buffer * writebuf; /* the packet which will go on the wire */
 	buffer * clearwritebuf; /* unencrypted, possibly compressed */
 	unsigned char type;
+	unsigned int clear_len;
 	
 	type = ses.writepayload->data[0];
 	TRACE(("enter encrypt_packet()"))
@@ -488,11 +488,12 @@ void encrypt_packet() {
 	/* Encrypted packet len is payload+5, then worst case is if we are 3 away
 	 * from a blocksize multiple. In which case we need to pad to the
 	 * multiple, then add another blocksize (or MIN_PACKET_LEN) */
-	clearwritebuf = buf_new((ses.writepayload->len+4+1) + MIN_PACKET_LEN + 3
+	clear_len = (ses.writepayload->len+4+1) + MIN_PACKET_LEN + 3;
+
 #ifndef DISABLE_ZLIB
-			+ ZLIB_COMPRESS_INCR /* bit of a kludge, but we can't know len*/
+	clear_len += ZLIB_COMPRESS_INCR; /* bit of a kludge, but we can't know len*/
 #endif
-			);
+	clearwritebuf = buf_new(clear_len);
 	buf_setlen(clearwritebuf, PACKET_PAYLOAD_OFF);
 	buf_setpos(clearwritebuf, PACKET_PAYLOAD_OFF);
 
@@ -500,7 +501,7 @@ void encrypt_packet() {
 
 #ifndef DISABLE_ZLIB
 	/* compression */
-	if (ses.keys->trans_algo_comp == DROPBEAR_COMP_ZLIB) {
+	if (is_compress_trans()) {
 		buf_compress(clearwritebuf, ses.writepayload, ses.writepayload->len);
 	} else
 #endif
diff --git a/session.h b/session.h
index b63a258c..96f43634 100644
--- a/session.h
+++ b/session.h
@@ -71,6 +71,8 @@ struct key_context {
 
 	char recv_algo_comp; /* compression */
 	char trans_algo_comp;
+	int allow_compress; /* whether compression has started (useful in 
+							zlib@openssh.com delayed compression case) */
 #ifndef DISABLE_ZLIB
 	z_streamp recv_zstream;
 	z_streamp trans_zstream;
diff --git a/svr-auth.c b/svr-auth.c
index 4adf8098..5da0aa70 100644
--- a/svr-auth.c
+++ b/svr-auth.c
@@ -368,6 +368,8 @@ void send_msg_userauth_success() {
 	buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_SUCCESS);
 	encrypt_packet();
 
+	/* authdone must be set after encrypt_packet() for 
+	 * delayed-zlib mode */
 	ses.authstate.authdone = 1;
 	ses.connect_time = 0;
 
diff --git a/sysoptions.h b/sysoptions.h
index 6b171515..4899e420 100644
--- a/sysoptions.h
+++ b/sysoptions.h
@@ -68,6 +68,7 @@
 
 #define DROPBEAR_COMP_NONE 0
 #define DROPBEAR_COMP_ZLIB 1
+#define DROPBEAR_COMP_ZLIB_DELAY 2
 
 /* Required for pubkey auth */
 #if defined(ENABLE_SVR_PUBKEY_AUTH) || defined(DROPBEAR_CLIENT)
-- 
GitLab