Skip to content

OpenSSL linkage

linkage.py answers the question the tool exists for: does this wheel use the system OpenSSL, or does it carry its own? The first two entries below are about _binary_posture, the function that reads one object's own evidence, and both cover several object shapes the plain rule does not resolve on its own. The third is about the weakest evidence it reads: a Rust crate name, which can say unknown and nothing more. The fourth is back on _binary_posture: a version banner the object's headers put there is not a copy. The fifth is about the rule that says an object's evidence all points at the system library, and what it needs to know about the wheel's other objects to say so. The last is about the same weak claim as the third, made a second way: the wheel's own SBOM naming the library or such a crate.

A needed entry is bundled by what it resolves to, not by whether its name was renamed

Accepted, and it changes openssl_linkage and one finding.

Keyed on the name alone, a base name in a crypto library's sonames is bundled only when the name itself carries a content hash (libcrypto-3a1f2b4c.so.3), otherwise system. That is exactly what auditwheel and delvewheel produce, and it is not what delocate produces. delocate copies a dependency into .dylibs/ and rewrites the load command to point there — @loader_path/.dylibs/libcrypto.3.dylib — without renaming the file.

pkg/_ext.cpython-312-darwin.so   needed: @loader_path/.dylibs/libcrypto.3.dylib
pkg/.dylibs/libcrypto.3.dylib    vendored_path: true, defines EVP_DigestInit_ex, SSL_new
-> by name alone: openssl_linkage: mixed, BIN_OPENSSL_LINKAGE_UNKNOWN, BIN_NEEDED_SYSTEM_OPENSSL
-> by resolution: openssl_linkage: bundled, BIN_BUNDLED_OPENSSL

By name alone that is two postures for one OpenSSL, with "could not be resolved" and "links the system OpenSSL" both firing — wrong on every count. The same shape reaches ELF too: nothing stops a build placing an unmangled dependency beside the extension with RUNPATH $ORIGIN.

Two parts, matched to the two ways a needed entry can prove it names a file the wheel ships. Conventions.raw_stem reduces a name the way own_base does but stops short of undoing a content-hash rename; linkage.member_stem_counts counts every object's raw_stem across the wheel, and a needed entry whose own raw_stem some other object answers to is bundled, mangled or not. Second, _looks_vendored reads the needed string itself and, for @rpath-relative names, the object's own LC_RPATH list — a weaker signal used only as a backstop that never asserts bundled by itself.

Why the path-convention half is capped at unknown, never bundled. A needed entry can look exactly like delocate's convention and still name nothing the wheel actually ships. Letting the shape alone promote to bundled would manufacture the same overconfidence, aimed the other way.

A residual: basename, not directory. member_stem_counts matches on file identity alone, not on directory, so two different libraries sharing a basename in one wheel would let an unrelated needed entry read as bundled. Resolving that precisely needs walking the actual search path, a meaningfully bigger mechanism. Recorded rather than closed, because the failure direction — reading system as bundled — is the safe one for a FIPS-risk tool.

Full entry

What resolving by basename does not prove

Accepted, and it changes openssl_linkage. Resolving a needed entry by basename alone proves less than it first appears to. A 150-shape differential matrix over real and synthetic wheels finds two claims weaker than they read: that a false bundled needs a second object under the same name, and that _looks_vendored's path handling stays confined to a narrower case.

A false bundled from basename alone does not need a second file; a single object can match itself — no vendor directory, no second file, whose own file name reduces to the same stem as an absolute, genuinely-system dependency it declares:

fakecrypto/libcrypto.so   soname: libcrypto.so
                          needed: /usr/lib64/libcrypto.so.3, libc.so.6
-> without an own-stem discount: openssl_linkage: bundled, verdict.class: NO_CRYPTO_DETECTED,
                                  rule_ids: [], needs_human_review: false

Counting the querying object itself lets the object answer its own question. Worse than a wrong posture, that bundled has no rule behind it unless one claims it: resolution is a third route to bundled, beside a vendored member's own record and a literal hash rename.

Two parts. The stems are counted (member_stem_counts, a Counter), and _resolves_within_wheel discounts an object's own contribution to its own answer. And a rule, BIN_NEEDED_VENDORED_CRYPTO, claims this third route the same way the other two are claimed, so the residual imprecision the two-file case allows is never silent: needs_human_review is true even when the bundled classification is a false positive from the coincidence.

