注:

对内核模块进行签名以用于 UEFI 安全引导

本教程介绍了如何对自己的模块进行签名,以便在安装了 Unbreakable Enterprise Kernel 的 Oracle Linux 上使用 UEFI Secure Boot。本教程还说明了如何在 UEK R6U3 之前使用 UEK R6 内核的情况下将您自己的证书添加到内核的可信证书密钥环中;以及如何使用 mokutil 实用程序使用签名证书更新 UEFI 引导 shim。

警告
本教程面向正在执行软件测试的开发人员。请勿对 Oracle 支持的生产系统使用本教程中的过程。

先决条件

目标

在本教程结束时,您应该能够完成以下工作:

安装所需程序包

  1. 根据 Oracle Linux 系统运行以下命令。

    • Oracle Linux 8

      sudo dnf update
      sudo dnf group install "Development Tools"
      sudo dnf install kernel-uek kernel-uek-devel keyutils mokutil pesign
      
    • Oracle Linux 7

      sudo yum-config-manager --enable ol7_optional_latest
      sudo yum update
      sudo yum group install "Development Tools"
      sudo yum-config-manager --enable ol7_UEKR6 && yum-config-manager --disable ol7_UEKR5
      
  2. 重新启动系统。

创建示例定制内核模块

为了本教程的目的,我们创建并构建示例定制内核模块。在大多数情况下,模块已由外部供应商提供,因此不需要执行此步骤。此处描述的过程是为了说明目的。

  1. 创建 ~/hello 目录。

  2. 使用以下条目创建文件 ~/hello/hello.c

    #include <linux/module.h>  
    #include <linux/kernel.h>  
    #include <linux/init.h>
    
    MODULE_LICENSE("GPL");  
    MODULE_AUTHOR("A.Developer");  
    MODULE_DESCRIPTION("Hello World Linux Kernel Module");
    
    static int __init hello_init(void)
    {  
      printk(KERN_INFO "Hello world!\n");  
      return 0;
    }
    
    static void __exit hello_exit(void)  
    {  
      printk(KERN_INFO "Unloading Hello world.\n");  
    }
    
    module_init(hello_init);
    module_exit(hello_exit);
    
  3. 使用以下条目创建文件 ~/hello/Makefile

    obj-m += hello.o
    
    all:  
      make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
    
    clean:  
      make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
    
    install:  
      cp hello.ko  /lib/modules/$(shell uname -r)/extra  
      depmod
    
  4. 构建然后安装模块。

    cd ~/hello
    make && sudo make install
    
  5. 测试模块。

    sudo modprobe hello
    
    modprobe: ERROR: could not insert 'hello': Operation not permitted
    

    如果系统在安全引导模式下运行,则 modprobe 将失败,因为模块尚未有效。

