xref: /aosp_15_r20/external/grpc-grpc/test/core/tsi/ssl_session_cache_test.cc (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
1 //
2 //
3 // Copyright 2018 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18 
19 #include "src/core/tsi/ssl/session_cache/ssl_session_cache.h"
20 
21 #include <string>
22 #include <unordered_set>
23 
24 #include <gtest/gtest.h>
25 
26 #include <grpc/grpc.h>
27 #include <grpc/support/log.h>
28 
29 #include "src/core/lib/gprpp/crash.h"
30 #include "test/core/util/test_config.h"
31 
32 namespace grpc_core {
33 
34 namespace {
35 
36 class SessionTracker;
37 
38 struct SessionExDataId {
39   SessionTracker* tracker;
40   long id;
41 };
42 
43 class SessionTracker {
44  public:
SessionTracker()45   SessionTracker() { ssl_context_ = SSL_CTX_new(TLSv1_2_method()); }
46 
~SessionTracker()47   ~SessionTracker() { SSL_CTX_free(ssl_context_); }
48 
NewSession(long id)49   tsi::SslSessionPtr NewSession(long id) {
50     static int ex_data_id = SSL_SESSION_get_ex_new_index(
51         0, nullptr, nullptr, nullptr, DestroyExData);
52     GPR_ASSERT(ex_data_id != -1);
53     // OpenSSL and different version of BoringSSL don't agree on API
54     // so try both.
55     tsi::SslSessionPtr session = NewSessionInternal(SSL_SESSION_new);
56     SessionExDataId* data = new SessionExDataId{this, id};
57     int result = SSL_SESSION_set_ex_data(session.get(), ex_data_id, data);
58     EXPECT_EQ(result, 1);
59     alive_sessions_.insert(id);
60     return session;
61   }
62 
IsAlive(long id) const63   bool IsAlive(long id) const {
64     return alive_sessions_.find(id) != alive_sessions_.end();
65   }
66 
AliveCount() const67   size_t AliveCount() const { return alive_sessions_.size(); }
68 
69  private:
NewSessionInternal(SSL_SESSION * (* cb)())70   tsi::SslSessionPtr NewSessionInternal(SSL_SESSION* (*cb)()) {
71     return tsi::SslSessionPtr(cb());
72   }
73 
NewSessionInternal(SSL_SESSION * (* cb)(const SSL_CTX *))74   tsi::SslSessionPtr NewSessionInternal(SSL_SESSION* (*cb)(const SSL_CTX*)) {
75     return tsi::SslSessionPtr(cb(ssl_context_));
76   }
77 
DestroyExData(void *,void * ptr,CRYPTO_EX_DATA *,int,long,void *)78   static void DestroyExData(void* /*parent*/, void* ptr, CRYPTO_EX_DATA* /*ad*/,
79                             int /*index*/, long /*argl*/, void* /*argp*/) {
80     SessionExDataId* data = static_cast<SessionExDataId*>(ptr);
81     data->tracker->alive_sessions_.erase(data->id);
82     delete data;
83   }
84 
85   SSL_CTX* ssl_context_;
86   std::unordered_set<long> alive_sessions_;
87 };
88 
TEST(SslSessionCacheTest,InitialState)89 TEST(SslSessionCacheTest, InitialState) {
90   SessionTracker tracker;
91   // Verify session initial state.
92   {
93     tsi::SslSessionPtr tmp_sess = tracker.NewSession(1);
94     EXPECT_TRUE(tracker.IsAlive(1));
95     EXPECT_EQ(tracker.AliveCount(), 1);
96   }
97   EXPECT_FALSE(tracker.IsAlive(1));
98   EXPECT_EQ(tracker.AliveCount(), 0);
99 }
100 
TEST(SslSessionCacheTest,LruCache)101 TEST(SslSessionCacheTest, LruCache) {
102   SessionTracker tracker;
103   {
104     RefCountedPtr<tsi::SslSessionLRUCache> cache =
105         tsi::SslSessionLRUCache::Create(3);
106     EXPECT_EQ(cache->Size(), 0);
107     tsi::SslSessionPtr sess2 = tracker.NewSession(2);
108     SSL_SESSION* sess2_ptr = sess2.get();
109     cache->Put("first.dropbox.com", std::move(sess2));
110     EXPECT_EQ(cache->Get("first.dropbox.com").get(), sess2_ptr);
111     EXPECT_TRUE(tracker.IsAlive(2));
112     EXPECT_EQ(tracker.AliveCount(), 1);
113     // Putting element with the same key destroys old session.
114     tsi::SslSessionPtr sess3 = tracker.NewSession(3);
115     SSL_SESSION* sess3_ptr = sess3.get();
116     cache->Put("first.dropbox.com", std::move(sess3));
117     EXPECT_FALSE(tracker.IsAlive(2));
118     EXPECT_EQ(cache->Get("first.dropbox.com").get(), sess3_ptr);
119     EXPECT_TRUE(tracker.IsAlive(3));
120     EXPECT_EQ(tracker.AliveCount(), 1);
121     // Putting three more elements discards current one.
122     for (long id = 4; id < 7; id++) {
123       EXPECT_TRUE(tracker.IsAlive(3));
124       std::string domain = std::to_string(id) + ".random.domain";
125       cache->Put(domain.c_str(), tracker.NewSession(id));
126     }
127     EXPECT_EQ(cache->Size(), 3);
128     EXPECT_FALSE(tracker.IsAlive(3));
129     EXPECT_EQ(tracker.AliveCount(), 3);
130     // Accessing element moves it into front of the queue.
131     EXPECT_TRUE(cache->Get("4.random.domain"));
132     EXPECT_TRUE(tracker.IsAlive(4));
133     EXPECT_TRUE(tracker.IsAlive(5));
134     EXPECT_TRUE(tracker.IsAlive(6));
135     // One element has to be evicted from cache->
136     cache->Put("7.random.domain", tracker.NewSession(7));
137     EXPECT_TRUE(tracker.IsAlive(4));
138     EXPECT_FALSE(tracker.IsAlive(5));
139     EXPECT_TRUE(tracker.IsAlive(6));
140     EXPECT_TRUE(tracker.IsAlive(7));
141     EXPECT_EQ(tracker.AliveCount(), 3);
142   }
143   // Cache destructor destroys all sessions.
144   EXPECT_EQ(tracker.AliveCount(), 0);
145 }
146 
TEST(SslSessionCacheTest,PutAndGet)147 TEST(SslSessionCacheTest, PutAndGet) {
148   // Set up an empty cache and an SSL session.
149   SSL_CTX* ssl_ctx = SSL_CTX_new(TLS_method());
150   tsi::SslSessionPtr ssl_session_ptr(SSL_SESSION_new(ssl_ctx));
151   RefCountedPtr<tsi::SslSessionLRUCache> cache =
152       tsi::SslSessionLRUCache::Create(1);
153   EXPECT_EQ(cache->Size(), 0);
154   // Put the SSL session in the cache.
155   cache->Put("foo.domain", std::move(ssl_session_ptr));
156   EXPECT_EQ(cache->Size(), 1);
157   // Get a copy of the SSL session from the cache.
158   EXPECT_EQ(cache->Size(), 1);
159   EXPECT_NE(cache->Get("foo.domain"), nullptr);
160   // Try to put a null SSL session in the cache and check that it was not
161   // successful.
162   cache->Put("foo.domain.2", /*session=*/nullptr);
163   EXPECT_EQ(cache->Size(), 1);
164   EXPECT_NE(cache->Get("foo.domain"), nullptr);
165   EXPECT_EQ(cache->Get("foo.domain.2"), nullptr);
166   // Cleanup.
167   SSL_CTX_free(ssl_ctx);
168 }
169 
TEST(SslSessionCacheTest,CapacityZeroCache)170 TEST(SslSessionCacheTest, CapacityZeroCache) {
171   // Set up an empty cache and an SSL session.
172   SSL_CTX* ssl_ctx = SSL_CTX_new(TLS_method());
173   tsi::SslSessionPtr ssl_session_ptr(SSL_SESSION_new(ssl_ctx));
174   RefCountedPtr<tsi::SslSessionLRUCache> cache =
175       tsi::SslSessionLRUCache::Create(0);
176   EXPECT_EQ(cache->Size(), 0);
177   // Try to put the SSL session in the cache and check that it was not
178   // successful.
179   cache->Put("foo.domain", std::move(ssl_session_ptr));
180   EXPECT_EQ(cache->Size(), 0);
181   EXPECT_EQ(cache->Get("foo.domain"), nullptr);
182   // Cleanup.
183   SSL_CTX_free(ssl_ctx);
184 }
185 
186 }  // namespace
187 }  // namespace grpc_core
188 
main(int argc,char ** argv)189 int main(int argc, char** argv) {
190   ::testing::InitGoogleTest(&argc, argv);
191   grpc::testing::TestEnvironment env(&argc, argv);
192   grpc_init();
193   int ret = RUN_ALL_TESTS();
194   grpc_shutdown();
195   return ret;
196 }
197