内核模块签名工具

内核模块签名工具

我正在开发一个内核模块,它运行良好。但是,查看 dmesg 时,我看到一条有关我的模块的消息,指出模块验证失败(模块验证签名失败和/或缺少所需密钥)。

我该如何解决这个问题?如何让我的模块签名以进行验证?

谢谢。

答案1

您所需要的一切都已描述这里

内核模块签名工具


内容

  • 概述。
  • 配置模块签名。
  • 生成签名密钥。
  • 内核中的公钥。
  • 手动签名模块。
  • 签名模块和剥离。
  • 正在加载已签名的模块。
  • 无效的签名和未签名的模块。
  • 管理/保护私钥。

概述

内核模块签名工具在安装期间对模块进行加密签名,然后在加载模块时检查签名。通过禁止加载未签名的模块或使用无效密钥签名的模块,可以提高内核安全性。模块签名使恶意模块更难加载到内核中,从而提高了安全性。模块签名检查由内核完成,因此不需要具有可信的用户空间位。

该工具使用 X.509 ITU-T 标准证书对所涉及的公钥进行编码。签名本身不以任何工业标准类型进行编码。该工具目前仅支持 RSA 公钥加密标准(尽管它是可插入的并允许使用其他标准)。可以使用的哈希算法包括 SHA-1、SHA-224、SHA-256、SHA-384 和 SHA-512(算法由签名中的数据选择)。


配置模块签名

模块签名功能可以通过进入内核配置的“启用可加载模块支持”部分并打开来启用

CONFIG_MODULE_SIG   "Module signature verification"

有多个可用选项:

  1. “要求模块有效签名”(CONFIG_MODULE_SIG_FORCE)

    这指定了内核应如何处理具有未知密钥的签名或未签名的模块。

    如果关闭(即“允许”),则允许使用密钥不可用的模块和未签名的模块,但内核将被标记为受污染,并且相关模块将被标记为受污染,用字符“E”显示。

    如果启用该选项(即“限制性”),则只有具有有效签名(可通过内核拥有的公钥验证)的模块才会被加载。所有其他模块都将生成错误。

    无论这里的设置如何,如果模块具有无法解析的签名块,它将被立即拒绝。

  2. “自动签名所有模块”(CONFIG_MODULE_SIG_ALL)

    如果启用该选项,则模块将在构建的 modules_install 阶段自动签名。如果禁用该选项,则必须使用以下方法手动签名模块:

    scripts/sign-file
    
  3. “应该使用哪种哈希算法对模块进行签名?”

    这给出了安装阶段使用哪种哈希算法对模块进行签名的选择:

    CONFIG_MODULE_SIG_SHA1      "Sign modules with SHA-1"
    CONFIG_MODULE_SIG_SHA224    "Sign modules with SHA-224"
    CONFIG_MODULE_SIG_SHA256    "Sign modules with SHA-256"
    CONFIG_MODULE_SIG_SHA384    "Sign modules with SHA-384"
    CONFIG_MODULE_SIG_SHA512    "Sign modules with SHA-512"
    

    这里选择的算法也将被构建到内核中(而不是作为一个模块),以便用该算法签名的模块可以检查其签名而不会导致依赖循环。

  4. “模块签名密钥的文件名或 PKCS#11 URI”(CONFIG_MODULE_SIG_KEY)

    将此选项设置为除其默认值“certs/signing_key.pem”以外的其他值将禁用签名密钥的自动生成,并允许使用您选择的密钥对内核模块进行签名。提供的字符串应标识一个文件,该文件包含私钥及其对应的 PEM 格式的 X.509 证书,或者 — 在 OpenSSL ENGINE_pkcs11 可运行的系统上 — RFC7512 定义的 PKCS#11 URI。在后一种情况下,PKCS#11 URI 应同时引用证书和私钥。

    如果包含私钥的 PEM 文件是加密的,或者 PKCS#11 令牌需要 PIN,则可以在构建时通过 KBUILD_SIGN_PIN 变量提供该 PIN。

  5. “默认系统密钥环的附加 X.509 密钥”(CONFIG_SYSTEM_TRUSTED_KEYS)

    此选项可以设置为包含附加证书的 PEM 编码文件的文件名,这些证书默认包含在系统密钥环中。

请注意,启用模块签名会将对 OpenSSL 开发包的依赖添加到执行签名的工具的内核构建过程中。


生成签名密钥

生成和检查签名需要加密密钥对。私钥用于生成签名,相应的公钥用于检查签名。私钥仅在构建期间需要,之后可以删除或安全存储。公钥内置于内核中,因此可以在加载模块时使用它来检查签名。

