Portál AbcLinuxu, 23. říjen 2017 01:48

Nebojte se SELinuxu – 5 (psaní modulů politiky)

13. 10. 2009 | Michal Svoboda
Články - Nebojte se SELinuxu – 5 (psaní modulů politiky)  

Po krátké exkurzi útrobami SELinuxu se podíváme na praktickou ukázku psaní vlastního modulu politiky. Tato znalost je nezbytná pro údržbu SELinuxového systému. Je to také počítačové vyjádření naší představy o tom, jak má vypadat zabezpečení našeho systému; tuto představu bychom měli mít zcela jasně před sebou pokaždé, když sedneme ke své klávesnici.

Obsah

Příklady politik v UNIXu

link

V klasickém UNIXu byla bezpečnostní politika více méně tvořena devíticí bitů ve stylu rwxr-x--- u souborů plus nějaké čachry se setuid a několika statickými pravidly typu „raw socket smí otevřít pouze root“. Tento systém nedával příliš mnoho možností, jak politiku měnit: Změna práv souborů příkazy chmodchown, případně nastavení seteuid/setegid bitů pomocí stejných příkazů. Typickými příklady UNIXové bezpečnostní politiky jsou:

  1. Soubory utmpwtmp udržují informace o právě přihlášených uživatelích, respektive o historii jejich přihlášení a odhlášení. Přístup k těmto souborům je omezen tak, že práva k těmto souborům jsou rw-rw-r-- pro uživatele root a skupinu wtmp. Pouze několik programů (přihlašovací a emulátory terminálu) mají nastaveno spouštění jako setegid wtmp a tyto programy manipulují se soubory utmp/wtmp předem daným způsobem
  2. DNS server běží pod vlastním uživatelem named a skupinou named, přístup k jeho datům (DNS zónám) je nastaven tak, že zapisovat může pouze administrátor DNS a DNS server pouze číst (rw-r----- dnsadmin:named). Podobně, web server běží pod vlastním uživatelem a skupinou, s podobným nastavením práv k datům. DNS a web server tak navzájem nemohou číst svoje data a DNS a web administrátoři si nemohou navzájem svoje data přepsat

Při tvoření modulu politiky pro SELinux budeme používat podobné uvažování, pouze s pokročilejšími nástroji.

Ukázková politika pro SVN

link

Zadání

link

Máme na serveru SVN repozitář našeho nejnovějšího projektu. Přístup uživatelů očekáváme pomocí protokolu svn+ssh, tj. uživatelé jsou vedeni v systému, přihlašují se pomocí ssh, které pod jejich účtem spustí program svnserve pro práci s repozitářem. Chceme izolovat SVN repozitář a zbytek systému, přičemž spojovacím můstkem bude program svnserve. Požadavky tedy jsou:

  1. Omezení aplikace svnserve tak, aby mohla provádět pouze nezbytně nutné operace – práce s repozitářem a nic víc.
  2. Omezení přístupu k SVN repozitáři z druhé strany: Pouze pomocí svnserve.

Příprava prostředí pro překlad

link

Začneme s velmi jednoduchou kostrou:

[root@localhost svn_policy]# touch svn.te svn.if svn.fc
[root@localhost svn_policy]# cat >>svn.te
policy_module(svn,1.0.0)
^D
[root@localhost svn_policy]# ls
svn.fc  svn.if  svn.te
[root@localhost svn_policy]# make -f /usr/share/selinux/devel/Makefile
Compiling targeted svn module
/usr/bin/checkmodule:  loading policy configuration from tmp/svn.tmp
/usr/bin/checkmodule:  policy configuration loaded
/usr/bin/checkmodule:  writing binary representation (version 10) to tmp/svn.mod
Creating targeted svn.pp policy package
rm tmp/svn.mod.fc tmp/svn.mod
[root@localhost svn_policy]# semodule -i svn.pp
[root@localhost svn_policy]# semodule -l | grep svn
svn     1.0.0

Tato ukázka je z distribuce Fedora 11, bude ale fungovat na spoustě dalších. Direktivou policy_module říkáme kompilátoru politiky, jak se náš modul jmenuje, případně jakou má verzi. Jak bylo řečeno v minulém díle, při překladu jsou naše soubory různě zpracovány programem m4 a výsledek je předhozen kompilátoru politiky (checkmodule). Mezivýsledek mezi m4checkmodule si můžeme prohlédnout ve zmíněném souboru tmp/svn.tmp, výsledek po veškeré kompilaci je pak svn.pp.

