26.8. Signing Kernel Modules for Secure Boot

Red Hat Enterprise Linux 7 includes support for the UEFI Secure Boot feature, which means that Red Hat Enterprise Linux 7 can be installed and run on systems where UEFI Secure Boot is enabled. Note that Red Hat Enterprise Linux 7 does not require the use of Secure Boot on UEFI systems.
When Secure Boot is enabled, the EFI operating system boot loaders, the Red Hat Enterprise Linux kernel, and all kernel modules must be signed with a private key and authenticated with the corresponding public key. The Red Hat Enterprise Linux 7 distribution includes signed boot loaders, signed kernels, and signed kernel modules. In addition, the signed first-stage boot loader and the signed kernel include embedded Red Hat public keys. These signed executable binaries and embedded keys enable Red Hat Enterprise Linux 7 to install, boot, and run with the Microsoft UEFI Secure Boot CA keys that are provided by the UEFI firmware on systems that support UEFI Secure Boot. Note that not all UEFI-based systems include support for Secure Boot.
The information provided in the following sections describes steps necessary to enable you to self-sign privately built kernel modules for use with Red Hat Enterprise Linux 7 on UEFI-based systems where Secure Boot is enabled. These sections also provide an overview of available options for getting your public key onto the target system where you want to deploy your kernel module.

26.8.1. Prerequisites

In order to enable signing of externally built modules, the tools listed in the following table are required to be installed on the system.

Table 26.1. Required Tools

ToolProvided by PackageUsed onPurpose
opensslopensslBuild systemGenerates public and private X.509 key pair
sign-filekernel-develBuild systemPerl script used to sign kernel modules
perlperlBuild systemPerl interpreter used to run the signing script
mokutilmokutilTarget systemOptional tool used to manually enroll the public key
keyctlkeyutilsTarget systemOptional tool used to display public keys in the system key ring

Note

Note that the build system, where you build and sign your kernel module, does not need to have UEFI Secure Boot enabled and does not even need to be a UEFI-based system.

26.8.2. Kernel Module Authentication

In Red Hat Enterprise Linux 7, when a kernel module is loaded, the module's signature is checked using the public X.509 keys on the kernel's system key ring, excluding those keys that are on the kernel's system black-list key ring.

26.8.2.1. Sources For Public Keys Used To Authenticate Kernel Modules

During boot, the kernel loads X.509 keys into the system key ring or the system black-list key ring from a set of persistent key stores as shown in Table 26.2, “Sources For System Key Rings”

Table 26.2. Sources For System Key Rings

Source of X.509 KeysUser Ability to Add KeysUEFI Secure Boot StateKeys Loaded During Boot
Embedded in kernelNo-.system_keyring
UEFI Secure Boot "db"LimitedNot enabledNo
Enabled.system_keyring
UEFI Secure Boot "dbx"LimitedNot enabledNo
Enabled.system_keyring
Embedded in shim.efi boot loaderNoNot enabledNo
Enabled.system_keyring
Machine Owner Key (MOK) listYesNot enabledNo
Enabled.system_keyring
Note that if the system is not UEFI-based or if UEFI Secure Boot is not enabled, then only the keys that are embedded in the kernel are loaded onto the system key ring and you have no ability to augment that set of keys without rebuilding the kernel. The system black list key ring is a list of X.509 keys which have been revoked. If your module is signed by a key on the black list then it will fail authentication even if your public key is in the system key ring.
You can display information about the keys on the system key rings using the keyctl utility. The following is abbreviated example output from a Red Hat Enterprise Linux 7 system where UEFI Secure Boot is not enabled.
~]# keyctl list %:.system_keyring
3 keys in keyring:
...asymmetric: Red Hat Enterprise Linux Driver Update Program (key 3): bf57f3e87...
...asymmetric: Red Hat Enterprise Linux kernel signing key: 4249689eefc77e95880b...
...asymmetric: Red Hat Enterprise Linux kpatch signing key: 4d38fd864ebe18c5f0b7...
The following is abbreviated example output from a Red Hat Enterprise Linux 7 system where UEFI Secure Boot is enabled.
~]# keyctl list %:.system_keyring
6 keys in keyring:
...asymmetric: Red Hat Enterprise Linux Driver Update Program (key 3): bf57f3e87...
...asymmetric: Red Hat Secure Boot (CA key 1): 4016841644ce3a810408050766e8f8a29...
...asymmetric: Microsoft Corporation UEFI CA 2011: 13adbf4309bd82709c8cd54f316ed...
...asymmetric: Microsoft Windows Production PCA 2011: a92902398e16c49778cd90f99e...
...asymmetric: Red Hat Enterprise Linux kernel signing key: 4249689eefc77e95880b...
...asymmetric: Red Hat Enterprise Linux kpatch signing key: 4d38fd864ebe18c5f0b7...
The above output shows the addition of two keys from the UEFI Secure Boot "db" keys plus the Red Hat Secure Boot (CA key 1) which is embedded in the shim.efi boot loader. You can also look for the kernel console messages that identify the keys with an UEFI Secure Boot related source, that is UEFI Secure Boot db, embedded shim, and MOK list.
~]# dmesg | grep 'EFI: Loaded cert'
[5.160660] EFI: Loaded cert 'Microsoft Windows Production PCA 2011: a9290239...
[5.160674] EFI: Loaded cert 'Microsoft Corporation UEFI CA 2011: 13adbf4309b...
[5.165794] EFI: Loaded cert 'Red Hat Secure Boot (CA key 1): 4016841644ce3a8...

