Signed kernel module support
Since Linux kernel version 3.7 onwards, support has been added for signed kernel modules. When enabled, the Linux kernel will only load kernel modules that are digitally signed with the proper key. This allows further hardening of the system by disallowing unsigned kernel modules, or kernel modules signed with the wrong key, to be loaded. Malicious kernel modules are a common method for loading rootkits on a Linux system.
Enabling support is a matter of toggling a few settings in the Linux kernel configuration. Unless you want to use your own keypair, this is all that has to be done to enable kernel module support.Enabling module signature verification
Configuring module signature verification
Module signature verification is a kernel feature, so has to be enabled through the Linux kernel configuration. You can find the necessary options under Enable loadable module support.
--- Enable loadable module support [*] Module signature verification [*] Require modules to be validly signed [*] Automatically sign all modules Which hash algorithm should modules be signed with? (Sign modules with SHA-512) --->
The option Module signature verification (CONFIG_MODULE_SIG) enables the module signature verification in the Linux kernel. It supports two approaches on signed module support: a rather permissive one and a strict one. By default, the permissive approach is used, which means that the Linux kernel module either has to have a valid signature, or no signature. With the strict approach, a valid signature must be present. In the above example, the strict approach is used by selecting Require modules to be validly signed (CONFIG_MODULE_SIG_FORCE). Another way of enabling this strict approach is to set the kernel boot option enforcemodulesig=1.
When building the Linux kernel, the kernel modules will not be signed automatically unless you select Automatically sign all modules (CONFIG_MODULE_SIG_ALL).
Finally, we need to select the hash algorithm to use with the cryptographic signature. In the above example, we use SHA-512.
Building the kernel with proper keys
When the Linux kernel is building with module signature verification support enabled, then you can use your own keys or have the Linux kernel build infrastructure create a set for you. If you want the Linux kernel build infrastructure to create it for you, just continue as you always do with a make and make modules_install. At the end of the build process, you will notice that signing_key.priv and signing_key.x509 will be available on the root of the Linux kernel sources.
If we want to use our own keys, you can use openssl to create a key pair (private key and public key). The following command, taken from kernel/Makefile, creates such a key pair.
[ req ] default_bits = 4096 distinguished_name = req_distinguished_name prompt = no string_mask = utf8only x509_extensions = myexts [ req_distinguished_name ] O = GenFic CN = Kernel Signing Key emailAddress = server.support@genfic.com [ myexts ] basicConstraints=critical,CA:FALSE keyUsage=digitalSignature subjectKeyIdentifier=hash authorityKeyIdentifier=keyid
The resulting files need to be stored as signing_key.x509 and signing_key.priv in the root of the Linux kernel source tree.
The public key part will be build inside the Linux kernel. If you configured the kernel to sign modules, this signing will take place during the make modules_install part.
Validating module signature support
Reboot with the newly configured kernel. In the output of dmesg you should be able to confirm that the proper certificate is loaded:
The kernel modules have the digital signature appended at the end. A simple hexdump can confirm if a signature is present or not:
The string ~Module signature appended~ at the end confirms that a signature is present. Of course, it does not confirm that the signature is valid or not.
To remove the signature, we can use the strip command:
If we try to load this module now, we get a failure:
This confirms that modules without a signature are not loaded.
Administering kernel module signatures
Once the kernel boots and we have validated that the signed kernel module support works, it is important to correctly handle the keys themselves.
Protecting the private key
The private key, stored as signing_key.priv, needs to be moved to a secure location (unless you will be creating new keys for new kernels, in which case the file can be removed). Do not keep it at /usr/src/linux on production systems as malware can then easily use this key to sign the malicious kernel modules (such as rootkits) and compromise the system further.
Manually signing modules
If you ever need to manually sign a kernel module, you can use the scripts/sign-file script available in the Linux kernel source tree. It requires four arguments:
- The hash algorithm to use, such as sha512
- The private key location
- The certificate (which includes the public key) location
- The kernel module to sign
In this case, the key pair does not need to be named signing_file.priv and such, nor do they need to be in the root of the Linux kernel source tree location.
Distributing the kernel and modules
If we create a kernel package through make tarbz2-pkg, the modules in it will be signed already so we do not need to manually sign them afterwards. The signing keys themselves are not distributed with it.
More resources
In Booting a self-signed Linux kernel Greg Kroah-Hartman describes how to boot a self-signed Linux kernel from EFI. As having signed kernel module support is only secure if the Linux kernel is trusted, this is an important (and related) feature to work with.
最近在调试一个驱动的时候,用insmod加载.ko的时候,提示Required key not available,
内核配置内核从3.7后开始支持模块签名,这个功能使能以后,内核只允许安装特定key签名的模块。
内核配置项
CONFIG_MODULE_SIG=y
表示开启了签名机制,但是这时候模块签名或不签名都可以使用。
CONFIG_MODULE_SIG_FORCE=y
如果上述配置项使能,则模块必须有正确的签名才能正常使用。
CONFIG_MODULE_SIG_ALL=y
内核在编译的时候,并不会主动去给模块签名,除非你把上述配置项打开。
另外CONFIG_MODULE_SIG=sha256指定了加密的算法
查看内核配置文件,发现上面3个配置项确实都打开了,因此肯定是ko签名的问题。内核如何签名
在内核kernel/kernel下的Makefile中有如下,
signing_key.priv signing_key.x509: x509.genkey
其中,x509.genkey是生成key pair时的配置项,signing_key.priv signing_key.x509分别为private key和数字证书。数字证书会打包进内核,里面有公钥等,用来解密嘛。每编译一次,虽然配置文件每次都相同,但是生成的key pair是不同的。
查看签名信息
利用下面命令查看设备中的ko文件信息,
hexdump -C my_ko.ko |tail -5
下面是输出(内核签名后会把签名信息附在模块的最后面),
005387a0 73 c9 a2 78 6a 6e 4c f7 4f 36 b3 45 1b 64 73 b8 |s..xjnL.O6.E.ds.|
005387b0 1d ca 49 ff 59 6a 99 4b 5b 13 40 75 01 06 01 1e |..I.Yj.K[.@u....|
005387c0 14 00 00 00 00 00 02 02 7e 4d 6f 64 75 6c 65 20 |........~Module |
005387d0 73 69 67 6e 61 74 75 72 65 20 61 70 70 65 6e 64 |signature append|
005387e0 65 64 7e 0a |ed~.|
005387e4
由上面输出,我们发现这个ko已经有签名信息(Module signature appended),为何还是提示key不对。于是我将编译机中版本的my_ko.ko和设备中的做比较,发现唯有最后部分不同,我猜一定是两个ko的签名不同,这应该就是初步原因。
问题根源
仔细分析后,得到原因:
原来设备中的内核是后来编译的,编译完成后我将内核单独烧录进设备(内核肯定就放在kernel的分区),而未改变文件系统(这样会造成新kernel中的数字证书已经改变,但是文件系统中的my_ko.ko未改变,而是用以前的内核中private key进行签名的)。重新完整烧录版本后,一切功能正常!
linux配置文件:
Linux的内核配置文件有两个,一个是隐含的.config文件,嵌入到主Makefile中;另一个是include/linux/autoconf.h,嵌入到各个c源文件中,它们由make config、make menuconfig、make xconfig这些过程创建。几乎所有的源文件都会通过linux/config.h而嵌入autoconf.h,如果按照通常方法建立文件依赖关系(.depend),只要更新过autoconf.h,就会造成所有源代码的重新编绎。
为了优化make过程,减少不必要的重新编绎,Linux开发了专用的mkdep工具,用它来取代gcc来生成.depend文件。mkdep在处理源文件时,忽略linux/config.h这样的头文件,识别源文件宏指令中具有"CONFIG_"特征的行。例如,如果有"#ifdef CONFIG_SMP"这样的行,它就会在.depend文件中输出$(wildcard /usr/src/linux/include/config/smp.h)。
include/config/下的文件是另一个工具split-include从autoconf.h中生成,它利用autoconf.h中的CONFIG_标记,生成与mkdep相对应的文件。例如,如果autoconf.h中有"#undef CONFIG_SMP"这一行,它就生成include/config/smp.h文件,内容为"#undef CONFIG_SMP"。这些文件名只在.depend文件中出现,内核源文件是不会嵌入它们的。每配置一次内核,运行split-include一次。split-include会检查旧的子文件的内容,确定是不是要更新它们。这样,不管autoconf.h修改日期如何,只要其配置不变,make就不会重新编绎内核。
如果系统的编绎选项发生了变化,Linux也能进行增量编绎。为了做到这一点,make每编绎一个源文件时生成一个flags文件。例如编绎sched.c时,会在相同的目录下生成隐含的.sched.o.flags文件。它是Makefile的一个片断,当make进入某个子目录编绎时,会搜索其中的flags文件,将它们嵌入到Makefile中。这些flags代码测试当前的编绎选项与原来的是不是相同,如果相同,就将自已对应的目标文件加入FILES_FLAGS_UP_TO_DATE列表,然后,系统从编绎对象表中删除它们,得到FILES_FLAGS_CHANGED列表,最后,将它们设为目标进行更新。