Dobrá zpráva je, že se náš modul podařilo přeložit. Špatná zpráva je, že nesplňuje požadavky, takže na oběd ještě nejdeme.

Definice typu (domény) pro aplikaci

link

Podobně, jako bychom v klasickém UNIXu vytvořili dedikovaného uživatele, vytvoříme dedikovaný typ (doménu). Do souboru svn.te napíšeme:

type svn_t;
type svn_exec_t;

application_domain(svn_t, svn_exec_t)

Tím zadefinujeme typy pro běžící aplikaci svnserve a její obraz na disku (spustitelný soubor). Makro application_domain zajistí hned několik věcí, které si zhruba popíšeme níže. Konkrétní obsah můžeme dohledat ve zdrojových souborech referenční politiky nebo nějakým auditovacím nástrojem (sesearch, apol).

Další řádek přijde do souboru svn.fc, kde zadefinujeme kontext pro soubor svnserve:

/usr/bin/svnserve               gen_context(system_u:object_r:svn_exec_t,s0)

Po kompilaci a instalaci modulu tento kontext nastavíme:

[root@localhost svn_policy]# restorecon /usr/bin/svnserve
[root@localhost svn_policy]# ls -lZ /usr/bin/svnserve
-rwxr-xr-x. root root system_u:object_r:svn_exec_t:s0  /usr/bin/svnserve

Je dobré si povšimnout, co všechno za nás makro application_domain zařídilo: Můžeme jako administrátor změnit kontext souboru, jako uživatel nad ním provést ls, spouštět, a dokonce i číst. Pokud bychom pouze zadefinovali typ, nemohli bychom nad ním provést žádnou operaci (pokud nejsme „unconfined“ uživatel, což budeme předpokládat, že nejsme). Mezi těmito dvěma stavy existuje samozřejmě několik mezistupňů, například soubor sshd, mající typ sshd_exec_t, není přístupný uživatelům ani pro prohlížení atributů pomocí ls.

Pravidlo pro přeměnu typu

link

Jak bylo řečeno, program svnserve můžeme nyní spouštět dle libovůle, ovšem pod typem, ve kterém běží náš shell (např. sysadm_t). Pro spuštění pod typem svn_t je potřeba použít pravidlo přeměny typu. Pro tento účel si vyrobíme makro v souboru svn.if:

## <summary>svn policy</summary>
## <desc>
##      <p>
##              subversion policy for selinux
##      </p>
## </desc>
#

