-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathca_store.cpp
More file actions
177 lines (155 loc) · 6.71 KB
/
Copy pathca_store.cpp
File metadata and controls
177 lines (155 loc) · 6.71 KB
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// Copyright (c) 2014-2026 The Reddcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <node/ca_store.h>
#include <openssl/err.h>
#include <openssl/opensslv.h>
#include <openssl/ssl.h>
#include <openssl/x509.h>
#include <cstdlib>
#include <string>
#if defined(WIN32)
#if OPENSSL_VERSION_NUMBER < 0x30000000L
#error "Windows builds need OpenSSL 3.0 or later for the winstore certificate loader; build against depends."
#endif
#elif defined(MAC_OSX)
#include <CoreFoundation/CoreFoundation.h>
#include <Security/Security.h>
#else
#include <sys/stat.h>
#endif
namespace {
#if defined(MAC_OSX)
//! Add one DER-encoded certificate to a store.
//!
//! A certificate the store already holds is not a failure. Platform trust
//! stores can list the same root more than once, and OpenSSL reports the
//! duplicate as an error that would otherwise abort the whole load.
bool AddDerCertificate(X509_STORE* store, const unsigned char* der, long der_len)
{
// d2i_X509 advances the pointer it is given, so hand it a copy.
const unsigned char* pos{der};
X509* cert{d2i_X509(nullptr, &pos, der_len)};
if (cert == nullptr) {
ERR_clear_error();
return false;
}
bool ok{true};
if (X509_STORE_add_cert(store, cert) != 1) {
ok = ERR_GET_REASON(ERR_peek_last_error()) == X509_R_CERT_ALREADY_IN_HASH_TABLE;
ERR_clear_error();
}
// X509_STORE_add_cert takes its own reference, so drop ours either way.
X509_free(cert);
return ok;
}
#endif // MAC_OSX
#if !defined(WIN32) && !defined(MAC_OSX)
//! Bundle locations used by the distributions this is likely to run on. The
//! first one that exists wins; they hold the same set of public roots.
const char* const CA_FILE_CANDIDATES[]{
"/etc/ssl/certs/ca-certificates.crt", // Debian, Ubuntu, Gentoo, Alpine
"/etc/pki/tls/certs/ca-bundle.crt", // Fedora, RHEL, CentOS
"/etc/ssl/ca-bundle.pem", // openSUSE
"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", // CentOS 7 and later
"/etc/ssl/cert.pem", // FreeBSD, OpenBSD, Alpine
};
//! Hashed certificate directories, used only if no bundle file is found.
const char* const CA_DIR_CANDIDATES[]{
"/etc/ssl/certs",
"/etc/pki/tls/certs",
};
bool PathExists(const char* path)
{
struct stat sb;
return ::stat(path, &sb) == 0;
}
#endif // !WIN32 && !MAC_OSX
} // namespace
std::string node::LoadTrustedCACertificates(SSL_CTX* ssl_ctx)
{
if (ssl_ctx == nullptr) return "No TLS context to load certificates into";
#if defined(WIN32)
// OpenSSL 3.2 and later expose the Windows ROOT store through the OSSL_STORE
// URI below. It reads the same anchors the rest of the system trusts, so it
// picks up enterprise roots and administrator revocations without any
// enumeration code here. depends must not be configured with no-winstore.
if (SSL_CTX_load_verify_store(ssl_ctx, "org.openssl.winstore://") != 1) {
ERR_clear_error();
return "Could not read the Windows system certificate store";
}
return "";
#elif defined(MAC_OSX)
// OpenSSL has no equivalent loader for the macOS keychain, so the anchors
// are copied across one at a time.
CFArrayRef anchors{nullptr};
if (SecTrustCopyAnchorCertificates(&anchors) != errSecSuccess || anchors == nullptr) {
return "Could not read the macOS system trust store";
}
X509_STORE* store{SSL_CTX_get_cert_store(ssl_ctx)};
if (store == nullptr) {
CFRelease(anchors);
return "No TLS certificate store to load anchors into";
}
long loaded{0};
const CFIndex count{CFArrayGetCount(anchors)};
for (CFIndex i = 0; i < count; ++i) {
const void* elem{CFArrayGetValueAtIndex(anchors, i)};
if (elem == nullptr) continue;
SecCertificateRef cert{reinterpret_cast<SecCertificateRef>(const_cast<void*>(elem))};
CFDataRef der{SecCertificateCopyData(cert)};
if (der == nullptr) continue;
if (AddDerCertificate(store, CFDataGetBytePtr(der), static_cast<long>(CFDataGetLength(der)))) {
++loaded;
}
CFRelease(der);
}
CFRelease(anchors);
if (loaded == 0) return "The macOS system trust store contained no usable certificates";
return "";
#else
// An explicit environment setting is honoured first, so a container or a
// distribution that keeps its bundle somewhere unusual can point at it
// without a rebuild. These are the same two variables OpenSSL's own default
// verify paths consult.
const char* const env_file{std::getenv("SSL_CERT_FILE")};
const char* const env_dir{std::getenv("SSL_CERT_DIR")};
if ((env_file != nullptr && *env_file != '\0') || (env_dir != nullptr && *env_dir != '\0')) {
if (SSL_CTX_load_verify_locations(ssl_ctx,
(env_file != nullptr && *env_file != '\0') ? env_file : nullptr,
(env_dir != nullptr && *env_dir != '\0') ? env_dir : nullptr) == 1) {
return "";
}
ERR_clear_error();
return "SSL_CERT_FILE or SSL_CERT_DIR is set but no certificates could be read from it";
}
for (const char* const candidate : CA_FILE_CANDIDATES) {
if (!PathExists(candidate)) continue;
if (SSL_CTX_load_verify_locations(ssl_ctx, candidate, nullptr) == 1) return "";
ERR_clear_error();
}
for (const char* const candidate : CA_DIR_CANDIDATES) {
if (!PathExists(candidate)) continue;
if (SSL_CTX_load_verify_locations(ssl_ctx, nullptr, candidate) == 1) return "";
ERR_clear_error();
}
// Last resort: OpenSSL's compiled-in location, which is what a build
// against a system OpenSSL rather than against depends will normally
// succeed with.
//
// Whether it exists has to be checked here. SSL_CTX_set_default_verify_paths
// only registers the lookups and reports success without touching the
// filesystem, so trusting its return value would report an empty trust
// store as a working one, which is precisely the depends case this function
// exists to catch.
const char* const default_file{X509_get_default_cert_file()};
const char* const default_dir{X509_get_default_cert_dir()};
if ((default_file != nullptr && PathExists(default_file)) ||
(default_dir != nullptr && PathExists(default_dir))) {
if (SSL_CTX_set_default_verify_paths(ssl_ctx) == 1) return "";
ERR_clear_error();
}
return "No CA certificate bundle was found in any of the usual locations; "
"set SSL_CERT_FILE or SSL_CERT_DIR to point at one";
#endif
}