Mitől véd az O_NOFOLLOW?

Amit értek: megnyitnám a "/fontos/outputfile"-t írásra, de aggódom, hogy a haxor esetleg odatett egy /fontos/outputfile nevű szimlinket, ami a /etc/passwd-re mutat. Mondjuk neki nincs írási joga a /etc/passwd fájlra, de nekem van, és ezzel rávesz, hogy tegyek kárt a fájlban.

Vagy lehet az egy érvénytelen symlink, amit ha megnyitok írásra, akkor ott hozok létre egy fájlt, ahol nem akartam.. és mondjuk elhasználom a lemezhelyet, vagy információ jut ki a gépből (ha mondjuk az egy NFS-en felmountolt lemezhely), stb.

Nyilván lehetne a megnyitás előtt tájékozódni (lstat(2)), de a gonosz haxor a lstat és az open között is változtathat a helyzeten. Az open után meg már késő a fstat(2), ha mondjuk egy jó kis O_RDWR|O_CREAT|O_TRUNC volt a nyitási mód.

Ha viszont az open-nél O_NOFOLLOW is meg van adva, akkor az open nem sikerül (ELOOP); ettől gondolkodóba esem. (Mondjuk törlöm a fájlt, és újra próbálkozom; ha a haxor elég lelkes, akkor a két folyamat akár végtelen ciklusban is pöröghet vidáman.)

Ha nincs O_NOFOLLOW, akkor egy rövidebb úton keresztül mehetek az erdőn keresztül:


fd= open ("/fontos/hosszuveletlenstring", O_RDWR_HANDLE);
rename ("/fontos/hosszuveletlenstring", "/fontos/outputfile");

A rename jó barátunk: ha az 'újnév' létező szimlink, akkor azt nem követi, hanem törli (éppúgy, mintha közönséges file lenne).

Na most jön az a rész, hogy egy smbd, ftpd, Apache szerű szerverprogram a kliens kérésére megnyit egy fájlt, de közben szeretnénk korlátozni, hogy mihez férhet hozzá. Tipikusan egy adott könyvtárra és attól lefelé (mondjuk pl. ~user vagy ~user/public_html).

(Adott esetben be is chroot-olhatnánk magunkat, de mondjuk most nem azt akarjuk.)

Namost ilyenkor is előfordulhat, hogy a kliens a /gyoker/dir1/dir2/passwd fájlt akarja elérni, és a szerver lstat-tal, O_NOFOLLOW-val vagy egyéb technikával megállapítja, hogy az egy szimlink a /etc/passwd-re, akkor jól megtagadja a hozzáférést (nyilván írást és olvasást is, ha már).

Ekkor jön az újabb haxor, aki a /gyoker/dir1/dir2-t teszi egy szimlinkké a /etc-re. Na ezen lstat és O_NOFOLLOW nem segít, hiszen a dir2/passwd nem szimlink.

Ezért a gondos szerver szépen végigsétál a /gyoker/dir1/dir2/ sorozaton, és minden lépésnél ellenőrzi, hogy nem szimlinkelték-e ki a /gyoker alól. Ha minden oké, akkor kicsit szusszan, erőt gyűjt, és aztán jól megnyitja a fájlt. Na ezt a dolgot hívják úgy, hogy versenyhelyzet: a derék haxor az ellenőrzés az open(2) között kell elkövesse az elkövetnivaló gonoszságot.
(szerk: ezen a ponton emlékezzünk meg a realpath(3) függvényről és a realpath(1) programról.)

Megjegyzés: attól még, hogy én benne vagyok (chdir(2)) egy könyvtárban, attól azt még másvalaki áthelyezheti, velem együtt:


$ pwd -P
/home/projects/tmp/y3/els
$ pwd -P
/home/projects/els
# ja hogy valaki más áthelyezte a könyvtáramat: mv ~/tmp/y3/els ~

Szerk: Azon gondolkozom, hogy egy olyan getrealpath_and_subdircheck_and_open függvényt lehetne tákolni, ami a "megszépített" path előállítása mellett ellenőrzi is, hogy nem szédültünk-e ki a könyvtárból, és ha nem, akkor meg is nyitja a fájlt. Közben az egyes könyvtárakat O_PATH|O_NOFOLLOW-val openat-oljuk, fstat-tal és readlinkat-tel vizsgáljuk.

Hozzászólások