cli: verify signatures of side-loadable DLLs on Windows (#2411) - #2478
Open
ronaldtse wants to merge 7 commits into
Open
cli: verify signatures of side-loadable DLLs on Windows (#2411)#2478ronaldtse wants to merge 7 commits into
ronaldtse wants to merge 7 commits into
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #2478 +/- ##
=======================================
Coverage 85.46% 85.46%
=======================================
Files 125 125
Lines 22962 22962
=======================================
Hits 19625 19625
Misses 3337 3337 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
2 tasks
The Windows loader resolves rnp.dll and any other DLL from the executable's directory before main() runs, so replacing a DLL next to a signed rnpkeys.exe (as shipped with Thunderbird) runs attacker code inside a trusted-looking process. rnp and rnpkeys now refuse to continue when a module loaded from the executable's own directory has no valid Authenticode signature, but only if the executable itself is signed - unsigned development builds are unaffected. The check cannot undo the loader-time DLL initialization, so signed distributions are also advised to build the tools statically (BUILD_SHARED_LIBS=OFF), documented in docs/packaging.adoc.
WINTRUST_FILE_INFOW and WinVerifyTrustW do not exist - the file-info struct is wide-only without a suffix and WinVerifyTrust has no A/W variants; only WINTRUST_DATA is split.
Rework per the discussion in #2411 so that embedders compiling the rnp CLI sources (Thunderbird's third_party/rnp moz.build) get the protection with a plain re-vendor and no build configuration changes: - the check now lives in fficli.cpp, which both rnp and rnpkeys programs already compile, instead of a new translation unit; - WinVerifyTrust is resolved at runtime from System32 via LoadLibraryEx + GetProcAddress (GUIDs instantiated via initguid), so no wintrust/crypt32 link-time dependency is added for anyone - which also makes the verification API itself non-side-loadable; - only modules from the executable's directory that are part of its transitive import chain are verified (walking the in-memory PE import tables) instead of every application-directory module: Thunderbird's install directory holds around 100 DLLs which are not rnp's to police, and verifying each would cost seconds per run; - unsigned executables still skip the check entirely, and the check fails open if the verification API is unavailable.
ronaldtse
force-pushed
the
fix-2411-dll-sideload
branch
from
September 3, 2026 06:18
f3d0acf to
e2e65d9
Compare
Contributor
Author
|
@ni4 your approval here was auto-dismissed when I rebased the branch onto current main (to pick up the #2480 gpg-flake coverage) - the content is otherwise exactly what you reviewed: the fficli-based DLL verification plus the macOS library-validation docs. Could you re-approve when convenient? Everything is green except the fuzzers-openssl red that clears once #2477 lands. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
rnp.dll(and any other DLL) from the executable's directory beforemain()runs, so replacing a DLL next to a signedrnpkeys.exe— as distributed with Thunderbird — executes attacker code inside a process that appears trusted to EDR/AV.main()-time check runs after the loader has already mapped the DLLs, so it detects and aborts but cannot prevent the replaced module'sDllMainfrom executing. Complete closure for signed distributions is static linking (BUILD_SHARED_LIBS=OFF), documented indocs/packaging.adoc— Thunderbird has ruled that out due to size, hence this runtime check.src/rnp/fficli.cpp, a file every embedder of the rnp CLI sources (Thunderbird'sthird_party/rnpmoz.build) already compiles into bothrnpandrnpkeys— a plain re-vendor picks it up;WinVerifyTrustis resolved at runtime from System32 (LoadLibraryExW+GetProcAddress, action GUIDs instantiated locally viainitguid), so nowintrust/crypt32link-time dependency is added for anyone, and the verification API itself cannot be side-loaded;rnp.dll(plus any sibling backend DLLs) at tens of milliseconds;Test plan
rnp_tests.test_dll_verify_unsignedpasses on Windows