/*
- * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved.
* Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved
* Copyright 2005 Nokia. All rights reserved.
*
#include "../ssl_locl.h"
#include "statem_locl.h"
#include "internal/constant_time_locl.h"
+#include "internal/cryptlib.h"
#include <openssl/buffer.h>
#include <openssl/rand.h>
#include <openssl/objects.h>
#include <openssl/bn.h>
#include <openssl/md5.h>
+#define TICKET_NONCE_SIZE 8
+
static int tls_construct_encrypted_extensions(SSL *s, WPACKET *pkt);
/*
*/
if (s->early_data_state == SSL_EARLY_DATA_READING)
break;
+
+ if (mt == SSL3_MT_CERTIFICATE
+ && s->post_handshake_auth == SSL_PHA_REQUESTED) {
+ st->hand_state = TLS_ST_SR_CERT;
+ return 1;
+ }
+
if (mt == SSL3_MT_KEY_UPDATE) {
st->hand_state = TLS_ST_SR_KEY_UPDATE;
return 1;
err:
/* No valid transition found */
+ if (SSL_IS_DTLS(s) && mt == SSL3_MT_CHANGE_CIPHER_SPEC) {
+ BIO *rbio;
+
+ /*
+ * CCS messages don't have a message sequence number so this is probably
+ * because of an out-of-order CCS. We'll just drop it.
+ */
+ s->init_num = 0;
+ s->rwstate = SSL_READING;
+ rbio = SSL_get_rbio(s);
+ BIO_clear_retry_flags(rbio);
+ BIO_set_retry_read(rbio);
+ return 0;
+ }
SSLfatal(s, SSL3_AD_UNEXPECTED_MESSAGE,
SSL_F_OSSL_STATEM_SERVER_READ_TRANSITION,
SSL_R_UNEXPECTED_MESSAGE);
* 1: Yes
* 0: No
*/
-static int send_certificate_request(SSL *s)
+int send_certificate_request(SSL *s)
{
if (
/* don't request cert unless asked for it: */
s->verify_mode & SSL_VERIFY_PEER
+ /*
+ * don't request if post-handshake-only unless doing
+ * post-handshake in TLSv1.3:
+ */
+ && (!SSL_IS_TLS13(s) || !(s->verify_mode & SSL_VERIFY_POST_HANDSHAKE)
+ || s->post_handshake_auth == SSL_PHA_REQUEST_PENDING)
/*
* if SSL_VERIFY_CLIENT_ONCE is set, don't request cert
- * during re-negotiation:
+ * a second time:
*/
- && (s->s3->tmp.finish_md_len == 0 ||
+ && (s->certreqs_sent < 1 ||
!(s->verify_mode & SSL_VERIFY_CLIENT_ONCE))
/*
* never request cert in anonymous ciphersuites (see
st->hand_state = TLS_ST_SW_KEY_UPDATE;
return WRITE_TRAN_CONTINUE;
}
+ if (s->post_handshake_auth == SSL_PHA_REQUEST_PENDING) {
+ st->hand_state = TLS_ST_SW_CERT_REQ;
+ return WRITE_TRAN_CONTINUE;
+ }
/* Try to read from the client instead */
return WRITE_TRAN_FINISHED;
return WRITE_TRAN_CONTINUE;
case TLS_ST_SW_CERT_REQ:
- st->hand_state = TLS_ST_SW_CERT;
+ if (s->post_handshake_auth == SSL_PHA_REQUEST_PENDING) {
+ s->post_handshake_auth = SSL_PHA_REQUESTED;
+ st->hand_state = TLS_ST_OK;
+ } else {
+ st->hand_state = TLS_ST_SW_CERT;
+ }
return WRITE_TRAN_CONTINUE;
case TLS_ST_SW_CERT:
case TLS_ST_SR_FINISHED:
/*
* Technically we have finished the handshake at this point, but we're
- * going to remain "in_init" for now and write out the session ticket
+ * going to remain "in_init" for now and write out any session tickets
* immediately.
- * TODO(TLS1.3): Perhaps we need to be able to control this behaviour
- * and give the application the opportunity to delay sending the
- * session ticket?
*/
- st->hand_state = TLS_ST_SW_SESSION_TICKET;
+ if (s->post_handshake_auth == SSL_PHA_REQUESTED) {
+ s->post_handshake_auth = SSL_PHA_EXT_RECEIVED;
+ } else if (!s->ext.ticket_expected) {
+ /*
+ * If we're not going to renew the ticket then we just finish the
+ * handshake at this point.
+ */
+ st->hand_state = TLS_ST_OK;
+ return WRITE_TRAN_CONTINUE;
+ }
+ if (s->num_tickets > s->sent_tickets)
+ st->hand_state = TLS_ST_SW_SESSION_TICKET;
+ else
+ st->hand_state = TLS_ST_OK;
return WRITE_TRAN_CONTINUE;
case TLS_ST_SR_KEY_UPDATE:
/* Fall through */
case TLS_ST_SW_KEY_UPDATE:
- case TLS_ST_SW_SESSION_TICKET:
st->hand_state = TLS_ST_OK;
return WRITE_TRAN_CONTINUE;
+
+ case TLS_ST_SW_SESSION_TICKET:
+ /* In a resumption we only ever send a maximum of one new ticket.
+ * Following an initial handshake we send the number of tickets we have
+ * been configured for.
+ */
+ if (s->hit || s->num_tickets <= s->sent_tickets) {
+ /* We've written enough tickets out. */
+ st->hand_state = TLS_ST_OK;
+ }
+ return WRITE_TRAN_CONTINUE;
}
}
case TLS_ST_SR_CLNT_HELLO:
if (SSL_IS_DTLS(s) && !s->d1->cookie_verified
- && (SSL_get_options(s) & SSL_OP_COOKIE_EXCHANGE))
+ && (SSL_get_options(s) & SSL_OP_COOKIE_EXCHANGE)) {
st->hand_state = DTLS_ST_SW_HELLO_VERIFY_REQUEST;
- else
+ } else if (s->renegotiate == 0 && !SSL_IS_FIRST_HANDSHAKE(s)) {
+ /* We must have rejected the renegotiation */
+ st->hand_state = TLS_ST_OK;
+ return WRITE_TRAN_CONTINUE;
+ } else {
st->hand_state = TLS_ST_SW_SRVR_HELLO;
+ }
return WRITE_TRAN_CONTINUE;
case DTLS_ST_SW_HELLO_VERIFY_REQUEST:
return WORK_FINISHED_CONTINUE;
case TLS_ST_SW_SESSION_TICKET:
- if (SSL_IS_TLS13(s)) {
+ if (SSL_IS_TLS13(s) && s->sent_tickets == 0) {
/*
* Actually this is the end of the handshake, but we're going
* straight into writing the session ticket out. So we finish off
return WORK_FINISHED_CONTINUE;
case TLS_ST_EARLY_DATA:
- if (s->early_data_state != SSL_EARLY_DATA_ACCEPTING)
+ if (s->early_data_state != SSL_EARLY_DATA_ACCEPTING
+ && (s->s3->flags & TLS1_FLAGS_STATELESS) == 0)
return WORK_FINISHED_CONTINUE;
/* Fall through */
return WORK_MORE_A;
break;
}
- /*
- * TODO(TLS1.3): This actually causes a problem. We don't yet know
- * whether the next record we are going to receive is an unencrypted
- * alert, or an encrypted handshake message. We're going to need
- * something clever in the record layer for this.
- */
+
if (SSL_IS_TLS13(s)) {
if (!s->method->ssl3_enc->setup_key_block(s)
|| !s->method->ssl3_enc->change_cipher_state(s,
/* SSLfatal() already called */
return WORK_ERROR;
}
+ /*
+ * We don't yet know whether the next record we are going to receive
+ * is an unencrypted alert, an encrypted alert, or an encrypted
+ * handshake message. We temporarily tolerate unencrypted alerts.
+ */
+ s->statem.enc_read_state = ENC_READ_STATE_ALLOW_PLAIN_ALERTS;
break;
}
}
break;
+ case TLS_ST_SW_CERT_REQ:
+ if (s->post_handshake_auth == SSL_PHA_REQUEST_PENDING) {
+ if (statem_flush(s) != 1)
+ return WORK_MORE_A;
+ }
+ break;
+
case TLS_ST_SW_KEY_UPDATE:
if (statem_flush(s) != 1)
return WORK_MORE_A;
case TLS_ST_SR_KEY_EXCH:
return tls_post_process_client_key_exchange(s, wst);
}
- return WORK_FINISHED_CONTINUE;
}
#ifndef OPENSSL_NO_SRP
/* |cookie| will only be initialized for DTLS. */
PACKET session_id, compression, extensions, cookie;
static const unsigned char null_compression = 0;
- CLIENTHELLO_MSG *clienthello;
+ CLIENTHELLO_MSG *clienthello = NULL;
- clienthello = OPENSSL_zalloc(sizeof(*clienthello));
- if (clienthello == NULL) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_PROCESS_CLIENT_HELLO,
- ERR_R_INTERNAL_ERROR);
- goto err;
- }
/* Check if this is actually an unexpected renegotiation ClientHello */
if (s->renegotiate == 0 && !SSL_IS_FIRST_HANDSHAKE(s)) {
- if ((s->options & SSL_OP_NO_RENEGOTIATION)) {
- ssl3_send_alert(s, SSL3_AL_WARNING, SSL_AD_NO_RENEGOTIATION);
+ if (!ossl_assert(!SSL_IS_TLS13(s))) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_PROCESS_CLIENT_HELLO,
+ ERR_R_INTERNAL_ERROR);
goto err;
}
+ if ((s->options & SSL_OP_NO_RENEGOTIATION) != 0
+ || (!s->s3->send_connection_binding
+ && (s->options
+ & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION) == 0)) {
+ ssl3_send_alert(s, SSL3_AL_WARNING, SSL_AD_NO_RENEGOTIATION);
+ return MSG_PROCESS_FINISHED_READING;
+ }
s->renegotiate = 1;
s->new_session = 1;
}
+ clienthello = OPENSSL_zalloc(sizeof(*clienthello));
+ if (clienthello == NULL) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_PROCESS_CLIENT_HELLO,
+ ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+
/*
* First, parse the raw ClientHello data into the CLIENTHELLO_MSG structure.
*/
s->ext.early_data_ok = 0;
if (!s->hit) {
- /* If a new session update it with the new ALPN value */
+ /*
+ * This is a new session and so alpn_selected should have
+ * been initialised to NULL. We should update it with the
+ * selected ALPN.
+ */
+ if (!ossl_assert(s->session->ext.alpn_selected == NULL)) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+ SSL_F_TLS_HANDLE_ALPN,
+ ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
s->session->ext.alpn_selected = OPENSSL_memdup(selected,
selected_len);
if (s->session->ext.alpn_selected == NULL) {
if (!WPACKET_sub_memcpy_u8(pkt, session_id, sl)
|| !s->method->put_cipher_by_char(s->s3->tmp.new_cipher, pkt, &len)
- || !WPACKET_put_bytes_u8(pkt, compm)
- || !tls_construct_extensions(s, pkt,
- s->hello_retry_request
- == SSL_HRR_PENDING
- ? SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST
- : (SSL_IS_TLS13(s)
- ? SSL_EXT_TLS1_3_SERVER_HELLO
- : SSL_EXT_TLS1_2_SERVER_HELLO),
- NULL, 0)) {
+ || !WPACKET_put_bytes_u8(pkt, compm)) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_SERVER_HELLO,
+ ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
+ if (!tls_construct_extensions(s, pkt,
+ s->hello_retry_request == SSL_HRR_PENDING
+ ? SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST
+ : (SSL_IS_TLS13(s)
+ ? SSL_EXT_TLS1_3_SERVER_HELLO
+ : SSL_EXT_TLS1_2_SERVER_HELLO),
+ NULL, 0)) {
/* SSLfatal() already called */
return 0;
}
* Re-initialise the Transcript Hash. We're going to prepopulate it with
* a synthetic message_hash in place of ClientHello1.
*/
- if (!create_synthetic_message_hash(s)) {
+ if (!create_synthetic_message_hash(s, NULL, 0, NULL, 0)) {
/* SSLfatal() already called */
return 0;
}
}
dh = EVP_PKEY_get0_DH(s->s3->tmp.pkey);
+ if (dh == NULL) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+ SSL_F_TLS_CONSTRUCT_SERVER_KEY_EXCHANGE,
+ ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
EVP_PKEY_free(pkdh);
pkdh = NULL;
int tls_construct_certificate_request(SSL *s, WPACKET *pkt)
{
if (SSL_IS_TLS13(s)) {
- /* TODO(TLS1.3) for now send empty request context */
- if (!WPACKET_put_bytes_u8(pkt, 0)) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR,
- SSL_F_TLS_CONSTRUCT_CERTIFICATE_REQUEST,
- ERR_R_INTERNAL_ERROR);
- return 0;
+ /* Send random context when doing post-handshake auth */
+ if (s->post_handshake_auth == SSL_PHA_REQUEST_PENDING) {
+ OPENSSL_free(s->pha_context);
+ s->pha_context_len = 32;
+ if ((s->pha_context = OPENSSL_malloc(s->pha_context_len)) == NULL
+ || RAND_bytes(s->pha_context, s->pha_context_len) <= 0
+ || !WPACKET_sub_memcpy_u8(pkt, s->pha_context, s->pha_context_len)) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+ SSL_F_TLS_CONSTRUCT_CERTIFICATE_REQUEST,
+ ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+ /* reset the handshake hash back to just after the ClientFinished */
+ if (!tls13_restore_handshake_digest_for_pha(s)) {
+ /* SSLfatal() already called */
+ return 0;
+ }
+ } else {
+ if (!WPACKET_put_bytes_u8(pkt, 0)) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+ SSL_F_TLS_CONSTRUCT_CERTIFICATE_REQUEST,
+ ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
}
if (!tls_construct_extensions(s, pkt,
}
done:
+ s->certreqs_sent++;
s->s3->tmp.cert_request = 1;
return 1;
}
* fails. See https://tools.ietf.org/html/rfc5246#section-7.4.7.1
*/
- if (ssl_randbytes(s, rand_premaster_secret,
+ if (RAND_priv_bytes(rand_premaster_secret,
sizeof(rand_premaster_secret)) <= 0) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_PROCESS_CKE_RSA,
ERR_R_INTERNAL_ERROR);
SSL_R_BN_LIB);
goto err;
}
+
cdh = EVP_PKEY_get0_DH(ckey);
pub_key = BN_bin2bn(data, i, NULL);
-
- if (pub_key == NULL || !DH_set0_key(cdh, pub_key, NULL)) {
+ if (pub_key == NULL || cdh == NULL || !DH_set0_key(cdh, pub_key, NULL)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_PROCESS_CKE_DHE,
ERR_R_INTERNAL_ERROR);
- if (pub_key != NULL)
- BN_free(pub_key);
+ BN_free(pub_key);
goto err;
}
const unsigned char *start;
size_t outlen = 32, inlen;
unsigned long alg_a;
- int Ttag, Tclass;
- long Tlen;
- size_t sess_key_len;
- const unsigned char *data;
+ unsigned int asn1id, asn1len;
int ret = 0;
+ PACKET encdata;
/* Get our certificate private key */
alg_a = s->s3->tmp.new_cipher->algorithm_auth;
ERR_clear_error();
}
/* Decrypt session key */
- sess_key_len = PACKET_remaining(pkt);
- if (!PACKET_get_bytes(pkt, &data, sess_key_len)) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_PROCESS_CKE_GOST,
- ERR_R_INTERNAL_ERROR);
+ if (!PACKET_get_1(pkt, &asn1id)
+ || asn1id != (V_ASN1_SEQUENCE | V_ASN1_CONSTRUCTED)
+ || !PACKET_peek_1(pkt, &asn1len)) {
+ SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PROCESS_CKE_GOST,
+ SSL_R_DECRYPTION_FAILED);
goto err;
}
- /* TODO(size_t): Convert this function */
- if (ASN1_get_object((const unsigned char **)&data, &Tlen, &Ttag,
- &Tclass, (long)sess_key_len) != V_ASN1_CONSTRUCTED
- || Ttag != V_ASN1_SEQUENCE || Tclass != V_ASN1_UNIVERSAL) {
+ if (asn1len == 0x81) {
+ /*
+ * Long form length. Should only be one byte of length. Anything else
+ * isn't supported.
+ * We did a successful peek before so this shouldn't fail
+ */
+ if (!PACKET_forward(pkt, 1)) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_PROCESS_CKE_GOST,
+ SSL_R_DECRYPTION_FAILED);
+ goto err;
+ }
+ } else if (asn1len >= 0x80) {
+ /*
+ * Indefinite length, or more than one long form length bytes. We don't
+ * support it
+ */
+ SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PROCESS_CKE_GOST,
+ SSL_R_DECRYPTION_FAILED);
+ goto err;
+ } /* else short form length */
+
+ if (!PACKET_as_length_prefixed_1(pkt, &encdata)) {
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PROCESS_CKE_GOST,
SSL_R_DECRYPTION_FAILED);
goto err;
}
- start = data;
- inlen = Tlen;
+ inlen = PACKET_remaining(&encdata);
+ start = PACKET_data(&encdata);
+
if (EVP_PKEY_decrypt(pkey_ctx, premaster_secret, &outlen, start,
inlen) <= 0) {
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PROCESS_CKE_GOST,
int i;
MSG_PROCESS_RETURN ret = MSG_PROCESS_ERROR;
X509 *x = NULL;
- unsigned long l, llen;
+ unsigned long l;
const unsigned char *certstart, *certbytes;
STACK_OF(X509) *sk = NULL;
PACKET spkt, context;
size_t chainidx;
+ SSL_SESSION *new_sess = NULL;
+
+ /*
+ * To get this far we must have read encrypted data from the client. We no
+ * longer tolerate unencrypted alerts. This value is ignored if less than
+ * TLSv1.3
+ */
+ s->statem.enc_read_state = ENC_READ_STATE_VALID;
if ((sk = sk_X509_new_null()) == NULL) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_PROCESS_CLIENT_CERTIFICATE,
goto err;
}
- /* TODO(TLS1.3): For now we ignore the context. We need to verify this */
- if ((SSL_IS_TLS13(s) && !PACKET_get_length_prefixed_1(pkt, &context))
- || !PACKET_get_net_3(pkt, &llen)
- || !PACKET_get_sub_packet(pkt, &spkt, llen)
+ if (SSL_IS_TLS13(s) && (!PACKET_get_length_prefixed_1(pkt, &context)
+ || (s->pha_context == NULL && PACKET_remaining(&context) != 0)
+ || (s->pha_context != NULL &&
+ !PACKET_equal(&context, s->pha_context, s->pha_context_len)))) {
+ SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PROCESS_CLIENT_CERTIFICATE,
+ SSL_R_INVALID_CONTEXT);
+ goto err;
+ }
+
+ if (!PACKET_get_length_prefixed_3(pkt, &spkt)
|| PACKET_remaining(pkt) != 0) {
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PROCESS_CLIENT_CERTIFICATE,
SSL_R_LENGTH_MISMATCH);
EVP_PKEY *pkey;
i = ssl_verify_cert_chain(s, sk);
if (i <= 0) {
- SSLfatal(s, ssl_verify_alarm_type(s->verify_result),
+ SSLfatal(s, ssl_x509err2alert(s->verify_result),
SSL_F_TLS_PROCESS_CLIENT_CERTIFICATE,
SSL_R_CERTIFICATE_VERIFY_FAILED);
goto err;
}
}
+ /*
+ * Sessions must be immutable once they go into the session cache. Otherwise
+ * we can get multi-thread problems. Therefore we don't "update" sessions,
+ * we replace them with a duplicate. Here, we need to do this every time
+ * a new certificate is received via post-handshake authentication, as the
+ * session may have already gone into the session cache.
+ */
+
+ if (s->post_handshake_auth == SSL_PHA_REQUESTED) {
+ if ((new_sess = ssl_session_dup(s->session, 0)) == 0) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+ SSL_F_TLS_PROCESS_CLIENT_CERTIFICATE,
+ ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+
+ SSL_SESSION_free(s->session);
+ s->session = new_sess;
+ }
+
X509_free(s->session->peer);
s->session->peer = sk_X509_shift(sk);
s->session->verify_result = s->verify_result;
sk = NULL;
/* Save the current hash state for when we receive the CertificateVerify */
- if (SSL_IS_TLS13(s)
- && !ssl_handshake_hash(s, s->cert_verify_hash,
- sizeof(s->cert_verify_hash),
- &s->cert_verify_hash_len)) {
- /* SSLfatal() already called */
- goto err;
+ if (SSL_IS_TLS13(s)) {
+ if (!ssl_handshake_hash(s, s->cert_verify_hash,
+ sizeof(s->cert_verify_hash),
+ &s->cert_verify_hash_len)) {
+ /* SSLfatal() already called */
+ goto err;
+ }
+
+ /* Resend session tickets */
+ s->sent_tickets = 0;
}
ret = MSG_PROCESS_CONTINUE_READING;
return 1;
}
-int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt)
+static int create_ticket_prequel(SSL *s, WPACKET *pkt, uint32_t age_add,
+ unsigned char *tick_nonce)
+{
+ /*
+ * Ticket lifetime hint: For TLSv1.2 this is advisory only and we leave this
+ * unspecified for resumed session (for simplicity).
+ * In TLSv1.3 we reset the "time" field above, and always specify the
+ * timeout.
+ */
+ if (!WPACKET_put_bytes_u32(pkt,
+ (s->hit && !SSL_IS_TLS13(s))
+ ? 0 : s->session->timeout)) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CREATE_TICKET_PREQUEL,
+ ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
+ if (SSL_IS_TLS13(s)) {
+ if (!WPACKET_put_bytes_u32(pkt, age_add)
+ || !WPACKET_sub_memcpy_u8(pkt, tick_nonce, TICKET_NONCE_SIZE)) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CREATE_TICKET_PREQUEL,
+ ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+ }
+
+ /* Start the sub-packet for the actual ticket data */
+ if (!WPACKET_start_sub_packet_u16(pkt)) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CREATE_TICKET_PREQUEL,
+ ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int construct_stateless_ticket(SSL *s, WPACKET *pkt, uint32_t age_add,
+ unsigned char *tick_nonce)
{
unsigned char *senc = NULL;
EVP_CIPHER_CTX *ctx = NULL;
SSL_CTX *tctx = s->session_ctx;
unsigned char iv[EVP_MAX_IV_LENGTH];
unsigned char key_name[TLSEXT_KEYNAME_LENGTH];
- int iv_len;
+ int iv_len, ok = 0;
size_t macoffset, macendoffset;
- union {
- unsigned char age_add_c[sizeof(uint32_t)];
- uint32_t age_add;
- } age_add_u;
-
- if (SSL_IS_TLS13(s)) {
- if (ssl_randbytes(s, age_add_u.age_add_c, sizeof(age_add_u)) <= 0) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR,
- SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET,
- ERR_R_INTERNAL_ERROR);
- goto err;
- }
- s->session->ext.tick_age_add = age_add_u.age_add;
- /*
- * ticket_nonce is set to a single 0 byte because we only ever send a
- * single ticket per connection. IMPORTANT: If we ever support multiple
- * tickets per connection then this will need to be changed.
- */
- OPENSSL_free(s->session->ext.tick_nonce);
- s->session->ext.tick_nonce = OPENSSL_zalloc(sizeof(char));
- if (s->session->ext.tick_nonce == NULL) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR,
- SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET,
- ERR_R_MALLOC_FAILURE);
- goto err;
- }
- s->session->ext.tick_nonce_len = 1;
- s->session->time = (long)time(NULL);
- if (s->s3->alpn_selected != NULL) {
- OPENSSL_free(s->session->ext.alpn_selected);
- s->session->ext.alpn_selected =
- OPENSSL_memdup(s->s3->alpn_selected, s->s3->alpn_selected_len);
- if (s->session->ext.alpn_selected == NULL) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR,
- SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET,
- ERR_R_MALLOC_FAILURE);
- goto err;
- }
- s->session->ext.alpn_selected_len = s->s3->alpn_selected_len;
- }
- s->session->ext.max_early_data = s->max_early_data;
- }
/* get session encoding length */
slen_full = i2d_SSL_SESSION(s->session, NULL);
* long
*/
if (slen_full == 0 || slen_full > 0xFF00) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR,
- SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET, ERR_R_INTERNAL_ERROR);
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CONSTRUCT_STATELESS_TICKET,
+ ERR_R_INTERNAL_ERROR);
goto err;
}
senc = OPENSSL_malloc(slen_full);
if (senc == NULL) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR,
- SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET, ERR_R_MALLOC_FAILURE);
+ SSL_F_CONSTRUCT_STATELESS_TICKET, ERR_R_MALLOC_FAILURE);
goto err;
}
ctx = EVP_CIPHER_CTX_new();
hctx = HMAC_CTX_new();
if (ctx == NULL || hctx == NULL) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR,
- SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET, ERR_R_MALLOC_FAILURE);
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CONSTRUCT_STATELESS_TICKET,
+ ERR_R_MALLOC_FAILURE);
goto err;
}
p = senc;
if (!i2d_SSL_SESSION(s->session, &p)) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR,
- SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET, ERR_R_INTERNAL_ERROR);
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CONSTRUCT_STATELESS_TICKET,
+ ERR_R_INTERNAL_ERROR);
goto err;
}
const_p = senc;
sess = d2i_SSL_SESSION(NULL, &const_p, slen_full);
if (sess == NULL) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR,
- SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET, ERR_R_INTERNAL_ERROR);
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CONSTRUCT_STATELESS_TICKET,
+ ERR_R_INTERNAL_ERROR);
goto err;
}
- sess->session_id_length = 0; /* ID is irrelevant for the ticket */
slen = i2d_SSL_SESSION(sess, NULL);
if (slen == 0 || slen > slen_full) {
/* shouldn't ever happen */
- SSLfatal(s, SSL_AD_INTERNAL_ERROR,
- SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET, ERR_R_INTERNAL_ERROR);
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CONSTRUCT_STATELESS_TICKET,
+ ERR_R_INTERNAL_ERROR);
SSL_SESSION_free(sess);
goto err;
}
p = senc;
if (!i2d_SSL_SESSION(sess, &p)) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR,
- SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET, ERR_R_INTERNAL_ERROR);
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CONSTRUCT_STATELESS_TICKET,
+ ERR_R_INTERNAL_ERROR);
SSL_SESSION_free(sess);
goto err;
}
if (!WPACKET_put_bytes_u32(pkt, 0)
|| !WPACKET_put_bytes_u16(pkt, 0)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR,
- SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET,
+ SSL_F_CONSTRUCT_STATELESS_TICKET,
ERR_R_INTERNAL_ERROR);
goto err;
}
return 1;
}
if (ret < 0) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR,
- SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET,
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CONSTRUCT_STATELESS_TICKET,
SSL_R_CALLBACK_FAILED);
goto err;
}
const EVP_CIPHER *cipher = EVP_aes_256_cbc();
iv_len = EVP_CIPHER_iv_length(cipher);
- if (ssl_randbytes(s, iv, iv_len) <= 0
+ if (RAND_bytes(iv, iv_len) <= 0
|| !EVP_EncryptInit_ex(ctx, cipher, NULL,
- tctx->ext.tick_aes_key, iv)
- || !HMAC_Init_ex(hctx, tctx->ext.tick_hmac_key,
- sizeof(tctx->ext.tick_hmac_key),
+ tctx->ext.secure->tick_aes_key, iv)
+ || !HMAC_Init_ex(hctx, tctx->ext.secure->tick_hmac_key,
+ sizeof(tctx->ext.secure->tick_hmac_key),
EVP_sha256(), NULL)) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR,
- SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET,
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CONSTRUCT_STATELESS_TICKET,
ERR_R_INTERNAL_ERROR);
goto err;
}
sizeof(tctx->ext.tick_key_name));
}
- /*
- * Ticket lifetime hint: For TLSv1.2 this is advisory only and we leave this
- * unspecified for resumed session (for simplicity).
- * In TLSv1.3 we reset the "time" field above, and always specify the
- * timeout.
- */
- if (!WPACKET_put_bytes_u32(pkt,
- (s->hit && !SSL_IS_TLS13(s))
- ? 0 : s->session->timeout)
- || (SSL_IS_TLS13(s)
- && (!WPACKET_put_bytes_u32(pkt, age_add_u.age_add)
- || !WPACKET_sub_memcpy_u8(pkt, s->session->ext.tick_nonce,
- s->session->ext.tick_nonce_len)))
- /* Now the actual ticket data */
- || !WPACKET_start_sub_packet_u16(pkt)
- || !WPACKET_get_total_written(pkt, &macoffset)
+ if (!create_ticket_prequel(s, pkt, age_add, tick_nonce)) {
+ /* SSLfatal() already called */
+ goto err;
+ }
+
+ if (!WPACKET_get_total_written(pkt, &macoffset)
/* Output key name */
|| !WPACKET_memcpy(pkt, key_name, sizeof(key_name))
/* output IV */
|| !HMAC_Final(hctx, macdata1, &hlen)
|| hlen > EVP_MAX_MD_SIZE
|| !WPACKET_allocate_bytes(pkt, hlen, &macdata2)
- || macdata1 != macdata2
- || !WPACKET_close(pkt)) {
+ || macdata1 != macdata2) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR,
- SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET, ERR_R_INTERNAL_ERROR);
+ SSL_F_CONSTRUCT_STATELESS_TICKET, ERR_R_INTERNAL_ERROR);
goto err;
}
- if (SSL_IS_TLS13(s)
- && !tls_construct_extensions(s, pkt,
- SSL_EXT_TLS1_3_NEW_SESSION_TICKET,
- NULL, 0)) {
- /* SSLfatal() already called */
+
+ /* Close the sub-packet created by create_ticket_prequel() */
+ if (!WPACKET_close(pkt)) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CONSTRUCT_STATELESS_TICKET,
+ ERR_R_INTERNAL_ERROR);
goto err;
}
- EVP_CIPHER_CTX_free(ctx);
- HMAC_CTX_free(hctx);
- OPENSSL_free(senc);
- return 1;
+ ok = 1;
err:
OPENSSL_free(senc);
EVP_CIPHER_CTX_free(ctx);
HMAC_CTX_free(hctx);
+ return ok;
+}
+
+static int construct_stateful_ticket(SSL *s, WPACKET *pkt, uint32_t age_add,
+ unsigned char *tick_nonce)
+{
+ if (!create_ticket_prequel(s, pkt, age_add, tick_nonce)) {
+ /* SSLfatal() already called */
+ return 0;
+ }
+
+ if (!WPACKET_memcpy(pkt, s->session->session_id,
+ s->session->session_id_length)
+ || !WPACKET_close(pkt)) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CONSTRUCT_STATEFUL_TICKET,
+ ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
+ return 1;
+}
+
+int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt)
+{
+ SSL_CTX *tctx = s->session_ctx;
+ unsigned char tick_nonce[TICKET_NONCE_SIZE];
+ union {
+ unsigned char age_add_c[sizeof(uint32_t)];
+ uint32_t age_add;
+ } age_add_u;
+
+ age_add_u.age_add = 0;
+
+ if (SSL_IS_TLS13(s)) {
+ size_t i, hashlen;
+ uint64_t nonce;
+ static const unsigned char nonce_label[] = "resumption";
+ const EVP_MD *md = ssl_handshake_md(s);
+ void (*cb) (const SSL *ssl, int type, int val) = NULL;
+ int hashleni = EVP_MD_size(md);
+
+ /* Ensure cast to size_t is safe */
+ if (!ossl_assert(hashleni >= 0)) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+ SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET,
+ ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ hashlen = (size_t)hashleni;
+
+ if (s->info_callback != NULL)
+ cb = s->info_callback;
+ else if (s->ctx->info_callback != NULL)
+ cb = s->ctx->info_callback;
+
+ if (cb != NULL) {
+ /*
+ * We don't start and stop the handshake in between each ticket when
+ * sending more than one - but it should appear that way to the info
+ * callback.
+ */
+ if (s->sent_tickets != 0) {
+ ossl_statem_set_in_init(s, 0);
+ cb(s, SSL_CB_HANDSHAKE_DONE, 1);
+ ossl_statem_set_in_init(s, 1);
+ }
+ cb(s, SSL_CB_HANDSHAKE_START, 1);
+ }
+ /*
+ * If we already sent one NewSessionTicket, or we resumed then
+ * s->session may already be in a cache and so we must not modify it.
+ * Instead we need to take a copy of it and modify that.
+ */
+ if (s->sent_tickets != 0 || s->hit) {
+ SSL_SESSION *new_sess = ssl_session_dup(s->session, 0);
+
+ if (new_sess == NULL) {
+ /* SSLfatal already called */
+ goto err;
+ }
+
+ SSL_SESSION_free(s->session);
+ s->session = new_sess;
+ }
+
+ if (!ssl_generate_session_id(s, s->session)) {
+ /* SSLfatal() already called */
+ goto err;
+ }
+ if (RAND_bytes(age_add_u.age_add_c, sizeof(age_add_u)) <= 0) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+ SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET,
+ ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ s->session->ext.tick_age_add = age_add_u.age_add;
+
+ nonce = s->next_ticket_nonce;
+ for (i = TICKET_NONCE_SIZE; i > 0; i--) {
+ tick_nonce[i - 1] = (unsigned char)(nonce & 0xff);
+ nonce >>= 8;
+ }
+
+ if (!tls13_hkdf_expand(s, md, s->resumption_master_secret,
+ nonce_label,
+ sizeof(nonce_label) - 1,
+ tick_nonce,
+ TICKET_NONCE_SIZE,
+ s->session->master_key,
+ hashlen)) {
+ /* SSLfatal() already called */
+ goto err;
+ }
+ s->session->master_key_length = hashlen;
+
+ s->session->time = (long)time(NULL);
+ if (s->s3->alpn_selected != NULL) {
+ OPENSSL_free(s->session->ext.alpn_selected);
+ s->session->ext.alpn_selected =
+ OPENSSL_memdup(s->s3->alpn_selected, s->s3->alpn_selected_len);
+ if (s->session->ext.alpn_selected == NULL) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+ SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET,
+ ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+ s->session->ext.alpn_selected_len = s->s3->alpn_selected_len;
+ }
+ s->session->ext.max_early_data = s->max_early_data;
+ }
+
+ if (tctx->generate_ticket_cb != NULL &&
+ tctx->generate_ticket_cb(s, tctx->ticket_cb_data) == 0)
+ goto err;
+
+ /*
+ * If we are using anti-replay protection then we behave as if
+ * SSL_OP_NO_TICKET is set - we are caching tickets anyway so there
+ * is no point in using full stateless tickets.
+ */
+ if (SSL_IS_TLS13(s)
+ && ((s->options & SSL_OP_NO_TICKET) != 0
+ || (s->max_early_data > 0
+ && (s->options & SSL_OP_NO_ANTI_REPLAY) == 0))) {
+ if (!construct_stateful_ticket(s, pkt, age_add_u.age_add, tick_nonce)) {
+ /* SSLfatal() already called */
+ goto err;
+ }
+ } else if (!construct_stateless_ticket(s, pkt, age_add_u.age_add,
+ tick_nonce)) {
+ /* SSLfatal() already called */
+ goto err;
+ }
+
+ if (SSL_IS_TLS13(s)) {
+ if (!tls_construct_extensions(s, pkt,
+ SSL_EXT_TLS1_3_NEW_SESSION_TICKET,
+ NULL, 0)) {
+ /* SSLfatal() already called */
+ goto err;
+ }
+ /*
+ * Increment both |sent_tickets| and |next_ticket_nonce|. |sent_tickets|
+ * gets reset to 0 if we send more tickets following a post-handshake
+ * auth, but |next_ticket_nonce| does not.
+ */
+ s->sent_tickets++;
+ s->next_ticket_nonce++;
+ ssl_update_cache(s, SSL_SESS_CACHE_SERVER);
+ }
+
+ return 1;
+ err:
return 0;
}