The Uncoöperative Organization

Programming and other human stuff.

Another Benefit of Secure Boot

One thing we’ve heard several times during the discussions of the UEFI “Secure Boot” mechanism is that there’s no genuine security benefits. In fact there are several. The ability to guard against “bootkit” systems has been covered elsewhere, so I’ll not cover it here. Instead, I’d like to focus on a less publicized aspect of Secure Boot’s mechanism.

With secure boot, it’s possible to verify any UEFI application which has been signed. There’s a reasonable standard of attestation that if a binary has been signed by our key, it was consciously signed by us.

A simple example

One side effect of this is that with a few hoops, we can sign arbitrary data, and wrap it in a UEFI envelope. Such code would look something like:

data.c
1
2
3
4
5
6
7
8
9
10
11
12
#include <efi.h>
#include <efilib.h>

EFI_STATUS
efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *systab)
{
  InitializeLib(image_handle, systab);
  Print(L"This program contains data.\n");
  return EFI_SUCCESS;
}

int data __attribute__ ((__section__(".data.hellothere"))) = 3;

At this point you’d build it like any gnu-efi application, and then have it signed as a UEFI application.

At this point you’ve got an envelope for data which is signed and explicitly trusted by your hardware platform. At runtime, you can verify the signature against DB, the UEFI key database, and verify that this is a trusted binary. At that point in time you can unwrap the UEFI binary to get at the ELF DSO that fundamentally lies underneath, and examine the DSO. If you do “eu-readelf —section-headers ${file}” on the DSO, you’ll see that there are 21 section headers. Here’s a subset:

Section Headers:
[Nr] Name Type Addr Off Size ES Flags Lk Inf Al
[ 0] NULL 0000000000000000 00000000 00000000 0 0 0 0
[ 1] .hash HASH 0000000000000000 00200000 00000690 4 A 9 0 8
[ 2] .eh_frame PROGBITS 0000000000001000 00201000 000016b0 0 A 0 0 8
[ 3] .text PROGBITS 0000000000003000 00203000 000053bd 0 AX 0 0 16
[ 4] .reloc PROGBITS 0000000000009000 00209000 0000000a 0 A 0 0 1
[ 5] .data.hellothere PROGBITS 000000000000a000 0020a000 00000004 0 WA 0 0 4
[ 6] .data PROGBITS 000000000000a020 0020a020 00001770 8 WA 0 0 32

.data.hellothere starts at 0x20a000 and is 4 bytes long.

Something with more substance

What’s this useful for, you ask? Well, let’s assume we’ve got a slightly different binary:

keys.c
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
#include <efi.h>
#include <efilib.h>

EFI_STATUS
efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *systab)
{
  InitializeLib(image_handle, systab);
  Print(L"This program contains a list of public keys.\n");
  return EFI_SUCCESS;
}

struct key {
  int size;
};

struct keylist {
  int numkeys;
};

