A little background
When we talk about UEFI Secure Boot, a lot of times we talk about what we call the whitelist and the blacklist. These databases go by many names—formally
d719b2cb-3d3a-4596-a3bc-dad00e67656f:dbx, but most often just
dbx. These two1 databases, stored in UEFI Authenticated Variables2, constitute lists of which binaries can and cannot be executed within a UEFI environment when Secure Boot is enabled.
When a UEFI binary is loaded, the system first checks to see if any
dbx are applicable to that binary. A
may apply in three ways—it may contain the hash of a specific binary,
an X.509 certificate, or the hash of a certificate3. If any of these
matches the binary in question, UEFI raises the error
EFI_SECURITY_VIOLATION, and your binary will not go to space today.
If a binary successfully passes that hurdle, then the same verification
method is processed with
db, the whitelist, but with the opposite
policy: if any entry describes the binary in question, verification has
succeeded. If no entry does,
EFI_SECURITY_VIOLATION is raised.
The need for updates
When a UEFI binary is discovered to have a serious security flaw which would allow a malicious user to circumvent Secure Boot, it becomes necessary to prevent it from running on machines. When this happens, the UEFI CA issues a dbx update. The mechanism for the update is a file that’s structured as a UEFI Authenticated Variable append. This file gets distributed as part of an OS update, and when the update is applied, the UEFI variable is updated.
One mechanism for doing this in Linux is via dbxtool. In Fedora, the
dbxtool package includes the current
dbx updates in
/usr/share/dbxtool/DBXUpdate-2014-04-13-22-14-00.bin, as well as a
systemd service to apply them during boot4. In the special case of
shim being added to a blacklist, we would include a
dependency on the fixed version of
shim in the
dbxtool package so
that systems would remain bootable.
The structure of a `dbx` update
dbx variable is composed of an array of
structures, each themselves containing an array of
dbx update is that same structure, but wrapped in a
EFI_VARIABLE_AUTHENTICATION_2 structure to authenticate it. The
definitions look like this5:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
Conceptually, this means the structure we’ve got is:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
So a full update looks something like this:
2010-03-06 19:17:21 GMT+0000
It is 0x00000cbd bytes long6.
It’s revision is 2. It is always revision 2.
0x0ef1, which as we see above is
WIN_CERT_TYPE_EFI_GUID. The interesting
bit here is that
.bCertificate isn’t quite a real thing. This is
actually describing that
auth2.AuthInfo is a
.bCertificate is actually the fields other than
auth2.AuthInfo.CertData. Again, this
shouldn’t confuse you at all.
As a result, next, in the place of
.bCertificate, we have:
.CertData is verified against
EFI_SIGNATURE_DATA entries in
the Key Exchange Keys database (
KEK), but only those entries which are
EFI_SIGNATURE_LIST structures with
1 2 3 4 5
Yep, that’s an ASN.1 DER encoding of a PKCS-7 Certificate.
That’s the end of the
EFI_VARIABLE_AUTHENTICATION_2 structure, and so on
to the actual data:
That’s c1c41626-504c-4092-aca9-41f936934328, aka
The list size is 0x00001cc bytes
This is actually always 0.
0x00000030 bytes. That’s 16 bytes for the
efi_guid_t, and since
EFI_GUID_SHA256, 32 bytes of SHA-256 data.
77fa9abd-0359-4d32-bd60-28f4e78f784b , which is the GUID Microsoft uses to identify themselves7
1 2 3
This is the actual SHA-256 data. It goes on like this:
1 2 3 4 5 6 7 8 9 10 11 12
How the structure is used
When you apply a database update, you’re basically doing a
call to UEFI with a couple of flags set:
1 2 3 4 5 6
These flags tell the firmware some crucial things – that this variable
is authenticated with the
EFI_VARIABLE_AUTHENTICATION_2 structure, that
this is an append, that both Boot Services (i.e. the firmware) and Runtime
Services (i.e. the OS) should have access to it, and that it should
persist across a reboot. As a special case in the spec, an append has a
special meaning for the UEFI security databases:
For variables with the GUID EFI_IMAGE_SECURITY_DATABASE_GUID (i.e. where the data buffer is formatted as EFI_SIGNATURE_LIST), the driver shallnot perform an append of EFI_SIGNATURE_DATA values that are already part of the existing variable value.
Note: This situation is not considered an error, and shall in itself not cause a status code other than EFI_SUCCESS to be returned or the timestamp associated with the variable not to be updated.
As a result, what happens here is that the first time you write to
EFI_SIGNATURE_LIST structures and the
entries they contain get added to the variable, but not the
EFI_VARIABLE_AUTHENTICATION_2 structure. Then when later
updates are issued, they contain a superset of the previous ones. When
you apply them, the firmware only appends the difference to the
Obviously a system this complex needs some tools. To manage these
databases on linux, I’ve written a tool called
dbxtool, which may
be available in your linux distribution of choice. It can be used to
dbx changes8, to list the contents of the UEFI Security
Databases, and to list the contents of updates files:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
As usual, there are a couple of places where vendors have not gotten everything quite right, and sometimes things fail to work correctly—but that’s for another post.
There is actually a third in this set,
dbt, which can be used in revocation processing.↩
Strictly speaking these are only an analog to Authenticated Variables, which can only be appended to, replaced, or deleted by updates signed with the key that created them. Key database updates are instead controlled by a list of keys stored in another variable called
KEK– the Key Exchange Keys. Otherwise the mechanism is the same.↩
This is currently disabled by default in Fedora. I’m looking at enabling this as an F22 feature. Getting these things right is important, and it takes time.↩
I have left out the definitions of
efi_guid_t; they are quite boring.↩
dwLengthincludes the size of
Hdritself (that is, the size of
wCertificateType) as well as the data following it (
bCertificate). Because we live in the best of all possible worlds, Authenticode signatures—the signatures on binaries themselves—use the same structure but only include the size of
Hdr.bCertificate. This hasn’t ever confused anybody.↩
Big congratulations to Acer, who used 55555555-5555-5555-5555-555555555555 for this in one of their db entries. Not only did they win the random number generator lottery to get that, but also experienced a minimum of 3 single bit errors, since the closest valid GUID to that is 55555555-5555-4555-9555-555555555555.↩
KEKupdates, but those are much more rare.↩