Browse Source

TLS client: Multi-OCSP check to cover intermediate CAs

This extends multi-OCSP support to verify status for intermediate CAs in
the server certificate chain.

Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
Jouni Malinen 9 years ago
parent
commit
0764dd6849
5 changed files with 81 additions and 22 deletions
  1. 0 6
      src/crypto/tls_internal.c
  2. 42 12
      src/tls/tlsv1_client_ocsp.c
  3. 32 4
      src/tls/tlsv1_client_read.c
  4. 2 0
      src/tls/x509v3.c
  5. 5 0
      src/tls/x509v3.h

+ 0 - 6
src/crypto/tls_internal.c

@@ -200,12 +200,6 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
 	if (conn->client == NULL)
 	if (conn->client == NULL)
 		return -1;
 		return -1;
 
 
-	if (params->flags & TLS_CONN_REQUIRE_OCSP_ALL) {
-		wpa_printf(MSG_INFO,
-			   "TLS: ocsp=3 not supported");
-		return -1;
-	}
-
 	if (params->flags & TLS_CONN_EXT_CERT_CHECK) {
 	if (params->flags & TLS_CONN_EXT_CERT_CHECK) {
 		wpa_printf(MSG_INFO,
 		wpa_printf(MSG_INFO,
 			   "TLS: tls_ext_cert_check=1 not supported");
 			   "TLS: tls_ext_cert_check=1 not supported");

+ 42 - 12
src/tls/tlsv1_client_ocsp.c

@@ -316,6 +316,7 @@ static int tls_process_ocsp_single_response(struct tlsv1_client *conn,
 
 
 static enum tls_ocsp_result
 static enum tls_ocsp_result
 tls_process_ocsp_responses(struct tlsv1_client *conn,
 tls_process_ocsp_responses(struct tlsv1_client *conn,
+			   struct x509_certificate *cert,
 			   struct x509_certificate *issuer, const u8 *resp,
 			   struct x509_certificate *issuer, const u8 *resp,
 			   size_t len)
 			   size_t len)
 {
 {
@@ -335,8 +336,7 @@ tls_process_ocsp_responses(struct tlsv1_client *conn,
 				   hdr.class, hdr.tag);
 				   hdr.class, hdr.tag);
 			return TLS_OCSP_INVALID;
 			return TLS_OCSP_INVALID;
 		}
 		}
-		if (tls_process_ocsp_single_response(conn, conn->server_cert,
-						     issuer,
+		if (tls_process_ocsp_single_response(conn, cert, issuer,
 						     hdr.payload, hdr.length,
 						     hdr.payload, hdr.length,
 						     &res) == 0)
 						     &res) == 0)
 			return res;
 			return res;
@@ -350,8 +350,9 @@ tls_process_ocsp_responses(struct tlsv1_client *conn,
 
 
 
 
 static enum tls_ocsp_result
 static enum tls_ocsp_result
-tls_process_basic_ocsp_response(struct tlsv1_client *conn, const u8 *resp,
-				size_t len)
+tls_process_basic_ocsp_response(struct tlsv1_client *conn,
+				struct x509_certificate *srv_cert,
+				const u8 *resp, size_t len)
 {
 {
 	struct asn1_hdr hdr;
 	struct asn1_hdr hdr;
 	const u8 *pos, *end;
 	const u8 *pos, *end;
@@ -365,6 +366,7 @@ tls_process_basic_ocsp_response(struct tlsv1_client *conn, const u8 *resp,
 	struct x509_name name; /* used if key_hash == NULL */
 	struct x509_name name; /* used if key_hash == NULL */
 	char buf[100];
 	char buf[100];
 	os_time_t produced_at;
 	os_time_t produced_at;
+	enum tls_ocsp_result res;
 
 
 	wpa_hexdump(MSG_MSGDUMP, "OCSP: BasicOCSPResponse", resp, len);
 	wpa_hexdump(MSG_MSGDUMP, "OCSP: BasicOCSPResponse", resp, len);
 
 
@@ -594,20 +596,20 @@ tls_process_basic_ocsp_response(struct tlsv1_client *conn, const u8 *resp,
 		/* Ignore for now. */
 		/* Ignore for now. */
 	}
 	}
 
 
-	if (!conn->server_cert) {
+	if (!srv_cert) {
 		wpa_printf(MSG_DEBUG,
 		wpa_printf(MSG_DEBUG,
 			   "OCSP: Server certificate not known - cannot check OCSP response");
 			   "OCSP: Server certificate not known - cannot check OCSP response");
 		goto no_resp;
 		goto no_resp;
 	}
 	}
 
 
-	if (conn->server_cert->next) {
+	if (srv_cert->next) {
 		/* Issuer has already been verified in the chain */
 		/* Issuer has already been verified in the chain */
-		issuer = conn->server_cert->next;
+		issuer = srv_cert->next;
 	} else {
 	} else {
 		/* Find issuer from the set of trusted certificates */
 		/* Find issuer from the set of trusted certificates */
 		for (issuer = conn->cred ? conn->cred->trusted_certs : NULL;
 		for (issuer = conn->cred ? conn->cred->trusted_certs : NULL;
 		     issuer; issuer = issuer->next) {
 		     issuer; issuer = issuer->next) {
-			if (x509_name_compare(&conn->server_cert->issuer,
+			if (x509_name_compare(&srv_cert->issuer,
 					      &issuer->subject) == 0)
 					      &issuer->subject) == 0)
 				break;
 				break;
 		}
 		}
@@ -625,7 +627,7 @@ tls_process_basic_ocsp_response(struct tlsv1_client *conn, const u8 *resp,
 	} else {
 	} else {
 		for (signer = certs; signer; signer = signer->next) {
 		for (signer = certs; signer; signer = signer->next) {
 			if (!ocsp_responder_id_match(signer, &name, key_hash) ||
 			if (!ocsp_responder_id_match(signer, &name, key_hash) ||
-			    x509_name_compare(&conn->server_cert->issuer,
+			    x509_name_compare(&srv_cert->issuer,
 					      &issuer->subject) != 0 ||
 					      &issuer->subject) != 0 ||
 			    !(signer->ext_key_usage &
 			    !(signer->ext_key_usage &
 			      X509_EXT_KEY_USAGE_OCSP) ||
 			      X509_EXT_KEY_USAGE_OCSP) ||
@@ -654,8 +656,13 @@ tls_process_basic_ocsp_response(struct tlsv1_client *conn, const u8 *resp,
 		    return TLS_OCSP_INVALID;
 		    return TLS_OCSP_INVALID;
 	}
 	}
 
 
-	return tls_process_ocsp_responses(conn, issuer, responses,
-					  responses_len);
+	res = tls_process_ocsp_responses(conn, srv_cert, issuer,
+					 responses, responses_len);
+	if (res == TLS_OCSP_REVOKED)
+		srv_cert->ocsp_revoked = 1;
+	else if (res == TLS_OCSP_GOOD)
+		srv_cert->ocsp_good = 1;
+	return res;
 
 
 no_resp:
 no_resp:
 	x509_free_name(&name);
 	x509_free_name(&name);
@@ -677,6 +684,9 @@ enum tls_ocsp_result tls_process_ocsp_response(struct tlsv1_client *conn,
 	u8 resp_status;
 	u8 resp_status;
 	struct asn1_oid oid;
 	struct asn1_oid oid;
 	char obuf[80];
 	char obuf[80];
+	struct x509_certificate *cert;
+	enum tls_ocsp_result res = TLS_OCSP_NO_RESPONSE;
+	enum tls_ocsp_result res_first = res;
 
 
 	wpa_hexdump(MSG_MSGDUMP, "TLSv1: OCSPResponse", resp, len);
 	wpa_hexdump(MSG_MSGDUMP, "TLSv1: OCSPResponse", resp, len);
 
 
@@ -769,5 +779,25 @@ enum tls_ocsp_result tls_process_ocsp_response(struct tlsv1_client *conn,
 		return TLS_OCSP_INVALID;
 		return TLS_OCSP_INVALID;
 	}
 	}
 
 
-	return tls_process_basic_ocsp_response(conn, hdr.payload, hdr.length);
+	cert = conn->server_cert;
+	while (cert) {
+		if (!cert->ocsp_good && !cert->ocsp_revoked) {
+			char sbuf[128];
+
+			x509_name_string(&cert->subject, sbuf, sizeof(sbuf));
+			wpa_printf(MSG_DEBUG,
+				   "OCSP: Trying to find certificate status for %s",
+				   sbuf);
+
+			res = tls_process_basic_ocsp_response(conn, cert,
+							      hdr.payload,
+							      hdr.length);
+			if (cert == conn->server_cert)
+				res_first = res;
+		}
+		if (res == TLS_OCSP_REVOKED || cert->issuer_trusted)
+			break;
+		cert = cert->next;
+	}
+	return res == TLS_OCSP_REVOKED ? res : res_first;
 }
 }

+ 32 - 4
src/tls/tlsv1_client_read.c

@@ -822,6 +822,8 @@ static int tls_process_certificate_status(struct tlsv1_client *conn, u8 ct,
 	size_t left, len;
 	size_t left, len;
 	u8 type, status_type;
 	u8 type, status_type;
 	enum tls_ocsp_result res;
 	enum tls_ocsp_result res;
+	struct x509_certificate *cert;
+	int depth;
 
 
 	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
 	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
 		wpa_printf(MSG_DEBUG,
 		wpa_printf(MSG_DEBUG,
@@ -955,13 +957,39 @@ done:
 	if (res == TLS_OCSP_REVOKED) {
 	if (res == TLS_OCSP_REVOKED) {
 		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
 		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
 			  TLS_ALERT_CERTIFICATE_REVOKED);
 			  TLS_ALERT_CERTIFICATE_REVOKED);
-		if (conn->server_cert)
-			tls_cert_chain_failure_event(
-				conn, 0, conn->server_cert, TLS_FAIL_REVOKED,
-				"certificate revoked");
+		for (cert = conn->server_cert, depth = 0; cert;
+		     cert = cert->next, depth++) {
+			if (cert->ocsp_revoked) {
+				tls_cert_chain_failure_event(
+					conn, depth, cert, TLS_FAIL_REVOKED,
+					"certificate revoked");
+			}
+		}
 		return -1;
 		return -1;
 	}
 	}
 
 
+	if (conn->flags & TLS_CONN_REQUIRE_OCSP_ALL) {
+		/*
+		 * Verify that each certificate on the chain that is not part
+		 * of the trusted certificates has a good status. If not,
+		 * terminate handshake.
+		 */
+		for (cert = conn->server_cert, depth = 0; cert;
+		     cert = cert->next, depth++) {
+			if (!cert->ocsp_good) {
+				tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					  TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE);
+				tls_cert_chain_failure_event(
+					conn, depth, cert,
+					TLS_FAIL_UNSPECIFIED,
+					"bad certificate status response");
+				return -1;
+			}
+			if (cert->issuer_trusted)
+				break;
+		}
+	}
+
 	if ((conn->flags & TLS_CONN_REQUIRE_OCSP) && res != TLS_OCSP_GOOD) {
 	if ((conn->flags & TLS_CONN_REQUIRE_OCSP) && res != TLS_OCSP_GOOD) {
 		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
 		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
 			  res == TLS_OCSP_INVALID ? TLS_ALERT_DECODE_ERROR :
 			  res == TLS_OCSP_INVALID ? TLS_ALERT_DECODE_ERROR :

+ 2 - 0
src/tls/x509v3.c

@@ -2038,6 +2038,7 @@ int x509_certificate_chain_validate(struct x509_certificate *trusted,
 	os_get_time(&now);
 	os_get_time(&now);
 
 
 	for (cert = chain, idx = 0; cert; cert = cert->next, idx++) {
 	for (cert = chain, idx = 0; cert; cert = cert->next, idx++) {
+		cert->issuer_trusted = 0;
 		x509_name_string(&cert->subject, buf, sizeof(buf)); 
 		x509_name_string(&cert->subject, buf, sizeof(buf)); 
 		wpa_printf(MSG_DEBUG, "X509: %lu: %s", idx, buf);
 		wpa_printf(MSG_DEBUG, "X509: %lu: %s", idx, buf);
 
 
@@ -2123,6 +2124,7 @@ int x509_certificate_chain_validate(struct x509_certificate *trusted,
 
 
 			wpa_printf(MSG_DEBUG, "X509: Trusted certificate "
 			wpa_printf(MSG_DEBUG, "X509: Trusted certificate "
 				   "found to complete the chain");
 				   "found to complete the chain");
+			cert->issuer_trusted = 1;
 			chain_trusted = 1;
 			chain_trusted = 1;
 		}
 		}
 	}
 	}

+ 5 - 0
src/tls/x509v3.h

@@ -106,6 +106,11 @@ struct x509_certificate {
 	size_t cert_len;
 	size_t cert_len;
 	const u8 *tbs_cert_start;
 	const u8 *tbs_cert_start;
 	size_t tbs_cert_len;
 	size_t tbs_cert_len;
+
+	/* Meta data used for certificate validation */
+	unsigned int ocsp_good:1;
+	unsigned int ocsp_revoked:1;
+	unsigned int issuer_trusted:1;
 };
 };
 
 
 enum {
 enum {