TLS: Parse CertificateStatus message

This allows the internal TLS client implementation to accept
CertificateStatus message from the server when trying to use OCSP
stapling. The actual OCSPResponse is not yet processed in this commit,
but the CertificateStatus message is accepted to allow the TLS handshake
to continue.

Signed-off-by: Jouni Malinen <j@w1.fi>
This commit is contained in:
Jouni Malinen 2015-12-13 23:11:32 +02:00
parent eeba168453
commit d560288a44
7 changed files with 192 additions and 3 deletions

View File

@ -24,6 +24,7 @@ LIB_OBJS= \
tlsv1_client.o \
tlsv1_client_read.o \
tlsv1_client_write.o \
tlsv1_client_ocsp.o \
tlsv1_common.o \
tlsv1_cred.o \
tlsv1_record.o \

View File

@ -1,6 +1,6 @@
/*
* TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246)
* Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
* Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@ -11,6 +11,7 @@
#include "common.h"
#include "crypto/sha1.h"
#include "crypto/tls.h"
#include "x509v3.h"
#include "tlsv1_common.h"
#include "tlsv1_record.h"
#include "tlsv1_client.h"
@ -494,6 +495,7 @@ void tlsv1_client_deinit(struct tlsv1_client *conn)
tlsv1_client_free_dh(conn);
tlsv1_cred_free(conn->cred);
wpabuf_free(conn->partial_input);
x509_certificate_chain_free(conn->server_cert);
os_free(conn);
}

View File

@ -36,6 +36,7 @@ struct tlsv1_client {
unsigned int session_ticket_included:1;
unsigned int use_session_ticket:1;
unsigned int cert_in_cb:1;
unsigned int ocsp_resp_received:1;
struct crypto_public_key *server_rsa_key;
@ -70,6 +71,8 @@ struct tlsv1_client {
void (*event_cb)(void *ctx, enum tls_event ev,
union tls_event_data *data);
void *cb_ctx;
struct x509_certificate *server_cert;
};
@ -87,4 +90,11 @@ int tlsv1_client_process_handshake(struct tlsv1_client *conn, u8 ct,
const u8 *buf, size_t *len,
u8 **out_data, size_t *out_len);
enum tls_ocsp_result {
TLS_OCSP_NO_RESPONSE, TLS_OCSP_INVALID, TLS_OCSP_GOOD, TLS_OCSP_REVOKED
};
enum tls_ocsp_result tls_process_ocsp_response(struct tlsv1_client *conn,
const u8 *resp, size_t len);
#endif /* TLSV1_CLIENT_I_H */

View File

@ -0,0 +1,26 @@
/*
* TLSv1 client - OCSP
* Copyright (c) 2015, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto/tls.h"
#include "tlsv1_common.h"
#include "tlsv1_record.h"
#include "tlsv1_client.h"
#include "tlsv1_client_i.h"
enum tls_ocsp_result tls_process_ocsp_response(struct tlsv1_client *conn,
const u8 *resp, size_t len)
{
wpa_hexdump(MSG_MSGDUMP, "TLSv1: OCSPResponse", resp, len);
/* TODO */
return TLS_OCSP_NO_RESPONSE;
}

View File

@ -614,7 +614,12 @@ static int tls_process_certificate(struct tlsv1_client *conn, u8 ct,
return -1;
}
x509_certificate_chain_free(chain);
if (conn->flags & TLS_CONN_REQUEST_OCSP) {
x509_certificate_chain_free(conn->server_cert);
conn->server_cert = chain;
} else {
x509_certificate_chain_free(chain);
}
*in_len = end - in_data;
@ -785,6 +790,134 @@ fail:
}
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) {
wpa_printf(MSG_DEBUG,
"TLSv1: Expected Handshake; received content type 0x%x",
ct);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_UNEXPECTED_MESSAGE);
return -1;
}
pos = in_data;
left = *in_len;
if (left < 4) {
wpa_printf(MSG_DEBUG,
"TLSv1: Too short CertificateStatus (left=%lu)",
(unsigned long) left);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
return -1;
}
type = *pos++;
len = WPA_GET_BE24(pos);
pos += 3;
left -= 4;
if (len > left) {
wpa_printf(MSG_DEBUG,
"TLSv1: Mismatch in CertificateStatus length (len=%lu != left=%lu)",
(unsigned long) len, (unsigned long) left);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
return -1;
}
end = pos + len;
if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_STATUS) {
wpa_printf(MSG_DEBUG,
"TLSv1: Received unexpected handshake message %d (expected CertificateStatus)",
type);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_UNEXPECTED_MESSAGE);
return -1;
}
wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateStatus");
/*
* struct {
* CertificateStatusType status_type;
* select (status_type) {
* case ocsp: OCSPResponse;
* } response;
* } CertificateStatus;
*/
if (end - pos < 1) {
wpa_printf(MSG_INFO, "TLSv1: Too short CertificateStatus");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
return -1;
}
status_type = *pos++;
wpa_printf(MSG_DEBUG, "TLSv1: CertificateStatus status_type %u",
status_type);
if (status_type != 1 /* ocsp */) {
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);
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;
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_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);
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:
*in_len = end - in_data;
conn->state = SERVER_KEY_EXCHANGE;
return 0;
}
static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct,
const u8 *in_data, size_t *in_len)
{
@ -826,6 +959,10 @@ static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct,
end = pos + len;
if ((conn->flags & TLS_CONN_REQUEST_OCSP) &&
type == TLS_HANDSHAKE_TYPE_CERTIFICATE_STATUS)
return tls_process_certificate_status(conn, ct, in_data,
in_len);
if (type == TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST)
return tls_process_certificate_request(conn, ct, in_data,
in_len);
@ -835,7 +972,9 @@ static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct,
if (type != TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE) {
wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
"message %d (expected ServerKeyExchange/"
"CertificateRequest/ServerHelloDone)", type);
"CertificateRequest/ServerHelloDone%s)", type,
(conn->flags & TLS_CONN_REQUEST_OCSP) ?
"/CertificateStatus" : "");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_UNEXPECTED_MESSAGE);
return -1;
@ -989,6 +1128,15 @@ static int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct,
wpa_printf(MSG_DEBUG, "TLSv1: Received ServerHelloDone");
if ((conn->flags & TLS_CONN_REQUIRE_OCSP) &&
!conn->ocsp_resp_received) {
wpa_printf(MSG_INFO,
"TLSv1: No OCSP response received - reject handshake");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE);
return -1;
}
*in_len = end - in_data;
conn->state = CLIENT_KEY_EXCHANGE;

View File

@ -981,6 +981,7 @@ OBJS += src/tls/tlsv1_cred.c
OBJS += src/tls/tlsv1_client.c
OBJS += src/tls/tlsv1_client_write.c
OBJS += src/tls/tlsv1_client_read.c
OBJS += src/tls/tlsv1_client_ocsp.c
OBJS += src/tls/asn1.c
OBJS += src/tls/rsa.c
OBJS += src/tls/x509v3.c

View File

@ -1010,6 +1010,7 @@ OBJS += ../src/tls/tlsv1_cred.o
OBJS += ../src/tls/tlsv1_client.o
OBJS += ../src/tls/tlsv1_client_write.o
OBJS += ../src/tls/tlsv1_client_read.o
OBJS += ../src/tls/tlsv1_client_ocsp.o
OBJS += ../src/tls/asn1.o
OBJS += ../src/tls/rsa.o
OBJS += ../src/tls/x509v3.o