在正常情况下,当 CONFIG_MODULE_SIG_KEY 与默认值保持不变时,如果文件中不存在密钥对,内核构建将自动使用 openssl 生成一个新的密钥对:

certs/signing_key.pem

在构建 vmlinux 期间(密钥的公共部分需要构建到 vmlinux 中),使用以下参数:

certs/x509.genkey

文件(如果不存在也会生成该文件)。

强烈建议您提供自己的 x509.genkey 文件。

最值得注意的是,在 x509.genkey 文件中,req_distinguished_name 部分应该从默认值更改:

[ req_distinguished_name ]
#O = Unspecified company
CN = Build time autogenerated kernel key
#emailAddress = [email protected]

生成的 RSA 密钥大小也可以通过以下方式设置:

[ req ]
default_bits = 4096

也可以使用 Linux 内核源代码树根节点中的 x509.genkey 密钥生成配置文件和 openssl 命令手动生成密钥私钥/公钥文件。以下是生成公钥​​/私钥文件的示例:

openssl req -new -nodes -utf8 -sha256 -days 36500 -batch -x509 \
   -config x509.genkey -outform PEM -out kernel_key.pem \
   -keyout kernel_key.pem

然后可以在 CONFIG_MODULE_SIG_KEY 选项中指定生成的 kernel_key.pem 文件的完整路径名,并且将使用其中的证书和密钥来代替自动生成的密钥对。


内核中的公钥

内核包含一组公钥,可供 root 查看。它们位于名为“.system_keyring”的密钥环中,可供以下用户查看:

[root@deneb ~]# cat /proc/keys
...
223c7853 I------     1 perm 1f030000     0     0 keyring   .system_keyring: 1
302d2d52 I------     1 perm 1f010000     0     0 asymmetri Fedora kernel signing key: d69a84e6bce3d216b979e9505b3e3ef9a7118079: X509.RSA a7118079 []
...

除了专门为模块签名生成的公钥之外,还可以在 CONFIG_SYSTEM_TRUSTED_KEYS 配置选项引用的 PEM 编码文件中提供其他受信任的证书。

此外,架构代码可以从硬件存储中获取公钥并将其添加进去(例如来自 UEFI 密钥数据库)。

最后,可以通过执行以下操作添加其他公钥:

keyctl padd asymmetric "" [.system_keyring-ID] <[key-file]

例如:

keyctl padd asymmetric "" 0x223c7853 <my_public_key.x509

但请注意,内核只允许将密钥添加到 .system_keyring如果新密钥的 X.509 包装器由在添加密钥时已驻留在 .system_keyring 中的密钥有效签名。


手动签名模块

要手动签名模块,请使用 Linux 内核源代码树中提供的 scripts/sign-file 工具。该脚本需要 4 个参数:

1.  The hash algorithm (e.g., sha256)
2.  The private key filename or PKCS#11 URI
3.  The public key filename
4.  The kernel module to be signed

以下是对内核模块进行签名的示例:

scripts/sign-file sha512 kernel-signkey.priv \
    kernel-signkey.x509 module.ko

使用的哈希算法不必与配置的算法匹配,但如果不匹配,则应确保哈希算法内置于内核中,或者可以在不需要自身的情况下加载。

如果私钥需要密码或 PIN,则可以在 $KBUILD_SIGN_PIN 环境变量中提供。


签名模块和剥离

已签名的模块在末尾附加了数字签名。模块文件末尾的字符串“~Module signature appended~.”确认了签名的存在,但无法确认签名是否有效!

签名模块很脆弱,因为签名位于定义的 ELF 容器之外。因此,一旦计算并附加签名,就不能剥离它们。请注意,整个模块都是签名的有效负载,包括签名时存在的任何和所有调试信息。


加载已签名的模块

模块使用 insmod、modprobe、init_module() 或 finit_module() 加载,与未签名模块完全相同,因为用户空间中不进行任何处理。签名检查全部在内核中完成。


无效签名和未签名的模块

如果启用了 CONFIG_MODULE_SIG_FORCE 或在内核命令行上提供了 forcemodulesig=1,则内核将仅加载具有公钥的有效签名模块。否则,它还将加载未签名的模块。任何内核有密钥但证明签名不匹配的模块将不允许加载。

任何具有无法解析签名的模块都将被拒绝。


管理/保护私钥

由于私钥用于对模块进行签名,因此病毒和恶意软件可能会使用私钥对模块进行签名并破坏操作系统。必须销毁私钥或将其移至安全位置,而不要将其保存在内核源代码树的根节点中。

答案2

编辑./include/generated/autoconf.h并更改行

define CONFIG_MODULE_SIG 1

define CONFIG_MODULE_SIG 0

相关内容