linux: 署名付きカーネルモジュールの署名の付き方

Fedora等、モジュールを署名しているディストリビューションの場合、カーネルモジュールのELFバイナリの最後に署名がついています。これがとんな感じなの?いうところはkernelのDocumentation/module-signing.txtに書かれています。/proc/keysで鍵の確認とかはrootじゃないとできません。 テストした環境はFedora 22(x86_64)です。

/proc/keysをrootで開くとこんなふうになってます。

root@saga:/proc# cat /proc/keys
02c9848e I------     1 perm 1f030000     0     0 keyring   .persistent_register: 1
039b4a4a I--Q---     3 perm 1f3f0000     0 65534 keyring   _uid.0: empty
096cc3a2 I------     1 perm 1f030000     0     0 keyring   .system_blacklist_keyring: empty
13630bc6 I------     1 perm 1f030000     0     0 asymmetri Fedora kernel signing key: 5df3cf03be233f1af50438d0fce94781004fd41d: X509.RSA 004fd41d []
22310e67 I------     1 perm 1f0b0000     0     0 keyring   .system_keyring: 1
37ff8e04 I--Q---     6 perm 3f030000     0     0 keyring   _ses: 1
3cbed499 I--Q---     1 perm 1f3f0000     0 65534 keyring   _uid_ses.0: 1

これらファイルのうち.system_keyringカーネルに埋め込まれている公開鍵とのことです。

そして、.system_keyringの行の一番左側のカラムにある数値(ここでは22310e67)を使って情報を見れます。

root@saga:/proc# keyctl list 0x22310e67
1 key in keyring:
325258182: ---lswrv     0     0 asymmetric: Fedora kernel signing key: 5df3cf03be233f1af50438d0fce94781004fd41d

そしたら、カーネルモジュールを見てます。署名はELFのセクションではなくて、単にELFバイナリの最後にくっついています。hexdumpで見てみると最後のほうはこうなっています。

