Skip to content

tls: read the peer certificate chain without consuming it - #65602

Open
tgies wants to merge 1 commit into
nodejs:mainfrom
tgies:tls-peer-cert-chain
Open

tls: read the peer certificate chain without consuming it#65602
tgies wants to merge 1 commit into
nodejs:mainfrom
tgies:tls-peer-cert-chain

Conversation

@tgies

@tgies tgies commented Aug 27, 2026

Copy link
Copy Markdown

Server-side getPeerCertificate(true) stopped returning the client certificate's issuer chain in v26.8.0. issuerCertificate became undefined.

The root cause actually predates this regression; X509Certificate::GetPeerCert() walked the stack from SSL_get_peer_cert_chain() with sk_X509_delete(), so the first read destroyed the chain for every later read. #64677 added an internal call to getPeerX509Certificate() in onServerSocketSecure() to detect presence of a certificate, so now the mutating read happens every time.

This changes getPeerX509Certificate() to read the chain non-destructively, copying issuers with X509_dup, and replaces the presence check with a hasPeerCertificate() binding.

Fixes: #65579
Refs: #64677

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/crypto
  • @nodejs/net

@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 involving general changes in the lib/ or src/ directories. needs-ci PRs that need a full CI run. labels Aug 27, 2026
@tgies

tgies commented Aug 27, 2026

Copy link
Copy Markdown
Author

BTW, the argument for adding the hasPeerCertificate() binding even after making getPeerX509Certificate() nondestructive is mainly that it's expensive and unnecessary to call getPeerX509Certificate() here anyway. That builds the whole cert chain and the JS X509Certificate wrapper objects on every handshake just so we can test != null.

tgies added a commit to tgies/client-certificate-auth that referenced this pull request Aug 28, 2026


Node 26.8.0 stopped exposing the server-side peer chain via
getPeerCertificate(true) (nodejs/node#65579, fix nodejs/node#65602), so the
socket-path includeChain test failed there. It now probes the running Node
once and asserts the issuerCertificate chain only when the runtime still
exposes it. README and the troubleshooting guide note the regression and the
allowFingerprints/allowCA/header-path workarounds.
@tgies tgies reopened this Aug 30, 2026
@tgies

tgies commented Aug 30, 2026

Copy link
Copy Markdown
Author

This was closed in error, sorry.

@panva
panva requested review from panva and pimterry August 31, 2026 08:06
@panva

panva commented Aug 31, 2026

Copy link
Copy Markdown
Member

@tgies please fix the first commit message, rebase and force push

@codecov

codecov Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 77.14286% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.07%. Comparing base (8fe4b64) to head (e39d831).
⚠️ Report is 5 commits behind head on main.

Files with missing lines Patch % Lines
src/crypto/crypto_x509.cc 73.07% 3 Missing and 4 partials ⚠️
src/crypto/crypto_tls.cc 87.50% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #65602      +/-   ##
==========================================
- Coverage   90.07%   90.07%   -0.01%     
==========================================
  Files         754      754              
  Lines      256317   256334      +17     
  Branches    48476    48474       -2     
==========================================
+ Hits       230871   230884      +13     
- Misses      16563    16565       +2     
- Partials     8883     8885       +2     
Files with missing lines Coverage Δ
lib/internal/tls/wrap.js 95.17% <100.00%> (ø)
src/crypto/crypto_tls.h 87.50% <ø> (ø)
src/crypto/crypto_x509.h 52.94% <ø> (ø)
src/crypto/crypto_tls.cc 78.74% <87.50%> (-0.03%) ⬇️
src/crypto/crypto_x509.cc 73.12% <73.07%> (+0.05%) ⬆️

... and 26 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

X509Certificate::GetPeerCert() built the certificate objects by deleting
entries from the stack returned by SSL_get_peer_cert_chain(), which is
owned by the SSL session. The first read emptied it, so any later read
by getPeerCertificate() or getPeerX509Certificate(), on either peer, saw
a truncated chain or nothing. Copy each issuer with X509_dup instead and
leave the session's stack untouched.

onServerSocketSecure() only called getPeerX509Certificate() to check
whether a peer certificate was present, building the whole chain on
every server handshake; that is what first exposed the destructive read.
Use a lightweight hasPeerCertificate() binding for the presence check.

Signed-off-by: Tony Gies <tgies@tgies.net>
@tgies
tgies force-pushed the tls-peer-cert-chain branch from 3574e55 to e39d831 Compare August 31, 2026 11:37
@panva panva added author ready PRs with CI started, the required approvals, and no outstanding review comments. request-ci Add this label to start a Jenkins CI on a PR. labels Aug 31, 2026
@github-actions github-actions Bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Aug 31, 2026
@nodejs-github-bot

This comment has been minimized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author ready PRs with CI started, the required approvals, and no outstanding review comments. c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs involving general changes in the lib/ or src/ directories. needs-ci PRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tls: server getPeerCertificate(true) drops the peer certificate chain since v26.8.0 (regressed by #64677)

4 participants