Session Ticket app data

Adds application data into the encrypted session ticket

Reviewed-by: Paul Dale <paul.dale@oracle.com>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/3802)
This commit is contained in:
Todd Short 2017-03-15 13:25:55 -04:00 committed by Matt Caswell
parent f1c00b93e2
commit df0fed9aab
20 changed files with 661 additions and 67 deletions

View file

@ -0,0 +1,149 @@
=pod
=head1 NAME
SSL_CTX_set_session_ticket_cb,
SSL_SESSION_get0_ticket_appdata,
SSL_SESSION_set1_ticket_appdata,
SSL_CTX_generate_session_ticket_fn,
SSL_CTX_decrypt_session_ticket_fn - manage session ticket application data
=head1 SYNOPSIS
#include <openssl/ssl.h>
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,
const unsigned char *keyname,
size_t keyname_len,
SSL_TICKET_RETURN retv,
void *arg);
int SSL_CTX_set_session_ticket_cb(SSL_CTX *ctx,
SSL_CTX_generate_session_ticket_fn gen_cb,
SSL_CTX_decrypt_session_ticket_fn dec_cb,
void *arg);
int SSL_SESSION_set1_ticket_appdata(SSL_SESSION *ss, const void *data, size_t len);
int SSL_SESSION_get0_ticket_appdata(SSL_SESSION *ss, void **data, size_t *len);
=head1 DESCRIPTION
SSL_CTX_set_set_session_ticket_cb() sets the application callbacks B<gen_cb>
and B<dec_cb> that are used by a server to set and get application data stored
with a session, and placed into a session ticket. Either callback function may
be set to NULL. The value of B<arg> is passed to the callbacks.
B<gen_cb> is the application defined callback invoked when a session ticket is
about to be created. The application can call SSL_SESSION_set1_ticket_appdata()
at this time to add application data to the session ticket. The value of B<arg>
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>.
B<dec_cb> is the application defined callback invoked after session ticket
decryption has been attempted and any session ticket application data is available.
The application can call SSL_SESSION_get_ticket_appdata() at this time to retrieve
the application data. The value of B<arg> is the same as that given to
SSL_CTX_set_session_ticket_cb(). The B<retv> arguement is the result of the ticket
decryption. The B<keyname> and B<keyname_len> identify the key used to decrypt the
session ticket. The 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
B<data> and B<len> into B<ss> which is then placed into any generated session
tickets. It can be called at any time before a session ticket is created to
update the data placed into the session ticket. However, given that sessions
and tickets are created by the handshake, the B<gen_cb> is provided to notify
the application that a session ticket is about to be generated.
SSL_SESSION_get0_ticket_appdata() assigns B<data> to the session ticket
application data and assigns B<len> to the length of the session ticket
application data from B<ss>. The application data can be set via
SSL_SESSION_set1_ticket_appdata() or by a session ticket. NULL will be assigned
to B<data> and 0 will be assigned to B<len> if there is no session ticket
application data. SSL_SESSION_get0_ticket_appdata() can be called any time
after a session has been created. The B<dec_cb> is provided to notify the
application that a session ticket has just been decrypted.
=head1 NOTES
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
decryption which can be modified by the callback before being returned. The
callback must check the B<retv> value before performing any action, as it's
called even if ticket decryption fails.
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.
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().
=head1 RETURN VALUES
The SSL_CTX_set_session_ticket_cb(), SSL_SESSION_set1_ticket_appdata() and
SSL_SESSION_get0_ticket_appdata() functions return 1 on success and 0 on
failure.
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.
The B<dec_cb> callback must return one of the following B<SSL_TICKET_RETURN>
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
L<ssl(7)>,
L<SSL_get_session(3)>
=head1 HISTORY
SSL_CTX_set_session_ticket_cb(), SSSL_SESSION_set1_ticket_appdata() and
SSL_SESSION_get_ticket_appdata() were added to OpenSSL 1.1.1.
=head1 COPYRIGHT
Copyright 2017 The OpenSSL Project Authors. All Rights Reserved.
Licensed under the OpenSSL license (the "License"). You may not use
this file except in compliance with the License. You can obtain a copy
in the file LICENSE in the source distribution or at
L<https://www.openssl.org/source/license.html>.
=cut

View file

@ -2294,6 +2294,38 @@ __owur const struct openssl_ssl_test_functions *SSL_test_functions(void);
__owur int SSL_free_buffers(SSL *ssl);
__owur int SSL_alloc_buffers(SSL *ssl);
/* Return codes for tls_get_ticket_from_client() and tls_decrypt_ticket() */
typedef int SSL_TICKET_RETURN;
/* Support for ticket appdata */
/* fatal error, malloc failure */
# define SSL_TICKET_FATAL_ERR_MALLOC 0
/* fatal error, either from parsing or decrypting the ticket */
# define SSL_TICKET_FATAL_ERR_OTHER 1
/* No ticket present */
# define SSL_TICKET_NONE 2
/* Empty ticket present */
# define SSL_TICKET_EMPTY 3
/* the ticket couldn't be decrypted */
# define SSL_TICKET_NO_DECRYPT 4
/* a ticket was successfully decrypted */
# define SSL_TICKET_SUCCESS 5
/* same as above but the ticket needs to be renewed */
# define SSL_TICKET_SUCCESS_RENEW 6
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,
const unsigned char *keyname,
size_t keyname_length,
SSL_TICKET_RETURN retv,
void *arg);
int SSL_CTX_set_session_ticket_cb(SSL_CTX *ctx,
SSL_CTX_generate_session_ticket_fn gen_cb,
SSL_CTX_decrypt_session_ticket_fn dec_cb,
void *arg);
int SSL_SESSION_set1_ticket_appdata(SSL_SESSION *ss, const void *data, size_t len);
int SSL_SESSION_get0_ticket_appdata(SSL_SESSION *ss, void **data, size_t *len);
extern const char SSL_version_str[];

