when I said some things about Secure Boot in June of 2014. I …">

The Uncoöperative Organization

Programming and other human stuff.

Secure Boot — Fedora, RHEL, and Shim Upstream Maintenance: Government Involvement or Lack Thereof

You probably remember when I said some things about Secure Boot in June of 2014. I said there’d be more along those lines, and there is.

So there’s another statement about that here.

I’m going to try to remember to post a message like this once per month or so. If I miss one, keep an eye out, but maybe don’t get terribly suspicious unless I miss several in a row.

Note that there are parts of this chain I’m not a part of, and obviously linux distributions I’m not involved in that support Secure Boot. I encourage other maintainers to offer similar statements for their respective involvement.

Secure Boot — Fedora, RHEL, and Shim Upstream Maintenance: Government Involvement or Lack Thereof

You probably remember when I said some things about Secure Boot in June of 2014. I said there’d be more along those lines, and there is.

So there’s another statement about that here.

I’m going to try to remember to post a message like this once per month or so. If I miss one, keep an eye out, but maybe don’t get terribly suspicious unless I miss several in a row.

Note that there are parts of this chain I’m not a part of, and obviously linux distributions I’m not involved in that support Secure Boot. I encourage other maintainers to offer similar statements for their respective involvement.

Secure Boot — Fedora, RHEL, and Shim Upstream Maintenance: Government Involvement or Lack Thereof

You probably remember when I said some things about Secure Boot in June of 2014. I said there’d be more along those lines, and there is.

So there’s another statement about that here.

This message fixes a small grammar error from the previous one and adds a section about CentOS and Secure Boot.

I’m going to try to remember to post a message like this once per month or so. If I miss one, keep an eye out, but maybe don’t get terribly suspicious unless I miss several in a row.

Note that there are parts of this chain I’m not a part of, and obviously linux distributions I’m not involved in that support Secure Boot. I encourage other maintainers to offer similar statements for their respective involvement.

Secure Boot — Fedora, RHEL, and Shim Upstream Maintenance: Government Involvement or Lack Thereof

After the big kerfuffle with TrueCrypt, it seems clear to me that I need to make some statements about Secure Boot and any interaction with governments whose regimes I fall under.

So there’s a statement about that here.

I’m going to try to remember to post a message like this once per month or so. If I miss one, keep an eye out, but maybe don’t get terribly suspicious unless I miss several in a row.

Note that there are parts of this chain I’m not a part of, and obviously linux distributions I’m not involved in that support Secure Boot. I encourage other maintainers to offer similar statements for their respective involvement.

The EFI System Partition and the Default Boot Behavior

A lot of the time, we talk about creating a partition to serve as the EFI System Partition. This partition is mandated by the UEFI specification for several tasks. Adam covered what’s going on at a relatively high level on his recent blog post, and you should read the whole thing:

An ‘EFI system partition’ is really just any partition formatted with
one of the UEFI spec-defined variants of FAT and given a specific GPT
partition type to help the firmware find it. And the purpose of this is
just as described above: allow everyone to rely on the fact that the
firmware layer will definitely be able to read data from a pretty
‘normal’ disk partition.

There’s nothing truly special about an ESP. It isn’t an ESP because of the GPT GUID and label, nor because of the file system type. Those are how the firmware identifies a partition, and the file system it contains, as candidates to treat as the ESP, when it really needs to find one. The only factor in determining if a partition is the ESP is this: is the firmware attempting to use it as the ESP?

At the same time, the requirements for the ESP give us latitude; we know that we can use UEFI’s APIs to find correctly constructed FAT file systems, but there’s no need for those to be the ESP. In fact, even when we create multiple partitions with the ESP’s GUID and label, there’s no requirement that the firmware looks at more than one of them if it needs to find the ESP, and there’s no guarantee as to which one it will pick, either.

Normal booting