0000d4b0  39 01 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |9...............|
0000d4c0  01 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
0000d4d0  46 65 64 6f 72 61 20 6b  65 72 6e 65 6c 20 73 69  |Fedora kernel si|
0000d4e0  67 6e 69 6e 67 20 6b 65  79 5d f3 cf 03 be 23 3f  |gning key]....#?|
0000d4f0  1a f5 04 38 d0 fc e9 47  81 00 4f d4 1d 02 00 7f  |...8...G..O.....|
0000d500  3b 03 3a 63 a8 cb bc f5  fd 02 02 5a 43 fd 48 69  |;.:c.......ZC.Hi|
0000d510  fe 1e 58 a0 9b 88 1d 24  f6 91 d1 14 f7 83 69 a7  |..X....$......i.|
0000d520  36 3c ac d0 90 6e e0 ed  c7 9d 7a e4 ff 2e c0 14  |6<...n....z.....|
0000d530  da 62 cc f8 d4 37 cf e5  f0 c5 ec 8b bf 8c a9 f3  |.b...7..........|
0000d540  38 bf 12 35 2b e8 30 8e  87 34 f3 f1 52 f8 55 0b  |8..5+.0..4..R.U.|
0000d550  46 74 37 1c da 4b 7d 79  47 a0 25 08 85 53 6d e6  |Ft7..K}yG.%..Sm.|
0000d560  de 4e 46 8d f8 5e 79 b5  64 fa 10 33 7f 87 29 34  |.NF..^y.d..3..)4|
0000d570  f5 92 4a 99 76 12 aa be  76 d0 09 85 72 91 37 4f  |..J.v...v...r.7O|
0000d580  0a ba 2a 56 cc 63 df a8  cb ff 8a 82 e7 f4 8a 8e  |..*V.c..........|
0000d590  b2 de e6 50 49 22 13 da  12 b0 42 ef 6a a6 12 b5  |...PI"....B.j...|
0000d5a0  8a 9b 7f e4 92 6e 3b 52  db 49 e5 30 e9 cf ed e9  |.....n;R.I.0....|
0000d5b0  c8 3a 84 6d a8 cb 53 fd  8e 11 8f 31 2b 15 18 3e  |.:.m..S....1+..>|
0000d5c0  14 53 a1 45 5f ce af ab  1d 7f e8 9d 93 66 5b ed  |.S.E_........f[.|
0000d5d0  df f5 42 da f3 e0 03 ec  8d 40 c4 f6 9a 3d 90 32  |..B......@...=.2|
0000d5e0  c1 02 06 f6 8d 54 c7 9c  27 e4 86 29 e5 37 72 01  |.....T..'..).7r.|
0000d5f0  9a 3b a6 3c df b8 15 47  13 a9 bd 9c f0 a5 ac 09  |.;.<...G........|
0000d600  bd 35 b0 43 a7 c7 64 76  e5 06 27 ea 96 c5 0d 1d  |.5.C..dv..'.....|
0000d610  2c ae 7a e0 27 3a 3e 24  b9 b9 ec eb 6f a1 05 ce  |,.z.':>$....o...|
0000d620  51 d4 af 45 7e 37 07 69  3c 1f 81 6f bf 06 aa 58  |Q..E~7.i<..o...X|
0000d630  24 92 24 cb c2 1d a6 88  be 0f bc 79 02 63 9d 9f  |$.$........y.c..|
0000d640  15 b7 65 fc f6 d3 93 d0  cd 68 4a 71 67 eb 9d 35  |..e......hJqg..5|
0000d650  ca b0 83 85 19 7b 95 b5  27 e1 16 57 d7 1b 93 d7  |.....{..'..W....|
0000d660  3f 34 04 0b d8 47 5b 6e  6a a9 a1 4c 9f 35 f4 e3  |?4...G[nj..L.5..|
0000d670  10 86 8a f4 8d 13 0d a7  41 0e 3e c4 ba 4b c2 b1  |........A.>..K..|
0000d680  e7 82 0c 1a b8 ba 1a 06  90 de 90 02 c2 8a ea 5a  |...............Z|
0000d690  63 48 6a f1 58 26 90 0a  84 5f 76 f6 e4 84 90 8e  |cHj.X&..._v.....|
0000d6a0  71 af 68 c5 44 63 3f 43  f8 f4 b2 6a 51 b5 74 5b  |q.h.Dc?C...jQ.t[|
0000d6b0  2f 3c c2 93 de 1a 4a d3  66 ec 25 be 51 59 35 0a  |/<....J.f.%.QY5.|
0000d6c0  fc bf 10 10 af 49 c4 26  0e 7a 79 09 de 0f 14 64  |.....I.&.zy....d|
0000d6d0  07 e6 a7 ae 74 67 09 98  b2 7b a5 1e db 75 ed 85  |....tg...{...u..|
0000d6e0  f6 9f 7e b5 27 f7 70 f2  99 36 ab c6 3f af 8e a2  |..~.'.p..6..?...|
0000d6f0  62 cb 16 0c ca 67 0b 72  46 7c ae 1d dc d0 2d 01  |b....g.rF|....-.|
0000d700  04 01 19 14 00 00 00 00  00 02 02 7e 4d 6f 64 75  |...........~Modu|
0000d710  6c 65 20 73 69 67 6e 61  74 75 72 65 20 61 70 70  |le signature app|
0000d720  65 6e 64 65 64 7e 0a                              |ended~.|
0000d727

バイナリの最後のところに~Module signature appended~とあり、これが署名の最後を表しているマーカーです。アドレス0x0000d4d0からはFedora kernel signing keyが見えていて、ここがFedoraカーネルビルド時につけた署名の先頭部分です。この範囲はELFのセクションでも何でもないところです。stripコマンドにかけると消えます。

ついでなので、このマーカーに挟まれた部分をシェルスクリプトで取り出してみようかなと思った結果が下です。c言語でやれば簡単なのは分かっているので、なんとなくシェルでやってみたと。 シェルスクリプトでバイナリを触るのはod(1)さえあればなんとかなるものですね。出力結果を加工するのにsedを使ってたりしますが(空白削るだけならtrでも良いけど)。。。

やってる内容としては、ファイルの最後の部分が~Module signature appended~になっているか調べて、マーカーが見つかったらその文字列が始まっているアドレスを16の倍数に切り下げて$(((${marker_start_offset} >> 4) << 4))16バイトごとに前に戻りつつFedora〜の場所を探してます。0xfffffff0でマスクしても良いんだけど、32/64bitの違いを気にするのも面倒だし、右シフトと左シフトでやったほうが楽かなーと。

#!/bin/bash

if [ $# -ne 1 ]; then
        echo "Usage $0 [kernel module]"
        exit 1
fi

kmodfile=$1
filesize=`stat -c %s ${kmodfile}`

printf "file size is 0x%x\n" ${filesize}

module_sign_marker="~Module signature appended~"
module_sign_marker_len=`echo ${#module_sign_marker}`

module_sign_fedora_key_marker="Fedora kernel signing key"
module_sign_fedora_key_marker_stripped=`echo ${module_sign_fedora_key_marker} | sed 's/ //g'`
module_sign_fedora_key_marker_len=${#module_sign_fedora_key_marker}

printf "fedora len: 0x%x\n" $module_sign_fedora_key_marker_len

function modsign_check_key() {
        strings ${kmodfile} | grep "${module_sign_marker}" 2>&1 >/dev/null
        if [ $? -ne 0 ]; then
                echo "Module not signed"
                return 1
        fi

        marker_start_offset=$((${filesize} - ${module_sign_marker_len}))
        printf "marker_start_offset is 0x%x\n" ${marker_start_offset}

        check_start_offset=$(((${marker_start_offset} >> 4) << 4))
        printf "check_start_offset is 0x%x\n" ${check_start_offset}

        fedora_key_start_addr=0
        for ((fedora_key_start_addr=${check_start_offset}; fedora_key_start_addr >= 0; fedora_key_start_addr-=16));
        do
                s=`od --width=64 -An  -tc -j ${fedora_key_start_addr} -N ${module_sign_fedora_key_marker_len} ${kmodfile} | sed 's/\\n//g' | sed 's/ //g'`

                if [ "${s}" = "${module_sign_fedora_key_marker_stripped}" ]; then
                        echo "found"
                        break
                fi
        done

        printf "fedora_key_start_addr is 0x%x\n" ${fedora_key_start_addr}
        key_start_addr=$((${fedora_key_start_addr} + ${module_sign_fedora_key_marker_len}))
        printf "key_start_addr is 0x%x\n" ${key_start_addr}
        key_size=$((${marker_start_offset} - ${key_start_addr}));
        printf "key_size is 0x%x\n" ${key_size}

    od --width=1024 -An -txC -j ${key_start_addr} -N ${key_size} ${kmodfile} | sed 's/ //g'
}


modsign_check_key

できるPRO Red Hat Enterprise Linux 7 (できるPROシリーズ)

できるPRO Red Hat Enterprise Linux 7 (できるPROシリーズ)