Reorder states and functions by where they appear in the handshake. Remove unnecessary hooks on SSL_PROTOCOL_METHOD. Change-Id: I78dae9cf70792170abed6f38510ce870707e82ff Reviewed-on: https://boringssl-review.googlesource.com/8184 Reviewed-by: David Benjamin <davidben@google.com>kris/onging/CECPQ3_patch15
@@ -63,8 +63,6 @@ static const SSL_PROTOCOL_METHOD DTLS_protocol_method = { | |||||
1 /* is_dtls */, | 1 /* is_dtls */, | ||||
dtls1_new, | dtls1_new, | ||||
dtls1_free, | dtls1_free, | ||||
ssl3_accept, | |||||
ssl3_connect, | |||||
dtls1_get_message, | dtls1_get_message, | ||||
dtls1_read_app_data, | dtls1_read_app_data, | ||||
dtls1_read_change_cipher_spec, | dtls1_read_change_cipher_spec, | ||||
@@ -806,8 +806,6 @@ struct ssl_protocol_method_st { | |||||
char is_dtls; | char is_dtls; | ||||
int (*ssl_new)(SSL *ssl); | int (*ssl_new)(SSL *ssl); | ||||
void (*ssl_free)(SSL *ssl); | void (*ssl_free)(SSL *ssl); | ||||
int (*ssl_accept)(SSL *ssl); | |||||
int (*ssl_connect)(SSL *ssl); | |||||
long (*ssl_get_message)(SSL *ssl, int msg_type, | long (*ssl_get_message)(SSL *ssl, int msg_type, | ||||
enum ssl_hash_message_t hash_message, int *ok); | enum ssl_hash_message_t hash_message, int *ok); | ||||
int (*ssl_read_app_data)(SSL *ssl, uint8_t *buf, int len, int peek); | int (*ssl_read_app_data)(SSL *ssl, uint8_t *buf, int len, int peek); | ||||
@@ -173,20 +173,18 @@ | |||||
static int ssl3_send_client_hello(SSL *ssl); | static int ssl3_send_client_hello(SSL *ssl); | ||||
static int dtls1_get_hello_verify(SSL *ssl); | static int dtls1_get_hello_verify(SSL *ssl); | ||||
static int ssl3_get_server_hello(SSL *ssl); | static int ssl3_get_server_hello(SSL *ssl); | ||||
static int ssl3_get_certificate_request(SSL *ssl); | |||||
static int ssl3_get_new_session_ticket(SSL *ssl); | |||||
static int ssl3_get_server_certificate(SSL *ssl); | |||||
static int ssl3_get_cert_status(SSL *ssl); | static int ssl3_get_cert_status(SSL *ssl); | ||||
static int ssl3_verify_server_cert(SSL *ssl); | |||||
static int ssl3_get_server_key_exchange(SSL *ssl); | |||||
static int ssl3_get_certificate_request(SSL *ssl); | |||||
static int ssl3_get_server_done(SSL *ssl); | static int ssl3_get_server_done(SSL *ssl); | ||||
static int ssl3_send_cert_verify(SSL *ssl); | |||||
static int ssl3_send_client_certificate(SSL *ssl); | static int ssl3_send_client_certificate(SSL *ssl); | ||||
static int ssl_do_client_cert_cb(SSL *ssl, X509 **out_x509, | |||||
EVP_PKEY **out_pkey); | |||||
static int ssl3_send_client_key_exchange(SSL *ssl); | static int ssl3_send_client_key_exchange(SSL *ssl); | ||||
static int ssl3_get_server_key_exchange(SSL *ssl); | |||||
static int ssl3_get_server_certificate(SSL *ssl); | |||||
static int ssl3_send_cert_verify(SSL *ssl); | |||||
static int ssl3_send_next_proto(SSL *ssl); | static int ssl3_send_next_proto(SSL *ssl); | ||||
static int ssl3_send_channel_id(SSL *ssl); | static int ssl3_send_channel_id(SSL *ssl); | ||||
static int ssl3_verify_server_cert(SSL *ssl); | |||||
static int ssl3_get_new_session_ticket(SSL *ssl); | |||||
int ssl3_connect(SSL *ssl) { | int ssl3_connect(SSL *ssl) { | ||||
BUF_MEM *buf = NULL; | BUF_MEM *buf = NULL; | ||||
@@ -287,6 +285,14 @@ int ssl3_connect(SSL *ssl) { | |||||
} | } | ||||
break; | break; | ||||
case SSL3_ST_CR_CERT_STATUS_A: | |||||
ret = ssl3_get_cert_status(ssl); | |||||
if (ret <= 0) { | |||||
goto end; | |||||
} | |||||
ssl->state = SSL3_ST_VERIFY_SERVER_CERT; | |||||
break; | |||||
case SSL3_ST_VERIFY_SERVER_CERT: | case SSL3_ST_VERIFY_SERVER_CERT: | ||||
ret = ssl3_verify_server_cert(ssl); | ret = ssl3_verify_server_cert(ssl); | ||||
if (ret <= 0) { | if (ret <= 0) { | ||||
@@ -448,6 +454,19 @@ int ssl3_connect(SSL *ssl) { | |||||
} | } | ||||
break; | break; | ||||
case SSL3_ST_FALSE_START: | |||||
/* Allow NewSessionTicket if ticket expected */ | |||||
if (ssl->tlsext_ticket_expected) { | |||||
ssl->state = SSL3_ST_CR_SESSION_TICKET_A; | |||||
} else { | |||||
ssl->state = SSL3_ST_CR_CHANGE; | |||||
} | |||||
ssl->s3->tmp.in_false_start = 1; | |||||
ssl_free_wbio_buffer(ssl); | |||||
ret = 1; | |||||
goto end; | |||||
case SSL3_ST_CR_SESSION_TICKET_A: | case SSL3_ST_CR_SESSION_TICKET_A: | ||||
ret = ssl3_get_new_session_ticket(ssl); | ret = ssl3_get_new_session_ticket(ssl); | ||||
if (ret <= 0) { | if (ret <= 0) { | ||||
@@ -456,14 +475,6 @@ int ssl3_connect(SSL *ssl) { | |||||
ssl->state = SSL3_ST_CR_CHANGE; | ssl->state = SSL3_ST_CR_CHANGE; | ||||
break; | break; | ||||
case SSL3_ST_CR_CERT_STATUS_A: | |||||
ret = ssl3_get_cert_status(ssl); | |||||
if (ret <= 0) { | |||||
goto end; | |||||
} | |||||
ssl->state = SSL3_ST_VERIFY_SERVER_CERT; | |||||
break; | |||||
case SSL3_ST_CR_CHANGE: | case SSL3_ST_CR_CHANGE: | ||||
ret = ssl->method->ssl_read_change_cipher_spec(ssl); | ret = ssl->method->ssl_read_change_cipher_spec(ssl); | ||||
if (ret <= 0) { | if (ret <= 0) { | ||||
@@ -503,19 +514,6 @@ int ssl3_connect(SSL *ssl) { | |||||
} | } | ||||
break; | break; | ||||
case SSL3_ST_FALSE_START: | |||||
/* Allow NewSessionTicket if ticket expected */ | |||||
if (ssl->tlsext_ticket_expected) { | |||||
ssl->state = SSL3_ST_CR_SESSION_TICKET_A; | |||||
} else { | |||||
ssl->state = SSL3_ST_CR_CHANGE; | |||||
} | |||||
ssl->s3->tmp.in_false_start = 1; | |||||
ssl_free_wbio_buffer(ssl); | |||||
ret = 1; | |||||
goto end; | |||||
case SSL_ST_OK: | case SSL_ST_OK: | ||||
/* clean a few things up */ | /* clean a few things up */ | ||||
ssl3_cleanup_key_block(ssl); | ssl3_cleanup_key_block(ssl); | ||||
@@ -1083,6 +1081,63 @@ err: | |||||
return ret; | return ret; | ||||
} | } | ||||
static int ssl3_get_cert_status(SSL *ssl) { | |||||
int ok, al; | |||||
long n; | |||||
CBS certificate_status, ocsp_response; | |||||
uint8_t status_type; | |||||
n = ssl->method->ssl_get_message(ssl, -1, ssl_hash_message, &ok); | |||||
if (!ok) { | |||||
return n; | |||||
} | |||||
if (ssl->s3->tmp.message_type != SSL3_MT_CERTIFICATE_STATUS) { | |||||
/* A server may send status_request in ServerHello and then change | |||||
* its mind about sending CertificateStatus. */ | |||||
ssl->s3->tmp.reuse_message = 1; | |||||
return 1; | |||||
} | |||||
CBS_init(&certificate_status, ssl->init_msg, n); | |||||
if (!CBS_get_u8(&certificate_status, &status_type) || | |||||
status_type != TLSEXT_STATUSTYPE_ocsp || | |||||
!CBS_get_u24_length_prefixed(&certificate_status, &ocsp_response) || | |||||
CBS_len(&ocsp_response) == 0 || | |||||
CBS_len(&certificate_status) != 0) { | |||||
al = SSL_AD_DECODE_ERROR; | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); | |||||
goto f_err; | |||||
} | |||||
if (!CBS_stow(&ocsp_response, &ssl->session->ocsp_response, | |||||
&ssl->session->ocsp_response_length)) { | |||||
al = SSL_AD_INTERNAL_ERROR; | |||||
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); | |||||
goto f_err; | |||||
} | |||||
return 1; | |||||
f_err: | |||||
ssl3_send_alert(ssl, SSL3_AL_FATAL, al); | |||||
return -1; | |||||
} | |||||
static int ssl3_verify_server_cert(SSL *ssl) { | |||||
int ret = ssl_verify_cert_chain(ssl, ssl->session->cert_chain); | |||||
if (ssl->verify_mode != SSL_VERIFY_NONE && ret <= 0) { | |||||
int al = ssl_verify_alarm_type(ssl->verify_result); | |||||
ssl3_send_alert(ssl, SSL3_AL_FATAL, al); | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_CERTIFICATE_VERIFY_FAILED); | |||||
} else { | |||||
ret = 1; | |||||
ERR_clear_error(); /* but we keep ssl->verify_result */ | |||||
} | |||||
return ret; | |||||
} | |||||
static int ssl3_get_server_key_exchange(SSL *ssl) { | static int ssl3_get_server_key_exchange(SSL *ssl) { | ||||
EVP_MD_CTX md_ctx; | EVP_MD_CTX md_ctx; | ||||
int al, ok; | int al, ok; | ||||
@@ -1460,141 +1515,120 @@ err: | |||||
return ret; | return ret; | ||||
} | } | ||||
static int ssl3_get_new_session_ticket(SSL *ssl) { | |||||
int ok, al; | |||||
long n = ssl->method->ssl_get_message(ssl, SSL3_MT_NEW_SESSION_TICKET, | |||||
ssl_hash_message, &ok); | |||||
static int ssl3_get_server_done(SSL *ssl) { | |||||
int ok; | |||||
long n; | |||||
n = ssl->method->ssl_get_message(ssl, SSL3_MT_SERVER_DONE, ssl_hash_message, | |||||
&ok); | |||||
if (!ok) { | if (!ok) { | ||||
return n; | return n; | ||||
} | } | ||||
CBS new_session_ticket, ticket; | |||||
uint32_t ticket_lifetime_hint; | |||||
CBS_init(&new_session_ticket, ssl->init_msg, n); | |||||
if (!CBS_get_u32(&new_session_ticket, &ticket_lifetime_hint) || | |||||
!CBS_get_u16_length_prefixed(&new_session_ticket, &ticket) || | |||||
CBS_len(&new_session_ticket) != 0) { | |||||
al = SSL_AD_DECODE_ERROR; | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); | |||||
goto f_err; | |||||
} | |||||
if (CBS_len(&ticket) == 0) { | |||||
/* RFC 5077 allows a server to change its mind and send no ticket after | |||||
* negotiating the extension. The value of |tlsext_ticket_expected| is | |||||
* checked in |ssl_update_cache| so is cleared here to avoid an unnecessary | |||||
* update. */ | |||||
ssl->tlsext_ticket_expected = 0; | |||||
return 1; | |||||
if (n > 0) { | |||||
/* should contain no data */ | |||||
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_LENGTH_MISMATCH); | |||||
return -1; | |||||
} | } | ||||
if (ssl->hit) { | |||||
/* The server is sending a new ticket for an existing session. Sessions are | |||||
* immutable once established, so duplicate all but the ticket of the | |||||
* existing session. */ | |||||
uint8_t *bytes; | |||||
size_t bytes_len; | |||||
if (!SSL_SESSION_to_bytes_for_ticket(ssl->session, &bytes, &bytes_len)) { | |||||
goto err; | |||||
} | |||||
SSL_SESSION *new_session = SSL_SESSION_from_bytes(bytes, bytes_len); | |||||
OPENSSL_free(bytes); | |||||
if (new_session == NULL) { | |||||
/* This should never happen. */ | |||||
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); | |||||
goto err; | |||||
} | |||||
return 1; | |||||
} | |||||
SSL_SESSION_free(ssl->session); | |||||
ssl->session = new_session; | |||||
} | |||||
/* ssl3_has_client_certificate returns true if a client certificate is | |||||
* configured. */ | |||||
static int ssl3_has_client_certificate(SSL *ssl) { | |||||
return ssl->cert && ssl->cert->x509 && ssl_has_private_key(ssl); | |||||
} | |||||
if (!CBS_stow(&ticket, &ssl->session->tlsext_tick, | |||||
&ssl->session->tlsext_ticklen)) { | |||||
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); | |||||
goto err; | |||||
static int ssl_do_client_cert_cb(SSL *ssl, X509 **out_x509, | |||||
EVP_PKEY **out_pkey) { | |||||
if (ssl->ctx->client_cert_cb == NULL) { | |||||
return 0; | |||||
} | } | ||||
ssl->session->tlsext_tick_lifetime_hint = ticket_lifetime_hint; | |||||
/* Generate a session ID for this session based on the session ticket. We use | |||||
* the session ID mechanism for detecting ticket resumption. This also fits in | |||||
* with assumptions elsewhere in OpenSSL.*/ | |||||
if (!EVP_Digest(CBS_data(&ticket), CBS_len(&ticket), ssl->session->session_id, | |||||
&ssl->session->session_id_length, EVP_sha256(), NULL)) { | |||||
goto err; | |||||
int ret = ssl->ctx->client_cert_cb(ssl, out_x509, out_pkey); | |||||
if (ret <= 0) { | |||||
return ret; | |||||
} | } | ||||
assert(*out_x509 != NULL); | |||||
assert(*out_pkey != NULL); | |||||
return 1; | return 1; | ||||
f_err: | |||||
ssl3_send_alert(ssl, SSL3_AL_FATAL, al); | |||||
err: | |||||
return -1; | |||||
} | } | ||||
static int ssl3_get_cert_status(SSL *ssl) { | |||||
int ok, al; | |||||
long n; | |||||
CBS certificate_status, ocsp_response; | |||||
uint8_t status_type; | |||||
n = ssl->method->ssl_get_message(ssl, -1, ssl_hash_message, &ok); | |||||
static int ssl3_send_client_certificate(SSL *ssl) { | |||||
if (ssl->state == SSL3_ST_CW_CERT_A) { | |||||
/* Call cert_cb to update the certificate. */ | |||||
if (ssl->cert->cert_cb) { | |||||
int ret = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg); | |||||
if (ret < 0) { | |||||
ssl->rwstate = SSL_X509_LOOKUP; | |||||
return -1; | |||||
} | |||||
if (ret == 0) { | |||||
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); | |||||
return -1; | |||||
} | |||||
} | |||||
if (!ok) { | |||||
return n; | |||||
if (ssl3_has_client_certificate(ssl)) { | |||||
ssl->state = SSL3_ST_CW_CERT_C; | |||||
} else { | |||||
ssl->state = SSL3_ST_CW_CERT_B; | |||||
} | |||||
} | } | ||||
if (ssl->s3->tmp.message_type != SSL3_MT_CERTIFICATE_STATUS) { | |||||
/* A server may send status_request in ServerHello and then change | |||||
* its mind about sending CertificateStatus. */ | |||||
ssl->s3->tmp.reuse_message = 1; | |||||
return 1; | |||||
} | |||||
if (ssl->state == SSL3_ST_CW_CERT_B) { | |||||
/* Call client_cert_cb to update the certificate. */ | |||||
X509 *x509 = NULL; | |||||
EVP_PKEY *pkey = NULL; | |||||
int ret = ssl_do_client_cert_cb(ssl, &x509, &pkey); | |||||
if (ret < 0) { | |||||
ssl->rwstate = SSL_X509_LOOKUP; | |||||
return -1; | |||||
} | |||||
CBS_init(&certificate_status, ssl->init_msg, n); | |||||
if (!CBS_get_u8(&certificate_status, &status_type) || | |||||
status_type != TLSEXT_STATUSTYPE_ocsp || | |||||
!CBS_get_u24_length_prefixed(&certificate_status, &ocsp_response) || | |||||
CBS_len(&ocsp_response) == 0 || | |||||
CBS_len(&certificate_status) != 0) { | |||||
al = SSL_AD_DECODE_ERROR; | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); | |||||
goto f_err; | |||||
} | |||||
int setup_error = ret == 1 && (!SSL_use_certificate(ssl, x509) || | |||||
!SSL_use_PrivateKey(ssl, pkey)); | |||||
X509_free(x509); | |||||
EVP_PKEY_free(pkey); | |||||
if (setup_error) { | |||||
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); | |||||
return -1; | |||||
} | |||||
if (!CBS_stow(&ocsp_response, &ssl->session->ocsp_response, | |||||
&ssl->session->ocsp_response_length)) { | |||||
al = SSL_AD_INTERNAL_ERROR; | |||||
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); | |||||
goto f_err; | |||||
ssl->state = SSL3_ST_CW_CERT_C; | |||||
} | } | ||||
return 1; | |||||
f_err: | |||||
ssl3_send_alert(ssl, SSL3_AL_FATAL, al); | |||||
return -1; | |||||
} | |||||
static int ssl3_get_server_done(SSL *ssl) { | |||||
int ok; | |||||
long n; | |||||
n = ssl->method->ssl_get_message(ssl, SSL3_MT_SERVER_DONE, ssl_hash_message, | |||||
&ok); | |||||
if (ssl->state == SSL3_ST_CW_CERT_C) { | |||||
if (!ssl3_has_client_certificate(ssl)) { | |||||
/* Without a client certificate, the handshake buffer may be released. */ | |||||
ssl3_free_handshake_buffer(ssl); | |||||
if (!ok) { | |||||
return n; | |||||
} | |||||
if (ssl->version == SSL3_VERSION) { | |||||
/* In SSL 3.0, send no certificate by skipping both messages. */ | |||||
ssl->s3->tmp.cert_req = 0; | |||||
ssl3_send_alert(ssl, SSL3_AL_WARNING, SSL_AD_NO_CERTIFICATE); | |||||
return 1; | |||||
} | |||||
if (n > 0) { | |||||
/* should contain no data */ | |||||
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_LENGTH_MISMATCH); | |||||
return -1; | |||||
/* In TLS, send an empty Certificate message. */ | |||||
ssl->s3->tmp.cert_req = 2; | |||||
uint8_t *p = ssl_handshake_start(ssl); | |||||
l2n3(0, p); | |||||
if (!ssl_set_handshake_header(ssl, SSL3_MT_CERTIFICATE, 3)) { | |||||
return -1; | |||||
} | |||||
} else if (!ssl3_output_cert_chain(ssl)) { | |||||
return -1; | |||||
} | |||||
ssl->state = SSL3_ST_CW_CERT_D; | |||||
} | } | ||||
return 1; | |||||
assert(ssl->state == SSL3_ST_CW_CERT_D); | |||||
return ssl_do_write(ssl); | |||||
} | } | ||||
OPENSSL_COMPILE_ASSERT(sizeof(size_t) >= sizeof(unsigned), | OPENSSL_COMPILE_ASSERT(sizeof(size_t) >= sizeof(unsigned), | ||||
@@ -1887,85 +1921,6 @@ err: | |||||
return -1; | return -1; | ||||
} | } | ||||
/* ssl3_has_client_certificate returns true if a client certificate is | |||||
* configured. */ | |||||
static int ssl3_has_client_certificate(SSL *ssl) { | |||||
return ssl->cert && ssl->cert->x509 && ssl_has_private_key(ssl); | |||||
} | |||||
static int ssl3_send_client_certificate(SSL *ssl) { | |||||
if (ssl->state == SSL3_ST_CW_CERT_A) { | |||||
/* Call cert_cb to update the certificate. */ | |||||
if (ssl->cert->cert_cb) { | |||||
int ret = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg); | |||||
if (ret < 0) { | |||||
ssl->rwstate = SSL_X509_LOOKUP; | |||||
return -1; | |||||
} | |||||
if (ret == 0) { | |||||
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); | |||||
return -1; | |||||
} | |||||
} | |||||
if (ssl3_has_client_certificate(ssl)) { | |||||
ssl->state = SSL3_ST_CW_CERT_C; | |||||
} else { | |||||
ssl->state = SSL3_ST_CW_CERT_B; | |||||
} | |||||
} | |||||
if (ssl->state == SSL3_ST_CW_CERT_B) { | |||||
/* Call client_cert_cb to update the certificate. */ | |||||
X509 *x509 = NULL; | |||||
EVP_PKEY *pkey = NULL; | |||||
int ret = ssl_do_client_cert_cb(ssl, &x509, &pkey); | |||||
if (ret < 0) { | |||||
ssl->rwstate = SSL_X509_LOOKUP; | |||||
return -1; | |||||
} | |||||
int setup_error = ret == 1 && (!SSL_use_certificate(ssl, x509) || | |||||
!SSL_use_PrivateKey(ssl, pkey)); | |||||
X509_free(x509); | |||||
EVP_PKEY_free(pkey); | |||||
if (setup_error) { | |||||
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); | |||||
return -1; | |||||
} | |||||
ssl->state = SSL3_ST_CW_CERT_C; | |||||
} | |||||
if (ssl->state == SSL3_ST_CW_CERT_C) { | |||||
if (!ssl3_has_client_certificate(ssl)) { | |||||
/* Without a client certificate, the handshake buffer may be released. */ | |||||
ssl3_free_handshake_buffer(ssl); | |||||
if (ssl->version == SSL3_VERSION) { | |||||
/* In SSL 3.0, send no certificate by skipping both messages. */ | |||||
ssl->s3->tmp.cert_req = 0; | |||||
ssl3_send_alert(ssl, SSL3_AL_WARNING, SSL_AD_NO_CERTIFICATE); | |||||
return 1; | |||||
} | |||||
/* In TLS, send an empty Certificate message. */ | |||||
ssl->s3->tmp.cert_req = 2; | |||||
uint8_t *p = ssl_handshake_start(ssl); | |||||
l2n3(0, p); | |||||
if (!ssl_set_handshake_header(ssl, SSL3_MT_CERTIFICATE, 3)) { | |||||
return -1; | |||||
} | |||||
} else if (!ssl3_output_cert_chain(ssl)) { | |||||
return -1; | |||||
} | |||||
ssl->state = SSL3_ST_CW_CERT_D; | |||||
} | |||||
assert(ssl->state == SSL3_ST_CW_CERT_D); | |||||
return ssl_do_write(ssl); | |||||
} | |||||
static int ssl3_send_next_proto(SSL *ssl) { | static int ssl3_send_next_proto(SSL *ssl) { | ||||
if (ssl->state == SSL3_ST_CW_NEXT_PROTO_B) { | if (ssl->state == SSL3_ST_CW_NEXT_PROTO_B) { | ||||
return ssl_do_write(ssl); | return ssl_do_write(ssl); | ||||
@@ -2078,32 +2033,75 @@ err: | |||||
return ret; | return ret; | ||||
} | } | ||||
static int ssl_do_client_cert_cb(SSL *ssl, X509 **out_x509, | |||||
EVP_PKEY **out_pkey) { | |||||
if (ssl->ctx->client_cert_cb == NULL) { | |||||
return 0; | |||||
static int ssl3_get_new_session_ticket(SSL *ssl) { | |||||
int ok, al; | |||||
long n = ssl->method->ssl_get_message(ssl, SSL3_MT_NEW_SESSION_TICKET, | |||||
ssl_hash_message, &ok); | |||||
if (!ok) { | |||||
return n; | |||||
} | } | ||||
int ret = ssl->ctx->client_cert_cb(ssl, out_x509, out_pkey); | |||||
if (ret <= 0) { | |||||
return ret; | |||||
CBS new_session_ticket, ticket; | |||||
uint32_t ticket_lifetime_hint; | |||||
CBS_init(&new_session_ticket, ssl->init_msg, n); | |||||
if (!CBS_get_u32(&new_session_ticket, &ticket_lifetime_hint) || | |||||
!CBS_get_u16_length_prefixed(&new_session_ticket, &ticket) || | |||||
CBS_len(&new_session_ticket) != 0) { | |||||
al = SSL_AD_DECODE_ERROR; | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); | |||||
goto f_err; | |||||
} | } | ||||
assert(*out_x509 != NULL); | |||||
assert(*out_pkey != NULL); | |||||
return 1; | |||||
} | |||||
if (CBS_len(&ticket) == 0) { | |||||
/* RFC 5077 allows a server to change its mind and send no ticket after | |||||
* negotiating the extension. The value of |tlsext_ticket_expected| is | |||||
* checked in |ssl_update_cache| so is cleared here to avoid an unnecessary | |||||
* update. */ | |||||
ssl->tlsext_ticket_expected = 0; | |||||
return 1; | |||||
} | |||||
static int ssl3_verify_server_cert(SSL *ssl) { | |||||
int ret = ssl_verify_cert_chain(ssl, ssl->session->cert_chain); | |||||
if (ssl->verify_mode != SSL_VERIFY_NONE && ret <= 0) { | |||||
int al = ssl_verify_alarm_type(ssl->verify_result); | |||||
ssl3_send_alert(ssl, SSL3_AL_FATAL, al); | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_CERTIFICATE_VERIFY_FAILED); | |||||
} else { | |||||
ret = 1; | |||||
ERR_clear_error(); /* but we keep ssl->verify_result */ | |||||
if (ssl->hit) { | |||||
/* The server is sending a new ticket for an existing session. Sessions are | |||||
* immutable once established, so duplicate all but the ticket of the | |||||
* existing session. */ | |||||
uint8_t *bytes; | |||||
size_t bytes_len; | |||||
if (!SSL_SESSION_to_bytes_for_ticket(ssl->session, &bytes, &bytes_len)) { | |||||
goto err; | |||||
} | |||||
SSL_SESSION *new_session = SSL_SESSION_from_bytes(bytes, bytes_len); | |||||
OPENSSL_free(bytes); | |||||
if (new_session == NULL) { | |||||
/* This should never happen. */ | |||||
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); | |||||
goto err; | |||||
} | |||||
SSL_SESSION_free(ssl->session); | |||||
ssl->session = new_session; | |||||
} | } | ||||
return ret; | |||||
if (!CBS_stow(&ticket, &ssl->session->tlsext_tick, | |||||
&ssl->session->tlsext_ticklen)) { | |||||
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); | |||||
goto err; | |||||
} | |||||
ssl->session->tlsext_tick_lifetime_hint = ticket_lifetime_hint; | |||||
/* Generate a session ID for this session based on the session ticket. We use | |||||
* the session ID mechanism for detecting ticket resumption. This also fits in | |||||
* with assumptions elsewhere in OpenSSL.*/ | |||||
if (!EVP_Digest(CBS_data(&ticket), CBS_len(&ticket), ssl->session->session_id, | |||||
&ssl->session->session_id_length, EVP_sha256(), NULL)) { | |||||
goto err; | |||||
} | |||||
return 1; | |||||
f_err: | |||||
ssl3_send_alert(ssl, SSL3_AL_FATAL, al); | |||||
err: | |||||
return -1; | |||||
} | } |
@@ -63,8 +63,6 @@ static const SSL_PROTOCOL_METHOD TLS_protocol_method = { | |||||
0 /* is_dtls */, | 0 /* is_dtls */, | ||||
ssl3_new, | ssl3_new, | ||||
ssl3_free, | ssl3_free, | ||||
ssl3_accept, | |||||
ssl3_connect, | |||||
ssl3_get_message, | ssl3_get_message, | ||||
ssl3_read_app_data, | ssl3_read_app_data, | ||||
ssl3_read_change_cipher_spec, | ssl3_read_change_cipher_spec, | ||||
@@ -177,17 +177,17 @@ static int ssl3_get_initial_bytes(SSL *ssl); | |||||
static int ssl3_get_v2_client_hello(SSL *ssl); | static int ssl3_get_v2_client_hello(SSL *ssl); | ||||
static int ssl3_get_client_hello(SSL *ssl); | static int ssl3_get_client_hello(SSL *ssl); | ||||
static int ssl3_send_server_hello(SSL *ssl); | static int ssl3_send_server_hello(SSL *ssl); | ||||
static int ssl3_send_server_certificate(SSL *ssl); | |||||
static int ssl3_send_certificate_status(SSL *ssl); | static int ssl3_send_certificate_status(SSL *ssl); | ||||
static int ssl3_send_server_done(SSL *ssl); | |||||
static int ssl3_send_server_key_exchange(SSL *ssl); | static int ssl3_send_server_key_exchange(SSL *ssl); | ||||
static int ssl3_send_certificate_request(SSL *ssl); | static int ssl3_send_certificate_request(SSL *ssl); | ||||
static int ssl3_send_server_done(SSL *ssl); | |||||
static int ssl3_get_client_certificate(SSL *ssl); | |||||
static int ssl3_get_client_key_exchange(SSL *ssl); | static int ssl3_get_client_key_exchange(SSL *ssl); | ||||
static int ssl3_get_cert_verify(SSL *ssl); | static int ssl3_get_cert_verify(SSL *ssl); | ||||
static int ssl3_get_client_certificate(SSL *ssl); | |||||
static int ssl3_send_server_certificate(SSL *ssl); | |||||
static int ssl3_send_new_session_ticket(SSL *ssl); | |||||
static int ssl3_get_next_proto(SSL *ssl); | static int ssl3_get_next_proto(SSL *ssl); | ||||
static int ssl3_get_channel_id(SSL *ssl); | static int ssl3_get_channel_id(SSL *ssl); | ||||
static int ssl3_send_new_session_ticket(SSL *ssl); | |||||
int ssl3_accept(SSL *ssl) { | int ssl3_accept(SSL *ssl) { | ||||
BUF_MEM *buf = NULL; | BUF_MEM *buf = NULL; | ||||
@@ -352,19 +352,6 @@ int ssl3_accept(SSL *ssl) { | |||||
ssl->state = SSL3_ST_SW_FLUSH; | ssl->state = SSL3_ST_SW_FLUSH; | ||||
break; | break; | ||||
case SSL3_ST_SW_FLUSH: | |||||
if (BIO_flush(ssl->wbio) <= 0) { | |||||
ssl->rwstate = SSL_WRITING; | |||||
ret = -1; | |||||
goto end; | |||||
} | |||||
ssl->state = ssl->s3->tmp.next_state; | |||||
if (ssl->state != SSL_ST_OK) { | |||||
ssl->method->expect_flight(ssl); | |||||
} | |||||
break; | |||||
case SSL3_ST_SR_CERT_A: | case SSL3_ST_SR_CERT_A: | ||||
if (ssl->s3->tmp.cert_request) { | if (ssl->s3->tmp.cert_request) { | ||||
ret = ssl3_get_client_certificate(ssl); | ret = ssl3_get_client_certificate(ssl); | ||||
@@ -498,6 +485,19 @@ int ssl3_accept(SSL *ssl) { | |||||
} | } | ||||
break; | break; | ||||
case SSL3_ST_SW_FLUSH: | |||||
if (BIO_flush(ssl->wbio) <= 0) { | |||||
ssl->rwstate = SSL_WRITING; | |||||
ret = -1; | |||||
goto end; | |||||
} | |||||
ssl->state = ssl->s3->tmp.next_state; | |||||
if (ssl->state != SSL_ST_OK) { | |||||
ssl->method->expect_flight(ssl); | |||||
} | |||||
break; | |||||
case SSL_ST_OK: | case SSL_ST_OK: | ||||
/* clean a few things up */ | /* clean a few things up */ | ||||
ssl3_cleanup_key_block(ssl); | ssl3_cleanup_key_block(ssl); | ||||
@@ -1121,6 +1121,18 @@ static int ssl3_send_server_hello(SSL *ssl) { | |||||
return ssl_do_write(ssl); | return ssl_do_write(ssl); | ||||
} | } | ||||
static int ssl3_send_server_certificate(SSL *ssl) { | |||||
if (ssl->state == SSL3_ST_SW_CERT_A) { | |||||
if (!ssl3_output_cert_chain(ssl)) { | |||||
return 0; | |||||
} | |||||
ssl->state = SSL3_ST_SW_CERT_B; | |||||
} | |||||
/* SSL3_ST_SW_CERT_B */ | |||||
return ssl_do_write(ssl); | |||||
} | |||||
static int ssl3_send_certificate_status(SSL *ssl) { | static int ssl3_send_certificate_status(SSL *ssl) { | ||||
if (ssl->state == SSL3_ST_SW_CERT_STATUS_A) { | if (ssl->state == SSL3_ST_SW_CERT_STATUS_A) { | ||||
CBB out, ocsp_response; | CBB out, ocsp_response; | ||||
@@ -1147,18 +1159,6 @@ static int ssl3_send_certificate_status(SSL *ssl) { | |||||
return ssl_do_write(ssl); | return ssl_do_write(ssl); | ||||
} | } | ||||
static int ssl3_send_server_done(SSL *ssl) { | |||||
if (ssl->state == SSL3_ST_SW_SRVR_DONE_A) { | |||||
if (!ssl_set_handshake_header(ssl, SSL3_MT_SERVER_DONE, 0)) { | |||||
return -1; | |||||
} | |||||
ssl->state = SSL3_ST_SW_SRVR_DONE_B; | |||||
} | |||||
/* SSL3_ST_SW_SRVR_DONE_B */ | |||||
return ssl_do_write(ssl); | |||||
} | |||||
static int ssl3_send_server_key_exchange(SSL *ssl) { | static int ssl3_send_server_key_exchange(SSL *ssl) { | ||||
if (ssl->state == SSL3_ST_SW_KEY_EXCH_C) { | if (ssl->state == SSL3_ST_SW_KEY_EXCH_C) { | ||||
return ssl_do_write(ssl); | return ssl_do_write(ssl); | ||||
@@ -1415,6 +1415,157 @@ err: | |||||
return -1; | return -1; | ||||
} | } | ||||
static int ssl3_send_server_done(SSL *ssl) { | |||||
if (ssl->state == SSL3_ST_SW_SRVR_DONE_A) { | |||||
if (!ssl_set_handshake_header(ssl, SSL3_MT_SERVER_DONE, 0)) { | |||||
return -1; | |||||
} | |||||
ssl->state = SSL3_ST_SW_SRVR_DONE_B; | |||||
} | |||||
/* SSL3_ST_SW_SRVR_DONE_B */ | |||||
return ssl_do_write(ssl); | |||||
} | |||||
static int ssl3_get_client_certificate(SSL *ssl) { | |||||
int ok, al, ret = -1; | |||||
X509 *x = NULL; | |||||
unsigned long n; | |||||
STACK_OF(X509) *sk = NULL; | |||||
SHA256_CTX sha256; | |||||
CBS certificate_msg, certificate_list; | |||||
int is_first_certificate = 1; | |||||
assert(ssl->s3->tmp.cert_request); | |||||
n = ssl->method->ssl_get_message(ssl, -1, ssl_hash_message, &ok); | |||||
if (!ok) { | |||||
return n; | |||||
} | |||||
if (ssl->s3->tmp.message_type != SSL3_MT_CERTIFICATE) { | |||||
if (ssl->version == SSL3_VERSION && | |||||
ssl->s3->tmp.message_type == SSL3_MT_CLIENT_KEY_EXCHANGE) { | |||||
/* In SSL 3.0, the Certificate message is omitted to signal no certificate. */ | |||||
if ((ssl->verify_mode & SSL_VERIFY_PEER) && | |||||
(ssl->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) { | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE); | |||||
al = SSL_AD_HANDSHAKE_FAILURE; | |||||
goto f_err; | |||||
} | |||||
ssl->s3->tmp.reuse_message = 1; | |||||
return 1; | |||||
} | |||||
al = SSL_AD_UNEXPECTED_MESSAGE; | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE); | |||||
goto f_err; | |||||
} | |||||
CBS_init(&certificate_msg, ssl->init_msg, n); | |||||
sk = sk_X509_new_null(); | |||||
if (sk == NULL) { | |||||
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); | |||||
goto err; | |||||
} | |||||
if (!CBS_get_u24_length_prefixed(&certificate_msg, &certificate_list) || | |||||
CBS_len(&certificate_msg) != 0) { | |||||
al = SSL_AD_DECODE_ERROR; | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); | |||||
goto f_err; | |||||
} | |||||
while (CBS_len(&certificate_list) > 0) { | |||||
CBS certificate; | |||||
const uint8_t *data; | |||||
if (!CBS_get_u24_length_prefixed(&certificate_list, &certificate)) { | |||||
al = SSL_AD_DECODE_ERROR; | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); | |||||
goto f_err; | |||||
} | |||||
if (is_first_certificate && ssl->ctx->retain_only_sha256_of_client_certs) { | |||||
/* If this is the first certificate, and we don't want to keep peer | |||||
* certificates in memory, then we hash it right away. */ | |||||
SHA256_Init(&sha256); | |||||
SHA256_Update(&sha256, CBS_data(&certificate), CBS_len(&certificate)); | |||||
SHA256_Final(ssl->session->peer_sha256, &sha256); | |||||
ssl->session->peer_sha256_valid = 1; | |||||
} | |||||
is_first_certificate = 0; | |||||
/* A u24 length cannot overflow a long. */ | |||||
data = CBS_data(&certificate); | |||||
x = d2i_X509(NULL, &data, (long)CBS_len(&certificate)); | |||||
if (x == NULL) { | |||||
al = SSL_AD_BAD_CERTIFICATE; | |||||
OPENSSL_PUT_ERROR(SSL, ERR_R_ASN1_LIB); | |||||
goto f_err; | |||||
} | |||||
if (data != CBS_data(&certificate) + CBS_len(&certificate)) { | |||||
al = SSL_AD_DECODE_ERROR; | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_LENGTH_MISMATCH); | |||||
goto f_err; | |||||
} | |||||
if (!sk_X509_push(sk, x)) { | |||||
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); | |||||
goto err; | |||||
} | |||||
x = NULL; | |||||
} | |||||
if (sk_X509_num(sk) <= 0) { | |||||
/* No client certificate so the handshake buffer may be discarded. */ | |||||
ssl3_free_handshake_buffer(ssl); | |||||
/* TLS does not mind 0 certs returned */ | |||||
if (ssl->version == SSL3_VERSION) { | |||||
al = SSL_AD_HANDSHAKE_FAILURE; | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATES_RETURNED); | |||||
goto f_err; | |||||
} else if ((ssl->verify_mode & SSL_VERIFY_PEER) && | |||||
(ssl->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) { | |||||
/* Fail for TLS only if we required a certificate */ | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE); | |||||
al = SSL_AD_HANDSHAKE_FAILURE; | |||||
goto f_err; | |||||
} | |||||
} else { | |||||
if (ssl_verify_cert_chain(ssl, sk) <= 0) { | |||||
al = ssl_verify_alarm_type(ssl->verify_result); | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_CERTIFICATE_VERIFY_FAILED); | |||||
goto f_err; | |||||
} | |||||
} | |||||
X509_free(ssl->session->peer); | |||||
ssl->session->peer = sk_X509_shift(sk); | |||||
ssl->session->verify_result = ssl->verify_result; | |||||
sk_X509_pop_free(ssl->session->cert_chain, X509_free); | |||||
ssl->session->cert_chain = sk; | |||||
/* Inconsistency alert: cert_chain does *not* include the peer's own | |||||
* certificate, while we do include it in s3_clnt.c */ | |||||
sk = NULL; | |||||
ret = 1; | |||||
if (0) { | |||||
f_err: | |||||
ssl3_send_alert(ssl, SSL3_AL_FATAL, al); | |||||
} | |||||
err: | |||||
X509_free(x); | |||||
sk_X509_pop_free(sk, X509_free); | |||||
return ret; | |||||
} | |||||
static int ssl3_get_client_key_exchange(SSL *ssl) { | static int ssl3_get_client_key_exchange(SSL *ssl) { | ||||
int al; | int al; | ||||
CBS client_key_exchange; | CBS client_key_exchange; | ||||
@@ -1790,155 +1941,155 @@ err: | |||||
return ret; | return ret; | ||||
} | } | ||||
static int ssl3_get_client_certificate(SSL *ssl) { | |||||
int ok, al, ret = -1; | |||||
X509 *x = NULL; | |||||
unsigned long n; | |||||
STACK_OF(X509) *sk = NULL; | |||||
SHA256_CTX sha256; | |||||
CBS certificate_msg, certificate_list; | |||||
int is_first_certificate = 1; | |||||
assert(ssl->s3->tmp.cert_request); | |||||
n = ssl->method->ssl_get_message(ssl, -1, ssl_hash_message, &ok); | |||||
/* ssl3_get_next_proto reads a Next Protocol Negotiation handshake message. It | |||||
* sets the next_proto member in s if found */ | |||||
static int ssl3_get_next_proto(SSL *ssl) { | |||||
int ok; | |||||
long n; | |||||
CBS next_protocol, selected_protocol, padding; | |||||
if (!ok) { | |||||
return n; | |||||
/* Clients cannot send a NextProtocol message if we didn't see the extension | |||||
* in their ClientHello */ | |||||
if (!ssl->s3->next_proto_neg_seen) { | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_GOT_NEXT_PROTO_WITHOUT_EXTENSION); | |||||
return -1; | |||||
} | } | ||||
if (ssl->s3->tmp.message_type != SSL3_MT_CERTIFICATE) { | |||||
if (ssl->version == SSL3_VERSION && | |||||
ssl->s3->tmp.message_type == SSL3_MT_CLIENT_KEY_EXCHANGE) { | |||||
/* In SSL 3.0, the Certificate message is omitted to signal no certificate. */ | |||||
if ((ssl->verify_mode & SSL_VERIFY_PEER) && | |||||
(ssl->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) { | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE); | |||||
al = SSL_AD_HANDSHAKE_FAILURE; | |||||
goto f_err; | |||||
} | |||||
ssl->s3->tmp.reuse_message = 1; | |||||
return 1; | |||||
} | |||||
n = ssl->method->ssl_get_message(ssl, SSL3_MT_NEXT_PROTO, ssl_hash_message, | |||||
&ok); | |||||
al = SSL_AD_UNEXPECTED_MESSAGE; | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE); | |||||
goto f_err; | |||||
if (!ok) { | |||||
return n; | |||||
} | } | ||||
CBS_init(&certificate_msg, ssl->init_msg, n); | |||||
CBS_init(&next_protocol, ssl->init_msg, n); | |||||
sk = sk_X509_new_null(); | |||||
if (sk == NULL) { | |||||
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); | |||||
goto err; | |||||
} | |||||
if (!CBS_get_u24_length_prefixed(&certificate_msg, &certificate_list) || | |||||
CBS_len(&certificate_msg) != 0) { | |||||
al = SSL_AD_DECODE_ERROR; | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); | |||||
goto f_err; | |||||
/* The payload looks like: | |||||
* uint8 proto_len; | |||||
* uint8 proto[proto_len]; | |||||
* uint8 padding_len; | |||||
* uint8 padding[padding_len]; */ | |||||
if (!CBS_get_u8_length_prefixed(&next_protocol, &selected_protocol) || | |||||
!CBS_get_u8_length_prefixed(&next_protocol, &padding) || | |||||
CBS_len(&next_protocol) != 0 || | |||||
!CBS_stow(&selected_protocol, &ssl->s3->next_proto_negotiated, | |||||
&ssl->s3->next_proto_negotiated_len)) { | |||||
return 0; | |||||
} | } | ||||
while (CBS_len(&certificate_list) > 0) { | |||||
CBS certificate; | |||||
const uint8_t *data; | |||||
return 1; | |||||
} | |||||
if (!CBS_get_u24_length_prefixed(&certificate_list, &certificate)) { | |||||
al = SSL_AD_DECODE_ERROR; | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); | |||||
goto f_err; | |||||
} | |||||
/* ssl3_get_channel_id reads and verifies a ClientID handshake message. */ | |||||
static int ssl3_get_channel_id(SSL *ssl) { | |||||
int ret = -1, ok; | |||||
long n; | |||||
uint8_t channel_id_hash[EVP_MAX_MD_SIZE]; | |||||
size_t channel_id_hash_len; | |||||
const uint8_t *p; | |||||
uint16_t extension_type; | |||||
EC_GROUP *p256 = NULL; | |||||
EC_KEY *key = NULL; | |||||
EC_POINT *point = NULL; | |||||
ECDSA_SIG sig; | |||||
BIGNUM x, y; | |||||
CBS encrypted_extensions, extension; | |||||
if (is_first_certificate && ssl->ctx->retain_only_sha256_of_client_certs) { | |||||
/* If this is the first certificate, and we don't want to keep peer | |||||
* certificates in memory, then we hash it right away. */ | |||||
SHA256_Init(&sha256); | |||||
SHA256_Update(&sha256, CBS_data(&certificate), CBS_len(&certificate)); | |||||
SHA256_Final(ssl->session->peer_sha256, &sha256); | |||||
ssl->session->peer_sha256_valid = 1; | |||||
} | |||||
is_first_certificate = 0; | |||||
n = ssl->method->ssl_get_message(ssl, SSL3_MT_CHANNEL_ID_ENCRYPTED_EXTENSIONS, | |||||
ssl_dont_hash_message, &ok); | |||||
/* A u24 length cannot overflow a long. */ | |||||
data = CBS_data(&certificate); | |||||
x = d2i_X509(NULL, &data, (long)CBS_len(&certificate)); | |||||
if (x == NULL) { | |||||
al = SSL_AD_BAD_CERTIFICATE; | |||||
OPENSSL_PUT_ERROR(SSL, ERR_R_ASN1_LIB); | |||||
goto f_err; | |||||
} | |||||
if (data != CBS_data(&certificate) + CBS_len(&certificate)) { | |||||
al = SSL_AD_DECODE_ERROR; | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_LENGTH_MISMATCH); | |||||
goto f_err; | |||||
} | |||||
if (!sk_X509_push(sk, x)) { | |||||
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); | |||||
goto err; | |||||
} | |||||
x = NULL; | |||||
if (!ok) { | |||||
return n; | |||||
} | } | ||||
if (sk_X509_num(sk) <= 0) { | |||||
/* No client certificate so the handshake buffer may be discarded. */ | |||||
ssl3_free_handshake_buffer(ssl); | |||||
/* Before incorporating the EncryptedExtensions message to the handshake | |||||
* hash, compute the hash that should have been signed. */ | |||||
if (!tls1_channel_id_hash(ssl, channel_id_hash, &channel_id_hash_len)) { | |||||
return -1; | |||||
} | |||||
assert(channel_id_hash_len == SHA256_DIGEST_LENGTH); | |||||
/* TLS does not mind 0 certs returned */ | |||||
if (ssl->version == SSL3_VERSION) { | |||||
al = SSL_AD_HANDSHAKE_FAILURE; | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATES_RETURNED); | |||||
goto f_err; | |||||
} else if ((ssl->verify_mode & SSL_VERIFY_PEER) && | |||||
(ssl->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) { | |||||
/* Fail for TLS only if we required a certificate */ | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE); | |||||
al = SSL_AD_HANDSHAKE_FAILURE; | |||||
goto f_err; | |||||
} | |||||
} else { | |||||
if (ssl_verify_cert_chain(ssl, sk) <= 0) { | |||||
al = ssl_verify_alarm_type(ssl->verify_result); | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_CERTIFICATE_VERIFY_FAILED); | |||||
goto f_err; | |||||
} | |||||
if (!ssl3_hash_current_message(ssl)) { | |||||
return -1; | |||||
} | } | ||||
X509_free(ssl->session->peer); | |||||
ssl->session->peer = sk_X509_shift(sk); | |||||
ssl->session->verify_result = ssl->verify_result; | |||||
CBS_init(&encrypted_extensions, ssl->init_msg, n); | |||||
sk_X509_pop_free(ssl->session->cert_chain, X509_free); | |||||
ssl->session->cert_chain = sk; | |||||
/* Inconsistency alert: cert_chain does *not* include the peer's own | |||||
* certificate, while we do include it in s3_clnt.c */ | |||||
/* EncryptedExtensions could include multiple extensions, but the only | |||||
* extension that could be negotiated is ChannelID, so there can only be one | |||||
* entry. | |||||
* | |||||
* The payload looks like: | |||||
* uint16 extension_type | |||||
* uint16 extension_len; | |||||
* uint8 x[32]; | |||||
* uint8 y[32]; | |||||
* uint8 r[32]; | |||||
* uint8 s[32]; */ | |||||
sk = NULL; | |||||
if (!CBS_get_u16(&encrypted_extensions, &extension_type) || | |||||
!CBS_get_u16_length_prefixed(&encrypted_extensions, &extension) || | |||||
CBS_len(&encrypted_extensions) != 0 || | |||||
extension_type != TLSEXT_TYPE_channel_id || | |||||
CBS_len(&extension) != TLSEXT_CHANNEL_ID_SIZE) { | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_MESSAGE); | |||||
return -1; | |||||
} | |||||
ret = 1; | |||||
p256 = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); | |||||
if (!p256) { | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_NO_P256_SUPPORT); | |||||
return -1; | |||||
} | |||||
if (0) { | |||||
f_err: | |||||
ssl3_send_alert(ssl, SSL3_AL_FATAL, al); | |||||
BN_init(&x); | |||||
BN_init(&y); | |||||
sig.r = BN_new(); | |||||
sig.s = BN_new(); | |||||
if (sig.r == NULL || sig.s == NULL) { | |||||
goto err; | |||||
} | } | ||||
err: | |||||
X509_free(x); | |||||
sk_X509_pop_free(sk, X509_free); | |||||
return ret; | |||||
} | |||||
p = CBS_data(&extension); | |||||
if (BN_bin2bn(p + 0, 32, &x) == NULL || | |||||
BN_bin2bn(p + 32, 32, &y) == NULL || | |||||
BN_bin2bn(p + 64, 32, sig.r) == NULL || | |||||
BN_bin2bn(p + 96, 32, sig.s) == NULL) { | |||||
goto err; | |||||
} | |||||
static int ssl3_send_server_certificate(SSL *ssl) { | |||||
if (ssl->state == SSL3_ST_SW_CERT_A) { | |||||
if (!ssl3_output_cert_chain(ssl)) { | |||||
return 0; | |||||
} | |||||
ssl->state = SSL3_ST_SW_CERT_B; | |||||
point = EC_POINT_new(p256); | |||||
if (!point || | |||||
!EC_POINT_set_affine_coordinates_GFp(p256, point, &x, &y, NULL)) { | |||||
goto err; | |||||
} | } | ||||
/* SSL3_ST_SW_CERT_B */ | |||||
return ssl_do_write(ssl); | |||||
key = EC_KEY_new(); | |||||
if (!key || !EC_KEY_set_group(key, p256) || | |||||
!EC_KEY_set_public_key(key, point)) { | |||||
goto err; | |||||
} | |||||
/* We stored the handshake hash in |tlsext_channel_id| the first time that we | |||||
* were called. */ | |||||
if (!ECDSA_do_verify(channel_id_hash, channel_id_hash_len, &sig, key)) { | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_CHANNEL_ID_SIGNATURE_INVALID); | |||||
ssl->s3->tlsext_channel_id_valid = 0; | |||||
goto err; | |||||
} | |||||
memcpy(ssl->s3->tlsext_channel_id, p, 64); | |||||
ret = 1; | |||||
err: | |||||
BN_free(&x); | |||||
BN_free(&y); | |||||
BN_free(sig.r); | |||||
BN_free(sig.s); | |||||
EC_KEY_free(key); | |||||
EC_POINT_free(point); | |||||
EC_GROUP_free(p256); | |||||
return ret; | |||||
} | } | ||||
/* send a new session ticket (not necessarily for a new session) */ | /* send a new session ticket (not necessarily for a new session) */ | ||||
@@ -2072,154 +2223,3 @@ err: | |||||
HMAC_CTX_cleanup(&hctx); | HMAC_CTX_cleanup(&hctx); | ||||
return ret; | return ret; | ||||
} | } | ||||
/* ssl3_get_next_proto reads a Next Protocol Negotiation handshake message. It | |||||
* sets the next_proto member in s if found */ | |||||
static int ssl3_get_next_proto(SSL *ssl) { | |||||
int ok; | |||||
long n; | |||||
CBS next_protocol, selected_protocol, padding; | |||||
/* Clients cannot send a NextProtocol message if we didn't see the extension | |||||
* in their ClientHello */ | |||||
if (!ssl->s3->next_proto_neg_seen) { | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_GOT_NEXT_PROTO_WITHOUT_EXTENSION); | |||||
return -1; | |||||
} | |||||
n = ssl->method->ssl_get_message(ssl, SSL3_MT_NEXT_PROTO, ssl_hash_message, | |||||
&ok); | |||||
if (!ok) { | |||||
return n; | |||||
} | |||||
CBS_init(&next_protocol, ssl->init_msg, n); | |||||
/* The payload looks like: | |||||
* uint8 proto_len; | |||||
* uint8 proto[proto_len]; | |||||
* uint8 padding_len; | |||||
* uint8 padding[padding_len]; */ | |||||
if (!CBS_get_u8_length_prefixed(&next_protocol, &selected_protocol) || | |||||
!CBS_get_u8_length_prefixed(&next_protocol, &padding) || | |||||
CBS_len(&next_protocol) != 0 || | |||||
!CBS_stow(&selected_protocol, &ssl->s3->next_proto_negotiated, | |||||
&ssl->s3->next_proto_negotiated_len)) { | |||||
return 0; | |||||
} | |||||
return 1; | |||||
} | |||||
/* ssl3_get_channel_id reads and verifies a ClientID handshake message. */ | |||||
static int ssl3_get_channel_id(SSL *ssl) { | |||||
int ret = -1, ok; | |||||
long n; | |||||
uint8_t channel_id_hash[EVP_MAX_MD_SIZE]; | |||||
size_t channel_id_hash_len; | |||||
const uint8_t *p; | |||||
uint16_t extension_type; | |||||
EC_GROUP *p256 = NULL; | |||||
EC_KEY *key = NULL; | |||||
EC_POINT *point = NULL; | |||||
ECDSA_SIG sig; | |||||
BIGNUM x, y; | |||||
CBS encrypted_extensions, extension; | |||||
n = ssl->method->ssl_get_message(ssl, SSL3_MT_CHANNEL_ID_ENCRYPTED_EXTENSIONS, | |||||
ssl_dont_hash_message, &ok); | |||||
if (!ok) { | |||||
return n; | |||||
} | |||||
/* Before incorporating the EncryptedExtensions message to the handshake | |||||
* hash, compute the hash that should have been signed. */ | |||||
if (!tls1_channel_id_hash(ssl, channel_id_hash, &channel_id_hash_len)) { | |||||
return -1; | |||||
} | |||||
assert(channel_id_hash_len == SHA256_DIGEST_LENGTH); | |||||
if (!ssl3_hash_current_message(ssl)) { | |||||
return -1; | |||||
} | |||||
CBS_init(&encrypted_extensions, ssl->init_msg, n); | |||||
/* EncryptedExtensions could include multiple extensions, but the only | |||||
* extension that could be negotiated is ChannelID, so there can only be one | |||||
* entry. | |||||
* | |||||
* The payload looks like: | |||||
* uint16 extension_type | |||||
* uint16 extension_len; | |||||
* uint8 x[32]; | |||||
* uint8 y[32]; | |||||
* uint8 r[32]; | |||||
* uint8 s[32]; */ | |||||
if (!CBS_get_u16(&encrypted_extensions, &extension_type) || | |||||
!CBS_get_u16_length_prefixed(&encrypted_extensions, &extension) || | |||||
CBS_len(&encrypted_extensions) != 0 || | |||||
extension_type != TLSEXT_TYPE_channel_id || | |||||
CBS_len(&extension) != TLSEXT_CHANNEL_ID_SIZE) { | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_MESSAGE); | |||||
return -1; | |||||
} | |||||
p256 = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); | |||||
if (!p256) { | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_NO_P256_SUPPORT); | |||||
return -1; | |||||
} | |||||
BN_init(&x); | |||||
BN_init(&y); | |||||
sig.r = BN_new(); | |||||
sig.s = BN_new(); | |||||
if (sig.r == NULL || sig.s == NULL) { | |||||
goto err; | |||||
} | |||||
p = CBS_data(&extension); | |||||
if (BN_bin2bn(p + 0, 32, &x) == NULL || | |||||
BN_bin2bn(p + 32, 32, &y) == NULL || | |||||
BN_bin2bn(p + 64, 32, sig.r) == NULL || | |||||
BN_bin2bn(p + 96, 32, sig.s) == NULL) { | |||||
goto err; | |||||
} | |||||
point = EC_POINT_new(p256); | |||||
if (!point || | |||||
!EC_POINT_set_affine_coordinates_GFp(p256, point, &x, &y, NULL)) { | |||||
goto err; | |||||
} | |||||
key = EC_KEY_new(); | |||||
if (!key || !EC_KEY_set_group(key, p256) || | |||||
!EC_KEY_set_public_key(key, point)) { | |||||
goto err; | |||||
} | |||||
/* We stored the handshake hash in |tlsext_channel_id| the first time that we | |||||
* were called. */ | |||||
if (!ECDSA_do_verify(channel_id_hash, channel_id_hash_len, &sig, key)) { | |||||
OPENSSL_PUT_ERROR(SSL, SSL_R_CHANNEL_ID_SIGNATURE_INVALID); | |||||
ssl->s3->tlsext_channel_id_valid = 0; | |||||
goto err; | |||||
} | |||||
memcpy(ssl->s3->tlsext_channel_id, p, 64); | |||||
ret = 1; | |||||
err: | |||||
BN_free(&x); | |||||
BN_free(&y); | |||||
BN_free(sig.r); | |||||
BN_free(sig.s); | |||||
EC_KEY_free(key); | |||||
EC_POINT_free(point); | |||||
EC_GROUP_free(p256); | |||||
return ret; | |||||
} |
@@ -514,13 +514,13 @@ void SSL_free(SSL *ssl) { | |||||
void SSL_set_connect_state(SSL *ssl) { | void SSL_set_connect_state(SSL *ssl) { | ||||
ssl->server = 0; | ssl->server = 0; | ||||
ssl->state = SSL_ST_CONNECT; | ssl->state = SSL_ST_CONNECT; | ||||
ssl->handshake_func = ssl->method->ssl_connect; | |||||
ssl->handshake_func = ssl3_connect; | |||||
} | } | ||||
void SSL_set_accept_state(SSL *ssl) { | void SSL_set_accept_state(SSL *ssl) { | ||||
ssl->server = 1; | ssl->server = 1; | ||||
ssl->state = SSL_ST_ACCEPT; | ssl->state = SSL_ST_ACCEPT; | ||||
ssl->handshake_func = ssl->method->ssl_accept; | |||||
ssl->handshake_func = ssl3_accept; | |||||
} | } | ||||
void SSL_set_bio(SSL *ssl, BIO *rbio, BIO *wbio) { | void SSL_set_bio(SSL *ssl, BIO *rbio, BIO *wbio) { | ||||
@@ -579,8 +579,6 @@ int SSL_connect(SSL *ssl) { | |||||
SSL_set_connect_state(ssl); | SSL_set_connect_state(ssl); | ||||
} | } | ||||
assert(ssl->handshake_func == ssl->method->ssl_connect); | |||||
return SSL_do_handshake(ssl); | return SSL_do_handshake(ssl); | ||||
} | } | ||||
@@ -590,8 +588,6 @@ int SSL_accept(SSL *ssl) { | |||||
SSL_set_accept_state(ssl); | SSL_set_accept_state(ssl); | ||||
} | } | ||||
assert(ssl->handshake_func == ssl->method->ssl_accept); | |||||
return SSL_do_handshake(ssl); | return SSL_do_handshake(ssl); | ||||
} | } | ||||