Désolé pour la publication tardive. Calendrier de l'Avent Linux 2019 Ceci est l'article du 6ème jour. Aujourd'hui, je vais essayer le mécanisme de verrouillage du noyau fourni avec le noyau Linux 5.4.
Sous Linux (ou OS UNIX), en séparant l'autorité entre l'utilisateur général et l'utilisateur root, il est moins probable que la stabilité du système soit altérée en raison d'une utilisation imprudente par l'utilisateur général. .. Cependant, si l'utilisateur root effectue par inadvertance une opération ou si l'autorité racine est volée à un utilisateur malveillant, cette méthode de séparation d'autorité ne peut pas la gérer.
Le mécanisme de verrouillage du noyau est une fonction qui "restreint les opérations impliquant des modifications du système même s'il s'agit d'un utilisateur root", et si le noyau a un verrouillage du noyau défini, même s'il s'agit d'un utilisateur root, / dev L'accès à / mem
, / dev / kmen
et CPU MSR (registre spécifique au modèle) est bloqué, et en plus, il n'est pas possible de changer le noyau (= ajouter des fonctions par le module noyau). De plus, cette fonction impose de fortes restrictions sur le fonctionnement du système et on craint qu'elle ne fonctionne pas si elle est appliquée à un système existant tel quel, elle est donc désactivée par défaut.
Maintenant que vous avez un aperçu des fonctionnalités, essayons la fonction de verrouillage du noyau. Tout d'abord, construisez le noyau. Cette fois, je vais essayer de construire dans l'environnement de CentOS-7.
# cat /etc/redhat-release
CentOS Linux release 7.5.1804 (Core)
J'essaierai le noyau Linux avec la dernière version de la série 5.x (à partir du 08 décembre 2019) 5.4.2
. Suivez les étapes ci-dessous pour télécharger et extraire le code source du noyau. Les procédures de construction suivantes sont exécutées conformément à la Procédure de construction du noyau Linux précédemment écrite.
$ curl -O https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.4.2.tar.xz
$ cd /usr/src
$ sudo bash
# tar Jxf /home/centos/work/linux_build/linux-5.4.2.tar.xz
Maintenant que le code source du noyau a été développé, jetons un coup d'œil à la configuration liée à la fonction de verrouillage.
La configuration commençant par LOCK_DOWN_KERNEL_FORCE_
semble être le paramètre lié à la fonction de verrouillage.
# find . -type f | grep Kconfig | xargs grep LOCK_DOWN
./security/lockdown/Kconfig: default LOCK_DOWN_KERNEL_FORCE_NONE
./security/lockdown/Kconfig:config LOCK_DOWN_KERNEL_FORCE_NONE
./security/lockdown/Kconfig:config LOCK_DOWN_KERNEL_FORCE_INTEGRITY
./security/lockdown/Kconfig:config LOCK_DOWN_KERNEL_FORCE_CONFIDENTIALITY
En regardant LOCK_DOWN_KERNEL_FORCE_INTEGRITY
, il semble qu'il fonctionne en mode" intégrité "(par défaut avec le verrouillage du noyau activé) et invalide les modifications apportées au noyau lors de l'exécution.
config LOCK_DOWN_KERNEL_FORCE_INTEGRITY
bool "Integrity"
help
The kernel runs in integrity mode by default. Features that allow
the kernel to be modified at runtime are disabled.
Cela signifie que si vous activez ce paramètre, il se comportera comme une erreur lors du chargement du module du noyau. Construisons le noyau et essayons-le.
Suivez les étapes ci-dessous pour définir la configuration du noyau Linux.
$ cd linux-5.4.2
$ make defconfig
$ make menuconfig
L'emplacement de réglage de LOCK_DOWN_KERNEL_FORCE_INTEGRITY
semble se trouver à l'emplacement suivant.
-> Security options
-> Basic module for enforcing kernel lockdown
-> Kernel default lockdown mode
La différence avec make defconfig
(configuration par défaut du noyau Linux) est la suivante.
(Dans l'environnement de CentOS-7 ou version ultérieure, le système de fichiers est XFS, il est donc nécessaire de définir XFS pour qu'il soit inclus dans le noyau.)
# diff -ur .config.ORIG .config | grep -v '^ '
--- .config.ORIG 2019-12-07 16:45:20.264339000 +0900
+++ .config 2019-12-07 23:17:48.150270000 +0900
@@ -725,13 +725,22 @@
+CONFIG_MODULE_SIG_FORMAT=y
-# CONFIG_MODULE_SIG is not set
+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"
@@ -3890,7 +3899,13 @@
-# CONFIG_XFS_FS is not set
+CONFIG_XFS_FS=y
+# CONFIG_XFS_QUOTA is not set
+# CONFIG_XFS_POSIX_ACL is not set
+# CONFIG_XFS_RT is not set
+# CONFIG_XFS_ONLINE_SCRUB is not set
+# CONFIG_XFS_WARN is not set
+# CONFIG_XFS_DEBUG is not set
@@ -4109,7 +4124,11 @@
-# CONFIG_SECURITY_LOCKDOWN_LSM is not set
+CONFIG_SECURITY_LOCKDOWN_LSM=y
+# CONFIG_SECURITY_LOCKDOWN_LSM_EARLY is not set
+# CONFIG_LOCK_DOWN_KERNEL_FORCE_NONE is not set
+CONFIG_LOCK_DOWN_KERNEL_FORCE_INTEGRITY=y
+# CONFIG_LOCK_DOWN_KERNEL_FORCE_CONFIDENTIALITY is not set
@@ -4332,6 +4351,7 @@
+CONFIG_MODULE_SIG_KEY="certs/signing_key.pem"
@@ -4369,7 +4389,7 @@
-# CONFIG_LIBCRC32C is not set
+CONFIG_LIBCRC32C=y
Vous avez maintenant défini la configuration de noyau requise. Ensuite, lancez make
pour ajouter l'entrée du noyau construit à GRUB.
# make
# make modules_install \
&& cp -f arch/x86_64/boot/bzImage /boot/vmlinuz-5.4.2.x86_64 \
&& mkinitrd --force /boot/initramfs-5.4.2.x86_64.img 5.4.2 \
&& grub2-mkconfig -o /boot/grub2/grub.cfg
Redémarrez, sélectionnez Linux-5.4.2
dans GRUB et démarrez et vous êtes prêt à partir!
# uname -a
Linux linuxadvcal 5.4.2 #7 SMP Sat Dec 7 22:04:12 JST 2019 x86_64 x86_64 x86_64 GNU/Linux
Lorsque j'essaye de charger un module de noyau approprié, le chargement du module échoue (= blocs) même s'il s'agit de l'utilisateur root.
# insmod /usr/lib/modules/5.4.2/build/net/netfilter/xt_nat.ko
insmod: ERROR: could not insert module /usr/lib/modules/5.4.2/build/net/netfilter/xt_nat.ko: Operation not permitted
À ce stade, le message suivant est envoyé à la console.
[ 459.212341] Lockdown: insmod: unsigned module loading is restricted; see man kernel_lockdown.7
En raison du paramétrage de LOCK_DOWN_KERNEL_FORCE_INTEGRITY
, aucun module du noyau n'est chargé.
# lsmod
Module Size Used by
#
En regardant le contenu de dmesg
, le message" Le noyau est verrouillé depuis la configuration du noyau; "est sorti immédiatement après le démarrage de Linux, donc le réglage de LOCK_DOWN_KERNEL_FORCE_INTEGRITY
qui" désactive les changements du noyau après le démarrage " Le comportement est conforme à.
# dmesg -T | head -n1 ; dmesg -T | egrep '(Kernel configuration|kernel_lockdown)'
[Dim 8 décembre 00:38:21 2019] Linux version 5.4.2 (root@linuxadvcal) (gcc version 4.8.5 20150623 (Red Hat 4.8.5-28) (GCC)) #7 SMP Sat Dec 7 22:04:12 JST 2019
[Dim 8 décembre 00:38:20 2019] Kernel is locked down from Kernel configuration; see man kernel_lockdown.7
[Dim 8 décembre 00:38:21 2019] Lockdown: swapper/0: hibernation is restricted; see man kernel_lockdown.7
[Dim 8 décembre 00:38:55 2019] Lockdown: insmod: unsigned module loading is restricted; see man kernel_lockdown.7
J'ai brièvement essayé la fonction de verrouillage du noyau. J'ai en quelque sorte compris le comportement, mais c'est l'humanité (?) Qui me donne envie de regarder le code source. Jetons un coup d'œil au code source.
Tout d'abord, découvrons où l'erreur s'est produite (blocage du chargement du module) lors de l'exécution de ʻinsmod`. Le message suivant envoyé à la console peut être un indice pour l'enquête.
[Dim 8 décembre 00:38:55 2019] Lockdown: insmod: unsigned module loading is restricted; see man kernel_lockdown.7
Le message ci-dessus est émis aux endroits suivants.
security/lockdown/lockdown.c
79 /**
80 * lockdown_is_locked_down - Find out if the kernel is locked down
81 * @what: Tag to use in notice generated if lockdown is in effect
82 */
83 static int lockdown_is_locked_down(enum lockdown_reason what)
84 {
...
89 if (kernel_locked_down >= what) {
90 if (lockdown_reasons[what])
91 pr_notice("Lockdown: %s: %s is restricted; see man kernel_lockdown.7\n",
92 current->comm, lockdown_reasons[what]);
93 return -EPERM;
94 }
La partie "module non signé chargé" du message est la chaîne extraite de lockdown_readsons [what]
, apparemment référencée par la constante LOCKDOWN_MODULE_SIGNATURE
.
security/lockdown/lockdown.c
19 static const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX+1] = {
...
21 [LOCKDOWN_MODULE_SIGNATURE] = "unsigned module loading",
La substance de LOCKDOWN_MODULE_SIGNATURE
est une valeur d'énumération.
include/linux/security.h
104 enum lockdown_reason {
105 LOCKDOWN_NONE,
106 LOCKDOWN_MODULE_SIGNATURE,
...
Il semble que LOCKDOWN_MODULE_SIGNATURE
ne soit référencé qu'à deux endroits, security / lockdown / lockdown.c
(où vous êtes actuellement en train de rechercher la source) et kernel / module.c
.
Dans kernel / module.c
, le traitement est ramifié par la valeur de retour de mod_verify_sig ()
, et la structure est telle quesecurity_locked_down ()
avec LOCKDOWN_MODULE_SIGNATURE
comme argument est appelé (ligne 2882).
kernel/module.c
2839 #ifdef CONFIG_MODULE_SIG
2840 static int module_sig_check(struct load_info *info, int flags)
2841 {
...
2851 if (flags == 0 &&
2852 info->len > markerlen &&
2853 memcmp(mod + info->len - markerlen, MODULE_SIG_STRING, markerlen) == 0) {
2854 /* We truncate the module to discard the signature */
2855 info->len -= markerlen;
2856 err = mod_verify_sig(mod, info);
2857 }
2858
2859 switch (err) {
...
2871 case -ENOPKG:
2872 reason = "Loading of module with unsupported crypto";
2873 goto decide;
...
2876 decide:
2877 if (is_module_sig_enforced()) {
2878 pr_notice("%s is rejected\n", reason);
2879 return -EKEYREJECTED;
2880 }
2881
2882 return security_locked_down(LOCKDOWN_MODULE_SIGNATURE);
2883
2884 /* All other errors are fatal, including nomem, unparseable
2885 * signatures and signature check failures - even if signatures
2886 * aren't required.
2887 */
2888 default:
2889 return err;
2890 }
security_locked_down ()
est défini dans kernel / module.c
, d'où il appelle call_int_hook ()
.
security/security.c
2402 int security_locked_down(enum lockdown_reason what)
2403 {
2404 return call_int_hook(locked_down, 0, what);
2405 }
2406 EXPORT_SYMBOL(security_locked_down);
call_int_hook ()
est défini comme une macro de fonction dans le même fichier C et appelle security_hook_heads.FUNC ()
. Ceci est macro-développé pour que struct security_hook_hands-> lock_down ()
soit appelé.
security/security.c
38 struct security_hook_heads security_hook_heads __lsm_ro_after_init;
...
657 #define call_int_hook(FUNC, IRC, ...) ({ \
658 int RC = IRC; \
659 do { \
660 struct security_hook_list *P; \
661 \
662 hlist_for_each_entry(P, &security_hook_heads.FUNC, list) { \
663 RC = P->hook.FUNC(__VA_ARGS__); \
664 if (RC != 0) \
665 break; \
666 } \
667 } while (0); \
668 RC; \
669 })
Locked_down
est défini comme une variable membre de struct security_hook_hands
, et un pointeur de fonction est défini pour cette variable membre dans security / lockdown / lockdown.c
.
include/linux/lsm_hooks.h
1823 struct security_hook_heads {
...
2062 struct hlist_head locked_down;
2063 } __randomize_layout;
Si la valeur d'index passée dans l'argument what
in lockdown_is_locked_down ()
définie comme un pointeur de fonction peut être appelée tableaulockdown_reasons []
, le" Lockdown: insmod: le chargement du module non signé est restreint; voir man kernel_lockdown .7 "est en cours de sortie.
security/lockdown/lockdown.c
83 static int lockdown_is_locked_down(enum lockdown_reason what)
84 {
85 if (WARN(what >= LOCKDOWN_CONFIDENTIALITY_MAX,
86 "Invalid lockdown reason"))
87 return -EPERM;
88
89 if (kernel_locked_down >= what) {
90 if (lockdown_reasons[what])
91 pr_notice("Lockdown: %s: %s is restricted; see man kernel_lockdown.7\n",
92 current->comm, lockdown_reasons[what]);
93 return -EPERM;
94 }
95
96 return 0;
97 }
98
99 static struct security_hook_list lockdown_hooks[] __lsm_ro_after_init = {
100 LSM_HOOK_INIT(locked_down, lockdown_is_locked_down),
101 };
Dans ce cas, la valeur de l'argument what
is LOCKDOWN_MODULE_SIGNATURE
, qui fait référence au" chargement de module non signé ".
security/lockdown/lockdown.c
19 static const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX+1] = {
20 [LOCKDOWN_NONE] = "none",
21 [LOCKDOWN_MODULE_SIGNATURE] = "unsigned module loading",
...
41 [LOCKDOWN_CONFIDENTIALITY_MAX] = "confidentiality",
42 };
La compilation conditionnelle est effectuée par LOCK_DOWN_KERNEL_FORCE_INTEGRITY
défini comme configuration du noyau dans lockdown_lsm_init ()
, et le niveau de verrouillage est réglé sur LOCKDOWN_CONFIDENTIALITY_MAX
. C'est le comportement dans le tableau ci-dessus lockdown_reasons []
que les éléments avec une valeur supérieure à LOCKDOWN_MODULE_SIGNATURE
et une valeur plus petite (ou index de tableau) sont des facteurs valides pour le verrouillage du noyau.
security/lockdown/lockdown.c
51 static int lock_kernel_down(const char *where, enum lockdown_reason level)
52 {
53 if (kernel_locked_down >= level)
54 return -EPERM;
55
56 kernel_locked_down = level;
57 pr_notice("Kernel is locked down from %s; see man kernel_lockdown.7\n",
58 where);
59 return 0;
60 }
...
103 static int __init lockdown_lsm_init(void)
104 {
105 #if defined(CONFIG_LOCK_DOWN_KERNEL_FORCE_INTEGRITY)
106 lock_kernel_down("Kernel configuration", LOCKDOWN_INTEGRITY_MAX);
107 #elif defined(CONFIG_LOCK_DOWN_KERNEL_FORCE_CONFIDENTIALITY)
108 lock_kernel_down("Kernel configuration", LOCKDOWN_CONFIDENTIALITY_MAX);
109 #endif
110 security_add_hooks(lockdown_hooks, ARRAY_SIZE(lockdown_hooks),
111 "lockdown");
112 return 0;
113 }
Bien que ce soit une explication approximative, j'ai pu voir le déroulement du comportement lorsque LOCK_DOWN_KERNEL_FORCE_INTEGRITY
est spécifié dans la configuration du noyau à partir du code source.
J'ai introduit le mécanisme de verrouillage du noyau de Linux. Le comportement du module du noyau autour de la sécurité n'est pas complet en lui-même, et les valeurs et fonctions définies par le module sont utilisées à partir d'autres sources, il semble donc un peu difficile de lire le code source. Il semble bon de le combiner avec pr_notice ()
et de comprendre le comportement tout en le déplaçant.
Recommended Posts