- provos@cvs.openbsd.org 2001/03/27 17:46:50
     [compat.c compat.h dh.c dh.h ssh2.h sshconnect2.c sshd.c version.h]
     make dh group exchange more flexible, allow min and max group size,
     okay markus@, deraadt@
diff --git a/ChangeLog b/ChangeLog
index c892bd0..88c0af3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -16,6 +16,10 @@
      [compat.c compat.h ssh-rsa.c]
      some older systems use NID_md5 instead of NID_sha1 for RSASSA-PKCS1-v1_5
      signatures in SSH protocol 2, ok djm@
+   - provos@cvs.openbsd.org 2001/03/27 17:46:50
+     [compat.c compat.h dh.c dh.h ssh2.h sshconnect2.c sshd.c version.h]
+     make dh group exchange more flexible, allow min and max group size,
+     okay markus@, deraadt@
 
 20010328
  - (djm) Reorder tests and library inclusion for Krb4/AFS to try to 
@@ -4754,4 +4758,4 @@
  - Wrote replacements for strlcpy and mkdtemp
  - Released 1.0pre1
 
-$Id: ChangeLog,v 1.1030 2001/03/29 00:32:56 mouring Exp $
+$Id: ChangeLog,v 1.1031 2001/03/29 00:36:16 mouring Exp $
diff --git a/compat.c b/compat.c
index 98372e2..686016f 100644
--- a/compat.c
+++ b/compat.c
@@ -23,7 +23,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: compat.c,v 1.41 2001/03/27 10:57:00 markus Exp $");
+RCSID("$OpenBSD: compat.c,v 1.42 2001/03/27 17:46:49 provos Exp $");
 
 #ifdef HAVE_LIBPCRE
 #  include <pcreposix.h>
@@ -68,10 +68,14 @@
 		int	bugs;
 	} check[] = {
 		{ "^OpenSSH[-_]2\\.[012]",
-					SSH_OLD_SESSIONID|SSH_BUG_BANNER },
-		{ "^OpenSSH_2\\.3\\.0", SSH_BUG_BANNER|SSH_BUG_BIGENDIANAES },
+					SSH_OLD_SESSIONID|SSH_BUG_BANNER|
+					SSH_OLD_DHGEX },
+		{ "^OpenSSH_2\\.3\\.0", SSH_BUG_BANNER|SSH_BUG_BIGENDIANAES|
+					SSH_OLD_DHGEX},
 		{ "^OpenSSH_2\\.5\\.[01]p1",
-					SSH_BUG_BIGENDIANAES },
+					SSH_BUG_BIGENDIANAES|SSH_OLD_DHGEX },
+		{ "^OpenSSH_2\\.5\\.[012]",
+					SSH_OLD_DHGEX },
 		{ "^OpenSSH",		0 },
 		{ "MindTerm",		0 },
 		{ "^2\\.1\\.0",		SSH_BUG_SIGBLOB|SSH_BUG_HMAC|
diff --git a/compat.h b/compat.h
index 03f2361..e4ca5c1 100644
--- a/compat.h
+++ b/compat.h
@@ -21,7 +21,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-/* RCSID("$OpenBSD: compat.h,v 1.20 2001/03/27 10:57:00 markus Exp $"); */
+/* RCSID("$OpenBSD: compat.h,v 1.21 2001/03/27 17:46:49 provos Exp $"); */
 
 #ifndef COMPAT_H
 #define COMPAT_H
@@ -45,6 +45,7 @@
 #define SSH_BUG_SCANNER		0x0800
 #define SSH_BUG_BIGENDIANAES	0x1000
 #define SSH_BUG_RSASIGMD5	0x2000
+#define SSH_OLD_DHGEX		0x4000
 
 void    enable_compat13(void);
 void    enable_compat20(void);
diff --git a/dh.c b/dh.c
index ac73f84..5f441ee 100644
--- a/dh.c
+++ b/dh.c
@@ -23,7 +23,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: dh.c,v 1.8 2001/03/05 17:58:22 stevesk Exp $");
+RCSID("$OpenBSD: dh.c,v 1.9 2001/03/27 17:46:49 provos Exp $");
 
 #include "xmalloc.h"
 
@@ -69,6 +69,8 @@
 	if (cp == NULL || *strsize == '\0' ||
 	    (dhg->size = atoi(strsize)) == 0)
 		goto fail;
+	/* The whole group is one bit larger */
+	dhg->size++;
 	gen = strsep(&cp, " "); /* gen */
 	if (cp == NULL || *gen == '\0')
 		goto fail;
@@ -95,7 +97,7 @@
 }
 
 DH *
