Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added support for reading certificates from macOS system store #56599

Open
wants to merge 14 commits into
base: main
Choose a base branch
from

Conversation

timja
Copy link

@timja timja commented Jan 14, 2025

Fixes #39657

Builds on #44532 but for macOS

TODO:

  • Make it work, it works 🥳
  • Review that all CF resources are being appropriately released, I think its right now
  • Review whether and where tests are appropriate

I can take a look at the Windows one after, resolving the conflicts and addressing the review comments as well.


Happy to refactor heavily, I haven't used c++ before and I wrote it initially in objective c and ported it across.
This is heavily based upon chromium and some of OpenJDK along with a PR I have open with OpenJDK


Testing

I'm using https://github.com/timja/openjdk-intermediate-ca-reproducer as a reproducer:

docker compose up --build

Install the certificates, either by adding to keychain manually (see README) or using /usr/bin/security (see what the test is doing in this PR.

main.js

let resp = await fetch("https://localhost:8443");
console.log(resp.status); // 200
console.log(resp.headers.get("Content-Type")); // "text/html"
console.log(await resp.text()); // "Hello, World!"
/Users/$USER/projects/node/out/Release/node --use-system-ca main.js

I've also tested this through a ZScaler MiTM setup.

@nodejs-github-bot
Copy link
Collaborator

Review requested:

  • @nodejs/crypto
  • @nodejs/gyp

@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. labels Jan 14, 2025
@timja timja force-pushed the macos-system-ca-support branch from 8fd32ce to f3c212c Compare January 14, 2025 16:32
src/crypto/crypto_context.cc Outdated Show resolved Hide resolved
std::vector<X509*> system_root_certificates_X509;
for (int i = 0; i < count ; ++i) {
SecCertificateRef certRef = (SecCertificateRef) CFArrayGetValueAtIndex(
currAnchors, i);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should use reinterpret_cast

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fails to build with:

../../src/crypto/crypto_context.cc:447:33: error: reinterpret_cast from 'const void *' to 'SecCertificateRef' (aka '__SecCertificate *') casts away qualifiers
  447 |     SecCertificateRef certRef = reinterpret_cast<SecCertificateRef>(CFArrayGetValueAtIndex(

The linter hasn't asked me to change to it and it did in most of the other places.

src/crypto/crypto_context.cc Outdated Show resolved Hide resolved
@anonrig anonrig requested a review from jasnell January 14, 2025 17:29
@timja
Copy link
Author

timja commented Jan 15, 2025

Would it be possible for someone to re-open the feature request please? #39657. It was closed due to being stale / no progress on it.

doc/api/tls.md Outdated Show resolved Hide resolved
}

bool IsSelfSigned(X509* cert) {
auto issuerName = getX509Name(X509_get_issuer_name(cert));
Copy link
Member

@joyeecheung joyeecheung Jan 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can simply wrap it with an ncrypto::X509View and use getSubject() etc. to get the data and then strncmp the two.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any pointers? I'm struggling to find where/ how an example of how one gets an ncrypto::X509View (I'll keep looking though).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tried:

ncrypto::X509View x509_view = ncrypto::X509View::From(cert);

But that takes an SSLPointer& which I haven't figured out yet

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using ncrypto::X509View x509_view(cert); seems to compile although not quite sure on the next steps.

currently at:

  ncrypto::X509View x509_view(cert);
  auto subject = x509_view.getSubject();
  auto issuer = x509_view.getIssuer();

  if (strncmp(subject, issuer, 30) == 0) {
    fprintf(stderr, "Self-signed certificate detected\n");
    return true;
  } else {
    fprintf(stderr, "Self-signed certificate detected\n");
    return false;
  }

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like this? 9cb41b0

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why I would strncmp here as the string could be the same up to a point but still differ after. But I could be misunderstanding.

Copy link
Member

@joyeecheung joyeecheung Jan 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I realized that we can simply use X509_NAME_cmp and save all these copying/reading?

src/crypto/crypto_context.cc Outdated Show resolved Hide resolved
src/crypto/crypto_context.cc Outdated Show resolved Hide resolved
combined_root_certs.emplace_back(root_certs[i]);
}

if (per_process::cli_options->use_system_ca) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This question predates this PR I guess, but shouldn't these options be per-env instead of per-process? @addaleax @jasnell

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW existing code in the same area uses per_process for the same thing from my understanding:

if (per_process::cli_options->ssl_openssl_cert_store == false) {

(I don't know the difference as-of yet though)

@timja timja requested review from joyeecheung and addaleax January 15, 2025 17:03
@timja timja marked this pull request as ready for review January 16, 2025 15:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Allow Node to use certificates from the macOS Keychain when making HTTPS requests
4 participants