Browse Source

GnuTLS: Verify that server certificate EKU is valid for a server

The server certificate will be rejected if it includes any EKU and none
of the listed EKUs is either TLS Web Server Authentication or ANY.

Signed-off-by: Jouni Malinen <j@w1.fi>
Jouni Malinen 10 years ago
parent
commit
0e1bb94b91
1 changed files with 75 additions and 1 deletions
  1. 75 1
      src/crypto/tls_gnutls.c

+ 75 - 1
src/crypto/tls_gnutls.c

@@ -741,6 +741,42 @@ static void gnutls_tls_fail_event(struct tls_connection *conn,
 }
 
 
+#if GNUTLS_VERSION_NUMBER < 0x030300
+static int server_eku_purpose(gnutls_x509_crt_t cert)
+{
+	unsigned int i;
+
+	for (i = 0; ; i++) {
+		char oid[128];
+		size_t oid_size = sizeof(oid);
+		int res;
+
+		res = gnutls_x509_crt_get_key_purpose_oid(cert, i, oid,
+							  &oid_size, NULL);
+		if (res == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+			if (i == 0) {
+				/* No EKU - assume any use allowed */
+				return 1;
+			}
+			break;
+		}
+
+		if (res < 0) {
+			wpa_printf(MSG_INFO, "GnuTLS: Failed to get EKU");
+			return 0;
+		}
+
+		wpa_printf(MSG_DEBUG, "GnuTLS: Certificate purpose: %s", oid);
+		if (os_strcmp(oid, GNUTLS_KP_TLS_WWW_SERVER) == 0 ||
+		    os_strcmp(oid, GNUTLS_KP_ANY) == 0)
+			return 1;
+	}
+
+	return 0;
+}
+#endif /* < 3.3.0 */
+
+
 static int tls_connection_verify_peer(gnutls_session_t session)
 {
 	struct tls_connection *conn;
@@ -749,6 +785,7 @@ static int tls_connection_verify_peer(gnutls_session_t session)
 	const gnutls_datum_t *certs;
 	gnutls_x509_crt_t cert;
 	gnutls_alert_description_t err;
+	int res;
 
 	conn = gnutls_session_get_ptr(session);
 	if (!conn->verify_peer) {
@@ -759,7 +796,24 @@ static int tls_connection_verify_peer(gnutls_session_t session)
 
 	wpa_printf(MSG_DEBUG, "GnuTSL: Verifying peer certificate");
 
-	if (gnutls_certificate_verify_peers2(session, &status) < 0) {
+#if GNUTLS_VERSION_NUMBER >= 0x030300
+	{
+		gnutls_typed_vdata_st data[1];
+		unsigned int elements = 0;
+
+		os_memset(data, 0, sizeof(data));
+		if (!conn->global->server) {
+			data[elements].type = GNUTLS_DT_KEY_PURPOSE_OID;
+			data[elements].data = (void *) GNUTLS_KP_TLS_WWW_SERVER;
+			elements++;
+		}
+		res = gnutls_certificate_verify_peers(session, data, 1,
+						      &status);
+	}
+#else /* < 3.3.0 */
+	res = gnutls_certificate_verify_peers2(session, &status);
+#endif
+	if (res < 0) {
 		wpa_printf(MSG_INFO, "TLS: Failed to verify peer "
 			   "certificate chain");
 		err = GNUTLS_A_INTERNAL_ERROR;
@@ -904,6 +958,26 @@ static int tls_connection_verify_peer(gnutls_session_t session)
 			/* TODO: validate altsubject_match.
 			 * For now, any such configuration is rejected in
 			 * tls_connection_set_params() */
+
+#if GNUTLS_VERSION_NUMBER < 0x030300
+			/*
+			 * gnutls_certificate_verify_peers() not available, so
+			 * need to check EKU separately.
+			 */
+			if (!conn->global->server &&
+			    !server_eku_purpose(cert)) {
+				wpa_printf(MSG_WARNING,
+					   "GnuTLS: No server EKU");
+				gnutls_tls_fail_event(
+					conn, &certs[i], i, buf,
+					"No server EKU",
+					TLS_FAIL_BAD_CERTIFICATE);
+				err = GNUTLS_A_BAD_CERTIFICATE;
+				gnutls_x509_crt_deinit(cert);
+				os_free(buf);
+				goto out;
+			}
+#endif /* < 3.3.0 */
 		}
 
 		if (!conn->disable_time_checks &&