Cleanup ticket processing and session lookup.
Use more sensible variable names. Also move some work between the helpers and
s3_srvr.c a little; the session lookup functions now only return a new session.
Whether to send a ticket is now an additional output to avoid the enum
explosion around renewal. The actual SSL state is not modified.
This is somewhat cleaner as s3_srvr.c may still reject a session for other
reasons, so we avoid setting ssl->session and ssl->verify_result to a session
that wouldn't be used. (They get fixed up in ssl_get_new_session, so it didn't
actually matter.)
Change-Id: Ib52fabbe993b5e2b7408395a02cdea3dee66df7b
Reviewed-on: https://boringssl-review.googlesource.com/5235
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 5259793..84ce436 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -595,6 +595,10 @@
OPENSSL_EXPORT int SSL_CTX_set_tlsext_ticket_keys(SSL_CTX *ctx, const void *in,
size_t len);
+/* SSL_TICKET_KEY_NAME_LEN is the length of the key name prefix of a session
+ * ticket. */
+#define SSL_TICKET_KEY_NAME_LEN 16
+
/* SSL_CTX_set_tlsext_ticket_key_cb sets the ticket callback to |callback| and
* returns one. |callback| will be called when encrypting a new ticket and when
* decrypting a ticket from the client.
@@ -1113,7 +1117,7 @@
int (*tlsext_servername_callback)(SSL *, int *, void *);
void *tlsext_servername_arg;
/* RFC 4507 session ticket keys */
- uint8_t tlsext_tick_key_name[16];
+ uint8_t tlsext_tick_key_name[SSL_TICKET_KEY_NAME_LEN];
uint8_t tlsext_tick_hmac_key[16];
uint8_t tlsext_tick_aes_key[16];
/* Callback to support customisation of ticket key setting */
diff --git a/ssl/internal.h b/ssl/internal.h
index c136167..c6f7fd9 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -507,8 +507,6 @@
* 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
@@ -828,7 +826,23 @@
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(
@@ -1094,8 +1108,16 @@
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);
+
+/* 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);
int tls12_get_sigandhash(SSL *ssl, uint8_t *p, const EVP_PKEY *pk,
const EVP_MD *md);
diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c
index 3e6903d..0a26689 100644
--- a/ssl/s3_srvr.c
+++ b/ssl/s3_srvr.c
@@ -815,6 +815,7 @@
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
@@ -935,13 +936,17 @@
}
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,8 +961,8 @@
&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. */
@@ -970,13 +975,20 @@
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;
}
@@ -1132,6 +1144,7 @@
err:
sk_SSL_CIPHER_free(ciphers);
+ SSL_SESSION_free(session);
return ret;
}
diff --git a/ssl/ssl_sess.c b/ssl/ssl_sess.c
index 765c9d9..f5c4249 100644
--- a/ssl/ssl_sess.c
+++ b/ssl/ssl_sess.c
@@ -133,6 +133,7 @@
* OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR
* OTHERWISE. */
+#include <assert.h>
#include <stdio.h>
#include <string.h>
@@ -358,120 +359,110 @@
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;
+/* 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 > SSL_MAX_SSL_SESSION_ID_LENGTH) {
- goto err;
+ if (session_id_len == 0 || session_id_len > SSL_MAX_SSL_SESSION_ID_LENGTH) {
+ return ssl_session_success;
}
- if (ctx->session_id_len == 0) {
- try_session_cache = 0;
- }
-
- 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);
- if (ret != NULL) {
- SSL_SESSION_up_ref(ret);
- }
- CRYPTO_MUTEX_unlock(&s->initial_ctx->lock);
- }
-
- 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);
- }
-
- /* 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);
- }
+ if (session != NULL) {
+ *out_session = session;
+ return ssl_session_success;
}
}
- if (ret == NULL) {
- goto err;
+ /* 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;
}
- /* Now ret is non-NULL and we own one of its reference counts. */
-
- 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 */
+ /* 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 ((s->verify_mode & SSL_VERIFY_PEER) && s->sid_ctx_length == 0) {
+ /* 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);
+ }
+
+ *out_session = session;
+ return ssl_session_success;
+}
+
+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;
+ size_t ticket_len;
+ 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 (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.
@@ -482,37 +473,30 @@
* noticing). */
OPENSSL_PUT_ERROR(SSL, ssl_get_prev_session,
SSL_R_SESSION_ID_CONTEXT_UNINITIALIZED);
- fatal = 1;
- goto err;
+ 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) {
@@ -528,12 +512,13 @@
CRYPTO_MUTEX_lock_write(&ctx->lock);
if (!lh_SSL_SESSION_insert(ctx->sessions, &s, c)) {
CRYPTO_MUTEX_unlock(&ctx->lock);
+ SSL_SESSION_free(c);
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. */
+ /* s != NULL iff we already had a session with the given session ID. 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) {
/* We *are* in trouble ... */
SSL_SESSION_list_remove(ctx, s);
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index 654cf6a..d5aa8d5 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -107,6 +107,7 @@
* Hudson (tjh@cryptsoft.com). */
#include <assert.h>
+#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -122,9 +123,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);
@@ -2288,195 +2286,124 @@
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;
+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;
- /* 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;
+ HMAC_CTX hmac_ctx;
+ HMAC_CTX_init(&hmac_ctx);
+ EVP_CIPHER_CTX cipher_ctx;
+ EVP_CIPHER_CTX_init(&cipher_ctx);
+
+ *out_send_ticket = 0;
+ *out_session = NULL;
+
+ if (session_id_len > SSL_MAX_SSL_SESSION_ID_LENGTH) {
+ goto done;
}
- if (len == 0) {
+ if (ticket_len == 0) {
/* The client will accept a ticket but doesn't currently have one. */
- s->tlsext_ticket_expected = 1;
- return 1;
+ *out_send_ticket = 1;
+ goto done;
}
- 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;
-
- case 3: /* ticket was decrypted */
- return r;
-
- case 4: /* ticket decrypted but need to renew */
- s->tlsext_ticket_expected = 1;
- return 3;
-
- default: /* fatal error */
- return -1;
- }
-}
-
-/* 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;
-
/* 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;
+ if (ticket_len < SSL_TICKET_KEY_NAME_LEN + EVP_MAX_IV_LENGTH) {
+ goto done;
}
+ const uint8_t *iv = ticket + SSL_TICKET_KEY_NAME_LEN;
- /* 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 (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;
+ goto done;
}
- 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;
+ 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;
}
- /* 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;
+ /* 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;
}
- 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;
+ if (ciphertext_len >= INT_MAX) {
+ goto done;
}
- 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;
+ 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;
}
- ERR_clear_error();
- /* For session parse failure, indicate that we need to send a new ticket. */
- return 2;
+ /* 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;
+ }
+
+ /* 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 */