Skip to content

feat(computers): a TLS shim keeps OpenSSL's certificate checks on real time on a computer's universe clock (FD-5475) - #639

Open
earakely-scale wants to merge 3 commits into
mainfrom
edgararakelyan/fd-5475-hb15-openssl-certificate-checks-on
Open

earakely-scale wants to merge 3 commits into
mainfrom
edgararakelyan/fd-5475-hb15-openssl-certificate-checks-on

Conversation

@earakely-scale

@earakely-scale earakely-scale commented Nov 12, 2026 •

Copy link
Copy Markdown
Collaborator

Part of FD-5420: a computer's own clock follows the gateway's universe clock. This is FD-5475, Q15 of the design: certificate checks use real time.

A program on universe time months from real time finds every live site's certificate not yet valid or expired. Most clients will reach live sites through the computer's egress proxy (FD-5478), which hands them certificates valid at the universe date. This shim covers the clients that bypass it.

What it is

src/agent_env/computers/clock/tls_realtime.c, agent-env's own code (Apache-2.0), built by build.sh beside the clock library as libtls_realtime.so. FD-5476 lists it in /etc/ld.so.preload after the library.

  • What it intercepts: it defines X509_verify_cert, which OpenSSL's libssl calls to verify a peer's chain. Being first in the global scope, it catches libssl's calls.
  • What it sets: when the caller set no check time (X509_V_FLAG_USE_CHECK_TIME), it sets one to real time. It reads real time with a raw clock_gettime system call (x86_64 and aarch64), which the clock library doesn't intercept. Then it calls OpenSSL's own X509_verify_cert.
  • Which OpenSSL it calls: the one in the caller's own libcrypto. It finds the calling object with dladdr and looks the function up in that object's own dependencies. It does this on every check, once a handshake (a few microseconds), so a copy of OpenSSL loaded where an unloaded one was can't be handed the old one's functions.
    • A program that calls it itself gets the next definition in the global scope.
    • A Python extension's privately loaded system libcrypto is found too.
  • A change from the prototype: the prototype always took the next definition in the global scope, so in a process holding two OpenSSL copies (a bundled one beside the system's), the bundled libssl's call reached the system copy with its own structures. Now each copy verifies with its own, OpenSSL 1.1 included.
  • Not covered: TLS stacks of their own (Chromium's BoringSSL, GnuTLS, NSS, Java's) and static binaries. FD-5486 covers GnuTLS and NSS.

Testing

  • The clock tier's tls gate (tst/clock/tls_test.py) runs curl, Python and openssl s_client as the session user, with the clock 182 days back and no proxy. It passes on both EC2 hosts (x86_64 and Graviton arm64). On x86_64:

    curl Python s_client
    Clock library alone, a local certificate valid only from real now refused refused refused
    Clock library alone, a local certificate valid at the universe date and expired since ok ok ok
    With the shim, the certificate valid from now ok ok ok
    With the shim, the certificate expired since refused refused refused
    With the shim, 6 live sites (example.com, www.google.com, github.com, pypi.org, www.wikipedia.org, letsencrypt.org) 6/6 ok 6/6 ok 6/6 ok
    With the shim, the certificate valid from now, checked against the system's roots, which don't hold its issuer refused refused refused

    The local pair shows the check moves from the universe's date to real time and no further: each certificate's answer flips. The refusals are the gate's own servers, an expired certificate and an untrusted one, so only the live sites need the network. The second was made by the session user under the clock armed 200 days back, valid for 100 days.

  • Two copies of OpenSSL in one process. The gate image builds OpenSSL 1.1.1w's libraries beside the system's 3.0, from the release tarball pinned by sha256. I used a different version on purpose: a renamed copy of 3.0 has the same symbol versions, so its calls bind to the system copy and a wrong lookup can't be seen.

    • tlscopies.c verifies the same two certificates through both copies in turn. Each copy goes through a small library loaded privately (RTLD_LOCAL), as Python loads an extension.
    • It runs with the system's libcrypto also in the global scope (global, as in curl) and without it (private, as in Python).
    • Both copies, in both modes, give each certificate's right answer with the shim (by real time) and without it (by the universe's date).
    • With the prototype's lookup preloaded instead, the program segfaults (exit 139) in both modes: the 1.1 copy's check reaches 3.0's libcrypto.
  • The whole tier passes on both hosts: 30 tests. With the two-copy test it's 31; the TLS tests pass on x86_64, and CI's gate jobs run them on both arches.

  • On a real computer: the clock desktop test's app now builds and preloads the shim, and adds curl, which the base doesn't have. With the clock 182 days back, curl and Python in the session reach https://example.com.

    • The test passes on both hosts, x86_64 and arm64, and again on x86_64 at the final head.

Docs

The computer clock page (FD-5476) should say that OpenSSL's certificate checks use real time through the shim, and which TLS stacks it doesn't cover.

🤖 Generated with Claude Code

RetriggerView in GreptileConfidence Score: 5/5

The PR appears safe to merge; no new actionable issues were found.

Summary

Adds a TLS shim that keeps OpenSSL certificate checks on real time while programs read universe time.

  • OpenSSL checks certificates against real time on universe-clock computers.

Reviews (3) · Last reviewed commit: "fix(computers): the TLS shim looks OpenS..." · Reviewed by Greptile

…l time on a computer's universe clock (FD-5475)

A program on universe time months from real time finds every live site's certificate not yet valid or expired. Most
clients go through the egress proxy; tls_realtime.c, preloaded after the clock library, covers the ones that don't.
It defines X509_verify_cert, sets the check time to real time (a raw clock_gettime system call) unless the caller set
one, and calls the real one from the caller's own libcrypto (dladdr on the caller, then its own dependencies), so a
process holding two OpenSSL copies verifies each with its own. build.sh builds it as libtls_realtime.so.

The clock tier's tls gate runs curl, Python and openssl s_client with the clock 182 days back and no proxy: six live
sites verify, badssl's expired and untrusted-root ones don't, and two local servers show the check is real time's
(a certificate valid only from now, refused without the shim and accepted with it; one valid at the universe date and
since expired, the other way round). The clock desktop test preloads it and reaches a live site from the session.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@earakely-scale
earakely-scale requested a review from a team as a code owner November 12, 2026 17:10
Comment thread tst/clock/tls_test.py Resolved
earakely-scale and others added 2 commits November 12, 2026 09:26
…ates with their own

The TLS gate verifies its two certificates through the system's OpenSSL 3.0 and a 1.1.1w built beside it, each
through a library loaded privately, with the system's also in the global scope or not. With the prototype's lookup
the program segfaults in both modes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… gate refuses certificates without badssl.com

The per-thread cache keyed by the caller's load address could hand a copy of OpenSSL loaded where an unloaded one was
the old one's functions; the lookup costs microseconds once a handshake. The gate's refusals are its own servers: the
certificate expired at real time, and the one valid now checked against the system's roots, which don't hold its
issuer. Only the live sites still need the network.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant