我正在开发一个内核模块,它工作正常。但是,通过dmesg查看时,我看到有关模块的消息,表明模块验证失败(模块验证失败签名和/或缺少必需的密钥)。
我该如何解决这个问题?如何为我的模块签名以进行验证?
谢谢。
我正在开发一个内核模块,它工作正常。但是,通过dmesg查看时,我看到有关模块的消息,表明模块验证失败(模块验证失败签名和/或缺少必需的密钥)。
我该如何解决这个问题?如何为我的模块签名以进行验证?
谢谢。
Answers:
您在此描述的所有需求
内核模块签名工具在安装过程中对模块进行加密签名,然后在加载模块时检查签名。通过禁止加载未签名的模块或使用无效密钥签名的模块,可以提高内核的安全性。模块签名通过使更难将恶意模块加载到内核中来提高安全性。模块签名检查由内核完成,因此不必具有受信任的用户空间位。
该设施使用X.509 ITU-T标准证书对涉及的公钥进行编码。签名本身未编码为任何工业标准类型。该工具当前仅支持RSA公钥加密标准(尽管它是可插拔的,并允许使用其他标准)。可以使用的可能的哈希算法为SHA-1,SHA-224,SHA-256,SHA-384和SHA-512(该算法由签名中的数据选择)。
通过转到内核配置的“启用可加载模块支持”部分并打开,可以启用模块签名功能。
CONFIG_MODULE_SIG "Module signature verification"
有很多可用的选项:
“要求模块进行有效签名”(CONFIG_MODULE_SIG_FORCE)
这指定内核应如何处理具有未知密钥的签名的模块或未签名的模块。
如果关闭(例如“ permissive”),则允许使用密钥不可用的模块和未签名的模块,但是内核将被标记为已污染,而相关模块将被标记为已污染,如下所示字符“ E”。
如果启用此选项(即“限制性”),则只会加载具有有效签名且可以通过内核拥有的公钥验证的模块。所有其他模块都会产生错误。
不管此处的设置如何,如果模块具有无法解析的签名块,它将立即被拒绝。
“自动签名所有模块”(CONFIG_MODULE_SIG_ALL)
如果启用,则将在构建的modules_install阶段自动对模块进行签名。如果关闭,则必须使用以下方法手动对模块进行签名:
scripts/sign-file
“模块应使用哪种哈希算法签名?”
这提供了安装阶段将使用以下方式对模块进行签名的哈希算法的选择:
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"
此处选择的算法也将内置到内核中(而不是作为模块),以便使用该算法签名的模块可以检查其签名而不会引起依赖关系循环。
“模块签名密钥的文件名或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。
“默认系统密钥环的其他X.509密钥”(CONFIG_SYSTEM_TRUSTED_KEYS)
可以将此选项设置为包含其他证书的PEM编码文件的文件名,默认情况下,这些证书将包含在系统密钥环中。
请注意,启用模块签名将对OpenSSL devel软件包的依赖性添加到执行签名工具的内核构建过程中。
需要使用密钥对来生成和检查签名。私钥用于生成签名,相应的公钥用于对其进行检查。仅在构建期间需要私钥,之后才能安全删除或存储私钥。公钥已内置到内核中,以便在模块加载时可以用来检查签名。
在正常情况下,如果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 = unspecified.user@unspecified.company
生成的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文件的完整路径名,并且将使用其中的证书和密钥代替自动生成的密钥对。
内核包含一个可以由根用户查看的公共密钥环。它们位于称为“ .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
但是请注意,如果新密钥的X.509包装器由添加密钥时已经驻留在.system_keyring中的密钥有效地签名,则内核仅允许将密钥添加到.system_keyring 。
要手动签名模块,请使用Linux内核源代码树中可用的脚本/签名文件工具。该脚本需要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环境变量中提供。
签名模块的末尾简单地附加了数字签名。字符串“〜附加模块签名〜”。模块文件末尾的确认存在签名,但不确认签名有效!
由于签名位于已定义的ELF容器之外,因此已签名的模块为BRITTLE。因此,一旦计算并附加了签名,就不得剥夺它们。请注意,整个模块是已签名的有效负载,包括在签名时出现的所有调试信息。
模块将使用insmod,modprobe,init_module()或finit_module()加载,这与未签名的模块完全一样,因为用户空间中未进行任何处理。签名检查全部在内核中完成。
如果启用了CONFIG_MODULE_SIG_FORCE或内核命令行上提供了configuremodulesig = 1,则内核将仅加载具有公钥的经过有效签名的模块。否则,它还将加载未签名的模块。内核具有密钥但被证明具有签名不匹配的任何模块将不允许加载。
签名无法解析的模块将被拒绝。
由于使用私钥对模块进行签名,因此病毒和恶意软件可能会使用私钥对模块进行签名并破坏操作系统。私钥必须被销毁或移至安全位置,并且不能保留在内核源树的根节点中。