◐ Shell
clean mode source ↗

crypto: support --use-system-ca on Windows · nodejs/node@3e207bd

@@ -22,6 +22,11 @@

2222

#include <Security/Security.h>

2323

#endif

242425+

#ifdef _WIN32

26+

#include <Windows.h>

27+

#include <wincrypt.h>

28+

#endif

29+2530

namespace node {

26312732

using ncrypto::BignumPointer;

@@ -285,13 +290,15 @@ void X509VectorToPEMVector(const std::vector<X509Pointer>& src,

285290

}

286291

}

287292288-

#ifdef __APPLE__

289-

// This code is loosely based on

293+

// The following code is loosely based on

290294

// https://github.com/chromium/chromium/blob/54bd8e3/net/cert/internal/trust_store_mac.cc

295+

// and

296+

// https://github.com/chromium/chromium/blob/0192587/net/cert/internal/trust_store_win.cc

291297

// Copyright 2015 The Chromium Authors

292298

// Licensed under a BSD-style license

293299

// See https://chromium.googlesource.com/chromium/src/+/HEAD/LICENSE for

294300

// details.

301+

#ifdef __APPLE__

295302

TrustStatus IsTrustDictionaryTrustedForPolicy(CFDictionaryRef trust_dict,

296303

bool is_self_issued) {

297304

// Trust settings may be scoped to a single application

@@ -524,11 +531,160 @@ void ReadMacOSKeychainCertificates(

524531

}

525532

#endif // __APPLE__

526533534+

#ifdef _WIN32

535+536+

// Returns true if the cert can be used for server authentication, based on

537+

// certificate properties.

538+

//

539+

// While there are a variety of certificate properties that can affect how

540+

// trust is computed, the main property is CERT_ENHKEY_USAGE_PROP_ID, which

541+

// is intersected with the certificate's EKU extension (if present).

542+

// The intersection is documented in the Remarks section of

543+

// CertGetEnhancedKeyUsage, and is as follows:

544+

// - No EKU property, and no EKU extension = Trusted for all purpose

545+

// - Either an EKU property, or EKU extension, but not both = Trusted only

546+

// for the listed purposes

547+

// - Both an EKU property and an EKU extension = Trusted for the set

548+

// intersection of the listed purposes

549+

// CertGetEnhancedKeyUsage handles this logic, and if an empty set is

550+

// returned, the distinction between the first and third case can be

551+

// determined by GetLastError() returning CRYPT_E_NOT_FOUND.

552+

//

553+

// See:

554+

// https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certgetenhancedkeyusage

555+

//

556+

// If we run into any errors reading the certificate properties, we fail

557+

// closed.

558+

bool IsCertTrustedForServerAuth(PCCERT_CONTEXT cert) {

559+

DWORD usage_size = 0;

560+561+

if (!CertGetEnhancedKeyUsage(cert, 0, nullptr, &usage_size)) {

562+

return false;

563+

}

564+565+

std::vector<BYTE> usage_bytes(usage_size);

566+

CERT_ENHKEY_USAGE* usage =

567+

reinterpret_cast<CERT_ENHKEY_USAGE*>(usage_bytes.data());

568+

if (!CertGetEnhancedKeyUsage(cert, 0, usage, &usage_size)) {

569+

return false;

570+

}

571+572+

if (usage->cUsageIdentifier == 0) {

573+

// check GetLastError

574+

HRESULT error_code = GetLastError();

575+576+

switch (error_code) {

577+

case CRYPT_E_NOT_FOUND:

578+

return true;

579+

case S_OK:

580+

return false;

581+

default:

582+

return false;

583+

}

584+

}

585+586+

// SAFETY: `usage->rgpszUsageIdentifier` is an array of LPSTR (pointer to null

587+

// terminated string) of length `usage->cUsageIdentifier`.

588+

for (DWORD i = 0; i < usage->cUsageIdentifier; ++i) {

589+

std::string_view eku(usage->rgpszUsageIdentifier[i]);

590+

if ((eku == szOID_PKIX_KP_SERVER_AUTH) ||

591+

(eku == szOID_ANY_ENHANCED_KEY_USAGE)) {

592+

return true;

593+

}

594+

}

595+596+

return false;

597+

}

598+599+

void GatherCertsForLocation(std::vector<X509Pointer>* vector,

600+

DWORD location,

601+

LPCWSTR store_name) {

602+

if (!(location == CERT_SYSTEM_STORE_LOCAL_MACHINE ||

603+

location == CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY ||

604+

location == CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE ||

605+

location == CERT_SYSTEM_STORE_CURRENT_USER ||

606+

location == CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY)) {

607+

return;

608+

}

609+610+

DWORD flags =

611+

location | CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG;

612+613+

HCERTSTORE opened_store(

614+

CertOpenStore(CERT_STORE_PROV_SYSTEM,

615+

0,

616+

// The Windows API only accepts NULL for hCryptProv.

617+

NULL, /* NOLINT (readability/null_usage) */

618+

flags,

619+

store_name));

620+

if (!opened_store) {

621+

return;

622+

}

623+624+

auto cleanup = OnScopeLeave(

625+

[opened_store]() { CHECK_EQ(CertCloseStore(opened_store, 0), TRUE); });

626+627+

PCCERT_CONTEXT cert_from_store = nullptr;

628+

while ((cert_from_store = CertEnumCertificatesInStore(

629+

opened_store, cert_from_store)) != nullptr) {

630+

if (!IsCertTrustedForServerAuth(cert_from_store)) {

631+

continue;

632+

}

633+

const unsigned char* cert_data =

634+

reinterpret_cast<const unsigned char*>(cert_from_store->pbCertEncoded);

635+

const size_t cert_size = cert_from_store->cbCertEncoded;

636+637+

vector->emplace_back(d2i_X509(nullptr, &cert_data, cert_size));

638+

}

639+

}

640+641+

void ReadWindowsCertificates(

642+

std::vector<std::string>* system_root_certificates) {

643+

std::vector<X509Pointer> system_root_certificates_X509;

644+

// TODO(joyeecheung): match Chromium's policy, collect more certificates

645+

// from user-added CAs and support disallowed (revoked) certificates.

646+647+

// Grab the user-added roots.

648+

GatherCertsForLocation(

649+

&system_root_certificates_X509, CERT_SYSTEM_STORE_LOCAL_MACHINE, L"ROOT");

650+

GatherCertsForLocation(&system_root_certificates_X509,

651+

CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY,

652+

L"ROOT");

653+

GatherCertsForLocation(&system_root_certificates_X509,

654+

CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE,

655+

L"ROOT");

656+

GatherCertsForLocation(

657+

&system_root_certificates_X509, CERT_SYSTEM_STORE_CURRENT_USER, L"ROOT");

658+

GatherCertsForLocation(&system_root_certificates_X509,

659+

CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY,

660+

L"ROOT");

661+662+

// Grab the user-added trusted server certs. Trusted end-entity certs are

663+

// only allowed for server auth in the "local machine" store, but not in the

664+

// "current user" store.

665+

GatherCertsForLocation(&system_root_certificates_X509,

666+

CERT_SYSTEM_STORE_LOCAL_MACHINE,

667+

L"TrustedPeople");

668+

GatherCertsForLocation(&system_root_certificates_X509,

669+

CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY,

670+

L"TrustedPeople");

671+

GatherCertsForLocation(&system_root_certificates_X509,

672+

CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE,

673+

L"TrustedPeople");

674+675+

X509VectorToPEMVector(system_root_certificates_X509,

676+

system_root_certificates);

677+

}

678+

#endif

679+527680

void ReadSystemStoreCertificates(

528681

std::vector<std::string>* system_root_certificates) {

529682

#ifdef __APPLE__

530683

ReadMacOSKeychainCertificates(system_root_certificates);

531684

#endif

685+

#ifdef _WIN32

686+

ReadWindowsCertificates(system_root_certificates);

687+

#endif

532688

}

533689534690

std::vector<std::string> getCombinedRootCertificates() {