2016-05-17 18:18:30 +00:00
|
|
|
/*
|
2018-03-20 13:00:17 +00:00
|
|
|
* Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved.
|
2017-06-20 14:14:36 +00:00
|
|
|
* Copyright 2005 Nokia. All rights reserved.
|
2006-01-03 03:27:19 +00:00
|
|
|
*
|
2016-05-17 18:18:30 +00:00
|
|
|
* 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
|
2006-01-03 03:27:19 +00:00
|
|
|
*/
|
2016-05-17 18:18:30 +00:00
|
|
|
|
1998-12-21 10:52:47 +00:00
|
|
|
#include <stdio.h>
|
1999-04-23 22:13:45 +00:00
|
|
|
#include <openssl/rand.h>
|
2016-03-18 18:30:20 +00:00
|
|
|
#include <openssl/engine.h>
|
2017-08-21 21:17:35 +00:00
|
|
|
#include "internal/refcount.h"
|
2018-06-13 14:57:39 +00:00
|
|
|
#include "internal/cryptlib.h"
|
1998-12-21 10:52:47 +00:00
|
|
|
#include "ssl_locl.h"
|
2017-01-18 16:28:23 +00:00
|
|
|
#include "statem/statem_locl.h"
|
1998-12-21 10:52:47 +00:00
|
|
|
|
1998-12-21 10:56:39 +00:00
|
|
|
static void SSL_SESSION_list_remove(SSL_CTX *ctx, SSL_SESSION *s);
|
2015-01-22 03:40:55 +00:00
|
|
|
static void SSL_SESSION_list_add(SSL_CTX *ctx, SSL_SESSION *s);
|
1999-04-29 22:25:52 +00:00
|
|
|
static int remove_session_lock(SSL_CTX *ctx, SSL_SESSION *c, int lck);
|
1998-12-21 10:56:39 +00:00
|
|
|
|
2017-01-13 13:34:49 +00:00
|
|
|
/*
|
2017-03-21 13:50:31 +00:00
|
|
|
* SSL_get_session() and SSL_get1_session() are problematic in TLS1.3 because,
|
|
|
|
* unlike in earlier protocol versions, the session ticket may not have been
|
|
|
|
* sent yet even though a handshake has finished. The session ticket data could
|
|
|
|
* come in sometime later...or even change if multiple session ticket messages
|
|
|
|
* are sent from the server. The preferred way for applications to obtain
|
|
|
|
* a resumable session is to use SSL_CTX_sess_set_new_cb().
|
2017-01-13 13:34:49 +00:00
|
|
|
*/
|
|
|
|
|
2005-03-30 10:26:02 +00:00
|
|
|
SSL_SESSION *SSL_get_session(const SSL *ssl)
|
2000-01-26 22:36:55 +00:00
|
|
|
/* aka SSL_get0_session; gets 0 objects, just returns a copy of the pointer */
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
2017-10-17 14:04:09 +00:00
|
|
|
return ssl->session;
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
2000-01-26 22:36:55 +00:00
|
|
|
|
|
|
|
SSL_SESSION *SSL_get1_session(SSL *ssl)
|
|
|
|
/* variant of SSL_get_session: caller really gets something */
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
|
|
|
SSL_SESSION *sess;
|
|
|
|
/*
|
|
|
|
* Need to lock this all up rather than just use CRYPTO_add so that
|
|
|
|
* somebody doesn't free ssl->session between when we check it's non-null
|
|
|
|
* and when we up the reference count.
|
|
|
|
*/
|
2016-02-29 17:26:07 +00:00
|
|
|
CRYPTO_THREAD_read_lock(ssl->lock);
|
2015-01-22 03:40:55 +00:00
|
|
|
sess = ssl->session;
|
|
|
|
if (sess)
|
2016-02-29 17:26:07 +00:00
|
|
|
SSL_SESSION_up_ref(sess);
|
|
|
|
CRYPTO_THREAD_unlock(ssl->lock);
|
|
|
|
return sess;
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
|
|
|
|
1999-04-19 21:31:43 +00:00
|
|
|
int SSL_SESSION_set_ex_data(SSL_SESSION *s, int idx, void *arg)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
2017-10-17 14:04:09 +00:00
|
|
|
return CRYPTO_set_ex_data(&s->ex_data, idx, arg);
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
1998-12-21 10:56:39 +00:00
|
|
|
|
2005-03-30 10:26:02 +00:00
|
|
|
void *SSL_SESSION_get_ex_data(const SSL_SESSION *s, int idx)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
2017-10-17 14:04:09 +00:00
|
|
|
return CRYPTO_get_ex_data(&s->ex_data, idx);
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
1998-12-21 10:56:39 +00:00
|
|
|
|
1999-04-19 21:31:43 +00:00
|
|
|
SSL_SESSION *SSL_SESSION_new(void)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
|
|
|
SSL_SESSION *ss;
|
|
|
|
|
2017-01-19 15:01:55 +00:00
|
|
|
if (!OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL))
|
|
|
|
return NULL;
|
|
|
|
|
2015-08-25 17:25:58 +00:00
|
|
|
ss = OPENSSL_zalloc(sizeof(*ss));
|
2015-01-22 03:40:55 +00:00
|
|
|
if (ss == NULL) {
|
|
|
|
SSLerr(SSL_F_SSL_SESSION_NEW, ERR_R_MALLOC_FAILURE);
|
2016-02-29 17:26:07 +00:00
|
|
|
return NULL;
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ss->verify_result = 1; /* avoid 0 (= X509_V_OK) just in case */
|
|
|
|
ss->references = 1;
|
|
|
|
ss->timeout = 60 * 5 + 4; /* 5 minute timeout by default */
|
|
|
|
ss->time = (unsigned long)time(NULL);
|
2016-02-29 17:26:07 +00:00
|
|
|
ss->lock = CRYPTO_THREAD_lock_new();
|
|
|
|
if (ss->lock == NULL) {
|
|
|
|
SSLerr(SSL_F_SSL_SESSION_NEW, ERR_R_MALLOC_FAILURE);
|
|
|
|
OPENSSL_free(ss);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-02-13 18:29:34 +00:00
|
|
|
if (!CRYPTO_new_ex_data(CRYPTO_EX_INDEX_SSL_SESSION, ss, &ss->ex_data)) {
|
|
|
|
CRYPTO_THREAD_lock_free(ss->lock);
|
|
|
|
OPENSSL_free(ss);
|
|
|
|
return NULL;
|
|
|
|
}
|
2016-02-29 17:26:07 +00:00
|
|
|
return ss;
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
1998-12-21 10:52:47 +00:00
|
|
|
|
2017-08-02 12:32:56 +00:00
|
|
|
SSL_SESSION *SSL_SESSION_dup(SSL_SESSION *src)
|
|
|
|
{
|
|
|
|
return ssl_session_dup(src, 1);
|
|
|
|
}
|
|
|
|
|
2015-05-18 15:27:48 +00:00
|
|
|
/*
|
|
|
|
* Create a new SSL_SESSION and duplicate the contents of |src| into it. If
|
|
|
|
* ticket == 0 then no ticket information is duplicated, otherwise it is.
|
|
|
|
*/
|
|
|
|
SSL_SESSION *ssl_session_dup(SSL_SESSION *src, int ticket)
|
|
|
|
{
|
|
|
|
SSL_SESSION *dest;
|
|
|
|
|
|
|
|
dest = OPENSSL_malloc(sizeof(*src));
|
|
|
|
if (dest == NULL) {
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
memcpy(dest, src, sizeof(*dest));
|
|
|
|
|
2015-06-11 00:30:06 +00:00
|
|
|
/*
|
|
|
|
* Set the various pointers to NULL so that we can call SSL_SESSION_free in
|
|
|
|
* the case of an error whilst halfway through constructing dest
|
|
|
|
*/
|
|
|
|
#ifndef OPENSSL_NO_PSK
|
|
|
|
dest->psk_identity_hint = NULL;
|
|
|
|
dest->psk_identity = NULL;
|
|
|
|
#endif
|
|
|
|
dest->ciphers = NULL;
|
2016-12-08 19:18:40 +00:00
|
|
|
dest->ext.hostname = NULL;
|
2015-06-11 00:30:06 +00:00
|
|
|
#ifndef OPENSSL_NO_EC
|
2016-12-08 19:18:40 +00:00
|
|
|
dest->ext.ecpointformats = NULL;
|
|
|
|
dest->ext.supportedgroups = NULL;
|
2015-06-11 00:30:06 +00:00
|
|
|
#endif
|
2016-12-08 19:18:40 +00:00
|
|
|
dest->ext.tick = NULL;
|
2017-06-26 13:21:20 +00:00
|
|
|
dest->ext.alpn_selected = NULL;
|
2015-06-11 00:30:06 +00:00
|
|
|
#ifndef OPENSSL_NO_SRP
|
|
|
|
dest->srp_username = NULL;
|
|
|
|
#endif
|
2017-04-26 18:05:49 +00:00
|
|
|
dest->peer_chain = NULL;
|
|
|
|
dest->peer = NULL;
|
2017-03-15 17:25:55 +00:00
|
|
|
dest->ticket_appdata = NULL;
|
2017-07-07 10:21:29 +00:00
|
|
|
memset(&dest->ex_data, 0, sizeof(dest->ex_data));
|
2017-07-05 07:45:46 +00:00
|
|
|
|
2015-06-11 00:30:06 +00:00
|
|
|
/* We deliberately don't copy the prev and next pointers */
|
|
|
|
dest->prev = NULL;
|
|
|
|
dest->next = NULL;
|
|
|
|
|
|
|
|
dest->references = 1;
|
|
|
|
|
2016-02-29 17:26:07 +00:00
|
|
|
dest->lock = CRYPTO_THREAD_lock_new();
|
|
|
|
if (dest->lock == NULL)
|
|
|
|
goto err;
|
|
|
|
|
2017-04-26 18:05:49 +00:00
|
|
|
if (!CRYPTO_new_ex_data(CRYPTO_EX_INDEX_SSL_SESSION, dest, &dest->ex_data))
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
if (src->peer != NULL) {
|
|
|
|
if (!X509_up_ref(src->peer))
|
|
|
|
goto err;
|
|
|
|
dest->peer = src->peer;
|
|
|
|
}
|
2015-06-11 00:30:06 +00:00
|
|
|
|
2015-06-30 12:58:25 +00:00
|
|
|
if (src->peer_chain != NULL) {
|
|
|
|
dest->peer_chain = X509_chain_up_ref(src->peer_chain);
|
|
|
|
if (dest->peer_chain == NULL)
|
|
|
|
goto err;
|
|
|
|
}
|
2015-05-18 15:27:48 +00:00
|
|
|
#ifndef OPENSSL_NO_PSK
|
|
|
|
if (src->psk_identity_hint) {
|
Rename some BUF_xxx to OPENSSL_xxx
Rename BUF_{strdup,strlcat,strlcpy,memdup,strndup,strnlen}
to OPENSSL_{strdup,strlcat,strlcpy,memdup,strndup,strnlen}
Add #define's for the old names.
Add CRYPTO_{memdup,strndup}, called by OPENSSL_{memdup,strndup} macros.
Reviewed-by: Tim Hudson <tjh@openssl.org>
2015-12-16 21:12:24 +00:00
|
|
|
dest->psk_identity_hint = OPENSSL_strdup(src->psk_identity_hint);
|
2015-05-18 15:27:48 +00:00
|
|
|
if (dest->psk_identity_hint == NULL) {
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (src->psk_identity) {
|
Rename some BUF_xxx to OPENSSL_xxx
Rename BUF_{strdup,strlcat,strlcpy,memdup,strndup,strnlen}
to OPENSSL_{strdup,strlcat,strlcpy,memdup,strndup,strnlen}
Add #define's for the old names.
Add CRYPTO_{memdup,strndup}, called by OPENSSL_{memdup,strndup} macros.
Reviewed-by: Tim Hudson <tjh@openssl.org>
2015-12-16 21:12:24 +00:00
|
|
|
dest->psk_identity = OPENSSL_strdup(src->psk_identity);
|
2015-05-18 15:27:48 +00:00
|
|
|
if (dest->psk_identity == NULL) {
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-06-28 22:18:50 +00:00
|
|
|
if (src->ciphers != NULL) {
|
2015-05-18 15:27:48 +00:00
|
|
|
dest->ciphers = sk_SSL_CIPHER_dup(src->ciphers);
|
|
|
|
if (dest->ciphers == NULL)
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!CRYPTO_dup_ex_data(CRYPTO_EX_INDEX_SSL_SESSION,
|
2016-08-05 17:03:17 +00:00
|
|
|
&dest->ex_data, &src->ex_data)) {
|
2015-05-18 15:27:48 +00:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2016-12-08 19:18:40 +00:00
|
|
|
if (src->ext.hostname) {
|
|
|
|
dest->ext.hostname = OPENSSL_strdup(src->ext.hostname);
|
|
|
|
if (dest->ext.hostname == NULL) {
|
2015-05-18 15:27:48 +00:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
2015-06-11 00:30:06 +00:00
|
|
|
#ifndef OPENSSL_NO_EC
|
2016-12-08 19:18:40 +00:00
|
|
|
if (src->ext.ecpointformats) {
|
|
|
|
dest->ext.ecpointformats =
|
|
|
|
OPENSSL_memdup(src->ext.ecpointformats,
|
|
|
|
src->ext.ecpointformats_len);
|
|
|
|
if (dest->ext.ecpointformats == NULL)
|
2015-05-18 15:27:48 +00:00
|
|
|
goto err;
|
|
|
|
}
|
2016-12-08 19:18:40 +00:00
|
|
|
if (src->ext.supportedgroups) {
|
|
|
|
dest->ext.supportedgroups =
|
|
|
|
OPENSSL_memdup(src->ext.supportedgroups,
|
2018-05-25 06:06:55 +00:00
|
|
|
src->ext.supportedgroups_len
|
|
|
|
* sizeof(*src->ext.supportedgroups));
|
2016-12-08 19:18:40 +00:00
|
|
|
if (dest->ext.supportedgroups == NULL)
|
2015-05-18 15:27:48 +00:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-04-26 18:05:49 +00:00
|
|
|
if (ticket != 0 && src->ext.tick != NULL) {
|
2016-12-08 19:18:40 +00:00
|
|
|
dest->ext.tick =
|
|
|
|
OPENSSL_memdup(src->ext.tick, src->ext.ticklen);
|
|
|
|
if (dest->ext.tick == NULL)
|
2015-05-18 15:27:48 +00:00
|
|
|
goto err;
|
2015-06-11 00:30:06 +00:00
|
|
|
} else {
|
2016-12-08 19:18:40 +00:00
|
|
|
dest->ext.tick_lifetime_hint = 0;
|
|
|
|
dest->ext.ticklen = 0;
|
2015-05-18 15:27:48 +00:00
|
|
|
}
|
|
|
|
|
2018-06-18 10:30:21 +00:00
|
|
|
if (src->ext.alpn_selected != NULL) {
|
|
|
|
dest->ext.alpn_selected = OPENSSL_memdup(src->ext.alpn_selected,
|
|
|
|
src->ext.alpn_selected_len);
|
|
|
|
if (dest->ext.alpn_selected == NULL)
|
2017-06-26 13:21:20 +00:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2015-05-18 15:27:48 +00:00
|
|
|
#ifndef OPENSSL_NO_SRP
|
|
|
|
if (src->srp_username) {
|
Rename some BUF_xxx to OPENSSL_xxx
Rename BUF_{strdup,strlcat,strlcpy,memdup,strndup,strnlen}
to OPENSSL_{strdup,strlcat,strlcpy,memdup,strndup,strnlen}
Add #define's for the old names.
Add CRYPTO_{memdup,strndup}, called by OPENSSL_{memdup,strndup} macros.
Reviewed-by: Tim Hudson <tjh@openssl.org>
2015-12-16 21:12:24 +00:00
|
|
|
dest->srp_username = OPENSSL_strdup(src->srp_username);
|
2015-05-18 15:27:48 +00:00
|
|
|
if (dest->srp_username == NULL) {
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-03-15 17:25:55 +00:00
|
|
|
if (src->ticket_appdata != NULL) {
|
|
|
|
dest->ticket_appdata =
|
|
|
|
OPENSSL_memdup(src->ticket_appdata, src->ticket_appdata_len);
|
|
|
|
if (dest->ticket_appdata == NULL)
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2015-05-18 15:27:48 +00:00
|
|
|
return dest;
|
2016-08-05 17:03:17 +00:00
|
|
|
err:
|
2015-05-18 15:27:48 +00:00
|
|
|
SSLerr(SSL_F_SSL_SESSION_DUP, ERR_R_MALLOC_FAILURE);
|
|
|
|
SSL_SESSION_free(dest);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-08-05 17:03:17 +00:00
|
|
|
const unsigned char *SSL_SESSION_get_id(const SSL_SESSION *s, unsigned int *len)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
|
|
|
if (len)
|
2016-10-19 14:11:24 +00:00
|
|
|
*len = (unsigned int)s->session_id_length;
|
2015-01-22 03:40:55 +00:00
|
|
|
return s->session_id;
|
|
|
|
}
|
2016-08-06 10:54:29 +00:00
|
|
|
const unsigned char *SSL_SESSION_get0_id_context(const SSL_SESSION *s,
|
|
|
|
unsigned int *len)
|
|
|
|
{
|
|
|
|
if (len != NULL)
|
2016-10-19 14:11:24 +00:00
|
|
|
*len = (unsigned int)s->sid_ctx_length;
|
2016-08-06 10:54:29 +00:00
|
|
|
return s->sid_ctx;
|
|
|
|
}
|
2003-02-15 20:38:57 +00:00
|
|
|
|
2011-12-22 15:14:32 +00:00
|
|
|
unsigned int SSL_SESSION_get_compress_id(const SSL_SESSION *s)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
|
|
|
return s->compress_meth;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* SSLv3/TLSv1 has 32 bytes (256 bits) of session ID space. As such, filling
|
|
|
|
* the ID with random junk repeatedly until we have no conflict is going to
|
|
|
|
* complete in one iteration pretty much "most" of the time (btw:
|
|
|
|
* understatement). So, if it takes us 10 iterations and we still can't avoid
|
|
|
|
* a conflict - well that's a reasonable point to call it quits. Either the
|
|
|
|
* RAND code is broken or someone is trying to open roughly very close to
|
|
|
|
* 2^256 SSL sessions to our server. How you might store that many sessions
|
|
|
|
* is perhaps a more interesting question ...
|
|
|
|
*/
|
2001-02-21 18:06:26 +00:00
|
|
|
|
|
|
|
#define MAX_SESS_ID_ATTEMPTS 10
|
2017-08-03 14:24:03 +00:00
|
|
|
static int def_generate_session_id(SSL *ssl, unsigned char *id,
|
2015-01-22 03:40:55 +00:00
|
|
|
unsigned int *id_len)
|
2001-02-21 18:06:26 +00:00
|
|
|
{
|
2015-01-22 03:40:55 +00:00
|
|
|
unsigned int retry = 0;
|
|
|
|
do
|
2018-03-08 21:30:28 +00:00
|
|
|
if (RAND_bytes(id, *id_len) <= 0)
|
2015-01-22 03:40:55 +00:00
|
|
|
return 0;
|
|
|
|
while (SSL_has_matching_session_id(ssl, id, *id_len) &&
|
|
|
|
(++retry < MAX_SESS_ID_ATTEMPTS)) ;
|
|
|
|
if (retry < MAX_SESS_ID_ATTEMPTS)
|
|
|
|
return 1;
|
|
|
|
/* else - woops a session_id match */
|
|
|
|
/*
|
|
|
|
* XXX We should also check the external cache -- but the probability of
|
|
|
|
* a collision is negligible, and we could not prevent the concurrent
|
|
|
|
* creation of sessions with identical IDs since we currently don't have
|
|
|
|
* means to atomically check whether a session ID already exists and make
|
|
|
|
* a reservation for it if it does not (this problem applies to the
|
|
|
|
* internal cache as well).
|
|
|
|
*/
|
|
|
|
return 0;
|
2001-02-21 18:06:26 +00:00
|
|
|
}
|
|
|
|
|
Session resume broken switching contexts
When an SSL's context is swtiched from a ticket-enabled context to
a ticket-disabled context in the servername callback, no session-id
is generated, so the session can't be resumed.
If a servername callback changes the SSL_OP_NO_TICKET option, check
to see if it's changed to disable, and whether a session ticket is
expected (i.e. the client indicated ticket support and the SSL had
tickets enabled at the time), and whether we already have a previous
session (i.e. s->hit is set).
In this case, clear the ticket-expected flag, remove any ticket data
and generate a session-id in the session.
If the SSL hit (resumed) and switched to a ticket-disabled context,
assume that the resumption was via session-id, and don't bother to
update the session.
Before this fix, the updated unit-tests in 06-sni-ticket.conf would
fail test #4 (server1 = SNI, server2 = no SNI).
Reviewed-by: Rich Salz <rsalz@openssl.org>
Reviewed-by: Richard Levitte <levitte@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Paul Dale <paul.dale@oracle.com>
(Merged from https://github.com/openssl/openssl/pull/1529)
2016-09-01 12:40:54 +00:00
|
|
|
int ssl_generate_session_id(SSL *s, SSL_SESSION *ss)
|
|
|
|
{
|
|
|
|
unsigned int tmp;
|
|
|
|
GEN_SESSION_CB cb = def_generate_session_id;
|
|
|
|
|
|
|
|
switch (s->version) {
|
|
|
|
case SSL3_VERSION:
|
|
|
|
case TLS1_VERSION:
|
|
|
|
case TLS1_1_VERSION:
|
|
|
|
case TLS1_2_VERSION:
|
|
|
|
case TLS1_3_VERSION:
|
|
|
|
case DTLS1_BAD_VER:
|
|
|
|
case DTLS1_VERSION:
|
|
|
|
case DTLS1_2_VERSION:
|
|
|
|
ss->session_id_length = SSL3_SSL_SESSION_ID_LENGTH;
|
|
|
|
break;
|
|
|
|
default:
|
2017-11-21 17:18:43 +00:00
|
|
|
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_GENERATE_SESSION_ID,
|
|
|
|
SSL_R_UNSUPPORTED_SSL_VERSION);
|
Session resume broken switching contexts
When an SSL's context is swtiched from a ticket-enabled context to
a ticket-disabled context in the servername callback, no session-id
is generated, so the session can't be resumed.
If a servername callback changes the SSL_OP_NO_TICKET option, check
to see if it's changed to disable, and whether a session ticket is
expected (i.e. the client indicated ticket support and the SSL had
tickets enabled at the time), and whether we already have a previous
session (i.e. s->hit is set).
In this case, clear the ticket-expected flag, remove any ticket data
and generate a session-id in the session.
If the SSL hit (resumed) and switched to a ticket-disabled context,
assume that the resumption was via session-id, and don't bother to
update the session.
Before this fix, the updated unit-tests in 06-sni-ticket.conf would
fail test #4 (server1 = SNI, server2 = no SNI).
Reviewed-by: Rich Salz <rsalz@openssl.org>
Reviewed-by: Richard Levitte <levitte@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Paul Dale <paul.dale@oracle.com>
(Merged from https://github.com/openssl/openssl/pull/1529)
2016-09-01 12:40:54 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*-
|
|
|
|
* If RFC5077 ticket, use empty session ID (as server).
|
|
|
|
* Note that:
|
|
|
|
* (a) ssl_get_prev_session() does lookahead into the
|
|
|
|
* ClientHello extensions to find the session ticket.
|
|
|
|
* When ssl_get_prev_session() fails, statem_srvr.c calls
|
|
|
|
* ssl_get_new_session() in tls_process_client_hello().
|
|
|
|
* At that point, it has not yet parsed the extensions,
|
|
|
|
* however, because of the lookahead, it already knows
|
|
|
|
* whether a ticket is expected or not.
|
|
|
|
*
|
|
|
|
* (b) statem_clnt.c calls ssl_get_new_session() before parsing
|
|
|
|
* ServerHello extensions, and before recording the session
|
|
|
|
* ID received from the server, so this block is a noop.
|
|
|
|
*/
|
|
|
|
if (s->ext.ticket_expected) {
|
|
|
|
ss->session_id_length = 0;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Choose which callback will set the session ID */
|
|
|
|
CRYPTO_THREAD_read_lock(s->lock);
|
|
|
|
CRYPTO_THREAD_read_lock(s->session_ctx->lock);
|
|
|
|
if (s->generate_session_id)
|
|
|
|
cb = s->generate_session_id;
|
|
|
|
else if (s->session_ctx->generate_session_id)
|
|
|
|
cb = s->session_ctx->generate_session_id;
|
|
|
|
CRYPTO_THREAD_unlock(s->session_ctx->lock);
|
|
|
|
CRYPTO_THREAD_unlock(s->lock);
|
|
|
|
/* Choose a session ID */
|
|
|
|
memset(ss->session_id, 0, ss->session_id_length);
|
|
|
|
tmp = (int)ss->session_id_length;
|
|
|
|
if (!cb(s, ss->session_id, &tmp)) {
|
|
|
|
/* The callback failed */
|
2017-11-21 17:18:43 +00:00
|
|
|
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_GENERATE_SESSION_ID,
|
|
|
|
SSL_R_SSL_SESSION_ID_CALLBACK_FAILED);
|
Session resume broken switching contexts
When an SSL's context is swtiched from a ticket-enabled context to
a ticket-disabled context in the servername callback, no session-id
is generated, so the session can't be resumed.
If a servername callback changes the SSL_OP_NO_TICKET option, check
to see if it's changed to disable, and whether a session ticket is
expected (i.e. the client indicated ticket support and the SSL had
tickets enabled at the time), and whether we already have a previous
session (i.e. s->hit is set).
In this case, clear the ticket-expected flag, remove any ticket data
and generate a session-id in the session.
If the SSL hit (resumed) and switched to a ticket-disabled context,
assume that the resumption was via session-id, and don't bother to
update the session.
Before this fix, the updated unit-tests in 06-sni-ticket.conf would
fail test #4 (server1 = SNI, server2 = no SNI).
Reviewed-by: Rich Salz <rsalz@openssl.org>
Reviewed-by: Richard Levitte <levitte@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Paul Dale <paul.dale@oracle.com>
(Merged from https://github.com/openssl/openssl/pull/1529)
2016-09-01 12:40:54 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Don't allow the callback to set the session length to zero. nor
|
|
|
|
* set it higher than it was.
|
|
|
|
*/
|
|
|
|
if (tmp == 0 || tmp > ss->session_id_length) {
|
|
|
|
/* The callback set an illegal length */
|
2017-11-21 17:18:43 +00:00
|
|
|
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_GENERATE_SESSION_ID,
|
|
|
|
SSL_R_SSL_SESSION_ID_HAS_BAD_LENGTH);
|
Session resume broken switching contexts
When an SSL's context is swtiched from a ticket-enabled context to
a ticket-disabled context in the servername callback, no session-id
is generated, so the session can't be resumed.
If a servername callback changes the SSL_OP_NO_TICKET option, check
to see if it's changed to disable, and whether a session ticket is
expected (i.e. the client indicated ticket support and the SSL had
tickets enabled at the time), and whether we already have a previous
session (i.e. s->hit is set).
In this case, clear the ticket-expected flag, remove any ticket data
and generate a session-id in the session.
If the SSL hit (resumed) and switched to a ticket-disabled context,
assume that the resumption was via session-id, and don't bother to
update the session.
Before this fix, the updated unit-tests in 06-sni-ticket.conf would
fail test #4 (server1 = SNI, server2 = no SNI).
Reviewed-by: Rich Salz <rsalz@openssl.org>
Reviewed-by: Richard Levitte <levitte@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Paul Dale <paul.dale@oracle.com>
(Merged from https://github.com/openssl/openssl/pull/1529)
2016-09-01 12:40:54 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
ss->session_id_length = tmp;
|
|
|
|
/* Finally, check for a conflict */
|
|
|
|
if (SSL_has_matching_session_id(s, ss->session_id,
|
|
|
|
(unsigned int)ss->session_id_length)) {
|
2017-11-21 17:18:43 +00:00
|
|
|
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_GENERATE_SESSION_ID,
|
|
|
|
SSL_R_SSL_SESSION_ID_CONFLICT);
|
Session resume broken switching contexts
When an SSL's context is swtiched from a ticket-enabled context to
a ticket-disabled context in the servername callback, no session-id
is generated, so the session can't be resumed.
If a servername callback changes the SSL_OP_NO_TICKET option, check
to see if it's changed to disable, and whether a session ticket is
expected (i.e. the client indicated ticket support and the SSL had
tickets enabled at the time), and whether we already have a previous
session (i.e. s->hit is set).
In this case, clear the ticket-expected flag, remove any ticket data
and generate a session-id in the session.
If the SSL hit (resumed) and switched to a ticket-disabled context,
assume that the resumption was via session-id, and don't bother to
update the session.
Before this fix, the updated unit-tests in 06-sni-ticket.conf would
fail test #4 (server1 = SNI, server2 = no SNI).
Reviewed-by: Rich Salz <rsalz@openssl.org>
Reviewed-by: Richard Levitte <levitte@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Paul Dale <paul.dale@oracle.com>
(Merged from https://github.com/openssl/openssl/pull/1529)
2016-09-01 12:40:54 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
1999-04-19 21:31:43 +00:00
|
|
|
int ssl_get_new_session(SSL *s, int session)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
|
|
|
/* This gets used by clients and servers. */
|
|
|
|
|
|
|
|
SSL_SESSION *ss = NULL;
|
|
|
|
|
2017-11-21 17:18:43 +00:00
|
|
|
if ((ss = SSL_SESSION_new()) == NULL) {
|
|
|
|
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_GET_NEW_SESSION,
|
|
|
|
ERR_R_MALLOC_FAILURE);
|
Session resume broken switching contexts
When an SSL's context is swtiched from a ticket-enabled context to
a ticket-disabled context in the servername callback, no session-id
is generated, so the session can't be resumed.
If a servername callback changes the SSL_OP_NO_TICKET option, check
to see if it's changed to disable, and whether a session ticket is
expected (i.e. the client indicated ticket support and the SSL had
tickets enabled at the time), and whether we already have a previous
session (i.e. s->hit is set).
In this case, clear the ticket-expected flag, remove any ticket data
and generate a session-id in the session.
If the SSL hit (resumed) and switched to a ticket-disabled context,
assume that the resumption was via session-id, and don't bother to
update the session.
Before this fix, the updated unit-tests in 06-sni-ticket.conf would
fail test #4 (server1 = SNI, server2 = no SNI).
Reviewed-by: Rich Salz <rsalz@openssl.org>
Reviewed-by: Richard Levitte <levitte@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Paul Dale <paul.dale@oracle.com>
(Merged from https://github.com/openssl/openssl/pull/1529)
2016-09-01 12:40:54 +00:00
|
|
|
return 0;
|
2017-11-21 17:18:43 +00:00
|
|
|
}
|
2015-01-22 03:40:55 +00:00
|
|
|
|
|
|
|
/* If the context has a default timeout, use it */
|
|
|
|
if (s->session_ctx->session_timeout == 0)
|
|
|
|
ss->timeout = SSL_get_default_timeout(s);
|
|
|
|
else
|
|
|
|
ss->timeout = s->session_ctx->session_timeout;
|
|
|
|
|
2015-04-11 14:22:36 +00:00
|
|
|
SSL_SESSION_free(s->session);
|
|
|
|
s->session = NULL;
|
2015-01-22 03:40:55 +00:00
|
|
|
|
|
|
|
if (session) {
|
2018-03-15 17:47:29 +00:00
|
|
|
if (SSL_IS_TLS13(s)) {
|
|
|
|
/*
|
|
|
|
* We generate the session id while constructing the
|
|
|
|
* NewSessionTicket in TLSv1.3.
|
|
|
|
*/
|
|
|
|
ss->session_id_length = 0;
|
|
|
|
} else if (!ssl_generate_session_id(s, ss)) {
|
2017-11-21 17:18:43 +00:00
|
|
|
/* SSLfatal() already called */
|
2015-01-22 03:40:55 +00:00
|
|
|
SSL_SESSION_free(ss);
|
Session resume broken switching contexts
When an SSL's context is swtiched from a ticket-enabled context to
a ticket-disabled context in the servername callback, no session-id
is generated, so the session can't be resumed.
If a servername callback changes the SSL_OP_NO_TICKET option, check
to see if it's changed to disable, and whether a session ticket is
expected (i.e. the client indicated ticket support and the SSL had
tickets enabled at the time), and whether we already have a previous
session (i.e. s->hit is set).
In this case, clear the ticket-expected flag, remove any ticket data
and generate a session-id in the session.
If the SSL hit (resumed) and switched to a ticket-disabled context,
assume that the resumption was via session-id, and don't bother to
update the session.
Before this fix, the updated unit-tests in 06-sni-ticket.conf would
fail test #4 (server1 = SNI, server2 = no SNI).
Reviewed-by: Rich Salz <rsalz@openssl.org>
Reviewed-by: Richard Levitte <levitte@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Paul Dale <paul.dale@oracle.com>
(Merged from https://github.com/openssl/openssl/pull/1529)
2016-09-01 12:40:54 +00:00
|
|
|
return 0;
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
2015-05-15 09:49:56 +00:00
|
|
|
|
2015-01-22 03:40:55 +00:00
|
|
|
} else {
|
|
|
|
ss->session_id_length = 0;
|
|
|
|
}
|
|
|
|
|
2017-12-07 18:39:34 +00:00
|
|
|
if (s->sid_ctx_length > sizeof(ss->sid_ctx)) {
|
2017-11-21 17:18:43 +00:00
|
|
|
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_GET_NEW_SESSION,
|
|
|
|
ERR_R_INTERNAL_ERROR);
|
2015-01-22 03:40:55 +00:00
|
|
|
SSL_SESSION_free(ss);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
memcpy(ss->sid_ctx, s->sid_ctx, s->sid_ctx_length);
|
|
|
|
ss->sid_ctx_length = s->sid_ctx_length;
|
|
|
|
s->session = ss;
|
|
|
|
ss->ssl_version = s->version;
|
|
|
|
ss->verify_result = X509_V_OK;
|
|
|
|
|
2015-12-04 19:48:15 +00:00
|
|
|
/* If client supports extended master secret set it in session */
|
|
|
|
if (s->s3->flags & TLS1_FLAGS_RECEIVED_EXTMS)
|
|
|
|
ss->flags |= SSL_SESS_FLAG_EXTMS;
|
|
|
|
|
Session resume broken switching contexts
When an SSL's context is swtiched from a ticket-enabled context to
a ticket-disabled context in the servername callback, no session-id
is generated, so the session can't be resumed.
If a servername callback changes the SSL_OP_NO_TICKET option, check
to see if it's changed to disable, and whether a session ticket is
expected (i.e. the client indicated ticket support and the SSL had
tickets enabled at the time), and whether we already have a previous
session (i.e. s->hit is set).
In this case, clear the ticket-expected flag, remove any ticket data
and generate a session-id in the session.
If the SSL hit (resumed) and switched to a ticket-disabled context,
assume that the resumption was via session-id, and don't bother to
update the session.
Before this fix, the updated unit-tests in 06-sni-ticket.conf would
fail test #4 (server1 = SNI, server2 = no SNI).
Reviewed-by: Rich Salz <rsalz@openssl.org>
Reviewed-by: Richard Levitte <levitte@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Paul Dale <paul.dale@oracle.com>
(Merged from https://github.com/openssl/openssl/pull/1529)
2016-09-01 12:40:54 +00:00
|
|
|
return 1;
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
1998-12-21 10:52:47 +00:00
|
|
|
|
2018-06-13 14:57:39 +00:00
|
|
|
SSL_SESSION *lookup_sess_in_cache(SSL *s, const unsigned char *sess_id,
|
|
|
|
size_t sess_id_len)
|
|
|
|
{
|
|
|
|
SSL_SESSION *ret = NULL;
|
|
|
|
|
|
|
|
if ((s->session_ctx->session_cache_mode
|
|
|
|
& SSL_SESS_CACHE_NO_INTERNAL_LOOKUP) == 0) {
|
|
|
|
SSL_SESSION data;
|
|
|
|
|
|
|
|
data.ssl_version = s->version;
|
|
|
|
if (!ossl_assert(sess_id_len <= SSL_MAX_SSL_SESSION_ID_LENGTH))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
memcpy(data.session_id, sess_id, sess_id_len);
|
|
|
|
data.session_id_length = sess_id_len;
|
|
|
|
|
|
|
|
CRYPTO_THREAD_read_lock(s->session_ctx->lock);
|
|
|
|
ret = lh_SSL_SESSION_retrieve(s->session_ctx->sessions, &data);
|
|
|
|
if (ret != NULL) {
|
|
|
|
/* don't allow other threads to steal it: */
|
|
|
|
SSL_SESSION_up_ref(ret);
|
|
|
|
}
|
|
|
|
CRYPTO_THREAD_unlock(s->session_ctx->lock);
|
|
|
|
if (ret == NULL)
|
2018-07-29 12:12:53 +00:00
|
|
|
tsan_counter(&s->session_ctx->stats.sess_miss);
|
2018-06-13 14:57:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == NULL && s->session_ctx->get_session_cb != NULL) {
|
|
|
|
int copy = 1;
|
|
|
|
|
|
|
|
ret = s->session_ctx->get_session_cb(s, sess_id, sess_id_len, ©);
|
|
|
|
|
|
|
|
if (ret != NULL) {
|
2018-07-29 12:12:53 +00:00
|
|
|
tsan_counter(&s->session_ctx->stats.sess_cb_hit);
|
2018-06-13 14:57:39 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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->session_ctx->session_cache_mode &
|
|
|
|
SSL_SESS_CACHE_NO_INTERNAL_STORE) == 0) {
|
|
|
|
/*
|
|
|
|
* Either return value of SSL_CTX_add_session should not
|
|
|
|
* interrupt the session resumption process. The return
|
|
|
|
* value is intentionally ignored.
|
|
|
|
*/
|
|
|
|
(void)SSL_CTX_add_session(s->session_ctx, ret);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-01-05 00:34:00 +00:00
|
|
|
/*-
|
|
|
|
* ssl_get_prev attempts to find an SSL_SESSION to be used to resume this
|
2011-09-05 13:36:23 +00:00
|
|
|
* connection. It is only called by servers.
|
|
|
|
*
|
2016-10-31 13:20:03 +00:00
|
|
|
* hello: The parsed ClientHello data
|
2011-09-05 13:36:23 +00:00
|
|
|
*
|
|
|
|
* Returns:
|
2017-01-18 16:28:23 +00:00
|
|
|
* -1: fatal error
|
|
|
|
* 0: no session found
|
|
|
|
* 1: a session may have been found.
|
2011-09-05 13:36:23 +00:00
|
|
|
*
|
|
|
|
* 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.
|
2016-12-08 19:18:40 +00:00
|
|
|
* - Both for new and resumed sessions, s->ext.ticket_expected is set to 1
|
2011-09-05 13:36:23 +00:00
|
|
|
* if the server should issue a new session ticket (to 0 otherwise).
|
|
|
|
*/
|
2017-11-21 17:18:43 +00:00
|
|
|
int ssl_get_prev_session(SSL *s, CLIENTHELLO_MSG *hello)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
|
|
|
/* This is used only by servers. */
|
1999-05-13 15:09:38 +00:00
|
|
|
|
2015-01-22 03:40:55 +00:00
|
|
|
SSL_SESSION *ret = NULL;
|
2018-07-29 12:12:53 +00:00
|
|
|
int fatal = 0;
|
2017-01-18 16:28:23 +00:00
|
|
|
int try_session_cache = 0;
|
2018-05-09 17:22:36 +00:00
|
|
|
SSL_TICKET_STATUS r;
|
1998-12-21 10:52:47 +00:00
|
|
|
|
2017-01-18 16:28:23 +00:00
|
|
|
if (SSL_IS_TLS13(s)) {
|
2018-05-09 17:22:36 +00:00
|
|
|
/*
|
|
|
|
* By default we will send a new ticket. This can be overridden in the
|
|
|
|
* ticket processing.
|
|
|
|
*/
|
|
|
|
s->ext.ticket_expected = 1;
|
2017-04-04 10:40:02 +00:00
|
|
|
if (!tls_parse_extension(s, TLSEXT_IDX_psk_kex_modes,
|
|
|
|
SSL_EXT_CLIENT_HELLO, hello->pre_proc_exts,
|
2017-11-21 17:18:43 +00:00
|
|
|
NULL, 0)
|
2017-04-04 10:40:02 +00:00
|
|
|
|| !tls_parse_extension(s, TLSEXT_IDX_psk, SSL_EXT_CLIENT_HELLO,
|
2017-11-21 17:18:43 +00:00
|
|
|
hello->pre_proc_exts, NULL, 0))
|
2017-01-18 16:28:23 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = s->session;
|
|
|
|
} else {
|
|
|
|
/* sets s->ext.ticket_expected */
|
|
|
|
r = tls_get_ticket_from_client(s, hello, &ret);
|
|
|
|
switch (r) {
|
2017-03-15 17:25:55 +00:00
|
|
|
case SSL_TICKET_FATAL_ERR_MALLOC:
|
|
|
|
case SSL_TICKET_FATAL_ERR_OTHER:
|
2017-01-18 16:28:23 +00:00
|
|
|
fatal = 1;
|
2017-11-21 17:18:43 +00:00
|
|
|
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_GET_PREV_SESSION,
|
|
|
|
ERR_R_INTERNAL_ERROR);
|
2017-01-18 16:28:23 +00:00
|
|
|
goto err;
|
2017-03-15 17:25:55 +00:00
|
|
|
case SSL_TICKET_NONE:
|
|
|
|
case SSL_TICKET_EMPTY:
|
2018-06-13 14:57:39 +00:00
|
|
|
if (hello->session_id_len > 0) {
|
2017-03-31 13:52:56 +00:00
|
|
|
try_session_cache = 1;
|
2018-06-13 14:57:39 +00:00
|
|
|
ret = lookup_sess_in_cache(s, hello->session_id,
|
|
|
|
hello->session_id_len);
|
|
|
|
}
|
2017-01-27 12:11:23 +00:00
|
|
|
break;
|
2017-03-15 17:25:55 +00:00
|
|
|
case SSL_TICKET_NO_DECRYPT:
|
|
|
|
case SSL_TICKET_SUCCESS:
|
|
|
|
case SSL_TICKET_SUCCESS_RENEW:
|
2017-01-18 16:28:23 +00:00
|
|
|
break;
|
|
|
|
}
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
2011-09-05 13:36:23 +00:00
|
|
|
|
2015-01-22 03:40:55 +00:00
|
|
|
if (ret == NULL)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
/* Now ret is non-NULL and we own one of its reference counts. */
|
|
|
|
|
2017-01-19 10:46:53 +00:00
|
|
|
/* Check TLS version consistency */
|
|
|
|
if (ret->ssl_version != s->version)
|
|
|
|
goto err;
|
|
|
|
|
2015-01-22 03:40:55 +00:00
|
|
|
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 */
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((s->verify_mode & SSL_VERIFY_PEER) && s->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. For this error
|
|
|
|
* case, we generate an error instead of treating the event like a
|
|
|
|
* cache miss (otherwise it would be easy for applications to
|
|
|
|
* effectively disable the session cache by accident without anyone
|
|
|
|
* noticing).
|
|
|
|
*/
|
|
|
|
|
2017-11-21 17:18:43 +00:00
|
|
|
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_GET_PREV_SESSION,
|
|
|
|
SSL_R_SESSION_ID_CONTEXT_UNINITIALIZED);
|
2015-01-22 03:40:55 +00:00
|
|
|
fatal = 1;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret->timeout < (long)(time(NULL) - ret->time)) { /* timeout */
|
2018-07-29 12:12:53 +00:00
|
|
|
tsan_counter(&s->session_ctx->stats.sess_timeout);
|
2015-01-22 03:40:55 +00:00
|
|
|
if (try_session_cache) {
|
|
|
|
/* session was from the cache, so remove it */
|
|
|
|
SSL_CTX_remove_session(s->session_ctx, ret);
|
|
|
|
}
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2015-12-04 19:48:15 +00:00
|
|
|
/* Check extended master secret extension consistency */
|
|
|
|
if (ret->flags & SSL_SESS_FLAG_EXTMS) {
|
|
|
|
/* If old session includes extms, but new does not: abort handshake */
|
|
|
|
if (!(s->s3->flags & TLS1_FLAGS_RECEIVED_EXTMS)) {
|
2017-11-21 17:18:43 +00:00
|
|
|
SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_F_SSL_GET_PREV_SESSION,
|
|
|
|
SSL_R_INCONSISTENT_EXTMS);
|
2015-12-04 19:48:15 +00:00
|
|
|
fatal = 1;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
} else if (s->s3->flags & TLS1_FLAGS_RECEIVED_EXTMS) {
|
|
|
|
/* If new session includes extms, but old does not: do not resume */
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2017-01-18 16:28:23 +00:00
|
|
|
if (!SSL_IS_TLS13(s)) {
|
|
|
|
/* We already did this for TLS1.3 */
|
|
|
|
SSL_SESSION_free(s->session);
|
|
|
|
s->session = ret;
|
|
|
|
}
|
2015-01-22 03:40:55 +00:00
|
|
|
|
2018-07-29 12:12:53 +00:00
|
|
|
tsan_counter(&s->session_ctx->stats.sess_hit);
|
2015-01-22 03:40:55 +00:00
|
|
|
s->verify_result = s->session->verify_result;
|
|
|
|
return 1;
|
1999-05-23 13:07:03 +00:00
|
|
|
|
|
|
|
err:
|
2015-01-22 03:40:55 +00:00
|
|
|
if (ret != NULL) {
|
|
|
|
SSL_SESSION_free(ret);
|
2017-01-20 16:02:07 +00:00
|
|
|
/* In TLSv1.3 s->session was already set to ret, so we NULL it out */
|
2017-01-19 10:46:53 +00:00
|
|
|
if (SSL_IS_TLS13(s))
|
|
|
|
s->session = NULL;
|
2015-05-15 09:49:56 +00:00
|
|
|
|
2015-01-22 03:40:55 +00:00
|
|
|
if (!try_session_cache) {
|
|
|
|
/*
|
|
|
|
* The session was from a ticket, so we should issue a ticket for
|
|
|
|
* the new session
|
|
|
|
*/
|
2016-12-08 19:18:40 +00:00
|
|
|
s->ext.ticket_expected = 1;
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
|
|
|
}
|
2017-11-21 17:18:43 +00:00
|
|
|
if (fatal)
|
2015-01-22 03:40:55 +00:00
|
|
|
return -1;
|
2017-01-27 15:17:51 +00:00
|
|
|
|
|
|
|
return 0;
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
1998-12-21 10:52:47 +00:00
|
|
|
|
1999-04-19 21:31:43 +00:00
|
|
|
int SSL_CTX_add_session(SSL_CTX *ctx, SSL_SESSION *c)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
2018-07-29 12:12:53 +00:00
|
|
|
int ret = 0;
|
2015-01-22 03:40:55 +00:00
|
|
|
SSL_SESSION *s;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* add just 1 reference count for the SSL_CTX's session cache even though
|
|
|
|
* it has two ways of access: each session is in a doubly linked list and
|
|
|
|
* an lhash
|
|
|
|
*/
|
2016-02-29 17:26:07 +00:00
|
|
|
SSL_SESSION_up_ref(c);
|
2015-01-22 03:40:55 +00:00
|
|
|
/*
|
|
|
|
* if session c is in already in cache, we take back the increment later
|
|
|
|
*/
|
|
|
|
|
2016-02-29 17:26:07 +00:00
|
|
|
CRYPTO_THREAD_write_lock(ctx->lock);
|
2015-01-22 03:40:55 +00:00
|
|
|
s = lh_SSL_SESSION_insert(ctx->sessions, c);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* s != NULL iff we already had a session with the given PID. In this
|
|
|
|
* case, s == c should hold (then we did not really modify
|
|
|
|
* ctx->sessions), or we're in trouble.
|
|
|
|
*/
|
|
|
|
if (s != NULL && s != c) {
|
|
|
|
/* We *are* in trouble ... */
|
|
|
|
SSL_SESSION_list_remove(ctx, s);
|
|
|
|
SSL_SESSION_free(s);
|
|
|
|
/*
|
|
|
|
* ... so pretend the other session did not exist in cache (we cannot
|
|
|
|
* handle two SSL_SESSION structures with identical session ID in the
|
|
|
|
* same cache, which could happen e.g. when two threads concurrently
|
|
|
|
* obtain the same session from an external cache)
|
|
|
|
*/
|
|
|
|
s = NULL;
|
2016-12-22 19:17:29 +00:00
|
|
|
} else if (s == NULL &&
|
|
|
|
lh_SSL_SESSION_retrieve(ctx->sessions, c) == NULL) {
|
|
|
|
/* s == NULL can also mean OOM error in lh_SSL_SESSION_insert ... */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ... so take back the extra reference and also don't add
|
|
|
|
* the session to the SSL_SESSION_list at this time
|
|
|
|
*/
|
|
|
|
s = c;
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Put at the head of the queue unless it is already in the cache */
|
|
|
|
if (s == NULL)
|
|
|
|
SSL_SESSION_list_add(ctx, c);
|
|
|
|
|
|
|
|
if (s != NULL) {
|
|
|
|
/*
|
|
|
|
* existing cache entry -- decrement previously incremented reference
|
|
|
|
* count because it already takes into account the cache
|
|
|
|
*/
|
|
|
|
|
|
|
|
SSL_SESSION_free(s); /* s == c */
|
|
|
|
ret = 0;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* new cache entry -- remove old ones if cache has become too large
|
|
|
|
*/
|
|
|
|
|
|
|
|
ret = 1;
|
|
|
|
|
|
|
|
if (SSL_CTX_sess_get_cache_size(ctx) > 0) {
|
2016-08-05 17:03:17 +00:00
|
|
|
while (SSL_CTX_sess_number(ctx) > SSL_CTX_sess_get_cache_size(ctx)) {
|
2015-01-22 03:40:55 +00:00
|
|
|
if (!remove_session_lock(ctx, ctx->session_cache_tail, 0))
|
|
|
|
break;
|
|
|
|
else
|
2018-07-29 12:12:53 +00:00
|
|
|
tsan_counter(&ctx->stats.sess_cache_full);
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-02-29 17:26:07 +00:00
|
|
|
CRYPTO_THREAD_unlock(ctx->lock);
|
|
|
|
return ret;
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
1998-12-21 10:52:47 +00:00
|
|
|
|
1999-04-19 21:31:43 +00:00
|
|
|
int SSL_CTX_remove_session(SSL_CTX *ctx, SSL_SESSION *c)
|
1999-04-29 22:25:52 +00:00
|
|
|
{
|
2015-01-22 03:40:55 +00:00
|
|
|
return remove_session_lock(ctx, c, 1);
|
1999-04-29 22:25:52 +00:00
|
|
|
}
|
|
|
|
|
1999-05-01 00:18:54 +00:00
|
|
|
static int remove_session_lock(SSL_CTX *ctx, SSL_SESSION *c, int lck)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
|
|
|
SSL_SESSION *r;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if ((c != NULL) && (c->session_id_length != 0)) {
|
|
|
|
if (lck)
|
2016-02-29 17:26:07 +00:00
|
|
|
CRYPTO_THREAD_write_lock(ctx->lock);
|
2018-03-16 09:25:34 +00:00
|
|
|
if ((r = lh_SSL_SESSION_retrieve(ctx->sessions, c)) != NULL) {
|
2015-01-22 03:40:55 +00:00
|
|
|
ret = 1;
|
2018-03-16 09:25:34 +00:00
|
|
|
r = lh_SSL_SESSION_delete(ctx->sessions, r);
|
|
|
|
SSL_SESSION_list_remove(ctx, r);
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
2016-06-13 10:24:15 +00:00
|
|
|
c->not_resumable = 1;
|
2015-01-22 03:40:55 +00:00
|
|
|
|
|
|
|
if (lck)
|
2016-02-29 17:26:07 +00:00
|
|
|
CRYPTO_THREAD_unlock(ctx->lock);
|
2015-01-22 03:40:55 +00:00
|
|
|
|
2016-06-13 10:24:15 +00:00
|
|
|
if (ctx->remove_session_cb != NULL)
|
|
|
|
ctx->remove_session_cb(ctx, c);
|
Do not free a session before calling the remove_session_cb
If the remove_session_cb accesses the session's data (for instance,
via SSL_SESSION_get_protocol_version), a potential use after free
can occur. For this, consider the following scenario when adding
a new session via SSL_CTX_add_session:
- The session cache is full
(SSL_CTX_sess_number(ctx) > SSL_CTX_sess_get_cache_size(ctx))
- Only the session cache has a reference to ctx->session_cache_tail
(that is, ctx->session_cache_tail->references == 1)
Since the cache is full, remove_session_lock is called to remove
ctx->session_cache_tail from the cache. That is, it
SSL_SESSION_free()s the session, which free()s the data. Afterwards,
the free()d session is passed to the remove_session_cb. If the callback
accesses the session's data, we have a use after free.
The free before calling the callback behavior was introduced in
commit e4612d02c53cccd24fa97b08fc01250d1238cca1 ("Remove sessions
from external cache, even if internal cache not used.").
CLA: trivial
Reviewed-by: Rich Salz <rsalz@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/6222)
2018-05-11 10:24:56 +00:00
|
|
|
|
|
|
|
if (ret)
|
|
|
|
SSL_SESSION_free(r);
|
2015-01-22 03:40:55 +00:00
|
|
|
} else
|
|
|
|
ret = 0;
|
2017-10-17 14:04:09 +00:00
|
|
|
return ret;
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
1998-12-21 10:52:47 +00:00
|
|
|
|
1999-04-19 21:31:43 +00:00
|
|
|
void SSL_SESSION_free(SSL_SESSION *ss)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
|
|
|
int i;
|
1998-12-21 10:52:47 +00:00
|
|
|
|
2018-03-27 20:25:08 +00:00
|
|
|
if (ss == NULL)
|
|
|
|
return;
|
2016-08-27 14:01:08 +00:00
|
|
|
CRYPTO_DOWN_REF(&ss->references, &i, ss->lock);
|
2016-01-30 17:04:25 +00:00
|
|
|
REF_PRINT_COUNT("SSL_SESSION", ss);
|
2015-01-22 03:40:55 +00:00
|
|
|
if (i > 0)
|
|
|
|
return;
|
2016-01-30 17:04:25 +00:00
|
|
|
REF_ASSERT_ISNT(i < 0);
|
1998-12-21 10:52:47 +00:00
|
|
|
|
2015-01-22 03:40:55 +00:00
|
|
|
CRYPTO_free_ex_data(CRYPTO_EX_INDEX_SSL_SESSION, ss, &ss->ex_data);
|
1998-12-21 10:56:39 +00:00
|
|
|
|
2017-12-07 18:39:34 +00:00
|
|
|
OPENSSL_cleanse(ss->master_key, sizeof(ss->master_key));
|
|
|
|
OPENSSL_cleanse(ss->session_id, sizeof(ss->session_id));
|
2015-04-30 21:33:59 +00:00
|
|
|
X509_free(ss->peer);
|
2015-06-21 18:34:33 +00:00
|
|
|
sk_X509_pop_free(ss->peer_chain, X509_free);
|
2015-05-01 18:37:16 +00:00
|
|
|
sk_SSL_CIPHER_free(ss->ciphers);
|
2016-12-08 19:18:40 +00:00
|
|
|
OPENSSL_free(ss->ext.hostname);
|
|
|
|
OPENSSL_free(ss->ext.tick);
|
2015-05-15 09:49:56 +00:00
|
|
|
#ifndef OPENSSL_NO_EC
|
2016-12-08 19:18:40 +00:00
|
|
|
OPENSSL_free(ss->ext.ecpointformats);
|
|
|
|
ss->ext.ecpointformats = NULL;
|
|
|
|
ss->ext.ecpointformats_len = 0;
|
|
|
|
OPENSSL_free(ss->ext.supportedgroups);
|
|
|
|
ss->ext.supportedgroups = NULL;
|
|
|
|
ss->ext.supportedgroups_len = 0;
|
2016-08-05 17:03:17 +00:00
|
|
|
#endif /* OPENSSL_NO_EC */
|
2006-03-10 23:06:27 +00:00
|
|
|
#ifndef OPENSSL_NO_PSK
|
2015-05-01 18:37:16 +00:00
|
|
|
OPENSSL_free(ss->psk_identity_hint);
|
|
|
|
OPENSSL_free(ss->psk_identity);
|
2011-03-12 17:01:19 +00:00
|
|
|
#endif
|
|
|
|
#ifndef OPENSSL_NO_SRP
|
2015-05-01 18:37:16 +00:00
|
|
|
OPENSSL_free(ss->srp_username);
|
2006-01-02 23:14:37 +00:00
|
|
|
#endif
|
2017-02-24 12:45:37 +00:00
|
|
|
OPENSSL_free(ss->ext.alpn_selected);
|
2017-03-15 17:25:55 +00:00
|
|
|
OPENSSL_free(ss->ticket_appdata);
|
2016-02-29 17:26:07 +00:00
|
|
|
CRYPTO_THREAD_lock_free(ss->lock);
|
2015-04-30 21:57:32 +00:00
|
|
|
OPENSSL_clear_free(ss, sizeof(*ss));
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
1998-12-21 10:52:47 +00:00
|
|
|
|
2016-02-29 17:26:07 +00:00
|
|
|
int SSL_SESSION_up_ref(SSL_SESSION *ss)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2016-08-27 14:01:08 +00:00
|
|
|
if (CRYPTO_UP_REF(&ss->references, &i, ss->lock) <= 0)
|
2016-02-29 17:26:07 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
REF_PRINT_COUNT("SSL_SESSION", ss);
|
|
|
|
REF_ASSERT_ISNT(i < 2);
|
|
|
|
return ((i > 1) ? 1 : 0);
|
|
|
|
}
|
|
|
|
|
1999-04-19 21:31:43 +00:00
|
|
|
int SSL_set_session(SSL *s, SSL_SESSION *session)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
2016-06-09 12:24:54 +00:00
|
|
|
ssl_clear_bad_session(s);
|
|
|
|
if (s->ctx->method != s->method) {
|
|
|
|
if (!SSL_set_ssl_method(s, s->ctx->method))
|
|
|
|
return 0;
|
|
|
|
}
|
2015-01-22 03:40:55 +00:00
|
|
|
|
2016-06-09 12:24:54 +00:00
|
|
|
if (session != NULL) {
|
2016-02-29 17:26:07 +00:00
|
|
|
SSL_SESSION_up_ref(session);
|
2016-06-09 12:24:54 +00:00
|
|
|
s->verify_result = session->verify_result;
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
2016-06-09 12:24:54 +00:00
|
|
|
SSL_SESSION_free(s->session);
|
|
|
|
s->session = session;
|
|
|
|
|
|
|
|
return 1;
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
1998-12-21 10:52:47 +00:00
|
|
|
|
2016-08-06 10:54:29 +00:00
|
|
|
int SSL_SESSION_set1_id(SSL_SESSION *s, const unsigned char *sid,
|
|
|
|
unsigned int sid_len)
|
|
|
|
{
|
|
|
|
if (sid_len > SSL_MAX_SSL_SESSION_ID_LENGTH) {
|
|
|
|
SSLerr(SSL_F_SSL_SESSION_SET1_ID,
|
|
|
|
SSL_R_SSL_SESSION_ID_TOO_LONG);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
s->session_id_length = sid_len;
|
2017-02-26 23:47:40 +00:00
|
|
|
if (sid != s->session_id)
|
|
|
|
memcpy(s->session_id, sid, sid_len);
|
2016-08-06 10:54:29 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
1999-04-19 21:31:43 +00:00
|
|
|
long SSL_SESSION_set_timeout(SSL_SESSION *s, long t)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
|
|
|
if (s == NULL)
|
2017-10-17 14:04:09 +00:00
|
|
|
return 0;
|
2015-01-22 03:40:55 +00:00
|
|
|
s->timeout = t;
|
2017-10-09 11:05:58 +00:00
|
|
|
return 1;
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
1998-12-21 10:52:47 +00:00
|
|
|
|
2005-03-30 10:26:02 +00:00
|
|
|
long SSL_SESSION_get_timeout(const SSL_SESSION *s)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
|
|
|
if (s == NULL)
|
2017-10-17 14:04:09 +00:00
|
|
|
return 0;
|
|
|
|
return s->timeout;
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
1998-12-21 10:52:47 +00:00
|
|
|
|
2005-03-30 10:26:02 +00:00
|
|
|
long SSL_SESSION_get_time(const SSL_SESSION *s)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
|
|
|
if (s == NULL)
|
2017-10-17 14:04:09 +00:00
|
|
|
return 0;
|
|
|
|
return s->time;
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
1998-12-21 10:52:47 +00:00
|
|
|
|
1999-04-19 21:31:43 +00:00
|
|
|
long SSL_SESSION_set_time(SSL_SESSION *s, long t)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
|
|
|
if (s == NULL)
|
2017-10-17 14:04:09 +00:00
|
|
|
return 0;
|
2015-01-22 03:40:55 +00:00
|
|
|
s->time = t;
|
2017-10-17 14:04:09 +00:00
|
|
|
return t;
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
1998-12-21 10:52:47 +00:00
|
|
|
|
2016-05-26 22:40:13 +00:00
|
|
|
int SSL_SESSION_get_protocol_version(const SSL_SESSION *s)
|
|
|
|
{
|
|
|
|
return s->ssl_version;
|
|
|
|
}
|
|
|
|
|
2017-06-12 18:40:11 +00:00
|
|
|
int SSL_SESSION_set_protocol_version(SSL_SESSION *s, int version)
|
|
|
|
{
|
|
|
|
s->ssl_version = version;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-08-12 19:02:00 +00:00
|
|
|
const SSL_CIPHER *SSL_SESSION_get0_cipher(const SSL_SESSION *s)
|
|
|
|
{
|
|
|
|
return s->cipher;
|
|
|
|
}
|
2017-06-12 18:12:04 +00:00
|
|
|
|
|
|
|
int SSL_SESSION_set_cipher(SSL_SESSION *s, const SSL_CIPHER *cipher)
|
|
|
|
{
|
|
|
|
s->cipher = cipher;
|
|
|
|
return 1;
|
|
|
|
}
|
2016-08-12 19:02:00 +00:00
|
|
|
|
2016-04-11 14:08:00 +00:00
|
|
|
const char *SSL_SESSION_get0_hostname(const SSL_SESSION *s)
|
|
|
|
{
|
2016-12-08 19:18:40 +00:00
|
|
|
return s->ext.hostname;
|
2016-04-11 14:08:00 +00:00
|
|
|
}
|
|
|
|
|
2017-08-03 09:13:31 +00:00
|
|
|
int SSL_SESSION_set1_hostname(SSL_SESSION *s, const char *hostname)
|
|
|
|
{
|
|
|
|
OPENSSL_free(s->ext.hostname);
|
|
|
|
if (hostname == NULL) {
|
|
|
|
s->ext.hostname = NULL;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
s->ext.hostname = OPENSSL_strdup(hostname);
|
|
|
|
|
|
|
|
return s->ext.hostname != NULL;
|
|
|
|
}
|
|
|
|
|
2015-02-08 15:43:16 +00:00
|
|
|
int SSL_SESSION_has_ticket(const SSL_SESSION *s)
|
|
|
|
{
|
2016-12-08 19:18:40 +00:00
|
|
|
return (s->ext.ticklen > 0) ? 1 : 0;
|
2015-02-08 15:43:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
unsigned long SSL_SESSION_get_ticket_lifetime_hint(const SSL_SESSION *s)
|
|
|
|
{
|
2016-12-08 19:18:40 +00:00
|
|
|
return s->ext.tick_lifetime_hint;
|
2015-02-08 15:43:16 +00:00
|
|
|
}
|
|
|
|
|
2016-08-13 13:29:41 +00:00
|
|
|
void SSL_SESSION_get0_ticket(const SSL_SESSION *s, const unsigned char **tick,
|
2016-08-05 17:03:17 +00:00
|
|
|
size_t *len)
|
2015-02-08 23:37:54 +00:00
|
|
|
{
|
2016-12-08 19:18:40 +00:00
|
|
|
*len = s->ext.ticklen;
|
2015-04-16 05:50:03 +00:00
|
|
|
if (tick != NULL)
|
2016-12-08 19:18:40 +00:00
|
|
|
*tick = s->ext.tick;
|
2015-02-08 23:37:54 +00:00
|
|
|
}
|
|
|
|
|
2017-02-24 14:08:06 +00:00
|
|
|
uint32_t SSL_SESSION_get_max_early_data(const SSL_SESSION *s)
|
|
|
|
{
|
|
|
|
return s->ext.max_early_data;
|
|
|
|
}
|
|
|
|
|
2017-07-08 10:42:55 +00:00
|
|
|
int SSL_SESSION_set_max_early_data(SSL_SESSION *s, uint32_t max_early_data)
|
|
|
|
{
|
|
|
|
s->ext.max_early_data = max_early_data;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2017-08-03 09:13:31 +00:00
|
|
|
void SSL_SESSION_get0_alpn_selected(const SSL_SESSION *s,
|
|
|
|
const unsigned char **alpn,
|
|
|
|
size_t *len)
|
|
|
|
{
|
|
|
|
*alpn = s->ext.alpn_selected;
|
|
|
|
*len = s->ext.alpn_selected_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SSL_SESSION_set1_alpn_selected(SSL_SESSION *s, const unsigned char *alpn,
|
|
|
|
size_t len)
|
|
|
|
{
|
|
|
|
OPENSSL_free(s->ext.alpn_selected);
|
|
|
|
if (alpn == NULL || len == 0) {
|
|
|
|
s->ext.alpn_selected = NULL;
|
|
|
|
s->ext.alpn_selected_len = 0;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
s->ext.alpn_selected = OPENSSL_memdup(alpn, len);
|
|
|
|
if (s->ext.alpn_selected == NULL) {
|
|
|
|
s->ext.alpn_selected_len = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
s->ext.alpn_selected_len = len;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2011-04-29 22:37:12 +00:00
|
|
|
X509 *SSL_SESSION_get0_peer(SSL_SESSION *s)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
|
|
|
return s->peer;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SSL_SESSION_set1_id_context(SSL_SESSION *s, const unsigned char *sid_ctx,
|
|
|
|
unsigned int sid_ctx_len)
|
|
|
|
{
|
|
|
|
if (sid_ctx_len > SSL_MAX_SID_CTX_LENGTH) {
|
|
|
|
SSLerr(SSL_F_SSL_SESSION_SET1_ID_CONTEXT,
|
|
|
|
SSL_R_SSL_SESSION_ID_CONTEXT_TOO_LONG);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
s->sid_ctx_length = sid_ctx_len;
|
2017-02-26 23:47:40 +00:00
|
|
|
if (sid_ctx != s->sid_ctx)
|
|
|
|
memcpy(s->sid_ctx, sid_ctx, sid_ctx_len);
|
2015-01-22 03:40:55 +00:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
2011-04-29 22:37:12 +00:00
|
|
|
|
2017-03-21 13:50:31 +00:00
|
|
|
int SSL_SESSION_is_resumable(const SSL_SESSION *s)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* In the case of EAP-FAST, we can have a pre-shared "ticket" without a
|
|
|
|
* session ID.
|
|
|
|
*/
|
|
|
|
return !s->not_resumable
|
|
|
|
&& (s->session_id_length > 0 || s->ext.ticklen > 0);
|
|
|
|
}
|
|
|
|
|
1999-04-19 21:31:43 +00:00
|
|
|
long SSL_CTX_set_timeout(SSL_CTX *s, long t)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
|
|
|
long l;
|
|
|
|
if (s == NULL)
|
2017-10-17 14:04:09 +00:00
|
|
|
return 0;
|
2015-01-22 03:40:55 +00:00
|
|
|
l = s->session_timeout;
|
|
|
|
s->session_timeout = t;
|
2017-10-17 14:04:09 +00:00
|
|
|
return l;
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
Updates to the new SSL compression code
[Eric A. Young, (from changes to C2Net SSLeay, integrated by Mark Cox)]
Fix so that the version number in the master secret, when passed
via RSA, checks that if TLS was proposed, but we roll back to SSLv3
(because the server will not accept higher), that the version number
is 0x03,0x01, not 0x03,0x00
[Eric A. Young, (from changes to C2Net SSLeay, integrated by Mark Cox)]
Submitted by:
Reviewed by:
PR:
1999-02-16 09:22:21 +00:00
|
|
|
|
2005-03-30 10:26:02 +00:00
|
|
|
long SSL_CTX_get_timeout(const SSL_CTX *s)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
|
|
|
if (s == NULL)
|
2017-10-17 14:04:09 +00:00
|
|
|
return 0;
|
|
|
|
return s->session_timeout;
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
Updates to the new SSL compression code
[Eric A. Young, (from changes to C2Net SSLeay, integrated by Mark Cox)]
Fix so that the version number in the master secret, when passed
via RSA, checks that if TLS was proposed, but we roll back to SSLv3
(because the server will not accept higher), that the version number
is 0x03,0x01, not 0x03,0x00
[Eric A. Young, (from changes to C2Net SSLeay, integrated by Mark Cox)]
Submitted by:
Reviewed by:
PR:
1999-02-16 09:22:21 +00:00
|
|
|
|
2015-01-22 03:40:55 +00:00
|
|
|
int SSL_set_session_secret_cb(SSL *s,
|
2016-12-08 19:18:40 +00:00
|
|
|
tls_session_secret_cb_fn tls_session_secret_cb,
|
2015-01-22 03:40:55 +00:00
|
|
|
void *arg)
|
|
|
|
{
|
|
|
|
if (s == NULL)
|
2017-10-17 14:04:09 +00:00
|
|
|
return 0;
|
2016-12-08 19:18:40 +00:00
|
|
|
s->ext.session_secret_cb = tls_session_secret_cb;
|
|
|
|
s->ext.session_secret_cb_arg = arg;
|
2017-10-09 11:05:58 +00:00
|
|
|
return 1;
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
2008-11-15 17:18:12 +00:00
|
|
|
|
|
|
|
int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb,
|
2015-01-22 03:40:55 +00:00
|
|
|
void *arg)
|
|
|
|
{
|
|
|
|
if (s == NULL)
|
2017-10-17 14:04:09 +00:00
|
|
|
return 0;
|
2016-12-08 19:18:40 +00:00
|
|
|
s->ext.session_ticket_cb = cb;
|
|
|
|
s->ext.session_ticket_cb_arg = arg;
|
2017-10-09 11:05:58 +00:00
|
|
|
return 1;
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
2008-11-15 17:18:12 +00:00
|
|
|
|
|
|
|
int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
|
|
|
if (s->version >= TLS1_VERSION) {
|
2016-12-08 19:18:40 +00:00
|
|
|
OPENSSL_free(s->ext.session_ticket);
|
|
|
|
s->ext.session_ticket = NULL;
|
|
|
|
s->ext.session_ticket =
|
2015-01-22 03:40:55 +00:00
|
|
|
OPENSSL_malloc(sizeof(TLS_SESSION_TICKET_EXT) + ext_len);
|
2016-12-08 19:18:40 +00:00
|
|
|
if (s->ext.session_ticket == NULL) {
|
2015-01-22 03:40:55 +00:00
|
|
|
SSLerr(SSL_F_SSL_SET_SESSION_TICKET_EXT, ERR_R_MALLOC_FAILURE);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-01-09 17:42:15 +00:00
|
|
|
if (ext_data != NULL) {
|
2016-12-08 19:18:40 +00:00
|
|
|
s->ext.session_ticket->length = ext_len;
|
|
|
|
s->ext.session_ticket->data = s->ext.session_ticket + 1;
|
|
|
|
memcpy(s->ext.session_ticket->data, ext_data, ext_len);
|
2015-01-22 03:40:55 +00:00
|
|
|
} else {
|
2016-12-08 19:18:40 +00:00
|
|
|
s->ext.session_ticket->length = 0;
|
|
|
|
s->ext.session_ticket->data = NULL;
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct timeout_param_st {
|
|
|
|
SSL_CTX *ctx;
|
|
|
|
long time;
|
|
|
|
LHASH_OF(SSL_SESSION) *cache;
|
|
|
|
} TIMEOUT_PARAM;
|
1998-12-21 10:52:47 +00:00
|
|
|
|
2015-12-24 16:20:54 +00:00
|
|
|
static void timeout_cb(SSL_SESSION *s, TIMEOUT_PARAM *p)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
|
|
|
if ((p->time == 0) || (p->time > (s->time + s->timeout))) { /* timeout */
|
|
|
|
/*
|
|
|
|
* The reason we don't call SSL_CTX_remove_session() is to save on
|
|
|
|
* locking overhead
|
|
|
|
*/
|
|
|
|
(void)lh_SSL_SESSION_delete(p->cache, s);
|
|
|
|
SSL_SESSION_list_remove(p->ctx, s);
|
|
|
|
s->not_resumable = 1;
|
|
|
|
if (p->ctx->remove_session_cb != NULL)
|
|
|
|
p->ctx->remove_session_cb(p->ctx, s);
|
|
|
|
SSL_SESSION_free(s);
|
|
|
|
}
|
|
|
|
}
|
1998-12-21 10:52:47 +00:00
|
|
|
|
2015-12-24 16:20:54 +00:00
|
|
|
IMPLEMENT_LHASH_DOALL_ARG(SSL_SESSION, TIMEOUT_PARAM);
|
2001-01-09 00:24:38 +00:00
|
|
|
|
1999-04-19 21:31:43 +00:00
|
|
|
void SSL_CTX_flush_sessions(SSL_CTX *s, long t)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
|
|
|
unsigned long i;
|
|
|
|
TIMEOUT_PARAM tp;
|
|
|
|
|
|
|
|
tp.ctx = s;
|
|
|
|
tp.cache = s->sessions;
|
|
|
|
if (tp.cache == NULL)
|
|
|
|
return;
|
|
|
|
tp.time = t;
|
2016-02-29 17:26:07 +00:00
|
|
|
CRYPTO_THREAD_write_lock(s->lock);
|
2016-05-20 14:46:29 +00:00
|
|
|
i = lh_SSL_SESSION_get_down_load(s->sessions);
|
|
|
|
lh_SSL_SESSION_set_down_load(s->sessions, 0);
|
2015-12-24 16:20:54 +00:00
|
|
|
lh_SSL_SESSION_doall_TIMEOUT_PARAM(tp.cache, timeout_cb, &tp);
|
2016-05-20 14:46:29 +00:00
|
|
|
lh_SSL_SESSION_set_down_load(s->sessions, i);
|
2016-02-29 17:26:07 +00:00
|
|
|
CRYPTO_THREAD_unlock(s->lock);
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
1998-12-21 10:52:47 +00:00
|
|
|
|
1999-04-19 21:31:43 +00:00
|
|
|
int ssl_clear_bad_session(SSL *s)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
|
|
|
if ((s->session != NULL) &&
|
|
|
|
!(s->shutdown & SSL_SENT_SHUTDOWN) &&
|
|
|
|
!(SSL_in_init(s) || SSL_in_before(s))) {
|
2016-05-26 17:49:36 +00:00
|
|
|
SSL_CTX_remove_session(s->session_ctx, s->session);
|
2017-10-09 11:05:58 +00:00
|
|
|
return 1;
|
2015-01-22 03:40:55 +00:00
|
|
|
} else
|
2017-10-17 14:04:09 +00:00
|
|
|
return 0;
|
2015-01-22 03:40:55 +00:00
|
|
|
}
|
1998-12-21 10:56:39 +00:00
|
|
|
|
|
|
|
/* locked by SSL_CTX in the calling function */
|
1999-04-19 21:31:43 +00:00
|
|
|
static void SSL_SESSION_list_remove(SSL_CTX *ctx, SSL_SESSION *s)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
|
|
|
if ((s->next == NULL) || (s->prev == NULL))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (s->next == (SSL_SESSION *)&(ctx->session_cache_tail)) {
|
|
|
|
/* last element in list */
|
|
|
|
if (s->prev == (SSL_SESSION *)&(ctx->session_cache_head)) {
|
|
|
|
/* only one element in list */
|
|
|
|
ctx->session_cache_head = NULL;
|
|
|
|
ctx->session_cache_tail = NULL;
|
|
|
|
} else {
|
|
|
|
ctx->session_cache_tail = s->prev;
|
|
|
|
s->prev->next = (SSL_SESSION *)&(ctx->session_cache_tail);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (s->prev == (SSL_SESSION *)&(ctx->session_cache_head)) {
|
|
|
|
/* first element in list */
|
|
|
|
ctx->session_cache_head = s->next;
|
|
|
|
s->next->prev = (SSL_SESSION *)&(ctx->session_cache_head);
|
|
|
|
} else {
|
|
|
|
/* middle of list */
|
|
|
|
s->next->prev = s->prev;
|
|
|
|
s->prev->next = s->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s->prev = s->next = NULL;
|
|
|
|
}
|
1998-12-21 10:56:39 +00:00
|
|
|
|
1999-04-19 21:31:43 +00:00
|
|
|
static void SSL_SESSION_list_add(SSL_CTX *ctx, SSL_SESSION *s)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
|
|
|
if ((s->next != NULL) && (s->prev != NULL))
|
|
|
|
SSL_SESSION_list_remove(ctx, s);
|
|
|
|
|
|
|
|
if (ctx->session_cache_head == NULL) {
|
|
|
|
ctx->session_cache_head = s;
|
|
|
|
ctx->session_cache_tail = s;
|
|
|
|
s->prev = (SSL_SESSION *)&(ctx->session_cache_head);
|
|
|
|
s->next = (SSL_SESSION *)&(ctx->session_cache_tail);
|
|
|
|
} else {
|
|
|
|
s->next = ctx->session_cache_head;
|
|
|
|
s->next->prev = s;
|
|
|
|
s->prev = (SSL_SESSION *)&(ctx->session_cache_head);
|
|
|
|
ctx->session_cache_head = s;
|
|
|
|
}
|
|
|
|
}
|
1998-12-21 10:56:39 +00:00
|
|
|
|
2006-11-29 20:54:57 +00:00
|
|
|
void SSL_CTX_sess_set_new_cb(SSL_CTX *ctx,
|
2016-08-05 17:03:17 +00:00
|
|
|
int (*cb) (struct ssl_st *ssl, SSL_SESSION *sess))
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
|
|
|
ctx->new_session_cb = cb;
|
|
|
|
}
|
2006-11-29 20:54:57 +00:00
|
|
|
|
2015-01-22 03:40:55 +00:00
|
|
|
int (*SSL_CTX_sess_get_new_cb(SSL_CTX *ctx)) (SSL *ssl, SSL_SESSION *sess) {
|
|
|
|
return ctx->new_session_cb;
|
|
|
|
}
|
2006-11-29 20:54:57 +00:00
|
|
|
|
|
|
|
void SSL_CTX_sess_set_remove_cb(SSL_CTX *ctx,
|
2015-01-22 03:40:55 +00:00
|
|
|
void (*cb) (SSL_CTX *ctx, SSL_SESSION *sess))
|
|
|
|
{
|
|
|
|
ctx->remove_session_cb = cb;
|
|
|
|
}
|
2006-11-29 20:54:57 +00:00
|
|
|
|
2015-01-22 03:40:55 +00:00
|
|
|
void (*SSL_CTX_sess_get_remove_cb(SSL_CTX *ctx)) (SSL_CTX *ctx,
|
|
|
|
SSL_SESSION *sess) {
|
|
|
|
return ctx->remove_session_cb;
|
|
|
|
}
|
2006-11-29 20:54:57 +00:00
|
|
|
|
|
|
|
void SSL_CTX_sess_set_get_cb(SSL_CTX *ctx,
|
2015-01-22 03:40:55 +00:00
|
|
|
SSL_SESSION *(*cb) (struct ssl_st *ssl,
|
2016-02-01 14:26:18 +00:00
|
|
|
const unsigned char *data,
|
|
|
|
int len, int *copy))
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
|
|
|
ctx->get_session_cb = cb;
|
|
|
|
}
|
|
|
|
|
|
|
|
SSL_SESSION *(*SSL_CTX_sess_get_get_cb(SSL_CTX *ctx)) (SSL *ssl,
|
2016-08-05 17:03:17 +00:00
|
|
|
const unsigned char
|
|
|
|
*data, int len,
|
|
|
|
int *copy) {
|
2015-01-22 03:40:55 +00:00
|
|
|
return ctx->get_session_cb;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SSL_CTX_set_info_callback(SSL_CTX *ctx,
|
|
|
|
void (*cb) (const SSL *ssl, int type, int val))
|
|
|
|
{
|
|
|
|
ctx->info_callback = cb;
|
|
|
|
}
|
|
|
|
|
|
|
|
void (*SSL_CTX_get_info_callback(SSL_CTX *ctx)) (const SSL *ssl, int type,
|
|
|
|
int val) {
|
|
|
|
return ctx->info_callback;
|
|
|
|
}
|
2006-11-29 20:54:57 +00:00
|
|
|
|
|
|
|
void SSL_CTX_set_client_cert_cb(SSL_CTX *ctx,
|
2015-01-22 03:40:55 +00:00
|
|
|
int (*cb) (SSL *ssl, X509 **x509,
|
|
|
|
EVP_PKEY **pkey))
|
|
|
|
{
|
|
|
|
ctx->client_cert_cb = cb;
|
|
|
|
}
|
2006-11-29 20:54:57 +00:00
|
|
|
|
2015-01-22 03:40:55 +00:00
|
|
|
int (*SSL_CTX_get_client_cert_cb(SSL_CTX *ctx)) (SSL *ssl, X509 **x509,
|
|
|
|
EVP_PKEY **pkey) {
|
|
|
|
return ctx->client_cert_cb;
|
|
|
|
}
|
2006-11-29 20:54:57 +00:00
|
|
|
|
2008-06-01 22:33:24 +00:00
|
|
|
#ifndef OPENSSL_NO_ENGINE
|
|
|
|
int SSL_CTX_set_client_cert_engine(SSL_CTX *ctx, ENGINE *e)
|
2015-01-22 03:40:55 +00:00
|
|
|
{
|
|
|
|
if (!ENGINE_init(e)) {
|
|
|
|
SSLerr(SSL_F_SSL_CTX_SET_CLIENT_CERT_ENGINE, ERR_R_ENGINE_LIB);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (!ENGINE_get_ssl_client_cert_function(e)) {
|
|
|
|
SSLerr(SSL_F_SSL_CTX_SET_CLIENT_CERT_ENGINE,
|
|
|
|
SSL_R_NO_CLIENT_CERT_METHOD);
|
|
|
|
ENGINE_finish(e);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
ctx->client_cert_engine = e;
|
|
|
|
return 1;
|
|
|
|
}
|
2008-06-01 22:33:24 +00:00
|
|
|
#endif
|
|
|
|
|
2006-11-29 20:54:57 +00:00
|
|
|
void SSL_CTX_set_cookie_generate_cb(SSL_CTX *ctx,
|
2015-01-22 03:40:55 +00:00
|
|
|
int (*cb) (SSL *ssl,
|
|
|
|
unsigned char *cookie,
|
|
|
|
unsigned int *cookie_len))
|
|
|
|
{
|
|
|
|
ctx->app_gen_cookie_cb = cb;
|
|
|
|
}
|
2006-11-29 20:54:57 +00:00
|
|
|
|
|
|
|
void SSL_CTX_set_cookie_verify_cb(SSL_CTX *ctx,
|
2016-08-05 17:03:17 +00:00
|
|
|
int (*cb) (SSL *ssl,
|
|
|
|
const unsigned char *cookie,
|
2015-01-22 03:40:55 +00:00
|
|
|
unsigned int cookie_len))
|
|
|
|
{
|
|
|
|
ctx->app_verify_cookie_cb = cb;
|
|
|
|
}
|
2006-11-29 20:54:57 +00:00
|
|
|
|
2017-03-15 17:25:55 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-02-26 02:39:11 +00:00
|
|
|
void SSL_CTX_set_stateless_cookie_generate_cb(
|
|
|
|
SSL_CTX *ctx,
|
|
|
|
int (*cb) (SSL *ssl,
|
|
|
|
unsigned char *cookie,
|
|
|
|
size_t *cookie_len))
|
|
|
|
{
|
|
|
|
ctx->gen_stateless_cookie_cb = cb;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SSL_CTX_set_stateless_cookie_verify_cb(
|
|
|
|
SSL_CTX *ctx,
|
|
|
|
int (*cb) (SSL *ssl,
|
|
|
|
const unsigned char *cookie,
|
|
|
|
size_t cookie_len))
|
|
|
|
{
|
|
|
|
ctx->verify_stateless_cookie_cb = cb;
|
|
|
|
}
|
|
|
|
|
2016-08-05 17:03:17 +00:00
|
|
|
IMPLEMENT_PEM_rw(SSL_SESSION, SSL_SESSION, PEM_STRING_SSL_SESSION, SSL_SESSION)
|