Jaki jest najbezpieczniejszy sposób programowego zapisu do pliku z uprawnieniami administratora?


35

Ogromna aplikacja musi w tym samym czasie wykonać niewielką liczbę zapisów do pliku wymagającego uprawnień administratora. To nie jest tak naprawdę plik, ale interfejs sprzętowy, który jest udostępniany Linuksowi jako plik.

Aby uniknąć nadawania uprawnień roota całej aplikacji, napisałem skrypt bash, który wykonuje najważniejsze zadania. Na przykład poniższy skrypt włączy wyjście 17 interfejsu sprzętowego jako dane wyjściowe:

echo "17" > /sys/class/gpio/export
echo "out" > /sys/class/gpio/gpio17/direction

Jednakże, ponieważ suidjest wyłączone dla skryptów bash w moim systemie, zastanawiam się, jaki jest najlepszy sposób na osiągnięcie tego.

  1. Skorzystaj z przedstawionego tutaj obejścia

  2. Wywołaj skrypt za pomocą sudoz głównej aplikacji i odpowiednio edytuj listę sudoers, aby uniknąć konieczności podawania hasła podczas wywoływania skryptu. Nie czuję się komfortowo, aby dać przywileje sudo echo.

  3. Po prostu napisz program w C fprintfi ustaw na suid root. Koduj ciągi i nazwy plików i upewnij się, że tylko root może je edytować. Lub przeczytaj ciągi z pliku tekstowego, podobnie upewniając się, że nikt nie może edytować pliku.

  4. Jakieś inne rozwiązanie, które nie przyszło mi do głowy i jest bezpieczniejsze lub prostsze niż te przedstawione powyżej?


10
Dlaczego po prostu nie uruchomisz programu z uprawnieniami roota, nie otworzysz pliku i nie upuścisz uprawnień? Tak właśnie robi każdy serwer WWW dla gniazd. W rzeczywistości nie działasz jako root, ani pomocnik nie jest konieczny.
Damon

Odpowiedzi:


33

Nie musisz dawać sudodostępu do echo. W rzeczywistości jest to bezcelowe, ponieważ np sudo echo foo > bar. Przekierowanie jest wykonywane jako pierwotny użytkownik, a nie jako root.

Wywołaj mały skrypt za pomocą sudo, umożliwiając NOPASSWD:dostęp TYLKO temu skryptowi (i innym podobnym skryptom) przez użytkowników, którzy potrzebują do niego dostępu.

Jest to zawsze najlepszy / najbezpieczniejszy sposób użycia sudo. Wyizoluj niewielką liczbę poleceń wymagających uprawnień roota w osobnych skryptach i zezwól niezaufanemu lub częściowo zaufanemu użytkownikowi na uruchomienie tego skryptu tylko jako root.

sudoSkrypty o niewielkich rozmiarach nie powinny pobierać argumentów (ani danych wejściowych) od użytkownika (tzn. Wszelkie inne wywoływane przez niego programy powinny mieć zakodowane opcje i argumenty) lub powinny bardzo uważnie sprawdzać poprawność argumentów / danych wejściowych, które muszą zaakceptować od użytkownika.

Zachowaj paranoję podczas sprawdzania poprawności - zamiast szukać „znanych złych” rzeczy do wykluczenia, zezwalaj tylko na „znane dobre rzeczy” i rezygnuj z wszelkich niedopasowań, błędów lub czegokolwiek, nawet zdalnie podejrzanego.

Sprawdzanie poprawności powinno nastąpić jak najwcześniej w skrypcie (najlepiej zanim zrobi cokolwiek innego jako root).


I naprawdę powinien wymienić to, kiedy pierwszy raz napisał tę odpowiedź, ale jeśli skrypt jest skryptem powłoki to MUSI prawidłowo zacytować wszystkie zmienne. Zachowaj szczególną ostrożność, aby cytować zmienne zawierające dane wejściowe dostarczone przez użytkownika w jakikolwiek sposób, ale nie zakładaj, że niektóre zmienne są bezpieczne, PODAJ WSZYSTKO .

Który zawiera zmienne środowiskowe potencjalnie sterowane przez użytkownika (na przykład "$PATH", "$HOME", "$USER"itd. I na pewno w tym "$QUERY_STRING"i "HTTP_USER_AGENT"etc w skrypcie CGI). W rzeczywistości, po prostu zacytuj je wszystkie. Jeśli musisz zbudować wiersz poleceń z wieloma argumentami, użyj tablicy do zbudowania listy argumentów i zacytuj ją - "${myarray[@]}".

Czy dość często powtarzałem „zacytuj je wszystkie”? pamiętam. Zrób to.


18
Zapomniałeś wspomnieć, że sam skrypt powinien być własnością roota, z uprawnieniami 500.
Wildcard

