内核模块签名机制¶
概述¶
内核模块签名机制在安装期间对模块进行加密签名,然后在加载模块时检查签名。这通过禁止加载未签名的模块或使用无效密钥签名的模块来提高内核安全性。模块签名通过使恶意模块更难以加载到内核中来提高安全性。模块签名检查由内核完成,因此无需信任的用户空间位。
此机制使用 X.509 ITU-T 标准证书来编码所涉及的公钥。签名本身未以任何行业标准类型进行编码。内置机制当前仅支持 RSA 和 NIST P-384 ECDSA 公钥签名标准(尽管它是可插拔的并允许使用其他标准)。可使用的哈希算法是大小为 256、384 和 512 的 SHA-2 和 SHA-3(算法由签名中的数据选择)。
配置模块签名¶
通过转到内核配置的
部分并启用以下选项来启用模块签名机制CONFIG_MODULE_SIG "Module signature verification"
这有许多可用的选项
(
CONFIG_MODULE_SIG_FORCE
)这指定了内核应如何处理密钥未知签名的模块或未签名的模块。
如果此选项关闭(即“宽容”),则允许密钥不可用的模块和未签名的模块,但内核将被标记为已污染,并且相关模块将被标记为已污染,并显示字符“E”。
如果此选项打开(即“限制性”),则仅加载具有有效签名且可由内核拥有的公钥验证的模块。所有其他模块将生成错误。
无论此处的设置如何,如果模块具有无法解析的签名块,都将被直接拒绝。
(
CONFIG_MODULE_SIG_ALL
)如果此选项打开,则将在构建的 modules_install 阶段自动签名模块。如果此选项关闭,则必须使用以下方法手动签名模块
scripts/sign-file
这提供了一个选择,即安装阶段将使用哪种哈希算法来签名模块
CONFIG_MODULE_SIG_SHA256
CONFIG_MODULE_SIG_SHA384
CONFIG_MODULE_SIG_SHA512
CONFIG_MODULE_SIG_SHA3_256
CONFIG_MODULE_SIG_SHA3_384
CONFIG_MODULE_SIG_SHA3_512
此处选择的算法也将构建到内核中(而不是作为模块),以便可以使用该算法签名的模块检查其签名,而不会导致依赖循环。
(
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
变量在构建时提供。(
CONFIG_SYSTEM_TRUSTED_KEYS
)此选项可以设置为 PEM 编码文件的文件名,该文件包含默认情况下将包含在系统密钥环中的其他证书。
请注意,启用模块签名会为执行签名的工具向内核构建过程添加对 OpenSSL 开发包的依赖项。
生成签名密钥¶
生成和检查签名需要加密密钥对。私钥用于生成签名,相应的公钥用于检查签名。私钥仅在构建期间需要,之后可以删除或安全存储。公钥被构建到内核中,以便可以在加载模块时使用它来检查签名。
在正常情况下,当 CONFIG_MODULE_SIG_KEY
从其默认值不变时,如果文件中不存在密钥对,则内核构建将在 vmlinux 的构建过程中自动使用 openssl 生成新的密钥对(公钥部分需要构建到 vmlinux 中),并使用
certs/signing_key.pem
文件中的参数(如果该文件尚不存在,也会生成)。
certs/x509.genkey
文件中的参数(如果该文件尚不存在,也会生成)。
可以选择 RSA (MODULE_SIG_KEY_TYPE_RSA
) 和 ECDSA (MODULE_SIG_KEY_TYPE_ECDSA
) 来生成 RSA 4k 或 NIST P-384 密钥对。
强烈建议您提供自己的 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 用户查看。它们位于名为“.builtin_trusted_keys”的密钥环中,可以通过以下方式查看
[root@deneb ~]# cat /proc/keys
...
223c7853 I------ 1 perm 1f030000 0 0 keyring .builtin_trusted_keys: 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 "" [.builtin_trusted_keys-ID] <[key-file]
例如
keyctl padd asymmetric "" 0x223c7853 <my_public_key.x509
但是请注意,只有当新密钥的 X.509 包装器由添加密钥时已驻留在 .builtin_trusted_keys
中的密钥有效签名时,内核才会允许将密钥添加到 .builtin_trusted_keys
。
手动签名模块¶
要手动签名模块,请使用 Linux 内核源代码树中可用的 scripts/sign-file 工具。该脚本需要 4 个参数
哈希算法(例如,sha256)
私钥文件名或 PKCS#11 URI
公钥文件名
要签名的内核模块
以下是签名内核模块的示例
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
或在内核命令行中提供了 module.sig_enforce=1,则内核只会加载具有有效签名且具有公钥的模块。否则,它还会加载未签名的模块。任何内核拥有密钥,但被证明签名不匹配的模块都将不允许加载。
任何具有无法解析的签名的模块都将被拒绝。
管理/保护私钥¶
由于私钥用于签名模块,病毒和恶意软件可以使用私钥签名模块并危害操作系统。私钥必须被销毁或移动到安全位置,而不能保留在内核源代码树的根节点中。
如果您使用相同的私钥为多个内核配置签名模块,则必须确保模块版本信息足以防止将模块加载到不同的内核中。可以设置 CONFIG_MODVERSIONS=y
,或者通过更改 EXTRAVERSION
或 CONFIG_LOCALVERSION
来确保每个配置都有不同的内核发行字符串。