Jak skompilować kompilator C od zera, a następnie skompilować Unix / Linux od zera


64

Załóżmy, że pracuję dla dużej organizacji usługowej poza USA / Wielką Brytanią. Korzystamy z serwerów UNIX i Linux w szerokim zakresie.

Czytając ten artykuł , wspomniano, że łatwo byłoby wstawić backdoora do kompilatora C, a następnie każdy kod skompilowany z tym kompilatorem również zawierałby backdoora. Biorąc pod uwagę ostatnie wycieki dotyczące mandatu NSA / GCHQ do wprowadzenia backdoorów / słabości we wszystkich metodach szyfrowania, sprzęcie i oprogramowaniu, kompilator jest teraz krytycznym punktem awarii. Potencjalnie wszystkie standardowe dystrybucje UNIX / Linix mogą zostać naruszone. Nie możemy sobie pozwolić na to, aby nasze systemy, dane i dane naszych klientów zostały naruszone przez nieuczciwe rządy.

Biorąc pod uwagę te informacje, chciałbym zbudować od podstaw zaufany kompilator, a następnie mam bezpieczną bazę do zbudowania systemu operacyjnego i aplikacji z kodu źródłowego przy użyciu tego kompilatora.

Pytanie

Jaka jest poprawna (i bezpieczna metoda) kompilacja kompilatora z kodu źródłowego (scenariusz na pozór jaja kurzego), a następnie kompilacja zaufanej dystrybucji Unix / Linux od zera?

Możesz założyć, że ja lub inni potrafimy odczytać i zrozumieć kod źródłowy pod kątem błędów bezpieczeństwa, więc kod źródłowy zostanie sprawdzony przed kompilacją. To, co naprawdę mnie interesuje, to działający przewodnik do bezpiecznego tworzenia tego kompilatora od zera, który można wykorzystać do kompilacji jądra, innych części systemu operacyjnego i aplikacji.

Stos bezpieczeństwa musi zaczynać się od poziomu podstawowego, jeśli mamy mieć zaufanie do systemu operacyjnego lub aplikacji działających na tym stosie. Tak, rozumiem, że mogą istnieć sprzętowe backdoory, które mogą wstawiać mikrokod do kompilatora podczas jego tworzenia. W tej chwili niewiele możemy na to poradzić, z wyjątkiem użycia układów nie zaprojektowanych w USA. Najpierw posortujmy tę warstwę i załóżmy, że mogę ją zbudować na starym komputerze potencjalnie przed wstawieniem jakichkolwiek backdoorów.

Jak mówi Bruce Schneier: „Inżynierom to mówię: zbudowaliśmy internet, a niektórzy z nas pomogli go obalić. Teraz ci z nas, którzy kochają wolność, muszą to naprawić”.

Dodatkowe linki:


7
Cholera, to bardzo interesujące pytanie i nie chcę go migrować, ale tak naprawdę nie sądzę, żeby było na ten temat. Lepiej nadaje się do stackoverflow.com, ponieważ twoje podstawowe pytanie dotyczy tego, jak skompilować kompilator od zera, który jest w dużej mierze niezależny od systemu operacyjnego i jest bardzo pytaniem programistycznym. Jeśli po pewnym czasie nie otrzymasz odpowiedzi, zastanów się nad użyciem linku „flaga” pod tagami pytania i poproś moderatora o przeniesienie go do SO.
terdon

2
@terdon To może być lepsze rozwiązanie dla Programmers.SE, ponieważ dotyczy bardziej ogólnych problemów programistycznych niż konkretnego problemu programistycznego. W rzeczywistości może to być duplikat .
CVn

2
GCC jest oprogramowaniem typu open source, w jaki sposób można wstawić dowolny backdoor?
Michael Pankov,

2
Należy pamiętać, że stabilny exploit Thompson wymaga kodu, który rozpoznaje kompilację programu logowania lub kompilatora. Jeśli możesz ręcznie przekształcić źródło w formę, która nie jest rozpoznawalna przez kompilator jako jeden z tych programów, backdoor nie będzie propagowany.
Russell Borogove

