]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
TLS client: OCSP stapling with ocsp_multi option (RFC 6961)
authorJouni Malinen <jouni@qca.qualcomm.com>
Tue, 22 Dec 2015 17:39:37 +0000 (19:39 +0200)
committerJouni Malinen <j@w1.fi>
Tue, 22 Dec 2015 18:44:56 +0000 (20:44 +0200)
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>
src/tls/tlsv1_client_read.c
src/tls/tlsv1_client_write.c

index ff1245204e582cfedfe67353afdc409010c514cb..ab0d9d76f13f45181cbd77e35708b88f21aca1ef 100644 (file)
@@ -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:
index 8e8cb5e4902f3cdb5b8a7e66f798f2529c817d5b..04d895e61926aad62fe3f28700615405e11c5133 100644 (file)
@@ -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)