Kea  1.9.9-git
botan_boost_tls.cc
Go to the documentation of this file.
1 // Copyright (C) 2021 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 #if defined(WITH_BOTAN) && defined(WITH_BOTAN_BOOST)
12 
13 #include <asiolink/asio_wrapper.h>
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 
22 using namespace isc::cryptolink;
23 
24 namespace isc {
25 namespace asiolink {
26 
27 // Classes of Kea certificate stores.
28 using KeaCertificateStorePath = Botan::Certificate_Store_In_Memory;
29 using KeaCertificateStoreFile = Botan::Flatfile_Certificate_Store;
30 
31 // Class of Kea credential managers.
32 class KeaCredentialsManager : public Botan::Credentials_Manager {
33 public:
34  // Constructor.
35  KeaCredentialsManager() : store_(), use_stores_(true), certs_(), key_() {
36  }
37 
38  // Destructor.
39  virtual ~KeaCredentialsManager() {
40  }
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::string&,
58  const std::string&) override {
59  return (certs_);
60  }
61 
62  // Private key.
63  Botan::Private_Key*
64  private_key_for(const Botan::X509_Certificate&,
65  const std::string&,
66  const std::string&) override {
67  return (key_.get());
68  }
69 
70  // Set the store from a path.
71  void setStorePath(const std::string& path) {
72  store_.reset(new KeaCertificateStorePath(path));
73  }
74 
75  // Set the store from a file.
76  void setStoreFile(const std::string& file) {
77  store_.reset(new KeaCertificateStoreFile(file));
78  }
79 
80  // Get the use of CA certificate stores flag.
81  bool getUseStores() const {
82  return (use_stores_);
83  }
84 
85  // Set the use of CA certificate stores flag.
86  void setUseStores(bool use_stores) {
87  use_stores_ = use_stores;
88  }
89 
90  // Set the certificate chain.
91  void setCertChain(const std::string& file) {
92  Botan::DataSource_Stream source(file);
93  certs_.clear();
94  while (!source.end_of_data()) {
95  std::string label;
96  std::vector<uint8_t> cert;
97  try {
98  cert = unlock(Botan::PEM_Code::decode(source, label));
99  if ((label != "CERTIFICATE") &&
100  (label != "X509 CERTIFICATE") &&
101  (label != "TRUSTED CERTIFICATE")) {
102  isc_throw(LibraryError, "Expected a certificate, got '"
103  << label << "'");
104  }
105  certs_.push_back(Botan::X509_Certificate(cert));
106  } catch (const std::exception& ex) {
107  if (certs_.empty()) {
108  throw;
109  }
110  // Got one certificate so skipping garbage.
111  continue;
112  }
113  }
114  if (certs_.empty()) {
115  isc_throw(LibraryError, "Found no certificate?");
116  }
117  }
118 
119  // Set the private key.
120  void setPrivateKey(const std::string& file,
121  Botan::RandomNumberGenerator& rng,
122  bool& is_rsa) {
123  key_.reset(Botan::PKCS8::load_key(file, rng));
124  if (!key_) {
125  isc_throw(Unexpected,
126  "Botan::PKCS8::load_key failed but not threw?");
127  }
128  is_rsa = (key_->algo_name() == "RSA");
129  }
130 
131  // Pointer to the CA certificate store.
132  std::unique_ptr<Botan::Certificate_Store> store_;
133 
134  // Use the CA ceertificate store flag.
135  bool use_stores_;
136 
137  // The certificate chain.
138  std::vector<Botan::X509_Certificate> certs_;
139 
140  // Pointer to the private key.
141  std::unique_ptr<Botan::Private_Key> key_;
142 };
143 
144 // Class of Kea policy.
145 // Use Strict_Policy?
146 class KeaPolicy : public Botan::TLS::Default_Policy {
147 public:
148  // Constructor.
149  KeaPolicy() : prefer_rsa_(true) {
150  }
151 
152  // Destructor.
153  virtual ~KeaPolicy() {
154  }
155 
156  // Allowed signature methods in preference order.
157  std::vector<std::string> allowed_signature_methods() const override {
158  if (prefer_rsa_) {
159  return (AllowedSignatureMethodsRSA);
160  } else {
161  return (AllowedSignatureMethodsECDSA);
162  }
163  }
164 
165  // Disable OSCP.
166  bool require_cert_revocation_info() const override {
167  return false;
168  }
169 
170  // Set the RSA preferred flag.
171  void setPrefRSA(bool prefer_rsa) {
172  prefer_rsa_ = prefer_rsa;
173  }
174 
175  // Prefer RSA preferred flag.
176  bool prefer_rsa_;
177 
178  // Allowed signature methods which prefers RSA.
179  static const std::vector<std::string> AllowedSignatureMethodsRSA;
180 
181  // Allowed signature methods which prefers ECDSA.
182  static const std::vector<std::string> AllowedSignatureMethodsECDSA;
183 };
184 
185 
186 // Kea session manager.
187 using KeaSessionManager = Botan::TLS::Session_Manager_Noop;
188 
189 // Allowed signature methods which prefers RSA.
190 const std::vector<std::string>
191 KeaPolicy::AllowedSignatureMethodsRSA = { "RSA", "DSA", "ECDSA" };
192 
193 // Allowed signature methods which prefers ECDSA.
194 const std::vector<std::string>
195 KeaPolicy::AllowedSignatureMethodsECDSA = { "ECDSA", "RSA", "DSA" };
196 
197 // Class of Botan TLS context implementations.
198 class TlsContextImpl {
199 public:
200  // Constructor.
201  TlsContextImpl() : cred_mgr_(), rng_(), sess_mgr_(), policy_() {
202  }
203 
204  // Destructor.
205  virtual ~TlsContextImpl() {
206  }
207 
208  // Get the peer certificate requirement mode.
209  virtual bool getCertRequired() const {
210  return (cred_mgr_.getUseStores());
211  }
212 
213  // Set the peer certificate requirement mode.
214  //
215  // With Botan this means to provide or not the CA certificate stores.
216  virtual void setCertRequired(bool cert_required) {
217  cred_mgr_.setUseStores(cert_required);
218  }
219 
220  // Load the trust anchor aka certificate authority (path).
221  virtual void loadCaPath(const std::string& ca_path) {
222  try {
223  cred_mgr_.setStorePath(ca_path);
224  } catch (const std::exception& ex) {
225  isc_throw(LibraryError, ex.what());
226  }
227  }
228 
229  // Load the trust anchor aka certificate authority (file).
230  virtual void loadCaFile(const std::string& ca_file) {
231  try {
232  cred_mgr_.setStoreFile(ca_file);
233  } catch (const std::exception& ex) {
234  isc_throw(LibraryError, ex.what());
235  }
236  }
237 
239  virtual void loadCertFile(const std::string& cert_file) {
240  try {
241  cred_mgr_.setCertChain(cert_file);
242  } catch (const std::exception& ex) {
243  isc_throw(LibraryError, ex.what());
244  }
245  }
246 
250  virtual void loadKeyFile(const std::string& key_file) {
251  try {
252  bool is_rsa = true;
253  cred_mgr_.setPrivateKey(key_file, rng_, is_rsa);
254  policy_.setPrefRSA(is_rsa);
255  } catch (const std::exception& ex) {
256  isc_throw(LibraryError, ex.what());
257  }
258  }
259 
260  // Build the context if not yet done.
261  virtual void build() {
262  if (context_) {
263  return;
264  }
265  context_.reset(new Botan::TLS::Context(cred_mgr_,
266  rng_,
267  sess_mgr_,
268  policy_));
269  }
270 
271  virtual Botan::TLS::Context& get() {
272  return (*context_);
273  }
274 
275  // Credentials Manager.
276  KeaCredentialsManager cred_mgr_;
277 
278  // Random Number Generator.
279  Botan::AutoSeeded_RNG rng_;
280 
281  // Session Manager.
282  KeaSessionManager sess_mgr_;
283 
284  KeaPolicy policy_;
285 
286  std::unique_ptr<Botan::TLS::Context> context_;
287 };
288 
289 TlsContext::~TlsContext() {
290 }
291 
292 TlsContext::TlsContext(TlsRole role)
293  : TlsContextBase(role), impl_(new TlsContextImpl()) {
294 }
295 
296 Botan::TLS::Context&
297 TlsContext::getContext() {
298  impl_->build();
299  return (impl_->get());
300 }
301 
302 void
303 TlsContext::setCertRequired(bool cert_required) {
304  if (!cert_required && (getRole() == TlsRole::CLIENT)) {
305  isc_throw(BadValue,
306  "'cert-required' parameter must be true for a TLS client");
307  }
308  impl_->setCertRequired(cert_required);
309 }
310 
311 bool
312 TlsContext::getCertRequired() const {
313  return (impl_->getCertRequired());
314 }
315 
316 void
317 TlsContext::loadCaFile(const std::string& ca_file) {
318  impl_->loadCaFile(ca_file);
319 }
320 
321 void
322 TlsContext::loadCaPath(const std::string& ca_path) {
323  impl_->loadCaPath(ca_path);
324 }
325 
326 void
327 TlsContext::loadCertFile(const std::string& cert_file) {
328  impl_->loadCertFile(cert_file);
329 }
330 
331 void
332 TlsContext::loadKeyFile(const std::string& key_file) {
333  impl_->loadKeyFile(key_file);
334 }
335 
336 } // namespace asiolink
337 } // namespace isc
338 
339 #endif // WITH_BOTAN && WITH_BOTAN_BOOST
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
Defines the logger used by the top-level component of kea-dhcp-ddns.
TLS API.