| /** @file |
| |
| @section license License |
| |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| #include "P_SSLConfig.h" |
| #include "SSLSessionCache.h" |
| #include "P_SSLUtils.h" |
| #include "SSLStats.h" |
| #include "iocore/eventsystem/IOBuffer.h" |
| |
| #include <cstring> |
| #include <memory> |
| #include <shared_mutex> |
| |
| namespace |
| { |
| DbgCtl dbg_ctl_ssl_origin_session_cache{"ssl.origin_session_cache"}; |
| } // end anonymous namespace |
| |
| // Custom deleter for shared origin sessions |
| void |
| SSLSessDeleter(SSL_SESSION *_p) |
| { |
| SSL_SESSION_free(_p); |
| } |
| |
| SSLOriginSessionCache::SSLOriginSessionCache() {} |
| |
| SSLOriginSessionCache::~SSLOriginSessionCache() TS_NO_THREAD_SAFETY_ANALYSIS // single-threaded teardown |
| { |
| while (auto *node = orig_sess_que.pop()) { |
| delete node; |
| } |
| orig_sess_map.clear(); |
| } |
| |
| void |
| SSLOriginSessionCache::insert_session(const std::string &lookup_key, SSL_SESSION *sess, SSL *ssl) |
| { |
| // i2d_SSL_SESSION() returns an int and reports failure as a non-positive value, so |
| // check it before widening: assigning straight to size_t turns a failure into a huge |
| // length that then misreports itself as an oversized session. |
| int serialized_len = i2d_SSL_SESSION(sess, nullptr); |
| if (serialized_len <= 0) { |
| Dbg(dbg_ctl_ssl_origin_session_cache, "Unable to save SSL session because it could not be serialized"); |
| return; |
| } |
| |
| /* do not cache a session that's too big. */ |
| size_t len = static_cast<size_t>(serialized_len); |
| if (len > SSLConfigParams::origin_session_max_size) { |
| Dbg(dbg_ctl_ssl_origin_session_cache, "Unable to save SSL session because size of %zu exceeds the max of %zu", len, |
| SSLConfigParams::origin_session_max_size); |
| return; |
| } |
| |
| // Duplicate the session from the connection, we'll be keeping track the ref-count with a shared pointer ourself |
| SSL_SESSION *sess_ptr = SSLSessionDup(sess); |
| |
| Dbg(dbg_ctl_ssl_origin_session_cache, "insert session: %s = %p", lookup_key.c_str(), sess_ptr); |
| |
| ssl_curve_id curve = (ssl == nullptr) ? 0 : SSLGetCurveNID(ssl); |
| std::string group_name = (ssl == nullptr) ? "" : std::string{SSLGetGroupName(ssl)}; |
| |
| std::unique_ptr<SSLOriginSession> ssl_orig_session( |
| new SSLOriginSession(lookup_key, curve, group_name, std::shared_ptr<SSL_SESSION>{sess_ptr, SSLSessDeleter})); |
| auto new_node = ssl_orig_session.release(); |
| |
| ts::write_guard lock(mutex); |
| auto entry = orig_sess_map.find(lookup_key); |
| if (entry != orig_sess_map.end()) { |
| auto node = entry->second; |
| Dbg(dbg_ctl_ssl_origin_session_cache, "found duplicate key: %s, replacing %p with %p", lookup_key.c_str(), |
| node->shared_sess.get(), sess_ptr); |
| orig_sess_que.remove(node); |
| orig_sess_map.erase(entry); |
| delete node; |
| } else if (orig_sess_map.size() >= SSLConfigParams::origin_session_cache_size) { |
| Dbg(dbg_ctl_ssl_origin_session_cache, "origin session cache full, removing oldest session"); |
| remove_oldest_session(); |
| } |
| |
| orig_sess_que.enqueue(new_node); |
| orig_sess_map[lookup_key] = new_node; |
| } |
| |
| std::shared_ptr<SSL_SESSION> |
| SSLOriginSessionCache::get_session(const std::string &lookup_key, ssl_curve_id *curve, std::string &group_name) |
| { |
| Dbg(dbg_ctl_ssl_origin_session_cache, "get session: %s", lookup_key.c_str()); |
| |
| ts::read_guard lock(mutex); |
| auto entry = orig_sess_map.find(lookup_key); |
| if (entry == orig_sess_map.end()) { |
| return nullptr; |
| } |
| |
| if (curve != nullptr) { |
| *curve = entry->second->curve_id; |
| } |
| |
| group_name = entry->second->group_name; |
| |
| return entry->second->shared_sess; |
| } |
| |
| void |
| SSLOriginSessionCache::remove_oldest_session() |
| { |
| while (orig_sess_que.head && orig_sess_que.size >= static_cast<int>(SSLConfigParams::origin_session_cache_size)) { |
| auto node = orig_sess_que.pop(); |
| Dbg(dbg_ctl_ssl_origin_session_cache, "remove oldest session: %s, session ptr: %p", node->key.c_str(), node->shared_sess.get()); |
| orig_sess_map.erase(node->key); |
| delete node; |
| } |
| } |
| |
| void |
| SSLOriginSessionCache::remove_session(const std::string &lookup_key) |
| { |
| // We can't bail on contention here because this session MUST be removed. |
| ts::write_guard lock(mutex); |
| auto entry = orig_sess_map.find(lookup_key); |
| if (entry != orig_sess_map.end()) { |
| auto node = entry->second; |
| Dbg(dbg_ctl_ssl_origin_session_cache, "remove session: %s, session ptr: %p", lookup_key.c_str(), node->shared_sess.get()); |
| orig_sess_que.remove(node); |
| orig_sess_map.erase(entry); |
| delete node; |
| } |
| |
| return; |
| } |