View file

@ -43,6 +43,7 @@ typedef struct {
ASN1_OCTET_STRING *alpn_selected;
ASN1_OCTET_STRING *tick_nonce;
uint32_t tlsext_max_fragment_len_mode;
ASN1_OCTET_STRING *ticket_appdata;
} SSL_SESSION_ASN1;
ASN1_SEQUENCE(SSL_SESSION_ASN1) = {
@ -73,7 +74,8 @@ ASN1_SEQUENCE(SSL_SESSION_ASN1) = {
ASN1_EXP_OPT_EMBED(SSL_SESSION_ASN1, max_early_data, ZUINT32, 15),
ASN1_EXP_OPT(SSL_SESSION_ASN1, alpn_selected, ASN1_OCTET_STRING, 16),
ASN1_EXP_OPT(SSL_SESSION_ASN1, tick_nonce, ASN1_OCTET_STRING, 17),
ASN1_EXP_OPT_EMBED(SSL_SESSION_ASN1, tlsext_max_fragment_len_mode, ZUINT32, 18)
ASN1_EXP_OPT_EMBED(SSL_SESSION_ASN1, tlsext_max_fragment_len_mode, ZUINT32, 18),
ASN1_EXP_OPT(SSL_SESSION_ASN1, ticket_appdata, ASN1_OCTET_STRING, 19)
} static_ASN1_SEQUENCE_END(SSL_SESSION_ASN1)
IMPLEMENT_STATIC_ASN1_ENCODE_FUNCTIONS(SSL_SESSION_ASN1)
@ -123,6 +125,7 @@ int i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp)
#endif
ASN1_OCTET_STRING alpn_selected;
ASN1_OCTET_STRING tick_nonce;
ASN1_OCTET_STRING ticket_appdata;
long l;
@ -200,6 +203,12 @@ int i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp)
as.tlsext_max_fragment_len_mode = in->ext.max_fragment_len_mode;
if (in->ticket_appdata == NULL)
as.ticket_appdata = NULL;
else
ssl_session_oinit(&as.ticket_appdata, &ticket_appdata,
in->ticket_appdata, in->ticket_appdata_len);
return i2d_SSL_SESSION_ASN1(&as, pp);
}
@ -376,6 +385,15 @@ SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a, const unsigned char **pp,
ret->ext.max_fragment_len_mode = as->tlsext_max_fragment_len_mode;
if (as->ticket_appdata != NULL) {
ret->ticket_appdata = as->ticket_appdata->data;
ret->ticket_appdata_len = as->ticket_appdata->length;
as->ticket_appdata->data = NULL;
} else {
ret->ticket_appdata = NULL;
ret->ticket_appdata_len = 0;
}
M_ASN1_free_of(as, SSL_SESSION_ASN1);
if ((a != NULL) && (*a == NULL))

View file

@ -5409,3 +5409,14 @@ int SSL_verify_client_post_handshake(SSL *ssl)
ossl_statem_set_in_init(ssl, 1);
return 1;
}
int SSL_CTX_set_session_ticket_cb(SSL_CTX *ctx,
SSL_CTX_generate_session_ticket_fn gen_cb,
SSL_CTX_decrypt_session_ticket_fn dec_cb,
void *arg)
{
ctx->generate_ticket_cb = gen_cb;
ctx->decrypt_ticket_cb = dec_cb;
ctx->ticket_cb_data = arg;
return 1;
}

View file

