[tls] Accept only explicitly supported FFDHE groups

We do not currently perform any validation on the DHE field prime or
generator.  RFC 7919 defines a family of known-safe finite fields, and
TLS version 1.3 completely removes the ability to provide an explicit
field prime and generator.

Verify that the field prime and generator correspond to one of the
explicitly configured groups.

This may break connections to the (now very rare) TLS servers that use
custom FFDHE groups and that choose to use DHE rather than ECDHE (or
that do not support ECDHE).  We already advertise ECDHE cipher suites
as preferred over DHE cipher suites, and advertise all ECDHE groups as
preferred over all FFDHE groups.  It is therefore very unlikely that
this change will cause any issues in practice.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
diff --git a/src/net/tls.c b/src/net/tls.c
index 41d5028..bd4b14b 100644
--- a/src/net/tls.c
+++ b/src/net/tls.c
@@ -51,6 +51,7 @@
 #include <ipxe/validator.h>
 #include <ipxe/job.h>
 #include <ipxe/dhe.h>
+#include <ipxe/ffdhe.h>
 #include <ipxe/tls.h>
 #include <config/crypto.h>
 
@@ -1012,6 +1013,32 @@
 	return NULL;
 }
 
+/**
+ * Identify named key exchange group by Diffie-Hellman parameters
+ *
+ * @v dh_p		Prime modulus
+ * @v dh_p_len		Length of prime modulus
+ * @v dh_g		Generator
+ * @v dh_g_len		Length of generator
+ * @ret group		Named group, or NULL
+ */
+static struct tls_named_group *
+tls_find_param_group ( const void *dh_p, size_t dh_p_len, const void *dh_g,
+		       size_t dh_g_len ) {
+	struct tls_named_group *group;
+
+	/* Identify named group by parameters */
+	for_each_table_entry ( group, TLS_NAMED_GROUPS ) {
+		if ( is_ffdhe ( group->exchange ) &&
+		     ffdhe_has_params ( group->exchange, dh_p, dh_p_len,
+					dh_g, dh_g_len ) ) {
+			return group;
+		}
+	}
+
+	return NULL;
+}
+
 /******************************************************************************
  *
  * Record handling
@@ -1507,11 +1534,16 @@
  * @ret rc		Return status code
  */
 static int tls_send_client_key_exchange_dhe ( struct tls_connection *tls ) {
+	struct tls_named_group *group;
+	struct exchange_algorithm *exchange;
 	uint8_t private[ sizeof ( tls->client.random.random ) ];
 	const struct {
 		uint16_t len;
 		uint8_t data[0];
 	} __attribute__ (( packed )) *dh_val[3];
+	typeof ( dh_val[0] ) dh_p;
+	typeof ( dh_val[1] ) dh_g;
+	typeof ( dh_val[2] ) dh_ys;
 	const void *data;
 	size_t remaining;
 	size_t frag_len;
@@ -1544,6 +1576,23 @@
 	if ( ( rc = tls_verify_dh_params ( tls, param_len ) ) != 0 )
 		goto err_verify;
 
+	/* Identify named group */
+	dh_p = dh_val[0];
+	dh_g = dh_val[1];
+	dh_ys = dh_val[2];
+	group = tls_find_param_group ( dh_p->data, ntohs ( dh_p->len ),
+				       dh_g->data, ntohs ( dh_g->len ) );
+	if ( ! group ) {
+		DBGC ( tls, "TLS %p unsupported %d-bit group:\n",
+		       tls, ( 8 * ntohs ( dh_p->len ) ) );
+		DBGC_HDA ( tls, 0, tls->server.exchange,
+			   tls->server.exchange_len );
+		rc = -ENOTSUP_GROUP;
+		goto err_group;
+	}
+	exchange = group->exchange;
+	DBGC ( tls, "TLS %p using named group %s\n", tls, exchange->name );
+
 	/* Generate Diffie-Hellman private key */
 	if ( ( rc = tls_generate_random ( tls, private,
 					  sizeof ( private ) ) ) != 0 ) {
@@ -1552,9 +1601,6 @@
 
 	/* Construct pre-master secret and ClientKeyExchange record */
 	{
-		typeof ( dh_val[0] ) dh_p = dh_val[0];
-		typeof ( dh_val[1] ) dh_g = dh_val[1];
-		typeof ( dh_val[2] ) dh_ys = dh_val[2];
 		size_t len = ntohs ( dh_p->len );
 		struct {
 			uint32_t type_length;
@@ -1614,6 +1660,7 @@
 	}
  err_alloc:
  err_random:
+ err_group:
  err_verify:
  err_header:
 	return rc;