########################################
## <summary>
##      Execute a domain transition to run svn.
## </summary>
## <param name="domain">
##      Domain allowed to transition.
## </param>
## <param name="role">
##      Role allowed to transition.
## </param>
#
interface(`svn_domtrans',`
        gen_require(`
                type svn_t, svn_exec_t, $1;
                role $2;
        ')

        domtrans_pattern($1,svn_exec_t,svn_t)
        role $2 types svn_t;
')

A zavoláme jej ze souboru svn.te. V našem příkladu budeme transformovat z běžné uživatelské role user_r. Je ale možné pro tento účel vytvořit vlastní roli.

svn_domtrans(user_t, user_r)

Přeložíme, přihlásíme se jako uživatel s rolí user_r. Zkusíme spustit svnserve:

[a@localhost ~]$ svnserve
[a@localhost ~]$

Program na rozdíl od předchozího případu nevypsal žádnou nápovědu a pokud se podíváme do logu, najdeme několik hlášek od AVC, například:

type=AVC msg=audit(1254851349.524:227): avc:  denied  { read write } for
pid=18512 comm="svnserve" name="tty1" dev=tmpfs ino=474
scontext=user_u:user_r:svn_t:s0
tcontext=user_u:object_r:user_tty_device_t:s0 tclass=chr_file

Což znamená, že se podařilo úspěšně přeměnit typ procesu na svn_t, a tento nový typ nemá dostatečná oprávnění pro zápis na náš terminál. Vlastně v různých verzích politiky (tj. na různých distribucích) narazíme na různá omezení. V Debianu Lenny nemá nově vytvořený typ pro aplikace ani právo na otevření dynamického linkeru (ld.so), tudíž se ani nespustí. Takto SELinux funguje – co není povoleno, je zakázáno, a stěží bychom hledali argument pro tvrzení, že všechny možné aplikace potřebují zapisovat na terminál uživatele.

Přidávání privilegií pro aplikaci

link

Další fáze tedy spočívá v iterativním povolování různých akcí, nejlépe tak, že najdeme vhodné makro v rejstříku a/nebo zdrojových kódech referenční politiky. Začneme s

libs_use_ld_so(svn_t)
libs_use_shared_libs(svn_t)
term_use_all_terms(svn_t)
domain_use_interactive_fds(svn_t)

Zjistíme, že aplikace má problémy se soubory lokalizace, najdeme tedy makro, které povoluje jejich čtení:

miscfiles_read_localization(svn_t)

Dále se aplikaci nedaří zjistit, kdo jsme (uživatelské jméno). Standardní mechanizmus je přes NSS, což vyžaduje čtení /etc/nsswitch.conf, obvykle následované čtením /etc/passwd.

files_read_etc_files(svn_t)

Uvedené makro sice povoluje čtení spousty věcí v /etc navíc, obsah těchto souborů ale nemá bezpečnostní následky. (Například /etc/shadow do působnosti tohoto makra nespadá.) Některé instalace můžou využívat další databáze uživatelů (např. LDAP), pak musíme povolit i příslušné interakce s LDAP serverem. V mém konkrétním případě je před LDAP ještě strčen démon NSCD, takže povoluji interakci s ním:

nscd_socket_use(svn_t)

Aplikace často potřebují dočasné soubory, obvykle v adresáři /tmp. Typické nastavení politiky vytvoří k dané aplikační doméně (u nás svn_t) souborový typ pro dočasné soubory (svn_tmp_t) a pomocí pravidel přeměny typů nařídí používání tohoto privátního typu pro dočasné soubory. Tím se zajistí, že ostatní aplikace nebudou mít k dočasným souborům přístup, i kdyby v rámci klasického UNIXu tento přístup byl povolen (např. naše zákeřné getty běžící pod rootem).

type svn_tmp_t;
files_tmp_file(svn_tmp_t)
manage_dirs_pattern(svn_t, svn_tmp_t, svn_tmp_t)
manage_files_pattern(svn_t, svn_tmp_t, svn_tmp_t)
files_tmp_filetrans(svn_t, svn_tmp_t, { file dir })

Při přímém přístupu přes ssh, kde se nebude alokovat interaktivní terminál, narazíme ještě na problém, že náš proces typu svn_t nebude mít povoleno zapisovat do roury, která vede ze ssh démona na jeho standardní výstup (obdobně pro čtení ze standardního vstupu). V době psaní jsem nenarazil na příhodné makro, ale v zásadě se nic neděje, pokud věci napíšeme bez makra:

require {
	type sshd_t;
        class fifo_file { read write };
}
allow svn_t sshd_t : fifo_file { read write getattr };

Posledním úskalím, které není až tak podstatné, ale bude generovat zbytečné hlášky v logu, je poslání signálu SIGCHLD ssh démonovi, pokud náš proces svnserve skončí.

ssh_sigchld(svn_t)

Po přeložení a nainstalování této politiky bychom měli být schopni bez problémů spustit svnserve z role user_r a vidět nápovědu; a to jak interaktivně, tak přímo přes ssh.

[a@localhost ~]$ svnserve
You must specify exactly one of -d, -i, -t or -X.
Type 'svnserve --help' for usage.
[a@localhost ~]$ ssh a@0 svnserve
a@0's password:
You must specify exactly one of -d, -i, -t or -X.
Type 'svnserve --help' for usage.
[a@localhost ~]$

Tím bychom měli hotové rozchození aplikace v omezené doméně. Tento postup budeme jistě v praxi aplikovat ještě mnohokrát při omezování dalších aplikací. Jak bylo řečeno, jde o podobný princip, jako v klasickém UNIXu založit dedikovaného uživatele pro nějakou aplikaci. Rozdíl je ale v tom, že v UNIXu budou mít procesy spuštěné pod tímto uživatelem automaticky mnohem širší práva, než jaká jsme zadefinovali pro typ svn_t; namátkou přístup k síti nebo možnost spouštění jiných procesů. Dále, pokud bychom přeměnu typu procesu z user_t na svn_t při spuštění svnserve přirovnali k UNIXovému mechanizmu seteuid/setegid, bude mít v UNIXu spuštěná aplikace stále přístup ke všem datům uživatele, který ji spustil; typ procesu svn_t nemá ale bez povolení přístup ani k našemu terminálu, natož pak k datům.

Bezpečnostní kontext pro soubory repozitáře

link

Mluvíme-li o datech, zbývá ještě zadefinovat typ pro soubory repozitáře a přidělit k němu přístup pro svnserve. Do souboru svn.te připíšeme:

type svndata_t;
files_type(svndata_t);
svn_manage_data(svn_t)

Přičemž makro svn_manage_data si zavedeme jednoduše v svn.if takto:

########################################
## <summary>
##      Manage svn data files (repos).
## </summary>
## <param name="domain">
##      Domain allowed access.
## </param>
#
interface(`svn_manage_data',`
        gen_require(`
                type svndata_t, $1;
                class file { manage_file_perms };
                class dir { manage_dir_perms };
        ')

        allow $1 svndata_t : file { manage_file_perms };
        allow $1 svndata_t : dir { manage_dir_perms };
')

A v souboru svn.fc zadefinujeme příslušný kontext pro adresář, kde budou repozitáře:

/data/svn(/.*)?                 gen_context(system_u:object_r:svndata_t,s0)

Po přeložení a instalaci modulu můžeme jako administrátor pomocí svnadmin repozitář vyrobit a nastavit mu kontext.

[root@localhost ~]# mkdir -p /data/svn
[root@localhost ~]# svnadmin create /data/svn/gaussgun
[root@localhost ~]# restorecon -R /data/svn
[root@localhost ~]# ls -lZa /data
drwxr-xr-x. root root root:object_r:default_t:s0       .
drwxr-xr-x. root root system_u:object_r:root_t:s0      ..
drwxr-xr-x. root root system_u:object_r:svndata_t:s0   svn

Typ default_t, který se přiřadil adresáři /data, bude dělat neplechu při pokusu o přístup. Je to „catch-all“ typ, ke kterému nemá mimo administrátora nikdo přístup. Pro adresáře kořenové struktury se více hodí root_t:

[root@localhost ~]# semanage fcontext -a -t root_t /data
[root@localhost ~]# restorecon /data
[root@localhost ~]# ls -laZ /data
drwxr-xr-x. root root system_u:object_r:root_t:s0      .
drwxr-xr-x. root root system_u:object_r:root_t:s0      ..
drwxr-xr-x. root root system_u:object_r:svndata_t:s0   svn

Nyní už můžeme jako uživatel zkusit první checkout:

[a@localhost ~]$ svn co svn+ssh://a@localhost/data/svn/gaussgun
a@localhost's password:
a@localhost's password:
Checked out revision 0.

(Dvojitý prompt pro heslo je vlastnost SVN.)

Můžeme si také ověřit, že konvenční cestou nemáme k repozitáři přístup:

[a@localhost ~]$ ls -lZ /data
ls: cannot access /data/svn: Permission denied
?--------- ? ?                                  svn
[a@localhost ~]$ ls /data/svn/gaussgun
ls: cannot access /data/svn/gaussgun: Permission denied

Vše tedy zatím funguje, jak má. Pokud ale zkusíme commit, narazíme na problém.

[a@localhost gaussgun]$ echo 'specifikace gauss gun' > spec.txt
[a@localhost gaussgun]$ svn add spec.txt
A         spec.txt
[a@localhost gaussgun]$ svn commit -m 'prvni nastrel specifikace'
a@localhost's password:
Adding         spec.txt
Transmitting file data .svn: Commit failed (details follow):
svn: Can't open file '/data/svn/gaussgun/db/txn-current-lock': Permission denied

Tentokrát není problém v SELinuxu, ale v UNIXových právech. Soubory v repozitáři patří rootovi, a tudíž je uživatel nemůže přepsat. Vyrobíme unixovou skupinu svn, do ní přidáme potřebné uživatele a nastavíme práva např. takto:

[root@localhost svn]# chown -R .svn /data/svn/gaussgun
[root@localhost svn]# chmod -R o-rwx /data/svn/gaussgun
[root@localhost svn]# chmod -R g+wX /data/svn/gaussgun/db
[root@localhost svn]# ls -laZ /data/svn/gaussgun/
drwxr-x---. root svn  system_u:object_r:svndata_t:s0   .
drwxr-xr-x. root root system_u:object_r:svndata_t:s0   ..
drwxr-x---. root svn  system_u:object_r:svndata_t:s0   conf
drwxrws---. root svn  system_u:object_r:svndata_t:s0   db
-r--r-----. root svn  system_u:object_r:svndata_t:s0   format
drwxr-x---. root svn  system_u:object_r:svndata_t:s0   hooks
drwxr-x---. root svn  system_u:object_r:svndata_t:s0   locks
-rw-r-----. root svn  system_u:object_r:svndata_t:s0   README.txt

Nyní bude vše fungovat, jak má. SVN umí pracovat s UNIX právy, takže nové revize budou mít správná práva, a dědičnost typů se postará o to, aby nové soubory měly typ svndata_t.

[a@localhost gaussgun]$ svn commit -m 'prvni nastrel specifikace'
a@localhost's password:
Adding         spec.txt
Transmitting file data .
Committed revision 1.

Shrnutí

link

Podařilo se nám vytvořit netriviální modul politiky, který definuje pravidla pro aplikaci svnserve a data v SVN repozitářích pod /data/svn. Zdrojové kódy modulu mají asi 100 řádků včetně dokumentace.

Podobným principem lze omezovat libovolnou aplikaci, pouze ve fázi iterativního přidávání práv se budou různé aplikace lišit. Tato fáze se dá obecně použít i při opravě existující (ale nefungující) politiky. Obecný postup je:

  1. Z různých indicií, nejčastěji hlášky v logu, případně pomocí strace zjistit konkrétní příčinu problému.
  2. Z této konkrétní příčiny vygenerovat konkrétní pravidlo, například pomocí audit2allow.
  3. Je-li to potřeba, z konkrétního pravidla a znalosti systému odvodit obecnější formu pravidla. Chceme, aby pravidlo pokrylo co nejpřesněji daný typ přístupu, tj. povolit co nejméně, ale tak, aby to pokrylo všechny praktické nároky.
  4. Obvykle je rozumná forma pravidla k dispozici ve formě makra nějakého modulu referenční politiky.
  5. Pokud se nám nedaří najít odpovídající obecné pravidlo, je někdy potřeba rozdělit problém na podproblémy, tj. zavést nové typy pro soubory nebo procesy, případně přeorganizovat současné označení souborů.

Náš modul politiky splňuje naše původní požadavky. Tyto požadavky ale vyšly z nějakých předpokladů a potřeb. Je vhodné si čas od času ověřit, zda tyto stále odpovídají realitě. Pro různé jiné situace nemusí být naše politika dostatečná. Možné náměty na vylepšení by mohly být:

  1. Duplikace provedeného postupu na program svnadmin, aby správci SVN repozitářů nemuseli být správci systému.
  2. Vytvoření politiky, která umožňuje přístup pouze pro čtení.
  3. Separace různých projektů (repozitářů) a selektivní přidělování přístupu uživatelům.
  4. Vytvoření dedikované role, která dovoluje spuštění pouze svnserve a nedovoluje obecný přístup do shellu.
CVUT logo

Příště

link

Úspěšné vytvoření vlastního modulu politiky v rozsahu, jako je náš modul pro SVN, lze pokládat za ukončení „základního výcviku“ práce se SELinuxem. Měli bychom být schopni nainstalovat, nakonfigurovat a spravovat SELinuxový systém, chápat jeho vnitřní funkce a případně jej tu a tam doladit. Zbývá snad pouze dodat, že polévka Gazpacho se jí studená. V příštím díle se vydáme dále, konkrétně se podíváme na koncepty MCS a MLS.

Poděkování

link

Článek vznikl za podpory ČVUT FEL, Katedra kybernetiky, kde jsou k dispozici, mimo jiné, studijní programy Otevřená informatikaKybernetika a robotika.

Seriál SELinux – nebojte se (dílů: 6)

První díl: Nebojte se SELinuxu – 1 (úvod, první spuštění), poslední díl: Nebojte se SELinuxu – 6 (MLS a MCS).
Předchozí díl: Nebojte se SELinuxu – 4 (reference policy)
Následující díl: Nebojte se SELinuxu – 6 (MLS a MCS)

Související články

Novější jádra a starší SELinux politiky
Smack: zjednodušená kontrola přístupu
SMACK a Jediný Správný Bezpečnostní Modul
Začíná diskuze o AppArmor
Linuxové bezpečnostní ne-moduly a AppArmor
LCA: Diskuze o bezpečnosti
Bezpečnostní modul Snet a API LSM
TOMOYO Linux a bezpečnost založená na pathname
Budoucnost API pro linuxové bezpečnostní moduly (LSM)

Další články z této rubriky

V sobotu se uskuteční konference CryptoFest
Pozor na androidové aplikace
Silent Circle představil bezpečný smartphone Blackphone 2
Android je bezpečnější, řada hrozeb však stále přetrvává
Avast varuje před nebezpečnými aplikacemi v Google Play

Diskuse k tomuto článku

ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.