@ -591,6 +591,8 @@ struct ssl_session_st {
# ifndef OPENSSL_NO_SRP
char *srp_username;
# endif
unsigned char *ticket_appdata;
size_t ticket_appdata_len;
uint32_t flags;
CRYPTO_RWLOCK *lock;
};
@ -1025,6 +1027,11 @@ struct ssl_ctx_st {
size_t (*record_padding_cb)(SSL *s, int type, size_t len, void *arg);
void *record_padding_arg;
size_t block_padding;
/* Session ticket appdata */
SSL_CTX_generate_session_ticket_fn generate_ticket_cb;
SSL_CTX_decrypt_session_ticket_fn decrypt_ticket_cb;
void *ticket_cb_data;
};
struct ssl_st {
@ -2446,30 +2453,12 @@ void tls1_get_supported_groups(SSL *s, const uint16_t **pgroups,
__owur int tls1_set_server_sigalgs(SSL *s);
/* Return codes for tls_get_ticket_from_client() and tls_decrypt_ticket() */
typedef enum ticket_en {
/* fatal error, malloc failure */
TICKET_FATAL_ERR_MALLOC,
/* fatal error, either from parsing or decrypting the ticket */
TICKET_FATAL_ERR_OTHER,
/* No ticket present */
TICKET_NONE,
/* Empty ticket present */
TICKET_EMPTY,
/* the ticket couldn't be decrypted */
TICKET_NO_DECRYPT,
/* a ticket was successfully decrypted */
TICKET_SUCCESS,
/* same as above but the ticket needs to be renewed */
TICKET_SUCCESS_RENEW
} TICKET_RETURN;
__owur TICKET_RETURN tls_get_ticket_from_client(SSL *s, CLIENTHELLO_MSG *hello,
SSL_SESSION **ret);
__owur TICKET_RETURN tls_decrypt_ticket(SSL *s, const unsigned char *etick,
size_t eticklen,
const unsigned char *sess_id,
size_t sesslen, SSL_SESSION **psess);
__owur SSL_TICKET_RETURN tls_get_ticket_from_client(SSL *s, CLIENTHELLO_MSG *hello,
SSL_SESSION **ret);
__owur SSL_TICKET_RETURN tls_decrypt_ticket(SSL *s, const unsigned char *etick,
size_t eticklen,
const unsigned char *sess_id,
size_t sesslen, SSL_SESSION **psess);
__owur int tls_use_ticket(SSL *s);

View file

@ -134,6 +134,7 @@ SSL_SESSION *ssl_session_dup(SSL_SESSION *src, int ticket)
dest->peer_chain = NULL;
dest->peer = NULL;
dest->ext.tick_nonce = NULL;
dest->ticket_appdata = NULL;
memset(&dest->ex_data, 0, sizeof(dest->ex_data));
/* We deliberately don't copy the prev and next pointers */
@ -244,6 +245,13 @@ SSL_SESSION *ssl_session_dup(SSL_SESSION *src, int ticket)
}
#endif
if (src->ticket_appdata != NULL) {
dest->ticket_appdata =
OPENSSL_memdup(src->ticket_appdata, src->ticket_appdata_len);
if (dest->ticket_appdata == NULL)
goto err;
}
return dest;
err:
SSLerr(SSL_F_SSL_SESSION_DUP, ERR_R_MALLOC_FAILURE);
@ -471,7 +479,7 @@ int ssl_get_prev_session(SSL *s, CLIENTHELLO_MSG *hello)
SSL_SESSION *ret = NULL;
int fatal = 0, discard;
int try_session_cache = 0;
TICKET_RETURN r;
SSL_TICKET_RETURN r;
if (SSL_IS_TLS13(s)) {
if (!tls_parse_extension(s, TLSEXT_IDX_psk_kex_modes,
@ -486,20 +494,20 @@ int ssl_get_prev_session(SSL *s, CLIENTHELLO_MSG *hello)
/* sets s->ext.ticket_expected */
r = tls_get_ticket_from_client(s, hello, &ret);
switch (r) {
case TICKET_FATAL_ERR_MALLOC:
case TICKET_FATAL_ERR_OTHER:
case SSL_TICKET_FATAL_ERR_MALLOC:
case SSL_TICKET_FATAL_ERR_OTHER:
fatal = 1;
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_GET_PREV_SESSION,
ERR_R_INTERNAL_ERROR);
goto err;
case TICKET_NONE:
case TICKET_EMPTY:
case SSL_TICKET_NONE:
case SSL_TICKET_EMPTY:
if (hello->session_id_len > 0)
try_session_cache = 1;
break;
case TICKET_NO_DECRYPT:
case TICKET_SUCCESS:
case TICKET_SUCCESS_RENEW:
case SSL_TICKET_NO_DECRYPT:
case SSL_TICKET_SUCCESS:
case SSL_TICKET_SUCCESS_RENEW:
break;
}
}
@ -806,6 +814,7 @@ void SSL_SESSION_free(SSL_SESSION *ss)
#endif
OPENSSL_free(ss->ext.alpn_selected);
OPENSSL_free(ss->ext.tick_nonce);
OPENSSL_free(ss->ticket_appdata);
CRYPTO_THREAD_lock_free(ss->lock);
OPENSSL_clear_free(ss, sizeof(*ss));
}
@ -1269,4 +1278,27 @@ void SSL_CTX_set_cookie_verify_cb(SSL_CTX *ctx,
ctx->app_verify_cookie_cb = cb;
}
int SSL_SESSION_set1_ticket_appdata(SSL_SESSION *ss, const void *data, size_t len)
{
OPENSSL_free(ss->ticket_appdata);
ss->ticket_appdata_len = 0;
if (data == NULL || len == 0) {
ss->ticket_appdata = NULL;
return 1;
}
ss->ticket_appdata = OPENSSL_memdup(data, len);
if (ss->ticket_appdata != NULL) {
ss->ticket_appdata_len = len;
return 1;
}
return 0;
}
int SSL_SESSION_get0_ticket_appdata(SSL_SESSION *ss, void **data, size_t *len)
{
*data = ss->ticket_appdata;
*len = ss->ticket_appdata_len;
return 1;
}
IMPLEMENT_PEM_rw(SSL_SESSION, SSL_SESSION, PEM_STRING_SSL_SESSION, SSL_SESSION)

View file

@ -1124,13 +1124,13 @@ int tls_parse_ctos_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
PACKET_remaining(&identity), NULL, 0,
&sess);
if (ret == TICKET_FATAL_ERR_MALLOC
|| ret == TICKET_FATAL_ERR_OTHER) {
if (ret == SSL_TICKET_FATAL_ERR_MALLOC
|| ret == SSL_TICKET_FATAL_ERR_OTHER) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR,
SSL_F_TLS_PARSE_CTOS_PSK, ERR_R_INTERNAL_ERROR);
return 0;
}
if (ret == TICKET_NO_DECRYPT)
if (ret == SSL_TICKET_NO_DECRYPT)
continue;
ticket_age = (uint32_t)ticket_agel;

View file

