Allow a SSL_SESSION to only ever live in one SSL_CTX. As SSL_SESSION is part of an intrusive linked list, it cannot be in more than one chain at once. Bug: 527997772 Change-Id: I381184d7641407edc4d593594d41efe26a6a6964 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/97869 Reviewed-by: David Benjamin <davidben@google.com> Commit-Queue: Rudolf Polzer <rpolzer@google.com>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index eb4e772..5c6d17a 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h
@@ -2310,7 +2310,10 @@ // SSL_CTX_add_session inserts `session` into `ctx`'s internal session cache. It // returns one on success and zero on error or if `session` is already in the -// cache. The caller retains its reference to `session`. +// cache. The caller retains its reference to `session`. A `session` can be +// only in one `ctx` at any given time; it is an error to add it to more. +// +// TODO(crbug.com/527997772): Remove this restriction. OPENSSL_EXPORT int SSL_CTX_add_session(SSL_CTX *ctx, SSL_SESSION *session); // SSL_CTX_remove_session removes `session` from `ctx`'s internal session cache.
diff --git a/ssl/ssl_session.cc b/ssl/ssl_session.cc index 0db1798..6c94441 100644 --- a/ssl/ssl_session.cc +++ b/ssl/ssl_session.cc
@@ -718,6 +718,14 @@ static bool add_session_locked(SSLContext *ctx, UniquePtr<SSL_SESSION> session) { SSL_SESSION *new_session = session.get(); + + // Sessions have intrusive linked lists, so they cannot be stored in two + // SSLContexts at once. + // TODO(crbug.com/527997772): Remove this restriction. + if ((new_session->prev != nullptr || new_session->next != nullptr)) { + return false; + } + SSL_SESSION *old_session; if (!lh_SSL_SESSION_insert(ctx->sessions, &old_session, new_session)) { return false; @@ -730,11 +738,9 @@ session.reset(old_session); if (old_session != nullptr) { - if (old_session == new_session) { - // `session` was already in the cache. There are no linked list pointers - // to update. - return false; - } + // The session cannot have been in the session, due to the check above. + // TODO(crbug.com/527997772): Revisit this when we remove the check. + BSSL_CHECK(old_session != new_session); // There was a session ID collision. `old_session` was replaced with // `session` in the hash table, so `old_session` must be removed from the
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc index 7975325..9c8101c 100644 --- a/ssl/ssl_test.cc +++ b/ssl/ssl_test.cc
@@ -11961,5 +11961,23 @@ ASSERT_TRUE(CompleteHandshakes(client.get(), server.get())); } +TEST(SSLTest, SessionCrossContextCache) { + UniquePtr<SSL_CTX> ctx_a(SSL_CTX_new(TLS_method())); + UniquePtr<SSL_CTX> ctx_b(SSL_CTX_new(TLS_method())); + ASSERT_TRUE(ctx_a); + ASSERT_TRUE(ctx_b); + + UniquePtr<SSL_SESSION> session(SSL_SESSION_new(ctx_b.get())); + ASSERT_TRUE(session); + uint8_t sid[32] = {}; + ASSERT_TRUE(SSL_SESSION_set1_id(session.get(), sid, sizeof(sid))); + + EXPECT_TRUE(SSL_CTX_add_session(ctx_a.get(), session.get())); + + // This should fail because the session is already in ctx_a. + // TODO(crbug.com/527997772): Remove this restriction. + EXPECT_FALSE(SSL_CTX_add_session(ctx_b.get(), session.get())); +} + } // namespace BSSL_NAMESPACE_END