]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
TLS server: OCSP stapling with ocsp_multi option (RFC 6961)
authorJouni Malinen <jouni@qca.qualcomm.com>
Tue, 22 Dec 2015 17:37:12 +0000 (19:37 +0200)
committerJouni Malinen <j@w1.fi>
Tue, 22 Dec 2015 18:44:52 +0000 (20:44 +0200)
This allows hostapd with the internal TLS server implementation to
support the extended OCSP stapling mechanism with multiple responses
(ocsp_stapling_response_multi).

Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
src/crypto/tls_internal.c
src/tls/tlsv1_common.h
src/tls/tlsv1_cred.c
src/tls/tlsv1_cred.h
src/tls/tlsv1_server_i.h
src/tls/tlsv1_server_read.c
src/tls/tlsv1_server_write.c

index 4b87b304f09536edd927a30eebc9b178178369c7..01a7c97de3e611c15cbe8ec24cb51603a045815f 100644 (file)
@@ -334,6 +334,9 @@ int tls_global_set_params(void *tls_ctx,
        if (params->ocsp_stapling_response)
                cred->ocsp_stapling_response =
                        os_strdup(params->ocsp_stapling_response);
+       if (params->ocsp_stapling_response_multi)
+               cred->ocsp_stapling_response_multi =
+                       os_strdup(params->ocsp_stapling_response_multi);
 
        return 0;
 #else /* CONFIG_TLS_INTERNAL_SERVER */
index 7a252feec8aec04ea88040bd399eeb0b68f1df0b..e30b15a030a801c8cc72e808d16588f48dca19a3 100644 (file)
@@ -170,6 +170,7 @@ enum {
 #define TLS_EXT_TRUNCATED_HMAC                 4 /* RFC 4366 */
 #define TLS_EXT_STATUS_REQUEST                 5 /* RFC 4366 */
 #define TLS_EXT_SIGNATURE_ALGORITHMS           13 /* RFC 5246 */
+#define TLS_EXT_STATUS_REQUEST_V2              17 /* RFC 6961 */
 #define TLS_EXT_SESSION_TICKET                 35 /* RFC 4507 */
 
 #define TLS_EXT_PAC_OPAQUE TLS_EXT_SESSION_TICKET /* EAP-FAST terminology */
index 732ed4aa19fb008c6c494da933cb50d3f5efb7ec..52c1ae0143da3cb82a173beefaa673e34c4b4bf8 100644 (file)
@@ -37,6 +37,7 @@ void tlsv1_cred_free(struct tlsv1_credentials *cred)
        os_free(cred->dh_p);
        os_free(cred->dh_g);
        os_free(cred->ocsp_stapling_response);
+       os_free(cred->ocsp_stapling_response_multi);
        os_free(cred);
 }
 
index cbf4d39e418c82f8db9b77ccdb4499883216580d..716e93c39006ca818d6737495c39b4b53cdeaaa9 100644 (file)
@@ -26,6 +26,7 @@ struct tlsv1_credentials {
        size_t dh_g_len;
 
        char *ocsp_stapling_response;
+       char *ocsp_stapling_response_multi;
 };
 
 
index 81439d15ea6dc88fa0411835a69010a7c711bc05..29c6678772150c8e813c307b0a54f370a5611276 100644 (file)
@@ -56,6 +56,8 @@ struct tlsv1_server {
 
        int use_session_ticket;
        unsigned int status_request:1;
+       unsigned int status_request_v2:1;
+       unsigned int status_request_multi:1;
 
        u8 *dh_secret;
        size_t dh_secret_len;
index 5109b60716e46e4acf7e2b330ac4553c311bd396..4aa8a019f3e6e4fe86a54e7ea6aa1b252ea7f2f7 100644 (file)
@@ -46,6 +46,78 @@ static int testing_cipher_suite_filter(struct tlsv1_server *conn, u16 suite)
 }
 
 
+static void tls_process_status_request_item(struct tlsv1_server *conn,
+                                           const u8 *req, size_t req_len)
+{
+       const u8 *pos, *end;
+       u8 status_type;
+
+       pos = req;
+       end = req + req_len;
+
+       /*
+        * 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;
+        */
+
+       if (end - pos < 1)
+               return; /* Truncated data */
+
+       status_type = *pos++;
+       wpa_printf(MSG_DEBUG, "TLSv1: CertificateStatusType %u", status_type);
+       if (status_type != 1 && status_type != 2)
+               return; /* Unsupported status type */
+       /*
+        * For now, only OCSP stapling is supported, so ignore the specific
+        * request, if any.
+        */
+       wpa_hexdump(MSG_DEBUG, "TLSv1: OCSPStatusRequest", pos, end - pos);
+
+       if (status_type == 2)
+               conn->status_request_multi = 1;
+}
+
+
+static void tls_process_status_request_v2(struct tlsv1_server *conn,
+                                         const u8 *ext, size_t ext_len)
+{
+       const u8 *pos, *end;
+
+       conn->status_request_v2 = 1;
+
+       pos = ext;
+       end = ext + ext_len;
+
+       /*
+        * RFC 6961, 2.2:
+        * struct {
+        *   CertificateStatusRequestItemV2
+        *                    certificate_status_req_list<1..2^16-1>;
+        * } CertificateStatusRequestListV2;
+        */
+
+       while (end - pos >= 2) {
+               u16 len;
+
+               len = WPA_GET_BE16(pos);
+               pos += 2;
+               if (len > end - pos)
+                       break; /* Truncated data */
+               tls_process_status_request_item(conn, pos, len);
+               pos += len;
+       }
+}
+
+
 static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct,
                                    const u8 *in_data, size_t *in_len)
 {
@@ -269,6 +341,9 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct,
                                }
                        } else if (ext_type == TLS_EXT_STATUS_REQUEST) {
                                conn->status_request = 1;
+                       } else if (ext_type == TLS_EXT_STATUS_REQUEST_V2) {
+                               tls_process_status_request_v2(conn, pos,
+                                                             ext_len);
                        }
 
                        pos += ext_len;
