credentials_manager.cpp 4.59 KB
Newer Older
louiz’'s avatar
louiz’ committed
1
#include "biboumi.h"
2 3

#ifdef BOTAN_FOUND
4
#include <network/tcp_socket_handler.hpp>
5 6
#include <network/credentials_manager.hpp>
#include <logger/logger.hpp>
7
#include <botan/tls_exceptn.h>
louiz’'s avatar
louiz’ committed
8
#include <config/config.hpp>
9

10 11 12 13
#ifdef USE_DATABASE
# include <database/database.hpp>
#endif

louiz’'s avatar
louiz’ committed
14 15 16 17 18 19 20 21 22 23
/**
 * TODO find a standard way to find that out.
 */
static const std::vector<std::string> default_cert_files = {
    "/etc/ssl/certs/ca-bundle.crt",
    "/etc/pki/tls/certs/ca-bundle.crt",
    "/etc/ssl/certs/ca-certificates.crt",
    "/etc/ca-certificates/extracted/tls-ca-bundle.pem"
};

louiz’'s avatar
louiz’ committed
24 25
Botan::Certificate_Store_In_Memory BasicCredentialsManager::certificate_store;
bool BasicCredentialsManager::certs_loaded = false;
26

louiz’'s avatar
louiz’ committed
27
BasicCredentialsManager::BasicCredentialsManager(const TCPSocketHandler* const socket_handler):
28
    Botan::Credentials_Manager(),
29 30
    socket_handler(socket_handler),
    trusted_fingerprint{}
31
{
louiz’'s avatar
louiz’ committed
32
  BasicCredentialsManager::load_certs();
33
}
34

louiz’'s avatar
louiz’ committed
35
void BasicCredentialsManager::set_trusted_fingerprint(const std::string& fingerprint)
36 37 38 39
{
  this->trusted_fingerprint = fingerprint;
}

40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
const std::string& BasicCredentialsManager::get_trusted_fingerprint() const
{
  return this->trusted_fingerprint;
}

void check_tls_certificate(const std::vector<Botan::X509_Certificate>& certs,
                           const std::string& hostname, const std::string& trusted_fingerprint,
                           std::exception_ptr exc)
{

  if (!trusted_fingerprint.empty() && !certs.empty() &&
      trusted_fingerprint == certs[0].fingerprint() &&
      certs[0].matches_dns_name(hostname))
    // We trust the certificate, based on the trusted fingerprint and
    // the fact that the hostname matches
    return;

  if (exc)
    std::rethrow_exception(exc);
}

#if BOTAN_VERSION_CODE < BOTAN_VERSION_CODE_FOR(1,11,34)
louiz’'s avatar
louiz’ committed
62 63 64
void BasicCredentialsManager::verify_certificate_chain(const std::string& type,
                                                       const std::string& purported_hostname,
                                                       const std::vector<Botan::X509_Certificate>& certs)
65
{
66
  log_debug("Checking remote certificate (", type, ") for hostname ", purported_hostname);
67 68 69 70 71 72 73
  try
    {
      Botan::Credentials_Manager::verify_certificate_chain(type, purported_hostname, certs);
      log_debug("Certificate is valid");
    }
  catch (const std::exception& tls_exception)
    {
74
      log_warning("TLS certificate check failed: ", tls_exception.what());
75
      std::exception_ptr exception_ptr{};
76
      if (this->socket_handler->abort_on_invalid_cert())
77 78 79
        exception_ptr = std::current_exception();

      check_tls_certificate(certs, purported_hostname, this->trusted_fingerprint, exception_ptr);
80
    }
81
}
82
#endif
83

louiz’'s avatar
louiz’ committed
84
bool BasicCredentialsManager::try_to_open_one_ca_bundle(const std::vector<std::string>& paths)
85 86 87
{
  for (const auto& path: paths)
    {
louiz’'s avatar
louiz’ committed
88 89 90
      try
        {
          Botan::DataSource_Stream bundle(path);
91
          log_debug("Using ca bundle: ", path);
louiz’'s avatar
louiz’ committed
92 93
          while (!bundle.end_of_data() && bundle.check_available(27))
            {
94 95 96 97 98 99
              // TODO: remove this work-around for Botan 1.11.29
              // https://github.com/randombit/botan/issues/438#issuecomment-192866796
              // Note that every certificate that fails to be transcoded into latin-1
              // will be ignored. As a result, some TLS connection may be refused
              // because the certificate is signed by an issuer that was ignored.
              try {
louiz’'s avatar
louiz’ committed
100
                  Botan::X509_Certificate cert(bundle);
101
                  BasicCredentialsManager::certificate_store.add_certificate(cert);
louiz’'s avatar
louiz’ committed
102
                } catch (const Botan::Decoding_Error& error) {
103 104
                  continue;
                }
louiz’'s avatar
louiz’ committed
105 106
            }
          // Only use the first file that can successfully be read.
louiz’'s avatar
louiz’ committed
107
          return true;
louiz’'s avatar
louiz’ committed
108
        }
louiz’'s avatar
louiz’ committed
109
      catch (const Botan::Stream_IO_Error& e)
110
        {
louiz’'s avatar
louiz’ committed
111
          log_debug(e.what());
112 113
        }
    }
louiz’'s avatar
louiz’ committed
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
  return false;
}

void BasicCredentialsManager::load_certs()
{
  //  Only load the certificates the first time
  if (BasicCredentialsManager::certs_loaded)
    return;
  const std::string conf_path = Config::get("ca_file", "");
  std::vector<std::string> paths;
  if (conf_path.empty())
    paths = default_cert_files;
  else
    paths.push_back(conf_path);

  if (BasicCredentialsManager::try_to_open_one_ca_bundle(paths))
    BasicCredentialsManager::certs_loaded = true;
  else
    log_warning("The CA could not be loaded, TLS negociation will probably fail.");
133
}
134

louiz’'s avatar
louiz’ committed
135
std::vector<Botan::Certificate_Store*> BasicCredentialsManager::trusted_certificate_authorities(const std::string&, const std::string&)
136 137 138
{
  return {&this->certificate_store};
}
139 140

#endif