13
Przynajmniej usuń uprawnienia do zapisu, na miłość boską. To był mój cel. Reszta po prostu twardnieje.
Wildcard

10
Dobrze napisany program C będzie miał mniejszą powierzchnię ataku niż skrypt powłoki.
user253751

7
@immibis, być może. Ale pisanie i debugowanie zajmie znacznie więcej czasu niż skrypt powłoki. I wymaga posiadania kompilatora C (który jest zabroniony na niektórych serwerach produkcyjnych, aby zmniejszyć ryzyko bezpieczeństwa, utrudniając atakującym kompilowanie exploitów). Ponadto IMO skrypt powłoki napisany przez nowicjusza na poziomie pośrednim sysadmin lub programistę ma mniejsze szanse na wykorzystanie niż program C napisany przez kogoś o podobnych umiejętnościach - zwłaszcza jeśli musi akceptować i sprawdzać poprawność danych dostarczonych przez użytkownika.
cas

3
@JonasWielicki - myślę, że jesteśmy teraz w sferze opinii, a nie faktów. Możesz tworzyć poprawne argumenty dla powłoki lub C (albo perla, pytona lub awk itp.), Które są mniej lub bardziej podatne na błędy, które można wykorzystać. Kiedy tak naprawdę zależy to głównie od umiejętności programisty i dbałości o szczegóły (i zmęczenia, pośpiechu, ostrożności itp.). Faktem jest jednak, że języki niższego poziomu zwykle wymagają napisania większej ilości kodu, aby osiągnąć to, co można zrobić w znacznie mniejszej liczbie wierszy kodu w językach wyższego poziomu ... a każdy LoC to kolejna okazja do błąd do popełnienia.
cas

16

Sprawdź właściciela plików gpio:

ls -l /sys/class/gpio/

Najprawdopodobniej dowiesz się, że są własnością grupy gpio:

-rwxrwx--- 1 root     gpio     4096 Mar  8 10:50 export
...

W takim przypadku możesz po prostu dodać użytkownika do gpiogrupy, aby udzielić dostępu bez sudo:

sudo usermod -aG gpio myusername

Musisz się wylogować i zalogować ponownie, aby zmiana zaczęła obowiązywać.


To nie działa Rzeczywiście, właścicielem grupy wszystkiego w /sys/class/gpio/gpio jest, ale nawet po dodaniu się do tej grupy, wciąż dostaję „pozwolenie odmowy” za każdym razem, gdy próbuję coś tam napisać.
vsz

1
Problem polega na tym, że pliki w /sys/class/gpio/rzeczywistości są po prostu dowiązaniami symbolicznymi do miejsca, w /sys/devices/platform/soc/<some temporary name>/gpioktórym zarówno właściciel, jak i grupa są rootami.
vsz

