Pisanie polityki SELinux

Projekt Android Open Source Project (AOSP) zapewnia solidną podstawę zasad dla aplikacji i usług wspólnych dla wszystkich urządzeń z systemem Android. Współpracownicy AOSP regularnie udoskonalają tę politykę. Oczekuje się, że podstawowe zasady będą stanowić około 90–95% ostatecznych zasad obowiązujących na urządzeniu, a dostosowania specyficzne dla urządzenia będą stanowić pozostałe 5–10%. W tym artykule skupiono się na dostosowaniach specyficznych dla urządzenia, sposobie pisania zasad dla poszczególnych urządzeń oraz niektórych pułapkach, których należy unikać.

Przywołanie urządzenia

Pisząc zasady specyficzne dla urządzenia, wykonaj następujące kroki.

Uruchom w trybie zezwalającym

Gdy urządzenie znajduje się w trybie zezwalającym , odmowy są rejestrowane, ale nie są wymuszane. Tryb zezwalający jest ważny z dwóch powodów:

  • Tryb zezwalający zapewnia, że ​​uruchomienie zasad nie opóźnia innych zadań związanych z wczesnym uruchamianiem urządzenia.
  • Wymuszona odmowa może maskować inne odmowy. Na przykład dostęp do pliku zazwyczaj obejmuje przeszukanie katalogu, otwarcie pliku, a następnie odczytanie pliku. W trybie wymuszania wystąpiłaby jedynie odmowa wyszukiwania w katalogu. Tryb zezwalający zapewnia, że ​​wszystkie odmowy będą widoczne.

Najprostszym sposobem przełączenia urządzenia w tryb zezwalający jest użycie wiersza poleceń jądra . Można to dodać do pliku BoardConfig.mk urządzenia: platform/device/<vendor>/<target>/BoardConfig.mk . Po zmodyfikowaniu wiersza poleceń wykonaj make clean , następnie make bootimage i flashuj nowy obraz startowy.

Następnie potwierdź tryb zezwalający za pomocą:

adb shell getenforce

Dwa tygodnie to rozsądna ilość czasu, aby działać w globalnym trybie zezwalającym. Po rozwiązaniu większości odmów wróć do trybu wymuszania i usuwaj pojawiające się błędy. Domeny nadal generujące odmowy lub usługi będące w fazie intensywnego rozwoju można tymczasowo przełączyć w tryb zezwalający, ale jak najszybciej przenieś je z powrotem do trybu wymuszania.

Egzekwuj wcześnie

W trybie wymuszania odmowy są rejestrowane i egzekwowane. Najlepszą praktyką jest jak najwcześniejsze przełączenie urządzenia w tryb wymuszania. Oczekiwanie na utworzenie i egzekwowanie zasad dotyczących konkretnego urządzenia często skutkuje błędami produktu i złym doświadczeniem użytkownika. Rozpocznij wystarczająco wcześnie, aby wziąć udział w testowaniu funkcjonalności i zapewnić pełny zakres testów funkcjonalności w rzeczywistym użytkowaniu. Wczesne rozpoczęcie pracy gwarantuje, że kwestie bezpieczeństwa będą miały wpływ na decyzje projektowe. Z drugiej strony przyznawanie uprawnień wyłącznie na podstawie zaobserwowanych odmów jest podejściem niebezpiecznym. Wykorzystaj ten czas na przeprowadzenie audytu bezpieczeństwa urządzenia i zgłoszenie błędów dotyczących zachowań, które nie powinny być dozwolone.

Usuń lub usuń istniejącą politykę

Istnieje wiele dobrych powodów, dla których warto utworzyć od podstaw zasady dotyczące konkretnego urządzenia na nowym urządzeniu, między innymi:

Rozwiązanie problemu odmów świadczenia usług podstawowych

Odmowy generowane przez usługi podstawowe są zwykle rozwiązywane poprzez etykietowanie plików. Na przykład:

avc: denied { open } for pid=1003 comm=”mediaserver” path="/dev/kgsl-3d0”
dev="tmpfs" scontext=u:r:mediaserver:s0 tcontext=u:object_r:device:s0
tclass=chr_file permissive=1
avc: denied { read write } for pid=1003 name="kgsl-3d0" dev="tmpfs"
scontext=u:r:mediaserver:s0
tcontext=u:object_r:device:s0 tclass=chr_file permissive=1

zostało całkowicie rozwiązane poprzez odpowiednie oznaczenie /dev/kgsl-3d0 . W tym przykładzie tcontext to device . Reprezentuje to domyślny kontekst, w którym wszystko w /dev otrzymuje etykietę „ urządzenie ”, chyba że przypisano bardziej szczegółową etykietę. Samo zaakceptowanie danych wyjściowych z audytu2allow tutaj spowodowałoby niepoprawną i nadmiernie liberalną regułę.

Aby rozwiązać tego rodzaju problem, nadaj plikowi bardziej szczegółową etykietę, która w tym przypadku to gpu_device . Żadne dalsze uprawnienia nie są potrzebne, ponieważ serwer multimediów ma już niezbędne uprawnienia w zasadach podstawowych, aby uzyskać dostęp do urządzenia gpu.

Inne pliki specyficzne dla urządzenia, które powinny być oznaczone typami predefiniowanymi w zasadach podstawowych:

Ogólnie rzecz biorąc, nadawanie uprawnień domyślnym etykietom jest błędne. Wiele z tych uprawnień jest zabronionych przez reguły Neverallow , ale nawet jeśli nie są one wyraźnie zabronione, najlepszą praktyką jest zapewnienie określonej etykiety.

Oznacz nowe usługi i adresuj odmowy

Usługi uruchamiane przez Init muszą działać we własnych domenach SELinux. Poniższy przykład umieszcza usługę „foo” w jej własnej domenie SELinux i przyznaje jej uprawnienia.