@ -3728,6 +3728,10 @@ int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt)
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;
/* get session encoding length */
slen_full = i2d_SSL_SESSION(s->session, NULL);
/*

View file

@ -1205,8 +1205,8 @@ int tls1_set_server_sigalgs(SSL *s)
* s->ctx->ext.ticket_key_cb asked to renew the client's ticket.
* Otherwise, s->ext.ticket_expected is set to 0.
*/
TICKET_RETURN tls_get_ticket_from_client(SSL *s, CLIENTHELLO_MSG *hello,
SSL_SESSION **ret)
SSL_TICKET_RETURN tls_get_ticket_from_client(SSL *s, CLIENTHELLO_MSG *hello,
SSL_SESSION **ret)
{
int retv;
size_t size;
@ -1221,11 +1221,11 @@ TICKET_RETURN tls_get_ticket_from_client(SSL *s, CLIENTHELLO_MSG *hello,
* resumption.
*/
if (s->version <= SSL3_VERSION || !tls_use_ticket(s))
return TICKET_NONE;
return SSL_TICKET_NONE;
ticketext = &hello->pre_proc_exts[TLSEXT_IDX_session_ticket];
if (!ticketext->present)
return TICKET_NONE;
return SSL_TICKET_NONE;
size = PACKET_remaining(&ticketext->data);
if (size == 0) {
@ -1234,7 +1234,7 @@ TICKET_RETURN tls_get_ticket_from_client(SSL *s, CLIENTHELLO_MSG *hello,
* one.
*/
s->ext.ticket_expected = 1;
return TICKET_EMPTY;
return SSL_TICKET_EMPTY;
}
if (s->ext.session_secret_cb) {
/*
@ -1243,25 +1243,49 @@ TICKET_RETURN tls_get_ticket_from_client(SSL *s, CLIENTHELLO_MSG *hello,
* abbreviated handshake based on external mechanism to
* calculate the master secret later.
*/
return TICKET_NO_DECRYPT;
return SSL_TICKET_NO_DECRYPT;
}
retv = tls_decrypt_ticket(s, PACKET_data(&ticketext->data), size,
hello->session_id, hello->session_id_len, ret);
/*
* If set, the decrypt_ticket_cb() is always called regardless of the
* return from tls_decrypt_ticket(). The callback is responsible for
* checking |retv| before it performs any action
*/
if (s->session_ctx->decrypt_ticket_cb != NULL) {
size_t keyname_len = size;
if (keyname_len > TLSEXT_KEYNAME_LENGTH)
keyname_len = TLSEXT_KEYNAME_LENGTH;
retv = s->session_ctx->decrypt_ticket_cb(s, *ret,
PACKET_data(&ticketext->data),
keyname_len,
retv, s->session_ctx->ticket_cb_data);
}
switch (retv) {
case TICKET_NO_DECRYPT:
case SSL_TICKET_NO_DECRYPT:
s->ext.ticket_expected = 1;
return TICKET_NO_DECRYPT;
return SSL_TICKET_NO_DECRYPT;
case TICKET_SUCCESS:
return TICKET_SUCCESS;
case SSL_TICKET_SUCCESS:
return SSL_TICKET_SUCCESS;
case TICKET_SUCCESS_RENEW:
case SSL_TICKET_SUCCESS_RENEW:
s->ext.ticket_expected = 1;
return TICKET_SUCCESS;
return SSL_TICKET_SUCCESS;
case SSL_TICKET_EMPTY:
s->ext.ticket_expected = 1;
return SSL_TICKET_EMPTY;
case SSL_TICKET_NONE:
return SSL_TICKET_NONE;
default:
return TICKET_FATAL_ERR_OTHER;
return SSL_TICKET_FATAL_ERR_OTHER;
}
}
@ -1275,15 +1299,15 @@ 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
* point to the resulting session.
*/
TICKET_RETURN tls_decrypt_ticket(SSL *s, const unsigned char *etick,
size_t eticklen, const unsigned char *sess_id,
size_t sesslen, SSL_SESSION **psess)
SSL_TICKET_RETURN tls_decrypt_ticket(SSL *s, const unsigned char *etick,
size_t eticklen, const unsigned char *sess_id,
size_t sesslen, SSL_SESSION **psess)
{
SSL_SESSION *sess;
unsigned char *sdec;
const unsigned char *p;
int slen, renew_ticket = 0, declen;
TICKET_RETURN ret = TICKET_FATAL_ERR_OTHER;
SSL_TICKET_RETURN ret = SSL_TICKET_FATAL_ERR_OTHER;
size_t mlen;
unsigned char tick_hmac[EVP_MAX_MD_SIZE];
HMAC_CTX *hctx = NULL;
@ -1292,17 +1316,17 @@ TICKET_RETURN tls_decrypt_ticket(SSL *s, const unsigned char *etick,
/* Need at least keyname + iv */
if (eticklen < TLSEXT_KEYNAME_LENGTH + EVP_MAX_IV_LENGTH) {
ret = TICKET_NO_DECRYPT;
ret = SSL_TICKET_NO_DECRYPT;
goto err;
}
/* Initialize session ticket encryption and HMAC contexts */
hctx = HMAC_CTX_new();
if (hctx == NULL)
return TICKET_FATAL_ERR_MALLOC;
return SSL_TICKET_FATAL_ERR_MALLOC;
ctx = EVP_CIPHER_CTX_new();
if (ctx == NULL) {
ret = TICKET_FATAL_ERR_MALLOC;
ret = SSL_TICKET_FATAL_ERR_MALLOC;
goto err;
}
if (tctx->ext.ticket_key_cb) {
@ -1313,7 +1337,7 @@ TICKET_RETURN tls_decrypt_ticket(SSL *s, const unsigned char *etick,
if (rv < 0)
goto err;
if (rv == 0) {
ret = TICKET_NO_DECRYPT;
ret = SSL_TICKET_NO_DECRYPT;
goto err;
}
if (rv == 2)
@ -1322,7 +1346,7 @@ TICKET_RETURN tls_decrypt_ticket(SSL *s, const unsigned char *etick,
/* Check key name matches */
if (memcmp(etick, tctx->ext.tick_key_name,
TLSEXT_KEYNAME_LENGTH) != 0) {
ret = TICKET_NO_DECRYPT;
ret = SSL_TICKET_NO_DECRYPT;
goto err;
}
if (HMAC_Init_ex(hctx, tctx->ext.tick_hmac_key,
@ -1345,7 +1369,7 @@ TICKET_RETURN tls_decrypt_ticket(SSL *s, const unsigned char *etick,
/* Sanity check ticket length: must exceed keyname + IV + HMAC */
if (eticklen <=
TLSEXT_KEYNAME_LENGTH + EVP_CIPHER_CTX_iv_length(ctx) + mlen) {
ret = TICKET_NO_DECRYPT;
ret = SSL_TICKET_NO_DECRYPT;
goto err;
}
eticklen -= mlen;
@ -1357,7 +1381,7 @@ TICKET_RETURN tls_decrypt_ticket(SSL *s, const unsigned char *etick,
HMAC_CTX_free(hctx);
if (CRYPTO_memcmp(tick_hmac, etick + eticklen, mlen)) {
EVP_CIPHER_CTX_free(ctx);
return TICKET_NO_DECRYPT;
return SSL_TICKET_NO_DECRYPT;
}
/* Attempt to decrypt session data */
/* Move p after IV to start of encrypted ticket, update length */
@ -1368,12 +1392,12 @@ TICKET_RETURN tls_decrypt_ticket(SSL *s, const unsigned char *etick,
(int)eticklen) <= 0) {
EVP_CIPHER_CTX_free(ctx);
OPENSSL_free(sdec);
return TICKET_FATAL_ERR_OTHER;
return SSL_TICKET_FATAL_ERR_OTHER;
}
if (EVP_DecryptFinal(ctx, sdec + slen, &declen) <= 0) {
EVP_CIPHER_CTX_free(ctx);
OPENSSL_free(sdec);
return TICKET_NO_DECRYPT;
return SSL_TICKET_NO_DECRYPT;
}
slen += declen;
EVP_CIPHER_CTX_free(ctx);
@ -1387,7 +1411,7 @@ TICKET_RETURN tls_decrypt_ticket(SSL *s, const unsigned char *etick,
/* Some additional consistency checks */
if (slen != 0 || sess->session_id_length != 0) {
SSL_SESSION_free(sess);
return TICKET_NO_DECRYPT;
return SSL_TICKET_NO_DECRYPT;
}
/*
* The session ID, if non-empty, is used by some clients to detect
@ -1400,15 +1424,15 @@ TICKET_RETURN tls_decrypt_ticket(SSL *s, const unsigned char *etick,
sess->session_id_length = sesslen;
*psess = sess;
if (renew_ticket)
return TICKET_SUCCESS_RENEW;
return SSL_TICKET_SUCCESS_RENEW;
else
return TICKET_SUCCESS;
return SSL_TICKET_SUCCESS;
}
ERR_clear_error();
/*
* For session parse failure, indicate that we need to send a new ticket.
*/
return TICKET_NO_DECRYPT;
return SSL_TICKET_NO_DECRYPT;
err:
EVP_CIPHER_CTX_free(ctx);
HMAC_CTX_free(hctx);

View file

@ -38,6 +38,7 @@ void HANDSHAKE_RESULT_free(HANDSHAKE_RESULT *result)
OPENSSL_free(result->server_npn_negotiated);
OPENSSL_free(result->client_alpn_negotiated);
OPENSSL_free(result->server_alpn_negotiated);
OPENSSL_free(result->result_session_ticket_app_data);
sk_X509_NAME_pop_free(result->server_ca_names, X509_NAME_free);
sk_X509_NAME_pop_free(result->client_ca_names, X509_NAME_free);
OPENSSL_free(result->cipher);
@ -64,6 +65,7 @@ typedef struct ctx_data_st {
size_t alpn_protocols_len;
char *srp_user;
char *srp_password;
char *session_ticket_app_data;
} CTX_DATA;
/* |ctx_data| itself is stack-allocated. */
@ -77,6 +79,8 @@ static void ctx_data_free_data(CTX_DATA *ctx_data)
ctx_data->srp_user = NULL;
OPENSSL_free(ctx_data->srp_password);
ctx_data->srp_password = NULL;
OPENSSL_free(ctx_data->session_ticket_app_data);
ctx_data->session_ticket_app_data = NULL;
}
static int ex_data_idx;
@ -453,6 +457,26 @@ static int server_srp_cb(SSL *s, int *ad, void *arg)
}
#endif /* !OPENSSL_NO_SRP */
static int generate_session_ticket_cb(SSL *s, void *arg)
{
CTX_DATA *server_ctx_data = arg;
SSL_SESSION *ss = SSL_get_session(s);
char *app_data = server_ctx_data->session_ticket_app_data;
if (ss == NULL || app_data == NULL)
return 0;
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,
const unsigned char *keyname,
size_t keyname_len,
SSL_TICKET_RETURN retv, void *arg)
{
return retv;
}
/*
* Configure callbacks and other properties that can't be set directly
* in the server/client CONF.
@ -607,6 +631,21 @@ static int configure_handshake_ctx(SSL_CTX *server_ctx, SSL_CTX *server2_ctx,
OPENSSL_free(alpn_protos);
}
if (extra->server.session_ticket_app_data != NULL) {
server_ctx_data->session_ticket_app_data =
OPENSSL_strdup(extra->server.session_ticket_app_data);
SSL_CTX_set_session_ticket_cb(server_ctx, generate_session_ticket_cb,
decrypt_session_ticket_cb, server_ctx_data);
}
if (extra->server2.session_ticket_app_data != NULL) {
if (!TEST_ptr(server2_ctx))
goto err;
server2_ctx_data->session_ticket_app_data =
OPENSSL_strdup(extra->server2.session_ticket_app_data);
SSL_CTX_set_session_ticket_cb(server2_ctx, NULL,
decrypt_session_ticket_cb, server2_ctx_data);
}
/*
* Use fixed session ticket keys so that we can decrypt a ticket created with
* one CTX in another CTX. Don't address server2 for the moment.
@ -1583,6 +1622,11 @@ static HANDSHAKE_RESULT *do_handshake_internal(
SSL_get0_alpn_selected(server.ssl, &proto, &proto_len);
ret->server_alpn_negotiated = dup_str(proto, proto_len);
if ((sess = SSL_get0_session(server.ssl)) != NULL) {
SSL_SESSION_get0_ticket_appdata(sess, (void**)&tick, &tick_len);
ret->result_session_ticket_app_data = OPENSSL_strndup((const char*)tick, tick_len);
}
ret->client_resumed = SSL_session_reused(client.ssl);
ret->server_resumed = SSL_session_reused(server.ssl);

View file

@ -65,6 +65,8 @@ typedef struct handshake_result {
/* Session id status */
ssl_session_id_t session_id;
char *cipher;
/* session ticket application data */
char *result_session_ticket_app_data;
} HANDSHAKE_RESULT;
HANDSHAKE_RESULT *HANDSHAKE_RESULT_new(void);

View file

@ -28,7 +28,7 @@ map { s/\^// } @conf_files if $^O eq "VMS";
# We hard-code the number of tests to double-check that the globbing above
# finds all files as expected.
plan tests => 26; # = scalar @conf_srcs
plan tests => 27; # = scalar @conf_srcs
# Some test results depend on the configuration of enabled protocols. We only
# verify generated sources in the default configuration.

View file

@ -0,0 +1,146 @@
# Generated with generate_ssl_tests.pl
num_tests = 4
test-0 = 0-session-ticket-app-data12
test-1 = 1-session-ticket-app-data12
test-2 = 2-session-ticket-app-data13
test-3 = 3-session-ticket-app-data13
# ===========================================================
[0-session-ticket-app-data12]
ssl_conf = 0-session-ticket-app-data12-ssl
[0-session-ticket-app-data12-ssl]
server = 0-session-ticket-app-data12-server
client = 0-session-ticket-app-data12-client
resume-server = 0-session-ticket-app-data12-server
resume-client = 0-session-ticket-app-data12-client
[0-session-ticket-app-data12-server]
Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
CipherString = DEFAULT
Options = SessionTicket
PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
[0-session-ticket-app-data12-client]
CipherString = DEFAULT
MaxProtocol = TLSv1.2
Options = SessionTicket
VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
VerifyMode = Peer
[test-0]
ExpectedResult = Success
ExpectedSessionTicketAppData = HelloWorld
HandshakeMode = Resume
ResumptionExpected = Yes
SessionTicketExpected = Yes
server = 0-session-ticket-app-data12-server-extra
resume-server = 0-session-ticket-app-data12-server-extra
[0-session-ticket-app-data12-server-extra]
SessionTicketAppData = HelloWorld
# ===========================================================
[1-session-ticket-app-data12]
ssl_conf = 1-session-ticket-app-data12-ssl
[1-session-ticket-app-data12-ssl]
server = 1-session-ticket-app-data12-server
client = 1-session-ticket-app-data12-client
resume-server = 1-session-ticket-app-data12-server
resume-client = 1-session-ticket-app-data12-client
[1-session-ticket-app-data12-server]
Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
CipherString = DEFAULT
Options = SessionTicket
PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
[1-session-ticket-app-data12-client]
CipherString = DEFAULT
MaxProtocol = TLSv1.2
Options = SessionTicket
VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
VerifyMode = Peer
[test-1]
ExpectedResult = Success
ExpectedSessionTicketAppData =
HandshakeMode = Resume
ResumptionExpected = Yes
SessionTicketExpected = Yes
# ===========================================================
[2-session-ticket-app-data13]
ssl_conf = 2-session-ticket-app-data13-ssl
[2-session-ticket-app-data13-ssl]
server = 2-session-ticket-app-data13-server
client = 2-session-ticket-app-data13-client
resume-server = 2-session-ticket-app-data13-server
resume-client = 2-session-ticket-app-data13-client
[2-session-ticket-app-data13-server]
Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
CipherString = DEFAULT
Options = SessionTicket
PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
[2-session-ticket-app-data13-client]
CipherString = DEFAULT
MaxProtocol = TLSv1.3
Options = SessionTicket
VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
VerifyMode = Peer
[test-2]
ExpectedResult = Success
ExpectedSessionTicketAppData = HelloWorld
HandshakeMode = Resume
ResumptionExpected = Yes
SessionTicketExpected = Yes
server = 2-session-ticket-app-data13-server-extra
resume-server = 2-session-ticket-app-data13-server-extra
[2-session-ticket-app-data13-server-extra]
SessionTicketAppData = HelloWorld
# ===========================================================
[3-session-ticket-app-data13]
ssl_conf = 3-session-ticket-app-data13-ssl
[3-session-ticket-app-data13-ssl]
server = 3-session-ticket-app-data13-server
client = 3-session-ticket-app-data13-client
resume-server = 3-session-ticket-app-data13-server
resume-client = 3-session-ticket-app-data13-client
[3-session-ticket-app-data13-server]
Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
CipherString = DEFAULT
Options = SessionTicket
PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
[3-session-ticket-app-data13-client]
CipherString = DEFAULT
MaxProtocol = TLSv1.3
Options = SessionTicket
VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
VerifyMode = Peer
[test-3]
ExpectedResult = Success
ExpectedSessionTicketAppData =
HandshakeMode = Resume
ResumptionExpected = Yes
SessionTicketExpected = Yes

View file

@ -0,0 +1,99 @@
# -*- mode: perl; -*-
# Copyright 2018 The OpenSSL Project Authors. All Rights Reserved.
#
# Licensed under the OpenSSL license (the "License"). You may not use
# this file except in compliance with the License. You can obtain a copy
# in the file LICENSE in the source distribution or at
# https://www.openssl.org/source/license.html
## Test session ticket app data
use strict;
use warnings;
package ssltests;
use OpenSSL::Test::Utils;
our @tests12 = (
{
"name" => "session-ticket-app-data12",
"client" => {
"MaxProtocol" => "TLSv1.2",
"Options" => "SessionTicket",
},
"server" => {
"Options" => "SessionTicket",
"extra" => {
"SessionTicketAppData" => "HelloWorld",
},
},
"test" => {
"HandshakeMode" => "Resume",
"ExpectedResult" => "Success",
"SessionTicketExpected" => "Yes",
"ResumptionExpected" => "Yes",
"ExpectedSessionTicketAppData" => "HelloWorld",
}
},
{
"name" => "session-ticket-app-data12",
"client" => {
"MaxProtocol" => "TLSv1.2",
"Options" => "SessionTicket",
},
"server" => {
"Options" => "SessionTicket",
},
"test" => {
"HandshakeMode" => "Resume",
"ExpectedResult" => "Success",
"SessionTicketExpected" => "Yes",
"ResumptionExpected" => "Yes",
"ExpectedSessionTicketAppData" => "",
}
}
);
our @tests13 = (
{
"name" => "session-ticket-app-data13",
"client" => {
"MaxProtocol" => "TLSv1.3",
"Options" => "SessionTicket",
},
"server" => {
"Options" => "SessionTicket",
"extra" => {
"SessionTicketAppData" => "HelloWorld",
},
},
"test" => {
"HandshakeMode" => "Resume",
"ExpectedResult" => "Success",
"SessionTicketExpected" => "Yes",
"ResumptionExpected" => "Yes",
"ExpectedSessionTicketAppData" => "HelloWorld",
}
},
{
"name" => "session-ticket-app-data13",
"client" => {
"MaxProtocol" => "TLSv1.3",
"Options" => "SessionTicket",
},
"server" => {
"Options" => "SessionTicket",
},
"test" => {
"HandshakeMode" => "Resume",
"ExpectedResult" => "Success",
"SessionTicketExpected" => "Yes",
"ResumptionExpected" => "Yes",
"ExpectedSessionTicketAppData" => "",
}
}
);
our @tests = ();
push @tests, @tests12 unless disabled("tls1_2");
push @tests, @tests13 unless disabled("tls1_3");

View file

@ -188,6 +188,27 @@ static int check_alpn(HANDSHAKE_RESULT *result, SSL_TEST_CTX *test_ctx)
return ret;
}
static int check_session_ticket_app_data(HANDSHAKE_RESULT *result,
SSL_TEST_CTX *test_ctx)
{
size_t result_len = 0;
size_t expected_len = 0;
/* consider empty and NULL strings to be the same */
if (result->result_session_ticket_app_data != NULL)
result_len = strlen(result->result_session_ticket_app_data);
if (test_ctx->expected_session_ticket_app_data != NULL)
expected_len = strlen(test_ctx->expected_session_ticket_app_data);
if (result_len == 0 && expected_len == 0)
return 1;
if (!TEST_str_eq(result->result_session_ticket_app_data,
test_ctx->expected_session_ticket_app_data))
return 0;
return 1;
}
static int check_resumption(HANDSHAKE_RESULT *result, SSL_TEST_CTX *test_ctx)
{
if (!TEST_int_eq(result->client_resumed, result->server_resumed))
@ -352,6 +373,7 @@ static int check_test(HANDSHAKE_RESULT *result, SSL_TEST_CTX *test_ctx)
#endif
ret &= check_cipher(result, test_ctx);
ret &= check_alpn(result, test_ctx);
ret &= check_session_ticket_app_data(result, test_ctx);
ret &= check_resumption(result, test_ctx);
ret &= check_tmp_key(result, test_ctx);
ret &= check_server_cert_type(result, test_ctx);

View file

@ -99,6 +99,7 @@ static const test_enum ssl_test_results[] = {
{"ServerFail", SSL_TEST_SERVER_FAIL},
{"ClientFail", SSL_TEST_CLIENT_FAIL},
{"InternalError", SSL_TEST_INTERNAL_ERROR},
{"FirstHandshakeFailed", SSL_TEST_FIRST_HANDSHAKE_FAILED},
};
__owur static int parse_expected_result(SSL_TEST_CTX *test_ctx, const char *value)
@ -360,6 +361,10 @@ IMPLEMENT_SSL_TEST_STRING_OPTION(SSL_TEST_SERVER_CONF, server, srp_user)
IMPLEMENT_SSL_TEST_STRING_OPTION(SSL_TEST_CLIENT_CONF, client, srp_password)
IMPLEMENT_SSL_TEST_STRING_OPTION(SSL_TEST_SERVER_CONF, server, srp_password)
/* Session Ticket App Data options */
IMPLEMENT_SSL_TEST_STRING_OPTION(SSL_TEST_CTX, test, expected_session_ticket_app_data)
IMPLEMENT_SSL_TEST_STRING_OPTION(SSL_TEST_SERVER_CONF, server, session_ticket_app_data)
/* Handshake mode */
static const test_enum ssl_handshake_modes[] = {
@ -664,6 +669,7 @@ static const ssl_test_ctx_option ssl_test_ctx_options[] = {
{ "ExpectedClientCANames", &parse_expected_client_ca_names },
{ "UseSCTP", &parse_test_use_sctp },
{ "ExpectedCipher", &parse_test_expected_cipher },
{ "ExpectedSessionTicketAppData", &parse_test_expected_session_ticket_app_data },
};
/* Nested client options. */
@ -700,6 +706,7 @@ static const ssl_test_server_option ssl_test_server_options[] = {
{ "SRPUser", &parse_server_srp_user },
{ "SRPPassword", &parse_server_srp_password },
{ "ForcePHA", &parse_server_force_pha },
{ "SessionTicketAppData", &parse_server_session_ticket_app_data },
};
SSL_TEST_CTX *SSL_TEST_CTX_new()
@ -729,6 +736,8 @@ static void ssl_test_extra_conf_free_data(SSL_TEST_EXTRA_CONF *conf)
OPENSSL_free(conf->server2.srp_password);
OPENSSL_free(conf->client.srp_user);
OPENSSL_free(conf->client.srp_password);
OPENSSL_free(conf->server.session_ticket_app_data);
OPENSSL_free(conf->server2.session_ticket_app_data);
}
static void ssl_test_ctx_free_extra_data(SSL_TEST_CTX *ctx)
@ -742,6 +751,7 @@ void SSL_TEST_CTX_free(SSL_TEST_CTX *ctx)
ssl_test_ctx_free_extra_data(ctx);
OPENSSL_free(ctx->expected_npn_protocol);
OPENSSL_free(ctx->expected_alpn_protocol);
OPENSSL_free(ctx->expected_session_ticket_app_data);
sk_X509_NAME_pop_free(ctx->expected_server_ca_names, X509_NAME_free);
sk_X509_NAME_pop_free(ctx->expected_client_ca_names, X509_NAME_free);
OPENSSL_free(ctx->expected_cipher);

View file

@ -127,6 +127,7 @@ typedef struct {
char *srp_password;
/* Forced PHA */
int force_pha;
char *session_ticket_app_data;
} SSL_TEST_SERVER_CONF;
typedef struct {
@ -216,6 +217,8 @@ typedef struct {
/* Whether to expect a session id from the server */
ssl_session_id_t session_id_expected;
char *expected_cipher;
/* Expected Session Ticket Application Data */
char *expected_session_ticket_app_data;
} SSL_TEST_CTX;
const char *ssl_test_result_name(ssl_test_result_t result);

View file

@ -55,6 +55,8 @@ static int serverconf_eq(SSL_TEST_SERVER_CONF *serv,
|| !TEST_str_eq(serv->alpn_protocols, serv2->alpn_protocols)
|| !TEST_int_eq(serv->broken_session_ticket,
serv2->broken_session_ticket)
|| !TEST_str_eq(serv->session_ticket_app_data,
serv2->session_ticket_app_data)
|| !TEST_int_eq(serv->cert_status, serv2->cert_status))
return 0;
return 1;
@ -95,6 +97,8 @@ static int testctx_eq(SSL_TEST_CTX *ctx, SSL_TEST_CTX *ctx2)
ctx2->expected_alpn_protocol)
|| !TEST_str_eq(ctx->expected_cipher,
ctx2->expected_cipher)
|| !TEST_str_eq(ctx->expected_session_ticket_app_data,
ctx2->expected_session_ticket_app_data)
|| !TEST_int_eq(ctx->resumption_expected,
ctx2->resumption_expected)
|| !TEST_int_eq(ctx->session_id_expected,

View file

@ -479,3 +479,6 @@ SSL_force_post_handshake_auth 479 1_1_1 EXIST::FUNCTION:
SSL_export_keying_material_early 480 1_1_1 EXIST::FUNCTION:
SSL_CTX_use_cert_and_key 481 1_1_1 EXIST::FUNCTION:
SSL_use_cert_and_key 482 1_1_1 EXIST::FUNCTION:
SSL_SESSION_get0_ticket_appdata 483 1_1_1 EXIST::FUNCTION:
SSL_SESSION_set1_ticket_appdata 484 1_1_1 EXIST::FUNCTION:
SSL_CTX_set_session_ticket_cb 485 1_1_1 EXIST::FUNCTION:

View file

@ -248,7 +248,9 @@ SSL_CTX_build_cert_chain define
SSL_CTX_clear_chain_certs define
SSL_CTX_clear_extra_chain_certs define
SSL_CTX_clear_mode define
SSL_CTX_decrypt_session_ticket_fn define
SSL_CTX_disable_ct define
SSL_CTX_generate_session_ticket_fn define
SSL_CTX_get0_chain_certs define
SSL_CTX_get_default_read_ahead define
SSL_CTX_get_max_cert_list define