###1. linux内核模块签名

linux内核从3.7 开始加入模块签名检查机制, 校验签名是否与已编译的内核公钥匹配。目前只支持RSA X.509验证, 模块签名验证并非强制使用, 可在编译内核时配置是否开启

linux 内核中与模块签名验证相关的config项有:

  • CONFIG_MODULE_SIG : 模块签名验证功能, 配置为‘y’后, 还有如下的子选项可以配置
  • CONFIG_MODULE_SIG_FORCE :是否验证模块的签名, 配置为‘n’时, 对于未签名或者使用无效的key签名的模块, 内核都允许加载, 但是kernel会被标记为被感染(tainted), 并且使用商业许可的模块也会被标记为被感染, 若配置为‘y’, 则只有正确签名的模块才能被允许加载, 其他的模块将被拒绝加载, 并且kernel会打出错误信息
  • CONFIG_MODULE_SIG_ALL : 若配置为’n’, 则在build kernel的过程中, 所有的模块都不会被签名, 需要自己手动签名, 若被配置为‘y’, 则在build kernel的 “modules_install”阶段, 所有的模块都会被自动签名

若不希望在加载模块的过程中进行签名验证, 只需要在build kernel 时关闭相关config项即可

若build kernel 时打开了CONFIG_MODULE_SIG, 但是没有打开CONFIG_MODULE_SIG_FORCE, 则还可以在启动kernel时传递内核参数”module.sig_enforce“来打开模块签名验证

内核提供了多种签名算法供使用, 可在编译内核时进行配置

  • CONFIG_MODULE_SIG_SHA1
  • CONFIG_MODULE_SIG_SHA224
  • CONFIG_MODULE_SIG_SHA256
  • CONFIG_MODULE_SIG_SHA384
  • CONFIG_MODULE_SIG_SHA512

当选中某一中算法时, 该算法相关的代码也会被built-in到内核中

###2. 签名所用的key

在编译内核时, 需要一对公钥和私钥文件

  • signing_key.priv : 私钥用于对模块进行签名
  • signing_key.x509 : 公钥则会被编译进内核中, 用于为模块进行签名验证

公钥和私钥文件需要放置在编译内核时的out目录的根目录下, 内核的Makefile中会使用“MODPUBKEY”和“MODSECKEY”两个变量来指定公钥和私钥文件

默认情况下, 在编译内核的过程中, 会利用 /dev/random 中的数据自动生成公钥和私钥, 当然, 在生成公钥和私钥前, 还会生成必需的根证书文件 x509.genkey, 这些文件的生成过程是在 KERNEL_SRC/kernel/Makefile 中完成的

signing_key.priv signing_key.x509: x509.genkey
    @echo "###"
    @echo "### Now generating an X.509 key pair to be used for signing modules."
    @echo "###"
    @echo "### If this takes a long time, you might wish to run rngd in the"
    @echo "### background to keep the supply of entropy topped up.  It"
    @echo "### needs to be run as root, and uses a hardware random"
    @echo "### number generator if one is available."
    @echo "###"
    openssl req -new -nodes -utf8 -$(CONFIG_MODULE_SIG_HASH) -days 36500 \
            -batch -x509 -config x509.genkey \
            -outform DER -out signing_key.x509 \
            -keyout signing_key.priv 2>&1
    @echo "###"
    @echo "### Key pair generated."
    @echo "###"

