next up previous contents
Next: A UNIX SYSTEM Up: Security Previous: A Kerberos illetékesség-vizsgáló

Tanácsok setuid root programok írásához

Ha lehet ne írjunk ilyen programokat, mivel ezek futtatáskor korlátozás nélkül garázdálkodhatnak a rendszerben (ez talán a legkomolyabban legmegfontolandóbb tanács). Ha arra van szükség, hogy bizonyos fájlokhoz (itt nem rendszerfájlokra gondolok, hanem mondjuk egy programmal szállított licensz-fájl vagy valami hasonló) csak a program férjen hozzá, akkor elég mondjuk egy új felhasználót létrehozni, és a programhoz tartozó "kényes" fájlokat ezen új felhasználó tulajdonaként kell kezelni, és amely programnak ehhez hozzá kell férnie, azt el kell látni egy setuid bittel (de ettol még nem setuid root programot kapunk!).

A setuid root programok írásakor törekedjünk arra, hogy minél rövidebb legyen a program azon szakasza, ahol a program kihasználhatja azt, hogy "mindent szabad neki".

Például azokat a szervereket szokták setuid root bittel ellátni, amelyek a felhasználótól megkövetelik, hogy beadja a jelszavát és a login nevét (userid-jét). Ha jó a beadott jelszó, akkor a szerver végrehajtja a setuid() és setgid() UNIX rendszerhívásokat annak a felhasználónak a felhasználói- illetve csoport-azonosítójával, aki bejelentkezett, és a szerver ezután már el is veszti a superuser jogokat - ezután már csak azt szabad neki is, amit a bejelentkezett felhasználó interaktívan is megtehetne a rendszerben.

Ha egy szervert úgy akarunk megírni, hogy azt bárki használhassa anélkül, hogy ténylegesen bejelentkezett volna a rendszerbe (mint például az anonymous FTP szolgáltatás), akkor hozzunk létre egy új felhasználót (az anonymous FTP szolgáltatás esetén ennek a felhasználónak mindig ftp a neve), amelyre a szerver olyankor setuid()-el (és setgid()-el), ha a kliens nem tudott neki más ("jobb") felhasználói azonosítót és jelszót beadni.

A setuid root programok minden fájlhoz hozzáférnek - függetlenül attól, hogy ki indította el oket. A UNIX operációs rendszer lehetoséget nyújt a korábban bemutatott access() rendszerhívással, hogy egy fájlra vonatkozóan eldöntsük, hogy aki a setuid root programot elindította, az a saját jogán olvashatja-e, felülírhatja-e és végrehajthatja-e azt a fájlt. (Ez az út azért járható, mert az access() a valódi felhasználói azonosító alapján vizsgálja a jogokat, nem pedig az effektiv felhasználói azonosító alapján; a setuid bit csak az effektiv felhasználói azonosítót állítja -- a valódi felhasználói azonosító is tárolva van a processz-táblában, amit a getuid() rendszerhívással lekérdezhetünk vagy a setuid() rendszerhívással megváltoztathatunk).

A setuid programok mindig zárják le a megnyitott fájljaikat, mert a gyermek-folyamatként végrehajtott folyamatok a megnyitott fájlokat öröklik, és így sok értékes információhoz juthatnak (mindegy, hogy miért - ennek lehet az oka programhiba is vagy lehet akár egy ravasz rendszerprogramozó is).

A programok által használt temporális fájlokat úgy hozzuk létre, hogy azokra a tulajdonosuknak minden (ésszeru) jogokat meg kell adni, míg másoknak ne legyen semmi joguk a fájlra vonatkozóan (vagyis a 0700 oktális szám gyakran megfelel a creat()-nél).

Mindig ellenorizzük a rendszerhívások visszatérési értékét, és ha szükséges, az errno változó értékét is. Ezzel kapcsolatban ismert a következo security-lyuk a UNIX egy korábbi verziójával kapcsolatban: bárki rendszergazda jogokhoz juthatott egy kis ügyességgel, ugyanis a su parancsban volt egy óriási hiba. Az su rendszerprogram olyan esetben, amikor nem tudja megnyitni a jelszófájlt (neve /etc/passwd), jelszóbekérés nélkül rendszergazda jogokkal illeti meg a végrehajtóját. A UNIX készítoi úgy gondolták, hogy a jelszófájl hiánya olyan komoly probléma -- véletlenül nem is fordulhat elo --, hogy ez a muködés elfogadható. Viszont a su rendszerprogramban a jelszófájlt megnyitó open() rendszerhívás sikertelenségének nem vizsgálták meg az okát vagyis azt, hogy valóban nem létezik-e a jelszófájl, vagy pedig más oka van a fájlmegnyitás sikertelenségének. Ezt a legtöbben úgy játszották ki, hogy megnyitottak annyi fájlt, amennyit csak lehetettgif , és végrehajtották a su parancsot. Ekkor az elindított su parancs örökölte az ot elindító folyamat megnyitott fájldeszkriptorait, de a jelszófájlt már nem tudta megnyitni, mert nem volt szabad fájldeszkriptora: már megnyitott annyi fájlt, amennyi a folyamat számára maximálisan engedélyezve volt. Így tehát az open() rendszerhívás sikertelen volt, annak ellenére, hogy volt egy olvasható jelszófájl, mégis úgy tekintette a rendszer, hogy "nagy baj van", és rendszergazda jogokkal ajándékozta meg az óvatlan végrehajtót ...



next up previous contents
Next: A UNIX SYSTEM Up: Security Previous: A Kerberos illetékesség-vizsgáló



Csizmazia Balazs
Tue Apr 2 00:06:27 MET DST 1996