创建本地证书并对模块签名

  1. 创建 OpenSSL 配置文件以创建本地签名证书。

    类型:

    cat >>/tmp/x509.conf <<EOF
    [ req ]
    default_bits = 4096
    distinguished_name = req_distinguished_name
    prompt = no
    string_mask = utf8only
    x509_extensions = extensions
    
    [ req_distinguished_name ]
    O = Module Signing Example
    CN = Module Signing Example Key
    emailAddress = first.last@example.com
    
    [ extensions ]
    basicConstraints=critical,CA:FALSE
    keyUsage=digitalSignature
    subjectKeyIdentifier=hash
    authorityKeyIdentifier=keyid
    EOF
    

    对于 OCNemailAddress 字段,输入更合适的值。

  2. 根据新配置生成新的专用 / 公共密钥对。

    请注意,证书有效期为 10 年(3,650 天)。

    mkdir ~/certs
    cd ~/certs
    openssl req -x509 -new -nodes -utf8 -sha512 -days 3650 -batch -config /tmp/x509.conf -outform DER -out pubkey.der -keyout priv.key
    
    Generating a RSA private key
    ........................................................
    ............................++++
    writing new private key to 'priv.key'
    -----
    
  3. 以 PEM 格式导出证书。

    openssl x509 -inform DER -in pubkey.der -out pubkey.pem
    
  4. 创建 PKCS#12 版本的证书。

    在出现提示时指定导出密码。

    openssl pkcs12 -export -inkey priv.key -in pubkey.pem -name cert -out cert.p12
    
    Enter Export Password:
    Verifying - Enter Export Password:
    
  5. 使用刚生成的密钥对模块签名。

    cd ~/certs
    sudo /usr/src/kernels/$(uname -r)/scripts/sign-file sha512 priv.key pubkey.der /lib/modules/$(uname -r)/extra/hello.ko
    
  6. 使用 modinfo 命令,验证模块是否已使用您创建的密钥进行签名。

    modinfo hello
    
    filename:       /lib/modules/5.4.17-2036.103.3.1.el8uek.x86_64/extra/hello.ko
    description:    Hello World Linux Kernel Module
    author:         A.Developer
    license:        GPL
    srcversion:     D51FB4CF0B86314953EE797
    depends:        
    retpoline:      Y
    name:           hello
    vermagic:       5.4.17-2036.103.3.1.el8uek.x86_64 SMP mod_unload modversions 
    sig_id:         PKCS#7
    signer:         Module Signing Example Key
    sig_key:        AB:2C:E3:AB:87:D9:9C:6A:31:B8:80:20:D4:92:25:F3:9A:26:DC
    sig_hashalgo:   sha512
    signature:      9F:B0:25:CB:14:C1:C7:10:7F:60:1E:E6:66:82:64:58:91:1F:01:A5:
                    D9:03:1B:9C:2D:42:00:45:78:2B:FA:70:F8:C7:3B:1A:A2:42:00:09:
                    33:E0:81:1D:C6:E6:46:A5:FE:8B:9F:8C:3D:4E:A1:3A:05:52:ED:F6:
                    25:F9:88:98:D3:70:78:1D:7E:63:F3:73:C8:C8:14:C2:3A:52:B4:8F: 
                    ...
    

将证书添加到内核可信密钥环(UEK R6U2 及更低版本)

注:
如果您运行的是 UEK R6U3 或更高版本,则不需要执行此步骤。UEK R6U3 中的安全引导实现已更新,以允许对使用 MOK 数据库中的密钥进行签名的模块进行模块装入。对于 UEK R6U3 之前的 UEK R6 发行版,仅当与它们签署的密钥位于内核的内置可信密钥环中时,模块才能够装入。

  1. 创建要与 pesign 一起使用的 NSS 数据库。

    cd ~/certs
    certutil -d . -N
    
    Enter a password which will be used to encrypt your keys.
    The password should be at least 8 characters long,
    and should contain at least one non-alphabetic character.
    
    Enter new password:
    Re-enter password:
    
  2. 将 PCKS#12 密钥添加到数据库。

    cd ~/certs
    pk12util -d . -i cert.p12
    
    Enter Password or Pin for "NSS Certificate DB":
    Enter password for PKCS12 file:
    pk12util: PKCS12 IMPORT SUCCESSFUL
    
  3. 将密钥插入内核的 bzImage 中。

    注:只能向内核添加一个额外的定制证书,因为内核的引导映像的压缩大小无法增加。

    cd ~/certs
    sudo /usr/src/kernels/$(uname -r)/scripts/insert-sys-cert -s /boot/System.map-$(uname -r) -z /boot/vmlinuz-$(uname -r) -c pubkey.der
    
    INFO:    Executing: gunzip <vmlinux-ceDv0X >vmlinux-rFFumH
    WARNING: Could not find the symbol table.
    INFO:    sym:    system_extra_cert
    INFO:    addr:   0xffffffff828915d6
    INFO:    size:   8194
    INFO:    offset: 0x1c915d6
    INFO:    sym:    system_extra_cert_used
    INFO:    addr:   0xffffffff828935d8
    INFO:    size:   0
    INFO:    offset: 0x1c935d8
    INFO:    sym:    system_certificate_list_size
    INFO:    addr:   0xffffffff828935e0
    INFO:    size:   0
    INFO:    offset: 0x1c935e0
    INFO:    Inserted the contents of pubkey.der into ffffffff828915d6.
    INFO:    Used 1481 bytes out of 8194 bytes reserved.
    INFO:    Executing: gzip -n -f -9 <vmlinux-rFFumH >vmlinux-5wHA2r
    
  4. 删除现有的 PE 签名并使用先前创建的 NSS 数据库密码重新签名内核。

    cd ~/certs
    sudo pesign -u 0 -i /boot/vmlinuz-$(uname -r) --remove-signature -o vmlinuz.unsigned
    sudo pesign -n . -c cert -i vmlinuz.unsigned -o vmlinuz.signed -s
    
    Enter Password or PIN for 'NSS Certificate DB":
    
  5. 将已签名内核复制回 /boot

    cd ~/certs
    sudo cp -bf vmlinuz.signed /boot/vmlinuz-$(uname -r)
    

