From 246b86ee329c70cbf8c822e852cc31e1076d53fd Mon Sep 17 00:00:00 2001
From: Laszlo Kovacs <lkovacs@akamai.com>
Date: Thu, 26 Mar 2015 14:39:26 -0400
Subject: [PATCH 14/26] RT3869 Add shared session lists in SSL_CTX

Support for shared session lists via SSL_CTX_share_session_cache().
Added locking during the sharing and cleanup.

(cherry picked from commit 269bf18acaa9fe249178bd020d2329f6b25aa2ee)

Conflicts:
	include/openssl/ssl.h
	ssl/ssl_lib.c
---
 doc/ssl/SSL_CTX_share_session_cache.pod | 54 +++++++++++++++++++++++++++++++++
 include/openssl/ssl.h                   |  4 +++
 ssl/ssl_lib.c                           | 42 ++++++++++++++++++++++---
 ssl/ssl_locl.h                          |  9 ++++--
 ssl/ssl_sess.c                          | 36 +++++++++++-----------
 5 files changed, 120 insertions(+), 25 deletions(-)
 create mode 100644 doc/ssl/SSL_CTX_share_session_cache.pod

diff --git a/doc/ssl/SSL_CTX_share_session_cache.pod b/doc/ssl/SSL_CTX_share_session_cache.pod
new file mode 100644
index 0000000..832e845
--- /dev/null
+++ b/doc/ssl/SSL_CTX_share_session_cache.pod
@@ -0,0 +1,54 @@
+=pod
+
+=head1 NAME
+
+SSL_CTX_share_session_cache - enable sharing of session cache
+
+=head1 SYNOPSIS
+
+ #include <openssl/ssl.h>
+
+ void SSL_CTX_share_session_cache(SSL_CTX *a, SSL_CTX *b);
+
+=head1 DESCRIPTION
+
+SSL_CTX_share_session_cache() function releases the session list of B<b> and
+then sets the session list to be the same as B<a>. Both SSL_CTX objects
+then share and modify the same session list.
+
+=head1 NOTES
+
+The sessions lists are stored in their own reference-counted object. When
+an SSL_CTX is created, it has its own session list. This function releases
+the session list of the second argument SSL_CTX B<b> and has B<b> reference
+the session list of SSL_CTX B<a>.
+
+In the following example:
+
+ SSL_CTX_share_session_cache(b, c);
+ SSL_CTX_share_session_cache(a, b);
+
+The result of this is that B<c>'s initial session list is freed, and references
+B<b>'s initial session list. B<b> and B<a> both reference B<a>'s initial
+session list.
+
+There is no way to undo this; a new SSL_CTX should be created instead.
+
+=head1 RETURN VALUES
+
+No return values.
+
+=head1 SEE ALSO
+
+L<ssl(3)|ssl(3)>, L<SSL_set_session(3)|SSL_set_session(3)>,
+L<SSL_session_reused(3)|SSL_session_reused(3)>,
+L<SSL_CTX_add_session(3)|SSL_CTX_add_session(3)>,
+L<SSL_CTX_sess_number(3)|SSL_CTX_sess_number(3)>,
+L<SSL_CTX_sess_set_cache_size(3)|SSL_CTX_sess_set_cache_size(3)>,
+L<SSL_CTX_sess_set_get_cb(3)|SSL_CTX_sess_set_get_cb(3)>,
+L<SSL_CTX_set_session_id_context(3)|SSL_CTX_set_session_id_context(3)>,
+L<SSL_CTX_set_timeout(3)|SSL_CTX_set_timeout(3)>,
+L<SSL_CTX_flush_sessions(3)|SSL_CTX_flush_sessions(3)>,
+L<SSL_CTX_get_session_cache_mode(3)|SSL_CTX_get_session_cache_mode(3)>
+
+=cut
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index db46604..51bb816 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -319,6 +319,8 @@ typedef struct ssl_cipher_st SSL_CIPHER;
 typedef struct ssl_session_st SSL_SESSION;
 typedef struct tls_sigalgs_st TLS_SIGALGS;
 typedef struct ssl_conf_ctx_st SSL_CONF_CTX;
+typedef struct ssl_ctx_session_list_st SSL_CTX_SESSION_LIST;
+
 
 DECLARE_STACK_OF(SSL_CIPHER)
 
