Zadanie 2: BPF redactor¶
Data ogłoszenia: 26.03.2024
Materiały dodatkowe¶
Plik .config:
CONFIG_Z2
Testy:
z2-tst-public.tar.gz
Wprowadzenie¶
Redagowanie to proces polegający na dokonywaniu zmian w tekście przed jego publikacją. W systemach informatycznych, termin “redakcja danych” pojawia się w odniesieniu do usuwaniu z plików wrażyliwych danych takich jak hasła.
Współczesne wersje jądra mają podsystem BPF pozwalający na ładowanie prostych fragmentów kodu i wykonywanie ich w jądrze. Możemy wykorzystać ten podsystem do wykonania redkacji pliku w trakcie jego odczytu, aby zapobiec odczytaniu wrażliwych danych przez użytkownika.
Zadanie¶
Zaimplementować system do redagowania plików podczas ich odczytu. W tym celu należy dodać nowe wywołania systemowe, a także możliwość wpięcia do kernela mechanizmu sprawdzania — pary programów BPF, które będą wykonane przy syscallach (odpowiednio): a) open, openat, openat2, creat w celu ustalenia czy przy następnych operacjach odczytu wykonanych na pliku zostanie wykonana redakcja. b) w przypadku plików dla których redakcja ma zostać wykonana: read, pread, readv, preadv, preadv2 dokona redakcji oraz zapamięta liczbę zmodyfikowanych bajtów.
Tak zainstalowany program BPF powinien mieć następujące możliwości:
dostęp (tylko do odczytu) do struktury readctor_ctx opisanej dalej
dostęp (do odczytu i zapisu) do pamięci w której znajdują sie odczytywane dane
dostęp (do odczytu) do informacji o pliku z którego czytamy
Liczba znaków zmienionych podczas redakcji każdego pliku powinna być zapamiętana. Do zarządzania redakcją należy zaimplementować następujące wywołania systemowe:
int count_redactions(int fd)
- zwraca liczbę zmienionych znaków podczas redakcji danego pliku
int reset_redactions(int fd)
- czyści licznik zapamiętanych zmian dla danego pliku
Ustalenia techniczne¶
Należy dodać nowy typ programów BPF: BPF_PROG_TYPE_REDACTOR
(mający numer o 1 większy od BPF_PROG_TYPE_NETFILTER
).
Należy dodać nowy typ podpięcia programów BPF: BPF_REDACTOR
(mający numer o 1 większy od BPF_NETKIT_PEER
).
Dane przekazywane w kontekście wywołania BPF mają format:
struct redactor_ctx {
union {
struct {
loff_t offset;
size_t size;
};
struct {
u64 flags;
umode_t mode;
kuid_t uid;
kgid_t gid;
};
};
};
Należy dodać dwa punkty podpięcia:
int bpf_redactor_decide(struct redactor_ctx *)
- jeśli zwróci dodatnią liczbę, to plik zostanie poddany redakcji
int bpf_redactor_redact(struct redactor_ctx *)
- dokona redakcji danych odczytywanych z pliku oraz zwróci liczbę wyredaktowanych; w przypadku błędu, wykonywanego syscalla należy przerwać zwracając -EINVAL.
Nowododany typ BPF powinien móc podpiąć się do nich (i tylko do nich) poprzez BPF_RAW_TRACEPOINT_OPEN
.
Nie trzeba wspierać w zadaniu funkcji mmap, ani vmsplice, splice, tee, sendfile.
Należy dodać nowe funkcje dostępne dla nowego typu programów (i tylko dla nich), która umożliwią redakcję przez program BPF.
int bpf_copy_to_buffer (void *ctx, unsigned long offset, void *ptr, unsigned long size)
int bpf_copy_from_buffer (void *ctx, unsigned long offset, void *ptr, unsigned long size)
Nowy typ programu powinien mieć dostęp do następujących funkcji (i żadnych innych):
bazowy wspólny zbiór funkcji
powyższe funkcje
istniejące funkcje get_current_uid_gid i get_current_pid_tgid
Kompilacja rozwiązania¶
Uruchomienie rozwiązania wymaga prawidłowo skonfigurowanego jądra, z ustawieniami takimi jak CONFIG_DEBUG_INFO_BTF=y
które celowo nie zostały włączone w konfiguracji zzałączonej przy trzecim laboratorium, ponieważ owe opcje wydłużają kompilację oraz zwiększają wymagania na zasoby.
Z uwagi na to, może być wymagane powiększenie obrazu używanego na laboratoriach o kilka lub kilkanaście gigabajtów.
Aby przetestować rozwiązanie najłatwiej wykorzystać plik konfiguracyjny załączony w sekcji z materiałami.
Skompilowanie testów wymaga pliku vmlinux.h, który nie został dołączony do testów. Można go wygenerować np. przy pomocy programu bpftool. Natomiast żadne dodatkowe zmiany w załączonej bibliotece libbpf nie są już wymagane.
Skompilowanie rozwiązania i testów w maszynie wirtualnej wymaga też instalację pakietów dwarves oraz clang.
Wskazówki¶
Podczas testowania rozwiazązania może się okazać, że wprowadzony przez studenta błąd uniemożliwia bootowanie maszyny wirtualnej. W takiej sytuacji polecam użyć parametrów -kernel, -hda oraz -append “root=/dev/sda3” wraz z wcześniej skopiowanymi działającymi plikami vmlinuz i initrd (https://qemu-project.gitlab.io/qemu/system/linuxboot.html). Jeśli nie przygotowaliście sobie wcześniej plików vmlinuz i initrd na taką ewentualność, to ewentualnie można je wykopiować z obrazu bazowago używanego na laboratorium.
W celu kompilacji jądra przy użyciu wielu rdzeni należy nie tylko pamiętać o opcji -j do polecenia make, ale również zapenić, że maszyna wirtualna ma możliwość wykorzystania wielu rdzeni (np. przez opcję -smp).
Forma rozwiązania¶
Jako rozwiązanie należy wysłać paczkę zawierającą patch na jądro w wersji 6.7.6 wygenerowany przez git format-patch`
oraz krótki opis rozwiazania.