2
@ Constantius - przeczytaj artykuł Thompsona połączony w pierwszym wierszu. Kto kompiluje kompilator?
Russell Borogove

Odpowiedzi:


30

AFAIK jedynym sposobem na całkowitą pewność bezpieczeństwa byłoby napisanie kompilatora w języku asemblera (lub samodzielna modyfikacja dysku ). Tylko wtedy możesz upewnić się, że Twój kompilator nie wstawia backdoora - działa to, ponieważ faktycznie całkowicie eliminujesz kompilator.

Stamtąd możesz użyć kompilatora od zera, aby uruchomić np. Łańcuch narzędzi GNU. Następnie możesz użyć własnego zestawu narzędzi do skompilowania systemu Linux From Scratch .

Pamiętaj, że aby ułatwić sobie życie, możesz mieć drugi kompilator pośredni, napisany w C (lub w innym języku). Więc napisałbyś kompilator A w asemblerze, a następnie przepisałeś ten kompilator w C / C ++ / Python / Brainfuck / cokolwiek, aby uzyskać kompilator B, który skompilowałbyś za pomocą kompilatora A. Następnie użyłbyś kompilatora B do kompilacji gcc i przyjaciół.


13
Mimo to chroni to tylko przed złośliwym kompilatorem. Nadal musisz zaufać systemowi, na którym działa kompilator. Żadne oprogramowanie nie istnieje w izolacji.
CVn

3
Wszystko autonomiczne jest z natury niebezpieczne. Skutecznie proponujesz kompilator łańcucha narzędzi (choć dziwny), co oznacza, że ​​prawdopodobnie można go zmodyfikować dokładnie tak, jak próbujesz go uniknąć. Co więcej, można go zmodyfikować w drodze przez MitM.
strugee

1
Musicie zdawać sobie sprawę, że ta odpowiedź pochodzi od 15-latka. Idź strikee!
mtahmed

3
Nie należy również zapominać o napisaniu edytora kodu od zera - kto wie, czy prekompilowana <code> vim </code> lub <code> vim </code> kompilujesz za pomocą dobrego kompilatora ze źródła, które skontrolowałeś tylko za pomocą zainfekowanego <code> vim </code> jest godny zaufania?
Hagen von Eitzen,

1
Nigdy nie zapominaj, że o ile nie napisałeś osobiście tego pierwszego kodu maszynowego (nie asemblującego. Rzeczywisty kod maszynowy), i jesteś ekspertem w rozpoznawaniu słabych punktów bezpieczeństwa oraz czytaniu i sprawdzaniu każdego wiersza kodu, który kompilujesz… lub przynajmniej znasz osoba, która zrobiła to osobiście , i ufam mu to zrobić .... nic z tego nie pomoże. Dlatego próba Kickstarter to rujnuje cały punkt. Który jest: Wysoka wiarygodność.
Evi1M4chine

22

Jednym z możliwych sposobów, choć w praktyce zajęłoby to wyjątkowo dużo czasu, byłoby powrót do korzeni. Rozwój GNU rozpoczął się w 1984 roku, a oryginalna wersja Minix (która była używana podczas wczesnego rozwoju Linuksa do celów ładowania systemu) została wydana w 1987 roku.

Cała odpowiedź opiera się na założeniu, że „[ty] lub inne osoby potrafią odczytać i zrozumieć kod źródłowy pod kątem błędów bezpieczeństwa, więc kod źródłowy zostanie sprawdzony przed kompilacją” i że można ufać wynikowi takiej analizy . Bez tego odpowiedź ta jest prawdopodobnie gorsza niż bezwartościowa, ponieważ będziesz spędzać ogromną ilość czasu bez żadnej korzyści.

Jeśli możesz znaleźć kopię oryginalnej książki Minix z kodem źródłowym, możesz ją wpisać z książki. Skompiluj go, a następnie użyj innego dekompilatora w innym systemie, aby sprawdzić, czy kompilator generuje oczekiwane wyjście binarne języka maszynowego. (Kod jest tylko 12000 linie, przypuszczalnie C, Czyniąc tak jest czasochłonne, ale nadal w powodu jeśli myślisz poważnie o takim projekcie). Można nawet napisać własny dezasembler; to nie powinno być bardzo trudne.