Usługa uruchamia się w init. device .rc jako:

service foo /system/bin/foo
    class core
  1. Utwórz nową domenę „foo”

    Utwórz plik device/ manufacturer / device-name /sepolicy/foo.te z następującą zawartością:

    # foo service
    type foo, domain;
    type foo_exec, exec_type, file_type;
    
    init_daemon_domain(foo)
    

    Jest to początkowy szablon domeny foo SELinux, do którego można dodawać reguły oparte na konkretnych operacjach wykonywanych przez ten plik wykonywalny.

  2. Etykieta /system/bin/foo

    Dodaj następujące polecenie do device/ manufacturer / device-name /sepolicy/file_contexts :

    /system/bin/foo   u:object_r:foo_exec:s0
    

    Dzięki temu plik wykonywalny jest odpowiednio oznaczony, dzięki czemu SELinux uruchamia usługę w odpowiedniej domenie.

  3. Zbuduj i flashuj obrazy rozruchowe i systemowe.
  4. Udoskonal reguły SELinux dla domeny.

    Użyj odmów, aby określić wymagane uprawnienia. Narzędzie audyt2allow zapewnia dobre wytyczne, ale używaj go wyłącznie do celów tworzenia polityki. Nie kopiuj tylko wyników.

Przełącz z powrotem do trybu wymuszania

Rozwiązywanie problemów w trybie zezwalającym jest w porządku, ale jak najszybciej przełącz się z powrotem do trybu wymuszania i staraj się w nim pozostać.

Typowe błędy

Oto kilka rozwiązań typowych błędów, które zdarzają się podczas pisania zasad dotyczących konkretnych urządzeń.

Nadużywanie negacji

Następująca przykładowa zasada przypomina zamykanie drzwi wejściowych i pozostawienie otwartych okien:

allow { domain -untrusted_app } scary_debug_device:chr_file rw_file_perms

Intencja jest jasna: dostęp do urządzenia debugującego mogą mieć wszyscy oprócz aplikacji innych firm.

Przepis jest wadliwy pod kilkoma względami. Wyłączenie untrusted_app jest proste do obejścia, ponieważ wszystkie aplikacje mogą opcjonalnie uruchamiać usługi w domenie isolated_app . Podobnie, jeśli do AOSP zostaną dodane nowe domeny dla aplikacji innych firm, będą one również miały dostęp do scary_debug_device . Przepis jest zbyt liberalny. Większość domen nie odniesie korzyści z dostępu do tego narzędzia do debugowania. Reguła powinna zostać napisana tak, aby zezwalała tylko na domeny wymagające dostępu.

Debugowanie funkcji w środowisku produkcyjnym

Funkcje debugowania nie powinny być obecne w kompilacjach produkcyjnych ani nie powinny być obecne w ich zasadach.

Najprostszą alternatywą jest zezwolenie na funkcję debugowania tylko wtedy, gdy SELinux jest wyłączony w kompilacjach eng/userdebug, takich jak adb root i adb shell setenforce 0 .

Inną bezpieczną alternatywą jest zawarcie uprawnień do debugowania w instrukcji userdebug_or_eng .

Eksplozja rozmiarów polityki

Charakterystyka zasad SEAndroid in the Wild opisuje niepokojący trend w zakresie dostosowywania zasad dotyczących urządzeń. Zasady specyficzne dla urządzenia powinny stanowić 5–10% ogólnej polityki działającej na urządzeniu. Dostosowania w zakresie 20%+ prawie na pewno zawierają domeny nadmiernie uprzywilejowane i martwe zasady.

Niepotrzebnie duża polityka:

  • Podwójnie obciąża pamięć, ponieważ polityka znajduje się w ramdysku i jest również ładowana do pamięci jądra.
  • Marnuje miejsce na dysku, wymagając większego obrazu rozruchowego.
  • Wpływa na czas wyszukiwania zasad środowiska wykonawczego.

Poniższy przykład pokazuje dwa urządzenia, w przypadku których zasady specyficzne dla producenta stanowią 50% i 40% zasad obowiązujących na urządzeniu. Przepisanie zasad przyniosło znaczną poprawę bezpieczeństwa bez utraty funkcjonalności, jak pokazano poniżej. (W celach porównawczych dołączono urządzenia AOSP Shamu i Flounder.)

Rysunek 1: Porównanie rozmiaru zasad specyficznych dla urządzenia po audycie bezpieczeństwa.

Rysunek 1 . Porównanie rozmiaru zasad specyficznych dla urządzenia po audycie bezpieczeństwa.

W obu przypadkach polityka została radykalnie zmniejszona zarówno pod względem rozmiaru, jak i liczby uprawnień. Zmniejszenie rozmiaru polityki jest prawie całkowicie spowodowane usunięciem niepotrzebnych uprawnień, z których wiele to prawdopodobnie reguły wygenerowane przez audit2allow , które zostały bezkrytycznie dodane do polityki. Martwe domeny również stanowiły problem w przypadku obu urządzeń.

Przyznanie możliwości dac_override

Odmowa dac_override oznacza, że ​​proces powodujący naruszenie próbuje uzyskać dostęp do pliku z nieprawidłowymi uprawnieniami użytkownika/grupy/świata systemu Unix. Właściwym rozwiązaniem prawie nigdy nie jest przyznanie uprawnienia dac_override . Zamiast tego zmień uprawnienia Unix do pliku lub procesu . Kilka domen, takich jak init , vold i installd naprawdę potrzebuje możliwości nadpisywania uprawnień do plików uniksowych, aby uzyskać dostęp do plików innych procesów. Bardziej szczegółowe wyjaśnienia można znaleźć na blogu Dana Walsha .