Skip to content

Commit e1229cf

Browse files
committed
SecurityPkg: DxeImageVerificationLib: Add further implementation to ValidateSignedImage
1 parent a38aaf1 commit e1229cf

9 files changed

Lines changed: 1683 additions & 1060 deletions

File tree

SecurityPkg/Library/DxeImageVerificationLib2/Certificate.c

Lines changed: 345 additions & 175 deletions
Large diffs are not rendered by default.

SecurityPkg/Library/DxeImageVerificationLib2/Certificate.h

Lines changed: 121 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -17,58 +17,137 @@
1717
#include <Guid/WinCertificate.h>
1818

1919
/**
20-
Determine whether a signed PE/COFF image is authorized to execute by the platform's `db`
21-
signature database.
22-
23-
An image can be authorized one of two ways:
24-
1. It's authenticode image hash is found in the `db` database.
25-
2. A X.509 certificate in the `db` is a trust anchor for one of the image's signatures, and
26-
that X.509 certificate is not also found in the `dbx`.
27-
28-
@param[in] SecDataDir Security data directory in the PE/COFF image described by Cache.
29-
@param[in] Db The `db` signature database.
30-
@param[in] DbSize The size of the `db`.
31-
@param[in] Dbx The `dbx` signature database.
32-
@param[in] DbxSize The size of the `dbx`.
33-
@param[in,out] Cache DIGEST_CACHE pointer bound to the image being searched. The cache may
34-
be updated during the search.
35-
@param[in,out] Action Updated to reflect the outcome of the authorization check.
36-
37-
@retval TRUE The image is authorized by `db`.
38-
@retval FALSE The image is not authorized, or an error prevented a
20+
Extract the DER-encoded PKCS#7 SignedData payload from a single WIN_CERTIFICATE entry.
21+
22+
Supports `WIN_CERT_TYPE_PKCS_SIGNED_DATA` (a bare PKCS#7 payload following the
23+
WIN_CERTIFICATE header) and `WIN_CERT_TYPE_EFI_GUID` whose `CertType` is
24+
`gEfiCertPkcs7Guid`. On success, `AuthData` points into `Cert`'s storage and is
25+
valid for the lifetime of `Cert`.
26+
27+
@param[in] Cert The certificate to inspect.
28+
@param[out] AuthData On success, set to point at the PKCS#7 payload inside Cert.
29+
@param[out] AuthDataSize On success, set to the PKCS#7 payload length in bytes.
30+
31+
@retval EFI_SUCCESS AuthData / AuthDataSize were populated.
32+
@retval EFI_INVALID_PARAMETER A required pointer is NULL.
33+
@retval EFI_UNSUPPORTED Unsupported WIN_CERTIFICATE type.
34+
@retval EFI_VOLUME_CORRUPTED dwLength is too small to contain the required header
35+
for the declared type.
36+
**/
37+
EFI_STATUS
38+
GetWinCertificatePkcs7AuthData (
39+
IN CONST WIN_CERTIFICATE *Cert,
40+
OUT CONST UINT8 **AuthData,
41+
OUT UINTN *AuthDataSize
42+
);
43+
44+
/**
45+
Determine whether a single embedded WIN_CERTIFICATE is revoked by `dbx`.
46+
47+
Runs two checks against the same PKCS#7 SignedData payload:
48+
49+
1. For every X.509 trust anchor enrolled in `dbx`, ask `AuthenticodeVerify`
50+
whether it verifies the signature. Any verifying anchor revokes the
51+
certificate.
52+
2. For every signer reported by `Pkcs7GetSigners`, hash the TBS bytes and
53+
ask `IsTBSCertHashInDbx` whether the resulting hash is enrolled in `dbx`.
54+
Any enrolled signer revokes the certificate (the chain is poisoned).
55+
56+
The caller is responsible for extracting `AuthData` from the WIN_CERTIFICATE
57+
via `GetWinCertificatePkcs7AuthData` and computing the image's Authenticode
58+
digest under the algorithm reported by `GetAuthenticodeHashAlgorithm`.
59+
60+
@param[in] AuthData DER-encoded PKCS#7 SignedData payload.
61+
@param[in] AuthDataSize Size of AuthData in bytes; must be non-zero.
62+
@param[in] ImageHash Authenticode digest of the image under the algorithm
63+
AuthData uses.
64+
@param[in] ImageHashSize Size of ImageHash in bytes; must be non-zero.
65+
@param[in] Dbx Raw `dbx` contents, or NULL.
66+
@param[in] DbxSize Size of Dbx in bytes; 0 when Dbx is NULL.
67+
68+
@retval TRUE The signature is revoked by `dbx`, or a missing/empty `AuthData`
69+
or `ImageHash` prevented a safe determination (fail closed).
70+
@retval FALSE The signature is not revoked by `dbx`, including when `dbx` is
71+
absent or empty.
72+
**/
73+
BOOLEAN
74+
IsCertRevoked (
75+
IN CONST UINT8 *AuthData,
76+
IN UINTN AuthDataSize,
77+
IN CONST UINT8 *ImageHash,
78+
IN UINTN ImageHashSize,
79+
IN CONST VOID *Dbx,
80+
IN UINTN DbxSize
81+
);
82+
83+
/**
84+
Determine whether a single PKCS#7 SignedData payload authorizes the image against `db`.
85+
86+
Walks each `EFI_SIGNATURE_LIST` in `db` looking for an X.509 trust anchor that verifies the
87+
signature. A verifying anchor whose TBS hash is enrolled in `dbx` is rejected and iteration
88+
continues. The first verifying anchor that is not revoked authorizes the image.
89+
90+
The caller is responsible for extracting `AuthData` from the WIN_CERTIFICATE via
91+
`GetWinCertificatePkcs7AuthData` and computing the image's Authenticode digest under the
92+
algorithm reported by `GetAuthenticodeHashAlgorithm`.
93+
94+
@param[in] AuthData DER-encoded PKCS#7 SignedData payload.
95+
@param[in] AuthDataSize Size of AuthData in bytes; must be non-zero.
96+
@param[in] ImageHash Authenticode digest of the image under the algorithm
97+
AuthData uses.
98+
@param[in] ImageHashSize Size of ImageHash in bytes; must be non-zero.
99+
@param[in] Db Raw `db` contents, or NULL.
100+
@param[in] DbSize Size of Db in bytes; 0 when Db is NULL.
101+
@param[in] Dbx Raw `dbx` contents, or NULL.
102+
@param[in] DbxSize Size of Dbx in bytes; 0 when Dbx is NULL.
103+
104+
@retval TRUE The signature authorizes the image.
105+
@retval FALSE The signature does not authorize the image, or an error prevented a
39106
definitive answer.
40107
**/
41108
BOOLEAN
42-
IsSignedImageAuthorized (
43-
IN CONST EFI_IMAGE_DATA_DIRECTORY *SecDataDir,
44-
IN CONST VOID *Db,
45-
IN UINTN DbSize,
46-
IN CONST VOID *Dbx,
47-
IN UINTN DbxSize,
48-
IN OUT DIGEST_CACHE *Cache,
49-
IN OUT EFI_IMAGE_EXECUTION_ACTION *Action
109+
IsCertAuthorized (
110+
IN CONST UINT8 *AuthData,
111+
IN UINTN AuthDataSize,
112+
IN CONST UINT8 *ImageHash,
113+
IN UINTN ImageHashSize,
114+
IN CONST VOID *Db,
115+
IN UINTN DbSize,
116+
IN CONST VOID *Dbx,
117+
IN UINTN DbxSize
50118
);
51119

52120
/**
53-
Determine whether a signed PE/COFF image is revoked by the platform's `dbx` signature database.
121+
Determine whether a single embedded WIN_CERTIFICATE entry authorizes the image.
122+
123+
Extracts the PKCS#7 SignedData payload from `Cert`, identifies the Authenticode hash
124+
algorithm declared by the signature, computes (or retrieves from `Cache`) the image
125+
digest under that algorithm, then runs the certificate through `IsCertRevoked` and
126+
`IsCertAuthorized` in sequence. Any failure in the prelude, or a hit in `dbx`, causes
127+
the certificate to be skipped (FALSE). The certificate authorizes only when it is not
128+
revoked and `IsCertAuthorized` accepts it.
54129
55-
@param[in] SecDataDir Security data directory describing the embedded WIN_CERTIFICATE table.
56-
@param[in] Dbx The `dbx` signature database.
57-
@param[in] DbxSize The size of the `dbx`.
58-
@param[in,out] Cache DIGEST_CACHE pointer bound to the image being searched. The cache may
59-
be updated during the search.
60-
@param[in,out] Action Updated to reflect the outcome of the revocation check.
130+
@param[in] Cert The certificate to evaluate.
131+
@param[in,out] Cache Image digest cache bound to the image buffer; the cache may
132+
memoize one digest per algorithm across calls.
133+
@param[in] Db Raw `db` contents, or NULL.
134+
@param[in] DbSize Size of Db in bytes; 0 when Db is NULL.
135+
@param[in] Dbx Raw `dbx` contents, or NULL.
136+
@param[in] DbxSize Size of Dbx in bytes; 0 when Dbx is NULL.
61137
62-
@retval TRUE The image is revoked by `dbx`.
63-
@retval FALSE The image is not revoked.
138+
@retval TRUE The certificate is not revoked by `dbx` and authorizes the image via `db`.
139+
@retval FALSE The certificate is malformed, declares an unsupported hash algorithm, the
140+
image digest could not be computed, the certificate is revoked, or it
141+
does not authorize the image.
64142
**/
65143
BOOLEAN
66-
IsSignedImageRevoked (
67-
IN CONST EFI_IMAGE_DATA_DIRECTORY *SecDataDir,
68-
IN CONST VOID *Dbx,
69-
IN UINTN DbxSize,
70-
IN OUT DIGEST_CACHE *Cache,
71-
IN OUT EFI_IMAGE_EXECUTION_ACTION *Action
144+
IsImageAuthorizedByCert (
145+
IN CONST WIN_CERTIFICATE *Cert,
146+
IN OUT DIGEST_CACHE *Cache,
147+
IN CONST VOID *Db,
148+
IN UINTN DbSize,
149+
IN CONST VOID *Dbx,
150+
IN UINTN DbxSize
72151
);
73152

74153
#endif // DXE_IMAGE_VERIFICATION_LIB_CERTIFICATE_H_

SecurityPkg/Library/DxeImageVerificationLib2/Database.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -272,17 +272,17 @@ IsTBSCertHashInDbx (
272272
}
273273

274274
if (EFI_ERROR (Status)) {
275-
DEBUG ((DEBUG_WARN, "DxeImageVerificationLib: X509 hash computation failed; treating cert as revoked (%r).\n", Status));
275+
DEBUG ((DEBUG_WARN, "DxeImageVerificationLib: failed to compute X509 hash (%r).\n", Status));
276276
return TRUE;
277277
}
278278

279279
if (List->SignatureSize < sizeof (EFI_GUID) + DigestSize) {
280-
DEBUG ((DEBUG_WARN, "DxeImageVerificationLib: dbx signature size is malformed; treating cert as revoked.\n"));
280+
DEBUG ((DEBUG_WARN, "DxeImageVerificationLib: malformed dbx signature list size.\n"));
281281
return TRUE;
282282
}
283283

284284
if (EFI_ERROR (SigListIterInit (&ListIter, List))) {
285-
DEBUG ((DEBUG_WARN, "DxeImageVerificationLib: failed to iterate dbx signature list; treating cert as revoked.\n"));
285+
DEBUG ((DEBUG_WARN, "DxeImageVerificationLib: malformed dbx signature list.\n"));
286286
return TRUE;
287287
}
288288

SecurityPkg/Library/DxeImageVerificationLib2/DxeImageVerificationLib.c

Lines changed: 51 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
2222
#include "DxeImageVerificationLib.h"
2323
#include "Certificate.h"
2424
#include "Database.h"
25+
#include "Iterator.h"
2526
#include "Support.h"
2627
#include "Policy.h"
2728

@@ -120,15 +121,23 @@ ValidateUnsignedImage (
120121
/**
121122
Validate a signed PE/COFF image against the platform signature databases.
122123
124+
The verification flow is:
125+
1. Reject immediately if the image's Authenticode hash is enrolled in the `dbx`.
126+
2. Walk each WIN_CERTIFICATE in the image's security data directory to determine if the
127+
Auth Data from it is not revoked by the `dbx` and is authorized by the `db`. Only one
128+
WIN_CERTIFICATE needs to authorize the image for it to be validated.
129+
3. Authorize the image if the image's Authenticode hash is enrolled in the `db`.
130+
123131
@param[in] FileBuffer Pointer to the in-memory PE/COFF image.
124132
@param[in] FileSize Size of FileBuffer in bytes.
125133
@param[in] SecDataDir Security data directory describing the
126134
embedded WIN_CERTIFICATE table.
127135
@param[out] Action Set to the EFI_IMAGE_EXECUTION_ACTION value
128136
that best describes the outcome.
129137
130-
@retval EFI_UNSUPPORTED The signed-image verification path is not yet
131-
implemented.
138+
@retval EFI_SUCCESS The image is authorized.
139+
@retval EFI_ACCESS_DENIED The image is revoked, not authorized, or the
140+
databases could not be loaded.
132141
**/
133142
EFI_STATUS
134143
ValidateSignedImage (
@@ -138,12 +147,15 @@ ValidateSignedImage (
138147
OUT EFI_IMAGE_EXECUTION_ACTION *Action
139148
)
140149
{
141-
EFI_STATUS Status;
142-
DIGEST_CACHE Cache;
143-
VOID *Db;
144-
UINTN DbSize;
145-
VOID *Dbx;
146-
UINTN DbxSize;
150+
EFI_STATUS Status;
151+
BOOLEAN IsFound;
152+
DIGEST_CACHE Cache;
153+
VOID *Db;
154+
UINTN DbSize;
155+
VOID *Dbx;
156+
UINTN DbxSize;
157+
WIN_CERT_ITER CertIter;
158+
CONST WIN_CERTIFICATE *Cert;
147159

148160
Db = NULL;
149161
Dbx = NULL;
@@ -162,34 +174,55 @@ ValidateSignedImage (
162174

163175
//
164176
// Setup digest cache for the image. This prevents redundant authenticode hash computations
165-
// across both authorization and revocation checks.
177+
// across the image-hash revocation check, per-cert authorization, and the image-hash fallback.
166178
//
167179
ZeroMem (&Cache, sizeof (Cache));
168180
Cache.Type = DigestCacheTypeImage;
169181
Cache.Buffer = FileBuffer;
170182
Cache.BufferSize = FileSize;
171183

172184
//
173-
// Revocation is checked before authorization so that the authorization
174-
// path can write to PCR[7] with confidence that the image is not in dbx.
185+
// Step 1: Reject the image if its Authenticode hash is enrolled in `dbx`.
175186
//
176-
if (IsSignedImageRevoked (SecDataDir, Dbx, DbxSize, &Cache, Action)) {
177-
DEBUG ((DEBUG_ERROR, "DxeImageVerificationLib: Signed image is forbidden by DBX.\n"));
187+
Status = IsImageDigestInDatabase (Dbx, DbxSize, &Cache, &IsFound);
188+
if (EFI_ERROR (Status) || IsFound) {
189+
DEBUG ((DEBUG_ERROR, "DxeImageVerificationLib: Signed image hash is forbidden by DBX.\n"));
178190
Status = EFI_ACCESS_DENIED;
179191
goto Exit;
180192
}
181193

182194
//
183-
// Authorization check. Only reached when the image is not revoked.
195+
// Step 2: For each WIN_CERTIFICATE in the image's security data directory, extract the auth data
196+
// and check if it authorizes the image per the `db` and `dbx`.
184197
//
185-
if (!IsSignedImageAuthorized (SecDataDir, Db, DbSize, Dbx, DbxSize, &Cache, Action)) {
186-
DEBUG ((DEBUG_ERROR, "DxeImageVerificationLib: Signed image is not authorized by DB.\n"));
198+
Status = WinCertIterInit (&CertIter, FileBuffer, FileSize, SecDataDir);
199+
if (EFI_ERROR (Status)) {
200+
DEBUG ((DEBUG_ERROR, "DxeImageVerificationLib: WinCertIterInit failed (%r).\n", Status));
187201
Status = EFI_ACCESS_DENIED;
188202
goto Exit;
189203
}
190204

191-
*Action = EFI_IMAGE_EXECUTION_AUTH_SIG_PASSED;
192-
Status = EFI_SUCCESS;
205+
while ((Cert = WinCertIterNext (&CertIter)) != NULL) {
206+
if (IsImageAuthorizedByCert (Cert, &Cache, Db, DbSize, Dbx, DbxSize)) {
207+
*Action = EFI_IMAGE_EXECUTION_AUTH_SIG_PASSED;
208+
Status = EFI_SUCCESS;
209+
goto Exit;
210+
}
211+
}
212+
213+
//
214+
// Step 3: Authorize the image if the image authenticode hash is in the `db`.
215+
//
216+
Status = IsImageDigestInDatabase (Db, DbSize, &Cache, &IsFound);
217+
if (!EFI_ERROR (Status) && IsFound) {
218+
*Action = EFI_IMAGE_EXECUTION_AUTH_SIG_PASSED;
219+
Status = EFI_SUCCESS;
220+
goto Exit;
221+
}
222+
223+
DEBUG ((DEBUG_ERROR, "DxeImageVerificationLib: Signed image is not authorized by DB.\n"));
224+
*Action = EFI_IMAGE_EXECUTION_AUTH_SIG_NOT_FOUND;
225+
Status = EFI_ACCESS_DENIED;
193226

194227
Exit:
195228
if (Db != NULL) {

0 commit comments

Comments
 (0)