Entschuldigung für die späte Veröffentlichung. Linux Adventskalender 2019 Dies ist der Artikel am 6. Tag. Heute werde ich den Kernel-Sperrmechanismus ausprobieren, der mit dem Linux-Kernel [5.4] geliefert wurde (https://lkml.org/lkml/2019/11/24/187).
Unter Linux (oder UNIX-basierten Betriebssystemen) ist es weniger wahrscheinlich, dass durch die Trennung der Berechtigung zwischen dem allgemeinen Benutzer und dem Root-Benutzer die Stabilität des Systems durch unachtsamen Betrieb durch den allgemeinen Benutzer beeinträchtigt wird. .. Wenn der Root-Benutzer jedoch versehentlich einen Vorgang ausführt oder wenn die Root-Berechtigung einem böswilligen Benutzer gestohlen wird, kann diese Berechtigungs-Trennungsmethode dies nicht verarbeiten.
Der Kernel-Sperrmechanismus ist eine Funktion, die "Operationen einschränkt, die Systemänderungen beinhalten, selbst wenn es sich um einen Root-Benutzer handelt", und wenn für den Kernel die Kernel-Sperrung festgelegt ist, selbst wenn es sich um einen Root-Benutzer handelt, / dev Der Zugriff auf / mem
, / dev / kmen
und CPU MSR (modellspezifisches Register) ist gesperrt, und außerdem ist es nicht möglich, den Kernel zu ändern (= Funktionen durch das Kernelmodul hinzufügen). Darüber hinaus unterliegt diese Funktion starken Einschränkungen für den Betrieb des Systems, und es besteht die Sorge, dass sie nicht funktioniert, wenn sie so wie sie ist auf ein vorhandenes System angewendet wird, sodass sie standardmäßig deaktiviert ist.
Nachdem Sie sich einen Überblick über die Funktionen verschafft haben, versuchen wir es mit der Kernel-Sperrfunktion. Erstellen Sie zunächst den Kernel. Dieses Mal werde ich versuchen, in die Umgebung von CentOS-7 zu bauen.
# cat /etc/redhat-release
CentOS Linux release 7.5.1804 (Core)
Ich werde den Linux-Kernel mit der neuesten Version der 5.x-Serie (Stand: 08. Dezember 2019) 5.4.2
testen. Führen Sie die folgenden Schritte aus, um den Kernel-Quellcode herunterzuladen und zu extrahieren. Nachfolgende Erstellungsprozeduren werden gemäß der zuvor geschriebenen Linux-Kernel-Erstellungsprozedur ausgeführt.
$ 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
Nachdem der Kernel-Quellcode erweitert wurde, werfen wir einen Blick auf die Konfiguration für die Sperrfunktion.
Die Konfiguration, die mit LOCK_DOWN_KERNEL_FORCE_
beginnt, scheint die Einstellung zu sein, die sich auf die Sperrfunktion bezieht.
# 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
Wenn man sich "LOCK_DOWN_KERNEL_FORCE_INTEGRITY" ansieht, scheint es, dass es im "Integritäts" -Modus ausgeführt wird (standardmäßig mit aktivierter Kernel-Sperre) und Änderungen am Kernel zur Laufzeit ungültig macht.
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.
Wenn Sie diese Einstellung aktivieren, verhält sie sich beim Laden des Kernelmoduls wie ein Fehler. Lassen Sie uns den Kernel erstellen und ausprobieren.
Führen Sie die folgenden Schritte aus, um die Linux-Kernelkonfiguration festzulegen.
$ cd linux-5.4.2
$ make defconfig
$ make menuconfig
Der Einstellungsort von LOCK_DOWN_KERNEL_FORCE_INTEGRITY
scheint sich an dem folgenden Ort zu befinden.
-> Security options
-> Basic module for enforcing kernel lockdown
-> Kernel default lockdown mode
Der Unterschied zu make defconfig
(Standardkonfiguration des Linux-Kernels) ist wie folgt.
(In der Umgebung von CentOS-7 oder höher ist das Dateisystem XFS, daher muss XFS so eingestellt werden, dass es in den Kernel aufgenommen wird.)
# 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
Sie haben jetzt die erforderliche Kernelkonfiguration festgelegt. Führen Sie dann make
aus, um den Eintrag für den erstellten Kernel zu GRUB hinzuzufügen.
# 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
Starten Sie neu, wählen Sie "Linux-5.4.2" aus GRUB und starten Sie und los geht's!
# 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
Wenn ich versuche, ein geeignetes Kernelmodul zu laden, schlägt das Laden des Moduls fehl (= blockiert), selbst wenn es sich um den Root-Benutzer handelt.
# 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
Zu diesem Zeitpunkt wird die folgende Nachricht an die Konsole ausgegeben.
[ 459.212341] Lockdown: insmod: unsigned module loading is restricted; see man kernel_lockdown.7
Aufgrund der Einstellung von LOCK_DOWN_KERNEL_FORCE_INTEGRITY
wird kein Kernelmodul geladen.
# lsmod
Module Size Used by
#
Wenn Sie sich den Inhalt von dmesg
ansehen, wird die Meldung" Kernel ist von der Kernelkonfiguration gesperrt; "wird unmittelbar nach dem Starten von Linux ausgegeben, daher die Einstellung" LOCK_DOWN_KERNEL_FORCE_INTEGRITY ", die" Kerneländerungen nach dem Booten deaktiviert ". Das Verhalten stimmt mit überein.
# dmesg -T | head -n1 ; dmesg -T | egrep '(Kernel configuration|kernel_lockdown)'
[So 8. Dezember 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
[So 8. Dezember 00:38:20 2019] Kernel is locked down from Kernel configuration; see man kernel_lockdown.7
[So 8. Dezember 00:38:21 2019] Lockdown: swapper/0: hibernation is restricted; see man kernel_lockdown.7
[So 8. Dezember 00:38:55 2019] Lockdown: insmod: unsigned module loading is restricted; see man kernel_lockdown.7
Ich habe kurz die Kernel-Sperrfunktion ausprobiert. Ich habe das Verhalten irgendwie verstanden, aber es ist die Menschlichkeit (?), Die mich dazu bringt, den Quellcode tatsächlich zu betrachten. Werfen wir einen kurzen Blick auf den Quellcode.
Lassen Sie uns zunächst herausfinden, wo der Fehler aufgetreten ist (Blockieren des Modulladens), als "insmod" ausgeführt wurde. Die folgende an die Konsole ausgegebene Nachricht kann ein Hinweis auf die Untersuchung sein.
[So 8. Dezember 00:38:55 2019] Lockdown: insmod: unsigned module loading is restricted; see man kernel_lockdown.7
Die obige Nachricht wird an den folgenden Stellen ausgegeben.
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 }
Der "nicht signierte Modul geladen" Teil der Nachricht ist die Zeichenfolge aus "lockdown_readsons [what]", auf die anscheinend durch die Konstante "LOCKDOWN_MODULE_SIGNATURE" verwiesen wird.
security/lockdown/lockdown.c
19 static const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX+1] = {
...
21 [LOCKDOWN_MODULE_SIGNATURE] = "unsigned module loading",
Die Substanz von LOCKDOWN_MODULE_SIGNATURE
ist ein Aufzählungswert.
include/linux/security.h
104 enum lockdown_reason {
105 LOCKDOWN_NONE,
106 LOCKDOWN_MODULE_SIGNATURE,
...
Es scheint, dass "LOCKDOWN_MODULE_SIGNATURE" nur an zwei Stellen referenziert wird: "security / lockdown / lockdown.c" (wo Sie gerade die Quelle verfolgen) und "kernel / module.c".
In kernel / module.c
wird die Verarbeitung durch den Rückgabewert von mod_verify_sig ()
verzweigt, und die Struktur ist so, dasssecurity_locked_down ()
mit LOCKDOWN_MODULE_SIGNATURE
als Argument aufgerufen wird (Zeile 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 ()
ist in kernel / module.c
definiert, von dem aus call_int_hook ()
aufgerufen wird.
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 ()
ist als Funktionsmakro in derselben C-Datei definiert und ruft security_hook_heads.FUNC ()
auf. Dies wird durch Makros erweitert, so dass "struct security_hook_hands-> Locked_down ()" aufgerufen wird.
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
ist als Mitgliedsvariable von struct security_hook_hands
definiert, und für diese Mitgliedsvariable wird in security / lockdown / lockdown.c
ein Funktionszeiger gesetzt.
include/linux/lsm_hooks.h
1823 struct security_hook_heads {
...
2062 struct hlist_head locked_down;
2063 } __randomize_layout;
Wenn der im Argument what
in lockdown_is_locked_down ()
als Funktionszeiger festgelegte Indexwert als Arraylockdown_reasons []
bezeichnet werden kann, ist das oben erwähnte Laden von" Lockdown: insmod: unsigned module "eingeschränkt; siehe man kernel_lockdown .7 "wird ausgegeben.
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 };
In diesem Fall ist der Wert des Arguments "what" "LOCKDOWN_MODULE_SIGNATURE", das sich auf "vorzeichenloses Laden von Modulen" bezieht.
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 };
Die bedingte Kompilierung wird von LOCK_DOWN_KERNEL_FORCE_INTEGRITY
durchgeführt, das als Kernelkonfiguration in lockdown_lsm_init ()
festgelegt ist, und die Sperrstufe wird auf LOCKDOWN_CONFIDENTIALITY_MAX
festgelegt. Dies ist das Verhalten im obigen Array lockdown_reasons []
, dass Elemente mit einem Wert größer als LOCKDOWN_MODULE_SIGNATURE
und einem kleineren Wert (oder Array-Index) gültige Faktoren für die Kernel-Sperrung sind.
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 }
Obwohl es sich um eine grobe Erklärung handelt, konnte ich den Verhaltensfluss sehen, wenn LOCK_DOWN_KERNEL_FORCE_INTEGRITY
in der Kernelkonfiguration aus dem Quellcode angegeben wurde.
Ich habe den Kernel-Lockdown-Mechanismus von Linux eingeführt. Das Verhalten des Kernelmoduls in Bezug auf die Sicherheit ist an sich nicht vollständig, und die vom Modul festgelegten Werte und Funktionen werden aus anderen Quellen verwendet, sodass das Lesen des Quellcodes etwas schwierig ist. Es scheint gut, es mit "pr_notice ()" zu kombinieren und das Verhalten zu verstehen, während es tatsächlich verschoben wird.
Recommended Posts