Thankfully, most of the time we don’t care. Under normal circumstances, the system isn’t actually looking for something to fill the role of the ESP. When the system starts up, it consults the BootOrder, which is just a list of 16-bit numbers, such as BootOrder: 0001,001A,0003. When this is found, the firmware will iterate the list, as you might expect. For each entry in the list, it looks for a corresponding Boot#### variable — Boot0001 for 0001, Boot001A for 0x001a, and so on, with the value from BootOrder rendered in capitalized hexadecimal. If it doesn’t find the variable, it continues to the next entry in BootOrder. If it does find the variable, it reads the contents of the variable to try to decide what to do. Generally speaking, what’s in there is a human friendly label, a Device Path like ACPI(a0341d0,0)PCI(1f,2)SATA(0,0,0)HD(1,800,64000,12029cda-8961-470d-82ba-aeb17dba91a5)File(\EFI\fedora\shim.efi), and optional data to be passed to whatever program is to be loaded. This Device Path reflects the series of parts that need to be initialized in order to find the ultimate media device HD(1,800,64000,12029cda-8961-470d-82ba-aeb17dba91a5)1, which specifies a partition number, the partition’s offset and size, and the partition’s GUID.

This partition need not be an “EFI System Partition” properly; the firmware doesn’t care if the GUID in the partition table is the ESP GUID, only that it matches the GUID in the Device Path. Likewise, it doesn’t care about the label. This file system may be the ESP, but there’s no requirement. The only real requirement here is that we have to use FAT, because it’s the only file system the firmware is guaranteed to know about.

At this point, the system firmware is going to initialize each device that’s part of that Device Path, in order, that hasn’t already been initialized. Some things will always be initialized — it’s unlikely that ACPI tables and the PCI root hub specified in this particular path2 weren’t already needed to get this far — but other things, like the SATA controller or its port 0, port multiplier 0, LUN 0 device, SATA(0,0,0)3, will probably need to be initialized. Once that is done, the firmware will examine the disk and see if it has a partition matching the HD() path above. If it finds that, and it contains a FAT file system, it looks for File(\EFI\fedora\shim.efi).

If any of that goes wrong, it moves on to the next Boot#### entry, and follow the exact same process, initializing one peripheral at a time until it can load a file from the FAT it finds.

What if everything we know is wrong?

If all of that fails, and there is no working boot entry found by traversing BootOrder, there are still things to do. At this point, the firmware will start initializing all peripherals it can find, in whatever order it happens to choose — for some peripherals this will be linear and predictable, for some it will start things all at once and they’ll respond in whatever order they become ready. For each media device it finds, it will check if it’s a removable device, such as optical media, or a fixed device like a hard drive. At this phase, the firmware only considers removable devices. On each of those, it looks for an EFI System Partition4 with a FAT file system using whatever method it knows for that type of media, and if it finds one, it checks for\EFI\BOOT\BOOTX64.EFI5.

Typically, if it finds a removable device it can boot, that’s because you have your OS install image connected in some way, which may well be an indicator that you’re trying to start the OS installer. It’s also possible you’ve installed to removable media, or you’re running off a live image.

If it doesn’t find suitable removable media, or if a failure condition is returned by the application it tries to start, it continues traversing all removable media until it has exhausted the possibilities.

If it still hasn’t found any removable media it can boot, the firmware will move on to fixed media. At this point, all media is probably discovered — you may have hot-plugged a USB stick or something, but don’t plan on that sort of action working reliably at this stage. Since there’s no real device or media discovery left to do, the firmware is going to iterate back over the devices once more, and will try to start each fixed media device in the same manner it previously did with the removable devices – it will look for an ESP, and try to start \EFI\BOOT\BOOTX64.efi. This is known as the “Default Boot Behavior”.

The Default Boot Behavior

On Fedora systems, and likely any other OS using shim, we handle the Default Boot Behavior through a utility shim provides named fallback.efi. Our signed shim package provides several files, all of which reside on whichever partition we’ve mounted as the ESP, which is mounted at /boot/efi:

1
2
3
4
5
6
/boot/efi/EFI/BOOT/BOOTX64.EFI
/boot/efi/EFI/BOOT/fallback.efi
/boot/efi/EFI/fedora/BOOT.CSV
/boot/efi/EFI/fedora/MokManager.efi
/boot/efi/EFI/fedora/shim-fedora.efi
/boot/efi/EFI/fedora/shim.efi

When we’re operating under the Default Boot Behavior, the system firmware launches the Device Path File(EFI\BOOT\BOOTX64.EFI), which corresponds to /boot/efi/EFI/BOOT/BOOTX64.EFI on our file system. This is actually just another copy of /boot/efi/EFI/fedora/shim.efi, but it behaves slightly differently.

