Hlavní navigace

Podepisování jaderných modulů

27. 9. 2004
Doba čtení: 4 minuty

Sdílet

Jaderné moduly jsou velice praktické. Umožňují za běhu přidávat či odebírat podporu různého hardwaru, protokolů nebo téměř jakoukoliv jinou funkcionalitu. A právě tyto široké možnosti představují značné bezpečnostní riziko. O jedné metodě zabezpečení modulů si povíme v tomto článku.

Jaderný modul obsahuje kód, který se po zavedení modulu stane součástí kódu jádra. Tento kód je potom vykonáván v režimu jádra (Kernel Mode), a jako takový má neomezený přístup k celému systému. Je to samozřejmě nezbytné, ale zároveň velice nebezpečné. Jako příklad lze uvést „populární“ rootkit Adore obsahující modul, který po nahrání do jádra umožní skrývat procesy, soubory, síťová spojení a vůbec provádět všelijaké další neplechy. Zavádění modulů do systému je samozřejmě omezeno pouze na uživatele root, ale to je leckdy nedostačující. S použitím zmiňovaného rootkitu je útočník, který získal oprávnění roota, schopen svou činnost i přítomnost velmi dobře zamaskovat.

Nejjednodušší obranou je samozřejmě použití monolitického jádra (ani to však není 100% záruka bezpečnosti, zkušený útočník dokáže změnit kód jádra i bez použití modulů). Toto řešení však není nejlepší, protože přicházíme o všechny výhody, které nám moduly poskytují.

Jakýmsi kompromisem je použití systémů, které dokáží jádro zapečetit. Takový mechanismus obsahuje například systém LIDS, který umožní zakázat zavádění dalších modulů do jádra. Systém LIDS (Linux Intrusion Detection System) poskytuje i ochranu souborů, takže je možné ochránit i startovací skripty, které moduly nahrávají. Více o systému LIDS najdete například v tomto článku. Jednodušší variantou je použití patche modulesoff, kterým můžete zapečetit kernel pomocí zápisu řetězce „off“ do /proc/modules. Nevýhodou těchto řešení je fakt, že pokud chceme zavést nový modul, je potřeba počítač restartovat.

V tomto článku popíšu metodu založenou na digitálním podepisování modulů pomocí asymetrické kryptografie. Jádro obsahuje veřejný klíč nebo klíče a neumožní zavést modul, který není podepsán jedním z odpovídajících tajných klíčů. Ani tato metoda zabezpečení ale není absolutně bezpečná, protože se útočníkovi může podařit změnit veřejný klíč v jádře.

Nejprve je potřeba přeložit kernel se záplatou Module Signaturte od Davida Howellse. Tento patch je součástí aktuálních jader v distribuci Fedora nebo je k dispozici na stránce Rustyho Russella. Patch je k dispozici pouze pro jádra řady 2.6. Při konfiguraci jádra je potřeba zapnout tyto volby:

CRYPTO_SIGNATURE
CRYPTO_SIGNATURE_DSA
CRYPTO_MPILIB

Při konfiguraci kernelu je také možné zvolit, zda se má podpis vyžadovat. Na testování doporučuji nejprve povolit zavádění nepodepsaných modulů, usnadníte si tím práci :) Patch používá funkce a formát klíčů z projektu GnuPG, a je tedy možné použít všech jeho funkcí na správu klíčů. Pokud GnuPG nepoužíváte, můžete například následujícím postupem pouze vygenerovat dvojici klíčů:

mkdir /tmp/gpg
gpg --homedir=/tmp/gpg --gen-key
gpg --homedir=/tmp/gpg -o /tmp/key.pub --export
gpg --homedir=/tmp/gpg -o /tmp/key.sec --export-secret-key
rm -rf /tmp/gpg

Tento postup vytvoří v adresáři /tmp/gpg novou klíčenku, v ní potom klíč (GnuPG se vás zeptá na detaily) a oba klíče vyexportuje do adresáře /tmp. Veřejný klíč je potřeba v hexadecimálním zápisu uložit v souboru crypto/signatu­re/key.h. K tomu lze použít například skript scripts/bin2c. Doporučuji si vytvořit ještě jednu dvojici klíčů na testování a odpovídající veřejný klíč do jádra nedávat. Je možné si tak vyzkoušet, jak se bude systém chovat, zkusíme-li mu nahrát modul sice podepsaný, ale neznámým klíčem.

Teď jádro přeložíme a nainstalujeme. Pokud používáme initrd ramdisk, je s jeho vytvořením potřeba počkat, až budeme mít moduly, které používá, podepsané. Moduly jsou uloženy ve formátu ELF (Executable and Linking Format). Soubory v tomto formátu se dělí do sekcí. K jejich vypsání je možno použít například příkaz readelf:

[root@radek ~]# readelf -S loop.ko
There are 24 section headers, starting at offset 0x24fc:

Section Headers:
[Nr] Name       Type     Addr     Off    Size   ES Flg Lk Inf Al
[ 0]            NULL     00000000 000000 000000 00      0   0  0
[ 1] .text      PROGBITS 00000000 000034 001a38 00  AX  0   0  4
[ 2] .rel.text  REL      00000000 0036b0 0004f0 08     22   1  4
[ 3] .init.text PROGBITS 00000000 001a6c 0002a6 00  AX  0   0  1
..... 

Před podepisováním modulů si doporučuju zazálohovat nějaký nepodepsaný modul na testování (mně se osvědčil loop.ko). Při vlastním podepisování modulu se nejprve spočítá hash důležitých sekcí (kódových a datových), a ten se pomocí tajného klíče zašifruje. Výsledek se přidá do souboru jako sekce s označením module_sig. Součástí patche je jednoduchá utilitka scripts/modsig­n/modsign.sh, která to za nás udělá. Pomocí readelf si můžeme ověřit, že sekce byla opravdu přidána:

[root@radek ~]# readelf -S loop.ko.signed
There are 24 section headers, starting at offset 0×24fc:

Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 0000­00 000000 00 0 0 0
[ 1] .text PROGBITS 00000000 0000­34 001a38 00 AX 0 0 4
[ 2] .rel.text REL 00000000 0036­b0 0004f0 08 22 1 4
[ 3] .init.text PROGBITS 00000000 001a6c 0002a6 00 AX 0 0 1
.....
[20] module_sig PROGBITS 00000000 0023­e6 000041 00 WA 0 0 1

Napsal jsem jednoduchý skript, který podepíše všechny moduly v zadaném adresáři.

Nyní již můžeme vytvořit initrd ramdisk, upravit konfiguraci lila nebo grubu a restartovat. Při startu jádra by se mělo objevit něco jako:

CS24_early

ksign: Installing public key data
Loading keyring
- Added public key 5BD84902BD52AD1A
- User ID: nazev.klice

Nyní si můžete zkusit zavést podepsaný a nepodepsaný modul. Také je dobré zkusit zavést modul podepsaný špatným klíčem. Patch je standardně poměrně málo upovídaný, ale není problém si do souboru kernel/module-verify.c připsat požadované hlášky.

Patch na podepisování modulů je rozumným kompromisem mezi běžným modulárním a monolitickým kernelem. Zároveň je jeho instalace poměrně jednoduchá a při rozumné správě klíčů můžeme dosáhnout slušné úrovně zabezpečení. Stále však musíme mít na zřeteli, že každou ochranu lze prolomit. Další informace lze najít ve starším článku na LinuxJournalu.

Byl pro vás článek přínosný?