26.8.2.2.  Kernel Module Authentication Requirements

If UEFI Secure Boot is enabled or if the module.sig_enforce kernel parameter has been specified, then only signed kernel modules that are authenticated using a key on the system key ring can be successfully loaded, provided that the public key is not on the system black list key ring. If UEFI Secure Boot is disabled and if the module.sig_enforce kernel parameter has not been specified, then unsigned kernel modules and signed kernel modules without a public key can be successfully loaded. This is summarized in Table 26.3, “Kernel Module Authentication Requirements for Loading”.

Table 26.3. Kernel Module Authentication Requirements for Loading

Module SignedPublic Key Found and Signature ValidUEFI Secure Boot Statemodule.sig_enforceModule LoadKernel Tainted
Unsigned-Not enabledNot enabledSucceedsYes
Not enabledEnabledFails 
Enabled-Fails-
SignedNoNot enabledNot enabledSucceedsYes
Not enabledEnabledFails-
Enabled-Fails-
SignedYesNot enabledNot enabledSucceedsNo
Not enabledEnabledSucceedsNo
Enabled-SucceedsNo
Subsequent sections will describe how to generate a public and private X.509 key pair, how to use the private key to sign a kernel module, and how to enroll the public key into a source for the system key ring.

26.8.3. Generating a Public and Private X.509 Key Pair

You need to generate a public and private X.509 key pair that will be used to sign a kernel module after it has been built. The corresponding public key will be used to authenticate the kernel module when it is loaded.
  1. The openssl tool can be used to generate a key pair that satisfies the requirements for kernel module signing in Red Hat Enterprise Linux 7. Some of the parameters for this key generation request are best specified with a configuration file; follow the example below to create your own configuration file.
    ~]# cat << EOF > configuration_file.config
    [ req ]
    default_bits = 4096
    distinguished_name = req_distinguished_name
    prompt = no
    string_mask = utf8only
    x509_extensions = myexts
    
    [ req_distinguished_name ]
    O = Organization
    CN = Organization signing key
    emailAddress = E-mail address
    
    [ myexts ]
    basicConstraints=critical,CA:FALSE
    keyUsage=digitalSignature
    subjectKeyIdentifier=hash
    authorityKeyIdentifier=keyid
    EOF
  2. After you have created the configuration file, you can create an X.509 public and private key pair. The public key will be written to the public_key.der file and the private key will be written to the private_key.priv file.
    ~]# openssl req -x509 -new -nodes -utf8 -sha256 -days 36500 \
    -batch -config configuration_file.config -outform DER \
    -out public_key.der \  
    -keyout private_key.priv
  3. Enroll your public key on all systems where you want to authenticate and load your kernel module.

Warning

Take proper care to guard the contents of your private key. In the wrong hands, the key could be used to compromise any system which has your public key.

26.8.4. Enrolling Public Key on Target System

When Red Hat Enterprise Linux 7 boots on a UEFI-based system with Secure Boot enabled, all keys that are in the Secure Boot db key database, but not in the dbx database of revoked keys, are loaded onto the system keyring by the kernel. The system keyring is used to authenticate kernel modules.

26.8.4.1. Factory Firmware Image Including Public Key

To facilitate authentication of your kernel module on your systems, consider requesting your system vendor to incorporate your public key into the UEFI Secure Boot key database in their factory firmware image.

26.8.4.2. Executable Key Enrollment Image Adding Public Key