Upon startup, shim checks to see if it’s been launched from \EFI\BOOT6. If it has, it checks to see if there’s another file in this directory called fallback.efi, which our shim package provides on an installed system, but which we purposefully omit from removable media. If it finds fallback.efi, it executes it as a normal UEFI application.

fallback.efi

It is expected that this default boot will load an operating system or a maintenance utility. If this is an operating system setup program it is then responsible for setting the requisite environment variables for subsequent boots.

UEFI Specification section 3.3 Revision 2.4

As you may well have come to realize, the purpose of fallback.efi is to rebuild boot options in the case that BootOrder or the Boot#### variables it references have been destroyed, or the case that you’ve moved a fixed disk between machines with the intent on booting from it.

When fallback runs, it queries the firmware for the disk from which it was loaded. It then iterates over every subdirectory of \EFI on that disk that isn’t BOOT, looking for files named BOOT.CSV. On Fedora, we provide such a file in \EFI\fedora\BOOT.CSV. This file is a UCS-2 file of comma separated values, and its contents look like this:

1
shim.efi,Fedora,,This is the boot entry for Fedora

For each valid entry in each BOOT.CSV file it finds, fallback creates a new Boot#### variable and appends it to BootOrder. In this case it creates a boot entry with the label ‘Fedora’, with a Device Path that points to the disk fallback was run from, and the file path corresponding to the directory in which shim found this particular BOOT.CSV, with the first entry in the CSV appended: File(\EFI\fedora\shim.efi)

Once fallback has finished iterating all the CSV files in all the directories other than BOOT, it boots the first option it has added. On the next reboot, there will be a BootOrder variable, and its first entries will be whichever Boot#### variables fallback created.


  1. Astute readers will no doubt notice that often we have device paths with only the HD(1,800,64000,12029cda-8961-470d-82ba-aeb17dba91a5) and File(\EFI\fedora\shim.efi) components. The spec allows this explicitly, and in this case it initializes every peripheral in no particular order until it finds a partition matching that Hard Disk Media Device Path, and the appropriate file on it.

  2. In this case ACPI(a0341d0,0) represents the PCI Express Root Port on my CPU, and PCI(1f,2) is what lspci whould show as 00:1f.2 SATA controller: Intel Corporation 6 Series/C200 Series Chipset Family SATA AHCI Controller (rev 05).

  3. Yes, seriously, SATA(0,0,0) represents the disk and HD(1,800,64000,12029cda-8961-470d-82ba-aeb17dba91a5) represents the partition on it. I’m so sorry.

  4. Strictly speaking, the firmware is required to check one partition which has the correct GUID and label in GPT and is a FAT file system. It is allowed to check partitions without that GUID and label, but you can’t depend on it, and it is allowed to check other partitions that have file systems it understands, but you can’t depend on that either.

  5. Or whatever file name is appropriate on your architecture.

  6. I’m using backslashes for paths seen by code that’s running under UEFI, and forward slashes for code running under Linux, because that’s what those parts of the system typically use.

UEFI Binary Signature Alignment Requirements

James Bottomley recently posted an item regarding problems he’s seen involving UEFI binaries’ signature alignment, and I’d like to fill in a bit more detail.

UEFI systems use Microsoft’s PE/COFF binary format, with a modification in the headers to reflect the status as UEFI binaries rather than Windows Executables or DSOs. When the UEFI Spec Working Group (USWG) began considering how to specify Secure Boot, the obvious route was to use Microsoft’s binary signing system, Authenticode. Authenticode appears, at first glance, to be a straightforward signing system. Specific parts of a binary are digested with a cryptographic hash — in Secure Boot’s case, generally SHA-256 — and that hash is used in a PKCS#7 signature. The result is embedded in a special section of the binary, the “Certificate Table”. Neither the pointer to the Certificate Table, nor the table itself are included in the hashing, and so the same algorithm can be used during verification. Hash the binary, find a signature in the Certificate Table, and compare the stored hash from that signature to the one you’ve computed. If they match, the signature is for this binary, if they don’t, the binary has been modified. Once the hash has been verified, it’s a simple matter of checking the authorization of the signer against your system’s certificate databases, and you know whether to allow execution.

