1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
|
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012- OpenVPN Inc.
//
// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
//
#pragma once
#include <string>
#include <map>
#include <set>
#include <tuple>
#include <memory>
#include <utility>
#include <openssl/ssl.h>
#include <openvpn/common/rc.hpp>
#include <openvpn/common/msfind.hpp>
namespace openvpn {
// Client-side session cache.
// (We don't cache server-side sessions because we use TLS
// session resumption tickets which are stateless on the server).
class OpenSSLSessionCache : public RC<thread_unsafe_refcount>
{
public:
typedef RCPtr<OpenSSLSessionCache> Ptr;
OPENVPN_EXCEPTION(openssl_sess_cache_error);
// Wrapper for OpenSSL SSL_SESSION pointers that manages reference counts.
class Session
{
public:
Session(::SSL_SESSION *sess) // caller must pre-increment refcount on sess
: sess_(sess)
{
}
Session(Session &&other) noexcept
{
sess_ = other.sess_;
other.sess_ = nullptr;
}
Session &operator=(Session &&other) noexcept
{
if (sess_)
::SSL_SESSION_free(sess_);
sess_ = other.sess_;
other.sess_ = nullptr;
return *this;
}
::SSL_SESSION *openssl_session() const
{
return sess_;
}
bool operator<(const Session &rhs) const // used when Session is a std::set key
{
return sess_ < rhs.sess_;
}
explicit operator bool() const
{
return sess_ != nullptr;
}
~Session()
{
if (sess_)
::SSL_SESSION_free(sess_);
}
private:
// These methods are deleted because we have no way to increment
// an SSL_SESSION refcount until OpenSSL 1.1.
Session(const Session &) = delete;
Session &operator=(const Session &) = delete;
::SSL_SESSION *sess_;
};
class Key
{
public:
typedef std::unique_ptr<Key> UPtr;
Key(const std::string &key_arg,
OpenSSLSessionCache::Ptr cache_arg)
: key(key_arg),
cache(std::move(cache_arg))
{
// OPENVPN_LOG("OpenSSLSessionCache::Key CONSTRUCT key=" << key);
}
void commit(::SSL_SESSION *sess)
{
if (!sess)
return;
auto mi = MSF::find(cache->map, key);
if (mi)
{
/* auto ins = */ mi->second.emplace(sess);
// OPENVPN_LOG("OpenSSLSessionCache::Key::commit ADD=" << ins.second << " key=" << key);
}
else
{
// OPENVPN_LOG("OpenSSLSessionCache::Key::commit CREATE key=" << key);
auto ins = cache->map.emplace(std::piecewise_construct,
std::forward_as_tuple(key),
std::forward_as_tuple());
ins.first->second.emplace(sess);
}
}
private:
const std::string key;
OpenSSLSessionCache::Ptr cache;
};
// Remove a session from the map after calling func() on it.
// This would be a lot cleaner if we had C++17 std::set::extract().
template <typename FUNC>
void extract(const std::string &key, FUNC func)
{
auto mi = MSF::find(map, key);
if (mi)
{
// OPENVPN_LOG("OpenSSLSessionCache::Key::lookup EXISTS key=" << key);
SessionSet &ss = mi->second;
if (ss.empty())
throw openssl_sess_cache_error("internal error: SessionSet is empty");
auto ssi = ss.begin();
try
{
func(ssi->openssl_session());
}
catch (...)
{
remove_session(mi, ss, ssi);
throw;
}
remove_session(mi, ss, ssi);
}
else
{
// OPENVPN_LOG("OpenSSLSessionCache::Key::lookup NOT_FOUND key=" << key);
}
}
private:
struct SessionSet : public std::set<Session>
{
};
typedef std::map<std::string, SessionSet> Map;
void remove_session(Map::iterator mi, SessionSet &ss, SessionSet::iterator ssi)
{
ss.erase(ssi);
if (ss.empty())
map.erase(mi);
}
Map map;
};
} // namespace openvpn
|