index dafe3f970d8a10f170efd673444e0f8af75223fa..bdc6c119923847244a3252b42e10ee2b37beceb0 100644 (file)
@@ -111,6 +111,18 @@ static int tls_write_server_hello(struct tlsv1_server *conn,
                pos += 2;
        }
 
+       if (conn->status_request_v2) {
+               /*
+                 Add a status_request_v2 extension with empty extension_data
+               */
+               /* 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, 0);
+               pos += 2;
+       }
+
        if (conn->session_ticket && conn->session_ticket_cb) {
                int res = conn->session_ticket_cb(
                        conn->session_ticket_cb_ctx,
@@ -264,30 +276,31 @@ static int tls_write_server_certificate(struct tlsv1_server *conn,
 
 
 static int tls_write_server_certificate_status(struct tlsv1_server *conn,
-                                              u8 **msgpos, u8 *end)
+                                              u8 **msgpos, u8 *end,
+                                              int ocsp_multi,
+                                              char *ocsp_resp,
+                                              size_t ocsp_resp_len)
 {
        u8 *pos, *rhdr, *hs_start, *hs_length;
-       char *resp;
-       size_t rlen, len;
+       size_t rlen;
 
-       if (!conn->status_request)
-               return 0; /* Client did not request certificate status */
-       if (!conn->cred->ocsp_stapling_response)
-               return 0; /* No cached OCSP stapling response */
-       resp = os_readfile(conn->cred->ocsp_stapling_response, &len);
-       if (!resp)
-               return 0; /* No cached OCSP stapling response */
+       if (!ocsp_resp) {
+                /*
+                 * Client did not request certificate status or there is no
+                 * matching response cached.
+                 */
+               return 0;
+       }
 
        pos = *msgpos;
-       if (TLS_RECORD_HEADER_LEN + 1 + 3 + 1 + 3 + len >
+       if (TLS_RECORD_HEADER_LEN + 1 + 3 + 1 + 3 + ocsp_resp_len >
            (unsigned int) (end - pos)) {
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_INTERNAL_ERROR);
-               os_free(resp);
                return -1;
        }
 
-       tlsv1_server_log(conn, "Send CertificateStatus");
+       tlsv1_server_log(conn, "Send CertificateStatus (multi=%d)", ocsp_multi);
        rhdr = pos;
        pos += TLS_RECORD_HEADER_LEN;
 
@@ -307,20 +320,27 @@ static int tls_write_server_certificate_status(struct tlsv1_server *conn,
         *     CertificateStatusType status_type;
         *     select (status_type) {
         *         case ocsp: OCSPResponse;
+        *         case ocsp_multi: OCSPResponseList;
         *     } response;
         * } CertificateStatus;
         *
         * opaque OCSPResponse<1..2^24-1>;
+        *
+        * struct {
+        *   OCSPResponse ocsp_response_list<1..2^24-1>;
+        * } OCSPResponseList;
         */
 
        /* CertificateStatusType status_type */
-       *pos++ = 1; /* ocsp(1) */
+       if (ocsp_multi)
+               *pos++ = 2; /* ocsp_multi(2) */
+       else
+               *pos++ = 1; /* ocsp(1) */
        /* uint24 length of OCSPResponse */
-       WPA_PUT_BE24(pos, len);
+       WPA_PUT_BE24(pos, ocsp_resp_len);
        pos += 3;
-       os_memcpy(pos, resp, len);
-       os_free(resp);
-       pos += len;
+       os_memcpy(pos, ocsp_resp, ocsp_resp_len);
+       pos += ocsp_resp_len;
 
        WPA_PUT_BE24(hs_length, pos - hs_length - 3);
 
@@ -908,34 +928,46 @@ static u8 * tls_send_server_hello(struct tlsv1_server *conn, size_t *out_len)
 {
        u8 *msg, *end, *pos;
        size_t msglen;
+       int ocsp_multi = 0;
+       char *ocsp_resp = NULL;
+       size_t ocsp_resp_len = 0;
 
        *out_len = 0;
 
-       msglen = 1000 + tls_server_cert_chain_der_len(conn);
-       if (conn->status_request && conn->cred->ocsp_stapling_response) {
-               char *resp;
-               size_t len;
-
-               resp = os_readfile(conn->cred->ocsp_stapling_response, &len);
-               if (resp) {
-                       msglen += 10 + len;
-                       os_free(resp);
-               }
+       if (conn->status_request_multi &&
+           conn->cred->ocsp_stapling_response_multi) {
+               ocsp_resp = os_readfile(
+                       conn->cred->ocsp_stapling_response_multi,
+                       &ocsp_resp_len);
+               ocsp_multi = 1;
+       } else if ((conn->status_request || conn->status_request_v2) &&
+                  conn->cred->ocsp_stapling_response) {
+               ocsp_resp = os_readfile(conn->cred->ocsp_stapling_response,
+                                       &ocsp_resp_len);
        }
+       if (!ocsp_resp)
+               ocsp_resp_len = 0;
+
+       msglen = 1000 + tls_server_cert_chain_der_len(conn) + ocsp_resp_len;
 
        msg = os_malloc(msglen);
-       if (msg == NULL)
+       if (msg == NULL) {
+               os_free(ocsp_resp);
                return NULL;
+       }
 
        pos = msg;
        end = msg + msglen;
 
        if (tls_write_server_hello(conn, &pos, end) < 0) {
                os_free(msg);
+               os_free(ocsp_resp);
                return NULL;
        }
 
        if (conn->use_session_ticket) {
+               os_free(ocsp_resp);
+
                /* Abbreviated handshake using session ticket; RFC 4507 */
                if (tls_write_server_change_cipher_spec(conn, &pos, end) < 0 ||
                    tls_write_server_finished(conn, &pos, end) < 0) {
@@ -952,13 +984,16 @@ static u8 * tls_send_server_hello(struct tlsv1_server *conn, size_t *out_len)
 
        /* Full handshake */
        if (tls_write_server_certificate(conn, &pos, end) < 0 ||
-           tls_write_server_certificate_status(conn, &pos, end) < 0 ||
+           tls_write_server_certificate_status(conn, &pos, end, ocsp_multi,
+                                               ocsp_resp, ocsp_resp_len) < 0 ||
            tls_write_server_key_exchange(conn, &pos, end) < 0 ||
            tls_write_server_certificate_request(conn, &pos, end) < 0 ||
            tls_write_server_hello_done(conn, &pos, end) < 0) {
                os_free(msg);
+               os_free(ocsp_resp);
                return NULL;
        }
+       os_free(ocsp_resp);
 
        *out_len = pos - msg;