diff options
author | Adam Langley <agl@google.com> | 2015-09-24 23:03:06 +0000 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2015-09-24 23:03:06 +0000 |
commit | c737bc23bc868fff21e5c1b95940813f709ea550 (patch) | |
tree | dd743d9d64af3145fe96b8d5fc2f3427544794bd /src/ssl | |
parent | 0267d647a4d272af8b9e7c91063d374f7e2775bb (diff) | |
parent | 3781a60670f92c3c6fca860cb4589495cefa2e56 (diff) | |
download | external_boringssl-c737bc23bc868fff21e5c1b95940813f709ea550.zip external_boringssl-c737bc23bc868fff21e5c1b95940813f709ea550.tar.gz external_boringssl-c737bc23bc868fff21e5c1b95940813f709ea550.tar.bz2 |
am 3781a606: am 1e4884f6: external/boringssl: sync with upstream.
* commit '3781a60670f92c3c6fca860cb4589495cefa2e56':
external/boringssl: sync with upstream.
Diffstat (limited to 'src/ssl')
46 files changed, 10093 insertions, 8841 deletions
diff --git a/src/ssl/CMakeLists.txt b/src/ssl/CMakeLists.txt index cf5a29d..2785dcf 100644 --- a/src/ssl/CMakeLists.txt +++ b/src/ssl/CMakeLists.txt @@ -1,10 +1,11 @@ -include_directories(. .. ../include) +include_directories(../include) add_subdirectory(pqueue) add_library( ssl + custom_extensions.c d1_both.c d1_clnt.c d1_lib.c @@ -12,6 +13,7 @@ add_library( d1_pkt.c d1_srtp.c d1_srvr.c + dtls_record.c s3_both.c s3_clnt.c s3_enc.c @@ -20,18 +22,19 @@ add_library( s3_pkt.c s3_srvr.c ssl_aead_ctx.c - ssl_algs.c ssl_asn1.c + ssl_buffer.c ssl_cert.c ssl_cipher.c + ssl_file.c ssl_lib.c ssl_rsa.c - ssl_sess.c + ssl_session.c ssl_stat.c ssl_txt.c t1_enc.c t1_lib.c - t1_reneg.c + tls_record.c $<TARGET_OBJECTS:pqueue> ) diff --git a/src/ssl/custom_extensions.c b/src/ssl/custom_extensions.c new file mode 100644 index 0000000..c94543d --- /dev/null +++ b/src/ssl/custom_extensions.c @@ -0,0 +1,257 @@ +/* Copyright (c) 2014, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#include <openssl/ssl.h> + +#include <assert.h> +#include <string.h> + +#include <openssl/bytestring.h> +#include <openssl/err.h> +#include <openssl/mem.h> +#include <openssl/stack.h> + +#include "internal.h" + + +void SSL_CUSTOM_EXTENSION_free(SSL_CUSTOM_EXTENSION *custom_extension) { + OPENSSL_free(custom_extension); +} + +static const SSL_CUSTOM_EXTENSION *custom_ext_find( + STACK_OF(SSL_CUSTOM_EXTENSION) *stack, + unsigned *out_index, uint16_t value) { + size_t i; + for (i = 0; i < sk_SSL_CUSTOM_EXTENSION_num(stack); i++) { + const SSL_CUSTOM_EXTENSION *ext = sk_SSL_CUSTOM_EXTENSION_value(stack, i); + if (ext->value == value) { + if (out_index != NULL) { + *out_index = i; + } + return ext; + } + } + + return NULL; +} + +/* default_add_callback is used as the |add_callback| when the user doesn't + * provide one. For servers, it does nothing while, for clients, it causes an + * empty extension to be included. */ +static int default_add_callback(SSL *ssl, unsigned extension_value, + const uint8_t **out, size_t *out_len, + int *out_alert_value, void *add_arg) { + if (ssl->server) { + return 0; + } + *out_len = 0; + return 1; +} + +static int custom_ext_add_hello(SSL *ssl, CBB *extensions) { + STACK_OF(SSL_CUSTOM_EXTENSION) *stack = ssl->ctx->client_custom_extensions; + if (ssl->server) { + stack = ssl->ctx->server_custom_extensions; + } + + if (stack == NULL) { + return 1; + } + + size_t i; + for (i = 0; i < sk_SSL_CUSTOM_EXTENSION_num(stack); i++) { + const SSL_CUSTOM_EXTENSION *ext = sk_SSL_CUSTOM_EXTENSION_value(stack, i); + + if (ssl->server && + !(ssl->s3->tmp.custom_extensions.received & (1u << i))) { + /* Servers cannot echo extensions that the client didn't send. */ + continue; + } + + const uint8_t *contents; + size_t contents_len; + int alert = SSL_AD_DECODE_ERROR; + CBB contents_cbb; + + switch (ext->add_callback(ssl, ext->value, &contents, &contents_len, &alert, + ext->add_arg)) { + case 1: + if (!CBB_add_u16(extensions, ext->value) || + !CBB_add_u16_length_prefixed(extensions, &contents_cbb) || + !CBB_add_bytes(&contents_cbb, contents, contents_len) || + !CBB_flush(extensions)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + ERR_add_error_dataf("extension: %u", (unsigned) ext->value); + if (ext->free_callback && 0 < contents_len) { + ext->free_callback(ssl, ext->value, contents, ext->add_arg); + } + return 0; + } + + if (ext->free_callback && 0 < contents_len) { + ext->free_callback(ssl, ext->value, contents, ext->add_arg); + } + + if (!ssl->server) { + assert((ssl->s3->tmp.custom_extensions.sent & (1u << i)) == 0); + ssl->s3->tmp.custom_extensions.sent |= (1u << i); + } + break; + + case 0: + break; + + default: + ssl3_send_alert(ssl, SSL3_AL_FATAL, alert); + OPENSSL_PUT_ERROR(SSL, SSL_R_CUSTOM_EXTENSION_ERROR); + ERR_add_error_dataf("extension: %u", (unsigned) ext->value); + return 0; + } + } + + return 1; +} + +int custom_ext_add_clienthello(SSL *ssl, CBB *extensions) { + return custom_ext_add_hello(ssl, extensions); +} + +int custom_ext_parse_serverhello(SSL *ssl, int *out_alert, uint16_t value, + const CBS *extension) { + unsigned index; + const SSL_CUSTOM_EXTENSION *ext = + custom_ext_find(ssl->ctx->client_custom_extensions, &index, value); + + if (/* Unknown extensions are not allowed in a ServerHello. */ + ext == NULL || + /* Also, if we didn't send the extension, that's also unacceptable. */ + !(ssl->s3->tmp.custom_extensions.sent & (1u << index))) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION); + ERR_add_error_dataf("extension: %u", (unsigned)value); + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + if (ext->parse_callback != NULL && + !ext->parse_callback(ssl, value, CBS_data(extension), CBS_len(extension), + out_alert, ext->parse_arg)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_CUSTOM_EXTENSION_ERROR); + ERR_add_error_dataf("extension: %u", (unsigned)ext->value); + return 0; + } + + return 1; +} + +int custom_ext_parse_clienthello(SSL *ssl, int *out_alert, uint16_t value, + const CBS *extension) { + unsigned index; + const SSL_CUSTOM_EXTENSION *ext = + custom_ext_find(ssl->ctx->server_custom_extensions, &index, value); + + if (ext == NULL) { + return 1; + } + + assert((ssl->s3->tmp.custom_extensions.received & (1u << index)) == 0); + ssl->s3->tmp.custom_extensions.received |= (1u << index); + + if (ext->parse_callback && + !ext->parse_callback(ssl, value, CBS_data(extension), CBS_len(extension), + out_alert, ext->parse_arg)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_CUSTOM_EXTENSION_ERROR); + ERR_add_error_dataf("extension: %u", (unsigned)ext->value); + return 0; + } + + return 1; +} + +int custom_ext_add_serverhello(SSL *ssl, CBB *extensions) { + return custom_ext_add_hello(ssl, extensions); +} + +/* MAX_NUM_CUSTOM_EXTENSIONS is the maximum number of custom extensions that + * can be set on an |SSL_CTX|. It's determined by the size of the bitset used + * to track when an extension has been sent. */ +#define MAX_NUM_CUSTOM_EXTENSIONS \ + (sizeof(((struct ssl3_state_st *)NULL)->tmp.custom_extensions.sent) * 8) + +static int custom_ext_append(STACK_OF(SSL_CUSTOM_EXTENSION) **stack, + unsigned extension_value, + SSL_custom_ext_add_cb add_cb, + SSL_custom_ext_free_cb free_cb, void *add_arg, + SSL_custom_ext_parse_cb parse_cb, + void *parse_arg) { + if (add_cb == NULL || + 0xffff < extension_value || + SSL_extension_supported(extension_value) || + /* Specifying a free callback without an add callback is nonsensical + * and an error. */ + (*stack != NULL && + (MAX_NUM_CUSTOM_EXTENSIONS <= sk_SSL_CUSTOM_EXTENSION_num(*stack) || + custom_ext_find(*stack, NULL, extension_value) != NULL))) { + return 0; + } + + SSL_CUSTOM_EXTENSION *ext = OPENSSL_malloc(sizeof(SSL_CUSTOM_EXTENSION)); + if (ext == NULL) { + return 0; + } + ext->add_callback = add_cb; + ext->add_arg = add_arg; + ext->free_callback = free_cb; + ext->parse_callback = parse_cb; + ext->parse_arg = parse_arg; + ext->value = extension_value; + + if (*stack == NULL) { + *stack = sk_SSL_CUSTOM_EXTENSION_new_null(); + if (*stack == NULL) { + SSL_CUSTOM_EXTENSION_free(ext); + return 0; + } + } + + if (!sk_SSL_CUSTOM_EXTENSION_push(*stack, ext)) { + SSL_CUSTOM_EXTENSION_free(ext); + if (sk_SSL_CUSTOM_EXTENSION_num(*stack) == 0) { + sk_SSL_CUSTOM_EXTENSION_free(*stack); + *stack = NULL; + } + return 0; + } + + return 1; +} + +int SSL_CTX_add_client_custom_ext(SSL_CTX *ctx, unsigned extension_value, + SSL_custom_ext_add_cb add_cb, + SSL_custom_ext_free_cb free_cb, void *add_arg, + SSL_custom_ext_parse_cb parse_cb, + void *parse_arg) { + return custom_ext_append(&ctx->client_custom_extensions, extension_value, + add_cb ? add_cb : default_add_callback, free_cb, + add_arg, parse_cb, parse_arg); +} + +int SSL_CTX_add_server_custom_ext(SSL_CTX *ctx, unsigned extension_value, + SSL_custom_ext_add_cb add_cb, + SSL_custom_ext_free_cb free_cb, void *add_arg, + SSL_custom_ext_parse_cb parse_cb, + void *parse_arg) { + return custom_ext_append(&ctx->server_custom_extensions, extension_value, + add_cb ? add_cb : default_add_callback, free_cb, + add_arg, parse_cb, parse_arg); +} diff --git a/src/ssl/d1_both.c b/src/ssl/d1_both.c index ac35a66..1acb3ce 100644 --- a/src/ssl/d1_both.c +++ b/src/ssl/d1_both.c @@ -111,6 +111,8 @@ * copied and put under another distribution licence * [including the GNU Public Licence.] */ +#include <openssl/ssl.h> + #include <assert.h> #include <limits.h> #include <stdio.h> @@ -147,52 +149,44 @@ static void dtls1_fix_message_header(SSL *s, unsigned long frag_off, unsigned long frag_len); static unsigned char *dtls1_write_message_header(SSL *s, unsigned char *p); -static hm_fragment *dtls1_hm_fragment_new(unsigned long frag_len, - int reassembly) { - hm_fragment *frag = NULL; - uint8_t *buf = NULL; - uint8_t *bitmask = NULL; - - frag = (hm_fragment *)OPENSSL_malloc(sizeof(hm_fragment)); +static hm_fragment *dtls1_hm_fragment_new(size_t frag_len, int reassembly) { + hm_fragment *frag = OPENSSL_malloc(sizeof(hm_fragment)); if (frag == NULL) { - OPENSSL_PUT_ERROR(SSL, dtls1_hm_fragment_new, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); return NULL; } + memset(frag, 0, sizeof(hm_fragment)); - if (frag_len) { - buf = (uint8_t *)OPENSSL_malloc(frag_len); - if (buf == NULL) { - OPENSSL_PUT_ERROR(SSL, dtls1_hm_fragment_new, ERR_R_MALLOC_FAILURE); - OPENSSL_free(frag); - return NULL; + /* If the handshake message is empty, |frag->fragment| and |frag->reassembly| + * are NULL. */ + if (frag_len > 0) { + frag->fragment = OPENSSL_malloc(frag_len); + if (frag->fragment == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; } - } - - /* zero length fragment gets zero frag->fragment */ - frag->fragment = buf; - /* Initialize reassembly bitmask if necessary */ - if (reassembly && frag_len > 0) { - if (frag_len + 7 < frag_len) { - OPENSSL_PUT_ERROR(SSL, dtls1_hm_fragment_new, ERR_R_OVERFLOW); - return NULL; - } - size_t bitmask_len = (frag_len + 7) / 8; - bitmask = (uint8_t *)OPENSSL_malloc(bitmask_len); - if (bitmask == NULL) { - OPENSSL_PUT_ERROR(SSL, dtls1_hm_fragment_new, ERR_R_MALLOC_FAILURE); - if (buf != NULL) { - OPENSSL_free(buf); + if (reassembly) { + /* Initialize reassembly bitmask. */ + if (frag_len + 7 < frag_len) { + OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW); + goto err; } - OPENSSL_free(frag); - return NULL; + size_t bitmask_len = (frag_len + 7) / 8; + frag->reassembly = OPENSSL_malloc(bitmask_len); + if (frag->reassembly == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + memset(frag->reassembly, 0, bitmask_len); } - memset(bitmask, 0, bitmask_len); } - frag->reassembly = bitmask; - return frag; + +err: + dtls1_hm_fragment_free(frag); + return NULL; } void dtls1_hm_fragment_free(hm_fragment *frag) { @@ -326,7 +320,7 @@ int dtls1_do_write(SSL *s, int type, enum dtls1_use_epoch_t use_epoch) { if (curr_mtu <= DTLS1_HM_HEADER_LENGTH) { /* To make forward progress, the MTU must, at minimum, fit the handshake * header and one byte of handshake body. */ - OPENSSL_PUT_ERROR(SSL, dtls1_do_write, SSL_R_MTU_TOO_SMALL); + OPENSSL_PUT_ERROR(SSL, SSL_R_MTU_TOO_SMALL); return -1; } @@ -344,7 +338,7 @@ int dtls1_do_write(SSL *s, int type, enum dtls1_use_epoch_t use_epoch) { assert(type == SSL3_RT_CHANGE_CIPHER_SPEC); /* ChangeCipherSpec cannot be fragmented. */ if (s->init_num > curr_mtu) { - OPENSSL_PUT_ERROR(SSL, dtls1_do_write, SSL_R_MTU_TOO_SMALL); + OPENSSL_PUT_ERROR(SSL, SSL_R_MTU_TOO_SMALL); return -1; } len = s->init_num; @@ -450,8 +444,7 @@ static hm_fragment *dtls1_get_buffered_message( frag->msg_header.msg_len != msg_hdr->msg_len) { /* The new fragment must be compatible with the previous fragments from * this message. */ - OPENSSL_PUT_ERROR(SSL, dtls1_get_buffered_message, - SSL_R_FRAGMENT_MISMATCH); + OPENSSL_PUT_ERROR(SSL, SSL_R_FRAGMENT_MISMATCH); ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); return NULL; } @@ -473,11 +466,7 @@ static size_t dtls1_max_handshake_message_len(const SSL *s) { /* dtls1_process_fragment reads a handshake fragment and processes it. It * returns one if a fragment was successfully processed and 0 or -1 on error. */ static int dtls1_process_fragment(SSL *s) { - /* Read handshake message header. - * - * TODO(davidben): ssl_read_bytes allows splitting the fragment header and - * body across two records. Change this interface to consume the fragment in - * one pass. */ + /* Read handshake message header. */ uint8_t header[DTLS1_HM_HEADER_LENGTH]; int ret = dtls1_read_bytes(s, SSL3_RT_HANDSHAKE, header, DTLS1_HM_HEADER_LENGTH, 0); @@ -485,7 +474,7 @@ static int dtls1_process_fragment(SSL *s) { return ret; } if (ret != DTLS1_HM_HEADER_LENGTH) { - OPENSSL_PUT_ERROR(SSL, dtls1_process_fragment, SSL_R_UNEXPECTED_MESSAGE); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE); ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); return -1; } @@ -494,14 +483,16 @@ static int dtls1_process_fragment(SSL *s) { struct hm_header_st msg_hdr; dtls1_get_message_header(header, &msg_hdr); + /* TODO(davidben): dtls1_read_bytes is the wrong abstraction for DTLS. There + * should be no need to reach into |s->s3->rrec.length|. */ const size_t frag_off = msg_hdr.frag_off; const size_t frag_len = msg_hdr.frag_len; const size_t msg_len = msg_hdr.msg_len; if (frag_off > msg_len || frag_off + frag_len < frag_off || frag_off + frag_len > msg_len || - msg_len > dtls1_max_handshake_message_len(s)) { - OPENSSL_PUT_ERROR(SSL, dtls1_process_fragment, - SSL_R_EXCESSIVE_MESSAGE_SIZE); + msg_len > dtls1_max_handshake_message_len(s) || + frag_len > s->s3->rrec.length) { + OPENSSL_PUT_ERROR(SSL, SSL_R_EXCESSIVE_MESSAGE_SIZE); ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); return -1; } @@ -535,8 +526,8 @@ static int dtls1_process_fragment(SSL *s) { ret = dtls1_read_bytes(s, SSL3_RT_HANDSHAKE, frag->fragment + frag_off, frag_len, 0); if (ret != frag_len) { - OPENSSL_PUT_ERROR(SSL, dtls1_process_fragment, SSL_R_UNEXPECTED_MESSAGE); - ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); return -1; } dtls1_hm_fragment_mark(frag, frag_off, frag_off + frag_len); @@ -563,7 +554,7 @@ long dtls1_get_message(SSL *s, int st1, int stn, int msg_type, long max, s->s3->tmp.reuse_message = 0; if (msg_type >= 0 && s->s3->tmp.message_type != msg_type) { al = SSL_AD_UNEXPECTED_MESSAGE; - OPENSSL_PUT_ERROR(SSL, dtls1_get_message, SSL_R_UNEXPECTED_MESSAGE); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE); goto f_err; } *ok = 1; @@ -589,22 +580,19 @@ long dtls1_get_message(SSL *s, int st1, int stn, int msg_type, long max, assert(frag->reassembly == NULL); if (frag->msg_header.msg_len > (size_t)max) { - OPENSSL_PUT_ERROR(SSL, dtls1_get_message, SSL_R_EXCESSIVE_MESSAGE_SIZE); + OPENSSL_PUT_ERROR(SSL, SSL_R_EXCESSIVE_MESSAGE_SIZE); goto err; } + /* Reconstruct the assembled message. */ + size_t len; CBB cbb; + CBB_zero(&cbb); if (!BUF_MEM_grow(s->init_buf, (size_t)frag->msg_header.msg_len + DTLS1_HM_HEADER_LENGTH) || - !CBB_init_fixed(&cbb, (uint8_t *)s->init_buf->data, s->init_buf->max)) { - OPENSSL_PUT_ERROR(SSL, dtls1_get_message, ERR_R_MALLOC_FAILURE); - goto err; - } - - /* Reconstruct the assembled message. */ - size_t len; - if (!CBB_add_u8(&cbb, frag->msg_header.type) || + !CBB_init_fixed(&cbb, (uint8_t *)s->init_buf->data, s->init_buf->max) || + !CBB_add_u8(&cbb, frag->msg_header.type) || !CBB_add_u24(&cbb, frag->msg_header.msg_len) || !CBB_add_u16(&cbb, frag->msg_header.seq) || !CBB_add_u24(&cbb, 0 /* frag_off */) || @@ -612,7 +600,7 @@ long dtls1_get_message(SSL *s, int st1, int stn, int msg_type, long max, !CBB_add_bytes(&cbb, frag->fragment, frag->msg_header.msg_len) || !CBB_finish(&cbb, NULL, &len)) { CBB_cleanup(&cbb); - OPENSSL_PUT_ERROR(SSL, dtls1_get_message, ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } assert(len == (size_t)frag->msg_header.msg_len + DTLS1_HM_HEADER_LENGTH); @@ -628,7 +616,7 @@ long dtls1_get_message(SSL *s, int st1, int stn, int msg_type, long max, if (msg_type >= 0 && s->s3->tmp.message_type != msg_type) { al = SSL_AD_UNEXPECTED_MESSAGE; - OPENSSL_PUT_ERROR(SSL, dtls1_get_message, SSL_R_UNEXPECTED_MESSAGE); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE); goto f_err; } if (hash_message == ssl_hash_message && !ssl3_hash_current_message(s)) { diff --git a/src/ssl/d1_clnt.c b/src/ssl/d1_clnt.c index 92fb8f6..73a3f8a 100644 --- a/src/ssl/d1_clnt.c +++ b/src/ssl/d1_clnt.c @@ -112,6 +112,8 @@ * [including the GNU Public Licence.] */ +#include <openssl/ssl.h> + #include <assert.h> #include <stdio.h> #include <string.h> @@ -128,6 +130,7 @@ #include "internal.h" + static int dtls1_get_hello_verify(SSL *s); int dtls1_connect(SSL *s) { @@ -188,9 +191,8 @@ int dtls1_connect(SSL *s) { case SSL3_ST_CW_CLNT_HELLO_B: s->shutdown = 0; - /* every DTLS ClientHello resets Finished MAC */ - if (!ssl3_init_finished_mac(s)) { - OPENSSL_PUT_ERROR(SSL, dtls1_connect, ERR_R_INTERNAL_ERROR); + if (!ssl3_init_handshake_buffer(s)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); ret = -1; goto end; } @@ -261,7 +263,7 @@ int dtls1_connect(SSL *s) { if (s->s3->tmp.certificate_status_expected) { s->state = SSL3_ST_CR_CERT_STATUS_A; } else { - s->state = SSL3_ST_CR_KEY_EXCH_A; + s->state = SSL3_ST_VERIFY_SERVER_CERT; } } else { skip = 1; @@ -270,6 +272,16 @@ int dtls1_connect(SSL *s) { s->init_num = 0; break; + case SSL3_ST_VERIFY_SERVER_CERT: + ret = ssl3_verify_server_cert(s); + if (ret <= 0) { + goto end; + } + + s->state = SSL3_ST_CR_KEY_EXCH_A; + s->init_num = 0; + break; + case SSL3_ST_CR_KEY_EXCH_A: case SSL3_ST_CR_KEY_EXCH_B: ret = ssl3_get_server_key_exchange(s); @@ -278,13 +290,6 @@ int dtls1_connect(SSL *s) { } s->state = SSL3_ST_CR_CERT_REQ_A; s->init_num = 0; - - /* at this point we check that we have the - * required stuff from the server */ - if (!ssl3_check_cert_and_algorithm(s)) { - ret = -1; - goto end; - } break; case SSL3_ST_CR_CERT_REQ_A: @@ -426,7 +431,7 @@ int dtls1_connect(SSL *s) { if (ret <= 0) { goto end; } - s->state = SSL3_ST_CR_KEY_EXCH_A; + s->state = SSL3_ST_VERIFY_SERVER_CERT; s->init_num = 0; break; @@ -483,7 +488,7 @@ int dtls1_connect(SSL *s) { goto end; default: - OPENSSL_PUT_ERROR(SSL, dtls1_connect, SSL_R_UNKNOWN_STATE); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_STATE); ret = -1; goto end; } @@ -538,7 +543,7 @@ static int dtls1_get_hello_verify(SSL *s) { !CBS_get_u8_length_prefixed(&hello_verify_request, &cookie) || CBS_len(&hello_verify_request) != 0) { al = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, dtls1_get_hello_verify, SSL_R_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); goto f_err; } diff --git a/src/ssl/d1_lib.c b/src/ssl/d1_lib.c index ef7a9c9..cb95585 100644 --- a/src/ssl/d1_lib.c +++ b/src/ssl/d1_lib.c @@ -54,12 +54,18 @@ * (eay@cryptsoft.com). This product includes software written by Tim * Hudson (tjh@cryptsoft.com). */ -#include <openssl/base.h> +#include <openssl/ssl.h> #include <limits.h> #include <stdio.h> #include <string.h> +#include <openssl/err.h> +#include <openssl/mem.h> +#include <openssl/obj.h> + +#include "internal.h" + #if defined(OPENSSL_WINDOWS) #include <sys/timeb.h> #else @@ -67,11 +73,6 @@ #include <sys/time.h> #endif -#include <openssl/err.h> -#include <openssl/mem.h> -#include <openssl/obj.h> - -#include "internal.h" /* DTLS1_MTU_TIMEOUTS is the maximum number of timeouts to expire * before starting to decrease the MTU. */ @@ -152,8 +153,9 @@ void dtls1_free(SSL *s) { } int dtls1_supports_cipher(const SSL_CIPHER *cipher) { - /* DTLS does not support stream ciphers. */ - return cipher->algorithm_enc != SSL_RC4; + /* DTLS does not support stream ciphers. The NULL cipher is rejected because + * it's not needed. */ + return cipher->algorithm_enc != SSL_RC4 && cipher->algorithm_enc != SSL_eNULL; } void dtls1_start_timer(SSL *s) { @@ -262,7 +264,7 @@ int dtls1_check_timeout_num(SSL *s) { if (s->d1->num_timeouts > DTLS1_MAX_TIMEOUTS) { /* fail the connection, enough alerts have been sent */ - OPENSSL_PUT_ERROR(SSL, dtls1_check_timeout_num, SSL_R_READ_TIMEOUT_EXPIRED); + OPENSSL_PUT_ERROR(SSL, SSL_R_READ_TIMEOUT_EXPIRED); return -1; } @@ -328,8 +330,9 @@ int dtls1_set_handshake_header(SSL *s, int htype, unsigned long len) { s2n(msg_hdr->seq, p); l2n3(0, p); l2n3(msg_hdr->msg_len, p); - return ssl3_finish_mac(s, serialised_header, sizeof(serialised_header)) && - ssl3_finish_mac(s, message + DTLS1_HM_HEADER_LENGTH, len); + return ssl3_update_handshake_hash(s, serialised_header, + sizeof(serialised_header)) && + ssl3_update_handshake_hash(s, message + DTLS1_HM_HEADER_LENGTH, len); } int dtls1_handshake_write(SSL *s) { diff --git a/src/ssl/d1_meth.c b/src/ssl/d1_meth.c index d90f75b..d54a037 100644 --- a/src/ssl/d1_meth.c +++ b/src/ssl/d1_meth.c @@ -55,6 +55,8 @@ * (eay@cryptsoft.com). This product includes software written by Tim * Hudson (tjh@cryptsoft.com). */ +#include <openssl/ssl.h> + #include "internal.h" @@ -69,8 +71,6 @@ static const SSL_PROTOCOL_METHOD DTLS_protocol_method = { dtls1_read_close_notify, dtls1_write_app_data, dtls1_dispatch_alert, - ssl3_ctrl, - ssl3_ctx_ctrl, dtls1_supports_cipher, DTLS1_HM_HEADER_LENGTH, dtls1_set_handshake_header, diff --git a/src/ssl/d1_pkt.c b/src/ssl/d1_pkt.c index 553499f..e2d505c 100644 --- a/src/ssl/d1_pkt.c +++ b/src/ssl/d1_pkt.c @@ -109,6 +109,8 @@ * copied and put under another distribution licence * [including the GNU Public Licence.] */ +#include <openssl/ssl.h> + #include <assert.h> #include <stdio.h> #include <string.h> @@ -122,270 +124,66 @@ #include "internal.h" -/* mod 128 saturating subtract of two 64-bit values in big-endian order */ -static int satsub64be(const uint8_t *v1, const uint8_t *v2) { - int ret, sat, brw, i; - - if (sizeof(long) == 8) { - do { - const union { - long one; - char little; - } is_endian = {1}; - long l; - - if (is_endian.little) { - break; - } - /* not reached on little-endians */ - /* following test is redundant, because input is - * always aligned, but I take no chances... */ - if (((size_t)v1 | (size_t)v2) & 0x7) { - break; - } - - l = *((long *)v1); - l -= *((long *)v2); - if (l > 128) { - return 128; - } else if (l < -128) { - return -128; - } else { - return (int)l; - } - } while (0); - } - - ret = (int)v1[7] - (int)v2[7]; - sat = 0; - brw = ret >> 8; /* brw is either 0 or -1 */ - if (ret & 0x80) { - for (i = 6; i >= 0; i--) { - brw += (int)v1[i] - (int)v2[i]; - sat |= ~brw; - brw >>= 8; - } - } else { - for (i = 6; i >= 0; i--) { - brw += (int)v1[i] - (int)v2[i]; - sat |= brw; - brw >>= 8; - } - } - brw <<= 8; /* brw is either 0 or -256 */ - - if (sat & 0xff) { - return brw | 0x80; - } else { - return brw + (ret & 0xFF); - } -} - -static int dtls1_record_replay_check(SSL *s, DTLS1_BITMAP *bitmap); -static void dtls1_record_bitmap_update(SSL *s, DTLS1_BITMAP *bitmap); -static int dtls1_process_record(SSL *s); static int do_dtls1_write(SSL *s, int type, const uint8_t *buf, unsigned int len, enum dtls1_use_epoch_t use_epoch); -static int dtls1_process_record(SSL *s) { - int al; - SSL3_RECORD *rr = &s->s3->rrec; - - /* check is not needed I believe */ - if (rr->length > SSL3_RT_MAX_ENCRYPTED_LENGTH) { - al = SSL_AD_RECORD_OVERFLOW; - OPENSSL_PUT_ERROR(SSL, dtls1_process_record, - SSL_R_ENCRYPTED_LENGTH_TOO_LONG); - goto f_err; - } - - /* |rr->data| points to |rr->length| bytes of ciphertext in |s->packet|. */ - rr->data = &s->packet[DTLS1_RT_HEADER_LENGTH]; - - uint8_t seq[8]; - seq[0] = rr->epoch >> 8; - seq[1] = rr->epoch & 0xff; - memcpy(&seq[2], &rr->seq_num[2], 6); - - /* Decrypt the packet in-place. Note it is important that |SSL_AEAD_CTX_open| - * not write beyond |rr->length|. There may be another record in the packet. - * - * TODO(davidben): This assumes |s->version| is the same as the record-layer - * version which isn't always true, but it only differs with the NULL cipher - * which ignores the parameter. */ - size_t plaintext_len; - if (!SSL_AEAD_CTX_open(s->aead_read_ctx, rr->data, &plaintext_len, rr->length, - rr->type, s->version, seq, rr->data, rr->length)) { - /* Bad packets are silently dropped in DTLS. Clear the error queue of any - * errors decryption may have added. */ - ERR_clear_error(); - rr->length = 0; - s->packet_length = 0; - goto err; - } - - if (plaintext_len > SSL3_RT_MAX_PLAIN_LENGTH) { - al = SSL_AD_RECORD_OVERFLOW; - OPENSSL_PUT_ERROR(SSL, dtls1_process_record, SSL_R_DATA_LENGTH_TOO_LONG); - goto f_err; - } - assert(plaintext_len < (1u << 16)); - rr->length = plaintext_len; - - rr->off = 0; - /* So at this point the following is true - * ssl->s3->rrec.type is the type of record - * ssl->s3->rrec.length == number of bytes in record - * ssl->s3->rrec.off == offset to first valid byte - * ssl->s3->rrec.data == the first byte of the record body. */ - - /* we have pulled in a full packet so zero things */ - s->packet_length = 0; - return 1; - -f_err: - ssl3_send_alert(s, SSL3_AL_FATAL, al); - -err: - return 0; -} - -/* Call this to get a new input record. - * It will return <= 0 if more data is needed, normally due to an error - * or non-blocking IO. - * When it finishes, one packet has been decoded and can be found in - * ssl->s3->rrec.type - is the type of record - * ssl->s3->rrec.data, - data - * ssl->s3->rrec.length, - number of bytes - * - * used only by dtls1_read_bytes */ -int dtls1_get_record(SSL *s) { - uint8_t ssl_major, ssl_minor; - int n; - SSL3_RECORD *rr; - uint8_t *p = NULL; - uint16_t version; - - rr = &(s->s3->rrec); - - /* get something from the wire */ +/* dtls1_get_record reads a new input record. On success, it places it in + * |ssl->s3->rrec| and returns one. Otherwise it returns <= 0 on error or if + * more data is needed. */ +static int dtls1_get_record(SSL *ssl) { again: - /* check if we have the header */ - if ((s->rstate != SSL_ST_READ_BODY) || - (s->packet_length < DTLS1_RT_HEADER_LENGTH)) { - n = ssl3_read_n(s, DTLS1_RT_HEADER_LENGTH, 0); - /* read timeout is handled by dtls1_read_bytes */ - if (n <= 0) { - return n; /* error or non-blocking */ - } - - /* this packet contained a partial record, dump it */ - if (s->packet_length != DTLS1_RT_HEADER_LENGTH) { - s->packet_length = 0; - goto again; + /* Read a new packet if there is no unconsumed one. */ + if (ssl_read_buffer_len(ssl) == 0) { + int ret = ssl_read_buffer_extend_to(ssl, 0 /* unused */); + if (ret <= 0) { + return ret; } + } + assert(ssl_read_buffer_len(ssl) > 0); - s->rstate = SSL_ST_READ_BODY; + /* Ensure the packet is large enough to decrypt in-place. */ + if (ssl_read_buffer_len(ssl) < ssl_record_prefix_len(ssl)) { + ssl_read_buffer_clear(ssl); + goto again; + } - p = s->packet; + uint8_t *out = ssl_read_buffer(ssl) + ssl_record_prefix_len(ssl); + size_t max_out = ssl_read_buffer_len(ssl) - ssl_record_prefix_len(ssl); + uint8_t type, alert; + size_t len, consumed; + switch (dtls_open_record(ssl, &type, out, &len, &consumed, &alert, max_out, + ssl_read_buffer(ssl), ssl_read_buffer_len(ssl))) { + case ssl_open_record_success: + ssl_read_buffer_consume(ssl, consumed); - if (s->msg_callback) { - s->msg_callback(0, 0, SSL3_RT_HEADER, p, DTLS1_RT_HEADER_LENGTH, s, - s->msg_callback_arg); - } - - /* Pull apart the header into the DTLS1_RECORD */ - rr->type = *(p++); - ssl_major = *(p++); - ssl_minor = *(p++); - version = (((uint16_t)ssl_major) << 8) | ssl_minor; - - /* sequence number is 64 bits, with top 2 bytes = epoch */ - n2s(p, rr->epoch); - - memcpy(&(s->s3->read_sequence[2]), p, 6); - p += 6; - - n2s(p, rr->length); - - /* Lets check version */ - if (s->s3->have_version) { - if (version != s->version) { - /* The record's version doesn't match, so silently drop it. - * - * TODO(davidben): This doesn't work. The DTLS record layer is not - * packet-based, so the remainder of the packet isn't dropped and we - * get a framing error. It's also unclear what it means to silently - * drop a record in a packet containing two records. */ - rr->length = 0; - s->packet_length = 0; - goto again; + if (len > 0xffff) { + OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW); + return -1; } - } - if ((version & 0xff00) != (s->version & 0xff00)) { - /* wrong version, silently discard record */ - rr->length = 0; - s->packet_length = 0; - goto again; - } + SSL3_RECORD *rr = &ssl->s3->rrec; + rr->type = type; + rr->length = (uint16_t)len; + rr->off = 0; + rr->data = out; + return 1; - if (rr->length > SSL3_RT_MAX_ENCRYPTED_LENGTH) { - /* record too long, silently discard it */ - rr->length = 0; - s->packet_length = 0; - goto again; - } - - /* now s->rstate == SSL_ST_READ_BODY */ - } - - /* s->rstate == SSL_ST_READ_BODY, get and decode the data */ - - if (rr->length > s->packet_length - DTLS1_RT_HEADER_LENGTH) { - /* now s->packet_length == DTLS1_RT_HEADER_LENGTH */ - n = ssl3_read_n(s, rr->length, 1); - /* This packet contained a partial record, dump it. */ - if (n != rr->length) { - rr->length = 0; - s->packet_length = 0; + case ssl_open_record_discard: + ssl_read_buffer_consume(ssl, consumed); goto again; - } - - /* now n == rr->length, - * and s->packet_length == DTLS1_RT_HEADER_LENGTH + rr->length */ - } - s->rstate = SSL_ST_READ_HEADER; /* set state for later operations */ - - if (rr->epoch != s->d1->r_epoch) { - /* This record is from the wrong epoch. If it is the next epoch, it could be - * buffered. For simplicity, drop it and expect retransmit to handle it - * later; DTLS is supposed to handle packet loss. */ - rr->length = 0; - s->packet_length = 0; - goto again; - } - - /* Check whether this is a repeat, or aged record. */ - if (!dtls1_record_replay_check(s, &s->d1->bitmap)) { - rr->length = 0; - s->packet_length = 0; /* dump this record */ - goto again; /* get another record */ - } - /* just read a 0 length packet */ - if (rr->length == 0) { - goto again; - } + case ssl_open_record_error: + ssl3_send_alert(ssl, SSL3_AL_FATAL, alert); + return -1; - if (!dtls1_process_record(s)) { - rr->length = 0; - s->packet_length = 0; /* dump this record */ - goto again; /* get another record */ + case ssl_open_record_partial: + /* Impossible in DTLS. */ + break; } - dtls1_record_bitmap_update(s, &s->d1->bitmap); /* Mark receipt of record. */ - return 1; + assert(0); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return -1; } int dtls1_read_app_data(SSL *ssl, uint8_t *buf, int len, int peek) { @@ -393,7 +191,11 @@ int dtls1_read_app_data(SSL *ssl, uint8_t *buf, int len, int peek) { } void dtls1_read_close_notify(SSL *ssl) { - dtls1_read_bytes(ssl, 0, NULL, 0, 0); + /* Bidirectional shutdown doesn't make sense for an unordered transport. DTLS + * alerts also aren't delivered reliably, so we may even time out because the + * peer never received our close_notify. Report to the caller that the channel + * has fully shut down. */ + ssl->shutdown |= SSL_RECEIVED_SHUTDOWN; } /* Return up to 'len' payload bytes received in 'type' records. @@ -401,7 +203,6 @@ void dtls1_read_close_notify(SSL *ssl) { * * - SSL3_RT_HANDSHAKE (when ssl3_get_message calls us) * - SSL3_RT_APPLICATION_DATA (when ssl3_read calls us) - * - 0 (during a shutdown, no data has to be returned) * * If we don't have stored data to work from, read a SSL/TLS record first * (possibly multiple records if we still don't have anything to return). @@ -429,11 +230,9 @@ int dtls1_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek) { SSL3_RECORD *rr; void (*cb)(const SSL *ssl, int type2, int val) = NULL; - /* XXX: check what the second '&& type' is about */ - if ((type && (type != SSL3_RT_APPLICATION_DATA) && - (type != SSL3_RT_HANDSHAKE) && type) || - (peek && (type != SSL3_RT_APPLICATION_DATA))) { - OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, ERR_R_INTERNAL_ERROR); + if ((type != SSL3_RT_APPLICATION_DATA && type != SSL3_RT_HANDSHAKE) || + (peek && type != SSL3_RT_APPLICATION_DATA)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return -1; } @@ -444,7 +243,7 @@ int dtls1_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek) { return i; } if (i == 0) { - OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_SSL_HANDSHAKE_FAILURE); + OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_HANDSHAKE_FAILURE); return -1; } } @@ -464,7 +263,7 @@ start: } /* get new packet if necessary */ - if (rr->length == 0 || s->rstate == SSL_ST_READ_BODY) { + if (rr->length == 0) { ret = dtls1_get_record(s); if (ret <= 0) { ret = dtls1_read_failed(s, ret); @@ -507,10 +306,15 @@ start: /* TODO(davidben): Is this check redundant with the handshake_func * check? */ al = SSL_AD_UNEXPECTED_MESSAGE; - OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_APP_DATA_IN_HANDSHAKE); + OPENSSL_PUT_ERROR(SSL, SSL_R_APP_DATA_IN_HANDSHAKE); goto f_err; } + /* Discard empty records. */ + if (rr->length == 0) { + goto start; + } + if (len <= 0) { return len; } @@ -526,8 +330,9 @@ start: rr->length -= n; rr->off += n; if (rr->length == 0) { - s->rstate = SSL_ST_READ_HEADER; rr->off = 0; + /* The record has been consumed, so we may now clear the buffer. */ + ssl_read_buffer_discard(s); } } @@ -542,7 +347,7 @@ start: /* Alerts may not be fragmented. */ if (rr->length < 2) { al = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_BAD_ALERT); + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ALERT); goto f_err; } @@ -576,8 +381,7 @@ start: s->rwstate = SSL_NOTHING; s->s3->fatal_alert = alert_descr; - OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, - SSL_AD_REASON_OFFSET + alert_descr); + OPENSSL_PUT_ERROR(SSL, SSL_AD_REASON_OFFSET + alert_descr); BIO_snprintf(tmp, sizeof tmp, "%d", alert_descr); ERR_add_error_data(2, "SSL alert number ", tmp); s->shutdown |= SSL_RECEIVED_SHUTDOWN; @@ -585,26 +389,19 @@ start: return 0; } else { al = SSL_AD_ILLEGAL_PARAMETER; - OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_UNKNOWN_ALERT_TYPE); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_ALERT_TYPE); goto f_err; } goto start; } - if (s->shutdown & SSL_SENT_SHUTDOWN) { - /* but we have not received a shutdown */ - s->rwstate = SSL_NOTHING; - rr->length = 0; - return 0; - } - if (rr->type == SSL3_RT_CHANGE_CIPHER_SPEC) { /* 'Change Cipher Spec' is just a single byte, so we know exactly what the * record payload has to look like */ if (rr->length != 1 || rr->off != 0 || rr->data[0] != SSL3_MT_CCS) { al = SSL_AD_ILLEGAL_PARAMETER; - OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_BAD_CHANGE_CIPHER_SPEC); + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_CHANGE_CIPHER_SPEC); goto f_err; } @@ -641,7 +438,7 @@ start: if (rr->type == SSL3_RT_HANDSHAKE && !s->in_handshake) { if (rr->length < DTLS1_HM_HEADER_LENGTH) { al = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_BAD_HANDSHAKE_RECORD); + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_HANDSHAKE_RECORD); goto f_err; } struct hm_header_st msg_hdr; @@ -669,7 +466,7 @@ start: assert(rr->type != SSL3_RT_CHANGE_CIPHER_SPEC && rr->type != SSL3_RT_ALERT); al = SSL_AD_UNEXPECTED_MESSAGE; - OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_UNEXPECTED_RECORD); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD); f_err: ssl3_send_alert(s, SSL3_AL_FATAL, al); @@ -686,13 +483,13 @@ int dtls1_write_app_data(SSL *s, const void *buf_, int len) { return i; } if (i == 0) { - OPENSSL_PUT_ERROR(SSL, dtls1_write_app_data, SSL_R_SSL_HANDSHAKE_FAILURE); + OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_HANDSHAKE_FAILURE); return -1; } } if (len > SSL3_RT_MAX_PLAIN_LENGTH) { - OPENSSL_PUT_ERROR(SSL, dtls1_write_app_data, SSL_R_DTLS_MESSAGE_TOO_BIG); + OPENSSL_PUT_ERROR(SSL, SSL_R_DTLS_MESSAGE_TOO_BIG); return -1; } @@ -713,73 +510,11 @@ int dtls1_write_bytes(SSL *s, int type, const void *buf, int len, return i; } -/* dtls1_seal_record seals a new record of type |type| and plaintext |in| and - * writes it to |out|. At most |max_out| bytes will be written. It returns one - * on success and zero on error. On success, it updates the write sequence - * number. */ -static int dtls1_seal_record(SSL *s, uint8_t *out, size_t *out_len, - size_t max_out, uint8_t type, const uint8_t *in, - size_t in_len, enum dtls1_use_epoch_t use_epoch) { - if (max_out < DTLS1_RT_HEADER_LENGTH) { - OPENSSL_PUT_ERROR(SSL, dtls1_seal_record, SSL_R_BUFFER_TOO_SMALL); - return 0; - } - - /* Determine the parameters for the current epoch. */ - uint16_t epoch = s->d1->w_epoch; - SSL_AEAD_CTX *aead = s->aead_write_ctx; - uint8_t *seq = s->s3->write_sequence; - if (use_epoch == dtls1_use_previous_epoch) { - /* DTLS renegotiation is unsupported, so only epochs 0 (NULL cipher) and 1 - * (negotiated cipher) exist. */ - assert(s->d1->w_epoch == 1); - epoch = s->d1->w_epoch - 1; - aead = NULL; - seq = s->d1->last_write_sequence; - } - - out[0] = type; - - uint16_t wire_version = s->s3->have_version ? s->version : DTLS1_VERSION; - out[1] = wire_version >> 8; - out[2] = wire_version & 0xff; - - out[3] = epoch >> 8; - out[4] = epoch & 0xff; - memcpy(&out[5], &seq[2], 6); - - size_t ciphertext_len; - if (!SSL_AEAD_CTX_seal(aead, out + DTLS1_RT_HEADER_LENGTH, &ciphertext_len, - max_out - DTLS1_RT_HEADER_LENGTH, type, wire_version, - &out[3] /* seq */, in, in_len) || - !ssl3_record_sequence_update(&seq[2], 6)) { - return 0; - } - - if (ciphertext_len >= 1 << 16) { - OPENSSL_PUT_ERROR(SSL, dtls1_seal_record, ERR_R_OVERFLOW); - return 0; - } - out[11] = ciphertext_len >> 8; - out[12] = ciphertext_len & 0xff; - - *out_len = DTLS1_RT_HEADER_LENGTH + ciphertext_len; - - if (s->msg_callback) { - s->msg_callback(1 /* write */, 0, SSL3_RT_HEADER, out, - DTLS1_RT_HEADER_LENGTH, s, s->msg_callback_arg); - } - - return 1; -} - static int do_dtls1_write(SSL *s, int type, const uint8_t *buf, unsigned int len, enum dtls1_use_epoch_t use_epoch) { - SSL3_BUFFER *wb = &s->s3->wbuf; - /* ssl3_write_pending drops the write if |BIO_write| fails in DTLS, so there * is never pending data. */ - assert(s->s3->wbuf.left == 0); + assert(!ssl_write_buffer_is_pending(s)); /* If we have an alert to send, lets send it */ if (s->s3->alert_dispatch) { @@ -790,7 +525,8 @@ static int do_dtls1_write(SSL *s, int type, const uint8_t *buf, /* if it went, fall through and send more stuff */ } - if (wb->buf == NULL && !ssl3_setup_write_buffer(s)) { + if (len > SSL3_RT_MAX_PLAIN_LENGTH) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return -1; } @@ -798,21 +534,15 @@ static int do_dtls1_write(SSL *s, int type, const uint8_t *buf, return 0; } - /* Align the output so the ciphertext is aligned to |SSL3_ALIGN_PAYLOAD|. */ - uintptr_t align = (uintptr_t)wb->buf + DTLS1_RT_HEADER_LENGTH; - align = (0 - align) & (SSL3_ALIGN_PAYLOAD - 1); - uint8_t *out = wb->buf + align; - wb->offset = align; - size_t max_out = wb->len - wb->offset; - + size_t max_out = len + ssl_max_seal_overhead(s); + uint8_t *out; size_t ciphertext_len; - if (!dtls1_seal_record(s, out, &ciphertext_len, max_out, type, buf, len, - use_epoch)) { + if (!ssl_write_buffer_init(s, &out, max_out) || + !dtls_seal_record(s, out, &ciphertext_len, max_out, type, buf, len, + use_epoch)) { return -1; } - - /* now let's set up wb */ - wb->left = ciphertext_len; + ssl_write_buffer_set_len(s, ciphertext_len); /* memorize arguments so that ssl3_write_pending can detect bad write retries * later */ @@ -825,49 +555,6 @@ static int do_dtls1_write(SSL *s, int type, const uint8_t *buf, return ssl3_write_pending(s, type, buf, len); } -static int dtls1_record_replay_check(SSL *s, DTLS1_BITMAP *bitmap) { - int cmp; - unsigned int shift; - const uint8_t *seq = s->s3->read_sequence; - - cmp = satsub64be(seq, bitmap->max_seq_num); - if (cmp > 0) { - memcpy(s->s3->rrec.seq_num, seq, 8); - return 1; /* this record in new */ - } - shift = -cmp; - if (shift >= sizeof(bitmap->map) * 8) { - return 0; /* stale, outside the window */ - } else if (bitmap->map & (((uint64_t)1) << shift)) { - return 0; /* record previously received */ - } - - memcpy(s->s3->rrec.seq_num, seq, 8); - return 1; -} - -static void dtls1_record_bitmap_update(SSL *s, DTLS1_BITMAP *bitmap) { - int cmp; - unsigned int shift; - const uint8_t *seq = s->s3->read_sequence; - - cmp = satsub64be(seq, bitmap->max_seq_num); - if (cmp > 0) { - shift = cmp; - if (shift < sizeof(bitmap->map) * 8) { - bitmap->map <<= shift, bitmap->map |= 1UL; - } else { - bitmap->map = 1UL; - } - memcpy(bitmap->max_seq_num, seq, 8); - } else { - shift = -cmp; - if (shift < sizeof(bitmap->map) * 8) { - bitmap->map |= ((uint64_t)1) << shift; - } - } -} - int dtls1_dispatch_alert(SSL *s) { int i, j; void (*cb)(const SSL *ssl, int type, int val) = NULL; diff --git a/src/ssl/d1_srtp.c b/src/ssl/d1_srtp.c index 5928fc8..2fcc1ea 100644 --- a/src/ssl/d1_srtp.c +++ b/src/ssl/d1_srtp.c @@ -114,6 +114,8 @@ Copyright (C) 2011, RTFM, Inc. */ +#include <openssl/ssl.h> + #include <stdio.h> #include <string.h> @@ -122,15 +124,14 @@ #include <openssl/obj.h> #include "internal.h" -#include <openssl/srtp.h> -static const SRTP_PROTECTION_PROFILE srtp_known_profiles[] = { +const SRTP_PROTECTION_PROFILE kSRTPProfiles[] = { { - "SRTP_AES128_CM_SHA1_80", SRTP_AES128_CM_SHA1_80, + "SRTP_AES128_CM_SHA1_80", SRTP_AES128_CM_SHA1_80, }, { - "SRTP_AES128_CM_SHA1_32", SRTP_AES128_CM_SHA1_32, + "SRTP_AES128_CM_SHA1_32", SRTP_AES128_CM_SHA1_32, }, {0}, }; @@ -140,7 +141,7 @@ static int find_profile_by_name(const char *profile_name, size_t len) { const SRTP_PROTECTION_PROFILE *p; - p = srtp_known_profiles; + p = kSRTPProfiles; while (p->name) { if (len == strlen(p->name) && !strncmp(p->name, profile_name, len)) { *pptr = p; @@ -153,22 +154,6 @@ static int find_profile_by_name(const char *profile_name, return 0; } -static int find_profile_by_num(unsigned profile_num, - const SRTP_PROTECTION_PROFILE **pptr) { - const SRTP_PROTECTION_PROFILE *p; - - p = srtp_known_profiles; - while (p->name) { - if (p->id == profile_num) { - *pptr = p; - return 1; - } - p++; - } - - return 0; -} - static int ssl_ctx_make_profiles(const char *profiles_string, STACK_OF(SRTP_PROTECTION_PROFILE) **out) { STACK_OF(SRTP_PROTECTION_PROFILE) *profiles; @@ -178,8 +163,7 @@ static int ssl_ctx_make_profiles(const char *profiles_string, profiles = sk_SRTP_PROTECTION_PROFILE_new_null(); if (profiles == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl_ctx_make_profiles, - SSL_R_SRTP_COULD_NOT_ALLOCATE_PROFILES); + OPENSSL_PUT_ERROR(SSL, SSL_R_SRTP_COULD_NOT_ALLOCATE_PROFILES); return 0; } @@ -190,8 +174,7 @@ static int ssl_ctx_make_profiles(const char *profiles_string, if (find_profile_by_name(ptr, &p, col ? col - ptr : strlen(ptr))) { sk_SRTP_PROTECTION_PROFILE_push(profiles, p); } else { - OPENSSL_PUT_ERROR(SSL, ssl_ctx_make_profiles, - SSL_R_SRTP_UNKNOWN_PROTECTION_PROFILE); + OPENSSL_PUT_ERROR(SSL, SSL_R_SRTP_UNKNOWN_PROTECTION_PROFILE); return 0; } @@ -209,28 +192,28 @@ int SSL_CTX_set_srtp_profiles(SSL_CTX *ctx, const char *profiles) { return ssl_ctx_make_profiles(profiles, &ctx->srtp_profiles); } -int SSL_set_srtp_profiles(SSL *s, const char *profiles) { - return ssl_ctx_make_profiles(profiles, &s->srtp_profiles); +int SSL_set_srtp_profiles(SSL *ssl, const char *profiles) { + return ssl_ctx_make_profiles(profiles, &ssl->srtp_profiles); } -STACK_OF(SRTP_PROTECTION_PROFILE) *SSL_get_srtp_profiles(SSL *s) { - if (s == NULL) { +STACK_OF(SRTP_PROTECTION_PROFILE) *SSL_get_srtp_profiles(SSL *ssl) { + if (ssl == NULL) { return NULL; } - if (s->srtp_profiles != NULL) { - return s->srtp_profiles; + if (ssl->srtp_profiles != NULL) { + return ssl->srtp_profiles; } - if (s->ctx != NULL && s->ctx->srtp_profiles != NULL) { - return s->ctx->srtp_profiles; + if (ssl->ctx != NULL && ssl->ctx->srtp_profiles != NULL) { + return ssl->ctx->srtp_profiles; } return NULL; } -const SRTP_PROTECTION_PROFILE *SSL_get_selected_srtp_profile(SSL *s) { - return s->srtp_profile; +const SRTP_PROTECTION_PROFILE *SSL_get_selected_srtp_profile(SSL *ssl) { + return ssl->srtp_profile; } int SSL_CTX_set_tlsext_use_srtp(SSL_CTX *ctx, const char *profiles) { @@ -238,195 +221,7 @@ int SSL_CTX_set_tlsext_use_srtp(SSL_CTX *ctx, const char *profiles) { return !SSL_CTX_set_srtp_profiles(ctx, profiles); } -int SSL_set_tlsext_use_srtp(SSL *s, const char *profiles) { +int SSL_set_tlsext_use_srtp(SSL *ssl, const char *profiles) { /* This API inverts its return value. */ - return !SSL_set_srtp_profiles(s, profiles); -} - -/* Note: this function returns 0 length if there are no profiles specified */ -int ssl_add_clienthello_use_srtp_ext(SSL *s, uint8_t *p, int *len, int maxlen) { - int ct = 0; - int i; - STACK_OF(SRTP_PROTECTION_PROFILE) *clnt = 0; - const SRTP_PROTECTION_PROFILE *prof; - - clnt = SSL_get_srtp_profiles(s); - ct = sk_SRTP_PROTECTION_PROFILE_num(clnt); /* -1 if clnt == 0 */ - - if (p) { - if (ct == 0) { - OPENSSL_PUT_ERROR(SSL, ssl_add_clienthello_use_srtp_ext, - SSL_R_EMPTY_SRTP_PROTECTION_PROFILE_LIST); - return 0; - } - - if (2 + ct * 2 + 1 > maxlen) { - OPENSSL_PUT_ERROR(SSL, ssl_add_clienthello_use_srtp_ext, - SSL_R_SRTP_PROTECTION_PROFILE_LIST_TOO_LONG); - return 0; - } - - /* Add the length */ - s2n(ct * 2, p); - for (i = 0; i < ct; i++) { - prof = sk_SRTP_PROTECTION_PROFILE_value(clnt, i); - s2n(prof->id, p); - } - - /* Add an empty use_mki value */ - *p++ = 0; - } - - *len = 2 + ct * 2 + 1; - - return 1; -} - -int ssl_parse_clienthello_use_srtp_ext(SSL *s, CBS *cbs, int *out_alert) { - CBS profile_ids, srtp_mki; - const SRTP_PROTECTION_PROFILE *cprof, *sprof; - STACK_OF(SRTP_PROTECTION_PROFILE) *client_profiles = 0, *server_profiles; - size_t i, j; - int ret = 0; - - if (!CBS_get_u16_length_prefixed(cbs, &profile_ids) || - CBS_len(&profile_ids) < 2 || - !CBS_get_u8_length_prefixed(cbs, &srtp_mki) || - CBS_len(cbs) != 0) { - OPENSSL_PUT_ERROR(SSL, ssl_parse_clienthello_use_srtp_ext, - SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST); - *out_alert = SSL_AD_DECODE_ERROR; - goto done; - } - - client_profiles = sk_SRTP_PROTECTION_PROFILE_new_null(); - if (client_profiles == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl_parse_clienthello_use_srtp_ext, - ERR_R_MALLOC_FAILURE); - *out_alert = SSL_AD_INTERNAL_ERROR; - goto done; - } - - while (CBS_len(&profile_ids) > 0) { - uint16_t profile_id; - - if (!CBS_get_u16(&profile_ids, &profile_id)) { - *out_alert = SSL_AD_DECODE_ERROR; - goto done; - } - - if (find_profile_by_num(profile_id, &cprof)) { - sk_SRTP_PROTECTION_PROFILE_push(client_profiles, cprof); - } - } - - /* Discard the MKI value for now. */ - - server_profiles = SSL_get_srtp_profiles(s); - - /* Pick the server's most preferred profile. */ - for (i = 0; i < sk_SRTP_PROTECTION_PROFILE_num(server_profiles); i++) { - sprof = sk_SRTP_PROTECTION_PROFILE_value(server_profiles, i); - - for (j = 0; j < sk_SRTP_PROTECTION_PROFILE_num(client_profiles); j++) { - cprof = sk_SRTP_PROTECTION_PROFILE_value(client_profiles, j); - - if (cprof->id == sprof->id) { - s->srtp_profile = sprof; - ret = 1; - goto done; - } - } - } - - ret = 1; - -done: - if (client_profiles) { - sk_SRTP_PROTECTION_PROFILE_free(client_profiles); - } - - return ret; -} - -int ssl_add_serverhello_use_srtp_ext(SSL *s, unsigned char *p, int *len, - int maxlen) { - if (p) { - if (maxlen < 5) { - OPENSSL_PUT_ERROR(SSL, ssl_add_serverhello_use_srtp_ext, - SSL_R_SRTP_PROTECTION_PROFILE_LIST_TOO_LONG); - return 0; - } - - if (s->srtp_profile == 0) { - OPENSSL_PUT_ERROR(SSL, ssl_add_serverhello_use_srtp_ext, - SSL_R_USE_SRTP_NOT_NEGOTIATED); - return 0; - } - - s2n(2, p); - s2n(s->srtp_profile->id, p); - *p++ = 0; - } - - *len = 5; - - return 1; -} - -int ssl_parse_serverhello_use_srtp_ext(SSL *s, CBS *cbs, int *out_alert) { - CBS profile_ids, srtp_mki; - uint16_t profile_id; - size_t i; - - STACK_OF(SRTP_PROTECTION_PROFILE) *client_profiles; - const SRTP_PROTECTION_PROFILE *prof; - - /* The extension consists of a u16-prefixed profile ID list containing a - * single uint16_t profile ID, then followed by a u8-prefixed srtp_mki field. - * - * See https://tools.ietf.org/html/rfc5764#section-4.1.1 */ - if (!CBS_get_u16_length_prefixed(cbs, &profile_ids) || - !CBS_get_u16(&profile_ids, &profile_id) || CBS_len(&profile_ids) != 0 || - !CBS_get_u8_length_prefixed(cbs, &srtp_mki) || CBS_len(cbs) != 0) { - OPENSSL_PUT_ERROR(SSL, ssl_parse_serverhello_use_srtp_ext, - SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST); - *out_alert = SSL_AD_DECODE_ERROR; - return 0; - } - - if (CBS_len(&srtp_mki) != 0) { - /* Must be no MKI, since we never offer one. */ - OPENSSL_PUT_ERROR(SSL, ssl_parse_serverhello_use_srtp_ext, - SSL_R_BAD_SRTP_MKI_VALUE); - *out_alert = SSL_AD_ILLEGAL_PARAMETER; - return 0; - } - - client_profiles = SSL_get_srtp_profiles(s); - - /* Throw an error if the server gave us an unsolicited extension */ - if (client_profiles == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl_parse_serverhello_use_srtp_ext, - SSL_R_NO_SRTP_PROFILES); - *out_alert = SSL_AD_DECODE_ERROR; - return 0; - } - - /* Check to see if the server gave us something we support - (and presumably offered). */ - for (i = 0; i < sk_SRTP_PROTECTION_PROFILE_num(client_profiles); i++) { - prof = sk_SRTP_PROTECTION_PROFILE_value(client_profiles, i); - - if (prof->id == profile_id) { - s->srtp_profile = prof; - *out_alert = 0; - return 1; - } - } - - OPENSSL_PUT_ERROR(SSL, ssl_parse_serverhello_use_srtp_ext, - SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST); - *out_alert = SSL_AD_ILLEGAL_PARAMETER; - return 0; + return !SSL_set_srtp_profiles(ssl, profiles); } diff --git a/src/ssl/d1_srvr.c b/src/ssl/d1_srvr.c index e49a3f0..89c26aa 100644 --- a/src/ssl/d1_srvr.c +++ b/src/ssl/d1_srvr.c @@ -112,6 +112,8 @@ * [including the GNU Public Licence.] */ +#include <openssl/ssl.h> + #include <assert.h> #include <stdio.h> @@ -150,11 +152,6 @@ int dtls1_accept(SSL *s) { s->in_handshake++; - if (s->cert == NULL) { - OPENSSL_PUT_ERROR(SSL, dtls1_accept, SSL_R_NO_CERTIFICATE_SET); - return -1; - } - for (;;) { state = s->state; @@ -181,8 +178,8 @@ int dtls1_accept(SSL *s) { goto end; } - if (!ssl3_init_finished_mac(s)) { - OPENSSL_PUT_ERROR(SSL, dtls1_accept, ERR_R_INTERNAL_ERROR); + if (!ssl3_init_handshake_buffer(s)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); ret = -1; goto end; } @@ -244,8 +241,19 @@ int dtls1_accept(SSL *s) { s->init_num = 0; break; + case SSL3_ST_SW_CERT_STATUS_A: + case SSL3_ST_SW_CERT_STATUS_B: + ret = ssl3_send_certificate_status(s); + if (ret <= 0) { + goto end; + } + s->state = SSL3_ST_SW_KEY_EXCH_A; + s->init_num = 0; + break; + case SSL3_ST_SW_KEY_EXCH_A: case SSL3_ST_SW_KEY_EXCH_B: + case SSL3_ST_SW_KEY_EXCH_C: alg_a = s->s3->tmp.new_cipher->algorithm_auth; /* Send a ServerKeyExchange message if: @@ -439,7 +447,7 @@ int dtls1_accept(SSL *s) { goto end; default: - OPENSSL_PUT_ERROR(SSL, dtls1_accept, SSL_R_UNKNOWN_STATE); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_STATE); ret = -1; goto end; } diff --git a/src/ssl/dtls_record.c b/src/ssl/dtls_record.c new file mode 100644 index 0000000..940494a --- /dev/null +++ b/src/ssl/dtls_record.c @@ -0,0 +1,308 @@ +/* DTLS implementation written by Nagendra Modadugu + * (nagendra@cs.stanford.edu) for the OpenSSL project 2005. */ +/* ==================================================================== + * Copyright (c) 1998-2005 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] */ + +#include <openssl/ssl.h> + +#include <assert.h> +#include <string.h> + +#include <openssl/bytestring.h> +#include <openssl/err.h> + +#include "internal.h" + + +/* to_u64_be treats |in| as a 8-byte big-endian integer and returns the value as + * a |uint64_t|. */ +static uint64_t to_u64_be(const uint8_t in[8]) { + uint64_t ret = 0; + unsigned i; + for (i = 0; i < 8; i++) { + ret <<= 8; + ret |= in[i]; + } + return ret; +} + +/* dtls1_bitmap_should_discard returns one if |seq_num| has been seen in |bitmap| + * or is stale. Otherwise it returns zero. */ +static int dtls1_bitmap_should_discard(DTLS1_BITMAP *bitmap, + const uint8_t seq_num[8]) { + const unsigned kWindowSize = sizeof(bitmap->map) * 8; + + uint64_t seq_num_u = to_u64_be(seq_num); + if (seq_num_u > bitmap->max_seq_num) { + return 0; + } + uint64_t idx = bitmap->max_seq_num - seq_num_u; + return idx >= kWindowSize || (bitmap->map & (((uint64_t)1) << idx)); +} + +/* dtls1_bitmap_record updates |bitmap| to record receipt of sequence number + * |seq_num|. It slides the window forward if needed. It is an error to call + * this function on a stale sequence number. */ +static void dtls1_bitmap_record(DTLS1_BITMAP *bitmap, + const uint8_t seq_num[8]) { + const unsigned kWindowSize = sizeof(bitmap->map) * 8; + + uint64_t seq_num_u = to_u64_be(seq_num); + /* Shift the window if necessary. */ + if (seq_num_u > bitmap->max_seq_num) { + uint64_t shift = seq_num_u - bitmap->max_seq_num; + if (shift >= kWindowSize) { + bitmap->map = 0; + } else { + bitmap->map <<= shift; + } + bitmap->max_seq_num = seq_num_u; + } + + uint64_t idx = bitmap->max_seq_num - seq_num_u; + if (idx < kWindowSize) { + bitmap->map |= ((uint64_t)1) << idx; + } +} + +enum ssl_open_record_t dtls_open_record( + SSL *ssl, uint8_t *out_type, uint8_t *out, size_t *out_len, + size_t *out_consumed, uint8_t *out_alert, size_t max_out, const uint8_t *in, + size_t in_len) { + CBS cbs; + CBS_init(&cbs, in, in_len); + + /* Decode the record. */ + uint8_t type; + uint16_t version; + uint8_t sequence[8]; + CBS body; + if (!CBS_get_u8(&cbs, &type) || + !CBS_get_u16(&cbs, &version) || + !CBS_copy_bytes(&cbs, sequence, 8) || + !CBS_get_u16_length_prefixed(&cbs, &body) || + (ssl->s3->have_version && version != ssl->version) || + (version >> 8) != DTLS1_VERSION_MAJOR || + CBS_len(&body) > SSL3_RT_MAX_ENCRYPTED_LENGTH) { + /* The record header was incomplete or malformed. Drop the entire packet. */ + *out_consumed = in_len; + return ssl_open_record_discard; + } + + if (ssl->msg_callback != NULL) { + ssl->msg_callback(0 /* read */, 0, SSL3_RT_HEADER, in, + DTLS1_RT_HEADER_LENGTH, ssl, ssl->msg_callback_arg); + } + + uint16_t epoch = (((uint16_t)sequence[0]) << 8) | sequence[1]; + if (epoch != ssl->d1->r_epoch || + dtls1_bitmap_should_discard(&ssl->d1->bitmap, sequence)) { + /* Drop this record. It's from the wrong epoch or is a replay. Note that if + * |epoch| is the next epoch, the record could be buffered for later. For + * simplicity, drop it and expect retransmit to handle it later; DTLS must + * handle packet loss anyway. */ + *out_consumed = in_len - CBS_len(&cbs); + return ssl_open_record_discard; + } + + /* Decrypt the body. */ + size_t plaintext_len; + if (!SSL_AEAD_CTX_open(ssl->aead_read_ctx, out, &plaintext_len, max_out, + type, version, sequence, CBS_data(&body), + CBS_len(&body))) { + /* Bad packets are silently dropped in DTLS. See section 4.2.1 of RFC 6347. + * Clear the error queue of any errors decryption may have added. Drop the + * entire packet as it must not have come from the peer. + * + * TODO(davidben): This doesn't distinguish malloc failures from encryption + * failures. */ + ERR_clear_error(); + *out_consumed = in_len - CBS_len(&cbs); + return ssl_open_record_discard; + } + + /* Check the plaintext length. */ + if (plaintext_len > SSL3_RT_MAX_PLAIN_LENGTH) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DATA_LENGTH_TOO_LONG); + *out_alert = SSL_AD_RECORD_OVERFLOW; + return ssl_open_record_error; + } + + dtls1_bitmap_record(&ssl->d1->bitmap, sequence); + + /* TODO(davidben): Limit the number of empty records as in TLS? This is only + * useful if we also limit discarded packets. */ + + *out_type = type; + *out_len = plaintext_len; + *out_consumed = in_len - CBS_len(&cbs); + return ssl_open_record_success; +} + +int dtls_seal_record(SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out, + uint8_t type, const uint8_t *in, size_t in_len, + enum dtls1_use_epoch_t use_epoch) { + /* Determine the parameters for the current epoch. */ + uint16_t epoch = ssl->d1->w_epoch; + SSL_AEAD_CTX *aead = ssl->aead_write_ctx; + uint8_t *seq = ssl->s3->write_sequence; + if (use_epoch == dtls1_use_previous_epoch) { + /* DTLS renegotiation is unsupported, so only epochs 0 (NULL cipher) and 1 + * (negotiated cipher) exist. */ + assert(ssl->d1->w_epoch == 1); + epoch = ssl->d1->w_epoch - 1; + aead = NULL; + seq = ssl->d1->last_write_sequence; + } + + if (max_out < DTLS1_RT_HEADER_LENGTH) { + OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL); + return 0; + } + /* Check the record header does not alias any part of the input. + * |SSL_AEAD_CTX_seal| will internally enforce other aliasing requirements. */ + if (in < out + DTLS1_RT_HEADER_LENGTH && out < in + in_len) { + OPENSSL_PUT_ERROR(SSL, SSL_R_OUTPUT_ALIASES_INPUT); + return 0; + } + + out[0] = type; + + uint16_t wire_version = ssl->s3->have_version ? ssl->version : DTLS1_VERSION; + out[1] = wire_version >> 8; + out[2] = wire_version & 0xff; + + out[3] = epoch >> 8; + out[4] = epoch & 0xff; + memcpy(&out[5], &seq[2], 6); + + size_t ciphertext_len; + if (!SSL_AEAD_CTX_seal(aead, out + DTLS1_RT_HEADER_LENGTH, &ciphertext_len, + max_out - DTLS1_RT_HEADER_LENGTH, type, wire_version, + &out[3] /* seq */, in, in_len) || + !ssl3_record_sequence_update(&seq[2], 6)) { + return 0; + } + + if (ciphertext_len >= 1 << 16) { + OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW); + return 0; + } + out[11] = ciphertext_len >> 8; + out[12] = ciphertext_len & 0xff; + + *out_len = DTLS1_RT_HEADER_LENGTH + ciphertext_len; + + if (ssl->msg_callback) { + ssl->msg_callback(1 /* write */, 0, SSL3_RT_HEADER, out, + DTLS1_RT_HEADER_LENGTH, ssl, ssl->msg_callback_arg); + } + + return 1; +} diff --git a/src/ssl/internal.h b/src/ssl/internal.h index 4d70431..6fb8dbe 100644 --- a/src/ssl/internal.h +++ b/src/ssl/internal.h @@ -182,6 +182,7 @@ #define SSL_AES128GCM 0x00000010L #define SSL_AES256GCM 0x00000020L #define SSL_CHACHA20POLY1305 0x00000040L +#define SSL_eNULL 0x00000080L #define SSL_AES (SSL_AES128 | SSL_AES256 | SSL_AES128GCM | SSL_AES256GCM) @@ -202,25 +203,15 @@ #define SSL_TLSV1 SSL_SSLV3 #define SSL_TLSV1_2 0x00000004L -/* Bits for |algorithm2| (handshake digests and other extra flags). */ - -#define SSL_HANDSHAKE_MAC_MD5 0x10 -#define SSL_HANDSHAKE_MAC_SHA 0x20 -#define SSL_HANDSHAKE_MAC_SHA256 0x40 -#define SSL_HANDSHAKE_MAC_SHA384 0x80 -#define SSL_HANDSHAKE_MAC_DEFAULT \ - (SSL_HANDSHAKE_MAC_MD5 | SSL_HANDSHAKE_MAC_SHA) +/* Bits for |algorithm_prf| (handshake digest). */ +#define SSL_HANDSHAKE_MAC_DEFAULT 0x1 +#define SSL_HANDSHAKE_MAC_SHA256 0x2 +#define SSL_HANDSHAKE_MAC_SHA384 0x4 /* SSL_MAX_DIGEST is the number of digest types which exist. When adding a new * one, update the table in ssl_cipher.c. */ #define SSL_MAX_DIGEST 4 -/* SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD is a flag in - * SSL_CIPHER.algorithm2 which indicates that the variable part of the nonce is - * included as a prefix of the record. (AES-GCM, for example, does with with an - * 8-byte variable nonce.) */ -#define SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD (1<<22) - /* Bits for |algo_strength|, cipher strength information. */ #define SSL_MEDIUM 0x00000001L #define SSL_HIGH 0x00000002L @@ -236,11 +227,11 @@ int ssl_cipher_get_evp_aead(const EVP_AEAD **out_aead, size_t *out_fixed_iv_len, const SSL_CIPHER *cipher, uint16_t version); -/* ssl_get_handshake_digest looks up the |i|th handshake digest type and sets - * |*out_mask| to the |SSL_HANDSHAKE_MAC_*| mask and |*out_md| to the - * |EVP_MD|. It returns one on successs and zero if |i| >= |SSL_MAX_DIGEST|. */ -int ssl_get_handshake_digest(uint32_t *out_mask, const EVP_MD **out_md, - size_t i); +/* ssl_get_handshake_digest returns the |EVP_MD| corresponding to + * |algorithm_prf|. It returns SHA-1 for |SSL_HANDSHAKE_DEFAULT|. The caller is + * responsible for maintaining the additional MD5 digest and switching to + * SHA-256 in TLS 1.2. */ +const EVP_MD *ssl_get_handshake_digest(uint32_t algorithm_prf); /* ssl_create_cipher_list evaluates |rule_str| according to the ciphers in * |ssl_method|. It sets |*out_cipher_list| to a newly-allocated @@ -254,18 +245,12 @@ ssl_create_cipher_list(const SSL_PROTOCOL_METHOD *ssl_method, STACK_OF(SSL_CIPHER) **out_cipher_list_by_id, const char *rule_str); -/* SSL_PKEY_* denote certificate types. */ -#define SSL_PKEY_RSA_ENC 0 -#define SSL_PKEY_RSA_SIGN 1 -#define SSL_PKEY_ECC 2 -#define SSL_PKEY_NUM 3 - /* ssl_cipher_get_value returns the cipher suite id of |cipher|. */ uint16_t ssl_cipher_get_value(const SSL_CIPHER *cipher); -/* ssl_cipher_get_cert_index returns the |SSL_PKEY_*| value corresponding to the - * certificate type of |cipher| or -1 if there is none. */ -int ssl_cipher_get_cert_index(const SSL_CIPHER *cipher); +/* ssl_cipher_get_key_type returns the |EVP_PKEY_*| value corresponding to the + * server key used in |cipher| or |EVP_PKEY_NONE| if there is none. */ +int ssl_cipher_get_key_type(const SSL_CIPHER *cipher); /* ssl_cipher_has_server_public_key returns 1 if |cipher| involves a server * public key in the key exchange, sent in a server Certificate message. @@ -275,11 +260,16 @@ int ssl_cipher_has_server_public_key(const SSL_CIPHER *cipher); /* ssl_cipher_requires_server_key_exchange returns 1 if |cipher| requires a * ServerKeyExchange message. Otherwise it returns 0. * - * Unlike ssl_cipher_has_server_public_key, some ciphers take optional - * ServerKeyExchanges. PSK and RSA_PSK only use the ServerKeyExchange to - * communicate a psk_identity_hint, so it is optional. */ + * Unlike |ssl_cipher_has_server_public_key|, this function may return zero + * while still allowing |cipher| an optional ServerKeyExchange. This is the + * case for plain PSK ciphers. */ int ssl_cipher_requires_server_key_exchange(const SSL_CIPHER *cipher); +/* ssl_cipher_get_record_split_len, for TLS 1.0 CBC mode ciphers, returns the + * length of an encrypted 1-byte record, for use in record-splitting. Otherwise + * it returns zero. */ +size_t ssl_cipher_get_record_split_len(const SSL_CIPHER *cipher); + /* Encryption layer. */ @@ -350,6 +340,238 @@ int SSL_AEAD_CTX_seal(SSL_AEAD_CTX *ctx, uint8_t *out, size_t *out_len, size_t in_len); +/* DTLS replay bitmap. */ + +/* DTLS1_BITMAP maintains a sliding window of 64 sequence numbers to detect + * replayed packets. It should be initialized by zeroing every field. */ +typedef struct dtls1_bitmap_st { + /* map is a bit mask of the last 64 sequence numbers. Bit + * |1<<i| corresponds to |max_seq_num - i|. */ + uint64_t map; + /* max_seq_num is the largest sequence number seen so far as a 64-bit + * integer. */ + uint64_t max_seq_num; +} DTLS1_BITMAP; + + +/* Record layer. */ + +/* ssl_record_prefix_len returns the length of the prefix before the ciphertext + * of a record for |ssl|. + * + * TODO(davidben): Expose this as part of public API once the high-level + * buffer-free APIs are available. */ +size_t ssl_record_prefix_len(const SSL *ssl); + +enum ssl_open_record_t { + ssl_open_record_success, + ssl_open_record_discard, + ssl_open_record_partial, + ssl_open_record_error, +}; + +/* tls_open_record decrypts a record from |in|. + * + * On success, it returns |ssl_open_record_success|. It sets |*out_type| to the + * record type, |*out_len| to the plaintext length, and writes the record body + * to |out|. It sets |*out_consumed| to the number of bytes of |in| consumed. + * Note that |*out_len| may be zero. + * + * If a record was successfully processed but should be discarded, it returns + * |ssl_open_record_discard| and sets |*out_consumed| to the number of bytes + * consumed. + * + * If the input did not contain a complete record, it returns + * |ssl_open_record_partial|. It sets |*out_consumed| to the total number of + * bytes necessary. It is guaranteed that a successful call to |tls_open_record| + * will consume at least that many bytes. + * + * On failure, it returns |ssl_open_record_error| and sets |*out_alert| to an + * alert to emit. + * + * If |in| and |out| alias, |out| must be <= |in| + |ssl_record_prefix_len|. */ +enum ssl_open_record_t tls_open_record( + SSL *ssl, uint8_t *out_type, uint8_t *out, size_t *out_len, + size_t *out_consumed, uint8_t *out_alert, size_t max_out, const uint8_t *in, + size_t in_len); + +/* dtls_open_record implements |tls_open_record| for DTLS. It never returns + * |ssl_open_record_partial| but otherwise behaves analogously. */ +enum ssl_open_record_t dtls_open_record( + SSL *ssl, uint8_t *out_type, uint8_t *out, size_t *out_len, + size_t *out_consumed, uint8_t *out_alert, size_t max_out, const uint8_t *in, + size_t in_len); + +/* ssl_seal_prefix_len returns the length of the prefix before the ciphertext + * when sealing a record with |ssl|. Note that this value may differ from + * |ssl_record_prefix_len| when TLS 1.0 CBC record-splitting is enabled. Sealing + * a small record may also result in a smaller output than this value. + * + * TODO(davidben): Expose this as part of public API once the high-level + * buffer-free APIs are available. */ +size_t ssl_seal_prefix_len(const SSL *ssl); + +/* ssl_max_seal_overhead returns the maximum overhead of sealing a record with + * |ssl|. This includes |ssl_seal_prefix_len|. + * + * TODO(davidben): Expose this as part of public API once the high-level + * buffer-free APIs are available. */ +size_t ssl_max_seal_overhead(const SSL *ssl); + +/* tls_seal_record seals a new record of type |type| and body |in| and writes it + * to |out|. At most |max_out| bytes will be written. It returns one on success + * and zero on error. If enabled, |tls_seal_record| implements TLS 1.0 CBC 1/n-1 + * record splitting and may write two records concatenated. + * + * For a large record, the ciphertext will begin |ssl_seal_prefix_len| bytes + * into out. Aligning |out| appropriately may improve performance. It writes at + * most |in_len| + |ssl_max_seal_overhead| bytes to |out|. + * + * If |in| and |out| alias, |out| + |ssl_seal_prefix_len| must be <= |in|. */ +int tls_seal_record(SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out, + uint8_t type, const uint8_t *in, size_t in_len); + +enum dtls1_use_epoch_t { + dtls1_use_previous_epoch, + dtls1_use_current_epoch, +}; + +/* dtls_seal_record implements |tls_seal_record| for DTLS. |use_epoch| selects + * which epoch's cipher state to use. */ +int dtls_seal_record(SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out, + uint8_t type, const uint8_t *in, size_t in_len, + enum dtls1_use_epoch_t use_epoch); + + +/* Private key operations. */ + +/* ssl_has_private_key returns one if |ssl| has a private key + * configured and zero otherwise. */ +int ssl_has_private_key(SSL *ssl); + +/* ssl_private_key_* call the corresponding function on the + * |SSL_PRIVATE_KEY_METHOD| for |ssl|, if configured. Otherwise, they implement + * the operation with |EVP_PKEY|. */ + +int ssl_private_key_type(SSL *ssl); + +size_t ssl_private_key_max_signature_len(SSL *ssl); + +enum ssl_private_key_result_t ssl_private_key_sign( + SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out, const EVP_MD *md, + const uint8_t *in, size_t in_len); + +enum ssl_private_key_result_t ssl_private_key_sign_complete( + SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out); + + +/* Custom extensions */ + +/* ssl_custom_extension (a.k.a. SSL_CUSTOM_EXTENSION) is a structure that + * contains information about custom-extension callbacks. */ +struct ssl_custom_extension { + SSL_custom_ext_add_cb add_callback; + void *add_arg; + SSL_custom_ext_free_cb free_callback; + SSL_custom_ext_parse_cb parse_callback; + void *parse_arg; + uint16_t value; +}; + +void SSL_CUSTOM_EXTENSION_free(SSL_CUSTOM_EXTENSION *custom_extension); + +int custom_ext_add_clienthello(SSL *ssl, CBB *extensions); +int custom_ext_parse_serverhello(SSL *ssl, int *out_alert, uint16_t value, + const CBS *extension); +int custom_ext_parse_clienthello(SSL *ssl, int *out_alert, uint16_t value, + const CBS *extension); +int custom_ext_add_serverhello(SSL *ssl, CBB *extensions); + + +/* Handshake hash. + * + * The TLS handshake maintains a transcript of all handshake messages. At + * various points in the protocol, this is either a handshake buffer, a rolling + * hash (selected by cipher suite) or both. */ + +/* ssl3_init_handshake_buffer initializes the handshake buffer and resets the + * handshake hash. It returns one success and zero on failure. */ +int ssl3_init_handshake_buffer(SSL *ssl); + +/* ssl3_init_handshake_hash initializes the handshake hash based on the pending + * cipher and the contents of the handshake buffer. Subsequent calls to + * |ssl3_update_handshake_hash| will update the rolling hash. It returns one on + * success and zero on failure. It is an error to call this function after the + * handshake buffer is released. */ +int ssl3_init_handshake_hash(SSL *ssl); + +/* ssl3_free_handshake_buffer releases the handshake buffer. Subsequent calls + * to |ssl3_update_handshake_hash| will not update the handshake buffer. */ +void ssl3_free_handshake_buffer(SSL *ssl); + +/* ssl3_free_handshake_hash releases the handshake hash. */ +void ssl3_free_handshake_hash(SSL *s); + +/* ssl3_update_handshake_hash adds |in| to the handshake buffer and handshake + * hash, whichever is enabled. It returns one on success and zero on failure. */ +int ssl3_update_handshake_hash(SSL *ssl, const uint8_t *in, size_t in_len); + + +/* Transport buffers. */ + +/* ssl_read_buffer returns a pointer to contents of the read buffer. */ +uint8_t *ssl_read_buffer(SSL *ssl); + +/* ssl_read_buffer_len returns the length of the read buffer. */ +size_t ssl_read_buffer_len(const SSL *ssl); + +/* ssl_read_buffer_extend_to extends the read buffer to the desired length. For + * TLS, it reads to the end of the buffer until the buffer is |len| bytes + * long. For DTLS, it reads a new packet and ignores |len|. It returns one on + * success, zero on EOF, and a negative number on error. + * + * It is an error to call |ssl_read_buffer_extend_to| in DTLS when the buffer is + * non-empty. */ +int ssl_read_buffer_extend_to(SSL *ssl, size_t len); + +/* ssl_read_buffer_consume consumes |len| bytes from the read buffer. It + * advances the data pointer and decrements the length. The memory consumed will + * remain valid until the next call to |ssl_read_buffer_extend| or it is + * discarded with |ssl_read_buffer_discard|. */ +void ssl_read_buffer_consume(SSL *ssl, size_t len); + +/* ssl_read_buffer_discard discards the consumed bytes from the read buffer. If + * the buffer is now empty, it releases memory used by it. */ +void ssl_read_buffer_discard(SSL *ssl); + +/* ssl_read_buffer_clear releases all memory associated with the read buffer and + * zero-initializes it. */ +void ssl_read_buffer_clear(SSL *ssl); + +/* ssl_write_buffer_is_pending returns one if the write buffer has pending data + * and zero if is empty. */ +int ssl_write_buffer_is_pending(const SSL *ssl); + +/* ssl_write_buffer_init initializes the write buffer. On success, it sets + * |*out_ptr| to the start of the write buffer with space for up to |max_len| + * bytes. It returns one on success and zero on failure. Call + * |ssl_write_buffer_set_len| to complete initialization. */ +int ssl_write_buffer_init(SSL *ssl, uint8_t **out_ptr, size_t max_len); + +/* ssl_write_buffer_set_len is called after |ssl_write_buffer_init| to complete + * initialization after |len| bytes are written to the buffer. */ +void ssl_write_buffer_set_len(SSL *ssl, size_t len); + +/* ssl_write_buffer_flush flushes the write buffer to the transport. It returns + * one on success and <= 0 on error. For DTLS, whether or not the write + * succeeds, the write buffer will be cleared. */ +int ssl_write_buffer_flush(SSL *ssl); + +/* ssl_write_buffer_clear releases all memory associated with the write buffer + * and zero-initializes it. */ +void ssl_write_buffer_clear(SSL *ssl); + + /* Underdocumented functions. * * Functions below here haven't been touched up and may be underdocumented. */ @@ -483,8 +705,6 @@ int SSL_AEAD_CTX_seal(SSL_AEAD_CTX *ctx, uint8_t *out, size_t *out_len, * SSL_aRSA <- RSA_ENC | RSA_SIGN * SSL_aDSS <- DSA_SIGN */ -#define PENDING_SESSION -10000 - /* From RFC4492, used in encoding the curve type in ECParameters */ #define EXPLICIT_PRIME_CURVE_TYPE 1 #define EXPLICIT_CHAR2_CURVE_TYPE 2 @@ -495,18 +715,21 @@ enum ssl_hash_message_t { ssl_hash_message, }; -typedef struct cert_pkey_st { +/* Structure containing decoded values of signature algorithms extension */ +typedef struct tls_sigalgs_st { + uint8_t rsign; + uint8_t rhash; +} TLS_SIGALGS; + +typedef struct cert_st { X509 *x509; EVP_PKEY *privatekey; /* Chain for this certificate */ STACK_OF(X509) *chain; -} CERT_PKEY; -typedef struct cert_st { - /* Current active set */ - CERT_PKEY *key; /* ALWAYS points to an element of the pkeys array - * Probably it would make more sense to store - * an index, not a pointer. */ + /* key_method, if non-NULL, is a set of callbacks to call for private key + * operations. */ + const SSL_PRIVATE_KEY_METHOD *key_method; /* For clients the following masks are of *disabled* key and auth algorithms * based on the current session. @@ -531,39 +754,18 @@ typedef struct cert_st { * keys. If NULL, a curve is selected automatically. See * |SSL_CTX_set_tmp_ecdh_callback|. */ EC_KEY *(*ecdh_tmp_cb)(SSL *ssl, int is_export, int keysize); - CERT_PKEY pkeys[SSL_PKEY_NUM]; - - /* Server-only: client_certificate_types is list of certificate types to - * include in the CertificateRequest message. - */ - uint8_t *client_certificate_types; - size_t num_client_certificate_types; - - /* signature algorithms peer reports: e.g. supported signature - * algorithms extension for server or as part of a certificate - * request for client. */ - uint8_t *peer_sigalgs; - /* Size of above array */ + + /* peer_sigalgs are the algorithm/hash pairs that the peer supports. These + * are taken from the contents of signature algorithms extension for a server + * or from the CertificateRequest for a client. */ + TLS_SIGALGS *peer_sigalgs; + /* peer_sigalgslen is the number of entries in |peer_sigalgs|. */ size_t peer_sigalgslen; - /* suppported signature algorithms. - * When set on a client this is sent in the client hello as the - * supported signature algorithms extension. For servers - * it represents the signature algorithms we are willing to use. */ - uint8_t *conf_sigalgs; - /* Size of above array */ - size_t conf_sigalgslen; - /* Client authentication signature algorithms, if not set then - * uses conf_sigalgs. On servers these will be the signature - * algorithms sent to the client in a cerificate request for TLS 1.2. - * On a client this represents the signature algortithms we are - * willing to use for client authentication. */ - uint8_t *client_sigalgs; - /* Size of above array */ - size_t client_sigalgslen; - /* Signature algorithms shared by client and server: cached - * because these are used most often. */ - TLS_SIGALGS *shared_sigalgs; - size_t shared_sigalgslen; + + /* digest_nids, if non-NULL, is the set of digests supported by |privatekey| + * in decreasing order of preference. */ + int *digest_nids; + size_t num_digest_nids; /* Certificate setup callback: if set is called whenever a * certificate may be required (client or server). the callback @@ -573,39 +775,8 @@ typedef struct cert_st { * supported signature algorithms or curves. */ int (*cert_cb)(SSL *ssl, void *arg); void *cert_cb_arg; - - /* Optional X509_STORE for chain building or certificate validation - * If NULL the parent SSL_CTX store is used instead. */ - X509_STORE *chain_store; - X509_STORE *verify_store; } CERT; -typedef struct sess_cert_st { - /* cert_chain is the certificate chain sent by the peer. NOTE: for a client, - * this does includes the server's leaf certificate, but, for a server, this - * does NOT include the client's leaf. */ - STACK_OF(X509) *cert_chain; - - /* peer_cert, on a client, is the leaf certificate of the peer. */ - X509 *peer_cert; - - DH *peer_dh_tmp; - EC_KEY *peer_ecdh_tmp; -} SESS_CERT; - -/* Structure containing decoded values of signature algorithms extension */ -struct tls_sigalgs_st { - /* NID of hash algorithm */ - int hash_nid; - /* NID of signature algorithm */ - int sign_nid; - /* Combined hash and signature NID */ - int signandhash_nid; - /* Raw values used in extension */ - uint8_t rsign; - uint8_t rhash; -}; - /* SSL_METHOD is a compatibility structure to support the legacy version-locked * methods. */ struct ssl_method_st { @@ -632,8 +803,6 @@ struct ssl_protocol_method_st { void (*ssl_read_close_notify)(SSL *s); int (*ssl_write_app_data)(SSL *s, const void *buf_, int len); int (*ssl_dispatch_alert)(SSL *s); - long (*ssl_ctrl)(SSL *s, int cmd, long larg, void *parg); - long (*ssl_ctx_ctrl)(SSL_CTX *ctx, int cmd, long larg, void *parg); /* supports_cipher returns one if |cipher| is supported by this protocol and * zero otherwise. */ int (*supports_cipher)(const SSL_CIPHER *cipher); @@ -696,15 +865,6 @@ struct ssl3_enc_method { #define DTLS1_AL_HEADER_LENGTH 2 -typedef struct dtls1_bitmap_st { - /* map is a bit mask of the last 64 sequence numbers. Bit - * |1<<i| corresponds to |max_seq_num - i|. */ - uint64_t map; - /* max_seq_num is the largest sequence number seen so far. It - * is a 64-bit value in big-endian encoding. */ - uint8_t max_seq_num[8]; -} DTLS1_BITMAP; - /* TODO(davidben): This structure is used for both incoming messages and * outgoing messages. |is_ccs| and |epoch| are only used in the latter and * should be moved elsewhere. */ @@ -789,6 +949,7 @@ extern const SSL3_ENC_METHOD TLSv1_enc_data; extern const SSL3_ENC_METHOD TLSv1_1_enc_data; extern const SSL3_ENC_METHOD TLSv1_2_enc_data; extern const SSL3_ENC_METHOD SSLv3_enc_data; +extern const SRTP_PROTECTION_PROFILE kSRTPProfiles[]; void ssl_clear_cipher_ctx(SSL *s); int ssl_clear_bad_session(SSL *s); @@ -796,11 +957,24 @@ CERT *ssl_cert_new(void); CERT *ssl_cert_dup(CERT *cert); void ssl_cert_clear_certs(CERT *c); void ssl_cert_free(CERT *c); -SESS_CERT *ssl_sess_cert_new(void); -SESS_CERT *ssl_sess_cert_dup(const SESS_CERT *sess_cert); -void ssl_sess_cert_free(SESS_CERT *sess_cert); int ssl_get_new_session(SSL *s, int session); -int ssl_get_prev_session(SSL *s, const struct ssl_early_callback_ctx *ctx); + +enum ssl_session_result_t { + ssl_session_success, + ssl_session_error, + ssl_session_retry, +}; + +/* ssl_get_prev_session looks up the previous session based on |ctx|. On + * success, it sets |*out_session| to the session or NULL if none was found. It + * sets |*out_send_ticket| to whether a ticket should be sent at the end of the + * handshake. If the session could not be looked up synchronously, it returns + * |ssl_session_retry| and should be called again. Otherwise, it returns + * |ssl_session_error|. */ +enum ssl_session_result_t ssl_get_prev_session( + SSL *ssl, SSL_SESSION **out_session, int *out_send_ticket, + const struct ssl_early_callback_ctx *ctx); + STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s, const CBS *cbs); int ssl_cipher_list_to_bytes(SSL *s, STACK_OF(SSL_CIPHER) *sk, uint8_t *p); struct ssl_cipher_preference_list_st *ssl_cipher_preference_list_dup( @@ -811,21 +985,16 @@ struct ssl_cipher_preference_list_st *ssl_cipher_preference_list_from_ciphers( STACK_OF(SSL_CIPHER) *ciphers); struct ssl_cipher_preference_list_st *ssl_get_cipher_preferences(SSL *s); -int ssl_cert_set0_chain(CERT *c, STACK_OF(X509) *chain); -int ssl_cert_set1_chain(CERT *c, STACK_OF(X509) *chain); -int ssl_cert_add0_chain_cert(CERT *c, X509 *x); -int ssl_cert_add1_chain_cert(CERT *c, X509 *x); -int ssl_cert_select_current(CERT *c, X509 *x); -void ssl_cert_set_cert_cb(CERT *c, int (*cb)(SSL *ssl, void *arg), void *arg); - -int ssl_verify_cert_chain(SSL *s, STACK_OF(X509) *sk); -int ssl_add_cert_chain(SSL *s, CERT_PKEY *cpk, unsigned long *l); -int ssl_build_cert_chain(CERT *c, X509_STORE *chain_store, int flags); -int ssl_cert_set_cert_store(CERT *c, X509_STORE *store, int chain, int ref); -CERT_PKEY *ssl_get_server_send_pkey(const SSL *s); -EVP_PKEY *ssl_get_sign_pkey(SSL *s, const SSL_CIPHER *c); +int ssl_cert_set0_chain(CERT *cert, STACK_OF(X509) *chain); +int ssl_cert_set1_chain(CERT *cert, STACK_OF(X509) *chain); +int ssl_cert_add0_chain_cert(CERT *cert, X509 *x509); +int ssl_cert_add1_chain_cert(CERT *cert, X509 *x509); +void ssl_cert_set_cert_cb(CERT *cert, + int (*cb)(SSL *ssl, void *arg), void *arg); + +int ssl_verify_cert_chain(SSL *ssl, STACK_OF(X509) *cert_chain); +int ssl_add_cert_chain(SSL *s, unsigned long *l); void ssl_update_cache(SSL *s, int mode); -int ssl_cert_type(EVP_PKEY *pkey); /* ssl_get_compatible_server_ciphers determines the key exchange and * authentication cipher suite masks compatible with the server configuration @@ -841,10 +1010,9 @@ int ssl_verify_alarm_type(long type); * |len|. It returns one on success and zero on failure. */ int ssl_fill_hello_random(uint8_t *out, size_t len, int is_server); -int ssl3_init_finished_mac(SSL *s); int ssl3_send_server_certificate(SSL *s); int ssl3_send_new_session_ticket(SSL *s); -int ssl3_send_cert_status(SSL *s); +int ssl3_send_certificate_status(SSL *s); int ssl3_get_finished(SSL *s, int state_a, int state_b); int ssl3_send_change_cipher_spec(SSL *s, int state_a, int state_b); int ssl3_prf(SSL *s, uint8_t *out, size_t out_len, const uint8_t *secret, @@ -865,11 +1033,11 @@ int ssl3_hash_current_message(SSL *s); /* ssl3_cert_verify_hash writes the CertificateVerify hash into the bytes * pointed to by |out| and writes the number of bytes to |*out_len|. |out| must * have room for EVP_MAX_MD_SIZE bytes. For TLS 1.2 and up, |*out_md| is used - * for the hash function, otherwise the hash function depends on the type of - * |pkey| and is written to |*out_md|. It returns one on success and zero on + * for the hash function, otherwise the hash function depends on |pkey_type| + * and is written to |*out_md|. It returns one on success and zero on * failure. */ int ssl3_cert_verify_hash(SSL *s, uint8_t *out, size_t *out_len, - const EVP_MD **out_md, EVP_PKEY *pkey); + const EVP_MD **out_md, int pkey_type); int ssl3_send_finished(SSL *s, int a, int b, const char *sender, int slen); int ssl3_supports_cipher(const SSL_CIPHER *cipher); @@ -882,29 +1050,15 @@ int ssl3_write_app_data(SSL *ssl, const void *buf, int len); int ssl3_write_bytes(SSL *s, int type, const void *buf, int len); int ssl3_final_finish_mac(SSL *s, const char *sender, int slen, uint8_t *p); int ssl3_cert_verify_mac(SSL *s, int md_nid, uint8_t *p); -int ssl3_finish_mac(SSL *s, const uint8_t *buf, int len); -void ssl3_free_digest_list(SSL *s); -int ssl3_output_cert_chain(SSL *s, CERT_PKEY *cpk); +int ssl3_output_cert_chain(SSL *s); const SSL_CIPHER *ssl3_choose_cipher( SSL *ssl, STACK_OF(SSL_CIPHER) *clnt, struct ssl_cipher_preference_list_st *srvr); -int ssl3_setup_read_buffer(SSL *s); -int ssl3_setup_write_buffer(SSL *s); -int ssl3_release_read_buffer(SSL *s); -int ssl3_release_write_buffer(SSL *s); - -enum should_free_handshake_buffer_t { - free_handshake_buffer, - dont_free_handshake_buffer, -}; -int ssl3_digest_cached_records(SSL *s, enum should_free_handshake_buffer_t); int ssl3_new(SSL *s); void ssl3_free(SSL *s); int ssl3_accept(SSL *s); int ssl3_connect(SSL *s); -long ssl3_ctrl(SSL *s, int cmd, long larg, void *parg); -long ssl3_ctx_ctrl(SSL_CTX *s, int cmd, long larg, void *parg); /* ssl3_record_sequence_update increments the sequence number in |seq|. It * returns one on success and zero on wraparound. */ @@ -915,13 +1069,7 @@ int ssl3_do_change_cipher_spec(SSL *ssl); int ssl3_set_handshake_header(SSL *s, int htype, unsigned long len); int ssl3_handshake_write(SSL *s); -enum dtls1_use_epoch_t { - dtls1_use_previous_epoch, - dtls1_use_current_epoch, -}; - int dtls1_do_write(SSL *s, int type, enum dtls1_use_epoch_t use_epoch); -int ssl3_read_n(SSL *s, int n, int extend); int dtls1_read_app_data(SSL *ssl, uint8_t *buf, int len, int peek); void dtls1_read_close_notify(SSL *ssl); int dtls1_read_bytes(SSL *s, int type, uint8_t *buf, int len, int peek); @@ -968,11 +1116,9 @@ int ssl_do_client_cert_cb(SSL *s, X509 **px509, EVP_PKEY **ppkey); int ssl3_send_client_key_exchange(SSL *s); int ssl3_get_server_key_exchange(SSL *s); int ssl3_get_server_certificate(SSL *s); -int ssl3_check_cert_and_algorithm(SSL *s); int ssl3_send_next_proto(SSL *s); int ssl3_send_channel_id(SSL *s); - -int dtls1_client_hello(SSL *s); +int ssl3_verify_server_cert(SSL *s); /* some server-only functions */ int ssl3_get_initial_bytes(SSL *s); @@ -995,7 +1141,6 @@ void dtls1_free(SSL *s); long dtls1_get_message(SSL *s, int st1, int stn, int mt, long max, enum ssl_hash_message_t hash_message, int *ok); -int dtls1_get_record(SSL *s); int dtls1_dispatch_alert(SSL *s); int ssl_init_wbio_buffer(SSL *s, int push); @@ -1025,12 +1170,15 @@ int tls1_export_keying_material(SSL *s, uint8_t *out, size_t out_len, int tls1_alert_code(int code); int ssl3_alert_code(int code); -int ssl_check_srvr_ecc_cert_and_alg(X509 *x, SSL *s); - char ssl_early_callback_init(struct ssl_early_callback_ctx *ctx); int tls1_ec_curve_id2nid(uint16_t curve_id); int tls1_ec_nid2curve_id(uint16_t *out_curve_id, int nid); +/* tls1_ec_curve_id2name returns a human-readable name for the + * curve specified by the TLS curve id in |curve_id|. If the + * curve is unknown, it returns NULL. */ +const char* tls1_ec_curve_id2name(uint16_t curve_id); + /* tls1_check_curve parses ECParameters out of |cbs|, modifying it. It * checks the curve is one of our preferences and writes the * NamedCurve value to |*out_curve_id|. It returns one on success and @@ -1060,27 +1208,38 @@ int tls1_check_ec_tmp_key(SSL *s); int tls1_shared_list(SSL *s, const uint8_t *l1, size_t l1len, const uint8_t *l2, size_t l2len, int nmatch); -uint8_t *ssl_add_clienthello_tlsext(SSL *s, uint8_t *buf, uint8_t *limit, - size_t header_len); -uint8_t *ssl_add_serverhello_tlsext(SSL *s, uint8_t *buf, uint8_t *limit); +uint8_t *ssl_add_clienthello_tlsext(SSL *s, uint8_t *const buf, + uint8_t *const limit, size_t header_len); +uint8_t *ssl_add_serverhello_tlsext(SSL *s, uint8_t *const buf, + uint8_t *const limit); int ssl_parse_clienthello_tlsext(SSL *s, CBS *cbs); int ssl_parse_serverhello_tlsext(SSL *s, CBS *cbs); -int ssl_prepare_clienthello_tlsext(SSL *s); -int ssl_prepare_serverhello_tlsext(SSL *s); #define tlsext_tick_md EVP_sha256 -int tls1_process_ticket(SSL *s, const struct ssl_early_callback_ctx *ctx, - SSL_SESSION **ret); -int tls12_get_sigandhash(uint8_t *p, const EVP_PKEY *pk, const EVP_MD *md); -int tls12_get_sigid(const EVP_PKEY *pk); +/* tls_process_ticket processes the session ticket extension. On success, it + * sets |*out_session| to the decrypted session or NULL if the ticket was + * rejected. It sets |*out_send_ticket| to whether a new ticket should be sent + * at the end of the handshake. It returns one on success and zero on fatal + * error. */ +int tls_process_ticket(SSL *ssl, SSL_SESSION **out_session, + int *out_send_ticket, const uint8_t *ticket, + size_t ticket_len, const uint8_t *session_id, + size_t session_id_len); + +/* tls12_get_sigandhash assembles the SignatureAndHashAlgorithm corresponding to + * |ssl|'s private key and |md|. The two-byte value is written to |p|. It + * returns one on success and zero on failure. */ +int tls12_get_sigandhash(SSL *ssl, uint8_t *p, const EVP_MD *md); +int tls12_get_sigid(int pkey_type); const EVP_MD *tls12_get_hash(uint8_t hash_alg); -int tls1_channel_id_hash(EVP_MD_CTX *ctx, SSL *s); -int tls1_record_handshake_hashes_for_channel_id(SSL *s); +/* tls1_channel_id_hash computes the hash to be signed by Channel ID and writes + * it to |out|, which must contain at least |EVP_MAX_MD_SIZE| bytes. It returns + * one on success and zero on failure. */ +int tls1_channel_id_hash(SSL *ssl, uint8_t *out, size_t *out_len); -int tls1_set_sigalgs_list(CERT *c, const char *str, int client); -int tls1_set_sigalgs(CERT *c, const int *salg, size_t salglen, int client); +int tls1_record_handshake_hashes_for_channel_id(SSL *s); /* ssl_ctx_log_rsa_client_key_exchange logs |premaster| to |ctx|, if logging is * enabled. It returns one on success and zero on failure. The entry is @@ -1134,27 +1293,16 @@ int ssl3_is_version_enabled(SSL *s, uint16_t version); * the wire version except at API boundaries. */ uint16_t ssl3_version_from_wire(SSL *s, uint16_t wire_version); -int ssl_add_serverhello_renegotiate_ext(SSL *s, uint8_t *p, int *len, - int maxlen); -int ssl_parse_serverhello_renegotiate_ext(SSL *s, CBS *cbs, int *out_alert); -int ssl_add_clienthello_renegotiate_ext(SSL *s, uint8_t *p, int *len, - int maxlen); -int ssl_parse_clienthello_renegotiate_ext(SSL *s, CBS *cbs, int *out_alert); -uint32_t ssl_get_algorithm2(SSL *s); -int tls1_process_sigalgs(SSL *s, const CBS *sigalgs); +uint32_t ssl_get_algorithm_prf(SSL *s); +int tls1_parse_peer_sigalgs(SSL *s, const CBS *sigalgs); -/* tls1_choose_signing_digest returns a digest for use with |pkey| based on the - * peer's preferences recorded for |s| and the digests supported by |pkey|. */ -const EVP_MD *tls1_choose_signing_digest(SSL *s, EVP_PKEY *pkey); +/* tls1_choose_signing_digest returns a digest for use with |ssl|'s private key + * based on the peer's preferences the digests supported. */ +const EVP_MD *tls1_choose_signing_digest(SSL *ssl); size_t tls12_get_psigalgs(SSL *s, const uint8_t **psigs); int tls12_check_peer_sigalg(const EVP_MD **out_md, int *out_alert, SSL *s, CBS *cbs, EVP_PKEY *pkey); void ssl_set_client_disabled(SSL *s); -int ssl_add_clienthello_use_srtp_ext(SSL *s, uint8_t *p, int *len, int maxlen); -int ssl_parse_clienthello_use_srtp_ext(SSL *s, CBS *cbs, int *out_alert); -int ssl_add_serverhello_use_srtp_ext(SSL *s, uint8_t *p, int *len, int maxlen); -int ssl_parse_serverhello_use_srtp_ext(SSL *s, CBS *cbs, int *out_alert); - #endif /* OPENSSL_HEADER_SSL_INTERNAL_H */ diff --git a/src/ssl/s3_both.c b/src/ssl/s3_both.c index 06338b7..31e36c7 100644 --- a/src/ssl/s3_both.c +++ b/src/ssl/s3_both.c @@ -110,6 +110,8 @@ * ECC cipher suite support in OpenSSL originally developed by * SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project. */ +#include <openssl/ssl.h> + #include <assert.h> #include <limits.h> #include <stdio.h> @@ -242,7 +244,7 @@ int ssl3_get_finished(SSL *s, int a, int b) { * TODO(davidben): Is this check now redundant with SSL3_FLAGS_EXPECT_CCS? */ if (!s->s3->change_cipher_spec) { al = SSL_AD_UNEXPECTED_MESSAGE; - OPENSSL_PUT_ERROR(SSL, ssl3_get_finished, SSL_R_GOT_A_FIN_BEFORE_A_CCS); + OPENSSL_PUT_ERROR(SSL, SSL_R_GOT_A_FIN_BEFORE_A_CCS); goto f_err; } s->s3->change_cipher_spec = 0; @@ -252,13 +254,13 @@ int ssl3_get_finished(SSL *s, int a, int b) { if (finished_len != message_len) { al = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_get_finished, SSL_R_BAD_DIGEST_LENGTH); + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_DIGEST_LENGTH); goto f_err; } if (CRYPTO_memcmp(p, s->s3->tmp.peer_finish_md, finished_len) != 0) { al = SSL_AD_DECRYPT_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_get_finished, SSL_R_DIGEST_CHECK_FAILED); + OPENSSL_PUT_ERROR(SSL, SSL_R_DIGEST_CHECK_FAILED); goto f_err; } @@ -301,17 +303,11 @@ int ssl3_send_change_cipher_spec(SSL *s, int a, int b) { return ssl3_do_write(s, SSL3_RT_CHANGE_CIPHER_SPEC); } -int ssl3_output_cert_chain(SSL *s, CERT_PKEY *cpk) { +int ssl3_output_cert_chain(SSL *s) { uint8_t *p; unsigned long l = 3 + SSL_HM_HEADER_LENGTH(s); - if (cpk == NULL) { - /* TLSv1 sends a chain with nothing in it, instead of an alert. */ - if (!BUF_MEM_grow_clean(s->init_buf, l)) { - OPENSSL_PUT_ERROR(SSL, ssl3_output_cert_chain, ERR_R_BUF_LIB); - return 0; - } - } else if (!ssl_add_cert_chain(s, cpk, &l)) { + if (!ssl_add_cert_chain(s, &l)) { return 0; } @@ -340,7 +336,7 @@ long ssl3_get_message(SSL *s, int header_state, int body_state, int msg_type, s->s3->tmp.reuse_message = 0; if (msg_type >= 0 && s->s3->tmp.message_type != msg_type) { al = SSL_AD_UNEXPECTED_MESSAGE; - OPENSSL_PUT_ERROR(SSL, ssl3_get_message, SSL_R_UNEXPECTED_MESSAGE); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE); goto f_err; } *ok = 1; @@ -386,7 +382,7 @@ long ssl3_get_message(SSL *s, int header_state, int body_state, int msg_type, if (msg_type >= 0 && *p != msg_type) { al = SSL_AD_UNEXPECTED_MESSAGE; - OPENSSL_PUT_ERROR(SSL, ssl3_get_message, SSL_R_UNEXPECTED_MESSAGE); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE); goto f_err; } s->s3->tmp.message_type = *(p++); @@ -394,12 +390,12 @@ long ssl3_get_message(SSL *s, int header_state, int body_state, int msg_type, n2l3(p, l); if (l > (unsigned long)max) { al = SSL_AD_ILLEGAL_PARAMETER; - OPENSSL_PUT_ERROR(SSL, ssl3_get_message, SSL_R_EXCESSIVE_MESSAGE_SIZE); + OPENSSL_PUT_ERROR(SSL, SSL_R_EXCESSIVE_MESSAGE_SIZE); goto f_err; } if (l && !BUF_MEM_grow_clean(s->init_buf, l + 4)) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_message, ERR_R_BUF_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB); goto err; } s->s3->tmp.message_size = l; @@ -447,8 +443,8 @@ int ssl3_hash_current_message(SSL *s) { /* The handshake header (different size between DTLS and TLS) is included in * the hash. */ size_t header_len = s->init_msg - (uint8_t *)s->init_buf->data; - return ssl3_finish_mac(s, (uint8_t *)s->init_buf->data, - s->init_num + header_len); + return ssl3_update_handshake_hash(s, (uint8_t *)s->init_buf->data, + s->init_num + header_len); } /* ssl3_cert_verify_hash is documented as needing EVP_MAX_MD_SIZE because that @@ -457,30 +453,25 @@ OPENSSL_COMPILE_ASSERT(EVP_MAX_MD_SIZE > MD5_DIGEST_LENGTH + SHA_DIGEST_LENGTH, combined_tls_hash_fits_in_max); int ssl3_cert_verify_hash(SSL *s, uint8_t *out, size_t *out_len, - const EVP_MD **out_md, EVP_PKEY *pkey) { + const EVP_MD **out_md, int pkey_type) { /* For TLS v1.2 send signature algorithm and signature using * agreed digest and cached handshake records. Otherwise, use * SHA1 or MD5 + SHA1 depending on key type. */ if (SSL_USE_SIGALGS(s)) { - const uint8_t *hdata; - size_t hdatalen; EVP_MD_CTX mctx; unsigned len; - if (!BIO_mem_contents(s->s3->handshake_buffer, &hdata, &hdatalen)) { - OPENSSL_PUT_ERROR(SSL, ssl3_cert_verify_hash, ERR_R_INTERNAL_ERROR); - return 0; - } EVP_MD_CTX_init(&mctx); if (!EVP_DigestInit_ex(&mctx, *out_md, NULL) || - !EVP_DigestUpdate(&mctx, hdata, hdatalen) || + !EVP_DigestUpdate(&mctx, s->s3->handshake_buffer->data, + s->s3->handshake_buffer->length) || !EVP_DigestFinal(&mctx, out, &len)) { - OPENSSL_PUT_ERROR(SSL, ssl3_cert_verify_hash, ERR_R_EVP_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_EVP_LIB); EVP_MD_CTX_cleanup(&mctx); return 0; } *out_len = len; - } else if (pkey->type == EVP_PKEY_RSA) { + } else if (pkey_type == EVP_PKEY_RSA) { if (s->enc_method->cert_verify_mac(s, NID_md5, out) == 0 || s->enc_method->cert_verify_mac(s, NID_sha1, out + MD5_DIGEST_LENGTH) == 0) { @@ -488,31 +479,20 @@ int ssl3_cert_verify_hash(SSL *s, uint8_t *out, size_t *out_len, } *out_len = MD5_DIGEST_LENGTH + SHA_DIGEST_LENGTH; *out_md = EVP_md5_sha1(); - } else if (pkey->type == EVP_PKEY_EC) { + } else if (pkey_type == EVP_PKEY_EC) { if (s->enc_method->cert_verify_mac(s, NID_sha1, out) == 0) { return 0; } *out_len = SHA_DIGEST_LENGTH; *out_md = EVP_sha1(); } else { - OPENSSL_PUT_ERROR(SSL, ssl3_cert_verify_hash, ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return 0; } return 1; } -int ssl_cert_type(EVP_PKEY *pkey) { - switch (pkey->type) { - case EVP_PKEY_RSA: - return SSL_PKEY_RSA_ENC; - case EVP_PKEY_EC: - return SSL_PKEY_ECC; - default: - return -1; - } -} - int ssl_verify_alarm_type(long type) { int al; @@ -581,92 +561,6 @@ int ssl_verify_alarm_type(long type) { return al; } -int ssl3_setup_read_buffer(SSL *s) { - uint8_t *p; - size_t len, align = 0, headerlen; - - if (SSL_IS_DTLS(s)) { - headerlen = DTLS1_RT_HEADER_LENGTH; - } else { - headerlen = SSL3_RT_HEADER_LENGTH; - } - -#if defined(SSL3_ALIGN_PAYLOAD) && SSL3_ALIGN_PAYLOAD != 0 - align = (-SSL3_RT_HEADER_LENGTH) & (SSL3_ALIGN_PAYLOAD - 1); -#endif - - if (s->s3->rbuf.buf == NULL) { - len = SSL3_RT_MAX_ENCRYPTED_LENGTH + headerlen + align; - if (s->options & SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER) { - s->s3->init_extra = 1; - len += SSL3_RT_MAX_EXTRA; - } - p = OPENSSL_malloc(len); - if (p == NULL) { - goto err; - } - s->s3->rbuf.buf = p; - s->s3->rbuf.len = len; - } - - s->packet = &s->s3->rbuf.buf[0]; - return 1; - -err: - OPENSSL_PUT_ERROR(SSL, ssl3_setup_read_buffer, ERR_R_MALLOC_FAILURE); - return 0; -} - -int ssl3_setup_write_buffer(SSL *s) { - uint8_t *p; - size_t len, align = 0, headerlen; - - if (SSL_IS_DTLS(s)) { - headerlen = DTLS1_RT_HEADER_LENGTH + 1; - } else { - headerlen = SSL3_RT_HEADER_LENGTH; - } - -#if defined(SSL3_ALIGN_PAYLOAD) && SSL3_ALIGN_PAYLOAD != 0 - align = (-SSL3_RT_HEADER_LENGTH) & (SSL3_ALIGN_PAYLOAD - 1); -#endif - - if (s->s3->wbuf.buf == NULL) { - len = s->max_send_fragment + SSL3_RT_SEND_MAX_ENCRYPTED_OVERHEAD + - headerlen + align; - /* Account for 1/n-1 record splitting. */ - if (s->mode & SSL_MODE_CBC_RECORD_SPLITTING) { - len += headerlen + align + 1 + SSL3_RT_SEND_MAX_ENCRYPTED_OVERHEAD; - } - - p = OPENSSL_malloc(len); - if (p == NULL) { - goto err; - } - s->s3->wbuf.buf = p; - s->s3->wbuf.len = len; - } - - return 1; - -err: - OPENSSL_PUT_ERROR(SSL, ssl3_setup_write_buffer, ERR_R_MALLOC_FAILURE); - return 0; -} - -int ssl3_release_write_buffer(SSL *s) { - OPENSSL_free(s->s3->wbuf.buf); - s->s3->wbuf.buf = NULL; - return 1; -} - -int ssl3_release_read_buffer(SSL *s) { - OPENSSL_free(s->s3->rbuf.buf); - s->s3->rbuf.buf = NULL; - s->packet = NULL; - return 1; -} - int ssl_fill_hello_random(uint8_t *out, size_t len, int is_server) { if (is_server) { const uint32_t current_time = time(NULL); diff --git a/src/ssl/s3_clnt.c b/src/ssl/s3_clnt.c index d334e1d..559db72 100644 --- a/src/ssl/s3_clnt.c +++ b/src/ssl/s3_clnt.c @@ -148,21 +148,26 @@ * OTHERWISE. */ +#include <openssl/ssl.h> + #include <assert.h> #include <stdio.h> #include <string.h> +#include <openssl/bn.h> #include <openssl/buf.h> #include <openssl/bytestring.h> -#include <openssl/rand.h> -#include <openssl/obj.h> +#include <openssl/dh.h> +#include <openssl/ec_key.h> +#include <openssl/ecdsa.h> #include <openssl/err.h> #include <openssl/evp.h> -#include <openssl/mem.h> #include <openssl/md5.h> -#include <openssl/dh.h> -#include <openssl/bn.h> +#include <openssl/mem.h> +#include <openssl/obj.h> +#include <openssl/rand.h> #include <openssl/x509.h> +#include <openssl/x509v3.h> #include "internal.h" #include "../crypto/dh/internal.h" @@ -217,8 +222,8 @@ int ssl3_connect(SSL *s) { /* don't push the buffering BIO quite yet */ - if (!ssl3_init_finished_mac(s)) { - OPENSSL_PUT_ERROR(SSL, ssl3_connect, ERR_R_INTERNAL_ERROR); + if (!ssl3_init_handshake_buffer(s)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); ret = -1; goto end; } @@ -273,7 +278,7 @@ int ssl3_connect(SSL *s) { if (s->s3->tmp.certificate_status_expected) { s->state = SSL3_ST_CR_CERT_STATUS_A; } else { - s->state = SSL3_ST_CR_KEY_EXCH_A; + s->state = SSL3_ST_VERIFY_SERVER_CERT; } } else { skip = 1; @@ -282,6 +287,16 @@ int ssl3_connect(SSL *s) { s->init_num = 0; break; + case SSL3_ST_VERIFY_SERVER_CERT: + ret = ssl3_verify_server_cert(s); + if (ret <= 0) { + goto end; + } + + s->state = SSL3_ST_CR_KEY_EXCH_A; + s->init_num = 0; + break; + case SSL3_ST_CR_KEY_EXCH_A: case SSL3_ST_CR_KEY_EXCH_B: ret = ssl3_get_server_key_exchange(s); @@ -290,13 +305,6 @@ int ssl3_connect(SSL *s) { } s->state = SSL3_ST_CR_CERT_REQ_A; s->init_num = 0; - - /* at this point we check that we have the - * required stuff from the server */ - if (!ssl3_check_cert_and_algorithm(s)) { - ret = -1; - goto end; - } break; case SSL3_ST_CR_CERT_REQ_A: @@ -356,6 +364,7 @@ int ssl3_connect(SSL *s) { case SSL3_ST_CW_CERT_VRFY_A: case SSL3_ST_CW_CERT_VRFY_B: + case SSL3_ST_CW_CERT_VRFY_C: ret = ssl3_send_cert_verify(s); if (ret <= 0) { goto end; @@ -433,11 +442,9 @@ int ssl3_connect(SSL *s) { * record the handshake hashes at this point in the session so that * any resumption of this session with ChannelID can sign those * hashes. */ - if (s->s3->tlsext_channel_id_new) { - ret = tls1_record_handshake_hashes_for_channel_id(s); - if (ret <= 0) { - goto end; - } + ret = tls1_record_handshake_hashes_for_channel_id(s); + if (ret <= 0) { + goto end; } if ((SSL_get_mode(s) & SSL_MODE_ENABLE_FALSE_START) && ssl3_can_false_start(s) && @@ -473,7 +480,7 @@ int ssl3_connect(SSL *s) { if (ret <= 0) { goto end; } - s->state = SSL3_ST_CR_KEY_EXCH_A; + s->state = SSL3_ST_VERIFY_SERVER_CERT; s->init_num = 0; break; @@ -536,11 +543,16 @@ int ssl3_connect(SSL *s) { /* Remove write buffering now. */ ssl_free_wbio_buffer(s); + const int is_initial_handshake = !s->s3->initial_handshake_complete; + s->init_num = 0; s->s3->tmp.in_false_start = 0; s->s3->initial_handshake_complete = 1; - ssl_update_cache(s, SSL_SESS_CACHE_CLIENT); + if (is_initial_handshake) { + /* Renegotiations do not participate in session resumption. */ + ssl_update_cache(s, SSL_SESS_CACHE_CLIENT); + } ret = 1; /* s->server=0; */ @@ -552,7 +564,7 @@ int ssl3_connect(SSL *s) { goto end; default: - OPENSSL_PUT_ERROR(SSL, ssl3_connect, SSL_R_UNKNOWN_STATE); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_STATE); ret = -1; goto end; } @@ -588,7 +600,7 @@ int ssl3_send_client_hello(SSL *s) { uint16_t max_version = ssl3_get_max_client_version(s); /* Disabling all versions is silly: return an error. */ if (max_version == 0) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_hello, SSL_R_WRONG_SSL_VERSION); + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SSL_VERSION); goto err; } s->version = max_version; @@ -661,7 +673,7 @@ int ssl3_send_client_hello(SSL *s) { *(p++) = i; if (i != 0) { if (i > (int)sizeof(s->session->session_id)) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_hello, ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); goto err; } memcpy(p, s->session->session_id, i); @@ -671,7 +683,7 @@ int ssl3_send_client_hello(SSL *s) { /* cookie stuff for DTLS */ if (SSL_IS_DTLS(s)) { if (s->d1->cookie_len > sizeof(s->d1->cookie)) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_hello, ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); goto err; } *(p++) = s->d1->cookie_len; @@ -682,8 +694,7 @@ int ssl3_send_client_hello(SSL *s) { /* Ciphers supported */ i = ssl_cipher_list_to_bytes(s, SSL_get_ciphers(s), &p[2]); if (i == 0) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_hello, - SSL_R_NO_CIPHERS_AVAILABLE); + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CIPHERS_AVAILABLE); goto err; } s2n(i, p); @@ -694,15 +705,10 @@ int ssl3_send_client_hello(SSL *s) { *(p++) = 0; /* Add the NULL method */ /* TLS extensions*/ - if (ssl_prepare_clienthello_tlsext(s) <= 0) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_hello, SSL_R_CLIENTHELLO_TLSEXT); - goto err; - } - p = ssl_add_clienthello_tlsext(s, p, buf + SSL3_RT_MAX_PLAIN_LENGTH, p - buf); if (p == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_hello, ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); goto err; } @@ -746,8 +752,7 @@ int ssl3_get_server_hello(SSL *s) { * parameters. Note: this error code comes after the original one. * * See https://crbug.com/446505. */ - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_hello, - SSL_R_HANDSHAKE_FAILURE_ON_CLIENT_HELLO); + OPENSSL_PUT_ERROR(SSL, SSL_R_HANDSHAKE_FAILURE_ON_CLIENT_HELLO); } return n; } @@ -761,14 +766,14 @@ int ssl3_get_server_hello(SSL *s) { !CBS_get_u16(&server_hello, &cipher_suite) || !CBS_get_u8(&server_hello, &compression_method)) { al = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_hello, SSL_R_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); goto f_err; } assert(s->s3->have_version == s->s3->initial_handshake_complete); if (!s->s3->have_version) { if (!ssl3_is_version_enabled(s, server_version)) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_hello, SSL_R_UNSUPPORTED_PROTOCOL); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL); s->version = server_version; /* Mark the version as fixed so the record-layer version is not clamped * to TLS 1.0. */ @@ -783,7 +788,7 @@ int ssl3_get_server_hello(SSL *s) { * fixed. Begin enforcing the record-layer version. */ s->s3->have_version = 1; } else if (server_version != s->version) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_hello, SSL_R_WRONG_SSL_VERSION); + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SSL_VERSION); al = SSL_AD_PROTOCOL_VERSION; goto f_err; } @@ -799,7 +804,7 @@ int ssl3_get_server_hello(SSL *s) { memcmp(s->session->sid_ctx, s->sid_ctx, s->sid_ctx_length)) { /* actually a client application bug */ al = SSL_AD_ILLEGAL_PARAMETER; - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_hello, + OPENSSL_PUT_ERROR(SSL, SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT); goto f_err; } @@ -820,8 +825,7 @@ int ssl3_get_server_hello(SSL *s) { if (c == NULL) { /* unknown cipher */ al = SSL_AD_ILLEGAL_PARAMETER; - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_hello, - SSL_R_UNKNOWN_CIPHER_RETURNED); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CIPHER_RETURNED); goto f_err; } /* ct->mask_ssl was computed from client capabilities. Now @@ -837,7 +841,7 @@ int ssl3_get_server_hello(SSL *s) { (c->algorithm_mkey & ct->mask_k) || (c->algorithm_auth & ct->mask_a)) { al = SSL_AD_ILLEGAL_PARAMETER; - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_hello, SSL_R_WRONG_CIPHER_RETURNED); + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED); goto f_err; } @@ -845,45 +849,46 @@ int ssl3_get_server_hello(SSL *s) { if (!sk_SSL_CIPHER_find(sk, NULL, c)) { /* we did not say we would use this cipher */ al = SSL_AD_ILLEGAL_PARAMETER; - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_hello, SSL_R_WRONG_CIPHER_RETURNED); + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED); goto f_err; } if (s->hit) { if (s->session->cipher != c) { al = SSL_AD_ILLEGAL_PARAMETER; - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_hello, - SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED); + OPENSSL_PUT_ERROR(SSL, SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED); goto f_err; } if (s->session->ssl_version != s->version) { al = SSL_AD_ILLEGAL_PARAMETER; - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_hello, - SSL_R_OLD_SESSION_VERSION_NOT_RETURNED); + OPENSSL_PUT_ERROR(SSL, SSL_R_OLD_SESSION_VERSION_NOT_RETURNED); goto f_err; } } s->s3->tmp.new_cipher = c; + /* Now that the cipher is known, initialize the handshake hash. */ + if (!ssl3_init_handshake_hash(s)) { + goto f_err; + } + /* If doing a full handshake with TLS 1.2, the server may request a client * certificate which requires hashing the handshake transcript under a - * different hash. Otherwise, release the handshake buffer. */ - if ((!SSL_USE_SIGALGS(s) || s->hit) && - !ssl3_digest_cached_records(s, free_handshake_buffer)) { - goto f_err; + * different hash. Otherwise, the handshake buffer may be released. */ + if (!SSL_USE_SIGALGS(s) || s->hit) { + ssl3_free_handshake_buffer(s); } /* Only the NULL compression algorithm is supported. */ if (compression_method != 0) { al = SSL_AD_ILLEGAL_PARAMETER; - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_hello, - SSL_R_UNSUPPORTED_COMPRESSION_ALGORITHM); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_COMPRESSION_ALGORITHM); goto f_err; } /* TLS extensions */ if (!ssl_parse_serverhello_tlsext(s, &server_hello)) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_hello, SSL_R_PARSE_TLSEXT); + OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT); goto err; } @@ -891,7 +896,7 @@ int ssl3_get_server_hello(SSL *s) { if (CBS_len(&server_hello) != 0) { /* wrong packet length */ al = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_hello, SSL_R_BAD_PACKET_LENGTH); + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_PACKET_LENGTH); goto f_err; } @@ -899,11 +904,9 @@ int ssl3_get_server_hello(SSL *s) { s->s3->tmp.extended_master_secret != s->session->extended_master_secret) { al = SSL_AD_HANDSHAKE_FAILURE; if (s->session->extended_master_secret) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_hello, - SSL_R_RESUMED_EMS_SESSION_WITHOUT_EMS_EXTENSION); + OPENSSL_PUT_ERROR(SSL, SSL_R_RESUMED_EMS_SESSION_WITHOUT_EMS_EXTENSION); } else { - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_hello, - SSL_R_RESUMED_NON_EMS_SESSION_WITH_EMS_EXTENSION); + OPENSSL_PUT_ERROR(SSL, SSL_R_RESUMED_NON_EMS_SESSION_WITH_EMS_EXTENSION); } goto f_err; } @@ -916,12 +919,49 @@ err: return -1; } +/* ssl3_check_certificate_for_cipher returns one if |leaf| is a suitable server + * certificate type for |cipher|. Otherwise, it returns zero and pushes an error + * on the error queue. */ +static int ssl3_check_certificate_for_cipher(X509 *leaf, + const SSL_CIPHER *cipher) { + int ret = 0; + EVP_PKEY *pkey = X509_get_pubkey(leaf); + if (pkey == NULL) { + goto err; + } + + /* Check the certificate's type matches the cipher. */ + int expected_type = ssl_cipher_get_key_type(cipher); + assert(expected_type != EVP_PKEY_NONE); + if (pkey->type != expected_type) { + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CERTIFICATE_TYPE); + goto err; + } + + /* TODO(davidben): This behavior is preserved from upstream. Should key usages + * be checked in other cases as well? */ + if (cipher->algorithm_auth & SSL_aECDSA) { + /* This call populates the ex_flags field correctly */ + X509_check_purpose(leaf, -1, 0); + if ((leaf->ex_flags & EXFLAG_KUSAGE) && + !(leaf->ex_kusage & X509v3_KU_DIGITAL_SIGNATURE)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_ECC_CERT_NOT_FOR_SIGNING); + goto err; + } + } + + ret = 1; + +err: + EVP_PKEY_free(pkey); + return ret; +} + int ssl3_get_server_certificate(SSL *s) { - int al, i, ok, ret = -1; + int al, ok, ret = -1; unsigned long n; X509 *x = NULL; STACK_OF(X509) *sk = NULL; - SESS_CERT *sc; EVP_PKEY *pkey = NULL; CBS cbs, certificate_list; const uint8_t *data; @@ -938,14 +978,15 @@ int ssl3_get_server_certificate(SSL *s) { sk = sk_X509_new_null(); if (sk == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_certificate, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } if (!CBS_get_u24_length_prefixed(&cbs, &certificate_list) || + CBS_len(&certificate_list) == 0 || CBS_len(&cbs) != 0) { al = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_certificate, SSL_R_LENGTH_MISMATCH); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); goto f_err; } @@ -953,90 +994,45 @@ int ssl3_get_server_certificate(SSL *s) { CBS certificate; if (!CBS_get_u24_length_prefixed(&certificate_list, &certificate)) { al = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_certificate, - SSL_R_CERT_LENGTH_MISMATCH); + OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_LENGTH_MISMATCH); goto f_err; } data = CBS_data(&certificate); x = d2i_X509(NULL, &data, CBS_len(&certificate)); if (x == NULL) { al = SSL_AD_BAD_CERTIFICATE; - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_certificate, ERR_R_ASN1_LIB); + 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, ssl3_get_server_certificate, - SSL_R_CERT_LENGTH_MISMATCH); + OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_LENGTH_MISMATCH); goto f_err; } if (!sk_X509_push(sk, x)) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_certificate, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } x = NULL; } - i = ssl_verify_cert_chain(s, sk); - if (s->verify_mode != SSL_VERIFY_NONE && i <= 0) { - al = ssl_verify_alarm_type(s->verify_result); - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_certificate, - SSL_R_CERTIFICATE_VERIFY_FAILED); + X509 *leaf = sk_X509_value(sk, 0); + if (!ssl3_check_certificate_for_cipher(leaf, s->s3->tmp.new_cipher)) { + al = SSL_AD_ILLEGAL_PARAMETER; goto f_err; } - ERR_clear_error(); /* but we keep s->verify_result */ - - sc = ssl_sess_cert_new(); - if (sc == NULL) { - goto err; - } - ssl_sess_cert_free(s->session->sess_cert); - s->session->sess_cert = sc; - - sc->cert_chain = sk; - /* Inconsistency alert: cert_chain does include the peer's certificate, which - * we don't include in s3_srvr.c */ - x = sk_X509_value(sk, 0); + /* NOTE: Unlike the server half, the client's copy of |cert_chain| includes + * the leaf. */ + sk_X509_pop_free(s->session->cert_chain, X509_free); + s->session->cert_chain = sk; sk = NULL; - /* VRS 19990621: possible memory leak; sk=null ==> !sk_pop_free() @end*/ - - pkey = X509_get_pubkey(x); - - if (pkey == NULL || EVP_PKEY_missing_parameters(pkey)) { - x = NULL; - al = SSL3_AL_FATAL; - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_certificate, - SSL_R_UNABLE_TO_FIND_PUBLIC_KEY_PARAMETERS); - goto f_err; - } - - i = ssl_cert_type(pkey); - if (i < 0) { - x = NULL; - al = SSL3_AL_FATAL; - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_certificate, - SSL_R_UNKNOWN_CERTIFICATE_TYPE); - goto f_err; - } - - int exp_idx = ssl_cipher_get_cert_index(s->s3->tmp.new_cipher); - if (exp_idx >= 0 && i != exp_idx) { - x = NULL; - al = SSL_AD_ILLEGAL_PARAMETER; - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_certificate, - SSL_R_WRONG_CERTIFICATE_TYPE); - goto f_err; - } - X509_free(sc->peer_cert); - sc->peer_cert = X509_up_ref(x); X509_free(s->session->peer); - s->session->peer = X509_up_ref(x); + s->session->peer = X509_up_ref(leaf); s->session->verify_result = s->verify_result; - x = NULL; ret = 1; if (0) { @@ -1075,25 +1071,14 @@ int ssl3_get_server_key_exchange(SSL *s) { if (s->s3->tmp.message_type != SSL3_MT_SERVER_KEY_EXCHANGE) { if (ssl_cipher_requires_server_key_exchange(s->s3->tmp.new_cipher)) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, - SSL_R_UNEXPECTED_MESSAGE); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE); ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); return -1; } - /* In plain PSK ciphersuite, ServerKeyExchange can be - omitted if no identity hint is sent. Set session->sess_cert anyway to - avoid problems later.*/ + /* In plain PSK ciphersuite, ServerKeyExchange may be omitted to send no + * identity hint. */ if (s->s3->tmp.new_cipher->algorithm_auth & SSL_aPSK) { - /* PSK ciphersuites that also send a Certificate would have already - * initialized |sess_cert|. */ - if (s->session->sess_cert == NULL) { - s->session->sess_cert = ssl_sess_cert_new(); - if (s->session->sess_cert == NULL) { - return -1; - } - } - /* TODO(davidben): This should be reset in one place with the rest of the * handshake state. */ OPENSSL_free(s->s3->tmp.peer_psk_identity_hint); @@ -1107,18 +1092,6 @@ int ssl3_get_server_key_exchange(SSL *s) { CBS_init(&server_key_exchange, s->init_msg, n); server_key_exchange_orig = server_key_exchange; - if (s->session->sess_cert != NULL) { - DH_free(s->session->sess_cert->peer_dh_tmp); - s->session->sess_cert->peer_dh_tmp = NULL; - EC_KEY_free(s->session->sess_cert->peer_ecdh_tmp); - s->session->sess_cert->peer_ecdh_tmp = NULL; - } else { - s->session->sess_cert = ssl_sess_cert_new(); - if (s->session->sess_cert == NULL) { - return -1; - } - } - alg_k = s->s3->tmp.new_cipher->algorithm_mkey; alg_a = s->s3->tmp.new_cipher->algorithm_auth; EVP_MD_CTX_init(&md_ctx); @@ -1130,7 +1103,7 @@ int ssl3_get_server_key_exchange(SSL *s) { if (!CBS_get_u16_length_prefixed(&server_key_exchange, &psk_identity_hint)) { al = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, SSL_R_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); goto f_err; } @@ -1144,16 +1117,14 @@ int ssl3_get_server_key_exchange(SSL *s) { if (CBS_len(&psk_identity_hint) > PSK_MAX_IDENTITY_LEN || CBS_contains_zero_byte(&psk_identity_hint)) { al = SSL_AD_HANDSHAKE_FAILURE; - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, - SSL_R_DATA_LENGTH_TOO_LONG); + OPENSSL_PUT_ERROR(SSL, SSL_R_DATA_LENGTH_TOO_LONG); goto f_err; } /* Save the identity hint as a C string. */ if (!CBS_strdup(&psk_identity_hint, &s->s3->tmp.peer_psk_identity_hint)) { al = SSL_AD_INTERNAL_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, - ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto f_err; } } @@ -1168,13 +1139,13 @@ int ssl3_get_server_key_exchange(SSL *s) { !CBS_get_u16_length_prefixed(&server_key_exchange, &dh_Ys) || CBS_len(&dh_Ys) == 0) { al = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, SSL_R_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); goto f_err; } dh = DH_new(); if (dh == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, ERR_R_DH_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_DH_LIB); goto err; } @@ -1182,22 +1153,17 @@ int ssl3_get_server_key_exchange(SSL *s) { (dh->g = BN_bin2bn(CBS_data(&dh_g), CBS_len(&dh_g), NULL)) == NULL || (dh->pub_key = BN_bin2bn(CBS_data(&dh_Ys), CBS_len(&dh_Ys), NULL)) == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, ERR_R_BN_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_BN_LIB); goto err; } - if (DH_num_bits(dh) < 1024) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, - SSL_R_BAD_DH_P_LENGTH); + s->session->key_exchange_info = DH_num_bits(dh); + if (s->session->key_exchange_info < 1024) { + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_DH_P_LENGTH); goto err; } - - if (alg_a & SSL_aRSA) { - pkey = X509_get_pubkey(s->session->sess_cert->peer_cert); - } - /* else anonymous DH, so no certificate or pkey. */ - - s->session->sess_cert->peer_dh_tmp = dh; + DH_free(s->s3->tmp.peer_dh_tmp); + s->s3->tmp.peer_dh_tmp = dh; dh = NULL; } else if (alg_k & SSL_kECDHE) { uint16_t curve_id; @@ -1210,22 +1176,21 @@ int ssl3_get_server_key_exchange(SSL *s) { * invalid curve. */ if (!tls1_check_curve(s, &server_key_exchange, &curve_id)) { al = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, SSL_R_WRONG_CURVE); + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE); goto f_err; } curve_nid = tls1_ec_curve_id2nid(curve_id); if (curve_nid == 0) { al = SSL_AD_INTERNAL_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, - SSL_R_UNABLE_TO_FIND_ECDH_PARAMETERS); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNABLE_TO_FIND_ECDH_PARAMETERS); goto f_err; } ecdh = EC_KEY_new_by_curve_name(curve_nid); + s->session->key_exchange_info = curve_id; if (ecdh == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, - ERR_R_EC_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_EC_LIB); goto err; } @@ -1234,35 +1199,25 @@ int ssl3_get_server_key_exchange(SSL *s) { /* Next, get the encoded ECPoint */ if (!CBS_get_u8_length_prefixed(&server_key_exchange, &point)) { al = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, SSL_R_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); goto f_err; } if (((srvr_ecpoint = EC_POINT_new(group)) == NULL) || ((bn_ctx = BN_CTX_new()) == NULL)) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, - ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } if (!EC_POINT_oct2point(group, srvr_ecpoint, CBS_data(&point), CBS_len(&point), bn_ctx)) { al = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, SSL_R_BAD_ECPOINT); + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); goto f_err; } - - /* The ECC/TLS specification does not mention the use of DSA to sign - * ECParameters in the server key exchange message. We do support RSA and - * ECDSA. */ - if (alg_a & (SSL_aRSA|SSL_aECDSA)) { - pkey = X509_get_pubkey(s->session->sess_cert->peer_cert); - } else { - /* Otherwise this is ECDHE_PSK, so no public key. */ - assert(alg_a == SSL_aPSK); - } EC_KEY_set_public_key(ecdh, srvr_ecpoint); - s->session->sess_cert->peer_ecdh_tmp = ecdh; + EC_KEY_free(s->s3->tmp.peer_ecdh_tmp); + s->s3->tmp.peer_ecdh_tmp = ecdh; ecdh = NULL; BN_CTX_free(bn_ctx); bn_ctx = NULL; @@ -1270,8 +1225,7 @@ int ssl3_get_server_key_exchange(SSL *s) { srvr_ecpoint = NULL; } else if (!(alg_k & SSL_kPSK)) { al = SSL_AD_UNEXPECTED_MESSAGE; - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, - SSL_R_UNEXPECTED_MESSAGE); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE); goto f_err; } @@ -1281,9 +1235,12 @@ int ssl3_get_server_key_exchange(SSL *s) { CBS_init(¶meter, CBS_data(&server_key_exchange_orig), CBS_len(&server_key_exchange_orig) - CBS_len(&server_key_exchange)); - /* if it was signed, check the signature */ - if (pkey != NULL) { - CBS signature; + /* ServerKeyExchange should be signed by the server's public key. */ + if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher)) { + pkey = X509_get_pubkey(s->session->peer); + if (pkey == NULL) { + goto err; + } if (SSL_USE_SIGALGS(s)) { if (!tls12_check_peer_sigalg(&md, &al, s, &server_key_exchange, pkey)) { @@ -1296,10 +1253,11 @@ int ssl3_get_server_key_exchange(SSL *s) { } /* The last field in |server_key_exchange| is the signature. */ + CBS signature; if (!CBS_get_u16_length_prefixed(&server_key_exchange, &signature) || CBS_len(&server_key_exchange) != 0) { al = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, SSL_R_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); goto f_err; } @@ -1314,24 +1272,16 @@ int ssl3_get_server_key_exchange(SSL *s) { CBS_len(&signature))) { /* bad signature */ al = SSL_AD_DECRYPT_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, SSL_R_BAD_SIGNATURE); + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SIGNATURE); goto f_err; } } else { - if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher)) { - /* Might be wrong key type, check it */ - if (ssl3_check_cert_and_algorithm(s)) { - /* Otherwise this shouldn't happen */ - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, - ERR_R_INTERNAL_ERROR); - } - goto err; - } - /* still data left over */ + /* PSK ciphers are the only supported certificate-less ciphers. */ + assert(alg_a == SSL_aPSK); + if (CBS_len(&server_key_exchange) > 0) { al = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, - SSL_R_EXTRA_DATA_IN_MESSAGE); + OPENSSL_PUT_ERROR(SSL, SSL_R_EXTRA_DATA_IN_MESSAGE); goto f_err; } } @@ -1378,19 +1328,15 @@ int ssl3_get_certificate_request(SSL *s) { if (s->s3->tmp.message_type == SSL3_MT_SERVER_DONE) { s->s3->tmp.reuse_message = 1; - /* If we get here we don't need any cached handshake records as we wont be - * doing client auth. */ - if (s->s3->handshake_buffer && - !ssl3_digest_cached_records(s, free_handshake_buffer)) { - goto err; - } + /* If we get here we don't need the handshake buffer as we won't be doing + * client auth. */ + ssl3_free_handshake_buffer(s); return 1; } if (s->s3->tmp.message_type != SSL3_MT_CERTIFICATE_REQUEST) { ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); - OPENSSL_PUT_ERROR(SSL, ssl3_get_certificate_request, - SSL_R_WRONG_MESSAGE_TYPE); + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_MESSAGE_TYPE); goto err; } @@ -1398,14 +1344,14 @@ int ssl3_get_certificate_request(SSL *s) { ca_sk = sk_X509_NAME_new(ca_dn_cmp); if (ca_sk == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_certificate_request, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } /* get the certificate types */ if (!CBS_get_u8_length_prefixed(&cbs, &certificate_types)) { ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); - OPENSSL_PUT_ERROR(SSL, ssl3_get_certificate_request, SSL_R_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); goto err; } @@ -1417,16 +1363,10 @@ int ssl3_get_certificate_request(SSL *s) { if (SSL_USE_SIGALGS(s)) { CBS supported_signature_algorithms; - if (!CBS_get_u16_length_prefixed(&cbs, &supported_signature_algorithms)) { + if (!CBS_get_u16_length_prefixed(&cbs, &supported_signature_algorithms) || + !tls1_parse_peer_sigalgs(s, &supported_signature_algorithms)) { ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); - OPENSSL_PUT_ERROR(SSL, ssl3_get_certificate_request, SSL_R_DECODE_ERROR); - goto err; - } - - if (!tls1_process_sigalgs(s, &supported_signature_algorithms)) { - ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); - OPENSSL_PUT_ERROR(SSL, ssl3_get_certificate_request, - SSL_R_SIGNATURE_ALGORITHMS_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); goto err; } } @@ -1434,7 +1374,7 @@ int ssl3_get_certificate_request(SSL *s) { /* get the CA RDNs */ if (!CBS_get_u16_length_prefixed(&cbs, &certificate_authorities)) { ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); - OPENSSL_PUT_ERROR(SSL, ssl3_get_certificate_request, SSL_R_LENGTH_MISMATCH); + OPENSSL_PUT_ERROR(SSL, SSL_R_LENGTH_MISMATCH); goto err; } @@ -1443,8 +1383,7 @@ int ssl3_get_certificate_request(SSL *s) { if (!CBS_get_u16_length_prefixed(&certificate_authorities, &distinguished_name)) { ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); - OPENSSL_PUT_ERROR(SSL, ssl3_get_certificate_request, - SSL_R_CA_DN_TOO_LONG); + OPENSSL_PUT_ERROR(SSL, SSL_R_CA_DN_TOO_LONG); goto err; } @@ -1453,26 +1392,24 @@ int ssl3_get_certificate_request(SSL *s) { xn = d2i_X509_NAME(NULL, &data, CBS_len(&distinguished_name)); if (xn == NULL) { ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); - OPENSSL_PUT_ERROR(SSL, ssl3_get_certificate_request, ERR_R_ASN1_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_ASN1_LIB); goto err; } if (!CBS_skip(&distinguished_name, data - CBS_data(&distinguished_name))) { ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); - OPENSSL_PUT_ERROR(SSL, ssl3_get_certificate_request, ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); goto err; } if (CBS_len(&distinguished_name) != 0) { ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); - OPENSSL_PUT_ERROR(SSL, ssl3_get_certificate_request, - SSL_R_CA_DN_LENGTH_MISMATCH); + OPENSSL_PUT_ERROR(SSL, SSL_R_CA_DN_LENGTH_MISMATCH); goto err; } if (!sk_X509_NAME_push(ca_sk, xn)) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_certificate_request, - ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } } @@ -1516,18 +1453,9 @@ int ssl3_get_new_session_ticket(SSL *s) { OPENSSL_free(bytes); if (new_session == NULL) { /* This should never happen. */ - OPENSSL_PUT_ERROR(SSL, ssl3_get_new_session_ticket, ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); goto err; } - if (s->session->sess_cert != NULL) { - /* |sess_cert| is not serialized and must be duplicated explicitly. */ - assert(new_session->sess_cert == NULL); - new_session->sess_cert = ssl_sess_cert_dup(s->session->sess_cert); - if (new_session->sess_cert == NULL) { - SSL_SESSION_free(new_session); - goto err; - } - } SSL_SESSION_free(s->session); s->session = new_session; @@ -1540,13 +1468,13 @@ int ssl3_get_new_session_ticket(SSL *s) { !CBS_get_u16_length_prefixed(&new_session_ticket, &ticket) || CBS_len(&new_session_ticket) != 0) { al = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_get_new_session_ticket, SSL_R_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); goto f_err; } if (!CBS_stow(&ticket, &s->session->tlsext_tick, &s->session->tlsext_ticklen)) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_new_session_ticket, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } @@ -1594,14 +1522,14 @@ int ssl3_get_cert_status(SSL *s) { CBS_len(&ocsp_response) == 0 || CBS_len(&certificate_status) != 0) { al = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_get_cert_status, SSL_R_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); goto f_err; } if (!CBS_stow(&ocsp_response, &s->session->ocsp_response, &s->session->ocsp_response_length)) { al = SSL_AD_INTERNAL_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_get_cert_status, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto f_err; } return 1; @@ -1627,7 +1555,7 @@ int ssl3_get_server_done(SSL *s) { if (n > 0) { /* should contain no data */ ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); - OPENSSL_PUT_ERROR(SSL, ssl3_get_server_done, SSL_R_LENGTH_MISMATCH); + OPENSSL_PUT_ERROR(SSL, SSL_R_LENGTH_MISMATCH); return -1; } @@ -1665,8 +1593,7 @@ int ssl3_send_client_key_exchange(SSL *s) { size_t identity_len; if (s->psk_client_callback == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, - SSL_R_PSK_NO_CLIENT_CB); + OPENSSL_PUT_ERROR(SSL, SSL_R_PSK_NO_CLIENT_CB); goto err; } @@ -1675,28 +1602,24 @@ int ssl3_send_client_key_exchange(SSL *s) { s->psk_client_callback(s, s->s3->tmp.peer_psk_identity_hint, identity, sizeof(identity), psk, sizeof(psk)); if (psk_len > PSK_MAX_PSK_LEN) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, - ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); goto err; } else if (psk_len == 0) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, - SSL_R_PSK_IDENTITY_NOT_FOUND); + OPENSSL_PUT_ERROR(SSL, SSL_R_PSK_IDENTITY_NOT_FOUND); ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE); goto err; } identity_len = OPENSSL_strnlen(identity, sizeof(identity)); if (identity_len > PSK_MAX_IDENTITY_LEN) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, - ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); goto err; } OPENSSL_free(s->session->psk_identity); s->session->psk_identity = BUF_strdup(identity); if (s->session->psk_identity == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, - ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } @@ -1715,28 +1638,20 @@ int ssl3_send_client_key_exchange(SSL *s) { pms_len = SSL_MAX_MASTER_KEY_LENGTH; pms = OPENSSL_malloc(pms_len); if (pms == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, - ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } - if (s->session->sess_cert == NULL) { - /* We should always have a server certificate with SSL_kRSA. */ - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, - ERR_R_INTERNAL_ERROR); - goto err; - } - - pkey = X509_get_pubkey(s->session->sess_cert->peer_cert); + pkey = X509_get_pubkey(s->session->peer); if (pkey == NULL || pkey->type != EVP_PKEY_RSA || pkey->pkey.rsa == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, - ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); EVP_PKEY_free(pkey); goto err; } + s->session->key_exchange_info = EVP_PKEY_bits(pkey); rsa = pkey->pkey.rsa; EVP_PKEY_free(pkey); @@ -1756,8 +1671,7 @@ int ssl3_send_client_key_exchange(SSL *s) { } if (!RSA_encrypt(rsa, &enc_pms_len, p, RSA_size(rsa), pms, pms_len, RSA_PKCS1_PADDING)) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, - SSL_R_BAD_RSA_ENCRYPT); + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_RSA_ENCRYPT); goto err; } n += enc_pms_len; @@ -1774,32 +1688,23 @@ int ssl3_send_client_key_exchange(SSL *s) { } } else if (alg_k & SSL_kDHE) { DH *dh_srvr, *dh_clnt; - SESS_CERT *scert = s->session->sess_cert; int dh_len; size_t pub_len; - if (scert == NULL) { - ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, - SSL_R_UNEXPECTED_MESSAGE); - goto err; - } - - if (scert->peer_dh_tmp == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, - ERR_R_INTERNAL_ERROR); + if (s->s3->tmp.peer_dh_tmp == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); goto err; } - dh_srvr = scert->peer_dh_tmp; + dh_srvr = s->s3->tmp.peer_dh_tmp; /* generate a new random key */ dh_clnt = DHparams_dup(dh_srvr); if (dh_clnt == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_DH_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_DH_LIB); goto err; } if (!DH_generate_key(dh_clnt)) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_DH_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_DH_LIB); DH_free(dh_clnt); goto err; } @@ -1807,15 +1712,14 @@ int ssl3_send_client_key_exchange(SSL *s) { pms_len = DH_size(dh_clnt); pms = OPENSSL_malloc(pms_len); if (pms == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, - ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); DH_free(dh_clnt); goto err; } dh_len = DH_compute_key(pms, dh_srvr->pub_key, dh_clnt); if (dh_len <= 0) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_DH_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_DH_LIB); DH_free(dh_clnt); goto err; } @@ -1833,64 +1737,53 @@ int ssl3_send_client_key_exchange(SSL *s) { EC_KEY *tkey; int field_size = 0, ecdh_len; - if (s->session->sess_cert == NULL) { - ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, - SSL_R_UNEXPECTED_MESSAGE); + if (s->s3->tmp.peer_ecdh_tmp == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); goto err; } - if (s->session->sess_cert->peer_ecdh_tmp == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, - ERR_R_INTERNAL_ERROR); - goto err; - } - - tkey = s->session->sess_cert->peer_ecdh_tmp; + tkey = s->s3->tmp.peer_ecdh_tmp; srvr_group = EC_KEY_get0_group(tkey); srvr_ecpoint = EC_KEY_get0_public_key(tkey); if (srvr_group == NULL || srvr_ecpoint == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, - ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); goto err; } clnt_ecdh = EC_KEY_new(); if (clnt_ecdh == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, - ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } if (!EC_KEY_set_group(clnt_ecdh, srvr_group)) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_EC_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_EC_LIB); goto err; } /* Generate a new ECDH key pair */ if (!EC_KEY_generate_key(clnt_ecdh)) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_ECDH_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_ECDH_LIB); goto err; } field_size = EC_GROUP_get_degree(srvr_group); if (field_size <= 0) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_ECDH_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_ECDH_LIB); goto err; } pms_len = (field_size + 7) / 8; pms = OPENSSL_malloc(pms_len); if (pms == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, - ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } ecdh_len = ECDH_compute_key(pms, pms_len, srvr_ecpoint, clnt_ecdh, NULL); if (ecdh_len <= 0) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_ECDH_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_ECDH_LIB); goto err; } pms_len = ecdh_len; @@ -1904,8 +1797,7 @@ int ssl3_send_client_key_exchange(SSL *s) { (uint8_t *)OPENSSL_malloc(encoded_pt_len * sizeof(uint8_t)); bn_ctx = BN_CTX_new(); if (encodedPoint == NULL || bn_ctx == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, - ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } @@ -1938,15 +1830,13 @@ int ssl3_send_client_key_exchange(SSL *s) { pms_len = psk_len; pms = OPENSSL_malloc(pms_len); if (pms == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, - ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } memset(pms, 0, pms_len); } else { ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE); - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, - ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); goto err; } @@ -1957,19 +1847,15 @@ int ssl3_send_client_key_exchange(SSL *s) { uint8_t *new_pms; size_t new_pms_len; - if (!CBB_init(&cbb, 2 + psk_len + 2 + pms_len)) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, - ERR_R_MALLOC_FAILURE); - goto err; - } - if (!CBB_add_u16_length_prefixed(&cbb, &child) || + CBB_zero(&cbb); + if (!CBB_init(&cbb, 2 + psk_len + 2 + pms_len) || + !CBB_add_u16_length_prefixed(&cbb, &child) || !CBB_add_bytes(&child, pms, pms_len) || !CBB_add_u16_length_prefixed(&cbb, &child) || !CBB_add_bytes(&child, psk, psk_len) || !CBB_finish(&cbb, &new_pms, &new_pms_len)) { CBB_cleanup(&cbb); - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, - ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } OPENSSL_cleanse(pms, pms_len); @@ -2011,87 +1897,87 @@ err: } int ssl3_send_cert_verify(SSL *s) { - uint8_t *buf, *p; - const EVP_MD *md = NULL; - uint8_t digest[EVP_MAX_MD_SIZE]; - size_t digest_length; - EVP_PKEY *pkey; - EVP_PKEY_CTX *pctx = NULL; - size_t signature_length = 0; - unsigned long n = 0; - - buf = (uint8_t *)s->init_buf->data; - - if (s->state == SSL3_ST_CW_CERT_VRFY_A) { - p = ssl_handshake_start(s); - pkey = s->cert->key->privatekey; - - /* Write out the digest type if needbe. */ - if (SSL_USE_SIGALGS(s)) { - md = tls1_choose_signing_digest(s, pkey); - if (!tls12_get_sigandhash(p, pkey, md)) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_cert_verify, ERR_R_INTERNAL_ERROR); - goto err; + if (s->state == SSL3_ST_CW_CERT_VRFY_A || + s->state == SSL3_ST_CW_CERT_VRFY_B) { + enum ssl_private_key_result_t sign_result; + uint8_t *p = ssl_handshake_start(s); + size_t signature_length = 0; + unsigned long n = 0; + assert(ssl_has_private_key(s)); + + if (s->state == SSL3_ST_CW_CERT_VRFY_A) { + uint8_t *buf = (uint8_t *)s->init_buf->data; + const EVP_MD *md = NULL; + uint8_t digest[EVP_MAX_MD_SIZE]; + size_t digest_length; + + /* Write out the digest type if need be. */ + if (SSL_USE_SIGALGS(s)) { + md = tls1_choose_signing_digest(s); + if (!tls12_get_sigandhash(s, p, md)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return -1; + } + p += 2; + n += 2; } - p += 2; - n += 2; - } - /* Compute the digest. */ - if (!ssl3_cert_verify_hash(s, digest, &digest_length, &md, pkey)) { - goto err; - } + /* Compute the digest. */ + const int pkey_type = ssl_private_key_type(s); + if (!ssl3_cert_verify_hash(s, digest, &digest_length, &md, pkey_type)) { + return -1; + } - /* The handshake buffer is no longer necessary. */ - if (s->s3->handshake_buffer && - !ssl3_digest_cached_records(s, free_handshake_buffer)) { - goto err; - } + /* The handshake buffer is no longer necessary. */ + ssl3_free_handshake_buffer(s); - /* Sign the digest. */ - pctx = EVP_PKEY_CTX_new(pkey, NULL); - if (pctx == NULL) { - goto err; - } + /* Sign the digest. */ + signature_length = ssl_private_key_max_signature_len(s); + if (p + 2 + signature_length > buf + SSL3_RT_MAX_PLAIN_LENGTH) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DATA_LENGTH_TOO_LONG); + return -1; + } - /* Initialize the EVP_PKEY_CTX and determine the size of the signature. */ - if (!EVP_PKEY_sign_init(pctx) || !EVP_PKEY_CTX_set_signature_md(pctx, md) || - !EVP_PKEY_sign(pctx, NULL, &signature_length, digest, digest_length)) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_cert_verify, ERR_R_EVP_LIB); - goto err; + s->rwstate = SSL_PRIVATE_KEY_OPERATION; + sign_result = ssl_private_key_sign(s, &p[2], &signature_length, + signature_length, md, digest, + digest_length); + } else { + if (SSL_USE_SIGALGS(s)) { + /* The digest has already been selected and written. */ + p += 2; + n += 2; + } + signature_length = ssl_private_key_max_signature_len(s); + s->rwstate = SSL_PRIVATE_KEY_OPERATION; + sign_result = ssl_private_key_sign_complete(s, &p[2], &signature_length, + signature_length); } - if (p + 2 + signature_length > buf + SSL3_RT_MAX_PLAIN_LENGTH) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_cert_verify, SSL_R_DATA_LENGTH_TOO_LONG); - goto err; + if (sign_result == ssl_private_key_retry) { + s->state = SSL3_ST_CW_CERT_VRFY_B; + return -1; } - - if (!EVP_PKEY_sign(pctx, &p[2], &signature_length, digest, digest_length)) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_cert_verify, ERR_R_EVP_LIB); - goto err; + s->rwstate = SSL_NOTHING; + if (sign_result != ssl_private_key_success) { + return -1; } s2n(signature_length, p); n += signature_length + 2; - if (!ssl_set_handshake_header(s, SSL3_MT_CERTIFICATE_VERIFY, n)) { - goto err; + return -1; } - s->state = SSL3_ST_CW_CERT_VRFY_B; + s->state = SSL3_ST_CW_CERT_VRFY_C; } - EVP_PKEY_CTX_free(pctx); return ssl_do_write(s); - -err: - EVP_PKEY_CTX_free(pctx); - return -1; } /* ssl3_has_client_certificate returns true if a client certificate is * configured. */ -static int ssl3_has_client_certificate(SSL *s) { - return s->cert && s->cert->key->x509 && s->cert->key->privatekey; +static int ssl3_has_client_certificate(SSL *ssl) { + return ssl->cert && ssl->cert->x509 && ssl_has_private_key(ssl); } int ssl3_send_client_certificate(SSL *s) { @@ -2139,8 +2025,7 @@ int ssl3_send_client_certificate(SSL *s) { } } else if (i == 1) { i = 0; - OPENSSL_PUT_ERROR(SSL, ssl3_send_client_certificate, - SSL_R_BAD_DATA_RETURNED_BY_CALLBACK); + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_DATA_RETURNED_BY_CALLBACK); } X509_free(x509); @@ -2157,11 +2042,7 @@ int ssl3_send_client_certificate(SSL *s) { s->s3->tmp.cert_req = 2; /* There is no client certificate, so the handshake buffer may be * released. */ - if (s->s3->handshake_buffer && - !ssl3_digest_cached_records(s, free_handshake_buffer)) { - ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); - return -1; - } + ssl3_free_handshake_buffer(s); } } @@ -2170,80 +2051,23 @@ int ssl3_send_client_certificate(SSL *s) { } if (s->state == SSL3_ST_CW_CERT_C) { - CERT_PKEY *cert_pkey = (s->s3->tmp.cert_req == 2) ? NULL : s->cert->key; - if (!ssl3_output_cert_chain(s, cert_pkey)) { + if (s->s3->tmp.cert_req == 2) { + /* Send an empty Certificate message. */ + uint8_t *p = ssl_handshake_start(s); + l2n3(0, p); + if (!ssl_set_handshake_header(s, SSL3_MT_CERTIFICATE, 3)) { + return -1; + } + } else if (!ssl3_output_cert_chain(s)) { return -1; } + s->state = SSL3_ST_CW_CERT_D; } /* SSL3_ST_CW_CERT_D */ return ssl_do_write(s); } -#define has_bits(i, m) (((i) & (m)) == (m)) - -int ssl3_check_cert_and_algorithm(SSL *s) { - long alg_k, alg_a; - SESS_CERT *sc; - DH *dh; - - /* we don't have a certificate */ - if (!ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher)) { - return 1; - } - - alg_k = s->s3->tmp.new_cipher->algorithm_mkey; - alg_a = s->s3->tmp.new_cipher->algorithm_auth; - - sc = s->session->sess_cert; - if (sc == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_check_cert_and_algorithm, ERR_R_INTERNAL_ERROR); - goto err; - } - - dh = s->session->sess_cert->peer_dh_tmp; - - int cert_type = X509_certificate_type(sc->peer_cert, NULL); - if (cert_type & EVP_PK_EC) { - if (ssl_check_srvr_ecc_cert_and_alg(sc->peer_cert, s) == 0) { - /* check failed */ - OPENSSL_PUT_ERROR(SSL, ssl3_check_cert_and_algorithm, SSL_R_BAD_ECC_CERT); - goto f_err; - } else { - return 1; - } - } else if (alg_a & SSL_aECDSA) { - OPENSSL_PUT_ERROR(SSL, ssl3_check_cert_and_algorithm, - SSL_R_MISSING_ECDSA_SIGNING_CERT); - goto f_err; - } - - /* Check that we have a certificate if we require one */ - if ((alg_a & SSL_aRSA) && !has_bits(cert_type, EVP_PK_RSA | EVP_PKT_SIGN)) { - OPENSSL_PUT_ERROR(SSL, ssl3_check_cert_and_algorithm, - SSL_R_MISSING_RSA_SIGNING_CERT); - goto f_err; - } - - if ((alg_k & SSL_kRSA) && !has_bits(cert_type, EVP_PK_RSA | EVP_PKT_ENC)) { - OPENSSL_PUT_ERROR(SSL, ssl3_check_cert_and_algorithm, - SSL_R_MISSING_RSA_ENCRYPTING_CERT); - goto f_err; - } - - if ((alg_k & SSL_kDHE) && dh == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_check_cert_and_algorithm, SSL_R_MISSING_DH_KEY); - goto f_err; - } - - return 1; - -f_err: - ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE); -err: - return 0; -} - int ssl3_send_next_proto(SSL *s) { unsigned int len, padding_len; uint8_t *d, *p; @@ -2273,7 +2097,6 @@ int ssl3_send_channel_id(SSL *s) { uint8_t *d; int ret = -1, public_key_len; EVP_MD_CTX md_ctx; - size_t sig_len; ECDSA_SIG *sig = NULL; uint8_t *public_key = NULL, *derp, *der_sig = NULL; @@ -2295,72 +2118,48 @@ int ssl3_send_channel_id(SSL *s) { } s->rwstate = SSL_NOTHING; - d = ssl_handshake_start(s); - if (s->s3->tlsext_channel_id_new) { - s2n(TLSEXT_TYPE_channel_id_new, d); - } else { - s2n(TLSEXT_TYPE_channel_id, d); + if (EVP_PKEY_id(s->tlsext_channel_id_private) != EVP_PKEY_EC) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return -1; } + EC_KEY *ec_key = s->tlsext_channel_id_private->pkey.ec; + + d = ssl_handshake_start(s); + s2n(TLSEXT_TYPE_channel_id, d); s2n(TLSEXT_CHANNEL_ID_SIZE, d); EVP_MD_CTX_init(&md_ctx); - public_key_len = i2d_PublicKey(s->tlsext_channel_id_private, NULL); + public_key_len = i2o_ECPublicKey(ec_key, NULL); if (public_key_len <= 0) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_channel_id, - SSL_R_CANNOT_SERIALIZE_PUBLIC_KEY); + OPENSSL_PUT_ERROR(SSL, SSL_R_CANNOT_SERIALIZE_PUBLIC_KEY); goto err; } - /* i2d_PublicKey will produce an ANSI X9.62 public key which, for a + /* i2o_ECPublicKey will produce an ANSI X9.62 public key which, for a * P-256 key, is 0x04 (meaning uncompressed) followed by the x and y * field elements as 32-byte, big-endian numbers. */ if (public_key_len != 65) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_channel_id, SSL_R_CHANNEL_ID_NOT_P256); + OPENSSL_PUT_ERROR(SSL, SSL_R_CHANNEL_ID_NOT_P256); goto err; } public_key = OPENSSL_malloc(public_key_len); if (!public_key) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_channel_id, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } derp = public_key; - i2d_PublicKey(s->tlsext_channel_id_private, &derp); + i2o_ECPublicKey(ec_key, &derp); - if (EVP_DigestSignInit(&md_ctx, NULL, EVP_sha256(), NULL, - s->tlsext_channel_id_private) != 1) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_channel_id, - SSL_R_EVP_DIGESTSIGNINIT_FAILED); - goto err; - } - - if (!tls1_channel_id_hash(&md_ctx, s)) { - goto err; - } - - if (!EVP_DigestSignFinal(&md_ctx, NULL, &sig_len)) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_channel_id, - SSL_R_EVP_DIGESTSIGNFINAL_FAILED); - goto err; - } - - der_sig = OPENSSL_malloc(sig_len); - if (!der_sig) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_channel_id, ERR_R_MALLOC_FAILURE); - goto err; - } - - if (!EVP_DigestSignFinal(&md_ctx, der_sig, &sig_len)) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_channel_id, - SSL_R_EVP_DIGESTSIGNFINAL_FAILED); + uint8_t digest[EVP_MAX_MD_SIZE]; + size_t digest_len; + if (!tls1_channel_id_hash(s, digest, &digest_len)) { goto err; } - derp = der_sig; - sig = d2i_ECDSA_SIG(NULL, (const uint8_t **)&derp, sig_len); + sig = ECDSA_do_sign(digest, digest_len, ec_key); if (sig == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_channel_id, SSL_R_D2I_ECDSA_SIG); goto err; } @@ -2369,7 +2168,7 @@ int ssl3_send_channel_id(SSL *s) { d += 64; if (!BN_bn2bin_padded(d, 32, sig->r) || !BN_bn2bin_padded(d + 32, 32, sig->s)) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_channel_id, ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); goto err; } @@ -2397,3 +2196,17 @@ int ssl_do_client_cert_cb(SSL *s, X509 **px509, EVP_PKEY **ppkey) { } return i; } + +int ssl3_verify_server_cert(SSL *s) { + int ret = ssl_verify_cert_chain(s, s->session->cert_chain); + if (s->verify_mode != SSL_VERIFY_NONE && ret <= 0) { + int al = ssl_verify_alarm_type(s->verify_result); + ssl3_send_alert(s, SSL3_AL_FATAL, al); + OPENSSL_PUT_ERROR(SSL, SSL_R_CERTIFICATE_VERIFY_FAILED); + } else { + ret = 1; + ERR_clear_error(); /* but we keep s->verify_result */ + } + + return ret; +} diff --git a/src/ssl/s3_enc.c b/src/ssl/s3_enc.c index fbe68da..f1924c0 100644 --- a/src/ssl/s3_enc.c +++ b/src/ssl/s3_enc.c @@ -4,21 +4,21 @@ * This package is an SSL implementation written * by Eric Young (eay@cryptsoft.com). * The implementation was written so as to conform with Netscapes SSL. - *g + * * This library is free for commercial and non-commercial use as long as * the following conditions are aheared to. The following conditions * apply to all code found in this distribution, be it the RC4, RSA, * lhash, DES, etc., code; not just the SSL code. The SSL documentation * included with this distribution is covered by the same copyright terms * except that the holder is Tim Hudson (tjh@cryptsoft.com). - *g + * * Copyright remains Eric Young's, and as such any Copyright notices in * the code are not to be removed. * If this package is used in a product, Eric Young should be given attribution * as the author of the parts of the library used. * This can be in the form of a textual message at program startup or * in documentation (online or textual) provided with the package. - *g + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -33,10 +33,10 @@ * Eric Young (eay@cryptsoft.com)" * The word 'cryptographic' can be left out if the rouines from the library * being used are not cryptographic related :-). - * 4. If you include any Windows specific code (or a derivative thereof) fromg + * 4. If you include any Windows specific code (or a derivative thereof) from * the apps directory (application code) you must include an acknowledgement: * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" - *g + * * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -48,7 +48,7 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - *g + * * The licence and distribution terms for any publically available version or * derivative of this code cannot be changed. i.e. this code cannot simply be * copied and put under another distribution licence @@ -62,7 +62,7 @@ * are met: * * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer.g + * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in @@ -133,6 +133,8 @@ * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR * OTHERWISE. */ +#include <openssl/ssl.h> + #include <assert.h> #include <stdio.h> #include <string.h> @@ -180,7 +182,7 @@ int ssl3_prf(SSL *s, uint8_t *out, size_t out_len, const uint8_t *secret, k++; if (k > sizeof(buf)) { /* bug: 'buf' is too small for this ciphersuite */ - OPENSSL_PUT_ERROR(SSL, ssl3_prf, ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return 0; } @@ -189,7 +191,7 @@ int ssl3_prf(SSL *s, uint8_t *out, size_t out_len, const uint8_t *secret, } c++; if (!EVP_DigestInit_ex(&sha1, EVP_sha1(), NULL)) { - OPENSSL_PUT_ERROR(SSL, ssl3_prf, ERR_LIB_EVP); + OPENSSL_PUT_ERROR(SSL, ERR_LIB_EVP); return 0; } EVP_DigestUpdate(&sha1, buf, k); @@ -204,7 +206,7 @@ int ssl3_prf(SSL *s, uint8_t *out, size_t out_len, const uint8_t *secret, EVP_DigestFinal_ex(&sha1, smd, NULL); if (!EVP_DigestInit_ex(&md5, EVP_md5(), NULL)) { - OPENSSL_PUT_ERROR(SSL, ssl3_prf, ERR_LIB_EVP); + OPENSSL_PUT_ERROR(SSL, ERR_LIB_EVP); return 0; } EVP_DigestUpdate(&md5, secret, secret_len); @@ -235,96 +237,75 @@ void ssl3_cleanup_key_block(SSL *s) { s->s3->tmp.key_block_length = 0; } -int ssl3_init_finished_mac(SSL *s) { - BIO_free(s->s3->handshake_buffer); - ssl3_free_digest_list(s); - s->s3->handshake_buffer = BIO_new(BIO_s_mem()); - if (s->s3->handshake_buffer == NULL) { - return 0; - } - BIO_set_close(s->s3->handshake_buffer, BIO_CLOSE); - - return 1; +int ssl3_init_handshake_buffer(SSL *ssl) { + ssl3_free_handshake_buffer(ssl); + ssl3_free_handshake_hash(ssl); + ssl->s3->handshake_buffer = BUF_MEM_new(); + return ssl->s3->handshake_buffer != NULL; } -void ssl3_free_digest_list(SSL *s) { - int i; - if (!s->s3->handshake_dgst) { - return; - } - for (i = 0; i < SSL_MAX_DIGEST; i++) { - if (s->s3->handshake_dgst[i]) { - EVP_MD_CTX_destroy(s->s3->handshake_dgst[i]); - } +/* init_digest_with_data calls |EVP_DigestInit_ex| on |ctx| with |md| and then + * writes the data in |buf| to it. */ +static int init_digest_with_data(EVP_MD_CTX *ctx, const EVP_MD *md, + const BUF_MEM *buf) { + if (!EVP_DigestInit_ex(ctx, md, NULL)) { + return 0; } - OPENSSL_free(s->s3->handshake_dgst); - s->s3->handshake_dgst = NULL; + EVP_DigestUpdate(ctx, buf->data, buf->length); + return 1; } -int ssl3_finish_mac(SSL *s, const uint8_t *buf, int len) { - int i; +int ssl3_init_handshake_hash(SSL *ssl) { + ssl3_free_handshake_hash(ssl); - if (s->s3->handshake_buffer) { - return BIO_write(s->s3->handshake_buffer, (void *)buf, len) >= 0; + uint32_t algorithm_prf = ssl_get_algorithm_prf(ssl); + if (!init_digest_with_data(&ssl->s3->handshake_hash, + ssl_get_handshake_digest(algorithm_prf), + ssl->s3->handshake_buffer)) { + return 0; } - for (i = 0; i < SSL_MAX_DIGEST; i++) { - if (s->s3->handshake_dgst[i] != NULL) { - EVP_DigestUpdate(s->s3->handshake_dgst[i], buf, len); - } + if (algorithm_prf == SSL_HANDSHAKE_MAC_DEFAULT && + !init_digest_with_data(&ssl->s3->handshake_md5, EVP_md5(), + ssl->s3->handshake_buffer)) { + return 0; } + return 1; } -int ssl3_digest_cached_records( - SSL *s, enum should_free_handshake_buffer_t should_free_handshake_buffer) { - int i; - uint32_t mask; - const EVP_MD *md; - const uint8_t *hdata; - size_t hdatalen; - - /* Allocate handshake_dgst array */ - ssl3_free_digest_list(s); - s->s3->handshake_dgst = OPENSSL_malloc(SSL_MAX_DIGEST * sizeof(EVP_MD_CTX *)); - if (s->s3->handshake_dgst == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_digest_cached_records, ERR_R_MALLOC_FAILURE); - return 0; - } +void ssl3_free_handshake_hash(SSL *ssl) { + EVP_MD_CTX_cleanup(&ssl->s3->handshake_hash); + EVP_MD_CTX_cleanup(&ssl->s3->handshake_md5); +} - memset(s->s3->handshake_dgst, 0, SSL_MAX_DIGEST * sizeof(EVP_MD_CTX *)); - if (!BIO_mem_contents(s->s3->handshake_buffer, &hdata, &hdatalen)) { - OPENSSL_PUT_ERROR(SSL, ssl3_digest_cached_records, - SSL_R_BAD_HANDSHAKE_LENGTH); - return 0; - } +void ssl3_free_handshake_buffer(SSL *ssl) { + BUF_MEM_free(ssl->s3->handshake_buffer); + ssl->s3->handshake_buffer = NULL; +} - /* Loop through bits of algorithm2 field and create MD_CTX-es */ - for (i = 0; ssl_get_handshake_digest(&mask, &md, i); i++) { - if ((mask & ssl_get_algorithm2(s)) && md) { - s->s3->handshake_dgst[i] = EVP_MD_CTX_create(); - if (s->s3->handshake_dgst[i] == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_digest_cached_records, ERR_LIB_EVP); - return 0; - } - if (!EVP_DigestInit_ex(s->s3->handshake_dgst[i], md, NULL)) { - EVP_MD_CTX_destroy(s->s3->handshake_dgst[i]); - s->s3->handshake_dgst[i] = NULL; - OPENSSL_PUT_ERROR(SSL, ssl3_digest_cached_records, ERR_LIB_EVP); - return 0; - } - EVP_DigestUpdate(s->s3->handshake_dgst[i], hdata, hdatalen); - } else { - s->s3->handshake_dgst[i] = NULL; +int ssl3_update_handshake_hash(SSL *ssl, const uint8_t *in, size_t in_len) { + /* Depending on the state of the handshake, either the handshake buffer may be + * active, the rolling hash, or both. */ + + if (ssl->s3->handshake_buffer != NULL) { + size_t new_len = ssl->s3->handshake_buffer->length + in_len; + if (new_len < in_len) { + OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW); + return 0; + } + if (!BUF_MEM_grow(ssl->s3->handshake_buffer, new_len)) { + return 0; } + memcpy(ssl->s3->handshake_buffer->data + new_len - in_len, in, in_len); } - if (should_free_handshake_buffer == free_handshake_buffer) { - /* Free handshake_buffer BIO */ - BIO_free(s->s3->handshake_buffer); - s->s3->handshake_buffer = NULL; + if (EVP_MD_CTX_md(&ssl->s3->handshake_hash) != NULL) { + EVP_DigestUpdate(&ssl->s3->handshake_hash, in, in_len); + } + if (EVP_MD_CTX_md(&ssl->s3->handshake_md5) != NULL) { + EVP_DigestUpdate(&ssl->s3->handshake_md5, in, in_len); } - return 1; } @@ -356,31 +337,22 @@ static int ssl3_handshake_mac(SSL *s, int md_nid, const char *sender, int len, int npad, n; unsigned int i; uint8_t md_buf[EVP_MAX_MD_SIZE]; - EVP_MD_CTX ctx, *d = NULL; - - if (s->s3->handshake_buffer && - !ssl3_digest_cached_records(s, free_handshake_buffer)) { - return 0; - } - - /* Search for digest of specified type in the handshake_dgst array. */ - for (i = 0; i < SSL_MAX_DIGEST; i++) { - if (s->s3->handshake_dgst[i] && - EVP_MD_CTX_type(s->s3->handshake_dgst[i]) == md_nid) { - d = s->s3->handshake_dgst[i]; - break; - } - } - - if (!d) { - OPENSSL_PUT_ERROR(SSL, ssl3_handshake_mac, SSL_R_NO_REQUIRED_DIGEST); + EVP_MD_CTX ctx; + const EVP_MD_CTX *ctx_template; + + if (md_nid == NID_md5) { + ctx_template = &s->s3->handshake_md5; + } else if (md_nid == EVP_MD_CTX_type(&s->s3->handshake_hash)) { + ctx_template = &s->s3->handshake_hash; + } else { + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_REQUIRED_DIGEST); return 0; } EVP_MD_CTX_init(&ctx); - if (!EVP_MD_CTX_copy_ex(&ctx, d)) { + if (!EVP_MD_CTX_copy_ex(&ctx, ctx_template)) { EVP_MD_CTX_cleanup(&ctx); - OPENSSL_PUT_ERROR(SSL, ssl3_handshake_mac, ERR_LIB_EVP); + OPENSSL_PUT_ERROR(SSL, ERR_LIB_EVP); return 0; } @@ -399,7 +371,7 @@ static int ssl3_handshake_mac(SSL *s, int md_nid, const char *sender, int len, if (!EVP_DigestInit_ex(&ctx, EVP_MD_CTX_md(&ctx), NULL)) { EVP_MD_CTX_cleanup(&ctx); - OPENSSL_PUT_ERROR(SSL, ssl3_handshake_mac, ERR_LIB_EVP); + OPENSSL_PUT_ERROR(SSL, ERR_LIB_EVP); return 0; } EVP_DigestUpdate(&ctx, s->session->master_key, s->session->master_key_length); @@ -420,7 +392,7 @@ int ssl3_record_sequence_update(uint8_t *seq, size_t seq_len) { return 1; } } - OPENSSL_PUT_ERROR(SSL, ssl3_record_sequence_update, ERR_R_OVERFLOW); + OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW); return 0; } diff --git a/src/ssl/s3_lib.c b/src/ssl/s3_lib.c index 1c28a73..617ea6e 100644 --- a/src/ssl/s3_lib.c +++ b/src/ssl/s3_lib.c @@ -146,12 +146,15 @@ * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR * OTHERWISE. */ +#include <openssl/ssl.h> + #include <assert.h> #include <stdio.h> #include <string.h> #include <openssl/buf.h> #include <openssl/dh.h> +#include <openssl/digest.h> #include <openssl/err.h> #include <openssl/md5.h> #include <openssl/mem.h> @@ -186,7 +189,8 @@ int ssl3_set_handshake_header(SSL *s, int htype, unsigned long len) { s->init_off = 0; /* Add the message to the handshake hash. */ - return ssl3_finish_mac(s, (uint8_t *)s->init_buf->data, s->init_num); + return ssl3_update_handshake_hash(s, (uint8_t *)s->init_buf->data, + s->init_num); } int ssl3_handshake_write(SSL *s) { return ssl3_do_write(s, SSL3_RT_HANDSHAKE); } @@ -199,7 +203,9 @@ int ssl3_new(SSL *s) { goto err; } memset(s3, 0, sizeof *s3); - memset(s3->rrec.seq_num, 0, sizeof(s3->rrec.seq_num)); + + EVP_MD_CTX_init(&s3->handshake_hash); + EVP_MD_CTX_init(&s3->handshake_md5); s->s3 = s3; @@ -219,20 +225,20 @@ void ssl3_free(SSL *s) { return; } - BUF_MEM_free(s->s3->sniff_buffer); ssl3_cleanup_key_block(s); - ssl3_release_read_buffer(s); - ssl3_release_write_buffer(s); + ssl_read_buffer_clear(s); + ssl_write_buffer_clear(s); DH_free(s->s3->tmp.dh); EC_KEY_free(s->s3->tmp.ecdh); sk_X509_NAME_pop_free(s->s3->tmp.ca_names, X509_NAME_free); OPENSSL_free(s->s3->tmp.certificate_types); - OPENSSL_free(s->s3->tmp.peer_ecpointformatlist); OPENSSL_free(s->s3->tmp.peer_ellipticcurvelist); OPENSSL_free(s->s3->tmp.peer_psk_identity_hint); - BIO_free(s->s3->handshake_buffer); - ssl3_free_digest_list(s); + DH_free(s->s3->tmp.peer_dh_tmp); + EC_KEY_free(s->s3->tmp.peer_ecdh_tmp); + ssl3_free_handshake_buffer(s); + ssl3_free_handshake_hash(s); OPENSSL_free(s->s3->alpn_selected); OPENSSL_cleanse(s->s3, sizeof *s->s3); @@ -240,8 +246,6 @@ void ssl3_free(SSL *s) { s->s3 = NULL; } -static int ssl3_set_req_cert_type(CERT *c, const uint8_t *p, size_t len); - int SSL_session_reused(const SSL *ssl) { return ssl->hit; } @@ -274,7 +278,7 @@ int SSL_CTX_set_tmp_dh(SSL_CTX *ctx, const DH *dh) { DH_free(ctx->cert->dh_tmp); ctx->cert->dh_tmp = DHparams_dup(dh); if (ctx->cert->dh_tmp == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_set_tmp_dh, ERR_R_DH_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_DH_LIB); return 0; } return 1; @@ -284,7 +288,7 @@ int SSL_set_tmp_dh(SSL *ssl, const DH *dh) { DH_free(ssl->cert->dh_tmp); ssl->cert->dh_tmp = DHparams_dup(dh); if (ssl->cert->dh_tmp == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_set_tmp_dh, ERR_R_DH_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_DH_LIB); return 0; } return 1; @@ -292,7 +296,7 @@ int SSL_set_tmp_dh(SSL *ssl, const DH *dh) { int SSL_CTX_set_tmp_ecdh(SSL_CTX *ctx, const EC_KEY *ec_key) { if (ec_key == NULL || EC_KEY_get0_group(ec_key) == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_set_tmp_ecdh, ERR_R_PASSED_NULL_PARAMETER); + OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER); return 0; } ctx->cert->ecdh_nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key)); @@ -301,7 +305,7 @@ int SSL_CTX_set_tmp_ecdh(SSL_CTX *ctx, const EC_KEY *ec_key) { int SSL_set_tmp_ecdh(SSL *ssl, const EC_KEY *ec_key) { if (ec_key == NULL || EC_KEY_get0_group(ec_key) == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_set_tmp_ecdh, ERR_R_PASSED_NULL_PARAMETER); + OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER); return 0; } ssl->cert->ecdh_nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key)); @@ -322,8 +326,7 @@ int SSL_CTX_set1_tls_channel_id(SSL_CTX *ctx, EVP_PKEY *private_key) { ctx->tlsext_channel_id_enabled = 1; if (EVP_PKEY_id(private_key) != EVP_PKEY_EC || EVP_PKEY_bits(private_key) != 256) { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_set1_tls_channel_id, - SSL_R_CHANNEL_ID_NOT_P256); + OPENSSL_PUT_ERROR(SSL, SSL_R_CHANNEL_ID_NOT_P256); return 0; } EVP_PKEY_free(ctx->tlsext_channel_id_private); @@ -335,7 +338,7 @@ int SSL_set1_tls_channel_id(SSL *ssl, EVP_PKEY *private_key) { ssl->tlsext_channel_id_enabled = 1; if (EVP_PKEY_id(private_key) != EVP_PKEY_EC || EVP_PKEY_bits(private_key) != 256) { - OPENSSL_PUT_ERROR(SSL, SSL_set1_tls_channel_id, SSL_R_CHANNEL_ID_NOT_P256); + OPENSSL_PUT_ERROR(SSL, SSL_R_CHANNEL_ID_NOT_P256); return 0; } EVP_PKEY_free(ssl->tlsext_channel_id_private); @@ -359,238 +362,36 @@ int SSL_set_tlsext_host_name(SSL *ssl, const char *name) { return 1; } if (strlen(name) > TLSEXT_MAXLEN_host_name) { - OPENSSL_PUT_ERROR(SSL, SSL_set_tlsext_host_name, - SSL_R_SSL3_EXT_INVALID_SERVERNAME); + OPENSSL_PUT_ERROR(SSL, SSL_R_SSL3_EXT_INVALID_SERVERNAME); return 0; } ssl->tlsext_hostname = BUF_strdup(name); if (ssl->tlsext_hostname == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_set_tlsext_host_name, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); return 0; } return 1; } -long ssl3_ctrl(SSL *s, int cmd, long larg, void *parg) { - int ret = 0; - - switch (cmd) { - case SSL_CTRL_CHAIN: - if (larg) { - return ssl_cert_set1_chain(s->cert, (STACK_OF(X509) *)parg); - } else { - return ssl_cert_set0_chain(s->cert, (STACK_OF(X509) *)parg); - } - - case SSL_CTRL_CHAIN_CERT: - if (larg) { - return ssl_cert_add1_chain_cert(s->cert, (X509 *)parg); - } else { - return ssl_cert_add0_chain_cert(s->cert, (X509 *)parg); - } - - case SSL_CTRL_GET_CHAIN_CERTS: - *(STACK_OF(X509) **)parg = s->cert->key->chain; - ret = 1; - break; - - case SSL_CTRL_SELECT_CURRENT_CERT: - return ssl_cert_select_current(s->cert, (X509 *)parg); - - case SSL_CTRL_GET_CURVES: { - const uint16_t *clist = s->s3->tmp.peer_ellipticcurvelist; - size_t clistlen = s->s3->tmp.peer_ellipticcurvelist_length; - if (parg) { - size_t i; - int *cptr = parg; - int nid; - for (i = 0; i < clistlen; i++) { - nid = tls1_ec_curve_id2nid(clist[i]); - if (nid != NID_undef) { - cptr[i] = nid; - } else { - cptr[i] = TLSEXT_nid_unknown | clist[i]; - } - } - } - return (int)clistlen; - } - - case SSL_CTRL_SET_CURVES: - return tls1_set_curves(&s->tlsext_ellipticcurvelist, - &s->tlsext_ellipticcurvelist_length, parg, larg); - - case SSL_CTRL_SET_SIGALGS: - return tls1_set_sigalgs(s->cert, parg, larg, 0); - - case SSL_CTRL_SET_CLIENT_SIGALGS: - return tls1_set_sigalgs(s->cert, parg, larg, 1); - - case SSL_CTRL_GET_CLIENT_CERT_TYPES: { - const uint8_t **pctype = parg; - if (s->server || !s->s3->tmp.cert_req) { - return 0; - } - if (pctype) { - *pctype = s->s3->tmp.certificate_types; - } - return (int)s->s3->tmp.num_certificate_types; - } - - case SSL_CTRL_SET_CLIENT_CERT_TYPES: - if (!s->server) { - return 0; - } - return ssl3_set_req_cert_type(s->cert, parg, larg); - - case SSL_CTRL_BUILD_CERT_CHAIN: - return ssl_build_cert_chain(s->cert, s->ctx->cert_store, larg); - - case SSL_CTRL_SET_VERIFY_CERT_STORE: - return ssl_cert_set_cert_store(s->cert, parg, 0, larg); - - case SSL_CTRL_SET_CHAIN_CERT_STORE: - return ssl_cert_set_cert_store(s->cert, parg, 1, larg); - - case SSL_CTRL_GET_SERVER_TMP_KEY: - if (s->server || !s->session || !s->session->sess_cert) { - return 0; - } else { - SESS_CERT *sc; - EVP_PKEY *ptmp; - int rv = 0; - sc = s->session->sess_cert; - if (!sc->peer_dh_tmp && !sc->peer_ecdh_tmp) { - return 0; - } - ptmp = EVP_PKEY_new(); - if (!ptmp) { - return 0; - } - if (sc->peer_dh_tmp) { - rv = EVP_PKEY_set1_DH(ptmp, sc->peer_dh_tmp); - } else if (sc->peer_ecdh_tmp) { - rv = EVP_PKEY_set1_EC_KEY(ptmp, sc->peer_ecdh_tmp); - } - if (rv) { - *(EVP_PKEY **)parg = ptmp; - return 1; - } - EVP_PKEY_free(ptmp); - return 0; - } - - case SSL_CTRL_GET_EC_POINT_FORMATS: { - const uint8_t **pformat = parg; - if (!s->s3->tmp.peer_ecpointformatlist) { - return 0; - } - *pformat = s->s3->tmp.peer_ecpointformatlist; - return (int)s->s3->tmp.peer_ecpointformatlist_length; - } - - default: - break; +size_t SSL_get0_certificate_types(SSL *ssl, const uint8_t **out_types) { + if (ssl->server || !ssl->s3->tmp.cert_req) { + *out_types = NULL; + return 0; } - - return ret; + *out_types = ssl->s3->tmp.certificate_types; + return ssl->s3->tmp.num_certificate_types; } -long ssl3_ctx_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg) { - switch (cmd) { - case SSL_CTRL_SET_TLSEXT_TICKET_KEYS: - case SSL_CTRL_GET_TLSEXT_TICKET_KEYS: { - uint8_t *keys = parg; - if (!keys) { - return 48; - } - if (larg != 48) { - OPENSSL_PUT_ERROR(SSL, ssl3_ctx_ctrl, SSL_R_INVALID_TICKET_KEYS_LENGTH); - return 0; - } - if (cmd == SSL_CTRL_SET_TLSEXT_TICKET_KEYS) { - memcpy(ctx->tlsext_tick_key_name, keys, 16); - memcpy(ctx->tlsext_tick_hmac_key, keys + 16, 16); - memcpy(ctx->tlsext_tick_aes_key, keys + 32, 16); - } else { - memcpy(keys, ctx->tlsext_tick_key_name, 16); - memcpy(keys + 16, ctx->tlsext_tick_hmac_key, 16); - memcpy(keys + 32, ctx->tlsext_tick_aes_key, 16); - } - return 1; - } - - case SSL_CTRL_SET_CURVES: - return tls1_set_curves(&ctx->tlsext_ellipticcurvelist, - &ctx->tlsext_ellipticcurvelist_length, parg, larg); - - case SSL_CTRL_SET_SIGALGS: - return tls1_set_sigalgs(ctx->cert, parg, larg, 0); - - case SSL_CTRL_SET_CLIENT_SIGALGS: - return tls1_set_sigalgs(ctx->cert, parg, larg, 1); - - case SSL_CTRL_SET_CLIENT_CERT_TYPES: - return ssl3_set_req_cert_type(ctx->cert, parg, larg); - - case SSL_CTRL_BUILD_CERT_CHAIN: - return ssl_build_cert_chain(ctx->cert, ctx->cert_store, larg); - - case SSL_CTRL_SET_VERIFY_CERT_STORE: - return ssl_cert_set_cert_store(ctx->cert, parg, 0, larg); - - case SSL_CTRL_SET_CHAIN_CERT_STORE: - return ssl_cert_set_cert_store(ctx->cert, parg, 1, larg); - - case SSL_CTRL_EXTRA_CHAIN_CERT: - if (ctx->extra_certs == NULL) { - ctx->extra_certs = sk_X509_new_null(); - if (ctx->extra_certs == NULL) { - return 0; - } - } - sk_X509_push(ctx->extra_certs, (X509 *)parg); - break; - - case SSL_CTRL_GET_EXTRA_CHAIN_CERTS: - if (ctx->extra_certs == NULL && larg == 0) { - *(STACK_OF(X509) **)parg = ctx->cert->key->chain; - } else { - *(STACK_OF(X509) **)parg = ctx->extra_certs; - } - break; - - case SSL_CTRL_CLEAR_EXTRA_CHAIN_CERTS: - sk_X509_pop_free(ctx->extra_certs, X509_free); - ctx->extra_certs = NULL; - break; - - case SSL_CTRL_CHAIN: - if (larg) { - return ssl_cert_set1_chain(ctx->cert, (STACK_OF(X509) *)parg); - } else { - return ssl_cert_set0_chain(ctx->cert, (STACK_OF(X509) *)parg); - } - - case SSL_CTRL_CHAIN_CERT: - if (larg) { - return ssl_cert_add1_chain_cert(ctx->cert, (X509 *)parg); - } else { - return ssl_cert_add0_chain_cert(ctx->cert, (X509 *)parg); - } - - case SSL_CTRL_GET_CHAIN_CERTS: - *(STACK_OF(X509) **)parg = ctx->cert->key->chain; - break; - - case SSL_CTRL_SELECT_CURRENT_CERT: - return ssl_cert_select_current(ctx->cert, (X509 *)parg); - - default: - return 0; - } +int SSL_CTX_set1_curves(SSL_CTX *ctx, const int *curves, size_t curves_len) { + return tls1_set_curves(&ctx->tlsext_ellipticcurvelist, + &ctx->tlsext_ellipticcurvelist_length, curves, + curves_len); +} - return 1; +int SSL_set1_curves(SSL *ssl, const int *curves, size_t curves_len) { + return tls1_set_curves(&ssl->tlsext_ellipticcurvelist, + &ssl->tlsext_ellipticcurvelist_length, curves, + curves_len); } int SSL_CTX_set_tlsext_servername_callback( @@ -604,6 +405,36 @@ int SSL_CTX_set_tlsext_servername_arg(SSL_CTX *ctx, void *arg) { return 1; } +int SSL_CTX_get_tlsext_ticket_keys(SSL_CTX *ctx, void *out, size_t len) { + if (out == NULL) { + return 48; + } + if (len != 48) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_TICKET_KEYS_LENGTH); + return 0; + } + uint8_t *out_bytes = out; + memcpy(out_bytes, ctx->tlsext_tick_key_name, 16); + memcpy(out_bytes + 16, ctx->tlsext_tick_hmac_key, 16); + memcpy(out_bytes + 32, ctx->tlsext_tick_aes_key, 16); + return 1; +} + +int SSL_CTX_set_tlsext_ticket_keys(SSL_CTX *ctx, const void *in, size_t len) { + if (in == NULL) { + return 48; + } + if (len != 48) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_TICKET_KEYS_LENGTH); + return 0; + } + const uint8_t *in_bytes = in; + memcpy(ctx->tlsext_tick_key_name, in_bytes, 16); + memcpy(ctx->tlsext_tick_hmac_key, in_bytes + 16, 16); + memcpy(ctx->tlsext_tick_aes_key, in_bytes + 32, 16); + return 1; +} + int SSL_CTX_set_tlsext_ticket_key_cb( SSL_CTX *ctx, int (*callback)(SSL *ssl, uint8_t *key_name, uint8_t *iv, EVP_CIPHER_CTX *ctx, HMAC_CTX *hmac_ctx, @@ -622,6 +453,11 @@ struct ssl_cipher_preference_list_st *ssl_get_cipher_preferences(SSL *s) { return s->ctx->cipher_list_tls11; } + if (s->version >= TLS1_VERSION && s->ctx != NULL && + s->ctx->cipher_list_tls10 != NULL) { + return s->ctx->cipher_list_tls10; + } + if (s->ctx != NULL && s->ctx->cipher_list != NULL) { return s->ctx->cipher_list; } @@ -708,13 +544,6 @@ int ssl3_get_req_cert_type(SSL *s, uint8_t *p) { int have_rsa_sign = 0; int have_ecdsa_sign = 0; - /* If we have custom certificate types set, use them */ - if (s->cert->client_certificate_types) { - memcpy(p, s->cert->client_certificate_types, - s->cert->num_client_certificate_types); - return s->cert->num_client_certificate_types; - } - /* get configured sigalgs */ siglen = tls12_get_psigalgs(s, &sig); for (i = 0; i < siglen; i += 2, sig += 2) { @@ -742,36 +571,13 @@ int ssl3_get_req_cert_type(SSL *s, uint8_t *p) { return ret; } -static int ssl3_set_req_cert_type(CERT *c, const uint8_t *p, size_t len) { - OPENSSL_free(c->client_certificate_types); - c->client_certificate_types = NULL; - c->num_client_certificate_types = 0; - - if (!p || !len) { - return 1; - } - - if (len > 0xff) { - return 0; - } - - c->client_certificate_types = BUF_memdup(p, len); - if (!c->client_certificate_types) { - return 0; - } - - c->num_client_certificate_types = len; - return 1; -} - /* If we are using default SHA1+MD5 algorithms switch to new SHA256 PRF and * handshake macs if required. */ -uint32_t ssl_get_algorithm2(SSL *s) { - static const uint32_t kMask = SSL_HANDSHAKE_MAC_DEFAULT; - uint32_t alg2 = s->s3->tmp.new_cipher->algorithm2; +uint32_t ssl_get_algorithm_prf(SSL *s) { + uint32_t algorithm_prf = s->s3->tmp.new_cipher->algorithm_prf; if (s->enc_method->enc_flags & SSL_ENC_FLAG_SHA256_PRF && - (alg2 & kMask) == kMask) { + algorithm_prf == SSL_HANDSHAKE_MAC_DEFAULT) { return SSL_HANDSHAKE_MAC_SHA256; } - return alg2; + return algorithm_prf; } diff --git a/src/ssl/s3_meth.c b/src/ssl/s3_meth.c index 66bbb29..01c1101 100644 --- a/src/ssl/s3_meth.c +++ b/src/ssl/s3_meth.c @@ -54,6 +54,8 @@ * copied and put under another distribution licence * [including the GNU Public Licence.] */ +#include <openssl/ssl.h> + #include "internal.h" @@ -68,8 +70,6 @@ static const SSL_PROTOCOL_METHOD TLS_protocol_method = { ssl3_read_close_notify, ssl3_write_app_data, ssl3_dispatch_alert, - ssl3_ctrl, - ssl3_ctx_ctrl, ssl3_supports_cipher, SSL3_HM_HEADER_LENGTH, ssl3_set_handshake_header, diff --git a/src/ssl/s3_pkt.c b/src/ssl/s3_pkt.c index 4a9ae83..3c2435d 100644 --- a/src/ssl/s3_pkt.c +++ b/src/ssl/s3_pkt.c @@ -106,6 +106,8 @@ * (eay@cryptsoft.com). This product includes software written by Tim * Hudson (tjh@cryptsoft.com). */ +#include <openssl/ssl.h> + #include <assert.h> #include <limits.h> #include <stdio.h> @@ -120,282 +122,65 @@ #include "internal.h" -static int do_ssl3_write(SSL *s, int type, const uint8_t *buf, unsigned int len, - char fragment); -static int ssl3_get_record(SSL *s); +static int do_ssl3_write(SSL *s, int type, const uint8_t *buf, unsigned len); -int ssl3_read_n(SSL *s, int n, int extend) { - /* If |extend| is 0, obtain new n-byte packet; - * if |extend| is 1, increase packet by another n bytes. - * - * The packet will be in the sub-array of |s->s3->rbuf.buf| specified by - * |s->packet| and |s->packet_length|. (If DTLS and |extend| is 0, additional - * bytes will be read into |rbuf|, up to the size of the buffer.) - * - * TODO(davidben): |dtls1_get_record| and |ssl3_get_record| have very - * different needs. Separate the two record layers. In DTLS, |BIO_read| is - * called at most once, and only when |extend| is 0. In TLS, the buffer never - * contains more than one record. */ - int i, len, left; - uintptr_t align = 0; - uint8_t *pkt; - SSL3_BUFFER *rb; - - if (n <= 0) { - return n; - } +/* kMaxWarningAlerts is the number of consecutive warning alerts that will be + * processed. */ +static const uint8_t kMaxWarningAlerts = 4; - rb = &s->s3->rbuf; - if (rb->buf == NULL && !ssl3_setup_read_buffer(s)) { - return -1; - } - - left = rb->left; - - align = (uintptr_t)rb->buf + SSL3_RT_HEADER_LENGTH; - align = (0 - align) & (SSL3_ALIGN_PAYLOAD - 1); - - if (!extend) { - /* start with empty packet ... */ - if (left == 0) { - rb->offset = align; - } else if (align != 0 && left >= SSL3_RT_HEADER_LENGTH) { - /* check if next packet length is large enough to justify payload - * alignment... */ - pkt = rb->buf + rb->offset; - if (pkt[0] == SSL3_RT_APPLICATION_DATA && (pkt[3] << 8 | pkt[4]) >= 128) { - /* Note that even if packet is corrupted and its length field is - * insane, we can only be led to wrong decision about whether memmove - * will occur or not. Header values has no effect on memmove arguments - * and therefore no buffer overrun can be triggered. */ - memmove(rb->buf + align, pkt, left); - rb->offset = align; +/* ssl3_get_record reads a new input record. On success, it places it in + * |ssl->s3->rrec| and returns one. Otherwise it returns <= 0 on error or if + * more data is needed. */ +static int ssl3_get_record(SSL *ssl) { + int ret; +again: + /* Ensure the buffer is large enough to decrypt in-place. */ + ret = ssl_read_buffer_extend_to(ssl, ssl_record_prefix_len(ssl)); + if (ret <= 0) { + return ret; + } + assert(ssl_read_buffer_len(ssl) >= ssl_record_prefix_len(ssl)); + + uint8_t *out = ssl_read_buffer(ssl) + ssl_record_prefix_len(ssl); + size_t max_out = ssl_read_buffer_len(ssl) - ssl_record_prefix_len(ssl); + uint8_t type, alert; + size_t len, consumed; + switch (tls_open_record(ssl, &type, out, &len, &consumed, &alert, max_out, + ssl_read_buffer(ssl), ssl_read_buffer_len(ssl))) { + case ssl_open_record_success: + ssl_read_buffer_consume(ssl, consumed); + + if (len > 0xffff) { + OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW); + return -1; } - } - s->packet = rb->buf + rb->offset; - s->packet_length = 0; - /* ... now we can act as if 'extend' was set */ - } - - /* In DTLS, if there is leftover data from the previous packet or |extend| is - * true, clamp to the previous read. DTLS records may not span packet - * boundaries. */ - if (SSL_IS_DTLS(s) && n > left && (left > 0 || extend)) { - n = left; - } - /* if there is enough in the buffer from a previous read, take some */ - if (left >= n) { - s->packet_length += n; - rb->left = left - n; - rb->offset += n; - return n; - } - - /* else we need to read more data */ - - len = s->packet_length; - pkt = rb->buf + align; - /* Move any available bytes to front of buffer: |len| bytes already pointed - * to by |packet|, |left| extra ones at the end. */ - if (s->packet != pkt) { - /* len > 0 */ - memmove(pkt, s->packet, len + left); - s->packet = pkt; - rb->offset = len + align; - } - - if (n > (int)(rb->len - rb->offset)) { - OPENSSL_PUT_ERROR(SSL, ssl3_read_n, ERR_R_INTERNAL_ERROR); - return -1; - } - - int max = n; - if (SSL_IS_DTLS(s) && !extend) { - max = rb->len - rb->offset; - } - - while (left < n) { - /* Now we have len+left bytes at the front of s->s3->rbuf.buf and need to - * read in more until we have len+n (up to len+max if possible). */ - ERR_clear_system_error(); - if (s->rbio != NULL) { - s->rwstate = SSL_READING; - i = BIO_read(s->rbio, pkt + len + left, max - left); - } else { - OPENSSL_PUT_ERROR(SSL, ssl3_read_n, SSL_R_READ_BIO_NOT_SET); - i = -1; - } - - if (i <= 0) { - rb->left = left; - if (len + left == 0) { - ssl3_release_read_buffer(s); + SSL3_RECORD *rr = &ssl->s3->rrec; + rr->type = type; + rr->length = (uint16_t)len; + rr->off = 0; + rr->data = out; + return 1; + + case ssl_open_record_partial: + ret = ssl_read_buffer_extend_to(ssl, consumed); + if (ret <= 0) { + return ret; } - return i; - } - left += i; - /* reads should *never* span multiple packets for DTLS because the - * underlying transport protocol is message oriented as opposed to byte - * oriented as in the TLS case. */ - if (SSL_IS_DTLS(s) && n > left) { - n = left; /* makes the while condition false */ - } - } + goto again; - /* done reading, now the book-keeping */ - rb->offset += n; - rb->left = left - n; - s->packet_length += n; - s->rwstate = SSL_NOTHING; + case ssl_open_record_discard: + ssl_read_buffer_consume(ssl, consumed); + goto again; - return n; -} - -/* MAX_EMPTY_RECORDS defines the number of consecutive, empty records that will - * be processed per call to ssl3_get_record. Without this limit an attacker - * could send empty records at a faster rate than we can process and cause - * ssl3_get_record to loop forever. */ -#define MAX_EMPTY_RECORDS 32 - -/* Call this to get a new input record. It will return <= 0 if more data is - * needed, normally due to an error or non-blocking IO. When it finishes, one - * packet has been decoded and can be found in - * ssl->s3->rrec.type - is the type of record - * ssl->s3->rrec.data - data - * ssl->s3->rrec.length - number of bytes */ -/* used only by ssl3_read_bytes */ -static int ssl3_get_record(SSL *s) { - uint8_t ssl_major, ssl_minor; - int al, n, i, ret = -1; - SSL3_RECORD *rr = &s->s3->rrec; - uint8_t *p; - uint16_t version; - size_t extra; - unsigned empty_record_count = 0; - -again: - /* check if we have the header */ - if (s->rstate != SSL_ST_READ_BODY || - s->packet_length < SSL3_RT_HEADER_LENGTH) { - n = ssl3_read_n(s, SSL3_RT_HEADER_LENGTH, 0); - if (n <= 0) { - return n; /* error or non-blocking */ - } - s->rstate = SSL_ST_READ_BODY; - - /* Some bytes were read, so the read buffer must be existant and - * |s->s3->init_extra| is defined. */ - assert(s->s3->rbuf.buf != NULL); - extra = s->s3->init_extra ? SSL3_RT_MAX_EXTRA : 0; - - p = s->packet; - if (s->msg_callback) { - s->msg_callback(0, 0, SSL3_RT_HEADER, p, 5, s, s->msg_callback_arg); - } - - /* Pull apart the header into the SSL3_RECORD */ - rr->type = *(p++); - ssl_major = *(p++); - ssl_minor = *(p++); - version = (((uint16_t)ssl_major) << 8) | ssl_minor; - n2s(p, rr->length); - - if (s->s3->have_version && version != s->version) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_record, SSL_R_WRONG_VERSION_NUMBER); - al = SSL_AD_PROTOCOL_VERSION; - goto f_err; - } - - if ((version >> 8) != SSL3_VERSION_MAJOR) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_record, SSL_R_WRONG_VERSION_NUMBER); - goto err; - } - - if (rr->length > SSL3_RT_MAX_ENCRYPTED_LENGTH + extra) { - al = SSL_AD_RECORD_OVERFLOW; - OPENSSL_PUT_ERROR(SSL, ssl3_get_record, SSL_R_ENCRYPTED_LENGTH_TOO_LONG); - goto f_err; - } - - /* now s->rstate == SSL_ST_READ_BODY */ - } else { - /* |packet_length| is non-zero and |s->rstate| is |SSL_ST_READ_BODY|. The - * read buffer must be existant and |s->s3->init_extra| is defined. */ - assert(s->s3->rbuf.buf != NULL); - extra = s->s3->init_extra ? SSL3_RT_MAX_EXTRA : 0; - } - - /* s->rstate == SSL_ST_READ_BODY, get and decode the data */ - - if (rr->length > s->packet_length - SSL3_RT_HEADER_LENGTH) { - /* now s->packet_length == SSL3_RT_HEADER_LENGTH */ - i = rr->length; - n = ssl3_read_n(s, i, 1); - if (n <= 0) { - /* Error or non-blocking IO. Now |n| == |rr->length|, and - * |s->packet_length| == |SSL3_RT_HEADER_LENGTH| + |rr->length|. */ - return n; - } - } - - s->rstate = SSL_ST_READ_HEADER; /* set state for later operations */ - - /* |rr->data| points to |rr->length| bytes of ciphertext in |s->packet|. */ - rr->data = &s->packet[SSL3_RT_HEADER_LENGTH]; - - /* Decrypt the packet in-place. - * - * TODO(davidben): This assumes |s->version| is the same as the record-layer - * version which isn't always true, but it only differs with the NULL cipher - * which ignores the parameter. */ - size_t plaintext_len; - if (!SSL_AEAD_CTX_open(s->aead_read_ctx, rr->data, &plaintext_len, rr->length, - rr->type, s->version, s->s3->read_sequence, rr->data, - rr->length)) { - al = SSL_AD_BAD_RECORD_MAC; - OPENSSL_PUT_ERROR(SSL, ssl3_get_record, - SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC); - goto f_err; - } - if (!ssl3_record_sequence_update(s->s3->read_sequence, 8)) { - goto err; - } - if (plaintext_len > SSL3_RT_MAX_PLAIN_LENGTH + extra) { - al = SSL_AD_RECORD_OVERFLOW; - OPENSSL_PUT_ERROR(SSL, ssl3_get_record, SSL_R_DATA_LENGTH_TOO_LONG); - goto f_err; - } - assert(plaintext_len <= (1u << 16)); - rr->length = plaintext_len; - - rr->off = 0; - /* So at this point the following is true: - * ssl->s3->rrec.type is the type of record; - * ssl->s3->rrec.length is the number of bytes in the record; - * ssl->s3->rrec.off is the offset to first valid byte; - * ssl->s3->rrec.data the first byte of the record body. */ - - /* we have pulled in a full packet so zero things */ - s->packet_length = 0; - - /* just read a 0 length packet */ - if (rr->length == 0) { - empty_record_count++; - if (empty_record_count > MAX_EMPTY_RECORDS) { - al = SSL_AD_UNEXPECTED_MESSAGE; - OPENSSL_PUT_ERROR(SSL, ssl3_get_record, SSL_R_TOO_MANY_EMPTY_FRAGMENTS); - goto f_err; - } - goto again; + case ssl_open_record_error: + ssl3_send_alert(ssl, SSL3_AL_FATAL, alert); + return -1; } - return 1; - -f_err: - ssl3_send_alert(s, SSL3_AL_FATAL, al); -err: - return ret; + assert(0); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return -1; } int ssl3_write_app_data(SSL *ssl, const void *buf, int len) { @@ -420,7 +205,7 @@ int ssl3_write_bytes(SSL *s, int type, const void *buf_, int len) { return i; } if (i == 0) { - OPENSSL_PUT_ERROR(SSL, ssl3_write_bytes, SSL_R_SSL_HANDSHAKE_FAILURE); + OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_HANDSHAKE_FAILURE); return -1; } } @@ -433,33 +218,22 @@ int ssl3_write_bytes(SSL *s, int type, const void *buf_, int len) { * beyond the end of the users buffer ... so we trap and report the error in * a way the user will notice. */ if (len < 0 || (size_t)len < tot) { - OPENSSL_PUT_ERROR(SSL, ssl3_write_bytes, SSL_R_BAD_LENGTH); + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_LENGTH); return -1; } - int record_split_done = 0; n = (len - tot); for (;;) { /* max contains the maximum number of bytes that we can put into a * record. */ unsigned max = s->max_send_fragment; - /* fragment is true if do_ssl3_write should send the first byte in its own - * record in order to randomise a CBC IV. */ - int fragment = 0; - if (!record_split_done && s->s3->need_record_splitting && - type == SSL3_RT_APPLICATION_DATA) { - /* Only the the first record per write call needs to be split. The - * remaining plaintext was determined before the IV was randomized. */ - fragment = 1; - record_split_done = 1; - } if (n > max) { nw = max; } else { nw = n; } - i = do_ssl3_write(s, type, &buf[tot], nw, fragment); + i = do_ssl3_write(s, type, &buf[tot], nw); if (i <= 0) { s->s3->wnum = tot; return i; @@ -475,65 +249,10 @@ int ssl3_write_bytes(SSL *s, int type, const void *buf_, int len) { } } -/* ssl3_seal_record seals a new record of type |type| and plaintext |in| and - * writes it to |out|. At most |max_out| bytes will be written. It returns one - * on success and zero on error. On success, it updates the write sequence - * number. */ -static int ssl3_seal_record(SSL *s, uint8_t *out, size_t *out_len, - size_t max_out, uint8_t type, const uint8_t *in, - size_t in_len) { - if (max_out < SSL3_RT_HEADER_LENGTH) { - OPENSSL_PUT_ERROR(SSL, ssl3_seal_record, SSL_R_BUFFER_TOO_SMALL); - return 0; - } - - out[0] = type; - - /* Some servers hang if initial ClientHello is larger than 256 bytes and - * record version number > TLS 1.0. */ - uint16_t wire_version = s->version; - if (!s->s3->have_version && s->version > SSL3_VERSION) { - wire_version = TLS1_VERSION; - } - out[1] = wire_version >> 8; - out[2] = wire_version & 0xff; - - size_t ciphertext_len; - if (!SSL_AEAD_CTX_seal(s->aead_write_ctx, out + SSL3_RT_HEADER_LENGTH, - &ciphertext_len, max_out - SSL3_RT_HEADER_LENGTH, - type, wire_version, s->s3->write_sequence, in, - in_len) || - !ssl3_record_sequence_update(s->s3->write_sequence, 8)) { - return 0; - } - - if (ciphertext_len >= 1 << 16) { - OPENSSL_PUT_ERROR(SSL, ssl3_seal_record, ERR_R_OVERFLOW); - return 0; - } - out[3] = ciphertext_len >> 8; - out[4] = ciphertext_len & 0xff; - - *out_len = SSL3_RT_HEADER_LENGTH + ciphertext_len; - - if (s->msg_callback) { - s->msg_callback(1 /* write */, 0, SSL3_RT_HEADER, out, SSL3_RT_HEADER_LENGTH, - s, s->msg_callback_arg); - } - - return 1; -} - -/* do_ssl3_write writes an SSL record of the given type. If |fragment| is 1 - * then it splits the record into a one byte record and a record with the rest - * of the data in order to randomise a CBC IV. */ -static int do_ssl3_write(SSL *s, int type, const uint8_t *buf, unsigned int len, - char fragment) { - SSL3_BUFFER *wb = &s->s3->wbuf; - - /* first check if there is a SSL3_BUFFER still being written out. This will - * happen with non blocking IO */ - if (wb->left != 0) { +/* do_ssl3_write writes an SSL record of the given type. */ +static int do_ssl3_write(SSL *s, int type, const uint8_t *buf, unsigned len) { + /* If there is still data from the previous record, flush it. */ + if (ssl_write_buffer_is_pending(s)) { return ssl3_write_pending(s, type, buf, len); } @@ -546,113 +265,53 @@ static int do_ssl3_write(SSL *s, int type, const uint8_t *buf, unsigned int len, /* if it went, fall through and send more stuff */ } - if (wb->buf == NULL && !ssl3_setup_write_buffer(s)) { + if (len > SSL3_RT_MAX_PLAIN_LENGTH) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return -1; } if (len == 0) { return 0; } - if (len == 1) { - /* No sense in fragmenting a one-byte record. */ - fragment = 0; - } - /* Align the output so the ciphertext is aligned to |SSL3_ALIGN_PAYLOAD|. */ - uintptr_t align; - if (fragment) { - /* Only CBC-mode ciphers require fragmenting. CBC-mode ciphertext is a - * multiple of the block size which we may assume is aligned. Thus we only - * need to account for a second copy of the record header. */ - align = (uintptr_t)wb->buf + 2 * SSL3_RT_HEADER_LENGTH; - } else { - align = (uintptr_t)wb->buf + SSL3_RT_HEADER_LENGTH; - } - align = (0 - align) & (SSL3_ALIGN_PAYLOAD - 1); - uint8_t *out = wb->buf + align; - wb->offset = align; - size_t max_out = wb->len - wb->offset; - - const uint8_t *orig_buf = buf; - unsigned int orig_len = len; - size_t fragment_len = 0; - if (fragment) { - /* Write the first byte in its own record as a countermeasure against - * known-IV weaknesses in CBC ciphersuites. (See - * http://www.openssl.org/~bodo/tls-cbc.txt.) */ - if (!ssl3_seal_record(s, out, &fragment_len, max_out, type, buf, 1)) { - return -1; - } - out += fragment_len; - max_out -= fragment_len; - buf++; - len--; + size_t max_out = len + ssl_max_seal_overhead(s); + if (max_out < len) { + OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW); + return -1; } - - assert((((uintptr_t)out + SSL3_RT_HEADER_LENGTH) & (SSL3_ALIGN_PAYLOAD - 1)) - == 0); + uint8_t *out; size_t ciphertext_len; - if (!ssl3_seal_record(s, out, &ciphertext_len, max_out, type, buf, len)) { + if (!ssl_write_buffer_init(s, &out, max_out) || + !tls_seal_record(s, out, &ciphertext_len, max_out, type, buf, len)) { return -1; } - ciphertext_len += fragment_len; - - /* now let's set up wb */ - wb->left = ciphertext_len; + ssl_write_buffer_set_len(s, ciphertext_len); /* memorize arguments so that ssl3_write_pending can detect bad write retries * later */ - s->s3->wpend_tot = orig_len; - s->s3->wpend_buf = orig_buf; + s->s3->wpend_tot = len; + s->s3->wpend_buf = buf; s->s3->wpend_type = type; - s->s3->wpend_ret = orig_len; + s->s3->wpend_ret = len; /* we now just need to write the buffer */ - return ssl3_write_pending(s, type, orig_buf, orig_len); + return ssl3_write_pending(s, type, buf, len); } -/* if s->s3->wbuf.left != 0, we need to call this */ int ssl3_write_pending(SSL *s, int type, const uint8_t *buf, unsigned int len) { - int i; - SSL3_BUFFER *wb = &(s->s3->wbuf); - if (s->s3->wpend_tot > (int)len || (s->s3->wpend_buf != buf && !(s->mode & SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER)) || s->s3->wpend_type != type) { - OPENSSL_PUT_ERROR(SSL, ssl3_write_pending, SSL_R_BAD_WRITE_RETRY); + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_WRITE_RETRY); return -1; } - for (;;) { - ERR_clear_system_error(); - if (s->wbio != NULL) { - s->rwstate = SSL_WRITING; - i = BIO_write(s->wbio, (char *)&(wb->buf[wb->offset]), - (unsigned int)wb->left); - } else { - OPENSSL_PUT_ERROR(SSL, ssl3_write_pending, SSL_R_BIO_NOT_SET); - i = -1; - } - if (i == wb->left) { - wb->left = 0; - wb->offset += i; - ssl3_release_write_buffer(s); - s->rwstate = SSL_NOTHING; - return s->s3->wpend_ret; - } else if (i <= 0) { - if (SSL_IS_DTLS(s)) { - /* For DTLS, just drop it. That's kind of the whole point in - * using a datagram service */ - wb->left = 0; - } - return i; - } - /* TODO(davidben): This codepath is used in DTLS, but the write - * payload may not split across packets. */ - wb->offset += i; - wb->left -= i; + int ret = ssl_write_buffer_flush(s); + if (ret <= 0) { + return ret; } + return s->s3->wpend_ret; } /* ssl3_expect_change_cipher_spec informs the record layer that a @@ -662,8 +321,7 @@ int ssl3_write_pending(SSL *s, int type, const uint8_t *buf, unsigned int len) { * function returns zero. Otherwise, the function returns one. */ int ssl3_expect_change_cipher_spec(SSL *s) { if (s->s3->handshake_fragment_len > 0 || s->s3->tmp.reuse_message) { - OPENSSL_PUT_ERROR(SSL, ssl3_expect_change_cipher_spec, - SSL_R_UNPROCESSED_HANDSHAKE_DATA); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNPROCESSED_HANDSHAKE_DATA); return 0; } @@ -714,7 +372,7 @@ int ssl3_read_bytes(SSL *s, int type, uint8_t *buf, int len, int peek) { if ((type && type != SSL3_RT_APPLICATION_DATA && type != SSL3_RT_HANDSHAKE) || (peek && type != SSL3_RT_APPLICATION_DATA)) { - OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return -1; } @@ -753,7 +411,7 @@ int ssl3_read_bytes(SSL *s, int type, uint8_t *buf, int len, int peek) { return i; } if (i == 0) { - OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_SSL_HANDSHAKE_FAILURE); + OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_HANDSHAKE_FAILURE); return -1; } } @@ -768,7 +426,7 @@ start: rr = &s->s3->rrec; /* get new packet if necessary */ - if (rr->length == 0 || s->rstate == SSL_ST_READ_BODY) { + if (rr->length == 0) { ret = ssl3_get_record(s); if (ret <= 0) { return ret; @@ -782,8 +440,7 @@ start: if (s->s3->change_cipher_spec && rr->type != SSL3_RT_HANDSHAKE && rr->type != SSL3_RT_ALERT) { al = SSL_AD_UNEXPECTED_MESSAGE; - OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, - SSL_R_DATA_BETWEEN_CCS_AND_FINISHED); + OPENSSL_PUT_ERROR(SSL, SSL_R_DATA_BETWEEN_CCS_AND_FINISHED); goto f_err; } @@ -791,7 +448,7 @@ start: * Handshake record. */ if (rr->type == SSL3_RT_HANDSHAKE && (s->s3->flags & SSL3_FLAGS_EXPECT_CCS)) { al = SSL_AD_UNEXPECTED_MESSAGE; - OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_HANDSHAKE_RECORD_BEFORE_CCS); + OPENSSL_PUT_ERROR(SSL, SSL_R_HANDSHAKE_RECORD_BEFORE_CCS); goto f_err; } @@ -803,7 +460,9 @@ start: return 0; } - if (type == rr->type) { + if (type != 0 && type == rr->type) { + s->s3->warning_alert_count = 0; + /* SSL3_RT_APPLICATION_DATA or SSL3_RT_HANDSHAKE */ /* make sure that we are not getting application data when we are doing a * handshake for the first time */ @@ -812,10 +471,15 @@ start: /* TODO(davidben): Is this check redundant with the handshake_func * check? */ al = SSL_AD_UNEXPECTED_MESSAGE; - OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_APP_DATA_IN_HANDSHAKE); + OPENSSL_PUT_ERROR(SSL, SSL_R_APP_DATA_IN_HANDSHAKE); goto f_err; } + /* Discard empty records. */ + if (rr->length == 0) { + goto start; + } + if (len <= 0) { return len; } @@ -831,11 +495,9 @@ start: rr->length -= n; rr->off += n; if (rr->length == 0) { - s->rstate = SSL_ST_READ_HEADER; rr->off = 0; - if (s->s3->rbuf.left == 0) { - ssl3_release_read_buffer(s); - } + /* The record has been consumed, so we may now clear the buffer. */ + ssl_read_buffer_discard(s); } } @@ -849,7 +511,7 @@ start: * are fatal. Renegotiations as a server are never supported. */ if (!s->accept_peer_renegotiations || s->server) { al = SSL_AD_NO_RENEGOTIATION; - OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_NO_RENEGOTIATION); + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_RENEGOTIATION); goto f_err; } @@ -872,7 +534,7 @@ start: s->s3->handshake_fragment[2] != 0 || s->s3->handshake_fragment[3] != 0) { al = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_BAD_HELLO_REQUEST); + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_HELLO_REQUEST); goto f_err; } s->s3->handshake_fragment_len = 0; @@ -886,7 +548,7 @@ start: /* This cannot happen. If a handshake is in progress, |type| must be * |SSL3_RT_HANDSHAKE|. */ assert(0); - OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); goto err; } @@ -894,9 +556,9 @@ start: * protocol, namely in HTTPS, just before reading the HTTP response. Require * the record-layer be idle and avoid complexities of sending a handshake * record while an application_data record is being written. */ - if (s->s3->wbuf.left != 0 || s->s3->rbuf.left != 0) { + if (ssl_write_buffer_is_pending(s)) { al = SSL_AD_NO_RENEGOTIATION; - OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_NO_RENEGOTIATION); + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_RENEGOTIATION); goto f_err; } @@ -907,7 +569,7 @@ start: return i; } if (i == 0) { - OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_SSL_HANDSHAKE_FAILURE); + OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_HANDSHAKE_FAILURE); return -1; } @@ -921,7 +583,7 @@ start: /* Alerts may not be fragmented. */ if (rr->length < 2) { al = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_BAD_ALERT); + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ALERT); goto f_err; } @@ -960,7 +622,14 @@ start: * peer refused it where we carry on. */ else if (alert_descr == SSL_AD_NO_RENEGOTIATION) { al = SSL_AD_HANDSHAKE_FAILURE; - OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_NO_RENEGOTIATION); + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_RENEGOTIATION); + goto f_err; + } + + s->s3->warning_alert_count++; + if (s->s3->warning_alert_count > kMaxWarningAlerts) { + al = SSL_AD_UNEXPECTED_MESSAGE; + OPENSSL_PUT_ERROR(SSL, SSL_R_TOO_MANY_WARNING_ALERTS); goto f_err; } } else if (alert_level == SSL3_AL_FATAL) { @@ -968,8 +637,7 @@ start: s->rwstate = SSL_NOTHING; s->s3->fatal_alert = alert_descr; - OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, - SSL_AD_REASON_OFFSET + alert_descr); + OPENSSL_PUT_ERROR(SSL, SSL_AD_REASON_OFFSET + alert_descr); BIO_snprintf(tmp, sizeof(tmp), "%d", alert_descr); ERR_add_error_data(2, "SSL alert number ", tmp); s->shutdown |= SSL_RECEIVED_SHUTDOWN; @@ -977,7 +645,7 @@ start: return 0; } else { al = SSL_AD_ILLEGAL_PARAMETER; - OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_UNKNOWN_ALERT_TYPE); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_ALERT_TYPE); goto f_err; } @@ -985,10 +653,9 @@ start: } if (s->shutdown & SSL_SENT_SHUTDOWN) { - /* but we have not received a shutdown */ - s->rwstate = SSL_NOTHING; + /* close_notify has been sent, so discard all records other than alerts. */ rr->length = 0; - return 0; + goto start; } if (rr->type == SSL3_RT_CHANGE_CIPHER_SPEC) { @@ -996,20 +663,20 @@ start: * record payload has to look like */ if (rr->length != 1 || rr->off != 0 || rr->data[0] != SSL3_MT_CCS) { al = SSL_AD_ILLEGAL_PARAMETER; - OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_BAD_CHANGE_CIPHER_SPEC); + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_CHANGE_CIPHER_SPEC); goto f_err; } /* Check we have a cipher to change to */ if (s->s3->tmp.new_cipher == NULL) { al = SSL_AD_UNEXPECTED_MESSAGE; - OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_CCS_RECEIVED_EARLY); + OPENSSL_PUT_ERROR(SSL, SSL_R_CCS_RECEIVED_EARLY); goto f_err; } if (!(s->s3->flags & SSL3_FLAGS_EXPECT_CCS)) { al = SSL_AD_UNEXPECTED_MESSAGE; - OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_CCS_RECEIVED_EARLY); + OPENSSL_PUT_ERROR(SSL, SSL_R_CCS_RECEIVED_EARLY); goto f_err; } @@ -1035,7 +702,7 @@ start: rr->type != SSL3_RT_HANDSHAKE); al = SSL_AD_UNEXPECTED_MESSAGE; - OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_UNEXPECTED_RECORD); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD); f_err: ssl3_send_alert(s, SSL3_AL_FATAL, al); @@ -1055,8 +722,7 @@ int ssl3_do_change_cipher_spec(SSL *s) { if (s->s3->tmp.key_block == NULL) { if (s->session == NULL || s->session->master_key_length == 0) { /* might happen if dtls1_read_bytes() calls this */ - OPENSSL_PUT_ERROR(SSL, ssl3_do_change_cipher_spec, - SSL_R_CCS_RECEIVED_EARLY); + OPENSSL_PUT_ERROR(SSL, SSL_R_CCS_RECEIVED_EARLY); return 0; } @@ -1092,8 +758,9 @@ int ssl3_send_alert(SSL *s, int level, int desc) { s->s3->alert_dispatch = 1; s->s3->send_alert[0] = level; s->s3->send_alert[1] = desc; - if (s->s3->wbuf.left == 0) { - /* data is still being written out. */ + if (!ssl_write_buffer_is_pending(s)) { + /* Nothing is being written out, so the alert may be dispatched + * immediately. */ return s->method->ssl_dispatch_alert(s); } @@ -1107,7 +774,7 @@ int ssl3_dispatch_alert(SSL *s) { void (*cb)(const SSL *ssl, int type, int val) = NULL; s->s3->alert_dispatch = 0; - i = do_ssl3_write(s, SSL3_RT_ALERT, &s->s3->send_alert[0], 2, 0); + i = do_ssl3_write(s, SSL3_RT_ALERT, &s->s3->send_alert[0], 2); if (i <= 0) { s->s3->alert_dispatch = 1; } else { diff --git a/src/ssl/s3_srvr.c b/src/ssl/s3_srvr.c index a72e17e..b428043 100644 --- a/src/ssl/s3_srvr.c +++ b/src/ssl/s3_srvr.c @@ -146,6 +146,8 @@ * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR * OTHERWISE. */ +#include <openssl/ssl.h> + #include <assert.h> #include <stdio.h> #include <string.h> @@ -172,10 +174,6 @@ #include "../crypto/dh/internal.h" -/* INITIAL_SNIFF_BUFFER_SIZE is the number of bytes read in the initial sniff - * buffer. */ -#define INITIAL_SNIFF_BUFFER_SIZE 8 - int ssl3_accept(SSL *s) { BUF_MEM *buf = NULL; uint32_t alg_a; @@ -199,7 +197,7 @@ int ssl3_accept(SSL *s) { s->in_handshake++; if (s->cert == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_accept, SSL_R_NO_CERTIFICATE_SET); + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_SET); return -1; } @@ -230,8 +228,8 @@ int ssl3_accept(SSL *s) { goto end; } - if (!ssl3_init_finished_mac(s)) { - OPENSSL_PUT_ERROR(SSL, ssl3_accept, ERR_R_INTERNAL_ERROR); + if (!ssl3_init_handshake_buffer(s)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); ret = -1; goto end; } @@ -310,8 +308,19 @@ int ssl3_accept(SSL *s) { s->init_num = 0; break; + case SSL3_ST_SW_CERT_STATUS_A: + case SSL3_ST_SW_CERT_STATUS_B: + ret = ssl3_send_certificate_status(s); + if (ret <= 0) { + goto end; + } + s->state = SSL3_ST_SW_KEY_EXCH_A; + s->init_num = 0; + break; + case SSL3_ST_SW_KEY_EXCH_A: case SSL3_ST_SW_KEY_EXCH_B: + case SSL3_ST_SW_KEY_EXCH_C: alg_a = s->s3->tmp.new_cipher->algorithm_auth; /* Send a ServerKeyExchange message if: @@ -473,7 +482,7 @@ int ssl3_accept(SSL *s) { /* If this is a full handshake with ChannelID then record the hashshake * hashes in |s->session| in case we need them to verify a ChannelID * signature on a resumption of this session in the future. */ - if (!s->hit && s->s3->tlsext_channel_id_new) { + if (!s->hit) { ret = tls1_record_handshake_hashes_for_channel_id(s); if (ret <= 0) { goto end; @@ -550,6 +559,8 @@ int ssl3_accept(SSL *s) { if (s->ctx->retain_only_sha256_of_client_certs) { X509_free(s->session->peer); s->session->peer = NULL; + sk_X509_pop_free(s->session->cert_chain, X509_free); + s->session->cert_chain = NULL; } s->s3->initial_handshake_complete = 1; @@ -564,7 +575,7 @@ int ssl3_accept(SSL *s) { goto end; default: - OPENSSL_PUT_ERROR(SSL, ssl3_accept, SSL_R_UNKNOWN_STATE); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_STATE); ret = -1; goto end; } @@ -587,42 +598,16 @@ end: return ret; } -static int ssl3_read_sniff_buffer(SSL *s, size_t n) { - if (s->s3->sniff_buffer == NULL) { - s->s3->sniff_buffer = BUF_MEM_new(); - } - if (s->s3->sniff_buffer == NULL || !BUF_MEM_grow(s->s3->sniff_buffer, n)) { - return -1; - } - - while (s->s3->sniff_buffer_len < n) { - int ret; - - s->rwstate = SSL_READING; - ret = BIO_read(s->rbio, s->s3->sniff_buffer->data + s->s3->sniff_buffer_len, - n - s->s3->sniff_buffer_len); - if (ret <= 0) { - return ret; - } - s->rwstate = SSL_NOTHING; - s->s3->sniff_buffer_len += ret; - } - - return 1; -} - int ssl3_get_initial_bytes(SSL *s) { - int ret; - const uint8_t *p; - - /* Read the first 8 bytes. To recognize a ClientHello or V2ClientHello only - * needs the first 6 bytes, but 8 is needed to recognize CONNECT below. */ - ret = ssl3_read_sniff_buffer(s, INITIAL_SNIFF_BUFFER_SIZE); + /* Read the first 5 bytes, the size of the TLS record header. This is + * sufficient to detect a V2ClientHello and ensures that we never read beyond + * the first record. */ + int ret = ssl_read_buffer_extend_to(s, SSL3_RT_HEADER_LENGTH); if (ret <= 0) { return ret; } - assert(s->s3->sniff_buffer_len >= INITIAL_SNIFF_BUFFER_SIZE); - p = (const uint8_t *)s->s3->sniff_buffer->data; + assert(ssl_read_buffer_len(s) == SSL3_RT_HEADER_LENGTH); + const uint8_t *p = ssl_read_buffer(s); /* Some dedicated error codes for protocol mixups should the application wish * to interpret them differently. (These do not overlap with ClientHello or @@ -631,46 +616,25 @@ int ssl3_get_initial_bytes(SSL *s) { strncmp("POST ", (const char *)p, 5) == 0 || strncmp("HEAD ", (const char *)p, 5) == 0 || strncmp("PUT ", (const char *)p, 4) == 0) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_initial_bytes, SSL_R_HTTP_REQUEST); + OPENSSL_PUT_ERROR(SSL, SSL_R_HTTP_REQUEST); return -1; } - if (strncmp("CONNECT ", (const char *)p, 8) == 0) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_initial_bytes, SSL_R_HTTPS_PROXY_REQUEST); + if (strncmp("CONNE", (const char *)p, 5) == 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_HTTPS_PROXY_REQUEST); return -1; } - /* Determine if this is a ClientHello or V2ClientHello. */ + /* Determine if this is a V2ClientHello. */ if ((p[0] & 0x80) && p[2] == SSL2_MT_CLIENT_HELLO && p[3] >= SSL3_VERSION_MAJOR) { /* This is a V2ClientHello. */ s->state = SSL3_ST_SR_V2_CLIENT_HELLO; return 1; } - if (p[0] == SSL3_RT_HANDSHAKE && p[1] >= SSL3_VERSION_MAJOR && - p[5] == SSL3_MT_CLIENT_HELLO) { - /* This is a ClientHello. Initialize the record layer with the already - * consumed data and continue the handshake. */ - if (!ssl3_setup_read_buffer(s)) { - return -1; - } - assert(s->rstate == SSL_ST_READ_HEADER); - /* There cannot have already been data in the record layer. */ - assert(s->s3->rbuf.left == 0); - memcpy(s->s3->rbuf.buf, p, s->s3->sniff_buffer_len); - s->s3->rbuf.offset = 0; - s->s3->rbuf.left = s->s3->sniff_buffer_len; - s->packet_length = 0; - - BUF_MEM_free(s->s3->sniff_buffer); - s->s3->sniff_buffer = NULL; - s->s3->sniff_buffer_len = 0; - - s->state = SSL3_ST_SR_CLNT_HELLO_A; - return 1; - } - OPENSSL_PUT_ERROR(SSL, ssl3_get_initial_bytes, SSL_R_UNKNOWN_PROTOCOL); - return -1; + /* Fall through to the standard logic. */ + s->state = SSL3_ST_SR_CLNT_HELLO_A; + return 1; } int ssl3_get_v2_client_hello(SSL *s) { @@ -683,36 +647,34 @@ int ssl3_get_v2_client_hello(SSL *s) { CBB client_hello, hello_body, cipher_suites; uint8_t random[SSL3_RANDOM_SIZE]; - /* Read the remainder of the V2ClientHello. We have previously read 8 bytes - * in ssl3_get_initial_bytes. */ - assert(s->s3->sniff_buffer_len >= INITIAL_SNIFF_BUFFER_SIZE); - p = (const uint8_t *)s->s3->sniff_buffer->data; + /* Determine the length of the V2ClientHello. */ + assert(ssl_read_buffer_len(s) >= SSL3_RT_HEADER_LENGTH); + p = ssl_read_buffer(s); msg_length = ((p[0] & 0x7f) << 8) | p[1]; if (msg_length > (1024 * 4)) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_v2_client_hello, SSL_R_RECORD_TOO_LARGE); + OPENSSL_PUT_ERROR(SSL, SSL_R_RECORD_TOO_LARGE); return -1; } - if (msg_length < INITIAL_SNIFF_BUFFER_SIZE - 2) { - /* Reject lengths that are too short early. We have already read 8 bytes, - * so we should not attempt to process an (invalid) V2ClientHello which - * would be shorter than that. */ - OPENSSL_PUT_ERROR(SSL, ssl3_get_v2_client_hello, - SSL_R_RECORD_LENGTH_MISMATCH); + if (msg_length < SSL3_RT_HEADER_LENGTH - 2) { + /* Reject lengths that are too short early. We have already read + * |SSL3_RT_HEADER_LENGTH| bytes, so we should not attempt to process an + * (invalid) V2ClientHello which would be shorter than that. */ + OPENSSL_PUT_ERROR(SSL, SSL_R_RECORD_LENGTH_MISMATCH); return -1; } - ret = ssl3_read_sniff_buffer(s, msg_length + 2); + /* Read the remainder of the V2ClientHello. */ + ret = ssl_read_buffer_extend_to(s, 2 + msg_length); if (ret <= 0) { return ret; } - assert(s->s3->sniff_buffer_len == msg_length + 2); - CBS_init(&v2_client_hello, (const uint8_t *)s->s3->sniff_buffer->data + 2, - msg_length); + assert(ssl_read_buffer_len(s) == msg_length + 2); + CBS_init(&v2_client_hello, ssl_read_buffer(s) + 2, msg_length); - /* The V2ClientHello without the length is incorporated into the Finished + /* The V2ClientHello without the length is incorporated into the handshake * hash. */ - if (!ssl3_finish_mac(s, CBS_data(&v2_client_hello), - CBS_len(&v2_client_hello))) { + if (!ssl3_update_handshake_hash(s, CBS_data(&v2_client_hello), + CBS_len(&v2_client_hello))) { return -1; } if (s->msg_callback) { @@ -729,7 +691,7 @@ int ssl3_get_v2_client_hello(SSL *s) { !CBS_get_bytes(&v2_client_hello, &session_id, session_id_length) || !CBS_get_bytes(&v2_client_hello, &challenge, challenge_length) || CBS_len(&v2_client_hello) != 0) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_v2_client_hello, SSL_R_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); return -1; } @@ -747,12 +709,10 @@ int ssl3_get_v2_client_hello(SSL *s) { rand_len); /* Write out an equivalent SSLv3 ClientHello. */ + CBB_zero(&client_hello); if (!CBB_init_fixed(&client_hello, (uint8_t *)s->init_buf->data, - s->init_buf->max)) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_v2_client_hello, ERR_R_MALLOC_FAILURE); - return -1; - } - if (!CBB_add_u8(&client_hello, SSL3_MT_CLIENT_HELLO) || + s->init_buf->max) || + !CBB_add_u8(&client_hello, SSL3_MT_CLIENT_HELLO) || !CBB_add_u24_length_prefixed(&client_hello, &hello_body) || !CBB_add_u16(&hello_body, version) || !CBB_add_bytes(&hello_body, random, SSL3_RANDOM_SIZE) || @@ -760,7 +720,7 @@ int ssl3_get_v2_client_hello(SSL *s) { !CBB_add_u8(&hello_body, 0) || !CBB_add_u16_length_prefixed(&hello_body, &cipher_suites)) { CBB_cleanup(&client_hello); - OPENSSL_PUT_ERROR(SSL, ssl3_get_v2_client_hello, ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); return -1; } @@ -769,7 +729,7 @@ int ssl3_get_v2_client_hello(SSL *s) { uint32_t cipher_spec; if (!CBS_get_u24(&cipher_specs, &cipher_spec)) { CBB_cleanup(&client_hello); - OPENSSL_PUT_ERROR(SSL, ssl3_get_v2_client_hello, SSL_R_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); return -1; } @@ -779,7 +739,7 @@ int ssl3_get_v2_client_hello(SSL *s) { } if (!CBB_add_u16(&cipher_suites, cipher_spec)) { CBB_cleanup(&client_hello); - OPENSSL_PUT_ERROR(SSL, ssl3_get_v2_client_hello, ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return -1; } } @@ -788,7 +748,7 @@ int ssl3_get_v2_client_hello(SSL *s) { if (!CBB_add_u8(&hello_body, 1) || !CBB_add_u8(&hello_body, 0) || !CBB_finish(&client_hello, NULL, &len)) { CBB_cleanup(&client_hello); - OPENSSL_PUT_ERROR(SSL, ssl3_get_v2_client_hello, ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return -1; } @@ -798,10 +758,9 @@ int ssl3_get_v2_client_hello(SSL *s) { /* The handshake message header is 4 bytes. */ s->s3->tmp.message_size = len - 4; - /* Drop the sniff buffer. */ - BUF_MEM_free(s->s3->sniff_buffer); - s->s3->sniff_buffer = NULL; - s->s3->sniff_buffer_len = 0; + /* Consume and discard the V2ClientHello. */ + ssl_read_buffer_consume(s, 2 + msg_length); + ssl_read_buffer_discard(s); return 1; } @@ -815,6 +774,7 @@ int ssl3_get_client_hello(SSL *s) { CBS client_hello; uint16_t client_version; CBS client_random, session_id, cipher_suites, compression_methods; + SSL_SESSION *session = NULL; /* We do this so that we will respond with our native type. If we are TLSv1 * and we get SSLv3, we will respond with TLSv1, This down switching should @@ -847,8 +807,7 @@ int ssl3_get_client_hello(SSL *s) { early_ctx.client_hello_len = n; if (!ssl_early_callback_init(&early_ctx)) { al = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, - SSL_R_CLIENTHELLO_PARSE_FAILED); + OPENSSL_PUT_ERROR(SSL, SSL_R_CLIENTHELLO_PARSE_FAILED); goto f_err; } @@ -863,8 +822,7 @@ int ssl3_get_client_hello(SSL *s) { case -1: /* Connection rejected. */ al = SSL_AD_ACCESS_DENIED; - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, - SSL_R_CONNECTION_REJECTED); + OPENSSL_PUT_ERROR(SSL, SSL_R_CONNECTION_REJECTED); goto f_err; default: @@ -875,7 +833,7 @@ int ssl3_get_client_hello(SSL *s) { break; default: - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_UNKNOWN_STATE); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_STATE); return -1; } @@ -885,7 +843,7 @@ int ssl3_get_client_hello(SSL *s) { !CBS_get_u8_length_prefixed(&client_hello, &session_id) || CBS_len(&session_id) > SSL_MAX_SSL_SESSION_ID_LENGTH) { al = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); goto f_err; } @@ -902,7 +860,7 @@ int ssl3_get_client_hello(SSL *s) { if (!CBS_get_u8_length_prefixed(&client_hello, &cookie) || CBS_len(&cookie) > DTLS1_COOKIE_LENGTH) { al = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); goto f_err; } } @@ -916,7 +874,7 @@ int ssl3_get_client_hello(SSL *s) { /* Select version to use */ uint16_t version = ssl3_get_mutual_version(s, client_version); if (version == 0) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_UNSUPPORTED_PROTOCOL); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL); s->version = s->client_version; al = SSL_AD_PROTOCOL_VERSION; goto f_err; @@ -929,19 +887,23 @@ int ssl3_get_client_hello(SSL *s) { s->s3->have_version = 1; } else if (SSL_IS_DTLS(s) ? (s->client_version > s->version) : (s->client_version < s->version)) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_WRONG_VERSION_NUMBER); + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_VERSION_NUMBER); al = SSL_AD_PROTOCOL_VERSION; goto f_err; } s->hit = 0; - int session_ret = ssl_get_prev_session(s, &early_ctx); - if (session_ret == PENDING_SESSION) { - s->rwstate = SSL_PENDING_SESSION; - goto err; - } else if (session_ret == -1) { - goto err; + int send_new_ticket = 0; + switch (ssl_get_prev_session(s, &session, &send_new_ticket, &early_ctx)) { + case ssl_session_success: + break; + case ssl_session_error: + goto err; + case ssl_session_retry: + s->rwstate = SSL_PENDING_SESSION; + goto err; } + s->tlsext_ticket_expected = send_new_ticket; /* The EMS state is needed when making the resumption decision, but * extensions are not normally parsed until later. This detects the EMS @@ -956,34 +918,40 @@ int ssl3_get_client_hello(SSL *s) { &ems_data, &ems_len) && ems_len == 0; - if (session_ret == 1) { - if (s->session->extended_master_secret && + if (session != NULL) { + if (session->extended_master_secret && !have_extended_master_secret) { /* A ClientHello without EMS that attempts to resume a session with EMS * is fatal to the connection. */ al = SSL_AD_HANDSHAKE_FAILURE; - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, - SSL_R_RESUMED_EMS_SESSION_WITHOUT_EMS_EXTENSION); + OPENSSL_PUT_ERROR(SSL, SSL_R_RESUMED_EMS_SESSION_WITHOUT_EMS_EXTENSION); goto f_err; } s->hit = /* Only resume if the session's version matches the negotiated version: * most clients do not accept a mismatch. */ - s->version == s->session->ssl_version && + s->version == session->ssl_version && /* If the client offers the EMS extension, but the previous session * didn't use it, then negotiate a new session. */ - have_extended_master_secret == s->session->extended_master_secret; + have_extended_master_secret == session->extended_master_secret; } - if (!s->hit && !ssl_get_new_session(s, 1)) { + if (s->hit) { + /* Use the new session. */ + SSL_SESSION_free(s->session); + s->session = session; + session = NULL; + + s->verify_result = s->session->verify_result; + } else if (!ssl_get_new_session(s, 1)) { goto err; } if (s->ctx->dos_protection_cb != NULL && s->ctx->dos_protection_cb(&early_ctx) == 0) { /* Connection rejected for DOS reasons. */ al = SSL_AD_ACCESS_DENIED; - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_CONNECTION_REJECTED); + OPENSSL_PUT_ERROR(SSL, SSL_R_CONNECTION_REJECTED); goto f_err; } @@ -993,7 +961,7 @@ int ssl3_get_client_hello(SSL *s) { !CBS_get_u8_length_prefixed(&client_hello, &compression_methods) || CBS_len(&compression_methods) == 0) { al = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); goto f_err; } @@ -1020,8 +988,7 @@ int ssl3_get_client_hello(SSL *s) { /* we need to have the cipher in the cipher list if we are asked to reuse * it */ al = SSL_AD_ILLEGAL_PARAMETER; - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, - SSL_R_REQUIRED_CIPHER_MISSING); + OPENSSL_PUT_ERROR(SSL, SSL_R_REQUIRED_CIPHER_MISSING); goto f_err; } } @@ -1030,15 +997,14 @@ int ssl3_get_client_hello(SSL *s) { if (memchr(CBS_data(&compression_methods), 0, CBS_len(&compression_methods)) == NULL) { al = SSL_AD_ILLEGAL_PARAMETER; - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, - SSL_R_NO_COMPRESSION_SPECIFIED); + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_COMPRESSION_SPECIFIED); goto f_err; } /* TLS extensions. */ if (s->version >= SSL3_VERSION && !ssl_parse_clienthello_tlsext(s, &client_hello)) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_PARSE_TLSEXT); + OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT); goto err; } @@ -1046,13 +1012,13 @@ int ssl3_get_client_hello(SSL *s) { if (CBS_len(&client_hello) != 0) { /* wrong packet length */ al = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_BAD_PACKET_LENGTH); + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_PACKET_LENGTH); goto f_err; } if (have_extended_master_secret != s->s3->tmp.extended_master_secret) { al = SSL_AD_INTERNAL_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_EMS_STATE_INCONSISTENT); + OPENSSL_PUT_ERROR(SSL, SSL_R_EMS_STATE_INCONSISTENT); goto f_err; } @@ -1060,7 +1026,7 @@ int ssl3_get_client_hello(SSL *s) { if (!s->hit) { if (ciphers == NULL) { al = SSL_AD_ILLEGAL_PARAMETER; - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_NO_CIPHERS_PASSED); + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CIPHERS_PASSED); goto f_err; } @@ -1069,7 +1035,7 @@ int ssl3_get_client_hello(SSL *s) { int rv = s->cert->cert_cb(s, s->cert->cert_cb_arg); if (rv == 0) { al = SSL_AD_INTERNAL_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_CERT_CB_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_CB_ERROR); goto f_err; } if (rv < 0) { @@ -1082,7 +1048,7 @@ int ssl3_get_client_hello(SSL *s) { if (c == NULL) { al = SSL_AD_HANDSHAKE_FAILURE; - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_NO_SHARED_CIPHER); + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SHARED_CIPHER); goto f_err; } s->s3->tmp.new_cipher = c; @@ -1104,11 +1070,15 @@ int ssl3_get_client_hello(SSL *s) { s->s3->tmp.cert_request = 0; } + /* Now that the cipher is known, initialize the handshake hash. */ + if (!ssl3_init_handshake_hash(s)) { + goto f_err; + } + /* In TLS 1.2, client authentication requires hashing the handshake transcript * under a different hash. Otherwise, release the handshake buffer. */ - if ((!SSL_USE_SIGALGS(s) || !s->s3->tmp.cert_request) && - !ssl3_digest_cached_records(s, free_handshake_buffer)) { - goto f_err; + if (!SSL_USE_SIGALGS(s) || !s->s3->tmp.cert_request) { + ssl3_free_handshake_buffer(s); } /* we now have the following setup; @@ -1132,6 +1102,7 @@ int ssl3_get_client_hello(SSL *s) { err: sk_SSL_CIPHER_free(ciphers); + SSL_SESSION_free(session); return ret; } @@ -1152,8 +1123,7 @@ int ssl3_send_server_hello(SSL *s) { /* If this is a resumption and the original handshake didn't support * ChannelID then we didn't record the original handshake hashes in the * session and so cannot resume with ChannelIDs. */ - if (s->hit && s->s3->tlsext_channel_id_new && - s->session->original_handshake_hash_len == 0) { + if (s->hit && s->session->original_handshake_hash_len == 0) { s->s3->tlsext_channel_id_valid = 0; } @@ -1167,7 +1137,7 @@ int ssl3_send_server_hello(SSL *s) { /* Random stuff */ if (!ssl_fill_hello_random(s->s3->server_random, SSL3_RANDOM_SIZE, 1 /* server */)) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_server_hello, ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return -1; } memcpy(p, s->s3->server_random, SSL3_RANDOM_SIZE); @@ -1191,7 +1161,7 @@ int ssl3_send_server_hello(SSL *s) { sl = s->session->session_id_length; if (sl > (int)sizeof(s->session->session_id)) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_server_hello, ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return -1; } *(p++) = sl; @@ -1203,13 +1173,10 @@ int ssl3_send_server_hello(SSL *s) { /* put the compression method */ *(p++) = 0; - if (ssl_prepare_serverhello_tlsext(s) <= 0) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_server_hello, SSL_R_SERVERHELLO_TLSEXT); - return -1; - } + p = ssl_add_serverhello_tlsext(s, p, buf + SSL3_RT_MAX_PLAIN_LENGTH); if (p == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_server_hello, ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return -1; } @@ -1225,6 +1192,32 @@ int ssl3_send_server_hello(SSL *s) { return ssl_do_write(s); } +int ssl3_send_certificate_status(SSL *ssl) { + if (ssl->state == SSL3_ST_SW_CERT_STATUS_A) { + CBB out, ocsp_response; + size_t length; + + CBB_zero(&out); + if (!CBB_init_fixed(&out, ssl_handshake_start(ssl), + ssl->init_buf->max - SSL_HM_HEADER_LENGTH(ssl)) || + !CBB_add_u8(&out, TLSEXT_STATUSTYPE_ocsp) || + !CBB_add_u24_length_prefixed(&out, &ocsp_response) || + !CBB_add_bytes(&ocsp_response, ssl->ctx->ocsp_response, + ssl->ctx->ocsp_response_length) || + !CBB_finish(&out, NULL, &length) || + !ssl_set_handshake_header(ssl, SSL3_MT_CERTIFICATE_STATUS, length)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + CBB_cleanup(&out); + return -1; + } + + ssl->state = SSL3_ST_SW_CERT_STATUS_B; + } + + /* SSL3_ST_SW_CERT_STATUS_B */ + return ssl_do_write(ssl); +} + int ssl3_send_server_done(SSL *s) { if (s->state == SSL3_ST_SW_SRVR_DONE_A) { if (!ssl_set_handshake_header(s, SSL3_MT_SERVER_DONE, 0)) { @@ -1246,7 +1239,8 @@ int ssl3_send_server_key_exchange(SSL *s) { BN_CTX *bn_ctx = NULL; const char *psk_identity_hint = NULL; size_t psk_identity_hint_len = 0; - EVP_PKEY *pkey; + size_t sig_len; + size_t max_sig_len; uint8_t *p, *d; int al, i; uint32_t alg_k; @@ -1254,11 +1248,26 @@ int ssl3_send_server_key_exchange(SSL *s) { int n; CERT *cert; BIGNUM *r[4]; - int nr[4], kn; + int nr[4]; BUF_MEM *buf; EVP_MD_CTX md_ctx; + if (s->state == SSL3_ST_SW_KEY_EXCH_C) { + return ssl_do_write(s); + } + + if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher)) { + if (!ssl_has_private_key(s)) { + al = SSL_AD_INTERNAL_ERROR; + goto f_err; + } + max_sig_len = ssl_private_key_max_signature_len(s); + } else { + max_sig_len = 0; + } + EVP_MD_CTX_init(&md_ctx); + enum ssl_private_key_result_t sign_result; if (s->state == SSL3_ST_SW_KEY_EXCH_A) { alg_k = s->s3->tmp.new_cipher->algorithm_mkey; alg_a = s->s3->tmp.new_cipher->algorithm_auth; @@ -1286,25 +1295,23 @@ int ssl3_send_server_key_exchange(SSL *s) { } if (dhp == NULL) { al = SSL_AD_HANDSHAKE_FAILURE; - OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, - SSL_R_MISSING_TMP_DH_KEY); + OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_TMP_DH_KEY); goto f_err; } if (s->s3->tmp.dh != NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, - ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); goto err; } dh = DHparams_dup(dhp); if (dh == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, ERR_R_DH_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_DH_LIB); goto err; } s->s3->tmp.dh = dh; if (!DH_generate_key(dh)) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, ERR_R_DH_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_DH_LIB); goto err; } @@ -1328,14 +1335,12 @@ int ssl3_send_server_key_exchange(SSL *s) { } if (nid == NID_undef) { al = SSL_AD_HANDSHAKE_FAILURE; - OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, - SSL_R_MISSING_TMP_ECDH_KEY); + OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_TMP_ECDH_KEY); goto f_err; } if (s->s3->tmp.ecdh != NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, - ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); goto err; } ecdh = EC_KEY_new_by_curve_name(nid); @@ -1345,15 +1350,14 @@ int ssl3_send_server_key_exchange(SSL *s) { s->s3->tmp.ecdh = ecdh; if (!EC_KEY_generate_key(ecdh)) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, ERR_R_ECDH_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_ECDH_LIB); goto err; } /* We only support ephemeral ECDH keys over named (not generic) curves. */ const EC_GROUP *group = EC_KEY_get0_group(ecdh); if (!tls1_ec_nid2curve_id(&curve_id, EC_GROUP_get_curve_name(group))) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, - SSL_R_UNSUPPORTED_ELLIPTIC_CURVE); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_ELLIPTIC_CURVE); goto err; } @@ -1366,8 +1370,7 @@ int ssl3_send_server_key_exchange(SSL *s) { encodedPoint = (uint8_t *)OPENSSL_malloc(encodedlen * sizeof(uint8_t)); bn_ctx = BN_CTX_new(); if (encodedPoint == NULL || bn_ctx == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, - ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } @@ -1376,7 +1379,7 @@ int ssl3_send_server_key_exchange(SSL *s) { encodedPoint, encodedlen, bn_ctx); if (encodedlen == 0) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, ERR_R_ECDH_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_ECDH_LIB); goto err; } @@ -1396,8 +1399,7 @@ int ssl3_send_server_key_exchange(SSL *s) { r[3] = NULL; } else if (!(alg_k & SSL_kPSK)) { al = SSL_AD_HANDSHAKE_FAILURE; - OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, - SSL_R_UNKNOWN_KEY_EXCHANGE_TYPE); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_KEY_EXCHANGE_TYPE); goto f_err; } @@ -1406,20 +1408,8 @@ int ssl3_send_server_key_exchange(SSL *s) { n += 2 + nr[i]; } - if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher)) { - pkey = ssl_get_sign_pkey(s, s->s3->tmp.new_cipher); - if (pkey == NULL) { - al = SSL_AD_DECODE_ERROR; - goto f_err; - } - kn = EVP_PKEY_size(pkey); - } else { - pkey = NULL; - kn = 0; - } - - if (!BUF_MEM_grow_clean(buf, n + SSL_HM_HEADER_LENGTH(s) + kn)) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, ERR_LIB_BUF); + if (!BUF_MEM_grow_clean(buf, n + SSL_HM_HEADER_LENGTH(s) + max_sig_len)) { + OPENSSL_PUT_ERROR(SSL, ERR_LIB_BUF); goto err; } d = p = ssl_handshake_start(s); @@ -1458,54 +1448,81 @@ int ssl3_send_server_key_exchange(SSL *s) { encodedPoint = NULL; } - /* not anonymous */ - if (pkey != NULL) { - /* n is the length of the params, they start at &(d[4]) and p points to + if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher)) { + /* n is the length of the params, they start at d and p points to * the space at the end. */ const EVP_MD *md; - size_t sig_len = EVP_PKEY_size(pkey); + uint8_t digest[EVP_MAX_MD_SIZE]; + unsigned int digest_length; + + const int pkey_type = ssl_private_key_type(s); /* Determine signature algorithm. */ if (SSL_USE_SIGALGS(s)) { - md = tls1_choose_signing_digest(s, pkey); - if (!tls12_get_sigandhash(p, pkey, md)) { + md = tls1_choose_signing_digest(s); + if (!tls12_get_sigandhash(s, p, md)) { /* Should never happen */ al = SSL_AD_INTERNAL_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, - ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); goto f_err; } p += 2; - } else if (pkey->type == EVP_PKEY_RSA) { + } else if (pkey_type == EVP_PKEY_RSA) { md = EVP_md5_sha1(); } else { md = EVP_sha1(); } - if (!EVP_DigestSignInit(&md_ctx, NULL, md, NULL, pkey) || - !EVP_DigestSignUpdate(&md_ctx, s->s3->client_random, - SSL3_RANDOM_SIZE) || - !EVP_DigestSignUpdate(&md_ctx, s->s3->server_random, - SSL3_RANDOM_SIZE) || - !EVP_DigestSignUpdate(&md_ctx, d, n) || - !EVP_DigestSignFinal(&md_ctx, &p[2], &sig_len)) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, ERR_LIB_EVP); + if (!EVP_DigestInit_ex(&md_ctx, md, NULL) || + !EVP_DigestUpdate(&md_ctx, s->s3->client_random, SSL3_RANDOM_SIZE) || + !EVP_DigestUpdate(&md_ctx, s->s3->server_random, SSL3_RANDOM_SIZE) || + !EVP_DigestUpdate(&md_ctx, d, n) || + !EVP_DigestFinal_ex(&md_ctx, digest, &digest_length)) { + OPENSSL_PUT_ERROR(SSL, ERR_LIB_EVP); goto err; } - s2n(sig_len, p); - n += sig_len + 2; - if (SSL_USE_SIGALGS(s)) { - n += 2; - } + sign_result = ssl_private_key_sign(s, &p[2], &sig_len, max_sig_len, + EVP_MD_CTX_md(&md_ctx), digest, + digest_length); + } else { + /* This key exchange doesn't involve a signature. */ + sign_result = ssl_private_key_success; + sig_len = 0; } + } else { + assert(s->state == SSL3_ST_SW_KEY_EXCH_B); + /* Restore |p|. */ + p = ssl_handshake_start(s) + s->init_num - SSL_HM_HEADER_LENGTH(s); + sign_result = ssl_private_key_sign_complete(s, &p[2], &sig_len, + max_sig_len); + } - if (!ssl_set_handshake_header(s, SSL3_MT_SERVER_KEY_EXCHANGE, n)) { + switch (sign_result) { + case ssl_private_key_success: + s->rwstate = SSL_NOTHING; + break; + case ssl_private_key_failure: + s->rwstate = SSL_NOTHING; + goto err; + case ssl_private_key_retry: + s->rwstate = SSL_PRIVATE_KEY_OPERATION; + /* Stash away |p|. */ + s->init_num = p - ssl_handshake_start(s) + SSL_HM_HEADER_LENGTH(s); + s->state = SSL3_ST_SW_KEY_EXCH_B; goto err; - } } - s->state = SSL3_ST_SW_KEY_EXCH_B; + if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher)) { + s2n(sig_len, p); + p += sig_len; + } + if (!ssl_set_handshake_header(s, SSL3_MT_SERVER_KEY_EXCHANGE, + p - ssl_handshake_start(s))) { + goto err; + } + s->state = SSL3_ST_SW_KEY_EXCH_C; + EVP_MD_CTX_cleanup(&md_ctx); return ssl_do_write(s); @@ -1558,7 +1575,7 @@ int ssl3_send_certificate_request(SSL *s) { name = sk_X509_NAME_value(sk, i); j = i2d_X509_NAME(name, NULL); if (!BUF_MEM_grow_clean(buf, SSL_HM_HEADER_LENGTH(s) + n + j + 2)) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_certificate_request, ERR_R_BUF_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB); goto err; } p = ssl_handshake_start(s) + n; @@ -1629,30 +1646,27 @@ int ssl3_get_client_key_exchange(SSL *s) { * then this is the only field in the message. */ if (!CBS_get_u16_length_prefixed(&client_key_exchange, &psk_identity) || ((alg_k & SSL_kPSK) && CBS_len(&client_key_exchange) != 0)) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, SSL_R_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); al = SSL_AD_DECODE_ERROR; goto f_err; } if (s->psk_server_callback == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, - SSL_R_PSK_NO_SERVER_CB); + OPENSSL_PUT_ERROR(SSL, SSL_R_PSK_NO_SERVER_CB); al = SSL_AD_INTERNAL_ERROR; goto f_err; } if (CBS_len(&psk_identity) > PSK_MAX_IDENTITY_LEN || CBS_contains_zero_byte(&psk_identity)) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, - SSL_R_DATA_LENGTH_TOO_LONG); + OPENSSL_PUT_ERROR(SSL, SSL_R_DATA_LENGTH_TOO_LONG); al = SSL_AD_ILLEGAL_PARAMETER; goto f_err; } if (!CBS_strdup(&psk_identity, &s->session->psk_identity)) { al = SSL_AD_INTERNAL_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, - ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto f_err; } @@ -1660,14 +1674,12 @@ int ssl3_get_client_key_exchange(SSL *s) { psk_len = s->psk_server_callback(s, s->session->psk_identity, psk, sizeof(psk)); if (psk_len > PSK_MAX_PSK_LEN) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, - ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); al = SSL_AD_INTERNAL_ERROR; goto f_err; } else if (psk_len == 0) { /* PSK related to the given identity not found */ - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, - SSL_R_PSK_IDENTITY_NOT_FOUND); + OPENSSL_PUT_ERROR(SSL, SSL_R_PSK_IDENTITY_NOT_FOUND); al = SSL_AD_UNKNOWN_PSK_IDENTITY; goto f_err; } @@ -1681,11 +1693,10 @@ int ssl3_get_client_key_exchange(SSL *s) { uint8_t good; size_t rsa_size, decrypt_len, premaster_index, j; - pkey = s->cert->pkeys[SSL_PKEY_RSA_ENC].privatekey; + pkey = s->cert->privatekey; if (pkey == NULL || pkey->type != EVP_PKEY_RSA || pkey->pkey.rsa == NULL) { al = SSL_AD_HANDSHAKE_FAILURE; - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, - SSL_R_MISSING_RSA_CERTIFICATE); + OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_RSA_CERTIFICATE); goto f_err; } rsa = pkey->pkey.rsa; @@ -1698,8 +1709,7 @@ int ssl3_get_client_key_exchange(SSL *s) { CBS_len(&client_key_exchange) != 0) { if (!(s->options & SSL_OP_TLS_D5_BUG)) { al = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, - SSL_R_TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG); + OPENSSL_PUT_ERROR(SSL, SSL_R_TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG); goto f_err; } else { encrypted_premaster_secret = copy; @@ -1716,8 +1726,7 @@ int ssl3_get_client_key_exchange(SSL *s) { rsa_size = RSA_size(rsa); if (rsa_size < SSL_MAX_MASTER_KEY_LENGTH) { al = SSL_AD_DECRYPT_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, - SSL_R_DECRYPTION_FAILED); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECRYPTION_FAILED); goto f_err; } @@ -1733,8 +1742,7 @@ int ssl3_get_client_key_exchange(SSL *s) { /* Allocate a buffer large enough for an RSA decryption. */ decrypt_buf = OPENSSL_malloc(rsa_size); if (decrypt_buf == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, - ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } @@ -1748,8 +1756,7 @@ int ssl3_get_client_key_exchange(SSL *s) { if (decrypt_len != rsa_size) { /* This should never happen, but do a check so we do not read * uninitialized memory. */ - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, - ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); goto err; } @@ -1772,8 +1779,7 @@ int ssl3_get_client_key_exchange(SSL *s) { BUF_memdup(decrypt_buf + (rsa_size - SSL_MAX_MASTER_KEY_LENGTH), SSL_MAX_MASTER_KEY_LENGTH); if (premaster_secret == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, - ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } OPENSSL_free(decrypt_buf); @@ -1804,38 +1810,35 @@ int ssl3_get_client_key_exchange(SSL *s) { if (!CBS_get_u16_length_prefixed(&client_key_exchange, &dh_Yc) || CBS_len(&dh_Yc) == 0 || CBS_len(&client_key_exchange) != 0) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, - SSL_R_DH_PUBLIC_VALUE_LENGTH_IS_WRONG); + OPENSSL_PUT_ERROR(SSL, SSL_R_DH_PUBLIC_VALUE_LENGTH_IS_WRONG); al = SSL_R_DECODE_ERROR; goto f_err; } if (s->s3->tmp.dh == NULL) { al = SSL_AD_HANDSHAKE_FAILURE; - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, - SSL_R_MISSING_TMP_DH_KEY); + OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_TMP_DH_KEY); goto f_err; } dh_srvr = s->s3->tmp.dh; pub = BN_bin2bn(CBS_data(&dh_Yc), CBS_len(&dh_Yc), NULL); if (pub == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, SSL_R_BN_LIB); + OPENSSL_PUT_ERROR(SSL, SSL_R_BN_LIB); goto err; } /* Allocate a buffer for the premaster secret. */ premaster_secret = OPENSSL_malloc(DH_size(dh_srvr)); if (premaster_secret == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, - ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); BN_clear_free(pub); goto err; } dh_len = DH_compute_key(premaster_secret, pub, dh_srvr); if (dh_len <= 0) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, ERR_R_DH_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_DH_LIB); BN_clear_free(pub); goto err; } @@ -1856,8 +1859,7 @@ int ssl3_get_client_key_exchange(SSL *s) { /* initialize structures for server's ECDH key pair */ srvr_ecdh = EC_KEY_new(); if (srvr_ecdh == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, - ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } @@ -1870,15 +1872,14 @@ int ssl3_get_client_key_exchange(SSL *s) { if (!EC_KEY_set_group(srvr_ecdh, group) || !EC_KEY_set_private_key(srvr_ecdh, priv_key)) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, ERR_R_EC_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_EC_LIB); goto err; } /* Let's get client's public key */ clnt_ecpoint = EC_POINT_new(group); if (clnt_ecpoint == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, - ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } @@ -1887,35 +1888,33 @@ int ssl3_get_client_key_exchange(SSL *s) { if (!CBS_get_u8_length_prefixed(&client_key_exchange, &ecdh_Yc) || CBS_len(&client_key_exchange) != 0) { al = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, SSL_R_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); goto f_err; } bn_ctx = BN_CTX_new(); if (bn_ctx == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, - ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } if (!EC_POINT_oct2point(group, clnt_ecpoint, CBS_data(&ecdh_Yc), CBS_len(&ecdh_Yc), bn_ctx)) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, ERR_R_EC_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_EC_LIB); goto err; } /* Allocate a buffer for both the secret and the PSK. */ field_size = EC_GROUP_get_degree(group); if (field_size <= 0) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, ERR_R_ECDH_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_ECDH_LIB); goto err; } ecdh_len = (field_size + 7) / 8; premaster_secret = OPENSSL_malloc(ecdh_len); if (premaster_secret == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, - ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } @@ -1923,7 +1922,7 @@ int ssl3_get_client_key_exchange(SSL *s) { ecdh_len = ECDH_compute_key(premaster_secret, ecdh_len, clnt_ecpoint, srvr_ecdh, NULL); if (ecdh_len <= 0) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, ERR_R_ECDH_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_ECDH_LIB); goto err; } @@ -1945,15 +1944,13 @@ int ssl3_get_client_key_exchange(SSL *s) { premaster_secret_len = psk_len; premaster_secret = OPENSSL_malloc(premaster_secret_len); if (premaster_secret == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, - ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } memset(premaster_secret, 0, premaster_secret_len); } else { al = SSL_AD_HANDSHAKE_FAILURE; - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, - SSL_R_UNKNOWN_CIPHER_TYPE); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CIPHER_TYPE); goto f_err; } @@ -1964,18 +1961,14 @@ int ssl3_get_client_key_exchange(SSL *s) { uint8_t *new_data; size_t new_len; - if (!CBB_init(&new_premaster, 2 + psk_len + 2 + premaster_secret_len)) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, - ERR_R_MALLOC_FAILURE); - goto err; - } - if (!CBB_add_u16_length_prefixed(&new_premaster, &child) || + CBB_zero(&new_premaster); + if (!CBB_init(&new_premaster, 2 + psk_len + 2 + premaster_secret_len) || + !CBB_add_u16_length_prefixed(&new_premaster, &child) || !CBB_add_bytes(&child, premaster_secret, premaster_secret_len) || !CBB_add_u16_length_prefixed(&new_premaster, &child) || !CBB_add_bytes(&child, psk, psk_len) || !CBB_finish(&new_premaster, &new_data, &new_len)) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, - ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); CBB_cleanup(&new_premaster); goto err; } @@ -2031,10 +2024,7 @@ int ssl3_get_cert_verify(SSL *s) { * CertificateVerify is required if and only if there's a client certificate. * */ if (peer == NULL) { - if (s->s3->handshake_buffer && - !ssl3_digest_cached_records(s, free_handshake_buffer)) { - return -1; - } + ssl3_free_handshake_buffer(s); return 1; } @@ -2055,8 +2045,7 @@ int ssl3_get_cert_verify(SSL *s) { if (!(X509_certificate_type(peer, pkey) & EVP_PKT_SIGN) || (pkey->type != EVP_PKEY_RSA && pkey->type != EVP_PKEY_EC)) { al = SSL_AD_UNSUPPORTED_CERTIFICATE; - OPENSSL_PUT_ERROR(SSL, ssl3_get_cert_verify, - SSL_R_PEER_ERROR_UNSUPPORTED_CERTIFICATE_TYPE); + OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_ERROR_UNSUPPORTED_CERTIFICATE_TYPE); goto f_err; } @@ -2069,16 +2058,13 @@ int ssl3_get_cert_verify(SSL *s) { } /* Compute the digest. */ - if (!ssl3_cert_verify_hash(s, digest, &digest_length, &md, pkey)) { + if (!ssl3_cert_verify_hash(s, digest, &digest_length, &md, pkey->type)) { goto err; } /* The handshake buffer is no longer necessary, and we may hash the current * message.*/ - if (s->s3->handshake_buffer && - !ssl3_digest_cached_records(s, free_handshake_buffer)) { - goto err; - } + ssl3_free_handshake_buffer(s); if (!ssl3_hash_current_message(s)) { goto err; } @@ -2087,7 +2073,7 @@ int ssl3_get_cert_verify(SSL *s) { if (!CBS_get_u16_length_prefixed(&certificate_verify, &signature) || CBS_len(&certificate_verify) != 0) { al = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_get_cert_verify, SSL_R_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); goto f_err; } @@ -2100,7 +2086,7 @@ int ssl3_get_cert_verify(SSL *s) { !EVP_PKEY_verify(pctx, CBS_data(&signature), CBS_len(&signature), digest, digest_length)) { al = SSL_AD_DECRYPT_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_get_cert_verify, SSL_R_BAD_SIGNATURE); + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SIGNATURE); goto f_err; } @@ -2137,15 +2123,14 @@ int ssl3_get_client_certificate(SSL *s) { if (s->s3->tmp.message_type == SSL3_MT_CLIENT_KEY_EXCHANGE) { if ((s->verify_mode & SSL_VERIFY_PEER) && (s->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_certificate, - SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE); + OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE); al = SSL_AD_HANDSHAKE_FAILURE; goto f_err; } /* If tls asked for a client cert, the client must return a 0 list */ if (s->version > SSL3_VERSION && s->s3->tmp.cert_request) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_certificate, + OPENSSL_PUT_ERROR(SSL, SSL_R_TLS_PEER_DID_NOT_RESPOND_WITH_CERTIFICATE_LIST); al = SSL_AD_UNEXPECTED_MESSAGE; goto f_err; @@ -2157,8 +2142,7 @@ int ssl3_get_client_certificate(SSL *s) { if (s->s3->tmp.message_type != SSL3_MT_CERTIFICATE) { al = SSL_AD_UNEXPECTED_MESSAGE; - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_certificate, - SSL_R_WRONG_MESSAGE_TYPE); + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_MESSAGE_TYPE); goto f_err; } @@ -2166,14 +2150,14 @@ int ssl3_get_client_certificate(SSL *s) { sk = sk_X509_new_null(); if (sk == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_certificate, ERR_R_MALLOC_FAILURE); + 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, ssl3_get_client_certificate, SSL_R_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); goto f_err; } @@ -2183,7 +2167,7 @@ int ssl3_get_client_certificate(SSL *s) { if (!CBS_get_u24_length_prefixed(&certificate_list, &certificate)) { al = SSL_AD_DECODE_ERROR; - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_certificate, SSL_R_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); goto f_err; } @@ -2201,50 +2185,42 @@ int ssl3_get_client_certificate(SSL *s) { x = d2i_X509(NULL, &data, CBS_len(&certificate)); if (x == NULL) { al = SSL_AD_BAD_CERTIFICATE; - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_certificate, ERR_R_ASN1_LIB); + 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, ssl3_get_client_certificate, - SSL_R_CERT_LENGTH_MISMATCH); + OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_LENGTH_MISMATCH); goto f_err; } if (!sk_X509_push(sk, x)) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_certificate, ERR_R_MALLOC_FAILURE); + 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(s); + /* TLS does not mind 0 certs returned */ if (s->version == SSL3_VERSION) { al = SSL_AD_HANDSHAKE_FAILURE; - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_certificate, - SSL_R_NO_CERTIFICATES_RETURNED); + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATES_RETURNED); goto f_err; - } - /* Fail for TLS only if we required a certificate */ - else if ((s->verify_mode & SSL_VERIFY_PEER) && + } else if ((s->verify_mode & SSL_VERIFY_PEER) && (s->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_certificate, - SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE); + /* 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; } - /* No client certificate so digest cached records */ - if (s->s3->handshake_buffer && - !ssl3_digest_cached_records(s, free_handshake_buffer)) { - al = SSL_AD_INTERNAL_ERROR; - goto f_err; - } } else { i = ssl_verify_cert_chain(s, sk); if (i <= 0) { al = ssl_verify_alarm_type(s->verify_result); - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_certificate, - SSL_R_CERTIFICATE_VERIFY_FAILED); + OPENSSL_PUT_ERROR(SSL, SSL_R_CERTIFICATE_VERIFY_FAILED); goto f_err; } } @@ -2253,17 +2229,8 @@ int ssl3_get_client_certificate(SSL *s) { s->session->peer = sk_X509_shift(sk); s->session->verify_result = s->verify_result; - /* With the current implementation, sess_cert will always be NULL when we - * arrive here. */ - if (s->session->sess_cert == NULL) { - s->session->sess_cert = ssl_sess_cert_new(); - if (s->session->sess_cert == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_client_certificate, ERR_R_MALLOC_FAILURE); - goto err; - } - } - sk_X509_pop_free(s->session->sess_cert->cert_chain, X509_free); - s->session->sess_cert->cert_chain = sk; + sk_X509_pop_free(s->session->cert_chain, X509_free); + s->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 */ @@ -2283,17 +2250,8 @@ err: } int ssl3_send_server_certificate(SSL *s) { - CERT_PKEY *cpk; - if (s->state == SSL3_ST_SW_CERT_A) { - cpk = ssl_get_server_send_pkey(s); - if (cpk == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl3_send_server_certificate, - ERR_R_INTERNAL_ERROR); - return 0; - } - - if (!ssl3_output_cert_chain(s, cpk)) { + if (!ssl3_output_cert_chain(s)) { return 0; } s->state = SSL3_ST_SW_CERT_B; @@ -2444,8 +2402,7 @@ int ssl3_get_next_proto(SSL *s) { /* Clients cannot send a NextProtocol message if we didn't see the extension * in their ClientHello */ if (!s->s3->next_proto_neg_seen) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_next_proto, - SSL_R_GOT_NEXT_PROTO_WITHOUT_EXTENSION); + OPENSSL_PUT_ERROR(SSL, SSL_R_GOT_NEXT_PROTO_WITHOUT_EXTENSION); return -1; } @@ -2465,8 +2422,7 @@ int ssl3_get_next_proto(SSL *s) { * TODO(davidben): Is this check now redundant with * SSL3_FLAGS_EXPECT_CCS? */ if (!s->s3->change_cipher_spec) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_next_proto, - SSL_R_GOT_NEXT_PROTO_BEFORE_A_CCS); + OPENSSL_PUT_ERROR(SSL, SSL_R_GOT_NEXT_PROTO_BEFORE_A_CCS); return -1; } @@ -2492,11 +2448,10 @@ int ssl3_get_next_proto(SSL *s) { int ssl3_get_channel_id(SSL *s) { int ret = -1, ok; long n; - EVP_MD_CTX md_ctx; - uint8_t channel_id_hash[SHA256_DIGEST_LENGTH]; - unsigned int channel_id_hash_len; + uint8_t channel_id_hash[EVP_MAX_MD_SIZE]; + size_t channel_id_hash_len; const uint8_t *p; - uint16_t extension_type, expected_extension_type; + uint16_t extension_type; EC_GROUP *p256 = NULL; EC_KEY *key = NULL; EC_POINT *point = NULL; @@ -2515,15 +2470,9 @@ int ssl3_get_channel_id(SSL *s) { /* Before incorporating the EncryptedExtensions message to the handshake * hash, compute the hash that should have been signed. */ - channel_id_hash_len = sizeof(channel_id_hash); - EVP_MD_CTX_init(&md_ctx); - if (!EVP_DigestInit_ex(&md_ctx, EVP_sha256(), NULL) || - !tls1_channel_id_hash(&md_ctx, s) || - !EVP_DigestFinal(&md_ctx, channel_id_hash, &channel_id_hash_len)) { - EVP_MD_CTX_cleanup(&md_ctx); + if (!tls1_channel_id_hash(s, channel_id_hash, &channel_id_hash_len)) { return -1; } - EVP_MD_CTX_cleanup(&md_ctx); assert(channel_id_hash_len == SHA256_DIGEST_LENGTH); if (!ssl3_hash_current_message(s)) { @@ -2536,8 +2485,7 @@ int ssl3_get_channel_id(SSL *s) { * * TODO(davidben): Is this check now redundant with SSL3_FLAGS_EXPECT_CCS? */ if (!s->s3->change_cipher_spec) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_channel_id, - SSL_R_GOT_CHANNEL_ID_BEFORE_A_CCS); + OPENSSL_PUT_ERROR(SSL, SSL_R_GOT_CHANNEL_ID_BEFORE_A_CCS); return -1; } @@ -2554,23 +2502,19 @@ int ssl3_get_channel_id(SSL *s) { * uint8 y[32]; * uint8 r[32]; * uint8 s[32]; */ - expected_extension_type = TLSEXT_TYPE_channel_id; - if (s->s3->tlsext_channel_id_new) { - expected_extension_type = TLSEXT_TYPE_channel_id_new; - } if (!CBS_get_u16(&encrypted_extensions, &extension_type) || !CBS_get_u16_length_prefixed(&encrypted_extensions, &extension) || CBS_len(&encrypted_extensions) != 0 || - extension_type != expected_extension_type || + extension_type != TLSEXT_TYPE_channel_id || CBS_len(&extension) != TLSEXT_CHANNEL_ID_SIZE) { - OPENSSL_PUT_ERROR(SSL, ssl3_get_channel_id, SSL_R_INVALID_MESSAGE); + 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, ssl3_get_channel_id, SSL_R_NO_P256_SUPPORT); + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_P256_SUPPORT); return -1; } @@ -2604,8 +2548,7 @@ int ssl3_get_channel_id(SSL *s) { /* 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, ssl3_get_channel_id, - SSL_R_CHANNEL_ID_SIGNATURE_INVALID); + OPENSSL_PUT_ERROR(SSL, SSL_R_CHANNEL_ID_SIGNATURE_INVALID); s->s3->tlsext_channel_id_valid = 0; goto err; } diff --git a/src/ssl/ssl_aead_ctx.c b/src/ssl/ssl_aead_ctx.c index c2fba1d..f9001c7 100644 --- a/src/ssl/ssl_aead_ctx.c +++ b/src/ssl/ssl_aead_ctx.c @@ -12,6 +12,8 @@ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <openssl/ssl.h> + #include <assert.h> #include <string.h> @@ -34,7 +36,7 @@ SSL_AEAD_CTX *SSL_AEAD_CTX_new(enum evp_aead_direction_t direction, const EVP_AEAD *aead; size_t discard; if (!ssl_cipher_get_evp_aead(&aead, &discard, &discard, cipher, version)) { - OPENSSL_PUT_ERROR(SSL, SSL_AEAD_CTX_new, ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return 0; } @@ -43,7 +45,7 @@ SSL_AEAD_CTX *SSL_AEAD_CTX_new(enum evp_aead_direction_t direction, /* This is a "stateful" AEAD (for compatibility with pre-AEAD cipher * suites). */ if (mac_key_len + enc_key_len + fixed_iv_len > sizeof(merged_key)) { - OPENSSL_PUT_ERROR(SSL, SSL_AEAD_CTX_new, ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return 0; } memcpy(merged_key, mac_key, mac_key_len); @@ -56,7 +58,7 @@ SSL_AEAD_CTX *SSL_AEAD_CTX_new(enum evp_aead_direction_t direction, SSL_AEAD_CTX *aead_ctx = (SSL_AEAD_CTX *)OPENSSL_malloc(sizeof(SSL_AEAD_CTX)); if (aead_ctx == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_AEAD_CTX_new, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); return NULL; } memset(aead_ctx, 0, sizeof(SSL_AEAD_CTX)); @@ -76,16 +78,17 @@ SSL_AEAD_CTX *SSL_AEAD_CTX_new(enum evp_aead_direction_t direction, if (fixed_iv_len > sizeof(aead_ctx->fixed_nonce) || fixed_iv_len > aead_ctx->variable_nonce_len) { SSL_AEAD_CTX_free(aead_ctx); - OPENSSL_PUT_ERROR(SSL, SSL_AEAD_CTX_new, ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return 0; } aead_ctx->variable_nonce_len -= fixed_iv_len; memcpy(aead_ctx->fixed_nonce, fixed_iv, fixed_iv_len); aead_ctx->fixed_nonce_len = fixed_iv_len; - aead_ctx->variable_nonce_included_in_record = - (cipher->algorithm2 & - SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD) != 0; + /* AES-GCM uses an explicit nonce. */ + if (cipher->algorithm_enc & (SSL_AES128GCM | SSL_AES256GCM)) { + aead_ctx->variable_nonce_included_in_record = 1; + } } else { aead_ctx->variable_nonce_included_in_record = 1; aead_ctx->random_variable_nonce = 1; @@ -146,7 +149,7 @@ int SSL_AEAD_CTX_open(SSL_AEAD_CTX *aead, uint8_t *out, size_t *out_len, if (aead == NULL) { /* Handle the initial NULL cipher. */ if (in_len > max_out) { - OPENSSL_PUT_ERROR(SSL, SSL_AEAD_CTX_open, SSL_R_BUFFER_TOO_SMALL); + OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL); return 0; } memmove(out, in, in_len); @@ -161,7 +164,7 @@ int SSL_AEAD_CTX_open(SSL_AEAD_CTX *aead, uint8_t *out, size_t *out_len, size_t overhead = SSL_AEAD_CTX_max_overhead(aead); if (in_len < overhead) { /* Publicly invalid. */ - OPENSSL_PUT_ERROR(SSL, SSL_AEAD_CTX_open, SSL_R_BAD_PACKET_LENGTH); + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_PACKET_LENGTH); return 0; } plaintext_len = in_len - overhead; @@ -178,7 +181,7 @@ int SSL_AEAD_CTX_open(SSL_AEAD_CTX *aead, uint8_t *out, size_t *out_len, if (aead->variable_nonce_included_in_record) { if (in_len < aead->variable_nonce_len) { /* Publicly invalid. */ - OPENSSL_PUT_ERROR(SSL, SSL_AEAD_CTX_open, SSL_R_BAD_PACKET_LENGTH); + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_PACKET_LENGTH); return 0; } memcpy(nonce + nonce_len, in, aead->variable_nonce_len); @@ -201,7 +204,7 @@ int SSL_AEAD_CTX_seal(SSL_AEAD_CTX *aead, uint8_t *out, size_t *out_len, if (aead == NULL) { /* Handle the initial NULL cipher. */ if (in_len > max_out) { - OPENSSL_PUT_ERROR(SSL, SSL_AEAD_CTX_seal, SSL_R_BUFFER_TOO_SMALL); + OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL); return 0; } memmove(out, in, in_len); @@ -235,11 +238,11 @@ int SSL_AEAD_CTX_seal(SSL_AEAD_CTX *aead, uint8_t *out, size_t *out_len, size_t extra_len = 0; if (aead->variable_nonce_included_in_record) { if (max_out < aead->variable_nonce_len) { - OPENSSL_PUT_ERROR(SSL, SSL_AEAD_CTX_seal, SSL_R_BUFFER_TOO_SMALL); + OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL); return 0; } if (out < in + in_len && in < out + aead->variable_nonce_len) { - OPENSSL_PUT_ERROR(SSL, SSL_AEAD_CTX_seal, SSL_R_OUTPUT_ALIASES_INPUT); + OPENSSL_PUT_ERROR(SSL, SSL_R_OUTPUT_ALIASES_INPUT); return 0; } memcpy(out, nonce + aead->fixed_nonce_len, aead->variable_nonce_len); diff --git a/src/ssl/ssl_algs.c b/src/ssl/ssl_algs.c deleted file mode 100644 index fda39a5..0000000 --- a/src/ssl/ssl_algs.c +++ /dev/null @@ -1,66 +0,0 @@ -/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) - * All rights reserved. - * - * This package is an SSL implementation written - * by Eric Young (eay@cryptsoft.com). - * The implementation was written so as to conform with Netscapes SSL. - * - * This library is free for commercial and non-commercial use as long as - * the following conditions are aheared to. The following conditions - * apply to all code found in this distribution, be it the RC4, RSA, - * lhash, DES, etc., code; not just the SSL code. The SSL documentation - * included with this distribution is covered by the same copyright terms - * except that the holder is Tim Hudson (tjh@cryptsoft.com). - * - * Copyright remains Eric Young's, and as such any Copyright notices in - * the code are not to be removed. - * If this package is used in a product, Eric Young should be given attribution - * as the author of the parts of the library used. - * This can be in the form of a textual message at program startup or - * in documentation (online or textual) provided with the package. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * "This product includes cryptographic software written by - * Eric Young (eay@cryptsoft.com)" - * The word 'cryptographic' can be left out if the rouines from the library - * being used are not cryptographic related :-). - * 4. If you include any Windows specific code (or a derivative thereof) from - * the apps directory (application code) you must include an acknowledgement: - * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" - * - * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * The licence and distribution terms for any publically available version or - * derivative of this code cannot be changed. i.e. this code cannot simply be - * copied and put under another distribution licence - * [including the GNU Public Licence.] */ - -#include "internal.h" - -#include <openssl/crypto.h> - -int SSL_library_init(void) { - CRYPTO_library_init(); - return 1; -} - -void SSL_load_error_strings(void) {} diff --git a/src/ssl/ssl_asn1.c b/src/ssl/ssl_asn1.c index 76052df..0d4760d 100644 --- a/src/ssl/ssl_asn1.c +++ b/src/ssl/ssl_asn1.c @@ -80,6 +80,8 @@ * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR * OTHERWISE. */ +#include <openssl/ssl.h> + #include <limits.h> #include <string.h> @@ -114,8 +116,10 @@ * signedCertTimestampList [15] OCTET STRING OPTIONAL, * -- contents of SCT extension * ocspResponse [16] OCTET STRING OPTIONAL, - * -- stapled OCSP response from the server + * -- stapled OCSP response from the server * extendedMasterSecret [17] BOOLEAN OPTIONAL, + * keyExchangeInfo [18] INTEGER OPTIONAL, + * certChain [19] SEQUENCE OF Certificate OPTIONAL, * } * * Note: historically this serialization has included other optional @@ -154,8 +158,28 @@ static const int kOCSPResponseTag = CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 16; static const int kExtendedMasterSecretTag = CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 17; +static const int kKeyExchangeInfoTag = + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 18; +static const int kCertChainTag = + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 19; + +static int add_X509(CBB *cbb, X509 *x509) { + int len = i2d_X509(x509, NULL); + if (len < 0) { + return 0; + } + uint8_t *buf; + if (!CBB_add_space(cbb, &buf, len)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return 0; + } + if (buf != NULL && i2d_X509(x509, &buf) < 0) { + return 0; + } + return 1; +} -static int SSL_SESSION_to_bytes_full(SSL_SESSION *in, uint8_t **out_data, +static int SSL_SESSION_to_bytes_full(const SSL_SESSION *in, uint8_t **out_data, size_t *out_len, int for_ticket) { CBB cbb, session, child, child2; @@ -163,11 +187,9 @@ static int SSL_SESSION_to_bytes_full(SSL_SESSION *in, uint8_t **out_data, return 0; } - if (!CBB_init(&cbb, 0)) { - return 0; - } - - if (!CBB_add_asn1(&cbb, &session, CBS_ASN1_SEQUENCE) || + CBB_zero(&cbb); + if (!CBB_init(&cbb, 0) || + !CBB_add_asn1(&cbb, &session, CBS_ASN1_SEQUENCE) || !CBB_add_asn1_uint64(&session, SSL_SESSION_ASN1_VERSION) || !CBB_add_asn1_uint64(&session, in->ssl_version) || !CBB_add_asn1(&session, &child, CBS_ASN1_OCTETSTRING) || @@ -178,14 +200,14 @@ static int SSL_SESSION_to_bytes_full(SSL_SESSION *in, uint8_t **out_data, for_ticket ? 0 : in->session_id_length) || !CBB_add_asn1(&session, &child, CBS_ASN1_OCTETSTRING) || !CBB_add_bytes(&child, in->master_key, in->master_key_length)) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_to_bytes_full, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } if (in->time != 0) { if (!CBB_add_asn1(&session, &child, kTimeTag) || !CBB_add_asn1_uint64(&child, in->time)) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_to_bytes_full, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } } @@ -193,7 +215,7 @@ static int SSL_SESSION_to_bytes_full(SSL_SESSION *in, uint8_t **out_data, if (in->timeout != 0) { if (!CBB_add_asn1(&session, &child, kTimeoutTag) || !CBB_add_asn1_uint64(&child, in->timeout)) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_to_bytes_full, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } } @@ -201,17 +223,11 @@ static int SSL_SESSION_to_bytes_full(SSL_SESSION *in, uint8_t **out_data, /* The peer certificate is only serialized if the SHA-256 isn't * serialized instead. */ if (in->peer && !in->peer_sha256_valid) { - uint8_t *buf; - int len = i2d_X509(in->peer, NULL); - if (len < 0) { - goto err; - } - if (!CBB_add_asn1(&session, &child, kPeerTag) || - !CBB_add_space(&child, &buf, len)) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_to_bytes_full, ERR_R_MALLOC_FAILURE); + if (!CBB_add_asn1(&session, &child, kPeerTag)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } - if (buf != NULL && i2d_X509(in->peer, &buf) < 0) { + if (!add_X509(&child, in->peer)) { goto err; } } @@ -221,14 +237,14 @@ static int SSL_SESSION_to_bytes_full(SSL_SESSION *in, uint8_t **out_data, if (!CBB_add_asn1(&session, &child, kSessionIDContextTag) || !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) || !CBB_add_bytes(&child2, in->sid_ctx, in->sid_ctx_length)) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_to_bytes_full, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } if (in->verify_result != X509_V_OK) { if (!CBB_add_asn1(&session, &child, kVerifyResultTag) || !CBB_add_asn1_uint64(&child, in->verify_result)) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_to_bytes_full, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } } @@ -238,7 +254,7 @@ static int SSL_SESSION_to_bytes_full(SSL_SESSION *in, uint8_t **out_data, !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) || !CBB_add_bytes(&child2, (const uint8_t *)in->tlsext_hostname, strlen(in->tlsext_hostname))) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_to_bytes_full, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } } @@ -248,7 +264,7 @@ static int SSL_SESSION_to_bytes_full(SSL_SESSION *in, uint8_t **out_data, !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) || !CBB_add_bytes(&child2, (const uint8_t *)in->psk_identity, strlen(in->psk_identity))) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_to_bytes_full, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } } @@ -256,7 +272,7 @@ static int SSL_SESSION_to_bytes_full(SSL_SESSION *in, uint8_t **out_data, if (in->tlsext_tick_lifetime_hint > 0) { if (!CBB_add_asn1(&session, &child, kTicketLifetimeHintTag) || !CBB_add_asn1_uint64(&child, in->tlsext_tick_lifetime_hint)) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_to_bytes_full, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } } @@ -265,7 +281,7 @@ static int SSL_SESSION_to_bytes_full(SSL_SESSION *in, uint8_t **out_data, if (!CBB_add_asn1(&session, &child, kTicketTag) || !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) || !CBB_add_bytes(&child2, in->tlsext_tick, in->tlsext_ticklen)) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_to_bytes_full, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } } @@ -274,7 +290,7 @@ static int SSL_SESSION_to_bytes_full(SSL_SESSION *in, uint8_t **out_data, if (!CBB_add_asn1(&session, &child, kPeerSHA256Tag) || !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) || !CBB_add_bytes(&child2, in->peer_sha256, sizeof(in->peer_sha256))) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_to_bytes_full, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } } @@ -284,7 +300,7 @@ static int SSL_SESSION_to_bytes_full(SSL_SESSION *in, uint8_t **out_data, !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) || !CBB_add_bytes(&child2, in->original_handshake_hash, in->original_handshake_hash_len)) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_to_bytes_full, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } } @@ -294,7 +310,7 @@ static int SSL_SESSION_to_bytes_full(SSL_SESSION *in, uint8_t **out_data, !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) || !CBB_add_bytes(&child2, in->tlsext_signed_cert_timestamp_list, in->tlsext_signed_cert_timestamp_list_length)) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_to_bytes_full, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } } @@ -303,7 +319,7 @@ static int SSL_SESSION_to_bytes_full(SSL_SESSION *in, uint8_t **out_data, if (!CBB_add_asn1(&session, &child, kOCSPResponseTag) || !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) || !CBB_add_bytes(&child2, in->ocsp_response, in->ocsp_response_length)) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_to_bytes_full, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } } @@ -312,13 +328,35 @@ static int SSL_SESSION_to_bytes_full(SSL_SESSION *in, uint8_t **out_data, if (!CBB_add_asn1(&session, &child, kExtendedMasterSecretTag) || !CBB_add_asn1(&child, &child2, CBS_ASN1_BOOLEAN) || !CBB_add_u8(&child2, 0xff)) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_to_bytes_full, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } } + if (in->key_exchange_info > 0 && + (!CBB_add_asn1(&session, &child, kKeyExchangeInfoTag) || + !CBB_add_asn1_uint64(&child, in->key_exchange_info))) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + + /* The certificate chain is only serialized if the leaf's SHA-256 isn't + * serialized instead. */ + if (in->cert_chain != NULL && !in->peer_sha256_valid) { + if (!CBB_add_asn1(&session, &child, kCertChainTag)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + size_t i; + for (i = 0; i < sk_X509_num(in->cert_chain); i++) { + if (!add_X509(&child, sk_X509_value(in->cert_chain, i))) { + goto err; + } + } + } + if (!CBB_finish(&cbb, out_data, out_len)) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_to_bytes_full, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } return 1; @@ -328,11 +366,12 @@ static int SSL_SESSION_to_bytes_full(SSL_SESSION *in, uint8_t **out_data, return 0; } -int SSL_SESSION_to_bytes(SSL_SESSION *in, uint8_t **out_data, size_t *out_len) { +int SSL_SESSION_to_bytes(const SSL_SESSION *in, uint8_t **out_data, + size_t *out_len) { return SSL_SESSION_to_bytes_full(in, out_data, out_len, 0); } -int SSL_SESSION_to_bytes_for_ticket(SSL_SESSION *in, uint8_t **out_data, +int SSL_SESSION_to_bytes_for_ticket(const SSL_SESSION *in, uint8_t **out_data, size_t *out_len) { return SSL_SESSION_to_bytes_full(in, out_data, out_len, 1); } @@ -347,7 +386,7 @@ int i2d_SSL_SESSION(SSL_SESSION *in, uint8_t **pp) { if (len > INT_MAX) { OPENSSL_free(out); - OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_OVERFLOW); + OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW); return -1; } @@ -370,17 +409,16 @@ static int SSL_SESSION_parse_string(CBS *cbs, char **out, unsigned tag) { CBS value; int present; if (!CBS_get_optional_asn1_octet_string(cbs, &value, &present, tag)) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse_string, SSL_R_INVALID_SSL_SESSION); + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); return 0; } if (present) { if (CBS_contains_zero_byte(&value)) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse_string, - SSL_R_INVALID_SSL_SESSION); + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); return 0; } if (!CBS_strdup(&value, out)) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse_string, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); return 0; } } else { @@ -397,170 +435,168 @@ static int SSL_SESSION_parse_string(CBS *cbs, char **out, unsigned tag) { * |*out_ptr| to NULL. It returns one on success, whether or not the * element was found, and zero on decode error. */ static int SSL_SESSION_parse_octet_string(CBS *cbs, uint8_t **out_ptr, - size_t *out_len, unsigned tag) { + size_t *out_len, unsigned tag) { CBS value; if (!CBS_get_optional_asn1_octet_string(cbs, &value, NULL, tag)) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse_octet_string, - SSL_R_INVALID_SSL_SESSION); + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); return 0; } if (!CBS_stow(&value, out_ptr, out_len)) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse_octet_string, - ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); return 0; } return 1; } -static SSL_SESSION *SSL_SESSION_parse(CBS *cbs) { - SSL_SESSION *ret = NULL; - CBS session, cipher, session_id, master_key; - CBS peer, sid_ctx, peer_sha256, original_handshake_hash; - int has_peer, has_peer_sha256, extended_master_secret; - uint64_t version, ssl_version; - uint64_t session_time, timeout, verify_result, ticket_lifetime_hint; - - ret = SSL_SESSION_new(); - if (ret == NULL) { - goto err; +/* SSL_SESSION_parse_bounded_octet_string parses an optional ASN.1 OCTET STRING + * explicitly tagged with |tag| of size at most |max_out|. */ +static int SSL_SESSION_parse_bounded_octet_string( + CBS *cbs, uint8_t *out, unsigned *out_len, unsigned max_out, unsigned tag) { + CBS value; + if (!CBS_get_optional_asn1_octet_string(cbs, &value, NULL, tag) || + CBS_len(&value) > max_out) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); + return 0; } + memcpy(out, CBS_data(&value), CBS_len(&value)); + *out_len = (unsigned)CBS_len(&value); + return 1; +} - if (!CBS_get_asn1(cbs, &session, CBS_ASN1_SEQUENCE) || - !CBS_get_asn1_uint64(&session, &version) || - !CBS_get_asn1_uint64(&session, &ssl_version) || - !CBS_get_asn1(&session, &cipher, CBS_ASN1_OCTETSTRING) || - !CBS_get_asn1(&session, &session_id, CBS_ASN1_OCTETSTRING) || - !CBS_get_asn1(&session, &master_key, CBS_ASN1_OCTETSTRING) || - !CBS_get_optional_asn1_uint64(&session, &session_time, kTimeTag, - time(NULL)) || - !CBS_get_optional_asn1_uint64(&session, &timeout, kTimeoutTag, 3) || - !CBS_get_optional_asn1(&session, &peer, &has_peer, kPeerTag) || - !CBS_get_optional_asn1_octet_string(&session, &sid_ctx, NULL, - kSessionIDContextTag) || - !CBS_get_optional_asn1_uint64(&session, &verify_result, kVerifyResultTag, - X509_V_OK)) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse, SSL_R_INVALID_SSL_SESSION); - goto err; - } - if (!SSL_SESSION_parse_string(&session, &ret->tlsext_hostname, - kHostNameTag) || - !SSL_SESSION_parse_string(&session, &ret->psk_identity, - kPSKIdentityTag)) { - goto err; - } - if (!CBS_get_optional_asn1_uint64(&session, &ticket_lifetime_hint, - kTicketLifetimeHintTag, 0)) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse, SSL_R_INVALID_SSL_SESSION); - goto err; - } - if (!SSL_SESSION_parse_octet_string(&session, &ret->tlsext_tick, - &ret->tlsext_ticklen, kTicketTag)) { - goto err; +static int SSL_SESSION_parse_long(CBS *cbs, long *out, unsigned tag, + long default_value) { + uint64_t value; + if (!CBS_get_optional_asn1_uint64(cbs, &value, tag, + (uint64_t)default_value) || + value > LONG_MAX) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); + return 0; } - if (!CBS_get_optional_asn1_octet_string(&session, &peer_sha256, - &has_peer_sha256, kPeerSHA256Tag) || - !CBS_get_optional_asn1_octet_string(&session, &original_handshake_hash, - NULL, kOriginalHandshakeHashTag)) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse, SSL_R_INVALID_SSL_SESSION); - goto err; + *out = (long)value; + return 1; +} + +static int SSL_SESSION_parse_u32(CBS *cbs, uint32_t *out, unsigned tag, + uint32_t default_value) { + uint64_t value; + if (!CBS_get_optional_asn1_uint64(cbs, &value, tag, + (uint64_t)default_value) || + value > 0xffffffff) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); + return 0; } - if (!SSL_SESSION_parse_octet_string( - &session, &ret->tlsext_signed_cert_timestamp_list, - &ret->tlsext_signed_cert_timestamp_list_length, - kSignedCertTimestampListTag) || - !SSL_SESSION_parse_octet_string( - &session, &ret->ocsp_response, &ret->ocsp_response_length, - kOCSPResponseTag)) { - goto err; + *out = (uint32_t)value; + return 1; +} + +static X509 *parse_x509(CBS *cbs) { + const uint8_t *ptr = CBS_data(cbs); + X509 *ret = d2i_X509(NULL, &ptr, CBS_len(cbs)); + if (ret == NULL) { + return NULL; } - if (!CBS_get_optional_asn1_bool(&session, &extended_master_secret, - kExtendedMasterSecretTag, - 0 /* default to false */) || - CBS_len(&session) != 0) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse, SSL_R_INVALID_SSL_SESSION); + CBS_skip(cbs, ptr - CBS_data(cbs)); + return ret; +} + +static SSL_SESSION *SSL_SESSION_parse(CBS *cbs) { + SSL_SESSION *ret = SSL_SESSION_new(); + if (ret == NULL) { goto err; } - ret->extended_master_secret = extended_master_secret; - if (version != SSL_SESSION_ASN1_VERSION) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse, SSL_R_INVALID_SSL_SESSION); + CBS session; + uint64_t version, ssl_version; + if (!CBS_get_asn1(cbs, &session, CBS_ASN1_SEQUENCE) || + !CBS_get_asn1_uint64(&session, &version) || + version != SSL_SESSION_ASN1_VERSION || + !CBS_get_asn1_uint64(&session, &ssl_version)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } - /* Only support SSLv3/TLS and DTLS. */ if ((ssl_version >> 8) != SSL3_VERSION_MAJOR && (ssl_version >> 8) != (DTLS1_VERSION >> 8)) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse, SSL_R_UNKNOWN_SSL_VERSION); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_SSL_VERSION); goto err; } ret->ssl_version = ssl_version; + CBS cipher; uint16_t cipher_value; - if (!CBS_get_u16(&cipher, &cipher_value) || CBS_len(&cipher) != 0) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse, SSL_R_CIPHER_CODE_WRONG_LENGTH); + if (!CBS_get_asn1(&session, &cipher, CBS_ASN1_OCTETSTRING) || + !CBS_get_u16(&cipher, &cipher_value) || + CBS_len(&cipher) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } ret->cipher = SSL_get_cipher_by_value(cipher_value); if (ret->cipher == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse, SSL_R_UNSUPPORTED_CIPHER); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_CIPHER); goto err; } - if (CBS_len(&session_id) > SSL3_MAX_SSL_SESSION_ID_LENGTH) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse, SSL_R_INVALID_SSL_SESSION); + CBS session_id, master_key; + if (!CBS_get_asn1(&session, &session_id, CBS_ASN1_OCTETSTRING) || + CBS_len(&session_id) > SSL3_MAX_SSL_SESSION_ID_LENGTH || + !CBS_get_asn1(&session, &master_key, CBS_ASN1_OCTETSTRING) || + CBS_len(&master_key) > SSL_MAX_MASTER_KEY_LENGTH) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } memcpy(ret->session_id, CBS_data(&session_id), CBS_len(&session_id)); ret->session_id_length = CBS_len(&session_id); - - if (CBS_len(&master_key) > SSL_MAX_MASTER_KEY_LENGTH) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse, SSL_R_INVALID_SSL_SESSION); - goto err; - } memcpy(ret->master_key, CBS_data(&master_key), CBS_len(&master_key)); ret->master_key_length = CBS_len(&master_key); - if (session_time > LONG_MAX || - timeout > LONG_MAX) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse, SSL_R_INVALID_SSL_SESSION); + if (!SSL_SESSION_parse_long(&session, &ret->time, kTimeTag, time(NULL)) || + !SSL_SESSION_parse_long(&session, &ret->timeout, kTimeoutTag, 3)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } - ret->time = session_time; - ret->timeout = timeout; + CBS peer; + int has_peer; + if (!CBS_get_optional_asn1(&session, &peer, &has_peer, kPeerTag)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); + goto err; + } X509_free(ret->peer); ret->peer = NULL; if (has_peer) { - const uint8_t *ptr; - ptr = CBS_data(&peer); - ret->peer = d2i_X509(NULL, &ptr, CBS_len(&peer)); + ret->peer = parse_x509(&peer); if (ret->peer == NULL) { goto err; } - if (ptr != CBS_data(&peer) + CBS_len(&peer)) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse, SSL_R_INVALID_SSL_SESSION); + if (CBS_len(&peer) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } } - if (CBS_len(&sid_ctx) > sizeof(ret->sid_ctx)) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse, SSL_R_INVALID_SSL_SESSION); - goto err; - } - memcpy(ret->sid_ctx, CBS_data(&sid_ctx), CBS_len(&sid_ctx)); - ret->sid_ctx_length = CBS_len(&sid_ctx); - - if (verify_result > LONG_MAX || - ticket_lifetime_hint > 0xffffffff) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse, SSL_R_INVALID_SSL_SESSION); + if (!SSL_SESSION_parse_bounded_octet_string( + &session, ret->sid_ctx, &ret->sid_ctx_length, sizeof(ret->sid_ctx), + kSessionIDContextTag) || + !SSL_SESSION_parse_long(&session, &ret->verify_result, kVerifyResultTag, + X509_V_OK) || + !SSL_SESSION_parse_string(&session, &ret->tlsext_hostname, + kHostNameTag) || + !SSL_SESSION_parse_string(&session, &ret->psk_identity, + kPSKIdentityTag) || + !SSL_SESSION_parse_u32(&session, &ret->tlsext_tick_lifetime_hint, + kTicketLifetimeHintTag, 0) || + !SSL_SESSION_parse_octet_string(&session, &ret->tlsext_tick, + &ret->tlsext_ticklen, kTicketTag)) { goto err; } - ret->verify_result = verify_result; - ret->tlsext_tick_lifetime_hint = ticket_lifetime_hint; - if (has_peer_sha256) { - if (CBS_len(&peer_sha256) != sizeof(ret->peer_sha256)) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse, SSL_R_INVALID_SSL_SESSION); + if (CBS_peek_asn1_tag(&session, kPeerSHA256Tag)) { + CBS child, peer_sha256; + if (!CBS_get_asn1(&session, &child, kPeerSHA256Tag) || + !CBS_get_asn1(&child, &peer_sha256, CBS_ASN1_OCTETSTRING) || + CBS_len(&peer_sha256) != sizeof(ret->peer_sha256) || + CBS_len(&child) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } memcpy(ret->peer_sha256, CBS_data(&peer_sha256), sizeof(ret->peer_sha256)); @@ -569,14 +605,67 @@ static SSL_SESSION *SSL_SESSION_parse(CBS *cbs) { ret->peer_sha256_valid = 0; } - if (CBS_len(&original_handshake_hash) > - sizeof(ret->original_handshake_hash)) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_parse, SSL_R_INVALID_SSL_SESSION); + if (!SSL_SESSION_parse_bounded_octet_string( + &session, ret->original_handshake_hash, + &ret->original_handshake_hash_len, + sizeof(ret->original_handshake_hash), kOriginalHandshakeHashTag) || + !SSL_SESSION_parse_octet_string( + &session, &ret->tlsext_signed_cert_timestamp_list, + &ret->tlsext_signed_cert_timestamp_list_length, + kSignedCertTimestampListTag) || + !SSL_SESSION_parse_octet_string( + &session, &ret->ocsp_response, &ret->ocsp_response_length, + kOCSPResponseTag)) { + goto err; + } + + int extended_master_secret; + if (!CBS_get_optional_asn1_bool(&session, &extended_master_secret, + kExtendedMasterSecretTag, + 0 /* default to false */)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); + goto err; + } + ret->extended_master_secret = !!extended_master_secret; + + if (!SSL_SESSION_parse_u32(&session, &ret->key_exchange_info, + kKeyExchangeInfoTag, 0)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); + goto err; + } + + CBS cert_chain; + int has_cert_chain; + if (!CBS_get_optional_asn1(&session, &cert_chain, &has_cert_chain, + kCertChainTag)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); + goto err; + } + sk_X509_pop_free(ret->cert_chain, X509_free); + ret->cert_chain = NULL; + if (has_cert_chain) { + ret->cert_chain = sk_X509_new_null(); + if (ret->cert_chain == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + while (CBS_len(&cert_chain) > 0) { + X509 *x509 = parse_x509(&cert_chain); + if (x509 == NULL) { + goto err; + } + if (!sk_X509_push(ret->cert_chain, x509)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + X509_free(x509); + goto err; + } + } + } + + if (CBS_len(&session) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); goto err; } - memcpy(ret->original_handshake_hash, CBS_data(&original_handshake_hash), - CBS_len(&original_handshake_hash)); - ret->original_handshake_hash_len = CBS_len(&original_handshake_hash); return ret; @@ -593,7 +682,7 @@ SSL_SESSION *SSL_SESSION_from_bytes(const uint8_t *in, size_t in_len) { return NULL; } if (CBS_len(&cbs) != 0) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_from_bytes, SSL_R_INVALID_SSL_SESSION); + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); SSL_SESSION_free(ret); return NULL; } @@ -602,7 +691,7 @@ SSL_SESSION *SSL_SESSION_from_bytes(const uint8_t *in, size_t in_len) { SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a, const uint8_t **pp, long length) { if (length < 0) { - OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return NULL; } diff --git a/src/ssl/ssl_buffer.c b/src/ssl/ssl_buffer.c new file mode 100644 index 0000000..63dcd80 --- /dev/null +++ b/src/ssl/ssl_buffer.c @@ -0,0 +1,318 @@ +/* Copyright (c) 2015, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#include <openssl/ssl.h> + +#include <assert.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> + +#include <openssl/bio.h> +#include <openssl/err.h> +#include <openssl/mem.h> +#include <openssl/type_check.h> + +#include "internal.h" + + +OPENSSL_COMPILE_ASSERT(0xffff <= INT_MAX, uint16_fits_in_int); + +OPENSSL_COMPILE_ASSERT((SSL3_ALIGN_PAYLOAD & (SSL3_ALIGN_PAYLOAD - 1)) == 0, + align_to_a_power_of_two); + +/* setup_buffer initializes |buf| with capacity |cap|, aligned such that data + * written after |header_len| is aligned to a |SSL3_ALIGN_PAYLOAD|-byte + * boundary. It returns one on success and zero on error. */ +static int setup_buffer(SSL3_BUFFER *buf, size_t header_len, size_t cap) { + if (buf->buf != NULL || cap > 0xffff) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; + } + + /* Add up to |SSL3_ALIGN_PAYLOAD| - 1 bytes of slack for alignment. */ + buf->buf = OPENSSL_malloc(cap + SSL3_ALIGN_PAYLOAD - 1); + if (buf->buf == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return 0; + } + + /* Arrange the buffer such that the record body is aligned. */ + buf->offset = (0 - header_len - (uintptr_t)buf->buf) & + (SSL3_ALIGN_PAYLOAD - 1); + buf->len = 0; + buf->cap = cap; + return 1; +} + +static void consume_buffer(SSL3_BUFFER *buf, size_t len) { + if (len > buf->len) { + abort(); + } + buf->offset += (uint16_t)len; + buf->len -= (uint16_t)len; + buf->cap -= (uint16_t)len; +} + +static void clear_buffer(SSL3_BUFFER *buf) { + OPENSSL_free(buf->buf); + memset(buf, 0, sizeof(SSL3_BUFFER)); +} + +OPENSSL_COMPILE_ASSERT(DTLS1_RT_HEADER_LENGTH + SSL3_RT_MAX_ENCRYPTED_LENGTH + + SSL3_RT_MAX_EXTRA <= 0xffff, + maximum_read_buffer_too_large); + +/* setup_read_buffer initializes the read buffer if not already initialized. It + * returns one on success and zero on failure. */ +static int setup_read_buffer(SSL *ssl) { + SSL3_BUFFER *buf = &ssl->s3->read_buffer; + + if (buf->buf != NULL) { + return 1; + } + + size_t header_len = ssl_record_prefix_len(ssl); + size_t cap = SSL3_RT_MAX_ENCRYPTED_LENGTH; + if (SSL_IS_DTLS(ssl)) { + cap += DTLS1_RT_HEADER_LENGTH; + } else { + cap += SSL3_RT_HEADER_LENGTH; + } + if (ssl->options & SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER) { + cap += SSL3_RT_MAX_EXTRA; + } + + return setup_buffer(buf, header_len, cap); +} + +uint8_t *ssl_read_buffer(SSL *ssl) { + return ssl->s3->read_buffer.buf + ssl->s3->read_buffer.offset; +} + +size_t ssl_read_buffer_len(const SSL *ssl) { + return ssl->s3->read_buffer.len; +} + +static int dtls_read_buffer_next_packet(SSL *ssl) { + SSL3_BUFFER *buf = &ssl->s3->read_buffer; + + if (buf->len > 0) { + /* It is an error to call |dtls_read_buffer_extend| when the read buffer is + * not empty. */ + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return -1; + } + + /* Read a single packet from |ssl->rbio|. |buf->cap| must fit in an int. */ + ssl->rwstate = SSL_READING; + int ret = BIO_read(ssl->rbio, buf->buf + buf->offset, (int)buf->cap); + if (ret <= 0) { + return ret; + } + ssl->rwstate = SSL_NOTHING; + /* |BIO_read| was bound by |buf->cap|, so this cannot overflow. */ + buf->len = (uint16_t)ret; + return 1; +} + +static int tls_read_buffer_extend_to(SSL *ssl, size_t len) { + SSL3_BUFFER *buf = &ssl->s3->read_buffer; + + if (len > buf->cap) { + /* This may occur if |SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER| was toggled after + * |setup_read_buffer| was called. Stay within bounds, but do not attempt to + * recover. */ + OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL); + return -1; + } + + /* Read until the target length is reached. */ + while (buf->len < len) { + /* The amount of data to read is bounded by |buf->cap|, which must fit in an + * int. */ + ssl->rwstate = SSL_READING; + int ret = BIO_read(ssl->rbio, buf->buf + buf->offset + buf->len, + (int)(len - buf->len)); + if (ret <= 0) { + return ret; + } + ssl->rwstate = SSL_NOTHING; + /* |BIO_read| was bound by |buf->cap - buf->len|, so this cannot + * overflow. */ + buf->len += (uint16_t)ret; + } + + return 1; +} + +int ssl_read_buffer_extend_to(SSL *ssl, size_t len) { + /* |ssl_read_buffer_extend_to| implicitly discards any consumed data. */ + ssl_read_buffer_discard(ssl); + + if (!setup_read_buffer(ssl)) { + return -1; + } + + if (ssl->rbio == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_BIO_NOT_SET); + return -1; + } + + ERR_clear_system_error(); + + int ret; + if (SSL_IS_DTLS(ssl)) { + /* |len| is ignored for a datagram transport. */ + ret = dtls_read_buffer_next_packet(ssl); + } else { + ret = tls_read_buffer_extend_to(ssl, len); + } + + if (ret <= 0) { + /* If the buffer was empty originally and remained empty after attempting to + * extend it, release the buffer until the next attempt. */ + ssl_read_buffer_discard(ssl); + } + return ret; +} + +void ssl_read_buffer_consume(SSL *ssl, size_t len) { + SSL3_BUFFER *buf = &ssl->s3->read_buffer; + + consume_buffer(buf, len); + if (!SSL_IS_DTLS(ssl)) { + /* The TLS stack never reads beyond the current record, so there will never + * be unconsumed data. If read-ahead is ever reimplemented, + * |ssl_read_buffer_discard| will require a |memcpy| to shift the excess + * back to the front of the buffer, to ensure there is enough space for the + * next record. */ + assert(buf->len == 0); + } +} + +void ssl_read_buffer_discard(SSL *ssl) { + if (ssl->s3->read_buffer.len == 0) { + ssl_read_buffer_clear(ssl); + } +} + +void ssl_read_buffer_clear(SSL *ssl) { + clear_buffer(&ssl->s3->read_buffer); +} + + +int ssl_write_buffer_is_pending(const SSL *ssl) { + return ssl->s3->write_buffer.len > 0; +} + +OPENSSL_COMPILE_ASSERT(SSL3_RT_HEADER_LENGTH * 2 + + SSL3_RT_SEND_MAX_ENCRYPTED_OVERHEAD * 2 + + SSL3_RT_MAX_PLAIN_LENGTH <= 0xffff, + maximum_tls_write_buffer_too_large); + +OPENSSL_COMPILE_ASSERT(DTLS1_RT_HEADER_LENGTH + + SSL3_RT_SEND_MAX_ENCRYPTED_OVERHEAD + + SSL3_RT_MAX_PLAIN_LENGTH <= 0xffff, + maximum_dtls_write_buffer_too_large); + +int ssl_write_buffer_init(SSL *ssl, uint8_t **out_ptr, size_t max_len) { + SSL3_BUFFER *buf = &ssl->s3->write_buffer; + + if (buf->buf != NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; + } + + size_t header_len = ssl_seal_prefix_len(ssl); + + /* TODO(davidben): This matches the original behavior in keeping the malloc + * size consistent. Does this matter? |cap| could just be |max_len|. */ + size_t cap = SSL3_RT_MAX_PLAIN_LENGTH + SSL3_RT_SEND_MAX_ENCRYPTED_OVERHEAD; + if (SSL_IS_DTLS(ssl)) { + cap += DTLS1_RT_HEADER_LENGTH; + } else { + cap += SSL3_RT_HEADER_LENGTH; + if (ssl->mode & SSL_MODE_CBC_RECORD_SPLITTING) { + cap += SSL3_RT_HEADER_LENGTH + SSL3_RT_SEND_MAX_ENCRYPTED_OVERHEAD; + } + } + + if (max_len > cap) { + OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL); + return 0; + } + + if (!setup_buffer(buf, header_len, cap)) { + return 0; + } + *out_ptr = buf->buf + buf->offset; + return 1; +} + +void ssl_write_buffer_set_len(SSL *ssl, size_t len) { + SSL3_BUFFER *buf = &ssl->s3->write_buffer; + + if (len > buf->cap) { + abort(); + } + buf->len = len; +} + +static int tls_write_buffer_flush(SSL *ssl) { + SSL3_BUFFER *buf = &ssl->s3->write_buffer; + + while (buf->len > 0) { + ssl->rwstate = SSL_WRITING; + int ret = BIO_write(ssl->wbio, buf->buf + buf->offset, buf->len); + if (ret <= 0) { + return ret; + } + ssl->rwstate = SSL_NOTHING; + consume_buffer(buf, (size_t)ret); + } + ssl_write_buffer_clear(ssl); + return 1; +} + +static int dtls_write_buffer_flush(SSL *ssl) { + SSL3_BUFFER *buf = &ssl->s3->write_buffer; + if (buf->len == 0) { + return 1; + } + + int ret = BIO_write(ssl->wbio, buf->buf + buf->offset, buf->len); + /* Drop the write buffer whether or not the write succeeded synchronously. + * TODO(davidben): How does this interact with the retry flag? */ + ssl_write_buffer_clear(ssl); + return (ret <= 0) ? ret : 1; +} + +int ssl_write_buffer_flush(SSL *ssl) { + if (ssl->wbio == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_BIO_NOT_SET); + return -1; + } + ERR_clear_system_error(); + + if (SSL_IS_DTLS(ssl)) { + return dtls_write_buffer_flush(ssl); + } else { + return tls_write_buffer_flush(ssl); + } +} + +void ssl_write_buffer_clear(SSL *ssl) { + clear_buffer(&ssl->s3->write_buffer); +} diff --git a/src/ssl/ssl_cert.c b/src/ssl/ssl_cert.c index 85aa079..4094b27 100644 --- a/src/ssl/ssl_cert.c +++ b/src/ssl/ssl_cert.c @@ -112,84 +112,64 @@ * ECC cipher suite support in OpenSSL originally developed by * SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project. */ -#include <errno.h> -#include <stdio.h> +#include <openssl/ssl.h> + #include <string.h> -#include <openssl/bio.h> #include <openssl/bn.h> #include <openssl/buf.h> #include <openssl/ec_key.h> #include <openssl/dh.h> #include <openssl/err.h> #include <openssl/mem.h> -#include <openssl/obj.h> -#include <openssl/pem.h> #include <openssl/x509.h> #include <openssl/x509v3.h> #include "../crypto/dh/internal.h" -#include "../crypto/directory.h" #include "../crypto/internal.h" #include "internal.h" -static CRYPTO_once_t g_x509_store_ex_data_index_once; -static int g_x509_store_ex_data_index; - -static void ssl_x509_store_ex_data_index_init(void) { - g_x509_store_ex_data_index = X509_STORE_CTX_get_ex_new_index( - 0, "SSL for verify callback", NULL, NULL, NULL); -} - int SSL_get_ex_data_X509_STORE_CTX_idx(void) { - CRYPTO_once(&g_x509_store_ex_data_index_once, - ssl_x509_store_ex_data_index_init); - return g_x509_store_ex_data_index; + /* The ex_data index to go from |X509_STORE_CTX| to |SSL| always uses the + * reserved app_data slot. Before ex_data was introduced, app_data was used. + * Avoid breaking any software which assumes |X509_STORE_CTX_get_app_data| + * works. */ + return 0; } CERT *ssl_cert_new(void) { - CERT *ret; - - ret = (CERT *)OPENSSL_malloc(sizeof(CERT)); + CERT *ret = (CERT *)OPENSSL_malloc(sizeof(CERT)); if (ret == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl_cert_new, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); return NULL; } memset(ret, 0, sizeof(CERT)); - ret->key = &ret->pkeys[SSL_PKEY_RSA_ENC]; return ret; } CERT *ssl_cert_dup(CERT *cert) { - CERT *ret; - int i; - - ret = (CERT *)OPENSSL_malloc(sizeof(CERT)); + CERT *ret = (CERT *)OPENSSL_malloc(sizeof(CERT)); if (ret == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl_cert_dup, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); return NULL; } memset(ret, 0, sizeof(CERT)); - ret->key = &ret->pkeys[cert->key - &cert->pkeys[0]]; - /* or ret->key = ret->pkeys + (cert->key - cert->pkeys), if you find that - * more readable */ - ret->mask_k = cert->mask_k; ret->mask_a = cert->mask_a; if (cert->dh_tmp != NULL) { ret->dh_tmp = DHparams_dup(cert->dh_tmp); if (ret->dh_tmp == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl_cert_dup, ERR_R_DH_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_DH_LIB); goto err; } if (cert->dh_tmp->priv_key) { BIGNUM *b = BN_dup(cert->dh_tmp->priv_key); if (!b) { - OPENSSL_PUT_ERROR(SSL, ssl_cert_dup, ERR_R_BN_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_BN_LIB); goto err; } ret->dh_tmp->priv_key = b; @@ -197,7 +177,7 @@ CERT *ssl_cert_dup(CERT *cert) { if (cert->dh_tmp->pub_key) { BIGNUM *b = BN_dup(cert->dh_tmp->pub_key); if (!b) { - OPENSSL_PUT_ERROR(SSL, ssl_cert_dup, ERR_R_BN_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_BN_LIB); goto err; } ret->dh_tmp->pub_key = b; @@ -208,67 +188,25 @@ CERT *ssl_cert_dup(CERT *cert) { ret->ecdh_nid = cert->ecdh_nid; ret->ecdh_tmp_cb = cert->ecdh_tmp_cb; - for (i = 0; i < SSL_PKEY_NUM; i++) { - CERT_PKEY *cpk = cert->pkeys + i; - CERT_PKEY *rpk = ret->pkeys + i; - if (cpk->x509 != NULL) { - rpk->x509 = X509_up_ref(cpk->x509); - } - - if (cpk->privatekey != NULL) { - rpk->privatekey = EVP_PKEY_up_ref(cpk->privatekey); - } - - if (cpk->chain) { - rpk->chain = X509_chain_up_ref(cpk->chain); - if (!rpk->chain) { - OPENSSL_PUT_ERROR(SSL, ssl_cert_dup, ERR_R_MALLOC_FAILURE); - goto err; - } - } + if (cert->x509 != NULL) { + ret->x509 = X509_up_ref(cert->x509); } - /* Copy over signature algorithm configuration. */ - if (cert->conf_sigalgs) { - ret->conf_sigalgs = BUF_memdup(cert->conf_sigalgs, cert->conf_sigalgslen); - if (!ret->conf_sigalgs) { - goto err; - } - ret->conf_sigalgslen = cert->conf_sigalgslen; + if (cert->privatekey != NULL) { + ret->privatekey = EVP_PKEY_up_ref(cert->privatekey); } - if (cert->client_sigalgs) { - ret->client_sigalgs = BUF_memdup(cert->client_sigalgs, - cert->client_sigalgslen); - if (!ret->client_sigalgs) { + if (cert->chain) { + ret->chain = X509_chain_up_ref(cert->chain); + if (!ret->chain) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } - ret->client_sigalgslen = cert->client_sigalgslen; - } - - /* Copy any custom client certificate types */ - if (cert->client_certificate_types) { - ret->client_certificate_types = BUF_memdup( - cert->client_certificate_types, cert->num_client_certificate_types); - if (!ret->client_certificate_types) { - goto err; - } - ret->num_client_certificate_types = cert->num_client_certificate_types; } ret->cert_cb = cert->cert_cb; ret->cert_cb_arg = cert->cert_cb_arg; - if (cert->verify_store) { - CRYPTO_refcount_inc(&cert->verify_store->references); - ret->verify_store = cert->verify_store; - } - - if (cert->chain_store) { - CRYPTO_refcount_inc(&cert->chain_store->references); - ret->chain_store = cert->chain_store; - } - return ret; err: @@ -277,27 +215,18 @@ err: } /* Free up and clear all certificates and chains */ -void ssl_cert_clear_certs(CERT *c) { - int i; - if (c == NULL) { +void ssl_cert_clear_certs(CERT *cert) { + if (cert == NULL) { return; } - for (i = 0; i < SSL_PKEY_NUM; i++) { - CERT_PKEY *cpk = c->pkeys + i; - if (cpk->x509) { - X509_free(cpk->x509); - cpk->x509 = NULL; - } - if (cpk->privatekey) { - EVP_PKEY_free(cpk->privatekey); - cpk->privatekey = NULL; - } - if (cpk->chain) { - sk_X509_pop_free(cpk->chain, X509_free); - cpk->chain = NULL; - } - } + X509_free(cert->x509); + cert->x509 = NULL; + EVP_PKEY_free(cert->privatekey); + cert->privatekey = NULL; + sk_X509_pop_free(cert->chain, X509_free); + cert->chain = NULL; + cert->key_method = NULL; } void ssl_cert_free(CERT *c) { @@ -309,38 +238,29 @@ void ssl_cert_free(CERT *c) { ssl_cert_clear_certs(c); OPENSSL_free(c->peer_sigalgs); - OPENSSL_free(c->conf_sigalgs); - OPENSSL_free(c->client_sigalgs); - OPENSSL_free(c->shared_sigalgs); - OPENSSL_free(c->client_certificate_types); - X509_STORE_free(c->verify_store); - X509_STORE_free(c->chain_store); + OPENSSL_free(c->digest_nids); OPENSSL_free(c); } -int ssl_cert_set0_chain(CERT *c, STACK_OF(X509) *chain) { - CERT_PKEY *cpk = c->key; - if (!cpk) { - return 0; - } - sk_X509_pop_free(cpk->chain, X509_free); - cpk->chain = chain; +int ssl_cert_set0_chain(CERT *cert, STACK_OF(X509) *chain) { + sk_X509_pop_free(cert->chain, X509_free); + cert->chain = chain; return 1; } -int ssl_cert_set1_chain(CERT *c, STACK_OF(X509) *chain) { +int ssl_cert_set1_chain(CERT *cert, STACK_OF(X509) *chain) { STACK_OF(X509) *dchain; - if (!chain) { - return ssl_cert_set0_chain(c, NULL); + if (chain == NULL) { + return ssl_cert_set0_chain(cert, NULL); } dchain = X509_chain_up_ref(chain); - if (!dchain) { + if (dchain == NULL) { return 0; } - if (!ssl_cert_set0_chain(c, dchain)) { + if (!ssl_cert_set0_chain(cert, dchain)) { sk_X509_pop_free(dchain, X509_free); return 0; } @@ -348,158 +268,71 @@ int ssl_cert_set1_chain(CERT *c, STACK_OF(X509) *chain) { return 1; } -int ssl_cert_add0_chain_cert(CERT *c, X509 *x) { - CERT_PKEY *cpk = c->key; - if (!cpk) { - return 0; +int ssl_cert_add0_chain_cert(CERT *cert, X509 *x509) { + if (cert->chain == NULL) { + cert->chain = sk_X509_new_null(); } - - if (!cpk->chain) { - cpk->chain = sk_X509_new_null(); - } - if (!cpk->chain || !sk_X509_push(cpk->chain, x)) { + if (cert->chain == NULL || !sk_X509_push(cert->chain, x509)) { return 0; } return 1; } -int ssl_cert_add1_chain_cert(CERT *c, X509 *x) { - if (!ssl_cert_add0_chain_cert(c, x)) { +int ssl_cert_add1_chain_cert(CERT *cert, X509 *x509) { + if (!ssl_cert_add0_chain_cert(cert, x509)) { return 0; } - X509_up_ref(x); + X509_up_ref(x509); return 1; } -int ssl_cert_select_current(CERT *c, X509 *x) { - int i; - if (x == NULL) { - return 0; - } - - for (i = 0; i < SSL_PKEY_NUM; i++) { - if (c->pkeys[i].x509 == x) { - c->key = &c->pkeys[i]; - return 1; - } - } - - for (i = 0; i < SSL_PKEY_NUM; i++) { - if (c->pkeys[i].x509 && !X509_cmp(c->pkeys[i].x509, x)) { - c->key = &c->pkeys[i]; - return 1; - } - } - - return 0; -} - void ssl_cert_set_cert_cb(CERT *c, int (*cb)(SSL *ssl, void *arg), void *arg) { c->cert_cb = cb; c->cert_cb_arg = arg; } -SESS_CERT *ssl_sess_cert_new(void) { - SESS_CERT *ret; - - ret = OPENSSL_malloc(sizeof *ret); - if (ret == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl_sess_cert_new, ERR_R_MALLOC_FAILURE); - return NULL; - } - - memset(ret, 0, sizeof *ret); - - return ret; -} - -SESS_CERT *ssl_sess_cert_dup(const SESS_CERT *sess_cert) { - SESS_CERT *ret = ssl_sess_cert_new(); - if (ret == NULL) { - return NULL; - } - - if (sess_cert->cert_chain != NULL) { - ret->cert_chain = X509_chain_up_ref(sess_cert->cert_chain); - if (ret->cert_chain == NULL) { - ssl_sess_cert_free(ret); - return NULL; - } - } - if (sess_cert->peer_cert != NULL) { - ret->peer_cert = X509_up_ref(sess_cert->peer_cert); - } - if (sess_cert->peer_dh_tmp != NULL) { - ret->peer_dh_tmp = sess_cert->peer_dh_tmp; - DH_up_ref(ret->peer_dh_tmp); - } - if (sess_cert->peer_ecdh_tmp != NULL) { - ret->peer_ecdh_tmp = sess_cert->peer_ecdh_tmp; - EC_KEY_up_ref(ret->peer_ecdh_tmp); - } - return ret; -} - -void ssl_sess_cert_free(SESS_CERT *sess_cert) { - if (sess_cert == NULL) { - return; +int ssl_verify_cert_chain(SSL *ssl, STACK_OF(X509) *cert_chain) { + if (cert_chain == NULL || sk_X509_num(cert_chain) == 0) { + return 0; } - sk_X509_pop_free(sess_cert->cert_chain, X509_free); - X509_free(sess_cert->peer_cert); - DH_free(sess_cert->peer_dh_tmp); - EC_KEY_free(sess_cert->peer_ecdh_tmp); - - OPENSSL_free(sess_cert); -} - -int ssl_verify_cert_chain(SSL *s, STACK_OF(X509) *sk) { - X509 *x; - int i; - X509_STORE *verify_store; + X509 *leaf = sk_X509_value(cert_chain, 0); + int ret = 0; X509_STORE_CTX ctx; - - if (s->cert->verify_store) { - verify_store = s->cert->verify_store; - } else { - verify_store = s->ctx->cert_store; - } - - if (sk == NULL || sk_X509_num(sk) == 0) { + if (!X509_STORE_CTX_init(&ctx, ssl->ctx->cert_store, leaf, cert_chain)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB); return 0; } - - x = sk_X509_value(sk, 0); - if (!X509_STORE_CTX_init(&ctx, verify_store, x, sk)) { - OPENSSL_PUT_ERROR(SSL, ssl_verify_cert_chain, ERR_R_X509_LIB); - return 0; + if (!X509_STORE_CTX_set_ex_data(&ctx, SSL_get_ex_data_X509_STORE_CTX_idx(), + ssl)) { + goto err; } - X509_STORE_CTX_set_ex_data(&ctx, SSL_get_ex_data_X509_STORE_CTX_idx(), s); /* We need to inherit the verify parameters. These can be determined by the * context: if its a server it will verify SSL client certificates or vice * versa. */ - X509_STORE_CTX_set_default(&ctx, s->server ? "ssl_client" : "ssl_server"); + X509_STORE_CTX_set_default(&ctx, ssl->server ? "ssl_client" : "ssl_server"); /* Anything non-default in "param" should overwrite anything in the ctx. */ - X509_VERIFY_PARAM_set1(X509_STORE_CTX_get0_param(&ctx), s->param); + X509_VERIFY_PARAM_set1(X509_STORE_CTX_get0_param(&ctx), ssl->param); - if (s->verify_callback) { - X509_STORE_CTX_set_verify_cb(&ctx, s->verify_callback); + if (ssl->verify_callback) { + X509_STORE_CTX_set_verify_cb(&ctx, ssl->verify_callback); } - if (s->ctx->app_verify_callback != NULL) { - i = s->ctx->app_verify_callback(&ctx, s->ctx->app_verify_arg); + if (ssl->ctx->app_verify_callback != NULL) { + ret = ssl->ctx->app_verify_callback(&ctx, ssl->ctx->app_verify_arg); } else { - i = X509_verify_cert(&ctx); + ret = X509_verify_cert(&ctx); } - s->verify_result = ctx.error; - X509_STORE_CTX_cleanup(&ctx); + ssl->verify_result = ctx.error; - return i; +err: + X509_STORE_CTX_cleanup(&ctx); + return ret; } static void set_client_CA_list(STACK_OF(X509_NAME) **ca_list, @@ -508,15 +341,17 @@ static void set_client_CA_list(STACK_OF(X509_NAME) **ca_list, *ca_list = name_list; } -STACK_OF(X509_NAME) *SSL_dup_CA_list(STACK_OF(X509_NAME) *sk) { - size_t i; - STACK_OF(X509_NAME) *ret; - X509_NAME *name; +STACK_OF(X509_NAME) *SSL_dup_CA_list(STACK_OF(X509_NAME) *list) { + STACK_OF(X509_NAME) *ret = sk_X509_NAME_new_null(); + if (ret == NULL) { + return NULL; + } - ret = sk_X509_NAME_new_null(); - for (i = 0; i < sk_X509_NAME_num(sk); i++) { - name = X509_NAME_dup(sk_X509_NAME_value(sk, i)); + size_t i; + for (i = 0; i < sk_X509_NAME_num(list); i++) { + X509_NAME *name = X509_NAME_dup(sk_X509_NAME_value(list, i)); if (name == NULL || !sk_X509_NAME_push(ret, name)) { + X509_NAME_free(name); sk_X509_NAME_pop_free(ret, X509_NAME_free); return NULL; } @@ -525,38 +360,38 @@ STACK_OF(X509_NAME) *SSL_dup_CA_list(STACK_OF(X509_NAME) *sk) { return ret; } -void SSL_set_client_CA_list(SSL *s, STACK_OF(X509_NAME) *name_list) { - set_client_CA_list(&(s->client_CA), name_list); +void SSL_set_client_CA_list(SSL *ssl, STACK_OF(X509_NAME) *name_list) { + set_client_CA_list(&ssl->client_CA, name_list); } void SSL_CTX_set_client_CA_list(SSL_CTX *ctx, STACK_OF(X509_NAME) *name_list) { - set_client_CA_list(&(ctx->client_CA), name_list); + set_client_CA_list(&ctx->client_CA, name_list); } STACK_OF(X509_NAME) *SSL_CTX_get_client_CA_list(const SSL_CTX *ctx) { return ctx->client_CA; } -STACK_OF(X509_NAME) *SSL_get_client_CA_list(const SSL *s) { - if (s->server) { - if (s->client_CA != NULL) { - return s->client_CA; - } else { - return s->ctx->client_CA; - } - } else { - if ((s->version >> 8) == SSL3_VERSION_MAJOR && s->s3 != NULL) { - return s->s3->tmp.ca_names; - } else { - return NULL; - } +STACK_OF(X509_NAME) *SSL_get_client_CA_list(const SSL *ssl) { + /* For historical reasons, this function is used both to query configuration + * state on a server as well as handshake state on a client. However, whether + * |ssl| is a client or server is not known until explicitly configured with + * |SSL_set_connect_state|. If |handshake_func| is NULL, |ssl| is in an + * indeterminate mode and |ssl->server| is unset. */ + if (ssl->handshake_func != NULL && !ssl->server) { + return ssl->s3->tmp.ca_names; + } + + if (ssl->client_CA != NULL) { + return ssl->client_CA; } + return ssl->ctx->client_CA; } -static int add_client_CA(STACK_OF(X509_NAME) **sk, X509 *x) { +static int add_client_CA(STACK_OF(X509_NAME) **sk, X509 *x509) { X509_NAME *name; - if (x == NULL) { + if (x509 == NULL) { return 0; } if (*sk == NULL) { @@ -566,7 +401,7 @@ static int add_client_CA(STACK_OF(X509_NAME) **sk, X509 *x) { } } - name = X509_NAME_dup(X509_get_subject_name(x)); + name = X509_NAME_dup(X509_get_subject_name(x509)); if (name == NULL) { return 0; } @@ -579,195 +414,12 @@ static int add_client_CA(STACK_OF(X509_NAME) **sk, X509 *x) { return 1; } -int SSL_add_client_CA(SSL *ssl, X509 *x) { - return add_client_CA(&(ssl->client_CA), x); -} - -int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x) { - return add_client_CA(&(ctx->client_CA), x); +int SSL_add_client_CA(SSL *ssl, X509 *x509) { + return add_client_CA(&ssl->client_CA, x509); } -static int xname_cmp(const X509_NAME **a, const X509_NAME **b) { - return X509_NAME_cmp(*a, *b); -} - -/* Load CA certs from a file into a STACK. Note that it is somewhat misnamed; - * it doesn't really have anything to do with clients (except that a common use - * for a stack of CAs is to send it to the client). Actually, it doesn't have - * much to do with CAs, either, since it will load any old cert. - * - * \param file the file containing one or more certs. - * \return a ::STACK containing the certs. */ -STACK_OF(X509_NAME) *SSL_load_client_CA_file(const char *file) { - BIO *in; - X509 *x = NULL; - X509_NAME *xn = NULL; - STACK_OF(X509_NAME) *ret = NULL, *sk; - - sk = sk_X509_NAME_new(xname_cmp); - in = BIO_new(BIO_s_file()); - - if (sk == NULL || in == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_load_client_CA_file, ERR_R_MALLOC_FAILURE); - goto err; - } - - if (!BIO_read_filename(in, file)) { - goto err; - } - - for (;;) { - if (PEM_read_bio_X509(in, &x, NULL, NULL) == NULL) { - break; - } - if (ret == NULL) { - ret = sk_X509_NAME_new_null(); - if (ret == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_load_client_CA_file, ERR_R_MALLOC_FAILURE); - goto err; - } - } - xn = X509_get_subject_name(x); - if (xn == NULL) { - goto err; - } - - /* check for duplicates */ - xn = X509_NAME_dup(xn); - if (xn == NULL) { - goto err; - } - if (sk_X509_NAME_find(sk, NULL, xn)) { - X509_NAME_free(xn); - } else { - sk_X509_NAME_push(sk, xn); - sk_X509_NAME_push(ret, xn); - } - } - - if (0) { - err: - sk_X509_NAME_pop_free(ret, X509_NAME_free); - ret = NULL; - } - - sk_X509_NAME_free(sk); - BIO_free(in); - X509_free(x); - if (ret != NULL) { - ERR_clear_error(); - } - return ret; -} - -/* Add a file of certs to a stack. - * - * \param stack the stack to add to. - * \param file the file to add from. All certs in this file that are not - * already in the stack will be added. - * \return 1 for success, 0 for failure. Note that in the case of failure some - * certs may have been added to \c stack. */ -int SSL_add_file_cert_subjects_to_stack(STACK_OF(X509_NAME) *stack, - const char *file) { - BIO *in; - X509 *x = NULL; - X509_NAME *xn = NULL; - int ret = 1; - int (*oldcmp)(const X509_NAME **a, const X509_NAME **b); - - oldcmp = sk_X509_NAME_set_cmp_func(stack, xname_cmp); - in = BIO_new(BIO_s_file()); - - if (in == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_add_file_cert_subjects_to_stack, - ERR_R_MALLOC_FAILURE); - goto err; - } - - if (!BIO_read_filename(in, file)) { - goto err; - } - - for (;;) { - if (PEM_read_bio_X509(in, &x, NULL, NULL) == NULL) { - break; - } - xn = X509_get_subject_name(x); - if (xn == NULL) { - goto err; - } - xn = X509_NAME_dup(xn); - if (xn == NULL) { - goto err; - } - if (sk_X509_NAME_find(stack, NULL, xn)) { - X509_NAME_free(xn); - } else { - sk_X509_NAME_push(stack, xn); - } - } - - ERR_clear_error(); - - if (0) { - err: - ret = 0; - } - - BIO_free(in); - X509_free(x); - - (void) sk_X509_NAME_set_cmp_func(stack, oldcmp); - - return ret; -} - -/* Add a directory of certs to a stack. - * - * \param stack the stack to append to. - * \param dir the directory to append from. All files in this directory will be - * examined as potential certs. Any that are acceptable to - * SSL_add_dir_cert_subjects_to_stack() that are not already in the stack will - * be included. - * \return 1 for success, 0 for failure. Note that in the case of failure some - * certs may have been added to \c stack. */ -int SSL_add_dir_cert_subjects_to_stack(STACK_OF(X509_NAME) *stack, - const char *dir) { - OPENSSL_DIR_CTX *d = NULL; - const char *filename; - int ret = 0; - - /* Note that a side effect is that the CAs will be sorted by name */ - while ((filename = OPENSSL_DIR_read(&d, dir))) { - char buf[1024]; - int r; - - if (strlen(dir) + strlen(filename) + 2 > sizeof(buf)) { - OPENSSL_PUT_ERROR(SSL, SSL_add_dir_cert_subjects_to_stack, - SSL_R_PATH_TOO_LONG); - goto err; - } - - r = BIO_snprintf(buf, sizeof buf, "%s/%s", dir, filename); - if (r <= 0 || r >= (int)sizeof(buf) || - !SSL_add_file_cert_subjects_to_stack(stack, buf)) { - goto err; - } - } - - if (errno) { - OPENSSL_PUT_ERROR(SSL, SSL_add_dir_cert_subjects_to_stack, ERR_R_SYS_LIB); - ERR_add_error_data(3, "OPENSSL_DIR_read(&ctx, '", dir, "')"); - goto err; - } - - ret = 1; - -err: - if (d) { - OPENSSL_DIR_end(&d); - } - return ret; +int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x509) { + return add_client_CA(&ctx->client_CA, x509); } /* Add a certificate to a BUF_MEM structure */ @@ -777,7 +429,7 @@ static int ssl_add_cert_to_buf(BUF_MEM *buf, unsigned long *l, X509 *x) { n = i2d_X509(x, NULL); if (!BUF_MEM_grow_clean(buf, (int)(n + (*l) + 3))) { - OPENSSL_PUT_ERROR(SSL, ssl_add_cert_to_buf, ERR_R_BUF_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB); return 0; } p = (uint8_t *)&(buf->data[*l]); @@ -789,34 +441,21 @@ static int ssl_add_cert_to_buf(BUF_MEM *buf, unsigned long *l, X509 *x) { } /* Add certificate chain to internal SSL BUF_MEM structure. */ -int ssl_add_cert_chain(SSL *s, CERT_PKEY *cpk, unsigned long *l) { - BUF_MEM *buf = s->init_buf; +int ssl_add_cert_chain(SSL *ssl, unsigned long *l) { + CERT *cert = ssl->cert; + BUF_MEM *buf = ssl->init_buf; int no_chain = 0; size_t i; - X509 *x = cpk->x509; - STACK_OF(X509) *extra_certs; - X509_STORE *chain_store; + X509 *x = cert->x509; + STACK_OF(X509) *chain = cert->chain; if (x == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl_add_cert_chain, SSL_R_NO_CERTIFICATE_SET); + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_SET); return 0; } - if (s->cert->chain_store) { - chain_store = s->cert->chain_store; - } else { - chain_store = s->ctx->cert_store; - } - - /* If we have a certificate specific chain use it, else use parent ctx. */ - if (cpk && cpk->chain) { - extra_certs = cpk->chain; - } else { - extra_certs = s->ctx->extra_certs; - } - - if ((s->mode & SSL_MODE_NO_AUTO_CHAIN) || extra_certs) { + if ((ssl->mode & SSL_MODE_NO_AUTO_CHAIN) || chain != NULL) { no_chain = 1; } @@ -825,8 +464,8 @@ int ssl_add_cert_chain(SSL *s, CERT_PKEY *cpk, unsigned long *l) { return 0; } - for (i = 0; i < sk_X509_num(extra_certs); i++) { - x = sk_X509_value(extra_certs, i); + for (i = 0; i < sk_X509_num(chain); i++) { + x = sk_X509_value(chain, i); if (!ssl_add_cert_to_buf(buf, l, x)) { return 0; } @@ -834,8 +473,8 @@ int ssl_add_cert_chain(SSL *s, CERT_PKEY *cpk, unsigned long *l) { } else { X509_STORE_CTX xs_ctx; - if (!X509_STORE_CTX_init(&xs_ctx, chain_store, x, NULL)) { - OPENSSL_PUT_ERROR(SSL, ssl_add_cert_chain, ERR_R_X509_LIB); + if (!X509_STORE_CTX_init(&xs_ctx, ssl->ctx->cert_store, x, NULL)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB); return 0; } X509_verify_cert(&xs_ctx); @@ -855,132 +494,65 @@ int ssl_add_cert_chain(SSL *s, CERT_PKEY *cpk, unsigned long *l) { return 1; } -/* Build a certificate chain for current certificate */ -int ssl_build_cert_chain(CERT *c, X509_STORE *chain_store, int flags) { - CERT_PKEY *cpk = c->key; - X509_STORE_CTX xs_ctx; - STACK_OF(X509) *chain = NULL, *untrusted = NULL; - X509 *x; - int i, rv = 0; - uint32_t error; - - if (!cpk->x509) { - OPENSSL_PUT_ERROR(SSL, ssl_build_cert_chain, SSL_R_NO_CERTIFICATE_SET); - goto err; - } - - /* Rearranging and check the chain: add everything to a store */ - if (flags & SSL_BUILD_CHAIN_FLAG_CHECK) { - size_t j; - chain_store = X509_STORE_new(); - if (!chain_store) { - goto err; - } - - for (j = 0; j < sk_X509_num(cpk->chain); j++) { - x = sk_X509_value(cpk->chain, j); - if (!X509_STORE_add_cert(chain_store, x)) { - error = ERR_peek_last_error(); - if (ERR_GET_LIB(error) != ERR_LIB_X509 || - ERR_GET_REASON(error) != X509_R_CERT_ALREADY_IN_HASH_TABLE) { - goto err; - } - ERR_clear_error(); - } - } +int SSL_CTX_set0_chain(SSL_CTX *ctx, STACK_OF(X509) *chain) { + return ssl_cert_set0_chain(ctx->cert, chain); +} - /* Add EE cert too: it might be self signed */ - if (!X509_STORE_add_cert(chain_store, cpk->x509)) { - error = ERR_peek_last_error(); - if (ERR_GET_LIB(error) != ERR_LIB_X509 || - ERR_GET_REASON(error) != X509_R_CERT_ALREADY_IN_HASH_TABLE) { - goto err; - } - ERR_clear_error(); - } - } else { - if (c->chain_store) { - chain_store = c->chain_store; - } +int SSL_CTX_set1_chain(SSL_CTX *ctx, STACK_OF(X509) *chain) { + return ssl_cert_set1_chain(ctx->cert, chain); +} - if (flags & SSL_BUILD_CHAIN_FLAG_UNTRUSTED) { - untrusted = cpk->chain; - } - } +int SSL_set0_chain(SSL *ssl, STACK_OF(X509) *chain) { + return ssl_cert_set0_chain(ssl->cert, chain); +} - if (!X509_STORE_CTX_init(&xs_ctx, chain_store, cpk->x509, untrusted)) { - OPENSSL_PUT_ERROR(SSL, ssl_build_cert_chain, ERR_R_X509_LIB); - goto err; - } +int SSL_set1_chain(SSL *ssl, STACK_OF(X509) *chain) { + return ssl_cert_set1_chain(ssl->cert, chain); +} - i = X509_verify_cert(&xs_ctx); - if (i <= 0 && flags & SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR) { - if (flags & SSL_BUILD_CHAIN_FLAG_CLEAR_ERROR) { - ERR_clear_error(); - } - i = 1; - rv = 2; - } +int SSL_CTX_add0_chain_cert(SSL_CTX *ctx, X509 *x509) { + return ssl_cert_add0_chain_cert(ctx->cert, x509); +} - if (i > 0) { - chain = X509_STORE_CTX_get1_chain(&xs_ctx); - } - if (i <= 0) { - OPENSSL_PUT_ERROR(SSL, ssl_build_cert_chain, - SSL_R_CERTIFICATE_VERIFY_FAILED); - i = X509_STORE_CTX_get_error(&xs_ctx); - ERR_add_error_data(2, "Verify error:", X509_verify_cert_error_string(i)); +int SSL_CTX_add1_chain_cert(SSL_CTX *ctx, X509 *x509) { + return ssl_cert_add1_chain_cert(ctx->cert, x509); +} - X509_STORE_CTX_cleanup(&xs_ctx); - goto err; - } +int SSL_CTX_add_extra_chain_cert(SSL_CTX *ctx, X509 *x509) { + return SSL_CTX_add0_chain_cert(ctx, x509); +} - X509_STORE_CTX_cleanup(&xs_ctx); - if (cpk->chain) { - sk_X509_pop_free(cpk->chain, X509_free); - } +int SSL_add0_chain_cert(SSL *ssl, X509 *x509) { + return ssl_cert_add0_chain_cert(ssl->cert, x509); +} - /* Remove EE certificate from chain */ - x = sk_X509_shift(chain); - X509_free(x); - if (flags & SSL_BUILD_CHAIN_FLAG_NO_ROOT) { - if (sk_X509_num(chain) > 0) { - /* See if last cert is self signed */ - x = sk_X509_value(chain, sk_X509_num(chain) - 1); - X509_check_purpose(x, -1, 0); - if (x->ex_flags & EXFLAG_SS) { - x = sk_X509_pop(chain); - X509_free(x); - } - } - } +int SSL_add1_chain_cert(SSL *ssl, X509 *x509) { + return ssl_cert_add1_chain_cert(ssl->cert, x509); +} - cpk->chain = chain; - if (rv == 0) { - rv = 1; - } +int SSL_CTX_clear_chain_certs(SSL_CTX *ctx) { + return SSL_CTX_set0_chain(ctx, NULL); +} -err: - if (flags & SSL_BUILD_CHAIN_FLAG_CHECK) { - X509_STORE_free(chain_store); - } +int SSL_CTX_clear_extra_chain_certs(SSL_CTX *ctx) { + return SSL_CTX_clear_chain_certs(ctx); +} - return rv; +int SSL_clear_chain_certs(SSL *ssl) { + return SSL_set0_chain(ssl, NULL); } -int ssl_cert_set_cert_store(CERT *c, X509_STORE *store, int chain, int ref) { - X509_STORE **pstore; - if (chain) { - pstore = &c->chain_store; - } else { - pstore = &c->verify_store; - } +int SSL_CTX_get0_chain_certs(const SSL_CTX *ctx, STACK_OF(X509) **out_chain) { + *out_chain = ctx->cert->chain; + return 1; +} - X509_STORE_free(*pstore); - *pstore = store; +int SSL_CTX_get_extra_chain_certs(const SSL_CTX *ctx, + STACK_OF(X509) **out_chain) { + return SSL_CTX_get0_chain_certs(ctx, out_chain); +} - if (ref && store) { - CRYPTO_refcount_inc(&store->references); - } +int SSL_get0_chain_certs(const SSL *ssl, STACK_OF(X509) **out_chain) { + *out_chain = ssl->cert->chain; return 1; } diff --git a/src/ssl/ssl_cipher.c b/src/ssl/ssl_cipher.c index 8d03c9e..b23d775 100644 --- a/src/ssl/ssl_cipher.c +++ b/src/ssl/ssl_cipher.c @@ -138,6 +138,8 @@ * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR * OTHERWISE. */ +#include <openssl/ssl.h> + #include <assert.h> #include <stdio.h> #include <string.h> @@ -155,6 +157,12 @@ /* kCiphers is an array of all supported ciphers, sorted by id. */ const SSL_CIPHER kCiphers[] = { /* The RSA ciphers */ + /* Cipher 02 */ + { + SSL3_TXT_RSA_NULL_SHA, SSL3_CK_RSA_NULL_SHA, SSL_kRSA, SSL_aRSA, + SSL_eNULL, SSL_SHA1, SSL_SSLV3, SSL_FIPS, SSL_HANDSHAKE_MAC_DEFAULT, 0, 0, + }, + /* Cipher 04 */ { SSL3_TXT_RSA_RC4_128_MD5, SSL3_CK_RSA_RC4_128_MD5, SSL_kRSA, SSL_aRSA, @@ -270,8 +278,7 @@ const SSL_CIPHER kCiphers[] = { TLS1_TXT_RSA_WITH_AES_128_GCM_SHA256, TLS1_CK_RSA_WITH_AES_128_GCM_SHA256, SSL_kRSA, SSL_aRSA, SSL_AES128GCM, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS, - SSL_HANDSHAKE_MAC_SHA256 | - SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD, + SSL_HANDSHAKE_MAC_SHA256, 128, 128, }, @@ -280,8 +287,7 @@ const SSL_CIPHER kCiphers[] = { TLS1_TXT_RSA_WITH_AES_256_GCM_SHA384, TLS1_CK_RSA_WITH_AES_256_GCM_SHA384, SSL_kRSA, SSL_aRSA, SSL_AES256GCM, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS, - SSL_HANDSHAKE_MAC_SHA384 | - SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD, + SSL_HANDSHAKE_MAC_SHA384, 256, 256, }, @@ -290,8 +296,7 @@ const SSL_CIPHER kCiphers[] = { TLS1_TXT_DHE_RSA_WITH_AES_128_GCM_SHA256, TLS1_CK_DHE_RSA_WITH_AES_128_GCM_SHA256, SSL_kDHE, SSL_aRSA, SSL_AES128GCM, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS, - SSL_HANDSHAKE_MAC_SHA256 | - SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD, + SSL_HANDSHAKE_MAC_SHA256, 128, 128, }, @@ -300,8 +305,7 @@ const SSL_CIPHER kCiphers[] = { TLS1_TXT_DHE_RSA_WITH_AES_256_GCM_SHA384, TLS1_CK_DHE_RSA_WITH_AES_256_GCM_SHA384, SSL_kDHE, SSL_aRSA, SSL_AES256GCM, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS, - SSL_HANDSHAKE_MAC_SHA384 | - SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD, + SSL_HANDSHAKE_MAC_SHA384, 256, 256, }, @@ -395,8 +399,7 @@ const SSL_CIPHER kCiphers[] = { TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, SSL_kECDHE, SSL_aECDSA, SSL_AES128GCM, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS, - SSL_HANDSHAKE_MAC_SHA256 | - SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD, + SSL_HANDSHAKE_MAC_SHA256, 128, 128, }, @@ -405,8 +408,7 @@ const SSL_CIPHER kCiphers[] = { TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS1_CK_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, SSL_kECDHE, SSL_aECDSA, SSL_AES256GCM, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS, - SSL_HANDSHAKE_MAC_SHA384 | - SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD, + SSL_HANDSHAKE_MAC_SHA384, 256, 256, }, @@ -415,8 +417,7 @@ const SSL_CIPHER kCiphers[] = { TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, SSL_kECDHE, SSL_aRSA, SSL_AES128GCM, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS, - SSL_HANDSHAKE_MAC_SHA256 | - SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD, + SSL_HANDSHAKE_MAC_SHA256, 128, 128, }, @@ -425,8 +426,7 @@ const SSL_CIPHER kCiphers[] = { TLS1_TXT_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS1_CK_ECDHE_RSA_WITH_AES_256_GCM_SHA384, SSL_kECDHE, SSL_aRSA, SSL_AES256GCM, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS, - SSL_HANDSHAKE_MAC_SHA384 | - SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD, + SSL_HANDSHAKE_MAC_SHA384, 256, 256, }, @@ -448,15 +448,15 @@ const SSL_CIPHER kCiphers[] = { SSL_HANDSHAKE_MAC_DEFAULT, 256, 256, }, +#if !defined(BORINGSSL_ANDROID_SYSTEM) /* ChaCha20-Poly1305 cipher suites. */ -#if !defined(ANDROID) { TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305, TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305, SSL_kECDHE, SSL_aRSA, SSL_CHACHA20POLY1305, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH, SSL_HANDSHAKE_MAC_SHA256, - 256, 0, + 256, 256, }, { @@ -464,33 +464,13 @@ const SSL_CIPHER kCiphers[] = { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305, SSL_kECDHE, SSL_aECDSA, SSL_CHACHA20POLY1305, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH, SSL_HANDSHAKE_MAC_SHA256, - 256, 0, - }, - - { - TLS1_TXT_DHE_RSA_WITH_CHACHA20_POLY1305, - TLS1_CK_DHE_RSA_CHACHA20_POLY1305, SSL_kDHE, SSL_aRSA, - SSL_CHACHA20POLY1305, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH, - SSL_HANDSHAKE_MAC_SHA256, - 256, 0, + 256, 256, }, #endif }; static const size_t kCiphersLen = sizeof(kCiphers) / sizeof(kCiphers[0]); -struct handshake_digest { - uint32_t mask; - const EVP_MD *(*md_func)(void); -}; - -static const struct handshake_digest ssl_handshake_digests[SSL_MAX_DIGEST] = { - {SSL_HANDSHAKE_MAC_MD5, EVP_md5}, - {SSL_HANDSHAKE_MAC_SHA, EVP_sha1}, - {SSL_HANDSHAKE_MAC_SHA256, EVP_sha256}, - {SSL_HANDSHAKE_MAC_SHA384, EVP_sha384}, -}; - #define CIPHER_ADD 1 #define CIPHER_KILL 2 #define CIPHER_DEL 3 @@ -521,7 +501,8 @@ typedef struct cipher_alias_st { } CIPHER_ALIAS; static const CIPHER_ALIAS kCipherAliases[] = { - {SSL_TXT_ALL, ~0u, ~0u, ~0u, ~0u, ~0u, ~0u}, + /* "ALL" doesn't include eNULL (must be specifically enabled) */ + {SSL_TXT_ALL, ~0u, ~0u, ~SSL_eNULL, ~0u, ~0u, ~0u}, /* The "COMPLEMENTOFDEFAULT" rule is omitted. It matches nothing. */ @@ -542,7 +523,7 @@ static const CIPHER_ALIAS kCipherAliases[] = { {SSL_TXT_kPSK, SSL_kPSK, ~0u, ~0u, ~0u, ~0u, ~0u}, /* server authentication aliases */ - {SSL_TXT_aRSA, ~0u, SSL_aRSA, ~0u, ~0u, ~0u, ~0u}, + {SSL_TXT_aRSA, ~0u, SSL_aRSA, ~SSL_eNULL, ~0u, ~0u, ~0u}, {SSL_TXT_aECDSA, ~0u, SSL_aECDSA, ~0u, ~0u, ~0u, ~0u}, {SSL_TXT_ECDSA, ~0u, SSL_aECDSA, ~0u, ~0u, ~0u, ~0u}, {SSL_TXT_aPSK, ~0u, SSL_aPSK, ~0u, ~0u, ~0u, ~0u}, @@ -552,7 +533,7 @@ static const CIPHER_ALIAS kCipherAliases[] = { {SSL_TXT_EDH, SSL_kDHE, ~0u, ~0u, ~0u, ~0u, ~0u}, {SSL_TXT_ECDHE, SSL_kECDHE, ~0u, ~0u, ~0u, ~0u, ~0u}, {SSL_TXT_EECDH, SSL_kECDHE, ~0u, ~0u, ~0u, ~0u, ~0u}, - {SSL_TXT_RSA, SSL_kRSA, SSL_aRSA, ~0u, ~0u, ~0u, ~0u}, + {SSL_TXT_RSA, SSL_kRSA, SSL_aRSA, ~SSL_eNULL, ~0u, ~0u, ~0u}, {SSL_TXT_PSK, SSL_kPSK, SSL_aPSK, ~0u, ~0u, ~0u, ~0u}, /* symmetric encryption aliases */ @@ -566,21 +547,21 @@ static const CIPHER_ALIAS kCipherAliases[] = { /* MAC aliases */ {SSL_TXT_MD5, ~0u, ~0u, ~0u, SSL_MD5, ~0u, ~0u}, - {SSL_TXT_SHA1, ~0u, ~0u, ~0u, SSL_SHA1, ~0u, ~0u}, - {SSL_TXT_SHA, ~0u, ~0u, ~0u, SSL_SHA1, ~0u, ~0u}, + {SSL_TXT_SHA1, ~0u, ~0u, ~SSL_eNULL, SSL_SHA1, ~0u, ~0u}, + {SSL_TXT_SHA, ~0u, ~0u, ~SSL_eNULL, SSL_SHA1, ~0u, ~0u}, {SSL_TXT_SHA256, ~0u, ~0u, ~0u, SSL_SHA256, ~0u, ~0u}, {SSL_TXT_SHA384, ~0u, ~0u, ~0u, SSL_SHA384, ~0u, ~0u}, /* protocol version aliases */ - {SSL_TXT_SSLV3, ~0u, ~0u, ~0u, ~0u, SSL_SSLV3, ~0u}, - {SSL_TXT_TLSV1, ~0u, ~0u, ~0u, ~0u, SSL_TLSV1, ~0u}, - {SSL_TXT_TLSV1_2, ~0u, ~0u, ~0u, ~0u, SSL_TLSV1_2, ~0u}, + {SSL_TXT_SSLV3, ~0u, ~0u, ~SSL_eNULL, ~0u, SSL_SSLV3, ~0u}, + {SSL_TXT_TLSV1, ~0u, ~0u, ~SSL_eNULL, ~0u, SSL_TLSV1, ~0u}, + {SSL_TXT_TLSV1_2, ~0u, ~0u, ~SSL_eNULL, ~0u, SSL_TLSV1_2, ~0u}, /* strength classes */ {SSL_TXT_MEDIUM, ~0u, ~0u, ~0u, ~0u, ~0u, SSL_MEDIUM}, {SSL_TXT_HIGH, ~0u, ~0u, ~0u, ~0u, ~0u, SSL_HIGH}, /* FIPS 140-2 approved ciphersuite */ - {SSL_TXT_FIPS, ~0u, ~0u, ~0u, ~0u, ~0u, SSL_FIPS}, + {SSL_TXT_FIPS, ~0u, ~0u, ~SSL_eNULL, ~0u, ~0u, SSL_FIPS}, }; static const size_t kCipherAliasesLen = @@ -630,7 +611,7 @@ int ssl_cipher_get_evp_aead(const EVP_AEAD **out_aead, *out_fixed_iv_len = 4; return 1; -#if !defined(ANDROID) +#if !defined(BORINGSSL_ANDROID_SYSTEM) case SSL_CHACHA20POLY1305: *out_aead = EVP_aead_chacha20_poly1305(); *out_fixed_iv_len = 0; @@ -725,19 +706,36 @@ int ssl_cipher_get_evp_aead(const EVP_AEAD **out_aead, return 0; } + case SSL_eNULL: + switch (cipher->algorithm_mac) { + case SSL_SHA1: + if (version == SSL3_VERSION) { + *out_aead = EVP_aead_null_sha1_ssl3(); + } else { + *out_aead = EVP_aead_null_sha1_tls(); + } + *out_mac_secret_len = SHA_DIGEST_LENGTH; + return 1; + default: + return 0; + } + default: return 0; } } -int ssl_get_handshake_digest(uint32_t *out_mask, const EVP_MD **out_md, - size_t idx) { - if (idx >= SSL_MAX_DIGEST) { - return 0; +const EVP_MD *ssl_get_handshake_digest(uint32_t algorithm_prf) { + switch (algorithm_prf) { + case SSL_HANDSHAKE_MAC_DEFAULT: + return EVP_sha1(); + case SSL_HANDSHAKE_MAC_SHA256: + return EVP_sha256(); + case SSL_HANDSHAKE_MAC_SHA384: + return EVP_sha384(); + default: + return NULL; } - *out_mask = ssl_handshake_digests[idx].mask; - *out_md = ssl_handshake_digests[idx].md_func(); - return 1; } #define ITEM_SEP(a) \ @@ -979,7 +977,7 @@ static int ssl_cipher_strength_sort(CIPHER_ORDER **head_p, number_uses = OPENSSL_malloc((max_strength_bits + 1) * sizeof(int)); if (!number_uses) { - OPENSSL_PUT_ERROR(SSL, ssl_cipher_strength_sort, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); return 0; } memset(number_uses, 0, (max_strength_bits + 1) * sizeof(int)); @@ -1041,8 +1039,7 @@ static int ssl_cipher_process_rulestr(const SSL_PROTOCOL_METHOD *ssl_method, continue; } else if (!(ch >= 'a' && ch <= 'z') && !(ch >= 'A' && ch <= 'Z') && !(ch >= '0' && ch <= '9')) { - OPENSSL_PUT_ERROR(SSL, ssl_cipher_process_rulestr, - SSL_R_UNEXPECTED_OPERATOR_IN_GROUP); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_OPERATOR_IN_GROUP); retval = in_group = 0; break; } else { @@ -1062,7 +1059,7 @@ static int ssl_cipher_process_rulestr(const SSL_PROTOCOL_METHOD *ssl_method, l++; } else if (ch == '[') { if (in_group) { - OPENSSL_PUT_ERROR(SSL, ssl_cipher_process_rulestr, SSL_R_NESTED_GROUP); + OPENSSL_PUT_ERROR(SSL, SSL_R_NESTED_GROUP); retval = in_group = 0; break; } @@ -1077,8 +1074,7 @@ static int ssl_cipher_process_rulestr(const SSL_PROTOCOL_METHOD *ssl_method, /* If preference groups are enabled, the only legal operator is +. * Otherwise the in_group bits will get mixed up. */ if (has_group && rule != CIPHER_ADD) { - OPENSSL_PUT_ERROR(SSL, ssl_cipher_process_rulestr, - SSL_R_MIXED_SPECIAL_OPERATOR_WITH_GROUPS); + OPENSSL_PUT_ERROR(SSL, SSL_R_MIXED_SPECIAL_OPERATOR_WITH_GROUPS); retval = in_group = 0; break; } @@ -1110,8 +1106,7 @@ static int ssl_cipher_process_rulestr(const SSL_PROTOCOL_METHOD *ssl_method, if (buf_len == 0) { /* We hit something we cannot deal with, it is no command or separator * nor alphanumeric, so we call this an error. */ - OPENSSL_PUT_ERROR(SSL, ssl_cipher_process_rulestr, - SSL_R_INVALID_COMMAND); + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_COMMAND); retval = in_group = 0; l++; break; @@ -1165,8 +1160,7 @@ static int ssl_cipher_process_rulestr(const SSL_PROTOCOL_METHOD *ssl_method, if (buf_len == 8 && !strncmp(buf, "STRENGTH", 8)) { ok = ssl_cipher_strength_sort(head_p, tail_p); } else { - OPENSSL_PUT_ERROR(SSL, ssl_cipher_process_rulestr, - SSL_R_INVALID_COMMAND); + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_COMMAND); } if (ok == 0) { @@ -1186,7 +1180,7 @@ static int ssl_cipher_process_rulestr(const SSL_PROTOCOL_METHOD *ssl_method, } if (in_group) { - OPENSSL_PUT_ERROR(SSL, ssl_cipher_process_rulestr, SSL_R_INVALID_COMMAND); + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_COMMAND); retval = 0; } @@ -1216,7 +1210,7 @@ ssl_create_cipher_list(const SSL_PROTOCOL_METHOD *ssl_method, * allocation. */ co_list = (CIPHER_ORDER *)OPENSSL_malloc(sizeof(CIPHER_ORDER) * kCiphersLen); if (co_list == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl_create_cipher_list, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); return NULL; } @@ -1391,13 +1385,27 @@ int SSL_CIPHER_has_MD5_HMAC(const SSL_CIPHER *cipher) { } int SSL_CIPHER_is_AESGCM(const SSL_CIPHER *cipher) { - return (cipher->algorithm_mac & (SSL_AES128GCM | SSL_AES256GCM)) != 0; + return (cipher->algorithm_enc & (SSL_AES128GCM | SSL_AES256GCM)) != 0; } int SSL_CIPHER_is_CHACHA20POLY1305(const SSL_CIPHER *cipher) { return (cipher->algorithm_enc & SSL_CHACHA20POLY1305) != 0; } +int SSL_CIPHER_is_NULL(const SSL_CIPHER *cipher) { + return (cipher->algorithm_enc & SSL_eNULL) != 0; +} + +int SSL_CIPHER_is_RC4(const SSL_CIPHER *cipher) { + return (cipher->algorithm_enc & SSL_RC4) != 0; +} + +int SSL_CIPHER_is_block_cipher(const SSL_CIPHER *cipher) { + /* Neither stream cipher nor AEAD. */ + return (cipher->algorithm_enc & (SSL_RC4 | SSL_eNULL)) == 0 && + cipher->algorithm_mac != SSL_AEAD; +} + /* return the actual cipher being used */ const char *SSL_CIPHER_get_name(const SSL_CIPHER *cipher) { if (cipher != NULL) { @@ -1472,27 +1480,24 @@ static const char *ssl_cipher_get_enc_name(const SSL_CIPHER *cipher) { } static const char *ssl_cipher_get_prf_name(const SSL_CIPHER *cipher) { - if ((cipher->algorithm2 & SSL_HANDSHAKE_MAC_DEFAULT) == - SSL_HANDSHAKE_MAC_DEFAULT) { - /* Before TLS 1.2, the PRF component is the hash used in the HMAC, which is - * only ever MD5 or SHA-1. */ - switch (cipher->algorithm_mac) { - case SSL_MD5: - return "MD5"; - case SSL_SHA1: - return "SHA"; - default: - assert(0); - return "UNKNOWN"; - } - } else if (cipher->algorithm2 & SSL_HANDSHAKE_MAC_SHA256) { - return "SHA256"; - } else if (cipher->algorithm2 & SSL_HANDSHAKE_MAC_SHA384) { - return "SHA384"; - } else { - assert(0); - return "UNKNOWN"; + switch (cipher->algorithm_prf) { + case SSL_HANDSHAKE_MAC_DEFAULT: + /* Before TLS 1.2, the PRF component is the hash used in the HMAC, which is + * only ever MD5 or SHA-1. */ + switch (cipher->algorithm_mac) { + case SSL_MD5: + return "MD5"; + case SSL_SHA1: + return "SHA"; + } + break; + case SSL_HANDSHAKE_MAC_SHA256: + return "SHA256"; + case SSL_HANDSHAKE_MAC_SHA384: + return "SHA384"; } + assert(0); + return "UNKNOWN"; } char *SSL_CIPHER_get_rfc_name(const SSL_CIPHER *cipher) { @@ -1625,6 +1630,10 @@ const char *SSL_CIPHER_description(const SSL_CIPHER *cipher, char *buf, enc = "ChaCha20-Poly1305"; break; + case SSL_eNULL: + enc="None"; + break; + default: enc = "unknown"; break; @@ -1674,29 +1683,28 @@ const char *SSL_CIPHER_get_version(const SSL_CIPHER *cipher) { return "TLSv1/SSLv3"; } -void *SSL_COMP_get_compression_methods(void) { return NULL; } +COMP_METHOD *SSL_COMP_get_compression_methods(void) { return NULL; } -int SSL_COMP_add_compression_method(int id, void *cm) { return 1; } +int SSL_COMP_add_compression_method(int id, COMP_METHOD *cm) { return 1; } -const char *SSL_COMP_get_name(const void *comp) { return NULL; } +const char *SSL_COMP_get_name(const COMP_METHOD *comp) { return NULL; } -int ssl_cipher_get_cert_index(const SSL_CIPHER *cipher) { +int ssl_cipher_get_key_type(const SSL_CIPHER *cipher) { uint32_t alg_a = cipher->algorithm_auth; if (alg_a & SSL_aECDSA) { - return SSL_PKEY_ECC; + return EVP_PKEY_EC; } else if (alg_a & SSL_aRSA) { - return SSL_PKEY_RSA_ENC; + return EVP_PKEY_RSA; } - return -1; + return EVP_PKEY_NONE; } int ssl_cipher_has_server_public_key(const SSL_CIPHER *cipher) { - /* PSK-authenticated ciphers do not use a public key, except for - * RSA_PSK. */ - if ((cipher->algorithm_auth & SSL_aPSK) && - !(cipher->algorithm_mkey & SSL_kRSA)) { + /* PSK-authenticated ciphers do not use a certificate. (RSA_PSK is not + * supported.) */ + if (cipher->algorithm_auth & SSL_aPSK) { return 0; } @@ -1713,3 +1721,34 @@ int ssl_cipher_requires_server_key_exchange(const SSL_CIPHER *cipher) { /* It is optional in all others. */ return 0; } + +size_t ssl_cipher_get_record_split_len(const SSL_CIPHER *cipher) { + size_t block_size; + switch (cipher->algorithm_enc) { + case SSL_3DES: + block_size = 8; + break; + case SSL_AES128: + case SSL_AES256: + block_size = 16; + break; + default: + return 0; + } + + size_t mac_len; + switch (cipher->algorithm_mac) { + case SSL_MD5: + mac_len = MD5_DIGEST_LENGTH; + break; + case SSL_SHA1: + mac_len = SHA_DIGEST_LENGTH; + break; + default: + return 0; + } + + size_t ret = 1 + mac_len; + ret += block_size - (ret % block_size); + return ret; +} diff --git a/src/ssl/ssl_file.c b/src/ssl/ssl_file.c new file mode 100644 index 0000000..88ad5b7 --- /dev/null +++ b/src/ssl/ssl_file.c @@ -0,0 +1,623 @@ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ +/* ==================================================================== + * Copyright (c) 1998-2007 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + +#include <openssl/ssl.h> + +#include <errno.h> +#include <string.h> + +#include <openssl/bio.h> +#include <openssl/err.h> +#include <openssl/mem.h> +#include <openssl/pem.h> +#include <openssl/stack.h> +#include <openssl/x509.h> + +#include "../crypto/directory.h" +#include "internal.h" + + +static int xname_cmp(const X509_NAME **a, const X509_NAME **b) { + return X509_NAME_cmp(*a, *b); +} + +/* TODO(davidben): Is there any reason this doesn't call + * |SSL_add_file_cert_subjects_to_stack|? */ +STACK_OF(X509_NAME) *SSL_load_client_CA_file(const char *file) { + BIO *in; + X509 *x = NULL; + X509_NAME *xn = NULL; + STACK_OF(X509_NAME) *ret = NULL, *sk; + + sk = sk_X509_NAME_new(xname_cmp); + in = BIO_new(BIO_s_file()); + + if (sk == NULL || in == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + + if (!BIO_read_filename(in, file)) { + goto err; + } + + for (;;) { + if (PEM_read_bio_X509(in, &x, NULL, NULL) == NULL) { + break; + } + if (ret == NULL) { + ret = sk_X509_NAME_new_null(); + if (ret == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + } + xn = X509_get_subject_name(x); + if (xn == NULL) { + goto err; + } + + /* check for duplicates */ + xn = X509_NAME_dup(xn); + if (xn == NULL) { + goto err; + } + if (sk_X509_NAME_find(sk, NULL, xn)) { + X509_NAME_free(xn); + } else { + sk_X509_NAME_push(sk, xn); + sk_X509_NAME_push(ret, xn); + } + } + + if (0) { + err: + sk_X509_NAME_pop_free(ret, X509_NAME_free); + ret = NULL; + } + + sk_X509_NAME_free(sk); + BIO_free(in); + X509_free(x); + if (ret != NULL) { + ERR_clear_error(); + } + return ret; +} + +int SSL_add_file_cert_subjects_to_stack(STACK_OF(X509_NAME) *stack, + const char *file) { + BIO *in; + X509 *x = NULL; + X509_NAME *xn = NULL; + int ret = 1; + int (*oldcmp)(const X509_NAME **a, const X509_NAME **b); + + oldcmp = sk_X509_NAME_set_cmp_func(stack, xname_cmp); + in = BIO_new(BIO_s_file()); + + if (in == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + + if (!BIO_read_filename(in, file)) { + goto err; + } + + for (;;) { + if (PEM_read_bio_X509(in, &x, NULL, NULL) == NULL) { + break; + } + xn = X509_get_subject_name(x); + if (xn == NULL) { + goto err; + } + xn = X509_NAME_dup(xn); + if (xn == NULL) { + goto err; + } + if (sk_X509_NAME_find(stack, NULL, xn)) { + X509_NAME_free(xn); + } else { + sk_X509_NAME_push(stack, xn); + } + } + + ERR_clear_error(); + + if (0) { + err: + ret = 0; + } + + BIO_free(in); + X509_free(x); + + (void) sk_X509_NAME_set_cmp_func(stack, oldcmp); + + return ret; +} + +/* Add a directory of certs to a stack. + * + * \param stack the stack to append to. + * \param dir the directory to append from. All files in this directory will be + * examined as potential certs. Any that are acceptable to + * SSL_add_dir_cert_subjects_to_stack() that are not already in the stack will + * be included. + * \return 1 for success, 0 for failure. Note that in the case of failure some + * certs may have been added to \c stack. */ +int SSL_add_dir_cert_subjects_to_stack(STACK_OF(X509_NAME) *stack, + const char *dir) { + OPENSSL_DIR_CTX *d = NULL; + const char *filename; + int ret = 0; + + /* Note that a side effect is that the CAs will be sorted by name */ + while ((filename = OPENSSL_DIR_read(&d, dir))) { + char buf[1024]; + int r; + + if (strlen(dir) + strlen(filename) + 2 > sizeof(buf)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_PATH_TOO_LONG); + goto err; + } + + r = BIO_snprintf(buf, sizeof buf, "%s/%s", dir, filename); + if (r <= 0 || r >= (int)sizeof(buf) || + !SSL_add_file_cert_subjects_to_stack(stack, buf)) { + goto err; + } + } + + if (errno) { + OPENSSL_PUT_ERROR(SSL, ERR_R_SYS_LIB); + ERR_add_error_data(3, "OPENSSL_DIR_read(&ctx, '", dir, "')"); + goto err; + } + + ret = 1; + +err: + if (d) { + OPENSSL_DIR_end(&d); + } + return ret; +} + +int SSL_use_certificate_file(SSL *ssl, const char *file, int type) { + int reason_code; + BIO *in; + int ret = 0; + X509 *x = NULL; + + in = BIO_new(BIO_s_file()); + if (in == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB); + goto end; + } + + if (BIO_read_filename(in, file) <= 0) { + OPENSSL_PUT_ERROR(SSL, ERR_R_SYS_LIB); + goto end; + } + + if (type == SSL_FILETYPE_ASN1) { + reason_code = ERR_R_ASN1_LIB; + x = d2i_X509_bio(in, NULL); + } else if (type == SSL_FILETYPE_PEM) { + reason_code = ERR_R_PEM_LIB; + x = PEM_read_bio_X509(in, NULL, ssl->ctx->default_passwd_callback, + ssl->ctx->default_passwd_callback_userdata); + } else { + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SSL_FILETYPE); + goto end; + } + + if (x == NULL) { + OPENSSL_PUT_ERROR(SSL, reason_code); + goto end; + } + + ret = SSL_use_certificate(ssl, x); + +end: + X509_free(x); + BIO_free(in); + + return ret; +} + +int SSL_use_RSAPrivateKey_file(SSL *ssl, const char *file, int type) { + int reason_code, ret = 0; + BIO *in; + RSA *rsa = NULL; + + in = BIO_new(BIO_s_file()); + if (in == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB); + goto end; + } + + if (BIO_read_filename(in, file) <= 0) { + OPENSSL_PUT_ERROR(SSL, ERR_R_SYS_LIB); + goto end; + } + + if (type == SSL_FILETYPE_ASN1) { + reason_code = ERR_R_ASN1_LIB; + rsa = d2i_RSAPrivateKey_bio(in, NULL); + } else if (type == SSL_FILETYPE_PEM) { + reason_code = ERR_R_PEM_LIB; + rsa = + PEM_read_bio_RSAPrivateKey(in, NULL, ssl->ctx->default_passwd_callback, + ssl->ctx->default_passwd_callback_userdata); + } else { + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SSL_FILETYPE); + goto end; + } + + if (rsa == NULL) { + OPENSSL_PUT_ERROR(SSL, reason_code); + goto end; + } + ret = SSL_use_RSAPrivateKey(ssl, rsa); + RSA_free(rsa); + +end: + BIO_free(in); + return ret; +} + +int SSL_use_PrivateKey_file(SSL *ssl, const char *file, int type) { + int reason_code, ret = 0; + BIO *in; + EVP_PKEY *pkey = NULL; + + in = BIO_new(BIO_s_file()); + if (in == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB); + goto end; + } + + if (BIO_read_filename(in, file) <= 0) { + OPENSSL_PUT_ERROR(SSL, ERR_R_SYS_LIB); + goto end; + } + + if (type == SSL_FILETYPE_PEM) { + reason_code = ERR_R_PEM_LIB; + pkey = PEM_read_bio_PrivateKey(in, NULL, ssl->ctx->default_passwd_callback, + ssl->ctx->default_passwd_callback_userdata); + } else if (type == SSL_FILETYPE_ASN1) { + reason_code = ERR_R_ASN1_LIB; + pkey = d2i_PrivateKey_bio(in, NULL); + } else { + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SSL_FILETYPE); + goto end; + } + + if (pkey == NULL) { + OPENSSL_PUT_ERROR(SSL, reason_code); + goto end; + } + ret = SSL_use_PrivateKey(ssl, pkey); + EVP_PKEY_free(pkey); + +end: + BIO_free(in); + return ret; +} + +int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type) { + int reason_code; + BIO *in; + int ret = 0; + X509 *x = NULL; + + in = BIO_new(BIO_s_file()); + if (in == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB); + goto end; + } + + if (BIO_read_filename(in, file) <= 0) { + OPENSSL_PUT_ERROR(SSL, ERR_R_SYS_LIB); + goto end; + } + + if (type == SSL_FILETYPE_ASN1) { + reason_code = ERR_R_ASN1_LIB; + x = d2i_X509_bio(in, NULL); + } else if (type == SSL_FILETYPE_PEM) { + reason_code = ERR_R_PEM_LIB; + x = PEM_read_bio_X509(in, NULL, ctx->default_passwd_callback, + ctx->default_passwd_callback_userdata); + } else { + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SSL_FILETYPE); + goto end; + } + + if (x == NULL) { + OPENSSL_PUT_ERROR(SSL, reason_code); + goto end; + } + + ret = SSL_CTX_use_certificate(ctx, x); + +end: + X509_free(x); + BIO_free(in); + return ret; +} + +int SSL_CTX_use_RSAPrivateKey_file(SSL_CTX *ctx, const char *file, int type) { + int reason_code, ret = 0; + BIO *in; + RSA *rsa = NULL; + + in = BIO_new(BIO_s_file()); + if (in == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB); + goto end; + } + + if (BIO_read_filename(in, file) <= 0) { + OPENSSL_PUT_ERROR(SSL, ERR_R_SYS_LIB); + goto end; + } + + if (type == SSL_FILETYPE_ASN1) { + reason_code = ERR_R_ASN1_LIB; + rsa = d2i_RSAPrivateKey_bio(in, NULL); + } else if (type == SSL_FILETYPE_PEM) { + reason_code = ERR_R_PEM_LIB; + rsa = PEM_read_bio_RSAPrivateKey(in, NULL, ctx->default_passwd_callback, + ctx->default_passwd_callback_userdata); + } else { + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SSL_FILETYPE); + goto end; + } + + if (rsa == NULL) { + OPENSSL_PUT_ERROR(SSL, reason_code); + goto end; + } + ret = SSL_CTX_use_RSAPrivateKey(ctx, rsa); + RSA_free(rsa); + +end: + BIO_free(in); + return ret; +} + +int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type) { + int reason_code, ret = 0; + BIO *in; + EVP_PKEY *pkey = NULL; + + in = BIO_new(BIO_s_file()); + if (in == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB); + goto end; + } + + if (BIO_read_filename(in, file) <= 0) { + OPENSSL_PUT_ERROR(SSL, ERR_R_SYS_LIB); + goto end; + } + + if (type == SSL_FILETYPE_PEM) { + reason_code = ERR_R_PEM_LIB; + pkey = PEM_read_bio_PrivateKey(in, NULL, ctx->default_passwd_callback, + ctx->default_passwd_callback_userdata); + } else if (type == SSL_FILETYPE_ASN1) { + reason_code = ERR_R_ASN1_LIB; + pkey = d2i_PrivateKey_bio(in, NULL); + } else { + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SSL_FILETYPE); + goto end; + } + + if (pkey == NULL) { + OPENSSL_PUT_ERROR(SSL, reason_code); + goto end; + } + ret = SSL_CTX_use_PrivateKey(ctx, pkey); + EVP_PKEY_free(pkey); + +end: + BIO_free(in); + return ret; +} + +/* Read a file that contains our certificate in "PEM" format, possibly followed + * by a sequence of CA certificates that should be sent to the peer in the + * Certificate message. */ +int SSL_CTX_use_certificate_chain_file(SSL_CTX *ctx, const char *file) { + BIO *in; + int ret = 0; + X509 *x = NULL; + + ERR_clear_error(); /* clear error stack for SSL_CTX_use_certificate() */ + + in = BIO_new(BIO_s_file()); + if (in == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB); + goto end; + } + + if (BIO_read_filename(in, file) <= 0) { + OPENSSL_PUT_ERROR(SSL, ERR_R_SYS_LIB); + goto end; + } + + x = PEM_read_bio_X509_AUX(in, NULL, ctx->default_passwd_callback, + ctx->default_passwd_callback_userdata); + if (x == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_PEM_LIB); + goto end; + } + + ret = SSL_CTX_use_certificate(ctx, x); + + if (ERR_peek_error() != 0) { + ret = 0; /* Key/certificate mismatch doesn't imply ret==0 ... */ + } + + if (ret) { + /* If we could set up our certificate, now proceed to the CA + * certificates. */ + X509 *ca; + int r; + uint32_t err; + + SSL_CTX_clear_chain_certs(ctx); + + while ((ca = PEM_read_bio_X509(in, NULL, ctx->default_passwd_callback, + ctx->default_passwd_callback_userdata)) != + NULL) { + r = SSL_CTX_add0_chain_cert(ctx, ca); + if (!r) { + X509_free(ca); + ret = 0; + goto end; + } + /* Note that we must not free r if it was successfully added to the chain + * (while we must free the main certificate, since its reference count is + * increased by SSL_CTX_use_certificate). */ + } + + /* When the while loop ends, it's usually just EOF. */ + err = ERR_peek_last_error(); + if (ERR_GET_LIB(err) == ERR_LIB_PEM && + ERR_GET_REASON(err) == PEM_R_NO_START_LINE) { + ERR_clear_error(); + } else { + ret = 0; /* some real error */ + } + } + +end: + X509_free(x); + BIO_free(in); + return ret; +} + +void SSL_CTX_set_default_passwd_cb(SSL_CTX *ctx, pem_password_cb *cb) { + ctx->default_passwd_callback = cb; +} + +void SSL_CTX_set_default_passwd_cb_userdata(SSL_CTX *ctx, void *data) { + ctx->default_passwd_callback_userdata = data; +} + +IMPLEMENT_PEM_rw(SSL_SESSION, SSL_SESSION, PEM_STRING_SSL_SESSION, SSL_SESSION) diff --git a/src/ssl/ssl_lib.c b/src/ssl/ssl_lib.c index 9e1e308..74bd633 100644 --- a/src/ssl/ssl_lib.c +++ b/src/ssl/ssl_lib.c @@ -138,11 +138,14 @@ * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR * OTHERWISE. */ +#include <openssl/ssl.h> + #include <assert.h> #include <stdio.h> #include <string.h> #include <openssl/bytestring.h> +#include <openssl/crypto.h> #include <openssl/dh.h> #include <openssl/err.h> #include <openssl/lhash.h> @@ -155,6 +158,10 @@ #include "../crypto/internal.h" +/* |SSL_R_UNKNOWN_PROTOCOL| is no longer emitted, but continue to define it + * to avoid downstream churn. */ +OPENSSL_DECLARE_ERROR_REASON(SSL, UNKNOWN_PROTOCOL) + /* Some error codes are special. Ensure the make_errors.go script never * regresses this. */ OPENSSL_COMPILE_ASSERT(SSL_R_TLSV1_ALERT_NO_RENEGOTIATION == @@ -164,91 +171,192 @@ OPENSSL_COMPILE_ASSERT(SSL_R_TLSV1_ALERT_NO_RENEGOTIATION == /* kMaxHandshakeSize is the maximum size, in bytes, of a handshake message. */ static const size_t kMaxHandshakeSize = (1u << 24) - 1; -static CRYPTO_EX_DATA_CLASS g_ex_data_class_ssl = CRYPTO_EX_DATA_CLASS_INIT; -static CRYPTO_EX_DATA_CLASS g_ex_data_class_ssl_ctx = CRYPTO_EX_DATA_CLASS_INIT; +static CRYPTO_EX_DATA_CLASS g_ex_data_class_ssl = + CRYPTO_EX_DATA_CLASS_INIT_WITH_APP_DATA; +static CRYPTO_EX_DATA_CLASS g_ex_data_class_ssl_ctx = + CRYPTO_EX_DATA_CLASS_INIT_WITH_APP_DATA; -int SSL_clear(SSL *ssl) { - if (ssl->method == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_clear, SSL_R_NO_METHOD_SPECIFIED); - return 0; +int SSL_library_init(void) { + CRYPTO_library_init(); + return 1; +} + +static uint32_t ssl_session_hash(const SSL_SESSION *a) { + uint32_t hash = + ((uint32_t)a->session_id[0]) || + ((uint32_t)a->session_id[1] << 8) || + ((uint32_t)a->session_id[2] << 16) || + ((uint32_t)a->session_id[3] << 24); + + return hash; +} + +/* NB: If this function (or indeed the hash function which uses a sort of + * coarser function than this one) is changed, ensure + * SSL_CTX_has_matching_session_id() is checked accordingly. It relies on being + * able to construct an SSL_SESSION that will collide with any existing session + * with a matching session ID. */ +static int ssl_session_cmp(const SSL_SESSION *a, const SSL_SESSION *b) { + if (a->ssl_version != b->ssl_version) { + return 1; } - if (ssl_clear_bad_session(ssl)) { - SSL_SESSION_free(ssl->session); - ssl->session = NULL; + if (a->session_id_length != b->session_id_length) { + return 1; } - ssl->hit = 0; - ssl->shutdown = 0; + return memcmp(a->session_id, b->session_id, a->session_id_length); +} - /* SSL_clear may be called before or after the |ssl| is initialized in either - * accept or connect state. In the latter case, SSL_clear should preserve the - * half and reset |ssl->state| accordingly. */ - if (ssl->handshake_func != NULL) { - if (ssl->server) { - SSL_set_accept_state(ssl); - } else { - SSL_set_connect_state(ssl); - } - } else { - assert(ssl->state == 0); +SSL_CTX *SSL_CTX_new(const SSL_METHOD *method) { + SSL_CTX *ret = NULL; + + if (method == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_NULL_SSL_METHOD_PASSED); + return NULL; } - /* TODO(davidben): Some state on |ssl| is reset both in |SSL_new| and - * |SSL_clear| because it is per-connection state rather than configuration - * state. Per-connection state should be on |ssl->s3| and |ssl->d1| so it is - * naturally reset at the right points between |SSL_new|, |SSL_clear|, and - * |ssl3_new|. */ + if (SSL_get_ex_data_X509_STORE_CTX_idx() < 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_X509_VERIFICATION_SETUP_PROBLEMS); + goto err; + } - ssl->rwstate = SSL_NOTHING; - ssl->rstate = SSL_ST_READ_HEADER; + ret = (SSL_CTX *)OPENSSL_malloc(sizeof(SSL_CTX)); + if (ret == NULL) { + goto err; + } - BUF_MEM_free(ssl->init_buf); - ssl->init_buf = NULL; + memset(ret, 0, sizeof(SSL_CTX)); - ssl->packet = NULL; - ssl->packet_length = 0; + ret->method = method->method; - ssl_clear_cipher_ctx(ssl); + CRYPTO_MUTEX_init(&ret->lock); - OPENSSL_free(ssl->next_proto_negotiated); - ssl->next_proto_negotiated = NULL; - ssl->next_proto_negotiated_len = 0; + ret->session_cache_mode = SSL_SESS_CACHE_SERVER; + ret->session_cache_size = SSL_SESSION_CACHE_MAX_SIZE_DEFAULT; - /* The ssl->d1->mtu is simultaneously configuration (preserved across - * clear) and connection-specific state (gets reset). - * - * TODO(davidben): Avoid this. */ - unsigned mtu = 0; - if (ssl->d1 != NULL) { - mtu = ssl->d1->mtu; + /* We take the system default */ + ret->session_timeout = SSL_DEFAULT_SESSION_TIMEOUT; + + ret->references = 1; + + ret->max_cert_list = SSL_MAX_CERT_LIST_DEFAULT; + ret->verify_mode = SSL_VERIFY_NONE; + ret->cert = ssl_cert_new(); + if (ret->cert == NULL) { + goto err; } - ssl->method->ssl_free(ssl); - if (!ssl->method->ssl_new(ssl)) { - return 0; + ret->sessions = lh_SSL_SESSION_new(ssl_session_hash, ssl_session_cmp); + if (ret->sessions == NULL) { + goto err; + } + ret->cert_store = X509_STORE_new(); + if (ret->cert_store == NULL) { + goto err; } - ssl->enc_method = ssl3_get_enc_method(ssl->version); - assert(ssl->enc_method != NULL); - if (SSL_IS_DTLS(ssl) && (SSL_get_options(ssl) & SSL_OP_NO_QUERY_MTU)) { - ssl->d1->mtu = mtu; + ssl_create_cipher_list(ret->method, &ret->cipher_list, + &ret->cipher_list_by_id, SSL_DEFAULT_CIPHER_LIST); + if (ret->cipher_list == NULL || + sk_SSL_CIPHER_num(ret->cipher_list->ciphers) <= 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_LIBRARY_HAS_NO_CIPHERS); + goto err2; } - ssl->client_version = ssl->version; + ret->param = X509_VERIFY_PARAM_new(); + if (!ret->param) { + goto err; + } - return 1; + ret->client_CA = sk_X509_NAME_new_null(); + if (ret->client_CA == NULL) { + goto err; + } + + CRYPTO_new_ex_data(&g_ex_data_class_ssl_ctx, ret, &ret->ex_data); + + ret->max_send_fragment = SSL3_RT_MAX_PLAIN_LENGTH; + + /* Setup RFC4507 ticket keys */ + if (!RAND_bytes(ret->tlsext_tick_key_name, 16) || + !RAND_bytes(ret->tlsext_tick_hmac_key, 16) || + !RAND_bytes(ret->tlsext_tick_aes_key, 16)) { + ret->options |= SSL_OP_NO_TICKET; + } + + /* Default is to connect to non-RI servers. When RI is more widely deployed + * might change this. */ + ret->options |= SSL_OP_LEGACY_SERVER_CONNECT; + + /* Lock the SSL_CTX to the specified version, for compatibility with legacy + * uses of SSL_METHOD. */ + if (method->version != 0) { + SSL_CTX_set_max_version(ret, method->version); + SSL_CTX_set_min_version(ret, method->version); + } + + return ret; + +err: + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); +err2: + SSL_CTX_free(ret); + return NULL; +} + +void SSL_CTX_free(SSL_CTX *ctx) { + if (ctx == NULL || + !CRYPTO_refcount_dec_and_test_zero(&ctx->references)) { + return; + } + + X509_VERIFY_PARAM_free(ctx->param); + + /* Free internal session cache. However: the remove_cb() may reference the + * ex_data of SSL_CTX, thus the ex_data store can only be removed after the + * sessions were flushed. As the ex_data handling routines might also touch + * the session cache, the most secure solution seems to be: empty (flush) the + * cache, then free ex_data, then finally free the cache. (See ticket + * [openssl.org #212].) */ + SSL_CTX_flush_sessions(ctx, 0); + + CRYPTO_free_ex_data(&g_ex_data_class_ssl_ctx, ctx, &ctx->ex_data); + + CRYPTO_MUTEX_cleanup(&ctx->lock); + lh_SSL_SESSION_free(ctx->sessions); + X509_STORE_free(ctx->cert_store); + ssl_cipher_preference_list_free(ctx->cipher_list); + sk_SSL_CIPHER_free(ctx->cipher_list_by_id); + ssl_cipher_preference_list_free(ctx->cipher_list_tls10); + ssl_cipher_preference_list_free(ctx->cipher_list_tls11); + ssl_cert_free(ctx->cert); + sk_SSL_CUSTOM_EXTENSION_pop_free(ctx->client_custom_extensions, + SSL_CUSTOM_EXTENSION_free); + sk_SSL_CUSTOM_EXTENSION_pop_free(ctx->server_custom_extensions, + SSL_CUSTOM_EXTENSION_free); + sk_X509_NAME_pop_free(ctx->client_CA, X509_NAME_free); + sk_SRTP_PROTECTION_PROFILE_free(ctx->srtp_profiles); + OPENSSL_free(ctx->psk_identity_hint); + OPENSSL_free(ctx->tlsext_ellipticcurvelist); + OPENSSL_free(ctx->alpn_client_proto_list); + OPENSSL_free(ctx->ocsp_response); + OPENSSL_free(ctx->signed_cert_timestamp_list); + EVP_PKEY_free(ctx->tlsext_channel_id_private); + BIO_free(ctx->keylog_bio); + + OPENSSL_free(ctx); } SSL *SSL_new(SSL_CTX *ctx) { SSL *s; if (ctx == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_new, SSL_R_NULL_SSL_CTX); + OPENSSL_PUT_ERROR(SSL, SSL_R_NULL_SSL_CTX); return NULL; } if (ctx->method == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_new, SSL_R_SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION); + OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION); return NULL; } @@ -289,17 +397,8 @@ SSL *SSL_new(SSL_CTX *ctx) { CRYPTO_refcount_inc(&ctx->references); s->ctx = ctx; - s->tlsext_ticket_expected = 0; CRYPTO_refcount_inc(&ctx->references); s->initial_ctx = ctx; - if (ctx->tlsext_ecpointformatlist) { - s->tlsext_ecpointformatlist = BUF_memdup( - ctx->tlsext_ecpointformatlist, ctx->tlsext_ecpointformatlist_length); - if (!s->tlsext_ecpointformatlist) { - goto err; - } - s->tlsext_ecpointformatlist_length = ctx->tlsext_ecpointformatlist_length; - } if (ctx->tlsext_ellipticcurvelist) { s->tlsext_ellipticcurvelist = @@ -310,7 +409,6 @@ SSL *SSL_new(SSL_CTX *ctx) { } s->tlsext_ellipticcurvelist_length = ctx->tlsext_ellipticcurvelist_length; } - s->next_proto_negotiated = NULL; if (s->ctx->alpn_client_proto_list) { s->alpn_client_proto_list = BUF_memdup(s->ctx->alpn_client_proto_list, @@ -331,7 +429,6 @@ SSL *SSL_new(SSL_CTX *ctx) { assert(s->enc_method != NULL); s->rwstate = SSL_NOTHING; - s->rstate = SSL_ST_READ_HEADER; CRYPTO_new_ex_data(&g_ex_data_class_ssl, s, &s->ex_data); @@ -358,16 +455,474 @@ SSL *SSL_new(SSL_CTX *ctx) { err: SSL_free(s); - OPENSSL_PUT_ERROR(SSL, SSL_new, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); return NULL; } +void SSL_free(SSL *ssl) { + if (ssl == NULL) { + return; + } + + X509_VERIFY_PARAM_free(ssl->param); + + CRYPTO_free_ex_data(&g_ex_data_class_ssl, ssl, &ssl->ex_data); + + if (ssl->bbio != NULL) { + /* If the buffering BIO is in place, pop it off */ + if (ssl->bbio == ssl->wbio) { + ssl->wbio = BIO_pop(ssl->wbio); + } + BIO_free(ssl->bbio); + ssl->bbio = NULL; + } + + int free_wbio = ssl->wbio != ssl->rbio; + BIO_free_all(ssl->rbio); + if (free_wbio) { + BIO_free_all(ssl->wbio); + } + + BUF_MEM_free(ssl->init_buf); + + /* add extra stuff */ + ssl_cipher_preference_list_free(ssl->cipher_list); + sk_SSL_CIPHER_free(ssl->cipher_list_by_id); + + ssl_clear_bad_session(ssl); + SSL_SESSION_free(ssl->session); + + ssl_clear_cipher_ctx(ssl); + + ssl_cert_free(ssl->cert); + + OPENSSL_free(ssl->tlsext_hostname); + SSL_CTX_free(ssl->initial_ctx); + OPENSSL_free(ssl->tlsext_ellipticcurvelist); + OPENSSL_free(ssl->alpn_client_proto_list); + EVP_PKEY_free(ssl->tlsext_channel_id_private); + OPENSSL_free(ssl->psk_identity_hint); + sk_X509_NAME_pop_free(ssl->client_CA, X509_NAME_free); + OPENSSL_free(ssl->next_proto_negotiated); + sk_SRTP_PROTECTION_PROFILE_free(ssl->srtp_profiles); + + if (ssl->method != NULL) { + ssl->method->ssl_free(ssl); + } + SSL_CTX_free(ssl->ctx); + + OPENSSL_free(ssl); +} + +void SSL_set_connect_state(SSL *ssl) { + ssl->server = 0; + ssl->shutdown = 0; + ssl->state = SSL_ST_CONNECT; + ssl->handshake_func = ssl->method->ssl_connect; + /* clear the current cipher */ + ssl_clear_cipher_ctx(ssl); +} + +void SSL_set_accept_state(SSL *ssl) { + ssl->server = 1; + ssl->shutdown = 0; + ssl->state = SSL_ST_ACCEPT; + ssl->handshake_func = ssl->method->ssl_accept; + /* clear the current cipher */ + ssl_clear_cipher_ctx(ssl); +} + +void SSL_set_bio(SSL *ssl, BIO *rbio, BIO *wbio) { + /* If the output buffering BIO is still in place, remove it. */ + if (ssl->bbio != NULL) { + if (ssl->wbio == ssl->bbio) { + ssl->wbio = ssl->wbio->next_bio; + ssl->bbio->next_bio = NULL; + } + } + + if (ssl->rbio != rbio) { + BIO_free_all(ssl->rbio); + } + if (ssl->wbio != wbio && ssl->rbio != ssl->wbio) { + BIO_free_all(ssl->wbio); + } + ssl->rbio = rbio; + ssl->wbio = wbio; +} + +BIO *SSL_get_rbio(const SSL *ssl) { return ssl->rbio; } + +BIO *SSL_get_wbio(const SSL *ssl) { return ssl->wbio; } + +int SSL_do_handshake(SSL *ssl) { + if (ssl->handshake_func == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_CONNECTION_TYPE_NOT_SET); + return -1; + } + + if (!SSL_in_init(ssl)) { + return 1; + } + + return ssl->handshake_func(ssl); +} + +int SSL_connect(SSL *ssl) { + if (ssl->handshake_func == 0) { + /* Not properly initialized yet */ + SSL_set_connect_state(ssl); + } + + if (ssl->handshake_func != ssl->method->ssl_connect) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return -1; + } + + return ssl->handshake_func(ssl); +} + +int SSL_accept(SSL *ssl) { + if (ssl->handshake_func == 0) { + /* Not properly initialized yet */ + SSL_set_accept_state(ssl); + } + + if (ssl->handshake_func != ssl->method->ssl_accept) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return -1; + } + + return ssl->handshake_func(ssl); +} + +int SSL_read(SSL *ssl, void *buf, int num) { + if (ssl->handshake_func == 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNINITIALIZED); + return -1; + } + + if (ssl->shutdown & SSL_RECEIVED_SHUTDOWN) { + ssl->rwstate = SSL_NOTHING; + return 0; + } + + ERR_clear_system_error(); + return ssl->method->ssl_read_app_data(ssl, buf, num, 0); +} + +int SSL_peek(SSL *ssl, void *buf, int num) { + if (ssl->handshake_func == 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNINITIALIZED); + return -1; + } + + if (ssl->shutdown & SSL_RECEIVED_SHUTDOWN) { + return 0; + } + + ERR_clear_system_error(); + return ssl->method->ssl_read_app_data(ssl, buf, num, 1); +} + +int SSL_write(SSL *ssl, const void *buf, int num) { + if (ssl->handshake_func == 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNINITIALIZED); + return -1; + } + + if (ssl->shutdown & SSL_SENT_SHUTDOWN) { + ssl->rwstate = SSL_NOTHING; + OPENSSL_PUT_ERROR(SSL, SSL_R_PROTOCOL_IS_SHUTDOWN); + return -1; + } + + ERR_clear_system_error(); + return ssl->method->ssl_write_app_data(ssl, buf, num); +} + +int SSL_shutdown(SSL *ssl) { + /* Note that this function behaves differently from what one might expect. + * Return values are 0 for no success (yet), 1 for success; but calling it + * once is usually not enough, even if blocking I/O is used (see + * ssl3_shutdown). */ + + if (ssl->handshake_func == 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNINITIALIZED); + return -1; + } + + if (SSL_in_init(ssl)) { + return 1; + } + + /* Do nothing if configured not to send a close_notify. */ + if (ssl->quiet_shutdown) { + ssl->shutdown = SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN; + return 1; + } + + if (!(ssl->shutdown & SSL_SENT_SHUTDOWN)) { + ssl->shutdown |= SSL_SENT_SHUTDOWN; + ssl3_send_alert(ssl, SSL3_AL_WARNING, SSL_AD_CLOSE_NOTIFY); + + /* our shutdown alert has been sent now, and if it still needs to be + * written, ssl->s3->alert_dispatch will be true */ + if (ssl->s3->alert_dispatch) { + return -1; /* return WANT_WRITE */ + } + } else if (ssl->s3->alert_dispatch) { + /* resend it if not sent */ + int ret = ssl->method->ssl_dispatch_alert(ssl); + if (ret == -1) { + /* we only get to return -1 here the 2nd/Nth invocation, we must have + * already signalled return 0 upon a previous invoation, return + * WANT_WRITE */ + return ret; + } + } else if (!(ssl->shutdown & SSL_RECEIVED_SHUTDOWN)) { + /* If we are waiting for a close from our peer, we are closed */ + ssl->method->ssl_read_close_notify(ssl); + if (!(ssl->shutdown & SSL_RECEIVED_SHUTDOWN)) { + return -1; /* return WANT_READ */ + } + } + + if (ssl->shutdown == (SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN) && + !ssl->s3->alert_dispatch) { + return 1; + } else { + return 0; + } +} + +int SSL_get_error(const SSL *ssl, int ret_code) { + int reason; + uint32_t err; + BIO *bio; + + if (ret_code > 0) { + return SSL_ERROR_NONE; + } + + /* Make things return SSL_ERROR_SYSCALL when doing SSL_do_handshake etc, + * where we do encode the error */ + err = ERR_peek_error(); + if (err != 0) { + if (ERR_GET_LIB(err) == ERR_LIB_SYS) { + return SSL_ERROR_SYSCALL; + } + return SSL_ERROR_SSL; + } + + if (ret_code == 0) { + if ((ssl->shutdown & SSL_RECEIVED_SHUTDOWN) && + (ssl->s3->warn_alert == SSL_AD_CLOSE_NOTIFY)) { + /* The socket was cleanly shut down with a close_notify. */ + return SSL_ERROR_ZERO_RETURN; + } + /* An EOF was observed which violates the protocol, and the underlying + * transport does not participate in the error queue. Bubble up to the + * caller. */ + return SSL_ERROR_SYSCALL; + } + + if (SSL_want_session(ssl)) { + return SSL_ERROR_PENDING_SESSION; + } + + if (SSL_want_certificate(ssl)) { + return SSL_ERROR_PENDING_CERTIFICATE; + } + + if (SSL_want_read(ssl)) { + bio = SSL_get_rbio(ssl); + if (BIO_should_read(bio)) { + return SSL_ERROR_WANT_READ; + } + + if (BIO_should_write(bio)) { + /* This one doesn't make too much sense ... We never try to write to the + * rbio, and an application program where rbio and wbio are separate + * couldn't even know what it should wait for. However if we ever set + * s->rwstate incorrectly (so that we have SSL_want_read(s) instead of + * SSL_want_write(s)) and rbio and wbio *are* the same, this test works + * around that bug; so it might be safer to keep it. */ + return SSL_ERROR_WANT_WRITE; + } + + if (BIO_should_io_special(bio)) { + reason = BIO_get_retry_reason(bio); + if (reason == BIO_RR_CONNECT) { + return SSL_ERROR_WANT_CONNECT; + } + + if (reason == BIO_RR_ACCEPT) { + return SSL_ERROR_WANT_ACCEPT; + } + + return SSL_ERROR_SYSCALL; /* unknown */ + } + } + + if (SSL_want_write(ssl)) { + bio = SSL_get_wbio(ssl); + if (BIO_should_write(bio)) { + return SSL_ERROR_WANT_WRITE; + } + + if (BIO_should_read(bio)) { + /* See above (SSL_want_read(ssl) with BIO_should_write(bio)) */ + return SSL_ERROR_WANT_READ; + } + + if (BIO_should_io_special(bio)) { + reason = BIO_get_retry_reason(bio); + if (reason == BIO_RR_CONNECT) { + return SSL_ERROR_WANT_CONNECT; + } + + if (reason == BIO_RR_ACCEPT) { + return SSL_ERROR_WANT_ACCEPT; + } + + return SSL_ERROR_SYSCALL; + } + } + + if (SSL_want_x509_lookup(ssl)) { + return SSL_ERROR_WANT_X509_LOOKUP; + } + + if (SSL_want_channel_id_lookup(ssl)) { + return SSL_ERROR_WANT_CHANNEL_ID_LOOKUP; + } + + if (SSL_want_private_key_operation(ssl)) { + return SSL_ERROR_WANT_PRIVATE_KEY_OPERATION; + } + + return SSL_ERROR_SYSCALL; +} + +void SSL_CTX_set_min_version(SSL_CTX *ctx, uint16_t version) { + ctx->min_version = version; +} + +void SSL_CTX_set_max_version(SSL_CTX *ctx, uint16_t version) { + ctx->max_version = version; +} + +void SSL_set_min_version(SSL *ssl, uint16_t version) { + ssl->min_version = version; +} + +void SSL_set_max_version(SSL *ssl, uint16_t version) { + ssl->max_version = version; +} + +uint32_t SSL_CTX_set_options(SSL_CTX *ctx, uint32_t options) { + ctx->options |= options; + return ctx->options; +} + +uint32_t SSL_CTX_clear_options(SSL_CTX *ctx, uint32_t options) { + ctx->options &= ~options; + return ctx->options; +} + +uint32_t SSL_CTX_get_options(const SSL_CTX *ctx) { return ctx->options; } + +uint32_t SSL_set_options(SSL *ssl, uint32_t options) { + ssl->options |= options; + return ssl->options; +} + +uint32_t SSL_clear_options(SSL *ssl, uint32_t options) { + ssl->options &= ~options; + return ssl->options; +} + +uint32_t SSL_get_options(const SSL *ssl) { return ssl->options; } + +uint32_t SSL_CTX_set_mode(SSL_CTX *ctx, uint32_t mode) { + ctx->mode |= mode; + return ctx->mode; +} + +uint32_t SSL_CTX_clear_mode(SSL_CTX *ctx, uint32_t mode) { + ctx->mode &= ~mode; + return ctx->mode; +} + +uint32_t SSL_CTX_get_mode(const SSL_CTX *ctx) { return ctx->mode; } + +uint32_t SSL_set_mode(SSL *ssl, uint32_t mode) { + ssl->mode |= mode; + return ssl->mode; +} + +uint32_t SSL_clear_mode(SSL *ssl, uint32_t mode) { + ssl->mode &= ~mode; + return ssl->mode; +} + +uint32_t SSL_get_mode(const SSL *ssl) { return ssl->mode; } + +X509 *SSL_get_peer_certificate(const SSL *ssl) { + if (ssl == NULL || ssl->session == NULL || ssl->session->peer == NULL) { + return NULL; + } + return X509_up_ref(ssl->session->peer); +} + +STACK_OF(X509) *SSL_get_peer_cert_chain(const SSL *ssl) { + if (ssl == NULL || ssl->session == NULL) { + return NULL; + } + return ssl->session->cert_chain; +} + +int SSL_get_tls_unique(const SSL *ssl, uint8_t *out, size_t *out_len, + size_t max_out) { + /* The tls-unique value is the first Finished message in the handshake, which + * is the client's in a full handshake and the server's for a resumption. See + * https://tools.ietf.org/html/rfc5929#section-3.1. */ + const uint8_t *finished = ssl->s3->previous_client_finished; + size_t finished_len = ssl->s3->previous_client_finished_len; + if (ssl->hit) { + /* tls-unique is broken for resumed sessions unless EMS is used. */ + if (!ssl->session->extended_master_secret) { + goto err; + } + finished = ssl->s3->previous_server_finished; + finished_len = ssl->s3->previous_server_finished_len; + } + + if (!ssl->s3->initial_handshake_complete || + ssl->version < TLS1_VERSION) { + goto err; + } + + *out_len = finished_len; + if (finished_len > max_out) { + *out_len = max_out; + } + + memcpy(out, finished, *out_len); + return 1; + +err: + *out_len = 0; + memset(out, 0, max_out); + return 0; +} + int SSL_CTX_set_session_id_context(SSL_CTX *ctx, const uint8_t *sid_ctx, - unsigned int sid_ctx_len) { - if (sid_ctx_len > sizeof ctx->sid_ctx) { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_set_session_id_context, - SSL_R_SSL_SESSION_ID_CONTEXT_TOO_LONG); + unsigned sid_ctx_len) { + if (sid_ctx_len > sizeof(ctx->sid_ctx)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_SESSION_ID_CONTEXT_TOO_LONG); return 0; } ctx->sid_ctx_length = sid_ctx_len; @@ -377,10 +932,9 @@ int SSL_CTX_set_session_id_context(SSL_CTX *ctx, const uint8_t *sid_ctx, } int SSL_set_session_id_context(SSL *ssl, const uint8_t *sid_ctx, - unsigned int sid_ctx_len) { + unsigned sid_ctx_len) { if (sid_ctx_len > SSL_MAX_SID_CTX_LENGTH) { - OPENSSL_PUT_ERROR(SSL, SSL_set_session_id_context, - SSL_R_SSL_SESSION_ID_CONTEXT_TOO_LONG); + OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_SESSION_ID_CONTEXT_TOO_LONG); return 0; } ssl->sid_ctx_length = sid_ctx_len; @@ -400,7 +954,7 @@ int SSL_set_generate_session_id(SSL *ssl, GEN_SESSION_CB cb) { } int SSL_has_matching_session_id(const SSL *ssl, const uint8_t *id, - unsigned int id_len) { + unsigned id_len) { /* A quick examination of SSL_SESSION_hash and SSL_SESSION_cmp shows how we * can "construct" a session to give us the desired check - ie. to find if * there's a session in the hash table that would conflict with any new @@ -422,28 +976,28 @@ int SSL_has_matching_session_id(const SSL *ssl, const uint8_t *id, return p != NULL; } -int SSL_CTX_set_purpose(SSL_CTX *s, int purpose) { - return X509_VERIFY_PARAM_set_purpose(s->param, purpose); +int SSL_CTX_set_purpose(SSL_CTX *ctx, int purpose) { + return X509_VERIFY_PARAM_set_purpose(ctx->param, purpose); } -int SSL_set_purpose(SSL *s, int purpose) { - return X509_VERIFY_PARAM_set_purpose(s->param, purpose); +int SSL_set_purpose(SSL *ssl, int purpose) { + return X509_VERIFY_PARAM_set_purpose(ssl->param, purpose); } -int SSL_CTX_set_trust(SSL_CTX *s, int trust) { - return X509_VERIFY_PARAM_set_trust(s->param, trust); +int SSL_CTX_set_trust(SSL_CTX *ctx, int trust) { + return X509_VERIFY_PARAM_set_trust(ctx->param, trust); } -int SSL_set_trust(SSL *s, int trust) { - return X509_VERIFY_PARAM_set_trust(s->param, trust); +int SSL_set_trust(SSL *ssl, int trust) { + return X509_VERIFY_PARAM_set_trust(ssl->param, trust); } -int SSL_CTX_set1_param(SSL_CTX *ctx, X509_VERIFY_PARAM *vpm) { - return X509_VERIFY_PARAM_set1(ctx->param, vpm); +int SSL_CTX_set1_param(SSL_CTX *ctx, const X509_VERIFY_PARAM *param) { + return X509_VERIFY_PARAM_set1(ctx->param, param); } -int SSL_set1_param(SSL *ssl, X509_VERIFY_PARAM *vpm) { - return X509_VERIFY_PARAM_set1(ssl->param, vpm); +int SSL_set1_param(SSL *ssl, const X509_VERIFY_PARAM *param) { + return X509_VERIFY_PARAM_set1(ssl->param, param); } void ssl_cipher_preference_list_free( @@ -515,86 +1069,7 @@ X509_VERIFY_PARAM *SSL_CTX_get0_param(SSL_CTX *ctx) { return ctx->param; } X509_VERIFY_PARAM *SSL_get0_param(SSL *ssl) { return ssl->param; } -void SSL_certs_clear(SSL *s) { ssl_cert_clear_certs(s->cert); } - -void SSL_free(SSL *ssl) { - if (ssl == NULL) { - return; - } - - X509_VERIFY_PARAM_free(ssl->param); - - CRYPTO_free_ex_data(&g_ex_data_class_ssl, ssl, &ssl->ex_data); - - if (ssl->bbio != NULL) { - /* If the buffering BIO is in place, pop it off */ - if (ssl->bbio == ssl->wbio) { - ssl->wbio = BIO_pop(ssl->wbio); - } - BIO_free(ssl->bbio); - ssl->bbio = NULL; - } - - int free_wbio = ssl->wbio != ssl->rbio; - BIO_free_all(ssl->rbio); - if (free_wbio) { - BIO_free_all(ssl->wbio); - } - - BUF_MEM_free(ssl->init_buf); - - /* add extra stuff */ - ssl_cipher_preference_list_free(ssl->cipher_list); - sk_SSL_CIPHER_free(ssl->cipher_list_by_id); - - ssl_clear_bad_session(ssl); - SSL_SESSION_free(ssl->session); - - ssl_clear_cipher_ctx(ssl); - - ssl_cert_free(ssl->cert); - - OPENSSL_free(ssl->tlsext_hostname); - SSL_CTX_free(ssl->initial_ctx); - OPENSSL_free(ssl->tlsext_ecpointformatlist); - OPENSSL_free(ssl->tlsext_ellipticcurvelist); - OPENSSL_free(ssl->alpn_client_proto_list); - EVP_PKEY_free(ssl->tlsext_channel_id_private); - OPENSSL_free(ssl->psk_identity_hint); - sk_X509_NAME_pop_free(ssl->client_CA, X509_NAME_free); - OPENSSL_free(ssl->next_proto_negotiated); - sk_SRTP_PROTECTION_PROFILE_free(ssl->srtp_profiles); - - if (ssl->method != NULL) { - ssl->method->ssl_free(ssl); - } - SSL_CTX_free(ssl->ctx); - - OPENSSL_free(ssl); -} - -void SSL_set_bio(SSL *s, BIO *rbio, BIO *wbio) { - /* If the output buffering BIO is still in place, remove it. */ - if (s->bbio != NULL) { - if (s->wbio == s->bbio) { - s->wbio = s->wbio->next_bio; - s->bbio->next_bio = NULL; - } - } - - if (s->rbio != rbio) { - BIO_free_all(s->rbio); - } - if (s->wbio != wbio && s->rbio != s->wbio) { - BIO_free_all(s->wbio); - } - s->rbio = rbio; - s->wbio = wbio; -} - -BIO *SSL_get_rbio(const SSL *s) { return s->rbio; } - -BIO *SSL_get_wbio(const SSL *s) { return s->wbio; } +void SSL_certs_clear(SSL *ssl) { ssl_cert_clear_certs(ssl->cert); } int SSL_get_fd(const SSL *s) { return SSL_get_rfd(s); } @@ -630,7 +1105,7 @@ int SSL_set_fd(SSL *s, int fd) { bio = BIO_new(BIO_s_fd()); if (bio == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_set_fd, ERR_R_BUF_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB); goto err; } BIO_set_fd(bio, fd, BIO_NOCLOSE); @@ -650,7 +1125,7 @@ int SSL_set_wfd(SSL *s, int fd) { bio = BIO_new(BIO_s_fd()); if (bio == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_set_wfd, ERR_R_BUF_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB); goto err; } BIO_set_fd(bio, fd, BIO_NOCLOSE); @@ -674,7 +1149,7 @@ int SSL_set_rfd(SSL *s, int fd) { bio = BIO_new(BIO_s_fd()); if (bio == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_set_rfd, ERR_R_BUF_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB); goto err; } BIO_set_fd(bio, fd, BIO_NOCLOSE); @@ -718,14 +1193,18 @@ size_t SSL_get_peer_finished(const SSL *s, void *buf, size_t count) { return ret; } -int SSL_get_verify_mode(const SSL *s) { return s->verify_mode; } +int SSL_get_verify_mode(const SSL *ssl) { return ssl->verify_mode; } + +int SSL_get_verify_depth(const SSL *ssl) { + return X509_VERIFY_PARAM_get_depth(ssl->param); +} -int SSL_get_verify_depth(const SSL *s) { - return X509_VERIFY_PARAM_get_depth(s->param); +int SSL_get_extms_support(const SSL *ssl) { + return ssl->s3->tmp.extended_master_secret == 1; } -int (*SSL_get_verify_callback(const SSL *s))(int, X509_STORE_CTX *) { - return s->verify_callback; +int (*SSL_get_verify_callback(const SSL *ssl))(int, X509_STORE_CTX *) { + return ssl->verify_callback; } int SSL_CTX_get_verify_mode(const SSL_CTX *ctx) { return ctx->verify_mode; } @@ -734,20 +1213,21 @@ int SSL_CTX_get_verify_depth(const SSL_CTX *ctx) { return X509_VERIFY_PARAM_get_depth(ctx->param); } -int (*SSL_CTX_get_verify_callback(const SSL_CTX *ctx))(int, X509_STORE_CTX *) { +int (*SSL_CTX_get_verify_callback(const SSL_CTX *ctx))( + int ok, X509_STORE_CTX *store_ctx) { return ctx->default_verify_callback; } -void SSL_set_verify(SSL *s, int mode, - int (*callback)(int ok, X509_STORE_CTX *ctx)) { - s->verify_mode = mode; +void SSL_set_verify(SSL *ssl, int mode, + int (*callback)(int ok, X509_STORE_CTX *store_ctx)) { + ssl->verify_mode = mode; if (callback != NULL) { - s->verify_callback = callback; + ssl->verify_callback = callback; } } -void SSL_set_verify_depth(SSL *s, int depth) { - X509_VERIFY_PARAM_set_depth(s->param, depth); +void SSL_set_verify_depth(SSL *ssl, int depth) { + X509_VERIFY_PARAM_set_depth(ssl->param, depth); } int SSL_CTX_get_read_ahead(const SSL_CTX *ctx) { return 0; } @@ -759,226 +1239,47 @@ void SSL_CTX_set_read_ahead(SSL_CTX *ctx, int yes) { } void SSL_set_read_ahead(SSL *s, int yes) { } int SSL_pending(const SSL *s) { - if (s->rstate == SSL_ST_READ_BODY) { - return 0; - } - return (s->s3->rrec.type == SSL3_RT_APPLICATION_DATA) ? s->s3->rrec.length : 0; } -X509 *SSL_get_peer_certificate(const SSL *s) { - X509 *r; - - if (s == NULL || s->session == NULL) { - r = NULL; - } else { - r = s->session->peer; - } - - if (r == NULL) { - return NULL; - } - - return X509_up_ref(r); -} - -STACK_OF(X509) *SSL_get_peer_cert_chain(const SSL *s) { - STACK_OF(X509) *r; - - if (s == NULL || s->session == NULL || s->session->sess_cert == NULL) { - r = NULL; - } else { - r = s->session->sess_cert->cert_chain; - } - - /* If we are a client, cert_chain includes the peer's own certificate; if we - * are a server, it does not. */ - return r; -} - /* Fix this so it checks all the valid key/cert options */ int SSL_CTX_check_private_key(const SSL_CTX *ctx) { - if (ctx == NULL || ctx->cert == NULL || ctx->cert->key->x509 == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_check_private_key, - SSL_R_NO_CERTIFICATE_ASSIGNED); + if (ctx->cert->x509 == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_ASSIGNED); return 0; } - if (ctx->cert->key->privatekey == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_check_private_key, - SSL_R_NO_PRIVATE_KEY_ASSIGNED); + if (ctx->cert->privatekey == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_PRIVATE_KEY_ASSIGNED); return 0; } - return X509_check_private_key(ctx->cert->key->x509, - ctx->cert->key->privatekey); + return X509_check_private_key(ctx->cert->x509, ctx->cert->privatekey); } /* Fix this function so that it takes an optional type parameter */ int SSL_check_private_key(const SSL *ssl) { - if (ssl == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_check_private_key, ERR_R_PASSED_NULL_PARAMETER); - return 0; - } - - if (ssl->cert == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_check_private_key, - SSL_R_NO_CERTIFICATE_ASSIGNED); - return 0; - } - - if (ssl->cert->key->x509 == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_check_private_key, - SSL_R_NO_CERTIFICATE_ASSIGNED); + if (ssl->cert->x509 == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_ASSIGNED); return 0; } - if (ssl->cert->key->privatekey == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_check_private_key, - SSL_R_NO_PRIVATE_KEY_ASSIGNED); + if (ssl->cert->privatekey == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_PRIVATE_KEY_ASSIGNED); return 0; } - return X509_check_private_key(ssl->cert->key->x509, - ssl->cert->key->privatekey); -} - -int SSL_accept(SSL *s) { - if (s->handshake_func == 0) { - /* Not properly initialized yet */ - SSL_set_accept_state(s); - } - - if (s->handshake_func != s->method->ssl_accept) { - OPENSSL_PUT_ERROR(SSL, SSL_accept, ERR_R_INTERNAL_ERROR); - return -1; - } - - return s->handshake_func(s); -} - -int SSL_connect(SSL *s) { - if (s->handshake_func == 0) { - /* Not properly initialized yet */ - SSL_set_connect_state(s); - } - - if (s->handshake_func != s->method->ssl_connect) { - OPENSSL_PUT_ERROR(SSL, SSL_connect, ERR_R_INTERNAL_ERROR); - return -1; - } - - return s->handshake_func(s); + return X509_check_private_key(ssl->cert->x509, ssl->cert->privatekey); } -long SSL_get_default_timeout(const SSL *s) { +long SSL_get_default_timeout(const SSL *ssl) { return SSL_DEFAULT_SESSION_TIMEOUT; } -int SSL_read(SSL *s, void *buf, int num) { - if (s->handshake_func == 0) { - OPENSSL_PUT_ERROR(SSL, SSL_read, SSL_R_UNINITIALIZED); - return -1; - } - - if (s->shutdown & SSL_RECEIVED_SHUTDOWN) { - s->rwstate = SSL_NOTHING; - return 0; - } - - ERR_clear_system_error(); - return s->method->ssl_read_app_data(s, buf, num, 0); -} - -int SSL_peek(SSL *s, void *buf, int num) { - if (s->handshake_func == 0) { - OPENSSL_PUT_ERROR(SSL, SSL_peek, SSL_R_UNINITIALIZED); - return -1; - } - - if (s->shutdown & SSL_RECEIVED_SHUTDOWN) { - return 0; - } - - ERR_clear_system_error(); - return s->method->ssl_read_app_data(s, buf, num, 1); -} - -int SSL_write(SSL *s, const void *buf, int num) { - if (s->handshake_func == 0) { - OPENSSL_PUT_ERROR(SSL, SSL_write, SSL_R_UNINITIALIZED); - return -1; - } - - if (s->shutdown & SSL_SENT_SHUTDOWN) { - s->rwstate = SSL_NOTHING; - OPENSSL_PUT_ERROR(SSL, SSL_write, SSL_R_PROTOCOL_IS_SHUTDOWN); - return -1; - } - - ERR_clear_system_error(); - return s->method->ssl_write_app_data(s, buf, num); -} - -int SSL_shutdown(SSL *s) { - /* Note that this function behaves differently from what one might expect. - * Return values are 0 for no success (yet), 1 for success; but calling it - * once is usually not enough, even if blocking I/O is used (see - * ssl3_shutdown). */ - - if (s->handshake_func == 0) { - OPENSSL_PUT_ERROR(SSL, SSL_shutdown, SSL_R_UNINITIALIZED); - return -1; - } - - if (SSL_in_init(s)) { - return 1; - } - - /* Do nothing if configured not to send a close_notify. */ - if (s->quiet_shutdown) { - s->shutdown = SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN; - return 1; - } - - if (!(s->shutdown & SSL_SENT_SHUTDOWN)) { - s->shutdown |= SSL_SENT_SHUTDOWN; - ssl3_send_alert(s, SSL3_AL_WARNING, SSL_AD_CLOSE_NOTIFY); - - /* our shutdown alert has been sent now, and if it still needs to be - * written, s->s3->alert_dispatch will be true */ - if (s->s3->alert_dispatch) { - return -1; /* return WANT_WRITE */ - } - } else if (s->s3->alert_dispatch) { - /* resend it if not sent */ - int ret = s->method->ssl_dispatch_alert(s); - if (ret == -1) { - /* we only get to return -1 here the 2nd/Nth invocation, we must have - * already signalled return 0 upon a previous invoation, return - * WANT_WRITE */ - return ret; - } - } else if (!(s->shutdown & SSL_RECEIVED_SHUTDOWN)) { - /* If we are waiting for a close from our peer, we are closed */ - s->method->ssl_read_close_notify(s); - if (!(s->shutdown & SSL_RECEIVED_SHUTDOWN)) { - return -1; /* return WANT_READ */ - } - } - - if (s->shutdown == (SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN) && - !s->s3->alert_dispatch) { - return 1; - } else { - return 0; - } -} - int SSL_renegotiate(SSL *ssl) { /* Caller-initiated renegotiation is not supported. */ - OPENSSL_PUT_ERROR(SSL, SSL_renegotiate, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); return 0; } @@ -986,54 +1287,6 @@ int SSL_renegotiate_pending(SSL *ssl) { return SSL_in_init(ssl) && ssl->s3->initial_handshake_complete; } -uint32_t SSL_CTX_set_options(SSL_CTX *ctx, uint32_t options) { - ctx->options |= options; - return ctx->options; -} - -uint32_t SSL_set_options(SSL *ssl, uint32_t options) { - ssl->options |= options; - return ssl->options; -} - -uint32_t SSL_CTX_clear_options(SSL_CTX *ctx, uint32_t options) { - ctx->options &= ~options; - return ctx->options; -} - -uint32_t SSL_clear_options(SSL *ssl, uint32_t options) { - ssl->options &= ~options; - return ssl->options; -} - -uint32_t SSL_CTX_get_options(const SSL_CTX *ctx) { return ctx->options; } - -uint32_t SSL_get_options(const SSL *ssl) { return ssl->options; } - -uint32_t SSL_CTX_set_mode(SSL_CTX *ctx, uint32_t mode) { - ctx->mode |= mode; - return ctx->mode; -} - -uint32_t SSL_set_mode(SSL *ssl, uint32_t mode) { - ssl->mode |= mode; - return ssl->mode; -} - -uint32_t SSL_CTX_clear_mode(SSL_CTX *ctx, uint32_t mode) { - ctx->mode &= ~mode; - return ctx->mode; -} - -uint32_t SSL_clear_mode(SSL *ssl, uint32_t mode) { - ssl->mode &= ~mode; - return ssl->mode; -} - -uint32_t SSL_CTX_get_mode(const SSL_CTX *ctx) { return ctx->mode; } - -uint32_t SSL_get_mode(const SSL *ssl) { return ssl->mode; } - size_t SSL_CTX_get_max_cert_list(const SSL_CTX *ctx) { return ctx->max_cert_list; } @@ -1088,10 +1341,6 @@ int SSL_get_secure_renegotiation_support(const SSL *ssl) { return ssl->s3->send_connection_binding; } -long SSL_ctrl(SSL *s, int cmd, long larg, void *parg) { - return s->method->ssl_ctrl(s, cmd, larg, parg); -} - LHASH_OF(SSL_SESSION) *SSL_CTX_sessions(SSL_CTX *ctx) { return ctx->sessions; } size_t SSL_CTX_sess_number(const SSL_CTX *ctx) { @@ -1118,10 +1367,6 @@ int SSL_CTX_get_session_cache_mode(const SSL_CTX *ctx) { return ctx->session_cache_mode; } -long SSL_CTX_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg) { - return ctx->method->ssl_ctx_ctrl(ctx, cmd, larg, parg); -} - /* return a STACK of the ciphers available for the SSL and in order of * preference */ STACK_OF(SSL_CIPHER) *SSL_get_ciphers(const SSL *s) { @@ -1138,6 +1383,11 @@ STACK_OF(SSL_CIPHER) *SSL_get_ciphers(const SSL *s) { return s->ctx->cipher_list_tls11->ciphers; } + if (s->version >= TLS1_VERSION && s->ctx != NULL && + s->ctx->cipher_list_tls10 != NULL) { + return s->ctx->cipher_list_tls10->ciphers; + } + if (s->ctx != NULL && s->ctx->cipher_list != NULL) { return s->ctx->cipher_list->ciphers; } @@ -1199,7 +1449,21 @@ int SSL_CTX_set_cipher_list(SSL_CTX *ctx, const char *str) { if (sk == NULL) { return 0; } else if (sk_SSL_CIPHER_num(sk) == 0) { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_set_cipher_list, SSL_R_NO_CIPHER_MATCH); + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CIPHER_MATCH); + return 0; + } + + return 1; +} + +int SSL_CTX_set_cipher_list_tls10(SSL_CTX *ctx, const char *str) { + STACK_OF(SSL_CIPHER) *sk; + + sk = ssl_create_cipher_list(ctx->method, &ctx->cipher_list_tls10, NULL, str); + if (sk == NULL) { + return 0; + } else if (sk_SSL_CIPHER_num(sk) == 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CIPHER_MATCH); return 0; } @@ -1213,8 +1477,7 @@ int SSL_CTX_set_cipher_list_tls11(SSL_CTX *ctx, const char *str) { if (sk == NULL) { return 0; } else if (sk_SSL_CIPHER_num(sk) == 0) { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_set_cipher_list_tls11, - SSL_R_NO_CIPHER_MATCH); + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CIPHER_MATCH); return 0; } @@ -1232,7 +1495,7 @@ int SSL_set_cipher_list(SSL *s, const char *str) { if (sk == NULL) { return 0; } else if (sk_SSL_CIPHER_num(sk) == 0) { - OPENSSL_PUT_ERROR(SSL, SSL_set_cipher_list, SSL_R_NO_CIPHER_MATCH); + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CIPHER_MATCH); return 0; } @@ -1268,9 +1531,13 @@ int ssl_cipher_list_to_bytes(SSL *s, STACK_OF(SSL_CIPHER) *sk, uint8_t *p) { return 0; } - /* Add SCSVs. */ - if (!s->s3->initial_handshake_complete) { + /* For SSLv3, the SCSV is added. Otherwise the renegotiation extension is + * added. */ + if (s->client_version == SSL3_VERSION && + !s->s3->initial_handshake_complete) { s2n(SSL3_CK_SCSV & 0xffff, p); + /* The renegotiation extension is required to be at index zero. */ + s->s3->tmp.extensions.sent |= (1u << 0); } if (s->mode & SSL_MODE_SEND_FALLBACK_SCSV) { @@ -1290,14 +1557,13 @@ STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s, const CBS *cbs) { } if (CBS_len(&cipher_suites) % 2 != 0) { - OPENSSL_PUT_ERROR(SSL, ssl_bytes_to_cipher_list, - SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST); + OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST); return NULL; } sk = sk_SSL_CIPHER_new_null(); if (sk == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl_bytes_to_cipher_list, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } @@ -1305,7 +1571,7 @@ STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s, const CBS *cbs) { uint16_t cipher_suite; if (!CBS_get_u16(&cipher_suites, &cipher_suite)) { - OPENSSL_PUT_ERROR(SSL, ssl_bytes_to_cipher_list, ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); goto err; } @@ -1313,8 +1579,7 @@ STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s, const CBS *cbs) { if (s->s3 && cipher_suite == (SSL3_CK_SCSV & 0xffff)) { /* SCSV is fatal if renegotiating. */ if (s->s3->initial_handshake_complete) { - OPENSSL_PUT_ERROR(SSL, ssl_bytes_to_cipher_list, - SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING); + OPENSSL_PUT_ERROR(SSL, SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING); ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE); goto err; } @@ -1327,8 +1592,7 @@ STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s, const CBS *cbs) { uint16_t max_version = ssl3_get_max_server_version(s); if (SSL_IS_DTLS(s) ? (uint16_t)s->version > max_version : (uint16_t)s->version < max_version) { - OPENSSL_PUT_ERROR(SSL, ssl_bytes_to_cipher_list, - SSL_R_INAPPROPRIATE_FALLBACK); + OPENSSL_PUT_ERROR(SSL, SSL_R_INAPPROPRIATE_FALLBACK); ssl3_send_alert(s, SSL3_AL_FATAL, SSL3_AD_INAPPROPRIATE_FALLBACK); goto err; } @@ -1337,7 +1601,7 @@ STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s, const CBS *cbs) { c = SSL_get_cipher_by_value(cipher_suite); if (c != NULL && !sk_SSL_CIPHER_push(sk, c)) { - OPENSSL_PUT_ERROR(SSL, ssl_bytes_to_cipher_list, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } } @@ -1415,39 +1679,37 @@ void SSL_get0_ocsp_response(const SSL *ssl, const uint8_t **out, *out_len = session->ocsp_response_length; } -/* SSL_select_next_proto implements the standard protocol selection. It is - * expected that this function is called from the callback set by - * SSL_CTX_set_next_proto_select_cb. - * - * The protocol data is assumed to be a vector of 8-bit, length prefixed byte - * strings. The length byte itself is not included in the length. A byte - * string of length 0 is invalid. No byte string may be truncated. - * - * The current, but experimental algorithm for selecting the protocol is: - * - * 1) If the server doesn't support NPN then this is indicated to the - * callback. In this case, the client application has to abort the connection - * or have a default application level protocol. - * - * 2) If the server supports NPN, but advertises an empty list then the - * client selects the first protcol in its list, but indicates via the - * API that this fallback case was enacted. - * - * 3) Otherwise, the client finds the first protocol in the server's list - * that it supports and selects this protocol. This is because it's - * assumed that the server has better information about which protocol - * a client should use. - * - * 4) If the client doesn't support any of the server's advertised - * protocols, then this is treated the same as case 2. - * - * It returns either - * OPENSSL_NPN_NEGOTIATED if a common protocol was found, or - * OPENSSL_NPN_NO_OVERLAP if the fallback case was reached. - */ -int SSL_select_next_proto(uint8_t **out, uint8_t *outlen, const uint8_t *server, - unsigned int server_len, const uint8_t *client, - unsigned int client_len) { +int SSL_CTX_set_signed_cert_timestamp_list(SSL_CTX *ctx, const uint8_t *list, + size_t list_len) { + OPENSSL_free(ctx->signed_cert_timestamp_list); + ctx->signed_cert_timestamp_list_length = 0; + + ctx->signed_cert_timestamp_list = BUF_memdup(list, list_len); + if (ctx->signed_cert_timestamp_list == NULL) { + return 0; + } + ctx->signed_cert_timestamp_list_length = list_len; + + return 1; +} + +int SSL_CTX_set_ocsp_response(SSL_CTX *ctx, const uint8_t *response, + size_t response_len) { + OPENSSL_free(ctx->ocsp_response); + ctx->ocsp_response_length = 0; + + ctx->ocsp_response = BUF_memdup(response, response_len); + if (ctx->ocsp_response == NULL) { + return 0; + } + ctx->ocsp_response_length = response_len; + + return 1; +} + +int SSL_select_next_proto(uint8_t **out, uint8_t *out_len, + const uint8_t *server, unsigned server_len, + const uint8_t *client, unsigned client_len) { unsigned int i, j; const uint8_t *result; int status = OPENSSL_NPN_UNSUPPORTED; @@ -1475,57 +1737,31 @@ int SSL_select_next_proto(uint8_t **out, uint8_t *outlen, const uint8_t *server, found: *out = (uint8_t *)result + 1; - *outlen = result[0]; + *out_len = result[0]; return status; } -/* SSL_get0_next_proto_negotiated sets *data and *len to point to the client's - * requested protocol for this connection and returns 0. If the client didn't - * request any protocol, then *data is set to NULL. - * - * Note that the client can request any protocol it chooses. The value returned - * from this function need not be a member of the list of supported protocols - * provided by the callback. */ -void SSL_get0_next_proto_negotiated(const SSL *s, const uint8_t **data, - unsigned *len) { - *data = s->next_proto_negotiated; - if (!*data) { - *len = 0; +void SSL_get0_next_proto_negotiated(const SSL *ssl, const uint8_t **out_data, + unsigned *out_len) { + *out_data = ssl->next_proto_negotiated; + if (*out_data == NULL) { + *out_len = 0; } else { - *len = s->next_proto_negotiated_len; + *out_len = ssl->next_proto_negotiated_len; } } -/* SSL_CTX_set_next_protos_advertised_cb sets a callback that is called when a - * TLS server needs a list of supported protocols for Next Protocol - * Negotiation. The returned list must be in wire format. The list is returned - * by setting |out| to point to it and |outlen| to its length. This memory will - * not be modified, but one should assume that the SSL* keeps a reference to - * it. - * - * The callback should return SSL_TLSEXT_ERR_OK if it wishes to advertise. - * Otherwise, no such extension will be included in the ServerHello. */ void SSL_CTX_set_next_protos_advertised_cb( SSL_CTX *ctx, - int (*cb)(SSL *ssl, const uint8_t **out, unsigned int *outlen, void *arg), + int (*cb)(SSL *ssl, const uint8_t **out, unsigned *out_len, void *arg), void *arg) { ctx->next_protos_advertised_cb = cb; ctx->next_protos_advertised_cb_arg = arg; } -/* SSL_CTX_set_next_proto_select_cb sets a callback that is called when a - * client needs to select a protocol from the server's provided list. |out| - * must be set to point to the selected protocol (which may be within |in|). - * The length of the protocol name must be written into |outlen|. The server's - * advertised protocols are provided in |in| and |inlen|. The callback can - * assume that |in| is syntactically valid. - * - * The client must select a protocol. It is fatal to the connection if this - * callback returns a value other than SSL_TLSEXT_ERR_OK. - */ void SSL_CTX_set_next_proto_select_cb( - SSL_CTX *ctx, int (*cb)(SSL *s, uint8_t **out, uint8_t *outlen, - const uint8_t *in, unsigned int inlen, void *arg), + SSL_CTX *ctx, int (*cb)(SSL *ssl, uint8_t **out, uint8_t *out_len, + const uint8_t *in, unsigned in_len, void *arg), void *arg) { ctx->next_proto_select_cb = cb; ctx->next_proto_select_cb_arg = arg; @@ -1554,32 +1790,25 @@ int SSL_set_alpn_protos(SSL *ssl, const uint8_t *protos, unsigned protos_len) { return 0; } -/* SSL_CTX_set_alpn_select_cb sets a callback function on |ctx| that is called - * during ClientHello processing in order to select an ALPN protocol from the - * client's list of offered protocols. */ void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, int (*cb)(SSL *ssl, const uint8_t **out, - uint8_t *outlen, const uint8_t *in, - unsigned int inlen, void *arg), + uint8_t *out_len, const uint8_t *in, + unsigned in_len, void *arg), void *arg) { ctx->alpn_select_cb = cb; ctx->alpn_select_cb_arg = arg; } -/* SSL_get0_alpn_selected gets the selected ALPN protocol (if any) from |ssl|. - * On return it sets |*data| to point to |*len| bytes of protocol name (not - * including the leading length-prefix byte). If the server didn't respond with - * a negotiated protocol then |*len| will be zero. */ -void SSL_get0_alpn_selected(const SSL *ssl, const uint8_t **data, - unsigned *len) { - *data = NULL; +void SSL_get0_alpn_selected(const SSL *ssl, const uint8_t **out_data, + unsigned *out_len) { + *out_data = NULL; if (ssl->s3) { - *data = ssl->s3->alpn_selected; + *out_data = ssl->s3->alpn_selected; } - if (*data == NULL) { - *len = 0; + if (*out_data == NULL) { + *out_len = 0; } else { - *len = ssl->s3->alpn_selected_len; + *out_len = ssl->s3->alpn_selected_len; } } @@ -1595,210 +1824,9 @@ int SSL_export_keying_material(SSL *s, uint8_t *out, size_t out_len, s, out, out_len, label, label_len, context, context_len, use_context); } -static uint32_t ssl_session_hash(const SSL_SESSION *a) { - uint32_t hash = - ((uint32_t)a->session_id[0]) || - ((uint32_t)a->session_id[1] << 8) || - ((uint32_t)a->session_id[2] << 16) || - ((uint32_t)a->session_id[3] << 24); - - return hash; -} - -/* NB: If this function (or indeed the hash function which uses a sort of - * coarser function than this one) is changed, ensure - * SSL_CTX_has_matching_session_id() is checked accordingly. It relies on being - * able to construct an SSL_SESSION that will collide with any existing session - * with a matching session ID. */ -static int ssl_session_cmp(const SSL_SESSION *a, const SSL_SESSION *b) { - if (a->ssl_version != b->ssl_version) { - return 1; - } - - if (a->session_id_length != b->session_id_length) { - return 1; - } - - return memcmp(a->session_id, b->session_id, a->session_id_length); -} - -SSL_CTX *SSL_CTX_new(const SSL_METHOD *method) { - SSL_CTX *ret = NULL; - - if (method == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_new, SSL_R_NULL_SSL_METHOD_PASSED); - return NULL; - } - - if (SSL_get_ex_data_X509_STORE_CTX_idx() < 0) { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_new, SSL_R_X509_VERIFICATION_SETUP_PROBLEMS); - goto err; - } - - ret = (SSL_CTX *)OPENSSL_malloc(sizeof(SSL_CTX)); - if (ret == NULL) { - goto err; - } - - memset(ret, 0, sizeof(SSL_CTX)); - - ret->method = method->method; - - CRYPTO_MUTEX_init(&ret->lock); - - ret->cert_store = NULL; - ret->session_cache_mode = SSL_SESS_CACHE_SERVER; - ret->session_cache_size = SSL_SESSION_CACHE_MAX_SIZE_DEFAULT; - ret->session_cache_head = NULL; - ret->session_cache_tail = NULL; - - /* We take the system default */ - ret->session_timeout = SSL_DEFAULT_SESSION_TIMEOUT; - - ret->new_session_cb = 0; - ret->remove_session_cb = 0; - ret->get_session_cb = 0; - ret->generate_session_id = 0; - - ret->references = 1; - ret->quiet_shutdown = 0; - - ret->info_callback = NULL; - - ret->app_verify_callback = 0; - ret->app_verify_arg = NULL; - - ret->max_cert_list = SSL_MAX_CERT_LIST_DEFAULT; - ret->msg_callback = 0; - ret->msg_callback_arg = NULL; - ret->verify_mode = SSL_VERIFY_NONE; - ret->sid_ctx_length = 0; - ret->default_verify_callback = NULL; - ret->cert = ssl_cert_new(); - if (ret->cert == NULL) { - goto err; - } - - ret->default_passwd_callback = 0; - ret->default_passwd_callback_userdata = NULL; - ret->client_cert_cb = 0; - - ret->sessions = lh_SSL_SESSION_new(ssl_session_hash, ssl_session_cmp); - if (ret->sessions == NULL) { - goto err; - } - ret->cert_store = X509_STORE_new(); - if (ret->cert_store == NULL) { - goto err; - } - - ssl_create_cipher_list(ret->method, &ret->cipher_list, - &ret->cipher_list_by_id, SSL_DEFAULT_CIPHER_LIST); - if (ret->cipher_list == NULL || - sk_SSL_CIPHER_num(ret->cipher_list->ciphers) <= 0) { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_new, SSL_R_LIBRARY_HAS_NO_CIPHERS); - goto err2; - } - - ret->param = X509_VERIFY_PARAM_new(); - if (!ret->param) { - goto err; - } - - ret->client_CA = sk_X509_NAME_new_null(); - if (ret->client_CA == NULL) { - goto err; - } - - CRYPTO_new_ex_data(&g_ex_data_class_ssl_ctx, ret, &ret->ex_data); - - ret->extra_certs = NULL; - - ret->max_send_fragment = SSL3_RT_MAX_PLAIN_LENGTH; - - ret->tlsext_servername_callback = 0; - ret->tlsext_servername_arg = NULL; - /* Setup RFC4507 ticket keys */ - if (!RAND_bytes(ret->tlsext_tick_key_name, 16) || - !RAND_bytes(ret->tlsext_tick_hmac_key, 16) || - !RAND_bytes(ret->tlsext_tick_aes_key, 16)) { - ret->options |= SSL_OP_NO_TICKET; - } - - ret->next_protos_advertised_cb = 0; - ret->next_proto_select_cb = 0; - ret->psk_identity_hint = NULL; - ret->psk_client_callback = NULL; - ret->psk_server_callback = NULL; - - /* Default is to connect to non-RI servers. When RI is more widely deployed - * might change this. */ - ret->options |= SSL_OP_LEGACY_SERVER_CONNECT; - - /* Lock the SSL_CTX to the specified version, for compatibility with legacy - * uses of SSL_METHOD. */ - if (method->version != 0) { - SSL_CTX_set_max_version(ret, method->version); - SSL_CTX_set_min_version(ret, method->version); - } - - return ret; - -err: - OPENSSL_PUT_ERROR(SSL, SSL_CTX_new, ERR_R_MALLOC_FAILURE); -err2: - SSL_CTX_free(ret); - return NULL; -} - -void SSL_CTX_free(SSL_CTX *ctx) { - if (ctx == NULL || - !CRYPTO_refcount_dec_and_test_zero(&ctx->references)) { - return; - } - - X509_VERIFY_PARAM_free(ctx->param); - - /* Free internal session cache. However: the remove_cb() may reference the - * ex_data of SSL_CTX, thus the ex_data store can only be removed after the - * sessions were flushed. As the ex_data handling routines might also touch - * the session cache, the most secure solution seems to be: empty (flush) the - * cache, then free ex_data, then finally free the cache. (See ticket - * [openssl.org #212].) */ - SSL_CTX_flush_sessions(ctx, 0); - - CRYPTO_free_ex_data(&g_ex_data_class_ssl_ctx, ctx, &ctx->ex_data); - - CRYPTO_MUTEX_cleanup(&ctx->lock); - lh_SSL_SESSION_free(ctx->sessions); - X509_STORE_free(ctx->cert_store); - ssl_cipher_preference_list_free(ctx->cipher_list); - sk_SSL_CIPHER_free(ctx->cipher_list_by_id); - ssl_cipher_preference_list_free(ctx->cipher_list_tls11); - ssl_cert_free(ctx->cert); - sk_X509_NAME_pop_free(ctx->client_CA, X509_NAME_free); - sk_X509_pop_free(ctx->extra_certs, X509_free); - sk_SRTP_PROTECTION_PROFILE_free(ctx->srtp_profiles); - OPENSSL_free(ctx->psk_identity_hint); - OPENSSL_free(ctx->tlsext_ecpointformatlist); - OPENSSL_free(ctx->tlsext_ellipticcurvelist); - OPENSSL_free(ctx->alpn_client_proto_list); - EVP_PKEY_free(ctx->tlsext_channel_id_private); - BIO_free(ctx->keylog_bio); - - OPENSSL_free(ctx); -} - -void SSL_CTX_set_default_passwd_cb(SSL_CTX *ctx, pem_password_cb *cb) { - ctx->default_passwd_callback = cb; -} - -void SSL_CTX_set_default_passwd_cb_userdata(SSL_CTX *ctx, void *u) { - ctx->default_passwd_callback_userdata = u; -} - void SSL_CTX_set_cert_verify_callback(SSL_CTX *ctx, - int (*cb)(X509_STORE_CTX *, void *), + int (*cb)(X509_STORE_CTX *store_ctx, + void *arg), void *arg) { ctx->app_verify_callback = cb; ctx->app_verify_arg = arg; @@ -1814,57 +1842,48 @@ void SSL_CTX_set_verify_depth(SSL_CTX *ctx, int depth) { X509_VERIFY_PARAM_set_depth(ctx->param, depth); } -void SSL_CTX_set_cert_cb(SSL_CTX *c, int (*cb)(SSL *ssl, void *arg), +void SSL_CTX_set_cert_cb(SSL_CTX *ctx, int (*cb)(SSL *ssl, void *arg), void *arg) { - ssl_cert_set_cert_cb(c->cert, cb, arg); + ssl_cert_set_cert_cb(ctx->cert, cb, arg); } -void SSL_set_cert_cb(SSL *s, int (*cb)(SSL *ssl, void *arg), void *arg) { - ssl_cert_set_cert_cb(s->cert, cb, arg); -} - -static int ssl_has_key(SSL *s, size_t idx) { - CERT_PKEY *cpk = &s->cert->pkeys[idx]; - return cpk->x509 && cpk->privatekey; +void SSL_set_cert_cb(SSL *ssl, int (*cb)(SSL *ssl, void *arg), void *arg) { + ssl_cert_set_cert_cb(ssl->cert, cb, arg); } void ssl_get_compatible_server_ciphers(SSL *s, uint32_t *out_mask_k, uint32_t *out_mask_a) { CERT *c = s->cert; - int rsa_enc, rsa_sign, dh_tmp; + int have_rsa_cert = 0, dh_tmp; uint32_t mask_k, mask_a; - int have_ecc_cert, ecdsa_ok; + int have_ecc_cert = 0, ecdsa_ok; X509 *x; - if (c == NULL) { - /* TODO(davidben): Is this codepath possible? */ - *out_mask_k = 0; - *out_mask_a = 0; - return; - } - dh_tmp = (c->dh_tmp != NULL || c->dh_tmp_cb != NULL); - rsa_enc = ssl_has_key(s, SSL_PKEY_RSA_ENC); - rsa_sign = ssl_has_key(s, SSL_PKEY_RSA_SIGN); - have_ecc_cert = ssl_has_key(s, SSL_PKEY_ECC); + if (s->cert->x509 != NULL && ssl_has_private_key(s)) { + if (ssl_private_key_type(s) == EVP_PKEY_RSA) { + have_rsa_cert = 1; + } else if (ssl_private_key_type(s) == EVP_PKEY_EC) { + have_ecc_cert = 1; + } + } + mask_k = 0; mask_a = 0; - if (rsa_enc) { - mask_k |= SSL_kRSA; - } if (dh_tmp) { mask_k |= SSL_kDHE; } - if (rsa_enc || rsa_sign) { + if (have_rsa_cert) { + mask_k |= SSL_kRSA; mask_a |= SSL_aRSA; } /* An ECC certificate may be usable for ECDSA cipher suites depending on the * key usage extension and on the client's curve preferences. */ if (have_ecc_cert) { - x = c->pkeys[SSL_PKEY_ECC].x509; + x = c->x509; /* This call populates extension flags (ex_flags). */ X509_check_purpose(x, -1, 0); ecdsa_ok = (x->ex_flags & EXFLAG_KUSAGE) @@ -1894,81 +1913,6 @@ void ssl_get_compatible_server_ciphers(SSL *s, uint32_t *out_mask_k, *out_mask_a = mask_a; } -/* This handy macro borrowed from crypto/x509v3/v3_purp.c */ -#define ku_reject(x, usage) \ - (((x)->ex_flags & EXFLAG_KUSAGE) && !((x)->ex_kusage & (usage))) - -int ssl_check_srvr_ecc_cert_and_alg(X509 *x, SSL *s) { - const SSL_CIPHER *cs = s->s3->tmp.new_cipher; - uint32_t alg_a = cs->algorithm_auth; - int signature_nid = 0, md_nid = 0, pk_nid = 0; - - /* This call populates the ex_flags field correctly */ - X509_check_purpose(x, -1, 0); - if (x->sig_alg && x->sig_alg->algorithm) { - signature_nid = OBJ_obj2nid(x->sig_alg->algorithm); - OBJ_find_sigid_algs(signature_nid, &md_nid, &pk_nid); - } - if (alg_a & SSL_aECDSA) { - /* key usage, if present, must allow signing */ - if (ku_reject(x, X509v3_KU_DIGITAL_SIGNATURE)) { - OPENSSL_PUT_ERROR(SSL, ssl_check_srvr_ecc_cert_and_alg, - SSL_R_ECC_CERT_NOT_FOR_SIGNING); - return 0; - } - } - - return 1; /* all checks are ok */ -} - -static int ssl_get_server_cert_index(const SSL *s) { - int idx; - idx = ssl_cipher_get_cert_index(s->s3->tmp.new_cipher); - if (idx == SSL_PKEY_RSA_ENC && !s->cert->pkeys[SSL_PKEY_RSA_ENC].x509) { - idx = SSL_PKEY_RSA_SIGN; - } - if (idx == -1) { - OPENSSL_PUT_ERROR(SSL, ssl_get_server_cert_index, ERR_R_INTERNAL_ERROR); - } - return idx; -} - -CERT_PKEY *ssl_get_server_send_pkey(const SSL *s) { - int i = ssl_get_server_cert_index(s); - - /* This may or may not be an error. */ - if (i < 0) { - return NULL; - } - - /* May be NULL. */ - return &s->cert->pkeys[i]; -} - -EVP_PKEY *ssl_get_sign_pkey(SSL *s, const SSL_CIPHER *cipher) { - uint32_t alg_a = cipher->algorithm_auth; - CERT *c = s->cert; - int idx = -1; - - if (alg_a & SSL_aRSA) { - if (c->pkeys[SSL_PKEY_RSA_SIGN].privatekey != NULL) { - idx = SSL_PKEY_RSA_SIGN; - } else if (c->pkeys[SSL_PKEY_RSA_ENC].privatekey != NULL) { - idx = SSL_PKEY_RSA_ENC; - } - } else if ((alg_a & SSL_aECDSA) && - (c->pkeys[SSL_PKEY_ECC].privatekey != NULL)) { - idx = SSL_PKEY_ECC; - } - - if (idx == -1) { - OPENSSL_PUT_ERROR(SSL, ssl_get_sign_pkey, ERR_R_INTERNAL_ERROR); - return NULL; - } - - return c->pkeys[idx].privatekey; -} - void ssl_update_cache(SSL *s, int mode) { /* Never cache sessions with empty session IDs. */ if (s->session->session_id_length == 0) { @@ -2014,143 +1958,6 @@ void ssl_update_cache(SSL *s, int mode) { } } -int SSL_get_error(const SSL *s, int ret_code) { - int reason; - uint32_t err; - BIO *bio; - - if (ret_code > 0) { - return SSL_ERROR_NONE; - } - - /* Make things return SSL_ERROR_SYSCALL when doing SSL_do_handshake etc, - * where we do encode the error */ - err = ERR_peek_error(); - if (err != 0) { - if (ERR_GET_LIB(err) == ERR_LIB_SYS) { - return SSL_ERROR_SYSCALL; - } - return SSL_ERROR_SSL; - } - - if (ret_code == 0) { - if ((s->shutdown & SSL_RECEIVED_SHUTDOWN) && - (s->s3->warn_alert == SSL_AD_CLOSE_NOTIFY)) { - /* The socket was cleanly shut down with a close_notify. */ - return SSL_ERROR_ZERO_RETURN; - } - /* An EOF was observed which violates the protocol, and the underlying - * transport does not participate in the error queue. Bubble up to the - * caller. */ - return SSL_ERROR_SYSCALL; - } - - if (SSL_want_session(s)) { - return SSL_ERROR_PENDING_SESSION; - } - - if (SSL_want_certificate(s)) { - return SSL_ERROR_PENDING_CERTIFICATE; - } - - if (SSL_want_read(s)) { - bio = SSL_get_rbio(s); - if (BIO_should_read(bio)) { - return SSL_ERROR_WANT_READ; - } - - if (BIO_should_write(bio)) { - /* This one doesn't make too much sense ... We never try to write to the - * rbio, and an application program where rbio and wbio are separate - * couldn't even know what it should wait for. However if we ever set - * s->rwstate incorrectly (so that we have SSL_want_read(s) instead of - * SSL_want_write(s)) and rbio and wbio *are* the same, this test works - * around that bug; so it might be safer to keep it. */ - return SSL_ERROR_WANT_WRITE; - } - - if (BIO_should_io_special(bio)) { - reason = BIO_get_retry_reason(bio); - if (reason == BIO_RR_CONNECT) { - return SSL_ERROR_WANT_CONNECT; - } - - if (reason == BIO_RR_ACCEPT) { - return SSL_ERROR_WANT_ACCEPT; - } - - return SSL_ERROR_SYSCALL; /* unknown */ - } - } - - if (SSL_want_write(s)) { - bio = SSL_get_wbio(s); - if (BIO_should_write(bio)) { - return SSL_ERROR_WANT_WRITE; - } - - if (BIO_should_read(bio)) { - /* See above (SSL_want_read(s) with BIO_should_write(bio)) */ - return SSL_ERROR_WANT_READ; - } - - if (BIO_should_io_special(bio)) { - reason = BIO_get_retry_reason(bio); - if (reason == BIO_RR_CONNECT) { - return SSL_ERROR_WANT_CONNECT; - } - - if (reason == BIO_RR_ACCEPT) { - return SSL_ERROR_WANT_ACCEPT; - } - - return SSL_ERROR_SYSCALL; - } - } - - if (SSL_want_x509_lookup(s)) { - return SSL_ERROR_WANT_X509_LOOKUP; - } - - if (SSL_want_channel_id_lookup(s)) { - return SSL_ERROR_WANT_CHANNEL_ID_LOOKUP; - } - - return SSL_ERROR_SYSCALL; -} - -int SSL_do_handshake(SSL *s) { - int ret = 1; - - if (s->handshake_func == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_do_handshake, SSL_R_CONNECTION_TYPE_NOT_SET); - return -1; - } - - if (SSL_in_init(s)) { - ret = s->handshake_func(s); - } - return ret; -} - -void SSL_set_accept_state(SSL *ssl) { - ssl->server = 1; - ssl->shutdown = 0; - ssl->state = SSL_ST_ACCEPT; - ssl->handshake_func = ssl->method->ssl_accept; - /* clear the current cipher */ - ssl_clear_cipher_ctx(ssl); -} - -void SSL_set_connect_state(SSL *ssl) { - ssl->server = 0; - ssl->shutdown = 0; - ssl->state = SSL_ST_CONNECT; - ssl->handshake_func = ssl->method->ssl_connect; - /* clear the current cipher */ - ssl_clear_cipher_ctx(ssl); -} - static const char *ssl_get_version(int version) { switch (version) { case TLS1_2_VERSION: @@ -2176,12 +1983,16 @@ static const char *ssl_get_version(int version) { } } -const char *SSL_get_version(const SSL *s) { - return ssl_get_version(s->version); +const char *SSL_get_version(const SSL *ssl) { + return ssl_get_version(ssl->version); +} + +const char *SSL_SESSION_get_version(const SSL_SESSION *session) { + return ssl_get_version(session->ssl_version); } -const char *SSL_SESSION_get_version(const SSL_SESSION *sess) { - return ssl_get_version(sess->ssl_version); +const char* SSL_get_curve_name(uint16_t curve_id) { + return tls1_ec_curve_id2name(curve_id); } void ssl_clear_cipher_ctx(SSL *s) { @@ -2193,7 +2004,7 @@ void ssl_clear_cipher_ctx(SSL *s) { X509 *SSL_get_certificate(const SSL *s) { if (s->cert != NULL) { - return s->cert->key->x509; + return s->cert->x509; } return NULL; @@ -2201,7 +2012,7 @@ X509 *SSL_get_certificate(const SSL *s) { EVP_PKEY *SSL_get_privatekey(const SSL *s) { if (s->cert != NULL) { - return s->cert->key->privatekey; + return s->cert->privatekey; } return NULL; @@ -2209,7 +2020,7 @@ EVP_PKEY *SSL_get_privatekey(const SSL *s) { X509 *SSL_CTX_get0_certificate(const SSL_CTX *ctx) { if (ctx->cert != NULL) { - return ctx->cert->key->x509; + return ctx->cert->x509; } return NULL; @@ -2217,22 +2028,22 @@ X509 *SSL_CTX_get0_certificate(const SSL_CTX *ctx) { EVP_PKEY *SSL_CTX_get0_privatekey(const SSL_CTX *ctx) { if (ctx->cert != NULL) { - return ctx->cert->key->privatekey; + return ctx->cert->privatekey; } return NULL; } -const SSL_CIPHER *SSL_get_current_cipher(const SSL *s) { - if (s->aead_write_ctx == NULL) { +const SSL_CIPHER *SSL_get_current_cipher(const SSL *ssl) { + if (ssl->aead_write_ctx == NULL) { return NULL; } - return s->aead_write_ctx->cipher; + return ssl->aead_write_ctx->cipher; } -const void *SSL_get_current_compression(SSL *s) { return NULL; } +const COMP_METHOD *SSL_get_current_compression(SSL *s) { return NULL; } -const void *SSL_get_current_expansion(SSL *s) { return NULL; } +const COMP_METHOD *SSL_get_current_expansion(SSL *s) { return NULL; } int ssl_init_wbio_buffer(SSL *s, int push) { BIO *bbio; @@ -2252,7 +2063,7 @@ int ssl_init_wbio_buffer(SSL *s, int push) { BIO_reset(bbio); if (!BIO_set_read_buffer_size(bbio, 1)) { - OPENSSL_PUT_ERROR(SSL, ssl_init_wbio_buffer, ERR_R_BUF_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB); return 0; } @@ -2330,9 +2141,9 @@ int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx) { return X509_STORE_set_default_paths(ctx->cert_store); } -int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile, - const char *CApath) { - return X509_STORE_load_locations(ctx->cert_store, CAfile, CApath); +int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *ca_file, + const char *ca_dir) { + return X509_STORE_load_locations(ctx->cert_store, ca_file, ca_dir); } void SSL_set_info_callback(SSL *ssl, @@ -2349,7 +2160,9 @@ int SSL_state(const SSL *ssl) { return ssl->state; } void SSL_set_state(SSL *ssl, int state) { } -void SSL_set_verify_result(SSL *ssl, long arg) { ssl->verify_result = arg; } +void SSL_set_verify_result(SSL *ssl, long result) { + ssl->verify_result = result; +} long SSL_get_verify_result(const SSL *ssl) { return ssl->verify_result; } @@ -2363,12 +2176,12 @@ int SSL_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func, return index; } -int SSL_set_ex_data(SSL *s, int idx, void *arg) { - return CRYPTO_set_ex_data(&s->ex_data, idx, arg); +int SSL_set_ex_data(SSL *ssl, int idx, void *arg) { + return CRYPTO_set_ex_data(&ssl->ex_data, idx, arg); } -void *SSL_get_ex_data(const SSL *s, int idx) { - return CRYPTO_get_ex_data(&s->ex_data, idx); +void *SSL_get_ex_data(const SSL *ssl, int idx) { + return CRYPTO_get_ex_data(&ssl->ex_data, idx); } int SSL_CTX_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func, @@ -2382,12 +2195,12 @@ int SSL_CTX_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func, return index; } -int SSL_CTX_set_ex_data(SSL_CTX *s, int idx, void *arg) { - return CRYPTO_set_ex_data(&s->ex_data, idx, arg); +int SSL_CTX_set_ex_data(SSL_CTX *ctx, int idx, void *arg) { + return CRYPTO_set_ex_data(&ctx->ex_data, idx, arg); } -void *SSL_CTX_get_ex_data(const SSL_CTX *s, int idx) { - return CRYPTO_get_ex_data(&s->ex_data, idx); +void *SSL_CTX_get_ex_data(const SSL_CTX *ctx, int idx) { + return CRYPTO_get_ex_data(&ctx->ex_data, idx); } X509_STORE *SSL_CTX_get_cert_store(const SSL_CTX *ctx) { @@ -2435,8 +2248,7 @@ void SSL_set_tmp_ecdh_callback(SSL *ssl, int SSL_CTX_use_psk_identity_hint(SSL_CTX *ctx, const char *identity_hint) { if (identity_hint != NULL && strlen(identity_hint) > PSK_MAX_IDENTITY_LEN) { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_psk_identity_hint, - SSL_R_DATA_LENGTH_TOO_LONG); + OPENSSL_PUT_ERROR(SSL, SSL_R_DATA_LENGTH_TOO_LONG); return 0; } @@ -2454,24 +2266,23 @@ int SSL_CTX_use_psk_identity_hint(SSL_CTX *ctx, const char *identity_hint) { return 1; } -int SSL_use_psk_identity_hint(SSL *s, const char *identity_hint) { - if (s == NULL) { +int SSL_use_psk_identity_hint(SSL *ssl, const char *identity_hint) { + if (ssl == NULL) { return 0; } if (identity_hint != NULL && strlen(identity_hint) > PSK_MAX_IDENTITY_LEN) { - OPENSSL_PUT_ERROR(SSL, SSL_use_psk_identity_hint, - SSL_R_DATA_LENGTH_TOO_LONG); + OPENSSL_PUT_ERROR(SSL, SSL_R_DATA_LENGTH_TOO_LONG); return 0; } /* Clear currently configured hint, if any. */ - OPENSSL_free(s->psk_identity_hint); - s->psk_identity_hint = NULL; + OPENSSL_free(ssl->psk_identity_hint); + ssl->psk_identity_hint = NULL; if (identity_hint != NULL) { - s->psk_identity_hint = BUF_strdup(identity_hint); - if (s->psk_identity_hint == NULL) { + ssl->psk_identity_hint = BUF_strdup(identity_hint); + if (ssl->psk_identity_hint == NULL) { return 0; } } @@ -2479,63 +2290,47 @@ int SSL_use_psk_identity_hint(SSL *s, const char *identity_hint) { return 1; } -const char *SSL_get_psk_identity_hint(const SSL *s) { - if (s == NULL) { +const char *SSL_get_psk_identity_hint(const SSL *ssl) { + if (ssl == NULL) { return NULL; } - return s->psk_identity_hint; + return ssl->psk_identity_hint; } -const char *SSL_get_psk_identity(const SSL *s) { - if (s == NULL || s->session == NULL) { +const char *SSL_get_psk_identity(const SSL *ssl) { + if (ssl == NULL || ssl->session == NULL) { return NULL; } - return s->session->psk_identity; + return ssl->session->psk_identity; } void SSL_set_psk_client_callback( - SSL *s, unsigned int (*cb)(SSL *ssl, const char *hint, char *identity, - unsigned int max_identity_len, uint8_t *psk, - unsigned int max_psk_len)) { - s->psk_client_callback = cb; + SSL *ssl, unsigned (*cb)(SSL *ssl, const char *hint, char *identity, + unsigned max_identity_len, uint8_t *psk, + unsigned max_psk_len)) { + ssl->psk_client_callback = cb; } void SSL_CTX_set_psk_client_callback( - SSL_CTX *ctx, unsigned int (*cb)(SSL *ssl, const char *hint, char *identity, - unsigned int max_identity_len, - uint8_t *psk, unsigned int max_psk_len)) { + SSL_CTX *ctx, unsigned (*cb)(SSL *ssl, const char *hint, char *identity, + unsigned max_identity_len, uint8_t *psk, + unsigned max_psk_len)) { ctx->psk_client_callback = cb; } void SSL_set_psk_server_callback( - SSL *s, unsigned int (*cb)(SSL *ssl, const char *identity, uint8_t *psk, - unsigned int max_psk_len)) { - s->psk_server_callback = cb; + SSL *ssl, unsigned (*cb)(SSL *ssl, const char *identity, uint8_t *psk, + unsigned max_psk_len)) { + ssl->psk_server_callback = cb; } void SSL_CTX_set_psk_server_callback( - SSL_CTX *ctx, unsigned int (*cb)(SSL *ssl, const char *identity, - uint8_t *psk, unsigned int max_psk_len)) { + SSL_CTX *ctx, unsigned (*cb)(SSL *ssl, const char *identity, + uint8_t *psk, unsigned max_psk_len)) { ctx->psk_server_callback = cb; } -void SSL_CTX_set_min_version(SSL_CTX *ctx, uint16_t version) { - ctx->min_version = version; -} - -void SSL_CTX_set_max_version(SSL_CTX *ctx, uint16_t version) { - ctx->max_version = version; -} - -void SSL_set_min_version(SSL *ssl, uint16_t version) { - ssl->min_version = version; -} - -void SSL_set_max_version(SSL *ssl, uint16_t version) { - ssl->max_version = version; -} - void SSL_CTX_set_msg_callback(SSL_CTX *ctx, void (*cb)(int write_p, int version, int content_type, const void *buf, @@ -2596,16 +2391,13 @@ int ssl_ctx_log_rsa_client_key_exchange(SSL_CTX *ctx, } if (encrypted_premaster_len < 8) { - OPENSSL_PUT_ERROR(SSL, ssl_ctx_log_rsa_client_key_exchange, - ERR_R_INTERNAL_ERROR); - return 0; - } - - if (!CBB_init(&cbb, 4 + 16 + 1 + premaster_len * 2 + 1)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return 0; } - if (!CBB_add_bytes(&cbb, (const uint8_t *)"RSA ", 4) || + CBB_zero(&cbb); + if (!CBB_init(&cbb, 4 + 16 + 1 + premaster_len * 2 + 1) || + !CBB_add_bytes(&cbb, (const uint8_t *)"RSA ", 4) || /* Only the first 8 bytes of the encrypted premaster secret are * logged. */ !cbb_add_hex(&cbb, encrypted_premaster, 8) || @@ -2639,15 +2431,13 @@ int ssl_ctx_log_master_secret(SSL_CTX *ctx, const uint8_t *client_random, } if (client_random_len != 32) { - OPENSSL_PUT_ERROR(SSL, ssl_ctx_log_master_secret, ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return 0; } - if (!CBB_init(&cbb, 14 + 64 + 1 + master_len * 2 + 1)) { - return 0; - } - - if (!CBB_add_bytes(&cbb, (const uint8_t *)"CLIENT_RANDOM ", 14) || + CBB_zero(&cbb); + if (!CBB_init(&cbb, 14 + 64 + 1 + master_len * 2 + 1) || + !CBB_add_bytes(&cbb, (const uint8_t *)"CLIENT_RANDOM ", 14) || !cbb_add_hex(&cbb, client_random, 32) || !CBB_add_bytes(&cbb, (const uint8_t *)" ", 1) || !cbb_add_hex(&cbb, master, master_len) || @@ -2906,19 +2696,15 @@ uint16_t ssl3_version_from_wire(SSL *s, uint16_t wire_version) { return version; } -int SSL_cache_hit(SSL *s) { return s->hit; } +int SSL_cache_hit(SSL *ssl) { return SSL_session_reused(ssl); } -int SSL_is_server(SSL *s) { return s->server; } +int SSL_is_server(SSL *ssl) { return ssl->server; } void SSL_CTX_set_dos_protection_cb( SSL_CTX *ctx, int (*cb)(const struct ssl_early_callback_ctx *)) { ctx->dos_protection_cb = cb; } -void SSL_enable_fastradio_padding(SSL *s, char on_off) { - s->fastradio_padding = on_off; -} - void SSL_set_reject_peer_renegotiations(SSL *s, int reject) { s->accept_peer_renegotiations = !reject; } @@ -2933,43 +2719,73 @@ int SSL_get_rc4_state(const SSL *ssl, const RC4_KEY **read_key, EVP_AEAD_CTX_get_rc4_state(&ssl->aead_write_ctx->ctx, write_key); } -int SSL_get_tls_unique(const SSL *ssl, uint8_t *out, size_t *out_len, - size_t max_out) { - /* The tls-unique value is the first Finished message in the handshake, which - * is the client's in a full handshake and the server's for a resumption. See - * https://tools.ietf.org/html/rfc5929#section-3.1. */ - const uint8_t *finished = ssl->s3->previous_client_finished; - size_t finished_len = ssl->s3->previous_client_finished_len; - if (ssl->hit) { - /* tls-unique is broken for resumed sessions unless EMS is used. */ - if (!ssl->session->extended_master_secret) { - goto err; +int SSL_clear(SSL *ssl) { + if (ssl->method == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_METHOD_SPECIFIED); + return 0; + } + + if (ssl_clear_bad_session(ssl)) { + SSL_SESSION_free(ssl->session); + ssl->session = NULL; + } + + ssl->hit = 0; + ssl->shutdown = 0; + + /* SSL_clear may be called before or after the |ssl| is initialized in either + * accept or connect state. In the latter case, SSL_clear should preserve the + * half and reset |ssl->state| accordingly. */ + if (ssl->handshake_func != NULL) { + if (ssl->server) { + SSL_set_accept_state(ssl); + } else { + SSL_set_connect_state(ssl); } - finished = ssl->s3->previous_server_finished; - finished_len = ssl->s3->previous_server_finished_len; + } else { + assert(ssl->state == 0); } - if (!ssl->s3->initial_handshake_complete || - ssl->version < TLS1_VERSION) { - goto err; + /* TODO(davidben): Some state on |ssl| is reset both in |SSL_new| and + * |SSL_clear| because it is per-connection state rather than configuration + * state. Per-connection state should be on |ssl->s3| and |ssl->d1| so it is + * naturally reset at the right points between |SSL_new|, |SSL_clear|, and + * |ssl3_new|. */ + + ssl->rwstate = SSL_NOTHING; + + BUF_MEM_free(ssl->init_buf); + ssl->init_buf = NULL; + + ssl_clear_cipher_ctx(ssl); + + OPENSSL_free(ssl->next_proto_negotiated); + ssl->next_proto_negotiated = NULL; + ssl->next_proto_negotiated_len = 0; + + /* The ssl->d1->mtu is simultaneously configuration (preserved across + * clear) and connection-specific state (gets reset). + * + * TODO(davidben): Avoid this. */ + unsigned mtu = 0; + if (ssl->d1 != NULL) { + mtu = ssl->d1->mtu; } - *out_len = finished_len; - if (finished_len > max_out) { - *out_len = max_out; + ssl->method->ssl_free(ssl); + if (!ssl->method->ssl_new(ssl)) { + return 0; } + ssl->enc_method = ssl3_get_enc_method(ssl->version); + assert(ssl->enc_method != NULL); - memcpy(out, finished, *out_len); - return 1; + if (SSL_IS_DTLS(ssl) && (SSL_get_options(ssl) & SSL_OP_NO_QUERY_MTU)) { + ssl->d1->mtu = mtu; + } -err: - *out_len = 0; - memset(out, 0, max_out); - return 0; -} + ssl->client_version = ssl->version; -int SSL_initial_handshake_complete(const SSL *ssl) { - return ssl->s3->initial_handshake_complete; + return 1; } int SSL_CTX_sess_connect(const SSL_CTX *ctx) { return 0; } @@ -2983,3 +2799,5 @@ int SSL_CTX_sess_cb_hits(const SSL_CTX *ctx) { return 0; } int SSL_CTX_sess_misses(const SSL_CTX *ctx) { return 0; } int SSL_CTX_sess_timeouts(const SSL_CTX *ctx) { return 0; } int SSL_CTX_sess_cache_full(const SSL_CTX *ctx) { return 0; } +void ERR_load_SSL_strings(void) {} +void SSL_load_error_strings(void) {} diff --git a/src/ssl/ssl_rsa.c b/src/ssl/ssl_rsa.c index 87f4c1c..ccd3858 100644 --- a/src/ssl/ssl_rsa.c +++ b/src/ssl/ssl_rsa.c @@ -54,79 +54,38 @@ * copied and put under another distribution licence * [including the GNU Public Licence.] */ -#include <stdio.h> +#include <openssl/ssl.h> -#include <openssl/bio.h> #include <openssl/err.h> #include <openssl/evp.h> #include <openssl/mem.h> -#include <openssl/obj.h> -#include <openssl/pem.h> #include <openssl/x509.h> #include "internal.h" + static int ssl_set_cert(CERT *c, X509 *x509); static int ssl_set_pkey(CERT *c, EVP_PKEY *pkey); +static int is_key_type_supported(int key_type) { + return key_type == EVP_PKEY_RSA || key_type == EVP_PKEY_EC; +} + int SSL_use_certificate(SSL *ssl, X509 *x) { if (x == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_use_certificate, ERR_R_PASSED_NULL_PARAMETER); + OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER); return 0; } return ssl_set_cert(ssl->cert, x); } -int SSL_use_certificate_file(SSL *ssl, const char *file, int type) { - int reason_code; - BIO *in; - int ret = 0; - X509 *x = NULL; - - in = BIO_new(BIO_s_file()); - if (in == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_use_certificate_file, ERR_R_BUF_LIB); - goto end; - } - - if (BIO_read_filename(in, file) <= 0) { - OPENSSL_PUT_ERROR(SSL, SSL_use_certificate_file, ERR_R_SYS_LIB); - goto end; - } - - if (type == SSL_FILETYPE_ASN1) { - reason_code = ERR_R_ASN1_LIB; - x = d2i_X509_bio(in, NULL); - } else if (type == SSL_FILETYPE_PEM) { - reason_code = ERR_R_PEM_LIB; - x = PEM_read_bio_X509(in, NULL, ssl->ctx->default_passwd_callback, - ssl->ctx->default_passwd_callback_userdata); - } else { - OPENSSL_PUT_ERROR(SSL, SSL_use_certificate_file, SSL_R_BAD_SSL_FILETYPE); - goto end; - } - - if (x == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_use_certificate_file, reason_code); - goto end; - } - - ret = SSL_use_certificate(ssl, x); - -end: - X509_free(x); - BIO_free(in); - - return ret; -} - int SSL_use_certificate_ASN1(SSL *ssl, const uint8_t *d, int len) { X509 *x; int ret; x = d2i_X509(NULL, &d, (long)len); if (x == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_use_certificate_ASN1, ERR_R_ASN1_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_ASN1_LIB); return 0; } @@ -140,13 +99,13 @@ int SSL_use_RSAPrivateKey(SSL *ssl, RSA *rsa) { int ret; if (rsa == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_use_RSAPrivateKey, ERR_R_PASSED_NULL_PARAMETER); + OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER); return 0; } pkey = EVP_PKEY_new(); if (pkey == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_use_RSAPrivateKey, ERR_R_EVP_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_EVP_LIB); return 0; } @@ -160,86 +119,36 @@ int SSL_use_RSAPrivateKey(SSL *ssl, RSA *rsa) { } static int ssl_set_pkey(CERT *c, EVP_PKEY *pkey) { - int i; - - i = ssl_cert_type(pkey); - if (i < 0) { - OPENSSL_PUT_ERROR(SSL, ssl_set_pkey, SSL_R_UNKNOWN_CERTIFICATE_TYPE); + if (!is_key_type_supported(pkey->type)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE); return 0; } - if (c->pkeys[i].x509 != NULL) { + if (c->x509 != NULL) { /* Sanity-check that the private key and the certificate match, unless the * key is opaque (in case of, say, a smartcard). */ if (!EVP_PKEY_is_opaque(pkey) && - !X509_check_private_key(c->pkeys[i].x509, pkey)) { - X509_free(c->pkeys[i].x509); - c->pkeys[i].x509 = NULL; + !X509_check_private_key(c->x509, pkey)) { + X509_free(c->x509); + c->x509 = NULL; return 0; } } - EVP_PKEY_free(c->pkeys[i].privatekey); - c->pkeys[i].privatekey = EVP_PKEY_up_ref(pkey); - c->key = &(c->pkeys[i]); + EVP_PKEY_free(c->privatekey); + c->privatekey = EVP_PKEY_up_ref(pkey); return 1; } -int SSL_use_RSAPrivateKey_file(SSL *ssl, const char *file, int type) { - int reason_code, ret = 0; - BIO *in; - RSA *rsa = NULL; - - in = BIO_new(BIO_s_file()); - if (in == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_use_RSAPrivateKey_file, ERR_R_BUF_LIB); - goto end; - } - - if (BIO_read_filename(in, file) <= 0) { - OPENSSL_PUT_ERROR(SSL, SSL_use_RSAPrivateKey_file, ERR_R_SYS_LIB); - goto end; - } - - if (type == SSL_FILETYPE_ASN1) { - reason_code = ERR_R_ASN1_LIB; - rsa = d2i_RSAPrivateKey_bio(in, NULL); - } else if (type == SSL_FILETYPE_PEM) { - reason_code = ERR_R_PEM_LIB; - rsa = - PEM_read_bio_RSAPrivateKey(in, NULL, ssl->ctx->default_passwd_callback, - ssl->ctx->default_passwd_callback_userdata); - } else { - OPENSSL_PUT_ERROR(SSL, SSL_use_RSAPrivateKey_file, SSL_R_BAD_SSL_FILETYPE); - goto end; - } - - if (rsa == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_use_RSAPrivateKey_file, reason_code); - goto end; - } - ret = SSL_use_RSAPrivateKey(ssl, rsa); - RSA_free(rsa); - -end: - BIO_free(in); - return ret; -} - -int SSL_use_RSAPrivateKey_ASN1(SSL *ssl, uint8_t *d, long len) { - int ret; - const uint8_t *p; - RSA *rsa; - - p = d; - rsa = d2i_RSAPrivateKey(NULL, &p, (long)len); +int SSL_use_RSAPrivateKey_ASN1(SSL *ssl, const uint8_t *der, size_t der_len) { + RSA *rsa = RSA_private_key_from_bytes(der, der_len); if (rsa == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_use_RSAPrivateKey_ASN1, ERR_R_ASN1_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_ASN1_LIB); return 0; } - ret = SSL_use_RSAPrivateKey(ssl, rsa); + int ret = SSL_use_RSAPrivateKey(ssl, rsa); RSA_free(rsa); return ret; } @@ -248,7 +157,7 @@ int SSL_use_PrivateKey(SSL *ssl, EVP_PKEY *pkey) { int ret; if (pkey == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_use_PrivateKey, ERR_R_PASSED_NULL_PARAMETER); + OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER); return 0; } @@ -256,46 +165,6 @@ int SSL_use_PrivateKey(SSL *ssl, EVP_PKEY *pkey) { return ret; } -int SSL_use_PrivateKey_file(SSL *ssl, const char *file, int type) { - int reason_code, ret = 0; - BIO *in; - EVP_PKEY *pkey = NULL; - - in = BIO_new(BIO_s_file()); - if (in == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_use_PrivateKey_file, ERR_R_BUF_LIB); - goto end; - } - - if (BIO_read_filename(in, file) <= 0) { - OPENSSL_PUT_ERROR(SSL, SSL_use_PrivateKey_file, ERR_R_SYS_LIB); - goto end; - } - - if (type == SSL_FILETYPE_PEM) { - reason_code = ERR_R_PEM_LIB; - pkey = PEM_read_bio_PrivateKey(in, NULL, ssl->ctx->default_passwd_callback, - ssl->ctx->default_passwd_callback_userdata); - } else if (type == SSL_FILETYPE_ASN1) { - reason_code = ERR_R_ASN1_LIB; - pkey = d2i_PrivateKey_bio(in, NULL); - } else { - OPENSSL_PUT_ERROR(SSL, SSL_use_PrivateKey_file, SSL_R_BAD_SSL_FILETYPE); - goto end; - } - - if (pkey == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_use_PrivateKey_file, reason_code); - goto end; - } - ret = SSL_use_PrivateKey(ssl, pkey); - EVP_PKEY_free(pkey); - -end: - BIO_free(in); - return ret; -} - int SSL_use_PrivateKey_ASN1(int type, SSL *ssl, const uint8_t *d, long len) { int ret; const uint8_t *p; @@ -304,7 +173,7 @@ int SSL_use_PrivateKey_ASN1(int type, SSL *ssl, const uint8_t *d, long len) { p = d; pkey = d2i_PrivateKey(type, NULL, &p, (long)len); if (pkey == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_use_PrivateKey_ASN1, ERR_R_ASN1_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_ASN1_LIB); return 0; } @@ -315,8 +184,7 @@ int SSL_use_PrivateKey_ASN1(int type, SSL *ssl, const uint8_t *d, long len) { int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) { if (x == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_certificate, - ERR_R_PASSED_NULL_PARAMETER); + OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER); return 0; } @@ -324,32 +192,28 @@ int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) { } static int ssl_set_cert(CERT *c, X509 *x) { - EVP_PKEY *pkey; - int i; - - pkey = X509_get_pubkey(x); + EVP_PKEY *pkey = X509_get_pubkey(x); if (pkey == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl_set_cert, SSL_R_X509_LIB); + OPENSSL_PUT_ERROR(SSL, SSL_R_X509_LIB); return 0; } - i = ssl_cert_type(pkey); - if (i < 0) { - OPENSSL_PUT_ERROR(SSL, ssl_set_cert, SSL_R_UNKNOWN_CERTIFICATE_TYPE); + if (!is_key_type_supported(pkey->type)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE); EVP_PKEY_free(pkey); return 0; } - if (c->pkeys[i].privatekey != NULL) { + if (c->privatekey != NULL) { /* Sanity-check that the private key and the certificate match, unless the * key is opaque (in case of, say, a smartcard). */ - if (!EVP_PKEY_is_opaque(c->pkeys[i].privatekey) && - !X509_check_private_key(x, c->pkeys[i].privatekey)) { + if (!EVP_PKEY_is_opaque(c->privatekey) && + !X509_check_private_key(x, c->privatekey)) { /* don't fail for a cert/key mismatch, just free current private key * (when switching to a different cert & key, first this function should * be used, then ssl_set_pkey */ - EVP_PKEY_free(c->pkeys[i].privatekey); - c->pkeys[i].privatekey = NULL; + EVP_PKEY_free(c->privatekey); + c->privatekey = NULL; /* clear error queue */ ERR_clear_error(); } @@ -357,63 +221,19 @@ static int ssl_set_cert(CERT *c, X509 *x) { EVP_PKEY_free(pkey); - X509_free(c->pkeys[i].x509); - c->pkeys[i].x509 = X509_up_ref(x); - c->key = &(c->pkeys[i]); + X509_free(c->x509); + c->x509 = X509_up_ref(x); return 1; } -int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type) { - int reason_code; - BIO *in; - int ret = 0; - X509 *x = NULL; - - in = BIO_new(BIO_s_file()); - if (in == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_certificate_file, ERR_R_BUF_LIB); - goto end; - } - - if (BIO_read_filename(in, file) <= 0) { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_certificate_file, ERR_R_SYS_LIB); - goto end; - } - - if (type == SSL_FILETYPE_ASN1) { - reason_code = ERR_R_ASN1_LIB; - x = d2i_X509_bio(in, NULL); - } else if (type == SSL_FILETYPE_PEM) { - reason_code = ERR_R_PEM_LIB; - x = PEM_read_bio_X509(in, NULL, ctx->default_passwd_callback, - ctx->default_passwd_callback_userdata); - } else { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_certificate_file, - SSL_R_BAD_SSL_FILETYPE); - goto end; - } - - if (x == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_certificate_file, reason_code); - goto end; - } - - ret = SSL_CTX_use_certificate(ctx, x); - -end: - X509_free(x); - BIO_free(in); - return ret; -} - int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, const uint8_t *d) { X509 *x; int ret; x = d2i_X509(NULL, &d, (long)len); if (x == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_certificate_ASN1, ERR_R_ASN1_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_ASN1_LIB); return 0; } @@ -427,14 +247,13 @@ int SSL_CTX_use_RSAPrivateKey(SSL_CTX *ctx, RSA *rsa) { EVP_PKEY *pkey; if (rsa == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_RSAPrivateKey, - ERR_R_PASSED_NULL_PARAMETER); + OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER); return 0; } pkey = EVP_PKEY_new(); if (pkey == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_RSAPrivateKey, ERR_R_EVP_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_EVP_LIB); return 0; } @@ -446,113 +265,28 @@ int SSL_CTX_use_RSAPrivateKey(SSL_CTX *ctx, RSA *rsa) { return ret; } -int SSL_CTX_use_RSAPrivateKey_file(SSL_CTX *ctx, const char *file, int type) { - int reason_code, ret = 0; - BIO *in; - RSA *rsa = NULL; - - in = BIO_new(BIO_s_file()); - if (in == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_RSAPrivateKey_file, ERR_R_BUF_LIB); - goto end; - } - - if (BIO_read_filename(in, file) <= 0) { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_RSAPrivateKey_file, ERR_R_SYS_LIB); - goto end; - } - - if (type == SSL_FILETYPE_ASN1) { - reason_code = ERR_R_ASN1_LIB; - rsa = d2i_RSAPrivateKey_bio(in, NULL); - } else if (type == SSL_FILETYPE_PEM) { - reason_code = ERR_R_PEM_LIB; - rsa = PEM_read_bio_RSAPrivateKey(in, NULL, ctx->default_passwd_callback, - ctx->default_passwd_callback_userdata); - } else { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_RSAPrivateKey_file, - SSL_R_BAD_SSL_FILETYPE); - goto end; - } - - if (rsa == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_RSAPrivateKey_file, reason_code); - goto end; - } - ret = SSL_CTX_use_RSAPrivateKey(ctx, rsa); - RSA_free(rsa); - -end: - BIO_free(in); - return ret; -} - -int SSL_CTX_use_RSAPrivateKey_ASN1(SSL_CTX *ctx, const uint8_t *d, long len) { - int ret; - const uint8_t *p; - RSA *rsa; - - p = d; - rsa = d2i_RSAPrivateKey(NULL, &p, (long)len); +int SSL_CTX_use_RSAPrivateKey_ASN1(SSL_CTX *ctx, const uint8_t *der, + size_t der_len) { + RSA *rsa = RSA_private_key_from_bytes(der, der_len); if (rsa == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_RSAPrivateKey_ASN1, ERR_R_ASN1_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_ASN1_LIB); return 0; } - ret = SSL_CTX_use_RSAPrivateKey(ctx, rsa); + int ret = SSL_CTX_use_RSAPrivateKey(ctx, rsa); RSA_free(rsa); return ret; } int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey) { if (pkey == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_PrivateKey, ERR_R_PASSED_NULL_PARAMETER); + OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER); return 0; } return ssl_set_pkey(ctx->cert, pkey); } -int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type) { - int reason_code, ret = 0; - BIO *in; - EVP_PKEY *pkey = NULL; - - in = BIO_new(BIO_s_file()); - if (in == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_PrivateKey_file, ERR_R_BUF_LIB); - goto end; - } - - if (BIO_read_filename(in, file) <= 0) { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_PrivateKey_file, ERR_R_SYS_LIB); - goto end; - } - - if (type == SSL_FILETYPE_PEM) { - reason_code = ERR_R_PEM_LIB; - pkey = PEM_read_bio_PrivateKey(in, NULL, ctx->default_passwd_callback, - ctx->default_passwd_callback_userdata); - } else if (type == SSL_FILETYPE_ASN1) { - reason_code = ERR_R_ASN1_LIB; - pkey = d2i_PrivateKey_bio(in, NULL); - } else { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_PrivateKey_file, SSL_R_BAD_SSL_FILETYPE); - goto end; - } - - if (pkey == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_PrivateKey_file, reason_code); - goto end; - } - ret = SSL_CTX_use_PrivateKey(ctx, pkey); - EVP_PKEY_free(pkey); - -end: - BIO_free(in); - return ret; -} - int SSL_CTX_use_PrivateKey_ASN1(int type, SSL_CTX *ctx, const uint8_t *d, long len) { int ret; @@ -562,7 +296,7 @@ int SSL_CTX_use_PrivateKey_ASN1(int type, SSL_CTX *ctx, const uint8_t *d, p = d; pkey = d2i_PrivateKey(type, NULL, &p, (long)len); if (pkey == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_PrivateKey_ASN1, ERR_R_ASN1_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_ASN1_LIB); return 0; } @@ -571,76 +305,74 @@ int SSL_CTX_use_PrivateKey_ASN1(int type, SSL_CTX *ctx, const uint8_t *d, return ret; } +void SSL_set_private_key_method(SSL *ssl, + const SSL_PRIVATE_KEY_METHOD *key_method) { + ssl->cert->key_method = key_method; +} -/* Read a file that contains our certificate in "PEM" format, possibly followed - * by a sequence of CA certificates that should be sent to the peer in the - * Certificate message. */ -int SSL_CTX_use_certificate_chain_file(SSL_CTX *ctx, const char *file) { - BIO *in; - int ret = 0; - X509 *x = NULL; - - ERR_clear_error(); /* clear error stack for SSL_CTX_use_certificate() */ +int SSL_set_private_key_digest_prefs(SSL *ssl, const int *digest_nids, + size_t num_digests) { + OPENSSL_free(ssl->cert->digest_nids); - in = BIO_new(BIO_s_file()); - if (in == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_certificate_chain_file, ERR_R_BUF_LIB); - goto end; + ssl->cert->num_digest_nids = 0; + ssl->cert->digest_nids = BUF_memdup(digest_nids, num_digests*sizeof(int)); + if (ssl->cert->digest_nids == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return 0; } - if (BIO_read_filename(in, file) <= 0) { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_certificate_chain_file, ERR_R_SYS_LIB); - goto end; - } + ssl->cert->num_digest_nids = num_digests; + return 1; +} - x = PEM_read_bio_X509_AUX(in, NULL, ctx->default_passwd_callback, - ctx->default_passwd_callback_userdata); - if (x == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_certificate_chain_file, ERR_R_PEM_LIB); - goto end; +int ssl_has_private_key(SSL *ssl) { + return ssl->cert->privatekey != NULL || ssl->cert->key_method != NULL; +} + +int ssl_private_key_type(SSL *ssl) { + if (ssl->cert->key_method != NULL) { + return ssl->cert->key_method->type(ssl); } + return EVP_PKEY_id(ssl->cert->privatekey); +} - ret = SSL_CTX_use_certificate(ctx, x); +size_t ssl_private_key_max_signature_len(SSL *ssl) { + if (ssl->cert->key_method != NULL) { + return ssl->cert->key_method->max_signature_len(ssl); + } + return EVP_PKEY_size(ssl->cert->privatekey); +} - if (ERR_peek_error() != 0) { - ret = 0; /* Key/certificate mismatch doesn't imply ret==0 ... */ +enum ssl_private_key_result_t ssl_private_key_sign( + SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out, const EVP_MD *md, + const uint8_t *in, size_t in_len) { + if (ssl->cert->key_method != NULL) { + return ssl->cert->key_method->sign(ssl, out, out_len, max_out, md, in, + in_len); } - if (ret) { - /* If we could set up our certificate, now proceed to the CA - * certificates. */ - X509 *ca; - int r; - uint32_t err; - - SSL_CTX_clear_chain_certs(ctx); - - while ((ca = PEM_read_bio_X509(in, NULL, ctx->default_passwd_callback, - ctx->default_passwd_callback_userdata)) != - NULL) { - r = SSL_CTX_add0_chain_cert(ctx, ca); - if (!r) { - X509_free(ca); - ret = 0; - goto end; - } - /* Note that we must not free r if it was successfully added to the chain - * (while we must free the main certificate, since its reference count is - * increased by SSL_CTX_use_certificate). */ - } + enum ssl_private_key_result_t ret = ssl_private_key_failure; + EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(ssl->cert->privatekey, NULL); + if (ctx == NULL) { + goto end; + } - /* When the while loop ends, it's usually just EOF. */ - err = ERR_peek_last_error(); - if (ERR_GET_LIB(err) == ERR_LIB_PEM && - ERR_GET_REASON(err) == PEM_R_NO_START_LINE) { - ERR_clear_error(); - } else { - ret = 0; /* some real error */ - } + size_t len = max_out; + if (!EVP_PKEY_sign_init(ctx) || + !EVP_PKEY_CTX_set_signature_md(ctx, md) || + !EVP_PKEY_sign(ctx, out, &len, in, in_len)) { + goto end; } + *out_len = len; + ret = ssl_private_key_success; end: - X509_free(x); - BIO_free(in); + EVP_PKEY_CTX_free(ctx); return ret; } + +enum ssl_private_key_result_t ssl_private_key_sign_complete( + SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out) { + /* Only custom keys may be asynchronous. */ + return ssl->cert->key_method->sign_complete(ssl, out, out_len, max_out); +} diff --git a/src/ssl/ssl_sess.c b/src/ssl/ssl_session.c index 9ab4585..345aca2 100644 --- a/src/ssl/ssl_sess.c +++ b/src/ssl/ssl_session.c @@ -133,6 +133,9 @@ * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR * OTHERWISE. */ +#include <openssl/ssl.h> + +#include <assert.h> #include <stdio.h> #include <string.h> @@ -150,11 +153,111 @@ * that it needs to asynchronously fetch session information. */ static const char g_pending_session_magic = 0; -static CRYPTO_EX_DATA_CLASS g_ex_data_class = CRYPTO_EX_DATA_CLASS_INIT; +static CRYPTO_EX_DATA_CLASS g_ex_data_class = + CRYPTO_EX_DATA_CLASS_INIT_WITH_APP_DATA; + +static void SSL_SESSION_list_remove(SSL_CTX *ctx, SSL_SESSION *session); +static void SSL_SESSION_list_add(SSL_CTX *ctx, SSL_SESSION *session); +static int remove_session_lock(SSL_CTX *ctx, SSL_SESSION *session, int lock); + +SSL_SESSION *SSL_SESSION_new(void) { + SSL_SESSION *session = (SSL_SESSION *)OPENSSL_malloc(sizeof(SSL_SESSION)); + if (session == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return 0; + } + memset(session, 0, sizeof(SSL_SESSION)); + + session->verify_result = 1; /* avoid 0 (= X509_V_OK) just in case */ + session->references = 1; + session->timeout = SSL_DEFAULT_SESSION_TIMEOUT; + session->time = (unsigned long)time(NULL); + CRYPTO_new_ex_data(&g_ex_data_class, session, &session->ex_data); + return session; +} + +SSL_SESSION *SSL_SESSION_up_ref(SSL_SESSION *session) { + if (session != NULL) { + CRYPTO_refcount_inc(&session->references); + } + return session; +} + +void SSL_SESSION_free(SSL_SESSION *session) { + if (session == NULL || + !CRYPTO_refcount_dec_and_test_zero(&session->references)) { + return; + } + + CRYPTO_free_ex_data(&g_ex_data_class, session, &session->ex_data); + + OPENSSL_cleanse(session->master_key, sizeof(session->master_key)); + OPENSSL_cleanse(session->session_id, sizeof(session->session_id)); + X509_free(session->peer); + sk_X509_pop_free(session->cert_chain, X509_free); + OPENSSL_free(session->tlsext_hostname); + OPENSSL_free(session->tlsext_tick); + OPENSSL_free(session->tlsext_signed_cert_timestamp_list); + OPENSSL_free(session->ocsp_response); + OPENSSL_free(session->psk_identity); + OPENSSL_cleanse(session, sizeof(*session)); + OPENSSL_free(session); +} + +const uint8_t *SSL_SESSION_get_id(const SSL_SESSION *session, + unsigned *out_len) { + if (out_len != NULL) { + *out_len = session->session_id_length; + } + return session->session_id; +} + +long SSL_SESSION_get_timeout(const SSL_SESSION *session) { + return session->timeout; +} + +long SSL_SESSION_get_time(const SSL_SESSION *session) { + return session->time; +} + +uint32_t SSL_SESSION_get_key_exchange_info(const SSL_SESSION *session) { + return session->key_exchange_info; +} + +X509 *SSL_SESSION_get0_peer(const SSL_SESSION *session) { + return session->peer; +} + +long SSL_SESSION_set_time(SSL_SESSION *session, long time) { + if (session == NULL) { + return 0; + } + + session->time = time; + return time; +} + +long SSL_SESSION_set_timeout(SSL_SESSION *session, long timeout) { + if (session == NULL) { + return 0; + } -static void SSL_SESSION_list_remove(SSL_CTX *ctx, SSL_SESSION *s); -static void SSL_SESSION_list_add(SSL_CTX *ctx, SSL_SESSION *s); -static int remove_session_lock(SSL_CTX *ctx, SSL_SESSION *c, int lck); + session->timeout = timeout; + return 1; +} + +int SSL_SESSION_set1_id_context(SSL_SESSION *session, const uint8_t *sid_ctx, + unsigned sid_ctx_len) { + if (sid_ctx_len > SSL_MAX_SID_CTX_LENGTH) { + OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_SESSION_ID_CONTEXT_TOO_LONG); + return 0; + } + + session->sid_ctx_length = sid_ctx_len; + memcpy(session->sid_ctx, sid_ctx, sid_ctx_len); + + return 1; +} SSL_SESSION *SSL_magic_pending_session_ptr(void) { return (SSL_SESSION *)&g_pending_session_magic; @@ -182,37 +285,12 @@ int SSL_SESSION_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func, return index; } -int SSL_SESSION_set_ex_data(SSL_SESSION *s, int idx, void *arg) { - return CRYPTO_set_ex_data(&s->ex_data, idx, arg); -} - -void *SSL_SESSION_get_ex_data(const SSL_SESSION *s, int idx) { - return CRYPTO_get_ex_data(&s->ex_data, idx); +int SSL_SESSION_set_ex_data(SSL_SESSION *session, int idx, void *arg) { + return CRYPTO_set_ex_data(&session->ex_data, idx, arg); } -SSL_SESSION *SSL_SESSION_new(void) { - SSL_SESSION *ss; - - ss = (SSL_SESSION *)OPENSSL_malloc(sizeof(SSL_SESSION)); - if (ss == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_new, ERR_R_MALLOC_FAILURE); - return 0; - } - memset(ss, 0, sizeof(SSL_SESSION)); - - ss->verify_result = 1; /* avoid 0 (= X509_V_OK) just in case */ - ss->references = 1; - ss->timeout = SSL_DEFAULT_SESSION_TIMEOUT; - ss->time = (unsigned long)time(NULL); - CRYPTO_new_ex_data(&g_ex_data_class, ss, &ss->ex_data); - return ss; -} - -const uint8_t *SSL_SESSION_get_id(const SSL_SESSION *s, unsigned int *len) { - if (len) { - *len = s->session_id_length; - } - return s->session_id; +void *SSL_SESSION_get_ex_data(const SSL_SESSION *session, int idx) { + return CRYPTO_get_ex_data(&session->ex_data, idx); } /* Even with SSLv2, we have 16 bytes (128 bits) of session ID space. @@ -225,9 +303,9 @@ const uint8_t *SSL_SESSION_get_id(const SSL_SESSION *s, unsigned int *len) { * server. How you might store that many sessions is perhaps a more interesting * question ... */ static int def_generate_session_id(const SSL *ssl, uint8_t *id, - unsigned int *id_len) { + unsigned *id_len) { static const unsigned kMaxAttempts = 10; - unsigned int retry = 0; + unsigned retry = 0; do { if (!RAND_bytes(id, *id_len)) { return 0; @@ -257,8 +335,7 @@ int ssl_get_new_session(SSL *s, int session) { GEN_SESSION_CB cb = def_generate_session_id; if (s->mode & SSL_MODE_NO_SESSION_CREATION) { - OPENSSL_PUT_ERROR(SSL, ssl_get_new_session, - SSL_R_SESSION_MAY_NOT_BE_CREATED); + OPENSSL_PUT_ERROR(SSL, SSL_R_SESSION_MAY_NOT_BE_CREATED); return 0; } @@ -282,8 +359,7 @@ int ssl_get_new_session(SSL *s, int session) { ss->ssl_version = s->version; ss->session_id_length = SSL3_SSL_SESSION_ID_LENGTH; } else { - OPENSSL_PUT_ERROR(SSL, ssl_get_new_session, - SSL_R_UNSUPPORTED_SSL_VERSION); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_SSL_VERSION); SSL_SESSION_free(ss); return 0; } @@ -305,8 +381,7 @@ int ssl_get_new_session(SSL *s, int session) { tmp = ss->session_id_length; if (!cb(s, ss->session_id, &tmp)) { /* The callback failed */ - OPENSSL_PUT_ERROR(SSL, ssl_get_new_session, - SSL_R_SSL_SESSION_ID_CALLBACK_FAILED); + OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_SESSION_ID_CALLBACK_FAILED); SSL_SESSION_free(ss); return 0; } @@ -315,8 +390,7 @@ int ssl_get_new_session(SSL *s, int session) { * higher than it was. */ if (!tmp || tmp > ss->session_id_length) { /* The callback set an illegal length */ - OPENSSL_PUT_ERROR(SSL, ssl_get_new_session, - SSL_R_SSL_SESSION_ID_HAS_BAD_LENGTH); + OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_SESSION_ID_HAS_BAD_LENGTH); SSL_SESSION_free(ss); return 0; } @@ -324,8 +398,7 @@ int ssl_get_new_session(SSL *s, int session) { ss->session_id_length = tmp; /* Finally, check for a conflict */ if (SSL_has_matching_session_id(s, ss->session_id, ss->session_id_length)) { - OPENSSL_PUT_ERROR(SSL, ssl_get_new_session, - SSL_R_SSL_SESSION_ID_CONFLICT); + OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_SESSION_ID_CONFLICT); SSL_SESSION_free(ss); return 0; } @@ -334,7 +407,7 @@ int ssl_get_new_session(SSL *s, int session) { if (s->tlsext_hostname) { ss->tlsext_hostname = BUF_strdup(s->tlsext_hostname); if (ss->tlsext_hostname == NULL) { - OPENSSL_PUT_ERROR(SSL, ssl_get_new_session, ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); SSL_SESSION_free(ss); return 0; } @@ -344,7 +417,7 @@ int ssl_get_new_session(SSL *s, int session) { } if (s->sid_ctx_length > sizeof(ss->sid_ctx)) { - OPENSSL_PUT_ERROR(SSL, ssl_get_new_session, ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); SSL_SESSION_free(ss); return 0; } @@ -358,121 +431,110 @@ int ssl_get_new_session(SSL *s, int session) { return 1; } -/* ssl_get_prev attempts to find an SSL_SESSION to be used to resume this - * connection. It is only called by servers. - * - * ctx: contains the early callback context, which is the result of a - * shallow parse of the ClientHello. - * - * Returns: - * -1: error - * 0: a session may have been found. - * - * Side effects: - * - If a session is found then s->session is pointed at it (after freeing an - * existing session if need be) and s->verify_result is set from the session. - * - Both for new and resumed sessions, s->tlsext_ticket_expected is set to 1 - * if the server should issue a new session ticket (to 0 otherwise). */ -int ssl_get_prev_session(SSL *s, const struct ssl_early_callback_ctx *ctx) { - /* This is used only by servers. */ - SSL_SESSION *ret = NULL; - int fatal = 0; - int try_session_cache = 1; - int r; - - if (ctx->session_id_len > SSL_MAX_SSL_SESSION_ID_LENGTH) { - goto err; - } +/* ssl_lookup_session looks up |session_id| in the session cache and sets + * |*out_session| to an |SSL_SESSION| object if found. The caller takes + * ownership of the result. */ +static enum ssl_session_result_t ssl_lookup_session( + SSL *ssl, SSL_SESSION **out_session, const uint8_t *session_id, + size_t session_id_len) { + *out_session = NULL; - if (ctx->session_id_len == 0) { - try_session_cache = 0; + if (session_id_len == 0 || session_id_len > SSL_MAX_SSL_SESSION_ID_LENGTH) { + return ssl_session_success; } - r = tls1_process_ticket(s, ctx, &ret); /* sets s->tlsext_ticket_expected */ - switch (r) { - case -1: /* Error during processing */ - fatal = 1; - goto err; - - case 0: /* No ticket found */ - case 1: /* Zero length ticket found */ - break; /* Ok to carry on processing session id. */ - - case 2: /* Ticket found but not decrypted. */ - case 3: /* Ticket decrypted, *ret has been set. */ - try_session_cache = 0; - break; - - default: - abort(); - } - - if (try_session_cache && ret == NULL && - !(s->initial_ctx->session_cache_mode & + SSL_SESSION *session; + /* Try the internal cache, if it exists. */ + if (!(ssl->initial_ctx->session_cache_mode & SSL_SESS_CACHE_NO_INTERNAL_LOOKUP)) { SSL_SESSION data; - data.ssl_version = s->version; - data.session_id_length = ctx->session_id_len; - if (ctx->session_id_len == 0) { - return 0; + data.ssl_version = ssl->version; + data.session_id_length = session_id_len; + memcpy(data.session_id, session_id, session_id_len); + + CRYPTO_MUTEX_lock_read(&ssl->initial_ctx->lock); + session = lh_SSL_SESSION_retrieve(ssl->initial_ctx->sessions, &data); + if (session != NULL) { + SSL_SESSION_up_ref(session); } - memcpy(data.session_id, ctx->session_id, ctx->session_id_len); + CRYPTO_MUTEX_unlock(&ssl->initial_ctx->lock); - CRYPTO_MUTEX_lock_read(&s->initial_ctx->lock); - ret = lh_SSL_SESSION_retrieve(s->initial_ctx->sessions, &data); - CRYPTO_MUTEX_unlock(&s->initial_ctx->lock); - - if (ret != NULL) { - SSL_SESSION_up_ref(ret); + if (session != NULL) { + *out_session = session; + return ssl_session_success; } } - if (try_session_cache && ret == NULL && - s->initial_ctx->get_session_cb != NULL) { - int copy = 1; - - ret = s->initial_ctx->get_session_cb(s, (uint8_t *)ctx->session_id, - ctx->session_id_len, ©); - if (ret != NULL) { - if (ret == SSL_magic_pending_session_ptr()) { - /* This is a magic value which indicates that the callback needs to - * unwind the stack and figure out the session asynchronously. */ - return PENDING_SESSION; - } - - /* Increment reference count now if the session callback asks us to do so - * (note that if the session structures returned by the callback are - * shared between threads, it must handle the reference count itself - * [i.e. copy == 0], or things won't be thread-safe). */ - if (copy) { - SSL_SESSION_up_ref(ret); - } + /* Fall back to the external cache, if it exists. */ + if (ssl->initial_ctx->get_session_cb == NULL) { + return ssl_session_success; + } + int copy = 1; + session = ssl->initial_ctx->get_session_cb(ssl, (uint8_t *)session_id, + session_id_len, ©); + if (session == NULL) { + return ssl_session_success; + } + if (session == SSL_magic_pending_session_ptr()) { + return ssl_session_retry; + } - /* Add the externally cached session to the internal cache as well if and - * only if we are supposed to. */ - if (!(s->initial_ctx->session_cache_mode & - SSL_SESS_CACHE_NO_INTERNAL_STORE)) { - /* The following should not return 1, otherwise, things are very - * strange */ - SSL_CTX_add_session(s->initial_ctx, ret); - } - } + /* Increment reference count now if the session callback asks us to do so + * (note that if the session structures returned by the callback are shared + * between threads, it must handle the reference count itself [i.e. copy == + * 0], or things won't be thread-safe). */ + if (copy) { + SSL_SESSION_up_ref(session); } - if (ret == NULL) { - goto err; + /* Add the externally cached session to the internal cache if necessary. */ + if (!(ssl->initial_ctx->session_cache_mode & + SSL_SESS_CACHE_NO_INTERNAL_STORE)) { + SSL_CTX_add_session(ssl->initial_ctx, session); } - /* Now ret is non-NULL and we own one of its reference counts. */ + *out_session = session; + return ssl_session_success; +} - if (ret->sid_ctx_length != s->sid_ctx_length || - memcmp(ret->sid_ctx, s->sid_ctx, ret->sid_ctx_length)) { - /* We have the session requested by the client, but we don't want to use it - * in this context. */ - goto err; /* treat like cache miss */ +enum ssl_session_result_t ssl_get_prev_session( + SSL *ssl, SSL_SESSION **out_session, int *out_send_ticket, + const struct ssl_early_callback_ctx *ctx) { + /* This is used only by servers. */ + assert(ssl->server); + SSL_SESSION *session = NULL; + int send_ticket = 0; + + /* If tickets are disabled, always behave as if no tickets are present. */ + const uint8_t *ticket = NULL; + size_t ticket_len = 0; + const int tickets_supported = + !(SSL_get_options(ssl) & SSL_OP_NO_TICKET) && + (ssl->version > SSL3_VERSION || ctx->extensions != NULL) && + SSL_early_callback_ctx_extension_get(ctx, TLSEXT_TYPE_session_ticket, + &ticket, &ticket_len); + if (tickets_supported) { + if (!tls_process_ticket(ssl, &session, &send_ticket, ticket, ticket_len, + ctx->session_id, ctx->session_id_len)) { + return ssl_session_error; + } + } else { + /* The client does not support session tickets, so the session ID should be + * used instead. */ + enum ssl_session_result_t lookup_ret = ssl_lookup_session( + ssl, &session, ctx->session_id, ctx->session_id_len); + if (lookup_ret != ssl_session_success) { + return lookup_ret; + } } - if ((s->verify_mode & SSL_VERIFY_PEER) && s->sid_ctx_length == 0) { + if (session == NULL || + session->sid_ctx_length != ssl->sid_ctx_length || + memcmp(session->sid_ctx, ssl->sid_ctx, ssl->sid_ctx_length) != 0) { + goto no_session; + } + + if ((ssl->verify_mode & SSL_VERIFY_PEER) && ssl->sid_ctx_length == 0) { /* We can't be sure if this session is being used out of context, which is * especially important for SSL_VERIFY_PEER. The application should have * used SSL[_CTX]_set_session_id_context. @@ -481,83 +543,76 @@ int ssl_get_prev_session(SSL *s, const struct ssl_early_callback_ctx *ctx) { * like a cache miss (otherwise it would be easy for applications to * effectively disable the session cache by accident without anyone * noticing). */ - OPENSSL_PUT_ERROR(SSL, ssl_get_prev_session, - SSL_R_SESSION_ID_CONTEXT_UNINITIALIZED); - fatal = 1; - goto err; + OPENSSL_PUT_ERROR(SSL, SSL_R_SESSION_ID_CONTEXT_UNINITIALIZED); + goto fatal_error; } - if (ret->timeout < (long)(time(NULL) - ret->time)) { - /* timeout */ - if (try_session_cache) { - /* session was from the cache, so remove it */ - SSL_CTX_remove_session(s->initial_ctx, ret); + if (session->timeout < (long)(time(NULL) - session->time)) { + if (!tickets_supported) { + /* The session was from the cache, so remove it. */ + SSL_CTX_remove_session(ssl->initial_ctx, session); } - goto err; + goto no_session; } - SSL_SESSION_free(s->session); - s->session = ret; - s->verify_result = s->session->verify_result; - return 1; + *out_session = session; + *out_send_ticket = send_ticket; + return ssl_session_success; -err: - if (ret != NULL) { - SSL_SESSION_free(ret); - if (!try_session_cache) { - /* The session was from a ticket, so we should - * issue a ticket for the new session */ - s->tlsext_ticket_expected = 1; - } - } - if (fatal) { - return -1; - } - return 0; +fatal_error: + SSL_SESSION_free(session); + return ssl_session_error; + +no_session: + *out_session = NULL; + *out_send_ticket = tickets_supported; + SSL_SESSION_free(session); + return ssl_session_success; } -int SSL_CTX_add_session(SSL_CTX *ctx, SSL_SESSION *c) { +int SSL_CTX_add_session(SSL_CTX *ctx, SSL_SESSION *session) { int ret = 0; - SSL_SESSION *s; + SSL_SESSION *old_session; - /* add just 1 reference count for the SSL_CTX's session cache even though it + /* Add just 1 reference count for the |SSL_CTX|'s session cache even though it * has two ways of access: each session is in a doubly linked list and an - * lhash */ - SSL_SESSION_up_ref(c); - /* if session c is in already in cache, we take back the increment later */ + * lhash. */ + SSL_SESSION_up_ref(session); + /* If |session| is in already in cache, we take back the increment later. */ CRYPTO_MUTEX_lock_write(&ctx->lock); - if (!lh_SSL_SESSION_insert(ctx->sessions, &s, c)) { + if (!lh_SSL_SESSION_insert(ctx->sessions, &old_session, session)) { CRYPTO_MUTEX_unlock(&ctx->lock); + SSL_SESSION_free(session); return 0; } - /* s != NULL iff we already had a session with the given PID. In this case, s - * == c should hold (then we did not really modify ctx->sessions), or we're - * in trouble. */ - if (s != NULL && s != c) { + /* |old_session| != NULL iff we already had a session with the given session + * ID. In this case, |old_session| == |session| should hold (then we did not + * really modify |ctx->sessions|), or we're in trouble. */ + if (old_session != NULL && old_session != session) { /* We *are* in trouble ... */ - SSL_SESSION_list_remove(ctx, s); - SSL_SESSION_free(s); + SSL_SESSION_list_remove(ctx, old_session); + SSL_SESSION_free(old_session); /* ... so pretend the other session did not exist in cache (we cannot - * handle two SSL_SESSION structures with identical session ID in the same + * handle two |SSL_SESSION| structures with identical session ID in the same * cache, which could happen e.g. when two threads concurrently obtain the - * same session from an external cache) */ - s = NULL; + * same session from an external cache). */ + old_session = NULL; } - /* Put at the head of the queue unless it is already in the cache */ - if (s == NULL) { - SSL_SESSION_list_add(ctx, c); + /* Put at the head of the queue unless it is already in the cache. */ + if (old_session == NULL) { + SSL_SESSION_list_add(ctx, session); } - if (s != NULL) { - /* existing cache entry -- decrement previously incremented reference count - * because it already takes into account the cache */ - SSL_SESSION_free(s); /* s == c */ + if (old_session != NULL) { + /* Existing cache entry -- decrement previously incremented reference count + * because it already takes into account the cache. */ + SSL_SESSION_free(old_session); /* |old_session| == |session| */ ret = 0; } else { - /* new cache entry -- remove old ones if cache has become too large */ + /* New cache entry -- remove old ones if cache has become too large. */ ret = 1; if (SSL_CTX_sess_get_cache_size(ctx) > 0) { @@ -573,23 +628,23 @@ int SSL_CTX_add_session(SSL_CTX *ctx, SSL_SESSION *c) { return ret; } -int SSL_CTX_remove_session(SSL_CTX *ctx, SSL_SESSION *c) { - return remove_session_lock(ctx, c, 1); +int SSL_CTX_remove_session(SSL_CTX *ctx, SSL_SESSION *session) { + return remove_session_lock(ctx, session, 1); } -static int remove_session_lock(SSL_CTX *ctx, SSL_SESSION *c, int lock) { - SSL_SESSION *r; +static int remove_session_lock(SSL_CTX *ctx, SSL_SESSION *session, int lock) { int ret = 0; - if (c != NULL && c->session_id_length != 0) { + if (session != NULL && session->session_id_length != 0) { if (lock) { CRYPTO_MUTEX_lock_write(&ctx->lock); } - r = lh_SSL_SESSION_retrieve(ctx->sessions, c); - if (r == c) { + SSL_SESSION *found_session = lh_SSL_SESSION_retrieve(ctx->sessions, + session); + if (found_session == session) { ret = 1; - r = lh_SSL_SESSION_delete(ctx->sessions, c); - SSL_SESSION_list_remove(ctx, c); + found_session = lh_SSL_SESSION_delete(ctx->sessions, session); + SSL_SESSION_list_remove(ctx, session); } if (lock) { @@ -597,127 +652,48 @@ static int remove_session_lock(SSL_CTX *ctx, SSL_SESSION *c, int lock) { } if (ret) { - r->not_resumable = 1; + found_session->not_resumable = 1; if (ctx->remove_session_cb != NULL) { - ctx->remove_session_cb(ctx, r); + ctx->remove_session_cb(ctx, found_session); } - SSL_SESSION_free(r); + SSL_SESSION_free(found_session); } } return ret; } -SSL_SESSION *SSL_SESSION_up_ref(SSL_SESSION *session) { - if (session) { - CRYPTO_refcount_inc(&session->references); - } - return session; -} - -void SSL_SESSION_free(SSL_SESSION *session) { - if (session == NULL || - !CRYPTO_refcount_dec_and_test_zero(&session->references)) { - return; - } - - CRYPTO_free_ex_data(&g_ex_data_class, session, &session->ex_data); - - OPENSSL_cleanse(session->master_key, sizeof(session->master_key)); - OPENSSL_cleanse(session->session_id, sizeof(session->session_id)); - ssl_sess_cert_free(session->sess_cert); - X509_free(session->peer); - OPENSSL_free(session->tlsext_hostname); - OPENSSL_free(session->tlsext_tick); - OPENSSL_free(session->tlsext_signed_cert_timestamp_list); - OPENSSL_free(session->ocsp_response); - OPENSSL_free(session->psk_identity); - OPENSSL_cleanse(session, sizeof(*session)); - OPENSSL_free(session); -} - -int SSL_set_session(SSL *s, SSL_SESSION *session) { - if (s->session == session) { +int SSL_set_session(SSL *ssl, SSL_SESSION *session) { + if (ssl->session == session) { return 1; } - SSL_SESSION_free(s->session); - s->session = session; + SSL_SESSION_free(ssl->session); + ssl->session = session; if (session != NULL) { SSL_SESSION_up_ref(session); - s->verify_result = session->verify_result; + ssl->verify_result = session->verify_result; } return 1; } -long SSL_SESSION_set_timeout(SSL_SESSION *s, long t) { - if (s == NULL) { +long SSL_CTX_set_timeout(SSL_CTX *ctx, long timeout) { + if (ctx == NULL) { return 0; } - s->timeout = t; - return 1; + long old_timeout = ctx->session_timeout; + ctx->session_timeout = timeout; + return old_timeout; } -long SSL_SESSION_get_timeout(const SSL_SESSION *s) { - if (s == NULL) { +long SSL_CTX_get_timeout(const SSL_CTX *ctx) { + if (ctx == NULL) { return 0; } - return s->timeout; -} - -long SSL_SESSION_get_time(const SSL_SESSION *s) { - if (s == NULL) { - return 0; - } - - return s->time; -} - -long SSL_SESSION_set_time(SSL_SESSION *s, long t) { - if (s == NULL) { - return 0; - } - - s->time = t; - return t; -} - -X509 *SSL_SESSION_get0_peer(SSL_SESSION *s) { return s->peer; } - -int SSL_SESSION_set1_id_context(SSL_SESSION *s, const uint8_t *sid_ctx, - unsigned int sid_ctx_len) { - if (sid_ctx_len > SSL_MAX_SID_CTX_LENGTH) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_set1_id_context, - SSL_R_SSL_SESSION_ID_CONTEXT_TOO_LONG); - return 0; - } - - s->sid_ctx_length = sid_ctx_len; - memcpy(s->sid_ctx, sid_ctx, sid_ctx_len); - - return 1; -} - -long SSL_CTX_set_timeout(SSL_CTX *s, long t) { - long l; - if (s == NULL) { - return 0; - } - - l = s->session_timeout; - s->session_timeout = t; - return l; -} - -long SSL_CTX_get_timeout(const SSL_CTX *s) { - if (s == NULL) { - return 0; - } - - return s->session_timeout; + return ctx->session_timeout; } typedef struct timeout_param_st { @@ -726,25 +702,25 @@ typedef struct timeout_param_st { LHASH_OF(SSL_SESSION) *cache; } TIMEOUT_PARAM; -static void timeout_doall_arg(SSL_SESSION *sess, void *void_param) { +static void timeout_doall_arg(SSL_SESSION *session, void *void_param) { TIMEOUT_PARAM *param = void_param; if (param->time == 0 || - param->time > (sess->time + sess->timeout)) { + param->time > (session->time + session->timeout)) { /* timeout */ /* The reason we don't call SSL_CTX_remove_session() is to * save on locking overhead */ - (void) lh_SSL_SESSION_delete(param->cache, sess); - SSL_SESSION_list_remove(param->ctx, sess); - sess->not_resumable = 1; + (void) lh_SSL_SESSION_delete(param->cache, session); + SSL_SESSION_list_remove(param->ctx, session); + session->not_resumable = 1; if (param->ctx->remove_session_cb != NULL) { - param->ctx->remove_session_cb(param->ctx, sess); + param->ctx->remove_session_cb(param->ctx, session); } - SSL_SESSION_free(sess); + SSL_SESSION_free(session); } } -void SSL_CTX_flush_sessions(SSL_CTX *ctx, long t) { +void SSL_CTX_flush_sessions(SSL_CTX *ctx, long time) { TIMEOUT_PARAM tp; tp.ctx = ctx; @@ -752,7 +728,7 @@ void SSL_CTX_flush_sessions(SSL_CTX *ctx, long t) { if (tp.cache == NULL) { return; } - tp.time = t; + tp.time = time; CRYPTO_MUTEX_lock_write(&ctx->lock); lh_SSL_SESSION_doall_arg(tp.cache, timeout_doall_arg, &tp); CRYPTO_MUTEX_unlock(&ctx->lock); @@ -769,80 +745,80 @@ int ssl_clear_bad_session(SSL *s) { } /* locked by SSL_CTX in the calling function */ -static void SSL_SESSION_list_remove(SSL_CTX *ctx, SSL_SESSION *s) { - if (s->next == NULL || s->prev == NULL) { +static void SSL_SESSION_list_remove(SSL_CTX *ctx, SSL_SESSION *session) { + if (session->next == NULL || session->prev == NULL) { return; } - if (s->next == (SSL_SESSION *)&ctx->session_cache_tail) { + if (session->next == (SSL_SESSION *)&ctx->session_cache_tail) { /* last element in list */ - if (s->prev == (SSL_SESSION *)&ctx->session_cache_head) { + if (session->prev == (SSL_SESSION *)&ctx->session_cache_head) { /* only one element in list */ ctx->session_cache_head = NULL; ctx->session_cache_tail = NULL; } else { - ctx->session_cache_tail = s->prev; - s->prev->next = (SSL_SESSION *)&(ctx->session_cache_tail); + ctx->session_cache_tail = session->prev; + session->prev->next = (SSL_SESSION *)&(ctx->session_cache_tail); } } else { - if (s->prev == (SSL_SESSION *)&ctx->session_cache_head) { + if (session->prev == (SSL_SESSION *)&ctx->session_cache_head) { /* first element in list */ - ctx->session_cache_head = s->next; - s->next->prev = (SSL_SESSION *)&(ctx->session_cache_head); + ctx->session_cache_head = session->next; + session->next->prev = (SSL_SESSION *)&(ctx->session_cache_head); } else { /* middle of list */ - s->next->prev = s->prev; - s->prev->next = s->next; + session->next->prev = session->prev; + session->prev->next = session->next; } } - s->prev = s->next = NULL; + session->prev = session->next = NULL; } -static void SSL_SESSION_list_add(SSL_CTX *ctx, SSL_SESSION *s) { - if (s->next != NULL && s->prev != NULL) { - SSL_SESSION_list_remove(ctx, s); +static void SSL_SESSION_list_add(SSL_CTX *ctx, SSL_SESSION *session) { + if (session->next != NULL && session->prev != NULL) { + SSL_SESSION_list_remove(ctx, session); } if (ctx->session_cache_head == NULL) { - ctx->session_cache_head = s; - ctx->session_cache_tail = s; - s->prev = (SSL_SESSION *)&(ctx->session_cache_head); - s->next = (SSL_SESSION *)&(ctx->session_cache_tail); + ctx->session_cache_head = session; + ctx->session_cache_tail = session; + session->prev = (SSL_SESSION *)&(ctx->session_cache_head); + session->next = (SSL_SESSION *)&(ctx->session_cache_tail); } else { - s->next = ctx->session_cache_head; - s->next->prev = s; - s->prev = (SSL_SESSION *)&(ctx->session_cache_head); - ctx->session_cache_head = s; + session->next = ctx->session_cache_head; + session->next->prev = session; + session->prev = (SSL_SESSION *)&(ctx->session_cache_head); + ctx->session_cache_head = session; } } void SSL_CTX_sess_set_new_cb(SSL_CTX *ctx, - int (*cb)(struct ssl_st *ssl, SSL_SESSION *sess)) { + int (*cb)(SSL *ssl, SSL_SESSION *session)) { ctx->new_session_cb = cb; } -int (*SSL_CTX_sess_get_new_cb(SSL_CTX *ctx))(SSL *ssl, SSL_SESSION *sess) { +int (*SSL_CTX_sess_get_new_cb(SSL_CTX *ctx))(SSL *ssl, SSL_SESSION *session) { return ctx->new_session_cb; } -void SSL_CTX_sess_set_remove_cb(SSL_CTX *ctx, - void (*cb)(SSL_CTX *ctx, SSL_SESSION *sess)) { +void SSL_CTX_sess_set_remove_cb( + SSL_CTX *ctx, void (*cb)(SSL_CTX *ctx, SSL_SESSION *session)) { ctx->remove_session_cb = cb; } void (*SSL_CTX_sess_get_remove_cb(SSL_CTX *ctx))(SSL_CTX *ctx, - SSL_SESSION *sess) { + SSL_SESSION *session) { return ctx->remove_session_cb; } void SSL_CTX_sess_set_get_cb(SSL_CTX *ctx, - SSL_SESSION *(*cb)(struct ssl_st *ssl, - uint8_t *data, int len, - int *copy)) { + SSL_SESSION *(*cb)(SSL *ssl, + uint8_t *id, int id_len, + int *out_copy)) { ctx->get_session_cb = cb; } -SSL_SESSION *(*SSL_CTX_sess_get_get_cb(SSL_CTX *ctx))(SSL *ssl, uint8_t *data, - int len, int *copy) { +SSL_SESSION *(*SSL_CTX_sess_get_get_cb(SSL_CTX *ctx))( + SSL *ssl, uint8_t *id, int id_len, int *out_copy) { return ctx->get_session_cb; } @@ -874,5 +850,3 @@ void SSL_CTX_set_channel_id_cb(SSL_CTX *ctx, void (*SSL_CTX_get_channel_id_cb(SSL_CTX *ctx))(SSL *ssl, EVP_PKEY **pkey) { return ctx->channel_id_cb; } - -IMPLEMENT_PEM_rw(SSL_SESSION, SSL_SESSION, PEM_STRING_SSL_SESSION, SSL_SESSION) diff --git a/src/ssl/ssl_stat.c b/src/ssl/ssl_stat.c index fa5541b..5ad1e47 100644 --- a/src/ssl/ssl_stat.c +++ b/src/ssl/ssl_stat.c @@ -82,9 +82,11 @@ * OTHERWISE. */ -#include <stdio.h> +#include <openssl/ssl.h> + #include "internal.h" + const char *SSL_state_string_long(const SSL *s) { const char *str; @@ -334,37 +336,6 @@ const char *SSL_state_string_long(const SSL *s) { str = "SSLv3 read certificate verify B"; break; - /* SSLv2/v3 compatibility states */ - /* client */ - case SSL23_ST_CW_CLNT_HELLO_A: - str = "SSLv2/v3 write client hello A"; - break; - - case SSL23_ST_CW_CLNT_HELLO_B: - str = "SSLv2/v3 write client hello B"; - break; - - case SSL23_ST_CR_SRVR_HELLO_A: - str = "SSLv2/v3 read server hello A"; - break; - - case SSL23_ST_CR_SRVR_HELLO_B: - str = "SSLv2/v3 read server hello B"; - break; - - /* server */ - case SSL23_ST_SR_CLNT_HELLO: - str = "SSLv2/v3 read client hello"; - break; - - case SSL23_ST_SR_V2_CLNT_HELLO: - str = "SSLv2/v3 read v2 client hello"; - break; - - case SSL23_ST_SR_SWITCH_VERSION: - str = "SSLv2/v3 switch version"; - break; - /* DTLS */ case DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A: str = "DTLS1 read hello verify request A"; @@ -382,30 +353,6 @@ const char *SSL_state_string_long(const SSL *s) { return str; } -const char *SSL_rstate_string_long(const SSL *s) { - const char *str; - - switch (s->rstate) { - case SSL_ST_READ_HEADER: - str = "read header"; - break; - - case SSL_ST_READ_BODY: - str = "read body"; - break; - - case SSL_ST_READ_DONE: - str = "read done"; - break; - - default: - str = "unknown"; - break; - } - - return str; -} - const char *SSL_state_string(const SSL *s) { const char *str; @@ -635,37 +582,6 @@ const char *SSL_state_string(const SSL *s) { str = "3RCV_B"; break; - /* SSLv2/v3 compatibility states */ - /* client */ - case SSL23_ST_CW_CLNT_HELLO_A: - str = "23WCHA"; - break; - - case SSL23_ST_CW_CLNT_HELLO_B: - str = "23WCHB"; - break; - - case SSL23_ST_CR_SRVR_HELLO_A: - str = "23RSHA"; - break; - - case SSL23_ST_CR_SRVR_HELLO_B: - str = "23RSHA"; - break; - - /* server */ - case SSL23_ST_SR_CLNT_HELLO: - str = "23RCH_"; - break; - - case SSL23_ST_SR_V2_CLNT_HELLO: - str = "23R2CH"; - break; - - case SSL23_ST_SR_SWITCH_VERSION: - str = "23RSW_"; - break; - /* DTLS */ case DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A: str = "DRCHVA"; @@ -968,27 +884,3 @@ const char *SSL_alert_desc_string_long(int value) { return str; } - -const char *SSL_rstate_string(const SSL *s) { - const char *str; - - switch (s->rstate) { - case SSL_ST_READ_HEADER: - str = "RH"; - break; - - case SSL_ST_READ_BODY: - str = "RB"; - break; - - case SSL_ST_READ_DONE: - str = "RD"; - break; - - default: - str = "unknown"; - break; - } - - return str; -} diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc index 9f2ddb9..810d3fa 100644 --- a/src/ssl/ssl_test.cc +++ b/src/ssl/ssl_test.cc @@ -24,6 +24,8 @@ #include <openssl/ssl.h> #include "test/scoped_types.h" +#include "../crypto/test/test_util.h" + struct ExpectedCipher { unsigned long id; @@ -212,6 +214,21 @@ static const char *kBadRules[] = { NULL, }; +static const char *kMustNotIncludeNull[] = { + "ALL", + "DEFAULT", + "ALL:!eNULL", + "ALL:!NULL", + "FIPS", + "SHA", + "SHA1", + "RSA", + "SSLv3", + "TLSv1", + "TLSv1.2", + NULL +}; + static void PrintCipherPreferenceList(ssl_cipher_preference_list_st *list) { bool in_group = false; for (size_t i = 0; i < sk_SSL_CIPHER_num(list->ciphers); i++) { @@ -265,6 +282,24 @@ static bool TestCipherRule(CipherTest *t) { return true; } +static bool TestRuleDoesNotIncludeNull(const char *rule) { + ScopedSSL_CTX ctx(SSL_CTX_new(SSLv23_server_method())); + if (!ctx) { + return false; + } + if (!SSL_CTX_set_cipher_list(ctx.get(), rule)) { + fprintf(stderr, "Error: cipher rule '%s' failed\n", rule); + return false; + } + for (size_t i = 0; i < sk_SSL_CIPHER_num(ctx->cipher_list->ciphers); i++) { + if (SSL_CIPHER_is_NULL(sk_SSL_CIPHER_value(ctx->cipher_list->ciphers, i))) { + fprintf(stderr, "Error: cipher rule '%s' includes NULL\n",rule); + return false; + } + } + return true; +} + static bool TestCipherRules() { for (size_t i = 0; kCipherTests[i].rule != NULL; i++) { if (!TestCipherRule(&kCipherTests[i])) { @@ -284,6 +319,12 @@ static bool TestCipherRules() { ERR_clear_error(); } + for (size_t i = 0; kMustNotIncludeNull[i] != NULL; i++) { + if (!TestRuleDoesNotIncludeNull(kMustNotIncludeNull[i])) { + return false; + } + } + return true; } @@ -335,6 +376,104 @@ static const char kCustomSession[] = "q+Topyzvx9USFgRvyuoxn0Hgb+R0A3j6SLRuyOdAi4gv7Y5oliynrSIEIAYGBgYG" "BgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGrgMEAQevAwQBBLADBAEF"; +// kBoringSSLSession is a serialized SSL_SESSION generated from bssl client. +static const char kBoringSSLSession[] = + "MIIRwQIBAQICAwMEAsAvBCDdoGxGK26mR+8lM0uq6+k9xYuxPnwAjpcF9n0Yli9R" + "kQQwbyshfWhdi5XQ1++7n2L1qqrcVlmHBPpr6yknT/u4pUrpQB5FZ7vqvNn8MdHf" + "9rWgoQYCBFXgs7uiBAICHCCjggR6MIIEdjCCA16gAwIBAgIIf+yfD7Y6UicwDQYJ" + "KoZIhvcNAQELBQAwSTELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMx" + "JTAjBgNVBAMTHEdvb2dsZSBJbnRlcm5ldCBBdXRob3JpdHkgRzIwHhcNMTUwODEy" + "MTQ1MzE1WhcNMTUxMTEwMDAwMDAwWjBoMQswCQYDVQQGEwJVUzETMBEGA1UECAwK" + "Q2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzETMBEGA1UECgwKR29v" + "Z2xlIEluYzEXMBUGA1UEAwwOd3d3Lmdvb2dsZS5jb20wggEiMA0GCSqGSIb3DQEB" + "AQUAA4IBDwAwggEKAoIBAQC0MeG5YGQ0t+IeJeoneP/PrhEaieibeKYkbKVLNZpo" + "PLuBinvhkXZo3DC133NpCBpy6ZktBwamqyixAyuk/NU6OjgXqwwxfQ7di1AInLIU" + "792c7hFyNXSUCG7At8Ifi3YwBX9Ba6u/1d6rWTGZJrdCq3QU11RkKYyTq2KT5mce" + "Tv9iGKqSkSTlp8puy/9SZ/3DbU3U+BuqCFqeSlz7zjwFmk35acdCilpJlVDDN5C/" + "RCh8/UKc8PaL+cxlt531qoTENvYrflBno14YEZlCBZsPiFeUSILpKEj3Ccwhy0eL" + "EucWQ72YZU8mUzXBoXGn0zA0crFl5ci/2sTBBGZsylNBAgMBAAGjggFBMIIBPTAd" + "BgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwGQYDVR0RBBIwEIIOd3d3Lmdv" + "b2dsZS5jb20waAYIKwYBBQUHAQEEXDBaMCsGCCsGAQUFBzAChh9odHRwOi8vcGtp" + "Lmdvb2dsZS5jb20vR0lBRzIuY3J0MCsGCCsGAQUFBzABhh9odHRwOi8vY2xpZW50" + "czEuZ29vZ2xlLmNvbS9vY3NwMB0GA1UdDgQWBBS/bzHxcE73Q4j3slC4BLbMtLjG" + "GjAMBgNVHRMBAf8EAjAAMB8GA1UdIwQYMBaAFErdBhYbvPZotXb1gba7Yhq6WoEv" + "MBcGA1UdIAQQMA4wDAYKKwYBBAHWeQIFATAwBgNVHR8EKTAnMCWgI6Ahhh9odHRw" + "Oi8vcGtpLmdvb2dsZS5jb20vR0lBRzIuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQAb" + "qdWPZEHk0X7iKPCTHL6S3w6q1eR67goxZGFSM1lk1hjwyu7XcLJuvALVV9uY3ovE" + "kQZSHwT+pyOPWQhsSjO+1GyjvCvK/CAwiUmBX+bQRGaqHsRcio7xSbdVcajQ3bXd" + "X+s0WdbOpn6MStKAiBVloPlSxEI8pxY6x/BBCnTIk/+DMB17uZlOjG3vbAnkDkP+" + "n0OTucD9sHV7EVj9XUxi51nOfNBCN/s7lpUjDS/NJ4k3iwOtbCPswiot8vLO779a" + "f07vR03r349Iz/KTzk95rlFtX0IU+KYNxFNsanIXZ+C9FYGRXkwhHcvFb4qMUB1y" + "TTlM80jBMOwyjZXmjRAhpAIEAKUDAgEUqQUCAwGJwKqBpwSBpOgebbmn9NRUtMWH" + "+eJpqA5JLMFSMCChOsvKey3toBaCNGU7HfAEiiXNuuAdCBoK262BjQc2YYfqFzqH" + "zuppopXCvhohx7j/tnCNZIMgLYt/O9SXK2RYI5z8FhCCHvB4CbD5G0LGl5EFP27s" + "Jb6S3aTTYPkQe8yZSlxevg6NDwmTogLO9F7UUkaYmVcMQhzssEE2ZRYNwSOU6KjE" + "0Yj+8fAiBtbQriIEIN2L8ZlpaVrdN5KFNdvcmOxJu81P8q53X55xQyGTnGWwsgMC" + "ARezggvvMIIEdjCCA16gAwIBAgIIf+yfD7Y6UicwDQYJKoZIhvcNAQELBQAwSTEL" + "MAkGA1UEBhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMxJTAjBgNVBAMTHEdvb2ds" + "ZSBJbnRlcm5ldCBBdXRob3JpdHkgRzIwHhcNMTUwODEyMTQ1MzE1WhcNMTUxMTEw" + "MDAwMDAwWjBoMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQG" + "A1UEBwwNTW91bnRhaW4gVmlldzETMBEGA1UECgwKR29vZ2xlIEluYzEXMBUGA1UE" + "AwwOd3d3Lmdvb2dsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB" + "AQC0MeG5YGQ0t+IeJeoneP/PrhEaieibeKYkbKVLNZpoPLuBinvhkXZo3DC133Np" + "CBpy6ZktBwamqyixAyuk/NU6OjgXqwwxfQ7di1AInLIU792c7hFyNXSUCG7At8If" + "i3YwBX9Ba6u/1d6rWTGZJrdCq3QU11RkKYyTq2KT5mceTv9iGKqSkSTlp8puy/9S" + "Z/3DbU3U+BuqCFqeSlz7zjwFmk35acdCilpJlVDDN5C/RCh8/UKc8PaL+cxlt531" + "qoTENvYrflBno14YEZlCBZsPiFeUSILpKEj3Ccwhy0eLEucWQ72YZU8mUzXBoXGn" + "0zA0crFl5ci/2sTBBGZsylNBAgMBAAGjggFBMIIBPTAdBgNVHSUEFjAUBggrBgEF" + "BQcDAQYIKwYBBQUHAwIwGQYDVR0RBBIwEIIOd3d3Lmdvb2dsZS5jb20waAYIKwYB" + "BQUHAQEEXDBaMCsGCCsGAQUFBzAChh9odHRwOi8vcGtpLmdvb2dsZS5jb20vR0lB" + "RzIuY3J0MCsGCCsGAQUFBzABhh9odHRwOi8vY2xpZW50czEuZ29vZ2xlLmNvbS9v" + "Y3NwMB0GA1UdDgQWBBS/bzHxcE73Q4j3slC4BLbMtLjGGjAMBgNVHRMBAf8EAjAA" + "MB8GA1UdIwQYMBaAFErdBhYbvPZotXb1gba7Yhq6WoEvMBcGA1UdIAQQMA4wDAYK" + "KwYBBAHWeQIFATAwBgNVHR8EKTAnMCWgI6Ahhh9odHRwOi8vcGtpLmdvb2dsZS5j" + "b20vR0lBRzIuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQAbqdWPZEHk0X7iKPCTHL6S" + "3w6q1eR67goxZGFSM1lk1hjwyu7XcLJuvALVV9uY3ovEkQZSHwT+pyOPWQhsSjO+" + "1GyjvCvK/CAwiUmBX+bQRGaqHsRcio7xSbdVcajQ3bXdX+s0WdbOpn6MStKAiBVl" + "oPlSxEI8pxY6x/BBCnTIk/+DMB17uZlOjG3vbAnkDkP+n0OTucD9sHV7EVj9XUxi" + "51nOfNBCN/s7lpUjDS/NJ4k3iwOtbCPswiot8vLO779af07vR03r349Iz/KTzk95" + "rlFtX0IU+KYNxFNsanIXZ+C9FYGRXkwhHcvFb4qMUB1yTTlM80jBMOwyjZXmjRAh" + "MIID8DCCAtigAwIBAgIDAjqDMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT" + "MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i" + "YWwgQ0EwHhcNMTMwNDA1MTUxNTU2WhcNMTYxMjMxMjM1OTU5WjBJMQswCQYDVQQG" + "EwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzElMCMGA1UEAxMcR29vZ2xlIEludGVy" + "bmV0IEF1dGhvcml0eSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB" + "AJwqBHdc2FCROgajguDYUEi8iT/xGXAaiEZ+4I/F8YnOIe5a/mENtzJEiaB0C1NP" + "VaTOgmKV7utZX8bhBYASxF6UP7xbSDj0U/ck5vuR6RXEz/RTDfRK/J9U3n2+oGtv" + "h8DQUB8oMANA2ghzUWx//zo8pzcGjr1LEQTrfSTe5vn8MXH7lNVg8y5Kr0LSy+rE" + "ahqyzFPdFUuLH8gZYR/Nnag+YyuENWllhMgZxUYi+FOVvuOAShDGKuy6lyARxzmZ" + "EASg8GF6lSWMTlJ14rbtCMoU/M4iarNOz0YDl5cDfsCx3nuvRTPPuj5xt970JSXC" + "DTWJnZ37DhF5iR43xa+OcmkCAwEAAaOB5zCB5DAfBgNVHSMEGDAWgBTAephojYn7" + "qwVkDBF9qn1luMrMTjAdBgNVHQ4EFgQUSt0GFhu89mi1dvWBtrtiGrpagS8wDgYD" + "VR0PAQH/BAQDAgEGMC4GCCsGAQUFBwEBBCIwIDAeBggrBgEFBQcwAYYSaHR0cDov" + "L2cuc3ltY2QuY29tMBIGA1UdEwEB/wQIMAYBAf8CAQAwNQYDVR0fBC4wLDAqoCig" + "JoYkaHR0cDovL2cuc3ltY2IuY29tL2NybHMvZ3RnbG9iYWwuY3JsMBcGA1UdIAQQ" + "MA4wDAYKKwYBBAHWeQIFATANBgkqhkiG9w0BAQsFAAOCAQEAqvqpIM1qZ4PtXtR+" + "3h3Ef+AlBgDFJPupyC1tft6dgmUsgWM0Zj7pUsIItMsv91+ZOmqcUHqFBYx90SpI" + "hNMJbHzCzTWf84LuUt5oX+QAihcglvcpjZpNy6jehsgNb1aHA30DP9z6eX0hGfnI" + "Oi9RdozHQZJxjyXON/hKTAAj78Q1EK7gI4BzfE00LshukNYQHpmEcxpw8u1VDu4X" + "Bupn7jLrLN1nBz/2i8Jw3lsA5rsb0zYaImxssDVCbJAJPZPpZAkiDoUGn8JzIdPm" + "X4DkjYUiOnMDsWCOrmji9D6X52ASCWg23jrW4kOVWzeBkoEfu43XrVJkFleW2V40" + "fsg12DCCA30wggLmoAMCAQICAxK75jANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQG" + "EwJVUzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUg" + "Q2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTAyMDUyMTA0MDAwMFoXDTE4MDgyMTA0" + "MDAwMFowQjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xGzAZ" + "BgNVBAMTEkdlb1RydXN0IEdsb2JhbCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP" + "ADCCAQoCggEBANrMGGMw/fQXIxpWflvfPGw45HG3eJHUvKHYTPioQ7YD6U0hBwiI" + "2lgvZjkpvQV4i5046AW3an5xpObEYKaw74DkiSgPniXW7YPzraaRx5jJQhg1FJ2t" + "mEaSLk/K8YdDwRaVVy1Q74ktgHpXrfLuX2vSAI25FPgUFTXZwEaje3LIkb/JVSvN" + "0Jc+nCZkzN/Ogxlxyk7m1NV7qRnNVd7I7NJeOFPlXE+MLf5QIzb8ZubLjqQ5GQC3" + "lQI5kQsO/jgu0R0FmvZNPm8PBx2vLB6PYDni+jZTEznUXiYr2z2oFL0y6xgDKFIE" + "ceWrMz3hOLsHNoRinHnqFjD0X8Ar6HFr5PkCAwEAAaOB8DCB7TAfBgNVHSMEGDAW" + "gBRI5mj5K9KylddH2CMgEE8zmJCf1DAdBgNVHQ4EFgQUwHqYaI2J+6sFZAwRfap9" + "ZbjKzE4wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwOgYDVR0fBDMw" + "MTAvoC2gK4YpaHR0cDovL2NybC5nZW90cnVzdC5jb20vY3Jscy9zZWN1cmVjYS5j" + "cmwwTgYDVR0gBEcwRTBDBgRVHSAAMDswOQYIKwYBBQUHAgEWLWh0dHBzOi8vd3d3" + "Lmdlb3RydXN0LmNvbS9yZXNvdXJjZXMvcmVwb3NpdG9yeTANBgkqhkiG9w0BAQUF" + "AAOBgQB24RJuTksWEoYwBrKBCM/wCMfHcX5m7sLt1Dsf//DwyE7WQziwuTB9GNBV" + "g6JqyzYRnOhIZqNtf7gT1Ef+i1pcc/yu2RsyGTirlzQUqpbS66McFAhJtrvlke+D" + "NusdVm/K2rxzY5Dkf3s+Iss9B+1fOHSc4wNQTqGvmO5h8oQ/Eg=="; + // kBadSessionExtraField is a custom serialized SSL_SESSION generated by replacing // the final (optional) element of |kCustomSession| with tag number 30. static const char kBadSessionExtraField[] = @@ -418,6 +557,8 @@ static bool TestSSL_SESSIONEncoding(const char *input_b64) { if (encoded_len != input.size() || memcmp(bssl::vector_data(&input), encoded.get(), input.size()) != 0) { fprintf(stderr, "SSL_SESSION_to_bytes did not round-trip\n"); + hexdump(stderr, "Before: ", input.data(), input.size()); + hexdump(stderr, "After: ", encoded_raw, encoded_len); return false; } @@ -549,12 +690,141 @@ static bool TestCipherGetRFCName(void) { return true; } -int main(void) { +// CreateSessionWithTicket returns a sample |SSL_SESSION| with the ticket +// replaced for one of length |ticket_len| or nullptr on failure. +static ScopedSSL_SESSION CreateSessionWithTicket(size_t ticket_len) { + std::vector<uint8_t> der; + if (!DecodeBase64(&der, kOpenSSLSession)) { + return nullptr; + } + ScopedSSL_SESSION session(SSL_SESSION_from_bytes(bssl::vector_data(&der), + der.size())); + if (!session) { + return nullptr; + } + + // Swap out the ticket for a garbage one. + OPENSSL_free(session->tlsext_tick); + session->tlsext_tick = reinterpret_cast<uint8_t*>(OPENSSL_malloc(ticket_len)); + if (session->tlsext_tick == nullptr) { + return nullptr; + } + memset(session->tlsext_tick, 'a', ticket_len); + session->tlsext_ticklen = ticket_len; + return session; +} + +// GetClientHelloLen creates a client SSL connection with a ticket of length +// |ticket_len| and records the ClientHello. It returns the length of the +// ClientHello, not including the record header, on success and zero on error. +static size_t GetClientHelloLen(size_t ticket_len) { + ScopedSSL_CTX ctx(SSL_CTX_new(TLS_method())); + ScopedSSL_SESSION session = CreateSessionWithTicket(ticket_len); + if (!ctx || !session) { + return 0; + } + ScopedSSL ssl(SSL_new(ctx.get())); + ScopedBIO bio(BIO_new(BIO_s_mem())); + if (!ssl || !bio || !SSL_set_session(ssl.get(), session.get())) { + return 0; + } + // Do not configure a reading BIO, but record what's written to a memory BIO. + SSL_set_bio(ssl.get(), nullptr /* rbio */, BIO_up_ref(bio.get())); + int ret = SSL_connect(ssl.get()); + if (ret > 0) { + // SSL_connect should fail without a BIO to write to. + return 0; + } + ERR_clear_error(); + + const uint8_t *unused; + size_t client_hello_len; + if (!BIO_mem_contents(bio.get(), &unused, &client_hello_len) || + client_hello_len <= SSL3_RT_HEADER_LENGTH) { + return 0; + } + return client_hello_len - SSL3_RT_HEADER_LENGTH; +} + +struct PaddingTest { + size_t input_len, padded_len; +}; + +static const PaddingTest kPaddingTests[] = { + // ClientHellos of length below 0x100 do not require padding. + {0xfe, 0xfe}, + {0xff, 0xff}, + // ClientHellos of length 0x100 through 0x1fb are padded up to 0x200. + {0x100, 0x200}, + {0x123, 0x200}, + {0x1fb, 0x200}, + // ClientHellos of length 0x1fc through 0x1ff get padded beyond 0x200. The + // padding extension takes a minimum of four bytes plus one required content + // byte. (To work around yet more server bugs, we avoid empty final + // extensions.) + {0x1fc, 0x201}, + {0x1fd, 0x202}, + {0x1fe, 0x203}, + {0x1ff, 0x204}, + // Finally, larger ClientHellos need no padding. + {0x200, 0x200}, + {0x201, 0x201}, +}; + +static bool TestPaddingExtension() { + // Sample a baseline length. + size_t base_len = GetClientHelloLen(1); + if (base_len == 0) { + return false; + } + + for (const PaddingTest &test : kPaddingTests) { + if (base_len > test.input_len) { + fprintf(stderr, "Baseline ClientHello too long.\n"); + return false; + } + + size_t padded_len = GetClientHelloLen(1 + test.input_len - base_len); + if (padded_len != test.padded_len) { + fprintf(stderr, "%u-byte ClientHello padded to %u bytes, not %u.\n", + static_cast<unsigned>(test.input_len), + static_cast<unsigned>(padded_len), + static_cast<unsigned>(test.padded_len)); + return false; + } + } + return true; +} + +// Test that |SSL_get_client_CA_list| echoes back the configured parameter even +// before configuring as a server. +static bool TestClientCAList() { + ScopedSSL_CTX ctx(SSL_CTX_new(TLS_method())); + if (!ctx) { + return false; + } + ScopedSSL ssl(SSL_new(ctx.get())); + if (!ssl) { + return false; + } + + STACK_OF(X509_NAME) *stack = sk_X509_NAME_new_null(); + if (stack == nullptr) { + return false; + } + // |SSL_set_client_CA_list| takes ownership. + SSL_set_client_CA_list(ssl.get(), stack); + + return SSL_get_client_CA_list(ssl.get()) == stack; +} + +int main() { SSL_library_init(); if (!TestCipherRules() || !TestSSL_SESSIONEncoding(kOpenSSLSession) || !TestSSL_SESSIONEncoding(kCustomSession) || + !TestSSL_SESSIONEncoding(kBoringSSLSession) || !TestBadSSL_SESSIONEncoding(kBadSessionExtraField) || !TestBadSSL_SESSIONEncoding(kBadSessionVersion) || !TestBadSSL_SESSIONEncoding(kBadSessionTrailingData) || @@ -566,7 +836,9 @@ int main(void) { !TestDefaultVersion(0, &DTLS_method) || !TestDefaultVersion(DTLS1_VERSION, &DTLSv1_method) || !TestDefaultVersion(DTLS1_2_VERSION, &DTLSv1_2_method) || - !TestCipherGetRFCName()) { + !TestCipherGetRFCName() || + !TestPaddingExtension() || + !TestClientCAList()) { ERR_print_errors_fp(stderr); return 1; } diff --git a/src/ssl/ssl_txt.c b/src/ssl/ssl_txt.c index 2275f16..3ba0887 100644 --- a/src/ssl/ssl_txt.c +++ b/src/ssl/ssl_txt.c @@ -80,12 +80,14 @@ * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR * OTHERWISE. */ +#include <openssl/ssl.h> + #include <inttypes.h> #include <stdio.h> -#include <openssl/buf.h> +#include <openssl/bio.h> #include <openssl/err.h> -#include <openssl/mem.h> +#include <openssl/x509.h> #include "internal.h" @@ -96,7 +98,7 @@ int SSL_SESSION_print_fp(FILE *fp, const SSL_SESSION *x) { b = BIO_new(BIO_s_file()); if (b == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_SESSION_print_fp, ERR_R_BUF_LIB); + OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB); return 0; } diff --git a/src/ssl/t1_enc.c b/src/ssl/t1_enc.c index 6bd80c3..076f8bd 100644 --- a/src/ssl/t1_enc.c +++ b/src/ssl/t1_enc.c @@ -133,6 +133,8 @@ * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR * OTHERWISE. */ +#include <openssl/ssl.h> + #include <assert.h> #include <stdio.h> #include <string.h> @@ -149,7 +151,7 @@ /* tls1_P_hash computes the TLS P_<hash> function as described in RFC 5246, - * section 5. It writes |out_len| bytes to |out|, using |md| as the hash and + * section 5. It XORs |out_len| bytes to |out|, using |md| as the hash and * |secret| as the secret. |seed1| through |seed3| are concatenated to form the * seed parameter. It returns one on success and zero on failure. */ static int tls1_P_hash(uint8_t *out, size_t out_len, const EVP_MD *md, @@ -188,26 +190,32 @@ static int tls1_P_hash(uint8_t *out, size_t out_len, const EVP_MD *md, goto err; } - if (out_len > chunk) { - unsigned len; - if (!HMAC_Final(&ctx, out, &len)) { - goto err; - } - assert(len == chunk); - out += len; - out_len -= len; - /* Calculate the next A1 value. */ - if (!HMAC_Final(&ctx_tmp, A1, &A1_len)) { - goto err; - } - } else { - /* Last chunk. */ - if (!HMAC_Final(&ctx, A1, &A1_len)) { - goto err; - } - memcpy(out, A1, out_len); + unsigned len; + uint8_t hmac[EVP_MAX_MD_SIZE]; + if (!HMAC_Final(&ctx, hmac, &len)) { + goto err; + } + assert(len == chunk); + + /* XOR the result into |out|. */ + if (len > out_len) { + len = out_len; + } + unsigned i; + for (i = 0; i < len; i++) { + out[i] ^= hmac[i]; + } + out += len; + out_len -= len; + + if (out_len == 0) { break; } + + /* Calculate the next A1 value. */ + if (!HMAC_Final(&ctx_tmp, A1, &A1_len)) { + goto err; + } } ret = 1; @@ -224,62 +232,36 @@ int tls1_prf(SSL *s, uint8_t *out, size_t out_len, const uint8_t *secret, size_t secret_len, const char *label, size_t label_len, const uint8_t *seed1, size_t seed1_len, const uint8_t *seed2, size_t seed2_len) { - size_t idx, len, count, i; - const uint8_t *S1; - uint32_t m; - const EVP_MD *md; - int ret = 0; - uint8_t *tmp; if (out_len == 0) { return 1; } - /* Allocate a temporary buffer. */ - tmp = OPENSSL_malloc(out_len); - if (tmp == NULL) { - OPENSSL_PUT_ERROR(SSL, tls1_prf, ERR_R_MALLOC_FAILURE); - return 0; - } + memset(out, 0, out_len); - /* Count number of digests and partition |secret| evenly. */ - count = 0; - for (idx = 0; ssl_get_handshake_digest(&m, &md, idx); idx++) { - if (m & ssl_get_algorithm2(s)) { - count++; + uint32_t algorithm_prf = ssl_get_algorithm_prf(s); + if (algorithm_prf == SSL_HANDSHAKE_MAC_DEFAULT) { + /* If using the MD5/SHA1 PRF, |secret| is partitioned between SHA-1 and + * MD5, MD5 first. */ + size_t secret_half = secret_len - (secret_len / 2); + if (!tls1_P_hash(out, out_len, EVP_md5(), secret, secret_half, + (const uint8_t *)label, label_len, seed1, seed1_len, seed2, + seed2_len)) { + return 0; } + + /* Note that, if |secret_len| is odd, the two halves share a byte. */ + secret = secret + (secret_len - secret_half); + secret_len = secret_half; } - /* TODO(davidben): The only case where count isn't 1 is the old MD5/SHA-1 - * combination. The logic around multiple handshake digests can probably be - * simplified. */ - assert(count == 1 || count == 2); - len = secret_len / count; - if (count == 1) { - secret_len = 0; - } - S1 = secret; - memset(out, 0, out_len); - for (idx = 0; ssl_get_handshake_digest(&m, &md, idx); idx++) { - if (m & ssl_get_algorithm2(s)) { - /* If |count| is 2 and |secret_len| is odd, |secret| is partitioned into - * two halves with an overlapping byte. */ - if (!tls1_P_hash(tmp, out_len, md, S1, len + (secret_len & 1), - (const uint8_t *)label, label_len, seed1, seed1_len, - seed2, seed2_len)) { - goto err; - } - S1 += len; - for (i = 0; i < out_len; i++) { - out[i] ^= tmp[i]; - } - } + + if (!tls1_P_hash(out, out_len, ssl_get_handshake_digest(algorithm_prf), + secret, secret_len, (const uint8_t *)label, label_len, + seed1, seed1_len, seed2, seed2_len)) { + return 0; } - ret = 1; -err: - OPENSSL_cleanse(tmp, out_len); - OPENSSL_free(tmp); - return ret; + return 1; } static int tls1_generate_key_block(SSL *s, uint8_t *out, size_t out_len) { @@ -317,7 +299,7 @@ int tls1_change_cipher_state(SSL *s, int which) { iv_len = s->s3->tmp.new_fixed_iv_len; if (aead == NULL) { - OPENSSL_PUT_ERROR(SSL, tls1_change_cipher_state, ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return 0; } @@ -327,7 +309,7 @@ int tls1_change_cipher_state(SSL *s, int which) { * suites) the key length reported by |EVP_AEAD_key_length| will * include the MAC and IV key bytes. */ if (key_len < mac_secret_len + iv_len) { - OPENSSL_PUT_ERROR(SSL, tls1_change_cipher_state, ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return 0; } key_len -= mac_secret_len + iv_len; @@ -358,7 +340,7 @@ int tls1_change_cipher_state(SSL *s, int which) { } if (key_data - s->s3->tmp.key_block != s->s3->tmp.key_block_length) { - OPENSSL_PUT_ERROR(SSL, tls1_change_cipher_state, ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return 0; } @@ -369,14 +351,26 @@ int tls1_change_cipher_state(SSL *s, int which) { s->s3->tmp.new_cipher, key, key_len, mac_secret, mac_secret_len, iv, iv_len); return s->aead_read_ctx != NULL; - } else { - SSL_AEAD_CTX_free(s->aead_write_ctx); - s->aead_write_ctx = SSL_AEAD_CTX_new( - evp_aead_seal, ssl3_version_from_wire(s, s->version), - s->s3->tmp.new_cipher, key, key_len, mac_secret, mac_secret_len, iv, - iv_len); - return s->aead_write_ctx != NULL; } + + SSL_AEAD_CTX_free(s->aead_write_ctx); + s->aead_write_ctx = SSL_AEAD_CTX_new( + evp_aead_seal, ssl3_version_from_wire(s, s->version), + s->s3->tmp.new_cipher, key, key_len, mac_secret, mac_secret_len, iv, + iv_len); + if (s->aead_write_ctx == NULL) { + return 0; + } + + s->s3->need_record_splitting = 0; + if (!SSL_USE_EXPLICIT_IV(s) && + (s->mode & SSL_MODE_CBC_RECORD_SPLITTING) != 0 && + SSL_CIPHER_is_block_cipher(s->s3->tmp.new_cipher)) { + /* Enable 1/n-1 record-splitting to randomize the IV. See + * https://www.openssl.org/~bodo/tls-cbc.txt and the BEAST attack. */ + s->s3->need_record_splitting = 1; + } + return 1; } int tls1_setup_key_block(SSL *s) { @@ -406,14 +400,14 @@ int tls1_setup_key_block(SSL *s) { * key length reported by |EVP_AEAD_key_length| will include the MAC key * bytes and initial implicit IV. */ if (key_len < mac_secret_len + fixed_iv_len) { - OPENSSL_PUT_ERROR(SSL, tls1_setup_key_block, ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return 0; } key_len -= mac_secret_len + fixed_iv_len; } else { /* The nonce is split into a fixed portion and a variable portion. */ if (variable_iv_len < fixed_iv_len) { - OPENSSL_PUT_ERROR(SSL, tls1_setup_key_block, ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return 0; } variable_iv_len -= fixed_iv_len; @@ -435,7 +429,7 @@ int tls1_setup_key_block(SSL *s) { p = (uint8_t *)OPENSSL_malloc(key_block_len); if (p == NULL) { - OPENSSL_PUT_ERROR(SSL, tls1_setup_key_block, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto err; } @@ -446,60 +440,61 @@ int tls1_setup_key_block(SSL *s) { goto err; } - if (!SSL_USE_EXPLICIT_IV(s) && - (s->mode & SSL_MODE_CBC_RECORD_SPLITTING) != 0) { - /* enable vulnerability countermeasure for CBC ciphers with known-IV - * problem (http://www.openssl.org/~bodo/tls-cbc.txt). */ - s->s3->need_record_splitting = 1; - - if (s->session->cipher != NULL && - s->session->cipher->algorithm_enc == SSL_RC4) { - s->s3->need_record_splitting = 0; - } - } - ret = 1; err: return ret; cipher_unavailable_err: - OPENSSL_PUT_ERROR(SSL, tls1_setup_key_block, - SSL_R_CIPHER_OR_HASH_UNAVAILABLE); + OPENSSL_PUT_ERROR(SSL, SSL_R_CIPHER_OR_HASH_UNAVAILABLE); return 0; } int tls1_cert_verify_mac(SSL *s, int md_nid, uint8_t *out) { - unsigned int ret; - EVP_MD_CTX ctx, *d = NULL; - int i; - - if (s->s3->handshake_buffer && - !ssl3_digest_cached_records(s, free_handshake_buffer)) { - return 0; - } - - for (i = 0; i < SSL_MAX_DIGEST; i++) { - if (s->s3->handshake_dgst[i] && - EVP_MD_CTX_type(s->s3->handshake_dgst[i]) == md_nid) { - d = s->s3->handshake_dgst[i]; - break; - } - } - - if (!d) { - OPENSSL_PUT_ERROR(SSL, tls1_cert_verify_mac, SSL_R_NO_REQUIRED_DIGEST); + const EVP_MD_CTX *ctx_template; + if (md_nid == NID_md5) { + ctx_template = &s->s3->handshake_md5; + } else if (md_nid == EVP_MD_CTX_type(&s->s3->handshake_hash)) { + ctx_template = &s->s3->handshake_hash; + } else { + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_REQUIRED_DIGEST); return 0; } + EVP_MD_CTX ctx; EVP_MD_CTX_init(&ctx); - if (!EVP_MD_CTX_copy_ex(&ctx, d)) { + if (!EVP_MD_CTX_copy_ex(&ctx, ctx_template)) { EVP_MD_CTX_cleanup(&ctx); return 0; } + unsigned ret; EVP_DigestFinal_ex(&ctx, out, &ret); EVP_MD_CTX_cleanup(&ctx); + return ret; +} + +static int append_digest(const EVP_MD_CTX *ctx, uint8_t *out, size_t *out_len, + size_t max_out) { + int ret = 0; + EVP_MD_CTX ctx_copy; + EVP_MD_CTX_init(&ctx_copy); + + if (EVP_MD_CTX_size(ctx) > max_out) { + OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL); + goto err; + } + unsigned len; + if (!EVP_MD_CTX_copy_ex(&ctx_copy, ctx) || + !EVP_DigestFinal_ex(&ctx_copy, out, &len)) { + goto err; + } + assert(len == EVP_MD_CTX_size(ctx)); + + *out_len = len; + ret = 1; +err: + EVP_MD_CTX_cleanup(&ctx_copy); return ret; } @@ -509,44 +504,19 @@ int tls1_cert_verify_mac(SSL *s, int md_nid, uint8_t *out) { * underlying digests so can be called multiple times and prior to the final * update etc. */ int tls1_handshake_digest(SSL *s, uint8_t *out, size_t out_len) { - const EVP_MD *md; - EVP_MD_CTX ctx; - int err = 0, len = 0; - size_t i; - uint32_t mask; - - EVP_MD_CTX_init(&ctx); - - for (i = 0; ssl_get_handshake_digest(&mask, &md, i); i++) { - size_t hash_size; - unsigned int digest_len; - EVP_MD_CTX *hdgst = s->s3->handshake_dgst[i]; - - if ((mask & ssl_get_algorithm2(s)) == 0) { - continue; - } - - hash_size = EVP_MD_size(md); - if (!hdgst || - hash_size > out_len || - !EVP_MD_CTX_copy_ex(&ctx, hdgst) || - !EVP_DigestFinal_ex(&ctx, out, &digest_len) || - digest_len != hash_size /* internal error */) { - err = 1; - break; - } - - out += digest_len; - out_len -= digest_len; - len += digest_len; + size_t md5_len = 0; + if (EVP_MD_CTX_md(&s->s3->handshake_md5) != NULL && + !append_digest(&s->s3->handshake_md5, out, &md5_len, out_len)) { + return -1; } - EVP_MD_CTX_cleanup(&ctx); - - if (err != 0) { + size_t len; + if (!append_digest(&s->s3->handshake_hash, out + md5_len, &len, + out_len - md5_len)) { return -1; } - return len; + + return (int)(md5_len + len); } int tls1_final_finish_mac(SSL *s, const char *str, int slen, uint8_t *out) { @@ -555,14 +525,8 @@ int tls1_final_finish_mac(SSL *s, const char *str, int slen, uint8_t *out) { int digests_len; /* At this point, the handshake should have released the handshake buffer on - * its own. - * TODO(davidben): Apart from initialization, the handshake buffer should be - * orthogonal to the handshake digest. https://crbug.com/492371 */ + * its own. */ assert(s->s3->handshake_buffer == NULL); - if (s->s3->handshake_buffer && - !ssl3_digest_cached_records(s, free_handshake_buffer)) { - return 0; - } digests_len = tls1_handshake_digest(s, buf, sizeof(buf)); if (digests_len < 0) { @@ -587,21 +551,7 @@ int tls1_generate_master_secret(SSL *s, uint8_t *out, const uint8_t *premaster, size_t premaster_len) { if (s->s3->tmp.extended_master_secret) { uint8_t digests[2 * EVP_MAX_MD_SIZE]; - int digests_len; - - /* The master secret is based on the handshake hash just after sending the - * ClientKeyExchange. However, we might have a client certificate to send, - * in which case we might need different hashes for the verification and - * thus still need the handshake buffer around. Keeping both a handshake - * buffer *and* running hashes isn't yet supported so, when it comes to - * calculating the Finished hash, we'll have to hash the handshake buffer - * again. */ - if (s->s3->handshake_buffer && - !ssl3_digest_cached_records(s, dont_free_handshake_buffer)) { - return 0; - } - - digests_len = tls1_handshake_digest(s, digests, sizeof(digests)); + int digests_len = tls1_handshake_digest(s, digests, sizeof(digests)); if (digests_len == -1) { return 0; } @@ -630,22 +580,21 @@ int tls1_export_keying_material(SSL *s, uint8_t *out, size_t out_len, const uint8_t *context, size_t context_len, int use_context) { if (!s->s3->have_version || s->version == SSL3_VERSION) { - OPENSSL_PUT_ERROR(SSL, tls1_export_keying_material, - ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); return 0; } size_t seed_len = 2 * SSL3_RANDOM_SIZE; if (use_context) { if (context_len >= 1u << 16) { - OPENSSL_PUT_ERROR(SSL, tls1_export_keying_material, ERR_R_OVERFLOW); + OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW); return 0; } seed_len += 2 + context_len; } uint8_t *seed = OPENSSL_malloc(seed_len); if (seed == NULL) { - OPENSSL_PUT_ERROR(SSL, tls1_export_keying_material, ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); return 0; } diff --git a/src/ssl/t1_lib.c b/src/ssl/t1_lib.c index 213a647..f30e8eb 100644 --- a/src/ssl/t1_lib.c +++ b/src/ssl/t1_lib.c @@ -106,12 +106,16 @@ * (eay@cryptsoft.com). This product includes software written by Tim * Hudson (tjh@cryptsoft.com). */ +#include <openssl/ssl.h> + #include <assert.h> +#include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <openssl/bytestring.h> +#include <openssl/digest.h> #include <openssl/err.h> #include <openssl/evp.h> #include <openssl/hmac.h> @@ -122,9 +126,6 @@ #include "internal.h" -static int tls_decrypt_ticket(SSL *s, const uint8_t *tick, int ticklen, - const uint8_t *sess_id, int sesslen, - SSL_SESSION **psess); static int ssl_check_clienthello_tlsext(SSL *s); static int ssl_check_serverhello_tlsext(SSL *s); @@ -213,8 +214,7 @@ static int tls1_check_duplicate_extensions(const CBS *cbs) { extension_types = (uint16_t *)OPENSSL_malloc(sizeof(uint16_t) * num_extensions); if (extension_types == NULL) { - OPENSSL_PUT_ERROR(SSL, tls1_check_duplicate_extensions, - ERR_R_MALLOC_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); goto done; } @@ -338,24 +338,21 @@ char SSL_early_callback_ctx_extension_get( struct tls_curve { uint16_t curve_id; int nid; + const char curve_name[8]; }; /* ECC curves from RFC4492. */ static const struct tls_curve tls_curves[] = { - {21, NID_secp224r1}, - {23, NID_X9_62_prime256v1}, - {24, NID_secp384r1}, - {25, NID_secp521r1}, -}; - -static const uint8_t ecformats_default[] = { - TLSEXT_ECPOINTFORMAT_uncompressed, + {21, NID_secp224r1, "P-224"}, + {23, NID_X9_62_prime256v1, "P-256"}, + {24, NID_secp384r1, "P-384"}, + {25, NID_secp521r1, "P-521"}, }; static const uint16_t eccurves_default[] = { 23, /* X9_62_prime256v1 */ 24, /* secp384r1 */ -#if defined(ANDROID) +#if defined(BORINGSSL_ANDROID_SYSTEM) 25, /* secp521r1 */ #endif }; @@ -381,6 +378,16 @@ int tls1_ec_nid2curve_id(uint16_t *out_curve_id, int nid) { return 0; } +const char* tls1_ec_curve_id2name(uint16_t curve_id) { + size_t i; + for (i = 0; i < sizeof(tls_curves) / sizeof(tls_curves[0]); i++) { + if (curve_id == tls_curves[i].curve_id) { + return tls_curves[i].curve_name; + } + } + return NULL; +} + /* tls1_get_curvelist sets |*out_curve_ids| and |*out_curve_ids_len| to the * list of allowed curve IDs. If |get_peer_curves| is non-zero, return the * peer's curve list. Otherwise, return the preferred list. */ @@ -535,28 +542,6 @@ static int tls1_curve_params_from_ec_key(uint16_t *out_curve_id, return 1; } -/* tls1_check_point_format returns one if |comp_id| is consistent with the - * peer's point format preferences. */ -static int tls1_check_point_format(SSL *s, uint8_t comp_id) { - uint8_t *p = s->s3->tmp.peer_ecpointformatlist; - size_t plen = s->s3->tmp.peer_ecpointformatlist_length; - size_t i; - - /* If point formats extension present check it, otherwise everything is - * supported (see RFC4492). */ - if (p == NULL) { - return 1; - } - - for (i = 0; i < plen; i++) { - if (comp_id == p[i]) { - return 1; - } - } - - return 0; -} - /* tls1_check_curve_id returns one if |curve_id| is consistent with both our * and the peer's curve preferences. Note: if called as the client, only our * preferences are checked; the peer (the server) does not send preferences. */ @@ -593,18 +578,6 @@ static int tls1_check_curve_id(SSL *s, uint16_t curve_id) { return 1; } -static void tls1_get_formatlist(SSL *s, const uint8_t **pformats, - size_t *pformatslen) { - /* If we have a custom point format list use it otherwise use default */ - if (s->tlsext_ecpointformatlist) { - *pformats = s->tlsext_ecpointformatlist; - *pformatslen = s->tlsext_ecpointformatlist_length; - } else { - *pformats = ecformats_default; - *pformatslen = sizeof(ecformats_default); - } -} - int tls1_check_ec_cert(SSL *s, X509 *x) { int ret = 0; EVP_PKEY *pkey = X509_get_pubkey(x); @@ -615,7 +588,7 @@ int tls1_check_ec_cert(SSL *s, X509 *x) { pkey->type != EVP_PKEY_EC || !tls1_curve_params_from_ec_key(&curve_id, &comp_id, pkey->pkey.ec) || !tls1_check_curve_id(s, curve_id) || - !tls1_check_point_format(s, comp_id)) { + comp_id != TLSEXT_ECPOINTFORMAT_uncompressed) { goto done; } @@ -663,17 +636,8 @@ static const uint8_t tls12_sigalgs[] = { }; size_t tls12_get_psigalgs(SSL *s, const uint8_t **psigs) { - /* If server use client authentication sigalgs if not NULL */ - if (s->server && s->cert->client_sigalgs) { - *psigs = s->cert->client_sigalgs; - return s->cert->client_sigalgslen; - } else if (s->cert->conf_sigalgs) { - *psigs = s->cert->conf_sigalgs; - return s->cert->conf_sigalgslen; - } else { - *psigs = tls12_sigalgs; - return sizeof(tls12_sigalgs); - } + *psigs = tls12_sigalgs; + return sizeof(tls12_sigalgs); } /* tls12_check_peer_sigalg parses a SignatureAndHashAlgorithm out of |cbs|. It @@ -684,26 +648,26 @@ int tls12_check_peer_sigalg(const EVP_MD **out_md, int *out_alert, SSL *s, CBS *cbs, EVP_PKEY *pkey) { const uint8_t *sent_sigs; size_t sent_sigslen, i; - int sigalg = tls12_get_sigid(pkey); + int sigalg = tls12_get_sigid(pkey->type); uint8_t hash, signature; /* Should never happen */ if (sigalg == -1) { - OPENSSL_PUT_ERROR(SSL, tls12_check_peer_sigalg, ERR_R_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); *out_alert = SSL_AD_INTERNAL_ERROR; return 0; } if (!CBS_get_u8(cbs, &hash) || !CBS_get_u8(cbs, &signature)) { - OPENSSL_PUT_ERROR(SSL, tls12_check_peer_sigalg, SSL_R_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); *out_alert = SSL_AD_DECODE_ERROR; return 0; } /* Check key type is consistent with signature */ if (sigalg != signature) { - OPENSSL_PUT_ERROR(SSL, tls12_check_peer_sigalg, SSL_R_WRONG_SIGNATURE_TYPE); + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SIGNATURE_TYPE); *out_alert = SSL_AD_ILLEGAL_PARAMETER; return 0; } @@ -718,8 +682,8 @@ int tls12_check_peer_sigalg(const EVP_MD **out_md, int *out_alert, SSL *s, } if (s->server && (!tls1_check_curve_id(s, curve_id) || - !tls1_check_point_format(s, comp_id))) { - OPENSSL_PUT_ERROR(SSL, tls12_check_peer_sigalg, SSL_R_WRONG_CURVE); + comp_id != TLSEXT_ECPOINTFORMAT_uncompressed)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE); *out_alert = SSL_AD_ILLEGAL_PARAMETER; return 0; } @@ -735,14 +699,14 @@ int tls12_check_peer_sigalg(const EVP_MD **out_md, int *out_alert, SSL *s, /* Allow fallback to SHA-1. */ if (i == sent_sigslen && hash != TLSEXT_hash_sha1) { - OPENSSL_PUT_ERROR(SSL, tls12_check_peer_sigalg, SSL_R_WRONG_SIGNATURE_TYPE); + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SIGNATURE_TYPE); *out_alert = SSL_AD_ILLEGAL_PARAMETER; return 0; } *out_md = tls12_get_hash(hash); if (*out_md == NULL) { - OPENSSL_PUT_ERROR(SSL, tls12_check_peer_sigalg, SSL_R_UNKNOWN_DIGEST); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_DIGEST); *out_alert = SSL_AD_ILLEGAL_PARAMETER; return 0; } @@ -799,1128 +763,1785 @@ void ssl_set_client_disabled(SSL *s) { } } -/* header_len is the length of the ClientHello header written so far, used to - * compute padding. It does not include the record header. Pass 0 if no padding - * is to be done. */ -uint8_t *ssl_add_clienthello_tlsext(SSL *s, uint8_t *buf, uint8_t *limit, - size_t header_len) { - int extdatalen = 0; - uint8_t *ret = buf; - uint8_t *orig = buf; - /* See if we support any ECC ciphersuites */ - int using_ecc = 0; - - if (s->version >= TLS1_VERSION || SSL_IS_DTLS(s)) { - size_t i; - uint32_t alg_k, alg_a; - STACK_OF(SSL_CIPHER) *cipher_stack = SSL_get_ciphers(s); - - for (i = 0; i < sk_SSL_CIPHER_num(cipher_stack); i++) { - const SSL_CIPHER *c = sk_SSL_CIPHER_value(cipher_stack, i); - - alg_k = c->algorithm_mkey; - alg_a = c->algorithm_auth; - if ((alg_k & SSL_kECDHE) || (alg_a & SSL_aECDSA)) { - using_ecc = 1; - break; - } - } +/* tls_extension represents a TLS extension that is handled internally. The + * |init| function is called for each handshake, before any other functions of + * the extension. Then the add and parse callbacks are called as needed. + * + * The parse callbacks receive a |CBS| that contains the contents of the + * extension (i.e. not including the type and length bytes). If an extension is + * not received then the parse callbacks will be called with a NULL CBS so that + * they can do any processing needed to handle the absence of an extension. + * + * The add callbacks receive a |CBB| to which the extension can be appended but + * the function is responsible for appending the type and length bytes too. + * + * All callbacks return one for success and zero for error. If a parse function + * returns zero then a fatal alert with value |*out_alert| will be sent. If + * |*out_alert| isn't set, then a |decode_error| alert will be sent. */ +struct tls_extension { + uint16_t value; + void (*init)(SSL *ssl); + + int (*add_clienthello)(SSL *ssl, CBB *out); + int (*parse_serverhello)(SSL *ssl, uint8_t *out_alert, CBS *contents); + + int (*parse_clienthello)(SSL *ssl, uint8_t *out_alert, CBS *contents); + int (*add_serverhello)(SSL *ssl, CBB *out); +}; + + +/* Server name indication (SNI). + * + * https://tools.ietf.org/html/rfc6066#section-3. */ + +static void ext_sni_init(SSL *ssl) { + ssl->s3->tmp.should_ack_sni = 0; +} + +static int ext_sni_add_clienthello(SSL *ssl, CBB *out) { + if (ssl->tlsext_hostname == NULL) { + return 1; } - /* don't add extensions for SSLv3 unless doing secure renegotiation */ - if (s->client_version == SSL3_VERSION && !s->s3->send_connection_binding) { - return orig; + CBB contents, server_name_list, name; + if (!CBB_add_u16(out, TLSEXT_TYPE_server_name) || + !CBB_add_u16_length_prefixed(out, &contents) || + !CBB_add_u16_length_prefixed(&contents, &server_name_list) || + !CBB_add_u8(&server_name_list, TLSEXT_NAMETYPE_host_name) || + !CBB_add_u16_length_prefixed(&server_name_list, &name) || + !CBB_add_bytes(&name, (const uint8_t *)ssl->tlsext_hostname, + strlen(ssl->tlsext_hostname)) || + !CBB_flush(out)) { + return 0; } - ret += 2; + return 1; +} - if (ret >= limit) { - return NULL; /* should never occur. */ +static int ext_sni_parse_serverhello(SSL *ssl, uint8_t *out_alert, CBS *contents) { + if (contents == NULL) { + return 1; } - if (s->tlsext_hostname != NULL) { - /* Add TLS extension servername to the Client Hello message */ - unsigned long size_str; - long lenmax; + if (CBS_len(contents) != 0) { + return 0; + } - /* check for enough space. - 4 for the servername type and entension length - 2 for servernamelist length - 1 for the hostname type - 2 for hostname length - + hostname length */ + assert(ssl->tlsext_hostname != NULL); - lenmax = limit - ret - 9; - size_str = strlen(s->tlsext_hostname); - if (lenmax < 0 || size_str > (unsigned long)lenmax) { - return NULL; + if (!ssl->hit) { + assert(ssl->session->tlsext_hostname == NULL); + ssl->session->tlsext_hostname = BUF_strdup(ssl->tlsext_hostname); + if (!ssl->session->tlsext_hostname) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return 0; } + } - /* extension type and length */ - s2n(TLSEXT_TYPE_server_name, ret); - s2n(size_str + 5, ret); + return 1; +} - /* length of servername list */ - s2n(size_str + 3, ret); +static int ext_sni_parse_clienthello(SSL *ssl, uint8_t *out_alert, CBS *contents) { + if (contents == NULL) { + return 1; + } - /* hostname type, length and hostname */ - *(ret++) = (uint8_t)TLSEXT_NAMETYPE_host_name; - s2n(size_str, ret); - memcpy(ret, s->tlsext_hostname, size_str); - ret += size_str; + /* The servername extension is treated as follows: + * + * - Only the hostname type is supported with a maximum length of 255. + * - The servername is rejected if too long or if it contains zeros, in + * which case an fatal alert is generated. + * - The servername field is maintained together with the session cache. + * - When a session is resumed, the servername callback is invoked in order + * to allow the application to position itself to the right context. + * - The servername is acknowledged if it is new for a session or when + * it is identical to a previously used for the same session. + * Applications can control the behaviour. They can at any time + * set a 'desirable' servername for a new SSL object. This can be the + * case for example with HTTPS when a Host: header field is received and + * a renegotiation is requested. In this case, a possible servername + * presented in the new client hello is only acknowledged if it matches + * the value of the Host: field. + * - Applications must use SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION + * if they provide for changing an explicit servername context for the + * session, + * i.e. when the session has been established with a servername extension. + */ + + CBS server_name_list; + char have_seen_host_name = 0; + + if (!CBS_get_u16_length_prefixed(contents, &server_name_list) || + CBS_len(&server_name_list) == 0 || + CBS_len(contents) != 0) { + return 0; } - /* Add RI if renegotiating */ - if (s->s3->initial_handshake_complete) { - int el; + /* Decode each ServerName in the extension. */ + while (CBS_len(&server_name_list) > 0) { + uint8_t name_type; + CBS host_name; - if (!ssl_add_clienthello_renegotiate_ext(s, 0, &el, 0)) { - OPENSSL_PUT_ERROR(SSL, ssl_add_clienthello_tlsext, ERR_R_INTERNAL_ERROR); - return NULL; + if (!CBS_get_u8(&server_name_list, &name_type) || + !CBS_get_u16_length_prefixed(&server_name_list, &host_name)) { + return 0; } - if ((limit - ret - 4 - el) < 0) { - return NULL; + /* Only host_name is supported. */ + if (name_type != TLSEXT_NAMETYPE_host_name) { + continue; } - s2n(TLSEXT_TYPE_renegotiate, ret); - s2n(el, ret); - - if (!ssl_add_clienthello_renegotiate_ext(s, ret, &el, el)) { - OPENSSL_PUT_ERROR(SSL, ssl_add_clienthello_tlsext, ERR_R_INTERNAL_ERROR); - return NULL; + if (have_seen_host_name) { + /* The ServerNameList MUST NOT contain more than one name of the same + * name_type. */ + return 0; } - ret += el; - } + have_seen_host_name = 1; - /* Add extended master secret. */ - if (s->version != SSL3_VERSION) { - if (limit - ret - 4 < 0) { - return NULL; - } - s2n(TLSEXT_TYPE_extended_master_secret, ret); - s2n(0, ret); - } - - if (!(SSL_get_options(s) & SSL_OP_NO_TICKET)) { - int ticklen = 0; - /* Renegotiation does not participate in session resumption. However, still - * advertise the extension to avoid potentially breaking servers which carry - * over the state from the previous handshake, such as OpenSSL servers - * without upstream's 3c3f0259238594d77264a78944d409f2127642c4. */ - if (!s->s3->initial_handshake_complete && s->session != NULL && - s->session->tlsext_tick != NULL) { - ticklen = s->session->tlsext_ticklen; + if (CBS_len(&host_name) == 0 || + CBS_len(&host_name) > TLSEXT_MAXLEN_host_name || + CBS_contains_zero_byte(&host_name)) { + *out_alert = SSL_AD_UNRECOGNIZED_NAME; + return 0; } - /* Check for enough room 2 for extension type, 2 for len rest for - * ticket. */ - if ((long)(limit - ret - 4 - ticklen) < 0) { - return NULL; - } - s2n(TLSEXT_TYPE_session_ticket, ret); - s2n(ticklen, ret); - if (ticklen) { - memcpy(ret, s->session->tlsext_tick, ticklen); - ret += ticklen; + if (!ssl->hit) { + assert(ssl->session->tlsext_hostname == NULL); + if (ssl->session->tlsext_hostname) { + /* This should be impossible. */ + return 0; + } + + /* Copy the hostname as a string. */ + if (!CBS_strdup(&host_name, &ssl->session->tlsext_hostname)) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return 0; + } + + ssl->s3->tmp.should_ack_sni = 1; } } - if (ssl3_version_from_wire(s, s->client_version) >= TLS1_2_VERSION) { - size_t salglen; - const uint8_t *salg; - salglen = tls12_get_psigalgs(s, &salg); - if ((size_t)(limit - ret) < salglen + 6) { - return NULL; - } - s2n(TLSEXT_TYPE_signature_algorithms, ret); - s2n(salglen + 2, ret); - s2n(salglen, ret); - memcpy(ret, salg, salglen); - ret += salglen; - } - - if (s->ocsp_stapling_enabled) { - /* The status_request extension is excessively extensible at every layer. - * On the client, only support requesting OCSP responses with an empty - * responder_id_list and no extensions. */ - if (limit - ret - 4 - 1 - 2 - 2 < 0) { - return NULL; - } + return 1; +} - s2n(TLSEXT_TYPE_status_request, ret); - s2n(1 + 2 + 2, ret); - /* status_type */ - *(ret++) = TLSEXT_STATUSTYPE_ocsp; - /* responder_id_list - empty */ - s2n(0, ret); - /* request_extensions - empty */ - s2n(0, ret); - } - - if (s->ctx->next_proto_select_cb && !s->s3->initial_handshake_complete && - !SSL_IS_DTLS(s)) { - /* The client advertises an emtpy extension to indicate its support for - * Next Protocol Negotiation */ - if (limit - ret - 4 < 0) { - return NULL; - } - s2n(TLSEXT_TYPE_next_proto_neg, ret); - s2n(0, ret); +static int ext_sni_add_serverhello(SSL *ssl, CBB *out) { + if (ssl->hit || + !ssl->s3->tmp.should_ack_sni || + ssl->session->tlsext_hostname == NULL) { + return 1; } - if (s->signed_cert_timestamps_enabled) { - /* The client advertises an empty extension to indicate its support for - * certificate timestamps. */ - if (limit - ret - 4 < 0) { - return NULL; - } - s2n(TLSEXT_TYPE_certificate_timestamp, ret); - s2n(0, ret); + if (!CBB_add_u16(out, TLSEXT_TYPE_server_name) || + !CBB_add_u16(out, 0 /* length */)) { + return 0; } - if (s->alpn_client_proto_list && !s->s3->initial_handshake_complete) { - if ((size_t)(limit - ret) < 6 + s->alpn_client_proto_list_len) { - return NULL; - } - s2n(TLSEXT_TYPE_application_layer_protocol_negotiation, ret); - s2n(2 + s->alpn_client_proto_list_len, ret); - s2n(s->alpn_client_proto_list_len, ret); - memcpy(ret, s->alpn_client_proto_list, s->alpn_client_proto_list_len); - ret += s->alpn_client_proto_list_len; + return 1; +} + + +/* Renegotiation indication. + * + * https://tools.ietf.org/html/rfc5746 */ + +static int ext_ri_add_clienthello(SSL *ssl, CBB *out) { + CBB contents, prev_finished; + if (!CBB_add_u16(out, TLSEXT_TYPE_renegotiate) || + !CBB_add_u16_length_prefixed(out, &contents) || + !CBB_add_u8_length_prefixed(&contents, &prev_finished) || + !CBB_add_bytes(&prev_finished, ssl->s3->previous_client_finished, + ssl->s3->previous_client_finished_len) || + !CBB_flush(out)) { + return 0; } - if (s->tlsext_channel_id_enabled && !SSL_IS_DTLS(s)) { - /* The client advertises an emtpy extension to indicate its support for - * Channel ID. */ - if (limit - ret - 4 < 0) { - return NULL; + return 1; +} + +static int ext_ri_parse_serverhello(SSL *ssl, uint8_t *out_alert, + CBS *contents) { + if (contents == NULL) { + /* No renegotiation extension received. + * + * Strictly speaking if we want to avoid an attack we should *always* see + * RI even on initial ServerHello because the client doesn't see any + * renegotiation during an attack. However this would mean we could not + * connect to any server which doesn't support RI. + * + * A lack of the extension is allowed if SSL_OP_LEGACY_SERVER_CONNECT is + * defined. */ + if (ssl->options & SSL_OP_LEGACY_SERVER_CONNECT) { + return 1; } - if (s->ctx->tlsext_channel_id_enabled_new) { - s2n(TLSEXT_TYPE_channel_id_new, ret); + + *out_alert = SSL_AD_HANDSHAKE_FAILURE; + OPENSSL_PUT_ERROR(SSL, SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED); + return 0; + } + + const size_t expected_len = ssl->s3->previous_client_finished_len + + ssl->s3->previous_server_finished_len; + + /* Check for logic errors */ + assert(!expected_len || ssl->s3->previous_client_finished_len); + assert(!expected_len || ssl->s3->previous_server_finished_len); + + /* Parse out the extension contents. */ + CBS renegotiated_connection; + if (!CBS_get_u8_length_prefixed(contents, &renegotiated_connection) || + CBS_len(contents) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_RENEGOTIATION_ENCODING_ERR); + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return 0; + } + + /* Check that the extension matches. */ + if (CBS_len(&renegotiated_connection) != expected_len) { + OPENSSL_PUT_ERROR(SSL, SSL_R_RENEGOTIATION_MISMATCH); + *out_alert = SSL_AD_HANDSHAKE_FAILURE; + return 0; + } + + const uint8_t *d = CBS_data(&renegotiated_connection); + if (CRYPTO_memcmp(d, ssl->s3->previous_client_finished, + ssl->s3->previous_client_finished_len)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_RENEGOTIATION_MISMATCH); + *out_alert = SSL_AD_HANDSHAKE_FAILURE; + return 0; + } + d += ssl->s3->previous_client_finished_len; + + if (CRYPTO_memcmp(d, ssl->s3->previous_server_finished, + ssl->s3->previous_server_finished_len)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_RENEGOTIATION_MISMATCH); + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return 0; + } + ssl->s3->send_connection_binding = 1; + + return 1; +} + +static int ext_ri_parse_clienthello(SSL *ssl, uint8_t *out_alert, + CBS *contents) { + /* Renegotiation isn't supported as a server so this function should never be + * called after the initial handshake. */ + assert(!ssl->s3->initial_handshake_complete); + + CBS fake_contents; + static const uint8_t kFakeExtension[] = {0}; + + if (contents == NULL) { + if (ssl->s3->send_connection_binding) { + /* The renegotiation SCSV was received so pretend that we received a + * renegotiation extension. */ + CBS_init(&fake_contents, kFakeExtension, sizeof(kFakeExtension)); + contents = &fake_contents; + /* We require that the renegotiation extension is at index zero of + * kExtensions. */ + ssl->s3->tmp.extensions.received |= (1u << 0); } else { - s2n(TLSEXT_TYPE_channel_id, ret); + return 1; } - s2n(0, ret); } - if (SSL_get_srtp_profiles(s)) { - int el; + CBS renegotiated_connection; - ssl_add_clienthello_use_srtp_ext(s, 0, &el, 0); + if (!CBS_get_u8_length_prefixed(contents, &renegotiated_connection) || + CBS_len(contents) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_RENEGOTIATION_ENCODING_ERR); + return 0; + } - if ((limit - ret - 4 - el) < 0) { - return NULL; - } + /* Check that the extension matches */ + if (!CBS_mem_equal(&renegotiated_connection, ssl->s3->previous_client_finished, + ssl->s3->previous_client_finished_len)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_RENEGOTIATION_MISMATCH); + *out_alert = SSL_AD_HANDSHAKE_FAILURE; + return 0; + } - s2n(TLSEXT_TYPE_use_srtp, ret); - s2n(el, ret); + ssl->s3->send_connection_binding = 1; - if (!ssl_add_clienthello_use_srtp_ext(s, ret, &el, el)) { - OPENSSL_PUT_ERROR(SSL, ssl_add_clienthello_tlsext, ERR_R_INTERNAL_ERROR); - return NULL; - } - ret += el; + return 1; +} + +static int ext_ri_add_serverhello(SSL *ssl, CBB *out) { + CBB contents, prev_finished; + if (!CBB_add_u16(out, TLSEXT_TYPE_renegotiate) || + !CBB_add_u16_length_prefixed(out, &contents) || + !CBB_add_u8_length_prefixed(&contents, &prev_finished) || + !CBB_add_bytes(&prev_finished, ssl->s3->previous_client_finished, + ssl->s3->previous_client_finished_len) || + !CBB_add_bytes(&prev_finished, ssl->s3->previous_server_finished, + ssl->s3->previous_server_finished_len) || + !CBB_flush(out)) { + return 0; } - if (using_ecc) { - /* Add TLS extension ECPointFormats to the ClientHello message */ - long lenmax; - const uint8_t *formats; - const uint16_t *curves; - size_t formats_len, curves_len, i; + return 1; +} - tls1_get_formatlist(s, &formats, &formats_len); - lenmax = limit - ret - 5; - if (lenmax < 0) { - return NULL; - } - if (formats_len > (size_t)lenmax) { - return NULL; - } - if (formats_len > 255) { - OPENSSL_PUT_ERROR(SSL, ssl_add_clienthello_tlsext, ERR_R_INTERNAL_ERROR); - return NULL; - } +/* Extended Master Secret. + * + * https://tools.ietf.org/html/draft-ietf-tls-session-hash-05 */ - s2n(TLSEXT_TYPE_ec_point_formats, ret); - s2n(formats_len + 1, ret); - *(ret++) = (uint8_t)formats_len; - memcpy(ret, formats, formats_len); - ret += formats_len; +static void ext_ems_init(SSL *ssl) { + ssl->s3->tmp.extended_master_secret = 0; +} - /* Add TLS extension EllipticCurves to the ClientHello message */ - tls1_get_curvelist(s, 0, &curves, &curves_len); +static int ext_ems_add_clienthello(SSL *ssl, CBB *out) { + if (ssl->version == SSL3_VERSION) { + return 1; + } - lenmax = limit - ret - 6; - if (lenmax < 0) { - return NULL; - } - if (curves_len * 2 > (size_t)lenmax) { - return NULL; - } - if (curves_len * 2 > 65532) { - OPENSSL_PUT_ERROR(SSL, ssl_add_clienthello_tlsext, ERR_R_INTERNAL_ERROR); - return NULL; - } + if (!CBB_add_u16(out, TLSEXT_TYPE_extended_master_secret) || + !CBB_add_u16(out, 0 /* length */)) { + return 0; + } - s2n(TLSEXT_TYPE_elliptic_curves, ret); - s2n((curves_len * 2) + 2, ret); + return 1; +} - s2n(curves_len * 2, ret); - for (i = 0; i < curves_len; i++) { - s2n(curves[i], ret); - } +static int ext_ems_parse_serverhello(SSL *ssl, uint8_t *out_alert, + CBS *contents) { + if (contents == NULL) { + return 1; } - if (header_len > 0) { - size_t clienthello_minsize = 0; - header_len += ret - orig; - if (header_len > 0xff && header_len < 0x200) { - /* Add padding to workaround bugs in F5 terminators. See - * https://tools.ietf.org/html/draft-agl-tls-padding-03 - * - * NB: because this code works out the length of all existing extensions - * it MUST always appear last. */ - clienthello_minsize = 0x200; - } - if (s->fastradio_padding) { - /* Pad the ClientHello record to 1024 bytes to fast forward the radio - * into DCH (high data rate) state in 3G networks. Note that when - * fastradio_padding is enabled, even if the header_len is less than 255 - * bytes, the padding will be applied regardless. This is slightly - * different from the TLS padding extension suggested in - * https://tools.ietf.org/html/draft-agl-tls-padding-03 */ - clienthello_minsize = 0x400; - } - if (header_len < clienthello_minsize) { - size_t padding_len = clienthello_minsize - header_len; - /* Extensions take at least four bytes to encode. Always include least - * one byte of data if including the extension. WebSphere Application - * Server 7.0 is intolerant to the last extension being zero-length. */ - if (padding_len >= 4 + 1) { - padding_len -= 4; - } else { - padding_len = 1; - } + if (ssl->version == SSL3_VERSION || CBS_len(contents) != 0) { + return 0; + } - if (limit - ret - 4 - (long)padding_len < 0) { - return NULL; - } + ssl->s3->tmp.extended_master_secret = 1; + return 1; +} - s2n(TLSEXT_TYPE_padding, ret); - s2n(padding_len, ret); - memset(ret, 0, padding_len); - ret += padding_len; - } +static int ext_ems_parse_clienthello(SSL *ssl, uint8_t *out_alert, CBS *contents) { + if (ssl->version == SSL3_VERSION || contents == NULL) { + return 1; } - extdatalen = ret - orig - 2; - if (extdatalen == 0) { - return orig; + if (CBS_len(contents) != 0) { + return 0; } - s2n(extdatalen, orig); - return ret; + ssl->s3->tmp.extended_master_secret = 1; + return 1; } -uint8_t *ssl_add_serverhello_tlsext(SSL *s, uint8_t *buf, uint8_t *limit) { - int extdatalen = 0; - uint8_t *orig = buf; - uint8_t *ret = buf; - int next_proto_neg_seen; - uint32_t alg_k = s->s3->tmp.new_cipher->algorithm_mkey; - uint32_t alg_a = s->s3->tmp.new_cipher->algorithm_auth; - int using_ecc = (alg_k & SSL_kECDHE) || (alg_a & SSL_aECDSA); - using_ecc = using_ecc && (s->s3->tmp.peer_ecpointformatlist != NULL); +static int ext_ems_add_serverhello(SSL *ssl, CBB *out) { + if (!ssl->s3->tmp.extended_master_secret) { + return 1; + } - /* don't add extensions for SSLv3, unless doing secure renegotiation */ - if (s->version == SSL3_VERSION && !s->s3->send_connection_binding) { - return orig; + if (!CBB_add_u16(out, TLSEXT_TYPE_extended_master_secret) || + !CBB_add_u16(out, 0 /* length */)) { + return 0; } - ret += 2; - if (ret >= limit) { - return NULL; /* should never happen. */ + return 1; +} + + +/* Session tickets. + * + * https://tools.ietf.org/html/rfc5077 */ + +static int ext_ticket_add_clienthello(SSL *ssl, CBB *out) { + if (SSL_get_options(ssl) & SSL_OP_NO_TICKET) { + return 1; } - if (!s->hit && s->should_ack_sni && s->session->tlsext_hostname != NULL) { - if ((long)(limit - ret - 4) < 0) { - return NULL; - } + const uint8_t *ticket_data = NULL; + int ticket_len = 0; - s2n(TLSEXT_TYPE_server_name, ret); - s2n(0, ret); + /* Renegotiation does not participate in session resumption. However, still + * advertise the extension to avoid potentially breaking servers which carry + * over the state from the previous handshake, such as OpenSSL servers + * without upstream's 3c3f0259238594d77264a78944d409f2127642c4. */ + if (!ssl->s3->initial_handshake_complete && + ssl->session != NULL && + ssl->session->tlsext_tick != NULL) { + ticket_data = ssl->session->tlsext_tick; + ticket_len = ssl->session->tlsext_ticklen; } - if (s->s3->send_connection_binding) { - int el; + CBB ticket; + if (!CBB_add_u16(out, TLSEXT_TYPE_session_ticket) || + !CBB_add_u16_length_prefixed(out, &ticket) || + !CBB_add_bytes(&ticket, ticket_data, ticket_len) || + !CBB_flush(out)) { + return 0; + } - if (!ssl_add_serverhello_renegotiate_ext(s, 0, &el, 0)) { - OPENSSL_PUT_ERROR(SSL, ssl_add_serverhello_tlsext, ERR_R_INTERNAL_ERROR); - return NULL; - } + return 1; +} - if ((limit - ret - 4 - el) < 0) { - return NULL; - } +static int ext_ticket_parse_serverhello(SSL *ssl, uint8_t *out_alert, + CBS *contents) { + ssl->tlsext_ticket_expected = 0; - s2n(TLSEXT_TYPE_renegotiate, ret); - s2n(el, ret); + if (contents == NULL) { + return 1; + } - if (!ssl_add_serverhello_renegotiate_ext(s, ret, &el, el)) { - OPENSSL_PUT_ERROR(SSL, ssl_add_serverhello_tlsext, ERR_R_INTERNAL_ERROR); - return NULL; - } + /* If |SSL_OP_NO_TICKET| is set then no extension will have been sent and + * this function should never be called, even if the server tries to send the + * extension. */ + assert((SSL_get_options(ssl) & SSL_OP_NO_TICKET) == 0); - ret += el; + if (CBS_len(contents) != 0) { + return 0; } - if (s->s3->tmp.extended_master_secret) { - if ((long)(limit - ret - 4) < 0) { - return NULL; - } + ssl->tlsext_ticket_expected = 1; + return 1; +} + +static int ext_ticket_parse_clienthello(SSL *ssl, uint8_t *out_alert, CBS *contents) { + /* This function isn't used because the ticket extension from the client is + * handled in ssl_sess.c. */ + return 1; +} - s2n(TLSEXT_TYPE_extended_master_secret, ret); - s2n(0, ret); +static int ext_ticket_add_serverhello(SSL *ssl, CBB *out) { + if (!ssl->tlsext_ticket_expected) { + return 1; } - if (using_ecc) { - const uint8_t *plist; - size_t plistlen; - /* Add TLS extension ECPointFormats to the ServerHello message */ - long lenmax; + /* If |SSL_OP_NO_TICKET| is set, |tlsext_ticket_expected| should never be + * true. */ + assert((SSL_get_options(ssl) & SSL_OP_NO_TICKET) == 0); - tls1_get_formatlist(s, &plist, &plistlen); + if (!CBB_add_u16(out, TLSEXT_TYPE_session_ticket) || + !CBB_add_u16(out, 0 /* length */)) { + return 0; + } - lenmax = limit - ret - 5; - if (lenmax < 0) { - return NULL; - } - if (plistlen > (size_t)lenmax) { - return NULL; - } - if (plistlen > 255) { - OPENSSL_PUT_ERROR(SSL, ssl_add_serverhello_tlsext, ERR_R_INTERNAL_ERROR); - return NULL; - } + return 1; +} - s2n(TLSEXT_TYPE_ec_point_formats, ret); - s2n(plistlen + 1, ret); - *(ret++) = (uint8_t)plistlen; - memcpy(ret, plist, plistlen); - ret += plistlen; + +/* Signature Algorithms. + * + * https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 */ + +static int ext_sigalgs_add_clienthello(SSL *ssl, CBB *out) { + if (ssl3_version_from_wire(ssl, ssl->client_version) < TLS1_2_VERSION) { + return 1; } - /* Currently the server should not respond with a SupportedCurves extension */ - if (s->tlsext_ticket_expected && !(SSL_get_options(s) & SSL_OP_NO_TICKET)) { - if ((long)(limit - ret - 4) < 0) { - return NULL; - } - s2n(TLSEXT_TYPE_session_ticket, ret); - s2n(0, ret); + const uint8_t *sigalgs_data; + const size_t sigalgs_len = tls12_get_psigalgs(ssl, &sigalgs_data); + + CBB contents, sigalgs; + if (!CBB_add_u16(out, TLSEXT_TYPE_signature_algorithms) || + !CBB_add_u16_length_prefixed(out, &contents) || + !CBB_add_u16_length_prefixed(&contents, &sigalgs) || + !CBB_add_bytes(&sigalgs, sigalgs_data, sigalgs_len) || + !CBB_flush(out)) { + return 0; } - if (s->s3->tmp.certificate_status_expected) { - if ((long)(limit - ret - 4) < 0) { - return NULL; - } - s2n(TLSEXT_TYPE_status_request, ret); - s2n(0, ret); + return 1; +} + +static int ext_sigalgs_parse_serverhello(SSL *ssl, uint8_t *out_alert, + CBS *contents) { + if (contents != NULL) { + /* Servers MUST NOT send this extension. */ + *out_alert = SSL_AD_UNSUPPORTED_EXTENSION; + OPENSSL_PUT_ERROR(SSL, SSL_R_SIGNATURE_ALGORITHMS_EXTENSION_SENT_BY_SERVER); + return 0; } - if (s->srtp_profile) { - int el; + return 1; +} - ssl_add_serverhello_use_srtp_ext(s, 0, &el, 0); +static int ext_sigalgs_parse_clienthello(SSL *ssl, uint8_t *out_alert, + CBS *contents) { + OPENSSL_free(ssl->cert->peer_sigalgs); + ssl->cert->peer_sigalgs = NULL; + ssl->cert->peer_sigalgslen = 0; - if ((limit - ret - 4 - el) < 0) { - return NULL; - } + if (contents == NULL) { + return 1; + } - s2n(TLSEXT_TYPE_use_srtp, ret); - s2n(el, ret); + CBS supported_signature_algorithms; + if (!CBS_get_u16_length_prefixed(contents, &supported_signature_algorithms) || + CBS_len(contents) != 0 || + CBS_len(&supported_signature_algorithms) == 0 || + !tls1_parse_peer_sigalgs(ssl, &supported_signature_algorithms)) { + return 0; + } - if (!ssl_add_serverhello_use_srtp_ext(s, ret, &el, el)) { - OPENSSL_PUT_ERROR(SSL, ssl_add_serverhello_tlsext, ERR_R_INTERNAL_ERROR); - return NULL; - } - ret += el; + return 1; +} + +static int ext_sigalgs_add_serverhello(SSL *ssl, CBB *out) { + /* Servers MUST NOT send this extension. */ + return 1; +} + + +/* OCSP Stapling. + * + * https://tools.ietf.org/html/rfc6066#section-8 */ + +static void ext_ocsp_init(SSL *ssl) { + ssl->s3->tmp.certificate_status_expected = 0; +} + +static int ext_ocsp_add_clienthello(SSL *ssl, CBB *out) { + if (!ssl->ocsp_stapling_enabled) { + return 1; + } + + CBB contents; + if (!CBB_add_u16(out, TLSEXT_TYPE_status_request) || + !CBB_add_u16_length_prefixed(out, &contents) || + !CBB_add_u8(&contents, TLSEXT_STATUSTYPE_ocsp) || + !CBB_add_u16(&contents, 0 /* empty responder ID list */) || + !CBB_add_u16(&contents, 0 /* empty request extensions */) || + !CBB_flush(out)) { + return 0; } - next_proto_neg_seen = s->s3->next_proto_neg_seen; - s->s3->next_proto_neg_seen = 0; - if (next_proto_neg_seen && s->ctx->next_protos_advertised_cb) { - const uint8_t *npa; - unsigned int npalen; - int r; + return 1; +} - r = s->ctx->next_protos_advertised_cb( - s, &npa, &npalen, s->ctx->next_protos_advertised_cb_arg); - if (r == SSL_TLSEXT_ERR_OK) { - if ((long)(limit - ret - 4 - npalen) < 0) { - return NULL; - } - s2n(TLSEXT_TYPE_next_proto_neg, ret); - s2n(npalen, ret); - memcpy(ret, npa, npalen); - ret += npalen; - s->s3->next_proto_neg_seen = 1; - } +static int ext_ocsp_parse_serverhello(SSL *ssl, uint8_t *out_alert, + CBS *contents) { + if (contents == NULL) { + return 1; } - if (s->s3->alpn_selected) { - const uint8_t *selected = s->s3->alpn_selected; - size_t len = s->s3->alpn_selected_len; + if (CBS_len(contents) != 0) { + return 0; + } - if ((long)(limit - ret - 4 - 2 - 1 - len) < 0) { - return NULL; - } - s2n(TLSEXT_TYPE_application_layer_protocol_negotiation, ret); - s2n(3 + len, ret); - s2n(1 + len, ret); - *ret++ = len; - memcpy(ret, selected, len); - ret += len; - } - - /* If the client advertised support for Channel ID, and we have it - * enabled, then we want to echo it back. */ - if (s->s3->tlsext_channel_id_valid) { - if (limit - ret - 4 < 0) { - return NULL; - } - if (s->s3->tlsext_channel_id_new) { - s2n(TLSEXT_TYPE_channel_id_new, ret); - } else { - s2n(TLSEXT_TYPE_channel_id, ret); - } - s2n(0, ret); + ssl->s3->tmp.certificate_status_expected = 1; + return 1; +} + +static int ext_ocsp_parse_clienthello(SSL *ssl, uint8_t *out_alert, + CBS *contents) { + if (contents == NULL) { + return 1; } - extdatalen = ret - orig - 2; - if (extdatalen == 0) { - return orig; + uint8_t status_type; + if (!CBS_get_u8(contents, &status_type)) { + return 0; } - s2n(extdatalen, orig); - return ret; + /* We cannot decide whether OCSP stapling will occur yet because the correct + * SSL_CTX might not have been selected. */ + ssl->s3->tmp.ocsp_stapling_requested = status_type == TLSEXT_STATUSTYPE_ocsp; + + return 1; +} + +static int ext_ocsp_add_serverhello(SSL *ssl, CBB *out) { + /* The extension shouldn't be sent when resuming sessions. */ + if (ssl->hit || + !ssl->s3->tmp.ocsp_stapling_requested || + ssl->ctx->ocsp_response_length == 0) { + return 1; + } + + ssl->s3->tmp.certificate_status_expected = 1; + + return CBB_add_u16(out, TLSEXT_TYPE_status_request) && + CBB_add_u16(out, 0 /* length */); } -/* tls1_alpn_handle_client_hello is called to process the ALPN extension in a - * ClientHello. - * cbs: the contents of the extension, not including the type and length. - * out_alert: a pointer to the alert value to send in the event of a zero - * return. + +/* Next protocol negotiation. * - * returns: 1 on success. */ -static int tls1_alpn_handle_client_hello(SSL *s, CBS *cbs, int *out_alert) { - CBS protocol_name_list, protocol_name_list_copy; - const uint8_t *selected; + * https://htmlpreview.github.io/?https://github.com/agl/technotes/blob/master/nextprotoneg.html */ + +static void ext_npn_init(SSL *ssl) { + ssl->s3->next_proto_neg_seen = 0; +} + +static int ext_npn_add_clienthello(SSL *ssl, CBB *out) { + if (ssl->s3->initial_handshake_complete || + ssl->ctx->next_proto_select_cb == NULL || + SSL_IS_DTLS(ssl)) { + return 1; + } + + if (!CBB_add_u16(out, TLSEXT_TYPE_next_proto_neg) || + !CBB_add_u16(out, 0 /* length */)) { + return 0; + } + + return 1; +} + +static int ext_npn_parse_serverhello(SSL *ssl, uint8_t *out_alert, + CBS *contents) { + if (contents == NULL) { + return 1; + } + + /* If any of these are false then we should never have sent the NPN + * extension in the ClientHello and thus this function should never have been + * called. */ + assert(!ssl->s3->initial_handshake_complete); + assert(!SSL_IS_DTLS(ssl)); + assert(ssl->ctx->next_proto_select_cb != NULL); + + if (ssl->s3->alpn_selected != NULL) { + /* NPN and ALPN may not be negotiated in the same connection. */ + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + OPENSSL_PUT_ERROR(SSL, SSL_R_NEGOTIATED_BOTH_NPN_AND_ALPN); + return 0; + } + + const uint8_t *const orig_contents = CBS_data(contents); + const size_t orig_len = CBS_len(contents); + + while (CBS_len(contents) != 0) { + CBS proto; + if (!CBS_get_u8_length_prefixed(contents, &proto) || + CBS_len(&proto) == 0) { + return 0; + } + } + + uint8_t *selected; uint8_t selected_len; - int r; + if (ssl->ctx->next_proto_select_cb( + ssl, &selected, &selected_len, orig_contents, orig_len, + ssl->ctx->next_proto_select_cb_arg) != SSL_TLSEXT_ERR_OK) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return 0; + } + + OPENSSL_free(ssl->next_proto_negotiated); + ssl->next_proto_negotiated = BUF_memdup(selected, selected_len); + if (ssl->next_proto_negotiated == NULL) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return 0; + } + + ssl->next_proto_negotiated_len = selected_len; + ssl->s3->next_proto_neg_seen = 1; + + return 1; +} + +static int ext_npn_parse_clienthello(SSL *ssl, uint8_t *out_alert, + CBS *contents) { + if (contents != NULL && CBS_len(contents) != 0) { + return 0; + } + + if (contents == NULL || + ssl->s3->initial_handshake_complete || + /* If the ALPN extension is seen before NPN, ignore it. (If ALPN is seen + * afterwards, parsing the ALPN extension will clear + * |next_proto_neg_seen|. */ + ssl->s3->alpn_selected != NULL || + ssl->ctx->next_protos_advertised_cb == NULL || + SSL_IS_DTLS(ssl)) { + return 1; + } + + ssl->s3->next_proto_neg_seen = 1; + return 1; +} + +static int ext_npn_add_serverhello(SSL *ssl, CBB *out) { + /* |next_proto_neg_seen| might have been cleared when an ALPN extension was + * parsed. */ + if (!ssl->s3->next_proto_neg_seen) { + return 1; + } + + const uint8_t *npa; + unsigned npa_len; + + if (ssl->ctx->next_protos_advertised_cb( + ssl, &npa, &npa_len, ssl->ctx->next_protos_advertised_cb_arg) != + SSL_TLSEXT_ERR_OK) { + ssl->s3->next_proto_neg_seen = 0; + return 1; + } + + CBB contents; + if (!CBB_add_u16(out, TLSEXT_TYPE_next_proto_neg) || + !CBB_add_u16_length_prefixed(out, &contents) || + !CBB_add_bytes(&contents, npa, npa_len) || + !CBB_flush(out)) { + return 0; + } + + return 1; +} + + +/* Signed certificate timestamps. + * + * https://tools.ietf.org/html/rfc6962#section-3.3.1 */ - if (s->ctx->alpn_select_cb == NULL) { +static int ext_sct_add_clienthello(SSL *ssl, CBB *out) { + if (!ssl->signed_cert_timestamps_enabled) { return 1; } - if (!CBS_get_u16_length_prefixed(cbs, &protocol_name_list) || - CBS_len(cbs) != 0 || CBS_len(&protocol_name_list) < 2) { - goto parse_error; + if (!CBB_add_u16(out, TLSEXT_TYPE_certificate_timestamp) || + !CBB_add_u16(out, 0 /* length */)) { + return 0; + } + + return 1; +} + +static int ext_sct_parse_serverhello(SSL *ssl, uint8_t *out_alert, + CBS *contents) { + if (contents == NULL) { + return 1; + } + + /* If this is false then we should never have sent the SCT extension in the + * ClientHello and thus this function should never have been called. */ + assert(ssl->signed_cert_timestamps_enabled); + + if (CBS_len(contents) == 0) { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + /* Session resumption uses the original session information. */ + if (!ssl->hit && + !CBS_stow(contents, &ssl->session->tlsext_signed_cert_timestamp_list, + &ssl->session->tlsext_signed_cert_timestamp_list_length)) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return 0; + } + + return 1; +} + +static int ext_sct_parse_clienthello(SSL *ssl, uint8_t *out_alert, + CBS *contents) { + return contents == NULL || CBS_len(contents) == 0; +} + +static int ext_sct_add_serverhello(SSL *ssl, CBB *out) { + /* The extension shouldn't be sent when resuming sessions. */ + if (ssl->hit || + ssl->ctx->signed_cert_timestamp_list_length == 0) { + return 1; + } + + CBB contents; + return CBB_add_u16(out, TLSEXT_TYPE_certificate_timestamp) && + CBB_add_u16_length_prefixed(out, &contents) && + CBB_add_bytes(&contents, ssl->ctx->signed_cert_timestamp_list, + ssl->ctx->signed_cert_timestamp_list_length) && + CBB_flush(out); +} + + +/* Application-level Protocol Negotiation. + * + * https://tools.ietf.org/html/rfc7301 */ + +static void ext_alpn_init(SSL *ssl) { + OPENSSL_free(ssl->s3->alpn_selected); + ssl->s3->alpn_selected = NULL; +} + +static int ext_alpn_add_clienthello(SSL *ssl, CBB *out) { + if (ssl->alpn_client_proto_list == NULL || + ssl->s3->initial_handshake_complete) { + return 1; + } + + CBB contents, proto_list; + if (!CBB_add_u16(out, TLSEXT_TYPE_application_layer_protocol_negotiation) || + !CBB_add_u16_length_prefixed(out, &contents) || + !CBB_add_u16_length_prefixed(&contents, &proto_list) || + !CBB_add_bytes(&proto_list, ssl->alpn_client_proto_list, + ssl->alpn_client_proto_list_len) || + !CBB_flush(out)) { + return 0; + } + + return 1; +} + +static int ext_alpn_parse_serverhello(SSL *ssl, uint8_t *out_alert, + CBS *contents) { + if (contents == NULL) { + return 1; + } + + assert(!ssl->s3->initial_handshake_complete); + assert(ssl->alpn_client_proto_list != NULL); + + if (ssl->s3->next_proto_neg_seen) { + /* NPN and ALPN may not be negotiated in the same connection. */ + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + OPENSSL_PUT_ERROR(SSL, SSL_R_NEGOTIATED_BOTH_NPN_AND_ALPN); + return 0; + } + + /* The extension data consists of a ProtocolNameList which must have + * exactly one ProtocolName. Each of these is length-prefixed. */ + CBS protocol_name_list, protocol_name; + if (!CBS_get_u16_length_prefixed(contents, &protocol_name_list) || + CBS_len(contents) != 0 || + !CBS_get_u8_length_prefixed(&protocol_name_list, &protocol_name) || + /* Empty protocol names are forbidden. */ + CBS_len(&protocol_name) == 0 || + CBS_len(&protocol_name_list) != 0) { + return 0; + } + + if (!CBS_stow(&protocol_name, &ssl->s3->alpn_selected, + &ssl->s3->alpn_selected_len)) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return 0; + } + + return 1; +} + +static int ext_alpn_parse_clienthello(SSL *ssl, uint8_t *out_alert, + CBS *contents) { + if (contents == NULL) { + return 1; + } + + if (ssl->ctx->alpn_select_cb == NULL || + ssl->s3->initial_handshake_complete) { + return 1; + } + + /* ALPN takes precedence over NPN. */ + ssl->s3->next_proto_neg_seen = 0; + + CBS protocol_name_list; + if (!CBS_get_u16_length_prefixed(contents, &protocol_name_list) || + CBS_len(contents) != 0 || + CBS_len(&protocol_name_list) < 2) { + return 0; } /* Validate the protocol list. */ - protocol_name_list_copy = protocol_name_list; + CBS protocol_name_list_copy = protocol_name_list; while (CBS_len(&protocol_name_list_copy) > 0) { CBS protocol_name; - if (!CBS_get_u8_length_prefixed(&protocol_name_list_copy, &protocol_name)) { - goto parse_error; + if (!CBS_get_u8_length_prefixed(&protocol_name_list_copy, &protocol_name) || + /* Empty protocol names are forbidden. */ + CBS_len(&protocol_name) == 0) { + return 0; } } - r = s->ctx->alpn_select_cb( - s, &selected, &selected_len, CBS_data(&protocol_name_list), - CBS_len(&protocol_name_list), s->ctx->alpn_select_cb_arg); - if (r == SSL_TLSEXT_ERR_OK) { - OPENSSL_free(s->s3->alpn_selected); - s->s3->alpn_selected = BUF_memdup(selected, selected_len); - if (!s->s3->alpn_selected) { + const uint8_t *selected; + uint8_t selected_len; + if (ssl->ctx->alpn_select_cb( + ssl, &selected, &selected_len, CBS_data(&protocol_name_list), + CBS_len(&protocol_name_list), + ssl->ctx->alpn_select_cb_arg) == SSL_TLSEXT_ERR_OK) { + OPENSSL_free(ssl->s3->alpn_selected); + ssl->s3->alpn_selected = BUF_memdup(selected, selected_len); + if (ssl->s3->alpn_selected == NULL) { *out_alert = SSL_AD_INTERNAL_ERROR; return 0; } - s->s3->alpn_selected_len = selected_len; + ssl->s3->alpn_selected_len = selected_len; } return 1; +} -parse_error: - *out_alert = SSL_AD_DECODE_ERROR; - return 0; +static int ext_alpn_add_serverhello(SSL *ssl, CBB *out) { + if (ssl->s3->alpn_selected == NULL) { + return 1; + } + + CBB contents, proto_list, proto; + if (!CBB_add_u16(out, TLSEXT_TYPE_application_layer_protocol_negotiation) || + !CBB_add_u16_length_prefixed(out, &contents) || + !CBB_add_u16_length_prefixed(&contents, &proto_list) || + !CBB_add_u8_length_prefixed(&proto_list, &proto) || + !CBB_add_bytes(&proto, ssl->s3->alpn_selected, ssl->s3->alpn_selected_len) || + !CBB_flush(out)) { + return 0; + } + + return 1; } -static int ssl_scan_clienthello_tlsext(SSL *s, CBS *cbs, int *out_alert) { - int renegotiate_seen = 0; - CBS extensions; - s->should_ack_sni = 0; - s->srtp_profile = NULL; - s->s3->next_proto_neg_seen = 0; - s->s3->tmp.certificate_status_expected = 0; - s->s3->tmp.extended_master_secret = 0; +/* Channel ID. + * + * https://tools.ietf.org/html/draft-balfanz-tls-channelid-01 */ + +static void ext_channel_id_init(SSL *ssl) { + ssl->s3->tlsext_channel_id_valid = 0; +} + +static int ext_channel_id_add_clienthello(SSL *ssl, CBB *out) { + if (!ssl->tlsext_channel_id_enabled || + SSL_IS_DTLS(ssl)) { + return 1; + } + + if (!CBB_add_u16(out, TLSEXT_TYPE_channel_id) || + !CBB_add_u16(out, 0 /* length */)) { + return 0; + } - OPENSSL_free(s->s3->alpn_selected); - s->s3->alpn_selected = NULL; + return 1; +} - /* Clear any signature algorithms extension received */ - OPENSSL_free(s->cert->peer_sigalgs); - s->cert->peer_sigalgs = NULL; - s->cert->peer_sigalgslen = 0; +static int ext_channel_id_parse_serverhello(SSL *ssl, uint8_t *out_alert, + CBS *contents) { + if (contents == NULL) { + return 1; + } - /* Clear any shared signature algorithms */ - OPENSSL_free(s->cert->shared_sigalgs); - s->cert->shared_sigalgs = NULL; - s->cert->shared_sigalgslen = 0; + assert(!SSL_IS_DTLS(ssl)); + assert(ssl->tlsext_channel_id_enabled); - /* Clear ECC extensions */ - OPENSSL_free(s->s3->tmp.peer_ecpointformatlist); - s->s3->tmp.peer_ecpointformatlist = NULL; - s->s3->tmp.peer_ecpointformatlist_length = 0; + if (CBS_len(contents) != 0) { + return 0; + } - OPENSSL_free(s->s3->tmp.peer_ellipticcurvelist); - s->s3->tmp.peer_ellipticcurvelist = NULL; - s->s3->tmp.peer_ellipticcurvelist_length = 0; + ssl->s3->tlsext_channel_id_valid = 1; + return 1; +} - /* There may be no extensions. */ - if (CBS_len(cbs) == 0) { - goto ri_check; +static int ext_channel_id_parse_clienthello(SSL *ssl, uint8_t *out_alert, + CBS *contents) { + if (contents == NULL || + !ssl->tlsext_channel_id_enabled || + SSL_IS_DTLS(ssl)) { + return 1; } - /* Decode the extensions block and check it is valid. */ - if (!CBS_get_u16_length_prefixed(cbs, &extensions) || - !tls1_check_duplicate_extensions(&extensions)) { - *out_alert = SSL_AD_DECODE_ERROR; + if (CBS_len(contents) != 0) { return 0; } - while (CBS_len(&extensions) != 0) { - uint16_t type; - CBS extension; + ssl->s3->tlsext_channel_id_valid = 1; + return 1; +} - /* Decode the next extension. */ - if (!CBS_get_u16(&extensions, &type) || - !CBS_get_u16_length_prefixed(&extensions, &extension)) { - *out_alert = SSL_AD_DECODE_ERROR; +static int ext_channel_id_add_serverhello(SSL *ssl, CBB *out) { + if (!ssl->s3->tlsext_channel_id_valid) { + return 1; + } + + if (!CBB_add_u16(out, TLSEXT_TYPE_channel_id) || + !CBB_add_u16(out, 0 /* length */)) { + return 0; + } + + return 1; +} + + +/* Secure Real-time Transport Protocol (SRTP) extension. + * + * https://tools.ietf.org/html/rfc5764 */ + + +static void ext_srtp_init(SSL *ssl) { + ssl->srtp_profile = NULL; +} + +static int ext_srtp_add_clienthello(SSL *ssl, CBB *out) { + STACK_OF(SRTP_PROTECTION_PROFILE) *profiles = SSL_get_srtp_profiles(ssl); + if (profiles == NULL) { + return 1; + } + const size_t num_profiles = sk_SRTP_PROTECTION_PROFILE_num(profiles); + if (num_profiles == 0) { + return 1; + } + + CBB contents, profile_ids; + if (!CBB_add_u16(out, TLSEXT_TYPE_srtp) || + !CBB_add_u16_length_prefixed(out, &contents) || + !CBB_add_u16_length_prefixed(&contents, &profile_ids)) { + return 0; + } + + size_t i; + for (i = 0; i < num_profiles; i++) { + if (!CBB_add_u16(&profile_ids, + sk_SRTP_PROTECTION_PROFILE_value(profiles, i)->id)) { return 0; } + } - /* The servername extension is treated as follows: - - - Only the hostname type is supported with a maximum length of 255. - - The servername is rejected if too long or if it contains zeros, in - which case an fatal alert is generated. - - The servername field is maintained together with the session cache. - - When a session is resumed, the servername call back invoked in order - to allow the application to position itself to the right context. - - The servername is acknowledged if it is new for a session or when - it is identical to a previously used for the same session. - Applications can control the behaviour. They can at any time - set a 'desirable' servername for a new SSL object. This can be the - case for example with HTTPS when a Host: header field is received and - a renegotiation is requested. In this case, a possible servername - presented in the new client hello is only acknowledged if it matches - the value of the Host: field. - - Applications must use SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION - if they provide for changing an explicit servername context for the - session, - i.e. when the session has been established with a servername extension. - - On session reconnect, the servername extension may be absent. */ - - if (type == TLSEXT_TYPE_server_name) { - CBS server_name_list; - char have_seen_host_name = 0; - - if (!CBS_get_u16_length_prefixed(&extension, &server_name_list) || - CBS_len(&server_name_list) < 1 || CBS_len(&extension) != 0) { - *out_alert = SSL_AD_DECODE_ERROR; - return 0; - } + if (!CBB_add_u8(&contents, 0 /* empty use_mki value */) || + !CBB_flush(out)) { + return 0; + } - /* Decode each ServerName in the extension. */ - while (CBS_len(&server_name_list) > 0) { - uint8_t name_type; - CBS host_name; + return 1; +} - /* Decode the NameType. */ - if (!CBS_get_u8(&server_name_list, &name_type)) { - *out_alert = SSL_AD_DECODE_ERROR; - return 0; - } +static int ext_srtp_parse_serverhello(SSL *ssl, uint8_t *out_alert, + CBS *contents) { + if (contents == NULL) { + return 1; + } - /* Only host_name is supported. */ - if (name_type != TLSEXT_NAMETYPE_host_name) { - continue; - } + /* The extension consists of a u16-prefixed profile ID list containing a + * single uint16_t profile ID, then followed by a u8-prefixed srtp_mki field. + * + * See https://tools.ietf.org/html/rfc5764#section-4.1.1 */ + CBS profile_ids, srtp_mki; + uint16_t profile_id; + if (!CBS_get_u16_length_prefixed(contents, &profile_ids) || + !CBS_get_u16(&profile_ids, &profile_id) || + CBS_len(&profile_ids) != 0 || + !CBS_get_u8_length_prefixed(contents, &srtp_mki) || + CBS_len(contents) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST); + return 0; + } - if (have_seen_host_name) { - /* The ServerNameList MUST NOT contain more than one name of the same - * name_type. */ - *out_alert = SSL_AD_DECODE_ERROR; - return 0; - } + if (CBS_len(&srtp_mki) != 0) { + /* Must be no MKI, since we never offer one. */ + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SRTP_MKI_VALUE); + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return 0; + } - have_seen_host_name = 1; + STACK_OF(SRTP_PROTECTION_PROFILE) *profiles = SSL_get_srtp_profiles(ssl); - if (!CBS_get_u16_length_prefixed(&server_name_list, &host_name) || - CBS_len(&host_name) < 1) { - *out_alert = SSL_AD_DECODE_ERROR; - return 0; - } + /* Check to see if the server gave us something we support (and presumably + * offered). */ + size_t i; + for (i = 0; i < sk_SRTP_PROTECTION_PROFILE_num(profiles); i++) { + const SRTP_PROTECTION_PROFILE *profile = + sk_SRTP_PROTECTION_PROFILE_value(profiles, i); - if (CBS_len(&host_name) > TLSEXT_MAXLEN_host_name || - CBS_contains_zero_byte(&host_name)) { - *out_alert = SSL_AD_UNRECOGNIZED_NAME; - return 0; - } + if (profile->id == profile_id) { + ssl->srtp_profile = profile; + return 1; + } + } - if (!s->hit) { - assert(s->session->tlsext_hostname == NULL); - if (s->session->tlsext_hostname) { - /* This should be impossible. */ - *out_alert = SSL_AD_DECODE_ERROR; - return 0; - } - - /* Copy the hostname as a string. */ - if (!CBS_strdup(&host_name, &s->session->tlsext_hostname)) { - *out_alert = SSL_AD_INTERNAL_ERROR; - return 0; - } - - s->should_ack_sni = 1; - } - } - } else if (type == TLSEXT_TYPE_ec_point_formats) { - CBS ec_point_format_list; + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST); + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return 0; +} - if (!CBS_get_u8_length_prefixed(&extension, &ec_point_format_list) || - CBS_len(&extension) != 0) { - *out_alert = SSL_AD_DECODE_ERROR; - return 0; - } +static int ext_srtp_parse_clienthello(SSL *ssl, uint8_t *out_alert, + CBS *contents) { + if (contents == NULL) { + return 1; + } - if (!CBS_stow(&ec_point_format_list, &s->s3->tmp.peer_ecpointformatlist, - &s->s3->tmp.peer_ecpointformatlist_length)) { - *out_alert = SSL_AD_INTERNAL_ERROR; - return 0; - } - } else if (type == TLSEXT_TYPE_elliptic_curves) { - CBS elliptic_curve_list; - size_t i, num_curves; - - if (!CBS_get_u16_length_prefixed(&extension, &elliptic_curve_list) || - CBS_len(&elliptic_curve_list) == 0 || - (CBS_len(&elliptic_curve_list) & 1) != 0 || - CBS_len(&extension) != 0) { - *out_alert = SSL_AD_DECODE_ERROR; - return 0; - } + CBS profile_ids, srtp_mki; + if (!CBS_get_u16_length_prefixed(contents, &profile_ids) || + CBS_len(&profile_ids) < 2 || + !CBS_get_u8_length_prefixed(contents, &srtp_mki) || + CBS_len(contents) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST); + return 0; + } + /* Discard the MKI value for now. */ - OPENSSL_free(s->s3->tmp.peer_ellipticcurvelist); - s->s3->tmp.peer_ellipticcurvelist_length = 0; + const STACK_OF(SRTP_PROTECTION_PROFILE) *server_profiles = + SSL_get_srtp_profiles(ssl); - s->s3->tmp.peer_ellipticcurvelist = - (uint16_t *)OPENSSL_malloc(CBS_len(&elliptic_curve_list)); + /* Pick the server's most preferred profile. */ + size_t i; + for (i = 0; i < sk_SRTP_PROTECTION_PROFILE_num(server_profiles); i++) { + const SRTP_PROTECTION_PROFILE *server_profile = + sk_SRTP_PROTECTION_PROFILE_value(server_profiles, i); - if (s->s3->tmp.peer_ellipticcurvelist == NULL) { - *out_alert = SSL_AD_INTERNAL_ERROR; + CBS profile_ids_tmp; + CBS_init(&profile_ids_tmp, CBS_data(&profile_ids), CBS_len(&profile_ids)); + + while (CBS_len(&profile_ids_tmp) > 0) { + uint16_t profile_id; + if (!CBS_get_u16(&profile_ids_tmp, &profile_id)) { return 0; } - num_curves = CBS_len(&elliptic_curve_list) / 2; - for (i = 0; i < num_curves; i++) { - if (!CBS_get_u16(&elliptic_curve_list, - &s->s3->tmp.peer_ellipticcurvelist[i])) { - *out_alert = SSL_AD_INTERNAL_ERROR; - return 0; - } + if (server_profile->id == profile_id) { + ssl->srtp_profile = server_profile; + return 1; } + } + } - if (CBS_len(&elliptic_curve_list) != 0) { - *out_alert = SSL_AD_INTERNAL_ERROR; - return 0; - } + return 1; +} - s->s3->tmp.peer_ellipticcurvelist_length = num_curves; - } else if (type == TLSEXT_TYPE_renegotiate) { - if (!ssl_parse_clienthello_renegotiate_ext(s, &extension, out_alert)) { - return 0; - } - renegotiate_seen = 1; - } else if (type == TLSEXT_TYPE_signature_algorithms) { - CBS supported_signature_algorithms; +static int ext_srtp_add_serverhello(SSL *ssl, CBB *out) { + if (ssl->srtp_profile == NULL) { + return 1; + } - if (!CBS_get_u16_length_prefixed(&extension, - &supported_signature_algorithms) || - CBS_len(&extension) != 0) { - *out_alert = SSL_AD_DECODE_ERROR; - return 0; - } + CBB contents, profile_ids; + if (!CBB_add_u16(out, TLSEXT_TYPE_srtp) || + !CBB_add_u16_length_prefixed(out, &contents) || + !CBB_add_u16_length_prefixed(&contents, &profile_ids) || + !CBB_add_u16(&profile_ids, ssl->srtp_profile->id) || + !CBB_add_u8(&contents, 0 /* empty MKI */) || + !CBB_flush(out)) { + return 0; + } - /* Ensure the signature algorithms are non-empty. It contains a list of - * SignatureAndHashAlgorithms which are two bytes each. */ - if (CBS_len(&supported_signature_algorithms) == 0 || - (CBS_len(&supported_signature_algorithms) % 2) != 0) { - *out_alert = SSL_AD_DECODE_ERROR; - return 0; - } + return 1; +} - if (!tls1_process_sigalgs(s, &supported_signature_algorithms)) { - *out_alert = SSL_AD_DECODE_ERROR; - return 0; - } - /* If sigalgs received and no shared algorithms fatal error. */ - if (s->cert->peer_sigalgs && !s->cert->shared_sigalgs) { - OPENSSL_PUT_ERROR(SSL, ssl_scan_clienthello_tlsext, - SSL_R_NO_SHARED_SIGATURE_ALGORITHMS); - *out_alert = SSL_AD_ILLEGAL_PARAMETER; - return 0; - } - } else if (type == TLSEXT_TYPE_next_proto_neg && - !s->s3->initial_handshake_complete && - s->s3->alpn_selected == NULL && !SSL_IS_DTLS(s)) { - /* The extension must be empty. */ - if (CBS_len(&extension) != 0) { - *out_alert = SSL_AD_DECODE_ERROR; - return 0; - } - s->s3->next_proto_neg_seen = 1; - } else if (type == TLSEXT_TYPE_application_layer_protocol_negotiation && - s->ctx->alpn_select_cb && !s->s3->initial_handshake_complete) { - if (!tls1_alpn_handle_client_hello(s, &extension, out_alert)) { - return 0; - } - /* ALPN takes precedence over NPN. */ - s->s3->next_proto_neg_seen = 0; - } else if (type == TLSEXT_TYPE_channel_id && s->tlsext_channel_id_enabled && - !SSL_IS_DTLS(s)) { - /* The extension must be empty. */ - if (CBS_len(&extension) != 0) { - *out_alert = SSL_AD_DECODE_ERROR; - return 0; - } - s->s3->tlsext_channel_id_valid = 1; - } else if (type == TLSEXT_TYPE_channel_id_new && - s->tlsext_channel_id_enabled && !SSL_IS_DTLS(s)) { - /* The extension must be empty. */ - if (CBS_len(&extension) != 0) { - *out_alert = SSL_AD_DECODE_ERROR; - return 0; - } +/* EC point formats. + * + * https://tools.ietf.org/html/rfc4492#section-5.1.2 */ - s->s3->tlsext_channel_id_valid = 1; - s->s3->tlsext_channel_id_new = 1; - } else if (type == TLSEXT_TYPE_use_srtp) { - if (!ssl_parse_clienthello_use_srtp_ext(s, &extension, out_alert)) { - return 0; - } - } else if (type == TLSEXT_TYPE_extended_master_secret && - s->version != SSL3_VERSION) { - if (CBS_len(&extension) != 0) { - *out_alert = SSL_AD_DECODE_ERROR; - return 0; - } +static int ssl_any_ec_cipher_suites_enabled(const SSL *ssl) { + if (ssl->version < TLS1_VERSION && !SSL_IS_DTLS(ssl)) { + return 0; + } + + const STACK_OF(SSL_CIPHER) *cipher_stack = SSL_get_ciphers(ssl); + + size_t i; + for (i = 0; i < sk_SSL_CIPHER_num(cipher_stack); i++) { + const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(cipher_stack, i); - s->s3->tmp.extended_master_secret = 1; + const uint32_t alg_k = cipher->algorithm_mkey; + const uint32_t alg_a = cipher->algorithm_auth; + if ((alg_k & SSL_kECDHE) || (alg_a & SSL_aECDSA)) { + return 1; } } -ri_check: - /* Need RI if renegotiating */ + return 0; +} - if (!renegotiate_seen && s->s3->initial_handshake_complete && - !(s->options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION)) { - *out_alert = SSL_AD_HANDSHAKE_FAILURE; - OPENSSL_PUT_ERROR(SSL, ssl_scan_clienthello_tlsext, - SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED); +static int ext_ec_point_add_extension(SSL *ssl, CBB *out) { + CBB contents, formats; + if (!CBB_add_u16(out, TLSEXT_TYPE_ec_point_formats) || + !CBB_add_u16_length_prefixed(out, &contents) || + !CBB_add_u8_length_prefixed(&contents, &formats) || + !CBB_add_u8(&formats, TLSEXT_ECPOINTFORMAT_uncompressed) || + !CBB_flush(out)) { return 0; } return 1; } -int ssl_parse_clienthello_tlsext(SSL *s, CBS *cbs) { - int alert = -1; - if (ssl_scan_clienthello_tlsext(s, cbs, &alert) <= 0) { - ssl3_send_alert(s, SSL3_AL_FATAL, alert); +static int ext_ec_point_add_clienthello(SSL *ssl, CBB *out) { + if (!ssl_any_ec_cipher_suites_enabled(ssl)) { + return 1; + } + + return ext_ec_point_add_extension(ssl, out); +} + +static int ext_ec_point_parse_serverhello(SSL *ssl, uint8_t *out_alert, + CBS *contents) { + if (contents == NULL) { + return 1; + } + + CBS ec_point_format_list; + if (!CBS_get_u8_length_prefixed(contents, &ec_point_format_list) || + CBS_len(contents) != 0) { return 0; } - if (ssl_check_clienthello_tlsext(s) <= 0) { - OPENSSL_PUT_ERROR(SSL, ssl_parse_clienthello_tlsext, - SSL_R_CLIENTHELLO_TLSEXT); + /* Per RFC 4492, section 5.1.2, implementations MUST support the uncompressed + * point format. */ + if (memchr(CBS_data(&ec_point_format_list), TLSEXT_ECPOINTFORMAT_uncompressed, + CBS_len(&ec_point_format_list)) == NULL) { + *out_alert = SSL_AD_ILLEGAL_PARAMETER; return 0; } return 1; } -/* ssl_next_proto_validate validates a Next Protocol Negotiation block. No - * elements of zero length are allowed and the set of elements must exactly - * fill the length of the block. */ -static char ssl_next_proto_validate(const CBS *cbs) { - CBS copy = *cbs; +static int ext_ec_point_parse_clienthello(SSL *ssl, uint8_t *out_alert, + CBS *contents) { + return ext_ec_point_parse_serverhello(ssl, out_alert, contents); +} + +static int ext_ec_point_add_serverhello(SSL *ssl, CBB *out) { + const uint32_t alg_k = ssl->s3->tmp.new_cipher->algorithm_mkey; + const uint32_t alg_a = ssl->s3->tmp.new_cipher->algorithm_auth; + const int using_ecc = (alg_k & SSL_kECDHE) || (alg_a & SSL_aECDSA); - while (CBS_len(©) != 0) { - CBS proto; - if (!CBS_get_u8_length_prefixed(©, &proto) || CBS_len(&proto) == 0) { + if (!using_ecc) { + return 1; + } + + return ext_ec_point_add_extension(ssl, out); +} + + +/* EC supported curves. + * + * https://tools.ietf.org/html/rfc4492#section-5.1.2 */ + +static void ext_ec_curves_init(SSL *ssl) { + OPENSSL_free(ssl->s3->tmp.peer_ellipticcurvelist); + ssl->s3->tmp.peer_ellipticcurvelist = NULL; + ssl->s3->tmp.peer_ellipticcurvelist_length = 0; +} + +static int ext_ec_curves_add_clienthello(SSL *ssl, CBB *out) { + if (!ssl_any_ec_cipher_suites_enabled(ssl)) { + return 1; + } + + CBB contents, curves_bytes; + if (!CBB_add_u16(out, TLSEXT_TYPE_elliptic_curves) || + !CBB_add_u16_length_prefixed(out, &contents) || + !CBB_add_u16_length_prefixed(&contents, &curves_bytes)) { + return 0; + } + + const uint16_t *curves; + size_t curves_len; + tls1_get_curvelist(ssl, 0, &curves, &curves_len); + + size_t i; + for (i = 0; i < curves_len; i++) { + if (!CBB_add_u16(&curves_bytes, curves[i])) { return 0; } } + return CBB_flush(out); +} + +static int ext_ec_curves_parse_serverhello(SSL *ssl, uint8_t *out_alert, + CBS *contents) { + /* This extension is not expected to be echoed by servers and is ignored. */ return 1; } -static int ssl_scan_serverhello_tlsext(SSL *s, CBS *cbs, int *out_alert) { - int tlsext_servername = 0; - int renegotiate_seen = 0; - CBS extensions; +static int ext_ec_curves_parse_clienthello(SSL *ssl, uint8_t *out_alert, + CBS *contents) { + if (contents == NULL) { + return 1; + } - /* TODO(davidben): Move all of these to some per-handshake state that gets - * systematically reset on a new handshake; perhaps allocate it fresh each - * time so it's not even kept around post-handshake. */ - s->s3->next_proto_neg_seen = 0; - s->tlsext_ticket_expected = 0; - s->s3->tmp.certificate_status_expected = 0; - s->s3->tmp.extended_master_secret = 0; - s->srtp_profile = NULL; + CBS elliptic_curve_list; + if (!CBS_get_u16_length_prefixed(contents, &elliptic_curve_list) || + CBS_len(&elliptic_curve_list) == 0 || + (CBS_len(&elliptic_curve_list) & 1) != 0 || + CBS_len(contents) != 0) { + return 0; + } - OPENSSL_free(s->s3->alpn_selected); - s->s3->alpn_selected = NULL; + ssl->s3->tmp.peer_ellipticcurvelist = + (uint16_t *)OPENSSL_malloc(CBS_len(&elliptic_curve_list)); - /* Clear ECC extensions */ - OPENSSL_free(s->s3->tmp.peer_ecpointformatlist); - s->s3->tmp.peer_ecpointformatlist = NULL; - s->s3->tmp.peer_ecpointformatlist_length = 0; + if (ssl->s3->tmp.peer_ellipticcurvelist == NULL) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return 0; + } - /* There may be no extensions. */ - if (CBS_len(cbs) == 0) { - goto ri_check; + const size_t num_curves = CBS_len(&elliptic_curve_list) / 2; + size_t i; + for (i = 0; i < num_curves; i++) { + if (!CBS_get_u16(&elliptic_curve_list, + &ssl->s3->tmp.peer_ellipticcurvelist[i])) { + goto err; + } } - /* Decode the extensions block and check it is valid. */ - if (!CBS_get_u16_length_prefixed(cbs, &extensions) || - !tls1_check_duplicate_extensions(&extensions)) { - *out_alert = SSL_AD_DECODE_ERROR; - return 0; + assert(CBS_len(&elliptic_curve_list) == 0); + ssl->s3->tmp.peer_ellipticcurvelist_length = num_curves; + + return 1; + +err: + OPENSSL_free(ssl->s3->tmp.peer_ellipticcurvelist); + ssl->s3->tmp.peer_ellipticcurvelist = NULL; + *out_alert = SSL_AD_INTERNAL_ERROR; + return 0; +} + +static int ext_ec_curves_add_serverhello(SSL *ssl, CBB *out) { + /* Servers don't echo this extension. */ + return 1; +} + + +/* kExtensions contains all the supported extensions. */ +static const struct tls_extension kExtensions[] = { + { + /* The renegotiation extension must always be at index zero because the + * |received| and |sent| bitsets need to be tweaked when the "extension" is + * sent as an SCSV. */ + TLSEXT_TYPE_renegotiate, + NULL, + ext_ri_add_clienthello, + ext_ri_parse_serverhello, + ext_ri_parse_clienthello, + ext_ri_add_serverhello, + }, + { + TLSEXT_TYPE_server_name, + ext_sni_init, + ext_sni_add_clienthello, + ext_sni_parse_serverhello, + ext_sni_parse_clienthello, + ext_sni_add_serverhello, + }, + { + TLSEXT_TYPE_extended_master_secret, + ext_ems_init, + ext_ems_add_clienthello, + ext_ems_parse_serverhello, + ext_ems_parse_clienthello, + ext_ems_add_serverhello, + }, + { + TLSEXT_TYPE_session_ticket, + NULL, + ext_ticket_add_clienthello, + ext_ticket_parse_serverhello, + ext_ticket_parse_clienthello, + ext_ticket_add_serverhello, + }, + { + TLSEXT_TYPE_signature_algorithms, + NULL, + ext_sigalgs_add_clienthello, + ext_sigalgs_parse_serverhello, + ext_sigalgs_parse_clienthello, + ext_sigalgs_add_serverhello, + }, + { + TLSEXT_TYPE_status_request, + ext_ocsp_init, + ext_ocsp_add_clienthello, + ext_ocsp_parse_serverhello, + ext_ocsp_parse_clienthello, + ext_ocsp_add_serverhello, + }, + { + TLSEXT_TYPE_next_proto_neg, + ext_npn_init, + ext_npn_add_clienthello, + ext_npn_parse_serverhello, + ext_npn_parse_clienthello, + ext_npn_add_serverhello, + }, + { + TLSEXT_TYPE_certificate_timestamp, + NULL, + ext_sct_add_clienthello, + ext_sct_parse_serverhello, + ext_sct_parse_clienthello, + ext_sct_add_serverhello, + }, + { + TLSEXT_TYPE_application_layer_protocol_negotiation, + ext_alpn_init, + ext_alpn_add_clienthello, + ext_alpn_parse_serverhello, + ext_alpn_parse_clienthello, + ext_alpn_add_serverhello, + }, + { + TLSEXT_TYPE_channel_id, + ext_channel_id_init, + ext_channel_id_add_clienthello, + ext_channel_id_parse_serverhello, + ext_channel_id_parse_clienthello, + ext_channel_id_add_serverhello, + }, + { + TLSEXT_TYPE_srtp, + ext_srtp_init, + ext_srtp_add_clienthello, + ext_srtp_parse_serverhello, + ext_srtp_parse_clienthello, + ext_srtp_add_serverhello, + }, + { + TLSEXT_TYPE_ec_point_formats, + NULL, + ext_ec_point_add_clienthello, + ext_ec_point_parse_serverhello, + ext_ec_point_parse_clienthello, + ext_ec_point_add_serverhello, + }, + { + TLSEXT_TYPE_elliptic_curves, + ext_ec_curves_init, + ext_ec_curves_add_clienthello, + ext_ec_curves_parse_serverhello, + ext_ec_curves_parse_clienthello, + ext_ec_curves_add_serverhello, + }, +}; + +#define kNumExtensions (sizeof(kExtensions) / sizeof(struct tls_extension)) + +OPENSSL_COMPILE_ASSERT(kNumExtensions <= + sizeof(((SSL *)NULL)->s3->tmp.extensions.sent) * 8, + too_many_extensions_for_sent_bitset); +OPENSSL_COMPILE_ASSERT(kNumExtensions <= + sizeof(((SSL *)NULL)->s3->tmp.extensions.received) * + 8, + too_many_extensions_for_received_bitset); + +static const struct tls_extension *tls_extension_find(uint32_t *out_index, + uint16_t value) { + unsigned i; + for (i = 0; i < kNumExtensions; i++) { + if (kExtensions[i].value == value) { + *out_index = i; + return &kExtensions[i]; + } } - while (CBS_len(&extensions) != 0) { - uint16_t type; - CBS extension; + return NULL; +} - /* Decode the next extension. */ - if (!CBS_get_u16(&extensions, &type) || - !CBS_get_u16_length_prefixed(&extensions, &extension)) { - *out_alert = SSL_AD_DECODE_ERROR; - return 0; +int SSL_extension_supported(unsigned extension_value) { + uint32_t index; + return extension_value == TLSEXT_TYPE_padding || + tls_extension_find(&index, extension_value) != NULL; +} + +/* header_len is the length of the ClientHello header written so far, used to + * compute padding. It does not include the record header. Pass 0 if no padding + * is to be done. */ +uint8_t *ssl_add_clienthello_tlsext(SSL *s, uint8_t *const buf, + uint8_t *const limit, size_t header_len) { + /* don't add extensions for SSLv3 unless doing secure renegotiation */ + if (s->client_version == SSL3_VERSION && !s->s3->send_connection_binding) { + return buf; + } + + CBB cbb, extensions; + CBB_zero(&cbb); + if (!CBB_init_fixed(&cbb, buf, limit - buf) || + !CBB_add_u16_length_prefixed(&cbb, &extensions)) { + goto err; + } + + s->s3->tmp.extensions.sent = 0; + s->s3->tmp.custom_extensions.sent = 0; + + size_t i; + for (i = 0; i < kNumExtensions; i++) { + if (kExtensions[i].init != NULL) { + kExtensions[i].init(s); } + } - if (type == TLSEXT_TYPE_server_name) { - /* The extension must be empty. */ - if (CBS_len(&extension) != 0) { - *out_alert = SSL_AD_DECODE_ERROR; - return 0; + for (i = 0; i < kNumExtensions; i++) { + const size_t len_before = CBB_len(&extensions); + if (!kExtensions[i].add_clienthello(s, &extensions)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_ADDING_EXTENSION); + ERR_add_error_dataf("extension: %u", (unsigned)kExtensions[i].value); + goto err; + } + + if (CBB_len(&extensions) != len_before) { + s->s3->tmp.extensions.sent |= (1u << i); + } + } + + if (!custom_ext_add_clienthello(s, &extensions)) { + goto err; + } + + if (header_len > 0) { + header_len += CBB_len(&extensions); + if (header_len > 0xff && header_len < 0x200) { + /* Add padding to workaround bugs in F5 terminators. See + * https://tools.ietf.org/html/draft-agl-tls-padding-03 + * + * NB: because this code works out the length of all existing extensions + * it MUST always appear last. */ + size_t padding_len = 0x200 - header_len; + /* Extensions take at least four bytes to encode. Always include least + * one byte of data if including the extension. WebSphere Application + * Server 7.0 is intolerant to the last extension being zero-length. */ + if (padding_len >= 4 + 1) { + padding_len -= 4; + } else { + padding_len = 1; } - /* We must have sent it in ClientHello. */ - if (s->tlsext_hostname == NULL) { - *out_alert = SSL_AD_UNSUPPORTED_EXTENSION; - return 0; + uint8_t *padding_bytes; + if (!CBB_add_u16(&extensions, TLSEXT_TYPE_padding) || + !CBB_add_u16(&extensions, padding_len) || + !CBB_add_space(&extensions, &padding_bytes, padding_len)) { + goto err; } - tlsext_servername = 1; - } else if (type == TLSEXT_TYPE_ec_point_formats) { - CBS ec_point_format_list; + memset(padding_bytes, 0, padding_len); + } + } + + if (!CBB_flush(&cbb)) { + goto err; + } + + uint8_t *ret = buf; + const size_t cbb_len = CBB_len(&cbb); + /* If only two bytes have been written then the extensions are actually empty + * and those two bytes are the zero length. In that case, we don't bother + * sending the extensions length. */ + if (cbb_len > 2) { + ret += cbb_len; + } + + CBB_cleanup(&cbb); + return ret; + +err: + CBB_cleanup(&cbb); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return NULL; +} + +uint8_t *ssl_add_serverhello_tlsext(SSL *s, uint8_t *const buf, + uint8_t *const limit) { + /* don't add extensions for SSLv3, unless doing secure renegotiation */ + if (s->version == SSL3_VERSION && !s->s3->send_connection_binding) { + return buf; + } + + CBB cbb, extensions; + CBB_zero(&cbb); + if (!CBB_init_fixed(&cbb, buf, limit - buf) || + !CBB_add_u16_length_prefixed(&cbb, &extensions)) { + goto err; + } + + unsigned i; + for (i = 0; i < kNumExtensions; i++) { + if (!(s->s3->tmp.extensions.received & (1u << i))) { + /* Don't send extensions that were not received. */ + continue; + } + + if (!kExtensions[i].add_serverhello(s, &extensions)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_ADDING_EXTENSION); + ERR_add_error_dataf("extension: %u", (unsigned)kExtensions[i].value); + goto err; + } + } + + if (!custom_ext_add_serverhello(s, &extensions)) { + goto err; + } + + if (!CBB_flush(&cbb)) { + goto err; + } + + uint8_t *ret = buf; + const size_t cbb_len = CBB_len(&cbb); + /* If only two bytes have been written then the extensions are actually empty + * and those two bytes are the zero length. In that case, we don't bother + * sending the extensions length. */ + if (cbb_len > 2) { + ret += cbb_len; + } + + CBB_cleanup(&cbb); + return ret; + +err: + CBB_cleanup(&cbb); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return NULL; +} + +static int ssl_scan_clienthello_tlsext(SSL *s, CBS *cbs, int *out_alert) { + size_t i; + for (i = 0; i < kNumExtensions; i++) { + if (kExtensions[i].init != NULL) { + kExtensions[i].init(s); + } + } + + s->s3->tmp.extensions.received = 0; + s->s3->tmp.custom_extensions.received = 0; + /* The renegotiation extension must always be at index zero because the + * |received| and |sent| bitsets need to be tweaked when the "extension" is + * sent as an SCSV. */ + assert(kExtensions[0].value == TLSEXT_TYPE_renegotiate); - if (!CBS_get_u8_length_prefixed(&extension, &ec_point_format_list) || - CBS_len(&extension) != 0) { + /* There may be no extensions. */ + if (CBS_len(cbs) != 0) { + /* Decode the extensions block and check it is valid. */ + CBS extensions; + if (!CBS_get_u16_length_prefixed(cbs, &extensions) || + !tls1_check_duplicate_extensions(&extensions)) { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + while (CBS_len(&extensions) != 0) { + uint16_t type; + CBS extension; + + /* Decode the next extension. */ + if (!CBS_get_u16(&extensions, &type) || + !CBS_get_u16_length_prefixed(&extensions, &extension)) { *out_alert = SSL_AD_DECODE_ERROR; return 0; } - if (!CBS_stow(&ec_point_format_list, &s->s3->tmp.peer_ecpointformatlist, - &s->s3->tmp.peer_ecpointformatlist_length)) { - *out_alert = SSL_AD_INTERNAL_ERROR; - return 0; - } - } else if (type == TLSEXT_TYPE_session_ticket) { - if ((SSL_get_options(s) & SSL_OP_NO_TICKET) || CBS_len(&extension) > 0) { - *out_alert = SSL_AD_UNSUPPORTED_EXTENSION; - return 0; + unsigned ext_index; + const struct tls_extension *const ext = + tls_extension_find(&ext_index, type); + + if (ext == NULL) { + if (!custom_ext_parse_clienthello(s, out_alert, type, &extension)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_PARSING_EXTENSION); + return 0; + } + continue; } - s->tlsext_ticket_expected = 1; - } else if (type == TLSEXT_TYPE_status_request) { - /* The extension MUST be empty and may only sent if we've requested a - * status request message. */ - if (CBS_len(&extension) != 0) { - *out_alert = SSL_AD_DECODE_ERROR; + s->s3->tmp.extensions.received |= (1u << ext_index); + uint8_t alert = SSL_AD_DECODE_ERROR; + if (!ext->parse_clienthello(s, &alert, &extension)) { + *out_alert = alert; + OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_PARSING_EXTENSION); + ERR_add_error_dataf("extension: %u", (unsigned)type); return 0; } + } + } - if (!s->ocsp_stapling_enabled) { - *out_alert = SSL_AD_UNSUPPORTED_EXTENSION; + for (i = 0; i < kNumExtensions; i++) { + if (!(s->s3->tmp.extensions.received & (1u << i))) { + /* Extension wasn't observed so call the callback with a NULL + * parameter. */ + uint8_t alert = SSL_AD_DECODE_ERROR; + if (!kExtensions[i].parse_clienthello(s, &alert, NULL)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_EXTENSION); + ERR_add_error_dataf("extension: %u", (unsigned)kExtensions[i].value); + *out_alert = alert; return 0; } + } + } - /* Set a flag to expect a CertificateStatus message */ - s->s3->tmp.certificate_status_expected = 1; - } else if (type == TLSEXT_TYPE_next_proto_neg && - !s->s3->initial_handshake_complete && !SSL_IS_DTLS(s)) { - uint8_t *selected; - uint8_t selected_len; + return 1; +} - /* We must have requested it. */ - if (s->ctx->next_proto_select_cb == NULL) { - *out_alert = SSL_AD_UNSUPPORTED_EXTENSION; - return 0; - } +int ssl_parse_clienthello_tlsext(SSL *s, CBS *cbs) { + int alert = -1; + if (ssl_scan_clienthello_tlsext(s, cbs, &alert) <= 0) { + ssl3_send_alert(s, SSL3_AL_FATAL, alert); + return 0; + } - /* The data must be valid. */ - if (!ssl_next_proto_validate(&extension)) { - *out_alert = SSL_AD_DECODE_ERROR; - return 0; - } + if (ssl_check_clienthello_tlsext(s) <= 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_CLIENTHELLO_TLSEXT); + return 0; + } - if (s->ctx->next_proto_select_cb( - s, &selected, &selected_len, CBS_data(&extension), - CBS_len(&extension), - s->ctx->next_proto_select_cb_arg) != SSL_TLSEXT_ERR_OK) { - *out_alert = SSL_AD_INTERNAL_ERROR; - return 0; - } + return 1; +} - s->next_proto_negotiated = BUF_memdup(selected, selected_len); - if (s->next_proto_negotiated == NULL) { - *out_alert = SSL_AD_INTERNAL_ERROR; - return 0; - } +static int ssl_scan_serverhello_tlsext(SSL *s, CBS *cbs, int *out_alert) { + uint32_t received = 0; + assert(kNumExtensions <= sizeof(received) * 8); + + if (CBS_len(cbs) != 0) { + /* Decode the extensions block and check it is valid. */ + CBS extensions; + if (!CBS_get_u16_length_prefixed(cbs, &extensions) || + !tls1_check_duplicate_extensions(&extensions)) { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } - s->next_proto_negotiated_len = selected_len; - s->s3->next_proto_neg_seen = 1; - } else if (type == TLSEXT_TYPE_application_layer_protocol_negotiation && - !s->s3->initial_handshake_complete) { - CBS protocol_name_list, protocol_name; - /* We must have requested it. */ - if (s->alpn_client_proto_list == NULL) { - *out_alert = SSL_AD_UNSUPPORTED_EXTENSION; - return 0; - } + while (CBS_len(&extensions) != 0) { + uint16_t type; + CBS extension; - /* The extension data consists of a ProtocolNameList which must have - * exactly one ProtocolName. Each of these is length-prefixed. */ - if (!CBS_get_u16_length_prefixed(&extension, &protocol_name_list) || - CBS_len(&extension) != 0 || - !CBS_get_u8_length_prefixed(&protocol_name_list, &protocol_name) || - CBS_len(&protocol_name_list) != 0) { + /* Decode the next extension. */ + if (!CBS_get_u16(&extensions, &type) || + !CBS_get_u16_length_prefixed(&extensions, &extension)) { *out_alert = SSL_AD_DECODE_ERROR; return 0; } - if (!CBS_stow(&protocol_name, &s->s3->alpn_selected, - &s->s3->alpn_selected_len)) { - *out_alert = SSL_AD_INTERNAL_ERROR; - return 0; - } - } else if (type == TLSEXT_TYPE_channel_id && !SSL_IS_DTLS(s)) { - if (CBS_len(&extension) != 0) { - *out_alert = SSL_AD_DECODE_ERROR; - return 0; - } + unsigned ext_index; + const struct tls_extension *const ext = + tls_extension_find(&ext_index, type); - s->s3->tlsext_channel_id_valid = 1; - } else if (type == TLSEXT_TYPE_channel_id_new && !SSL_IS_DTLS(s)) { - if (CBS_len(&extension) != 0) { - *out_alert = SSL_AD_DECODE_ERROR; - return 0; + if (ext == NULL) { + if (!custom_ext_parse_serverhello(s, out_alert, type, &extension)) { + return 0; + } + continue; } - s->s3->tlsext_channel_id_valid = 1; - s->s3->tlsext_channel_id_new = 1; - } else if (type == TLSEXT_TYPE_certificate_timestamp) { - if (CBS_len(&extension) == 0) { + if (!(s->s3->tmp.extensions.sent & (1u << ext_index))) { + /* If the extension was never sent then it is illegal. */ + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION); + ERR_add_error_dataf("extension :%u", (unsigned)type); *out_alert = SSL_AD_DECODE_ERROR; return 0; } - /* Session resumption uses the original session information. */ - if (!s->hit && - !CBS_stow(&extension, &s->session->tlsext_signed_cert_timestamp_list, - &s->session->tlsext_signed_cert_timestamp_list_length)) { - *out_alert = SSL_AD_INTERNAL_ERROR; - return 0; - } - } else if (type == TLSEXT_TYPE_renegotiate) { - if (!ssl_parse_serverhello_renegotiate_ext(s, &extension, out_alert)) { - return 0; - } + received |= (1u << ext_index); - renegotiate_seen = 1; - } else if (type == TLSEXT_TYPE_use_srtp) { - if (!ssl_parse_serverhello_use_srtp_ext(s, &extension, out_alert)) { + uint8_t alert = SSL_AD_DECODE_ERROR; + if (!ext->parse_serverhello(s, &alert, &extension)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_PARSING_EXTENSION); + ERR_add_error_dataf("extension: %u", (unsigned)type); + *out_alert = alert; return 0; } - } else if (type == TLSEXT_TYPE_extended_master_secret) { - if (/* It is invalid for the server to select EMS and - SSLv3. */ - s->version == SSL3_VERSION || CBS_len(&extension) != 0) { - *out_alert = SSL_AD_DECODE_ERROR; - return 0; - } - - s->s3->tmp.extended_master_secret = 1; } } - if (!s->hit && tlsext_servername == 1 && s->tlsext_hostname) { - if (s->session->tlsext_hostname == NULL) { - s->session->tlsext_hostname = BUF_strdup(s->tlsext_hostname); - if (!s->session->tlsext_hostname) { - *out_alert = SSL_AD_UNRECOGNIZED_NAME; + size_t i; + for (i = 0; i < kNumExtensions; i++) { + if (!(received & (1u << i))) { + /* Extension wasn't observed so call the callback with a NULL + * parameter. */ + uint8_t alert = SSL_AD_DECODE_ERROR; + if (!kExtensions[i].parse_serverhello(s, &alert, NULL)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_EXTENSION); + ERR_add_error_dataf("extension: %u", (unsigned)kExtensions[i].value); + *out_alert = alert; return 0; } - } else { - *out_alert = SSL_AD_DECODE_ERROR; - return 0; } } -ri_check: - /* Determine if we need to see RI. Strictly speaking if we want to avoid an - * attack we should *always* see RI even on initial server hello because the - * client doesn't see any renegotiation during an attack. However this would - * mean we could not connect to any server which doesn't support RI so for - * the immediate future tolerate RI absence on initial connect only. */ - if (!renegotiate_seen && !(s->options & SSL_OP_LEGACY_SERVER_CONNECT) && - !(s->options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION)) { - *out_alert = SSL_AD_HANDSHAKE_FAILURE; - OPENSSL_PUT_ERROR(SSL, ssl_scan_serverhello_tlsext, - SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED); - return 0; - } - return 1; } -int ssl_prepare_clienthello_tlsext(SSL *s) { return 1; } - -int ssl_prepare_serverhello_tlsext(SSL *s) { return 1; } - static int ssl_check_clienthello_tlsext(SSL *s) { int ret = SSL_TLSEXT_ERR_NOACK; int al = SSL_AD_UNRECOGNIZED_NAME; @@ -1947,7 +2568,7 @@ static int ssl_check_clienthello_tlsext(SSL *s) { return 1; case SSL_TLSEXT_ERR_NOACK: - s->should_ack_sni = 0; + s->s3->tmp.should_ack_sni = 0; return 1; default: @@ -1956,22 +2577,9 @@ static int ssl_check_clienthello_tlsext(SSL *s) { } static int ssl_check_serverhello_tlsext(SSL *s) { - int ret = SSL_TLSEXT_ERR_NOACK; + int ret = SSL_TLSEXT_ERR_OK; int al = SSL_AD_UNRECOGNIZED_NAME; - /* If we are client and using an elliptic curve cryptography cipher suite, - * then if server returns an EC point formats lists extension it must contain - * uncompressed. */ - uint32_t alg_k = s->s3->tmp.new_cipher->algorithm_mkey; - uint32_t alg_a = s->s3->tmp.new_cipher->algorithm_auth; - if (((alg_k & SSL_kECDHE) || (alg_a & SSL_aECDSA)) && - !tls1_check_point_format(s, TLSEXT_ECPOINTFORMAT_uncompressed)) { - OPENSSL_PUT_ERROR(SSL, ssl_check_serverhello_tlsext, - SSL_R_TLS_INVALID_ECPOINTFORMAT_LIST); - return -1; - } - ret = SSL_TLSEXT_ERR_OK; - if (s->ctx != NULL && s->ctx->tlsext_servername_callback != 0) { ret = s->ctx->tlsext_servername_callback(s, &al, s->ctx->tlsext_servername_arg); @@ -2007,203 +2615,131 @@ int ssl_parse_serverhello_tlsext(SSL *s, CBS *cbs) { } if (ssl_check_serverhello_tlsext(s) <= 0) { - OPENSSL_PUT_ERROR(SSL, ssl_parse_serverhello_tlsext, - SSL_R_SERVERHELLO_TLSEXT); + OPENSSL_PUT_ERROR(SSL, SSL_R_SERVERHELLO_TLSEXT); return 0; } return 1; } -/* Since the server cache lookup is done early on in the processing of the - * ClientHello, and other operations depend on the result, we need to handle - * any TLS session ticket extension at the same time. - * - * ctx: contains the early callback context, which is the result of a - * shallow parse of the ClientHello. - * ret: (output) on return, if a ticket was decrypted, then this is set to - * point to the resulting session. - * - * Returns: - * -1: fatal error, either from parsing or decrypting the ticket. - * 0: no ticket was found (or was ignored, based on settings). - * 1: a zero length extension was found, indicating that the client supports - * session tickets but doesn't currently have one to offer. - * 2: a ticket was offered but couldn't be decrypted because of a non-fatal - * error. - * 3: a ticket was successfully decrypted and *ret was set. - * - * Side effects: - * Sets s->tlsext_ticket_expected to 1 if the server will have to issue - * a new session ticket to the client because the client indicated support - * but the client either doesn't have a session ticket or we couldn't use - * the one it gave us, or if s->ctx->tlsext_ticket_key_cb asked to renew - * the client's ticket. Otherwise, s->tlsext_ticket_expected is set to 0. - */ -int tls1_process_ticket(SSL *s, const struct ssl_early_callback_ctx *ctx, - SSL_SESSION **ret) { - *ret = NULL; - s->tlsext_ticket_expected = 0; - const uint8_t *data; - size_t len; - int r; - - /* If tickets disabled behave as if no ticket present to permit stateful - * resumption. */ - if ((SSL_get_options(s) & SSL_OP_NO_TICKET) || - (s->version <= SSL3_VERSION && !ctx->extensions) || - !SSL_early_callback_ctx_extension_get(ctx, TLSEXT_TYPE_session_ticket, - &data, &len)) { - return 0; - } - - if (len == 0) { - /* The client will accept a ticket but doesn't currently have one. */ - s->tlsext_ticket_expected = 1; - return 1; - } - - r = tls_decrypt_ticket(s, data, len, ctx->session_id, ctx->session_id_len, - ret); - switch (r) { - case 2: /* ticket couldn't be decrypted */ - s->tlsext_ticket_expected = 1; - return 2; +int tls_process_ticket(SSL *ssl, SSL_SESSION **out_session, + int *out_send_ticket, const uint8_t *ticket, + size_t ticket_len, const uint8_t *session_id, + size_t session_id_len) { + int ret = 1; /* Most errors are non-fatal. */ + SSL_CTX *ssl_ctx = ssl->initial_ctx; + uint8_t *plaintext = NULL; - case 3: /* ticket was decrypted */ - return r; + HMAC_CTX hmac_ctx; + HMAC_CTX_init(&hmac_ctx); + EVP_CIPHER_CTX cipher_ctx; + EVP_CIPHER_CTX_init(&cipher_ctx); - case 4: /* ticket decrypted but need to renew */ - s->tlsext_ticket_expected = 1; - return 3; + *out_send_ticket = 0; + *out_session = NULL; - default: /* fatal error */ - return -1; + if (session_id_len > SSL_MAX_SSL_SESSION_ID_LENGTH) { + goto done; } -} -/* tls_decrypt_ticket attempts to decrypt a session ticket. - * - * etick: points to the body of the session ticket extension. - * eticklen: the length of the session tickets extenion. - * sess_id: points at the session ID. - * sesslen: the length of the session ID. - * psess: (output) on return, if a ticket was decrypted, then this is set to - * point to the resulting session. - * - * Returns: - * -1: fatal error, either from parsing or decrypting the ticket. - * 2: the ticket couldn't be decrypted. - * 3: a ticket was successfully decrypted and *psess was set. - * 4: same as 3, but the ticket needs to be renewed. */ -static int tls_decrypt_ticket(SSL *s, const uint8_t *etick, int eticklen, - const uint8_t *sess_id, int sesslen, - SSL_SESSION **psess) { - SSL_SESSION *sess; - uint8_t *sdec; - const uint8_t *p; - int slen, mlen, renew_ticket = 0; - uint8_t tick_hmac[EVP_MAX_MD_SIZE]; - HMAC_CTX hctx; - EVP_CIPHER_CTX ctx; - SSL_CTX *tctx = s->initial_ctx; + if (ticket_len == 0) { + /* The client will accept a ticket but doesn't currently have one. */ + *out_send_ticket = 1; + goto done; + } /* Ensure there is room for the key name and the largest IV * |tlsext_ticket_key_cb| may try to consume. The real limit may be lower, but * the maximum IV length should be well under the minimum size for the * session material and HMAC. */ - if (eticklen < 16 + EVP_MAX_IV_LENGTH) { - return 2; - } - - /* Initialize session ticket encryption and HMAC contexts */ - HMAC_CTX_init(&hctx); - EVP_CIPHER_CTX_init(&ctx); - if (tctx->tlsext_ticket_key_cb) { - uint8_t *nctick = (uint8_t *)etick; - int rv = tctx->tlsext_ticket_key_cb(s, nctick, nctick + 16, &ctx, &hctx, - 0 /* decrypt */); - if (rv < 0) { - return -1; + if (ticket_len < SSL_TICKET_KEY_NAME_LEN + EVP_MAX_IV_LENGTH) { + goto done; + } + const uint8_t *iv = ticket + SSL_TICKET_KEY_NAME_LEN; + + if (ssl_ctx->tlsext_ticket_key_cb != NULL) { + int cb_ret = ssl_ctx->tlsext_ticket_key_cb(ssl, (uint8_t*)ticket /* name */, + (uint8_t*)iv, &cipher_ctx, &hmac_ctx, + 0 /* decrypt */); + if (cb_ret < 0) { + ret = 0; + goto done; } - if (rv == 0) { - return 2; + if (cb_ret == 0) { + goto done; } - if (rv == 2) { - renew_ticket = 1; + if (cb_ret == 2) { + *out_send_ticket = 1; } } else { - /* Check key name matches */ - if (memcmp(etick, tctx->tlsext_tick_key_name, 16)) { - return 2; + /* Check the key name matches. */ + if (memcmp(ticket, ssl_ctx->tlsext_tick_key_name, + SSL_TICKET_KEY_NAME_LEN) != 0) { + goto done; } - if (!HMAC_Init_ex(&hctx, tctx->tlsext_tick_hmac_key, 16, tlsext_tick_md(), + if (!HMAC_Init_ex(&hmac_ctx, ssl_ctx->tlsext_tick_hmac_key, + sizeof(ssl_ctx->tlsext_tick_hmac_key), tlsext_tick_md(), NULL) || - !EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, - tctx->tlsext_tick_aes_key, etick + 16)) { - HMAC_CTX_cleanup(&hctx); - EVP_CIPHER_CTX_cleanup(&ctx); - return -1; + !EVP_DecryptInit_ex(&cipher_ctx, EVP_aes_128_cbc(), NULL, + ssl_ctx->tlsext_tick_aes_key, iv)) { + ret = 0; + goto done; } } + size_t iv_len = EVP_CIPHER_CTX_iv_length(&cipher_ctx); - /* First, check the MAC. The MAC is at the end of the ticket. */ - mlen = HMAC_size(&hctx); - if ((size_t) eticklen < 16 + EVP_CIPHER_CTX_iv_length(&ctx) + 1 + mlen) { + /* Check the MAC at the end of the ticket. */ + uint8_t mac[EVP_MAX_MD_SIZE]; + size_t mac_len = HMAC_size(&hmac_ctx); + if (ticket_len < SSL_TICKET_KEY_NAME_LEN + iv_len + 1 + mac_len) { /* The ticket must be large enough for key name, IV, data, and MAC. */ - HMAC_CTX_cleanup(&hctx); - EVP_CIPHER_CTX_cleanup(&ctx); - return 2; - } - eticklen -= mlen; - /* Check HMAC of encrypted ticket */ - HMAC_Update(&hctx, etick, eticklen); - HMAC_Final(&hctx, tick_hmac, NULL); - HMAC_CTX_cleanup(&hctx); - if (CRYPTO_memcmp(tick_hmac, etick + eticklen, mlen)) { - EVP_CIPHER_CTX_cleanup(&ctx); - return 2; - } - - /* Attempt to decrypt session data */ - /* Move p after IV to start of encrypted ticket, update length */ - p = etick + 16 + EVP_CIPHER_CTX_iv_length(&ctx); - eticklen -= 16 + EVP_CIPHER_CTX_iv_length(&ctx); - sdec = OPENSSL_malloc(eticklen); - if (!sdec) { - EVP_CIPHER_CTX_cleanup(&ctx); - return -1; + goto done; } - EVP_DecryptUpdate(&ctx, sdec, &slen, p, eticklen); - if (EVP_DecryptFinal_ex(&ctx, sdec + slen, &mlen) <= 0) { - EVP_CIPHER_CTX_cleanup(&ctx); - OPENSSL_free(sdec); - return 2; - } - slen += mlen; - EVP_CIPHER_CTX_cleanup(&ctx); - p = sdec; - - sess = SSL_SESSION_from_bytes(sdec, slen); - OPENSSL_free(sdec); - if (sess) { - /* The session ID, if non-empty, is used by some clients to detect that the - * ticket has been accepted. So we copy it to the session structure. If it - * is empty set length to zero as required by standard. */ - if (sesslen) { - memcpy(sess->session_id, sess_id, sesslen); - } - sess->session_id_length = sesslen; - *psess = sess; - if (renew_ticket) { - return 4; - } - return 3; + HMAC_Update(&hmac_ctx, ticket, ticket_len - mac_len); + HMAC_Final(&hmac_ctx, mac, NULL); + if (CRYPTO_memcmp(mac, ticket + (ticket_len - mac_len), mac_len) != 0) { + goto done; + } + + /* Decrypt the session data. */ + const uint8_t *ciphertext = ticket + SSL_TICKET_KEY_NAME_LEN + iv_len; + size_t ciphertext_len = ticket_len - SSL_TICKET_KEY_NAME_LEN - iv_len - + mac_len; + plaintext = OPENSSL_malloc(ciphertext_len); + if (plaintext == NULL) { + ret = 0; + goto done; + } + if (ciphertext_len >= INT_MAX) { + goto done; + } + int len1, len2; + if (!EVP_DecryptUpdate(&cipher_ctx, plaintext, &len1, ciphertext, + (int)ciphertext_len) || + !EVP_DecryptFinal_ex(&cipher_ctx, plaintext + len1, &len2)) { + ERR_clear_error(); /* Don't leave an error on the queue. */ + goto done; + } + + /* Decode the session. */ + SSL_SESSION *session = SSL_SESSION_from_bytes(plaintext, len1 + len2); + if (session == NULL) { + ERR_clear_error(); /* Don't leave an error on the queue. */ + goto done; } - ERR_clear_error(); - /* For session parse failure, indicate that we need to send a new ticket. */ - return 2; + /* Copy the client's session ID into the new session, to denote the ticket has + * been accepted. */ + memcpy(session->session_id, session_id, session_id_len); + session->session_id_length = session_id_len; + + *out_session = session; + +done: + OPENSSL_free(plaintext); + HMAC_CTX_cleanup(&hmac_ctx); + EVP_CIPHER_CTX_cleanup(&cipher_ctx); + return ret; } /* Tables to translate from NIDs to TLS v1.2 ids */ @@ -2233,18 +2769,12 @@ static int tls12_find_id(int nid, const tls12_lookup *table, size_t tlen) { return -1; } -static int tls12_find_nid(int id, const tls12_lookup *table, size_t tlen) { - size_t i; - for (i = 0; i < tlen; i++) { - if (table[i].id == id) { - return table[i].nid; - } - } - - return NID_undef; +int tls12_get_sigid(int pkey_type) { + return tls12_find_id(pkey_type, tls12_sig, + sizeof(tls12_sig) / sizeof(tls12_lookup)); } -int tls12_get_sigandhash(uint8_t *p, const EVP_PKEY *pk, const EVP_MD *md) { +int tls12_get_sigandhash(SSL *ssl, uint8_t *p, const EVP_MD *md) { int sig_id, md_id; if (!md) { @@ -2257,7 +2787,7 @@ int tls12_get_sigandhash(uint8_t *p, const EVP_PKEY *pk, const EVP_MD *md) { return 0; } - sig_id = tls12_get_sigid(pk); + sig_id = tls12_get_sigid(ssl_private_key_type(ssl)); if (sig_id == -1) { return 0; } @@ -2267,11 +2797,6 @@ int tls12_get_sigandhash(uint8_t *p, const EVP_PKEY *pk, const EVP_MD *md) { return 1; } -int tls12_get_sigid(const EVP_PKEY *pk) { - return tls12_find_id(pk->type, tls12_sig, - sizeof(tls12_sig) / sizeof(tls12_lookup)); -} - const EVP_MD *tls12_get_hash(uint8_t hash_alg) { switch (hash_alg) { case TLSEXT_hash_md5: @@ -2312,256 +2837,129 @@ static int tls12_get_pkey_type(uint8_t sig_alg) { } } -/* Convert TLS 1.2 signature algorithm extension values into NIDs */ -static void tls1_lookup_sigalg(int *phash_nid, int *psign_nid, - int *psignhash_nid, const uint8_t *data) { - int sign_nid = 0, hash_nid = 0; - if (!phash_nid && !psign_nid && !psignhash_nid) { - return; - } - - if (phash_nid || psignhash_nid) { - hash_nid = tls12_find_nid(data[0], tls12_md, - sizeof(tls12_md) / sizeof(tls12_lookup)); - if (phash_nid) { - *phash_nid = hash_nid; - } - } - - if (psign_nid || psignhash_nid) { - sign_nid = tls12_find_nid(data[1], tls12_sig, - sizeof(tls12_sig) / sizeof(tls12_lookup)); - if (psign_nid) { - *psign_nid = sign_nid; - } - } - - if (psignhash_nid) { - if (sign_nid && hash_nid) { - OBJ_find_sigid_by_algs(psignhash_nid, hash_nid, sign_nid); - } else { - *psignhash_nid = NID_undef; - } - } -} - -/* Given preference and allowed sigalgs set shared sigalgs */ -static int tls12_do_shared_sigalgs(TLS_SIGALGS *shsig, const uint8_t *pref, - size_t preflen, const uint8_t *allow, - size_t allowlen) { - const uint8_t *ptmp, *atmp; - size_t i, j, nmatch = 0; - - for (i = 0, ptmp = pref; i < preflen; i += 2, ptmp += 2) { - /* Skip disabled hashes or signature algorithms */ - if (tls12_get_hash(ptmp[0]) == NULL || - tls12_get_pkey_type(ptmp[1]) == -1) { - continue; - } - - for (j = 0, atmp = allow; j < allowlen; j += 2, atmp += 2) { - if (ptmp[0] == atmp[0] && ptmp[1] == atmp[1]) { - nmatch++; - if (shsig) { - shsig->rhash = ptmp[0]; - shsig->rsign = ptmp[1]; - tls1_lookup_sigalg(&shsig->hash_nid, &shsig->sign_nid, - &shsig->signandhash_nid, ptmp); - shsig++; - } - - break; - } - } - } - - return nmatch; -} - -/* Set shared signature algorithms for SSL structures */ -static int tls1_set_shared_sigalgs(SSL *s) { - const uint8_t *pref, *allow, *conf; - size_t preflen, allowlen, conflen; - size_t nmatch; - TLS_SIGALGS *salgs = NULL; - CERT *c = s->cert; +OPENSSL_COMPILE_ASSERT(sizeof(TLS_SIGALGS) == 2, + sizeof_tls_sigalgs_is_not_two); - OPENSSL_free(c->shared_sigalgs); - c->shared_sigalgs = NULL; - c->shared_sigalgslen = 0; - - /* If client use client signature algorithms if not NULL */ - if (!s->server && c->client_sigalgs) { - conf = c->client_sigalgs; - conflen = c->client_sigalgslen; - } else if (c->conf_sigalgs) { - conf = c->conf_sigalgs; - conflen = c->conf_sigalgslen; - } else { - conflen = tls12_get_psigalgs(s, &conf); +int tls1_parse_peer_sigalgs(SSL *ssl, const CBS *in_sigalgs) { + /* Extension ignored for inappropriate versions */ + if (!SSL_USE_SIGALGS(ssl)) { + return 1; } - if (s->options & SSL_OP_CIPHER_SERVER_PREFERENCE) { - pref = conf; - preflen = conflen; - allow = c->peer_sigalgs; - allowlen = c->peer_sigalgslen; - } else { - allow = conf; - allowlen = conflen; - pref = c->peer_sigalgs; - preflen = c->peer_sigalgslen; - } + CERT *const cert = ssl->cert; + OPENSSL_free(cert->peer_sigalgs); + cert->peer_sigalgs = NULL; + cert->peer_sigalgslen = 0; - nmatch = tls12_do_shared_sigalgs(NULL, pref, preflen, allow, allowlen); - if (!nmatch) { - return 1; - } + size_t num_sigalgs = CBS_len(in_sigalgs); - salgs = OPENSSL_malloc(nmatch * sizeof(TLS_SIGALGS)); - if (!salgs) { + if (num_sigalgs % 2 != 0) { return 0; } + num_sigalgs /= 2; - nmatch = tls12_do_shared_sigalgs(salgs, pref, preflen, allow, allowlen); - c->shared_sigalgs = salgs; - c->shared_sigalgslen = nmatch; - return 1; -} - -/* Set preferred digest for each key type */ -int tls1_process_sigalgs(SSL *s, const CBS *sigalgs) { - CERT *c = s->cert; - - /* Extension ignored for inappropriate versions */ - if (!SSL_USE_SIGALGS(s)) { + /* supported_signature_algorithms in the certificate request is + * allowed to be empty. */ + if (num_sigalgs == 0) { return 1; } - if (CBS_len(sigalgs) % 2 != 0 || - !CBS_stow(sigalgs, &c->peer_sigalgs, &c->peer_sigalgslen) || - !tls1_set_shared_sigalgs(s)) { + /* This multiplication doesn't overflow because sizeof(TLS_SIGALGS) is two + * (statically asserted above) and we just divided |num_sigalgs| by two. */ + cert->peer_sigalgs = OPENSSL_malloc(num_sigalgs * sizeof(TLS_SIGALGS)); + if (cert->peer_sigalgs == NULL) { return 0; } + cert->peer_sigalgslen = num_sigalgs; - return 1; -} + CBS sigalgs; + CBS_init(&sigalgs, CBS_data(in_sigalgs), CBS_len(in_sigalgs)); -const EVP_MD *tls1_choose_signing_digest(SSL *s, EVP_PKEY *pkey) { - CERT *c = s->cert; - int type = EVP_PKEY_id(pkey); size_t i; - - /* Select the first shared digest supported by our key. */ - for (i = 0; i < c->shared_sigalgslen; i++) { - const EVP_MD *md = tls12_get_hash(c->shared_sigalgs[i].rhash); - if (md == NULL || - tls12_get_pkey_type(c->shared_sigalgs[i].rsign) != type || - !EVP_PKEY_supports_digest(pkey, md)) { - continue; - } - return md; - } - - /* If no suitable digest may be found, default to SHA-1. */ - return EVP_sha1(); -} - -int SSL_get_sigalgs(SSL *s, int idx, int *psign, int *phash, int *psignhash, - uint8_t *rsig, uint8_t *rhash) { - const uint8_t *psig = s->cert->peer_sigalgs; - - if (psig == NULL) { - return 0; - } - - if (idx >= 0) { - idx <<= 1; - if (idx >= (int)s->cert->peer_sigalgslen) { + for (i = 0; i < num_sigalgs; i++) { + TLS_SIGALGS *const sigalg = &cert->peer_sigalgs[i]; + if (!CBS_get_u8(&sigalgs, &sigalg->rhash) || + !CBS_get_u8(&sigalgs, &sigalg->rsign)) { return 0; } - psig += idx; - if (rhash) { - *rhash = psig[0]; - } - if (rsig) { - *rsig = psig[1]; - } - tls1_lookup_sigalg(phash, psign, psignhash, psig); } - return s->cert->peer_sigalgslen / 2; + return 1; } -int SSL_get_shared_sigalgs(SSL *s, int idx, int *psign, int *phash, - int *psignhash, uint8_t *rsig, uint8_t *rhash) { - TLS_SIGALGS *shsigalgs = s->cert->shared_sigalgs; - - if (!shsigalgs || idx >= (int)s->cert->shared_sigalgslen) { - return 0; - } +const EVP_MD *tls1_choose_signing_digest(SSL *ssl) { + CERT *cert = ssl->cert; + int type = ssl_private_key_type(ssl); + size_t i, j; + + static const int kDefaultDigestList[] = {NID_sha256, NID_sha384, NID_sha512, + NID_sha224, NID_sha1}; + + const int *digest_nids = kDefaultDigestList; + size_t num_digest_nids = + sizeof(kDefaultDigestList) / sizeof(kDefaultDigestList[0]); + if (cert->digest_nids != NULL) { + digest_nids = cert->digest_nids; + num_digest_nids = cert->num_digest_nids; + } + + for (i = 0; i < num_digest_nids; i++) { + const int digest_nid = digest_nids[i]; + for (j = 0; j < cert->peer_sigalgslen; j++) { + const EVP_MD *md = tls12_get_hash(cert->peer_sigalgs[j].rhash); + if (md == NULL || + digest_nid != EVP_MD_type(md) || + tls12_get_pkey_type(cert->peer_sigalgs[j].rsign) != type) { + continue; + } - shsigalgs += idx; - if (phash) { - *phash = shsigalgs->hash_nid; - } - if (psign) { - *psign = shsigalgs->sign_nid; - } - if (psignhash) { - *psignhash = shsigalgs->signandhash_nid; - } - if (rsig) { - *rsig = shsigalgs->rsign; - } - if (rhash) { - *rhash = shsigalgs->rhash; + return md; + } } - return s->cert->shared_sigalgslen; + /* If no suitable digest may be found, default to SHA-1. */ + return EVP_sha1(); } -/* tls1_channel_id_hash calculates the signed data for a Channel ID on the - * given SSL connection and writes it to |md|. */ -int tls1_channel_id_hash(EVP_MD_CTX *md, SSL *s) { +int tls1_channel_id_hash(SSL *ssl, uint8_t *out, size_t *out_len) { + int ret = 0; EVP_MD_CTX ctx; - uint8_t temp_digest[EVP_MAX_MD_SIZE]; - unsigned temp_digest_len; - int i; - static const char kClientIDMagic[] = "TLS Channel ID signature"; - if (s->s3->handshake_buffer && - !ssl3_digest_cached_records(s, free_handshake_buffer)) { - return 0; + EVP_MD_CTX_init(&ctx); + if (!EVP_DigestInit_ex(&ctx, EVP_sha256(), NULL)) { + goto err; } - EVP_DigestUpdate(md, kClientIDMagic, sizeof(kClientIDMagic)); + static const char kClientIDMagic[] = "TLS Channel ID signature"; + EVP_DigestUpdate(&ctx, kClientIDMagic, sizeof(kClientIDMagic)); - if (s->hit && s->s3->tlsext_channel_id_new) { + if (ssl->hit) { static const char kResumptionMagic[] = "Resumption"; - EVP_DigestUpdate(md, kResumptionMagic, sizeof(kResumptionMagic)); - if (s->session->original_handshake_hash_len == 0) { - return 0; + EVP_DigestUpdate(&ctx, kResumptionMagic, sizeof(kResumptionMagic)); + if (ssl->session->original_handshake_hash_len == 0) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + goto err; } - EVP_DigestUpdate(md, s->session->original_handshake_hash, - s->session->original_handshake_hash_len); + EVP_DigestUpdate(&ctx, ssl->session->original_handshake_hash, + ssl->session->original_handshake_hash_len); } - EVP_MD_CTX_init(&ctx); - for (i = 0; i < SSL_MAX_DIGEST; i++) { - if (s->s3->handshake_dgst[i] == NULL) { - continue; - } - if (!EVP_MD_CTX_copy_ex(&ctx, s->s3->handshake_dgst[i])) { - EVP_MD_CTX_cleanup(&ctx); - return 0; - } - EVP_DigestFinal_ex(&ctx, temp_digest, &temp_digest_len); - EVP_DigestUpdate(md, temp_digest, temp_digest_len); + uint8_t handshake_hash[EVP_MAX_MD_SIZE]; + int handshake_hash_len = tls1_handshake_digest(ssl, handshake_hash, + sizeof(handshake_hash)); + if (handshake_hash_len < 0) { + goto err; } - EVP_MD_CTX_cleanup(&ctx); + EVP_DigestUpdate(&ctx, handshake_hash, (size_t)handshake_hash_len); + unsigned len_u; + EVP_DigestFinal_ex(&ctx, out, &len_u); + *out_len = len_u; - return 1; + ret = 1; + +err: + EVP_MD_CTX_cleanup(&ctx); + return ret; } /* tls1_record_handshake_hashes_for_channel_id records the current handshake @@ -2575,12 +2973,6 @@ int tls1_record_handshake_hashes_for_channel_id(SSL *s) { return -1; } - /* It only makes sense to call this function if Channel IDs have been - * negotiated. */ - if (!s->s3->tlsext_channel_id_new) { - return -1; - } - digest_len = tls1_handshake_digest(s, s->session->original_handshake_hash, sizeof(s->session->original_handshake_hash)); @@ -2592,48 +2984,3 @@ int tls1_record_handshake_hashes_for_channel_id(SSL *s) { return 1; } - -int tls1_set_sigalgs(CERT *c, const int *psig_nids, size_t salglen, - int client) { - uint8_t *sigalgs, *sptr; - int rhash, rsign; - size_t i; - - if (salglen & 1) { - return 0; - } - - sigalgs = OPENSSL_malloc(salglen); - if (sigalgs == NULL) { - return 0; - } - - for (i = 0, sptr = sigalgs; i < salglen; i += 2) { - rhash = tls12_find_id(*psig_nids++, tls12_md, - sizeof(tls12_md) / sizeof(tls12_lookup)); - rsign = tls12_find_id(*psig_nids++, tls12_sig, - sizeof(tls12_sig) / sizeof(tls12_lookup)); - - if (rhash == -1 || rsign == -1) { - goto err; - } - *sptr++ = rhash; - *sptr++ = rsign; - } - - if (client) { - OPENSSL_free(c->client_sigalgs); - c->client_sigalgs = sigalgs; - c->client_sigalgslen = salglen; - } else { - OPENSSL_free(c->conf_sigalgs); - c->conf_sigalgs = sigalgs; - c->conf_sigalgslen = salglen; - } - - return 1; - -err: - OPENSSL_free(sigalgs); - return 0; -} diff --git a/src/ssl/t1_reneg.c b/src/ssl/t1_reneg.c deleted file mode 100644 index d0009c1..0000000 --- a/src/ssl/t1_reneg.c +++ /dev/null @@ -1,246 +0,0 @@ -/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) - * All rights reserved. - * - * This package is an SSL implementation written - * by Eric Young (eay@cryptsoft.com). - * The implementation was written so as to conform with Netscapes SSL. - * - * This library is free for commercial and non-commercial use as long as - * the following conditions are aheared to. The following conditions - * apply to all code found in this distribution, be it the RC4, RSA, - * lhash, DES, etc., code; not just the SSL code. The SSL documentation - * included with this distribution is covered by the same copyright terms - * except that the holder is Tim Hudson (tjh@cryptsoft.com). - * - * Copyright remains Eric Young's, and as such any Copyright notices in - * the code are not to be removed. - * If this package is used in a product, Eric Young should be given attribution - * as the author of the parts of the library used. - * This can be in the form of a textual message at program startup or - * in documentation (online or textual) provided with the package. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * "This product includes cryptographic software written by - * Eric Young (eay@cryptsoft.com)" - * The word 'cryptographic' can be left out if the rouines from the library - * being used are not cryptographic related :-). - * 4. If you include any Windows specific code (or a derivative thereof) from - * the apps directory (application code) you must include an acknowledgement: - * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" - * - * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * The licence and distribution terms for any publically available version or - * derivative of this code cannot be changed. i.e. this code cannot simply be - * copied and put under another distribution licence - * [including the GNU Public Licence.] - */ -/* ==================================================================== - * Copyright (c) 1998-2009 The OpenSSL Project. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. All advertising materials mentioning features or use of this - * software must display the following acknowledgment: - * "This product includes software developed by the OpenSSL Project - * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" - * - * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - * endorse or promote products derived from this software without - * prior written permission. For written permission, please contact - * openssl-core@openssl.org. - * - * 5. Products derived from this software may not be called "OpenSSL" - * nor may "OpenSSL" appear in their names without prior written - * permission of the OpenSSL Project. - * - * 6. Redistributions of any form whatsoever must retain the following - * acknowledgment: - * "This product includes software developed by the OpenSSL Project - * for use in the OpenSSL Toolkit (http://www.openssl.org/)" - * - * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY - * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - * ==================================================================== - * - * This product includes cryptographic software written by Eric Young - * (eay@cryptsoft.com). This product includes software written by Tim - * Hudson (tjh@cryptsoft.com). */ - -#include <assert.h> -#include <stdio.h> -#include <string.h> - -#include <openssl/bytestring.h> -#include <openssl/err.h> - -#include "internal.h" - - -/* Add the client's renegotiation binding */ -int ssl_add_clienthello_renegotiate_ext(SSL *s, unsigned char *p, int *len, - int maxlen) { - if (p) { - if (s->s3->previous_client_finished_len + 1 > maxlen) { - OPENSSL_PUT_ERROR(SSL, ssl_add_clienthello_renegotiate_ext, - SSL_R_RENEGOTIATE_EXT_TOO_LONG); - return 0; - } - - /* Length byte */ - *p = s->s3->previous_client_finished_len; - p++; - - memcpy(p, s->s3->previous_client_finished, - s->s3->previous_client_finished_len); - } - - *len = s->s3->previous_client_finished_len + 1; - - return 1; -} - -/* Parse the client's renegotiation binding and abort if it's not right */ -int ssl_parse_clienthello_renegotiate_ext(SSL *s, CBS *cbs, int *out_alert) { - CBS renegotiated_connection; - - if (!CBS_get_u8_length_prefixed(cbs, &renegotiated_connection) || - CBS_len(cbs) != 0) { - OPENSSL_PUT_ERROR(SSL, ssl_parse_clienthello_renegotiate_ext, - SSL_R_RENEGOTIATION_ENCODING_ERR); - *out_alert = SSL_AD_DECODE_ERROR; - return 0; - } - - /* Check that the extension matches */ - if (!CBS_mem_equal(&renegotiated_connection, s->s3->previous_client_finished, - s->s3->previous_client_finished_len)) { - OPENSSL_PUT_ERROR(SSL, ssl_parse_clienthello_renegotiate_ext, - SSL_R_RENEGOTIATION_MISMATCH); - *out_alert = SSL_AD_HANDSHAKE_FAILURE; - return 0; - } - - s->s3->send_connection_binding = 1; - - return 1; -} - -/* Add the server's renegotiation binding */ -int ssl_add_serverhello_renegotiate_ext(SSL *s, unsigned char *p, int *len, - int maxlen) { - if (p) { - if (s->s3->previous_client_finished_len + - s->s3->previous_server_finished_len + 1 > maxlen) { - OPENSSL_PUT_ERROR(SSL, ssl_add_serverhello_renegotiate_ext, - SSL_R_RENEGOTIATE_EXT_TOO_LONG); - return 0; - } - - /* Length byte */ - *p = s->s3->previous_client_finished_len + - s->s3->previous_server_finished_len; - p++; - - memcpy(p, s->s3->previous_client_finished, - s->s3->previous_client_finished_len); - p += s->s3->previous_client_finished_len; - - memcpy(p, s->s3->previous_server_finished, - s->s3->previous_server_finished_len); - } - - *len = s->s3->previous_client_finished_len + - s->s3->previous_server_finished_len + 1; - - return 1; -} - -/* Parse the server's renegotiation binding and abort if it's not right */ -int ssl_parse_serverhello_renegotiate_ext(SSL *s, CBS *cbs, int *out_alert) { - int expected_len = - s->s3->previous_client_finished_len + s->s3->previous_server_finished_len; - CBS renegotiated_connection; - const uint8_t *d; - - /* Check for logic errors */ - assert(!expected_len || s->s3->previous_client_finished_len); - assert(!expected_len || s->s3->previous_server_finished_len); - - /* Parse out the extension contents. */ - if (!CBS_get_u8_length_prefixed(cbs, &renegotiated_connection) || - CBS_len(cbs) != 0) { - OPENSSL_PUT_ERROR(SSL, ssl_parse_serverhello_renegotiate_ext, - SSL_R_RENEGOTIATION_ENCODING_ERR); - *out_alert = SSL_AD_ILLEGAL_PARAMETER; - return 0; - } - - /* Check that the extension matches. */ - if (CBS_len(&renegotiated_connection) != expected_len) { - OPENSSL_PUT_ERROR(SSL, ssl_parse_serverhello_renegotiate_ext, - SSL_R_RENEGOTIATION_MISMATCH); - *out_alert = SSL_AD_HANDSHAKE_FAILURE; - return 0; - } - - d = CBS_data(&renegotiated_connection); - if (memcmp(d, s->s3->previous_client_finished, - s->s3->previous_client_finished_len)) { - OPENSSL_PUT_ERROR(SSL, ssl_parse_serverhello_renegotiate_ext, - SSL_R_RENEGOTIATION_MISMATCH); - *out_alert = SSL_AD_HANDSHAKE_FAILURE; - return 0; - } - d += s->s3->previous_client_finished_len; - - if (memcmp(d, s->s3->previous_server_finished, - s->s3->previous_server_finished_len)) { - OPENSSL_PUT_ERROR(SSL, ssl_parse_serverhello_renegotiate_ext, - SSL_R_RENEGOTIATION_MISMATCH); - *out_alert = SSL_AD_ILLEGAL_PARAMETER; - return 0; - } - s->s3->send_connection_binding = 1; - - return 1; -} diff --git a/src/ssl/test/bssl_shim.cc b/src/ssl/test/bssl_shim.cc index 3b95d7e..edae67b 100644 --- a/src/ssl/test/bssl_shim.cc +++ b/src/ssl/test/bssl_shim.cc @@ -38,10 +38,14 @@ #include <openssl/bio.h> #include <openssl/buf.h> #include <openssl/bytestring.h> +#include <openssl/cipher.h> #include <openssl/err.h> +#include <openssl/hmac.h> +#include <openssl/rand.h> #include <openssl/ssl.h> #include <memory> +#include <string> #include <vector> #include "../../crypto/test/scoped_types.h" @@ -90,10 +94,17 @@ struct TestState { ScopedSSL_SESSION pending_session; bool early_callback_called = false; bool handshake_done = false; + // private_key is the underlying private key used when testing custom keys. + ScopedEVP_PKEY private_key; + std::vector<uint8_t> signature; + // signature_retries is the number of times an asynchronous sign operation has + // been retried. + unsigned signature_retries = 0; + bool got_new_session = false; }; static void TestStateExFree(void *parent, void *ptr, CRYPTO_EX_DATA *ad, - int index, long argl, void *argp) { + int index, long argl, void *argp) { delete ((TestState *)ptr); } @@ -129,18 +140,137 @@ static ScopedEVP_PKEY LoadPrivateKey(const std::string &file) { return pkey; } +static int AsyncPrivateKeyType(SSL *ssl) { + return EVP_PKEY_id(GetTestState(ssl)->private_key.get()); +} + +static size_t AsyncPrivateKeyMaxSignatureLen(SSL *ssl) { + return EVP_PKEY_size(GetTestState(ssl)->private_key.get()); +} + +static ssl_private_key_result_t AsyncPrivateKeySign( + SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out, + const EVP_MD *md, const uint8_t *in, size_t in_len) { + TestState *test_state = GetTestState(ssl); + if (!test_state->signature.empty()) { + fprintf(stderr, "AsyncPrivateKeySign called with operation pending.\n"); + abort(); + } + + ScopedEVP_PKEY_CTX ctx(EVP_PKEY_CTX_new(test_state->private_key.get(), + nullptr)); + if (!ctx) { + return ssl_private_key_failure; + } + + // Write the signature into |test_state|. + size_t len = 0; + if (!EVP_PKEY_sign_init(ctx.get()) || + !EVP_PKEY_CTX_set_signature_md(ctx.get(), md) || + !EVP_PKEY_sign(ctx.get(), nullptr, &len, in, in_len)) { + return ssl_private_key_failure; + } + test_state->signature.resize(len); + if (!EVP_PKEY_sign(ctx.get(), bssl::vector_data(&test_state->signature), &len, + in, in_len)) { + return ssl_private_key_failure; + } + test_state->signature.resize(len); + + // The signature will be released asynchronously in |AsyncPrivateKeySignComplete|. + return ssl_private_key_retry; +} + +static ssl_private_key_result_t AsyncPrivateKeySignComplete( + SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out) { + TestState *test_state = GetTestState(ssl); + if (test_state->signature.empty()) { + fprintf(stderr, + "AsyncPrivateKeySignComplete called without operation pending.\n"); + abort(); + } + + if (test_state->signature_retries < 2) { + // Only return the signature on the second attempt, to test both incomplete + // |sign| and |sign_complete|. + return ssl_private_key_retry; + } + + if (max_out < test_state->signature.size()) { + fprintf(stderr, "Output buffer too small.\n"); + return ssl_private_key_failure; + } + memcpy(out, bssl::vector_data(&test_state->signature), + test_state->signature.size()); + *out_len = test_state->signature.size(); + + test_state->signature.clear(); + test_state->signature_retries = 0; + return ssl_private_key_success; +} + +static const SSL_PRIVATE_KEY_METHOD g_async_private_key_method = { + AsyncPrivateKeyType, + AsyncPrivateKeyMaxSignatureLen, + AsyncPrivateKeySign, + AsyncPrivateKeySignComplete, +}; + +template<typename T> +struct Free { + void operator()(T *buf) { + free(buf); + } +}; + static bool InstallCertificate(SSL *ssl) { const TestConfig *config = GetConfigPtr(ssl); - if (!config->key_file.empty() && - !SSL_use_PrivateKey_file(ssl, config->key_file.c_str(), - SSL_FILETYPE_PEM)) { - return false; + TestState *test_state = GetTestState(ssl); + + if (!config->digest_prefs.empty()) { + std::unique_ptr<char, Free<char>> digest_prefs( + strdup(config->digest_prefs.c_str())); + std::vector<int> digest_list; + + for (;;) { + char *token = + strtok(digest_list.empty() ? digest_prefs.get() : nullptr, ","); + if (token == nullptr) { + break; + } + + digest_list.push_back(EVP_MD_type(EVP_get_digestbyname(token))); + } + + if (!SSL_set_private_key_digest_prefs(ssl, digest_list.data(), + digest_list.size())) { + return false; + } + } + + if (!config->key_file.empty()) { + if (config->use_async_private_key) { + test_state->private_key = LoadPrivateKey(config->key_file.c_str()); + if (!test_state->private_key) { + return false; + } + SSL_set_private_key_method(ssl, &g_async_private_key_method); + } else if (!SSL_use_PrivateKey_file(ssl, config->key_file.c_str(), + SSL_FILETYPE_PEM)) { + return false; + } } if (!config->cert_file.empty() && !SSL_use_certificate_file(ssl, config->cert_file.c_str(), SSL_FILETYPE_PEM)) { return false; } + if (!config->ocsp_response.empty() && + !SSL_CTX_set_ocsp_response(ssl->ctx, + (const uint8_t *)config->ocsp_response.data(), + config->ocsp_response.size())) { + return false; + } return true; } @@ -196,10 +326,29 @@ static int SelectCertificateCallback(const struct ssl_early_callback_ctx *ctx) { return 1; } -static int SkipVerify(int preverify_ok, X509_STORE_CTX *store_ctx) { +static int VerifySucceed(X509_STORE_CTX *store_ctx, void *arg) { + SSL* ssl = (SSL*)X509_STORE_CTX_get_ex_data(store_ctx, + SSL_get_ex_data_X509_STORE_CTX_idx()); + const TestConfig *config = GetConfigPtr(ssl); + + if (!config->expected_ocsp_response.empty()) { + const uint8_t *data; + size_t len; + SSL_get0_ocsp_response(ssl, &data, &len); + if (len == 0) { + fprintf(stderr, "OCSP response not available in verify callback\n"); + return 0; + } + } + return 1; } +static int VerifyFail(X509_STORE_CTX *store_ctx, void *arg) { + store_ctx->error = X509_V_ERR_APPLICATION_VERIFICATION; + return 0; +} + static int NextProtosAdvertisedCallback(SSL *ssl, const uint8_t **out, unsigned int *out_len, void *arg) { const TestConfig *config = GetConfigPtr(ssl); @@ -341,6 +490,94 @@ static void InfoCallback(const SSL *ssl, int type, int val) { } } +static int NewSessionCallback(SSL *ssl, SSL_SESSION *session) { + GetTestState(ssl)->got_new_session = true; + // BoringSSL passes a reference to |session|. + SSL_SESSION_free(session); + return 1; +} + +static int TicketKeyCallback(SSL *ssl, uint8_t *key_name, uint8_t *iv, + EVP_CIPHER_CTX *ctx, HMAC_CTX *hmac_ctx, + int encrypt) { + // This is just test code, so use the all-zeros key. + static const uint8_t kZeros[16] = {0}; + + if (encrypt) { + memcpy(key_name, kZeros, sizeof(kZeros)); + RAND_bytes(iv, 16); + } else if (memcmp(key_name, kZeros, 16) != 0) { + return 0; + } + + if (!HMAC_Init_ex(hmac_ctx, kZeros, sizeof(kZeros), EVP_sha256(), NULL) || + !EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, kZeros, iv, encrypt)) { + return -1; + } + + if (!encrypt) { + return GetConfigPtr(ssl)->renew_ticket ? 2 : 1; + } + return 1; +} + +// kCustomExtensionValue is the extension value that the custom extension +// callbacks will add. +static const uint16_t kCustomExtensionValue = 1234; +static void *const kCustomExtensionAddArg = + reinterpret_cast<void *>(kCustomExtensionValue); +static void *const kCustomExtensionParseArg = + reinterpret_cast<void *>(kCustomExtensionValue + 1); +static const char kCustomExtensionContents[] = "custom extension"; + +static int CustomExtensionAddCallback(SSL *ssl, unsigned extension_value, + const uint8_t **out, size_t *out_len, + int *out_alert_value, void *add_arg) { + if (extension_value != kCustomExtensionValue || + add_arg != kCustomExtensionAddArg) { + abort(); + } + + if (GetConfigPtr(ssl)->custom_extension_skip) { + return 0; + } + if (GetConfigPtr(ssl)->custom_extension_fail_add) { + return -1; + } + + *out = reinterpret_cast<const uint8_t*>(kCustomExtensionContents); + *out_len = sizeof(kCustomExtensionContents) - 1; + + return 1; +} + +static void CustomExtensionFreeCallback(SSL *ssl, unsigned extension_value, + const uint8_t *out, void *add_arg) { + if (extension_value != kCustomExtensionValue || + add_arg != kCustomExtensionAddArg || + out != reinterpret_cast<const uint8_t *>(kCustomExtensionContents)) { + abort(); + } +} + +static int CustomExtensionParseCallback(SSL *ssl, unsigned extension_value, + const uint8_t *contents, + size_t contents_len, + int *out_alert_value, void *parse_arg) { + if (extension_value != kCustomExtensionValue || + parse_arg != kCustomExtensionParseArg) { + abort(); + } + + if (contents_len != sizeof(kCustomExtensionContents) - 1 || + memcmp(contents, kCustomExtensionContents, contents_len) != 0) { + *out_alert_value = SSL_AD_DECODE_ERROR; + return 0; + } + + return 1; +} + // Connect returns a new socket connected to localhost on |port| or -1 on // error. static int Connect(uint16_t port) { @@ -406,7 +643,23 @@ static ScopedSSL_CTX SetupCtx(const TestConfig *config) { return nullptr; } - if (!SSL_CTX_set_cipher_list(ssl_ctx.get(), "ALL")) { + std::string cipher_list = "ALL"; + if (!config->cipher.empty()) { + cipher_list = config->cipher; + SSL_CTX_set_options(ssl_ctx.get(), SSL_OP_CIPHER_SERVER_PREFERENCE); + } + if (!SSL_CTX_set_cipher_list(ssl_ctx.get(), cipher_list.c_str())) { + return nullptr; + } + + if (!config->cipher_tls10.empty() && + !SSL_CTX_set_cipher_list_tls10(ssl_ctx.get(), + config->cipher_tls10.c_str())) { + return nullptr; + } + if (!config->cipher_tls11.empty() && + !SSL_CTX_set_cipher_list_tls11(ssl_ctx.get(), + config->cipher_tls11.c_str())) { return nullptr; } @@ -438,12 +691,46 @@ static ScopedSSL_CTX SetupCtx(const TestConfig *config) { SSL_CTX_set_alpn_select_cb(ssl_ctx.get(), AlpnSelectCallback, NULL); } - ssl_ctx->tlsext_channel_id_enabled_new = 1; + SSL_CTX_enable_tls_channel_id(ssl_ctx.get()); SSL_CTX_set_channel_id_cb(ssl_ctx.get(), ChannelIdCallback); ssl_ctx->current_time_cb = CurrentTimeCallback; SSL_CTX_set_info_callback(ssl_ctx.get(), InfoCallback); + SSL_CTX_sess_set_new_cb(ssl_ctx.get(), NewSessionCallback); + + if (config->use_ticket_callback) { + SSL_CTX_set_tlsext_ticket_key_cb(ssl_ctx.get(), TicketKeyCallback); + } + + if (config->enable_client_custom_extension && + !SSL_CTX_add_client_custom_ext( + ssl_ctx.get(), kCustomExtensionValue, CustomExtensionAddCallback, + CustomExtensionFreeCallback, kCustomExtensionAddArg, + CustomExtensionParseCallback, kCustomExtensionParseArg)) { + return nullptr; + } + + if (config->enable_server_custom_extension && + !SSL_CTX_add_server_custom_ext( + ssl_ctx.get(), kCustomExtensionValue, CustomExtensionAddCallback, + CustomExtensionFreeCallback, kCustomExtensionAddArg, + CustomExtensionParseCallback, kCustomExtensionParseArg)) { + return nullptr; + } + + if (config->verify_fail) { + SSL_CTX_set_cert_verify_callback(ssl_ctx.get(), VerifyFail, NULL); + } else { + SSL_CTX_set_cert_verify_callback(ssl_ctx.get(), VerifySucceed, NULL); + } + + if (!config->signed_cert_timestamps.empty() && + !SSL_CTX_set_signed_cert_timestamp_list( + ssl_ctx.get(), (const uint8_t *)config->signed_cert_timestamps.data(), + config->signed_cert_timestamps.size())) { + return nullptr; + } return ssl_ctx; } @@ -500,6 +787,9 @@ static bool RetryAsync(SSL *ssl, int ret) { case SSL_ERROR_PENDING_CERTIFICATE: // The handshake will resume without a second call to the early callback. return InstallCertificate(ssl); + case SSL_ERROR_WANT_PRIVATE_KEY_OPERATION: + test_state->signature_retries++; + return true; default: return false; } @@ -531,6 +821,177 @@ static int WriteAll(SSL *ssl, const uint8_t *in, size_t in_len) { return ret; } +// DoShutdown calls |SSL_shutdown|, resolving any asynchronous operations. It +// returns the result of the final |SSL_shutdown| call. +static int DoShutdown(SSL *ssl) { + const TestConfig *config = GetConfigPtr(ssl); + int ret; + do { + ret = SSL_shutdown(ssl); + } while (config->async && RetryAsync(ssl, ret)); + return ret; +} + +// CheckHandshakeProperties checks, immediately after |ssl| completes its +// initial handshake (or False Starts), whether all the properties are +// consistent with the test configuration and invariants. +static bool CheckHandshakeProperties(SSL *ssl, bool is_resume) { + const TestConfig *config = GetConfigPtr(ssl); + + if (SSL_get_current_cipher(ssl) == nullptr) { + fprintf(stderr, "null cipher after handshake\n"); + return false; + } + + if (is_resume && + (!!SSL_session_reused(ssl) == config->expect_session_miss)) { + fprintf(stderr, "session was%s reused\n", + SSL_session_reused(ssl) ? "" : " not"); + return false; + } + + bool expect_handshake_done = is_resume || !config->false_start; + if (expect_handshake_done != GetTestState(ssl)->handshake_done) { + fprintf(stderr, "handshake was%s completed\n", + GetTestState(ssl)->handshake_done ? "" : " not"); + return false; + } + + if (expect_handshake_done && !config->is_server) { + bool expect_new_session = + !config->expect_no_session && + (!SSL_session_reused(ssl) || config->expect_ticket_renewal); + if (expect_new_session != GetTestState(ssl)->got_new_session) { + fprintf(stderr, + "new session was%s established, but we expected the opposite\n", + GetTestState(ssl)->got_new_session ? "" : " not"); + return false; + } + } + + if (config->is_server && !GetTestState(ssl)->early_callback_called) { + fprintf(stderr, "early callback not called\n"); + return false; + } + + if (!config->expected_server_name.empty()) { + const char *server_name = + SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + if (server_name != config->expected_server_name) { + fprintf(stderr, "servername mismatch (got %s; want %s)\n", + server_name, config->expected_server_name.c_str()); + return false; + } + } + + if (!config->expected_certificate_types.empty()) { + const uint8_t *certificate_types; + size_t certificate_types_len = + SSL_get0_certificate_types(ssl, &certificate_types); + if (certificate_types_len != config->expected_certificate_types.size() || + memcmp(certificate_types, + config->expected_certificate_types.data(), + certificate_types_len) != 0) { + fprintf(stderr, "certificate types mismatch\n"); + return false; + } + } + + if (!config->expected_next_proto.empty()) { + const uint8_t *next_proto; + unsigned next_proto_len; + SSL_get0_next_proto_negotiated(ssl, &next_proto, &next_proto_len); + if (next_proto_len != config->expected_next_proto.size() || + memcmp(next_proto, config->expected_next_proto.data(), + next_proto_len) != 0) { + fprintf(stderr, "negotiated next proto mismatch\n"); + return false; + } + } + + if (!config->expected_alpn.empty()) { + const uint8_t *alpn_proto; + unsigned alpn_proto_len; + SSL_get0_alpn_selected(ssl, &alpn_proto, &alpn_proto_len); + if (alpn_proto_len != config->expected_alpn.size() || + memcmp(alpn_proto, config->expected_alpn.data(), + alpn_proto_len) != 0) { + fprintf(stderr, "negotiated alpn proto mismatch\n"); + return false; + } + } + + if (!config->expected_channel_id.empty()) { + uint8_t channel_id[64]; + if (!SSL_get_tls_channel_id(ssl, channel_id, sizeof(channel_id))) { + fprintf(stderr, "no channel id negotiated\n"); + return false; + } + if (config->expected_channel_id.size() != 64 || + memcmp(config->expected_channel_id.data(), + channel_id, 64) != 0) { + fprintf(stderr, "channel id mismatch\n"); + return false; + } + } + + if (config->expect_extended_master_secret) { + if (!ssl->session->extended_master_secret) { + fprintf(stderr, "No EMS for session when expected"); + return false; + } + } + + if (!config->expected_ocsp_response.empty()) { + const uint8_t *data; + size_t len; + SSL_get0_ocsp_response(ssl, &data, &len); + if (config->expected_ocsp_response.size() != len || + memcmp(config->expected_ocsp_response.data(), data, len) != 0) { + fprintf(stderr, "OCSP response mismatch\n"); + return false; + } + } + + if (!config->expected_signed_cert_timestamps.empty()) { + const uint8_t *data; + size_t len; + SSL_get0_signed_cert_timestamp_list(ssl, &data, &len); + if (config->expected_signed_cert_timestamps.size() != len || + memcmp(config->expected_signed_cert_timestamps.data(), + data, len) != 0) { + fprintf(stderr, "SCT list mismatch\n"); + return false; + } + } + + if (config->expect_verify_result) { + int expected_verify_result = config->verify_fail ? + X509_V_ERR_APPLICATION_VERIFICATION : + X509_V_OK; + + if (SSL_get_verify_result(ssl) != expected_verify_result) { + fprintf(stderr, "Wrong certificate verification result\n"); + return false; + } + } + + if (!config->is_server) { + /* Clients should expect a peer certificate chain iff this was not a PSK + * cipher suite. */ + if (config->psk.empty()) { + if (SSL_get_peer_cert_chain(ssl) == nullptr) { + fprintf(stderr, "Missing peer certificate chain!\n"); + return false; + } + } else if (SSL_get_peer_cert_chain(ssl) != nullptr) { + fprintf(stderr, "Unexpected peer certificate chain!\n"); + return false; + } + } + return true; +} + // DoExchange runs a test SSL exchange against the peer. On success, it returns // true and sets |*out_session| to the negotiated SSL session. If the test is a // resumption attempt, |is_resume| is true and |session| is the session from the @@ -562,7 +1023,10 @@ static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx, } if (config->require_any_client_certificate) { SSL_set_verify(ssl.get(), SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, - SkipVerify); + NULL); + } + if (config->verify_peer) { + SSL_set_verify(ssl.get(), SSL_VERIFY_PEER, NULL); } if (config->false_start) { SSL_set_mode(ssl.get(), SSL_MODE_ENABLE_FALSE_START); @@ -588,8 +1052,8 @@ static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx, if (config->tls_d5_bug) { SSL_set_options(ssl.get(), SSL_OP_TLS_D5_BUG); } - if (config->allow_unsafe_legacy_renegotiation) { - SSL_set_options(ssl.get(), SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION); + if (config->microsoft_big_sslv3_buffer) { + SSL_set_options(ssl.get(), SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER); } if (config->no_legacy_server_connect) { SSL_clear_options(ssl.get(), SSL_OP_LEGACY_SERVER_CONNECT); @@ -637,7 +1101,6 @@ static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx, !SSL_enable_signed_cert_timestamps(ssl.get())) { return false; } - SSL_enable_fastradio_padding(ssl.get(), config->fastradio_padding); if (config->min_version != 0) { SSL_set_min_version(ssl.get(), (uint16_t)config->min_version); } @@ -651,14 +1114,13 @@ static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx, if (config->install_ddos_callback) { SSL_CTX_set_dos_protection_cb(ssl_ctx, DDoSCallback); } - if (!config->cipher.empty() && - !SSL_set_cipher_list(ssl.get(), config->cipher.c_str())) { - return false; - } if (!config->reject_peer_renegotiations) { /* Renegotiations are disabled by default. */ SSL_set_reject_peer_renegotiations(ssl.get(), 0); } + if (!config->check_close_notify) { + SSL_set_quiet_shutdown(ssl.get(), 1); + } int sock = Connect(config->port); if (sock == -1) { @@ -719,139 +1181,14 @@ static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx, ret = SSL_connect(ssl.get()); } } while (config->async && RetryAsync(ssl.get(), ret)); - if (ret != 1) { - return false; - } - - if (SSL_get_current_cipher(ssl.get()) == nullptr) { - fprintf(stderr, "null cipher after handshake\n"); - return false; - } - - if (is_resume && - (!!SSL_session_reused(ssl.get()) == config->expect_session_miss)) { - fprintf(stderr, "session was%s reused\n", - SSL_session_reused(ssl.get()) ? "" : " not"); - return false; - } - - bool expect_handshake_done = is_resume || !config->false_start; - if (expect_handshake_done != GetTestState(ssl.get())->handshake_done) { - fprintf(stderr, "handshake was%s completed\n", - GetTestState(ssl.get())->handshake_done ? "" : " not"); - return false; - } - - if (config->is_server && !GetTestState(ssl.get())->early_callback_called) { - fprintf(stderr, "early callback not called\n"); + if (ret != 1 || + !CheckHandshakeProperties(ssl.get(), is_resume)) { return false; } - if (!config->expected_server_name.empty()) { - const char *server_name = - SSL_get_servername(ssl.get(), TLSEXT_NAMETYPE_host_name); - if (server_name != config->expected_server_name) { - fprintf(stderr, "servername mismatch (got %s; want %s)\n", - server_name, config->expected_server_name.c_str()); - return false; - } - } - - if (!config->expected_certificate_types.empty()) { - uint8_t *certificate_types; - int num_certificate_types = - SSL_get0_certificate_types(ssl.get(), &certificate_types); - if (num_certificate_types != - (int)config->expected_certificate_types.size() || - memcmp(certificate_types, - config->expected_certificate_types.data(), - num_certificate_types) != 0) { - fprintf(stderr, "certificate types mismatch\n"); - return false; - } - } - - if (!config->expected_next_proto.empty()) { - const uint8_t *next_proto; - unsigned next_proto_len; - SSL_get0_next_proto_negotiated(ssl.get(), &next_proto, &next_proto_len); - if (next_proto_len != config->expected_next_proto.size() || - memcmp(next_proto, config->expected_next_proto.data(), - next_proto_len) != 0) { - fprintf(stderr, "negotiated next proto mismatch\n"); - return false; - } - } - - if (!config->expected_alpn.empty()) { - const uint8_t *alpn_proto; - unsigned alpn_proto_len; - SSL_get0_alpn_selected(ssl.get(), &alpn_proto, &alpn_proto_len); - if (alpn_proto_len != config->expected_alpn.size() || - memcmp(alpn_proto, config->expected_alpn.data(), - alpn_proto_len) != 0) { - fprintf(stderr, "negotiated alpn proto mismatch\n"); - return false; - } - } - - if (!config->expected_channel_id.empty()) { - uint8_t channel_id[64]; - if (!SSL_get_tls_channel_id(ssl.get(), channel_id, sizeof(channel_id))) { - fprintf(stderr, "no channel id negotiated\n"); - return false; - } - if (config->expected_channel_id.size() != 64 || - memcmp(config->expected_channel_id.data(), - channel_id, 64) != 0) { - fprintf(stderr, "channel id mismatch\n"); - return false; - } - } - - if (config->expect_extended_master_secret) { - if (!ssl->session->extended_master_secret) { - fprintf(stderr, "No EMS for session when expected"); - return false; - } - } - - if (!config->expected_ocsp_response.empty()) { - const uint8_t *data; - size_t len; - SSL_get0_ocsp_response(ssl.get(), &data, &len); - if (config->expected_ocsp_response.size() != len || - memcmp(config->expected_ocsp_response.data(), data, len) != 0) { - fprintf(stderr, "OCSP response mismatch\n"); - return false; - } - } - - if (!config->expected_signed_cert_timestamps.empty()) { - const uint8_t *data; - size_t len; - SSL_get0_signed_cert_timestamp_list(ssl.get(), &data, &len); - if (config->expected_signed_cert_timestamps.size() != len || - memcmp(config->expected_signed_cert_timestamps.data(), - data, len) != 0) { - fprintf(stderr, "SCT list mismatch\n"); - return false; - } - } - - if (!config->is_server) { - /* Clients should expect a peer certificate chain iff this was not a PSK - * cipher suite. */ - if (config->psk.empty()) { - if (SSL_get_peer_cert_chain(ssl.get()) == nullptr) { - fprintf(stderr, "Missing peer certificate chain!\n"); - return false; - } - } else if (SSL_get_peer_cert_chain(ssl.get()) != nullptr) { - fprintf(stderr, "Unexpected peer certificate chain!\n"); - return false; - } - } + // Reset the state to assert later that the callback isn't called in + // renegotations. + GetTestState(ssl.get())->got_new_session = false; } if (config->export_keying_material > 0) { @@ -897,18 +1234,19 @@ static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx, } // This mode writes a number of different record sizes in an attempt to // trip up the CBC record splitting code. - uint8_t buf[32769]; - memset(buf, 0x42, sizeof(buf)); + static const size_t kBufLen = 32769; + std::unique_ptr<uint8_t[]> buf(new uint8_t[kBufLen]); + memset(buf.get(), 0x42, kBufLen); static const size_t kRecordSizes[] = { 0, 1, 255, 256, 257, 16383, 16384, 16385, 32767, 32768, 32769}; for (size_t i = 0; i < sizeof(kRecordSizes) / sizeof(kRecordSizes[0]); i++) { const size_t len = kRecordSizes[i]; - if (len > sizeof(buf)) { + if (len > kBufLen) { fprintf(stderr, "Bad kRecordSizes value.\n"); return false; } - if (WriteAll(ssl.get(), buf, len) < 0) { + if (WriteAll(ssl.get(), buf.get(), len) < 0) { return false; } } @@ -919,53 +1257,82 @@ static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx, return false; } } - for (;;) { - uint8_t buf[512]; - int n = DoRead(ssl.get(), buf, sizeof(buf)); - int err = SSL_get_error(ssl.get(), n); - if (err == SSL_ERROR_ZERO_RETURN || - (n == 0 && err == SSL_ERROR_SYSCALL)) { - if (n != 0) { - fprintf(stderr, "Invalid SSL_get_error output\n"); + if (!config->shim_shuts_down) { + for (;;) { + static const size_t kBufLen = 16384; + std::unique_ptr<uint8_t[]> buf(new uint8_t[kBufLen]); + + // Read only 512 bytes at a time in TLS to ensure records may be + // returned in multiple reads. + int n = DoRead(ssl.get(), buf.get(), config->is_dtls ? kBufLen : 512); + int err = SSL_get_error(ssl.get(), n); + if (err == SSL_ERROR_ZERO_RETURN || + (n == 0 && err == SSL_ERROR_SYSCALL)) { + if (n != 0) { + fprintf(stderr, "Invalid SSL_get_error output\n"); + return false; + } + // Stop on either clean or unclean shutdown. + break; + } else if (err != SSL_ERROR_NONE) { + if (n > 0) { + fprintf(stderr, "Invalid SSL_get_error output\n"); + return false; + } return false; } - // Accept shutdowns with or without close_notify. - // TODO(davidben): Write tests which distinguish these two cases. - break; - } else if (err != SSL_ERROR_NONE) { - if (n > 0) { + // Successfully read data. + if (n <= 0) { fprintf(stderr, "Invalid SSL_get_error output\n"); return false; } - return false; - } - // Successfully read data. - if (n <= 0) { - fprintf(stderr, "Invalid SSL_get_error output\n"); - return false; - } - // After a successful read, with or without False Start, the handshake - // must be complete. - if (!GetTestState(ssl.get())->handshake_done) { - fprintf(stderr, "handshake was not completed after SSL_read\n"); - return false; - } + // After a successful read, with or without False Start, the handshake + // must be complete. + if (!GetTestState(ssl.get())->handshake_done) { + fprintf(stderr, "handshake was not completed after SSL_read\n"); + return false; + } - for (int i = 0; i < n; i++) { - buf[i] ^= 0xff; - } - if (WriteAll(ssl.get(), buf, n) < 0) { - return false; + for (int i = 0; i < n; i++) { + buf[i] ^= 0xff; + } + if (WriteAll(ssl.get(), buf.get(), n) < 0) { + return false; + } } } } + if (!config->is_server && !config->false_start && + !config->implicit_handshake && + GetTestState(ssl.get())->got_new_session) { + fprintf(stderr, "new session was established after the handshake\n"); + return false; + } + if (out_session) { out_session->reset(SSL_get1_session(ssl.get())); } - SSL_shutdown(ssl.get()); + ret = DoShutdown(ssl.get()); + + if (config->shim_shuts_down && config->check_close_notify) { + // We initiate shutdown, so |SSL_shutdown| will return in two stages. First + // it returns zero when our close_notify is sent, then one when the peer's + // is received. + if (ret != 0) { + fprintf(stderr, "Unexpected SSL_shutdown result: %d != 0\n", ret); + return false; + } + ret = DoShutdown(ssl.get()); + } + + if (ret != 1) { + fprintf(stderr, "Unexpected SSL_shutdown result: %d != 1\n", ret); + return false; + } + return true; } diff --git a/src/ssl/test/runner/cipher_suites.go b/src/ssl/test/runner/cipher_suites.go index 70c7262..ffc056d 100644 --- a/src/ssl/test/runner/cipher_suites.go +++ b/src/ssl/test/runner/cipher_suites.go @@ -102,7 +102,6 @@ var cipherSuites = []*cipherSuite{ {TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, 32, 48, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12 | suiteSHA384, cipherAES, macSHA384, nil}, {TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil}, {TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherAES, macSHA1, nil}, - {TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 32, 0, 0, dheRSAKA, suiteTLS12, nil, nil, aeadCHACHA20POLY1305}, {TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, dheRSAKA, suiteTLS12, nil, nil, aeadAESGCM}, {TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, dheRSAKA, suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM}, {TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, dheRSAKA, suiteTLS12, cipherAES, macSHA256, nil}, @@ -120,12 +119,18 @@ var cipherSuites = []*cipherSuite{ {TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, suiteECDHE, cipher3DES, macSHA1, nil}, {TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, dheRSAKA, 0, cipher3DES, macSHA1, nil}, {TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, 0, cipher3DES, macSHA1, nil}, - {TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdhePSKKA, suiteECDHE | suiteTLS12 | suitePSK, nil, nil, aeadAESGCM}, {TLS_PSK_WITH_RC4_128_SHA, 16, 20, 0, pskKA, suiteNoDTLS | suitePSK, cipherRC4, macSHA1, nil}, {TLS_PSK_WITH_AES_128_CBC_SHA, 16, 20, 16, pskKA, suitePSK, cipherAES, macSHA1, nil}, {TLS_PSK_WITH_AES_256_CBC_SHA, 32, 20, 16, pskKA, suitePSK, cipherAES, macSHA1, nil}, {TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdhePSKKA, suiteECDHE | suitePSK, cipherAES, macSHA1, nil}, {TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdhePSKKA, suiteECDHE | suitePSK, cipherAES, macSHA1, nil}, + {TLS_RSA_WITH_NULL_SHA, 0, 20, 0, rsaKA, suiteNoDTLS, cipherNull, macSHA1, nil}, +} + +type nullCipher struct{} + +func cipherNull(key, iv []byte, isRead bool) interface{} { + return nullCipher{} } func cipherRC4(key, iv []byte, isRead bool) interface{} { @@ -370,6 +375,7 @@ func mutualCipherSuite(have []uint16, want uint16) *cipherSuite { // A list of the possible cipher suite ids. Taken from // http://www.iana.org/assignments/tls-parameters/tls-parameters.xml const ( + TLS_RSA_WITH_NULL_SHA uint16 = 0x0002 TLS_RSA_WITH_RC4_128_MD5 uint16 = 0x0004 TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005 TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000a @@ -406,13 +412,12 @@ const ( TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc030 TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA uint16 = 0xc035 TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA uint16 = 0xc036 + renegotiationSCSV uint16 = 0x00ff fallbackSCSV uint16 = 0x5600 ) // Additional cipher suite IDs, not IANA-assigned. const ( - TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256 uint16 = 0xcafe TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcc13 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcc14 - TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcc15 ) diff --git a/src/ssl/test/runner/common.go b/src/ssl/test/runner/common.go index edebba1..77be9f6 100644 --- a/src/ssl/test/runner/common.go +++ b/src/ssl/test/runner/common.go @@ -82,6 +82,7 @@ const ( extensionSignedCertificateTimestamp uint16 = 18 extensionExtendedMasterSecret uint16 = 23 extensionSessionTicket uint16 = 35 + extensionCustom uint16 = 1234 // not IANA assigned extensionNextProtoNeg uint16 = 13172 // not IANA assigned extensionRenegotiationInfo uint16 = 0xff01 extensionChannelID uint16 = 30032 // not IANA assigned @@ -188,7 +189,9 @@ type ConnectionState struct { VerifiedChains [][]*x509.Certificate // verified chains built from PeerCertificates ChannelID *ecdsa.PublicKey // the channel ID for this connection SRTPProtectionProfile uint16 // the negotiated DTLS-SRTP protection profile - TLSUnique []byte + TLSUnique []byte // the tls-unique channel binding + SCTList []byte // signed certificate timestamp list + ClientCertSignatureHash uint8 // TLS id of the hash used by the client to sign the handshake } // ClientAuthType declares the policy the server will follow for @@ -214,6 +217,8 @@ type ClientSessionState struct { handshakeHash []byte // Handshake hash for Channel ID purposes. serverCertificates []*x509.Certificate // Certificate chain presented by the server extendedMasterSecret bool // Whether an extended master secret was used to generate the session + sctList []byte + ocspResponse []byte } // ClientSessionCache is a cache of ClientSessionState objects that can be used @@ -399,6 +404,10 @@ type ProtocolBugs struct { // ServerKeyExchange message should be invalid. InvalidSKXSignature bool + // InvalidCertVerifySignature specifies that the signature in a + // CertificateVerify message should be invalid. + InvalidCertVerifySignature bool + // InvalidSKXCurve causes the curve ID in the ServerKeyExchange message // to be wrong. InvalidSKXCurve bool @@ -476,6 +485,10 @@ type ProtocolBugs struct { // TLS_FALLBACK_SCSV in the ClientHello. SendFallbackSCSV bool + // SendRenegotiationSCSV causes the client to include the renegotiation + // SCSV in the ClientHello. + SendRenegotiationSCSV bool + // MaxHandshakeRecordLength, if non-zero, is the maximum size of a // handshake record. Handshake messages will be split into multiple // records at the specified size, except that the client_version will @@ -535,11 +548,14 @@ type ProtocolBugs struct { // must specify in the server_name extension. ExpectServerName string - // SwapNPNAndALPN switches the relative order between NPN and - // ALPN on the server. This is to test that server preference - // of ALPN works regardless of their relative order. + // SwapNPNAndALPN switches the relative order between NPN and ALPN in + // both ClientHello and ServerHello. SwapNPNAndALPN bool + // ALPNProtocol, if not nil, sets the ALPN protocol that a server will + // return. + ALPNProtocol *string + // AllowSessionVersionMismatch causes the server to resume sessions // regardless of the version associated with the session. AllowSessionVersionMismatch bool @@ -572,11 +588,15 @@ type ProtocolBugs struct { // didn't support the renegotiation info extension. NoRenegotiationInfo bool - // SequenceNumberIncrement, if non-zero, causes outgoing sequence - // numbers in DTLS to increment by that value rather by 1. This is to - // stress the replay bitmap window by simulating extreme packet loss and - // retransmit at the record layer. - SequenceNumberIncrement uint64 + // RequireRenegotiationInfo, if true, causes the client to return an + // error if the server doesn't reply with the renegotiation extension. + RequireRenegotiationInfo bool + + // SequenceNumberMapping, if non-nil, is the mapping function to apply + // to the sequence number of outgoing packets. For both TLS and DTLS, + // the two most-significant bytes in the resulting sequence number are + // ignored so that the DTLS epoch cannot be changed. + SequenceNumberMapping func(uint64) uint64 // RSAEphemeralKey, if true, causes the server to send a // ServerKeyExchange message containing an ephemeral key (as in @@ -609,10 +629,6 @@ type ProtocolBugs struct { // across a renego. RequireSameRenegoClientVersion bool - // RequireFastradioPadding, if true, requires that ClientHello messages - // be at least 1000 bytes long. - RequireFastradioPadding bool - // ExpectInitialRecordVersion, if non-zero, is the expected // version of the records before the version is determined. ExpectInitialRecordVersion uint16 @@ -626,7 +642,11 @@ type ProtocolBugs struct { // the server believes it has actually negotiated. SendCipherSuite uint16 - // AppDataAfterChangeCipherSpec, if not null, causes application data to + // AppDataBeforeHandshake, if not nil, causes application data to be + // sent immediately before the first handshake message. + AppDataBeforeHandshake []byte + + // AppDataAfterChangeCipherSpec, if not nil, causes application data to // be sent immediately after ChangeCipherSpec. AppDataAfterChangeCipherSpec []byte @@ -668,17 +688,10 @@ type ProtocolBugs struct { // handshake fragments in DTLS to have the wrong message length. FragmentMessageLengthMismatch bool - // SplitFragmentHeader, if true, causes the handshake fragments in DTLS - // to be split across two records. - SplitFragmentHeader bool - - // SplitFragmentBody, if true, causes the handshake bodies in DTLS to be - // split across two records. - // - // TODO(davidben): There's one final split to test: when the header and - // body are split across two records. But those are (incorrectly) - // accepted right now. - SplitFragmentBody bool + // SplitFragments, if non-zero, causes the handshake fragments in DTLS + // to be split across two records. The value of |SplitFragments| is the + // number of bytes in the first fragment. + SplitFragments int // SendEmptyFragments, if true, causes handshakes to include empty // fragments in DTLS. @@ -705,10 +718,6 @@ type ProtocolBugs struct { // preferences to be ignored. IgnorePeerCurvePreferences bool - // SendWarningAlerts, if non-zero, causes every record to be prefaced by - // a warning alert. - SendWarningAlerts alert - // BadFinished, if true, causes the Finished hash to be broken. BadFinished bool @@ -727,6 +736,43 @@ type ProtocolBugs struct { // EnableAllCiphersInDTLS, if true, causes RC4 to be enabled in DTLS. EnableAllCiphersInDTLS bool + + // EmptyCertificateList, if true, causes the server to send an empty + // certificate list in the Certificate message. + EmptyCertificateList bool + + // ExpectNewTicket, if true, causes the client to abort if it does not + // receive a new ticket. + ExpectNewTicket bool + + // RequireClientHelloSize, if not zero, is the required length in bytes + // of the ClientHello /record/. This is checked by the server. + RequireClientHelloSize int + + // CustomExtension, if not empty, contains the contents of an extension + // that will be added to client/server hellos. + CustomExtension string + + // ExpectedCustomExtension, if not nil, contains the expected contents + // of a custom extension. + ExpectedCustomExtension *string + + // NoCloseNotify, if true, causes the close_notify alert to be skipped + // on connection shutdown. + NoCloseNotify bool + + // ExpectCloseNotify, if true, requires a close_notify from the peer on + // shutdown. Records from the peer received after close_notify is sent + // are not discard. + ExpectCloseNotify bool + + // SendLargeRecords, if true, allows outgoing records to be sent + // arbitrarily large. + SendLargeRecords bool + + // NegotiateALPNAndNPN, if true, causes the server to negotiate both + // ALPN and NPN in the same connetion. + NegotiateALPNAndNPN bool } func (c *Config) serverInit() { diff --git a/src/ssl/test/runner/conn.go b/src/ssl/test/runner/conn.go index adbc1c3..39bdfda 100644 --- a/src/ssl/test/runner/conn.go +++ b/src/ssl/test/runner/conn.go @@ -12,6 +12,7 @@ import ( "crypto/ecdsa" "crypto/subtle" "crypto/x509" + "encoding/binary" "errors" "fmt" "io" @@ -39,6 +40,7 @@ type Conn struct { extendedMasterSecret bool // whether this session used an extended master secret cipherSuite *cipherSuite ocspResponse []byte // stapled OCSP response + sctList []byte // signed certificate timestamp list peerCertificates []*x509.Certificate // verifiedChains contains the certificate chains that we built, as // opposed to the ones presented by the server. @@ -48,6 +50,11 @@ type Conn struct { // firstFinished contains the first Finished hash sent during the // handshake. This is the "tls-unique" channel binding value. firstFinished [12]byte + // clientCertSignatureHash contains the TLS hash id for the hash that + // was used by the client to sign the handshake with a client + // certificate. This is only set by a server and is zero if no client + // certificates were used. + clientCertSignatureHash uint8 clientRandom, serverRandom [32]byte masterSecret [48]byte @@ -87,6 +94,8 @@ func (c *Conn) init() { c.out.isDTLS = c.isDTLS c.in.config = c.config c.out.config = c.config + + c.out.updateOutSeq() } // Access to net.Conn methods. @@ -134,6 +143,7 @@ type halfConn struct { cipher interface{} // cipher algorithm mac macFunction seq [8]byte // 64-bit sequence number + outSeq [8]byte // Mapped sequence number bfree *block // list of free blocks nextCipher interface{} // next encryption state @@ -189,10 +199,6 @@ func (hc *halfConn) incSeq(isOutgoing bool) { if hc.isDTLS { // Increment up to the epoch in DTLS. limit = 2 - - if isOutgoing && hc.config.Bugs.SequenceNumberIncrement != 0 { - increment = hc.config.Bugs.SequenceNumberIncrement - } } for i := 7; i >= limit; i-- { increment += uint64(hc.seq[i]) @@ -206,6 +212,8 @@ func (hc *halfConn) incSeq(isOutgoing bool) { if increment != 0 { panic("TLS: sequence number wraparound") } + + hc.updateOutSeq() } // incNextSeq increments the starting sequence number for the next epoch. @@ -241,6 +249,22 @@ func (hc *halfConn) incEpoch() { hc.seq[i] = 0 } } + + hc.updateOutSeq() +} + +func (hc *halfConn) updateOutSeq() { + if hc.config.Bugs.SequenceNumberMapping != nil { + seqU64 := binary.BigEndian.Uint64(hc.seq[:]) + seqU64 = hc.config.Bugs.SequenceNumberMapping(seqU64) + binary.BigEndian.PutUint64(hc.outSeq[:], seqU64) + + // The DTLS epoch cannot be changed. + copy(hc.outSeq[:2], hc.seq[:2]) + return + } + + copy(hc.outSeq[:], hc.seq[:]) } func (hc *halfConn) recordHeaderLen() int { @@ -397,6 +421,8 @@ func (hc *halfConn) decrypt(b *block) (ok bool, prefixLen int, alertValue alert) // // However, our behavior matches OpenSSL, so we leak // only as much as they do. + case nullCipher: + break default: panic("unknown cipher type") } @@ -460,7 +486,7 @@ func (hc *halfConn) encrypt(b *block, explicitIVLen int) (bool, alert) { // mac if hc.mac != nil { - mac := hc.mac.MAC(hc.outDigestBuf, hc.seq[0:], b.data[:3], b.data[recordHeaderLen-2:recordHeaderLen], b.data[recordHeaderLen+explicitIVLen:]) + mac := hc.mac.MAC(hc.outDigestBuf, hc.outSeq[0:], b.data[:3], b.data[recordHeaderLen-2:recordHeaderLen], b.data[recordHeaderLen+explicitIVLen:]) n := len(b.data) b.resize(n + len(mac)) @@ -478,7 +504,7 @@ func (hc *halfConn) encrypt(b *block, explicitIVLen int) (bool, alert) { case *tlsAead: payloadLen := len(b.data) - recordHeaderLen - explicitIVLen b.resize(len(b.data) + c.Overhead()) - nonce := hc.seq[:] + nonce := hc.outSeq[:] if c.explicitNonce { nonce = b.data[recordHeaderLen : recordHeaderLen+explicitIVLen] } @@ -486,7 +512,7 @@ func (hc *halfConn) encrypt(b *block, explicitIVLen int) (bool, alert) { payload = payload[:payloadLen] var additionalData [13]byte - copy(additionalData[:], hc.seq[:]) + copy(additionalData[:], hc.outSeq[:]) copy(additionalData[8:], b.data[:3]) additionalData[11] = byte(payloadLen >> 8) additionalData[12] = byte(payloadLen) @@ -502,6 +528,8 @@ func (hc *halfConn) encrypt(b *block, explicitIVLen int) (bool, alert) { b.resize(recordHeaderLen + explicitIVLen + len(prefix) + len(finalBlock)) c.CryptBlocks(b.data[recordHeaderLen+explicitIVLen:], prefix) c.CryptBlocks(b.data[recordHeaderLen+explicitIVLen+len(prefix):], finalBlock) + case nullCipher: + break default: panic("unknown cipher type") } @@ -630,10 +658,10 @@ func (c *Conn) doReadRecord(want recordType) (recordType, *block, error) { if err := b.readFromUntil(c.conn, recordHeaderLen); err != nil { // RFC suggests that EOF without an alertCloseNotify is // an error, but popular web sites seem to do this, - // so we can't make it an error. - // if err == io.EOF { - // err = io.ErrUnexpectedEOF - // } + // so we can't make it an error, outside of tests. + if err == io.EOF && c.config.Bugs.ExpectCloseNotify { + err = io.ErrUnexpectedEOF + } if e, ok := err.(net.Error); !ok || !e.Temporary() { c.in.setErrorLocked(err) } @@ -722,6 +750,10 @@ func (c *Conn) readRecord(want recordType) error { c.sendAlert(alertInternalError) return c.in.setErrorLocked(errors.New("tls: application data record requested before handshake complete")) } + case recordTypeAlert: + // Looking for a close_notify. Note: unlike a real + // implementation, this is not tolerant of additional records. + // See the documentation for ExpectCloseNotify. } Again: @@ -784,7 +816,7 @@ Again: // A client might need to process a HelloRequest from // the server, thus receiving a handshake message when // application data is expected is ok. - if !c.isClient { + if !c.isClient || want != recordTypeApplicationData { return c.in.setErrorLocked(c.sendAlert(alertNoRenegotiation)) } } @@ -799,13 +831,8 @@ Again: // sendAlert sends a TLS alert message. // c.out.Mutex <= L. -func (c *Conn) sendAlertLocked(err alert) error { - switch err { - case alertNoRenegotiation, alertCloseNotify: - c.tmp[0] = alertLevelWarning - default: - c.tmp[0] = alertLevelError - } +func (c *Conn) sendAlertLocked(level byte, err alert) error { + c.tmp[0] = level c.tmp[1] = byte(err) if c.config.Bugs.FragmentAlert { c.writeRecord(recordTypeAlert, c.tmp[0:1]) @@ -813,8 +840,8 @@ func (c *Conn) sendAlertLocked(err alert) error { } else { c.writeRecord(recordTypeAlert, c.tmp[0:2]) } - // closeNotify is a special case in that it isn't an error: - if err != alertCloseNotify { + // Error alerts are fatal to the connection. + if level == alertLevelError { return c.out.setErrorLocked(&net.OpError{Op: "local error", Err: err}) } return nil @@ -823,9 +850,17 @@ func (c *Conn) sendAlertLocked(err alert) error { // sendAlert sends a TLS alert message. // L < c.out.Mutex. func (c *Conn) sendAlert(err alert) error { + level := byte(alertLevelError) + if err == alertNoRenegotiation || err == alertCloseNotify { + level = alertLevelWarning + } + return c.SendAlert(level, err) +} + +func (c *Conn) SendAlert(level byte, err alert) error { c.out.Lock() defer c.out.Unlock() - return c.sendAlertLocked(err) + return c.sendAlertLocked(level, err) } // writeV2Record writes a record for a V2ClientHello. @@ -841,13 +876,6 @@ func (c *Conn) writeV2Record(data []byte) (n int, err error) { // to the connection and updates the record layer state. // c.out.Mutex <= L. func (c *Conn) writeRecord(typ recordType, data []byte) (n int, err error) { - if typ != recordTypeAlert && c.config.Bugs.SendWarningAlerts != 0 { - alert := make([]byte, 2) - alert[0] = alertLevelWarning - alert[1] = byte(c.config.Bugs.SendWarningAlerts) - c.writeRecord(recordTypeAlert, alert) - } - if c.isDTLS { return c.dtlsWriteRecord(typ, data) } @@ -856,9 +884,9 @@ func (c *Conn) writeRecord(typ recordType, data []byte) (n int, err error) { b := c.out.newBlock() first := true isClientHello := typ == recordTypeHandshake && len(data) > 0 && data[0] == typeClientHello - for len(data) > 0 { + for len(data) > 0 || first { m := len(data) - if m > maxPlaintext { + if m > maxPlaintext && !c.config.Bugs.SendLargeRecords { m = maxPlaintext } if typ == recordTypeHandshake && c.config.Bugs.MaxHandshakeRecordLength > 0 && m > c.config.Bugs.MaxHandshakeRecordLength { @@ -1038,6 +1066,9 @@ func (c *Conn) readHandshake() (interface{}, error) { // sequence number expectations but otherwise ignores them. func (c *Conn) skipPacket(packet []byte) error { for len(packet) > 0 { + if len(packet) < 13 { + return errors.New("tls: bad packet") + } // Dropped packets are completely ignored save to update // expected sequence numbers for this and the next epoch. (We // don't assert on the contents of the packets both for @@ -1057,6 +1088,9 @@ func (c *Conn) skipPacket(packet []byte) error { } c.in.incNextSeq() } + if len(packet) < 13+int(length) { + return errors.New("tls: bad packet") + } packet = packet[13+length:] } return nil @@ -1113,7 +1147,7 @@ func (c *Conn) Write(b []byte) (int, error) { } if c.config.Bugs.SendSpuriousAlert != 0 { - c.sendAlertLocked(c.config.Bugs.SendSpuriousAlert) + c.sendAlertLocked(alertLevelError, c.config.Bugs.SendSpuriousAlert) } // SSL 3.0 and TLS 1.0 are susceptible to a chosen-plaintext @@ -1240,10 +1274,22 @@ func (c *Conn) Close() error { c.handshakeMutex.Lock() defer c.handshakeMutex.Unlock() - if c.handshakeComplete { + if c.handshakeComplete && !c.config.Bugs.NoCloseNotify { alertErr = c.sendAlert(alertCloseNotify) } + // Consume a close_notify from the peer if one hasn't been received + // already. This avoids the peer from failing |SSL_shutdown| due to a + // write failing. + if c.handshakeComplete && alertErr == nil && c.config.Bugs.ExpectCloseNotify { + for c.in.error() == nil { + c.readRecord(recordTypeAlert) + } + if c.in.error() != io.EOF { + alertErr = c.in.error() + } + } + if err := c.conn.Close(); err != nil { return err } @@ -1273,6 +1319,9 @@ func (c *Conn) Handshake() error { }) c.conn.Write([]byte{alertLevelError, byte(alertInternalError)}) } + if data := c.config.Bugs.AppDataBeforeHandshake; data != nil { + c.writeRecord(recordTypeApplicationData, data) + } if c.isClient { c.handshakeErr = c.clientHandshake() } else { @@ -1304,6 +1353,8 @@ func (c *Conn) ConnectionState() ConnectionState { state.ChannelID = c.channelID state.SRTPProtectionProfile = c.srtpProtectionProfile state.TLSUnique = c.firstFinished[:] + state.SCTList = c.sctList + state.ClientCertSignatureHash = c.clientCertSignatureHash } return state diff --git a/src/ssl/test/runner/dtls.go b/src/ssl/test/runner/dtls.go index 50f7786..5c59dea 100644 --- a/src/ssl/test/runner/dtls.go +++ b/src/ssl/test/runner/dtls.go @@ -216,13 +216,10 @@ func (c *Conn) dtlsFlushHandshake() error { // Pack handshake fragments into records. var records [][]byte for _, fragment := range fragments { - if c.config.Bugs.SplitFragmentHeader { - records = append(records, fragment[:2]) - records = append(records, fragment[2:]) - } else if c.config.Bugs.SplitFragmentBody { - if len(fragment) > 12 { - records = append(records, fragment[:13]) - records = append(records, fragment[13:]) + if n := c.config.Bugs.SplitFragments; n > 0 { + if len(fragment) > n { + records = append(records, fragment[:n]) + records = append(records, fragment[n:]) } else { records = append(records, fragment) } @@ -301,13 +298,13 @@ func (c *Conn) dtlsSealRecord(typ recordType, data []byte) (b *block, err error) b.data[1] = byte(vers >> 8) b.data[2] = byte(vers) // DTLS records include an explicit sequence number. - copy(b.data[3:11], c.out.seq[0:]) + copy(b.data[3:11], c.out.outSeq[0:]) b.data[11] = byte(len(data) >> 8) b.data[12] = byte(len(data)) if explicitIVLen > 0 { explicitIV := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen] if explicitIVIsSeq { - copy(explicitIV, c.out.seq[:]) + copy(explicitIV, c.out.outSeq[:]) } else { if _, err = io.ReadFull(c.config.rand(), explicitIV); err != nil { return diff --git a/src/ssl/test/runner/handshake_client.go b/src/ssl/test/runner/handshake_client.go index a950313..a3ce686 100644 --- a/src/ssl/test/runner/handshake_client.go +++ b/src/ssl/test/runner/handshake_client.go @@ -45,7 +45,7 @@ func (c *Conn) clientHandshake() error { nextProtosLength := 0 for _, proto := range c.config.NextProtos { - if l := len(proto); l == 0 || l > 255 { + if l := len(proto); l > 255 { return errors.New("tls: invalid NextProtos value") } else { nextProtosLength += 1 + l @@ -61,6 +61,7 @@ func (c *Conn) clientHandshake() error { compressionMethods: []uint8{compressionNone}, random: make([]byte, 32), ocspStapling: true, + sctListSupported: true, serverName: c.config.ServerName, supportedCurves: c.config.curvePreferences(), supportedPoints: []uint8{pointFormatUncompressed}, @@ -73,6 +74,7 @@ func (c *Conn) clientHandshake() error { extendedMasterSecret: c.config.maxVersion() >= VersionTLS10, srtpProtectionProfiles: c.config.SRTPProtectionProfiles, srtpMasterKeyIdentifier: c.config.Bugs.SRTPMasterKeyIdentifer, + customExtension: c.config.Bugs.CustomExtension, } if c.config.Bugs.SendClientVersion != 0 { @@ -123,6 +125,10 @@ NextCipherSuite: } } + if c.config.Bugs.SendRenegotiationSCSV { + hello.cipherSuites = append(hello.cipherSuites, renegotiationSCSV) + } + if c.config.Bugs.SendFallbackSCSV { hello.cipherSuites = append(hello.cipherSuites, fallbackSCSV) } @@ -272,6 +278,10 @@ NextCipherSuite: return fmt.Errorf("tls: server selected an unsupported cipher suite") } + if c.config.Bugs.RequireRenegotiationInfo && serverHello.secureRenegotiation == nil { + return errors.New("tls: renegotiation extension missing") + } + if len(c.clientVerify) > 0 && !c.config.Bugs.NoRenegotiationInfo { var expectedRenegInfo []byte expectedRenegInfo = append(expectedRenegInfo, c.clientVerify...) @@ -282,6 +292,12 @@ NextCipherSuite: } } + if expected := c.config.Bugs.ExpectedCustomExtension; expected != nil { + if serverHello.customExtension != *expected { + return fmt.Errorf("tls: bad custom extension contents %q", serverHello.customExtension) + } + } + hs := &clientHandshakeState{ c: c, serverHello: serverHello, @@ -356,6 +372,7 @@ NextCipherSuite: copy(c.clientRandom[:], hs.hello.random) copy(c.serverRandom[:], hs.serverHello.random) copy(c.masterSecret[:], hs.masterSecret) + return nil } @@ -607,6 +624,9 @@ func (hs *clientHandshakeState) doFullHandshake() error { c.sendAlert(alertInternalError) return err } + if c.config.Bugs.InvalidCertVerifySignature { + digest[0] ^= 0x80 + } switch key := c.config.Certificates[0].PrivateKey.(type) { case *ecdsa.PrivateKey: @@ -730,13 +750,28 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) { return false, errors.New("tls: server resumed session on renegotiation") } + if hs.serverHello.sctList != nil { + return false, errors.New("tls: server sent SCT extension on session resumption") + } + + if hs.serverHello.ocspStapling { + return false, errors.New("tls: server sent OCSP extension on session resumption") + } + // Restore masterSecret and peerCerts from previous state hs.masterSecret = hs.session.masterSecret c.peerCertificates = hs.session.serverCertificates c.extendedMasterSecret = hs.session.extendedMasterSecret + c.sctList = hs.session.sctList + c.ocspResponse = hs.session.ocspResponse hs.finishedHash.discardHandshakeBuffer() return true, nil } + + if hs.serverHello.sctList != nil { + c.sctList = hs.serverHello.sctList + } + return false, nil } @@ -783,9 +818,14 @@ func (hs *clientHandshakeState) readSessionTicket() error { masterSecret: hs.masterSecret, handshakeHash: hs.finishedHash.server.Sum(nil), serverCertificates: c.peerCertificates, + sctList: c.sctList, + ocspResponse: c.ocspResponse, } if !hs.serverHello.ticketSupported { + if c.config.Bugs.ExpectNewTicket { + return errors.New("tls: expected new ticket") + } if hs.session == nil && len(hs.serverHello.sessionId) > 0 { session.sessionId = hs.serverHello.sessionId hs.session = session diff --git a/src/ssl/test/runner/handshake_messages.go b/src/ssl/test/runner/handshake_messages.go index ce214fd..da85e7a 100644 --- a/src/ssl/test/runner/handshake_messages.go +++ b/src/ssl/test/runner/handshake_messages.go @@ -32,6 +32,7 @@ type clientHelloMsg struct { srtpProtectionProfiles []uint16 srtpMasterKeyIdentifier string sctListSupported bool + customExtension string } func (m *clientHelloMsg) equal(i interface{}) bool { @@ -65,7 +66,8 @@ func (m *clientHelloMsg) equal(i interface{}) bool { m.extendedMasterSecret == m1.extendedMasterSecret && eqUint16s(m.srtpProtectionProfiles, m1.srtpProtectionProfiles) && m.srtpMasterKeyIdentifier == m1.srtpMasterKeyIdentifier && - m.sctListSupported == m1.sctListSupported + m.sctListSupported == m1.sctListSupported && + m.customExtension == m1.customExtension } func (m *clientHelloMsg) marshal() []byte { @@ -119,7 +121,7 @@ func (m *clientHelloMsg) marshal() []byte { if len(m.alpnProtocols) > 0 { extensionsLength += 2 for _, s := range m.alpnProtocols { - if l := len(s); l == 0 || l > 255 { + if l := len(s); l > 255 { panic("invalid ALPN protocol") } extensionsLength++ @@ -138,6 +140,10 @@ func (m *clientHelloMsg) marshal() []byte { if m.sctListSupported { numExtensions++ } + if l := len(m.customExtension); l > 0 { + extensionsLength += l + numExtensions++ + } if numExtensions > 0 { extensionsLength += 4 * numExtensions length += 2 + extensionsLength @@ -376,6 +382,14 @@ func (m *clientHelloMsg) marshal() []byte { z[1] = byte(extensionSignedCertificateTimestamp & 0xff) z = z[4:] } + if l := len(m.customExtension); l > 0 { + z[0] = byte(extensionCustom >> 8) + z[1] = byte(extensionCustom & 0xff) + z[2] = byte(l >> 8) + z[3] = byte(l & 0xff) + copy(z[4:], []byte(m.customExtension)) + z = z[4+l:] + } m.raw = x @@ -443,6 +457,7 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool { m.signatureAndHashes = nil m.alpnProtocols = nil m.extendedMasterSecret = false + m.customExtension = "" if len(data) == 0 { // ClientHello is optionally followed by extension data @@ -604,6 +619,8 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool { return false } m.sctListSupported = true + case extensionCustom: + m.customExtension = string(data[:length]) } data = data[length:] } @@ -625,40 +642,15 @@ type serverHelloMsg struct { ticketSupported bool secureRenegotiation []byte alpnProtocol string + alpnProtocolEmpty bool duplicateExtension bool channelIDRequested bool extendedMasterSecret bool srtpProtectionProfile uint16 srtpMasterKeyIdentifier string sctList []byte -} - -func (m *serverHelloMsg) equal(i interface{}) bool { - m1, ok := i.(*serverHelloMsg) - if !ok { - return false - } - - return bytes.Equal(m.raw, m1.raw) && - m.isDTLS == m1.isDTLS && - m.vers == m1.vers && - bytes.Equal(m.random, m1.random) && - bytes.Equal(m.sessionId, m1.sessionId) && - m.cipherSuite == m1.cipherSuite && - m.compressionMethod == m1.compressionMethod && - m.nextProtoNeg == m1.nextProtoNeg && - eqStrings(m.nextProtos, m1.nextProtos) && - m.ocspStapling == m1.ocspStapling && - m.ticketSupported == m1.ticketSupported && - bytes.Equal(m.secureRenegotiation, m1.secureRenegotiation) && - (m.secureRenegotiation == nil) == (m1.secureRenegotiation == nil) && - m.alpnProtocol == m1.alpnProtocol && - m.duplicateExtension == m1.duplicateExtension && - m.channelIDRequested == m1.channelIDRequested && - m.extendedMasterSecret == m1.extendedMasterSecret && - m.srtpProtectionProfile == m1.srtpProtectionProfile && - m.srtpMasterKeyIdentifier == m1.srtpMasterKeyIdentifier && - bytes.Equal(m.sctList, m1.sctList) + customExtension string + npnLast bool } func (m *serverHelloMsg) marshal() []byte { @@ -695,7 +687,7 @@ func (m *serverHelloMsg) marshal() []byte { if m.channelIDRequested { numExtensions++ } - if alpnLen := len(m.alpnProtocol); alpnLen > 0 { + if alpnLen := len(m.alpnProtocol); alpnLen > 0 || m.alpnProtocolEmpty { if alpnLen >= 256 { panic("invalid ALPN protocol") } @@ -713,6 +705,10 @@ func (m *serverHelloMsg) marshal() []byte { extensionsLength += len(m.sctList) numExtensions++ } + if l := len(m.customExtension); l > 0 { + extensionsLength += l + numExtensions++ + } if numExtensions > 0 { extensionsLength += 4 * numExtensions @@ -747,7 +743,7 @@ func (m *serverHelloMsg) marshal() []byte { z[1] = 0xff z = z[4:] } - if m.nextProtoNeg { + if m.nextProtoNeg && !m.npnLast { z[0] = byte(extensionNextProtoNeg >> 8) z[1] = byte(extensionNextProtoNeg & 0xff) z[2] = byte(nextProtoLen >> 8) @@ -784,7 +780,7 @@ func (m *serverHelloMsg) marshal() []byte { copy(z, m.secureRenegotiation) z = z[len(m.secureRenegotiation):] } - if alpnLen := len(m.alpnProtocol); alpnLen > 0 { + if alpnLen := len(m.alpnProtocol); alpnLen > 0 || m.alpnProtocolEmpty { z[0] = byte(extensionALPN >> 8) z[1] = byte(extensionALPN & 0xff) l := 2 + 1 + alpnLen @@ -838,6 +834,31 @@ func (m *serverHelloMsg) marshal() []byte { copy(z[4:], m.sctList) z = z[4+l:] } + if l := len(m.customExtension); l > 0 { + z[0] = byte(extensionCustom >> 8) + z[1] = byte(extensionCustom & 0xff) + z[2] = byte(l >> 8) + z[3] = byte(l & 0xff) + copy(z[4:], []byte(m.customExtension)) + z = z[4+l:] + } + if m.nextProtoNeg && m.npnLast { + z[0] = byte(extensionNextProtoNeg >> 8) + z[1] = byte(extensionNextProtoNeg & 0xff) + z[2] = byte(nextProtoLen >> 8) + z[3] = byte(nextProtoLen) + z = z[4:] + + for _, v := range m.nextProtos { + l := len(v) + if l > 255 { + l = 255 + } + z[0] = byte(l) + copy(z[1:], []byte(v[0:l])) + z = z[1+l:] + } + } m.raw = x @@ -869,7 +890,9 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool { m.ocspStapling = false m.ticketSupported = false m.alpnProtocol = "" + m.alpnProtocolEmpty = false m.extendedMasterSecret = false + m.customExtension = "" if len(data) == 0 { // ServerHello is optionally followed by extension data @@ -940,6 +963,7 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool { } d = d[1:] m.alpnProtocol = string(d) + m.alpnProtocolEmpty = len(d) == 0 case extensionChannelID: if length > 0 { return false @@ -965,14 +989,9 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool { } m.srtpMasterKeyIdentifier = string(d[1:]) case extensionSignedCertificateTimestamp: - if length < 2 { - return false - } - l := int(data[0])<<8 | int(data[1]) - if l != len(data)-2 { - return false - } - m.sctList = data[2:length] + m.sctList = data[:length] + case extensionCustom: + m.customExtension = string(data[:length]) } data = data[length:] } @@ -985,16 +1004,6 @@ type certificateMsg struct { certificates [][]byte } -func (m *certificateMsg) equal(i interface{}) bool { - m1, ok := i.(*certificateMsg) - if !ok { - return false - } - - return bytes.Equal(m.raw, m1.raw) && - eqByteSlices(m.certificates, m1.certificates) -} - func (m *certificateMsg) marshal() (x []byte) { if m.raw != nil { return m.raw @@ -1072,16 +1081,6 @@ type serverKeyExchangeMsg struct { key []byte } -func (m *serverKeyExchangeMsg) equal(i interface{}) bool { - m1, ok := i.(*serverKeyExchangeMsg) - if !ok { - return false - } - - return bytes.Equal(m.raw, m1.raw) && - bytes.Equal(m.key, m1.key) -} - func (m *serverKeyExchangeMsg) marshal() []byte { if m.raw != nil { return m.raw @@ -1113,17 +1112,6 @@ type certificateStatusMsg struct { response []byte } -func (m *certificateStatusMsg) equal(i interface{}) bool { - m1, ok := i.(*certificateStatusMsg) - if !ok { - return false - } - - return bytes.Equal(m.raw, m1.raw) && - m.statusType == m1.statusType && - bytes.Equal(m.response, m1.response) -} - func (m *certificateStatusMsg) marshal() []byte { if m.raw != nil { return m.raw @@ -1175,11 +1163,6 @@ func (m *certificateStatusMsg) unmarshal(data []byte) bool { type serverHelloDoneMsg struct{} -func (m *serverHelloDoneMsg) equal(i interface{}) bool { - _, ok := i.(*serverHelloDoneMsg) - return ok -} - func (m *serverHelloDoneMsg) marshal() []byte { x := make([]byte, 4) x[0] = typeServerHelloDone @@ -1195,16 +1178,6 @@ type clientKeyExchangeMsg struct { ciphertext []byte } -func (m *clientKeyExchangeMsg) equal(i interface{}) bool { - m1, ok := i.(*clientKeyExchangeMsg) - if !ok { - return false - } - - return bytes.Equal(m.raw, m1.raw) && - bytes.Equal(m.ciphertext, m1.ciphertext) -} - func (m *clientKeyExchangeMsg) marshal() []byte { if m.raw != nil { return m.raw @@ -1239,16 +1212,6 @@ type finishedMsg struct { verifyData []byte } -func (m *finishedMsg) equal(i interface{}) bool { - m1, ok := i.(*finishedMsg) - if !ok { - return false - } - - return bytes.Equal(m.raw, m1.raw) && - bytes.Equal(m.verifyData, m1.verifyData) -} - func (m *finishedMsg) marshal() (x []byte) { if m.raw != nil { return m.raw @@ -1276,16 +1239,6 @@ type nextProtoMsg struct { proto string } -func (m *nextProtoMsg) equal(i interface{}) bool { - m1, ok := i.(*nextProtoMsg) - if !ok { - return false - } - - return bytes.Equal(m.raw, m1.raw) && - m.proto == m1.proto -} - func (m *nextProtoMsg) marshal() []byte { if m.raw != nil { return m.raw @@ -1353,18 +1306,6 @@ type certificateRequestMsg struct { certificateAuthorities [][]byte } -func (m *certificateRequestMsg) equal(i interface{}) bool { - m1, ok := i.(*certificateRequestMsg) - if !ok { - return false - } - - return bytes.Equal(m.raw, m1.raw) && - bytes.Equal(m.certificateTypes, m1.certificateTypes) && - eqByteSlices(m.certificateAuthorities, m1.certificateAuthorities) && - eqSignatureAndHashes(m.signatureAndHashes, m1.signatureAndHashes) -} - func (m *certificateRequestMsg) marshal() (x []byte) { if m.raw != nil { return m.raw @@ -1507,19 +1448,6 @@ type certificateVerifyMsg struct { signature []byte } -func (m *certificateVerifyMsg) equal(i interface{}) bool { - m1, ok := i.(*certificateVerifyMsg) - if !ok { - return false - } - - return bytes.Equal(m.raw, m1.raw) && - m.hasSignatureAndHash == m1.hasSignatureAndHash && - m.signatureAndHash.hash == m1.signatureAndHash.hash && - m.signatureAndHash.signature == m1.signatureAndHash.signature && - bytes.Equal(m.signature, m1.signature) -} - func (m *certificateVerifyMsg) marshal() (x []byte) { if m.raw != nil { return m.raw @@ -1589,16 +1517,6 @@ type newSessionTicketMsg struct { ticket []byte } -func (m *newSessionTicketMsg) equal(i interface{}) bool { - m1, ok := i.(*newSessionTicketMsg) - if !ok { - return false - } - - return bytes.Equal(m.raw, m1.raw) && - bytes.Equal(m.ticket, m1.ticket) -} - func (m *newSessionTicketMsg) marshal() (x []byte) { if m.raw != nil { return m.raw @@ -1651,19 +1569,6 @@ type v2ClientHelloMsg struct { challenge []byte } -func (m *v2ClientHelloMsg) equal(i interface{}) bool { - m1, ok := i.(*v2ClientHelloMsg) - if !ok { - return false - } - - return bytes.Equal(m.raw, m1.raw) && - m.vers == m1.vers && - eqUint16s(m.cipherSuites, m1.cipherSuites) && - bytes.Equal(m.sessionId, m1.sessionId) && - bytes.Equal(m.challenge, m1.challenge) -} - func (m *v2ClientHelloMsg) marshal() []byte { if m.raw != nil { return m.raw @@ -1703,17 +1608,6 @@ type helloVerifyRequestMsg struct { cookie []byte } -func (m *helloVerifyRequestMsg) equal(i interface{}) bool { - m1, ok := i.(*helloVerifyRequestMsg) - if !ok { - return false - } - - return bytes.Equal(m.raw, m1.raw) && - m.vers == m1.vers && - bytes.Equal(m.cookie, m1.cookie) -} - func (m *helloVerifyRequestMsg) marshal() []byte { if m.raw != nil { return m.raw @@ -1755,16 +1649,6 @@ type encryptedExtensionsMsg struct { channelID []byte } -func (m *encryptedExtensionsMsg) equal(i interface{}) bool { - m1, ok := i.(*encryptedExtensionsMsg) - if !ok { - return false - } - - return bytes.Equal(m.raw, m1.raw) && - bytes.Equal(m.channelID, m1.channelID) -} - func (m *encryptedExtensionsMsg) marshal() []byte { if m.raw != nil { return m.raw diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go index 85cc0d2..068dff9 100644 --- a/src/ssl/test/runner/handshake_server.go +++ b/src/ssl/test/runner/handshake_server.go @@ -139,8 +139,8 @@ func (hs *serverHandshakeState) readClientHello() (isResume bool, err error) { c.sendAlert(alertUnexpectedMessage) return false, unexpectedMessageError(hs.clientHello, msg) } - if config.Bugs.RequireFastradioPadding && len(hs.clientHello.raw) < 1000 { - return false, errors.New("tls: ClientHello record size should be larger than 1000 bytes when padding enabled.") + if size := config.Bugs.RequireClientHelloSize; size != 0 && len(hs.clientHello.raw) != size { + return false, fmt.Errorf("tls: ClientHello record size is %d, but expected %d", len(hs.clientHello.raw), size) } if c.isDTLS && !config.Bugs.SkipHelloVerifyRequest { @@ -210,8 +210,11 @@ func (hs *serverHandshakeState) readClientHello() (isResume bool, err error) { } c.haveVers = true - hs.hello = new(serverHelloMsg) - hs.hello.isDTLS = c.isDTLS + hs.hello = &serverHelloMsg{ + isDTLS: c.isDTLS, + customExtension: config.Bugs.CustomExtension, + npnLast: config.Bugs.SwapNPNAndALPN, + } supportedCurve := false preferredCurves := config.curvePreferences() @@ -285,12 +288,18 @@ Curves: } if len(hs.clientHello.alpnProtocols) > 0 { - if selectedProto, fallback := mutualProtocol(hs.clientHello.alpnProtocols, c.config.NextProtos); !fallback { + if proto := c.config.Bugs.ALPNProtocol; proto != nil { + hs.hello.alpnProtocol = *proto + hs.hello.alpnProtocolEmpty = len(*proto) == 0 + c.clientProtocol = *proto + c.usedALPN = true + } else if selectedProto, fallback := mutualProtocol(hs.clientHello.alpnProtocols, c.config.NextProtos); !fallback { hs.hello.alpnProtocol = selectedProto c.clientProtocol = selectedProto c.usedALPN = true } - } else { + } + if len(hs.clientHello.alpnProtocols) == 0 || c.config.Bugs.NegotiateALPNAndNPN { // Although sending an empty NPN extension is reasonable, Firefox has // had a bug around this. Best to send nothing at all if // config.NextProtos is empty. See @@ -335,6 +344,12 @@ Curves: hs.hello.srtpProtectionProfile = c.config.Bugs.SendSRTPProtectionProfile } + if expected := c.config.Bugs.ExpectedCustomExtension; expected != nil { + if hs.clientHello.customExtension != *expected { + return false, fmt.Errorf("tls: bad custom extension contents %q", hs.clientHello.customExtension) + } + } + _, hs.ecdsaOk = hs.cert.PrivateKey.(*ecdsa.PrivateKey) // For test purposes, check that the peer never offers a session when @@ -516,7 +531,9 @@ func (hs *serverHandshakeState) doFullHandshake() error { if !isPSK { certMsg := new(certificateMsg) - certMsg.certificates = hs.cert.Certificate + if !config.Bugs.EmptyCertificateList { + certMsg.certificates = hs.cert.Certificate + } if !config.Bugs.UnauthenticatedECDH { certMsgBytes := certMsg.marshal() if config.Bugs.WrongCertificateMessageType { @@ -668,6 +685,7 @@ func (hs *serverHandshakeState) doFullHandshake() error { if !isSupportedSignatureAndHash(signatureAndHash, config.signatureAndHashesForServer()) { return errors.New("tls: unsupported hash function for client certificate") } + c.clientCertSignatureHash = signatureAndHash.hash } else { // Before TLS 1.2 the signature algorithm was implicit // from the key type, and only one hash per signature diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go index 94c1d32..269a955 100644 --- a/src/ssl/test/runner/runner.go +++ b/src/ssl/test/runner/runner.go @@ -32,6 +32,10 @@ var ( mallocTestDebug = flag.Bool("malloc-test-debug", false, "If true, ask bssl_shim to abort rather than fail a malloc. This can be used with a specific value for --malloc-test to identity the malloc failing that is causing problems.") jsonOutput = flag.String("json-output", "", "The file to output JSON results to.") pipe = flag.Bool("pipe", false, "If true, print status output suitable for piping into another program.") + testToRun = flag.String("test", "", "The name of a test to run, or empty to run all tests") + numWorkers = flag.Int("num-workers", runtime.NumCPU(), "The number of workers to run in parallel.") + shimPath = flag.String("shim-path", "../../../build/ssl/test/bssl_shim", "The location of the shim binary.") + resourceDir = flag.String("resource-dir", ".", "The directory in which to find certificate and key files.") ) const ( @@ -54,21 +58,21 @@ var testSCTList = []byte{5, 6, 7, 8} func initCertificates() { var err error - rsaCertificate, err = LoadX509KeyPair(rsaCertificateFile, rsaKeyFile) + rsaCertificate, err = LoadX509KeyPair(path.Join(*resourceDir, rsaCertificateFile), path.Join(*resourceDir, rsaKeyFile)) if err != nil { panic(err) } rsaCertificate.OCSPStaple = testOCSPResponse rsaCertificate.SignedCertificateTimestampList = testSCTList - ecdsaCertificate, err = LoadX509KeyPair(ecdsaCertificateFile, ecdsaKeyFile) + ecdsaCertificate, err = LoadX509KeyPair(path.Join(*resourceDir, ecdsaCertificateFile), path.Join(*resourceDir, ecdsaKeyFile)) if err != nil { panic(err) } ecdsaCertificate.OCSPStaple = testOCSPResponse ecdsaCertificate.SignedCertificateTimestampList = testSCTList - channelIDPEMBlock, err := ioutil.ReadFile(channelIDKeyFile) + channelIDPEMBlock, err := ioutil.ReadFile(path.Join(*resourceDir, channelIDKeyFile)) if err != nil { panic(err) } @@ -151,9 +155,21 @@ type testCase struct { // expectedSRTPProtectionProfile is the DTLS-SRTP profile that // should be negotiated. If zero, none should be negotiated. expectedSRTPProtectionProfile uint16 + // expectedOCSPResponse, if not nil, is the expected OCSP response to be received. + expectedOCSPResponse []uint8 + // expectedSCTList, if not nil, is the expected SCT list to be received. + expectedSCTList []uint8 + // expectedClientCertSignatureHash, if not zero, is the TLS id of the + // hash function that the client should have used when signing the + // handshake with a client certificate. + expectedClientCertSignatureHash uint8 // messageLen is the length, in bytes, of the test message that will be // sent. messageLen int + // messageCount is the number of test messages that will be sent. + messageCount int + // digestPrefs is the list of digest preferences from the client. + digestPrefs string // certFile is the path to the certificate to use for the server. certFile string // keyFile is the path to the private key to use for the server. @@ -174,12 +190,19 @@ type testCase struct { // newSessionsOnResume, if true, will cause resumeConfig to // use a different session resumption context. newSessionsOnResume bool + // noSessionCache, if true, will cause the server to run without a + // session cache. + noSessionCache bool // sendPrefix sends a prefix on the socket before actually performing a // handshake. sendPrefix string // shimWritesFirst controls whether the shim sends an initial "hello" // message before doing a roundtrip with the runner. shimWritesFirst bool + // shimShutsDown, if true, runs a test where the shim shuts down the + // connection immediately after the handshake rather than echoing + // messages from the runner. + shimShutsDown bool // renegotiate indicates the the connection should be renegotiated // during the exchange. renegotiate bool @@ -204,941 +227,20 @@ type testCase struct { // testTLSUnique, if true, causes the shim to send the tls-unique value // which will be compared against the expected value. testTLSUnique bool + // sendEmptyRecords is the number of consecutive empty records to send + // before and after the test message. + sendEmptyRecords int + // sendWarningAlerts is the number of consecutive warning alerts to send + // before and after the test message. + sendWarningAlerts int + // expectMessageDropped, if true, means the test message is expected to + // be dropped by the client rather than echoed back. + expectMessageDropped bool } -var testCases = []testCase{ - { - name: "BadRSASignature", - config: Config{ - CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, - Bugs: ProtocolBugs{ - InvalidSKXSignature: true, - }, - }, - shouldFail: true, - expectedError: ":BAD_SIGNATURE:", - }, - { - name: "BadECDSASignature", - config: Config{ - CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, - Bugs: ProtocolBugs{ - InvalidSKXSignature: true, - }, - Certificates: []Certificate{getECDSACertificate()}, - }, - shouldFail: true, - expectedError: ":BAD_SIGNATURE:", - }, - { - name: "BadECDSACurve", - config: Config{ - CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, - Bugs: ProtocolBugs{ - InvalidSKXCurve: true, - }, - Certificates: []Certificate{getECDSACertificate()}, - }, - shouldFail: true, - expectedError: ":WRONG_CURVE:", - }, - { - testType: serverTest, - name: "BadRSAVersion", - config: Config{ - CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA}, - Bugs: ProtocolBugs{ - RsaClientKeyExchangeVersion: VersionTLS11, - }, - }, - shouldFail: true, - expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:", - }, - { - name: "NoFallbackSCSV", - config: Config{ - Bugs: ProtocolBugs{ - FailIfNotFallbackSCSV: true, - }, - }, - shouldFail: true, - expectedLocalError: "no fallback SCSV found", - }, - { - name: "SendFallbackSCSV", - config: Config{ - Bugs: ProtocolBugs{ - FailIfNotFallbackSCSV: true, - }, - }, - flags: []string{"-fallback-scsv"}, - }, - { - name: "ClientCertificateTypes", - config: Config{ - ClientAuth: RequestClientCert, - ClientCertificateTypes: []byte{ - CertTypeDSSSign, - CertTypeRSASign, - CertTypeECDSASign, - }, - }, - flags: []string{ - "-expect-certificate-types", - base64.StdEncoding.EncodeToString([]byte{ - CertTypeDSSSign, - CertTypeRSASign, - CertTypeECDSASign, - }), - }, - }, - { - name: "NoClientCertificate", - config: Config{ - ClientAuth: RequireAnyClientCert, - }, - shouldFail: true, - expectedLocalError: "client didn't provide a certificate", - }, - { - name: "UnauthenticatedECDH", - config: Config{ - CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, - Bugs: ProtocolBugs{ - UnauthenticatedECDH: true, - }, - }, - shouldFail: true, - expectedError: ":UNEXPECTED_MESSAGE:", - }, - { - name: "SkipCertificateStatus", - config: Config{ - CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, - Bugs: ProtocolBugs{ - SkipCertificateStatus: true, - }, - }, - flags: []string{ - "-enable-ocsp-stapling", - }, - }, - { - name: "SkipServerKeyExchange", - config: Config{ - CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, - Bugs: ProtocolBugs{ - SkipServerKeyExchange: true, - }, - }, - shouldFail: true, - expectedError: ":UNEXPECTED_MESSAGE:", - }, - { - name: "SkipChangeCipherSpec-Client", - config: Config{ - Bugs: ProtocolBugs{ - SkipChangeCipherSpec: true, - }, - }, - shouldFail: true, - expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:", - }, - { - testType: serverTest, - name: "SkipChangeCipherSpec-Server", - config: Config{ - Bugs: ProtocolBugs{ - SkipChangeCipherSpec: true, - }, - }, - shouldFail: true, - expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:", - }, - { - testType: serverTest, - name: "SkipChangeCipherSpec-Server-NPN", - config: Config{ - NextProtos: []string{"bar"}, - Bugs: ProtocolBugs{ - SkipChangeCipherSpec: true, - }, - }, - flags: []string{ - "-advertise-npn", "\x03foo\x03bar\x03baz", - }, - shouldFail: true, - expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:", - }, - { - name: "FragmentAcrossChangeCipherSpec-Client", - config: Config{ - Bugs: ProtocolBugs{ - FragmentAcrossChangeCipherSpec: true, - }, - }, - shouldFail: true, - expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:", - }, - { - testType: serverTest, - name: "FragmentAcrossChangeCipherSpec-Server", - config: Config{ - Bugs: ProtocolBugs{ - FragmentAcrossChangeCipherSpec: true, - }, - }, - shouldFail: true, - expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:", - }, - { - testType: serverTest, - name: "FragmentAcrossChangeCipherSpec-Server-NPN", - config: Config{ - NextProtos: []string{"bar"}, - Bugs: ProtocolBugs{ - FragmentAcrossChangeCipherSpec: true, - }, - }, - flags: []string{ - "-advertise-npn", "\x03foo\x03bar\x03baz", - }, - shouldFail: true, - expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:", - }, - { - testType: serverTest, - name: "Alert", - config: Config{ - Bugs: ProtocolBugs{ - SendSpuriousAlert: alertRecordOverflow, - }, - }, - shouldFail: true, - expectedError: ":TLSV1_ALERT_RECORD_OVERFLOW:", - }, - { - protocol: dtls, - testType: serverTest, - name: "Alert-DTLS", - config: Config{ - Bugs: ProtocolBugs{ - SendSpuriousAlert: alertRecordOverflow, - }, - }, - shouldFail: true, - expectedError: ":TLSV1_ALERT_RECORD_OVERFLOW:", - }, - { - testType: serverTest, - name: "FragmentAlert", - config: Config{ - Bugs: ProtocolBugs{ - FragmentAlert: true, - SendSpuriousAlert: alertRecordOverflow, - }, - }, - shouldFail: true, - expectedError: ":BAD_ALERT:", - }, - { - protocol: dtls, - testType: serverTest, - name: "FragmentAlert-DTLS", - config: Config{ - Bugs: ProtocolBugs{ - FragmentAlert: true, - SendSpuriousAlert: alertRecordOverflow, - }, - }, - shouldFail: true, - expectedError: ":BAD_ALERT:", - }, - { - testType: serverTest, - name: "EarlyChangeCipherSpec-server-1", - config: Config{ - Bugs: ProtocolBugs{ - EarlyChangeCipherSpec: 1, - }, - }, - shouldFail: true, - expectedError: ":CCS_RECEIVED_EARLY:", - }, - { - testType: serverTest, - name: "EarlyChangeCipherSpec-server-2", - config: Config{ - Bugs: ProtocolBugs{ - EarlyChangeCipherSpec: 2, - }, - }, - shouldFail: true, - expectedError: ":CCS_RECEIVED_EARLY:", - }, - { - name: "SkipNewSessionTicket", - config: Config{ - Bugs: ProtocolBugs{ - SkipNewSessionTicket: true, - }, - }, - shouldFail: true, - expectedError: ":CCS_RECEIVED_EARLY:", - }, - { - testType: serverTest, - name: "FallbackSCSV", - config: Config{ - MaxVersion: VersionTLS11, - Bugs: ProtocolBugs{ - SendFallbackSCSV: true, - }, - }, - shouldFail: true, - expectedError: ":INAPPROPRIATE_FALLBACK:", - }, - { - testType: serverTest, - name: "FallbackSCSV-VersionMatch", - config: Config{ - Bugs: ProtocolBugs{ - SendFallbackSCSV: true, - }, - }, - }, - { - testType: serverTest, - name: "FragmentedClientVersion", - config: Config{ - Bugs: ProtocolBugs{ - MaxHandshakeRecordLength: 1, - FragmentClientVersion: true, - }, - }, - expectedVersion: VersionTLS12, - }, - { - testType: serverTest, - name: "MinorVersionTolerance", - config: Config{ - Bugs: ProtocolBugs{ - SendClientVersion: 0x03ff, - }, - }, - expectedVersion: VersionTLS12, - }, - { - testType: serverTest, - name: "MajorVersionTolerance", - config: Config{ - Bugs: ProtocolBugs{ - SendClientVersion: 0x0400, - }, - }, - expectedVersion: VersionTLS12, - }, - { - testType: serverTest, - name: "VersionTooLow", - config: Config{ - Bugs: ProtocolBugs{ - SendClientVersion: 0x0200, - }, - }, - shouldFail: true, - expectedError: ":UNSUPPORTED_PROTOCOL:", - }, - { - testType: serverTest, - name: "HttpGET", - sendPrefix: "GET / HTTP/1.0\n", - shouldFail: true, - expectedError: ":HTTP_REQUEST:", - }, - { - testType: serverTest, - name: "HttpPOST", - sendPrefix: "POST / HTTP/1.0\n", - shouldFail: true, - expectedError: ":HTTP_REQUEST:", - }, - { - testType: serverTest, - name: "HttpHEAD", - sendPrefix: "HEAD / HTTP/1.0\n", - shouldFail: true, - expectedError: ":HTTP_REQUEST:", - }, - { - testType: serverTest, - name: "HttpPUT", - sendPrefix: "PUT / HTTP/1.0\n", - shouldFail: true, - expectedError: ":HTTP_REQUEST:", - }, - { - testType: serverTest, - name: "HttpCONNECT", - sendPrefix: "CONNECT www.google.com:443 HTTP/1.0\n", - shouldFail: true, - expectedError: ":HTTPS_PROXY_REQUEST:", - }, - { - testType: serverTest, - name: "Garbage", - sendPrefix: "blah", - shouldFail: true, - expectedError: ":UNKNOWN_PROTOCOL:", - }, - { - name: "SkipCipherVersionCheck", - config: Config{ - CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256}, - MaxVersion: VersionTLS11, - Bugs: ProtocolBugs{ - SkipCipherVersionCheck: true, - }, - }, - shouldFail: true, - expectedError: ":WRONG_CIPHER_RETURNED:", - }, - { - name: "RSAEphemeralKey", - config: Config{ - CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA}, - Bugs: ProtocolBugs{ - RSAEphemeralKey: true, - }, - }, - shouldFail: true, - expectedError: ":UNEXPECTED_MESSAGE:", - }, - { - name: "DisableEverything", - flags: []string{"-no-tls12", "-no-tls11", "-no-tls1", "-no-ssl3"}, - shouldFail: true, - expectedError: ":WRONG_SSL_VERSION:", - }, - { - protocol: dtls, - name: "DisableEverything-DTLS", - flags: []string{"-no-tls12", "-no-tls1"}, - shouldFail: true, - expectedError: ":WRONG_SSL_VERSION:", - }, - { - name: "NoSharedCipher", - config: Config{ - CipherSuites: []uint16{}, - }, - shouldFail: true, - expectedError: ":HANDSHAKE_FAILURE_ON_CLIENT_HELLO:", - }, - { - protocol: dtls, - testType: serverTest, - name: "MTU", - config: Config{ - Bugs: ProtocolBugs{ - MaxPacketLength: 256, - }, - }, - flags: []string{"-mtu", "256"}, - }, - { - protocol: dtls, - testType: serverTest, - name: "MTUExceeded", - config: Config{ - Bugs: ProtocolBugs{ - MaxPacketLength: 255, - }, - }, - flags: []string{"-mtu", "256"}, - shouldFail: true, - expectedLocalError: "dtls: exceeded maximum packet length", - }, - { - name: "CertMismatchRSA", - config: Config{ - CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, - Certificates: []Certificate{getECDSACertificate()}, - Bugs: ProtocolBugs{ - SendCipherSuite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - }, - }, - shouldFail: true, - expectedError: ":WRONG_CERTIFICATE_TYPE:", - }, - { - name: "CertMismatchECDSA", - config: Config{ - CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, - Certificates: []Certificate{getRSACertificate()}, - Bugs: ProtocolBugs{ - SendCipherSuite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - }, - }, - shouldFail: true, - expectedError: ":WRONG_CERTIFICATE_TYPE:", - }, - { - name: "TLSFatalBadPackets", - damageFirstWrite: true, - shouldFail: true, - expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:", - }, - { - protocol: dtls, - name: "DTLSIgnoreBadPackets", - damageFirstWrite: true, - }, - { - protocol: dtls, - name: "DTLSIgnoreBadPackets-Async", - damageFirstWrite: true, - flags: []string{"-async"}, - }, - { - name: "AppDataAfterChangeCipherSpec", - config: Config{ - Bugs: ProtocolBugs{ - AppDataAfterChangeCipherSpec: []byte("TEST MESSAGE"), - }, - }, - shouldFail: true, - expectedError: ":DATA_BETWEEN_CCS_AND_FINISHED:", - }, - { - protocol: dtls, - name: "AppDataAfterChangeCipherSpec-DTLS", - config: Config{ - Bugs: ProtocolBugs{ - AppDataAfterChangeCipherSpec: []byte("TEST MESSAGE"), - }, - }, - // BoringSSL's DTLS implementation will drop the out-of-order - // application data. - }, - { - name: "AlertAfterChangeCipherSpec", - config: Config{ - Bugs: ProtocolBugs{ - AlertAfterChangeCipherSpec: alertRecordOverflow, - }, - }, - shouldFail: true, - expectedError: ":TLSV1_ALERT_RECORD_OVERFLOW:", - }, - { - protocol: dtls, - name: "AlertAfterChangeCipherSpec-DTLS", - config: Config{ - Bugs: ProtocolBugs{ - AlertAfterChangeCipherSpec: alertRecordOverflow, - }, - }, - shouldFail: true, - expectedError: ":TLSV1_ALERT_RECORD_OVERFLOW:", - }, - { - protocol: dtls, - name: "ReorderHandshakeFragments-Small-DTLS", - config: Config{ - Bugs: ProtocolBugs{ - ReorderHandshakeFragments: true, - // Small enough that every handshake message is - // fragmented. - MaxHandshakeRecordLength: 2, - }, - }, - }, - { - protocol: dtls, - name: "ReorderHandshakeFragments-Large-DTLS", - config: Config{ - Bugs: ProtocolBugs{ - ReorderHandshakeFragments: true, - // Large enough that no handshake message is - // fragmented. - MaxHandshakeRecordLength: 2048, - }, - }, - }, - { - protocol: dtls, - name: "MixCompleteMessageWithFragments-DTLS", - config: Config{ - Bugs: ProtocolBugs{ - ReorderHandshakeFragments: true, - MixCompleteMessageWithFragments: true, - MaxHandshakeRecordLength: 2, - }, - }, - }, - { - name: "SendInvalidRecordType", - config: Config{ - Bugs: ProtocolBugs{ - SendInvalidRecordType: true, - }, - }, - shouldFail: true, - expectedError: ":UNEXPECTED_RECORD:", - }, - { - protocol: dtls, - name: "SendInvalidRecordType-DTLS", - config: Config{ - Bugs: ProtocolBugs{ - SendInvalidRecordType: true, - }, - }, - shouldFail: true, - expectedError: ":UNEXPECTED_RECORD:", - }, - { - name: "FalseStart-SkipServerSecondLeg", - config: Config{ - CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, - NextProtos: []string{"foo"}, - Bugs: ProtocolBugs{ - SkipNewSessionTicket: true, - SkipChangeCipherSpec: true, - SkipFinished: true, - ExpectFalseStart: true, - }, - }, - flags: []string{ - "-false-start", - "-handshake-never-done", - "-advertise-alpn", "\x03foo", - }, - shimWritesFirst: true, - shouldFail: true, - expectedError: ":UNEXPECTED_RECORD:", - }, - { - name: "FalseStart-SkipServerSecondLeg-Implicit", - config: Config{ - CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, - NextProtos: []string{"foo"}, - Bugs: ProtocolBugs{ - SkipNewSessionTicket: true, - SkipChangeCipherSpec: true, - SkipFinished: true, - }, - }, - flags: []string{ - "-implicit-handshake", - "-false-start", - "-handshake-never-done", - "-advertise-alpn", "\x03foo", - }, - shouldFail: true, - expectedError: ":UNEXPECTED_RECORD:", - }, - { - testType: serverTest, - name: "FailEarlyCallback", - flags: []string{"-fail-early-callback"}, - shouldFail: true, - expectedError: ":CONNECTION_REJECTED:", - expectedLocalError: "remote error: access denied", - }, - { - name: "WrongMessageType", - config: Config{ - Bugs: ProtocolBugs{ - WrongCertificateMessageType: true, - }, - }, - shouldFail: true, - expectedError: ":UNEXPECTED_MESSAGE:", - expectedLocalError: "remote error: unexpected message", - }, - { - protocol: dtls, - name: "WrongMessageType-DTLS", - config: Config{ - Bugs: ProtocolBugs{ - WrongCertificateMessageType: true, - }, - }, - shouldFail: true, - expectedError: ":UNEXPECTED_MESSAGE:", - expectedLocalError: "remote error: unexpected message", - }, - { - protocol: dtls, - name: "FragmentMessageTypeMismatch-DTLS", - config: Config{ - Bugs: ProtocolBugs{ - MaxHandshakeRecordLength: 2, - FragmentMessageTypeMismatch: true, - }, - }, - shouldFail: true, - expectedError: ":FRAGMENT_MISMATCH:", - }, - { - protocol: dtls, - name: "FragmentMessageLengthMismatch-DTLS", - config: Config{ - Bugs: ProtocolBugs{ - MaxHandshakeRecordLength: 2, - FragmentMessageLengthMismatch: true, - }, - }, - shouldFail: true, - expectedError: ":FRAGMENT_MISMATCH:", - }, - { - protocol: dtls, - name: "SplitFragmentHeader-DTLS", - config: Config{ - Bugs: ProtocolBugs{ - SplitFragmentHeader: true, - }, - }, - shouldFail: true, - expectedError: ":UNEXPECTED_MESSAGE:", - }, - { - protocol: dtls, - name: "SplitFragmentBody-DTLS", - config: Config{ - Bugs: ProtocolBugs{ - SplitFragmentBody: true, - }, - }, - shouldFail: true, - expectedError: ":UNEXPECTED_MESSAGE:", - }, - { - protocol: dtls, - name: "SendEmptyFragments-DTLS", - config: Config{ - Bugs: ProtocolBugs{ - SendEmptyFragments: true, - }, - }, - }, - { - name: "UnsupportedCipherSuite", - config: Config{ - CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA}, - Bugs: ProtocolBugs{ - IgnorePeerCipherPreferences: true, - }, - }, - flags: []string{"-cipher", "DEFAULT:!RC4"}, - shouldFail: true, - expectedError: ":WRONG_CIPHER_RETURNED:", - }, - { - name: "UnsupportedCurve", - config: Config{ - CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, - // BoringSSL implements P-224 but doesn't enable it by - // default. - CurvePreferences: []CurveID{CurveP224}, - Bugs: ProtocolBugs{ - IgnorePeerCurvePreferences: true, - }, - }, - shouldFail: true, - expectedError: ":WRONG_CURVE:", - }, - { - name: "SendWarningAlerts", - config: Config{ - Bugs: ProtocolBugs{ - SendWarningAlerts: alertAccessDenied, - }, - }, - }, - { - protocol: dtls, - name: "SendWarningAlerts-DTLS", - config: Config{ - Bugs: ProtocolBugs{ - SendWarningAlerts: alertAccessDenied, - }, - }, - }, - { - name: "BadFinished", - config: Config{ - Bugs: ProtocolBugs{ - BadFinished: true, - }, - }, - shouldFail: true, - expectedError: ":DIGEST_CHECK_FAILED:", - }, - { - name: "FalseStart-BadFinished", - config: Config{ - CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, - NextProtos: []string{"foo"}, - Bugs: ProtocolBugs{ - BadFinished: true, - ExpectFalseStart: true, - }, - }, - flags: []string{ - "-false-start", - "-handshake-never-done", - "-advertise-alpn", "\x03foo", - }, - shimWritesFirst: true, - shouldFail: true, - expectedError: ":DIGEST_CHECK_FAILED:", - }, - { - name: "NoFalseStart-NoALPN", - config: Config{ - CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, - Bugs: ProtocolBugs{ - ExpectFalseStart: true, - AlertBeforeFalseStartTest: alertAccessDenied, - }, - }, - flags: []string{ - "-false-start", - }, - shimWritesFirst: true, - shouldFail: true, - expectedError: ":TLSV1_ALERT_ACCESS_DENIED:", - expectedLocalError: "tls: peer did not false start: EOF", - }, - { - name: "NoFalseStart-NoAEAD", - config: Config{ - CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}, - NextProtos: []string{"foo"}, - Bugs: ProtocolBugs{ - ExpectFalseStart: true, - AlertBeforeFalseStartTest: alertAccessDenied, - }, - }, - flags: []string{ - "-false-start", - "-advertise-alpn", "\x03foo", - }, - shimWritesFirst: true, - shouldFail: true, - expectedError: ":TLSV1_ALERT_ACCESS_DENIED:", - expectedLocalError: "tls: peer did not false start: EOF", - }, - { - name: "NoFalseStart-RSA", - config: Config{ - CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256}, - NextProtos: []string{"foo"}, - Bugs: ProtocolBugs{ - ExpectFalseStart: true, - AlertBeforeFalseStartTest: alertAccessDenied, - }, - }, - flags: []string{ - "-false-start", - "-advertise-alpn", "\x03foo", - }, - shimWritesFirst: true, - shouldFail: true, - expectedError: ":TLSV1_ALERT_ACCESS_DENIED:", - expectedLocalError: "tls: peer did not false start: EOF", - }, - { - name: "NoFalseStart-DHE_RSA", - config: Config{ - CipherSuites: []uint16{TLS_DHE_RSA_WITH_AES_128_GCM_SHA256}, - NextProtos: []string{"foo"}, - Bugs: ProtocolBugs{ - ExpectFalseStart: true, - AlertBeforeFalseStartTest: alertAccessDenied, - }, - }, - flags: []string{ - "-false-start", - "-advertise-alpn", "\x03foo", - }, - shimWritesFirst: true, - shouldFail: true, - expectedError: ":TLSV1_ALERT_ACCESS_DENIED:", - expectedLocalError: "tls: peer did not false start: EOF", - }, - { - testType: serverTest, - name: "NoSupportedCurves", - config: Config{ - CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, - Bugs: ProtocolBugs{ - NoSupportedCurves: true, - }, - }, - }, - { - testType: serverTest, - name: "NoCommonCurves", - config: Config{ - CipherSuites: []uint16{ - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, - }, - CurvePreferences: []CurveID{CurveP224}, - }, - expectedCipher: TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, - }, - { - protocol: dtls, - name: "SendSplitAlert-Sync", - config: Config{ - Bugs: ProtocolBugs{ - SendSplitAlert: true, - }, - }, - }, - { - protocol: dtls, - name: "SendSplitAlert-Async", - config: Config{ - Bugs: ProtocolBugs{ - SendSplitAlert: true, - }, - }, - flags: []string{"-async"}, - }, - { - protocol: dtls, - name: "PackDTLSHandshake", - config: Config{ - Bugs: ProtocolBugs{ - MaxHandshakeRecordLength: 2, - PackHandshakeFragments: 20, - PackHandshakeRecords: 200, - }, - }, - }, - { - testType: serverTest, - protocol: dtls, - name: "NoRC4-DTLS", - config: Config{ - CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_RC4_128_SHA}, - Bugs: ProtocolBugs{ - EnableAllCiphersInDTLS: true, - }, - }, - shouldFail: true, - expectedError: ":NO_SHARED_CIPHER:", - }, -} +var testCases []testCase -func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int, isResume bool) error { +func doExchange(test *testCase, config *Config, conn net.Conn, isResume bool) error { var connDebug *recordingConn var connDamage *damageAdaptor if *flagDebug { @@ -1183,6 +285,7 @@ func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int, i tlsConn = Client(conn, config) } } + defer tlsConn.Close() if err := tlsConn.Handshake(); err != nil { return err @@ -1235,6 +338,18 @@ func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int, i return fmt.Errorf("SRTP profile mismatch: got %d, wanted %d", p, test.expectedSRTPProtectionProfile) } + if test.expectedOCSPResponse != nil && !bytes.Equal(test.expectedOCSPResponse, tlsConn.OCSPResponse()) { + return fmt.Errorf("OCSP Response mismatch") + } + + if test.expectedSCTList != nil && !bytes.Equal(test.expectedSCTList, connState.SCTList) { + return fmt.Errorf("SCT list mismatch") + } + + if expected := test.expectedClientCertSignatureHash; expected != 0 && expected != connState.ClientCertSignatureHash { + return fmt.Errorf("expected client to sign handshake with hash %d, but got %d", expected, connState.ClientCertSignatureHash) + } + if test.exportKeyingMaterial > 0 { actual := make([]byte, test.exportKeyingMaterial) if _, err := io.ReadFull(tlsConn, actual); err != nil { @@ -1271,6 +386,14 @@ func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int, i } } + for i := 0; i < test.sendEmptyRecords; i++ { + tlsConn.Write(nil) + } + + for i := 0; i < test.sendWarningAlerts; i++ { + tlsConn.SendAlert(alertLevelWarning, alertUnexpectedMessage) + } + if test.renegotiate { if test.renegotiateCiphers != nil { config.CipherSuites = test.renegotiateCiphers @@ -1288,6 +411,7 @@ func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int, i connDamage.setDamage(false) } + messageLen := test.messageLen if messageLen < 0 { if test.protocol == dtls { return fmt.Errorf("messageLen < 0 not supported for DTLS tests") @@ -1296,37 +420,57 @@ func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int, i _, err := io.Copy(ioutil.Discard, tlsConn) return err } - if messageLen == 0 { messageLen = 32 } - testMessage := make([]byte, messageLen) - for i := range testMessage { - testMessage[i] = 0x42 + + messageCount := test.messageCount + if messageCount == 0 { + messageCount = 1 } - tlsConn.Write(testMessage) - buf := make([]byte, len(testMessage)) - if test.protocol == dtls { - bufTmp := make([]byte, len(buf)+1) - n, err := tlsConn.Read(bufTmp) - if err != nil { - return err + for j := 0; j < messageCount; j++ { + testMessage := make([]byte, messageLen) + for i := range testMessage { + testMessage[i] = 0x42 ^ byte(j) } - if n != len(buf) { - return fmt.Errorf("bad reply; length mismatch (%d vs %d)", n, len(buf)) + tlsConn.Write(testMessage) + + for i := 0; i < test.sendEmptyRecords; i++ { + tlsConn.Write(nil) } - copy(buf, bufTmp) - } else { - _, err := io.ReadFull(tlsConn, buf) - if err != nil { - return err + + for i := 0; i < test.sendWarningAlerts; i++ { + tlsConn.SendAlert(alertLevelWarning, alertUnexpectedMessage) + } + + if test.shimShutsDown || test.expectMessageDropped { + // The shim will not respond. + continue + } + + buf := make([]byte, len(testMessage)) + if test.protocol == dtls { + bufTmp := make([]byte, len(buf)+1) + n, err := tlsConn.Read(bufTmp) + if err != nil { + return err + } + if n != len(buf) { + return fmt.Errorf("bad reply; length mismatch (%d vs %d)", n, len(buf)) + } + copy(buf, bufTmp) + } else { + _, err := io.ReadFull(tlsConn, buf) + if err != nil { + return err + } } - } - for i, v := range buf { - if v != testMessage[i]^0xff { - return fmt.Errorf("bad reply contents at byte %d", i) + for i, v := range buf { + if v != testMessage[i]^0xff { + return fmt.Errorf("bad reply contents at byte %d", i) + } } } @@ -1382,7 +526,7 @@ func acceptOrWait(listener net.Listener, waitChan chan error) (net.Conn, error) } } -func runTest(test *testCase, buildDir string, mallocNumToFail int64) error { +func runTest(test *testCase, shimPath string, mallocNumToFail int64) error { if !test.shouldFail && (len(test.expectedError) > 0 || len(test.expectedLocalError) > 0) { panic("Error expected without shouldFail in " + test.name) } @@ -1391,6 +535,10 @@ func runTest(test *testCase, buildDir string, mallocNumToFail int64) error { panic("expectResumeRejected without resumeSession in " + test.name) } + if test.testType != clientTest && test.expectedClientCertSignatureHash != 0 { + panic("expectedClientCertSignatureHash non-zero with serverTest in " + test.name) + } + listener, err := net.ListenTCP("tcp4", &net.TCPAddr{IP: net.IP{127, 0, 0, 1}}) if err != nil { panic(err) @@ -1401,26 +549,30 @@ func runTest(test *testCase, buildDir string, mallocNumToFail int64) error { } }() - shim_path := path.Join(buildDir, "ssl/test/bssl_shim") flags := []string{"-port", strconv.Itoa(listener.Addr().(*net.TCPAddr).Port)} if test.testType == serverTest { flags = append(flags, "-server") flags = append(flags, "-key-file") if test.keyFile == "" { - flags = append(flags, rsaKeyFile) + flags = append(flags, path.Join(*resourceDir, rsaKeyFile)) } else { - flags = append(flags, test.keyFile) + flags = append(flags, path.Join(*resourceDir, test.keyFile)) } flags = append(flags, "-cert-file") if test.certFile == "" { - flags = append(flags, rsaCertificateFile) + flags = append(flags, path.Join(*resourceDir, rsaCertificateFile)) } else { - flags = append(flags, test.certFile) + flags = append(flags, path.Join(*resourceDir, test.certFile)) } } + if test.digestPrefs != "" { + flags = append(flags, "-digest-prefs") + flags = append(flags, test.digestPrefs) + } + if test.protocol == dtls { flags = append(flags, "-dtls") } @@ -1433,6 +585,10 @@ func runTest(test *testCase, buildDir string, mallocNumToFail int64) error { flags = append(flags, "-shim-writes-first") } + if test.shimShutsDown { + flags = append(flags, "-shim-shuts-down") + } + if test.exportKeyingMaterial > 0 { flags = append(flags, "-export-keying-material", strconv.Itoa(test.exportKeyingMaterial)) flags = append(flags, "-export-label", test.exportLabel) @@ -1453,11 +609,11 @@ func runTest(test *testCase, buildDir string, mallocNumToFail int64) error { var shim *exec.Cmd if *useValgrind { - shim = valgrindOf(false, shim_path, flags...) + shim = valgrindOf(false, shimPath, flags...) } else if *useGDB { - shim = gdbOf(shim_path, flags...) + shim = gdbOf(shimPath, flags...) } else { - shim = exec.Command(shim_path, flags...) + shim = exec.Command(shimPath, flags...) } shim.Stdin = os.Stdin var stdoutBuf, stderrBuf bytes.Buffer @@ -1467,7 +623,7 @@ func runTest(test *testCase, buildDir string, mallocNumToFail int64) error { shim.Env = os.Environ() shim.Env = append(shim.Env, "MALLOC_NUMBER_TO_FAIL="+strconv.FormatInt(mallocNumToFail, 10)) if *mallocTestDebug { - shim.Env = append(shim.Env, "MALLOC_ABORT_ON_FAIL=1") + shim.Env = append(shim.Env, "MALLOC_BREAK_ON_FAIL=1") } shim.Env = append(shim.Env, "_MALLOC_CHECK=1") } @@ -1479,8 +635,10 @@ func runTest(test *testCase, buildDir string, mallocNumToFail int64) error { go func() { waitChan <- shim.Wait() }() config := test.config - config.ClientSessionCache = NewLRUClientSessionCache(1) - config.ServerSessionCache = NewLRUServerSessionCache(1) + if !test.noSessionCache { + config.ClientSessionCache = NewLRUClientSessionCache(1) + config.ServerSessionCache = NewLRUServerSessionCache(1) + } if test.testType == clientTest { if len(config.Certificates) == 0 { config.Certificates = []Certificate{getRSACertificate()} @@ -1495,7 +653,7 @@ func runTest(test *testCase, buildDir string, mallocNumToFail int64) error { conn, err := acceptOrWait(listener, waitChan) if err == nil { - err = doExchange(test, &config, conn, test.messageLen, false /* not a resumption */) + err = doExchange(test, &config, conn, false /* not a resumption */) conn.Close() } @@ -1509,7 +667,12 @@ func runTest(test *testCase, buildDir string, mallocNumToFail int64) error { if len(resumeConfig.Certificates) == 0 { resumeConfig.Certificates = []Certificate{getRSACertificate()} } - if !test.newSessionsOnResume { + if test.newSessionsOnResume { + if !test.noSessionCache { + resumeConfig.ClientSessionCache = NewLRUClientSessionCache(1) + resumeConfig.ServerSessionCache = NewLRUServerSessionCache(1) + } + } else { resumeConfig.SessionTicketKey = config.SessionTicketKey resumeConfig.ClientSessionCache = config.ClientSessionCache resumeConfig.ServerSessionCache = config.ServerSessionCache @@ -1520,7 +683,7 @@ func runTest(test *testCase, buildDir string, mallocNumToFail int64) error { var connResume net.Conn connResume, err = acceptOrWait(listener, waitChan) if err == nil { - err = doExchange(test, &resumeConfig, connResume, test.messageLen, true /* resumption */) + err = doExchange(test, &resumeConfig, connResume, true /* resumption */) connResume.Close() } } @@ -1606,7 +769,6 @@ var testCipherSuites = []struct { {"DHE-RSA-AES256-GCM", TLS_DHE_RSA_WITH_AES_256_GCM_SHA384}, {"DHE-RSA-AES256-SHA", TLS_DHE_RSA_WITH_AES_256_CBC_SHA}, {"DHE-RSA-AES256-SHA256", TLS_DHE_RSA_WITH_AES_256_CBC_SHA256}, - {"DHE-RSA-CHACHA20-POLY1305", TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256}, {"ECDHE-ECDSA-AES128-GCM", TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, {"ECDHE-ECDSA-AES128-SHA", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA}, {"ECDHE-ECDSA-AES128-SHA256", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256}, @@ -1630,6 +792,7 @@ var testCipherSuites = []struct { {"PSK-RC4-SHA", TLS_PSK_WITH_RC4_128_SHA}, {"RC4-MD5", TLS_RSA_WITH_RC4_128_MD5}, {"RC4-SHA", TLS_RSA_WITH_RC4_128_SHA}, + {"NULL-SHA", TLS_RSA_WITH_NULL_SHA}, } func hasComponent(suiteName, component string) bool { @@ -1644,7 +807,7 @@ func isTLS12Only(suiteName string) bool { } func isDTLSCipher(suiteName string) bool { - return !hasComponent(suiteName, "RC4") + return !hasComponent(suiteName, "RC4") && !hasComponent(suiteName, "NULL") } func bigFromHex(hex string) *big.Int { @@ -1655,6 +818,1152 @@ func bigFromHex(hex string) *big.Int { return ret } +func addBasicTests() { + basicTests := []testCase{ + { + name: "BadRSASignature", + config: Config{ + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + InvalidSKXSignature: true, + }, + }, + shouldFail: true, + expectedError: ":BAD_SIGNATURE:", + }, + { + name: "BadECDSASignature", + config: Config{ + CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + InvalidSKXSignature: true, + }, + Certificates: []Certificate{getECDSACertificate()}, + }, + shouldFail: true, + expectedError: ":BAD_SIGNATURE:", + }, + { + testType: serverTest, + name: "BadRSASignature-ClientAuth", + config: Config{ + Bugs: ProtocolBugs{ + InvalidCertVerifySignature: true, + }, + Certificates: []Certificate{getRSACertificate()}, + }, + shouldFail: true, + expectedError: ":BAD_SIGNATURE:", + flags: []string{"-require-any-client-certificate"}, + }, + { + testType: serverTest, + name: "BadECDSASignature-ClientAuth", + config: Config{ + Bugs: ProtocolBugs{ + InvalidCertVerifySignature: true, + }, + Certificates: []Certificate{getECDSACertificate()}, + }, + shouldFail: true, + expectedError: ":BAD_SIGNATURE:", + flags: []string{"-require-any-client-certificate"}, + }, + { + name: "BadECDSACurve", + config: Config{ + CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + InvalidSKXCurve: true, + }, + Certificates: []Certificate{getECDSACertificate()}, + }, + shouldFail: true, + expectedError: ":WRONG_CURVE:", + }, + { + testType: serverTest, + name: "BadRSAVersion", + config: Config{ + CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA}, + Bugs: ProtocolBugs{ + RsaClientKeyExchangeVersion: VersionTLS11, + }, + }, + shouldFail: true, + expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:", + }, + { + name: "NoFallbackSCSV", + config: Config{ + Bugs: ProtocolBugs{ + FailIfNotFallbackSCSV: true, + }, + }, + shouldFail: true, + expectedLocalError: "no fallback SCSV found", + }, + { + name: "SendFallbackSCSV", + config: Config{ + Bugs: ProtocolBugs{ + FailIfNotFallbackSCSV: true, + }, + }, + flags: []string{"-fallback-scsv"}, + }, + { + name: "ClientCertificateTypes", + config: Config{ + ClientAuth: RequestClientCert, + ClientCertificateTypes: []byte{ + CertTypeDSSSign, + CertTypeRSASign, + CertTypeECDSASign, + }, + }, + flags: []string{ + "-expect-certificate-types", + base64.StdEncoding.EncodeToString([]byte{ + CertTypeDSSSign, + CertTypeRSASign, + CertTypeECDSASign, + }), + }, + }, + { + name: "NoClientCertificate", + config: Config{ + ClientAuth: RequireAnyClientCert, + }, + shouldFail: true, + expectedLocalError: "client didn't provide a certificate", + }, + { + name: "UnauthenticatedECDH", + config: Config{ + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + UnauthenticatedECDH: true, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_MESSAGE:", + }, + { + name: "SkipCertificateStatus", + config: Config{ + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + SkipCertificateStatus: true, + }, + }, + flags: []string{ + "-enable-ocsp-stapling", + }, + }, + { + name: "SkipServerKeyExchange", + config: Config{ + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + SkipServerKeyExchange: true, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_MESSAGE:", + }, + { + name: "SkipChangeCipherSpec-Client", + config: Config{ + Bugs: ProtocolBugs{ + SkipChangeCipherSpec: true, + }, + }, + shouldFail: true, + expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:", + }, + { + testType: serverTest, + name: "SkipChangeCipherSpec-Server", + config: Config{ + Bugs: ProtocolBugs{ + SkipChangeCipherSpec: true, + }, + }, + shouldFail: true, + expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:", + }, + { + testType: serverTest, + name: "SkipChangeCipherSpec-Server-NPN", + config: Config{ + NextProtos: []string{"bar"}, + Bugs: ProtocolBugs{ + SkipChangeCipherSpec: true, + }, + }, + flags: []string{ + "-advertise-npn", "\x03foo\x03bar\x03baz", + }, + shouldFail: true, + expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:", + }, + { + name: "FragmentAcrossChangeCipherSpec-Client", + config: Config{ + Bugs: ProtocolBugs{ + FragmentAcrossChangeCipherSpec: true, + }, + }, + shouldFail: true, + expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:", + }, + { + testType: serverTest, + name: "FragmentAcrossChangeCipherSpec-Server", + config: Config{ + Bugs: ProtocolBugs{ + FragmentAcrossChangeCipherSpec: true, + }, + }, + shouldFail: true, + expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:", + }, + { + testType: serverTest, + name: "FragmentAcrossChangeCipherSpec-Server-NPN", + config: Config{ + NextProtos: []string{"bar"}, + Bugs: ProtocolBugs{ + FragmentAcrossChangeCipherSpec: true, + }, + }, + flags: []string{ + "-advertise-npn", "\x03foo\x03bar\x03baz", + }, + shouldFail: true, + expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:", + }, + { + testType: serverTest, + name: "Alert", + config: Config{ + Bugs: ProtocolBugs{ + SendSpuriousAlert: alertRecordOverflow, + }, + }, + shouldFail: true, + expectedError: ":TLSV1_ALERT_RECORD_OVERFLOW:", + }, + { + protocol: dtls, + testType: serverTest, + name: "Alert-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + SendSpuriousAlert: alertRecordOverflow, + }, + }, + shouldFail: true, + expectedError: ":TLSV1_ALERT_RECORD_OVERFLOW:", + }, + { + testType: serverTest, + name: "FragmentAlert", + config: Config{ + Bugs: ProtocolBugs{ + FragmentAlert: true, + SendSpuriousAlert: alertRecordOverflow, + }, + }, + shouldFail: true, + expectedError: ":BAD_ALERT:", + }, + { + protocol: dtls, + testType: serverTest, + name: "FragmentAlert-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + FragmentAlert: true, + SendSpuriousAlert: alertRecordOverflow, + }, + }, + shouldFail: true, + expectedError: ":BAD_ALERT:", + }, + { + testType: serverTest, + name: "EarlyChangeCipherSpec-server-1", + config: Config{ + Bugs: ProtocolBugs{ + EarlyChangeCipherSpec: 1, + }, + }, + shouldFail: true, + expectedError: ":CCS_RECEIVED_EARLY:", + }, + { + testType: serverTest, + name: "EarlyChangeCipherSpec-server-2", + config: Config{ + Bugs: ProtocolBugs{ + EarlyChangeCipherSpec: 2, + }, + }, + shouldFail: true, + expectedError: ":CCS_RECEIVED_EARLY:", + }, + { + name: "SkipNewSessionTicket", + config: Config{ + Bugs: ProtocolBugs{ + SkipNewSessionTicket: true, + }, + }, + shouldFail: true, + expectedError: ":CCS_RECEIVED_EARLY:", + }, + { + testType: serverTest, + name: "FallbackSCSV", + config: Config{ + MaxVersion: VersionTLS11, + Bugs: ProtocolBugs{ + SendFallbackSCSV: true, + }, + }, + shouldFail: true, + expectedError: ":INAPPROPRIATE_FALLBACK:", + }, + { + testType: serverTest, + name: "FallbackSCSV-VersionMatch", + config: Config{ + Bugs: ProtocolBugs{ + SendFallbackSCSV: true, + }, + }, + }, + { + testType: serverTest, + name: "FragmentedClientVersion", + config: Config{ + Bugs: ProtocolBugs{ + MaxHandshakeRecordLength: 1, + FragmentClientVersion: true, + }, + }, + expectedVersion: VersionTLS12, + }, + { + testType: serverTest, + name: "MinorVersionTolerance", + config: Config{ + Bugs: ProtocolBugs{ + SendClientVersion: 0x03ff, + }, + }, + expectedVersion: VersionTLS12, + }, + { + testType: serverTest, + name: "MajorVersionTolerance", + config: Config{ + Bugs: ProtocolBugs{ + SendClientVersion: 0x0400, + }, + }, + expectedVersion: VersionTLS12, + }, + { + testType: serverTest, + name: "VersionTooLow", + config: Config{ + Bugs: ProtocolBugs{ + SendClientVersion: 0x0200, + }, + }, + shouldFail: true, + expectedError: ":UNSUPPORTED_PROTOCOL:", + }, + { + testType: serverTest, + name: "HttpGET", + sendPrefix: "GET / HTTP/1.0\n", + shouldFail: true, + expectedError: ":HTTP_REQUEST:", + }, + { + testType: serverTest, + name: "HttpPOST", + sendPrefix: "POST / HTTP/1.0\n", + shouldFail: true, + expectedError: ":HTTP_REQUEST:", + }, + { + testType: serverTest, + name: "HttpHEAD", + sendPrefix: "HEAD / HTTP/1.0\n", + shouldFail: true, + expectedError: ":HTTP_REQUEST:", + }, + { + testType: serverTest, + name: "HttpPUT", + sendPrefix: "PUT / HTTP/1.0\n", + shouldFail: true, + expectedError: ":HTTP_REQUEST:", + }, + { + testType: serverTest, + name: "HttpCONNECT", + sendPrefix: "CONNECT www.google.com:443 HTTP/1.0\n", + shouldFail: true, + expectedError: ":HTTPS_PROXY_REQUEST:", + }, + { + testType: serverTest, + name: "Garbage", + sendPrefix: "blah", + shouldFail: true, + expectedError: ":WRONG_VERSION_NUMBER:", + }, + { + name: "SkipCipherVersionCheck", + config: Config{ + CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256}, + MaxVersion: VersionTLS11, + Bugs: ProtocolBugs{ + SkipCipherVersionCheck: true, + }, + }, + shouldFail: true, + expectedError: ":WRONG_CIPHER_RETURNED:", + }, + { + name: "RSAEphemeralKey", + config: Config{ + CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA}, + Bugs: ProtocolBugs{ + RSAEphemeralKey: true, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_MESSAGE:", + }, + { + name: "DisableEverything", + flags: []string{"-no-tls12", "-no-tls11", "-no-tls1", "-no-ssl3"}, + shouldFail: true, + expectedError: ":WRONG_SSL_VERSION:", + }, + { + protocol: dtls, + name: "DisableEverything-DTLS", + flags: []string{"-no-tls12", "-no-tls1"}, + shouldFail: true, + expectedError: ":WRONG_SSL_VERSION:", + }, + { + name: "NoSharedCipher", + config: Config{ + CipherSuites: []uint16{}, + }, + shouldFail: true, + expectedError: ":HANDSHAKE_FAILURE_ON_CLIENT_HELLO:", + }, + { + protocol: dtls, + testType: serverTest, + name: "MTU", + config: Config{ + Bugs: ProtocolBugs{ + MaxPacketLength: 256, + }, + }, + flags: []string{"-mtu", "256"}, + }, + { + protocol: dtls, + testType: serverTest, + name: "MTUExceeded", + config: Config{ + Bugs: ProtocolBugs{ + MaxPacketLength: 255, + }, + }, + flags: []string{"-mtu", "256"}, + shouldFail: true, + expectedLocalError: "dtls: exceeded maximum packet length", + }, + { + name: "CertMismatchRSA", + config: Config{ + CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + Certificates: []Certificate{getECDSACertificate()}, + Bugs: ProtocolBugs{ + SendCipherSuite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + }, + }, + shouldFail: true, + expectedError: ":WRONG_CERTIFICATE_TYPE:", + }, + { + name: "CertMismatchECDSA", + config: Config{ + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + Certificates: []Certificate{getRSACertificate()}, + Bugs: ProtocolBugs{ + SendCipherSuite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + }, + }, + shouldFail: true, + expectedError: ":WRONG_CERTIFICATE_TYPE:", + }, + { + name: "EmptyCertificateList", + config: Config{ + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + EmptyCertificateList: true, + }, + }, + shouldFail: true, + expectedError: ":DECODE_ERROR:", + }, + { + name: "TLSFatalBadPackets", + damageFirstWrite: true, + shouldFail: true, + expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:", + }, + { + protocol: dtls, + name: "DTLSIgnoreBadPackets", + damageFirstWrite: true, + }, + { + protocol: dtls, + name: "DTLSIgnoreBadPackets-Async", + damageFirstWrite: true, + flags: []string{"-async"}, + }, + { + name: "AppDataBeforeHandshake", + config: Config{ + Bugs: ProtocolBugs{ + AppDataBeforeHandshake: []byte("TEST MESSAGE"), + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + }, + { + name: "AppDataBeforeHandshake-Empty", + config: Config{ + Bugs: ProtocolBugs{ + AppDataBeforeHandshake: []byte{}, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + }, + { + protocol: dtls, + name: "AppDataBeforeHandshake-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + AppDataBeforeHandshake: []byte("TEST MESSAGE"), + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + }, + { + protocol: dtls, + name: "AppDataBeforeHandshake-DTLS-Empty", + config: Config{ + Bugs: ProtocolBugs{ + AppDataBeforeHandshake: []byte{}, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + }, + { + name: "AppDataAfterChangeCipherSpec", + config: Config{ + Bugs: ProtocolBugs{ + AppDataAfterChangeCipherSpec: []byte("TEST MESSAGE"), + }, + }, + shouldFail: true, + expectedError: ":DATA_BETWEEN_CCS_AND_FINISHED:", + }, + { + name: "AppDataAfterChangeCipherSpec-Empty", + config: Config{ + Bugs: ProtocolBugs{ + AppDataAfterChangeCipherSpec: []byte{}, + }, + }, + shouldFail: true, + expectedError: ":DATA_BETWEEN_CCS_AND_FINISHED:", + }, + { + protocol: dtls, + name: "AppDataAfterChangeCipherSpec-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + AppDataAfterChangeCipherSpec: []byte("TEST MESSAGE"), + }, + }, + // BoringSSL's DTLS implementation will drop the out-of-order + // application data. + }, + { + protocol: dtls, + name: "AppDataAfterChangeCipherSpec-DTLS-Empty", + config: Config{ + Bugs: ProtocolBugs{ + AppDataAfterChangeCipherSpec: []byte{}, + }, + }, + // BoringSSL's DTLS implementation will drop the out-of-order + // application data. + }, + { + name: "AlertAfterChangeCipherSpec", + config: Config{ + Bugs: ProtocolBugs{ + AlertAfterChangeCipherSpec: alertRecordOverflow, + }, + }, + shouldFail: true, + expectedError: ":TLSV1_ALERT_RECORD_OVERFLOW:", + }, + { + protocol: dtls, + name: "AlertAfterChangeCipherSpec-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + AlertAfterChangeCipherSpec: alertRecordOverflow, + }, + }, + shouldFail: true, + expectedError: ":TLSV1_ALERT_RECORD_OVERFLOW:", + }, + { + protocol: dtls, + name: "ReorderHandshakeFragments-Small-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + ReorderHandshakeFragments: true, + // Small enough that every handshake message is + // fragmented. + MaxHandshakeRecordLength: 2, + }, + }, + }, + { + protocol: dtls, + name: "ReorderHandshakeFragments-Large-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + ReorderHandshakeFragments: true, + // Large enough that no handshake message is + // fragmented. + MaxHandshakeRecordLength: 2048, + }, + }, + }, + { + protocol: dtls, + name: "MixCompleteMessageWithFragments-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + ReorderHandshakeFragments: true, + MixCompleteMessageWithFragments: true, + MaxHandshakeRecordLength: 2, + }, + }, + }, + { + name: "SendInvalidRecordType", + config: Config{ + Bugs: ProtocolBugs{ + SendInvalidRecordType: true, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + }, + { + protocol: dtls, + name: "SendInvalidRecordType-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + SendInvalidRecordType: true, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + }, + { + name: "FalseStart-SkipServerSecondLeg", + config: Config{ + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + NextProtos: []string{"foo"}, + Bugs: ProtocolBugs{ + SkipNewSessionTicket: true, + SkipChangeCipherSpec: true, + SkipFinished: true, + ExpectFalseStart: true, + }, + }, + flags: []string{ + "-false-start", + "-handshake-never-done", + "-advertise-alpn", "\x03foo", + }, + shimWritesFirst: true, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + }, + { + name: "FalseStart-SkipServerSecondLeg-Implicit", + config: Config{ + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + NextProtos: []string{"foo"}, + Bugs: ProtocolBugs{ + SkipNewSessionTicket: true, + SkipChangeCipherSpec: true, + SkipFinished: true, + }, + }, + flags: []string{ + "-implicit-handshake", + "-false-start", + "-handshake-never-done", + "-advertise-alpn", "\x03foo", + }, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + }, + { + testType: serverTest, + name: "FailEarlyCallback", + flags: []string{"-fail-early-callback"}, + shouldFail: true, + expectedError: ":CONNECTION_REJECTED:", + expectedLocalError: "remote error: access denied", + }, + { + name: "WrongMessageType", + config: Config{ + Bugs: ProtocolBugs{ + WrongCertificateMessageType: true, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_MESSAGE:", + expectedLocalError: "remote error: unexpected message", + }, + { + protocol: dtls, + name: "WrongMessageType-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + WrongCertificateMessageType: true, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_MESSAGE:", + expectedLocalError: "remote error: unexpected message", + }, + { + protocol: dtls, + name: "FragmentMessageTypeMismatch-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + MaxHandshakeRecordLength: 2, + FragmentMessageTypeMismatch: true, + }, + }, + shouldFail: true, + expectedError: ":FRAGMENT_MISMATCH:", + }, + { + protocol: dtls, + name: "FragmentMessageLengthMismatch-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + MaxHandshakeRecordLength: 2, + FragmentMessageLengthMismatch: true, + }, + }, + shouldFail: true, + expectedError: ":FRAGMENT_MISMATCH:", + }, + { + protocol: dtls, + name: "SplitFragments-Header-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + SplitFragments: 2, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_MESSAGE:", + }, + { + protocol: dtls, + name: "SplitFragments-Boundary-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + SplitFragments: dtlsRecordHeaderLen, + }, + }, + shouldFail: true, + expectedError: ":EXCESSIVE_MESSAGE_SIZE:", + }, + { + protocol: dtls, + name: "SplitFragments-Body-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + SplitFragments: dtlsRecordHeaderLen + 1, + }, + }, + shouldFail: true, + expectedError: ":EXCESSIVE_MESSAGE_SIZE:", + }, + { + protocol: dtls, + name: "SendEmptyFragments-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + SendEmptyFragments: true, + }, + }, + }, + { + name: "UnsupportedCipherSuite", + config: Config{ + CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA}, + Bugs: ProtocolBugs{ + IgnorePeerCipherPreferences: true, + }, + }, + flags: []string{"-cipher", "DEFAULT:!RC4"}, + shouldFail: true, + expectedError: ":WRONG_CIPHER_RETURNED:", + }, + { + name: "UnsupportedCurve", + config: Config{ + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + // BoringSSL implements P-224 but doesn't enable it by + // default. + CurvePreferences: []CurveID{CurveP224}, + Bugs: ProtocolBugs{ + IgnorePeerCurvePreferences: true, + }, + }, + shouldFail: true, + expectedError: ":WRONG_CURVE:", + }, + { + name: "BadFinished", + config: Config{ + Bugs: ProtocolBugs{ + BadFinished: true, + }, + }, + shouldFail: true, + expectedError: ":DIGEST_CHECK_FAILED:", + }, + { + name: "FalseStart-BadFinished", + config: Config{ + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + NextProtos: []string{"foo"}, + Bugs: ProtocolBugs{ + BadFinished: true, + ExpectFalseStart: true, + }, + }, + flags: []string{ + "-false-start", + "-handshake-never-done", + "-advertise-alpn", "\x03foo", + }, + shimWritesFirst: true, + shouldFail: true, + expectedError: ":DIGEST_CHECK_FAILED:", + }, + { + name: "NoFalseStart-NoALPN", + config: Config{ + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + ExpectFalseStart: true, + AlertBeforeFalseStartTest: alertAccessDenied, + }, + }, + flags: []string{ + "-false-start", + }, + shimWritesFirst: true, + shouldFail: true, + expectedError: ":TLSV1_ALERT_ACCESS_DENIED:", + expectedLocalError: "tls: peer did not false start: EOF", + }, + { + name: "NoFalseStart-NoAEAD", + config: Config{ + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}, + NextProtos: []string{"foo"}, + Bugs: ProtocolBugs{ + ExpectFalseStart: true, + AlertBeforeFalseStartTest: alertAccessDenied, + }, + }, + flags: []string{ + "-false-start", + "-advertise-alpn", "\x03foo", + }, + shimWritesFirst: true, + shouldFail: true, + expectedError: ":TLSV1_ALERT_ACCESS_DENIED:", + expectedLocalError: "tls: peer did not false start: EOF", + }, + { + name: "NoFalseStart-RSA", + config: Config{ + CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256}, + NextProtos: []string{"foo"}, + Bugs: ProtocolBugs{ + ExpectFalseStart: true, + AlertBeforeFalseStartTest: alertAccessDenied, + }, + }, + flags: []string{ + "-false-start", + "-advertise-alpn", "\x03foo", + }, + shimWritesFirst: true, + shouldFail: true, + expectedError: ":TLSV1_ALERT_ACCESS_DENIED:", + expectedLocalError: "tls: peer did not false start: EOF", + }, + { + name: "NoFalseStart-DHE_RSA", + config: Config{ + CipherSuites: []uint16{TLS_DHE_RSA_WITH_AES_128_GCM_SHA256}, + NextProtos: []string{"foo"}, + Bugs: ProtocolBugs{ + ExpectFalseStart: true, + AlertBeforeFalseStartTest: alertAccessDenied, + }, + }, + flags: []string{ + "-false-start", + "-advertise-alpn", "\x03foo", + }, + shimWritesFirst: true, + shouldFail: true, + expectedError: ":TLSV1_ALERT_ACCESS_DENIED:", + expectedLocalError: "tls: peer did not false start: EOF", + }, + { + testType: serverTest, + name: "NoSupportedCurves", + config: Config{ + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + NoSupportedCurves: true, + }, + }, + }, + { + testType: serverTest, + name: "NoCommonCurves", + config: Config{ + CipherSuites: []uint16{ + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + }, + CurvePreferences: []CurveID{CurveP224}, + }, + expectedCipher: TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + }, + { + protocol: dtls, + name: "SendSplitAlert-Sync", + config: Config{ + Bugs: ProtocolBugs{ + SendSplitAlert: true, + }, + }, + }, + { + protocol: dtls, + name: "SendSplitAlert-Async", + config: Config{ + Bugs: ProtocolBugs{ + SendSplitAlert: true, + }, + }, + flags: []string{"-async"}, + }, + { + protocol: dtls, + name: "PackDTLSHandshake", + config: Config{ + Bugs: ProtocolBugs{ + MaxHandshakeRecordLength: 2, + PackHandshakeFragments: 20, + PackHandshakeRecords: 200, + }, + }, + }, + { + testType: serverTest, + protocol: dtls, + name: "NoRC4-DTLS", + config: Config{ + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_RC4_128_SHA}, + Bugs: ProtocolBugs{ + EnableAllCiphersInDTLS: true, + }, + }, + shouldFail: true, + expectedError: ":NO_SHARED_CIPHER:", + }, + { + name: "SendEmptyRecords-Pass", + sendEmptyRecords: 32, + }, + { + name: "SendEmptyRecords", + sendEmptyRecords: 33, + shouldFail: true, + expectedError: ":TOO_MANY_EMPTY_FRAGMENTS:", + }, + { + name: "SendEmptyRecords-Async", + sendEmptyRecords: 33, + flags: []string{"-async"}, + shouldFail: true, + expectedError: ":TOO_MANY_EMPTY_FRAGMENTS:", + }, + { + name: "SendWarningAlerts-Pass", + sendWarningAlerts: 4, + }, + { + protocol: dtls, + name: "SendWarningAlerts-DTLS-Pass", + sendWarningAlerts: 4, + }, + { + name: "SendWarningAlerts", + sendWarningAlerts: 5, + shouldFail: true, + expectedError: ":TOO_MANY_WARNING_ALERTS:", + }, + { + name: "SendWarningAlerts-Async", + sendWarningAlerts: 5, + flags: []string{"-async"}, + shouldFail: true, + expectedError: ":TOO_MANY_WARNING_ALERTS:", + }, + { + name: "EmptySessionID", + config: Config{ + SessionTicketsDisabled: true, + }, + noSessionCache: true, + flags: []string{"-expect-no-session"}, + }, + { + name: "Unclean-Shutdown", + config: Config{ + Bugs: ProtocolBugs{ + NoCloseNotify: true, + ExpectCloseNotify: true, + }, + }, + shimShutsDown: true, + flags: []string{"-check-close-notify"}, + shouldFail: true, + expectedError: "Unexpected SSL_shutdown result: -1 != 1", + }, + { + name: "Unclean-Shutdown-Ignored", + config: Config{ + Bugs: ProtocolBugs{ + NoCloseNotify: true, + }, + }, + shimShutsDown: true, + }, + { + name: "LargePlaintext", + config: Config{ + Bugs: ProtocolBugs{ + SendLargeRecords: true, + }, + }, + messageLen: maxPlaintext + 1, + shouldFail: true, + expectedError: ":DATA_LENGTH_TOO_LONG:", + }, + { + protocol: dtls, + name: "LargePlaintext-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + SendLargeRecords: true, + }, + }, + messageLen: maxPlaintext + 1, + shouldFail: true, + expectedError: ":DATA_LENGTH_TOO_LONG:", + }, + { + name: "LargeCiphertext", + config: Config{ + Bugs: ProtocolBugs{ + SendLargeRecords: true, + }, + }, + messageLen: maxPlaintext * 2, + shouldFail: true, + expectedError: ":ENCRYPTED_LENGTH_TOO_LONG:", + }, + { + protocol: dtls, + name: "LargeCiphertext-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + SendLargeRecords: true, + }, + }, + messageLen: maxPlaintext * 2, + // Unlike the other four cases, DTLS drops records which + // are invalid before authentication, so the connection + // does not fail. + expectMessageDropped: true, + }, + } + testCases = append(testCases, basicTests...) +} + func addCipherSuiteTests() { for _, suite := range testCipherSuites { const psk = "12345" @@ -1679,6 +1988,10 @@ func addCipherSuiteTests() { "-psk", psk, "-psk-identity", pskIdentity) } + if hasComponent(suite.name, "NULL") { + // NULL ciphers must be explicitly enabled. + flags = append(flags, "-cipher", "DEFAULT:NULL-SHA") + } for _, ver := range tlsVersions { if ver.version < VersionTLS12 && isTLS12Only(suite.name) { @@ -1752,6 +2065,47 @@ func addCipherSuiteTests() { }) } } + + // Ensure both TLS and DTLS accept their maximum record sizes. + testCases = append(testCases, testCase{ + name: suite.name + "-LargeRecord", + config: Config{ + CipherSuites: []uint16{suite.id}, + Certificates: []Certificate{cert}, + PreSharedKey: []byte(psk), + PreSharedKeyIdentity: pskIdentity, + }, + flags: flags, + messageLen: maxPlaintext, + }) + testCases = append(testCases, testCase{ + name: suite.name + "-LargeRecord-Extra", + config: Config{ + CipherSuites: []uint16{suite.id}, + Certificates: []Certificate{cert}, + PreSharedKey: []byte(psk), + PreSharedKeyIdentity: pskIdentity, + Bugs: ProtocolBugs{ + SendLargeRecords: true, + }, + }, + flags: append(flags, "-microsoft-big-sslv3-buffer"), + messageLen: maxPlaintext + 16384, + }) + if isDTLSCipher(suite.name) { + testCases = append(testCases, testCase{ + protocol: dtls, + name: suite.name + "-LargeRecord-DTLS", + config: Config{ + CipherSuites: []uint16{suite.id}, + Certificates: []Certificate{cert}, + PreSharedKey: []byte(psk), + PreSharedKeyIdentity: pskIdentity, + }, + flags: flags, + messageLen: maxPlaintext, + }) + } } testCases = append(testCases, testCase{ @@ -1768,6 +2122,94 @@ func addCipherSuiteTests() { shouldFail: true, expectedError: "BAD_DH_P_LENGTH", }) + + // versionSpecificCiphersTest specifies a test for the TLS 1.0 and TLS + // 1.1 specific cipher suite settings. A server is setup with the given + // cipher lists and then a connection is made for each member of + // expectations. The cipher suite that the server selects must match + // the specified one. + var versionSpecificCiphersTest = []struct { + ciphersDefault, ciphersTLS10, ciphersTLS11 string + // expectations is a map from TLS version to cipher suite id. + expectations map[uint16]uint16 + }{ + { + // Test that the null case (where no version-specific ciphers are set) + // works as expected. + "RC4-SHA:AES128-SHA", // default ciphers + "", // no ciphers specifically for TLS ≥ 1.0 + "", // no ciphers specifically for TLS ≥ 1.1 + map[uint16]uint16{ + VersionSSL30: TLS_RSA_WITH_RC4_128_SHA, + VersionTLS10: TLS_RSA_WITH_RC4_128_SHA, + VersionTLS11: TLS_RSA_WITH_RC4_128_SHA, + VersionTLS12: TLS_RSA_WITH_RC4_128_SHA, + }, + }, + { + // With ciphers_tls10 set, TLS 1.0, 1.1 and 1.2 should get a different + // cipher. + "RC4-SHA:AES128-SHA", // default + "AES128-SHA", // these ciphers for TLS ≥ 1.0 + "", // no ciphers specifically for TLS ≥ 1.1 + map[uint16]uint16{ + VersionSSL30: TLS_RSA_WITH_RC4_128_SHA, + VersionTLS10: TLS_RSA_WITH_AES_128_CBC_SHA, + VersionTLS11: TLS_RSA_WITH_AES_128_CBC_SHA, + VersionTLS12: TLS_RSA_WITH_AES_128_CBC_SHA, + }, + }, + { + // With ciphers_tls11 set, TLS 1.1 and 1.2 should get a different + // cipher. + "RC4-SHA:AES128-SHA", // default + "", // no ciphers specifically for TLS ≥ 1.0 + "AES128-SHA", // these ciphers for TLS ≥ 1.1 + map[uint16]uint16{ + VersionSSL30: TLS_RSA_WITH_RC4_128_SHA, + VersionTLS10: TLS_RSA_WITH_RC4_128_SHA, + VersionTLS11: TLS_RSA_WITH_AES_128_CBC_SHA, + VersionTLS12: TLS_RSA_WITH_AES_128_CBC_SHA, + }, + }, + { + // With both ciphers_tls10 and ciphers_tls11 set, ciphers_tls11 should + // mask ciphers_tls10 for TLS 1.1 and 1.2. + "RC4-SHA:AES128-SHA", // default + "AES128-SHA", // these ciphers for TLS ≥ 1.0 + "AES256-SHA", // these ciphers for TLS ≥ 1.1 + map[uint16]uint16{ + VersionSSL30: TLS_RSA_WITH_RC4_128_SHA, + VersionTLS10: TLS_RSA_WITH_AES_128_CBC_SHA, + VersionTLS11: TLS_RSA_WITH_AES_256_CBC_SHA, + VersionTLS12: TLS_RSA_WITH_AES_256_CBC_SHA, + }, + }, + } + + for i, test := range versionSpecificCiphersTest { + for version, expectedCipherSuite := range test.expectations { + flags := []string{"-cipher", test.ciphersDefault} + if len(test.ciphersTLS10) > 0 { + flags = append(flags, "-cipher-tls10", test.ciphersTLS10) + } + if len(test.ciphersTLS11) > 0 { + flags = append(flags, "-cipher-tls11", test.ciphersTLS11) + } + + testCases = append(testCases, testCase{ + testType: serverTest, + name: fmt.Sprintf("VersionSpecificCiphersTest-%d-%x", i, version), + config: Config{ + MaxVersion: version, + MinVersion: version, + CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA}, + }, + flags: flags, + expectedCipher: expectedCipherSuite, + }) + } + } } func addBadECDSASignatureTests() { @@ -1837,7 +2279,8 @@ func addCBCSplittingTests() { MinVersion: VersionTLS10, CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}, }, - messageLen: -1, // read until EOF + messageLen: -1, // read until EOF + resumeSession: true, flags: []string{ "-async", "-write-different-record-sizes", @@ -1882,8 +2325,8 @@ func addClientAuthTests() { ClientCAs: certPool, }, flags: []string{ - "-cert-file", rsaCertificateFile, - "-key-file", rsaKeyFile, + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), }, }) testCases = append(testCases, testCase{ @@ -1917,8 +2360,8 @@ func addClientAuthTests() { ClientCAs: certPool, }, flags: []string{ - "-cert-file", ecdsaCertificateFile, - "-key-file", ecdsaKeyFile, + "-cert-file", path.Join(*resourceDir, ecdsaCertificateFile), + "-key-file", path.Join(*resourceDir, ecdsaKeyFile), }, }) } @@ -2075,6 +2518,7 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) RenewTicketOnResume: true, }, }, + flags: []string{"-expect-ticket-renewal"}, resumeSession: true, }) tests = append(tests, testCase{ @@ -2123,10 +2567,42 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) ClientAuth: RequireAnyClientCert, }, flags: []string{ - "-cert-file", rsaCertificateFile, - "-key-file", rsaKeyFile, + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), }, }) + if async { + tests = append(tests, testCase{ + testType: clientTest, + name: "ClientAuth-Client-AsyncKey", + config: Config{ + ClientAuth: RequireAnyClientCert, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + "-use-async-private-key", + }, + }) + tests = append(tests, testCase{ + testType: serverTest, + name: "Basic-Server-RSAAsyncKey", + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + "-use-async-private-key", + }, + }) + tests = append(tests, testCase{ + testType: serverTest, + name: "Basic-Server-ECDSAAsyncKey", + flags: []string{ + "-cert-file", path.Join(*resourceDir, ecdsaCertificateFile), + "-key-file", path.Join(*resourceDir, ecdsaKeyFile), + "-use-async-private-key", + }, + }) + } tests = append(tests, testCase{ testType: serverTest, name: "ClientAuth-Server", @@ -2171,6 +2647,57 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) flags: []string{"-psk", "secret"}, }) + tests = append(tests, testCase{ + testType: clientTest, + name: "OCSPStapling-Client", + flags: []string{ + "-enable-ocsp-stapling", + "-expect-ocsp-response", + base64.StdEncoding.EncodeToString(testOCSPResponse), + "-verify-peer", + }, + resumeSession: true, + }) + + tests = append(tests, testCase{ + testType: serverTest, + name: "OCSPStapling-Server", + expectedOCSPResponse: testOCSPResponse, + flags: []string{ + "-ocsp-response", + base64.StdEncoding.EncodeToString(testOCSPResponse), + }, + resumeSession: true, + }) + + tests = append(tests, testCase{ + testType: clientTest, + name: "CertificateVerificationSucceed", + flags: []string{ + "-verify-peer", + }, + }) + + tests = append(tests, testCase{ + testType: clientTest, + name: "CertificateVerificationFail", + flags: []string{ + "-verify-fail", + "-verify-peer", + }, + shouldFail: true, + expectedError: ":CERTIFICATE_VERIFY_FAILED:", + }) + + tests = append(tests, testCase{ + testType: clientTest, + name: "CertificateVerificationSoftFail", + flags: []string{ + "-verify-fail", + "-expect-verify-result", + }, + }) + if protocol == tls { tests = append(tests, testCase{ name: "Renegotiate-Client", @@ -2292,7 +2819,7 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) config: Config{ RequestChannelID: true, }, - flags: []string{"-send-channel-id", channelIDKeyFile}, + flags: []string{"-send-channel-id", path.Join(*resourceDir, channelIDKeyFile)}, resumeSession: true, expectChannelID: true, }) @@ -2311,6 +2838,33 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) resumeSession: true, expectChannelID: true, }) + + // Bidirectional shutdown with the runner initiating. + tests = append(tests, testCase{ + name: "Shutdown-Runner", + config: Config{ + Bugs: ProtocolBugs{ + ExpectCloseNotify: true, + }, + }, + flags: []string{"-check-close-notify"}, + }) + + // Bidirectional shutdown with the shim initiating. The runner, + // in the meantime, sends garbage before the close_notify which + // the shim must ignore. + tests = append(tests, testCase{ + name: "Shutdown-Shim", + config: Config{ + Bugs: ProtocolBugs{ + ExpectCloseNotify: true, + }, + }, + shimShutsDown: true, + sendEmptyRecords: 1, + sendWarningAlerts: 1, + flags: []string{"-check-close-notify"}, + }) } else { tests = append(tests, testCase{ name: "SkipHelloVerifyRequest", @@ -2721,6 +3275,70 @@ func addExtensionTests() { expectedNextProtoType: alpn, resumeSession: true, }) + var emptyString string + testCases = append(testCases, testCase{ + testType: clientTest, + name: "ALPNClient-EmptyProtocolName", + config: Config{ + NextProtos: []string{""}, + Bugs: ProtocolBugs{ + // A server returning an empty ALPN protocol + // should be rejected. + ALPNProtocol: &emptyString, + }, + }, + flags: []string{ + "-advertise-alpn", "\x03foo", + }, + shouldFail: true, + expectedError: ":PARSE_TLSEXT:", + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "ALPNServer-EmptyProtocolName", + config: Config{ + // A ClientHello containing an empty ALPN protocol + // should be rejected. + NextProtos: []string{"foo", "", "baz"}, + }, + flags: []string{ + "-select-alpn", "foo", + }, + shouldFail: true, + expectedError: ":PARSE_TLSEXT:", + }) + // Test that negotiating both NPN and ALPN is forbidden. + testCases = append(testCases, testCase{ + name: "NegotiateALPNAndNPN", + config: Config{ + NextProtos: []string{"foo", "bar", "baz"}, + Bugs: ProtocolBugs{ + NegotiateALPNAndNPN: true, + }, + }, + flags: []string{ + "-advertise-alpn", "\x03foo", + "-select-next-proto", "foo", + }, + shouldFail: true, + expectedError: ":NEGOTIATED_BOTH_NPN_AND_ALPN:", + }) + testCases = append(testCases, testCase{ + name: "NegotiateALPNAndNPN-Swapped", + config: Config{ + NextProtos: []string{"foo", "bar", "baz"}, + Bugs: ProtocolBugs{ + NegotiateALPNAndNPN: true, + SwapNPNAndALPN: true, + }, + }, + flags: []string{ + "-advertise-alpn", "\x03foo", + "-select-next-proto", "foo", + }, + shouldFail: true, + expectedError: ":NEGOTIATED_BOTH_NPN_AND_ALPN:", + }) // Resume with a corrupt ticket. testCases = append(testCases, testCase{ testType: serverTest, @@ -2733,6 +3351,24 @@ func addExtensionTests() { resumeSession: true, expectResumeRejected: true, }) + // Test the ticket callback, with and without renewal. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "TicketCallback", + resumeSession: true, + flags: []string{"-use-ticket-callback"}, + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "TicketCallback-Renew", + config: Config{ + Bugs: ProtocolBugs{ + ExpectNewTicket: true, + }, + }, + flags: []string{"-use-ticket-callback", "-renew-ticket"}, + resumeSession: true, + }) // Resume with an oversized session id. testCases = append(testCases, testCase{ testType: serverTest, @@ -2822,22 +3458,39 @@ func addExtensionTests() { shouldFail: true, expectedError: ":BAD_SRTP_PROTECTION_PROFILE_LIST:", }) - // Test OCSP stapling and SCT list. + // Test SCT list. testCases = append(testCases, testCase{ - name: "OCSPStapling", + name: "SignedCertificateTimestampList-Client", + testType: clientTest, flags: []string{ - "-enable-ocsp-stapling", - "-expect-ocsp-response", - base64.StdEncoding.EncodeToString(testOCSPResponse), + "-enable-signed-cert-timestamps", + "-expect-signed-cert-timestamps", + base64.StdEncoding.EncodeToString(testSCTList), }, + resumeSession: true, }) testCases = append(testCases, testCase{ - name: "SignedCertificateTimestampList", + name: "SignedCertificateTimestampList-Server", + testType: serverTest, flags: []string{ - "-enable-signed-cert-timestamps", - "-expect-signed-cert-timestamps", + "-signed-cert-timestamps", base64.StdEncoding.EncodeToString(testSCTList), }, + expectedSCTList: testSCTList, + resumeSession: true, + }) + testCases = append(testCases, testCase{ + testType: clientTest, + name: "ClientHelloPadding", + config: Config{ + Bugs: ProtocolBugs{ + RequireClientHelloSize: 512, + }, + }, + // This hostname just needs to be long enough to push the + // ClientHello into F5's danger zone between 256 and 511 bytes + // long. + flags: []string{"-host-name", "01234567890123456789012345678901234567890123456789012345678901234567890123456789.com"}, }) } @@ -2956,7 +3609,33 @@ func addRenegotiationTests() { expectedError: ":NO_RENEGOTIATION:", expectedLocalError: "remote error: no renegotiation", }) - // TODO(agl): test the renegotiation info SCSV. + // The server shouldn't echo the renegotiation extension unless + // requested by the client. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Renegotiate-Server-NoExt", + config: Config{ + Bugs: ProtocolBugs{ + NoRenegotiationInfo: true, + RequireRenegotiationInfo: true, + }, + }, + shouldFail: true, + expectedLocalError: "renegotiation extension missing", + }) + // The renegotiation SCSV should be sufficient for the server to echo + // the extension. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Renegotiate-Server-NoExt-SCSV", + config: Config{ + Bugs: ProtocolBugs{ + NoRenegotiationInfo: true, + SendRenegotiationSCSV: true, + RequireRenegotiationInfo: true, + }, + }, + }) testCases = append(testCases, testCase{ name: "Renegotiate-Client", config: Config{ @@ -2989,8 +3668,7 @@ func addRenegotiationTests() { expectedError: ":RENEGOTIATION_MISMATCH:", }) testCases = append(testCases, testCase{ - name: "Renegotiate-Client-NoExt", - renegotiate: true, + name: "Renegotiate-Client-NoExt", config: Config{ Bugs: ProtocolBugs{ NoRenegotiationInfo: true, @@ -3043,6 +3721,19 @@ func addRenegotiationTests() { }, }, }) + testCases = append(testCases, testCase{ + name: "Renegotiate-FalseStart", + renegotiate: true, + config: Config{ + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + NextProtos: []string{"foo"}, + }, + flags: []string{ + "-false-start", + "-select-next-proto", "foo", + }, + shimWritesFirst: true, + }) } func addDTLSReplayTests() { @@ -3050,43 +3741,39 @@ func addDTLSReplayTests() { testCases = append(testCases, testCase{ protocol: dtls, name: "DTLS-Replay", + messageCount: 200, replayWrites: true, }) - // Test the outgoing sequence number skipping by values larger + // Test the incoming sequence number skipping by values larger // than the retransmit window. testCases = append(testCases, testCase{ protocol: dtls, name: "DTLS-Replay-LargeGaps", config: Config{ Bugs: ProtocolBugs{ - SequenceNumberIncrement: 127, + SequenceNumberMapping: func(in uint64) uint64 { + return in * 127 + }, }, }, + messageCount: 200, replayWrites: true, }) -} -func addFastRadioPaddingTests() { - testCases = append(testCases, testCase{ - protocol: tls, - name: "FastRadio-Padding", - config: Config{ - Bugs: ProtocolBugs{ - RequireFastradioPadding: true, - }, - }, - flags: []string{"-fastradio-padding"}, - }) + // Test the incoming sequence number changing non-monotonically. testCases = append(testCases, testCase{ protocol: dtls, - name: "FastRadio-Padding-DTLS", + name: "DTLS-Replay-NonMonotonic", config: Config{ Bugs: ProtocolBugs{ - RequireFastradioPadding: true, + SequenceNumberMapping: func(in uint64) uint64 { + return in ^ 31 + }, }, }, - flags: []string{"-fastradio-padding"}, + messageCount: 200, + replayWrites: true, }) } @@ -3116,8 +3803,8 @@ func addSigningHashTests() { }, }, flags: []string{ - "-cert-file", rsaCertificateFile, - "-key-file", rsaKeyFile, + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), }, }) @@ -3147,8 +3834,8 @@ func addSigningHashTests() { }, }, flags: []string{ - "-cert-file", rsaCertificateFile, - "-key-file", rsaKeyFile, + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), }, }) @@ -3178,8 +3865,8 @@ func addSigningHashTests() { }, }, flags: []string{ - "-cert-file", rsaCertificateFile, - "-key-file", rsaKeyFile, + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), }, }) @@ -3235,6 +3922,73 @@ func addSigningHashTests() { shouldFail: true, expectedError: ":WRONG_SIGNATURE_TYPE:", }) + + // Test that the agreed upon digest respects the client preferences and + // the server digests. + testCases = append(testCases, testCase{ + name: "Agree-Digest-Fallback", + config: Config{ + ClientAuth: RequireAnyClientCert, + SignatureAndHashes: []signatureAndHash{ + {signatureRSA, hashSHA512}, + {signatureRSA, hashSHA1}, + }, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + }, + digestPrefs: "SHA256", + expectedClientCertSignatureHash: hashSHA1, + }) + testCases = append(testCases, testCase{ + name: "Agree-Digest-SHA256", + config: Config{ + ClientAuth: RequireAnyClientCert, + SignatureAndHashes: []signatureAndHash{ + {signatureRSA, hashSHA1}, + {signatureRSA, hashSHA256}, + }, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + }, + digestPrefs: "SHA256,SHA1", + expectedClientCertSignatureHash: hashSHA256, + }) + testCases = append(testCases, testCase{ + name: "Agree-Digest-SHA1", + config: Config{ + ClientAuth: RequireAnyClientCert, + SignatureAndHashes: []signatureAndHash{ + {signatureRSA, hashSHA1}, + }, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + }, + digestPrefs: "SHA512,SHA256,SHA1", + expectedClientCertSignatureHash: hashSHA1, + }) + testCases = append(testCases, testCase{ + name: "Agree-Digest-Default", + config: Config{ + ClientAuth: RequireAnyClientCert, + SignatureAndHashes: []signatureAndHash{ + {signatureRSA, hashSHA256}, + {signatureECDSA, hashSHA256}, + {signatureRSA, hashSHA1}, + {signatureECDSA, hashSHA1}, + }, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + }, + expectedClientCertSignatureHash: hashSHA256, + }) } // timeouts is the retransmit schedule for BoringSSL. It doubles and @@ -3441,7 +4195,111 @@ func addTLSUniqueTests() { } } -func worker(statusChan chan statusMsg, c chan *testCase, buildDir string, wg *sync.WaitGroup) { +func addCustomExtensionTests() { + expectedContents := "custom extension" + emptyString := "" + + for _, isClient := range []bool{false, true} { + suffix := "Server" + flag := "-enable-server-custom-extension" + testType := serverTest + if isClient { + suffix = "Client" + flag = "-enable-client-custom-extension" + testType = clientTest + } + + testCases = append(testCases, testCase{ + testType: testType, + name: "CustomExtensions-" + suffix, + config: Config{ + Bugs: ProtocolBugs{ + CustomExtension: expectedContents, + ExpectedCustomExtension: &expectedContents, + }, + }, + flags: []string{flag}, + }) + + // If the parse callback fails, the handshake should also fail. + testCases = append(testCases, testCase{ + testType: testType, + name: "CustomExtensions-ParseError-" + suffix, + config: Config{ + Bugs: ProtocolBugs{ + CustomExtension: expectedContents + "foo", + ExpectedCustomExtension: &expectedContents, + }, + }, + flags: []string{flag}, + shouldFail: true, + expectedError: ":CUSTOM_EXTENSION_ERROR:", + }) + + // If the add callback fails, the handshake should also fail. + testCases = append(testCases, testCase{ + testType: testType, + name: "CustomExtensions-FailAdd-" + suffix, + config: Config{ + Bugs: ProtocolBugs{ + CustomExtension: expectedContents, + ExpectedCustomExtension: &expectedContents, + }, + }, + flags: []string{flag, "-custom-extension-fail-add"}, + shouldFail: true, + expectedError: ":CUSTOM_EXTENSION_ERROR:", + }) + + // If the add callback returns zero, no extension should be + // added. + skipCustomExtension := expectedContents + if isClient { + // For the case where the client skips sending the + // custom extension, the server must not “echo” it. + skipCustomExtension = "" + } + testCases = append(testCases, testCase{ + testType: testType, + name: "CustomExtensions-Skip-" + suffix, + config: Config{ + Bugs: ProtocolBugs{ + CustomExtension: skipCustomExtension, + ExpectedCustomExtension: &emptyString, + }, + }, + flags: []string{flag, "-custom-extension-skip"}, + }) + } + + // The custom extension add callback should not be called if the client + // doesn't send the extension. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "CustomExtensions-NotCalled-Server", + config: Config{ + Bugs: ProtocolBugs{ + ExpectedCustomExtension: &emptyString, + }, + }, + flags: []string{"-enable-server-custom-extension", "-custom-extension-fail-add"}, + }) + + // Test an unknown extension from the server. + testCases = append(testCases, testCase{ + testType: clientTest, + name: "UnknownExtension-Client", + config: Config{ + Bugs: ProtocolBugs{ + CustomExtension: expectedContents, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_EXTENSION:", + }) +} + +func worker(statusChan chan statusMsg, c chan *testCase, shimPath string, wg *sync.WaitGroup) { defer wg.Done() for test := range c { @@ -3449,11 +4307,11 @@ func worker(statusChan chan statusMsg, c chan *testCase, buildDir string, wg *sy if *mallocTest < 0 { statusChan <- statusMsg{test: test, started: true} - err = runTest(test, buildDir, -1) + err = runTest(test, shimPath, -1) } else { for mallocNumToFail := int64(*mallocTest); ; mallocNumToFail++ { statusChan <- statusMsg{test: test, started: true} - if err = runTest(test, buildDir, mallocNumToFail); err != errMoreMallocs { + if err = runTest(test, shimPath, mallocNumToFail); err != errMoreMallocs { if err != nil { fmt.Printf("\n\nmalloc test failed at %d: %s\n", mallocNumToFail, err) } @@ -3515,12 +4373,10 @@ func statusPrinter(doneChan chan *testOutput, statusChan chan statusMsg, total i } func main() { - var flagTest *string = flag.String("test", "", "The name of a test to run, or empty to run all tests") - var flagNumWorkers *int = flag.Int("num-workers", runtime.NumCPU(), "The number of workers to run in parallel.") - var flagBuildDir *string = flag.String("build-dir", "../../../build", "The build directory to run the shim from.") - flag.Parse() + *resourceDir = path.Clean(*resourceDir) + addBasicTests() addCipherSuiteTests() addBadECDSASignatureTests() addCBCPaddingTests() @@ -3536,10 +4392,10 @@ func main() { addRenegotiationTests() addDTLSReplayTests() addSigningHashTests() - addFastRadioPaddingTests() addDTLSRetransmitTests() addExportKeyingMaterialTests() addTLSUniqueTests() + addCustomExtensionTests() for _, async := range []bool{false, true} { for _, splitHandshake := range []bool{false, true} { for _, protocol := range []protocol{tls, dtls} { @@ -3550,21 +4406,19 @@ func main() { var wg sync.WaitGroup - numWorkers := *flagNumWorkers - - statusChan := make(chan statusMsg, numWorkers) - testChan := make(chan *testCase, numWorkers) + statusChan := make(chan statusMsg, *numWorkers) + testChan := make(chan *testCase, *numWorkers) doneChan := make(chan *testOutput) go statusPrinter(doneChan, statusChan, len(testCases)) - for i := 0; i < numWorkers; i++ { + for i := 0; i < *numWorkers; i++ { wg.Add(1) - go worker(statusChan, testChan, *flagBuildDir, &wg) + go worker(statusChan, testChan, *shimPath, &wg) } for i := range testCases { - if len(*flagTest) == 0 || *flagTest == testCases[i].name { + if len(*testToRun) == 0 || *testToRun == testCases[i].name { testChan <- &testCases[i] } } diff --git a/src/ssl/test/test_config.cc b/src/ssl/test/test_config.cc index 363b6f3..1c42b2e 100644 --- a/src/ssl/test/test_config.cc +++ b/src/ssl/test/test_config.cc @@ -65,12 +65,9 @@ const Flag<bool> kBoolFlags[] = { { "-expect-session-miss", &TestConfig::expect_session_miss }, { "-expect-extended-master-secret", &TestConfig::expect_extended_master_secret }, - { "-allow-unsafe-legacy-renegotiation", - &TestConfig::allow_unsafe_legacy_renegotiation }, { "-enable-ocsp-stapling", &TestConfig::enable_ocsp_stapling }, { "-enable-signed-cert-timestamps", &TestConfig::enable_signed_cert_timestamps }, - { "-fastradio-padding", &TestConfig::fastradio_padding }, { "-implicit-handshake", &TestConfig::implicit_handshake }, { "-use-early-callback", &TestConfig::use_early_callback }, { "-fail-early-callback", &TestConfig::fail_early_callback }, @@ -82,9 +79,27 @@ const Flag<bool> kBoolFlags[] = { { "-reject-peer-renegotiations", &TestConfig::reject_peer_renegotiations }, { "-no-legacy-server-connect", &TestConfig::no_legacy_server_connect }, { "-tls-unique", &TestConfig::tls_unique }, + { "-use-async-private-key", &TestConfig::use_async_private_key }, + { "-expect-ticket-renewal", &TestConfig::expect_ticket_renewal }, + { "-expect-no-session", &TestConfig::expect_no_session }, + { "-use-ticket-callback", &TestConfig::use_ticket_callback }, + { "-renew-ticket", &TestConfig::renew_ticket }, + { "-enable-client-custom-extension", + &TestConfig::enable_client_custom_extension }, + { "-enable-server-custom-extension", + &TestConfig::enable_server_custom_extension }, + { "-custom-extension-skip", &TestConfig::custom_extension_skip }, + { "-custom-extension-fail-add", &TestConfig::custom_extension_fail_add }, + { "-check-close-notify", &TestConfig::check_close_notify }, + { "-shim-shuts-down", &TestConfig::shim_shuts_down }, + { "-microsoft-big-sslv3-buffer", &TestConfig::microsoft_big_sslv3_buffer }, + { "-verify-fail", &TestConfig::verify_fail }, + { "-verify-peer", &TestConfig::verify_peer }, + { "-expect-verify-result", &TestConfig::expect_verify_result } }; const Flag<std::string> kStringFlags[] = { + { "-digest-prefs", &TestConfig::digest_prefs }, { "-key-file", &TestConfig::key_file }, { "-cert-file", &TestConfig::cert_file }, { "-expect-server-name", &TestConfig::expected_server_name }, @@ -101,6 +116,8 @@ const Flag<std::string> kStringFlags[] = { { "-psk-identity", &TestConfig::psk_identity }, { "-srtp-profiles", &TestConfig::srtp_profiles }, { "-cipher", &TestConfig::cipher }, + { "-cipher-tls10", &TestConfig::cipher_tls10 }, + { "-cipher-tls11", &TestConfig::cipher_tls11 }, { "-export-label", &TestConfig::export_label }, { "-export-context", &TestConfig::export_context }, }; @@ -111,6 +128,8 @@ const Flag<std::string> kBase64Flags[] = { { "-expect-ocsp-response", &TestConfig::expected_ocsp_response }, { "-expect-signed-cert-timestamps", &TestConfig::expected_signed_cert_timestamps }, + { "-ocsp-response", &TestConfig::ocsp_response }, + { "-signed-cert-timestamps", &TestConfig::signed_cert_timestamps }, }; const Flag<int> kIntFlags[] = { diff --git a/src/ssl/test/test_config.h b/src/ssl/test/test_config.h index 5d753c8..9dea8e9 100644 --- a/src/ssl/test/test_config.h +++ b/src/ssl/test/test_config.h @@ -24,6 +24,7 @@ struct TestConfig { bool is_dtls = false; bool resume = false; bool fallback_scsv = false; + std::string digest_prefs; std::string key_file; std::string cert_file; std::string expected_server_name; @@ -54,13 +55,11 @@ struct TestConfig { bool expect_extended_master_secret = false; std::string psk; std::string psk_identity; - bool allow_unsafe_legacy_renegotiation = false; std::string srtp_profiles; bool enable_ocsp_stapling = false; std::string expected_ocsp_response; bool enable_signed_cert_timestamps = false; std::string expected_signed_cert_timestamps; - bool fastradio_padding = false; int min_version = 0; int max_version = 0; int mtu = 0; @@ -71,6 +70,8 @@ struct TestConfig { bool fail_ddos_callback = false; bool fail_second_ddos_callback = false; std::string cipher; + std::string cipher_tls10; + std::string cipher_tls11; bool handshake_never_done = false; int export_keying_material = 0; std::string export_label; @@ -79,6 +80,23 @@ struct TestConfig { bool reject_peer_renegotiations = false; bool no_legacy_server_connect = false; bool tls_unique = false; + bool use_async_private_key = false; + bool expect_ticket_renewal = false; + bool expect_no_session = false; + bool use_ticket_callback = false; + bool renew_ticket = false; + bool enable_client_custom_extension = false; + bool enable_server_custom_extension = false; + bool custom_extension_skip = false; + bool custom_extension_fail_add = false; + std::string ocsp_response; + bool check_close_notify = false; + bool shim_shuts_down = false; + bool microsoft_big_sslv3_buffer = false; + bool verify_fail = false; + bool verify_peer = false; + bool expect_verify_result = false; + std::string signed_cert_timestamps; }; bool ParseConfig(int argc, char **argv, TestConfig *out_config); diff --git a/src/ssl/tls_record.c b/src/ssl/tls_record.c new file mode 100644 index 0000000..36e31b4 --- /dev/null +++ b/src/ssl/tls_record.c @@ -0,0 +1,338 @@ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ +/* ==================================================================== + * Copyright (c) 1998-2002 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). */ + +#include <openssl/ssl.h> + +#include <assert.h> + +#include <openssl/bytestring.h> +#include <openssl/err.h> + +#include "internal.h" + + +/* kMaxEmptyRecords is the number of consecutive, empty records that will be + * processed. Without this limit an attacker could send empty records at a + * faster rate than we can process and cause record processing to loop + * forever. */ +static const uint8_t kMaxEmptyRecords = 32; + +size_t ssl_record_prefix_len(const SSL *ssl) { + if (SSL_IS_DTLS(ssl)) { + return DTLS1_RT_HEADER_LENGTH + + SSL_AEAD_CTX_explicit_nonce_len(ssl->aead_read_ctx); + } else { + return SSL3_RT_HEADER_LENGTH + + SSL_AEAD_CTX_explicit_nonce_len(ssl->aead_read_ctx); + } +} + +size_t ssl_seal_prefix_len(const SSL *ssl) { + if (SSL_IS_DTLS(ssl)) { + return DTLS1_RT_HEADER_LENGTH + + SSL_AEAD_CTX_explicit_nonce_len(ssl->aead_write_ctx); + } else { + size_t ret = SSL3_RT_HEADER_LENGTH + + SSL_AEAD_CTX_explicit_nonce_len(ssl->aead_write_ctx); + if (ssl->s3->need_record_splitting) { + ret += SSL3_RT_HEADER_LENGTH; + ret += ssl_cipher_get_record_split_len(ssl->aead_write_ctx->cipher); + } + return ret; + } +} + +size_t ssl_max_seal_overhead(const SSL *ssl) { + if (SSL_IS_DTLS(ssl)) { + return DTLS1_RT_HEADER_LENGTH + + SSL_AEAD_CTX_max_overhead(ssl->aead_write_ctx); + } else { + size_t ret = SSL3_RT_HEADER_LENGTH + + SSL_AEAD_CTX_max_overhead(ssl->aead_write_ctx); + if (ssl->s3->need_record_splitting) { + ret *= 2; + } + return ret; + } +} + +enum ssl_open_record_t tls_open_record( + SSL *ssl, uint8_t *out_type, uint8_t *out, size_t *out_len, + size_t *out_consumed, uint8_t *out_alert, size_t max_out, const uint8_t *in, + size_t in_len) { + CBS cbs; + CBS_init(&cbs, in, in_len); + + /* Decode the record header. */ + uint8_t type; + uint16_t version, ciphertext_len; + if (!CBS_get_u8(&cbs, &type) || + !CBS_get_u16(&cbs, &version) || + !CBS_get_u16(&cbs, &ciphertext_len)) { + *out_consumed = SSL3_RT_HEADER_LENGTH; + return ssl_open_record_partial; + } + + /* Check the version. */ + if ((ssl->s3->have_version && version != ssl->version) || + (version >> 8) != SSL3_VERSION_MAJOR) { + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_VERSION_NUMBER); + *out_alert = SSL_AD_PROTOCOL_VERSION; + return ssl_open_record_error; + } + + /* Check the ciphertext length. */ + size_t extra = 0; + if (ssl->options & SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER) { + extra = SSL3_RT_MAX_EXTRA; + } + if (ciphertext_len > SSL3_RT_MAX_ENCRYPTED_LENGTH + extra) { + OPENSSL_PUT_ERROR(SSL, SSL_R_ENCRYPTED_LENGTH_TOO_LONG); + *out_alert = SSL_AD_RECORD_OVERFLOW; + return ssl_open_record_error; + } + + /* Extract the body. */ + CBS body; + if (!CBS_get_bytes(&cbs, &body, ciphertext_len)) { + *out_consumed = SSL3_RT_HEADER_LENGTH + (size_t)ciphertext_len; + return ssl_open_record_partial; + } + + if (ssl->msg_callback != NULL) { + ssl->msg_callback(0 /* read */, 0, SSL3_RT_HEADER, in, + SSL3_RT_HEADER_LENGTH, ssl, ssl->msg_callback_arg); + } + + /* Decrypt the body. */ + size_t plaintext_len; + if (!SSL_AEAD_CTX_open(ssl->aead_read_ctx, out, &plaintext_len, max_out, + type, version, ssl->s3->read_sequence, CBS_data(&body), + CBS_len(&body))) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC); + *out_alert = SSL_AD_BAD_RECORD_MAC; + return ssl_open_record_error; + } + if (!ssl3_record_sequence_update(ssl->s3->read_sequence, 8)) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return ssl_open_record_error; + } + + /* Check the plaintext length. */ + if (plaintext_len > SSL3_RT_MAX_PLAIN_LENGTH + extra) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DATA_LENGTH_TOO_LONG); + *out_alert = SSL_AD_RECORD_OVERFLOW; + return ssl_open_record_error; + } + + /* Limit the number of consecutive empty records. */ + if (plaintext_len == 0) { + ssl->s3->empty_record_count++; + if (ssl->s3->empty_record_count > kMaxEmptyRecords) { + OPENSSL_PUT_ERROR(SSL, SSL_R_TOO_MANY_EMPTY_FRAGMENTS); + *out_alert = SSL_AD_UNEXPECTED_MESSAGE; + return ssl_open_record_error; + } + /* Apart from the limit, empty records are returned up to the caller. This + * allows the caller to reject records of the wrong type. */ + } else { + ssl->s3->empty_record_count = 0; + } + + *out_type = type; + *out_len = plaintext_len; + *out_consumed = in_len - CBS_len(&cbs); + return ssl_open_record_success; +} + +static int do_seal_record(SSL *ssl, uint8_t *out, size_t *out_len, + size_t max_out, uint8_t type, const uint8_t *in, + size_t in_len) { + if (max_out < SSL3_RT_HEADER_LENGTH) { + OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL); + return 0; + } + /* Check the record header does not alias any part of the input. + * |SSL_AEAD_CTX_seal| will internally enforce other aliasing requirements. */ + if (in < out + SSL3_RT_HEADER_LENGTH && out < in + in_len) { + OPENSSL_PUT_ERROR(SSL, SSL_R_OUTPUT_ALIASES_INPUT); + return 0; + } + + out[0] = type; + + /* Some servers hang if initial ClientHello is larger than 256 bytes and + * record version number > TLS 1.0. */ + uint16_t wire_version = ssl->version; + if (!ssl->s3->have_version && ssl->version > SSL3_VERSION) { + wire_version = TLS1_VERSION; + } + out[1] = wire_version >> 8; + out[2] = wire_version & 0xff; + + size_t ciphertext_len; + if (!SSL_AEAD_CTX_seal(ssl->aead_write_ctx, out + SSL3_RT_HEADER_LENGTH, + &ciphertext_len, max_out - SSL3_RT_HEADER_LENGTH, + type, wire_version, ssl->s3->write_sequence, in, + in_len) || + !ssl3_record_sequence_update(ssl->s3->write_sequence, 8)) { + return 0; + } + + if (ciphertext_len >= 1 << 16) { + OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW); + return 0; + } + out[3] = ciphertext_len >> 8; + out[4] = ciphertext_len & 0xff; + + *out_len = SSL3_RT_HEADER_LENGTH + ciphertext_len; + + if (ssl->msg_callback) { + ssl->msg_callback(1 /* write */, 0, SSL3_RT_HEADER, out, + SSL3_RT_HEADER_LENGTH, ssl, ssl->msg_callback_arg); + } + + return 1; +} + +int tls_seal_record(SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out, + uint8_t type, const uint8_t *in, size_t in_len) { + size_t frag_len = 0; + if (ssl->s3->need_record_splitting && type == SSL3_RT_APPLICATION_DATA && + in_len > 1) { + /* |do_seal_record| will notice if it clobbers |in[0]|, but not if it + * aliases the rest of |in|. */ + if (in + 1 <= out && out < in + in_len) { + OPENSSL_PUT_ERROR(SSL, SSL_R_OUTPUT_ALIASES_INPUT); + return 0; + } + /* Ensure |do_seal_record| does not write beyond |in[0]|. */ + size_t frag_max_out = max_out; + if (out <= in + 1 && in + 1 < out + frag_max_out) { + frag_max_out = (size_t)(in + 1 - out); + } + if (!do_seal_record(ssl, out, &frag_len, frag_max_out, type, in, 1)) { + return 0; + } + in++; + in_len--; + out += frag_len; + max_out -= frag_len; + + assert(SSL3_RT_HEADER_LENGTH + + ssl_cipher_get_record_split_len(ssl->aead_write_ctx->cipher) == + frag_len); + } + + if (!do_seal_record(ssl, out, out_len, max_out, type, in, in_len)) { + return 0; + } + *out_len += frag_len; + return 1; +} |