• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <grpc/grpc.h>
22 #include <gtest/gtest.h>
23 
24 #include <string>
25 #include <unordered_set>
26 
27 #include "absl/log/check.h"
28 #include "src/core/util/crash.h"
29 #include "test/core/test_util/test_config.h"
30 
31 namespace grpc_core {
32 
33 namespace {
34 
35 class SessionTracker;
36 
37 struct SessionExDataId {
38   SessionTracker* tracker;
39   long id;
40 };
41 
42 class SessionTracker {
43  public:
SessionTracker()44   SessionTracker() { ssl_context_ = SSL_CTX_new(TLSv1_2_method()); }
45 
~SessionTracker()46   ~SessionTracker() { SSL_CTX_free(ssl_context_); }
47 
NewSession(long id)48   tsi::SslSessionPtr NewSession(long id) {
49     static int ex_data_id = SSL_SESSION_get_ex_new_index(
50         0, nullptr, nullptr, nullptr, DestroyExData);
51     CHECK_NE(ex_data_id, -1);
52     // OpenSSL and different version of BoringSSL don't agree on API
53     // so try both.
54     tsi::SslSessionPtr session = NewSessionInternal(SSL_SESSION_new);
55     SessionExDataId* data = new SessionExDataId{this, id};
56     int result = SSL_SESSION_set_ex_data(session.get(), ex_data_id, data);
57     EXPECT_EQ(result, 1);
58     alive_sessions_.insert(id);
59     return session;
60   }
61 
IsAlive(long id) const62   bool IsAlive(long id) const {
63     return alive_sessions_.find(id) != alive_sessions_.end();
64   }
65 
AliveCount() const66   size_t AliveCount() const { return alive_sessions_.size(); }
67 
68  private:
NewSessionInternal(SSL_SESSION * (* cb)())69   tsi::SslSessionPtr NewSessionInternal(SSL_SESSION* (*cb)()) {
70     return tsi::SslSessionPtr(cb());
71   }
72 
NewSessionInternal(SSL_SESSION * (* cb)(const SSL_CTX *))73   tsi::SslSessionPtr NewSessionInternal(SSL_SESSION* (*cb)(const SSL_CTX*)) {
74     return tsi::SslSessionPtr(cb(ssl_context_));
75   }
76 
DestroyExData(void *,void * ptr,CRYPTO_EX_DATA *,int,long,void *)77   static void DestroyExData(void* /*parent*/, void* ptr, CRYPTO_EX_DATA* /*ad*/,
78                             int /*index*/, long /*argl*/, void* /*argp*/) {
79     SessionExDataId* data = static_cast<SessionExDataId*>(ptr);
80     data->tracker->alive_sessions_.erase(data->id);
81     delete data;
82   }
83 
84   SSL_CTX* ssl_context_;
85   std::unordered_set<long> alive_sessions_;
86 };
87 
TEST(SslSessionCacheTest,InitialState)88 TEST(SslSessionCacheTest, InitialState) {
89   SessionTracker tracker;
90   // Verify session initial state.
91   {
92     tsi::SslSessionPtr tmp_sess = tracker.NewSession(1);
93     EXPECT_TRUE(tracker.IsAlive(1));
94     EXPECT_EQ(tracker.AliveCount(), 1);
95   }
96   EXPECT_FALSE(tracker.IsAlive(1));
97   EXPECT_EQ(tracker.AliveCount(), 0);
98 }
99 
TEST(SslSessionCacheTest,LruCache)100 TEST(SslSessionCacheTest, LruCache) {
101   SessionTracker tracker;
102   {
103     RefCountedPtr<tsi::SslSessionLRUCache> cache =
104         tsi::SslSessionLRUCache::Create(3);
105     EXPECT_EQ(cache->Size(), 0);
106     tsi::SslSessionPtr sess2 = tracker.NewSession(2);
107     SSL_SESSION* sess2_ptr = sess2.get();
108     cache->Put("first.dropbox.com", std::move(sess2));
109     EXPECT_EQ(cache->Get("first.dropbox.com").get(), sess2_ptr);
110     EXPECT_TRUE(tracker.IsAlive(2));
111     EXPECT_EQ(tracker.AliveCount(), 1);
112     // Putting element with the same key destroys old session.
113     tsi::SslSessionPtr sess3 = tracker.NewSession(3);
114     SSL_SESSION* sess3_ptr = sess3.get();
115     cache->Put("first.dropbox.com", std::move(sess3));
116     EXPECT_FALSE(tracker.IsAlive(2));
117     EXPECT_EQ(cache->Get("first.dropbox.com").get(), sess3_ptr);
118     EXPECT_TRUE(tracker.IsAlive(3));
119     EXPECT_EQ(tracker.AliveCount(), 1);
120     // Putting three more elements discards current one.
121     for (long id = 4; id < 7; id++) {
122       EXPECT_TRUE(tracker.IsAlive(3));
123       std::string domain = std::to_string(id) + ".random.domain";
124       cache->Put(domain.c_str(), tracker.NewSession(id));
125     }
126     EXPECT_EQ(cache->Size(), 3);
127     EXPECT_FALSE(tracker.IsAlive(3));
128     EXPECT_EQ(tracker.AliveCount(), 3);
129     // Accessing element moves it into front of the queue.
130     EXPECT_TRUE(cache->Get("4.random.domain"));
131     EXPECT_TRUE(tracker.IsAlive(4));
132     EXPECT_TRUE(tracker.IsAlive(5));
133     EXPECT_TRUE(tracker.IsAlive(6));
134     // One element has to be evicted from cache->
135     cache->Put("7.random.domain", tracker.NewSession(7));
136     EXPECT_TRUE(tracker.IsAlive(4));
137     EXPECT_FALSE(tracker.IsAlive(5));
138     EXPECT_TRUE(tracker.IsAlive(6));
139     EXPECT_TRUE(tracker.IsAlive(7));
140     EXPECT_EQ(tracker.AliveCount(), 3);
141   }
142   // Cache destructor destroys all sessions.
143   EXPECT_EQ(tracker.AliveCount(), 0);
144 }
145 
TEST(SslSessionCacheTest,PutAndGet)146 TEST(SslSessionCacheTest, PutAndGet) {
147   // Set up an empty cache and an SSL session.
148   SSL_CTX* ssl_ctx = SSL_CTX_new(TLS_method());
149   tsi::SslSessionPtr ssl_session_ptr(SSL_SESSION_new(ssl_ctx));
150   RefCountedPtr<tsi::SslSessionLRUCache> cache =
151       tsi::SslSessionLRUCache::Create(1);
152   EXPECT_EQ(cache->Size(), 0);
153   // Put the SSL session in the cache.
154   cache->Put("foo.domain", std::move(ssl_session_ptr));
155   EXPECT_EQ(cache->Size(), 1);
156   // Get a copy of the SSL session from the cache.
157   EXPECT_EQ(cache->Size(), 1);
158   EXPECT_NE(cache->Get("foo.domain"), nullptr);
159   // Try to put a null SSL session in the cache and check that it was not
160   // successful.
161   cache->Put("foo.domain.2", /*session=*/nullptr);
162   EXPECT_EQ(cache->Size(), 1);
163   EXPECT_NE(cache->Get("foo.domain"), nullptr);
164   EXPECT_EQ(cache->Get("foo.domain.2"), nullptr);
165   // Cleanup.
166   SSL_CTX_free(ssl_ctx);
167 }
168 
TEST(SslSessionCacheTest,CapacityZeroCache)169 TEST(SslSessionCacheTest, CapacityZeroCache) {
170   // Set up an empty cache and an SSL session.
171   SSL_CTX* ssl_ctx = SSL_CTX_new(TLS_method());
172   tsi::SslSessionPtr ssl_session_ptr(SSL_SESSION_new(ssl_ctx));
173   RefCountedPtr<tsi::SslSessionLRUCache> cache =
174       tsi::SslSessionLRUCache::Create(0);
175   EXPECT_EQ(cache->Size(), 0);
176   // Try to put the SSL session in the cache and check that it was not
177   // successful.
178   cache->Put("foo.domain", std::move(ssl_session_ptr));
179   EXPECT_EQ(cache->Size(), 0);
180   EXPECT_EQ(cache->Get("foo.domain"), nullptr);
181   // Cleanup.
182   SSL_CTX_free(ssl_ctx);
183 }
184 
185 }  // namespace
186 }  // namespace grpc_core
187 
main(int argc,char ** argv)188 int main(int argc, char** argv) {
189   ::testing::InitGoogleTest(&argc, argv);
190   grpc::testing::TestEnvironment env(&argc, argv);
191   grpc_init();
192   int ret = RUN_ALL_TESTS();
193   grpc_shutdown();
194   return ret;
195 }
196