It is possible to add a key to an existing populated and active Secure Boot key database. This can be done by writing and providing an EFI executable enrollment image. Such an enrollment image contains a properly formed request to append a key to the Secure Boot key database. This request must include data that is properly signed by the private key that corresponds to a public key that is already in the system's Secure Boot Key Exchange Key (KEK) database. Additionally, this EFI image must be signed by a private key that corresponds to a public key that is already in the key database.
It is also possible to write an enrollment image that runs under Red Hat Enterprise Linux 7. However, the Red Hat Enterprise Linux 7 image must be properly signed by a private key that corresponds to a public key that is already in the KEK database.
The construction of either type of key enrollment images requires assistance from the platform vendor.

26.8.4.3. System Administrator Manually Adding Public Key to the MOK List

The Machine Owner Key (MOK) facility is a feature that is supported by Red Hat Enterprise Linux 7 and can be used to augment the UEFI Secure Boot key database. When Red Hat Enterprise Linux 7 boots on a UEFI-enabled system with Secure Boot enabled, the keys on the MOK list are also added to the system keyring in addition to the keys from the key database. The MOK list keys are also stored persistently and securely in the same fashion as the Secure Boot key database keys, but these are two separate facilities. The MOK facility is supported by shim.efi, MokManager.efi, grubx64.efi, and the Red Hat Enterprise Linux 7 mokutil utility.
The major capability provided by the MOK facility is the ability to add public keys to the MOK list without needing to have the key chain back to another key that is already in the KEK database. However, enrolling a MOK key requires manual interaction by a physically present user at the UEFI system console on each target system. Nevertheless, the MOK facility provides an excellent method for testing newly generated key pairs and testing kernel modules signed with them.
Follow these steps to add your public key to the MOK list:
  1. Request addition of your public key to the MOK list using a Red Hat Enterprise Linux 7 userspace utility:
    ~]# mokutil --import my_signing_key_pub.der
    You will be asked to enter and confirm a password for this MOK enrollment request.
  2. Reboot the machine.
  3. The pending MOK key enrollment request will be noticed by shim.efi and it will launch MokManager.efi to allow you to complete the enrollment from the UEFI console. You will need to enter the password you previously associated with this request and confirm the enrollment. Your public key is added to the MOK list, which is persistent.
Once a key is on the MOK list, it will be automatically propagated to the system key ring on this and subsequent boots when UEFI Secure Boot is enabled.

26.8.5. Signing Kernel Module with the Private Key

There are no extra steps required to prepare your kernel module for signing. You build your kernel module normally. Assuming an appropriate Makefile and corresponding sources, follow these steps to build your module and sign it:
  1. Build your my_module.ko module the standard way:
    ~]# make -C /usr/src/kernels/$(uname -r) M=$PWD modules
  2. Sign your kernel module with your private key. This is done with a Perl script. Note that the script requires that you provide both the files that contain your private and the public key as well as the kernel module file that you want to sign.
    ~]# perl /usr/src/kernels/$(uname -r)/scripts/sign-file \
    sha256 \
    my_signing_key.priv\
    my_signing_key_pub.der\
    my_module.ko
Your kernel module is in ELF image format and this script computes and appends the signature directly to the ELF image in your my_module.ko file. The modinfo utility can be used to display information about the kernel module's signature, if it is present. For information on using the utility, see Section 26.2, “Displaying Information About a Module”.
Note that this appended signature is not contained in an ELF image section and is not a formal part of the ELF image. Therefore, tools such as readelf will not be able to display the signature on your kernel module.
Your kernel module is now ready for loading. Note that your signed kernel module is also loadable on systems where UEFI Secure Boot is disabled or on a non-UEFI system. That means you do not need to provide both a signed and unsigned version of your kernel module.

26.8.6. Loading Signed Kernel Module

Once your public key is enrolled and is in the system keyring, the normal kernel module loading mechanisms will work transparently. In the following example, you will use mokutil to add your public key to the MOK list and you will manually load your kernel module with modprobe.
  1. Optionally, you can verify that your kernel module will not load before you have enrolled your public key. First, verify what keys have been added to the system key ring on the current boot by running the keyctl list %:.system_keyring as root. Since your public key has not been enrolled yet, it should not be displayed in the output of the command.
  2. Request enrollment of your public key.
    ~]# mokutil --import my_signing_key_pub.der
  3. Reboot, and complete the enrollment at the UEFI console.
    ~]# reboot
  4. After the system reboots, verify the keys on the system key ring again.
    ~]# keyctl list %:.system_keyring
  5. You should now be able to load your kernel module successfully.
    ~]# modprobe -v my_module
    insmod /lib/modules/3.10.0-123.el7.x86_64/extra/my_module.ko
    ~]# lsmod | grep my_module
    my_module 12425 0