Chwyć najstarsze wersje narzędzi GNU, które możesz zdobyć (ponieważ prawdopodobnie mają mniej kodu i mniej zależności od bibliotek zewnętrznych), przejrzyj kod, skompiluj go dla Minix (może to jednak trochę potrwać; absolutnie chcę tego uniknąć, wprowadzając poprawki do kodu źródłowego, ponieważ spowoduje to, że dodawanie łat później będzie bardzo podatne na błędy) i przejdzie podobny cykl deasemblacji-weryfikacji dla narzędzi GNU. W tym momencie ufasz systemowi operacyjnemu i zestawowi narzędzi, więc musisz tylko przejrzeć kod źródłowy w zestawie poprawek (wszystko, co nie znajduje się w zestawie poprawek, jest już zaufane), ale narzędzia będą nadal bardzo prymitywne i surowe w porównaniu do tego, czego używasz do dzisiaj. Nie spodziewaj się na przykład niczego więcej niż najbardziej podstawowej funkcjonalności narzędzi systemowych.Przeczytaj wiele XKCD.

W pewnym momencie będziesz mieć system, który może kompilować i ładować wczesną wersję jądra Linux, podobnie jak na początku lat 90., gdy Linux zaczął zyskiwać na popularności wśród hakerów. Sugerowałbym migrację do Linuksa w tym momencie (przebuduj biblioteki systemowe i zestaw narzędzi na Linuksa, zbuduj jądro Linuksa, uruchom system Linux i ewentualnie przebuduj jądro Linuksa i łańcuch narzędzi GNU w Linuksie; ostatni pokazuje, że system jest teraz samodzielny hosting), ale to w dużej mierze zależy od Ciebie. Kontynuuj sprawdzanie poprawek, łatanie jądra, bibliotek i podstawowych narzędzi GNU oraz przebudowywanie, aż przejdziesz do nowoczesnych wersji.

Wtedy masz zaufany podstawowy system operacyjny i kompilator, którego można użyć do budowy nowoczesnego oprogramowania. Do tego czasu możesz śledzić np. Przewodniki Linux From Scratch, aby zbudować system zdolny do wykonywania użytecznych zadań.

W żadnym momencie system „kompilatora” nie może być w żaden sposób podłączony do sieci (w tym jako maszyna wirtualna na hoście w sieci); ryzykujesz penetrację dowolnego komponentu sieciowego, w tym jądra. Jeśli martwisz się atakiem kompilatora Thompson , musisz spodziewać się, że dowolny host maszyny wirtualnej również może zostać zagrożony. Użyj sneakernet, aby pobrać kod źródłowy i pliki binarne z fizycznego hosta, na którym kompilujesz. Spodziewaj się problemów z włączaniem i wyłączaniem plików co najmniej zanim dojdziesz do punktu, w którym zaimplementowano obsługę pamięci masowej USB. Jeśli jesteś naprawdę paranoikiem, wykazy kodów źródłowych druk i wpisać je ręcznie (i mam nadzieję, że sterownik drukarki i drukarka nie mają podobny kod w nich) lub odczytaj kod na jednym monitorze komputera i wpisz go na innym komputerze fizycznie obok, ale nie podłączonego do niego.

Tak, zajmie to dużo czasu. Jednak zaletą tego podejścia jest to, że każdy krok ma charakter przyrostowy, co oznacza, że ​​o wiele trudniej będzie się przedostać przez szkodliwe oprogramowanie, o ile nie będzie on stopniowo wprowadzany przez wiele wersji; Dzieje się tak, ponieważ zestaw zmian na każdym etapie jest stosunkowo niewielki, a zatem znacznie łatwiejszy do przejrzenia. Porównaj zestaw poprawek z dziennikiem zmian i upewnij się, że możesz dokładnie określić, który wpis dziennika zmian odpowiada każdej zmianie w kodzie źródłowym. Ponownie zakłada to, że masz możliwość (prawdopodobnie przez kogoś, komu ufasz) sprawdzenia, czy takie zmiany nie zostały zakradzione w bazie kodu, ale powinno to doprowadzić cię tak blisko do zaufanego systemu, jak tylko oprogramowanie, z wyjątkiem… podejście oprogramowania układowego może.


