Build and install signed Kvaser driver modules

This is the second post in a 2-part series about Secure Boot and signing modules on Linux:

  1. Secure Boot on Linux systems
  2. Build and install signed Kvaser driver modules

The first part was an overview of what Secure Boot actually is and how it affects 3:rd party modules. In this part we take a look at how to build and sign the Kvaser driver modules to be able to use them on a Linux computer that has Secure Boot enabled.


The Linux system

We are doing this on a freshly installed Ubuntu 16.04.

  $ uname -a
  Linux mypc 4.8.0-51-generic #54~16.04.1-Ubuntu SMP Wed Apr 26 16:00:28 UTC 2017
x86_64 x86_64 x86_64 GNU/Linux
  $ lsb_release -a
  No LSB modules are available.
  Distributor ID: Ubuntu
  Description:    Ubuntu 16.04.2 LTS
  Release:        16.04
  Codename:       xenial

Create certificate and private key

The first step is to create a certificate/private RSA key pair which later will be used to sign the kernel modules.

Note that since the private key is used to sign modules, viruses and malware could use the private key to sign modules and compromise the operating system. Make sure to keep the private key protected.1

Using the openssl command, we now create a private key and a DER encoded certificate.2 Remember to set the Common Name (CN) field (YOUR_NAME) to something meaningful for a human observer.3 We use kvaser.com as the Common Name in our example.

  $ mkdir ~/sign_cert
  $ cd ~/sign_cert
  # Change YOUR_NAME below for identification purposes, we’ll use kvaser.com
  $ openssl req -new -x509 -newkey rsa:2048 -keyout modulesign.priv -outform DER
-out modulesign.der -nodes -days 36500 -subj "/CN=YOUR_NAME/"

We now end up with two files.

$ ls
modulesign.der modulesign.priv

Import and enroll the public key to the system

The next step is to use the mokutil4 command to import the public key so that it can be trusted by the system. This is a two step process where the key is first imported, and then later must be enrolled when the machine is booted the next time. A simple password is good enough, as it is only for temporary use.

$ sudo mokutil --import modulesign.der
input password:
input password again:

We can now verify that the correct certificate has been imported. Here we also note that the Common Name (CN) used above was kvaser.com.

$ sudo mokutil --list-new
[key 1]
SHA1 Fingerprint: 2c:d4:5b:a3:c6:34:3f:a6:1a:8f:e3:d3:23:8d:88:69:7d:33:ae:12
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 18315900181576503446 (0xfe2f262c60615096)
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN=kvaser.com
        Validity
            Not Before: May  9 07:52:56 2017 GMT
            Not After : Apr 15 07:52:56 2117 GMT
        Subject: CN=kvaser.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
:
:

Now reboot the machine. When the bootloader starts, the MOK manager EFI utility should automatically start. On my machine I got a screen with white text on light blue background, telling me to “Press any key to perform MOK management” and “Booting in 10 seconds”5, YMMV. Choose “Enroll MOK”, select the key, and enroll the key. We will be asked for the password we set during the import step above. Complete the enrollment steps, then continue with the boot. The Linux kernel will log the keys that are loaded, and we can see our own key with the dmesg command.

  $ dmesg|grep ’EFI: Loaded cert’
  [    0.671897] EFI: Loaded cert ’Microsoft Windows Production PCA 2011:
a92902398e16c49778cd90f99e4f9ae17c55af53’ linked to ’.builtin_trusted_keys’
  [    0.671908] EFI: Loaded cert ’Microsoft Corporation UEFI CA 2011:
13adbf4309bd82709c8cd54f316ed522988a1bd4’ linked to ’.builtin_trusted_keys’
  [    0.673771] EFI: Loaded cert ’kvaser.com: 74fd8929d9e2fb64a9bd0abe3bdf
42d519b7382f’ linked to ’.builtin_trusted_keys’
  [    0.673902] EFI: Loaded cert ’Canonical Ltd. Master Certificate Authority:
ad91990bc22ab1f517048c23b6655a268e345a63’ linked to ’.builtin_trusted_keys’

We can now also test that our certificate has been enrolled using the mokutil command.

$ cd ~/sign_cert
$ mokutil --test-key modulesign.der
modulesign.der is already enrolled

Build, sign and install the modules

Before building, we need to download the latest version of linuxcan from the Kvaser download page, which as of this writing is v5.20.6

$ wget http://www.kvaser.com/software/7330130980754/V5_20_0/linuxcan.tar.gz
$ tar xvzf linuxcan.tar.gz
$ cd linuxcan

In order to make it easy for us to sign the files as part of the normal build process, we modify the kv_module target in the file config.mak as follows.7

# Target found in config.mak located in the top level directory of linuxcan
kv_module:
        @echo --------------------------------------------------------------------
        @echo "building $(KV_MODULE_NAME) $(IS_DEBUG)"
        @echo "Kernel src:" $(KV_KERNEL_SRC_DIR)
        $(MAKE) -C $(KV_KERNEL_SRC_DIR) SUBDIRS=$(PWD) modules
        @if [ "$(KV_SIGN_CERT_PATH)" != "" ] && [ "$(KV_SIGN_CERT_NAME)" != "" ]; then \
                echo "Signing module $(KV_MODULE_NAME).ko with $(KV_SIGN_CERT_PATH)/$(KV_SIGN_CERT_NAME)"; \
                $(KV_KERNEL_SRC_DIR)/scripts/sign-file sha256 $(KV_SIGN_CERT_PATH)/$(KV_SIGN_CERT_NAME).priv \
                $(KV_SIGN_CERT_PATH)/$(KV_SIGN_CERT_NAME).der $(KV_MODULE_NAME).ko; \
        fi
        @echo --------------------------------------------------------------------

It is now possible to build and sign the driver modules using the normal make command by just adding the two environment variables KV_SIGN_CERT_PATH and KV_SIGN_CERT_NAME. We also install the module after building.

 # KV_SIGN_CERT_PATH is the path to your private key and certificate.
 # KV_SIGN_CERT_NAME is the base file name of your private key and
certificate, without suffix.
 $ make KV_SIGN_CERT_PATH=~/sign_cert KV_SIGN_CERT_NAME=modulesign
 $ sudo make install

To verify that the installed module is working, we use the modprobe command to manually load a module and verify that it has been loaded.8 We also make sure to remove the module so we don’t interfere with the automatic loading when Kvaser USB devices are connected.

$ sudo modprobe mhydra
$ lsmod | grep ’kvcommon\|mhydra’
mhydra                 45056  0
kvcommon               45056  1 mhydra

$ sudo modprobe -r mhydra kvcommon

Footnotes

1 Some notes regarding protecting the private key can be found in the Linux kernel documentation at https://static.lwn.net/kerneldoc/admin-guide/module-signing.html#administering-protecting-the-private-key.

2 You can read more about the different X509 file extensions such as DER in the SSL installation knowledge base at https://support.ssl.com/Knowledgebase/Article/View/19/0/der-vs-crt-vs-cer-vs-pem-certificates-and-how-to-convert-them/.

3 For a description of the significance of the Common Name, read more in the stackexchange article at https://security.stackexchange.com/questions/40026/openssl-x509-whats-the-significance-of-cn-common-name/.

4 You use the mokutil command to manage Machine Owner Keys (MOK) used by the shim layer to validate grub2 and kernel images.

5 If you miss this timeout, you need to rerun the mokutil --import command and reboot again.

6 Kvaser download page is located at https://www.kvaser.com/downloads.

7 The adjusted makefile target will be incorporated in the next release of linuxcan.

8 Unfortunatly it is currently not possible to use modinfo to see if a module is signed or not, see the github issue in coreos at https://github.com/coreos/bugs/issues/1054/ for more information.

Feedback