Zadanie 2: BPF sanitizer

Data ogłoszenia: 29.03.2022

Ostatnia aktualizacja: 5.04.2022

Materiały dodatkowe

Wprowadzenie

Czasem chcielibyśmy zagwarantować, że pewne krytyczne informacje nie zostaną zapisane (np. credentiale nie trafią w postaci jawnej do logów dostępnych dla szerszej grupy użytkowników). Współczesne wersje jądra mają podsystem eBPF pozwalający na ładowanie prostych fragmentów kodu i wykonywanie ich w jądrze. Możemy wykorzystać ten podsystem to filtrowania informacji zapisywanych przez programy.

Zadanie

Dodać możliwość wpięcia do kernela sanityzatora — pary programów eBPF, które będą wykonane przy syscallach (odpowiednio):

  • open, openat, openat2, creat - program eBPF zadecyduje, czy tak otwarty plik będzie podlegał sanityzacji,

  • w przypadku plików polegających sanityzacji: write, pwrite, writev, pwritev, pwritev2 i zadecyduje, czy zapisywane dane mogą być przekazane w niezmienionej postaci. Jeśli będzie to wymagane, program eBPF dokona niezbędnych modyfikacji.

Tak zainstalowany program eBPF powinien mieć następujące możliwości:

  • dostęp (tylko do odczytu) do kontekstu w formie struktury sanitizer_ctx opisanej w załączonym sanitizer.h,

  • dostęp (do odczytu i zapisu) do pamięci w której znajdują sie zapisywane dane,

Ustalenia techniczne

Należy dodać nowy typ programów eBPF: BPF_PROG_TYPE_SANITIZER (mający numer o 1 większy od BPF_PROG_TYPE_SYSCALL).

Należy dodać nowy typ podpięcia programów eBPF: BPF_SANITIZER (mający numer o 1 większy od BPF_PERF_EVENT).

Należy dodać dwa punkty podpięcia:

int bpf_sanitizer_check(struct sanitizer_ctx *)

zwraca 0 jeśli otwarty plik nie podlega sanityzacji, 1 w przeciwnym wypadku,

int bpf_sanitizer_sanitize(struct sanitizer_ctx *)

zwraca 0 jeśli proces sanityzacji przebiegł pomyślnie; w przypadku błędu, wykonywango syscalla należy przerwać zwracając -EINVAL.

Nowodododany typ eBPF powinien móc podpiąć się do nich (i tylko do nich) poprzez wywołanie BPF_RAW_TRACEPOINT_OPEN - należy adekwatnie dostosować ten proces. Można założyć, że dodane punkty podpięcia nie będą wykorzystywane przez inne programy eBPF.

Nie trzeba wspierać w zadaniu funkcji mmap — próba zamapowania pliku który podlega sanityzacji powinna się skończyć błędem. Nie trzeba też wspierać vmsplice, splice, tee, sendfile. W przypadku funkcji operujących na wektorach, każdy element podlega indywidualnej sanityzacji. Wykonywany program eBPF nie powinien modyfikować buforów po stronie użytkownika - należy utworzyć kopię, która będzie dostępna dla programu eBPF.

Należy dodać 2 nowe funkcje dostępne dla nowego typu programów (i tylko dla nich), które umożliwiają dostęp do zapisywanych danych:

int bpf_copy_from_buffer (void *ctx, u64 offset, void *ptr, u32 size)

Pobiera size danych podlegających sanityzacji spod offsetu offset do tablicy ptr w programie eBPF. Do wywołania należy przekazać aktualny kontekst ctx. Zwracana jest liczba nieskopiowanych bajtów.

int bpf_copy_to_buffer (void *ctx, u64 offset, void *ptr, u32 size)

Analogicznie jak wyżej, kopiuje dane z tablicy ptr w eBPF nadpisując sanityzowane dane pod offsetem offset.

Nowy typ programów powinien mieć dostęp do następujących funkcji (i żadnych innych):

  • bazowy wspólny zbiór funkcji,

  • powyższe 2 funkcje,

  • istniejące funkcje get_current_uid_gid, get_current_pid_tgid.

Forma rozwiązania

Jako rozwiązanie należy wysłać paczkę zawierającą:

  • patcha na jądro w wersji 5.16.5, w jednym z następujących formatów:

    • patch wygenerowaniy przez diffa z opcjami -uprN nakładający się przez patch -p1

    • git format-patch

  • krótki opis rozwiązania

Rozwiązania prosimy nadsyłać na adres p.zuk@mimuw.edu.pl z kopią do a.jackowski@mimuw.edu.pl.