_looks_vendored needs a gate. Unguarded, it fires whenever the object has any vendor-shaped search path, independent of whether the specific needed entry could plausibly resolve under it. The matrix finds this misreading a genuine system dependency as unknown on 11 of 150 shapes — a FIPS-conscious build running auditwheel's --exclude libcrypto.so.3 while vendoring an unrelated library, say libjpeg, in the same wheel.

wheel_incompletely_read(evidence) is the gate: true only when some member never became a BinaryEvidence at all. _looks_vendored is consulted only when that holds; when the wheel was read in full, a vendor-shaped path naming nothing is genuine system, because a complete member list that does not contain the answer is itself the answer.

How _looks_vendored is gated, and what pins it

Two more shapes the gate above must not miss:

The vendor-shape check inside the gate is pinned by its own test. With the gate pinned in both directions, mutating _looks_vendored to unconditionally return True would still leave a suite green that pins only the gate; the test that fails is test_a_plain_dependency_stays_system_even_in_an_incompletely_read_wheel, which pins the shape check inside the gate.

wheel_incompletely_read checks artifacts.symlinks too. A vendored library shipped as a symlink — ordinary practice for a versioned .so/.dylib — is never read as a binary at all, and records neither a skipped entry nor an error:

demo/_ext.abi3.so               needed: @loader_path/.dylibs/libcrypto.3.dylib
demo/.dylibs/libcrypto.3.dylib  -> a symlink, never read as a binary at all
-> without checking artifacts.symlinks: openssl_linkage: system, BIN_NEEDED_SYSTEM_OPENSSL, DERIVED_SYSTEM_OPENSSL_ONLY

Both finding descriptions would be affirmatively wrong here: an @loader_path-anchored load command is wheel-internal by construction and can never be the host's system OpenSSL. needs_human_review is true either way, so this is never silent, but it would be confidently wrong rather than honestly uncertain, which is the distinction this whole entry exists to draw.

Every bundled has a rule, by enumeration. Every bundled posture _binary_posture can produce has a rule behind it, audited one branch at a time. But system has an aggregate-level backstop that no per-mechanism enumeration needs to keep in step, and bundled does not: no aggregate rule stands behind it the way DERIVED_SYSTEM_OPENSSL_ONLY stands behind system. Behavioural invariant tests hold the enumeration instead of trusting it by inspection: test_every_definite_openssl_posture_has_a_finding_on_its_object asserts every object reading a definite posture carries a finding on it, in a category that fits, test_every_definite_return_in_the_posture_functions_is_reached_by_a_fixture fails when _binary_posture or needed_posture gains a definite return no fixture reaches, a pair of tests drops rules together to hold the category and subject filters to account rather than only the object's location, and test_aggregate_never_returns_a_definite_posture_without_one runs _aggregate over its whole input space to hold that unanswered/declared can never manufacture a definite posture on their own. A new mechanism needs a fixture before it can need a rule.

An absolute needed entry is never resolved by basename

Accepted, and it changes openssl_linkage, for one shape. The basename residual above accepts that a collision between two unrelated objects could manufacture a false bundled. That is not so when the colliding needed entry is an absolute path (/usr/lib64/libcrypto.so.3): no real dynamic loader resolves an absolute path against anything the wheel ships, so needed_posture answers system for it outright, before consulting the basename match or the vendor-shape check at all. The residual stays open for a relative entry that a loader genuinely could resolve via $ORIGIN/@rpath/RPATH/ RUNPATH — the ordinary vendoring shape this whole entry is about — and, narrower still, for a relative-but-non-wheel-resolvable shape like ../../hostlib/libcrypto.so.3 or a Mach-O @executable_path/... entry, neither of which this check covers even though the same argument applies to them.

Full entry


A needed match and a definition inside one object are both true, so the object is mixed

Accepted, and it changes openssl_linkage. The precedence question the entry above deliberately leaves open.

Returning system as soon as one needed entry resolves to the system library, before the defined-symbol and banner check runs, would read system for an object that both declares DT_NEEDED libssl.so.3 and defines EVP_DigestInit_ex — or carries an OpenSSL version banner, which a version script cannot hide — and the record would pair "every piece of OpenSSL evidence points at the system library" with "OpenSSL was compiled into it" in the same rule_ids list. A contradiction in the favourable direction.

