Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@
'src/crypto/crypto_timing.cc',
'src/crypto/crypto_cipher.cc',
'src/crypto/crypto_context.cc',
'src/crypto/crypto_tls_certificates.cc',
'src/crypto/crypto_ec.cc',
'src/crypto/crypto_pqc.cc',
'src/crypto/crypto_kem.cc',
Expand Down Expand Up @@ -449,6 +450,7 @@
'src/crypto/crypto_tls.h',
'src/crypto/crypto_clienthello.h',
'src/crypto/crypto_context.h',
'src/crypto/crypto_tls_certificates.h',
'src/crypto/crypto_ec.h',
'src/crypto/crypto_pqc.h',
'src/crypto/crypto_hkdf.h',
Expand Down
62 changes: 13 additions & 49 deletions src/crypto/crypto_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "base_object-inl.h"
#include "crypto/crypto_bio.h"
#include "crypto/crypto_common.h"
#include "crypto/crypto_tls_certificates.h"
#include "crypto/crypto_util.h"
#include "env-inl.h"
#include "memory_tracker-inl.h"
Expand Down Expand Up @@ -1731,22 +1732,14 @@ void SecureContext::SetKey(const FunctionCallbackInfo<Value>& args) {
ByteSource passphrase;
if (args[1]->IsString())
passphrase = ByteSource::FromString(env, args[1].As<String>());
// This redirection is necessary because the PasswordCallback expects a
// pointer to a pointer to the passphrase ByteSource to allow passing in
// const ByteSources.
const ByteSource* pass_ptr = &passphrase;

EVPKeyPointer key(
PEM_read_bio_PrivateKey(bio.get(),
nullptr,
PasswordCallback,
&pass_ptr));

if (!key)
return ThrowCryptoError(env, ERR_get_error(), "PEM_read_bio_PrivateKey");

if (!SSL_CTX_use_PrivateKey(sc->ctx_.get(), key.get()))
return ThrowCryptoError(env, ERR_get_error(), "SSL_CTX_use_PrivateKey");
switch (UsePrivateKey(sc->ctx_.get(), bio, &passphrase)) {
case PrivateKeyResult::kSuccess:
break;
case PrivateKeyResult::kParseError:
return ThrowCryptoError(env, ERR_get_error(), "PEM_read_bio_PrivateKey");
case PrivateKeyResult::kApplyError:
return ThrowCryptoError(env, ERR_get_error(), "SSL_CTX_use_PrivateKey");
}
}

