Rework the decrypt ticket callback

Don't call the decrypt ticket callback if we've already encountered a
fatal error. Do call it if we have an empty ticket present.

Change the return code to have 5 distinct returns codes and separate it
from the input status value.

Reviewed-by: Viktor Dukhovni <viktor@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/6198)
This commit is contained in:
Matt Caswell 2018-05-09 18:22:36 +01:00
parent c20e3b282c
commit 61fb59238d
9 changed files with 341 additions and 150 deletions

View File

@ -16,7 +16,7 @@ SSL_CTX_decrypt_session_ticket_fn - manage session ticket application data
typedef SSL_TICKET_RETURN (*SSL_CTX_decrypt_session_ticket_fn)(SSL *s, SSL_SESSION *ss, typedef SSL_TICKET_RETURN (*SSL_CTX_decrypt_session_ticket_fn)(SSL *s, SSL_SESSION *ss,
const unsigned char *keyname, const unsigned char *keyname,
size_t keyname_len, size_t keyname_len,
SSL_TICKET_RETURN retv, SSL_TICKET_STATUS status,
void *arg); void *arg);
int SSL_CTX_set_session_ticket_cb(SSL_CTX *ctx, int SSL_CTX_set_session_ticket_cb(SSL_CTX *ctx,
SSL_CTX_generate_session_ticket_fn gen_cb, SSL_CTX_generate_session_ticket_fn gen_cb,
@ -39,13 +39,13 @@ is the same as that given to SSL_CTX_set_session_ticket_cb(). The B<gen_cb>
callback is defined as type B<SSL_CTX_generate_session_ticket_fn>. callback is defined as type B<SSL_CTX_generate_session_ticket_fn>.
B<dec_cb> is the application defined callback invoked after session ticket B<dec_cb> is the application defined callback invoked after session ticket
decryption has been attempted and any session ticket application data is available. decryption has been attempted and any session ticket application data is
The application can call SSL_SESSION_get_ticket_appdata() at this time to retrieve available. If ticket decryption was successful then the B<ss> argument contains
the application data. The value of B<arg> is the same as that given to the session data. The B<keyname> and B<keyname_len> arguments identify the key
SSL_CTX_set_session_ticket_cb(). The B<retv> argument is the result of the ticket used to decrypt the session ticket. The B<status> argument is the result of the
decryption. The B<keyname> and B<keyname_len> identify the key used to decrypt the ticket decryption. See the L<NOTES> section below for further details. The value
session ticket. The B<dec_cb> callback is defined as type of B<arg> is the same as that given to SSL_CTX_set_session_ticket_cb(). The
B<SSL_CTX_decrypt_session_ticket_fn>. B<dec_cb> callback is defined as type B<SSL_CTX_decrypt_session_ticket_fn>.
SSL_SESSION_set1_ticket_appdata() sets the application data specified by SSL_SESSION_set1_ticket_appdata() sets the application data specified by
B<data> and B<len> into B<ss> which is then placed into any generated session B<data> and B<len> into B<ss> which is then placed into any generated session
@ -66,14 +66,90 @@ application that a session ticket has just been decrypted.
=head1 NOTES =head1 NOTES
When the B<dec_cb> callback is invoked, the SSL_SESSION B<ss> has not yet been When the B<dec_cb> callback is invoked, the SSL_SESSION B<ss> has not yet been
assigned to the SSL B<s>. The B<retv> indicates the result of the ticket assigned to the SSL B<s>. The B<status> indicates the result of the ticket
decryption which can be modified by the callback before being returned. The decryption. The callback must check the B<status> value before performing any
callback must check the B<retv> value before performing any action, as it's action, as it is called even if ticket decryption fails.
called even if ticket decryption fails.
The B<keyname> and B<keyname_len> arguments to B<dec_cb> may be used to identify The B<keyname> and B<keyname_len> arguments to B<dec_cb> may be used to identify
the key that was used to encrypt the session ticket. the key that was used to encrypt the session ticket.
The B<status> argument can be any of these values:
=over 4
=item SSL_TICKET_EMPTY
Empty ticket present. No ticket data will be used and a new ticket should be
sent to the client. This only occurs in TLSv1.2 or below. In TLSv1.3 it is not
valid for a client to send an empty ticket.
=item SSL_TICKET_NO_DECRYPT
The ticket couldn't be decrypted. No ticket data will be used and a new ticket
should be sent to the client.
=item SSL_TICKET_SUCCESS
A ticket was successfully decrypted, any session ticket application data should
be available. A new ticket should not be sent to the client.
=item SSL_TICKET_SUCCESS_RENEW
Same as B<SSL_TICKET_SUCCESS>, but a new ticket should be sent to the client.
=back
The return value can be any of these values:
=over 4
=item SSL_TICKET_RETURN_ABORT
The handshake should be aborted, either because of an error or because of some
policy. Note that in TLSv1.3 a client may send more than one ticket in a single
handshake. Therefore just because one ticket is unacceptable it does not mean
that all of them are. For this reason this option should be used with caution.
=item SSL_TICKET_RETURN_IGNORE
Do not use a ticket (if one was available). Do not send a renewed ticket to the
client.
=item SSL_TICKET_RETURN_IGNORE_RENEW
Do not use a ticket (if one was available). Send a renewed ticket to the client.
If the callback does not wish to change the default ticket behaviour then it
should return this value if B<status> is B<SSL_TICKET_EMPTY> or
B<SSL_TICKET_NO_DECRYPT>.
=item SSL_TICKET_RETURN_USE
Use the ticket. Do not send a renewed ticket to the client. It is an error for
the callback to return this value if B<status> has a value other than
B<SSL_TICKET_SUCCESS> or B<SSL_TICKET_SUCCESS_RENEW>.
If the callback does not wish to change the default ticket behaviour then it
should return this value if B<status> is B<SSL_TICKET_SUCCESS>.
=item SSL_TICKET_RETURN_USE_RENEW
Use the ticket. Send a renewed ticket to the client. It is an error for the
callback to return this value if B<status> has a value other than
B<SSL_TICKET_SUCCESS> or B<SSL_TICKET_SUCCESS_RENEW>.
If the callback does not wish to change the default ticket behaviour then it
should return this value if B<status> is B<SSL_TICKET_SUCCESS_RENEW>.
=back
If B<status> has the value B<SSL_TICKET_EMPTY> or B<SSL_TICKET_NO_DECRYPT> then
no session data will be available and the callback must not use the B<ss>
argument. If B<status> has the value B<SSL_TICKET_SUCCESS> or
B<SSL_TICKET_SUCCESS_RENEW> then the application can call
SSL_SESSION_get0_ticket_appdata() using the session provided in the B<ss>
argument to retrieve the application data.
When the B<gen_cb> callback is invoked, the SSL_get_session() function can be When the B<gen_cb> callback is invoked, the SSL_get_session() function can be
used to retrieve the SSL_SESSION for SSL_SESSION_set1_ticket_appdata(). used to retrieve the SSL_SESSION for SSL_SESSION_set1_ticket_appdata().
@ -92,46 +168,7 @@ failure.
The B<gen_cb> callback must return 1 to continue the connection. A return of 0 The B<gen_cb> callback must return 1 to continue the connection. A return of 0
will terminate the connection with an INTERNAL_ERROR alert. will terminate the connection with an INTERNAL_ERROR alert.
The B<dec_cb> callback must return one of the following B<SSL_TICKET_RETURN> The B<dec_cb> callback must return a value as described in L<NOTES> above.
values. Under normal circumstances the B<retv> value is returned unmodified,
but the callback can change the behavior of the post-ticket decryption code
by returning something different. The B<dec_cb> callback must check the B<retv>
value before performing any action.
typedef int SSL_TICKET_RETURN;
=over 4
=item SSL_TICKET_FATAL_ERR_MALLOC
Fatal error, malloc failure.
=item SSL_TICKET_FATAL_ERR_OTHER
Fatal error, either from parsing or decrypting the ticket.
=item SSL_TICKET_NONE
No ticket present.
=item SSL_TICKET_EMPTY
Empty ticket present.
=item SSL_TICKET_NO_DECRYPT
The ticket couldn't be decrypted.
=item SSL_TICKET_SUCCESS
A ticket was successfully decrypted, any session ticket application data should
be available.
=item TICKET_SUCCESS_RENEW
Same as B<TICKET_SUCCESS>, but the ticket needs to be renewed.
=back
=head1 SEE ALSO =head1 SEE ALSO

View File

@ -2330,8 +2330,9 @@ __owur const struct openssl_ssl_test_functions *SSL_test_functions(void);
__owur int SSL_free_buffers(SSL *ssl); __owur int SSL_free_buffers(SSL *ssl);
__owur int SSL_alloc_buffers(SSL *ssl); __owur int SSL_alloc_buffers(SSL *ssl);
/* Return codes for tls_get_ticket_from_client() and tls_decrypt_ticket() */ /* Status codes passed to the decrypt session ticket callback. Some of these
typedef int SSL_TICKET_RETURN; * are for internal use only and are never passed to the callback. */
typedef int SSL_TICKET_STATUS;
/* Support for ticket appdata */ /* Support for ticket appdata */
/* fatal error, malloc failure */ /* fatal error, malloc failure */
@ -2349,11 +2350,25 @@ typedef int SSL_TICKET_RETURN;
/* same as above but the ticket needs to be renewed */ /* same as above but the ticket needs to be renewed */
# define SSL_TICKET_SUCCESS_RENEW 6 # define SSL_TICKET_SUCCESS_RENEW 6
/* Return codes for the decrypt session ticket callback */
typedef int SSL_TICKET_RETURN;
/* An error occurred */
#define SSL_TICKET_RETURN_ABORT 0
/* Do not use the ticket, do not send a renewed ticket to the client */
#define SSL_TICKET_RETURN_IGNORE 1
/* Do not use the ticket, send a renewed ticket to the client */
#define SSL_TICKET_RETURN_IGNORE_RENEW 2
/* Use the ticket, do not send a renewed ticket to the client */
#define SSL_TICKET_RETURN_USE 3
/* Use the ticket, send a renewed ticket to the client */
#define SSL_TICKET_RETURN_USE_RENEW 4
typedef int (*SSL_CTX_generate_session_ticket_fn)(SSL *s, void *arg); typedef int (*SSL_CTX_generate_session_ticket_fn)(SSL *s, void *arg);
typedef SSL_TICKET_RETURN (*SSL_CTX_decrypt_session_ticket_fn)(SSL *s, SSL_SESSION *ss, typedef SSL_TICKET_RETURN (*SSL_CTX_decrypt_session_ticket_fn)(SSL *s, SSL_SESSION *ss,
const unsigned char *keyname, const unsigned char *keyname,
size_t keyname_length, size_t keyname_length,
SSL_TICKET_RETURN retv, SSL_TICKET_STATUS status,
void *arg); void *arg);
int SSL_CTX_set_session_ticket_cb(SSL_CTX *ctx, int SSL_CTX_set_session_ticket_cb(SSL_CTX *ctx,
SSL_CTX_generate_session_ticket_fn gen_cb, SSL_CTX_generate_session_ticket_fn gen_cb,

View File

@ -2473,9 +2473,9 @@ void tls1_get_supported_groups(SSL *s, const uint16_t **pgroups,
__owur int tls1_set_server_sigalgs(SSL *s); __owur int tls1_set_server_sigalgs(SSL *s);
__owur SSL_TICKET_RETURN tls_get_ticket_from_client(SSL *s, CLIENTHELLO_MSG *hello, __owur SSL_TICKET_STATUS tls_get_ticket_from_client(SSL *s, CLIENTHELLO_MSG *hello,
SSL_SESSION **ret); SSL_SESSION **ret);
__owur SSL_TICKET_RETURN tls_decrypt_ticket(SSL *s, const unsigned char *etick, __owur SSL_TICKET_STATUS tls_decrypt_ticket(SSL *s, const unsigned char *etick,
size_t eticklen, size_t eticklen,
const unsigned char *sess_id, const unsigned char *sess_id,
size_t sesslen, SSL_SESSION **psess); size_t sesslen, SSL_SESSION **psess);

View File

@ -485,9 +485,14 @@ int ssl_get_prev_session(SSL *s, CLIENTHELLO_MSG *hello)
SSL_SESSION *ret = NULL; SSL_SESSION *ret = NULL;
int fatal = 0, discard; int fatal = 0, discard;
int try_session_cache = 0; int try_session_cache = 0;
SSL_TICKET_RETURN r; SSL_TICKET_STATUS r;
if (SSL_IS_TLS13(s)) { if (SSL_IS_TLS13(s)) {
/*
* By default we will send a new ticket. This can be overridden in the
* ticket processing.
*/
s->ext.ticket_expected = 1;
if (!tls_parse_extension(s, TLSEXT_IDX_psk_kex_modes, if (!tls_parse_extension(s, TLSEXT_IDX_psk_kex_modes,
SSL_EXT_CLIENT_HELLO, hello->pre_proc_exts, SSL_EXT_CLIENT_HELLO, hello->pre_proc_exts,
NULL, 0) NULL, 0)

View File

@ -1030,6 +1030,7 @@ int tls_parse_ctos_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
return 0; return 0;
} }
s->ext.ticket_expected = 0;
for (id = 0; PACKET_remaining(&identities) != 0; id++) { for (id = 0; PACKET_remaining(&identities) != 0; id++) {
PACKET identity; PACKET identity;
unsigned long ticket_agel; unsigned long ticket_agel;
@ -1127,9 +1128,17 @@ int tls_parse_ctos_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
s->ext.early_data_ok = 1; s->ext.early_data_ok = 1;
} else { } else {
uint32_t ticket_age = 0, now, agesec, agems; uint32_t ticket_age = 0, now, agesec, agems;
int ret = tls_decrypt_ticket(s, PACKET_data(&identity), int ret;
PACKET_remaining(&identity), NULL, 0,
&sess); ret = tls_decrypt_ticket(s, PACKET_data(&identity),
PACKET_remaining(&identity), NULL, 0,
&sess);
if (ret == SSL_TICKET_EMPTY) {
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PARSE_CTOS_PSK,
SSL_R_BAD_EXTENSION);
return 0;
}
if (ret == SSL_TICKET_FATAL_ERR_MALLOC if (ret == SSL_TICKET_FATAL_ERR_MALLOC
|| ret == SSL_TICKET_FATAL_ERR_OTHER) { || ret == SSL_TICKET_FATAL_ERR_OTHER) {
@ -1137,7 +1146,7 @@ int tls_parse_ctos_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
SSL_F_TLS_PARSE_CTOS_PSK, ERR_R_INTERNAL_ERROR); SSL_F_TLS_PARSE_CTOS_PSK, ERR_R_INTERNAL_ERROR);
return 0; return 0;
} }
if (ret == SSL_TICKET_NO_DECRYPT) if (ret == SSL_TICKET_NONE || ret == SSL_TICKET_NO_DECRYPT)
continue; continue;
/* Check for replay */ /* Check for replay */

View File

@ -489,10 +489,10 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL *s)
st->hand_state = TLS_ST_SW_SESSION_TICKET; st->hand_state = TLS_ST_SW_SESSION_TICKET;
if (s->post_handshake_auth == SSL_PHA_REQUESTED) { if (s->post_handshake_auth == SSL_PHA_REQUESTED) {
s->post_handshake_auth = SSL_PHA_EXT_RECEIVED; s->post_handshake_auth = SSL_PHA_EXT_RECEIVED;
} else if (s->hit && !s->ext.ticket_expected) { } else if (!s->ext.ticket_expected) {
/* /*
* If we resumed and we're not going to renew the ticket then we * If we're not going to renew the ticket then we just finish the
* just finish the handshake at this point. * handshake at this point.
*/ */
st->hand_state = TLS_ST_OK; st->hand_state = TLS_ST_OK;
} }

View File

@ -1194,20 +1194,8 @@ int tls1_set_server_sigalgs(SSL *s)
* hello: The parsed ClientHello data * hello: The parsed ClientHello data
* ret: (output) on return, if a ticket was decrypted, then this is set to * ret: (output) on return, if a ticket was decrypted, then this is set to
* point to the resulting session. * point to the resulting session.
*
* If s->tls_session_secret_cb is set then we are expecting a pre-shared key
* ciphersuite, in which case we have no use for session tickets and one will
* never be decrypted, nor will s->ext.ticket_expected be set to 1.
*
* Side effects:
* Sets s->ext.ticket_expected to 1 if the server will have to issue
* a new session ticket to the client because the client indicated support
* (and s->tls_session_secret_cb is NULL) but the client either doesn't have
* a session ticket or we couldn't use the one it gave us, or if
* s->ctx->ext.ticket_key_cb asked to renew the client's ticket.
* Otherwise, s->ext.ticket_expected is set to 0.
*/ */
SSL_TICKET_RETURN tls_get_ticket_from_client(SSL *s, CLIENTHELLO_MSG *hello, SSL_TICKET_STATUS tls_get_ticket_from_client(SSL *s, CLIENTHELLO_MSG *hello,
SSL_SESSION **ret) SSL_SESSION **ret)
{ {
size_t size; size_t size;
@ -1229,23 +1217,6 @@ SSL_TICKET_RETURN tls_get_ticket_from_client(SSL *s, CLIENTHELLO_MSG *hello,
return SSL_TICKET_NONE; return SSL_TICKET_NONE;
size = PACKET_remaining(&ticketext->data); size = PACKET_remaining(&ticketext->data);
if (size == 0) {
/*
* The client will accept a ticket but doesn't currently have
* one.
*/
s->ext.ticket_expected = 1;
return SSL_TICKET_EMPTY;
}
if (s->ext.session_secret_cb) {
/*
* Indicate that the ticket couldn't be decrypted rather than
* generating the session from ticket now, trigger
* abbreviated handshake based on external mechanism to
* calculate the master secret later.
*/
return SSL_TICKET_NO_DECRYPT;
}
return tls_decrypt_ticket(s, PACKET_data(&ticketext->data), size, return tls_decrypt_ticket(s, PACKET_data(&ticketext->data), size,
hello->session_id, hello->session_id_len, ret); hello->session_id, hello->session_id_len, ret);
@ -1254,6 +1225,19 @@ SSL_TICKET_RETURN tls_get_ticket_from_client(SSL *s, CLIENTHELLO_MSG *hello,
/*- /*-
* tls_decrypt_ticket attempts to decrypt a session ticket. * tls_decrypt_ticket attempts to decrypt a session ticket.
* *
* If s->tls_session_secret_cb is set and we're not doing TLSv1.3 then we are
* expecting a pre-shared key ciphersuite, in which case we have no use for
* session tickets and one will never be decrypted, nor will
* s->ext.ticket_expected be set to 1.
*
* Side effects:
* Sets s->ext.ticket_expected to 1 if the server will have to issue
* a new session ticket to the client because the client indicated support
* (and s->tls_session_secret_cb is NULL) but the client either doesn't have
* a session ticket or we couldn't use the one it gave us, or if
* s->ctx->ext.ticket_key_cb asked to renew the client's ticket.
* Otherwise, s->ext.ticket_expected is set to 0.
*
* etick: points to the body of the session ticket extension. * etick: points to the body of the session ticket extension.
* eticklen: the length of the session tickets extension. * eticklen: the length of the session tickets extension.
* sess_id: points at the session ID. * sess_id: points at the session ID.
@ -1261,21 +1245,40 @@ SSL_TICKET_RETURN tls_get_ticket_from_client(SSL *s, CLIENTHELLO_MSG *hello,
* psess: (output) on return, if a ticket was decrypted, then this is set to * psess: (output) on return, if a ticket was decrypted, then this is set to
* point to the resulting session. * point to the resulting session.
*/ */
SSL_TICKET_RETURN tls_decrypt_ticket(SSL *s, const unsigned char *etick, SSL_TICKET_STATUS tls_decrypt_ticket(SSL *s, const unsigned char *etick,
size_t eticklen, const unsigned char *sess_id, size_t eticklen, const unsigned char *sess_id,
size_t sesslen, SSL_SESSION **psess) size_t sesslen, SSL_SESSION **psess)
{ {
SSL_SESSION *sess; SSL_SESSION *sess = NULL;
unsigned char *sdec; unsigned char *sdec;
const unsigned char *p; const unsigned char *p;
int slen, renew_ticket = 0, declen; int slen, renew_ticket = 0, declen;
SSL_TICKET_RETURN ret = SSL_TICKET_FATAL_ERR_OTHER; SSL_TICKET_STATUS ret = SSL_TICKET_FATAL_ERR_OTHER;
size_t mlen; size_t mlen;
unsigned char tick_hmac[EVP_MAX_MD_SIZE]; unsigned char tick_hmac[EVP_MAX_MD_SIZE];
HMAC_CTX *hctx = NULL; HMAC_CTX *hctx = NULL;
EVP_CIPHER_CTX *ctx = NULL; EVP_CIPHER_CTX *ctx = NULL;
SSL_CTX *tctx = s->session_ctx; SSL_CTX *tctx = s->session_ctx;
if (eticklen == 0) {
/*
* The client will accept a ticket but doesn't currently have
* one (TLSv1.2 and below), or treated as a fatal error in TLSv1.3
*/
ret = SSL_TICKET_EMPTY;
goto end;
}
if (!SSL_IS_TLS13(s) && s->ext.session_secret_cb) {
/*
* Indicate that the ticket couldn't be decrypted rather than
* generating the session from ticket now, trigger
* abbreviated handshake based on external mechanism to
* calculate the master secret later.
*/
ret = SSL_TICKET_NO_DECRYPT;
goto end;
}
/* Need at least keyname + iv */ /* Need at least keyname + iv */
if (eticklen < TLSEXT_KEYNAME_LENGTH + EVP_MAX_IV_LENGTH) { if (eticklen < TLSEXT_KEYNAME_LENGTH + EVP_MAX_IV_LENGTH) {
ret = SSL_TICKET_NO_DECRYPT; ret = SSL_TICKET_NO_DECRYPT;
@ -1394,7 +1397,6 @@ SSL_TICKET_RETURN tls_decrypt_ticket(SSL *s, const unsigned char *etick,
memcpy(sess->session_id, sess_id, sesslen); memcpy(sess->session_id, sess_id, sesslen);
sess->session_id_length = sesslen; sess->session_id_length = sesslen;
} }
*psess = sess;
if (renew_ticket) if (renew_ticket)
ret = SSL_TICKET_SUCCESS_RENEW; ret = SSL_TICKET_SUCCESS_RENEW;
else else
@ -1412,18 +1414,56 @@ SSL_TICKET_RETURN tls_decrypt_ticket(SSL *s, const unsigned char *etick,
HMAC_CTX_free(hctx); HMAC_CTX_free(hctx);
/* /*
* If set, the decrypt_ticket_cb() is always called regardless of the * If set, the decrypt_ticket_cb() is called unless a fatal error was
* return value determined above. The callback is responsible for checking * detected above. The callback is responsible for checking |ret| before it
* |ret| before it performs any action * performs any action
*/ */
if (s->session_ctx->decrypt_ticket_cb != NULL) { if (s->session_ctx->decrypt_ticket_cb != NULL
&& (ret == SSL_TICKET_EMPTY
|| ret == SSL_TICKET_NO_DECRYPT
|| ret == SSL_TICKET_SUCCESS
|| ret == SSL_TICKET_SUCCESS_RENEW)) {
size_t keyname_len = eticklen; size_t keyname_len = eticklen;
int retcb;
if (keyname_len > TLSEXT_KEYNAME_LENGTH) if (keyname_len > TLSEXT_KEYNAME_LENGTH)
keyname_len = TLSEXT_KEYNAME_LENGTH; keyname_len = TLSEXT_KEYNAME_LENGTH;
ret = s->session_ctx->decrypt_ticket_cb(s, *psess, etick, keyname_len, retcb = s->session_ctx->decrypt_ticket_cb(s, sess, etick, keyname_len,
ret, ret,
s->session_ctx->ticket_cb_data); s->session_ctx->ticket_cb_data);
switch (retcb) {
case SSL_TICKET_RETURN_ABORT:
ret = SSL_TICKET_FATAL_ERR_OTHER;
break;
case SSL_TICKET_RETURN_IGNORE:
ret = SSL_TICKET_NONE;
SSL_SESSION_free(sess);
sess = NULL;
break;
case SSL_TICKET_RETURN_IGNORE_RENEW:
if (ret != SSL_TICKET_EMPTY && ret != SSL_TICKET_NO_DECRYPT)
ret = SSL_TICKET_NO_DECRYPT;
/* else the value of |ret| will already do the right thing */
SSL_SESSION_free(sess);
sess = NULL;
break;
case SSL_TICKET_RETURN_USE:
case SSL_TICKET_RETURN_USE_RENEW:
if (ret != SSL_TICKET_SUCCESS
&& ret != SSL_TICKET_SUCCESS_RENEW)
ret = SSL_TICKET_FATAL_ERR_OTHER;
else if (retcb == SSL_TICKET_RETURN_USE)
ret = SSL_TICKET_SUCCESS;
else
ret = SSL_TICKET_SUCCESS_RENEW;
break;
default:
ret = SSL_TICKET_FATAL_ERR_OTHER;
}
} }
switch (ret) { switch (ret) {
@ -1431,13 +1471,11 @@ SSL_TICKET_RETURN tls_decrypt_ticket(SSL *s, const unsigned char *etick,
case SSL_TICKET_SUCCESS_RENEW: case SSL_TICKET_SUCCESS_RENEW:
case SSL_TICKET_EMPTY: case SSL_TICKET_EMPTY:
s->ext.ticket_expected = 1; s->ext.ticket_expected = 1;
/* Fall through */
case SSL_TICKET_SUCCESS:
case SSL_TICKET_NONE:
return ret;
} }
return SSL_TICKET_FATAL_ERR_OTHER; *psess = sess;
return ret;
} }
/* Check to see if a signature algorithm is allowed */ /* Check to see if a signature algorithm is allowed */

View File

@ -469,12 +469,24 @@ static int generate_session_ticket_cb(SSL *s, void *arg)
return SSL_SESSION_set1_ticket_appdata(ss, app_data, strlen(app_data)); return SSL_SESSION_set1_ticket_appdata(ss, app_data, strlen(app_data));
} }
static SSL_TICKET_RETURN decrypt_session_ticket_cb(SSL *s, SSL_SESSION *ss, static int decrypt_session_ticket_cb(SSL *s, SSL_SESSION *ss,
const unsigned char *keyname, const unsigned char *keyname,
size_t keyname_len, size_t keyname_len,
SSL_TICKET_RETURN retv, void *arg) SSL_TICKET_STATUS status,
void *arg)
{ {
return retv; switch (status) {
case SSL_TICKET_EMPTY:
case SSL_TICKET_NO_DECRYPT:
return SSL_TICKET_RETURN_IGNORE_RENEW;
case SSL_TICKET_SUCCESS:
return SSL_TICKET_RETURN_USE;
case SSL_TICKET_SUCCESS_RENEW:
return SSL_TICKET_RETURN_USE_RENEW;
default:
break;
}
return SSL_TICKET_RETURN_ABORT;
} }
/* /*

View File

@ -4596,7 +4596,9 @@ static int test_ssl_get_shared_ciphers(int tst)
} }
static const char *appdata = "Hello World"; static const char *appdata = "Hello World";
static int gen_tick_called, dec_tick_called; static int gen_tick_called, dec_tick_called, tick_key_cb_called;
static int tick_key_renew = 0;
static SSL_TICKET_RETURN tick_dec_ret = SSL_TICKET_RETURN_ABORT;
static int gen_tick_cb(SSL *s, void *arg) static int gen_tick_cb(SSL *s, void *arg)
{ {
@ -4609,27 +4611,47 @@ static int gen_tick_cb(SSL *s, void *arg)
static SSL_TICKET_RETURN dec_tick_cb(SSL *s, SSL_SESSION *ss, static SSL_TICKET_RETURN dec_tick_cb(SSL *s, SSL_SESSION *ss,
const unsigned char *keyname, const unsigned char *keyname,
size_t keyname_length, size_t keyname_length,
SSL_TICKET_RETURN retv, void *arg) SSL_TICKET_STATUS status,
void *arg)
{ {
void *tickdata; void *tickdata;
size_t tickdlen; size_t tickdlen;
dec_tick_called = 1; dec_tick_called = 1;
if (!TEST_true(retv == SSL_TICKET_SUCCESS if (status == SSL_TICKET_EMPTY)
|| retv == SSL_TICKET_SUCCESS_RENEW)) return SSL_TICKET_RETURN_IGNORE_RENEW;
return retv;
if (!TEST_true(SSL_SESSION_get0_ticket_appdata(ss, &tickdata, &tickdlen)) if (!TEST_true(status == SSL_TICKET_SUCCESS
|| status == SSL_TICKET_SUCCESS_RENEW))
return SSL_TICKET_RETURN_ABORT;
if (!TEST_true(SSL_SESSION_get0_ticket_appdata(ss, &tickdata,
&tickdlen))
|| !TEST_size_t_eq(tickdlen, strlen(appdata)) || !TEST_size_t_eq(tickdlen, strlen(appdata))
|| !TEST_int_eq(memcmp(tickdata, appdata, tickdlen), 0)) || !TEST_int_eq(memcmp(tickdata, appdata, tickdlen), 0))
return SSL_TICKET_FATAL_ERR_OTHER; return SSL_TICKET_RETURN_ABORT;
if (tick_key_cb_called) {
/* Don't change what the ticket key callback wanted to do */
switch (status) {
case SSL_TICKET_NO_DECRYPT:
return SSL_TICKET_RETURN_IGNORE_RENEW;
case SSL_TICKET_SUCCESS:
return SSL_TICKET_RETURN_USE;
case SSL_TICKET_SUCCESS_RENEW:
return SSL_TICKET_RETURN_USE_RENEW;
default:
return SSL_TICKET_RETURN_ABORT;
}
}
return tick_dec_ret;
return retv;
} }
static int tick_renew = 0;
static int tick_key_cb(SSL *s, unsigned char key_name[16], static int tick_key_cb(SSL *s, unsigned char key_name[16],
unsigned char iv[EVP_MAX_IV_LENGTH], EVP_CIPHER_CTX *ctx, unsigned char iv[EVP_MAX_IV_LENGTH], EVP_CIPHER_CTX *ctx,
HMAC_CTX *hctx, int enc) HMAC_CTX *hctx, int enc)
@ -4637,6 +4659,7 @@ static int tick_key_cb(SSL *s, unsigned char key_name[16],
const unsigned char tick_aes_key[16] = "0123456789abcdef"; const unsigned char tick_aes_key[16] = "0123456789abcdef";
const unsigned char tick_hmac_key[16] = "0123456789abcdef"; const unsigned char tick_hmac_key[16] = "0123456789abcdef";
tick_key_cb_called = 1;
memset(iv, 0, AES_BLOCK_SIZE); memset(iv, 0, AES_BLOCK_SIZE);
memset(key_name, 0, 16); memset(key_name, 0, 16);
if (!EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, tick_aes_key, iv, enc) if (!EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, tick_aes_key, iv, enc)
@ -4644,17 +4667,23 @@ static int tick_key_cb(SSL *s, unsigned char key_name[16],
EVP_sha256(), NULL)) EVP_sha256(), NULL))
return -1; return -1;
return tick_renew ? 2 : 1; return tick_key_renew ? 2 : 1;
} }
/* /*
* Test the various ticket callbacks * Test the various ticket callbacks
* Test 0: TLSv1.2, no ticket key callback (default no ticket renewal) * Test 0: TLSv1.2, no ticket key callback, no ticket, no renewal
* Test 1: TLSv1.3, no ticket key callback (default ticket renewal) * Test 1: TLSv1.3, no ticket key callback, no ticket, no renewal
* Test 2: TLSv1.2, ticket key callback, no ticket renewal * Test 2: TLSv1.2, no ticket key callback, no ticket, renewal
* Test 3: TLSv1.3, ticket key callback, no ticket renewal * Test 3: TLSv1.3, no ticket key callback, no ticket, renewal
* Test 4: TLSv1.2, ticket key callback, ticket renewal * Test 4: TLSv1.2, no ticket key callback, ticket, no renewal
* Test 5: TLSv1.3, ticket key callback, ticket renewal * Test 5: TLSv1.3, no ticket key callback, ticket, no renewal
* Test 6: TLSv1.2, no ticket key callback, ticket, renewal
* Test 7: TLSv1.3, no ticket key callback, ticket, renewal
* Test 8: TLSv1.2, ticket key callback, ticket, no renewal
* Test 9: TLSv1.3, ticket key callback, ticket, no renewal
* Test 10: TLSv1.2, ticket key callback, ticket, renewal
* Test 11: TLSv1.3, ticket key callback, ticket, renewal
*/ */
static int test_ticket_callbacks(int tst) static int test_ticket_callbacks(int tst)
{ {
@ -4672,27 +4701,60 @@ static int test_ticket_callbacks(int tst)
return 1; return 1;
#endif #endif
gen_tick_called = dec_tick_called = 0; gen_tick_called = dec_tick_called = tick_key_cb_called = 0;
/* Which tests the ticket key callback should request renewal for */ /* Which tests the ticket key callback should request renewal for */
if (tst == 4 || tst == 5) if (tst == 10 || tst == 11)
tick_renew = 1; tick_key_renew = 1;
else else
tick_renew = 0; tick_key_renew = 0;
/* Which tests the decrypt ticket callback should request renewal for */
switch (tst) {
case 0:
case 1:
tick_dec_ret = SSL_TICKET_RETURN_IGNORE;
break;
case 2:
case 3:
tick_dec_ret = SSL_TICKET_RETURN_IGNORE_RENEW;
break;
case 4:
case 5:
tick_dec_ret = SSL_TICKET_RETURN_USE;
break;
case 6:
case 7:
tick_dec_ret = SSL_TICKET_RETURN_USE_RENEW;
break;
default:
tick_dec_ret = SSL_TICKET_RETURN_ABORT;
}
if (!TEST_true(create_ssl_ctx_pair(TLS_server_method(), if (!TEST_true(create_ssl_ctx_pair(TLS_server_method(),
TLS_client_method(), TLS_client_method(),
TLS1_VERSION, TLS1_VERSION,
tst == 0 ? TLS1_2_VERSION ((tst % 2) == 0) ? TLS1_2_VERSION
: TLS1_3_VERSION, : TLS1_3_VERSION,
&sctx, &cctx, cert, privkey))) &sctx, &cctx, cert, privkey)))
goto end; goto end;
/*
* We only want sessions to resume from tickets - not the session cache. So
* switch the cache off.
*/
if (!TEST_true(SSL_CTX_set_session_cache_mode(sctx, SSL_SESS_CACHE_OFF)))
goto end;
if (!TEST_true(SSL_CTX_set_session_ticket_cb(sctx, gen_tick_cb, dec_tick_cb, if (!TEST_true(SSL_CTX_set_session_ticket_cb(sctx, gen_tick_cb, dec_tick_cb,
NULL))) NULL)))
goto end; goto end;
if (tst >= 2 if (tst >= 8
&& !TEST_true(SSL_CTX_set_tlsext_ticket_key_cb(sctx, tick_key_cb))) && !TEST_true(SSL_CTX_set_tlsext_ticket_key_cb(sctx, tick_key_cb)))
goto end; goto end;
@ -4702,8 +4764,15 @@ static int test_ticket_callbacks(int tst)
SSL_ERROR_NONE))) SSL_ERROR_NONE)))
goto end; goto end;
/*
* The decrypt ticket key callback in TLSv1.2 should be called even though
* we have no ticket yet, because it gets called with a status of
* SSL_TICKET_EMPTY (the client indicates support for tickets but does not
* actually send any ticket data). This does not happen in TLSv1.3 because
* it is not valid to send empty ticket data in TLSv1.3.
*/
if (!TEST_int_eq(gen_tick_called, 1) if (!TEST_int_eq(gen_tick_called, 1)
|| !TEST_int_eq(dec_tick_called, 0)) || !TEST_int_eq(dec_tick_called, ((tst % 2) == 0) ? 1 : 0))
goto end; goto end;
gen_tick_called = dec_tick_called = 0; gen_tick_called = dec_tick_called = 0;
@ -4720,17 +4789,23 @@ static int test_ticket_callbacks(int tst)
NULL)) NULL))
|| !TEST_true(SSL_set_session(clientssl, clntsess)) || !TEST_true(SSL_set_session(clientssl, clntsess))
|| !TEST_true(create_ssl_connection(serverssl, clientssl, || !TEST_true(create_ssl_connection(serverssl, clientssl,
SSL_ERROR_NONE)) SSL_ERROR_NONE)))
|| !TEST_true(SSL_session_reused(clientssl)))
goto end; goto end;
/* if (tick_dec_ret == SSL_TICKET_RETURN_IGNORE
* In TLSv1.2 we default to not renewing the ticket everytime. In TLSv1.3 || tick_dec_ret == SSL_TICKET_RETURN_IGNORE_RENEW) {
* we default to renewing. The defaults are overridden if a ticket key if (!TEST_false(SSL_session_reused(clientssl)))
* callback is in place. goto end;
*/ } else {
if (!TEST_true(SSL_session_reused(clientssl)))
goto end;
}
if (!TEST_int_eq(gen_tick_called, if (!TEST_int_eq(gen_tick_called,
(tst == 0 || tst == 2 || tst == 3) ? 0 : 1) (tick_key_renew
|| tick_dec_ret == SSL_TICKET_RETURN_IGNORE_RENEW
|| tick_dec_ret == SSL_TICKET_RETURN_USE_RENEW)
? 1 : 0)
|| !TEST_int_eq(dec_tick_called, 1)) || !TEST_int_eq(dec_tick_called, 1))
goto end; goto end;
@ -4839,7 +4914,7 @@ int setup_tests(void)
ADD_ALL_TESTS(test_info_callback, 6); ADD_ALL_TESTS(test_info_callback, 6);
ADD_ALL_TESTS(test_ssl_pending, 2); ADD_ALL_TESTS(test_ssl_pending, 2);
ADD_ALL_TESTS(test_ssl_get_shared_ciphers, OSSL_NELEM(shared_ciphers_data)); ADD_ALL_TESTS(test_ssl_get_shared_ciphers, OSSL_NELEM(shared_ciphers_data));
ADD_ALL_TESTS(test_ticket_callbacks, 6); ADD_ALL_TESTS(test_ticket_callbacks, 12);
return 1; return 1;
} }