demo/_ext.so   needed: libc.so.6, libssl.so.3
               defines EVP_DigestInit_ex, rodata: "OpenSSL 3.0.14 4 Jun 2024"
-> needed checked first, alone: openssl_linkage: system, with DERIVED_SYSTEM_OPENSSL_ONLY
-> needed and defined/banner together: openssl_linkage: mixed, with
   BIN_OPENSSL_LINKAGE_UNKNOWN and no DERIVED_SYSTEM_OPENSSL_ONLY

The decision: mixed, not static-wins. Both facts are independently true and independently reportable — a real DT_NEEDED entry names the system library, and a real symbol, or a banner that is not header text (see the entry on header banners), shows the object also carries its own copy. static-wins would suppress the needed evidence from the field most consumers filter on, which would then say static about an object that also, genuinely, links the system library. mixed costs no schema change: the value exists for the cross-object case.

What it costs. BIN_OPENSSL_LINKAGE_UNKNOWN also fires for this shape, and its own why — "the wheel calls OpenSSL without declaring a dependency on it, or the only evidence came from an object we could not read" — describes neither: here the dependency is declared and the object was read in full. Left as is rather than reworded, because rewording it correctly means splitting what are two different reasons mixed can fire.

Full entry

An uncertain needed match beside a definition is mixed

_binary_posture must not return unknown for the uncertain case — a needed entry whose shape looks vendor-directed but that an incompletely-read wheel cannot confirm — before the defined/banner check runs. An object with both an unconfirmed vendor-shaped entry and a confirmed static definition would otherwise read unknown, silently discarding the confirmed evidence in favour of the unconfirmed one.

demo/_ext.so  needed: libcrypto.so.3, RUNPATH: $ORIGIN/../p.libs (names nothing shipped,
              wheel incompletely read), defines EVP_DigestInit_ex
-> uncertain checked alone: openssl_linkage: unknown
-> uncertain and definite together: openssl_linkage: mixed

Precedence. What makes a confirmed entry beat an unconfirmed one is not where the branch sits in isolation: it is that if uncertain: return unknown sits below if system: in the ladder, applying within one object the same rule the aggregate applies across objects — a non-definite posture never outvotes a definite one already present. Moving the branch changes nothing the test suite can observe, confirmed by mutation.

The three-way case collapses into the two-way branch before uncertain is ever consulted, and that collapse is provable rather than merely observed: the two conditions can only be true together when system is also true, and both branches return the same value.

What it costs. This widens mixed one object beyond the one it fires on. A wheel with one object reading bundled and a second reading mixed aggregates to mixed rather than bundled, dropping out of the IN("bundled","static") triage recipe. The direction stays conservative — the wheel gains OPAQUE rather than losing anything silently.

Full entry

A bundled needed match beside system or static is mixed

The needed loop must not return bundled immediately, from inside the loop, before a second disagreeing entry or the defined/banner check runs. A universal Mach-O merges its slices' needed tuples into one, so an object whose slices disagree about bundled versus system would read bundled outright, unlike the same evidence read as two separate objects.

one object, needed=("libcrypto-3a1f2b4c.so.3", "/usr/lib64/libcrypto.so.3")
  -> bundled checked alone: bundled   -> both checked together: mixed
same evidence in two separate objects -> mixed, either way

How the loop works. It does not return from inside itself. It accumulates system, bundled and uncertain across the whole needed tuple before branching, and the disagreement check is a three-way count: sum((system, bundled, static)) > 1 is mixed.

system bundled static uncertain Result
2+ of the three true — — any mixed
T F F any system
F T F any bundled
F F T T mixed
F F T F static
F F F T unknown
F F F F falls through

bundled gets the same treatment as system beside uncertain because both are read off the same needed loop and if uncertain: sits below both. static is not read off needed at all, which is why it combines with uncertain into mixed rather than being outvoted.

Kept out of scope, and there is a real reproduction for it. An object identified by its own vendored_path rather than by a needed entry can still carry a disagreeing signal that its early return discards — a vendored libssl that itself links the host libcrypto, which is the real auditwheel --exclude libcrypto.so.3 shape:

demo/_ext.so                   needed: libc.so.6, libssl-abc123.so.3
demo.libs/libssl-abc123.so.3   needed: libc.so.6, libcrypto.so.3   defines SSL_new
-> openssl_linkage: bundled   (the system libcrypto dependency is discarded)