void SecureContext::SetSigalgs(const FunctionCallbackInfo<Value>& args) {
Expand Down Expand Up @@ -1849,16 +1842,7 @@ void SecureContext::SetX509StoreFlag(unsigned long flags) {
}

X509_STORE* SecureContext::GetCertStoreOwnedByThisSecureContext() {
Environment* env = this->env();
if (own_cert_store_cache_ != nullptr) return own_cert_store_cache_;

X509_STORE* cert_store = SSL_CTX_get_cert_store(ctx_.get());
if (cert_store == GetOrCreateRootCertStore(env)) {
cert_store = NewRootCertStore(env);
SSL_CTX_set_cert_store(ctx_.get(), cert_store);
}

return own_cert_store_cache_ = cert_store;
return GetOrCreateOwnedCertStore(env(), ctx_.get(), &own_cert_store_cache_);
}

void SecureContext::SetAllowPartialTrustChain(
Expand All @@ -1870,13 +1854,7 @@ void SecureContext::SetAllowPartialTrustChain(

void SecureContext::SetCACert(const BIOPointer& bio) {
ClearErrorOnReturn clear_error_on_return;
if (!bio) return;
while (X509Pointer x509 = X509Pointer(PEM_read_bio_X509_AUX(
bio.get(), nullptr, NoPasswordCallback, nullptr))) {
CHECK_EQ(1,
X509_STORE_add_cert(GetCertStoreOwnedByThisSecureContext(), x509));
CHECK_EQ(1, SSL_CTX_add_client_CA(ctx_.get(), x509));
}
AddCACertificates(env(), ctx_.get(), bio, &own_cert_store_cache_);
}

void SecureContext::AddCACert(const FunctionCallbackInfo<Value>& args) {
Expand All @@ -1896,20 +1874,11 @@ Maybe<void> SecureContext::SetCRL(Environment* env, const BIOPointer& bio) {
// TODO(tniessen): this should be checked by the caller and not treated as ok
if (!bio) return JustVoid();

DeleteFnPtr<X509_CRL, X509_CRL_free> crl(
PEM_read_bio_X509_CRL(bio.get(), nullptr, NoPasswordCallback, nullptr));

if (!crl) {
if (!crypto::AddCRL(env, ctx_.get(), bio, &own_cert_store_cache_)) {
THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to parse CRL");
return Nothing<void>();
}

X509_STORE* cert_store = GetCertStoreOwnedByThisSecureContext();

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 JustVoid();
}

Expand All @@ -1927,12 +1896,7 @@ void SecureContext::AddCRL(const FunctionCallbackInfo<Value>& args) {

void SecureContext::SetRootCerts() {
ClearErrorOnReturn clear_error_on_return;
Environment* env = this->env();
auto store = GetOrCreateRootCertStore(env);

// Increment reference count so global store is not deleted along with CTX.
X509_STORE_up_ref(store);
SSL_CTX_set_cert_store(ctx_.get(), store);
UseDefaultRootCertStore(env(), ctx_.get());
}

void SecureContext::AddRootCerts(const FunctionCallbackInfo<Value>& args) {
Expand Down
5 changes: 0 additions & 5 deletions src/crypto/crypto_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,11 +201,6 @@ class SecureContext final : public BaseObject {
#endif
};

int SSL_CTX_use_certificate_chain(SSL_CTX* ctx,
ncrypto::BIOPointer&& in,
ncrypto::X509Pointer* cert,
ncrypto::X509Pointer* issuer);

} // namespace crypto
} // namespace node

Expand Down
96 changes: 96 additions & 0 deletions src/crypto/crypto_tls_certificates.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#include "crypto/crypto_tls_certificates.h"

#include "crypto/crypto_context.h"
#include "crypto/crypto_keys.h"
#include "crypto/crypto_util.h"

namespace node::crypto {

using ncrypto::DeleteFnPtr;
using ncrypto::EVPKeyPointer;
using ncrypto::X509Pointer;

X509_STORE* GetOrCreateOwnedCertStore(Environment* env,
SSL_CTX* ctx,
X509_STORE** cache) {
if (cache != nullptr && *cache != nullptr) return *cache;

X509_STORE* cert_store = SSL_CTX_get_cert_store(ctx);
if (cert_store == GetOrCreateRootCertStore(env)) {
cert_store = NewRootCertStore(env);
SSL_CTX_set_cert_store(ctx, cert_store);
}

if (cache != nullptr) *cache = cert_store;
return cert_store;
}

void UseDefaultRootCertStore(Environment* env, SSL_CTX* ctx) {
X509_STORE* store = GetOrCreateRootCertStore(env);

// Increment reference count so global store is not deleted along with CTX.
X509_STORE_up_ref(store);
SSL_CTX_set_cert_store(ctx, store);
}

size_t AddCACertificates(Environment* env,
SSL_CTX* ctx,
const ncrypto::BIOPointer& bio,
X509_STORE** cache) {
if (!bio) return 0;

size_t count = 0;
while (X509Pointer x509 = X509Pointer(PEM_read_bio_X509_AUX(
bio.get(), nullptr, NoPasswordCallback, nullptr))) {
CHECK_EQ(1,
X509_STORE_add_cert(GetOrCreateOwnedCertStore(env, ctx, cache),
x509.get()));
CHECK_EQ(1, SSL_CTX_add_client_CA(ctx, x509.get()));
count++;
}
return count;
}

bool AddCRL(Environment* env,
SSL_CTX* ctx,
const ncrypto::BIOPointer& bio,
X509_STORE** cache) {
if (!bio) return false;

DeleteFnPtr<X509_CRL, X509_CRL_free> crl(
PEM_read_bio_X509_CRL(bio.get(), nullptr, NoPasswordCallback, nullptr));
if (!crl) return false;

X509_STORE* cert_store = GetOrCreateOwnedCertStore(env, ctx, cache);
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 true;
}

PrivateKeyResult UsePrivateKey(SSL_CTX* ctx,
const ncrypto::BIOPointer& bio,
const ByteSource* passphrase) {
if (!bio) return PrivateKeyResult::kParseError;

ByteSource empty_passphrase;
if (passphrase == nullptr) passphrase = &empty_passphrase;
// This redirection is necessary because the PasswordCallback expects a
// pointer to a pointer to the passphrase ByteSource to allow passing in
// const ByteSources.
const ByteSource* pass_ptr = passphrase;
EVPKeyPointer key(
PEM_read_bio_PrivateKey(bio.get(), nullptr, PasswordCallback, &pass_ptr));
if (!key) return PrivateKeyResult::kParseError;
if (!SSL_CTX_use_PrivateKey(ctx, key.get()))
return PrivateKeyResult::kApplyError;
return PrivateKeyResult::kSuccess;
}

bool UsePrivateKey(SSL_CTX* ctx, const KeyObjectData& key) {
if (key.GetKeyType() != KeyType::kKeyTypePrivate) return false;
return SSL_CTX_use_PrivateKey(ctx, key.GetAsymmetricKey().get());
}

} // namespace node::crypto
60 changes: 60 additions & 0 deletions src/crypto/crypto_tls_certificates.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#ifndef SRC_CRYPTO_CRYPTO_TLS_CERTIFICATES_H_
#define SRC_CRYPTO_CRYPTO_TLS_CERTIFICATES_H_

#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

#include "ncrypto.h"

namespace node {

class Environment;

namespace crypto {

class ByteSource;
class KeyObjectData;

// Return a store that may be modified by this SSL_CTX. If the context uses
// Node's shared root store, replace it with a private copy first.
X509_STORE* GetOrCreateOwnedCertStore(Environment* env,
SSL_CTX* ctx,
X509_STORE** cache = nullptr);

// Attach Node's process-wide default root store to the SSL_CTX.
void UseDefaultRootCertStore(Environment* env, SSL_CTX* ctx);

// Add every PEM certificate in |bio| to the context's certificate store and
// acceptable-client-CA list. Returns the number of certificates read.
size_t AddCACertificates(Environment* env,
SSL_CTX* ctx,
const ncrypto::BIOPointer& bio,
X509_STORE** cache = nullptr);

// Add one PEM CRL and enable CRL checking.
bool AddCRL(Environment* env,
SSL_CTX* ctx,
const ncrypto::BIOPointer& bio,
X509_STORE** cache = nullptr);

enum class PrivateKeyResult {
kSuccess,
kParseError,
kApplyError,
};

PrivateKeyResult UsePrivateKey(SSL_CTX* ctx,
const ncrypto::BIOPointer& bio,
const ByteSource* passphrase = nullptr);

bool UsePrivateKey(SSL_CTX* ctx, const KeyObjectData& key);

int SSL_CTX_use_certificate_chain(SSL_CTX* ctx,
ncrypto::BIOPointer&& in,
ncrypto::X509Pointer* cert,
ncrypto::X509Pointer* issuer);

} // namespace crypto
} // namespace node

#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
#endif // SRC_CRYPTO_CRYPTO_TLS_CERTIFICATES_H_
Loading
Loading