Skip to content

Build1 publisher2 min readPublished

libcurl handed an mTLS-authenticated connection to a handle carrying a different private key

The five options that decide which private key opens a client certificate lived in a struct libcurl's connection-reuse check never reads, so two handles sharing a pool could share one authenticated connection. Commit 7541ae5 moves them.

The Engineer · Build desk

Illustration accompanying libcurl handed an mTLS-authenticated connection to a handle carrying a different private key

What happened

  • CVE-2026-8932 excluded a handful of client-certificate options from libcurl's connection-reuse comparison, so two easy handles differing only in SSLKEY, SSLKEYTYPE, KEYPASSWD, SSLCERTTYPE or SSLKEYBLOB could share one already-authenticated connection.
  • The five fields that actually open a client certificate, cert_type, key, key_type, key_passwd and key_blob, lived in ssl_config_data instead of ssl_primary_config, putting them outside the reuse check's view.
  • In the writeup's walkthrough, a handle using the same SSLCERT with keyB.pem is handed the connection another handle authenticated with keyA.pem, and the server processes the request as user A.
  • The TLS session cache had a parallel problem: its key appended a fixed ":CCERT" string to note that a client certificate was used, without recording which one, so different certificates could collide.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • exposure A gateway that keeps one certificate path and varies the key per tenant can deliver a request to the backend under another tenant's identity, and the backend sees a connection it verified correctly.
  • constraint Isolating identities by forcing fresh connections would not have been enough on its own, because the session-cache key recorded only that some client certificate was in play.
  • decision Operators have to decide whether to rebuild against a commit hash or wait for a packaged libcurl, since the writeup offers no version range to compare their build against.
  • precedent With CVE-2022-27782 already on the record as the same class, reviewers now have reason to treat every new client-certificate option as a question about the reuse key.

libcurl keeps TLS connections around to avoid paying for another handshake, and hands one to a later transfer when the settings look the same [2]. When a transfer looks in the pool, `match_ssl_primary_config()` in `lib/vtls/vtls.c` compares two `ssl_primary_config` structs: CA path, CA file, issuer cert, client cert, curves, signature algorithms, pinned key, CRL file [6]. `clientcert`, the certificate file path, is on that list; `key` and `key_passwd` were not [7].

Four of the five excluded fields describe the private key: `key`, `key_type`, `key_passwd`, `key_blob`. The fifth, `cert_type`, is the certificate's format [1].

That bounds who is exposed. A gateway that gives every tenant its own certificate path does not reuse across tenants on this bug, because `clientcert` was compared [3]. The configuration that does hit it keeps one certificate path across identities and varies the key underneath: separate `SSLKEY` files, keys handed in through `SSLKEYBLOB`, or one key file with a per-identity `KEYPASSWD` [3]. None of it happens unless the pool is shared in the first place, through a `CURLSH` share handle or a multi handle [9].

`ssl_primary_config` is also what keys the TLS session cache, not only the reuse comparison [4]. So the promotion in commit 7541ae5 pulls the five fields into both lookups at once [2].

The curl team classified this CWE-305, authentication bypass, said it is a pure logic flaw and not a memory-safety bug, and pointed to CVE-2022-27782 as a similar prior case [11][12]. In my view the durable problem is the classification step. Every new TLS option has to be judged reuse-relevant or not, and the enforcement is whoever notices which struct it landed in.

The dev.to writeup identifies the fix only by commit hash, without an affected version range or a severity score [14]. For anyone running a shared pool, the question in your own code is whether two easy handles in that pool can agree on `SSLCERT` and differ on one of those five options [3].

What to watch

  • Whether distro and vendor libcurl packages backport commit 7541ae5 into their long-term-support builds, and how fast.
  • Whether curl publishes an advisory with an affected version range and a severity score for CVE-2026-8932.
  • Whether curl adds a test that fails when a new client-certificate option lands in ssl_config_data, given CVE-2022-27782 was the same class of error.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories