Skip to content

Commit 162c9eb

Browse files
committed
sbchooser: add --explain
This adds an explainer mode to sbchooser, which attempts to tell the user which PE binaries are allowed based on which db entries they are trusted or revoked by. Signed-off-by: Peter Jones <pjones@redhat.com>
1 parent 61101f3 commit 162c9eb

14 files changed

Lines changed: 322 additions & 11 deletions

docs/sbchooser.1.mdoc

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ Load the UEFI revoked key database from this system (default)
6666
Load an EFI binary from \fIpe\-file\fR.
6767

6868
By default, if \fB-i\fR is not used, \fBsbchooser\fR reads a list of input files on \fIstandard in\fR. If \fIpe-file\fR is \fB-\fR, \fBsbchooser\fR will look for input files on \fIstandard in\fR as well as any \fB-i\fR input options.
69+
.It Ao Fl e | Fl Fl explain Ac
70+
Instead of producing the normal results, attempt to explain the reason for
71+
trusting or distrusting each input PE file.
6972
.It Fl Fl Ec
7073
All following options are treated as input files. Can be used with \fB-i -\fR to suppliment \fIstandard in\fR.
7174
.El
@@ -177,6 +180,29 @@ host:~$ sbchooser -s -d db.shim-15.5.el7.x64.sha256 -- \\
177180
/usr/lib/shim/shim-15-2.fedora.x64.msft2011.efi
178181
host:~$
179182
.Ed
183+
.Ss Explanation of choosing which bootloader is most appropriate with local security databases:
184+
.Bd -literal
185+
host:~$ sbchooser --db db.msft2023 --dbx db.msft2011 --explain -- \\
186+
/usr/lib/shim/shim-13-0.2.fedora.x64.nosigs.efi \\
187+
/usr/lib/shim/shim-15-2.fedora.x64.msft2011.efi \\
188+
/usr/lib/shim/shim-15-7.el7_2.x64.msft2011.efi \\
189+
/usr/lib/shim/shim-15-7.el7_2.x64.nosigs.efi \\
190+
/usr/lib/shim/shim-15.5-1.el9.x64.msft2011.efi \\
191+
/usr/lib/shim/shim-15.5.el7.x64.nosigs.efi \\
192+
/usr/lib/shim/shim-16.1-4.el10.x64.msft2011.efi \\
193+
/usr/lib/shim/shim-16.1-4.el10.x64.msft2011.msft2023.efi \\
194+
/usr/lib/shim/shim-16.1-4.el10.x64.msft2023.efi
195+
/usr/lib/shim/shim-16.1-4.el10.x64.msft2011.msft2023.efi is trusted because cert "/C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft UEFI CA 2023 signer" is trusted by "/C=US/O=Microsoft Corporation/CN=Microsoft UEFI CA 2023" in db
196+
/usr/lib/shim/shim-16.1-4.el10.x64.msft2023.efi is trusted because cert "/C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft UEFI CA 2023 signer" is trusted by "/C=US/O=Microsoft Corporation/CN=Microsoft UEFI CA 2023" in db
197+
/usr/lib/shim/shim-16.1-4.el10.x64.msft2011.efi is not trusted because cert "/C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Windows UEFI Driver Publisher" is revoked by "/C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Corporation UEFI CA 2011" in dbx
198+
/usr/lib/shim/shim-15.5-1.el9.x64.msft2011.efi is not trusted because cert "/C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Windows UEFI Driver Publisher" is revoked by "/C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Corporation UEFI CA 2011" in dbx
199+
/usr/lib/shim/shim-15-7.el7_2.x64.msft2011.efi is not trusted because cert "/C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Windows UEFI Driver Publisher" is revoked by "/C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Corporation UEFI CA 2011" in dbx
200+
/usr/lib/shim/shim-15-2.fedora.x64.msft2011.efi is not trusted because cert "/C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Windows UEFI Driver Publisher" is revoked by "/C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Corporation UEFI CA 2011" in dbx
201+
/usr/lib/shim/shim-13-0.2.fedora.x64.nosigs.efi is not trusted because no certs or hashes trust it
202+
/usr/lib/shim/shim-15-7.el7_2.x64.nosigs.efi is not trusted because no certs or hashes trust it
203+
/usr/lib/shim/shim-15.5.el7.x64.nosigs.efi is not trusted because no certs or hashes trust it
204+
host:~$
205+
.Ed
180206
.Sh STANDARDS
181207
.Rs
182208
.%A UEFI Specification Working Group

src/sbchooser-pe.c

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,13 @@ update_cert_trust(sbchooser_context_t *ctx, cert_data_t *cert)
191191

192192
X509_NAME_oneline(cert->revoked_cert->subject, revoker, 4095);
193193

194+
if (cert->rationale) {
195+
free(cert->rationale);
196+
cert->rationale = NULL;
197+
debug("updating cert rationale to revoked");
198+
}
199+
asprintf(&cert->rationale, "cert \"%s\" is revoked by \"%s\" in dbx",
200+
subject, revoker);
194201
debug("cert \"%s\" revoked by \"%s\"", subject, revoker);
195202
cert->revoked = true;
196203
} else {
@@ -205,6 +212,11 @@ update_cert_trust(sbchooser_context_t *ctx, cert_data_t *cert)
205212
X509_NAME_oneline(cert->trust_anchor_cert->subject,
206213
trust_anchor, 4095);
207214

215+
if (!cert->rationale) {
216+
debug("updating cert rationale to trusted");
217+
asprintf(&cert->rationale, "cert \"%s\" is trusted by \"%s\" in db",
218+
subject, trust_anchor);
219+
}
208220
debug("cert \"%s\" trusted by \"%s\"", subject, trust_anchor);
209221
cert->trusted = true;
210222
} else {
@@ -308,11 +320,19 @@ update_sig_trust(sbchooser_context_t *ctx, sig_data_t *sig)
308320

309321
update_cert_trust(ctx, sigcert);
310322

311-
if (sigcert->trusted)
323+
if (sigcert->trusted) {
324+
if (!sig->revoked) {
325+
debug("updating sig rationale to trusted");
326+
sig->rationale = sigcert->rationale;
327+
}
312328
sig->trusted = true;
329+
}
313330

314-
if (sigcert->revoked)
331+
if (sigcert->revoked) {
332+
debug("updating sig rationale to revoked");
333+
sig->rationale = sigcert->rationale;
315334
sig->revoked = true;
335+
}
316336
}
317337
if (sig->revoked)
318338
sig->trusted = false;
@@ -931,9 +951,18 @@ update_pe_security(sbchooser_context_t *ctx, pe_file_t *pe)
931951

932952
update_sig_trust(ctx, sig);
933953

954+
if (sig->rationale && !pe->rationale) {
955+
debug("updating pe rationale to %s", sig->revoked ? "revoked" : (sig->trusted ? "trusted" : ""));
956+
pe->rationale = sig->rationale;
957+
}
958+
934959
if (sig->trusted) {
935-
pe->has_trusted_signature = true;
936960
found_trusted_sig = true;
961+
if (!pe->has_trusted_signature && pe->rationale) {
962+
debug("updating pe rationale to %s", sig->revoked ? "revoked" : (sig->trusted ? "trusted" : ""));
963+
pe->rationale = sig->rationale;
964+
}
965+
pe->has_trusted_signature = true;
937966

938967
if (sig->lowest_md_secbits < lowest_md_secbits) {
939968
lowest_md_secbits = sig->lowest_md_secbits;

src/sbchooser-pe.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ struct sig_data {
7171
*/
7272
const ASN1_TIME *earliest_not_before;
7373
const ASN1_TIME *latest_not_after;
74+
75+
/*
76+
* why was this revoked or trusted? Borrowed from certs
77+
*/
78+
char *rationale;
7479
};
7580

7681
typedef struct sig_data sig_data_t;
@@ -120,6 +125,11 @@ struct pe_file {
120125
const ASN1_TIME *earliest_not_before;
121126
const ASN1_TIME *latest_not_after;
122127

128+
/*
129+
* why was this revoked or trusted? Borrowed from sigs
130+
*/
131+
char *rationale;
132+
123133
bool first_sig_only; // should only the first signature be scored?
124134
};
125135

src/sbchooser-x509.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,12 @@ free_cert(cert_data_t *cert)
112112
X509_free(cert->x509);
113113
cert->x509 = NULL;
114114
}
115+
116+
if (cert->rationale) {
117+
free(cert->rationale);
118+
cert->rationale = NULL;
119+
}
120+
115121
free(cert);
116122
}
117123

src/sbchooser-x509.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ struct cert_data {
4444

4545
bool revoked;
4646
cert_data_t *revoked_cert; // the cert that's actually in dbx
47+
48+
char *rationale; // why was this revoked or trusted
4749
};
4850

4951
void free_cert(cert_data_t *cert);

src/sbchooser.c

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ add_one_pe_to_ctx(sbchooser_context_t *ctx, const char *filename)
8888
int
8989
main(int argc, char *argv[])
9090
{
91-
const char sopts[] = ":d:Dfi:sSx:Xvh";
91+
const char sopts[] = ":d:Defi:sSx:Xvh";
9292
const struct option lopts[] = {
9393
{"db", required_argument, NULL, 'd' },
9494
{"no-system-db", no_argument, NULL, 'D' },
@@ -97,6 +97,7 @@ main(int argc, char *argv[])
9797
{"no-system-dbx", no_argument, NULL, 'X' },
9898
{"system-dbx", no_argument, NULL, 'S' },
9999
{"first-sig-only", no_argument, NULL, 'f' },
100+
{"explain", no_argument, NULL, 'e' },
100101
{"in", required_argument, NULL, 'i' },
101102
{"verbose", no_argument, NULL, 'v' },
102103
{"usage", no_argument, NULL, 'h' },
@@ -109,6 +110,7 @@ main(int argc, char *argv[])
109110
bool needs_db = true;
110111
bool needs_dbx = true;
111112
bool read_inputs_from_stdin = false;
113+
bool explain = false;
112114

113115
sbchooser_context_t ctx;
114116

@@ -149,6 +151,9 @@ main(int argc, char *argv[])
149151
argv[optind-1]);
150152
needs_db = false;
151153
break;
154+
case 'e':
155+
explain = true;
156+
break;
152157
case 'f':
153158
ctx.first_sig_only = true;
154159
break;
@@ -278,6 +283,8 @@ main(int argc, char *argv[])
278283

279284
for (size_t i = 0; i < ctx.n_files; i++) {
280285
pe_file_t *pe = ctx.files[i];
286+
digest_data_t *dgst = NULL;
287+
char buf[1024];
281288

282289
/*
283290
* UEFI spec 2.12ish says:
@@ -302,21 +309,43 @@ main(int argc, char *argv[])
302309
*
303310
* And so we check dbx hashes first, then db.
304311
*/
305-
if (is_revoked_by_hash(pe, NULL)) {
306-
debug("PE \"%s\" is revoked by hash", pe->filename);
312+
if (is_revoked_by_hash(pe, &dgst)) {
313+
fmt_digest(dgst, buf, sizeof(buf));
314+
debug("PE \"%s\" is revoked by hash %s", pe->filename, buf);
315+
if (explain) {
316+
printf("%s is revoked by hash %s in dbx\n", pe->filename, buf);
317+
}
307318
continue;
308319
}
309-
if (is_trusted_by_hash(pe, NULL)) {
310-
debug("PE \"%s\" is trusted by hash", pe->filename);
311-
printf("%s\n", pe->filename);
320+
dgst = NULL;
321+
if (is_trusted_by_hash(pe, &dgst)) {
322+
fmt_digest(dgst, buf, sizeof(buf));
323+
debug("PE \"%s\" is trusted by hash %s", pe->filename, buf);
324+
if (explain) {
325+
printf("%s is trusted by hash %s in db\n", pe->filename, buf);
326+
} else {
327+
printf("%s\n", pe->filename);
328+
}
312329
continue;
313330
} else {
314331
debug("PE \"%s\" is not trusted by hash", pe->filename);
315332
}
316333

317334
debug("PE \"%s\" score 0x%"PRIx32, pe->filename, pe->secbits);
318-
if (pe->secbits != 0) {
319-
printf("%s\n", pe->filename);
335+
if (pe->secbits == 0) {
336+
if (explain) {
337+
if (!pe->rationale) {
338+
printf("%s is not trusted because no certs or hashes trust it\n", pe->filename);
339+
} else {
340+
printf("%s is not trusted because %s\n", pe->filename, pe->rationale);
341+
}
342+
}
343+
} else {
344+
if (explain) {
345+
printf("%s is trusted because %s\n", pe->filename, pe->rationale);
346+
} else {
347+
printf("%s\n", pe->filename);
348+
}
320349
}
321350
}
322351

0 commit comments

Comments
 (0)