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