-choose_dh(int minbits)
+choose_dh(int min, int wantbits, int max)
 {
 	FILE *f;
 	char line[1024];
@@ -118,8 +120,11 @@
 		BN_free(dhg.g);
 		BN_free(dhg.p);
 
-		if ((dhg.size > minbits && dhg.size < best) ||
-		    (dhg.size > best && best < minbits)) {
+		if (dhg.size > max || dhg.size < min)
+			continue;
+
+		if ((dhg.size > wantbits && dhg.size < best) ||
+		    (dhg.size > best && best < wantbits)) {
 			best = dhg.size;
 			bestcount = 0;
 		}
@@ -129,8 +134,8 @@
 	fclose (f);
 
 	if (bestcount == 0) {
-		log("WARNING: no primes in %s, using old prime", _PATH_DH_PRIMES);
-		return (dh_new_group1());
+		log("WARNING: no suitable primes in %s", _PATH_DH_PRIMES);
+		return (NULL);
 	}
 
 	f = fopen(_PATH_DH_PRIMES, "r");
@@ -143,6 +148,8 @@
 	while (fgets(line, sizeof(line), f)) {
 		if (!parse_prime(linenum, line, &dhg))
 			continue;
+		if (dhg.size > max || dhg.size < min)
+			continue;
 		if (dhg.size != best)
 			continue;
 		if (linenum++ != which) {
diff --git a/dh.h b/dh.h
index f08d70e..70b326e 100644
--- a/dh.h
+++ b/dh.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: dh.h,v 1.2 2001/01/29 01:58:15 niklas Exp $	*/
+/*	$OpenBSD: dh.h,v 1.3 2001/03/27 17:46:49 provos Exp $	*/
 
 /*
  * Copyright (c) 2000 Niels Provos.  All rights reserved.
@@ -32,6 +32,9 @@
 	BIGNUM *p;
 };
 
-DH *choose_dh(int minbits);
+DH *choose_dh(int min, int nbits, int max);
+
+#define DH_GRP_MIN	1024
+#define DH_GRP_MAX	8192
 
 #endif
diff --git a/ssh2.h b/ssh2.h
index fe0146c..e45aef2 100644
--- a/ssh2.h
+++ b/ssh2.h
@@ -52,7 +52,7 @@
  *
  *     192-255  Local extensions
  */
-/* RCSID("$OpenBSD: ssh2.h,v 1.5 2000/10/11 04:02:17 provos Exp $"); */
+/* RCSID("$OpenBSD: ssh2.h,v 1.6 2001/03/27 17:46:49 provos Exp $"); */
 
 /* transport layer: generic */
 
@@ -74,10 +74,11 @@
 #define SSH2_MSG_KEXDH_REPLY				31
 
 /* dh-group-exchange */
-#define SSH2_MSG_KEX_DH_GEX_REQUEST			30
+#define SSH2_MSG_KEX_DH_GEX_REQUEST_OLD			30
 #define SSH2_MSG_KEX_DH_GEX_GROUP			31
 #define SSH2_MSG_KEX_DH_GEX_INIT			32
 #define SSH2_MSG_KEX_DH_GEX_REPLY			33
+#define SSH2_MSG_KEX_DH_GEX_REQUEST			34
 
 /* user authentication: generic */
 
diff --git a/sshconnect2.c b/sshconnect2.c
index f636fb3..da8c822 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -23,7 +23,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: sshconnect2.c,v 1.56 2001/03/26 08:07:09 markus Exp $");
+RCSID("$OpenBSD: sshconnect2.c,v 1.57 2001/03/27 17:46:49 provos Exp $");
 
 #include <openssl/bn.h>
 #include <openssl/md5.h>
@@ -46,6 +46,7 @@
 #include "sshconnect.h"
 #include "authfile.h"
 #include "cli.h"
+#include "dh.h"
 #include "dispatch.h"
 #include "authfd.h"
 #include "log.h"
@@ -309,7 +310,7 @@
 	int plen, dlen;
 	u_int klen, kout;
 	char *signature = NULL;
-	u_int slen, nbits;
+	u_int slen, nbits, min, max;
 	char *server_host_key_blob = NULL;
 	Key *server_host_key;
 	u_int sbloblen;
@@ -322,14 +323,31 @@
 
 	nbits = dh_estimate(kex->we_need * 8);
 
-	debug("Sending SSH2_MSG_KEX_DH_GEX_REQUEST.");
-	packet_start(SSH2_MSG_KEX_DH_GEX_REQUEST);
-	packet_put_int(nbits);
+	if (datafellows & SSH_OLD_DHGEX) {
+		debug("Sending SSH2_MSG_KEX_DH_GEX_REQUEST_OLD.");
+
+		/* Old GEX request */
+		packet_start(SSH2_MSG_KEX_DH_GEX_REQUEST_OLD);
+		packet_put_int(nbits);
+		min = DH_GRP_MIN;
+		max = DH_GRP_MAX;
+	} else {
+		debug("Sending SSH2_MSG_KEX_DH_GEX_REQUEST.");
+
+		/* New GEX request */
+		min = DH_GRP_MIN;
+		max = MIN(DH_GRP_MAX, nbits * 1.25);
+
+		packet_start(SSH2_MSG_KEX_DH_GEX_REQUEST);
+		packet_put_int(min);
+		packet_put_int(nbits);
+		packet_put_int(max);
+	}
 	packet_send();
 	packet_write_wait();
 
 #ifdef DEBUG_KEXDH
-	fprintf(stderr, "\nnbits = %d", nbits);
+	fprintf(stderr, "\nmin = %d, nbits = %d, max = %d", min, nbits, max);
 #endif
 
 	debug("Wait SSH2_MSG_KEX_DH_GEX_GROUP.");
@@ -344,6 +362,11 @@
 	if ((g = BN_new()) == NULL)
 		fatal("BN_new");
 	packet_get_bignum2(g, &dlen);
+
+	if (BN_num_bits(p) < min || BN_num_bits(p) > max)
+		fatal("DH_GEX group out of range: %d !< %d !< %d",
+		    min, BN_num_bits(p), max);
+
 	dh = dh_new_group(g, p);
 
 	dh_gen_key(dh, kex->we_need * 8);
diff --git a/sshd.c b/sshd.c
index 961aeea..65cb832 100644
--- a/sshd.c
+++ b/sshd.c
@@ -40,7 +40,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: sshd.c,v 1.180 2001/03/27 10:34:08 markus Exp $");
+RCSID("$OpenBSD: sshd.c,v 1.181 2001/03/27 17:46:49 provos Exp $");
 
 #include <openssl/dh.h>
 #include <openssl/bn.h>
@@ -1614,7 +1614,7 @@
 	int i;
 #endif
 	int payload_len, dlen;
-	int slen, nbits;
+	int slen, nbits, type, min, max;
 	u_char *signature = NULL;
 	u_char *server_host_key_blob = NULL;
 	u_int sbloblen;
@@ -1632,9 +1632,33 @@
 
 /* KEXDHGEX */
 	debug("Wait SSH2_MSG_KEX_DH_GEX_REQUEST.");
-	packet_read_expect(&payload_len, SSH2_MSG_KEX_DH_GEX_REQUEST);
-	nbits = packet_get_int();
-	dh = choose_dh(nbits);
+	type = packet_read(&payload_len);
+	if (type != SSH2_MSG_KEX_DH_GEX_REQUEST_OLD &&
+	    type != SSH2_MSG_KEX_DH_GEX_REQUEST)
+		packet_disconnect("Protocol error: expected type %d or %d, got %d",
+		    SSH2_MSG_KEX_DH_GEX_REQUEST_OLD,
+		    SSH2_MSG_KEX_DH_GEX_REQUEST,
+		    type);
+	if (type == SSH2_MSG_KEX_DH_GEX_REQUEST_OLD) {
+		nbits = packet_get_int();
+		min = DH_GRP_MIN;
+		max = DH_GRP_MAX;
+	} else {
+		min = packet_get_int();
+		nbits = packet_get_int();
+		max = packet_get_int();
+
+		min = MAX(DH_GRP_MIN, min);
+		max = MIN(DH_GRP_MAX, max);
+	}
+
+	if (max < min || nbits < min || max < nbits)
+		fatal("DH_GEX_REQUEST, bad parameters: %d !< %d !< %d",
+		    min, nbits, max);
+
+	dh = choose_dh(min, nbits, max);
+	if (dh == NULL)
+		packet_disconnect("Protocol error: no matching DH grp found");
 
 	debug("Sending SSH2_MSG_KEX_DH_GEX_GROUP.");
 	packet_start(SSH2_MSG_KEX_DH_GEX_GROUP);
diff --git a/version.h b/version.h
index 8ece8d0..bdf8c1d 100644
--- a/version.h
+++ b/version.h
@@ -1,3 +1,3 @@
-/* $OpenBSD: version.h,v 1.20 2001/03/19 17:12:10 markus Exp $ */
-  
-#define SSH_VERSION	"OpenSSH_2.5.2p1"
+/* $OpenBSD: version.h,v 1.21 2001/03/27 17:46:50 provos Exp $ */
+
+#define SSH_VERSION	"OpenSSH_2.5.3p1"