Milder than a system read beside a definition — nothing reads clean, and needs_human_review is true — but the same family. Left open on scope discipline, not on cost: the extra checks are cheap, the early return is a genuinely different code path, and folding it in would extend an already-large precedence mechanism further than this entry's own reproductions ask for.

Full entry


An OpenSSL crate with no other evidence reads unknown, not none

Accepted, and it changes openssl_linkage.

An object read in full whose only OpenSSL evidence is a crate name (openssl-sys, openssl, openssl-src) would otherwise read openssl_linkage: none beside a CONDITIONAL crate finding: "no OpenSSL evidence" on a record carrying some.

How it reads a crate. [[crypto_library]] has crates, validated against [[rust_crate]] at load time. An object carrying a listed crate and nothing else _binary_posture reads gives unknown, the answer imported OpenSSL symbols with no declared dependency get, for the same reason: something uses OpenSSL, and the object does not say which copy.

Never a definite posture. The branch fires only on an object with no OpenSSL needed entry, symbol or banner, which is more likely a static copy, but only as far as the readers left nothing open. And the crate cannot settle it: OPENSSL_NO_VENDOR sends even a vendored openssl-sys build back to the host's OpenSSL. One crate, built both ways:

cryptography 50.0.1 off PyPI (manylinux, macOS, Windows)
  crates: openssl, openssl-sys   banner "OpenSSL 4.0.2 25 Aug 2026"          -> static
cryptography 50.0.0, Fedora 44 RPM, repackaged as a wheel
  needed: libcrypto.so.3, libssl.so.3   crates: openssl, openssl-sys       -> system

Same crate, two postures, so the crate check sits below every other one and its unknown never outvotes a definite posture elsewhere in the wheel. None of the four records depend on it. The Windows .pyd shows what does: its banner is its only OpenSSL evidence, and with openssl_banner restricted to 3., 1.1. and 1.0., it would read none without the crate check; with it, unknown. The Fedora row's crates come from the distro cargo-path layout, and its imports from the system library answer first.

What was rejected. A posture per crate, static for openssl-src: that crate in the build graph does not mean a vendored copy, and it leaves no path in the artifact to fire on. Leaving none and documenting it: "no OpenSSL evidence" on a record carrying some, when unknown already means what this case needs.

What it costs.

  • The headline stays CONDITIONAL; OPAQUE and BIN_OPENSSL_LINKAGE_UNKNOWN join it.
  • A crate-only object beside a system-linked sibling still lets the wheel read system, but the rules do not read that alone: DERIVED_SYSTEM_OPENSSL_ONLY is withheld beside it, and a complementary rule names the object instead (see "An object that read unknown withholds DERIVED_SYSTEM_OPENSSL_ONLY" below).
  • An SBOM component naming openssl-sys moves the field the same way a crate does: see the last entry on this page.
  • A build whose cargo paths use a layout the reader does not recognise, such as a git dependency checkout, carries no crate, so the crate check never fires on it.

Full entry


A version banner beside imports from the system library is header text, not a copy

Accepted, and it changes openssl_linkage.

Any consumer that includes OpenSSL's headers puts the version banner in read-only data, whether or not it links its own copy. Counted as static unconditionally, that banner makes an object whose needed entries resolve OpenSSL from the host, and that imports from it, read mixed.

cryptography 50.0.0, Fedora 44 RPM, repackaged as a wheel
  needed: libcrypto.so.3, libssl.so.3, OpenSSL symbols imported, header banner, no
  OPENSSLDIR: string beside it
-> every banner a copy:      openssl_linkage: mixed, with BIN_OPENSSL_LINKAGE_UNKNOWN
-> header banner told apart: openssl_linkage: system, with DERIVED_SYSTEM_OPENSSL_ONLY

How a header banner is told apart. A banner does not count toward static when all four hold on the one object: a needed entry resolved the library, from the host or from a copy the wheel ships (an unconfirmed, uncertain entry does not open this gate); the object imports a symbol from it; the object was read in full; and the library names a copy_string_group — strings only a real compiled-in copy carries — that the object matches none of. For OpenSSL, that group is OPENSSLDIR:, which OpenSSL_version() returns from the same call as the banner, so a compiled-in copy keeps both together and a header never supplies the second string. A defined symbol is unaffected and still makes static on its own.

