/*
- * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 1995-2017 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2005 Nokia. 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
* https://www.openssl.org/source/license.html
*/
-/* ====================================================================
- * Copyright 2005 Nokia. All rights reserved.
- *
- * The portions of the attached software ("Contribution") is developed by
- * Nokia Corporation and is licensed pursuant to the OpenSSL open source
- * license.
- *
- * The Contribution, originally written by Mika Kousa and Pasi Eronen of
- * Nokia Corporation, consists of the "PSK" (Pre-Shared Key) ciphersuites
- * support (see RFC 4279) to OpenSSL.
- *
- * No patent licenses or other rights except those expressly stated in
- * the OpenSSL open source license shall be deemed granted or received
- * expressly, by implication, estoppel, or otherwise.
- *
- * No assurances are provided by Nokia that the Contribution does not
- * infringe the patent or other intellectual property rights of any third
- * party or that the license provides you with all the necessary rights
- * to make use of the Contribution.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. IN
- * ADDITION TO THE DISCLAIMERS INCLUDED IN THE LICENSE, NOKIA
- * SPECIFICALLY DISCLAIMS ANY LIABILITY FOR CLAIMS BROUGHT BY YOU OR ANY
- * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR
- * OTHERWISE.
- */
-
#include <stdio.h>
#include <openssl/lhash.h>
#include <openssl/rand.h>
#include <openssl/engine.h>
+#include "internal/refcount.h"
#include "ssl_locl.h"
#include "statem/statem_locl.h"
static int remove_session_lock(SSL_CTX *ctx, SSL_SESSION *c, int lck);
/*
- * TODO(TLS1.3): 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. We need to work out how to deal
- * with this.
+ * 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().
*/
SSL_SESSION *SSL_get_session(const SSL *ssl)
{
SSL_SESSION *ss;
+ if (!OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL))
+ return NULL;
+
ss = OPENSSL_zalloc(sizeof(*ss));
if (ss == NULL) {
SSLerr(SSL_F_SSL_SESSION_NEW, ERR_R_MALLOC_FAILURE);
return ss;
}
+SSL_SESSION *SSL_SESSION_dup(SSL_SESSION *src)
+{
+ return ssl_session_dup(src, 1);
+}
+
/*
* 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.
dest->ext.supportedgroups = NULL;
#endif
dest->ext.tick = NULL;
+ dest->ext.alpn_selected = NULL;
#ifndef OPENSSL_NO_SRP
dest->srp_username = NULL;
#endif
+ dest->peer_chain = NULL;
+ dest->peer = NULL;
+ dest->ext.tick_nonce = NULL;
memset(&dest->ex_data, 0, sizeof(dest->ex_data));
/* We deliberately don't copy the prev and next pointers */
if (dest->lock == NULL)
goto err;
- if (src->peer != NULL)
- X509_up_ref(src->peer);
+ 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;
+ }
if (src->peer_chain != NULL) {
dest->peer_chain = X509_chain_up_ref(src->peer_chain);
}
#endif
- if (ticket != 0) {
+ if (ticket != 0 && src->ext.tick != NULL) {
dest->ext.tick =
OPENSSL_memdup(src->ext.tick, src->ext.ticklen);
if (dest->ext.tick == NULL)
dest->ext.ticklen = 0;
}
+ if (src->ext.alpn_selected) {
+ dest->ext.alpn_selected =
+ (unsigned char*)OPENSSL_strndup((char*)src->ext.alpn_selected,
+ src->ext.alpn_selected_len);
+ if (dest->ext.alpn_selected == NULL) {
+ goto err;
+ }
+ }
+
+ if (src->ext.tick_nonce != NULL) {
+ dest->ext.tick_nonce = OPENSSL_memdup(src->ext.tick_nonce,
+ src->ext.tick_nonce_len);
+ if (dest->ext.tick_nonce == NULL)
+ goto err;
+ }
+
#ifndef OPENSSL_NO_SRP
if (src->srp_username) {
dest->srp_username = OPENSSL_strdup(src->srp_username);
*/
#define MAX_SESS_ID_ATTEMPTS 10
-static int def_generate_session_id(const SSL *ssl, unsigned char *id,
+static int def_generate_session_id(SSL *ssl, unsigned char *id,
unsigned int *id_len)
{
unsigned int retry = 0;
do
- if (RAND_bytes(id, *id_len) <= 0)
+ if (ssl_randbytes(ssl, id, *id_len) <= 0)
return 0;
while (SSL_has_matching_session_id(ssl, id, *id_len) &&
(++retry < MAX_SESS_ID_ATTEMPTS)) ;
* - Both for new and resumed sessions, s->ext.ticket_expected is set to 1
* if the server should issue a new session ticket (to 0 otherwise).
*/
-int ssl_get_prev_session(SSL *s, CLIENTHELLO_MSG *hello)
+int ssl_get_prev_session(SSL *s, CLIENTHELLO_MSG *hello, int *al)
{
/* This is used only by servers. */
SSL_SESSION *ret = NULL;
int fatal = 0;
int try_session_cache = 0;
- int r;
+ TICKET_RETURN r;
if (SSL_IS_TLS13(s)) {
- int al;
-
- if (!tls_parse_extension(s, TLSEXT_IDX_psk, EXT_CLIENT_HELLO,
- hello->pre_proc_exts, NULL, 0, &al))
+ if (!tls_parse_extension(s, TLSEXT_IDX_psk_kex_modes,
+ SSL_EXT_CLIENT_HELLO, hello->pre_proc_exts,
+ NULL, 0, al)
+ || !tls_parse_extension(s, TLSEXT_IDX_psk, SSL_EXT_CLIENT_HELLO,
+ hello->pre_proc_exts, NULL, 0, al))
return -1;
ret = s->session;
/* sets s->ext.ticket_expected */
r = tls_get_ticket_from_client(s, hello, &ret);
switch (r) {
- case -1: /* Error during processing */
+ case TICKET_FATAL_ERR_MALLOC:
+ case TICKET_FATAL_ERR_OTHER:
fatal = 1;
goto err;
- case 0: /* No ticket found */
- case 1: /* Zero length ticket found */
- try_session_cache = 1;
- break; /* Ok to carry on processing session id. */
- case 2: /* Ticket found but not decrypted. */
- case 3: /* Ticket decrypted, *ret has been set. */
+ case TICKET_NONE:
+ case TICKET_EMPTY:
+ if (hello->session_id_len > 0)
+ try_session_cache = 1;
+ break;
+ case TICKET_NO_DECRYPT:
+ case TICKET_SUCCESS:
+ case TICKET_SUCCESS_RENEW:
break;
- default:
- abort();
}
}
SSL_SESSION data;
data.ssl_version = s->version;
- memset(data.session_id, 0, sizeof(data.session_id));
memcpy(data.session_id, hello->session_id, hello->session_id_len);
data.session_id_length = hello->session_id_len;
(s->session_ctx->session_cache_mode &
SSL_SESS_CACHE_NO_INTERNAL_STORE)) {
/*
- * The following should not return 1, otherwise, things are
- * very strange
+ * Either return value of SSL_CTX_add_session should not
+ * interrupt the session resumption process. The return
+ * value is intentionally ignored.
*/
- if (SSL_CTX_add_session(s->session_ctx, ret))
- goto err;
+ SSL_CTX_add_session(s->session_ctx, ret);
}
}
}
goto err;
}
- if (ret->cipher == NULL) {
- unsigned char buf[5], *p;
- unsigned long l;
-
- p = buf;
- l = ret->cipher_id;
- l2n(l, p);
- if ((ret->ssl_version >> 8) >= SSL3_VERSION_MAJOR)
- ret->cipher = ssl_get_cipher_by_char(s, &(buf[2]));
- else
- ret->cipher = ssl_get_cipher_by_char(s, &(buf[1]));
- if (ret->cipher == NULL)
- goto err;
- }
-
if (ret->timeout < (long)(time(NULL) - ret->time)) { /* timeout */
s->session_ctx->stats.sess_timeout++;
if (try_session_cache) {
/* If old session includes extms, but new does not: abort handshake */
if (!(s->s3->flags & TLS1_FLAGS_RECEIVED_EXTMS)) {
SSLerr(SSL_F_SSL_GET_PREV_SESSION, SSL_R_INCONSISTENT_EXTMS);
- ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+ ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
fatal = 1;
goto err;
}
s->session_ctx->stats.sess_hit++;
s->verify_result = s->session->verify_result;
-
return 1;
err:
if (ret != NULL) {
SSL_SESSION_free(ret);
- /* In TLSv1.3 we already set s->session, so better NULL it out */
+ /* In TLSv1.3 s->session was already set to ret, so we NULL it out */
if (SSL_IS_TLS13(s))
s->session = NULL;
s->ext.ticket_expected = 1;
}
}
- if (fatal)
+ if (fatal) {
+ *al = SSL_AD_INTERNAL_ERROR;
return -1;
- else
- return 0;
+ }
+
+ return 0;
}
int SSL_CTX_add_session(SSL_CTX *ctx, SSL_SESSION *c)
#ifndef OPENSSL_NO_SRP
OPENSSL_free(ss->srp_username);
#endif
+ OPENSSL_free(ss->ext.alpn_selected);
+ OPENSSL_free(ss->ext.tick_nonce);
CRYPTO_THREAD_lock_free(ss->lock);
OPENSSL_clear_free(ss, sizeof(*ss));
}
return 0;
}
s->session_id_length = sid_len;
- memcpy(s->session_id, sid, sid_len);
+ if (sid != s->session_id)
+ memcpy(s->session_id, sid, sid_len);
return 1;
}
return s->ssl_version;
}
+int SSL_SESSION_set_protocol_version(SSL_SESSION *s, int version)
+{
+ s->ssl_version = version;
+ return 1;
+}
+
const SSL_CIPHER *SSL_SESSION_get0_cipher(const SSL_SESSION *s)
{
return s->cipher;
}
+int SSL_SESSION_set_cipher(SSL_SESSION *s, const SSL_CIPHER *cipher)
+{
+ s->cipher = cipher;
+ return 1;
+}
+
const char *SSL_SESSION_get0_hostname(const SSL_SESSION *s)
{
return s->ext.hostname;
}
+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;
+}
+
int SSL_SESSION_has_ticket(const SSL_SESSION *s)
{
return (s->ext.ticklen > 0) ? 1 : 0;
*tick = s->ext.tick;
}
+uint32_t SSL_SESSION_get_max_early_data(const SSL_SESSION *s)
+{
+ return s->ext.max_early_data;
+}
+
+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;
+}
+
+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;
+}
+
X509 *SSL_SESSION_get0_peer(SSL_SESSION *s)
{
return s->peer;
return 0;
}
s->sid_ctx_length = sid_ctx_len;
- memcpy(s->sid_ctx, sid_ctx, sid_ctx_len);
+ if (sid_ctx != s->sid_ctx)
+ memcpy(s->sid_ctx, sid_ctx, sid_ctx_len);
return 1;
}
+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);
+}
+
long SSL_CTX_set_timeout(SSL_CTX *s, long t)
{
long l;