Práva hlouběji

SUID/SGID/sticky, umask, ACL, capabilities.

Základní rwx pro vlastníka / skupinu / ostatní znáš z minulé kapitoly. Jenže reálný systém potřebuje víc: jak může obyčejný uživatel změnit svoje heslo, když do souboru s hesly nesmí zapisovat? Jak nechat celý tým psát do jedné složky, aby se soubory neztrácely? Jak dát jednomu konkrétnímu kolegovi přístup, když on není ani vlastník, ani ve skupině? A jak dovolit programu obsadit port 80, aniž bys mu dal moc nad celým systémem? Tohle všechno řeší speciální bity, umask, ACL a capabilities — vrstvy, které klasické rwx doplňují tam, kde samo nestačí.


Pár pojmů na úvod

  • SUID (Set User ID) = bit na spustitelném souboru, který říká: „spusť mě s právy vlastníka souboru, ne toho, kdo mě spouští". Proto může passwd zapsat do systémového souboru s hesly.
  • SGID (Set Group ID) = obdoba pro skupinu. Na souboru běží program s právy skupiny souboru; na složce nová práva nedědíš — dědíš skupinu složky (skvělé pro sdílené adresáře).
  • Sticky bit = bit na složce, který zamkne mazání: smazat soubor smí jen jeho vlastník (nebo root), i když do složky můžou psát všichni. Klasika je /tmp.
  • umask = maska, která z „plných" práv odečítá bity a tím určuje výchozí práva nově vytvořených souborů a složek. Proto ti nový soubor vznikne jako 644 a ne 666.
  • ACL (Access Control List) = jemnější seznam práv nad rámec vlastník/skupina/ostatní — umí dát rwx konkrétnímu uživateli nebo konkrétní skupině, aniž bys sahal na vlastnictví.
  • Capabilities = rozdrobení všemocného roota na malé pravomoci (např. „smíš obsadit nízký port"). Program dostane jen ten střípek, který potřebuje, místo plné SUID-root moci.

Speciální bity: SUID, SGID, sticky

Vedle běžných devíti bitů (rwx × 3) má každý soubor ještě tři speciální bity. Mění se stejným chmod, jen dostávají čtvrtou (nejlevější) číslici v osmičkovém zápisu, nebo se přidávají symbolicky.

BitSymbolickyOsmičkověKde dává smyslCo dělá
SUIDu+s4000spustitelný souborběží s právy vlastníka souboru
SGIDg+s2000soubor / složkasoubor: běží s právy skupiny; složka: dědí skupinu
sticky+t1000složkasmazat smí jen vlastník souboru

SUID — proč vůbec existuje

Vezmi klasiku: každý uživatel si smí změnit svoje heslo příkazem passwd. Jenže hesla se ukládají do /etc/shadow, kam smí zapisovat jen root. Jak to tedy funguje?

ls -l /usr/bin/passwd
# -rwsr-xr-x. 1 root root 33544 ... /usr/bin/passwd
#    ^
#    tady je 's' místo 'x' → SUID

To malé s v místě, kde bys čekal x u vlastníka, znamená SUID. Vlastníkem souboru je root, takže když passwd spustí kdokoli, program po dobu běhu získá práva roota a smí do /etc/shadow zapsat. Bez SUID by si běžný uživatel heslo nikdy nezměnil.

chmod u+s program      # zapne SUID
chmod 4755 program     # totéž osmičkově (4 = SUID, 755 = rwxr-xr-x)

💡 V ls -l poznáš SUID podle s v pozici x vlastníka. Když je tam velké S, znamená to, že SUID je nastavený, ale vlastníkovi chybí x — bit je „nastražený", jenže vlastník soubor spustit nemůže, takže SUID je fakticky bezzubý (typicky chyba).

SGID na souboru vs na složce

Na souboru funguje SGID jako SUID, jen pro skupinu: program běží s právy skupiny souboru. Zajímavější a mnohem užitečnější je ale SGID na složce:

mkdir /srv/tym
chgrp vyvoj /srv/tym
chmod 2775 /srv/tym       # 2 = SGID, 775 = rwxrwxr-x

Bez SGID by každý soubor, který si někdo v /srv/tym vytvoří, dostal jako skupinu jeho vlastní skupinu. Kolega ze skupiny vyvoj by pak k němu nemusel mít přístup. Se SGID na složce každý nový soubor zdědí skupinu vyvoj — celý tým na sebe navzájem vidí. To je základní stavební kámen sdílených adresářů.

ls -ld /srv/tym
# drwxrwsr-x. 2 root vyvoj 4096 ... /srv/tym
#        ^
#        's' ve skupinové části = SGID

Sticky bit — kdo smaže cizí soubor

Problém sdílené složky, kam smí psát všichni (rwxrwxrwx): kdokoli tam smí i smazat cizí soubor, protože mazání je právo na složku, ne na soubor. Sticky bit to opraví:

ls -ld /tmp
# drwxrwxrwt. 20 root root ... /tmp
#           ^
#           't' na konci = sticky bit

Se sticky bitem smí soubor smazat nebo přejmenovat jen jeho vlastník (a root). Proto do /tmp zapisují všichni programy, ale nikdo tam nemůže mazat cizí dočasné soubory.

chmod +t /sdilena/slozka
chmod 1777 /sdilena/slozka   # 1 = sticky, 777 = rwxrwxrwx

💡 Stejná logika jako u SUID: malé t = sticky je nastavený a složka jde procházet; velké T = sticky nastavený, ale chybí x pro ostatní (nastražený, ale prakticky nefunkční).


umask — odkud se berou výchozí práva

Když vytvoříš nový soubor (touch, >, editor), nikdo mu práva ručně nenastaví — a přesto nevznikne s 777. O to se stará umask: maska, která z „maximálních" práv vypne (odmaskuje) některé bity.

Výchozí maxima jsou:

  • soubory 666 (rw-rw-rw-) — nové soubory nikdy nedostanou x automaticky, to by bylo nebezpečné,
  • složky 777 (rwxrwxrwx) — u složek x (procházení) dává smysl.

Od maxim se bity umasky vypnou (technicky je to bitová operace práva AND NOT umask, ne aritmetické odčítání). Nejběžnější hodnota je 022:

umask          # vypíše aktuální masku, typicky 0022

# Soubor:  666 bez bitů 022 = 644  → rw-r--r--
# Složka:  777 bez bitů 022 = 755  → rwxr-xr-x

⚠️ Model „odčítání" (666 − 022) vyjde správně jen pro masky ze sudých číslic. Pro liché to selže: umask 133 nedá „533", ale reálně 644 — protože bit x v základu 666 u souborů není, takže se nemá co „odečíst". Ber to jako vypínání bitů, ne matematiku.

Číslice 022 čti jako „vlastníkovi neber nic (0), skupině a ostatním ber právo zápisu (2)". Proto nový soubor můžeš číst i psát ty, ostatní ho jen čtou. Kdybys chtěl přísnější nastavení:

umask 077      # soubory 600, složky 700 → vidí jen vlastník
umask 002      # soubory 664, složky 775 → skupina smí i zapisovat (sdílená práce)

🚩 umask nepřidává práva, jen odebírá. Nikdy z ní nevznikne x u souboru, který by ho jinak neměl. Pozor taky na to, že jde o masku, ne o cílová práva: umask 022 neznamená „práva 022", ale „odeber bity 022". To lidi pravidelně mate.

Trvalé nastavení dáš do ~/.bashrc (pro sebe) nebo do /etc/profile / /etc/login.defs (systémově).


ACL — když tři kategorie nestačí

Klasický model má jen tři místa: vlastník, jedna skupina, ostatní. Co když chceš, aby ke složce měl přístup vlastník, skupina vyvoj a navíc jeden konkrétní člověk anna — ale nikdo jiný? V základním modelu bys musel zakládat novou skupinu a přehazovat členství. ACL to řeší přímo:

getfacl soubor.txt              # vypíše ACL (i klasická práva)

setfacl -m u:anna:rw soubor.txt # přidej uživateli anna právo rw
setfacl -m g:vyvoj:rx slozka    # přidej skupině vyvoj právo rx
setfacl -x u:anna soubor.txt    # odeber záznam pro annu
setfacl -b soubor.txt           # smaž všechna ACL, zpět k čistým rwx

Že soubor má nějaké ACL navíc, poznáš v ls -l podle + za právy:

ls -l soubor.txt
# -rw-rw-r--+ 1 pepa vyvoj 12 ... soubor.txt
#           ^
#           '+' = na souboru jsou ACL

Pro sdílené složky se hodí výchozí ACL (default, přepínač -d) — nastaví, jaká práva zdědí soubory nově vzniklé uvnitř složky:

setfacl -d -m g:vyvoj:rwx /srv/projekt   # vše nové uvnitř zdědí rwx pro skupinu vyvoj

💡 ACL je nadstavba, ne náhrada. Klasické rwx pořád platí; ACL jen přidává další záznamy. Používej je střídmě — pár cílených záznamů je fajn, deset překrývajících se pravidel je noční můra na audit.


Capabilities — rozdrobený root

Historicky se to řešilo brutálně: potřebuje program něco, co umí jen root? Dej mu SUID root a je z něj po dobu běhu všemocný. Jenže když v takovém programu najde útočník chybu, získá celého roota — protože program má úplně všechny pravomoci, i ty, které vůbec nepotřeboval.

Capabilities rozdělují roota na desítky malých pravomocí, které se přidělují samostatně. Klasický příklad: obsadit port pod 1024 smí normálně jen root. Místo SUID root stačí jedna capability:

getcap /usr/bin/ping
# /usr/bin/ping cap_net_raw=ep       ← smí posílat raw pakety, nic víc
# (na řadě moderních distr už ping capability nemá a jede přes ICMP sockety — ověř; koncept ale platí)

# Nech webový server naslouchat na portu 80 bez roota:
sudo setcap 'cap_net_bind_service=+ep' /usr/local/bin/muj-server
getcap /usr/local/bin/muj-server

Program dostane jen CAP_NET_BIND_SERVICE (smí na nízký port) a nic dalšího. Když ho někdo zneužije, nedostane celého roota — jen tu jednu drobnou schopnost. To je zásadně bezpečnější než SUID root.

Pár užitečných capabilities:

CapabilityCo umožňuje
CAP_NET_BIND_SERVICEobsadit port < 1024 (třeba 80/443)
CAP_NET_RAWposílat raw/ICMP pakety (ping)
CAP_CHOWNměnit vlastníka souborů
CAP_SYS_TIMEnastavovat systémový čas

💡 Pravidlo: potřebuješ jednu schopnost roota → dej capability, ne SUID root. SUID root je kanón na vrabce a každá chyba v tom programu je pak eskalace na celého roota.


Jak najít nebezpečné soubory

SUID/SGID binárky jsou oblíbený cíl útočníků — pokud jich máš v systému moc nebo nějakou vlastní, je to riziko. Najdeš je podle bitů:

find / -perm -4000 -type f 2>/dev/null    # všechny SUID soubory
find / -perm -2000 -type f 2>/dev/null    # všechny SGID soubory
find / -perm -6000 -type f 2>/dev/null    # SUID nebo SGID

find / -perm -0002 -type f 2>/dev/null    # soubory zapisovatelné kýmkoli (world-writable)

-perm -4000 znamená „má nastavený aspoň bit 4000" (SUID). 2>/dev/null zahodí spoustu hlášek o složkách, kam nemáš přístup. Seznam SUID binárek si projdi: systémové (passwd, sudo, mount) tam patří; cokoli vlastního nebo neznámého je varovný signál.


Failure modes — časté omyly

  • SUID na skript nebo vlastní binárku. Přímá cesta k eskalaci práv: útočník podstrčí vstup nebo zneužije chybu a spustí kód jako vlastník (často root). Jádro proto SUID na skriptech naštěstí ignoruje — ale u vlastních binárek platí. Nedávej SUID ničemu, co jsi pořádně neprověřil.
  • chmod 777 jako univerzální lék. „Nejde to, tak dám 777" znamená „ať to smí číst, psát i spouštět úplně každý". Zdánlivě to problém vyřeší, ve skutečnosti otevře díru. Skoro vždy stačí správný vlastník, skupina a 640/750 — nebo cílené ACL.
  • Moc široká ACL. setfacl -m u:kdokoli:rwx na kdejaký soubor je stejný hřích jako 777, jen hůř vidět (schová se za +). ACL dávej úzce a pravidelně kontroluj getfacl.
  • SUID root místo capability. Dát programu plného SUID roota, když potřebuje jen obsadit port, je zbytečně velká plocha útoku. Použij setcap a dej mu jen tu jednu schopnost.
  • Nepochopení umask. Lidi čtou umask 022 jako „práva budou 022". Je to maska, která se odečítá: z 666 → 644, z 777 → 755. Kdo to plete, diví se, proč mu soubory vznikají „jinak, než čekal".

🛠️ Cvičení

  1. Rozklíčuj ls -l. Podívej se na ls -l /usr/bin/passwd, ls -ld /tmp a nějakou vlastní složku. U každého urči, jestli má SUID, SGID nebo sticky bit, a podle čeho jsi to poznal.
  2. Postav sdílenou složku. Vytvoř složku /srv/tym, přiřaď jí nějakou skupinu a nastav ji tak, aby všechny nově vzniklé soubory uvnitř zdědily tuhle skupinu a nikdo nemohl mazat cizí soubory. Jaké dva speciální bity potřebuješ?
  3. Ověř si umask. Zjisti svou aktuální umask, pak vytvoř nový soubor a novou složku a zkontroluj jejich práva přes ls -l. Sedí to na vzorec 666−maska (soubor) a 777−maska (složka)?
  4. Dej práva jednomu člověku. Máš soubor, ke kterému má mít rw navíc jeden konkrétní uživatel, ale nechceš měnit vlastníka ani skupinu. Jak na to a jak si to pak ověříš?
  5. Nahraď SUID capability. Chceš, aby tvůj vlastní server naslouchal na portu 80 bez roota. Jakou capability mu dáš a čím ji nastavíš? Proč je to lepší než SUID root?
Náčrt řešení — rozbal, až si cvičení zkusíš sám
  1. Čtení bitůpasswd-rwsr-xr-xs v pozici x vlastníka = SUID (běží jako root). /tmpdrwxrwxrwtt na konci = sticky (smaže jen vlastník). Vlastní složka typicky drwxr-xr-x → žádný speciální bit. Pozor: velké S/T znamená, že bit je nastavený, ale chybí odpovídající x.
  2. Sdílená složkamkdir /srv/tym, chgrp vyvoj /srv/tym, chmod 2775 /srv/tym (SGID = dědění skupiny) a chmod +t /srv/tym (sticky = mazání jen vlastníkem). Dohromady chmod 3775. Potřebuješ tedy SGID (dědění skupiny) a sticky (ochrana před mazáním cizích souborů).
  3. umaskumask vypíše masku (typicky 0022). Po touch soubor uvidíš 644 (666−022) a po mkdir slozka 755 (777−022). Sedí to na vzorec. Pozor: x u souboru nikdy nevznikne automaticky, i kdyby maska byla 000 → soubor stejně 666, ne 777.
  4. Právo pro jednohosetfacl -m u:anna:rw soubor.txt přidá ACL záznam jen pro annu, vlastnictví ani skupina se nemění. Ověříš getfacl soubor.txt (uvidíš řádek user:anna:rw-) a v ls -l bude za právy +. Odebrat jde setfacl -x u:anna soubor.txt.
  5. Capability místo SUIDsudo setcap 'cap_net_bind_service=+ep' /cesta/k/serveru, ověříš getcap. Program pak smí obsadit port 80, ale nic víc. Lepší než SUID root proto, že při chybě v programu útočník nezíská celého roota, jen tu jednu drobnou schopnost (obsadit nízký port).

🧠 Otázky & odpovědi

K čemu je SUID bit a proč ho má passwd?

SUID říká: spusť tenhle program s právy vlastníka souboru, ne toho, kdo ho spouští. passwd vlastní root a má SUID, takže když ho spustí libovolný uživatel, program po dobu běhu získá práva roota a smí zapsat nové heslo do /etc/shadow (kam běžný uživatel nemá přístup). Bez SUID by si nikdo kromě roota heslo nezměnil. V ls -l poznáš SUID podle s v pozici x vlastníka.

Jaký je rozdíl mezi SGID na souboru a na složce?

Na souboru je SGID jako SUID, jen pro skupinu — program běží s právy skupiny souboru. Na složce dělá něco jiného a mnohem užitečnějšího: každý nově vytvořený soubor uvnitř zdědí skupinu složky místo výchozí skupiny toho, kdo ho vytvořil. Díky tomu funguje sdílený adresář týmu — všichni na sebe navzájem vidí. Nastavíš chmod g+s slozka nebo chmod 2775 slozka.

Co dělá sticky bit a kde ho poznám?

Sticky bit na složce zamkne mazání: soubor uvnitř smí smazat nebo přejmenovat jen jeho vlastník (a root), i když do složky smí zapisovat kdokoli. Bez něj by v rwxrwxrwx složce mohl každý mazat cizí soubory. Klasický příklad je /tmp — píšou tam všechny programy, ale nikdo nesmaže cizí dočasné soubory. V ls -l ho poznáš podle t na konci práv (drwxrwxrwt). Nastavíš chmod +t nebo chmod 1777.

Jak umask určuje výchozí práva nových souborů?

umask je maska, která se odečítá od maximálních práv. Maxima jsou 666 pro soubory a 777 pro složky. Při umask 022 vznikne soubor jako 644 (666−022) a složka jako 755 (777−022). Pozor na dvě věci: umask práva jen odebírá, nikdy nepřidává, a x u souboru z ní nikdy nevznikne automaticky. A hlavně — umask 022 neznamená práva 022, ale odečti bity 022. To je nejčastější zdroj zmatku.

Proč je capability lepší než SUID root?

SUID root dá programu po dobu běhu všechny pravomoci roota, i ty, které vůbec nepotřebuje. Když v něm útočník najde chybu, získá celého roota. Capability rozdrobí roota na malé kousky a dá programu jen ten jeden, který potřebuje — třeba CAP_NET_BIND_SERVICE (obsadit port pod 1024). Při zneužití útočník dostane jen tu drobnou schopnost, ne celý systém. Nastavíš přes setcap, ověříš přes getcap.