Signer ses modules kernel avec Yocto Project

Publié par cpb
Juin 25 2024

Pour des applications où l’aspect sécurité est particulièrement important, on souhaite souvent s’assurer de l’intégrité de l’espace mémoire et des activités du noyau Linux. Pour cela on vérifie que les modules chargés dynamiquement dans le noyau (les fichiers avec l’extension .ko) sont bien conformes à ceux prévus à la production du système en les signant cryptographiquement.

Si cette étape est souvent réalisée automatiquement pour les distributions Linux courantes (voir par exemple https://www.kernel.org/doc/html/latest/admin-guide/module-signing.html), elle est un peu moins simple à mettre en oeuvre pour les systèmes embarqués produits avec Yocto.

Lors d’une récente session de formation « Écriture de drivers pour Linux » que j’animais pour Logilin, un participant m’a interrogé sur les méthodes à employer pour signer les modules dans un contexte de système embarqué. Nous n’avons pas eu le temps de développer ce sujet durant cette session, mais j’ai voulu détailler ce thème dans ce petit article.

Installation d’un module kernel personnalisé avec Yocto Project

Les fichiers sources présentés ci-dessous sont disponibles ici : https://github.com/cpb-/meta-signing-modules.

Commençons par écrire un module kernel minimal nommé my-module.c :

// SPDX-License-Identifier: GPL-2.0
//
// (c) 2024 Christophe Blaess
//    https://www.logilin.fr/

#include <linux/module.h>

static int __init my_module_init(void)
{
    pr_info("%s: Hello guys!\n", THIS_MODULE->name);
    return 0;
}

static void __exit my_module_exit(void)
{
    pr_info("%s: That's all folk!\n", THIS_MODULE->name);
}

module_init(my_module_init);
module_exit(my_module_exit);

MODULE_DESCRIPTION("My custom module.");
MODULE_AUTHOR("Christophe Blaess <christophe.blaess@logilin.fr>");
MODULE_LICENSE("GPL v2");

Ce fichier source est accompagné d’un fichier Makefile typique d’un module du kernel :

# SPDX-License-Identifier: GPL-2.0

ifneq ($(KERNELRELEASE),)

    obj-m += my-module.o
else
    KERNEL_DIR ?= /lib/modules/$(shell uname -r)/build
    MODULE_DIR := $(shell pwd)

.PHONY: all modules clean

all: modules

modules:
    $(MAKE) -C $(KERNEL_DIR) M=$(MODULE_DIR)  modules

clean:
    rm -f *.o *.ko *.mod.c .*.o .*.o.d .*.ko .*.mod.c .*.cmd *~ *.ko.unsigned *.mod
    rm -f Module.symvers Module.markers modules.order
    rm -rf .tmp_versions .cache.mk
endif

Enfin, une recette pour Yocto Project permet de compiler ce module. Ce fichier my-module.bb est placé dans un layer personnalisé.

LICENSE = "GPL-2.0-only"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/GPL-2.0-only;md5=801f80980d171dd6425610833a22dbe6"

SRC_URI += "file://my-module.c"
SRC_URI += "file://Makefile"

inherit module

S = "${WORKDIR}"

EXTRA_OEMAKE:append:task-install = " -C ${STAGING_KERNEL_DIR} M=${S}"
EXTRA_OEMAKE += "KERNEL_DIR=${STAGING_KERNEL_DIR}"

Voici l’organisation sommaire de ce layer spécifique :

Commençons par faire une compilation pour une image minimale (core-image-base). J’ai intégré le layer personnalisé dans conf/bblayers.conf et ajouté les deux lignes suivantes dans conf/local.conf :

IMAGE_INSTALL:append = " my-module"
MACHINE = "qemuarm"

J’ai utilisé la version de Yocto Project la plus récente au moment de l’écriture de cet article (branche Scarthgap de Poky, release 5.0.1), mais ce que nous allons voir ici devrait être valable quelque soit la version.

Une fois le build terminé, je démarre ma cible Qemu et je teste le chargement du module :


Poky (Yocto Project Reference Distro) 5.0.1 qemuarm /dev/ttyAMA0

qemuarm login: root

WARNING: Poky is a reference Yocto Project distribution that should be used for
testing and development purposes only. It is recommended that you create your
own distribution for production use.

root@qemuarm:~# cat /proc/sys/kernel/tainted 
0
root@qemuarm:~# modprobe my-module
[   25.224565] my_module: loading out-of-tree module taints kernel.
[   25.227876] my_module: Hello guys!

root@qemuarm:~# cat /proc/sys/kernel/tainted 
4096

root@qemuarm:~# rmmod my-module
[   34.990447] my_module: That's all folk!


root@qemuarm:~# cat /proc/sys/kernel/tainted 
4096

root@qemuarm:~# 

Nous voyons que le module personnalisé est bien installé et accessible via modprobe (il est installé sous /lib/modules/<kernel-version>/updates/). Nous pouvons remarquer que le fait de le charger active un flag dans le pseudo-fichier /proc/sys/kernel/tainted qui persiste après le retrait du module.

Dans ce fichier se trouve des indications de l’historique du kernel depuis le boot, entre autres :

  • Le noyau a-t-il exécuté du code dont les sources ne sont pas disponibles, en chargeant un module sous licence « proprietary » ?
  • A-t-on chargé un module maintenu hors des sources officielles du kernel (c’est le message « loading out-of-tree module taints kernel » que nous apercevons au chargement de notre code) ?
  • A-t-on chargé ou déchargé un module de force (avec l’option « -f » ) ?
  • Le noyau a-t-il déclenché un « Warning » ? un « OOPS » ou un « BUG » ?
  • etc.

L’idée du pseudo-fichier /proc/sys/kernel/tainted est un peu la même que celle des étiquettes « Waranty void if removed » qui recouvrent les vis de démontage des appareils électroniques du commerce : ne pas offrir de support pour l’analyse d’un crash si le kernel a exécuté préalablement du code dont les sources ne sont pas accessibles.

Pour en savoir plus sur les flags tainted, voir : https://docs.kernel.org/admin-guide/tainted-kernels.html

Signature automatique de tous les modules

Je vais modifier la configuration du kernel pour demander à ce que les fichiers modules soient automatiquement signés lors de leur installation sur la cible.

$ bitbake -c menuconfig virtual/kernel
    [*] Enable loadable module support  --->
              [...]
        [*]   Module signature verification

Je ne modifie que l’option CONFIG_MODULE_SIG. Toutefois, après extraction des modifications (avec bitbake -c diffconfig virtual/kernel, j’obtiens un fichier fragment-01.cfg un peu plus complet :

CONFIG_SYSTEM_DATA_VERIFICATION=y
CONFIG_MODULE_SIG_FORMAT=y
CONFIG_MODULE_SIG=y
# CONFIG_MODULE_SIG_FORCE is not set
CONFIG_MODULE_SIG_ALL=y
CONFIG_MODULE_SIG_SHA1=y
# CONFIG_MODULE_SIG_SHA224 is not set
# CONFIG_MODULE_SIG_SHA256 is not set
# CONFIG_MODULE_SIG_SHA384 is not set
# CONFIG_MODULE_SIG_SHA512 is not set
CONFIG_MODULE_SIG_HASH="sha1"
CONFIG_CRYPTO_HASH_INFO=y

CONFIG_ASYMMETRIC_KEY_TYPE=y
CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y
CONFIG_X509_CERTIFICATE_PARSER=y
# CONFIG_PKCS8_PRIVATE_KEY_PARSER is not set
CONFIG_PKCS7_MESSAGE_PARSER=y
# CONFIG_PKCS7_TEST_KEY is not set
# CONFIG_SIGNED_PE_FILE_VERIFICATION is not set
# CONFIG_FIPS_SIGNATURE_SELFTEST is not set
CONFIG_MODULE_SIG_KEY="certs/signing_key.pem"
CONFIG_MODULE_SIG_KEY_TYPE_RSA=y
# CONFIG_MODULE_SIG_KEY_TYPE_ECDSA is not set
CONFIG_SYSTEM_TRUSTED_KEYRING=y
CONFIG_SYSTEM_TRUSTED_KEYS=""
# CONFIG_SYSTEM_EXTRA_CERTIFICATE is not set
# CONFIG_SECONDARY_TRUSTED_KEYRING is not se

J’ajoute ce fragment et j’écris une extension de recette pour le kernel : linux-yocto_%.bbappend

FILESEXTRAPATHS:prepend := "${THISDIR}/files:"

SRC_URI += "file://fragment-01.cfg"

Voici le schéma de l’installation du fragment et du fichier d’extension dans notre layer dédié.

Avant la re-compilation, il faut forcer l’effacement des sources précédentes du kernel et de notre module en utilisant :

$ bitbake -c clean virtual/kernel my-module

Après recompilation, je démarre Qemu. Le chargement du module se déroule exactement comme précédement. Petite nouveauté, nous pouvons voir que le module est bien signé :

root@qemuarm:~# modinfo my-module
filename:       /lib/modules/6.6.23-yocto-standard/updates/my-module.ko
license:        GPL v2
author:         Christophe Blaess <christophe.blaess@logilin.fr>
description:    My custom module.
depends:        
name:           my_module
vermagic:       6.6.23-yocto-standard SMP preempt mod_unload ARMv7 p2v8 
sig_id:         PKCS#7
signer:         Build time autogenerated kernel key
sig_key:        51:AC:FE:8F:23:0A:19:78:1A:BF:14:AA:41:66:89:83:A6:26:07:59
sig_hashalgo:   sha1
signature:      71:ED:24:CD:C5:0A:B8:AE:82:70:62:88:CB:FB:B9:9F:8A:3F:D0:C7:
		AD:6B:EB:24:E5:9F:BA:A4:15:6E:1C:D4:74:AD:7B:E5:25:1A:96:C2:
		8A:EF:A1:EC:C2:70:7D:CB:F5:CB:FC:E4:41:36:39:B0:6B:CF:FA:BA:
		E3:69:52:09:7A:66:6F:A5:34:66:8B:FE:31:20:6A:15:84:7A:2D:6F:
		37:DA:FD:06:AA:01:A4:45:AB:3A:DC:E2:15:87:14:EE:23:03:2C:57:
		3D:55:4F:3B:FB:6F:92:06:3C:24:11:79:48:DE:B0:9F:49:5C:BD:E4:
		24:0E:D6:FE:84:C2:32:03:CB:B7:A6:7A:BC:D5:23:76:B2:4A:57:F4:
		21:A2:2B:81:A9:42:7B:47:5B:E8:09:A0:BC:1D:47:C4:5A:DB:1A:27:
		47:68:43:CF:C1:08:11:66:8E:75:7A:9F:86:38:27:63:98:D1:10:5D:
		8C:17:3D:EE:0A:3C:85:A9:F2:B1:0C:BA:D1:10:E1:D2:13:28:C7:63:
		C3:8E:8C:29:45:5F:45:2C:8D:46:3B:16:D2:A0:50:80:6B:7C:9D:8B:
		47:F4:CE:1F:F3:B1:FD:22:4D:48:FB:12:3B:70:77:FC:76:10:6D:14:
		8E:58:3A:FD:62:E7:3C:C2:23:93:DA:9F:2E:FF:5F:CF:E1:B2:AF:03:
		14:EB:8B:FD:71:86:20:1E:C1:80:DD:16:EE:87:71:A7:84:C8:BE:6E:
		2F:DC:71:AC:7D:FD:C5:F4:C5:A9:4B:88:55:C6:CB:62:F5:4D:DF:B1:
		EC:F1:92:60:4D:05:65:67:E2:2D:91:52:63:8C:68:52:11:23:BC:6F:
		54:E6:BF:7C:AB:BF:16:88:81:D3:E6:30:DE:C0:61:7D:BF:9E:BE:EE:
		C1:6C:8A:62:B0:8F:67:93:79:CD:93:9C:CF:20:4D:85:B3:6E:1F:DE:
		BC:3E:9F:D3:10:C8:99:54:5E:D3:32:49:7B:91:0B:8A:CD:37:79:0E:
		39:89:71:EF:0D:4B:8E:12:CB:57:37:C3:4A:97:7B:30:7E:84:D2:05:
		2D:B1:39:C8:86:3B:4F:F1:2E:34:4E:0B:CA:98:86:3B:7E:F9:26:AD:
		63:3F:51:01:6B:6B:67:C3:FB:DD:0E:CA:DF:BF:A8:2F:CA:B5:57:AF:
		EF:D2:DB:65:FB:91:CD:68:59:AA:1F:2F:10:2D:53:61:BF:E6:47:9F:
		11:E7:22:1E:72:02:1B:6E:BA:3D:4D:78:92:BB:3D:26:29:F3:0D:DC:
		7C:BE:94:32:AC:33:E0:50:15:90:3D:53:9A:E4:62:1C:F1:B1:DE:C0:
		D5:AC:63:5B:86:88:DD:1E:C4:52:C7:7A

Il est d’ailleur possible de voir qu’un module est signé en observant simplement les derniers octets du fichier :

root@qemuarm:~# hexdump -C /lib/modules/6.6.23-yocto-standard/updates/my-module.ko | tail 
000194e0 87 67 4b 5c a3 6c 31 31  ee 91 6c 06 8f 1d e6 44 |.gK\.l11..l....D|
000194f0 51 0b 95 8c 7f 20 e3 eb  6a 8b f7 85 1b d5 9e 7e |Q.... ..j......~|
00019500 10 b0 3c 4a a3 2c f0 5b  de c6 f1 ac e1 56 42 df |..<J.,.[.....VB.|
00019510 a4 00 b8 75 45 e8 1a 6f  cb fd a8 17 8f 8c db 1e |...uE..o........|
00019520 37 5b d5 29 92 aa 7a aa  b0 4f 01 24 0e b9 a2 90 |7[.)..z..O.$....|
00019530 a5 64 e4 1a b1 96 8b 60  c9 52 c6 28 6b 11 52 31 |.d.....`.R.(k.R1|
00019540 66 f1 62 00 00 02 00 00  00 00 00 00 00 01 c3 7e |f.b............~|
00019550 4d 6f 64 75 6c 65 20 73  69 67 6e 61 74 75 72 65 |Module signature|
00019560 20 61 70 70 65 6e 64 65  64 7e 0a                | appended~.|
0001956b

J’avais conservé un exemplaire du fichier my-module.ko précédent, non signé. Lorsque je le charge dans le kernel après un reboot, j’observe :

root@qemuarm:~# insmod my-module.ko
[   71.294608] my_module: loading out-of-tree module taints kernel.
[   71.295015] my_module: module verification failed: signature and/or required key missing - tainting kernel
[   71.298868] my_module: Hello guys!

root@qemuarm:~# cat /proc/sys/kernel/tainted 
12288
root@qemuarm:~# 

Nous voyons que le chargement d’un module non-signé, dans un kernel compilé avec l’option CONFIG_MODULE_SIG est possible, mais nous ajoute un flag supplémentaire (8192) dans le pseudo-fichier /proc/sys/kernel/tainted.

Refus des modules non-signés

Il est également possible d’activer une option supplémentaire lors de la configuration du kernel :

$ bitbake -c menuconfig virtual/kernel
    [*] Enable loadable module support  --->
              [...]
        [*]   Require modules to be validly signed

J’active cette option (CONFIG_MODULE_SIG_FORCE) ce qui me conduit au fichier fragment-02.cfg

CONFIG_SYSTEM_DATA_VERIFICATION=y
CONFIG_MODULE_SIG_FORMAT=y
CONFIG_MODULE_SIG=y
CONFIG_MODULE_SIG_FORCE=y
CONFIG_MODULE_SIG_ALL=y
CONFIG_MODULE_SIG_SHA1=y
# CONFIG_MODULE_SIG_SHA224 is not set
# CONFIG_MODULE_SIG_SHA256 is not set
# CONFIG_MODULE_SIG_SHA384 is not set
# CONFIG_MODULE_SIG_SHA512 is not set
CONFIG_MODULE_SIG_HASH="sha1"
CONFIG_CRYPTO_HASH_INFO=y

CONFIG_ASYMMETRIC_KEY_TYPE=y
CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y
CONFIG_X509_CERTIFICATE_PARSER=y
# CONFIG_PKCS8_PRIVATE_KEY_PARSER is not set
CONFIG_PKCS7_MESSAGE_PARSER=y
# CONFIG_PKCS7_TEST_KEY is not set
# CONFIG_SIGNED_PE_FILE_VERIFICATION is not set
# CONFIG_FIPS_SIGNATURE_SELFTEST is not set
CONFIG_MODULE_SIG_KEY="certs/signing_key.pem"
CONFIG_MODULE_SIG_KEY_TYPE_RSA=y
# CONFIG_MODULE_SIG_KEY_TYPE_ECDSA is not set
CONFIG_SYSTEM_TRUSTED_KEYRING=y
CONFIG_SYSTEM_TRUSTED_KEYS=""
# CONFIG_SYSTEM_EXTRA_CERTIFICATE is not set
# CONFIG_SECONDARY_TRUSTED_KEYRING is not set

Après recompilation et démarrage, le chargement du module signé fonctionne normalement. Toutefois, si j’essaye de charger le premier module compilé en début d’article (non-signé), cela échoue :

root@qemuarm:~# insmod my-module.ko
[   81.964006] Loading of unsigned module is rejected
insmod: ERROR: could not insert module my-module.ko: Key was rejected by service

Nous avons ainsi une configuration où tous les modules (y compris ceux hors sources officielles du noyau) sont signés et où la présence de la signature est obligatoire.

Utilisation d’une clé de signature personnalisée

Pour signer ses modules, le noyau génère une clé nommée signing_key.pem qu’il place dans le répertoire certs/. Il est possible de la remplacer par notre propre clé.

Le fichier signing_key.pem du kernel est formatté de manière un peu particulière :

-----BEGIN PRIVATE KEY-----
MIIJQQIBADANBgkqhkiG9w0BAQEFAASCCSswggknAgEAAoICAQDPw19sLbG4swxZ
[...]
asYcpnPMyq1gneDqTHB4FSpx55xJ
-----END PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIFKDCCAxCgAwIBAgIUDzBsb9UUtq2PVdUBCQooLqGPyyIwDQYJKoZIhvcNAQEL
[...]
1nC1+p60Z+k087MvkqFVKwsBg==
-----END CERTIFICATE-----

La clé privée (utilisée pour signer les modules lors de la compilation) et le certificat (embarqué dans le noyau pour vérifier les modules au chargement sur la cible) se suivent dans le même fichier .pem. Pour réaliser cette mise en forme, on peut appeler openssl en précisant le même fichier de sortie pour la clé et le certificat :

$ openssl req -new -x509 -newkey rsa:2048 -nodes -days 36500 -config x509.genkey -keyout my-key.pem -out my-key.pem

Pour créer le certificat, j’ai fourni un fichier de description x509.genkey en m’inspirant de celui utilisé par le kernel :

[ req ]
default_bits = 4096
distinguished_name = req_distinguished_name
prompt = no
string_mask = utf8only
x509_extensions = myexts

[ req_distinguished_name ]
O = MyCompany
CN = My Company
emailAddress = contact@my-company.com

[ myexts ]
basicConstraints=critical,CA:FALSE
keyUsage=digitalSignature
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid

J’ai seulement modifié les trois lignes de la section req_distinguished_name.

On peut vérifier si la clé et le certificat sont bien présents dans le fichier .pem en les extrayant successivement ainsi :

$ openssl pkey -in my-key.pem  -noout -text
Private-Key: (2048 bit, 2 primes)
modulus:
    00:9b:66:dd:dd:ca:e3:1c:32:0f:93:42:11:07:7b:
    [...]
    52:9b
publicExponent: 65537 (0x10001)
privateExponent:
    0b:8f:53:dd:f4:1b:99:6e:a3:7e:90:e4:dc:cc:c2:
    [...]
    e9
prime1:
    00:ce:b6:87:c7:98:c2:3c:2a:88:16:f5:00:0c:90:
    [...]
    e8:f7:24:85:fa:15:66:38:b9
prime2:
    00:c0:74:5f:c2:3a:69:23:46:d3:7b:31:4c:c8:c8:
    [...]
    ec:60:e2:31:a8:9b:25:d3:f3
exponent1:
    20:9a:b7:c5:ea:b9:50:46:21:1b:05:df:d1:1b:76:
    [...]
    59:8b:00:3a:d6:df:3d:b9
exponent2:
    69:36:f6:27:24:71:d8:54:5f:47:e9:62:f6:1d:d5:
    [...]
    3f:83:49:ec:c8:ad:3c:9b
coefficient:
    55:b5:cd:16:75:99:82:2d:bc:a5:2b:10:24:e2:a9:
    [...]
    2f:fe:41:16:0c:8f:93:76

$ openssl x509 -in my-key.pem  -noout -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            4e:c9:a9:2b:4d:4d:57:ae:72:23:7f:77:bb:11:50:f1:ca:19:89:d3
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: O = MyCompany, CN = My Company, emailAddress = contact@my-company.com
        Validity
            Not Before: Jun 17 12:19:17 2024 GMT
            Not After : May 24 12:19:17 2124 GMT
        Subject: O = MyCompany, CN = My Company, emailAddress = contact@my-company.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:c6:10:93:36:ca:49:88:f5:7d:10:76:e2:46:79:
                    [...]
                    52:9b
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Key Usage: 
                Digital Signature
            X509v3 Subject Key Identifier: 
                2F:87:BE:24:8D:A9:45:35:BD:E0:34:DC:82:2E:30:FE:0E:89:35:A2
    Signature Algorithm: sha256WithRSAEncryption
    Signature Value:
        27:e9:fb:f6:16:34:69:8c:1f:ce:92:d3:8c:7c:f8:26:5f:1a:
        [...]
        b3:3b:30:22

Je place le fichier my-key.pem dans le sous-répertoire files/ de la recette concernant le kernel. Nous obtenons l’organisation suivante :

Stocker la clé privée à cet endroit n’est pas très sûr, il vaudrait mieux générer ce fichier juste avant la compilation (à partir d’une clé stockée de manière plus sécurisée) et l’effacer juste après la compilation. Néanmoins, dans le cadre de cet article, nous nous contenterons de cette configuration.

Je modifie le fichier fragment-02.cfg précédent pour obtenir ainsi fragment-03.cfg :

CONFIG_SYSTEM_DATA_VERIFICATION=y
CONFIG_MODULE_SIG_FORMAT=y
CONFIG_MODULE_SIG=y
CONFIG_MODULE_SIG_FORCE=y
CONFIG_MODULE_SIG_ALL=y
CONFIG_MODULE_SIG_SHA1=y
# CONFIG_MODULE_SIG_SHA224 is not set
# CONFIG_MODULE_SIG_SHA256 is not set
# CONFIG_MODULE_SIG_SHA384 is not set
# CONFIG_MODULE_SIG_SHA512 is not set
CONFIG_MODULE_SIG_HASH="sha1"
CONFIG_CRYPTO_HASH_INFO=y

CONFIG_ASYMMETRIC_KEY_TYPE=y
CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y
CONFIG_X509_CERTIFICATE_PARSER=y
# CONFIG_PKCS8_PRIVATE_KEY_PARSER is not set
CONFIG_PKCS7_MESSAGE_PARSER=y
# CONFIG_PKCS7_TEST_KEY is not set
# CONFIG_SIGNED_PE_FILE_VERIFICATION is not set
# CONFIG_FIPS_SIGNATURE_SELFTEST is not set
CONFIG_MODULE_SIG_KEY="certs/my-key.pem"
CONFIG_MODULE_SIG_KEY_TYPE_RSA=y
# CONFIG_MODULE_SIG_KEY_TYPE_ECDSA is not set
CONFIG_SYSTEM_TRUSTED_KEYRING=y
CONFIG_SYSTEM_TRUSTED_KEYS=""
# CONFIG_SYSTEM_EXTRA_CERTIFICATE is not set
# CONFIG_SECONDARY_TRUSTED_KEYRING is not set

Je vais ajouter au moment de la compilation mon fichier my-key.pem dans le répertoire certs/ des sources du kernel. Pour cela je modifie le fichier linux-yocto_%.bbappend ainsi :

FILESEXTRAPATHS:prepend := "${THISDIR}/files:"

SRC_URI += "file://fragment-03.cfg"
SRC_URI += "file://my-key.pem"

do_compile:prepend() {
    mv "${WORKDIR}/my-key.pem" ${S}/certs/
}

Après une recompilation et démarrage du noyau je peux observer :

root@qemuarm:~# modprobe my-module
[   20.987736] my_module: loading out-of-tree module taints kernel.
[   20.991480] my_module: Hello guys!

root@qemuarm:~# modinfo my-module
filename:       /lib/modules/6.6.23-yocto-standard/updates/my-module.ko
license:        GPL v2
author:         Christophe Blaess <christophe.blaess@logilin.fr>
description:    My custom module.
depends:        
name:           my_module
vermagic:       6.6.23-yocto-standard SMP preempt mod_unload ARMv7 p2v8 
sig_id:         PKCS#7
signer:         My Company
sig_key:        4E:C9:A9:2B:4D:4D:57:AE:72:23:7F:77:BB:11:50:F1:CA:19:89:D3
sig_hashalgo:   sha1
signature:      1D:59:D9:45:EA:C0:57:D9:5F:44:FD:E6:5C:B7:D9:44:59:EC:1E:9E:
		DE:93:2A:DF:6B:E7:1E:10:9A:85:45:36:64:A9:47:76:23:CC:91:4D:
		[...]

On peut observer sur la ligne « signer: » que la clé de signature utilisée est bien celle que nous avons générée. La ligne « sig_key » affiche également le même hachage que la ligne « Serial Number » lors de l’extraction du certificat avec Openssl plus haut.

Nous pouvons voir que notre clé est bien connue du kernel en regardant :

root@qemuarm:~# cat /proc/keys 
0da419c0 I------     1 perm 1f030000     0     0 asymmetri MyCompany: My Company: 2f87be248da94535bde034dc822e30fe0e8935a2: X509.rsa 0e8935a2 []
0fcc6f23 I------     1 perm 1f030000     0     0 keyring   .dns_resolver: empty
232a0295 I--Q---     1 perm 0c030000     0 65534 keyring   .user_reg: 2
[...]

Conclusion

Nous avons obtenu avec cet article de signer automatiquement tous les modules kernel que Yocto Project installera dans notre image, en utilisant une clé personnalisée. Ce n’est qu’un premier pas dans la sécurisation complète d’un système embarqué. Il faudra également que le bootloader s’assure de l’intégrité du noyau, voire du root filesystem complet.

2 Réponses

  1. Bergont dit :

    Merci pour cet article très complet !
    De la part du participant de la formation 🙂

URL de trackback pour cette page