Line data Source code
1 : #ifndef THINGER_ASIO_SSL_CERTIFICATE_MANAGER_HPP
2 : #define THINGER_ASIO_SSL_CERTIFICATE_MANAGER_HPP
3 :
4 : #include <memory>
5 : #include <unordered_map>
6 : #include <string>
7 : #include <set>
8 : #include <regex>
9 : #include <mutex>
10 : #include <optional>
11 : #include <boost/asio/ssl/context.hpp>
12 :
13 : namespace thinger::asio {
14 :
15 : // Forward declaration
16 : template<typename T>
17 : class regex_map;
18 :
19 : class certificate_manager {
20 : private:
21 : // Private constructor for singleton
22 : certificate_manager();
23 :
24 : // Delete copy and move operations
25 : certificate_manager(const certificate_manager&) = delete;
26 : certificate_manager& operator=(const certificate_manager&) = delete;
27 : certificate_manager(certificate_manager&&) = delete;
28 : certificate_manager& operator=(certificate_manager&&) = delete;
29 :
30 : // Create base SSL context with common settings
31 : std::shared_ptr<boost::asio::ssl::context> create_base_ssl_context();
32 :
33 : // Create SSL context from certificate and key strings
34 : std::shared_ptr<boost::asio::ssl::context> create_ssl_context(
35 : const std::string& cert_chain,
36 : const std::string& private_key);
37 :
38 : // Certificate storage with regex support for wildcards
39 : std::unique_ptr<regex_map<std::shared_ptr<boost::asio::ssl::context>>> ssl_contexts_;
40 :
41 : // Default certificate context
42 : std::shared_ptr<boost::asio::ssl::context> default_context_;
43 :
44 : // Default hostname
45 : std::string default_host_;
46 :
47 : // SSL/TLS configuration
48 : std::string server_ciphers_;
49 : bool prefer_server_ciphers_ = false;
50 : bool enable_legacy_protocols_ = false;
51 :
52 : // Thread safety
53 : mutable std::mutex mutex_;
54 :
55 : // Generate self-signed certificate for development
56 : void generate_self_signed_certificate();
57 :
58 : public:
59 : // Singleton instance
60 : static certificate_manager& instance();
61 :
62 : // Certificate management
63 : bool set_certificate(const std::string& hostname,
64 : const std::string& certificate,
65 : const std::string& private_key);
66 :
67 : bool set_certificate(const std::string& hostname,
68 : std::shared_ptr<boost::asio::ssl::context> ssl_context);
69 :
70 : std::shared_ptr<boost::asio::ssl::context> get_certificate(const std::string& hostname) const;
71 :
72 : bool has_certificate(const std::string& hostname) const;
73 :
74 : bool remove_certificate(const std::string& hostname);
75 :
76 : // Default certificate management
77 : void set_default_certificate(std::shared_ptr<boost::asio::ssl::context> ssl_context);
78 : void set_default_certificate(const std::string& certificate, const std::string& private_key);
79 : std::shared_ptr<boost::asio::ssl::context> get_default_certificate() const;
80 : void set_default_host(const std::string& host);
81 : const std::string& get_default_host() const;
82 :
83 : // Get all registered hostnames
84 : std::set<std::string> get_registered_hosts() const;
85 :
86 : // SSL/TLS configuration
87 : void set_server_ciphers(const std::string& ciphers, bool prefer_server_ciphers);
88 : void enable_legacy_protocols(bool enable);
89 :
90 : // SNI callback for SSL handshake
91 : static int sni_callback(SSL* ssl, int* al, void* arg);
92 : };
93 :
94 : // Simple regex map implementation for certificate hostname matching
95 : template<typename T>
96 : class regex_map {
97 : private:
98 : struct regex_entry {
99 : std::string key;
100 : T value;
101 : std::regex pattern;
102 :
103 369 : regex_entry(const std::string& k, const T& v, const std::regex& p)
104 369 : : key(k), value(v), pattern(p) {}
105 : };
106 :
107 : std::vector<regex_entry> regex_items_;
108 : std::unordered_map<std::string, T> non_regex_items_;
109 : mutable std::mutex mutex_;
110 :
111 : public:
112 369 : void set(const std::string& key, const T& value, const std::string& original = "") {
113 369 : std::lock_guard<std::mutex> lock(mutex_);
114 :
115 : // Store the original key
116 369 : std::string store_key = original.empty() ? key : original;
117 :
118 : // Check if key contains regex patterns
119 369 : bool has_regex_chars = key.find_first_of("^$.*+?{}[]|()\\") != std::string::npos;
120 :
121 369 : if (!has_regex_chars) {
122 : // Store as non-regex item
123 0 : non_regex_items_[store_key] = value;
124 : } else {
125 : // Remove any existing regex entry with the same original key
126 738 : regex_items_.erase(
127 369 : std::remove_if(regex_items_.begin(), regex_items_.end(),
128 14883 : [&store_key](const regex_entry& e) { return e.key == store_key; }),
129 369 : regex_items_.end()
130 : );
131 :
132 : // Treat as regex pattern
133 : try {
134 369 : regex_items_.emplace_back(store_key, value, std::regex(key));
135 0 : } catch (const std::regex_error& e) {
136 : // If regex compilation fails, skip this entry
137 0 : return;
138 : }
139 : }
140 369 : }
141 :
142 196 : std::optional<T> get(const std::string& key) const {
143 196 : std::lock_guard<std::mutex> lock(mutex_);
144 :
145 : // First try exact match in non-regex items
146 196 : auto it = non_regex_items_.find(key);
147 196 : if (it != non_regex_items_.end()) {
148 0 : return it->second;
149 : }
150 :
151 : // Check if the key itself is a stored regex pattern (like *.example.com)
152 337 : for (const auto& item : regex_items_) {
153 177 : if (item.key == key) {
154 36 : return item.value;
155 : }
156 : }
157 :
158 : // Then try regex matches
159 214 : for (const auto& item : regex_items_) {
160 108 : if (std::regex_match(key, item.pattern)) {
161 54 : return item.value;
162 : }
163 : }
164 :
165 106 : return std::nullopt;
166 196 : }
167 :
168 33 : bool has(const std::string& key) const {
169 33 : return get(key).has_value();
170 : }
171 :
172 363 : void erase(const std::string& key) {
173 363 : std::lock_guard<std::mutex> lock(mutex_);
174 :
175 : // Try to erase from non-regex items first
176 363 : auto it = non_regex_items_.find(key);
177 363 : if (it != non_regex_items_.end()) {
178 0 : non_regex_items_.erase(it);
179 0 : return;
180 : }
181 :
182 : // Then try regex items
183 726 : regex_items_.erase(
184 363 : std::remove_if(regex_items_.begin(), regex_items_.end(),
185 15240 : [&key](const regex_entry& e) { return e.key == key; }),
186 363 : regex_items_.end()
187 : );
188 363 : }
189 :
190 141 : std::set<std::string> keys() const {
191 141 : std::lock_guard<std::mutex> lock(mutex_);
192 141 : std::set<std::string> result;
193 :
194 : // Add non-regex keys
195 141 : for (const auto& pair : non_regex_items_) {
196 0 : result.insert(pair.first);
197 : }
198 :
199 : // Add regex keys
200 807 : for (const auto& entry : regex_items_) {
201 666 : result.insert(entry.key);
202 : }
203 :
204 282 : return result;
205 141 : }
206 : };
207 :
208 : } // namespace thinger::asio
209 :
210 : #endif // THINGER_ASIO_SSL_CERTIFICATE_MANAGER_HPP
|