Metoda dezasemblacji-weryfikacji jest bardzo wadliwa, ponieważ nadal przyjmuje ogromne założenie, że maszyna weryfikująca jest w pełni godna zaufania. Chyba że zbudujesz tę maszynę i jej oprogramowanie od zera lub nie poznasz osoby, która osobiście jej zaufała i jej zaufała, tak się nie stanie. To wciąż jest niepewne. Przepraszam. …… Również w tych kwestiach „tak blisko…” wciąż oznacza „niepewne”, ponieważ wymaga tylko jednego niewiarygodnego miejsca, aby zrujnować cały punkt.
Evi1M4chine

9

Jeśli potrzebujesz zaufanego kompilatora, możesz przyjrzeć się pracy akademickiej, takiej jak projekt compcert . Jest to kompilator zbudowany przez INRIA (francuskie publiczne laboratorium IT) zaprojektowany w celu uzyskania „certyfikatu”, tj. W celu uzyskania wykonywalnego semantycznie idealnie równoważnego z kodem (i oczywiście został matematycznie udowodniony).


1
Każdy potrzebuje zaufanego kompilatora. Jak działają matematyki, że mogą stworzyć „zaufany” kompilator?
David J

@DavidJ Bootstrapping, najprawdopodobniej. Zbuduj mały kawałek, który możesz całkowicie zweryfikować i udowodnić, że jest poprawny, a następnie użyj go jako podstawy do budowania bardziej złożonych kompilatorów.
CVn

1
„” „Co odróżnia CompCert C od jakiegokolwiek innego kompilatora produkcyjnego, to to, że jest formalnie weryfikowany, przy użyciu matematycznych dowodów obsługiwanych maszynowo, w celu wykluczenia problemów z błędną kompilacją .” „” Compcert.inria.fr/compcert-C.html Kompilacja nie jest tak empiryczny jak kiedyś.
lgeorget

1
@ MichaelKjörling, który prawdopodobnie nie bierze pod uwagę, że jądro może zostać przejęte przez włączenie backdoora do źródła kompilatora podczas odczytu przez kompilator
maniak ratchet

1
Znalazłem też ten link, który również może działać.
David J

2

Podczas gdy ręczne tworzenie własnego kompilatora jako punktu początkowego byłoby najbezpieczniejsze, kolejną opcją jest instalacja systemu z pięcioletniego (lub 10) letniego instalacyjnego dysku CD, który został utworzony przed istnieniem tych exploitów. Następnie użyj go jako podstawy do skompilowania nowego kontrolowanego źródła.


5
Atak jest znany publicznie od 1984 roku. Prawdopodobnie Thompson nie był pierwszym, który pomyślał o takiej możliwości. Cofnięcie się tak daleko oznacza, że ​​większość rzeczy, które dziś uważamy za coś oczywistego, nie było w pobliżu; zastanów się, co potrafiły komputery 20 lat temu i porównaj to z ich obecnym stanem. Nawet oryginalny system ładujący Linuksa Minix został wydany dopiero w 87 , a rozwój GNU rozpoczął się w 84. Chociaż teoretycznie może to odpowiedzieć na pytanie, w praktyce jest w dużej mierze bezużyteczne jako odpowiedź.
CVn

2
Najwcześniejszym komputerem, który mógłbym zdobyć, byłoby 286. Muszę sprawdzić, czy dziadkowie nadal go mają.
David J

1
Punkty bonusowe za wzięcie tego pod uwagę :-). @DavidJ
11684

@ MichaelKjörling: Nie bardzo; ponieważ wydłuża to tylko proces ładowania. Ale może nie tak długo, jak pisanie własnego kompilatora od podstaw w języku maszynowym.
Evi1M4chine
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.