Istnieje polecenie powłoki unix ( udevadm info -q path -n /dev/ttyUSB2
), które chcę wywołać z programu C. Mając zapewne około tydzień walki, mógłbym sam go wdrożyć, ale nie chcę tego robić.
Czy ogólnie przyjętą dobrą praktyką jest po prostu dzwonienie popen("my_command", "r");
, czy też spowoduje to niedopuszczalne problemy z bezpieczeństwem i przekaże problemy ze zgodnością? Robienie czegoś takiego jest dla mnie złe, ale nie mogę wskazać, dlaczego byłoby to złe.
udevadm
plik wykonywalny w tym samym katalogu co twój program i użyć go do eskalacji uprawnień? Nie wiem dużo o bezpieczeństwie unixowym, ale czy twój program będzie działałsetuid root
?PATH
, to nie - trzeba go uruchomić./udevadm
. IIRC Windows będzie jednak uruchamiał pliki wykonywalne tego samego katalogu.Odpowiedzi:
Nie jest to szczególnie złe, ale są pewne zastrzeżenia.
LANG
itp.?;rm -rf /
(na przykład) (zwróć uwagę, że niektóre interfejsy API pozwalają na bezpieczniejsze określanie argumentów niż podawanie ich w wierszu poleceń)Generalnie z przyjemnością wykonuję pliki binarne, gdy jestem w znanym środowisku, które mogę przewidzieć, kiedy dane binarne można łatwo przeanalizować (w razie potrzeby - może być potrzebny kod zakończenia) i nie muszę tego też robić często.
Jak już zauważyłeś, innym problemem jest to, ile pracy wymaga odtworzenie tego, co robi plik binarny? Czy korzysta z biblioteki, którą możesz wykorzystać?
źródło
Forking a binary results in a lot more load and requires more resources than executing library calls (generally speaking).
Gdyby tak było w systemie Windows, zgodziłbym się. Jednak OP określa Unix, gdzie rozwidlenie jest na tyle niskie, że trudno powiedzieć, czy dynamiczne ładowanie biblioteki jest gorsze niż rozwidlenie.exec
niż zfork
. Ładowanie sekcji, łączenie w udostępnionych obiektach, uruchamianie kodu inicjującego dla każdego. A następnie trzeba przekonwertować wszystkie dane na tekst, który zostanie przeanalizowany po drugiej stronie, zarówno dla danych wejściowych, jak i wyjściowych.udevadm info -q path -n /dev/ttyUSB2
mówiąc , i tak nie będzie działać na Windowsie, więc cała rozwidlona dyskusja jest nieco teoretyczna.Kiedy wprowadzasz potencjalny wektor, należy zachować szczególną ostrożność, aby uchronić się przed podatnością na wstrzyknięcia. To jest teraz w twojej głowie, ale później możesz potrzebować możliwości wyboru
ttyUSB0-3
, wtedy ta lista będzie używana w innych miejscach, więc zostanie uwzględniona zgodnie z zasadą jednej odpowiedzialności, a następnie klient będzie musiał wprowadzić dowolne urządzenie na liście, a programista dokonujący tej zmiany nie będzie miał pojęcia, że lista ostatecznie zostanie użyta w niebezpieczny sposób.Innymi słowy, kod jakby najbardziej nieostrożny programista, którego znasz, wprowadza niebezpieczną zmianę w pozornie niezwiązanej części kodu.
Po drugie, dane wyjściowe narzędzi CLI nie są ogólnie uważane za stabilne interfejsy, chyba że dokumentacja je specjalnie oznaczy. Być może możesz na nich liczyć, że uruchomisz skrypt, który możesz rozwiązać samodzielnie, ale nie na coś, co wdrożysz u klienta.
Po trzecie, jeśli chcesz w łatwy sposób wyodrębnić wartość ze swojego oprogramowania, istnieje szansa, że ktoś też tego chce. Poszukaj biblioteki, która już robi to, co chcesz.
libudev
został już zainstalowany w moim systemie:W tej bibliotece znajdują się inne przydatne funkcje. Domyślam się, że jeśli potrzebujesz tego, niektóre pokrewne funkcje również mogą się przydać.
źródło
ttyUSB2
i używaniasprintf(buf, "udevadm info -q path -n /dev/%s", argv[1])
. Teraz, gdy pojawia się dość bezpieczny, ale co jeśliargv[1]
jest"nul || echo OOPS"
?W konkretnym przypadku, w którym chcesz się wywołać
udevadm
, podejrzewam, że możesz pobrać sięudev
jako biblioteka i wykonać odpowiednie wywołania funkcji jako alternatywę?np. możesz rzucić okiem na to, co robi sam udevadm podczas wywoływania w trybie „informacji”: https://github.com/gentoo/eudev/blob/master/src/udev/udevadm-info.c i wykonywać połączenia equiv co do tych, które robi udevadm.
Pozwoliłoby to uniknąć wielu kompromisów wymienionych w doskonałej odpowiedzi Briana Agnewa - np. Nie polegając na pliku binarnym istniejącym na określonej ścieżce, unikając kosztów rozwidlenia itp.
źródło
Twoje pytanie wymagało leśnej odpowiedzi, a odpowiedzi tutaj wydają się podobne do drzew, więc pomyślałem, że dam ci leśną odpowiedź.
Bardzo rzadko pisane są programy C. Zawsze tak pisane są skrypty powłoki, a czasem pisane są programy Python, Perl lub Ruby.
Ludzie zwykle piszą w języku C, aby ułatwić korzystanie z bibliotek systemowych i bezpośredni dostęp do wywołań systemowych na niskim poziomie, a także szybkość. Język C jest trudny do napisania, więc jeśli ludzie nie potrzebują tych rzeczy, wówczas nie używają języka C. Oczekuje się również, że programy w języku C będą zależały tylko od bibliotek współdzielonych i plików konfiguracyjnych.
Ucieczka do podprocesu nie jest szczególnie szybka i nie wymaga drobnoziarnistego i kontrolowanego dostępu do obiektów systemowych niskiego poziomu, i wprowadza potencjalnie zaskakującą zależność od zewnętrznego pliku wykonywalnego, więc nie jest rzadkością w programach C.
Istnieją pewne dodatkowe obawy. Bezpieczeństwo i przenośność dotyczą osób, które wspominają, są całkowicie ważne. Oczywiście są one równie ważne dla skryptów powłoki, ale ludzie oczekują tego rodzaju problemów w skryptach powłoki. Jednak zwykle nie oczekuje się, że programy typu C będą miały tę klasę bezpieczeństwa, co czyni je bardziej niebezpiecznymi.
Ale moim zdaniem największe obawy dotyczą sposobu
popen
interakcji z resztą twojego programu.popen
musi utworzyć proces potomny, odczytać jego dane wyjściowe i zebrać status wyjścia. W międzyczasie stderr tego procesu zostanie podłączony do tego samego stderr co twój program, co może powodować mylące wyjście, a jego stdin będzie taki sam jak twój program, co może powodować inne interesujące problemy. Możesz rozwiązać ten problem, dołączając</dev/null 2>/dev/null
ciąg, który przekazujesz,popen
ponieważ jest on interpretowany przez powłokę.I
popen
tworzy proces potomny. Jeśli zrobisz coś z obsługą sygnałów lub rozwidlaniem procesów, możesz otrzymać dziwneSIGCHLD
sygnały. Twoje wezwania dowait
mogą dziwnie współdziałaćpopen
i stwarzać dziwne warunki wyścigowe.Istnieją oczywiście obawy dotyczące bezpieczeństwa i przenośności. Tak jak w przypadku skryptów powłoki lub czegokolwiek, co uruchamia inne pliki wykonywalne w systemie. I musisz uważać, aby ludzie używający twojego programu nie byli w stanie wprowadzić meta-znaków w powłoce do łańcucha, który przekazujesz,
popen
ponieważ ten łańcuch jest przekazywany bezpośrednio zash
pomocąsh -c <string from popen as a single argument>
.Ale nie sądzę, że są, dlaczego to jest dziwne, aby zobaczyć program C przy użyciu
popen
. To dziwne, ponieważ C jest zwykle językiem niskiego poziomu, apopen
nie niskim poziomem. A ponieważ użyciepopen
ograniczeń projektowych ogranicza Twój program, ponieważ dziwnie współdziała ze standardowym wejściem i wyjściem Twojego programu i utrudnia zarządzanie procesami lub obsługę sygnałów. A ponieważ zwykle nie oczekuje się, że programy C będą miały zależności od zewnętrznych plików wykonywalnych.źródło
Twój program może podlegać atakom hakerskim itp. Jednym ze sposobów ochrony przed tego rodzaju działaniami jest utworzenie kopii bieżącego środowiska komputera i uruchomienie programu przy użyciu systemu o nazwie chroot.
Z punktu widzenia twojego programu jego działanie odbywa się w normalnym środowisku, z punktu widzenia bezpieczeństwa, jeśli ktoś złamie twój program, ma on dostęp tylko do elementów, które podałeś podczas tworzenia kopii.
Taka konfiguracja nazywa się więzieniem chroot, aby uzyskać więcej informacji, zobacz więzienie chroot .
Zwykle służy do konfigurowania publicznie dostępnych serwerów itp.
źródło