Line data Source code
1 : #include "client_verify.hpp"
2 : #include "../../util/logger.hpp"
3 : #include <boost/asio/ssl/context.hpp>
4 : #include <openssl/evp.h>
5 : #include <openssl/x509.h>
6 : #include <string>
7 :
8 : namespace thinger::asio {
9 :
10 : namespace {
11 :
12 : // Deferred validation: any chain is accepted here, the application validates it. OpenSSL
13 : // still checks the signature of the client (CertificateVerify): it holds the key of the leaf
14 402 : int accept_chain(int /*preverified*/, X509_STORE_CTX* /*context*/) {
15 402 : return 1;
16 : }
17 :
18 5466 : int policy_index() {
19 5466 : static const int index = SSL_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr);
20 5466 : return index;
21 : }
22 :
23 : // The trusted CAs of a PEM, read as the CAs of a context (Boost.Asio's reader): none if
24 : // there is none, or one cannot be read
25 36 : X509_STORE* load_store(const std::string& pem) {
26 36 : boost::asio::ssl::context context(boost::asio::ssl::context::tls_server);
27 : try {
28 36 : context.add_certificate_authority(boost::asio::buffer(pem));
29 6 : } catch (const boost::system::system_error& e) {
30 6 : LOG_ERROR("cannot read the trusted CAs of the client certificates: {}", e.code().message());
31 6 : return nullptr;
32 6 : }
33 30 : X509_STORE* store = SSL_CTX_get_cert_store(context.native_handle());
34 30 : X509_STORE_up_ref(store);
35 30 : return store;
36 36 : }
37 :
38 : }
39 :
40 3861 : client_verify_policy::client_verify_policy(client_verify_options options) : options_(std::move(options)) {
41 : // What identifies the policy, as the context of its sessions (SHA-256: 32 bytes, the most
42 : // OpenSSL takes)
43 7722 : std::string identity = std::to_string(static_cast<int>(options_.mode)) + ":" + std::to_string(options_.max_depth) +
44 11583 : ":" + std::to_string(options_.max_cert_list) + ":" + options_.trusted_cas;
45 3861 : unsigned int size = 0;
46 7722 : EVP_Digest(identity.data(), identity.size(), session_id_context_.data(), &size, EVP_sha256(), nullptr);
47 3861 : }
48 :
49 3861 : client_verify_policy::~client_verify_policy() {
50 3861 : X509_STORE_free(store_);
51 3861 : }
52 :
53 3831 : std::shared_ptr<const client_verify_policy> client_verify_policy::create(const client_verify_options& options) {
54 3831 : std::shared_ptr<client_verify_policy> policy(new client_verify_policy(options));
55 3831 : if (options.mode != client_verify::none && !options.trusted_cas.empty()) {
56 36 : policy->store_ = load_store(options.trusted_cas);
57 36 : if (!policy->store_) return nullptr;
58 : }
59 3825 : return policy;
60 3831 : }
61 :
62 99 : const client_verify_policy& client_verify_policy::deferred() {
63 99 : static const client_verify_policy policy{client_verify_options{}};
64 99 : return policy;
65 : }
66 :
67 8292 : void client_verify_policy::apply(SSL* ssl) const {
68 8292 : if (options_.mode == client_verify::none) return;
69 237 : SSL_set_ex_data(ssl, policy_index(), const_cast<client_verify_policy*>(this));
70 474 : SSL_set_session_id_context(ssl, session_id_context_.data(), session_id_context_.size());
71 237 : set_mode(ssl, options_.mode);
72 : }
73 :
74 573 : void client_verify_policy::set_mode(SSL* ssl, client_verify mode) const {
75 573 : if (mode == client_verify::none) {
76 30 : SSL_set_verify(ssl, SSL_VERIFY_NONE, nullptr);
77 30 : return;
78 : }
79 543 : int flags = SSL_VERIFY_PEER;
80 543 : if (mode == client_verify::required) flags |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
81 543 : SSL_set_verify(ssl, flags, store_ ? nullptr : accept_chain);
82 543 : SSL_set_verify_depth(ssl, options_.max_depth);
83 543 : SSL_set_max_cert_list(ssl, static_cast<long>(options_.max_cert_list));
84 543 : if (store_) SSL_set1_verify_cert_store(ssl, store_);
85 : }
86 :
87 5229 : const client_verify_policy* client_verify_policy::of(const SSL* ssl) {
88 5229 : return static_cast<const client_verify_policy*>(SSL_get_ex_data(ssl, policy_index()));
89 : }
90 :
91 : } // namespace thinger::asio
|