Obecnie pracuję nad projektem, w którym mam proces nadrzędny, który konfiguruje parę gniazd, rozwidla, a następnie używa tej pary gniazd do komunikacji. Dziecko, jeśli chce otworzyć plik (lub dowolny inny zasób oparty na deskryptorze pliku), powinno zawsze udać się do elementu nadrzędnego, zażądać zasobu i pobrać fd
go za pośrednictwem pary gniazd. Ponadto chcę uniemożliwić dziecku samodzielne otwieranie dowolnego deskryptora pliku.
Natknąłem się na to, setrlimit
co skutecznie uniemożliwia dziecku otwieranie nowych deskryptorów plików, ale wydaje się również, że unieważnia wszelkie deskryptory plików wysyłane przez początkowe połączenie gniazda. Czy jest jakaś metoda w systemie Linux, która pozwala jednemu procesowi otworzyć dowolny plik, wysłać deskryptor pliku do innych procesów i pozwolić im z nich korzystać, nie pozwalając tym innym procesom na samodzielne otwieranie dowolnego deskryptora pliku?
W moim przypadku może to być dowolna konfiguracja jądra, wywołanie systemowe itp., O ile można ją zastosować po rozwidleniu i tak długo, jak dotyczy wszystkich deskryptorów plików (nie tylko plików, ale także gniazd, par gniazd itp.).
źródło
Odpowiedzi:
To, co masz tutaj, to dokładnie przypadek użycia seccomp .
Za pomocą seccomp możesz filtrować połączenia systemowe na różne sposoby. Co chcesz robić w tej sytuacji jest to, zaraz po
fork()
, aby zainstalowaćseccomp
filtr, który wykluczy stosowanieopen(2)
,openat(2)
,socket(2)
(i więcej). Aby to zrobić, możesz wykonać następujące czynności:seccomp_init(3)
domyślnego zachowaniaSCMP_ACT_ALLOW
.seccomp_rule_add(3)
dla każdego połączenia systemowego, którego chcesz odmówić. Możesz użyćSCMP_ACT_KILL
do zabicia procesu, jeśli próba syscall zostanie podjęta,SCMP_ACT_ERRNO(val)
aby syscall nie zwrócił określonejerrno
wartości lub innejaction
wartości zdefiniowanej na stronie podręcznika.seccomp_load(3)
aby był skuteczny.Zanim przejdziesz dalej, ZAUWAŻ, że takie podejście do czarnej listy jest ogólnie słabsze niż podejście do białej listy. Pozwala na każde wywołanie systemowe, które nie jest wyraźnie niedozwolone i może spowodować obejście filtra . Jeśli uważasz, że proces potomny, który chcesz wykonać, może złośliwie próbować ominąć filtr lub jeśli już wiesz, które wywołania systemowe będą potrzebne dzieciom, lepiej zastosować podejście z białej listy i powinieneś zrobić coś przeciwnego: utwórz filtr z domyślną akcją
SCMP_ACT_KILL
i zezwól na wymagane wywołania systemowe za pomocąSCMP_ACT_ALLOW
. Pod względem kodu różnica jest minimalna (biała lista jest prawdopodobnie dłuższa, ale kroki są takie same).Oto przykład powyższego (robię to
exit(-1)
w przypadku błędu tylko dla uproszczenia):Teraz w swoim programie możesz wywołać powyższą funkcję, aby zastosować filtr seccomp zaraz po
fork()
:Kilka ważnych uwag na temat seccomp:
fork(2)
lubclone(2)
jest dozwolony, wszelkie procesy potomne będą ograniczone przez ten sam filtr.execve(2)
jest to dozwolone, istniejący filtr zostanie zachowany podczas połączenia zexecve(2)
.prctl(2)
zezwolenie na syscall jest dozwolone, proces może zastosować kolejne filtry.źródło
socket(2)
to też może stworzyćfd
, więc to też powinno zostać zablokowane. Jeśli znasz proces potomny, podejście do białej listy jest lepsze.creat()
,dup()
, idup2()
są wszystkie wywołania systemowe Linux że deskryptorów powrotu. Na czarnej liście jest wiele sposobów ...