18
18
#ifndef OPENSSL_NO_ENGINE
19
19
#include < openssl/engine.h>
20
20
#endif // !OPENSSL_NO_ENGINE
21
+ #ifdef __APPLE__
22
+ #include < Security/Security.h>
23
+ #endif
21
24
22
25
namespace node {
23
26
@@ -232,6 +235,306 @@ unsigned long LoadCertsFromFile( // NOLINT(runtime/int)
232
235
}
233
236
}
234
237
238
+ // Indicates the trust status of a certificate.
239
+ enum class TrustStatus {
240
+ // Trust status is unknown / uninitialized.
241
+ UNKNOWN,
242
+ // Certificate inherits trust value from its issuer. If the certificate is the
243
+ // root of the chain, this implies distrust.
244
+ UNSPECIFIED,
245
+ // Certificate is a trust anchor.
246
+ TRUSTED,
247
+ // Certificate is blocked / explicitly distrusted.
248
+ DISTRUSTED
249
+ };
250
+
251
+ bool isSelfIssued (X509* cert) {
252
+ auto subject = X509_get_subject_name (cert);
253
+ auto issuer = X509_get_issuer_name (cert);
254
+
255
+ return X509_NAME_cmp (subject, issuer) == 0 ;
256
+ }
257
+
258
+ #ifdef __APPLE__
259
+ // This code is loosely based on
260
+ // https://github.com/chromium/chromium/blob/54bd8e3/net/cert/internal/trust_store_mac.cc
261
+ // Copyright 2015 The Chromium Authors
262
+ // Licensed under a BSD-style license
263
+ // See https://chromium.googlesource.com/chromium/src/+/HEAD/LICENSE for
264
+ // details.
265
+ TrustStatus IsTrustDictionaryTrustedForPolicy (CFDictionaryRef trust_dict,
266
+ bool is_self_issued) {
267
+ // Trust settings may be scoped to a single application
268
+ // skip as this is not supported
269
+ if (CFDictionaryContainsKey (trust_dict, kSecTrustSettingsApplication )) {
270
+ return TrustStatus::UNSPECIFIED;
271
+ }
272
+
273
+ // Trust settings may be scoped using policy-specific constraints. For
274
+ // example, SSL trust settings might be scoped to a single hostname, or EAP
275
+ // settings specific to a particular WiFi network.
276
+ // As this is not presently supported, skip any policy-specific trust
277
+ // settings.
278
+ if (CFDictionaryContainsKey (trust_dict, kSecTrustSettingsPolicyString )) {
279
+ return TrustStatus::UNSPECIFIED;
280
+ }
281
+
282
+ // If the trust settings are scoped to a specific policy (via
283
+ // kSecTrustSettingsPolicy), ensure that the policy is the same policy as
284
+ // |kSecPolicyAppleSSL|. If there is no kSecTrustSettingsPolicy key, it's
285
+ // considered a match for all policies.
286
+ if (CFDictionaryContainsKey (trust_dict, kSecTrustSettingsPolicy )) {
287
+ SecPolicyRef policy_ref = reinterpret_cast <SecPolicyRef>(const_cast <void *>(
288
+ CFDictionaryGetValue (trust_dict, kSecTrustSettingsPolicy )));
289
+
290
+ if (!policy_ref) {
291
+ return TrustStatus::UNSPECIFIED;
292
+ }
293
+
294
+ CFDictionaryRef policy_dict (SecPolicyCopyProperties (policy_ref));
295
+
296
+ // kSecPolicyOid is guaranteed to be present in the policy dictionary.
297
+ CFStringRef policy_oid = reinterpret_cast <CFStringRef >(
298
+ const_cast <void *>(CFDictionaryGetValue (policy_dict, kSecPolicyOid )));
299
+
300
+ if (!CFEqual (policy_oid, kSecPolicyAppleSSL )) {
301
+ return TrustStatus::UNSPECIFIED;
302
+ }
303
+ }
304
+
305
+ int trust_settings_result = kSecTrustSettingsResultTrustRoot ;
306
+ if (CFDictionaryContainsKey (trust_dict, kSecTrustSettingsResult )) {
307
+ CFNumberRef trust_settings_result_ref =
308
+ reinterpret_cast <CFNumberRef >(const_cast <void *>(
309
+ CFDictionaryGetValue (trust_dict, kSecTrustSettingsResult )));
310
+
311
+ if (!trust_settings_result_ref ||
312
+ !CFNumberGetValue (trust_settings_result_ref,
313
+ kCFNumberIntType ,
314
+ &trust_settings_result)) {
315
+ return TrustStatus::UNSPECIFIED;
316
+ }
317
+
318
+ if (trust_settings_result == kSecTrustSettingsResultDeny ) {
319
+ return TrustStatus::DISTRUSTED;
320
+ }
321
+
322
+ // This is a bit of a hack: if the cert is self-issued allow either
323
+ // kSecTrustSettingsResultTrustRoot or kSecTrustSettingsResultTrustAsRoot on
324
+ // the basis that SecTrustSetTrustSettings should not allow creating an
325
+ // invalid trust record in the first place. (The spec is that
326
+ // kSecTrustSettingsResultTrustRoot can only be applied to root(self-signed)
327
+ // certs and kSecTrustSettingsResultTrustAsRoot is used for other certs.)
328
+ // This hack avoids having to check the signature on the cert which is slow
329
+ // if using the platform APIs, and may require supporting MD5 signature
330
+ // algorithms on some older OSX versions or locally added roots, which is
331
+ // undesirable in the built-in signature verifier.
332
+ if (is_self_issued) {
333
+ return trust_settings_result == kSecTrustSettingsResultTrustRoot ||
334
+ trust_settings_result == kSecTrustSettingsResultTrustAsRoot
335
+ ? TrustStatus::TRUSTED
336
+ : TrustStatus::UNSPECIFIED;
337
+ }
338
+
339
+ // kSecTrustSettingsResultTrustAsRoot can only be applied to non-root certs.
340
+ return (trust_settings_result == kSecTrustSettingsResultTrustAsRoot )
341
+ ? TrustStatus::TRUSTED
342
+ : TrustStatus::UNSPECIFIED;
343
+ }
344
+
345
+ return TrustStatus::UNSPECIFIED;
346
+ }
347
+
348
+ TrustStatus IsTrustSettingsTrustedForPolicy (CFArrayRef trust_settings,
349
+ bool is_self_issued) {
350
+ // The trust_settings parameter can return a valid but empty CFArrayRef.
351
+ // This empty trust-settings array means “always trust this certificate”
352
+ // with an overall trust setting for the certificate of
353
+ // kSecTrustSettingsResultTrustRoot
354
+ if (CFArrayGetCount (trust_settings) == 0 ) {
355
+ return is_self_issued ? TrustStatus::TRUSTED : TrustStatus::UNSPECIFIED;
356
+ }
357
+
358
+ for (CFIndex i = 0 ; i < CFArrayGetCount (trust_settings); ++i) {
359
+ CFDictionaryRef trust_dict = reinterpret_cast <CFDictionaryRef >(
360
+ const_cast <void *>(CFArrayGetValueAtIndex (trust_settings, i)));
361
+
362
+ TrustStatus trust =
363
+ IsTrustDictionaryTrustedForPolicy (trust_dict, is_self_issued);
364
+
365
+ if (trust == TrustStatus::DISTRUSTED || trust == TrustStatus::TRUSTED) {
366
+ return trust;
367
+ }
368
+ }
369
+ return TrustStatus::UNSPECIFIED;
370
+ }
371
+
372
+ bool IsCertificateTrustValid (SecCertificateRef ref) {
373
+ SecTrustRef sec_trust = nullptr ;
374
+ CFMutableArrayRef subj_certs =
375
+ CFArrayCreateMutable (nullptr , 1 , &kCFTypeArrayCallBacks );
376
+ CFArraySetValueAtIndex (subj_certs, 0 , ref);
377
+
378
+ SecPolicyRef policy = SecPolicyCreateSSL (false , nullptr );
379
+ OSStatus ortn =
380
+ SecTrustCreateWithCertificates (subj_certs, policy, &sec_trust);
381
+ bool result = false ;
382
+ if (ortn) {
383
+ /* should never happen */
384
+ } else {
385
+ result = SecTrustEvaluateWithError (sec_trust, nullptr );
386
+ }
387
+
388
+ if (policy) {
389
+ CFRelease (policy);
390
+ }
391
+ if (sec_trust) {
392
+ CFRelease (sec_trust);
393
+ }
394
+ if (subj_certs) {
395
+ CFRelease (subj_certs);
396
+ }
397
+ return result;
398
+ }
399
+
400
+ bool IsCertificateTrustedForPolicy (X509* cert, SecCertificateRef ref) {
401
+ OSStatus err;
402
+
403
+ bool trust_evaluated = false ;
404
+ bool is_self_issued = isSelfIssued (cert);
405
+
406
+ // Evaluate user trust domain, then admin. User settings can override
407
+ // admin (and both override the system domain, but we don't check that).
408
+ for (const auto & trust_domain :
409
+ {kSecTrustSettingsDomainUser , kSecTrustSettingsDomainAdmin }) {
410
+ CFArrayRef trust_settings = nullptr ;
411
+ err = SecTrustSettingsCopyTrustSettings (ref, trust_domain, &trust_settings);
412
+
413
+ if (err != errSecSuccess && err != errSecItemNotFound) {
414
+ fprintf (stderr,
415
+ " ERROR: failed to copy trust settings of system certificate%d\n " ,
416
+ err);
417
+ continue ;
418
+ }
419
+
420
+ if (err == errSecSuccess && trust_settings != nullptr ) {
421
+ TrustStatus result =
422
+ IsTrustSettingsTrustedForPolicy (trust_settings, is_self_issued);
423
+ if (result != TrustStatus::UNSPECIFIED) {
424
+ CFRelease (trust_settings);
425
+ return result == TrustStatus::TRUSTED;
426
+ }
427
+ }
428
+
429
+ // An empty trust settings array isn’t the same as no trust settings,
430
+ // where the trust_settings parameter returns NULL.
431
+ // No trust-settings array means
432
+ // “this certificate must be verifiable using a known trusted certificate”.
433
+ if (trust_settings == nullptr && !trust_evaluated) {
434
+ bool result = IsCertificateTrustValid (ref);
435
+ if (result) {
436
+ return true ;
437
+ }
438
+ // no point re-evaluating this in the admin domain
439
+ trust_evaluated = true ;
440
+ } else if (trust_settings) {
441
+ CFRelease (trust_settings);
442
+ }
443
+ }
444
+ return false ;
445
+ }
446
+
447
+ void ReadMacOSKeychainCertificates (
448
+ std::vector<std::string>* system_root_certificates) {
449
+ CFTypeRef search_keys[] = {kSecClass , kSecMatchLimit , kSecReturnRef };
450
+ CFTypeRef search_values[] = {
451
+ kSecClassCertificate , kSecMatchLimitAll , kCFBooleanTrue };
452
+ CFDictionaryRef search = CFDictionaryCreate (kCFAllocatorDefault ,
453
+ search_keys,
454
+ search_values,
455
+ 3 ,
456
+ &kCFTypeDictionaryKeyCallBacks ,
457
+ &kCFTypeDictionaryValueCallBacks );
458
+
459
+ CFArrayRef curr_anchors = nullptr ;
460
+ OSStatus ortn =
461
+ SecItemCopyMatching (search, reinterpret_cast <CFTypeRef *>(&curr_anchors));
462
+ CFRelease (search);
463
+
464
+ if (ortn) {
465
+ fprintf (stderr, " ERROR: SecItemCopyMatching failed %d\n " , ortn);
466
+ }
467
+
468
+ CFIndex count = CFArrayGetCount (curr_anchors);
469
+
470
+ std::vector<X509*> system_root_certificates_X509;
471
+ for (int i = 0 ; i < count; ++i) {
472
+ SecCertificateRef cert_ref = reinterpret_cast <SecCertificateRef>(
473
+ const_cast <void *>(CFArrayGetValueAtIndex (curr_anchors, i)));
474
+
475
+ CFDataRef der_data = SecCertificateCopyData (cert_ref);
476
+ if (!der_data) {
477
+ fprintf (stderr, " ERROR: SecCertificateCopyData failed\n " );
478
+ continue ;
479
+ }
480
+ auto data_buffer_pointer = CFDataGetBytePtr (der_data);
481
+
482
+ X509* cert =
483
+ d2i_X509 (nullptr , &data_buffer_pointer, CFDataGetLength (der_data));
484
+ CFRelease (der_data);
485
+ bool is_valid = IsCertificateTrustedForPolicy (cert, cert_ref);
486
+ if (is_valid) {
487
+ system_root_certificates_X509.emplace_back (cert);
488
+ }
489
+ }
490
+ CFRelease (curr_anchors);
491
+
492
+ for (size_t i = 0 ; i < system_root_certificates_X509.size (); i++) {
493
+ ncrypto::X509View x509_view (system_root_certificates_X509[i]);
494
+
495
+ auto pem_bio = x509_view.toPEM ();
496
+ if (!pem_bio) {
497
+ fprintf (stderr,
498
+ " Warning: converting system certificate to PEM format failed\n " );
499
+ continue ;
500
+ }
501
+
502
+ char * pem_data = nullptr ;
503
+ auto pem_size = BIO_get_mem_data (pem_bio.get (), &pem_data);
504
+ if (pem_size <= 0 || !pem_data) {
505
+ fprintf (
506
+ stderr,
507
+ " Warning: cannot read PEM-encoded data from system certificate\n " );
508
+ continue ;
509
+ }
510
+ std::string certificate_string_pem (pem_data, pem_size);
511
+
512
+ system_root_certificates->emplace_back (certificate_string_pem);
513
+ }
514
+ }
515
+ #endif // __APPLE__
516
+
517
+ void ReadSystemStoreCertificates (
518
+ std::vector<std::string>* system_root_certificates) {
519
+ #ifdef __APPLE__
520
+ ReadMacOSKeychainCertificates (system_root_certificates);
521
+ #endif
522
+ }
523
+
524
+ std::vector<std::string> getCombinedRootCertificates () {
525
+ std::vector<std::string> combined_root_certs;
526
+
527
+ for (size_t i = 0 ; i < arraysize (root_certs); i++) {
528
+ combined_root_certs.emplace_back (root_certs[i]);
529
+ }
530
+
531
+ if (per_process::cli_options->use_system_ca ) {
532
+ ReadSystemStoreCertificates (&combined_root_certs);
533
+ }
534
+
535
+ return combined_root_certs;
536
+ }
537
+
235
538
X509_STORE* NewRootCertStore () {
236
539
static std::vector<X509*> root_certs_vector;
237
540
static bool root_certs_vector_loaded = false ;
@@ -240,12 +543,17 @@ X509_STORE* NewRootCertStore() {
240
543
241
544
if (!root_certs_vector_loaded) {
242
545
if (per_process::cli_options->ssl_openssl_cert_store == false ) {
243
- for (size_t i = 0 ; i < arraysize (root_certs); i++) {
244
- X509* x509 = PEM_read_bio_X509 (
245
- NodeBIO::NewFixed (root_certs[i], strlen (root_certs[i])).get (),
246
- nullptr , // no re-use of X509 structure
247
- NoPasswordCallback,
248
- nullptr ); // no callback data
546
+ std::vector<std::string> combined_root_certs =
547
+ getCombinedRootCertificates ();
548
+
549
+ for (size_t i = 0 ; i < combined_root_certs.size (); i++) {
550
+ X509* x509 =
551
+ PEM_read_bio_X509 (NodeBIO::NewFixed (combined_root_certs[i].data (),
552
+ combined_root_certs[i].length ())
553
+ .get (),
554
+ nullptr , // no re-use of X509 structure
555
+ NoPasswordCallback,
556
+ nullptr ); // no callback data
249
557
250
558
// Parse errors from the built-in roots are fatal.
251
559
CHECK_NOT_NULL (x509);
0 commit comments