x509.genkey:
    @echo Generating X.509 key generation config
    @echo  >x509.genkey "[ req ]"
    @echo >>x509.genkey "default_bits = 4096"
    @echo >>x509.genkey "distinguished_name = req_distinguished_name"
    @echo >>x509.genkey "prompt = no"
    @echo >>x509.genkey "string_mask = utf8only"
    @echo >>x509.genkey "x509_extensions = myexts"
    @echo >>x509.genkey
    @echo >>x509.genkey "[ req_distinguished_name ]"
    @echo >>x509.genkey "O = Magrathea"
    @echo >>x509.genkey "CN = Glacier signing key"
    @echo >>x509.genkey "emailAddress = slartibartfast@magrathea.h2g2"
    @echo >>x509.genkey
    @echo >>x509.genkey "[ myexts ]"
    @echo >>x509.genkey "basicConstraints=critical,CA:FALSE"
    @echo >>x509.genkey "keyUsage=digitalSignature"
    @echo >>x509.genkey "subjectKeyIdentifier=hash"
    @echo >>x509.genkey "authorityKeyIdentifier=keyid"

按照上面Makefile中的步骤, 我们可以很容易生成自己定制的的key pairs, 如果需要使用自己的key pairs而不是随机生成,请按照如下的步骤

  • 若编译内核时, src目录和out目录重合, 则直接将生成的key pairs文件放置在 kernel src 目录的根目录下
  • 若编译内核时, src目录和out目录不重合, 则直接将生成的key pairs文件放置在 kernel out 目录的根目录下, 且不要在kernel src 目录下放置这两个key文件以及任何和这两个文件同名的文件

###3. 内核中的公钥

模块签名所需要的公钥会被编译到内核中, 事实上内核中可以保存多份公钥(编译内核时, 在src或out目录根目录中的所有以”.x509”为后缀的文件都会作为公钥被编译进内核), 这涉及到linux 内核的密钥暂存服务, 会单独讲解

为查看内核中的所有公钥, 可以使用

$ cat /proc/keys

或者查看linux boot时加载的模块签名的公钥的log

$ dmesg | grep “MODSIGN: Loaded cer”

###4. 模块签名

linux 源码中提供了模块签名的工具 “ scripts/sign-file”, 在Makefile 中可以看到其使用方法

mod_sign_cmd = perl $(srctree)/scripts/sign-file $(CONFIG_MODULE_SIG_HASH) $(MODSECKEY) $(MODPUBKEY)
  • CONFIG_MODULE_SIG_HASH : 可以取值1, 224, 256, 384, 512
  • MODSECKEY : 私钥文件
  • MODPUBKEY : 公钥文件

例如:

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

签名后的模块, 在文件尾部回附加“~Module signature appended~.”字符串, 可以使用二进制文本编辑器查看

$ hexdump -C cpuid.ko  | tail -n 40

