Kea 3.1.1
botan_tls.cc
Go to the documentation of this file.
1// Copyright (C) 2021-2025 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
10
11#ifdef WITH_BOTAN
12
14#include <asiolink/crypto_tls.h>
15
16#include <botan/auto_rng.h>
17#include <botan/certstor_flatfile.h>
18#include <botan/data_src.h>
19#include <botan/pem.h>
20#include <botan/pkcs8.h>
21#include <botan/tls_session_manager_noop.h>
22
23using namespace isc::cryptolink;
24
25namespace isc {
26namespace asiolink {
27
28// Classes of Kea certificate stores.
29using KeaCertificateStorePath = Botan::Certificate_Store_In_Memory;
30using KeaCertificateStoreFile = Botan::Flatfile_Certificate_Store;
31
32// Class of Kea credential managers.
33class KeaCredentialsManager : public Botan::Credentials_Manager {
34public:
35 // Constructor.
36 KeaCredentialsManager() : store_(), use_stores_(true), certs_(), key_() {
37 }
38
39 // Destructor.
40 virtual ~KeaCredentialsManager() = default;
41
42 // CA certificate stores.
43 // nullptr means do not require or check peer certificate.
44 std::vector<Botan::Certificate_Store*>
45 trusted_certificate_authorities(const std::string&,
46 const std::string&) override {
47 std::vector<Botan::Certificate_Store*> result;
48 if (use_stores_ && store_) {
49 result.push_back(store_.get());
50 }
51 return (result);
52 }
53
54 // Certificate chain.
55 std::vector<Botan::X509_Certificate>
56 cert_chain(const std::vector<std::string>&,
57 const std::vector<Botan::AlgorithmIdentifier>&,
58 const std::string&,
59 const std::string&) override {
60 return (certs_);
61 }
62
63 // Private key.
64 std::shared_ptr<Botan::Private_Key>
65 private_key_for(const Botan::X509_Certificate&,
66 const std::string&,
67 const std::string&) override {
68 return (key_);
69 }
70
71 // Set the store from a path.
72 void setStorePath(const std::string& path) {
73 store_.reset(new KeaCertificateStorePath(path));
74 }
75
76 // Set the store from a file.
77 void setStoreFile(const std::string& file) {
78 store_.reset(new KeaCertificateStoreFile(file));
79 }
80
81 // Get the use of CA certificate stores flag.
82 bool getUseStores() const {
83 return (use_stores_);
84 }
85
86 // Set the use of CA certificate stores flag.
87 void setUseStores(bool use_stores) {
88 use_stores_ = use_stores;
89 }
90
91 // Set the certificate chain.
92 void setCertChain(const std::string& file) {
93 Botan::DataSource_Stream source(file);
94 certs_.clear();
95 while (!source.end_of_data()) {
96 std::string label;
97 std::vector<uint8_t> cert;
98 try {
99 cert = unlock(Botan::PEM_Code::decode(source, label));
100 if ((label != "CERTIFICATE") &&
101 (label != "X509 CERTIFICATE") &&
102 (label != "TRUSTED CERTIFICATE")) {
103 isc_throw(LibraryError, "Expected a certificate, got '"
104 << label << "'");
105 }
106 certs_.push_back(Botan::X509_Certificate(cert));
107 } catch (const std::exception& ex) {
108 if (certs_.empty()) {
109 throw;
110 }
111 // Got one certificate so skipping garbage.
112 continue;
113 }
114 }
115 if (certs_.empty()) {
116 isc_throw(LibraryError, "Found no certificate?");
117 }
118 }
119
120 // Set the private key.
121 void setPrivateKey(const std::string& file,
122 Botan::RandomNumberGenerator&,
123 bool& is_rsa) {
124 Botan::DataSource_Stream source(file);
125 auto priv_key = Botan::PKCS8::load_key(source);
126 if (!priv_key) {
127 isc_throw(Unexpected,
128 "Botan::PKCS8::load_key failed but not threw?");
129 }
130 key_ = std::move(priv_key);
131 is_rsa = (key_->algo_name() == "RSA");
132 }
133
134 // Pointer to the CA certificate store.
135 std::unique_ptr<Botan::Certificate_Store> store_;
136
137 // Use the CA certificate store flag.
138 bool use_stores_;
139
140 // The certificate chain.
141 std::vector<Botan::X509_Certificate> certs_;
142
143 // Pointer to the private key.
144 std::shared_ptr<Botan::Private_Key> key_;
145};
146
147// Class of Kea policy.
148// Use Strict_Policy?
149class KeaPolicy : public Botan::TLS::Default_Policy {
150public:
151 // Constructor.
152 KeaPolicy() : prefer_rsa_(true) {
153 }
154
155 // Destructor.
156 virtual ~KeaPolicy() {
157 }
158
159 // Allowed signature methods in preference order.
160 std::vector<std::string> allowed_signature_methods() const override {
161 if (prefer_rsa_) {
162 return (AllowedSignatureMethodsRSA);
163 } else {
164 return (AllowedSignatureMethodsECDSA);
165 }
166 }
167
168 // Disable OSCP.
169 bool require_cert_revocation_info() const override {
170 return false;
171 }
172
173 // Set the RSA preferred flag.
174 void setPrefRSA(bool prefer_rsa) {
175 prefer_rsa_ = prefer_rsa;
176 }
177
178 // Prefer RSA preferred flag.
179 bool prefer_rsa_;
180
181 // Allowed signature methods which prefers RSA.
182 static const std::vector<std::string> AllowedSignatureMethodsRSA;
183
184 // Allowed signature methods which prefers ECDSA.
185 static const std::vector<std::string> AllowedSignatureMethodsECDSA;
186};
187
188// Kea session manager.
189using KeaSessionManager = Botan::TLS::Session_Manager_Noop;
190
191// Allowed signature methods which prefers RSA.
192const std::vector<std::string>
193KeaPolicy::AllowedSignatureMethodsRSA = { "RSA", "DSA", "ECDSA" };
194
195// Allowed signature methods which prefers ECDSA.
196const std::vector<std::string>
197KeaPolicy::AllowedSignatureMethodsECDSA = { "ECDSA", "RSA", "DSA" };
198
199// Class of Botan TLS context implementations.
200class TlsContextImpl {
201public:
202 // Constructor.
203 TlsContextImpl() :
204 cred_mgr_(new KeaCredentialsManager()),
205 rng_(new Botan::AutoSeeded_RNG()),
206 sess_mgr_(new KeaSessionManager()),
207 policy_(new KeaPolicy()) {
208 }
209
210 // Destructor.
211 virtual ~TlsContextImpl() = default;
212
213 // Get the peer certificate requirement mode.
214 virtual bool getCertRequired() const {
215 return (cred_mgr_->getUseStores());
216 }
217
218 // Set the peer certificate requirement mode.
219 //
220 // With Botan this means to provide or not the CA certificate stores.
221 virtual void setCertRequired(bool cert_required) {
222 cred_mgr_->setUseStores(cert_required);
223 }
224
225 // Load the trust anchor aka certificate authority (path).
226 virtual void loadCaPath(const std::string& ca_path) {
227 try {
228 cred_mgr_->setStorePath(ca_path);
229 } catch (const std::exception& ex) {
230 isc_throw(LibraryError, ex.what());
231 }
232 }
233
234 // Load the trust anchor aka certificate authority (file).
235 virtual void loadCaFile(const std::string& ca_file) {
236 try {
237 cred_mgr_->setStoreFile(ca_file);
238 } catch (const std::exception& ex) {
239 isc_throw(LibraryError, ex.what());
240 }
241 }
242
244 virtual void loadCertFile(const std::string& cert_file) {
245 try {
246 cred_mgr_->setCertChain(cert_file);
247 } catch (const std::exception& ex) {
248 isc_throw(LibraryError, ex.what());
249 }
250 }
251
255 virtual void loadKeyFile(const std::string& key_file) {
256 try {
257 bool is_rsa = true;
258 cred_mgr_->setPrivateKey(key_file, *rng_, is_rsa);
259 policy_->setPrefRSA(is_rsa);
260 } catch (const std::exception& ex) {
261 isc_throw(LibraryError, ex.what());
262 }
263 }
264
265 // Build the context if not yet done.
266 virtual void build() {
267 if (context_) {
268 return;
269 }
270 context_.reset(new Botan::TLS::Context(cred_mgr_,
271 rng_,
272 sess_mgr_,
273 policy_));
274 }
275
276 // Get the context.
277 virtual std::shared_ptr<Botan::TLS::Context> get() {
278 return (context_);
279 }
280
281 // Credentials Manager.
282 std::shared_ptr<KeaCredentialsManager> cred_mgr_;
283
284 // Random Number Generator.
285 std::shared_ptr<Botan::AutoSeeded_RNG> rng_;
286
287 // Session Manager.
288 std::shared_ptr<KeaSessionManager> sess_mgr_;
289
290 std::shared_ptr<KeaPolicy> policy_;
291
292 std::shared_ptr<Botan::TLS::Context> context_;
293};
294
295TlsContext::~TlsContext() {
296}
297
298TlsContext::TlsContext(TlsRole role)
299 : TlsContextBase(role), impl_(new TlsContextImpl()) {
300}
301
302std::shared_ptr<Botan::TLS::Context>
303TlsContext::getContext() {
304 impl_->build();
305 return (impl_->get());
306}
307
308void
309TlsContext::setCertRequired(bool cert_required) {
310 if (!cert_required && (getRole() == TlsRole::CLIENT)) {
312 "'cert-required' parameter must be true for a TLS client");
313 }
314 impl_->setCertRequired(cert_required);
315}
316
317bool
318TlsContext::getCertRequired() const {
319 return (impl_->getCertRequired());
320}
321
322void
323TlsContext::loadCaFile(const std::string& ca_file) {
324 impl_->loadCaFile(ca_file);
325}
326
327void
328TlsContext::loadCaPath(const std::string& ca_path) {
329 impl_->loadCaPath(ca_path);
330}
331
332void
333TlsContext::loadCertFile(const std::string& cert_file) {
334 impl_->loadCertFile(cert_file);
335}
336
337void
338TlsContext::loadKeyFile(const std::string& key_file) {
339 impl_->loadKeyFile(key_file);
340}
341
342} // namespace asiolink
343} // namespace isc
344
345#endif // WITH_BOTAN
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
TLS API.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
int get(CalloutHandle &handle)
The gss-tsig-get command.
Defines the logger used by the top-level component of kea-lfc.