From 95065c318575e45925bdab85e3211b1da0fe8c0a Mon Sep 17 00:00:00 2001 From: James M Snell Date: Mon, 19 Dec 2022 11:46:48 -0800 Subject: [PATCH] src: add additional utilities to crypto::SecureContext In preparation for use by the QUIC implementation. PR-URL: https://github.com/nodejs/node/pull/45912 Reviewed-By: Matteo Collina Reviewed-By: Ben Noordhuis Reviewed-By: Yagiz Nizipli Reviewed-By: Rafael Gonzaga Reviewed-By: Minwoo Jung Reviewed-By: Robert Nagy Reviewed-By: Antoine du Hamel --- src/crypto/crypto_context.cc | 130 ++++++++++++++++++++++------------- src/crypto/crypto_context.h | 17 ++++- 2 files changed, 98 insertions(+), 49 deletions(-) diff --git a/src/crypto/crypto_context.cc b/src/crypto/crypto_context.cc index 1c02d2e9f56d56..a232e084ea34e5 100644 --- a/src/crypto/crypto_context.cc +++ b/src/crypto/crypto_context.cc @@ -32,7 +32,10 @@ using v8::HandleScope; using v8::Int32; using v8::Integer; using v8::Isolate; +using v8::Just; using v8::Local; +using v8::Maybe; +using v8::Nothing; using v8::Object; using v8::PropertyAttribute; using v8::ReadOnly; @@ -574,6 +577,22 @@ void SecureContext::SetKeylogCallback(KeylogCb cb) { SSL_CTX_set_keylog_callback(ctx_.get(), cb); } +Maybe SecureContext::UseKey(Environment* env, + std::shared_ptr key) { + if (key->GetKeyType() != KeyType::kKeyTypePrivate) { + THROW_ERR_CRYPTO_INVALID_KEYTYPE(env); + return Nothing(); + } + + ClearErrorOnReturn clear_error_on_return; + if (!SSL_CTX_use_PrivateKey(ctx_.get(), key->GetAsymmetricKey().get())) { + ThrowCryptoError(env, ERR_get_error(), "SSL_CTX_use_PrivateKey"); + return Nothing(); + } + + return Just(true); +} + void SecureContext::SetKey(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); @@ -662,97 +681,112 @@ void SecureContext::SetEngineKey(const FunctionCallbackInfo& args) { } #endif // !OPENSSL_NO_ENGINE -void SecureContext::SetCert(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - SecureContext* sc; - ASSIGN_OR_RETURN_UNWRAP(&sc, args.Holder()); - - CHECK_GE(args.Length(), 1); // Certificate argument is mandator - - BIOPointer bio(LoadBIO(env, args[0])); - if (!bio) - return; - - sc->cert_.reset(); - sc->issuer_.reset(); +Maybe SecureContext::AddCert(Environment* env, BIOPointer&& bio) { + ClearErrorOnReturn clear_error_on_return; + if (!bio) return Just(false); + cert_.reset(); + issuer_.reset(); - if (!SSL_CTX_use_certificate_chain( - sc->ctx_.get(), - std::move(bio), - &sc->cert_, - &sc->issuer_)) { - return ThrowCryptoError( - env, - ERR_get_error(), - "SSL_CTX_use_certificate_chain"); + // The SSL_CTX_use_certificate_chain call here is not from openssl, this is + // the method implemented elsewhere in this file. The naming is a bit + // confusing, unfortunately. + if (SSL_CTX_use_certificate_chain( + ctx_.get(), std::move(bio), &cert_, &issuer_) == 0) { + ThrowCryptoError(env, ERR_get_error(), "SSL_CTX_use_certificate_chain"); + return Nothing(); } + return Just(true); } -void SecureContext::AddCACert(const FunctionCallbackInfo& args) { +void SecureContext::SetCert(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); SecureContext* sc; ASSIGN_OR_RETURN_UNWRAP(&sc, args.Holder()); - ClearErrorOnReturn clear_error_on_return; - CHECK_GE(args.Length(), 1); // CA certificate argument is mandatory + CHECK_GE(args.Length(), 1); // Certificate argument is mandatory BIOPointer bio(LoadBIO(env, args[0])); - if (!bio) - return; + USE(sc->AddCert(env, std::move(bio))); +} - X509_STORE* cert_store = SSL_CTX_get_cert_store(sc->ctx_.get()); +void SecureContext::SetCACert(const BIOPointer& bio) { + ClearErrorOnReturn clear_error_on_return; + if (!bio) return; + X509_STORE* cert_store = SSL_CTX_get_cert_store(ctx_.get()); while (X509Pointer x509 = X509Pointer(PEM_read_bio_X509_AUX( bio.get(), nullptr, NoPasswordCallback, nullptr))) { if (cert_store == GetOrCreateRootCertStore()) { cert_store = NewRootCertStore(); - SSL_CTX_set_cert_store(sc->ctx_.get(), cert_store); + SSL_CTX_set_cert_store(ctx_.get(), cert_store); } - X509_STORE_add_cert(cert_store, x509.get()); - SSL_CTX_add_client_CA(sc->ctx_.get(), x509.get()); + CHECK_EQ(1, X509_STORE_add_cert(cert_store, x509.get())); + CHECK_EQ(1, SSL_CTX_add_client_CA(ctx_.get(), x509.get())); } } -void SecureContext::AddCRL(const FunctionCallbackInfo& args) { +void SecureContext::AddCACert(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); SecureContext* sc; ASSIGN_OR_RETURN_UNWRAP(&sc, args.Holder()); - CHECK_GE(args.Length(), 1); // CRL argument is mandatory - - ClearErrorOnReturn clear_error_on_return; + CHECK_GE(args.Length(), 1); // CA certificate argument is mandatory BIOPointer bio(LoadBIO(env, args[0])); - if (!bio) - return; + sc->SetCACert(bio); +} + +Maybe SecureContext::SetCRL(Environment* env, const BIOPointer& bio) { + ClearErrorOnReturn clear_error_on_return; + if (!bio) return Just(false); DeleteFnPtr crl( PEM_read_bio_X509_CRL(bio.get(), nullptr, NoPasswordCallback, nullptr)); - if (!crl) - return THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to parse CRL"); + if (!crl) { + THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to parse CRL"); + return Nothing(); + } - X509_STORE* cert_store = SSL_CTX_get_cert_store(sc->ctx_.get()); + X509_STORE* cert_store = SSL_CTX_get_cert_store(ctx_.get()); if (cert_store == GetOrCreateRootCertStore()) { cert_store = NewRootCertStore(); - SSL_CTX_set_cert_store(sc->ctx_.get(), cert_store); + SSL_CTX_set_cert_store(ctx_.get(), cert_store); } - X509_STORE_add_crl(cert_store, crl.get()); - X509_STORE_set_flags(cert_store, - X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); + CHECK_EQ(1, X509_STORE_add_crl(cert_store, crl.get())); + CHECK_EQ(1, + X509_STORE_set_flags( + cert_store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL)); + return Just(true); } -void SecureContext::AddRootCerts(const FunctionCallbackInfo& args) { +void SecureContext::AddCRL(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + SecureContext* sc; ASSIGN_OR_RETURN_UNWRAP(&sc, args.Holder()); + + CHECK_GE(args.Length(), 1); // CRL argument is mandatory + + BIOPointer bio(LoadBIO(env, args[0])); + USE(sc->SetCRL(env, bio)); +} + +void SecureContext::SetRootCerts() { ClearErrorOnReturn clear_error_on_return; - X509_STORE* store = GetOrCreateRootCertStore(); + auto store = GetOrCreateRootCertStore(); + // Increment reference count so global store is not deleted along with CTX. X509_STORE_up_ref(store); - SSL_CTX_set_cert_store(sc->ctx_.get(), store); + SSL_CTX_set_cert_store(ctx_.get(), store); +} + +void SecureContext::AddRootCerts(const FunctionCallbackInfo& args) { + SecureContext* sc; + ASSIGN_OR_RETURN_UNWRAP(&sc, args.Holder()); + sc->SetRootCerts(); } void SecureContext::SetCipherSuites(const FunctionCallbackInfo& args) { diff --git a/src/crypto/crypto_context.h b/src/crypto/crypto_context.h index 4dfd0dfa032cf7..607b0984ba647a 100644 --- a/src/crypto/crypto_context.h +++ b/src/crypto/crypto_context.h @@ -3,8 +3,9 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS -#include "crypto/crypto_util.h" #include "base_object.h" +#include "crypto/crypto_keys.h" +#include "crypto/crypto_util.h" #include "env.h" #include "memory_tracker.h" #include "v8.h" @@ -43,6 +44,10 @@ class SecureContext final : public BaseObject { const SSLCtxPointer& ctx() const { return ctx_; } + // Non-const ctx() that allows for non-default initialization of + // the SecureContext. + SSLCtxPointer& ctx() { return ctx_; } + SSLPointer CreateSSL(); void SetGetSessionCallback(GetSessionCb cb); @@ -50,6 +55,16 @@ class SecureContext final : public BaseObject { void SetNewSessionCallback(NewSessionCb cb); void SetSelectSNIContextCallback(SelectSNIContextCb cb); + inline const X509Pointer& issuer() const { return issuer_; } + inline const X509Pointer& cert() const { return cert_; } + + v8::Maybe AddCert(Environment* env, BIOPointer&& bio); + v8::Maybe SetCRL(Environment* env, const BIOPointer& bio); + v8::Maybe UseKey(Environment* env, std::shared_ptr key); + + void SetCACert(const BIOPointer& bio); + void SetRootCerts(); + // TODO(joyeecheung): track the memory used by OpenSSL types SET_NO_MEMORY_INFO() SET_MEMORY_INFO_NAME(SecureContext)