00 00 00 00 00 00 00 00  4d 61 67 72 61 74 68 65  |........Magrathe|
61 3a 20 47 6c 61 63 69  65 72 20 73 69 67 6e 69  |a: Glacier signi|
6e 67 20 6b 65 79 00 a5  a6 57 59 de 47 4b c5 c4  |ng key...WY.GK..|
31 20 88 0c 1b 94 a5 39  f4 31 02 00 29 18 64 dd  |1 .....9.1..).d.|
8c a6 b7 db 18 a3 e1 82  f0 58 97 07 be 7c 97 4a  |.........X...|.J|
3e 19 36 84 f8 c6 8d a9  92 3b 53 44 9a 9b f2 ab  |>.6......;SD....|
9a 34 82 0c 87 f1 2b 29  8a 9b 24 ea 0a db 3b 4c  |.4....+)..$...;L|
76 68 89 a7 d2 95 a9 9f  32 ff 69 ee 35 3a c4 35  |vh......2.i.5:.5|
c3 c3 c4 66 17 bf 98 23  a1 25 80 8d 2f 4f 9d ef  |...f...#.%../O..|
e0 24 77 d3 6b b5 62 b8  5e 90 3b 23 8c 88 4d d8  |.$w.k.b.^.;#..M.|
a1 0c 71 08 01 92 18 ba  c0 86 da 0d 07 ee ba 02  |..q.............|
c8 86 58 18 d0 0f 3b 90  c1 81 b6 aa d1 71 21 58  |..X...;......q!X|
75 f2 38 b0 89 12 e4 8d  71 6f 2e d0 2e 34 b3 fe  |u.8.....qo...4..|
a5 b8 46 b1 67 95 72 93  59 56 4a 69 30 d9 a7 24  |..F.g.r.YVJi0..$|
cd 02 cd c3 01 cf ed 24  34 b9 7f 20 c8 df f4 22  |.......$4.. ..."|
be 04 21 c4 38 aa 83 ba  b9 2a b1 59 c7 26 66 76  |..!.8....*.Y.&fv|
8e 50 90 32 6b 14 46 60  bf f6 d5 f9 44 6e 0d 10  |.P.2k.F`....Dn..|
3f de e8 59 3b d6 fd da  ea a6 d5 96 03 f7 27 ea  |?..Y;.........'.|
34 b1 f9 67 96 7e 8d 87  46 a9 53 1f b0 bc 3f 5b  |4..g.~..F.S...?[|
da 8e d1 fc 10 2a 91 d4  5a 88 91 38 47 54 33 90  |.....*..Z..8GT3.|
b7 b2 fa 36 a5 c0 83 fd  a6 19 85 de ee f7 06 90  |...6............|
6e 11 79 df bc 3b 6c fb  7a aa 52 a1 83 79 e2 13  |n.y..;l.z.R..y..|
83 22 79 29 de e7 15 6a  b1 f5 15 a5 f3 23 71 91  |."y)...j.....#q.|
de f7 2e 69 f1 d0 ee 56  ad 2f 5b 60 4b 42 65 ba  |...i...V./[`KBe.|
d0 ef ac c6 82 04 de a7  80 5a f4 e8 23 ff c2 6c  |.........Z..#..l|
a3 2b 8d d6 b8 26 e2 d2  95 7d 3e f5 d9 a0 36 10  |.+...&...}>...6.|
16 7b 77 6a 19 0d d5 4d  fd ec d4 36 42 da 12 54  |.{wj...M...6B..T|
9e a3 20 b6 11 e3 c3 cd  da b4 a5 1b 72 d7 24 9b  |.. .........r.$.|
d4 10 03 95 6d 79 21 5d  91 56 de f3 40 29 a9 5e  |....my!].V..@).^|
a6 9e a4 c9 be d4 09 09  28 44 11 30 6b 8c 64 4c  |........(D.0k.dL|
94 aa 16 ec 04 68 b6 7f  c8 5d a5 25 27 b3 15 91  |.....h...].%'...|
9b 49 81 8a 01 80 00 fd  9d 0a 3e d8 7f 9c 44 0b  |.I........>...D.|
f0 32 b6 f0 45 06 83 9c  9a 12 c0 dc 40 18 28 15  |.2..E.......@.(.|
98 c7 e0 46 09 d5 1c 69  eb 6e 80 44 9b de fb 4c  |...F...i.n.D...L|
d2 a7 1c d2 b6 69 72 ae  71 5e 61 68 96 6e a2 91  |.....ir.q^ah.n..|
d6 fe 44 e6 84 4b a3 b6  5a 72 e8 37 01 06 01 1e  |..D..K..Zr.7....|
14 00 00 00 00 00 02 02  7e 4d 6f 64 75 6c 65 20  |........~Module |
73 69 67 6e 61 74 75 72  65 20 61 70 70 65 6e 64  |signature append|
65 64 7e 0a                                       |ed~.|

一个内核模块能够被多次签名, 这些签名信息会依次累加,但是在校验时, 只会使用最后一个签名信息, 若希望清除该模块的签名信息, 则可以使用

$ stripe --strip--debug cpuid.ko

该命令会清除所有的签名信息

###5. linux内核模块的签名验证

若kernel打开了模块签名验证, 则在加载kernel module的过程中, 会进行模块的签名验证,由“kernel/module.c” 中的 “module_sign_check()” 来完成

事实上, linux 的kernel module 加载过程中存在诸多检查, 模块签名验证只是第一步, 后面还会有 vermagic 和 CRC验证

###END