RAND_bytes() failed to stwierdzenie o jednym lokalnym prymitywie, nie o koncie, kluczu wstępnie współdzielonym ani porcie zdalnym. Dwie bardzo różne awarie dają tę samą linię i zostawiają różne ślady. Jedna to backend kryptograficzny, który odmawia wydania bajtów; druga to proces, który nie dociera do źródła entropii, dla którego zbudowano jego kod. Rozdzielenie ich wymaga jednego odczytu kolejki błędów i jednego spojrzenia na widok systemu plików uwięzionego procesu, a ta kontrola należy przed wszystkim, co powstaje pod /dev.

Co rozróżnia wartość zwracana

Udokumentowany kontrakt rozróżnia trzy wyniki, nie dwa: 1 dla sukcesu, -1 gdy załadowana metoda RAND nie obsługuje żądania, oraz 0 dla innych awarii, przy czym wyjaśnienie pozostaje w kolejce błędów. Dokumentacja RAND_bytes dodaje punkt łatwy do przeoczenia: domyślny generator inicjuje się z systemu operacyjnego na obsługiwanych platformach, a gdy to zasiewanie zawiedzie, pozostaje w stanie błędu zamiast zwracać przewidywalne bajty.

Tylko jedna z tych liczb opisuje środowisko. -1 to kwestia kompilacji, dostawcy lub funkcji, ponieważ kod zażądał czegoś, czego aktywna implementacja nie oferuje. 0 ze skargą na zasiewanie to kwestia środowiska, ponieważ generator w ogóle nie mógł pozyskać entropii. Sama liczba nigdy nie mówi która, a nazwa funkcji też nie.

Kiedy ukryty węzeł urządzenia jest prawdziwą przyczyną

Ograniczona usługa widzi inne drzewo niż powłoka, w której piszesz. Więzienia chroot, obrazy kontenerów i utwardzone pliki jednostek regularnie pokazują /dev z mniejszą liczbą węzłów niż u gospodarza. Jeśli zawodna implementacja otwiera /dev/urandom po ścieżce, ta różnica wystarcza, by zepsuć start, podczas gdy sam gospodarz wygląda całkiem zdrowo. Urządzenie istniejące poza więzieniem niczego nie dowodzi o tym, co uwięziony proces może otworzyć.

Nie każde losowe interfejs potrzebuje jednak ścieżki. Wywołanie systemowe getrandom w Linuksie nie przyjmuje ani nazwy pliku, ani deskryptora pliku, a jego strona podręcznika zauważa, że ta właściwość jest przydatna właśnie tam, gdzie chroot ukrywa ścieżki urządzeń.

Wyjaśnienie brakującego węzła jest więc hipotezą o jednej gałęzi kodu, a nie regułą o liczbach losowych w ogóle. Utrzymuje się, gdy dowody nazywają ścieżkę, i jest domysłem, gdy dowody nazywają coś innego.

Najpierw przeczytaj kolejkę błędów, potem widok procesu

Najszybszym sposobem rozstrzygnięcia pytania jest odczytanie tego, co biblioteka już zapisała, a następnie bezpośrednie spojrzenie na widok wewnątrz więzienia. Zacznij od kompilacji OpenSSL, którą usługa faktycznie załadowała, ponieważ stosy dostawców różnią się między wersjami głównymi i między dystrybucjami. Potem zapytaj systemd, który proces działa i jakie ograniczenia go dotyczą. Dopiero gdy oba fakty leżą na stole, warto wymienić węzły, które ten proces widzi.

openssl version -a
systemctl show openvpn-server@lab -p MainPID -p PrivateDevices -p DevicePolicy
pid=$(systemctl show -p MainPID --value openvpn-server@lab)
sudo ls -l /proc/"$pid"/root/dev/urandom /proc/"$pid"/root/dev/random

Wykonaj te odczyty na tym samym procesie, nie na powłoce, która przypadkiem jest otwarta. Porównywanie /dev gospodarza z widokiem niepowiązanego procesu to sposób, w jaki zła zależność zostaje naprawiona dwa razy.

Dodaj zależność dopiero wtedy, gdy potrafisz ją nazwać

Gdy dowody rzeczywiście nazywają ścieżkę, naprawa jest mała: udostępnij tę jedną ścieżkę wewnątrz więzienia i zostaw wszystko inne w spokoju. Montowanie bind pojedynczego węzła zwykle wystarcza, a mknod ze standardowymi numerami major i minor węzła daje ten sam wynik w instalacjach, w których montowanie nie jest opcją. Rozszerzenie piaskownicy, przyznanie dostępu do każdego urządzenia znakowego lub wyłączenie PrivateDevices zamienia ukierunkowaną poprawkę na trwałą regresję bezpieczeństwa.

Demon entropii nie zastępuje tej naprawy. Pakiety takie jak haveged zmieniają sposób zasilania puli jądra; nie mogą utworzyć ścieżki, którą uwięziony proces ma prawo otworzyć. Zainstalowanie jednego może poprawić wygląd wykresu kondycji, podczas gdy wywołanie open nadal zawodzi. Trzymaj te dwa problemy osobno, inaczej następna awaria znowu zostanie przypisana niewłaściwej warstwie.

Sprawdź generator i tunel osobno

Restart to jedyny test, który ponownie wykorzystuje tę samą konfigurację, ten sam plik jednostki i to samo odizolowanie, które spowodowały awarię. Uruchom go, a potem czytaj najnowsze wpisy dziennika, a nie próbkę z wcześniejszego startu.

sudo systemctl restart openvpn-server@lab
sudo journalctl -u openvpn-server@lab --since -5min --no-pager | grep -iE 'rand|entropy|Initialization Sequence'

Szukaj w tym wyjściu dwóch osobnych rzeczy: znikającej wiadomości o losowym źródle i tunelu osiągającego swój zwykły kamień milowy startu. Generator, który znów działa, usuwa jedną lokalną awarię i nic nie mówi o tożsamości serwera, poświadczeniach ani routingu. Przetestuj to z tą samą konfiguracją klienta, która wcześniej zawiodła, aby wynik był porównywalny, a nie tylko lepszy.

Powiązane artykuły