Why a marker and not the imports alone. The banner-plus-imports shape alone is not enough: a merged universal binary whose other slice carries a hidden static copy, and a static libcrypto linked beside a dynamic system libssl, both satisfy it while genuinely carrying a copy. Both keep the copy's build strings beside its banner only when something in the object actually calls OpenSSL_version() -- the one function that returns both. cryptography guarantees that call; a generic consumer does not, and can link a static libcrypto whose cversion.o is never pulled in, carrying neither string.

What it costs. This moves the field in the favourable direction, which is why it takes four independent gates rather than one. A copy whose OpenSSL_version() is never called -- so its banner survives without its build strings -- reads system: a stripped, hidden static libcrypto beside a dynamic system libssl, with the consumer's own header supplying the banner, is the residual case, and the same object with no header banner reads system whatever the banner rule does. A library naming no marker counts every banner it finds as a copy. An object that is not read in full, for any cause, keeps mixed.

Full entry


A version banner with no dependency and no build strings reads unknown, not static

Accepted, and it changes openssl_linkage.

openssl_banner matches any sentence naming a dotted OpenSSL version, not only a real banner: prose such as "enable OpenSSL 3.0 legacy provider" matches the same way "OpenSSL 3.0.14 4 Jun 2024" does. On an object where no needed entry resolved the library at all, a banner counts as a copy only when the object also carries the build strings a compiled-in copy keeps beside it (copy_string_group, OPENSSLDIR: for OpenSSL). Without them the banner is uncorroborated prose, and the object reads unknown, not static: real evidence the library's API is named, not evidence of a compiled-in copy. The gate stays shut on an object not read in full, for the same reason the header-text gate does.

Full entry


OpenSSL-named definitions beside AWS-LC or BoringSSL read unknown, not static

Accepted, and it changes openssl_linkage.

AWS-LC and BoringSSL both implement OpenSSL's public API under OpenSSL's own names, so an object built from either defines the same EVP_*/BN_*/... entry points the openssl symbol_group claims. [[crypto_library]] openssl names fork_symbol_groups/fork_string_groups (aws_lc, aws_lc_fips, boringssl): on an object where one of them matched (a symbol group only when DEFINED there), the openssl group's own definitions read unknown rather than static. A banner does not save it: AWS-LC's and BoringSSL's own headers define OPENSSL_VERSION_TEXT as one literal naming the fork, so a banner sharing its printable run with the fork's own string marker is that fork's own header macro, not a real OpenSSL copy. A banner on a different run — a real, dotted version with its own build string, elsewhere in the same object — is not the fork's own text and still corroborates a real copy. It reads unknown, never none, because an object can carry a real OpenSSL and a fork marker at once.

Full entry


An object that read unknown withholds DERIVED_SYSTEM_OPENSSL_ONLY; the field stays system

Accepted, and it changes verdicts.

DERIVED_SYSTEM_OPENSSL_ONLY's why claims every piece of OpenSSL evidence in the wheel points at the system library -- false when a sibling object reads unknown (a listed crate with nothing else, imported symbols with no declared dependency, or an unconfirmed vendor-shaped needed entry), even though _aggregate never lets that unknown outvote the definite posture and the field reads system. In the import and uncertain shapes, the derived rule would be the only verdict-bearing finding on the wheel, so withholding it alone would read NO_CRYPTO_DETECTED on a wheel that plainly uses OpenSSL.

How the rules read per-object postures. linkage.object_postures exposes the per-object tuple _aggregate reduces, shared with the engine. DERIVED_SYSTEM_OPENSSL_ONLY takes exclude_object_values = ["unknown"]; a complementary rule, DERIVED_OPENSSL_UNRESOLVED_BESIDE_SYSTEM, takes object_values = ["unknown"] on the same match and carries OPAQUE, naming the object. DERIVED_SYSTEM_OPENSSL_ONLY fires exactly when neither complement does. openssl_linkage itself never moves, and an unreadable (opaque) sibling still leaves DERIVED_SYSTEM_OPENSSL_ONLY firing: object_postures never reports unknown for an object that was not read at all.

