summaryrefslogtreecommitdiff
path: root/louloulibs/network/credentials_manager.cpp
blob: ed04d24e56d8e4ae9d53c2269f0ca14e986018f4 (plain)
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
#include "louloulibs.h"

#ifdef BOTAN_FOUND
#include <network/tcp_socket_handler.hpp>
#include <network/credentials_manager.hpp>
#include <logger/logger.hpp>
#include <botan/tls_exceptn.h>
#include <config/config.hpp>

#ifdef USE_DATABASE
# include <database/database.hpp>
#endif

/**
 * 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"
};

Botan::Certificate_Store_In_Memory BasicCredentialsManager::certificate_store;
bool BasicCredentialsManager::certs_loaded = false;

BasicCredentialsManager::BasicCredentialsManager(const TCPSocketHandler* const socket_handler):
    Botan::Credentials_Manager(),
    socket_handler(socket_handler),
    trusted_fingerprint{}
{
  BasicCredentialsManager::load_certs();
}

void BasicCredentialsManager::set_trusted_fingerprint(const std::string& fingerprint)
{
  this->trusted_fingerprint = fingerprint;
}

void BasicCredentialsManager::verify_certificate_chain(const std::string& type,
                                                       const std::string& purported_hostname,
                                                       const std::vector<Botan::X509_Certificate>& certs)
{
  log_debug("Checking remote certificate (", type, ") for hostname ", purported_hostname);
  try
    {
      Botan::Credentials_Manager::verify_certificate_chain(type, purported_hostname, certs);
      log_debug("Certificate is valid");
    }
  catch (const std::exception& tls_exception)
    {
      log_warning("TLS certificate check failed: ", tls_exception.what());
      if (!this->trusted_fingerprint.empty() && !certs.empty() &&
          this->trusted_fingerprint == certs[0].fingerprint() &&
          certs[0].matches_dns_name(purported_hostname))
        // We trust the certificate, based on the trusted fingerprint and
        // the fact that the hostname matches
        return;

      if (this->socket_handler->abort_on_invalid_cert())
        throw;
    }
}

bool BasicCredentialsManager::try_to_open_one_ca_bundle(const std::vector<std::string>& paths)
{
  for (const auto& path: paths)
    {
      try
        {
          Botan::DataSource_Stream bundle(path);
          log_debug("Using ca bundle: ", path);
          while (!bundle.end_of_data() && bundle.check_available(27))
            {
              // 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 {
                  Botan::X509_Certificate cert(bundle);
                  BasicCredentialsManager::certificate_store.add_certificate(std::move(cert));
                } catch (const Botan::Decoding_Error& error) {
                  continue;
                }
            }
          // Only use the first file that can successfully be read.
          return true;
        }
      catch (const Botan::Stream_IO_Error& e)
        {
          log_debug(e.what());
        }
    }
  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.");
}

std::vector<Botan::Certificate_Store*> BasicCredentialsManager::trusted_certificate_authorities(const std::string&, const std::string&)
{
  return {&this->certificate_store};
}

#endif