4
@vsz Próbowałeś chgrp gpio /sys/devices/platform/soc/*/gpio? Być może coś takiego można umieścić w pliku startowym.
jpa

tak, ale to nie jest takie proste. Ponieważ zawsze są generowane w inny sposób, musiałem użyć czegoś takiegochgrp gpio `readlink -f /sys/class/gpio/gpio18`/*
vsz

7

Jednym z rozwiązań tego problemu (używanym zwłaszcza na pulpicie Linux, ale również w innych przypadkach) jest użycie D-Bus do aktywacji małej usługi działającej jako root i polkit w celu autoryzacji. Zasadniczo do tego został zaprojektowany pakiet polkit ; z dokumentacji wprowadzającej :

polkit zapewnia API autoryzacji przeznaczone do użytku przez programy uprzywilejowane („MECHANIZMY”) oferujące obsługę programów nieuprzywilejowanych („KLIENCI”). Zobacz stronę podręcznika programu Polkit, aby uzyskać informacje na temat architektury systemu i dużego obrazu.

Duży, nieuprzywilejowany program zamiast uruchamiać program pomocniczy wysyła żądanie do magistrali. Twój pomocnik może być uruchomiony jako demon uruchomiony przy starcie systemu, a najlepiej być aktywowany w razie potrzeby przez systemd . Następnie ten pomocnik użyłby pakietu polkit do sprawdzenia, czy żądanie pochodzi z autoryzowanego miejsca. (Lub, w tym przypadku, jeśli wydaje się to przesadą, możesz użyć innego zakodowanego na stałe mechanizmu uwierzytelniania / autoryzacji).

Znalazłem dobry podstawowy artykuł na temat komunikacji przez D-Bus i chociaż go nie testowałem, wydaje się, że jest to podstawowy przykład dodawania pakietu polkit do miksu .

W tym podejściu nic nie musi być oznaczone jako setuid.


5

Jednym ze sposobów, aby to zrobić, jest napisanie w C programu setuid-root, który robi tylko to, co jest potrzebne i nic więcej. W twoim przypadku nie musi on wcale patrzeć na dane wejściowe użytkownika.

#include <unistd.h>
#include <string.h>
#include <stdio.h>  // for perror(3)
// #include ...  more stuff for open(2)

static void write_str_to_file(const char*fn, const char*str) {
    int fd = open(fn, O_WRONLY)
    if (-1 == fd) {
        perror("opening device file");  // make this a CPP macro instead of function so you can use string concat to get the filename into the error msg
        exit(1);
    }
    int err = write(fd, str, strlen(str));
    // ... error check
    err = close(fd);
    // ... error check
}

int main(int argc, char**argv) {
    write_string_to_file("/sys/class/gpio/export", "17");
    write_string_to_file("/sys/class/gpio/gpio17/direction", "out");
    return 0;
}

Nie ma możliwości obalenia tego przez zmienne środowiskowe lub cokolwiek innego, ponieważ wystarczy wykonać kilka wywołań systemowych.

Wada: należy sprawdzić wartość zwracaną każdego wywołania systemowego.

Zaleta: sprawdzanie błędów jest naprawdę łatwe: jeśli w ogóle wystąpi jakiś błąd, po prostu perrori ratuj: wyjdź ze stanu niezerowego. Jeśli wystąpi błąd, sprawdź to strace. Nie potrzebujesz tego programu, aby wyświetlać naprawdę ładne komunikaty o błędach.


2
Mogę mieć pokusę, aby otworzyć oba pliki przed napisaniem czegokolwiek w celu ochrony przed brakiem drugiego. Mogę uruchomić ten program, sudowięc sam nie musi być ustawiony jako setuid. Nawiasem mówiąc, to jest <fcntl.h>dla open().
Jonathan Leffler

3

Bless tee zamiast echo dla sudo jest jednym z powszechnych sposobów podejścia do sytuacji, w której musisz ograniczyć perm root. Przekierowanie do / dev / null ma zatrzymać wyciekanie danych wyjściowych - tee robi to, co chcesz.

echo "17" | sudo tee /sys/class/gpio/export > /dev/null
echo "out" | sudo tee /sys/class/gpio/gpio17/direction > /dev/null

5
Jeśli zezwalasz teena uruchomienie sudo, zezwalasz na DOWOLNY plik do zastąpienia lub /etc/passwddołączenia (w tym na przykład - po prostu dodaj nowe konto UID = 0). Równie dobrze możesz zezwolić na uruchamianie wszystkich poleceń sudo(BTW /etc/sudoersnależy do zestawu „DOWOLNYCH plików”, więc można je zastąpić sudo tee)
cas

2
Najwyraźniej możesz ograniczyć pliki, do których teemożna pisać, jak opisano w tej odpowiedzi na osobne pytanie. Upewnij się tylko, że czytasz komentarze, ponieważ niektóre osoby miały problemy z zastosowaną oryginalną składnią i sugerują rozwiązania tego problemu.
Alex

1
@alex, tak, możesz to zrobić za pomocą dowolnego polecenia w sudo - ogranicz dopuszczalne argumenty. Konfiguracja może być bardzo długa i skomplikowana, jeśli chcesz zezwolić na teecokolwiek z wieloma plikami sudo.
cas

0

Możesz stworzyć skrypt, który wykona wybrane zadanie. Następnie utwórz nowe konto użytkownika, które będzie mogło się logować tylko poprzez podanie klucza używanego przez OpenSSH.

Uwaga: Każdy będzie mógł uruchomić ten skrypt, jeśli ma plik klucza, więc upewnij się, że plik klucza OpenSSH nie jest czytelny dla nikogo, komu chcesz uniemożliwić wykonanie zadania.

W konfiguracji OpenSSH (w pliku autoryzowanych_kluczy) przed określeniem klucza określ polecenie (po którym następuje spacja), jak opisano w tekście „przykładowym”:

command="myscript" keydata optionalComment

Ta opcja konfiguracji może ograniczyć ten klucz OpenSSH do uruchamiania tylko określonej komendy. Teraz masz sudo nadające uprawnienia, ale konfiguracja OpenSSH jest rozwiązaniem, które naprawdę jest używane do ograniczenia / ograniczenia tego, co ten użytkownik jest w stanie zrobić, aby użytkownik nie uruchamiał innych poleceń. Plik konfiguracyjny „sudo” również nie jest tak skomplikowany, więc jeśli używasz OpenBSD lub jeśli nowe „doas” OpenBSD („zrób jako”) stają się bardziej popularne (w przyszłych wersjach dowolnego używanego systemu operacyjnego) , nie będziesz musiał stawić czoła dużej złożoności konfiguracji sudo.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.