Browse Source

TLS client: OCSP stapling with ocsp_multi option (RFC 6961)

This adds a minimal support for using status_request_v2 extension and
ocsp_multi format (OCSPResponseList instead of OCSPResponse) for
CertificateStatus. This commit does not yet extend use of OCSP stapling
to validate the intermediate CA certificates.

Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
Jouni Malinen 9 years ago
parent
commit
b5677752b2
2 changed files with 136 additions and 39 deletions
  1. 96 39
      src/tls/tlsv1_client_read.c
  2. 40 0
      src/tls/tlsv1_client_write.c

+ 96 - 39
src/tls/tlsv1_client_read.c

@@ -790,13 +790,37 @@ fail:
 }
 
 
+static enum tls_ocsp_result
+tls_process_certificate_status_ocsp_response(struct tlsv1_client *conn,
+					     const u8 *pos, size_t len)
+{
+	const u8 *end = pos + len;
+	u32 ocsp_resp_len;
+
+	/* opaque OCSPResponse<1..2^24-1>; */
+	if (end - pos < 3) {
+		wpa_printf(MSG_INFO, "TLSv1: Too short OCSPResponse");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return TLS_OCSP_INVALID;
+	}
+	ocsp_resp_len = WPA_GET_BE24(pos);
+	pos += 3;
+	if (end - pos < ocsp_resp_len) {
+		wpa_printf(MSG_INFO, "TLSv1: Truncated OCSPResponse");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return TLS_OCSP_INVALID;
+	}
+
+	return tls_process_ocsp_response(conn, pos, ocsp_resp_len);
+}
+
+
 static int tls_process_certificate_status(struct tlsv1_client *conn, u8 ct,
 					   const u8 *in_data, size_t *in_len)
 {
 	const u8 *pos, *end;
 	size_t left, len;
 	u8 type, status_type;
-	u32 ocsp_resp_len;
 	enum tls_ocsp_result res;
 
 	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
@@ -850,6 +874,7 @@ static int tls_process_certificate_status(struct tlsv1_client *conn, u8 ct,
 	 *     CertificateStatusType status_type;
 	 *     select (status_type) {
 	 *         case ocsp: OCSPResponse;
+	 *         case ocsp_multi: OCSPResponseList;
 	 *     } response;
 	 * } CertificateStatus;
 	 */
@@ -862,32 +887,84 @@ static int tls_process_certificate_status(struct tlsv1_client *conn, u8 ct,
 	wpa_printf(MSG_DEBUG, "TLSv1: CertificateStatus status_type %u",
 		   status_type);
 
-	if (status_type != 1 /* ocsp */) {
+	if (status_type == 1 /* ocsp */) {
+		res = tls_process_certificate_status_ocsp_response(
+			conn, pos, end - pos);
+	} else if (status_type == 2 /* ocsp_multi */) {
+		int good = 0, revoked = 0;
+		u32 resp_len;
+
+		res = TLS_OCSP_NO_RESPONSE;
+
+		/*
+		 * opaque OCSPResponse<0..2^24-1>;
+		 *
+		 * struct {
+		 *   OCSPResponse ocsp_response_list<1..2^24-1>;
+		 * } OCSPResponseList;
+		 */
+		if (end - pos < 3) {
+			wpa_printf(MSG_DEBUG,
+				   "TLSv1: Truncated OCSPResponseList");
+			res = TLS_OCSP_INVALID;
+			goto done;
+		}
+		resp_len = WPA_GET_BE24(pos);
+		pos += 3;
+		if (end - pos < resp_len) {
+			wpa_printf(MSG_DEBUG,
+				   "TLSv1: Truncated OCSPResponseList(len=%u)",
+				   resp_len);
+			res = TLS_OCSP_INVALID;
+			goto done;
+		}
+		end = pos + resp_len;
+
+		while (end - pos >= 3) {
+			resp_len = WPA_GET_BE24(pos);
+			pos += 3;
+			if (resp_len > end - pos) {
+				wpa_printf(MSG_DEBUG,
+					   "TLSv1: Truncated OCSPResponse(len=%u; left=%d) in ocsp_multi",
+					   resp_len, (int) (end - pos));
+				res = TLS_OCSP_INVALID;
+				break;
+			}
+			if (!resp_len)
+				continue; /* Skip an empty response */
+			res = tls_process_certificate_status_ocsp_response(
+				conn, pos - 3, resp_len + 3);
+			if (res == TLS_OCSP_REVOKED)
+				revoked++;
+			else if (res == TLS_OCSP_GOOD)
+				good++;
+			pos += resp_len;
+		}
+
+		if (revoked)
+			res = TLS_OCSP_REVOKED;
+		else if (good)
+			res = TLS_OCSP_GOOD;
+	} else {
 		wpa_printf(MSG_DEBUG,
 			   "TLSv1: Ignore unsupported CertificateStatus");
 		goto skip;
 	}
 
-	/* opaque OCSPResponse<1..2^24-1>; */
-	if (end - pos < 3) {
-		wpa_printf(MSG_INFO, "TLSv1: Too short OCSPResponse");
-		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
-		return -1;
-	}
-	ocsp_resp_len = WPA_GET_BE24(pos);
-	pos += 3;
-	if (end - pos < ocsp_resp_len) {
-		wpa_printf(MSG_INFO, "TLSv1: Truncated OCSPResponse");
-		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+done:
+	if (res == TLS_OCSP_REVOKED) {
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_CERTIFICATE_REVOKED);
+		if (conn->server_cert)
+			tls_cert_chain_failure_event(
+				conn, 0, conn->server_cert, TLS_FAIL_REVOKED,
+				"certificate revoked");
 		return -1;
 	}
 
-	res = tls_process_ocsp_response(conn, pos, ocsp_resp_len);
-	switch (res) {
-	case TLS_OCSP_NO_RESPONSE:
-		if (!(conn->flags & TLS_CONN_REQUIRE_OCSP))
-			goto skip;
+	if ((conn->flags & TLS_CONN_REQUIRE_OCSP) && res != TLS_OCSP_GOOD) {
 		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  res == TLS_OCSP_INVALID ? TLS_ALERT_DECODE_ERROR :
 			  TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE);
 		if (conn->server_cert)
 			tls_cert_chain_failure_event(
@@ -895,28 +972,8 @@ static int tls_process_certificate_status(struct tlsv1_client *conn, u8 ct,
 				TLS_FAIL_UNSPECIFIED,
 				"bad certificate status response");
 		return -1;
-	case TLS_OCSP_INVALID:
-		if (!(conn->flags & TLS_CONN_REQUIRE_OCSP))
-			goto skip; /* ignore - process as if no response */
-		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
-		if (conn->server_cert)
-			tls_cert_chain_failure_event(
-				conn, 0, conn->server_cert,
-				TLS_FAIL_UNSPECIFIED,
-				"bad certificate status response");
-		return -1;
-	case TLS_OCSP_GOOD:
-		wpa_printf(MSG_DEBUG, "TLSv1: OCSP response good");
-		break;
-	case TLS_OCSP_REVOKED:
-		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
-			  TLS_ALERT_CERTIFICATE_REVOKED);
-		if (conn->server_cert)
-			tls_cert_chain_failure_event(
-				conn, 0, conn->server_cert, TLS_FAIL_REVOKED,
-				"certificate revoked");
-		return -1;
 	}
+
 	conn->ocsp_resp_received = 1;
 
 skip:

+ 40 - 0
src/tls/tlsv1_client_write.c

@@ -192,6 +192,46 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len)
 		pos += 2;
 		WPA_PUT_BE16(pos, 0); /* request_extensions(empty) */
 		pos += 2;
+
+		wpa_printf(MSG_DEBUG,
+			   "TLSv1: Add status_request_v2 extension for OCSP stapling");
+		/* ExtensionsType extension_type = status_request_v2(17) */
+		WPA_PUT_BE16(pos, TLS_EXT_STATUS_REQUEST_V2);
+		pos += 2;
+		/* opaque extension_data<0..2^16-1> length */
+		WPA_PUT_BE16(pos, 7);
+		pos += 2;
+
+		/*
+		 * RFC 6961, 2.2:
+		 * struct {
+		 *     CertificateStatusType status_type;
+		 *     uint16 request_length;
+		 *     select (status_type) {
+		 *         case ocsp: OCSPStatusRequest;
+		 *         case ocsp_multi: OCSPStatusRequest;
+		 *     } request;
+		 * } CertificateStatusRequestItemV2;
+		 *
+		 * enum { ocsp(1), ocsp_multi(2), (255) } CertificateStatusType;
+		 *
+		 * struct {
+		 * CertificateStatusRequestItemV2
+		 *     certificate_status_req_list<1..2^16-1>;
+		 * } CertificateStatusRequestListV2;
+		 */
+
+		/* certificate_status_req_list<1..2^16-1> */
+		WPA_PUT_BE16(pos, 5);
+		pos += 2;
+
+		/* CertificateStatusRequestItemV2 */
+		*pos++ = 2; /* status_type = ocsp_multi(2) */
+		/* OCSPStatusRequest as shown above for v1 */
+		WPA_PUT_BE16(pos, 0); /* responder_id_list(empty) */
+		pos += 2;
+		WPA_PUT_BE16(pos, 0); /* request_extensions(empty) */
+		pos += 2;
 	}
 
 	if (pos == ext_start + 2)