blob: febf72898fb5ee9977cb88ca06dac3fa423f3827 [file]
/** @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;
}