@@ -1991,6 +1993,8 @@ __owur void *SSL_CTX_get0_security_ex_data(const SSL_CTX *ctx);
 __owur const struct openssl_ssl_test_functions *SSL_test_functions(void);
 # endif
 
+void SSL_CTX_share_session_cache(SSL_CTX *a, SSL_CTX *b);
+
 /* BEGIN ERROR CODES */
 /*
  * The following lines are auto generated by the script mkerr.pl. Any changes
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 74ee2a8..134bd77 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -1756,9 +1756,12 @@ SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth)
     ret->cert_store = NULL;
     ret->session_cache_mode = SSL_SESS_CACHE_SERVER;
     ret->session_cache_size = SSL_SESSION_CACHE_MAX_SIZE_DEFAULT;
-    ret->session_cache_head = NULL;
-    ret->session_cache_tail = NULL;
+    if (!(ret->session_list = OPENSSL_malloc(sizeof(SSL_CTX_SESSION_LIST))))
+        goto err;
 
+    ret->session_list->session_cache_head=NULL;
+    ret->session_list->session_cache_tail=NULL;
+    ret->session_list->references = 1;
     /* We take the system default */
     ret->session_timeout = meth->get_timeout();
 
@@ -1918,17 +1921,25 @@ void SSL_CTX_free(SSL_CTX *a)
      * free ex_data, then finally free the cache.
      * (See ticket [openssl.org #212].)
      */
-    if (a->sessions != NULL)
-        SSL_CTX_flush_sessions(a, 0);
+    i = CRYPTO_add(&a->session_list->references, -1, CRYPTO_LOCK_SSL_CTX);
+    if (i == 0) {
+        if (a->sessions != NULL)
+            SSL_CTX_flush_sessions(a, 0);
+    }
 
     CRYPTO_free_ex_data(CRYPTO_EX_INDEX_SSL_CTX, a, &a->ex_data);
-    lh_SSL_SESSION_free(a->sessions);
+
+    if (i == 0) {
+        lh_SSL_SESSION_free(a->sessions);
+        OPENSSL_free(a->session_list);
+    }
     X509_STORE_free(a->cert_store);
     sk_SSL_CIPHER_free(a->cipher_list);
     sk_SSL_CIPHER_free(a->cipher_list_by_id);
     ssl_cert_free(a->cert);
     sk_X509_NAME_pop_free(a->client_CA, X509_NAME_free);
     sk_X509_pop_free(a->extra_certs, X509_free);
+
     a->comp_methods = NULL;
 #ifndef OPENSSL_NO_SRTP
     sk_SRTP_PROTECTION_PROFILE_free(a->srtp_profiles);
@@ -3508,4 +3519,25 @@ void *SSL_CTX_get0_security_ex_data(const SSL_CTX *ctx)
     return ctx->cert->sec_ex;
 }
 
+void SSL_CTX_share_session_cache(SSL_CTX *a, SSL_CTX *b)
+{
+    CRYPTO_w_lock(CRYPTO_LOCK_SSL_CTX);
+    b->session_list->references--;
+
+    if (b->session_list->references == 0) {
+        if (b->sessions) {
+            SSL_CTX_flush_sessions(b,0);
+            lh_SSL_SESSION_free(b->sessions);
+        }
+
+        if (b->session_list)
+            OPENSSL_free(b->session_list);
+    }
+
+    b->sessions     = a->sessions;
+    b->session_list = a->session_list;
+    a->session_list->references++;
+    CRYPTO_w_unlock(CRYPTO_LOCK_SSL_CTX);
+}
+
 IMPLEMENT_OBJ_BSEARCH_GLOBAL_CMP_FN(SSL_CIPHER, SSL_CIPHER, ssl_cipher_id);
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 3e38877..2c2c25d 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -714,6 +714,12 @@ struct ssl_comp_st {
 DECLARE_STACK_OF(SSL_COMP)
 DECLARE_LHASH_OF(SSL_SESSION);
 
+struct ssl_ctx_session_list_st {
+    struct ssl_session_st *session_cache_head;
+    struct ssl_session_st *session_cache_tail;
+    int references;
+};
+
 struct ssl_ctx_st {
     const SSL_METHOD *method;
     STACK_OF(SSL_CIPHER) *cipher_list;
@@ -726,8 +732,7 @@ struct ssl_ctx_st {
      * SSL_SESSION_CACHE_MAX_SIZE_DEFAULT. 0 is unlimited.
      */
     unsigned long session_cache_size;
-    struct ssl_session_st *session_cache_head;
-    struct ssl_session_st *session_cache_tail;
+    SSL_CTX_SESSION_LIST *session_list;
     /*
      * This can have one of 2 values, ored together, SSL_SESS_CACHE_CLIENT,
      * SSL_SESS_CACHE_SERVER, Default is SSL_SESSION_CACHE_SERVER, which
diff --git a/ssl/ssl_sess.c b/ssl/ssl_sess.c
index c639e53..5d5abe1 100644
--- a/ssl/ssl_sess.c
+++ b/ssl/ssl_sess.c
@@ -775,7 +775,7 @@ int SSL_CTX_add_session(SSL_CTX *ctx, SSL_SESSION *c)
         if (SSL_CTX_sess_get_cache_size(ctx) > 0) {
             while (SSL_CTX_sess_number(ctx) >
                    SSL_CTX_sess_get_cache_size(ctx)) {
-                if (!remove_session_lock(ctx, ctx->session_cache_tail, 0))
+                if (!remove_session_lock(ctx, ctx->session_list->session_cache_tail, 0))
                     break;
                 else
                     ctx->stats.sess_cache_full++;
@@ -1102,21 +1102,21 @@ static void SSL_SESSION_list_remove(SSL_CTX *ctx, SSL_SESSION *s)
     if ((s->next == NULL) || (s->prev == NULL))
         return;
 
-    if (s->next == (SSL_SESSION *)&(ctx->session_cache_tail)) {
+    if (s->next == (SSL_SESSION *)&(ctx->session_list->session_cache_tail)) {
         /* last element in list */
-        if (s->prev == (SSL_SESSION *)&(ctx->session_cache_head)) {
+        if (s->prev == (SSL_SESSION *)&(ctx->session_list->session_cache_head)) {
             /* only one element in list */
-            ctx->session_cache_head = NULL;
-            ctx->session_cache_tail = NULL;
+            ctx->session_list->session_cache_head = NULL;
+            ctx->session_list->session_cache_tail = NULL;
         } else {
-            ctx->session_cache_tail = s->prev;
-            s->prev->next = (SSL_SESSION *)&(ctx->session_cache_tail);
+            ctx->session_list->session_cache_tail = s->prev;
+            s->prev->next = (SSL_SESSION *)&(ctx->session_list->session_cache_tail);
         }
     } else {
-        if (s->prev == (SSL_SESSION *)&(ctx->session_cache_head)) {
+        if (s->prev == (SSL_SESSION *)&(ctx->session_list->session_cache_head)) {
             /* first element in list */
-            ctx->session_cache_head = s->next;
-            s->next->prev = (SSL_SESSION *)&(ctx->session_cache_head);
+            ctx->session_list->session_cache_head = s->next;
+            s->next->prev = (SSL_SESSION *)&(ctx->session_list->session_cache_head);
         } else {
             /* middle of list */
             s->next->prev = s->prev;
@@ -1131,16 +1131,16 @@ static void SSL_SESSION_list_add(SSL_CTX *ctx, SSL_SESSION *s)
     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);
+    if (ctx->session_list->session_cache_head == NULL) {
+        ctx->session_list->session_cache_head = s;
+        ctx->session_list->session_cache_tail = s;
+        s->prev = (SSL_SESSION *)&(ctx->session_list->session_cache_head);
+        s->next = (SSL_SESSION *)&(ctx->session_list->session_cache_tail);
     } else {
-        s->next = ctx->session_cache_head;
+        s->next = ctx->session_list->session_cache_head;
         s->next->prev = s;
-        s->prev = (SSL_SESSION *)&(ctx->session_cache_head);
-        ctx->session_cache_head = s;
+        s->prev = (SSL_SESSION *)&(ctx->session_list->session_cache_head);
+        ctx->session_list->session_cache_head = s;
     }
 }
 
-- 
2.3.2 (Apple Git-55)