The wheel's own SBOM carries the same claim -- unless it names the very object that already reads system. object_postures reads only per-object binary evidence, so linkage.declared_by_sbom exposes the wheel-level SBOM signal directly, and a linkage match takes it as sbom_declared. DERIVED_SYSTEM_OPENSSL_ONLY adds sbom_declared = false to its match, and a second complementary rule, DERIVED_OPENSSL_DECLARED_BESIDE_SYSTEM, takes sbom_declared = true on the same match and carries OPAQUE, naming the SBOM component through SBOM_CRYPTO_COMPONENT rather than an object. declared_by_sbom filters one component name out first: one also carried, as its own crate, by an object whose own posture already reads system (linkage._sbom_names_confirmed_by_system_objects, over the same cargo paths BIN_RUST_CRYPTO_CRATE reads). A single object declaring its own needed entry on the system library and its own openssl-sys crate is the ordinary shape of a system-linked Rust build, not a second, unaccounted-for copy the SBOM is separately naming, so a component that only restates it leaves DERIVED_SYSTEM_OPENSSL_ONLY firing rather than gaining DERIVED_OPENSSL_DECLARED_BESIDE_SYSTEM. Widening this to any posture, not only system, was rejected: the exemption exists because the object already answered system on its own evidence, not because it shares a crate name with the component, so extending it past a system-posture object would drop the finding beside an object whose own posture says nothing about which copy the component names. openssl-src is never confirmed this way and so never gains the exemption: a component names a package, not the object it was compiled into, and openssl-src never leaves cargo paths in a compiled object at all, so attribution can never legitimately confirm it.

What it costs. The import-only and uncertain shapes beside a system sibling read an OPAQUE headline rather than CONDITIONAL. The crate shape keeps its CONDITIONAL headline and gains OPAQUE. A wheel whose SBOM names an OpenSSL crate beside a system object that does not already carry that crate itself gains OPAQUE in classes too, and keeps its CONDITIONAL headline through SBOM_CRYPTO_COMPONENT; openssl_linkage itself never moves in any of these shapes. A system-linked object naming its own openssl-sys in the wheel's SBOM costs nothing beyond what its own posture already costs: DERIVED_SYSTEM_OPENSSL_ONLY still fires.

Full entry


An SBOM naming an OpenSSL crate reads unknown, not none

Accepted, and it changes openssl_linkage.

A wheel's own PEP 770 SBOM naming openssl-sys is the same claim the crate check reads from cargo paths, and it gets the same answer: unknown in place of none.

resolve_linkage has a second wheel-level signal, _declared_by_sbom, consulted only where _left_unanswered is: after every object's own evidence has been checked and none of it was definite. Unlike _left_unanswered, it is not gated on always_report, because it is about the specific library named, not about the wheel as a whole. It matches an SBOM component whose name equals a library's own name or one of its crates — the same names SBOM_CRYPTO_COMPONENT reports a finding for — compared case-insensitively, and for a crate with - and _ as the same character, the way crates.io treats them. It, like a crate, never gives a definite posture, only unknown in place of none. Where the library's own name also names an unrelated [[rust_crate]] the library does not list in crates (argon2, blake2), the component's own purl decides instead of the name alone: only a pkg:cargo/... purl reads as the crate and leaves the C library's field untouched; any other purl, or none, moves it. SBOM_CRYPTO_COMPONENT reads the same purl to pick which entry rates the component: a pkg:cargo/... purl is rated by the [[rust_crate]] entry, anything else by the [[crypto_library]] entry.

What was rejected. Two simpler alternatives. Matching by name alone, whatever the purl, gives a false positive: a component naming the pure-Rust crate under pkg:cargo/argon2@... would move the C library's field when nothing says the C library is present. Skipping the name arm on every colliding name, whatever the purl, gives the opposite failure: a component that really does name the C library under a non-cargo purl, or none, would leave SBOM_CRYPTO_COMPONENT firing with no field beside it.

The loader refuses a ruleset whose sbom_component rules do not, between them, report through both the crypto_library and rust_crate tables this signal reads names from, and refuses suppressed_by on a rule relied on for that coverage, so neither a coverage gap nor a suppressed finding can silently break the agreement between the field and the finding.

What it costs. Beside a system-linked object, this same signal withholds DERIVED_SYSTEM_OPENSSL_ONLY through sbom_declared on its match: see "An object that read unknown withholds DERIVED_SYSTEM_OPENSSL_ONLY; the field stays system" above.

Full entry