Except it’s not that simple.

In a PE/COFF binary, the Certificate Table is found by looking at a particular entry in an array known as the Data Directory. This entry contains a file offset for the table, as well as the size that the table takes up in the binary. Inside the table itself, there’s a header with a size, a version code, and a GUID to show that the body is an PKCS#7 signature. Given that it’s described as a table and clearly set up so the container can be larger than a given signature, the natural conclusion is that this is an array, and multiple signatures are allowed.

When I implemented pesign, I erroneously assumed that this was a packed array of signatures. When multiple signature support was added to Tiano, the UEFI reference implementation, the same assumption was made. Testing commenced, and the two worked together just fine.

Some time later, somebody noticed that the Tiano code was not in compliance with the specification. PE/COFF version 6.0 states:

Notice that certificates always start on an octaword boundary. If a certificate is not an even number of octawords long, it is zero padded to the next octaword boundary. However, the length of the certificate does not include this padding and so any certificate navigation software must be sure to round up to the next octaword to locate another certificate.

Aside from the constant use of the word “certificate” to refer to a signature, or the use of “octaword”, which has never confused anybody, I’m sure, what this says is that if you compute the address to put the Certificate Table, and that address isn’t on an 8 byte boundary — that is, the address modulo 8 is not zero — then you should round it up to the next 8 byte boundary. But it doesn’t really cover the array-like nature of the table — in fact, this version of the specification doesn’t really treat it as more than a single entry, though the data structure to allow you to treat it as an array is present.

In version 8.0, which UEFI binaries were standardized against, the format slightly changed, as did the language. The only mention of this alignment requirement is now (in 8.0 and 8.3):

The attribute certificate table is composed of a set of contiguous, octaword-aligned attribute certificate entries.

and

Notice that certificates always start on an octaword boundary. If a certificate is not an even number of octawords long, it is zero padded to the next octaword boundary. However, the length of the certificate does not include this padding and so any certificate navigation software must be sure to round up to the next octaword to locate another certificate.

Ignoring for the moment that “contiguous” and “octaword-aligned” stand in opposition to each other, this spells out how to structure the list in more detail than older versions of the specification.

This all brings us to Tiano revisision 14141:

Update the DxeImageVerificationLib to support for Authenticode-signed UEFI images with multiple signatures.
Signed-off-by: Fu Siyuan siyuan.fu@intel.com
Reviewed-by: Ye Ting ting.ye@intel.com
Reviewed-by: Dong Guo guo.dong@intel.com

Among other changes is this:

1
2
3
4
5
6
7
-  //
-  // Verify the image's Authenticode signature, only DER-encoded PKCS#7 signed data is supported.
-  //
-  if (WinCertificate->wCertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA) {
+  for (OffSet = SecDataDir->VirtualAddress;
+       OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);
+       OffSet += WinCertificate->dwLength, OffSet += ALIGN_SIZE (OffSet)) {

This, for the first time in UEFI code, enforce alignment requirements on the signatures embedded in the binary.

As a result, signers have needed to be fixed to properly reflect this requirement. In pesign, that work was done in March of 2012. But this, once again, isn’t as simple as it seems.

Typically the Certificate Table is the very last thing in the binary — after all, it’s the last change you’re going to make, or else it’s going to be full of invalid signatures. In UEFI, you can either enroll a certificate into the security databases, or you can enroll the hash of a specific binary. When you have an unsigned binary, there’s no guarantee of what the size of the binary will be. If the last section happens to be full of structured data, it will probably end on an alignment that matches that of that data. If the last thing is a string table or executable code for an 8086 CPU, it could end at any offset. So there’s basically only a one in eight chance that it’s aligned correctly for appending the Certificate table. If it isn’t, then you pad to the right alignment before appending the table.

When the signature is calculated on a binary with a Certificate Table, that padding is included in the data being digested. The inevitable conclusion is that the Authenticode hash of a signed binary is not guaranteed to be the same as the hash of the exact same binary if it has never been signed.

So now in addition to “pesign —sign”, the command to sign a binary, and “pesign —hash”, which outputs the hash of a binary so you can enroll it specifically, pesign now also supports “pesign —hash —pad”, which you can use to find out what the hash of an unsigned binary would be, were it signed.

This all went in to pesign 0.104, released Wed Mar 27 09:41:52 2013 -0400 .

Edit:

Just in case anybody is interested, in Fedora, we tracked this issue with bug 963361.

An Open Letter to Prospective Cambridge City Councilor Jefferson R. Smith

Sir,

Today I received a flier for your campaign for City Council. This in itself is not particularly surprising. The method by which it was delivered, however, is wholly unacceptable.

When campaign workers stuff campaign fliers haphazardly into the edges of urban mail boxes, it sends a clear signal: this campaign is about the candidate, not the voters. There are several problems with stuffing fliers into mail boxes this way: illegality and dangers you’re placing on voters being the most obvious.

Make no mistake — this behavior is illegal. Under DMM 508, section 3.1.3, your campaign owes postage on fliers delivered this way. Defrauding the government is a fairly bad sign for your dedication to the rule of law. If you’re willing to violate such simple regulations for a quick buck, why would a voter think you’re not willing to violate other laws?

Additionally, leaving mail dangling from a mail box places the recipient’s property in danger. Most urban mail boxes are closed and locked for a reason. If you leave mail dangling outside an urban mail box, you’re sending a message regarding that residence was last checked in on. Especially in the late Summer, this is a clear way to measure the vulnerability of a residence to theft.

Aside from the above, it’s sloppy and insulting to have your flier include a signature and a hand written note which are clearly in different hand writing, especially if the names in the note clearly include outdated entries from voter roles which don’t match the names printed on the actual mail box. I know your campaign workers are just going door to door and stuffing them in mail boxes, and that these fliers have been prepared ahead of time, but the careless failure of attention to detail just seems patronizing.

Fedora and Secure Boot Signing

Recent work

A lot of work has recently been going on in terms of Secure Boot support on Fedora. Recently we’ve set up builders with a special configuration for signing appropriate packages, provisioned keys onto hardware cryptographic devices to keep them safe in the event of an intrusion, and started building packages with our production signing keys.

What’s next

The next step will be to have shim signed by the UEFI signing service. But there’s still much that can be done before we cross that threshold, and we could use help from those that have appropriate hardware to test with. Today, Fedora-signed packages for shim and grub2 have been built, and they’ve got updates filed in Bodhi for Fedora 18: here for shim and here for grub2. Sometime soon – most likely tomorrow – we’ll build a kernel that will be signed by the production keys as well. You can tell if it’s a signed kernel by viewing the build log. Look for this:

+ ‘[’ -x /usr/bin/pesign -a x86_64 == x86_64 ‘]’
+ ‘[’ -e /var/run/pesign/socket ‘]’
+ /usr/bin/pesign-client -t ‘OpenSC Card (Fedora Signer)’ -c
‘/CN=Fedora Secure Boot Signer’ -i gcdx64.efi.orig -o gcdx64.efi -s

If the “-t” argument to pesign-client says “OpenSC Card (Fedora Signer)”, then it’s the correct build. I’ll post an update here with the relevant kernel build as well.

Once that happens, those of you who already have Fedora 18 test composes installed using UEFI on hardware that supports secure boot can help test.

The test procedure

First, ensure that your machine is in “setup” mode – that is, Secure Boot is disabled, and no keys are enrolled. Once you’ve done that, install the updated shim, grub2, and kernel packages, and then it’s time to install some new keys. Download this utility into /boot/efi/EFI/fedora/ . Now you’ll need to run this utility from your UEFI firmware. Unfortunately how to do that will vary across many systems, so you’re on your own figuring that out. Once you’ve run it, reboot the machine one more time so that the settings are live.

Now the machine should be booting in Secure Boot mode signed with the Fedora keys.

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.

UEFI Debugging Tools

One of the many things I work on is UEFI support. It’s an interesting thing to work on, in part because there’s a lot of new development and it’s at a fairly low level, which is just the sort of thing I like.

Often during UEFI development, we’ll see a bug and need to diagnose whether it’s a problem with the hardware, the firmware, the bootloader, the OS kernel, or even a userland program. One case of this is when console graphics don’t work right.

A recent bug reported that console graphics weren’t working due to an invalid pixel format being chosen. While diagnosing this problem, there are a couple of things that could go wrong. The firmware driver could be reporting the wrong modes, the bootloader could have a bug where it’s querying the modes incorrectly or misinforming the kernel as to the mode chosen, or the kernel could have a bug where it uses the mode incorrectly. It’s also possible that the console works until a KMS driver – that is, a native driver for the card – is loaded.

In UEFI, console graphics involves every part of the stack. The system firmware provides an API known as Graphics Output Protocol, or GOP. The algorithm used by the bootloader basically looks like this somewhat simplified function:

set_mode.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
int gop_enable(struct graphics *graphics, struct kernel_params *kernel_params) {
  efi_handle gop_intf = efi_locate_protocol(&graphics_output_guid, NULL);
  efi_status status = EFI_SUCCESS;

  if (!gop_intf)
      return -1;

  graphics->max_mode = gop_intf->mode->max_mode;
  graphics->modes = calloc(graphics->max_mode, sizeof(void *));
  if (!graphics->modes)
      return -1;

  for (int i = 0; i < graphics->max_mode; i++) {
      graphics->modes[i] = calloc(1, sizeof graphics->modes[0]);
      if (!graphics->modes[i]) {
          status = EFI_NO_MEMORY;
          goto fail;
      }

      graphics->modes[i]->number = i;
      status = gop_intf->query_mode(gop_intf, i,
                      &graphics->modes[i]->size,
                      &graphics->modes[i]->info);
      if (status != EFI_SUCCESS) {
          free(graphics->modes[i]);
          graphics->modes[i] = NULL;
          goto fail;
      }
  }

  /* this sorts the modes by size, but leaves mode.number intact
  * so that we have a way to reference it to the firmware */
  sort_modes(graphics->modes);

  gop_intf->set_mode(gop_intf, graphics->modes[0]->number);
  set_kernel_params(graphics->modes[0], kernel_params);

  return 0;
fail:
  /* best effort... */
  if (graphics->modes && graphics->modes[0]) {
      gop_intf->set_mode(gop_intf, graphics->modes[0]->number);
      set_kernel_params(graphics->modes[0], kernel_params);
  }
  return -1;
}

This is a simplified version – for more details, check out efigraph.c for the excrutiating details.

set_kernel_params() in turn fills out the data structure we give to the kenrel, which then uses the efifb driver to configure a framebuffer console with the correct resolution and pixel format.

Clearly, there is plenty that can go wrong here. With that in mind, I wrote a tool called modelist.efi to debug some of these problems.

In the bug, I’m told:

With a 1280x1024 capable monitor connected to a *censored* or *censored* in
uEFI mode, the remote video redirection via BMC (*censored*) does not display
anything. The reason is that the efifb driver chooses the VGA mode
1280x1024x32bpp which the video redirection doesn’t support (the *censored*
chip doesn’t support video modes that need more than 4MB video RAM).
See also bug #XXXXXX for the same problem reported in the past for Xorg / mga
driver.

efifb: probing for efifb
efifb: framebuffer at 0x90000000, mapped to 0xffffc90011800000, using 8192k,
total 8192k
efifb: mode is 1280x1024x32, linelength=5120, pages=1
efifb: scrolling: redraw
efifb: Truecolor: size=8:8:8:8, shift=24:16:8:0
fbcon: EFI VGA (fb0) is primary device
Console: switching to colour frame buffer device 160x64
fb0: EFI VGA frame buffer device

With the knowledge of how the stack works in mind, I know that we need to find out what the firmware is actually returning to the bootloader in order to tell what layer the problem is at, so I ask:

Can you run /boot/efi/EFI/redhat/modelist.efi (from the gnu-efi package) and
show us the output?

And the response is:

fs0:\EFI\redhat> modelist.efi
GOP reports MaxMode 4
0: 640x480 BGRR pitch 640
1: 800x600 BGRR pitch 800
2: 1024x768 BGRR pitch 1024
3: 1280x1024 BGRR pitch 1280

So this tells me that the firmware is listing 4 video modes, each of which has a 32-bit pixel of the form (blue,green,red,reserved). At this point we know that the firmware is telling us about modes that the hardware cannot support, and efifb cannot correctly support this device until the firmware is fixed.

It’s important to have and use the right tools.