diff --git a/src/crypto/tls_internal.c b/src/crypto/tls_internal.c index 4b87b304f..01a7c97de 100644 --- a/src/crypto/tls_internal.c +++ b/src/crypto/tls_internal.c @@ -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 */ diff --git a/src/tls/tlsv1_common.h b/src/tls/tlsv1_common.h index 7a252feec..e30b15a03 100644 --- a/src/tls/tlsv1_common.h +++ b/src/tls/tlsv1_common.h @@ -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 */ diff --git a/src/tls/tlsv1_cred.c b/src/tls/tlsv1_cred.c index 732ed4aa1..52c1ae014 100644 --- a/src/tls/tlsv1_cred.c +++ b/src/tls/tlsv1_cred.c @@ -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); } diff --git a/src/tls/tlsv1_cred.h b/src/tls/tlsv1_cred.h index cbf4d39e4..716e93c39 100644 --- a/src/tls/tlsv1_cred.h +++ b/src/tls/tlsv1_cred.h @@ -26,6 +26,7 @@ struct tlsv1_credentials { size_t dh_g_len; char *ocsp_stapling_response; + char *ocsp_stapling_response_multi; }; diff --git a/src/tls/tlsv1_server_i.h b/src/tls/tlsv1_server_i.h index 81439d15e..29c667877 100644 --- a/src/tls/tlsv1_server_i.h +++ b/src/tls/tlsv1_server_i.h @@ -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; diff --git a/src/tls/tlsv1_server_read.c b/src/tls/tlsv1_server_read.c index 5109b6071..4aa8a019f 100644 --- a/src/tls/tlsv1_server_read.c +++ b/src/tls/tlsv1_server_read.c @@ -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; diff --git a/src/tls/tlsv1_server_write.c b/src/tls/tlsv1_server_write.c index dafe3f970..bdc6c1199 100644 --- a/src/tls/tlsv1_server_write.c +++ b/src/tls/tlsv1_server_write.c @@ -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;