/proc/self/cwd/source/common/tls/context_impl.cc
Line | Count | Source (jump to first uncovered line) |
1 | | #include "source/common/tls/context_impl.h" |
2 | | |
3 | | #include <openssl/ssl.h> |
4 | | |
5 | | #include <algorithm> |
6 | | #include <cstddef> |
7 | | #include <cstdint> |
8 | | #include <memory> |
9 | | #include <string> |
10 | | #include <utility> |
11 | | #include <vector> |
12 | | |
13 | | #include "envoy/admin/v3/certs.pb.h" |
14 | | #include "envoy/common/exception.h" |
15 | | #include "envoy/common/platform.h" |
16 | | #include "envoy/ssl/ssl_socket_extended_info.h" |
17 | | #include "envoy/stats/scope.h" |
18 | | #include "envoy/type/matcher/v3/string.pb.h" |
19 | | |
20 | | #include "source/common/common/assert.h" |
21 | | #include "source/common/common/base64.h" |
22 | | #include "source/common/common/fmt.h" |
23 | | #include "source/common/common/hex.h" |
24 | | #include "source/common/common/utility.h" |
25 | | #include "source/common/network/address_impl.h" |
26 | | #include "source/common/protobuf/utility.h" |
27 | | #include "source/common/runtime/runtime_features.h" |
28 | | #include "source/common/stats/utility.h" |
29 | | #include "source/common/tls/cert_validator/factory.h" |
30 | | #include "source/common/tls/stats.h" |
31 | | #include "source/common/tls/utility.h" |
32 | | |
33 | | #include "absl/container/node_hash_set.h" |
34 | | #include "absl/strings/match.h" |
35 | | #include "absl/strings/str_join.h" |
36 | | #include "cert_validator/cert_validator.h" |
37 | | #include "openssl/evp.h" |
38 | | #include "openssl/hmac.h" |
39 | | #include "openssl/pkcs12.h" |
40 | | #include "openssl/rand.h" |
41 | | |
42 | | namespace Envoy { |
43 | | namespace { |
44 | | |
45 | 0 | void logSslErrorChain() { |
46 | 0 | while (uint64_t err = ERR_get_error()) { |
47 | 0 | ENVOY_LOG_MISC(debug, "SSL error: {}:{}:{}:{}", err, |
48 | 0 | absl::NullSafeStringView(ERR_lib_error_string(err)), |
49 | 0 | absl::NullSafeStringView(ERR_func_error_string(err)), ERR_GET_REASON(err), |
50 | 0 | absl::NullSafeStringView(ERR_reason_error_string(err))); |
51 | 0 | } |
52 | 0 | } |
53 | | |
54 | | } // namespace |
55 | | |
56 | | namespace Extensions { |
57 | | namespace TransportSockets { |
58 | | namespace Tls { |
59 | | |
60 | 0 | int ContextImpl::sslExtendedSocketInfoIndex() { |
61 | 0 | CONSTRUCT_ON_FIRST_USE(int, []() -> int { |
62 | 0 | int ssl_context_index = SSL_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr); |
63 | 0 | RELEASE_ASSERT(ssl_context_index >= 0, ""); |
64 | 0 | return ssl_context_index; |
65 | 0 | }()); |
66 | 0 | } |
67 | | |
68 | | ContextImpl::ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& config, |
69 | | Server::Configuration::CommonFactoryContext& factory_context, |
70 | | Ssl::ContextAdditionalInitFunc additional_init, |
71 | | absl::Status& creation_status) |
72 | | : scope_(scope), stats_(generateSslStats(scope)), factory_context_(factory_context), |
73 | | tls_max_version_(config.maxProtocolVersion()), |
74 | | stat_name_set_(scope.symbolTable().makeSet("TransportSockets::Tls")), |
75 | | unknown_ssl_cipher_(stat_name_set_->add("unknown_ssl_cipher")), |
76 | | unknown_ssl_curve_(stat_name_set_->add("unknown_ssl_curve")), |
77 | | unknown_ssl_algorithm_(stat_name_set_->add("unknown_ssl_algorithm")), |
78 | | unknown_ssl_version_(stat_name_set_->add("unknown_ssl_version")), |
79 | | ssl_ciphers_(stat_name_set_->add("ssl.ciphers")), |
80 | | ssl_versions_(stat_name_set_->add("ssl.versions")), |
81 | | ssl_curves_(stat_name_set_->add("ssl.curves")), |
82 | | ssl_sigalgs_(stat_name_set_->add("ssl.sigalgs")), capabilities_(config.capabilities()), |
83 | 0 | tls_keylog_local_(config.tlsKeyLogLocal()), tls_keylog_remote_(config.tlsKeyLogRemote()) { |
84 | |
|
85 | 0 | auto cert_validator_name = getCertValidatorName(config.certificateValidationContext()); |
86 | 0 | auto cert_validator_factory = |
87 | 0 | Registry::FactoryRegistry<CertValidatorFactory>::getFactory(cert_validator_name); |
88 | |
|
89 | 0 | if (!cert_validator_factory) { |
90 | 0 | creation_status = absl::InvalidArgumentError( |
91 | 0 | absl::StrCat("Failed to get certificate validator factory for ", cert_validator_name)); |
92 | 0 | return; |
93 | 0 | } |
94 | | |
95 | 0 | cert_validator_ = cert_validator_factory->createCertValidator( |
96 | 0 | config.certificateValidationContext(), stats_, factory_context_); |
97 | |
|
98 | 0 | const auto tls_certificates = config.tlsCertificates(); |
99 | 0 | tls_contexts_.resize(std::max(static_cast<size_t>(1), tls_certificates.size())); |
100 | |
|
101 | 0 | std::vector<SSL_CTX*> ssl_contexts(tls_contexts_.size()); |
102 | 0 | for (size_t i = 0; i < tls_contexts_.size(); i++) { |
103 | 0 | auto& ctx = tls_contexts_[i]; |
104 | 0 | ctx.ssl_ctx_.reset(SSL_CTX_new(TLS_method())); |
105 | 0 | ssl_contexts[i] = ctx.ssl_ctx_.get(); |
106 | |
|
107 | 0 | int rc = SSL_CTX_set_app_data(ctx.ssl_ctx_.get(), this); |
108 | 0 | RELEASE_ASSERT(rc == 1, Utility::getLastCryptoError().value_or("")); |
109 | | |
110 | 0 | rc = SSL_CTX_set_min_proto_version(ctx.ssl_ctx_.get(), config.minProtocolVersion()); |
111 | 0 | RELEASE_ASSERT(rc == 1, Utility::getLastCryptoError().value_or("")); |
112 | | |
113 | 0 | rc = SSL_CTX_set_max_proto_version(ctx.ssl_ctx_.get(), config.maxProtocolVersion()); |
114 | 0 | RELEASE_ASSERT(rc == 1, Utility::getLastCryptoError().value_or("")); |
115 | | |
116 | 0 | if (!capabilities_.provides_ciphers_and_curves && |
117 | 0 | !SSL_CTX_set_strict_cipher_list(ctx.ssl_ctx_.get(), config.cipherSuites().c_str())) { |
118 | | // Break up a set of ciphers into each individual cipher and try them each individually in |
119 | | // order to attempt to log which specific one failed. Example of config.cipherSuites(): |
120 | | // "-ALL:[ECDHE-ECDSA-AES128-GCM-SHA256|ECDHE-ECDSA-CHACHA20-POLY1305]:ECDHE-ECDSA-AES128-SHA". |
121 | | // |
122 | | // "-" is both an operator when in the leading position of a token (-ALL: don't allow this |
123 | | // cipher), and the common separator in names (ECDHE-ECDSA-AES128-GCM-SHA256). Don't split on |
124 | | // it because it will separate pieces of the same cipher. When it is a leading character, it |
125 | | // is removed below. |
126 | 0 | std::vector<absl::string_view> ciphers = |
127 | 0 | StringUtil::splitToken(config.cipherSuites(), ":+![|]", false); |
128 | 0 | std::vector<std::string> bad_ciphers; |
129 | 0 | for (const auto& cipher : ciphers) { |
130 | 0 | std::string cipher_str(cipher); |
131 | |
|
132 | 0 | if (absl::StartsWith(cipher_str, "-")) { |
133 | 0 | cipher_str.erase(cipher_str.begin()); |
134 | 0 | } |
135 | |
|
136 | 0 | if (!SSL_CTX_set_strict_cipher_list(ctx.ssl_ctx_.get(), cipher_str.c_str())) { |
137 | 0 | bad_ciphers.push_back(cipher_str); |
138 | 0 | } |
139 | 0 | } |
140 | 0 | creation_status = absl::InvalidArgumentError( |
141 | 0 | fmt::format("Failed to initialize cipher suites {}. The following " |
142 | 0 | "ciphers were rejected when tried individually: {}", |
143 | 0 | config.cipherSuites(), absl::StrJoin(bad_ciphers, ", "))); |
144 | 0 | return; |
145 | 0 | } |
146 | | |
147 | 0 | if (!capabilities_.provides_ciphers_and_curves && |
148 | 0 | !SSL_CTX_set1_curves_list(ctx.ssl_ctx_.get(), config.ecdhCurves().c_str())) { |
149 | 0 | creation_status = absl::InvalidArgumentError( |
150 | 0 | absl::StrCat("Failed to initialize ECDH curves ", config.ecdhCurves())); |
151 | 0 | return; |
152 | 0 | } |
153 | | |
154 | | // Set signature algorithms if given, otherwise fall back to BoringSSL defaults. |
155 | 0 | if (!capabilities_.provides_sigalgs && !config.signatureAlgorithms().empty()) { |
156 | 0 | if (!SSL_CTX_set1_sigalgs_list(ctx.ssl_ctx_.get(), config.signatureAlgorithms().c_str())) { |
157 | 0 | creation_status = absl::InvalidArgumentError(absl::StrCat( |
158 | 0 | "Failed to initialize TLS signature algorithms ", config.signatureAlgorithms())); |
159 | 0 | return; |
160 | 0 | } |
161 | 0 | } |
162 | 0 | } |
163 | | |
164 | 0 | auto verify_mode_or_error = cert_validator_->initializeSslContexts( |
165 | 0 | ssl_contexts, config.capabilities().provides_certificates); |
166 | 0 | SET_AND_RETURN_IF_NOT_OK(verify_mode_or_error.status(), creation_status); |
167 | 0 | auto verify_mode = verify_mode_or_error.value(); |
168 | |
|
169 | 0 | if (!capabilities_.verifies_peer_certificates) { |
170 | 0 | for (auto ctx : ssl_contexts) { |
171 | 0 | if (verify_mode != SSL_VERIFY_NONE) { |
172 | | // TODO(danzh) Envoy's use of SSL_VERIFY_NONE does not quite match the actual semantics as |
173 | | // a client. As a client, SSL_VERIFY_NONE means to verify the certificate (which will fail |
174 | | // without trust anchors), save the result in the session ticket, but otherwise continue |
175 | | // with the handshake. But Envoy actually wants it to accept all certificates. The |
176 | | // disadvantage of using SSL_VERIFY_NONE is that it records the verify_result, which Envoy |
177 | | // never queries but gets saved in session tickets, and tries to find an anchor that isn't |
178 | | // there. And also it differs from server side behavior of SSL_VERIFY_NONE which won't |
179 | | // even request client certs. So, instead, we should configure a callback to skip |
180 | | // validation and always supply the callback to boring SSL. |
181 | 0 | SSL_CTX_set_custom_verify(ctx, verify_mode, customVerifyCallback); |
182 | 0 | SSL_CTX_set_reverify_on_resume(ctx, /*reverify_on_resume_enabled)=*/1); |
183 | 0 | } |
184 | 0 | } |
185 | 0 | } |
186 | |
|
187 | | #ifdef BORINGSSL_FIPS |
188 | | if (!capabilities_.is_fips_compliant) { |
189 | | creation_status = absl::InvalidArgumentError( |
190 | | "Can't load a FIPS noncompliant custom handshaker while running in FIPS compliant mode."); |
191 | | return; |
192 | | } |
193 | | #endif |
194 | |
|
195 | 0 | if (!capabilities_.provides_certificates) { |
196 | 0 | for (uint32_t i = 0; i < tls_certificates.size(); ++i) { |
197 | 0 | auto& ctx = tls_contexts_[i]; |
198 | | // Load certificate chain. |
199 | 0 | const auto& tls_certificate = tls_certificates[i].get(); |
200 | 0 | if (!tls_certificate.pkcs12().empty()) { |
201 | 0 | creation_status = ctx.loadPkcs12(tls_certificate.pkcs12(), tls_certificate.pkcs12Path(), |
202 | 0 | tls_certificate.password()); |
203 | 0 | } else { |
204 | 0 | creation_status = ctx.loadCertificateChain(tls_certificate.certificateChain(), |
205 | 0 | tls_certificate.certificateChainPath()); |
206 | 0 | } |
207 | 0 | if (!creation_status.ok()) { |
208 | 0 | return; |
209 | 0 | } |
210 | | // The must staple extension means the certificate promises to carry |
211 | | // with it an OCSP staple. https://tools.ietf.org/html/rfc7633#section-6 |
212 | 0 | constexpr absl::string_view tls_feature_ext = "1.3.6.1.5.5.7.1.24"; |
213 | 0 | constexpr absl::string_view must_staple_ext_value = "\x30\x3\x02\x01\x05"; |
214 | 0 | auto must_staple = Utility::getCertificateExtensionValue(*ctx.cert_chain_, tls_feature_ext); |
215 | 0 | if (must_staple == must_staple_ext_value) { |
216 | 0 | ctx.is_must_staple_ = true; |
217 | 0 | } |
218 | |
|
219 | 0 | bssl::UniquePtr<EVP_PKEY> public_key(X509_get_pubkey(ctx.cert_chain_.get())); |
220 | 0 | const int pkey_id = EVP_PKEY_id(public_key.get()); |
221 | 0 | ctx.is_ecdsa_ = pkey_id == EVP_PKEY_EC; |
222 | 0 | switch (pkey_id) { |
223 | 0 | case EVP_PKEY_EC: { |
224 | | // We only support P-256 ECDSA today. |
225 | 0 | const EC_KEY* ecdsa_public_key = EVP_PKEY_get0_EC_KEY(public_key.get()); |
226 | | // Since we checked the key type above, this should be valid. |
227 | 0 | ASSERT(ecdsa_public_key != nullptr); |
228 | 0 | const EC_GROUP* ecdsa_group = EC_KEY_get0_group(ecdsa_public_key); |
229 | 0 | if (ecdsa_group == nullptr || |
230 | 0 | EC_GROUP_get_curve_name(ecdsa_group) != NID_X9_62_prime256v1) { |
231 | 0 | creation_status = absl::InvalidArgumentError( |
232 | 0 | fmt::format("Failed to load certificate chain from {}, only P-256 " |
233 | 0 | "ECDSA certificates are supported", |
234 | 0 | ctx.cert_chain_file_path_)); |
235 | 0 | return; |
236 | 0 | } |
237 | 0 | ctx.is_ecdsa_ = true; |
238 | 0 | } break; |
239 | 0 | case EVP_PKEY_RSA: { |
240 | | // We require RSA certificates with 2048-bit or larger keys. |
241 | 0 | const RSA* rsa_public_key = EVP_PKEY_get0_RSA(public_key.get()); |
242 | | // Since we checked the key type above, this should be valid. |
243 | 0 | ASSERT(rsa_public_key != nullptr); |
244 | 0 | const unsigned rsa_key_length = RSA_bits(rsa_public_key); |
245 | | #ifdef BORINGSSL_FIPS |
246 | | if (rsa_key_length != 2048 && rsa_key_length != 3072 && rsa_key_length != 4096) { |
247 | | creation_status = absl::InvalidArgumentError( |
248 | | fmt::format("Failed to load certificate chain from {}, only RSA certificates with " |
249 | | "2048-bit, 3072-bit or 4096-bit keys are supported in FIPS mode", |
250 | | ctx.cert_chain_file_path_)); |
251 | | return; |
252 | | } |
253 | | #else |
254 | 0 | if (rsa_key_length < 2048) { |
255 | 0 | creation_status = absl::InvalidArgumentError( |
256 | 0 | fmt::format("Failed to load certificate chain from {}, only RSA " |
257 | 0 | "certificates with 2048-bit or larger keys are supported", |
258 | 0 | ctx.cert_chain_file_path_)); |
259 | 0 | return; |
260 | 0 | } |
261 | 0 | #endif |
262 | 0 | } break; |
263 | | #ifdef BORINGSSL_FIPS |
264 | | default: |
265 | | creation_status = absl::InvalidArgumentError( |
266 | | fmt::format("Failed to load certificate chain from {}, only RSA and " |
267 | | "ECDSA certificates are supported in FIPS mode", |
268 | | ctx.cert_chain_file_path_)); |
269 | | return; |
270 | | #endif |
271 | 0 | } |
272 | | |
273 | 0 | Envoy::Ssl::PrivateKeyMethodProviderSharedPtr private_key_method_provider = |
274 | 0 | tls_certificate.privateKeyMethod(); |
275 | | // We either have a private key or a BoringSSL private key method provider. |
276 | 0 | if (private_key_method_provider) { |
277 | 0 | ctx.private_key_method_provider_ = private_key_method_provider; |
278 | | // The provider has a reference to the private key method for the context lifetime. |
279 | 0 | Ssl::BoringSslPrivateKeyMethodSharedPtr private_key_method = |
280 | 0 | private_key_method_provider->getBoringSslPrivateKeyMethod(); |
281 | 0 | if (private_key_method == nullptr) { |
282 | 0 | creation_status = absl::InvalidArgumentError( |
283 | 0 | fmt::format("Failed to get BoringSSL private key method from provider")); |
284 | 0 | return; |
285 | 0 | } |
286 | | #ifdef BORINGSSL_FIPS |
287 | | if (!ctx.private_key_method_provider_->checkFips()) { |
288 | | creation_status = absl::InvalidArgumentError( |
289 | | fmt::format("Private key method doesn't support FIPS mode with current parameters")); |
290 | | return; |
291 | | } |
292 | | #endif |
293 | 0 | SSL_CTX_set_private_key_method(ctx.ssl_ctx_.get(), private_key_method.get()); |
294 | 0 | } else if (!tls_certificate.privateKey().empty()) { |
295 | | // Load private key. |
296 | 0 | creation_status = |
297 | 0 | ctx.loadPrivateKey(tls_certificate.privateKey(), tls_certificate.privateKeyPath(), |
298 | 0 | tls_certificate.password()); |
299 | 0 | if (!creation_status.ok()) { |
300 | 0 | return; |
301 | 0 | } |
302 | 0 | } |
303 | | |
304 | 0 | if (additional_init != nullptr) { |
305 | 0 | absl::Status init_status = additional_init(ctx, tls_certificate); |
306 | 0 | SET_AND_RETURN_IF_NOT_OK(creation_status, init_status); |
307 | 0 | } |
308 | 0 | } |
309 | 0 | } |
310 | | |
311 | 0 | parsed_alpn_protocols_ = parseAlpnProtocols(config.alpnProtocols(), creation_status); |
312 | 0 | SET_AND_RETURN_IF_NOT_OK(creation_status, creation_status); |
313 | |
|
314 | 0 | #if BORINGSSL_API_VERSION >= 21 |
315 | | // Register stat names based on lists reported by BoringSSL. |
316 | 0 | std::vector<const char*> list(SSL_get_all_cipher_names(nullptr, 0)); |
317 | 0 | SSL_get_all_cipher_names(list.data(), list.size()); |
318 | 0 | stat_name_set_->rememberBuiltins(list); |
319 | |
|
320 | 0 | list.resize(SSL_get_all_curve_names(nullptr, 0)); |
321 | 0 | SSL_get_all_curve_names(list.data(), list.size()); |
322 | 0 | stat_name_set_->rememberBuiltins(list); |
323 | |
|
324 | 0 | list.resize(SSL_get_all_signature_algorithm_names(nullptr, 0)); |
325 | 0 | SSL_get_all_signature_algorithm_names(list.data(), list.size()); |
326 | 0 | stat_name_set_->rememberBuiltins(list); |
327 | |
|
328 | 0 | list.resize(SSL_get_all_version_names(nullptr, 0)); |
329 | 0 | SSL_get_all_version_names(list.data(), list.size()); |
330 | 0 | stat_name_set_->rememberBuiltins(list); |
331 | | #else |
332 | | // Use the SSL library to iterate over the configured ciphers. |
333 | | // |
334 | | // Note that if a negotiated cipher suite is outside of this set, we'll issue an ENVOY_BUG. |
335 | | for (Ssl::TlsContext& tls_context : tls_contexts_) { |
336 | | for (const SSL_CIPHER* cipher : SSL_CTX_get_ciphers(tls_context.ssl_ctx_.get())) { |
337 | | stat_name_set_->rememberBuiltin(SSL_CIPHER_get_name(cipher)); |
338 | | } |
339 | | } |
340 | | |
341 | | // Add supported cipher suites from the TLS 1.3 spec: |
342 | | // https://tools.ietf.org/html/rfc8446#appendix-B.4 |
343 | | // AES-CCM cipher suites are removed (no BoringSSL support). |
344 | | // |
345 | | // Note that if a negotiated cipher suite is outside of this set, we'll issue an ENVOY_BUG. |
346 | | stat_name_set_->rememberBuiltins( |
347 | | {"TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384", "TLS_CHACHA20_POLY1305_SHA256"}); |
348 | | |
349 | | // All supported curves. Source: |
350 | | // https://github.com/google/boringssl/blob/3743aafdacff2f7b083615a043a37101f740fa53/ssl/ssl_key_share.cc#L302-L309 |
351 | | // |
352 | | // Note that if a negotiated curve is outside of this set, we'll issue an ENVOY_BUG. |
353 | | stat_name_set_->rememberBuiltins({"P-224", "P-256", "P-384", "P-521", "X25519", "CECPQ2"}); |
354 | | |
355 | | // All supported signature algorithms. Source: |
356 | | // https://github.com/google/boringssl/blob/3743aafdacff2f7b083615a043a37101f740fa53/ssl/ssl_privkey.cc#L436-L453 |
357 | | // |
358 | | // Note that if a negotiated algorithm is outside of this set, we'll issue an ENVOY_BUG. |
359 | | stat_name_set_->rememberBuiltins({ |
360 | | "rsa_pkcs1_md5_sha1", |
361 | | "rsa_pkcs1_sha1", |
362 | | "rsa_pkcs1_sha256", |
363 | | "rsa_pkcs1_sha384", |
364 | | "rsa_pkcs1_sha512", |
365 | | "ecdsa_sha1", |
366 | | "ecdsa_secp256r1_sha256", |
367 | | "ecdsa_secp384r1_sha384", |
368 | | "ecdsa_secp521r1_sha512", |
369 | | "rsa_pss_rsae_sha256", |
370 | | "rsa_pss_rsae_sha384", |
371 | | "rsa_pss_rsae_sha512", |
372 | | "ed25519", |
373 | | }); |
374 | | |
375 | | // All supported protocol versions. |
376 | | // |
377 | | // Note that if a negotiated version is outside of this set, we'll issue an ENVOY_BUG. |
378 | | stat_name_set_->rememberBuiltins({"TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"}); |
379 | | #endif |
380 | | |
381 | | // As late as possible, run the custom SSL_CTX configuration callback on each |
382 | | // SSL_CTX, if set. |
383 | 0 | if (auto sslctx_cb = config.sslctxCb(); sslctx_cb) { |
384 | 0 | for (Ssl::TlsContext& ctx : tls_contexts_) { |
385 | 0 | sslctx_cb(ctx.ssl_ctx_.get()); |
386 | 0 | } |
387 | 0 | } |
388 | |
|
389 | 0 | if (!config.tlsKeyLogPath().empty()) { |
390 | 0 | ENVOY_LOG(debug, "Enable tls key log"); |
391 | 0 | auto file_or_error = config.accessLogManager().createAccessLog( |
392 | 0 | Filesystem::FilePathAndType{Filesystem::DestinationType::File, config.tlsKeyLogPath()}); |
393 | 0 | SET_AND_RETURN_IF_NOT_OK(file_or_error.status(), creation_status); |
394 | 0 | tls_keylog_file_ = file_or_error.value(); |
395 | 0 | for (auto& context : tls_contexts_) { |
396 | 0 | SSL_CTX* ctx = context.ssl_ctx_.get(); |
397 | 0 | ASSERT(ctx != nullptr); |
398 | 0 | SSL_CTX_set_keylog_callback(ctx, keylogCallback); |
399 | 0 | } |
400 | 0 | } |
401 | 0 | } Unexecuted instantiation: Envoy::Extensions::TransportSockets::Tls::ContextImpl::ContextImpl(Envoy::Stats::Scope&, Envoy::Ssl::ContextConfig const&, Envoy::Server::Configuration::CommonFactoryContext&, std::__1::function<absl::lts_20230802::Status (Envoy::Ssl::TlsContext&, Envoy::Ssl::TlsCertificateConfig const&)>, absl::lts_20230802::Status&) Unexecuted instantiation: Envoy::Extensions::TransportSockets::Tls::ContextImpl::ContextImpl(Envoy::Stats::Scope&, Envoy::Ssl::ContextConfig const&, Envoy::Server::Configuration::CommonFactoryContext&, std::__1::function<absl::lts_20230802::Status (Envoy::Ssl::TlsContext&, Envoy::Ssl::TlsCertificateConfig const&)>, absl::lts_20230802::Status&) |
402 | | |
403 | 0 | void ContextImpl::keylogCallback(const SSL* ssl, const char* line) { |
404 | 0 | ASSERT(ssl != nullptr); |
405 | 0 | auto callbacks = |
406 | 0 | static_cast<Network::TransportSocketCallbacks*>(SSL_get_ex_data(ssl, sslSocketIndex())); |
407 | 0 | auto ctx = static_cast<ContextImpl*>(SSL_CTX_get_app_data(SSL_get_SSL_CTX(ssl))); |
408 | 0 | ASSERT(callbacks != nullptr); |
409 | 0 | ASSERT(ctx != nullptr); |
410 | | |
411 | 0 | if ((ctx->tls_keylog_local_.getIpListSize() == 0 || |
412 | 0 | ctx->tls_keylog_local_.contains( |
413 | 0 | *(callbacks->connection().connectionInfoProvider().localAddress()))) && |
414 | 0 | (ctx->tls_keylog_remote_.getIpListSize() == 0 || |
415 | 0 | ctx->tls_keylog_remote_.contains( |
416 | 0 | *(callbacks->connection().connectionInfoProvider().remoteAddress())))) { |
417 | 0 | ctx->tls_keylog_file_->write(absl::StrCat(line, "\n")); |
418 | 0 | } |
419 | 0 | } |
420 | | |
421 | 0 | int ContextImpl::sslSocketIndex() { |
422 | 0 | CONSTRUCT_ON_FIRST_USE(int, []() -> int { |
423 | 0 | int ssl_socket_index = SSL_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr); |
424 | 0 | RELEASE_ASSERT(ssl_socket_index >= 0, ""); |
425 | 0 | return ssl_socket_index; |
426 | 0 | }()); |
427 | 0 | } |
428 | | |
429 | | std::vector<uint8_t> ContextImpl::parseAlpnProtocols(const std::string& alpn_protocols, |
430 | 0 | absl::Status& parse_status) { |
431 | 0 | if (alpn_protocols.empty()) { |
432 | 0 | return {}; |
433 | 0 | } |
434 | | |
435 | 0 | if (alpn_protocols.size() >= 65535) { |
436 | 0 | parse_status = absl::InvalidArgumentError("Invalid ALPN protocol string"); |
437 | 0 | return {}; |
438 | 0 | } |
439 | | |
440 | 0 | std::vector<uint8_t> out(alpn_protocols.size() + 1); |
441 | 0 | size_t start = 0; |
442 | 0 | for (size_t i = 0; i <= alpn_protocols.size(); i++) { |
443 | 0 | if (i == alpn_protocols.size() || alpn_protocols[i] == ',') { |
444 | 0 | if (i - start > 255) { |
445 | 0 | parse_status = absl::InvalidArgumentError("Invalid ALPN protocol string"); |
446 | 0 | return {}; |
447 | 0 | } |
448 | | |
449 | 0 | out[start] = i - start; |
450 | 0 | start = i + 1; |
451 | 0 | } else { |
452 | 0 | out[i + 1] = alpn_protocols[i]; |
453 | 0 | } |
454 | 0 | } |
455 | | |
456 | 0 | return out; |
457 | 0 | } |
458 | | |
459 | | absl::StatusOr<bssl::UniquePtr<SSL>> |
460 | 0 | ContextImpl::newSsl(const Network::TransportSocketOptionsConstSharedPtr& options) { |
461 | | // We use the first certificate for a new SSL object, later in the |
462 | | // SSL_CTX_set_select_certificate_cb() callback following ClientHello, we replace with the |
463 | | // selected certificate via SSL_set_SSL_CTX(). |
464 | 0 | auto ssl_con = bssl::UniquePtr<SSL>(SSL_new(tls_contexts_[0].ssl_ctx_.get())); |
465 | 0 | SSL_set_app_data(ssl_con.get(), &options); |
466 | 0 | return ssl_con; |
467 | 0 | } |
468 | | |
469 | 0 | enum ssl_verify_result_t ContextImpl::customVerifyCallback(SSL* ssl, uint8_t* out_alert) { |
470 | 0 | auto* extended_socket_info = reinterpret_cast<Envoy::Ssl::SslExtendedSocketInfo*>( |
471 | 0 | SSL_get_ex_data(ssl, ContextImpl::sslExtendedSocketInfoIndex())); |
472 | 0 | if (extended_socket_info->certificateValidationResult() != Ssl::ValidateStatus::NotStarted) { |
473 | 0 | if (extended_socket_info->certificateValidationResult() == Ssl::ValidateStatus::Pending) { |
474 | 0 | return ssl_verify_retry; |
475 | 0 | } |
476 | 0 | ENVOY_LOG(trace, "Already has a result: {}", |
477 | 0 | static_cast<int>(extended_socket_info->certificateValidationStatus())); |
478 | | // Already has a binary result, return immediately. |
479 | 0 | *out_alert = extended_socket_info->certificateValidationAlert(); |
480 | 0 | return extended_socket_info->certificateValidationResult() == Ssl::ValidateStatus::Successful |
481 | 0 | ? ssl_verify_ok |
482 | 0 | : ssl_verify_invalid; |
483 | 0 | } |
484 | | // Hasn't kicked off any validation for this connection yet. |
485 | 0 | SSL_CTX* ssl_ctx = SSL_get_SSL_CTX(ssl); |
486 | 0 | ContextImpl* context_impl = static_cast<ContextImpl*>(SSL_CTX_get_app_data(ssl_ctx)); |
487 | 0 | auto transport_socket_options_shared_ptr_ptr = |
488 | 0 | static_cast<const Network::TransportSocketOptionsConstSharedPtr*>(SSL_get_app_data(ssl)); |
489 | 0 | ASSERT(transport_socket_options_shared_ptr_ptr); |
490 | 0 | ValidationResults result = context_impl->customVerifyCertChain( |
491 | 0 | extended_socket_info, *transport_socket_options_shared_ptr_ptr, ssl); |
492 | 0 | switch (result.status) { |
493 | 0 | case ValidationResults::ValidationStatus::Successful: |
494 | 0 | return ssl_verify_ok; |
495 | 0 | case ValidationResults::ValidationStatus::Pending: |
496 | 0 | return ssl_verify_retry; |
497 | 0 | case ValidationResults::ValidationStatus::Failed: { |
498 | 0 | if (result.tls_alert.has_value() && out_alert) { |
499 | 0 | *out_alert = result.tls_alert.value(); |
500 | 0 | } |
501 | 0 | return ssl_verify_invalid; |
502 | 0 | } |
503 | 0 | } |
504 | 0 | PANIC("not reached"); |
505 | 0 | } |
506 | | |
507 | | ValidationResults ContextImpl::customVerifyCertChain( |
508 | | Envoy::Ssl::SslExtendedSocketInfo* extended_socket_info, |
509 | 0 | const Network::TransportSocketOptionsConstSharedPtr& transport_socket_options, SSL* ssl) { |
510 | 0 | ASSERT(extended_socket_info); |
511 | 0 | STACK_OF(X509)* cert_chain = SSL_get_peer_full_cert_chain(ssl); |
512 | 0 | if (cert_chain == nullptr) { |
513 | 0 | extended_socket_info->setCertificateValidationStatus(Ssl::ClientValidationStatus::NotValidated); |
514 | 0 | stats_.fail_verify_error_.inc(); |
515 | 0 | ENVOY_LOG(debug, "verify cert failed: no cert chain"); |
516 | 0 | return {ValidationResults::ValidationStatus::Failed, Ssl::ClientValidationStatus::NotValidated, |
517 | 0 | SSL_AD_INTERNAL_ERROR, absl::nullopt}; |
518 | 0 | } |
519 | 0 | ASSERT(cert_validator_); |
520 | 0 | const char* host_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); |
521 | |
|
522 | 0 | CertValidator::ExtraValidationContext validation_ctx; |
523 | 0 | validation_ctx.callbacks = |
524 | 0 | static_cast<Network::TransportSocketCallbacks*>(SSL_get_ex_data(ssl, sslSocketIndex())); |
525 | |
|
526 | 0 | ValidationResults result = cert_validator_->doVerifyCertChain( |
527 | 0 | *cert_chain, extended_socket_info->createValidateResultCallback(), transport_socket_options, |
528 | 0 | *SSL_get_SSL_CTX(ssl), validation_ctx, SSL_is_server(ssl), |
529 | 0 | absl::NullSafeStringView(host_name)); |
530 | 0 | if (result.status != ValidationResults::ValidationStatus::Pending) { |
531 | 0 | extended_socket_info->setCertificateValidationStatus(result.detailed_status); |
532 | 0 | extended_socket_info->onCertificateValidationCompleted( |
533 | 0 | result.status == ValidationResults::ValidationStatus::Successful, false); |
534 | 0 | } |
535 | 0 | return result; |
536 | 0 | } |
537 | | |
538 | | void ContextImpl::incCounter(const Stats::StatName name, absl::string_view value, |
539 | 0 | const Stats::StatName fallback) const { |
540 | 0 | const Stats::StatName value_stat_name = stat_name_set_->getBuiltin(value, fallback); |
541 | 0 | ENVOY_BUG(value_stat_name != fallback, |
542 | 0 | absl::StrCat("Unexpected ", scope_.symbolTable().toString(name), " value: ", value)); |
543 | 0 | Stats::Utility::counterFromElements(scope_, {name, value_stat_name}).inc(); |
544 | 0 | } |
545 | | |
546 | 0 | void ContextImpl::logHandshake(SSL* ssl) const { |
547 | 0 | stats_.handshake_.inc(); |
548 | |
|
549 | 0 | if (SSL_session_reused(ssl)) { |
550 | 0 | stats_.session_reused_.inc(); |
551 | 0 | } |
552 | |
|
553 | 0 | incCounter(ssl_ciphers_, SSL_get_cipher_name(ssl), unknown_ssl_cipher_); |
554 | 0 | incCounter(ssl_versions_, SSL_get_version(ssl), unknown_ssl_version_); |
555 | |
|
556 | 0 | const uint16_t curve_id = SSL_get_curve_id(ssl); |
557 | 0 | if (curve_id) { |
558 | 0 | incCounter(ssl_curves_, SSL_get_curve_name(curve_id), unknown_ssl_curve_); |
559 | 0 | } |
560 | |
|
561 | 0 | const uint16_t sigalg_id = SSL_get_peer_signature_algorithm(ssl); |
562 | 0 | if (sigalg_id) { |
563 | 0 | const char* sigalg = SSL_get_signature_algorithm_name(sigalg_id, 1 /* include curve */); |
564 | 0 | incCounter(ssl_sigalgs_, sigalg, unknown_ssl_algorithm_); |
565 | 0 | } |
566 | |
|
567 | 0 | bssl::UniquePtr<X509> cert(SSL_get_peer_certificate(ssl)); |
568 | 0 | if (!cert.get()) { |
569 | 0 | stats_.no_certificate_.inc(); |
570 | 0 | } |
571 | |
|
572 | | #if defined(BORINGSSL_FIPS) && BORINGSSL_API_VERSION >= 18 |
573 | | #error "Delete preprocessor check below; no longer needed" |
574 | | #endif |
575 | |
|
576 | 0 | #if BORINGSSL_API_VERSION >= 18 |
577 | | // Increment the `was_key_usage_invalid_` stats to indicate the given cert would have triggered an |
578 | | // error but is allowed because the enforcement that rsa key usage and tls usage need to be |
579 | | // matched has been disabled. |
580 | 0 | if (SSL_was_key_usage_invalid(ssl)) { |
581 | 0 | stats_.was_key_usage_invalid_.inc(); |
582 | 0 | } |
583 | 0 | #endif // BORINGSSL_API_VERSION |
584 | 0 | } |
585 | | |
586 | 0 | std::vector<Ssl::PrivateKeyMethodProviderSharedPtr> ContextImpl::getPrivateKeyMethodProviders() { |
587 | 0 | std::vector<Envoy::Ssl::PrivateKeyMethodProviderSharedPtr> providers; |
588 | |
|
589 | 0 | for (auto& tls_context : tls_contexts_) { |
590 | 0 | Envoy::Ssl::PrivateKeyMethodProviderSharedPtr provider = |
591 | 0 | tls_context.getPrivateKeyMethodProvider(); |
592 | 0 | if (provider) { |
593 | 0 | providers.push_back(provider); |
594 | 0 | } |
595 | 0 | } |
596 | 0 | return providers; |
597 | 0 | } |
598 | | |
599 | 0 | absl::optional<uint32_t> ContextImpl::daysUntilFirstCertExpires() const { |
600 | 0 | absl::optional<uint32_t> daysUntilExpiration = cert_validator_->daysUntilFirstCertExpires(); |
601 | 0 | if (!daysUntilExpiration.has_value()) { |
602 | 0 | return absl::nullopt; |
603 | 0 | } |
604 | 0 | for (auto& ctx : tls_contexts_) { |
605 | 0 | const absl::optional<uint32_t> tmp = |
606 | 0 | Utility::getDaysUntilExpiration(ctx.cert_chain_.get(), factory_context_.timeSource()); |
607 | 0 | if (!tmp.has_value()) { |
608 | 0 | return absl::nullopt; |
609 | 0 | } |
610 | 0 | daysUntilExpiration = std::min<uint32_t>(tmp.value(), daysUntilExpiration.value()); |
611 | 0 | } |
612 | 0 | return daysUntilExpiration; |
613 | 0 | } |
614 | | |
615 | 0 | absl::optional<uint64_t> ContextImpl::secondsUntilFirstOcspResponseExpires() const { |
616 | 0 | absl::optional<uint64_t> secs_until_expiration; |
617 | 0 | for (auto& ctx : tls_contexts_) { |
618 | 0 | if (ctx.ocsp_response_) { |
619 | 0 | uint64_t next_expiration = ctx.ocsp_response_->secondsUntilExpiration(); |
620 | 0 | secs_until_expiration = std::min<uint64_t>( |
621 | 0 | next_expiration, secs_until_expiration.value_or(std::numeric_limits<uint64_t>::max())); |
622 | 0 | } |
623 | 0 | } |
624 | |
|
625 | 0 | return secs_until_expiration; |
626 | 0 | } |
627 | | |
628 | 0 | Envoy::Ssl::CertificateDetailsPtr ContextImpl::getCaCertInformation() const { |
629 | 0 | return cert_validator_->getCaCertInformation(); |
630 | 0 | } |
631 | | |
632 | 0 | std::vector<Envoy::Ssl::CertificateDetailsPtr> ContextImpl::getCertChainInformation() const { |
633 | 0 | std::vector<Envoy::Ssl::CertificateDetailsPtr> cert_details; |
634 | 0 | for (const auto& ctx : tls_contexts_) { |
635 | 0 | if (ctx.cert_chain_ == nullptr) { |
636 | 0 | continue; |
637 | 0 | } |
638 | | |
639 | 0 | auto detail = Utility::certificateDetails(ctx.cert_chain_.get(), ctx.getCertChainFileName(), |
640 | 0 | factory_context_.timeSource()); |
641 | 0 | auto ocsp_resp = ctx.ocsp_response_.get(); |
642 | 0 | if (ocsp_resp) { |
643 | 0 | auto* ocsp_details = detail->mutable_ocsp_details(); |
644 | 0 | ProtobufWkt::Timestamp* valid_from = ocsp_details->mutable_valid_from(); |
645 | 0 | TimestampUtil::systemClockToTimestamp(ocsp_resp->getThisUpdate(), *valid_from); |
646 | 0 | ProtobufWkt::Timestamp* expiration = ocsp_details->mutable_expiration(); |
647 | 0 | TimestampUtil::systemClockToTimestamp(ocsp_resp->getNextUpdate(), *expiration); |
648 | 0 | } |
649 | 0 | cert_details.push_back(std::move(detail)); |
650 | 0 | } |
651 | 0 | return cert_details; |
652 | 0 | } |
653 | | |
654 | | bool ContextImpl::parseAndSetAlpn(const std::vector<std::string>& alpn, SSL& ssl, |
655 | 0 | absl::Status& parse_status) { |
656 | 0 | std::vector<uint8_t> parsed_alpn = parseAlpnProtocols(absl::StrJoin(alpn, ","), parse_status); |
657 | 0 | if (!parse_status.ok()) { |
658 | 0 | return false; |
659 | 0 | } |
660 | 0 | if (!parsed_alpn.empty()) { |
661 | 0 | const int rc = SSL_set_alpn_protos(&ssl, parsed_alpn.data(), parsed_alpn.size()); |
662 | | // This should only if memory allocation fails, e.g. OOM. |
663 | 0 | RELEASE_ASSERT(rc == 0, Utility::getLastCryptoError().value_or("")); |
664 | 0 | return true; |
665 | 0 | } |
666 | | |
667 | 0 | return false; |
668 | 0 | } |
669 | | |
670 | | ValidationResults ContextImpl::customVerifyCertChainForQuic( |
671 | | STACK_OF(X509)& cert_chain, Ssl::ValidateResultCallbackPtr callback, bool is_server, |
672 | | const Network::TransportSocketOptionsConstSharedPtr& transport_socket_options, |
673 | 0 | const CertValidator::ExtraValidationContext& validation_context, const std::string& host_name) { |
674 | 0 | ASSERT(!tls_contexts_.empty()); |
675 | | // It doesn't matter which SSL context is used, because they share the same cert validation |
676 | | // config. |
677 | 0 | SSL_CTX* ssl_ctx = tls_contexts_[0].ssl_ctx_.get(); |
678 | 0 | if (SSL_CTX_get_verify_mode(ssl_ctx) == SSL_VERIFY_NONE) { |
679 | | // Skip validation if the TLS is configured SSL_VERIFY_NONE. |
680 | 0 | return {ValidationResults::ValidationStatus::Successful, |
681 | 0 | Envoy::Ssl::ClientValidationStatus::NotValidated, absl::nullopt, absl::nullopt}; |
682 | 0 | } |
683 | 0 | ValidationResults result = |
684 | 0 | cert_validator_->doVerifyCertChain(cert_chain, std::move(callback), transport_socket_options, |
685 | 0 | *ssl_ctx, validation_context, is_server, host_name); |
686 | 0 | return result; |
687 | 0 | } |
688 | | |
689 | | } // namespace Tls |
690 | | } // namespace TransportSockets |
691 | | } // namespace Extensions |
692 | | |
693 | | namespace Ssl { |
694 | | |
695 | 0 | bool TlsContext::isCipherEnabled(uint16_t cipher_id, uint16_t client_version) const { |
696 | 0 | const SSL_CIPHER* c = SSL_get_cipher_by_value(cipher_id); |
697 | 0 | if (c == nullptr) { |
698 | 0 | return false; |
699 | 0 | } |
700 | | // Skip TLS 1.2 only ciphersuites unless the client supports it. |
701 | 0 | if (SSL_CIPHER_get_min_version(c) > client_version) { |
702 | 0 | return false; |
703 | 0 | } |
704 | 0 | if (SSL_CIPHER_get_auth_nid(c) != NID_auth_ecdsa) { |
705 | 0 | return false; |
706 | 0 | } |
707 | 0 | for (const SSL_CIPHER* our_c : SSL_CTX_get_ciphers(ssl_ctx_.get())) { |
708 | 0 | if (SSL_CIPHER_get_id(our_c) == SSL_CIPHER_get_id(c)) { |
709 | 0 | return true; |
710 | 0 | } |
711 | 0 | } |
712 | 0 | return false; |
713 | 0 | } |
714 | | |
715 | | absl::Status TlsContext::loadCertificateChain(const std::string& data, |
716 | 0 | const std::string& data_path) { |
717 | 0 | cert_chain_file_path_ = data_path; |
718 | 0 | bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(const_cast<char*>(data.data()), data.size())); |
719 | 0 | RELEASE_ASSERT(bio != nullptr, ""); |
720 | 0 | cert_chain_.reset(PEM_read_bio_X509_AUX(bio.get(), nullptr, nullptr, nullptr)); |
721 | 0 | if (cert_chain_ == nullptr || !SSL_CTX_use_certificate(ssl_ctx_.get(), cert_chain_.get())) { |
722 | 0 | logSslErrorChain(); |
723 | 0 | return absl::InvalidArgumentError( |
724 | 0 | absl::StrCat("Failed to load certificate chain from ", cert_chain_file_path_)); |
725 | 0 | } |
726 | | // Read rest of the certificate chain. |
727 | 0 | while (true) { |
728 | 0 | bssl::UniquePtr<X509> cert(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr)); |
729 | 0 | if (cert == nullptr) { |
730 | 0 | break; |
731 | 0 | } |
732 | 0 | if (!SSL_CTX_add_extra_chain_cert(ssl_ctx_.get(), cert.get())) { |
733 | 0 | return absl::InvalidArgumentError( |
734 | 0 | absl::StrCat("Failed to load certificate chain from ", cert_chain_file_path_)); |
735 | 0 | } |
736 | | // SSL_CTX_add_extra_chain_cert() takes ownership. |
737 | 0 | cert.release(); |
738 | 0 | } |
739 | | // Check for EOF. |
740 | 0 | const uint32_t err = ERR_peek_last_error(); |
741 | 0 | if (ERR_GET_LIB(err) == ERR_LIB_PEM && ERR_GET_REASON(err) == PEM_R_NO_START_LINE) { |
742 | 0 | ERR_clear_error(); |
743 | 0 | } else { |
744 | 0 | return absl::InvalidArgumentError( |
745 | 0 | absl::StrCat("Failed to load certificate chain from ", cert_chain_file_path_)); |
746 | 0 | } |
747 | 0 | return absl::OkStatus(); |
748 | 0 | } |
749 | | |
750 | | absl::Status TlsContext::loadPrivateKey(const std::string& data, const std::string& data_path, |
751 | 0 | const std::string& password) { |
752 | 0 | bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(const_cast<char*>(data.data()), data.size())); |
753 | 0 | RELEASE_ASSERT(bio != nullptr, ""); |
754 | 0 | bssl::UniquePtr<EVP_PKEY> pkey( |
755 | 0 | PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, |
756 | 0 | !password.empty() ? const_cast<char*>(password.c_str()) : nullptr)); |
757 | |
|
758 | 0 | if (pkey == nullptr || !SSL_CTX_use_PrivateKey(ssl_ctx_.get(), pkey.get())) { |
759 | 0 | return absl::InvalidArgumentError(fmt::format( |
760 | 0 | "Failed to load private key from {}, Cause: {}", data_path, |
761 | 0 | Extensions::TransportSockets::Tls::Utility::getLastCryptoError().value_or("unknown"))); |
762 | 0 | } |
763 | | |
764 | 0 | return checkPrivateKey(pkey, data_path); |
765 | 0 | } |
766 | | |
767 | | absl::Status TlsContext::loadPkcs12(const std::string& data, const std::string& data_path, |
768 | 0 | const std::string& password) { |
769 | 0 | cert_chain_file_path_ = data_path; |
770 | 0 | bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(const_cast<char*>(data.data()), data.size())); |
771 | 0 | RELEASE_ASSERT(bio != nullptr, ""); |
772 | 0 | bssl::UniquePtr<PKCS12> pkcs12(d2i_PKCS12_bio(bio.get(), nullptr)); |
773 | |
|
774 | 0 | EVP_PKEY* temp_private_key = nullptr; |
775 | 0 | X509* temp_cert = nullptr; |
776 | 0 | STACK_OF(X509)* temp_ca_certs = nullptr; |
777 | 0 | if (pkcs12 == nullptr || |
778 | 0 | !PKCS12_parse(pkcs12.get(), !password.empty() ? const_cast<char*>(password.c_str()) : nullptr, |
779 | 0 | &temp_private_key, &temp_cert, &temp_ca_certs)) { |
780 | 0 | logSslErrorChain(); |
781 | 0 | return absl::InvalidArgumentError(absl::StrCat("Failed to load pkcs12 from ", data_path)); |
782 | 0 | } |
783 | 0 | cert_chain_.reset(temp_cert); |
784 | 0 | bssl::UniquePtr<EVP_PKEY> pkey(temp_private_key); |
785 | 0 | bssl::UniquePtr<STACK_OF(X509)> ca_certificates(temp_ca_certs); |
786 | 0 | if (ca_certificates != nullptr) { |
787 | 0 | X509* ca_cert = nullptr; |
788 | 0 | while ((ca_cert = sk_X509_pop(ca_certificates.get())) != nullptr) { |
789 | | // This transfers ownership to ssl_ctx therefore ca_cert does not need to be freed. |
790 | 0 | SSL_CTX_add_extra_chain_cert(ssl_ctx_.get(), ca_cert); |
791 | 0 | } |
792 | 0 | } |
793 | 0 | if (!SSL_CTX_use_certificate(ssl_ctx_.get(), cert_chain_.get())) { |
794 | 0 | logSslErrorChain(); |
795 | 0 | return absl::InvalidArgumentError(absl::StrCat("Failed to load certificate from ", data_path)); |
796 | 0 | } |
797 | 0 | if (temp_private_key == nullptr || !SSL_CTX_use_PrivateKey(ssl_ctx_.get(), pkey.get())) { |
798 | 0 | return absl::InvalidArgumentError(fmt::format( |
799 | 0 | "Failed to load private key from {}, Cause: {}", data_path, |
800 | 0 | Extensions::TransportSockets::Tls::Utility::getLastCryptoError().value_or("unknown"))); |
801 | 0 | } |
802 | | |
803 | 0 | return checkPrivateKey(pkey, data_path); |
804 | 0 | } |
805 | | |
806 | | absl::Status TlsContext::checkPrivateKey(const bssl::UniquePtr<EVP_PKEY>& pkey, |
807 | 0 | const std::string& key_path) { |
808 | | #ifdef BORINGSSL_FIPS |
809 | | // Verify that private keys are passing FIPS pairwise consistency tests. |
810 | | switch (EVP_PKEY_id(pkey.get())) { |
811 | | case EVP_PKEY_EC: { |
812 | | const EC_KEY* ecdsa_private_key = EVP_PKEY_get0_EC_KEY(pkey.get()); |
813 | | if (!EC_KEY_check_fips(ecdsa_private_key)) { |
814 | | return absl::InvalidArgumentError( |
815 | | fmt::format("Failed to load private key from {}, ECDSA key failed " |
816 | | "pairwise consistency test required in FIPS mode", |
817 | | key_path)); |
818 | | } |
819 | | } break; |
820 | | case EVP_PKEY_RSA: { |
821 | | RSA* rsa_private_key = EVP_PKEY_get0_RSA(pkey.get()); |
822 | | if (!RSA_check_fips(rsa_private_key)) { |
823 | | return absl::InvalidArgumentError( |
824 | | fmt::format("Failed to load private key from {}, RSA key failed " |
825 | | "pairwise consistency test required in FIPS mode", |
826 | | key_path)); |
827 | | } |
828 | | } break; |
829 | | } |
830 | | #else |
831 | 0 | UNREFERENCED_PARAMETER(pkey); |
832 | 0 | UNREFERENCED_PARAMETER(key_path); |
833 | 0 | #endif |
834 | 0 | return absl::OkStatus(); |
835 | 0 | } |
836 | | |
837 | | } // namespace Ssl |
838 | | } // namespace Envoy |