#define KEYDATA \
"-----BEGIN PGP PUBLIC KEY BLOCK-----\n" \
"Version: GnuPG v1.4.5 (GNU/Linux)\n" \
"\n" \
"mQINBE8MkeIBEADfn8QtElquxUAVvS8th7UnVmfkExw7jC7SVy7dGlXo5rgxqETM\n" \
"AZorpDIvAtKX5VMr/lUfODB6sSymh0e6EdvYQfHrpImO05F7WBq2DhRW74j1DRNu\n" \
"8vohKPTsSZTEZo/mrUBDAAGtGOXcrsQxx4J2Ur73+r18ODd+v6O33YevlFmwmFYT\n" \
"uDcgdhxyxBdpJVES8MdxO349uP9bvrU+3KjpDDGHb/hMbY8az7lwLtBufRrAekhN\n" \
"5Cg7+zm+I+wVGzgzSw0yrIh4hdVts4RKZIrl2N3VeZcCY4IrNZFd2Do6HhIwA7l/\n" \
"/xpTEBTZ4BmnGP9iufUVa2h97/JmKy38rM88IovIcTSnYDG68k/NxUkC+dLriI4T\n" \
"BNc9kJLVo821x77WYViNXHscF8ujlf73HilfCnhEtNwViGO7x41guQrdv5k79UDf\n" \
"1CylSZ+76vKXziz6uNHzIagiViNOvYHOoEH8jDNnubqFTxYXUNks5l3/byllQ0XK\n" \
"Oj9RarmjsMS3udRB6RCiEnzDbPge2S9gmdmY8MA3QeZ3aWoZJqygDQ0WNUOjPZ0E\n" \
"80x+xbUyAK06/SCzs6bUx6GMXd5Iy5r3Gc+6LRt4WEG5r/vJEHPSj3qAoVqydsY0\n" \
"GjASPIQmYduCdi7Inf0w+2n+nJGPcK1k432xHD0z0kp1Q/xcOH/Tul3bcQARAQAB\n" \
"tCZGZWRvcmEgKDE3KSA8ZmVkb3JhQGZlZG9yYXByb2plY3Qub3JnPokCNgQTAQIA\n" \
"IAUCTwyR4gIbDwYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJEFDpTJkayjRlKxAQ\n" \
"AICtQT+j8Sum1J86yBwso1wuzK2sHOXbfC1LCdQw2u6QLfKSUsCnQ5AL+oCnS491\n" \
"5fjrYksT2siclLhgZX7/yF76XuYHHhRTO65NaPSCxxhN6S9zbExUPRoxFL1ay0cH\n" \
"p14WYI1/SGmKPcJmigV6n8+wGYl+zWlH9eiiFP/+JCxJ0ZvRg3mgT5zcPdIzTDOz\n" \
"4rTE3WfH3qqxYhw2ttDPomBSdbgJw2N6bj46t8rljIDGdeKpFHYMVXpUp0gDkEoY\n" \
"bAiQhvZaa2F6mGMfXRdr4Phs562+tF/Qy/JaK8uafYZPHTtC16NHf87DYceLzvci\n" \
"W1KNBYGrEHm5NMguBlqP348FYMp+6hJDYMl38Qx3BK9bz6lW0G75tgaAZ+sxAFGs\n" \
"/MUcjChkF9OcF7Y7W6hY3IQb/+FgB5eNpBqfmZZ76ywpS+D0sIqxzWoILtlXdHeN\n" \
"eM4YiX0gvVQXwn9S89O5vWKxWYspywfgV/aXHk14O6k7oi8wInJOvgxZKEHUT4XU\n" \
"K7DyogL45VV9iYoWYIym2L17VBnINE/Kwmc/81nYigE0tTOwaR+qMFBSRlkOA4+X\n" \
"ZlIvb5Cft7CdC183FYLIM+B6xNSKE/OhavHZsRJrLZC1aAP8MNh8Cy7Jqrn1/Qjj\n" \
"6sncrqaj/Yis4yAaINk4MrnbImN2MwzU70q0eR3kA6PY\n" \
"=3+4G\n" \
"-----END PGP PUBLIC KEY BLOCK-----\n"

struct keylist keyhdr __attribute__ ((__section__(".keylist"))) =
  { 1 };
struct key key __attribute__ ((__section__(".keylist"))) =
  { sizeof(KEYDATA) };
uint8_t pubkey[] __attribute__ ((__section__(".keylist"))) = KEYDATA ;

With this code, the extracted DSO has a section like this:

[ 5] .keylist PROGBITS 000000000000a000 0020a000 00000680 0 WA 0 0 32

But wait, there’s more

One additional component here is packaging. If we package the file on a per-repo basis, with a well known name, there’s a bit more.

So let’s say, for example, that we can have ${repo}-release provide a file, /etc/pki/sbkeys/${repo}-keys.efi, which looks roughly like what’s shown above.

At this point, we can add the extraction code to anaconda and kickstart, and we can automatically import trusted keys for any repository that includes a correctly signed keyfile. This is bug 253897, a bug that’s been open for 5 years.

Wait for it

There’s an even larger goal here – bug 253897 is the remaining blocker on bug 998, open since 1999.

With the above system, we can enable signature checking for all packages during installation.