将证书注册到 UEFI 安全引导密钥数据库

对于 UEK R6U3 之前的 UEK R6 内核,必须注册用于将内核映像签名到 MOK 数据库的密钥。对于所有其他内核,用于签署模块本身的键将注册到数据库中。如果您使用相同的密钥对模块和内核进行签名,这些密钥可能相同。

注:有关使用 mokutil 实用程序的更多信息,请参见 https://docs.oracle.com/learn/mokutil-uefi/

  1. 将相应的密钥作为计算机所有者密钥 (Machine Owner Key, MOK) 添加到 UEFI 引导 shim。mokutil import 命令提示您输入需要在下一步中提供的密码。

    sudo mokutil --import pubkey.der
    
    input password:
    input password again:
    
  2. 重新启动您的计算机。

    重新引导时,UEFI SHIM 应自动启动 MokManager,该 MokManager 用于将 MOK 密钥添加到 UEFI 安全引导密钥数据库。

    1. 按键以执行 MOK 管理。
      确保在 10 秒内按下某个键以中断引导过程以添加 MOK 密钥。
    2. 选择“注册 MOK”。
    3. 选择 "View key 0"。
    4. 验证密钥是否与所需的值匹配。
    5. 按任意键,然后选择“Continue(继续)”。
    6. 选择“是”以注册密钥。
    7. 提供使用 mokutil 导入密钥时使用的密码。
    8. 选择“reboot(重新引导)”。
  3. 验证证书是否已安装在可信密钥环中。

    sudo keyctl show %:.builtin_trusted_keys
    
    Keyring
     892034081 ---lswrv      0     0  keyring: .builtin_trusted_keys
     367808024 ---lswrv      0     0   \_ asymmetric: Oracle CA Server: fbcd3d4d950c6b2b0e01f0a146c5a4e3855ae704
     230958440 ---lswrv      0     0   \_ asymmetric: Module Signing Example Key: a43b4e638874b0656db2bc26216f56c0ac39f72b
     408597579 ---lswrv      0     0   \_ asymmetric: Oracle America, Inc.: Ksplice Kernel Module Signing Key: 09010ebef5545fa7c54b626ef518e077b5b1ee4c
     839574974 ---lswrv      0     0   \_ asymmetric: Oracle Linux Kernel Module Signing Key: 2bb352412969a3653f0eb6021763408ebb9bb5ab
    
  4. 验证模块是否可以成功装入。

    sudo modprobe hello
    lsmod | grep hello
    hello                  16384  0
    
    dmesg | tail -2
    [  108.848848] hello: loading out-of-tree module taints kernel.
    [  108.849191] Hello world! 
    

已知问题

Oracle Linux 7 上的以下内核版本在接受新证书时可能出现问题,同时对生成的 vmlinuz 文件保持相同的(或更小的)压缩大小。建议在开始此过程之前更新到最新的可用 UEKR6 发行版。

在 Oracle Linux 8 上,这些内核的等效版本中不会观察到压缩问题。

更多信息

更多学习资源

docs.oracle.com/learn 上浏览其他实验室,或者在 Oracle Learning YouTube 渠道上访问更多免费学习内容。此外,访问 education.oracle.com/learning-explorer 以成为 Oracle Learning Explorer。

有关产品文档,请访问 Oracle 帮助中心