Jak dodać własne klucze dla Secure Boot do firmware EFI/UEFI pod linux
Spis treści
W środowiskach linux'owych Secure Boot nie jest zbyt mile widziany i raczej postrzegany jako źródło wszelkiego zła na świecie. Sporo użytkowników linux'a dopatruje się w Secure Boot zamachu na wolność decydowania o swoim sprzęcie, który ma być serwowany przez Microsoft. Niemniej jednak, odsądzanie tego mechanizmu od czci i wiary jest moim zdaniem lekko nie na miejscu. Niewielu użytkowników linux'a zdaje sobie sprawę, że od prawie dekady istnieje taki wynalazek jak shim (no i jest też PreLoader), który umożliwia dystrybucjom linux'a (jak i ich końcowym użytkownikom) zaimplementowanie Secure Boot we własnym zakresie. Niemniej jednak, można całkowicie się obejść bez tych dodatków usuwając wbudowane w firmware EFI/UEFI certyfikaty i dodając na ich miejsce własnoręcznie wygenerowane klucze kryptograficzne. Takie podejście sprawia, że kod podpisany tylko i wyłącznie przez nas będzie mógł zostać uruchomiony przez firmware komputera w trybie Secure Boot, co może ździebko poprawić bezpieczeństwo naszego systemu. Poniższy artykuł ma na celu pokazać w jaki sposób zastąpić wbudowane w firmware EFI/UEFI klucze swoimi własnymi przy wykorzystaniu dystrybucji Debiana.
Opis działania Secure Boot w linux na przykładzie shim
By zrozumieć w jaki sposób Secure Boot jest w stanie poprawić bezpieczeństwo systemu, trzeba najpierw zaznajomić się z procedurą startu komputera w trybie EFI/UEFI. Można spotkać się z różnymi rozwiązaniami ale chyba najbardziej uniwersalnym i popularnym zarazem jest shim. W zasadzie te główniejsze dystrybucje linux'a, jak Ubuntu i Debian, korzystają właśnie z shim'a. Postanowiłem zatem przyjrzeć się nieco bliżej jak wygląda start linux'a w trybie EFI/UEFI z wykorzystaniem shim, tak by go dokładnie zrozumieć i w miarę przyzwoicie opisać.
Shim to bardzo proste oprogramowanie zaprojektowane przez deweloperów różnych dystrybucji linux'a, by działało ono jako bootloader pierwszego poziomu na systemach EFI/UEFI. Kod shim jest otwartoźródłowy, prosty i przy tym dobrze zrozumiały. Został on też poddany audytom bezpieczeństwa i kompleksowo przebadany. Można mu zatem zaufać, że będzie działał w taki sposób jak się od niego oczekuje. Dlatego też Microsoft może taki kawałek oprogramowania podpisać swoim kluczem prywatnym. Dlaczego właśnie MS, a nie inny podmiot? Wszystko przez popularność windows'a na desktopach i laptopach. Z racji, że ten system jest w dość powszechnym wykorzystaniu na domowych komputerach, to producenci sprzętu zaszywają domyślnie certyfikat Microsoft'u bezpośrednio w firmware EFI/UEFI maszyny. Ten certyfikat jest w stanie zweryfikować podpis złożony prywatnym kluczem MS. W taki sposób, Microsoft może skupić się jedynie na podpisaniu shim'a i nie musi przy tym martwić o tę całą masę oprogramowania, z którego my, jako docelowi użytkownicy komputera, zamierzamy korzystać. Wykorzystanie shim'a mogłoby być opcjonalne, gdyby tylko producenci sprzętu podeszli należycie do zakodowania kluczy różnych dystrybucji linux'a w firmware EFI/UEFI, a z tym różnie bywa i zwykle stosownych certyfikatów w firmware brakuje i dlatego są takie problemy z mechanizmem Secure Boot w przypadku linux'a. Dlatego też właśnie powstał shim, który z racji podpisania kluczem Microsoft'u rozwiązuje cały zaistniały problem z Secure Boot na linux.
Shim używany jest głównie do dostarczenia klucza publicznego CA danej dystrybucji linux'a, który to z kolei jest używany do podpisywania innych programów, np. kernela czy bootloader'a takiego jak Grub2. Pełna lista programów podpisywanych przez Debiana znajduje się tutaj. W taki sposób ciężar i obowiązek podpisania stosownych binarek spoczywa na dystrybucji linux'a jeśli ma ona zamiar wspierać mechanizm Secure Boot. Dodatkowo, w ramach poprawy bezpieczeństwa, binarki shim dostępne w repozytorium Debiana są w pełni reprodukowalne.
Klucz CA dystrybucji jest zaszyty w shim na stałe i nie możemy go ruszyć. Istnieje też druga baza
danych kluczy, która może być zarządzana przez użytkownika, zwana Machine Owner Key (MOK). Klucze w
tej bazie mogą być dodawane i usuwane przez użytkownika z poziomu działającego linux'a za pomocą
narzędzia mokutil
. Niemniej jednak, każdy klucz trzeba będzie potwierdzić w konsoli na wczesnym
etapie startu systemu, co zapobiega dodaniu podstawionych kluczy przez malware przestrzeni
użytkownika, który potencjalnie byłby w stanie obejść mechanizm Secure Boot. Zarządzanie tymi
kluczami jest niezależnie od głównego klucza CA dystrybucji linux'a.
Gdy maszyna z linux się uruchamia, to zaraz na początku fazy boot, firmware EFI/UEFI ładuje do
pamięci RAM binarkę shim, która została określona w zmiennej BootEntry w tym firmware (np. za
sprawą efibootmgr
). Podczas tradycyjnej instalacji linux'a, stosowny wpis powinien być
automatycznie dodany i aktualizowany ilekroć tylko będziemy instalować aktualizacje dla
bootloader'a (Grub2). Gdy przychodzi moment weryfikacji przez firmware podpisu złożonego pod shim,
to ta binarka jest zatwierdzana z racji pomyślnego zweryfikowania podpisu (jest podpisana kluczem
MS). Jako, że shim posiada również zaszyty certyfikat jakiejś dystrybucji (w tym przypadku Debiana),
jak i bazę danych MOK, to kolejne binarki mogą zostać zweryfikowane tymi dodatkowymi certyfikatami.
Następnie shim ładuje obraz drugiego poziomu (second-stage image), którym może być bootloader
(zwykle Grub2) lub MokManager jeśli zarządzanie kluczami jest z jakiegoś powodu wymagane.
W przypadku normalnego rozruchu, binarka bootloader'a ( grub*.efi
) jest ładowana do pamięci
operacyjnej i weryfikowana względem zaufanych certyfikatów. Jako, że Grub2 jest podpisany przez
klucz dystrybucji, to powinien zostać z powodzeniem zweryfikowany i załadowany, a proces startu
powinien być kontynuowany.
W przypadku, gdy zostanie zainicjowany proces zarządzania kluczami, to binarka MokManager
( mm*.efi
) zostanie załadowana do pamięci RAM. Ta binarka jest zaufana przez shim za sprawą
klucza efemerycznego (tymczasowego), który istnieje jedynie w czasie procesu kompilacji shim'a.
Oznacza to, że dana binarka MokManager'a może zostać zweryfikowana jedynie przez odpowiadający jej
shim i żaden inny shim nie będzie w stanie tej binarki zweryfikować, co zapobiega całej masie
problemów natury bezpieczeństwa.
MokManager pozwala każdemu użytkownikowi komputera mającemu dostęp do konsoli systemowej na przeprowadzenie poniższych akcji:
- dodanie nowych kluczy do bazy
- usunięcie zaufanych kluczy z bazy
- dodanie binarnych hash'y
- przełączenie weryfikacji Secure Boot na poziomie shim
Te powyższe czynności z reguły będą dostępne jedynie po wprowadzeniu uprzednio ustawionego hasła (w działającym systemie operacyjnym), tak by zweryfikować tożsamość osoby, która wydaje polecenia. Takie hasła są tworzone na czas pojedynczego uruchomienia shim'a lub MokManager'a i są czyszczone jak tylko proces dobiegnie końca lub zostanie anulowany. Gdy proces zarządzania kluczami zakończy się, komputer restartuje i kontynuuje fazę startu systemu operacyjnego.
Gdy Grub2 zostanie uruchomiony, ładuje on sobie całą potrzebną konfigurację (zwykle z partycji ESP), która wskazuje na kolejny plik konfiguracyjny zlokalizowany tym razem na na partycji BOOT (lub też w obrębie głównego systemu plików). Z kolei ten plik konfiguracyjny wskazuje położenie kernela i obrazu initrd, który trzeba będzie załadować do pamięci RAM w celu kontynuowania procesu boot.
Obraz kernela, który zostanie załadowany, również musi zostać zweryfikowany, a wcześniej też i podpisany zaufanym kluczem, który przechowywany jest w bazie danych MOK. Oficjalne kernele Debiana są podpisane kluczem dystrybucji i bez problemu powinny zostać zweryfikowane. Jak tylko ten proces zakończy się powodzeniem, to kontrola nad dalszym startem maszyny jest przekazywana kernelowi. Co ciekawe obraz initrd/initramfs nie jest weryfikowany w żaden sposób.
Gdy Secure Boot jest włączony, to każdy błąd przy weryfikacji ładowanych binarek do tego momentu będzie skutkował przerwaniem procesu startu maszyny. W przypadku, gdy błędów nie będzie, załadowany w pamięci operacyjnej kernel wyłączy usługi startu (Boot Services) w firmware EFI/UEFI zrzucając w ten sposób uprawnienia i efektywnie przełączy się w tryb użytkownika (user mode), gdzie dostęp do zaufanych zmiennych firmware EFI/UEFI jest ograniczony jedynie do odczytu.
Jako, że moduły kernela mogą posiadać rozbudowane uprawnienia, to każdy taki moduł niewbudowany bezpośrednio w kernel musi zostać podpisany osobno zaufanym kluczem, w przeciwnym razie system nie pozwoli go załadować. Wszystkie moduły, które są dostarczane z dystrybucyjnym kernelem Debiana są podpisane i zaufane. Pozostałe moduły, np. budowane za sprawą mechanizmu DKMS, muszą być podpisane przez użytkownika osobno. Próba załadowania niepodpisanego modułu zawsze zakończy się błędem, który zostanie odnotowany w logu systemowym.
Klucze wbudowane w firmware EFI/UEFI
Na wiki Gentoo dostępny jest przyzwoicie opisany tutorial, którego części postanowiłem przetłumaczyć w drodze konfigurowania swojego laptopa i umieścić w niniejszym artykule. Wszystkie rzeczy, które wymagały wyjaśnienia lub dopowiedzenia, zostały wyjaśnione i dopowiedziane, a brakujące informacje zostały dodane i wkomponowane między wersy, tak by poniższy artykuł był nieco pełniejszy niż ten podlinkowany tutek i zawierał wszystkie niezbędne rzeczy, których człowiek może potrzebować przy zaoraniu swojego firmware EFI/UEFI na potrzeby bezpiecznej instalacji Debian linux.
Mając rozeznanie jak mniej więcej działa Secure Boot, można zauważyć, że pewne zagrożenie może płynąć z racji zaszycia określonych certyfikatów bezpośrednio w firmware EFI/UEFI. W efekcie każde oprogramowanie podpisane przez klucze prywatne, np. Microsoft'u czy Lenovo, może zostać uruchomione na naszej maszynie i potencjalnie skompromitować zabezpieczenia jakie daje mechanizm Secure Boot. W przypadku mojego ThinkPad'a T430 wkodowane na stałe były te poniższe certyfikaty.
Zmienna PK
:
# efi-readvar -v PK
Variable PK, length 983
PK: List 0, type X509
Signature 0, size 955, owner 3cc24e96-22c7-41d8-8863-8e39dcdcc2cf
Subject:
C=JP, ST=Kanagawa, L=Yokohama, O=Lenovo Ltd., CN=Lenovo Ltd. PK CA 2012
Issuer:
C=JP, ST=Kanagawa, L=Yokohama, O=Lenovo Ltd., CN=Lenovo Ltd. PK CA 2012
Zmienna KEK
:
# efi-readvar -v KEK
Variable KEK, length 2545
KEK: List 0, type X509
Signature 0, size 957, owner 7facc7b6-127f-4e9c-9c5d-080f98994345
Subject:
C=JP, ST=Kanagawa, L=Yokohama, O=Lenovo Ltd., CN=Lenovo Ltd. KEK CA 2012
Issuer:
C=JP, ST=Kanagawa, L=Yokohama, O=Lenovo Ltd., CN=Lenovo Ltd. KEK CA 2012
KEK: List 1, type X509
Signature 0, size 1532, owner 77fa9abd-0359-4d32-bd60-28f4e78f784b
Subject:
C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Corporation KEK CA 2011
Issuer:
C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Corporation Third Party Marketplace Root
Zmienna db
:
# efi-readvar -v db
Variable db, length 4209
db: List 0, type SHA256
Signature 0, size 48, owner 7facc7b6-127f-4e9c-9c5d-080f98994345
Hash:14e62a4905e19189e70828983165939afc0a331d0b415f3332b0e818a827f436
db: List 1, type X509
Signature 0, size 1515, owner 77fa9abd-0359-4d32-bd60-28f4e78f784b
Subject:
C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Windows Production PCA 2011
Issuer:
C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Root Certificate Authority 2010
db: List 2, type X509
Signature 0, size 1572, owner 77fa9abd-0359-4d32-bd60-28f4e78f784b
Subject:
C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Corporation UEFI CA 2011
Issuer:
C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Corporation Third Party Marketplace Root
db: List 3, type X509
Signature 0, size 962, owner 7facc7b6-127f-4e9c-9c5d-080f98994345
Subject:
C=JP, ST=Kanagawa, L=Yokohama, O=Lenovo Ltd., CN=ThinkPad Product CA 2012
Issuer:
C=JP, ST=Kanagawa, L=Yokohama, O=Lenovo Ltd., CN=Lenovo Ltd. Root CA 2012
Zmienna dbx
:
# efi-readvar -v dbx
Variable dbx, length 3800
dbx: List 0, type SHA256
Signature 0, size 48, owner 7facc7b6-127f-4e9c-9c5d-080f98994345
Hash:14e62a4905e19189e70828983165939afc0a331d0b415f3332b0e818a827f436
dbx: List 1, type SHA256
Signature 0, size 48, owner 77fa9abd-0359-4d32-bd60-28f4e78f784b
Hash:80b4d96931bf0d02fd91a61e19d14f1da452e66db2408ca8604d411f92659f0a
...
Signature 76, size 48, owner 77fa9abd-0359-4d32-bd60-28f4e78f784b
Hash:45c7c8ae750acfbb48fc37527d6412dd644daed8913ccd8a24c94d856967df8e
Zmienna MokList
:
# efi-readvar -v MokList
Variable MokList has no entries
Zmienne PK, KEK, db, dbx i MokList
Specyfikacja EFI/UEFI definiuje 4 bezpieczne nieulotne zmienne (secure, non-volatile variables),
które są wykorzystywane do kontrolowania podsystemu Secure Boot. Są to PK
(Platform Key) , KEK
(Key Exchange Key) , db
(Signature Database) i dbx
(Forbidden Signatures Database). Określenie
tych zmiennych mianem bezpiecznych bierze się z faktu, że zmiany w nich mogą zostać przeprowadzone
jedynie przez osobę władającą prywatną częścią klucza, który został użyty do utworzenia tych
zmiennych.
W zmiennej PK
jest przechowywany główny klucz platformy i jego zadaniem jest kontrola dostępu do
tej zmiennej jak i również do zmiennej KEK
. W większości implementacji firmware EFI/UEFI w
zmiennej PK
może być przechowywany tylko jeden taki klucz (zwykle X.509 używający schematu RSA
2048-bit). Gdy zmienna PK
jest czyszczona (czynność do przeprowadzenia w konfiguracji
firmware EFI/UEFI), system przechodzi w tryb ustawień (setup mode) i jednocześnie Secure Mode
zostaje wyłączony. W trybie ustawień, zmienne PK
, KEK
, db
oraz dbx
mogą zostać
zaktualizowane bez jakiegokolwiek uwierzytelnienia całego procesu. Niemniej jednak, natychmiast
po zapisaniu zmiennej PK
, system przechodzi w tryb użytkownika (user mode). Dlatego też zmienna
PK powinna być aktualizowana zawsze jako ostatnia. Klucz PK
nie może być użyty do podpisania
plików wykonywalnych.
W zmiennej KEK
przechowywane są klucze (X509 lub RSA2048) wykorzystywane do nadzorowania procesu
aktualizacji bazy danych sygnatur przechowywanej w zmiennej db
. Klucze KEK
mogą także zostać
użyte do podpisywania plików wykonywalnych, choć jest to rzadkie ze względu na fakt, że do tego
celu wykorzystywane są klucze, których certyfikaty figurują w zmiennej db
.
Baza danych sygnatur przechowywana w zmiennej db
jest wykorzystywana do weryfikacji podpisanych
plików wykonywalnych w środowisku firmware EFI/UEFI przy włączonym Secure Boot. Może ona zawierać
dowolny miks kluczy publicznych, sygnatur i hash'y. Ta zmienna funkcjonuje w zasadzie jako biała
lista plików wykonywalnych podczas początkowej fazy startu maszyny. Z kolei baza zabronionych
sygnatur przechowywana jest w zmiennej dbx
i jest wykorzystywana do uniemożliwienia załadowania
binarek, które z jakiegoś powodu zostały określone jako niezaufane. Zmienna dbx
robi w ten sposób
za czarną listę.
Poza tymi czterema głównymi zmiennymi można było jeszcze zauważyć zmienną MokList
. Jest ona
bardzo podobna do zmiennej db
i zwykle jest pusta ale przy pomocy narzędzia KeyTool
można do
niej dodawać/usuwać dowolne klucze, zwłaszcza klucze różnych dystrybucji linux'a, tak by mogły one
współpracować z Secure Boot, prawdopodobnie bez potrzeby zaciągania do tego shim'a. Póki co jednak,
nie udało mi się nic do tej zmiennej dodać, więc dokładnie nie wiem jakie jest jej przeznaczenie.
Gdy system jest uruchomiony w trybie użytkownika oraz Secure Boot jest włączony, to firmware EFI/UEFI będzie w stanie uruchomić jedynie tylko te pliki wykonywalne, które:
- nie są podpisane ale mają hash w
db
i równocześnie nie mają go wdbx
- są podpisane i ich sygnatura występuje w
db
i nie ma jej przy tym wdbx
- są podpisane i ich sygnatura jest weryfikowalna za sprawą klucza publicznego obecnego w
db
lub klucza publicznego przechowywanego wKEK
i zarazem ani klucz ani sygnatura nie występują wdbx
.
Po nabyciu nowego komputera mającego windows'a na pokładzie, te cztery zmienne będą skonfigurowane zwykle w poniższy sposób:
- Zmienna
PK
będzie miała załadowany klucz publiczny wystawiony przez producenta sprzętu, np. Lenovo. - Zmienna
KEK
będzie miała załadowany klucz publiczny wystawiony przez Microsoft. - Zmienna
db
będzie miała załadowany zestaw kluczy publicznych wystawionych przez szereg producentów autoryzowanych przez Microsoft. - Zmienna
dbx
będzie zwykle zawierać cofnięte sygnatury lub hash'e (może też być pusta).
Usuwanie wbudowanych kluczy i dodanie własnych
W przypadku takich osób jak ja, które nie zamierzają instalować na swoim laptopie windows'a, te wbudowane klucze są niezbyt mile widziane patrząc z punktu widzenia bezpieczeństwa. Dodatkowo, mogą one powodować problemy z Secure Boot w przypadku instalacji linux'a. Część implementacji EFI/UEFI umożliwia użytkownikowi komputera usunięcie wszystkich domyślnych kluczy. W ten sposób zostaje otwarta droga do wdrożenia Secure Boot, nad którym to my będziemy mieć pełnię władzy z racji, że będziemy decydować jakie oprogramowanie będzie się w stanie uruchomić na naszej maszynie. Odbierzemy jednocześnie tę możliwość decydowania podmiotom takim jak Microsoft czy Lenovo.
Teoretycznie przy zaprzęgnięciu shim'a, Secure Boot powinien działać OOTB. Niemniej jednak, w przypadku mojego laptopa tak się nie stało. Być może jest on za stary i dlatego ma problem z odpaleniem Debiana w trybie Secure Boot na domyślnym zestawie kluczy wgranych w firmware EFI/UEFI. Tak czy inaczej, postanowiłem zaorać wszystkie te klucze i wgrać tam swoje własne, tak by sprawdzić czy to coś pomoże.
Backup stock'owych kluczy firmware EFI/UEFI
Jako, że zamierzamy wyczyścić stock'owe klucze w firmware EFI/UEFI, to wypadałoby zacząć od
zrobienia ich backup'u. Technicznie rzecz biorąc, to w konfiguracji tego firmware powinny być
stosowne opcje do odtworzenia tych kluczy (i w przypadku mojego laptopa są) ale lepiej jest mieć
własny backup, tak na wszelki wypadek. Musimy zatem zrobić zrzut zawartości zmiennych PK
,
KEK
, db
i dbx
. Kopię zapasową robimy przy pomocy narzędzia efi-readvar
dostępnego w
pakiecie efitools
:
# mkdir -p /etc/kernel_key/efikeys.orig/
# chmod 700 /etc/kernel_key/efikeys.orig/
# cd /etc/kernel_key/efikeys.orig/
# efi-readvar -v PK -o old_PK.esl
Variable PK, length 983
# efi-readvar -v KEK -o old_KEK.esl
Variable KEK, length 2545
# efi-readvar -v db -o old_db.esl
Variable db, length 4209
# efi-readvar -v dbx -o old_dbx.esl
Variable dbx, length 3800
Warto tutaj zaznaczyć, że backup musi mieć format listy sygnatur czytelny dla maszyn i dlatego
określony został przełącznik -o
. Bez niego byśmy dostali tylko tekstową reprezentację, której
nie bylibyśmy w stanie przywrócić w późniejszym czasie, gdyby zaszła taka potrzeba.
Tworzenie nowych kluczy
Mając zrobiony backup kluczy firmware EFI/UEFI, tworzymy nowe klucze. W późniejszym czasie wgramy
te nasze klucze w miejsce tych, które zostaną usunięte. Rozmiar klucza można podać naturalnie 4096
ale niektóre implementacje EFI/UEFI nie akceptują większych kluczy niż 2048 bitów. Dlatego lepiej
pozostać przy standardowym rozmiarze. Klucze generujemy zaś przy pomocy openssl
w poniższy sposób:
# cd /etc/kernel_key/
# openssl req -new -x509 -newkey rsa:2048 -subj "/CN=morfikov's platform key/" -keyout PK.key -out PK.crt -days 3650 -nodes -sha256
# openssl req -new -x509 -newkey rsa:2048 -subj "/CN=morfikov's key-exchange-key/" -keyout KEK.key -out KEK.crt -days 3650 -nodes -sha256
# openssl req -new -x509 -newkey rsa:2048 -subj "/CN=morfikov's kernel-signing key/" -keyout db.key -out db.crt -days 3650 -nodes -sha256
# chmod -v 400 *.key
mode of 'KEK.key' changed from 0600 (rw-------) to 0400 (r--------)
mode of 'PK.key' changed from 0600 (rw-------) to 0400 (r--------)
mode of 'db.key' changed from 0600 (rw-------) to 0400 (r--------)
Firmware EFI/UEFI różnych producentów sprzętu może mieć nieco inne wymagania co do formatów plików, którymi będziemy chcieli się posłużyć w celu zaktualizowania zawartości zmiennych. Dlatego też przydałoby się wygenerować kilka dodatkowych plików, tak na wszelki wypadek, gdyby w naszym przypadku to one jednak znalazły zastosowanie.
Na początek tworzymy wersję formatu .auth
(signed signature list) zmiennej PK
, jako że
efi-updatevar
(jak i sporo graficznych interfejsów firmware EFI/UEFI) akceptuje tylko ten format.
Pierwsze z poniższych poleceń tworzy listę sygnatur, która wymaga unikalnego ID, choć nie jest on
dla nas tak istotny. W drugim poleceniu zaś używamy naszego prywatnego klucza PK
(platform key),
by tę listę sygnatur podpisać (zarówno cert-to-efi-sig-list
jak i sign-efi-sig-list
są dostępne
w pakiecie efitools
):
# cert-to-efi-sig-list -g "$(uuidgen)" PK.crt PK.esl
# sign-efi-sig-list -k PK.key -c PK.crt PK PK.esl PK.auth
Timestamp is 2020-3-15 15:05:52
Authentication Payload size 893
Signature of size 1211
Signature at: 40
Podobnie postępujemy w przypadku klucza KEK
i tutaj do podpisywania również trzeba wykorzystać
prywatny klucz PK
. Przełącznik -a
z kolei wskazuje, że dane będą dołączane do zmiennej zamiast
zastępować je:
# cert-to-efi-sig-list -g "$(uuidgen)" KEK.crt KEK.esl
# sign-efi-sig-list -a -k PK.key -c PK.crt KEK KEK.esl KEK.auth
Timestamp is 0-0-0 00:00:00
Authentication Payload size 903
Signature of size 1211
Signature at: 40
Podobnie postępujemy w przypadku db
ale tutaj wykorzystujemy do podpisu prywatny klucz KEK
.
Parametr -a
również trzeba określić:
# cert-to-efi-sig-list -g "$(uuidgen)" db.crt db.esl
# sign-efi-sig-list -a -k KEK.key -c KEK.crt db db.esl db.auth
Timestamp is 0-0-0 00:00:00
Authentication Payload size 905
Signature of size 1223
Signature at: 40
Podobnie postępujemy dla dbx
ale tutaj skorzystamy z pliku backupu, który utworzyliśmy wcześniej.
W tym przypadku do podpisu również jest wykorzystywany klucz KEK
:
# cp efikeys.orig/old_dbx.esl ./
# sign-efi-sig-list -k KEK.key -c KEK.crt dbx old_dbx.esl old_dbx.auth
Timestamp is 2020-3-15 15:34:41
Authentication Payload size 3842
Signature of size 1223
Signature at: 40
Tworzymy teraz wersję DER dla każdego z trzech naszych kluczy publicznych:
# openssl x509 -outform DER -in PK.crt -out PK.cer
# openssl x509 -outform DER -in KEK.crt -out KEK.cer
# openssl x509 -outform DER -in db.crt -out db.cer
Wsparcie dla windows
Stwórzmy jeszcze zespolony plik .esl
dla KEK
i db
(pliki .esl
można zwyczajnie połączyć). W
tym kroku chodzi o zachowanie wsparcia dla windows'a przez połączenie starych bazy danych (tych z
backup'u) z nowymi. W przypadku tak utworzonych plików również będziemy potrzebować odpowiedników
.auth
:
# cp efikeys.orig/old_KEK.esl ./
# cp efikeys.orig/old_db.esl ./
# cat old_KEK.esl KEK.esl > compound_KEK.esl
# cat old_db.esl db.esl > compound_db.esl
# sign-efi-sig-list -k PK.key -c PK.crt KEK compound_KEK.esl compound_KEK.auth
Timestamp is 2020-3-15 15:40:50
Authentication Payload size 3448
Signature of size 1211
Signature at: 40
# sign-efi-sig-list -k KEK.key -c KEK.crt db compound_db.esl compound_db.auth
Timestamp is 2020-3-15 15:40:54
Authentication Payload size 5114
Signature of size 1223
Signature at: 40
Brak wsparcia dla windows
W przypadku, gdy nie chcemy zachowywać wsparcia dla windows, to można pozbyć się kluczy Microsoft'u
(i innych podmiotów), które znajdowały się w zmiennych KEK
i db
). Usunięcie tych kluczy sprawi,
że żaden windows zainstalowany na naszej maszynie nie będzie w stanie się uruchomić z włączonym
Secure Boot. Niemniej jednak, wsparcie dla windows będzie można zaimplementować w późniejszym
czasie dodając klucze Microsoft'u do bazy.
By usunąć klucze Microsoft'u, musimy stworzyć pliki zespolone dla KEK
i db
(zarówno .esl
jak
i .auth
) z pominięciem komponentów Microsoft'u (bez dodawania zawartości z backup'u):
# cp KEK.esl compound_KEK.esl
# cp db.esl compound_db.esl
# sign-efi-sig-list -k PK.key -c PK.crt KEK compound_KEK.esl compound_KEK.auth
Timestamp is 2020-3-15 15:53:01
Authentication Payload size 903
Signature of size 1211
Signature at: 40
# sign-efi-sig-list -k KEK.key -c KEK.crt db compound_db.esl compound_db.auth
Timestamp is 2020-3-15 15:53:05
Authentication Payload size 905
Signature of size 1223
Signature at: 40
Tryb ustawień (setup mode)
Mając przygotowane własne pliki z kluczami, możemy przejść do wyczyszczenia starych kluczy z poziomu interfejsu firmware EFI/UEFI naszego laptopa. Wyczyszczenie kluczy przełączy nas automatycznie w tryb ustawień (setup mode), w którym to będziemy mogli wgrać te wyżej utworzone klucze bez żadnej weryfikacji całego procesu.
W pewnych implementacjach EFI/UEFI wymagane jest ustawienie hasła nadzorczego (supervisor password), by opcja do wyczyszczenia kluczy Secure Boot stała się dostępna. W przypadku mojego laptopa Lenovo ThinkPad T430, czyszczenie wbudowanych kluczy w firmware EFI/UEFI odbywa się w poniższy sposób.
Wchodzimy w ustawienia firmware EFI/UEFI i szukamy opcji od Secure Boot:
Jak widać wyżej, mamy pozycję od resetowania kluczy ( Reset to Setup Mode
) oraz od przywrócenia
kluczy fabrycznych ( Restore Factory Keys
). Nas interesuje ta pierwsza opcja:
Po zaakceptowaniu, pozycja Platform Mode
powinna się zmienić z User Mode
na Setup Mode
, a Secure Boot Mode
z Standard Mode
na Custom Mode
:
Zapisujemy ustawienia firmware EFI/UEFI i restartujemy komputer (tryb Secure Boot powinien być automatycznie wyłączony):
Po zalogowaniu się w systemie sprawdzamy czy zmienne PK
, KEK
, db
oraz dbx
zostały
wyczyszczone. Odpalamy zatem terminal i wpisujemy w nim polecenie efi-readvar
:
# efi-readvar
Variable PK has no entries
Variable KEK, length 2545
KEK: List 0, type X509
Signature 0, size 957, owner 7facc7b6-127f-4e9c-9c5d-080f98994345
Subject:
C=JP, ST=Kanagawa, L=Yokohama, O=Lenovo Ltd., CN=Lenovo Ltd. KEK CA 2012
Issuer:
C=JP, ST=Kanagawa, L=Yokohama, O=Lenovo Ltd., CN=Lenovo Ltd. KEK CA 2012
KEK: List 1, type X509
Signature 0, size 1532, owner 77fa9abd-0359-4d32-bd60-28f4e78f784b
Subject:
C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Corporation KEK CA 2011
Issuer:
C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Corporation Third Party Marketplace Root
Variable db, length 4209
db: List 0, type SHA256
Signature 0, size 48, owner 7facc7b6-127f-4e9c-9c5d-080f98994345
Hash:14e62a4905e19189e70828983165939afc0a331d0b415f3332b0e818a827f436
db: List 1, type X509
Signature 0, size 1515, owner 77fa9abd-0359-4d32-bd60-28f4e78f784b
Subject:
C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Windows Production PCA 2011
Issuer:
C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Root Certificate Authority 2010
db: List 2, type X509
Signature 0, size 1572, owner 77fa9abd-0359-4d32-bd60-28f4e78f784b
Subject:
C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Corporation UEFI CA 2011
Issuer:
C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Corporation Third Party Marketplace Root
db: List 3, type X509
Signature 0, size 962, owner 7facc7b6-127f-4e9c-9c5d-080f98994345
Subject:
C=JP, ST=Kanagawa, L=Yokohama, O=Lenovo Ltd., CN=ThinkPad Product CA 2012
Issuer:
C=JP, ST=Kanagawa, L=Yokohama, O=Lenovo Ltd., CN=Lenovo Ltd. Root CA 2012
Variable dbx, length 76
dbx: List 0, type SHA256
Signature 0, size 48, owner 7facc7b6-127f-4e9c-9c5d-080f98994345
Hash:14e62a4905e19189e70828983165939afc0a331d0b415f3332b0e818a827f436
Variable MokList has no entries
Z powyższych informacji wynika, że w zasadzie to tylko zmienna PK
została wyczyszczona. Zmienne
KEK
i db
nie zostały ruszone w żaden sposób, zaś zmienna dbx
została dość mocno odchudzona.
Tak czy inaczej, widać, że klucze Lenovo i Microsoft'u są obecne w dalszym ciągu w zmiennych KEK
i db
. W niczym to nie przeszkadza, bo za moment zawartość tych zmiennych zostanie zastąpiona
plikami, które wyżej przygotowaliśmy.
Aktualizacja kluczy przez efi-updatevar
Najprostszym sposobem na wgranie wygenerowanych przez nas kluczy jest posłużenie się narzędziem
efi-updatevar
dostępnym w pakiecie efitools
. Będąc cały czas na działającym linux'ie odpalamy
terminal i wpisujemy te poniższe polecenia:
# efi-updatevar -e -f old_dbx.esl dbx
# efi-updatevar -e -f compound_db.esl db
# efi-updatevar -e -f compound_KEK.esl KEK
# efi-updatevar -f PK.auth PK
Niestety w przypadku mojego ThinkPad'a przy próbie zapisu tych zmiennych zostają zwrócone poniższe błędy:
# efi-updatevar -e -f old_dbx.esl dbx
Failed to update dbx: Operation not permitted
# efi-updatevar -e -f compound_db.esl db
Failed to update db: Operation not permitted
# efi-updatevar -e -f compound_KEK.esl KEK
Failed to update KEK: Operation not permitted
Teoretycznie te trzy polecenia powinny zadziałać w większości przypadków ale wygląda na to, że mój
przypadek się do takowych nie zalicza i posłużenie się efi-updatevar
w takiej sytuacji zwyczajnie
odpada i trzeba posiłkować się innymi sposobami.
Jeśli napotkaliśmy ten powyższy problem, to prawdopodobnie pliki w katalogu
/sys/firmware/efi/efivars/
mają ustawiony bit odporności i trzeba im go usunąć.
Wgrywanie kluczy z poziomu firmware EFI/UEFI
Firmware EFI/UEFI niektórych komputerów z wyższej półki umożliwiają manipulowanie kluczami z
poziomu swojego interfejsu graficznego. W przypadku firmware tego laptopa próżno jednak szukać
stosownych opcji w konfiguracji, zatem nie da rady przepisać zmiennych PK
, KEK
, db
i dbx
tym sposobem również.
Wgrywanie kluczy przez KeyTool
W pakiecie efitools
znajduje się plik KeyTool.efi
, którego przeznaczeniem jest umożliwienie
operowania na zmiennych firmware EFI/UEFI. Ten plik trzeba będzie uruchomić bezpośrednio w
środowisku firmware, tj. zanim zostanie załadowany system operacyjny czy nawet bootloader. Jako, że
ja korzystam z menadżera rozruchu rEFInd, to mogę przekopiować plik KeyTool.efi
na partycję
ESP do katalogu EFI/tools/
:
# cp /usr/lib/efitools/x86_64-linux-gnu/KeyTool.efi /efi/EFI/tools/
Na partycję ESP dobrze jest też przekopiować potrzebne pliki z kluczami (nie kopiujmy tylko kluczy prywatnych):
# cp /etc/kernel_key/*.{auth,cer,crt,esl} /efi/EFI/secure_boot_keys/
Po zresetowaniu maszyny stosowna ikonka powinna pojawić się w menu wyboru (to ten żółty klucz) na fotce niżej:
Naturalnie wybieramy tę pozycję, odpalamy KeyTool i z menu, które zostanie nam zaprezentowane,
wybieramy Edit Keys
:
Teraz kolejno edytujemy zmienne zastępując ich zawartość przygotowanymi wcześniej plikami.
Zmienna dbx
:
Zmienna db
:
Zmienna KEK
:
Zmienna PK
:
Po wgraniu klucza do zmiennej PK
, wracamy do menu głównego, w którym to powinniśmy zobaczyć, że
tryb w jakim operował firmware zmienił się z Setup Mode
na User Mode
(w górnej części ekranu):
Wychodzimy i uruchamiamy system bez włączonego póki co jeszcze Secure Boot:
Sprawdzenie zmiennych
Po wgraniu zawartości plików do określonych zmiennych i uruchomieniu systemu sprawdzamy, czy udało
się nam przepisać wartości zmiennych PK
, KEK
, db
oraz dbx
:
# efi-readvar
Variable PK, length 853
PK: List 0, type X509
Signature 0, size 825, owner 90a7d962-e601-4363-a326-524f9f76132b
Subject:
CN=morfikov's platform key
Issuer:
CN=morfikov's platform key
Variable KEK, length 861
KEK: List 0, type X509
Signature 0, size 833, owner 95b65092-8342-4a45-9dc2-d80233aba2b3
Subject:
CN=morfikov's key-exchange-key
Issuer:
CN=morfikov's key-exchange-key
Variable db, length 865
db: List 0, type X509
Signature 0, size 837, owner 1091ff9c-7b84-4df6-8163-ed1b8aa05096
Subject:
CN=morfikov's kernel-signing key
Issuer:
CN=morfikov's kernel-signing key
Variable dbx, length 3800
dbx: List 0, type SHA256
Signature 0, size 48, owner 7facc7b6-127f-4e9c-9c5d-080f98994345
Hash:14e62a4905e19189e70828983165939afc0a331d0b415f3332b0e818a827f436
dbx: List 1, type SHA256
Signature 0, size 48, owner 77fa9abd-0359-4d32-bd60-28f4e78f784b
Hash:80b4d96931bf0d02fd91a61e19d14f1da452e66db2408ca8604d411f92659f0a
...
Signature 76, size 48, owner 77fa9abd-0359-4d32-bd60-28f4e78f784b
Hash:45c7c8ae750acfbb48fc37527d6412dd644daed8913ccd8a24c94d856967df8e
Variable MokList has no entries
Jak widać, wbudowane w firmware EFI/UEFI klucze zostały zastąpione tymi wygenerowanymi przez nas i
w tej chwili tylko to oprogramowanie, które podpiszemy, może być załadowane w trybie Secure Boot na
naszym laptopie. W zmiennej dbx
są dokładnie takie same wpisy co poprzednio, bo została ona
odtworzona, jako że jest to w zasadzie jedynie czarna lista hash'y, których raczej nie powinniśmy
ignorować.
Testowanie Secure Boot z własnymi kluczami
Do wstępnego przetestowania mechanizmu Secure Boot z własnoręcznie wygenerowanymi kluczami
potrzebny jest nam jedynie podpisany przez nas kernel oraz zarejestrowany wpis w firmware EFI/UEFI,
który można dodać za pomocą efibootmgr
.
Podpisywanie kernela
O ile kernel dystrybucyjny i jego moduły są podpisane kluczem Debiana, o tyle ten nasz
własnoręcznie zbudowany nie jest. Po skończonym procesie kompilacji, wynikowy plik vmlinuz
nie jest w żaden sposób podpisany i trzeba go podpisać ręcznie przy pomocy prywatnego klucza db
.
Do tego celu służy narzędzie sbsign
dostępne w pakiecie sbsigntool
. Proces podpisywania
binarki kernela wygląda zaś następująco:
# cd /etc/kernel_key
# sbsign --cert db.crt --key db.key /boot/vmlinuz-5.5.9-amd64
Signing Unsigned original image
Zarówno certyfikat jak i klucz prywatny są niezbędne w procesie podpisywania i trzeba ścieżki do
nich określić w opcjach --cert
i --key
. Ostatni argument to ścieżka do pliku, który zamierzamy
podpisać. Plik, który podpisujemy nie jest zmieniany w żaden sposób, tylko jest on kopiowany z
nazwą mająca sufiks .signed
i to ta kopia jest podpisywana. By zweryfikować czy plik został
pomyślnie podpisany, wydajemy poniższe polecenia:
# sbverify --list '/boot/vmlinuz-5.5.9-amd64.signed'
signature 1
image signature issuers:
- /CN=morfikov's kernel-signing key
image signature certificates:
- subject: /CN=morfikov's kernel-signing key
issuer: /CN=morfikov's kernel-signing key
Jak widać plik zawiera sygnaturę naszego klucza db
. Sprawdźmy czy da radę ją zweryfikować:
# sbverify --cert db.crt /boot/vmlinuz-5.5.9-amd64.signed
Signature verification OK
Informacja Signature verification OK
świadczy o pomyślnym zakończeniu procesu weryfikacji.
Podpisywanie modułów kernela
Zwykle podpisanie binarki kernela nam wystarczy ale w przypadku, gdy budowaliśmy jakieś moduły
zamiast wszystko wkompilować na stałe w kernel, to trzeba zadbać również o podpisanie tych modułów.
W konfiguracji kernela (do wglądu przez make xconfig
), można doszukać się opcji
CONFIG_MODULE_SIG_KEY
, która wygląda mniej więcej tak:
Ta opcja (jak i jej podobne) odpowiada w zasadzie za podpisanie tylko i wyłączenie modułów, które
nie zostaną wkompilowane na stałe w kernel. Tak czy inaczej w polu widocznym wyżej musimy określić
ścieżkę do pliku klucza prywatnego db
i jego certyfikatu. Niemniej jednak, certyfikat jest w
osobny pliku .crt
, a klucz prywatny w osobnym pliku .key
. Trzeba te dwa pliki pierw połączyć:
# cat db.key db.crt > kernel-key.pem
Ścieżkę do tak otrzymanego pliku trzeba podać w zmiennej CONFIG_MODULE_SIG_KEY
w konfiguracji
kernela.
Podpisywanie modułów DKMS
Jeśli budujemy jakieś moduły zewnętrzne przy pomocy mechanizmu DKMS, to również musimy zatroszczyć
się o ich podpisanie. Oczywiście ręczne podpisywanie byłoby dość mozolne i na szczęście możemy ten
proces podpisywania modułów DKSM zautomatyzować. Ten podlinkowany artykuł idealnie rozprawia
się z tą kwestią, trzeba tylko wskazać odpowiednią ścieżkę do certyfikatu i klucza prywatnego db
w skrypcie /etc/kernel_key/sign-kernel.sh
.
Firmware do WiFi/BT, a Secure Boot
Część sprzętu, który będziemy chcieli używać pod linux, np. karty WiFi czy adaptery bluetooth,
wymaga dodatkowego firmware pod linux, by prawidłowo funkcjonować. Włączenie Secure Boot nie wpływa
w żadnym stopniu na fakt czy firmware, którego potrzebuje nasza karta, zostanie załadowany przez
kernel czy też nie. Z racji, że pliki firmware sprzętu znajdujące się w katalogu /lib/firmware/
nie są ładowane przez firmware EFI/UEFI tylko przez kernel linux'a, to nie ma potrzeby podpisywania
tych plików. Poniżej znajduje się potwierdzenie tych słów na przykładzie karty WiFi mojego laptopa
wykorzystującej moduł iwlwifi
, który potrzebny plik firmware iwlwifi-6000g2a-6.ucode
ładuje
sobie z obrazu initrd/initramfs:
...
kernel: secureboot: Secure boot enabled
...
kernel: iwlwifi 0000:03:00.0: can't disable ASPM; OS doesn't have ASPM control
kernel: iwlwifi 0000:03:00.0: firmware: direct-loading firmware iwlwifi-6000g2a-6.ucode
kernel: iwlwifi 0000:03:00.0: loaded firmware version 18.168.6.1 op_mode iwldvm
kernel: iwlwifi 0000:03:00.0: CONFIG_IWLWIFI_DEBUG enabled
kernel: iwlwifi 0000:03:00.0: CONFIG_IWLWIFI_DEBUGFS disabled
kernel: iwlwifi 0000:03:00.0: CONFIG_IWLWIFI_DEVICE_TRACING enabled
kernel: iwlwifi 0000:03:00.0: Detected Intel(R) Centrino(R) Advanced-N 6205 AGN, REV=0xB0
kernel: iwlwifi 0000:03:00.0: Radio type=0x1-0x2-0x0
kernel: iwlwifi 0000:03:00.0: Radio type=0x1-0x2-0x0
Jak widać, brak podpisanego firmware nie przeszkadza w załadowaniu go do pamięci przez kernel.
Dodanie wpisu w firmware EFI/UEFI via efibootmgr
Podpisany kernel kopiujemy wraz z ewentualnym obrazem initrd/initramfs na partycję ESP i przy
pomocy narzędzia efibootmgr
dostarczanego w pakiecie o tej samej nazwie dodajemy stosowny wpis
w konfiguracji firmware EFI/UEFI:
# cp /boot/vmlinuz-5.5.9-amd64.signed /efi/EFI/refind/vmlinuz-morfik.signed
# cp /boot/initrd.img-5.5.9-amd64 /efi/EFI/refind/initrd.img-morfik-amd64
# efibootmgr --create --disk /dev/sda --part 3 --label "Debian EFISTUB" \
--loader '\EFI\refind\vmlinuz-morfik-amd64.signed' --unicode \
'root=/dev/mapper/wd_blue_label-root ro \
initrd=\EFI\refind\initrd.img-morfik-amd64' --verbose
Włączenie Secure Boot w konfiguracji EFI/UEFI
Pozostało nam już w zasadzie włączyć w konfiguracji firmware EFI/UEFI opcję Secure Boot:
Jak tylko przełączymy tę opcję z Disabled
na Enabled
, to nie będziemy w stanie zmienić
ustawień co do trybu w jakim będzie uruchamiał się system (EFI/UEFI vs. BIOS):
Zapisujemy ustawienia firmware EFI/UEFI i uruchamiamy maszynę ponownie:
Podczas początkowej fazy startu komputera, w lewym górnym rogu ekranu powinna być informacja o tym,
że system operacyjny uruchamiany jest w trybie Secure Boot ( EFI stub: UEFI Secure Boot is enabled
):
Jeśli przegapimy tę informację, to zawsze też można sprawdzić log systemowy, by upewnić się ze nasz linux wystartował w trybie Secure Boot:
# journalctl -b | grep -i secureboot
kernel: secureboot: Secure boot enabled
Mechanizm kernel lockdown
Włączenie Secure Boot w firmware EFI/UEFI aktywuje w kernelu linux'a mechanizm kernel lockdown.
Po jego włączeniu nie będziemy mogli ładować modułów, które nie są podpisane zaufanym kluczem (np.
te budowane za sprawą DKMS). Nie będzie nam działał też kexec na niepodpisanych obrazach
kernela. Podobnie przestanie działać hibernacja systemu (przełączenie maszyny w tryb
uśpienia/wstrzymania w dalszym ciągu będzie możliwe). Dostęp do fizycznej pamięci i portów I/O
zostanie zabroniony z przestrzeni użytkownika. Nie będzie można też używać parametrów modułów,
które pozwalają na ustawianie adresów pamięci i portów I/O. Dodatkowo zapis do rejestrów procesora
MSR przez /dev/cpu/*/msr
nie będzie już możliwy. Podobnie nie będzie można używać
niestandardowych metod i tablic ACPI. Nie będzie też można używać APEI error injection.
Obecność włączonego w systemie kernel lockdown można poznać po poniższych komunikatach w logu systemowym:
# journalctl -b | grep Lockdown
kernel: Lockdown: swapper: use of tracefs is restricted; see https://wiki.debian.org/SecureBoot
kernel: Lockdown: swapper/0: use of tracefs is restricted; see https://wiki.debian.org/SecureBoot
kernel: Lockdown: swapper/0: hibernation is restricted; see https://wiki.debian.org/SecureBoot
kernel: Lockdown: resume: hibernation is restricted; see https://wiki.debian.org/SecureBoot
kernel: Lockdown: systemd: /dev/mem,kmem,port is restricted; see https://wiki.debian.org/SecureBoot
kernel: Lockdown: systemd-logind: hibernation is restricted; see https://wiki.debian.org/SecureBoot
Jak widać, kernel lockdown to dość inwazyjny mechanizm i włączenie go może coś popsuć w naszym
systemie. Naturalnie Secure Boot może działać bez kernel lockdown i jeśli potrzebujemy którejś z
tych wyżej wymienionych rzeczy, to zawsze możemy skompilować kernel z wyłączoną opcją
CONFIG_LOCK_DOWN_IN_EFI_SECURE_BOOT
:
Można też całkowicie pozbyć się modułu lockdown odznaczając opcję CONFIG_SECURITY_LOCKDOWN_LSM
:
W moim przypadku, mechanizm lockdown upośledza hibernację, z której korzystam dość często i nie zamierzam rezygnować, bo daje ona możliwość zapisania stanu środowiska pracy i nie trzeba po starcie maszyny wszystkiego na nowo uruchamiać, co znacznie przyśpiesza gotowość systemu. Dlaczego zatem tak użyteczny mechanizm wypadł z łask lockdown? Okazuje się, że weryfikacja obrazu pamięci RAM zrobionego przy hibernowaniu maszyny jest dość trudna i póki co nie istnieje rozwiązanie, które by taką weryfikację oferowało. Ja mam to szczęście, że korzystam z w pełni zaszyfrowanego systemu, zatem obraz pamięci operacyjnej mam zawsze zaszyfrowany, przez co jest on niepodatny na nieautoryzowane zmiany. Dlatego też postanowiłem całkowicie wyłączyć lockdown dla swojego systemu w konfiguracji kernela i pewne rzeczy skonfigurować, tak by upodobnić swój system do zachowania znanego z lockdown. Patrząc na to co lockdown próbuje robić (podpierając się również tym linkiem), odznaczyłem te poniższe opcje w kernelu:
CONFIG_DEVMEM:
CONFIG_DEVKMEM:
CONFIG_DEVPORT:
CONFIG_ACPI_APEI_EINJ:
CONFIG_X86_MSR:
CONFIG_KEXEC:
CONFIG_KEXEC_FILE:
CONFIG_ACPI_CUSTOM_METHOD:
CONFIG_PROC_KCORE:
CONFIG_KPROBES:
CONFIG_EFI_TEST:
CONFIG_MMIOTRACE:
CONFIG_DEBUG_KERNEL:
Nie jest to może pełny lockdown ale lepsze to niż nic i póki co system zdaje się działać stabilnie.
Podpisanie boodloader'a
W przypadku instalacji mojego Debiana zrezygnowałem z tradycyjnego bootloader'a Grub2 i używam
jedynie menadżera rozruchu rEFInd. Niemniej jednak, po włączeniu Secure Boot, firmware EFI/UEFI nie
będzie chciał załadować binarek rEFInd'a. Pod pojęciem "binarek" mam na myśli nie tylko sam plik
EFI/refind/refind_x64.efi
ale również wszystkie jego narzędzia dostępne w katalogu EFI/tools/
czy też sterowniki od systemu plików, które siedzą w EFI/refind/drivers_x64/
. Te wszystkie pliki
mające sufiks .efi
trzeba osobno podpisać jeśli chcemy by firmware EFI/UEFI umożliwił nam
korzystanie z nich.
Podpisanie binarki rEFInd'a (plik EFI/refind/refind_x64.efi
):
# sbsign --cert db.crt --key db.key '/efi/EFI/refind/refind_x64.efi'
Signing Unsigned original image
# sbverify --cert db.crt '/efi/EFI/refind/refind_x64.efi.signed'
Signature verification OK
Podpisanie sterownika ext4 dla rEFInd'a (plik EFI/refind/drivers_x64/ext4_x64.efi
):
# sbsign --cert db.crt --key db.key '/efi/EFI/refind/drivers_x64/ext4_x64.efi'
Signing Unsigned original image
# sbverify --cert db.crt '/efi/EFI/refind/drivers_x64/ext4_x64.efi.signed'
Signature verification OK
Shim i MokManager
Mając skonfigurowane własne klucze odpada nam potrzeba korzystania z shim. Jeśli jednak
chcielibyśmy go zainstalować, to potrzebna nam będzie wersja niepodpisana, którą sami sobie
podpiszemy. Musimy zatem przekopiować pliki shimx64.efi
oraz mmx64.efi
na partycję ESP.
Dodatkowo, trzeba zainstalować rEFInd'a zmieniając mu nazwę z refind_x64.efi
na grubx64.efi
,
by został podebrany przez shim:
# cp /usr/lib/shim/shimx64.efi /efi/EFI/refind/shimx64.efi
# cp /usr/lib/shim/mmx64.efi /efi/EFI/refind/mmx64.efi
# cp /usr/share/refind/refind/refind_x64.efi /efi/EFI/refind/grubx64.efi
Podpisanie shim'a (pliki EFI/refind/shimx64.efi
):
# sbsign --cert db.crt --key db.key '/efi/EFI/refind/shimx64.efi'
Signing Unsigned original image
# sbverify --cert db.crt '/efi/EFI/refind/shimx64.efi.signed'
Signature verification OK
Podpisanie rEFInd'a (zainstalowanego w pliku EFI/refind/grubx64.efi
):
# sbsign --cert db.crt --key db.key '/efi/EFI/refind/grubx64.efi'
Signing Unsigned original image
# sbverify --cert db.crt '/efi/EFI/refind/grubx64.efi.signed'
Signature verification OK
Jeśli zaś chodzi o MokManager, to lepiej jest go nie podpisywać (szczegóły niżej).
Narzędzia w EFI/tools/
Jeśli zaś chodzi o narzędzia, które znajdują się w katalogu EFI/tools/
na partycji ESP, to w ich
przypadku trzeba nieco uważać z pospisywaniem. Dla przykładu weźmy EFIshell. Ta aplikacja robi w
zasadzie za terminal, przy pomocy którego możemy między innymi:
- uruchamiać aplikacje w środowisku firmware EFI/UEFI, np. bootloader'y
- uruchamiać programy partycjonujące dysk twardy (diskpart)
- modyfikować zmienne menadżera rozruchu (bcfg)
- ładować sterowniki EFI/UEFI
- edytować pliki tekstowe (edit)
- uzyskać informacje o systemie lub samym firmware EFI/UEFI
- uzyskać informacje o mapie pamięci (memmap)
- korzystać z hexedit
Jak widać, podpisanie takiego EFIshell wiązałoby się ze sporymi problemami jeśli chodzi o bezpieczeństwo naszego komputera, bo każda osoba mająca fizyczny dostęp do naszej maszyny mogła by tę binarkę załadować bez problemu i Secure Boot by jej w tym nie przeszkodził.
Jako, że jest cała masa narzędzi, które można wgrać na partycję ESP, to musimy sobie zadać pytanie, czy te narzędzia, z których zamierzamy korzystać mogą w jakiś sposób zagrozić bezpieczeństwu naszej maszyny. Jeśli tak, to wyjściem z tej sytuacji jest niepodpisywanie tych binarek. Oczywiście w takim przypadku po aktywowaniu Secure Boot nie będziemy w stanie z tych aplikacji korzystać. Dlatego też trzeba będzie ten mechanizm pierw zdezaktywować. Naturalnie każdy z fizycznym dostępem do komputera mógłby w takim przypadku wejść w ustawienia firmware EFI/UEFI i przełączyć stosowną opcję, dlatego też wymagane będzie założenie hasła nadzorczego (supervisor password) na firmware EFI/UEFI, które uniemożliwi wyłączenie trybu Secure Boot.
Sufiks .signed
W procesie podpisywania plików stworzone zostały podpisane odpowiedniki mające sufiks .signed
.
Trzeba te nazwy zmienić, tak by nie miały tej końcówki:
# mv '/efi/EFI/refind/refind_x64.efi.signed' '/efi/EFI/refind/refind_x64.efi'
# mv '/efi/EFI/refind/drivers_x64/ext4_x64.efi.signed' '/efi/EFI/refind/drivers_x64/ext4_x64.efi'
# mv '/efi/EFI/refind/grubx64.efi.signed' '/efi/EFI/refind/grubx64.efi'
# mv '/efi/EFI/refind/shimx64.efi.signed' '/efi/EFI/refind/shimx64.efi'
W przypadku bootloader'a czy boot menadżera (i jego narzędzi), ta powyższa czynność będzie
przeprowadzana w zasadzie tylko raz. Jeśli zaś chodzi o kernel'a, to przy każdej jego aktualizacji
trzeba będzie pamiętać o dostosowaniu nazwy pliku vmlinuz
lub też uwzględnić nazwę z końcówką
.signed
w konfiguracji rEFInd'a.
warning: data remaining[]: gaps between PE/COFF sections?
Podczas podpisywania plików można zaobserwować ciągłe pojawianie się ostrzeżeń, których treść brzmi
mniej więcej tak: warning: data remaining[]: gaps between PE/COFF sections?
. Zgodnie z tym co
można wyczytać tutaj, to dokładnie nie jest znana przyczyna występowania tych komunikatów.
Niemniej jednak, zdają się one nie wpływać na proces podpisu/weryfikacji binarki i można je z
powodzeniem zignorować.
Problemy z system live przy Secure Boot
Problem możemy za to mieć z różnego różnego rodzaju systemami live dostarczanymi na płytce czy
pendrive, które są wypuszczane przez konkretną dystrybucje linux'a. Te systemy live nie będą nam
działać i przy próbie uruchomienia systemu z zawierających je nośników dostaniemy błąd: Secure Boot validation failure loading bootx64.efi
(lub podobny):
Z informacji, które widać na ekranie wynika, że mamy w zasadzie te poniższe opcje rozwiązania zaistniałego problemu:
- możemy zrezygnować z używania systemów live (raczej odpada)
- możemy wyłączyć Secure Boot w ustawieniach firmware EFI/UEFI na czas korzystania z systemu live w przypadku, gdy system live nie jest w żaden sposób podpisany
- możemy podpisać bootloader, kernel (i inne potrzebne elementy systemu live) naszym kluczem
prywatnym
db
- można przerobić nośnik live i zbudować go z wykorzystaniem
grub-mkstandalone
, wynikowy obraz Grub2 trzeba będzie oczywiście podpisać - możemy skorzystać z niepodpisanego shim'a, podpisać go własnym kluczem
db
i dodać do MOK klucz dystrybucji oferującej system live - możemy do zmiennej
db
dodać klucz Microsoft'u, którym jest podpisana wersja shim'a mająca w nazwie.signed
, choć tutaj też trzeba będzie dodać do bazy MOK klucz dystrybucji oferującej system live
Po chwili namysłu raczej dwie ostatnie opcje są godne rozważenia. Jeśli nie mamy zamiaru uruchamiać windows'a na naszym laptopie, czy też w ogóle mieć do czynienia z Microsoft, to zostaje nam w zasadzie tylko jedna możliwość.
Brak kluczy do weryfikacji systemów live linux'a
Po pomyślnym skonfigurowaniu Secure Boot powinniśmy mieć mniej więcej te poniższe klucze w
keyring'u kernela pod /proc/keys
:
# cat /proc/keys | grep -i asym
109d1ebc I------ 1 perm 1f010000 0 0 asymmetri morfikov's kernel-signing key: 8d9ac6d38bbf26e45a55454c54c448059f530633: X509.rsa 9f530633 []
16b541ff I------ 1 perm 1f030000 0 0 asymmetri benh@debian.org: 577e021cb980e0e820821ba7b54b4961b8b4fadf: X509.rsa []
24968f3b I------ 1 perm 1f030000 0 0 asymmetri sforshee: 00b28ddf47aef9cea7: X509.rsa []
2e344477 I------ 1 perm 1f030000 0 0 asymmetri morfikov's kernel-signing key: 8d9ac6d38bbf26e45a55454c54c448059f530633: X509.rsa 9f530633 []
Widać tutaj dwa klucze mające w nazwie morfikov's kernel-signing key
i to są klucze, które
dodaliśmy w procesie konfigurowania Secure Boot opisanym powyżej. Czemu dwa? Jeden klucz wziął się
z wbudowania go w kernel w czasie jego kompilacji, bo potrzebowaliśmy go do podpisania modułów.
Drugi klucz z kolei pochodzi ze zmiennej db
Secure Boot -- tu również dodaliśmy swój klucz. W
zasadzie użyliśmy jednego klucza w obu tych miejscach dlatego też mamy dwie podobne (różniące się
uprawnieniami) pozycje w keyring'u kernela.
# keyctl list %:.secondary_trusted_keys
1 key in keyring:
891874024: ---lswrv 0 0 keyring: .builtin_trusted_keys
# keyctl list %:.builtin_trusted_keys
1 key in keyring:
775177335: ---lswrv 0 0 asymmetric: morfikov's kernel-signing key: 8d9ac6d38bbf26e45a55454c54c448059f530633
Jeśli zaś chodzi o te dwa dodatkowe klucze ( benh@debian.org
oraz sforshee
), to nie mają one
nic wspólnego z Secure Boot. Zostały one dodane za sprawą wireless-regdb.
Każdy klucz, który jest dodawany do keyring'u kernela jest odnotowywany w logu systemowym:
...
kernel: Loading compiled-in X.509 certificates
kernel: Loaded X.509 cert 'morfikov's kernel-signing key: 8d9ac6d38bbf26e45a55454c54c448059f530633'
kernel: integrity: Loading X.509 certificate: UEFI:db
kernel: integrity: Loaded X.509 cert 'morfikov's kernel-signing key: 8d9ac6d38bbf26e45a55454c54c448059f530633'
...
kernel: cfg80211: Loading compiled-in X.509 certificates for regulatory database
kernel: cfg80211: Loaded X.509 cert 'benh@debian.org: 577e021cb980e0e820821ba7b54b4961b8b4fadf'
kernel: cfg80211: Loaded X.509 cert 'sforshee: 00b28ddf47aef9cea7'
...
Oczywiście ten powyższy listing nie jest pełny, bo w keyring'u kernela znajduje się także zawartość
zmiennej dbx
(czarnej listy):
# cat /proc/keys | grep -i "blacklist\ " | wc -l
78
Pozostałe wpisy w keyring'u dotyczą już uruchomionej sesji i zalogowanych użytkowników. Wszystko co
w tym keyring'u kernela się znajduje można także wyciągnąć z efi-readvar
.
Jak można było zauważyć, nie mamy żadnego klucza dystrybucji, które dostarczają systemy live, np. Debian czy Ubuntu. Jeśli chcemy mieć możliwość uruchamiania tych systemów live, to musimy pozyskać stosowne certyfikaty i dodać je do bazy MOK.
Problem z keyctl
Czasami budując własny kernel można zapomnieć o włączeniu kilku opcji, których brak może powodować
problemy, np. Can't find 'keyring:.secondary_trusted_keys'
. Poniżej znajduje się lista opcji,
które powinny się znaleźć w kernelu, by tych problemów uniknąć:
CONFIG_INTEGRITY=y
CONFIG_INTEGRITY_SIGNATURE=y
CONFIG_INTEGRITY_ASYMMETRIC_KEYS=y
CONFIG_INTEGRITY_TRUSTED_KEYRING=y
CONFIG_INTEGRITY_PLATFORM_KEYRING=y
CONFIG_LOAD_UEFI_KEYS=y
CONFIG_INTEGRITY_AUDIT=y
CONFIG_SIGNATURE=y
CONFIG_SECONDARY_TRUSTED_KEYRING=y
CONFIG_SYSTEM_BLACKLIST_KEYRING=y
Dodawanie kluczy dystrybucji do bazy MOK
Na samym początku w MOK nie powinno być póki co żadnych kluczy, bo przecie jeszcze nic tam nie
dodawaliśmy. Dla pewności sprawdźmy to przy pomocy mokutil
:
# mokutil --list-enrolled
MokListRT is empty
Certyfikaty niektórych dystrybucji zostały zebrane przez rEFInd i są dostępne tutaj. O ile certyfikat Canonical tam znajdziemy, to nie ma tam z jakiegoś powodu certyfikatu Debiana, a o te dwa nam głównie chodzi w tej chwili. Na szczęście certyfikat Debiana można znaleźć tutaj. Oba te certyfikaty są zakodowane w DER, czyli dokładnie w takim formacie jaki nam jest potrzebny. Pobieramy zatem te certy:
# wget https://dsa.debian.org/secure-boot-ca -O debian-uefi-ca.cer
# wget https://sourceforge.net/p/refind/code/ci/master/tree/keys/canonical-uefi-ca.cer?format=raw -O canonical-uefi-ca.cer
Dodajemy teraz te certyfikaty do bazy MOK za pomocą mokutil
(hasło ustawiamy krótkie, np. 1234):
# mokutil --import debian-uefi-ca.cer canonical-uefi-ca.cer
input password:
input password again:
Te klucze czekają teraz by je dodać z poziomu MokManager'a i musimy w tym celu uruchomić komputer
ponownie. Jeśli nie wiemy czy to powyższe polecenie dodało jakiekolwiek klucze do kolejki, to
zawsze możemy zweryfikować kolejkę nowych kluczy przy pomocy mokutil --list-new
.
Dodawanie certyfikatów w MokManager jest stosunkowo proste:
Sprawdzamy dane certyfikatu Debiana:
Podobnie weryfikujemy dane certyfikatu Canonical:
Jeśli dane się zgadzają, to przechodzimy dalej:
Potwierdzamy dodanie kluczy i wpisujemy hasło, które ustawiliśmy wcześniej:
Jeśli proces zakończył się bez żadnych błędów, to restartujemy maszynę:
Po ponownym uruchomieniu komputera, klucze powinny zostać z powodzeniem dodane, co możemy podejrzeć
w mokutil
:
# mokutil --list-enrolled
[key 1]
SHA1 Fingerprint: 53:61:0c:f8:1f:bd:7e:0c:eb:67:91:3c:9e:f3:e7:94:a9:63:3e:cb
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
ed:54:a1:d5:af:87:48:94:8d:9f:89:32:ee:9c:7c:34
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN=Debian Secure Boot CA
Validity
Not Before: Aug 16 18:09:18 2016 GMT
Not After : Aug 9 18:09:18 2046 GMT
Subject: CN=Debian Secure Boot CA
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
Modulus:
00:9d:95:d4:8b:9b:da:10:ac:2e:ca:82:37:c1:a4:
cb:4a:c3:1b:42:93:c2:7a:29:d3:6e:dd:64:af:80:
af:ea:66:a2:1b:61:9c:83:0c:c5:6b:b9:35:25:ff:
c5:fb:e8:29:43:de:ce:4b:3d:c6:12:4d:b1:ef:26:
43:95:68:cd:04:11:fe:c2:24:9b:de:14:d8:86:51:
e8:38:43:bd:b1:9a:15:e5:08:6b:f8:54:50:8b:b3:
4b:5f:fc:14:e4:35:50:7c:0b:b1:e2:03:84:a8:36:
48:e4:80:e8:ea:9f:fa:bf:c5:18:7b:5e:ce:1c:be:
2c:80:78:49:35:15:c0:21:cf:ef:66:d5:8a:96:08:
2b:66:2f:48:17:b1:e7:ec:82:8f:07:e6:ca:e0:5f:
71:24:39:50:0a:8e:d1:72:28:50:a5:9d:21:f4:e3:
61:ba:09:03:66:c8:df:4e:26:36:0b:15:0f:63:1f:
2b:af:ab:c4:28:a2:56:64:85:8d:a6:55:41:ae:3c:
88:95:dd:d0:6d:d9:29:db:d8:c4:68:b5:fc:f4:57:
89:6b:14:db:e0:ef:ee:40:0d:62:1f:ea:58:d4:a3:
d8:ba:03:a6:97:2e:c5:6b:13:a4:91:77:a6:b5:ad:
23:a7:eb:0a:49:14:46:7c:76:e9:9e:32:b4:89:af:
57:79
Exponent: 65537 (0x10001)
X509v3 extensions:
Authority Information Access:
CA Issuers - URI:https://dsa.debian.org/secure-boot-ca
X509v3 Authority Key Identifier:
keyid:6C:CE:CE:7E:4C:6C:0D:1F:61:49:F3:DD:27:DF:CC:5C:BB:41:9E:A1
Netscape Cert Type: critical
SSL Client, SSL Server, S/MIME, Object Signing, SSL CA, S/MIME CA, Object Signing CA
X509v3 Extended Key Usage:
Code Signing
X509v3 Key Usage: critical
Digital Signature, Certificate Sign, CRL Sign
X509v3 Basic Constraints: critical
CA:TRUE
X509v3 Subject Key Identifier:
6C:CE:CE:7E:4C:6C:0D:1F:61:49:F3:DD:27:DF:CC:5C:BB:41:9E:A1
Signature Algorithm: sha256WithRSAEncryption
77:96:3e:47:c9:ce:09:cf:8b:89:ce:59:ed:26:0e:26:0b:b9:
ad:a9:2b:bd:a1:eb:88:79:02:ff:31:de:fe:f5:6a:07:ef:61:
13:11:70:1e:bf:9c:4e:66:6c:e1:62:12:97:01:57:65:47:dd:
4a:c6:f7:f4:de:a8:f1:13:62:cc:83:57:ac:3c:a6:91:15:af:
55:26:72:69:2e:14:cd:dd:4d:b3:d1:60:24:2d:32:4f:19:6c:
11:5e:f2:a3:f2:a1:5f:62:0f:30:ae:ad:f1:48:66:64:7d:36:
44:0d:06:34:3d:2e:af:8e:9d:c3:ad:c2:91:d8:37:e0:ee:7a:
5f:82:3b:67:8e:00:8a:c4:a4:df:35:16:c2:72:2b:4c:51:d7:
93:93:9e:ba:08:0d:59:97:f2:e2:29:a0:44:4d:ea:ee:f8:3e:
02:60:ca:15:cf:4e:9a:25:91:84:3f:b7:5a:c7:ee:bc:6b:80:
a3:d9:fd:b2:6d:7a:1e:63:14:eb:ef:f1:b0:40:25:d5:e8:0e:
81:eb:6b:f7:cb:ff:e5:21:00:22:2c:2e:9a:35:60:12:4b:5b:
5f:38:46:84:0c:06:9c:cf:72:93:62:18:ee:5c:98:d6:b3:7d:
06:25:39:95:df:4e:60:76:b0:06:7b:08:b0:6e:e3:64:9f:21:
56:ad:39:0f
[key 2]
SHA1 Fingerprint: 76:a0:92:06:58:00:bf:37:69:01:c3:72:cd:55:a9:0e:1f:de:d2:e0
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
b9:41:24:a0:18:2c:92:67
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=GB, ST=Isle of Man, L=Douglas, O=Canonical Ltd., CN=Canonical Ltd. Master Certificate Authority
Validity
Not Before: Apr 12 11:12:51 2012 GMT
Not After : Apr 11 11:12:51 2042 GMT
Subject: C=GB, ST=Isle of Man, L=Douglas, O=Canonical Ltd., CN=Canonical Ltd. Master Certificate Authority
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
Modulus:
00:bf:5b:3a:16:74:ee:21:5d:ae:61:ed:9d:56:ac:
bd:de:de:72:f3:dd:7e:2d:4c:62:0f:ac:c0:6d:48:
08:11:cf:8d:8b:fb:61:1f:27:cc:11:6e:d9:55:3d:
39:54:eb:40:3b:b1:bb:e2:85:34:79:ca:f7:7b:bf:
ba:7a:c8:10:2d:19:7d:ad:59:cf:a6:d4:e9:4e:0f:
da:ae:52:ea:4c:9e:90:ce:c6:99:0d:4e:67:65:78:
5d:f9:d1:d5:38:4a:4a:7a:8f:93:9c:7f:1a:a3:85:
db:ce:fa:8b:f7:c2:a2:21:2d:9b:54:41:35:10:57:
13:8d:6c:bc:29:06:50:4a:7e:ea:99:a9:68:a7:3b:
c7:07:1b:32:9e:a0:19:87:0e:79:bb:68:99:2d:7e:
93:52:e5:f6:eb:c9:9b:f9:2b:ed:b8:68:49:bc:d9:
95:50:40:5b:c5:b2:71:aa:eb:5c:57:de:71:f9:40:
0a:dd:5b:ac:1e:84:2d:50:1a:52:d6:e1:f3:6b:6e:
90:64:4f:5b:b4:eb:20:e4:61:10:da:5a:f0:ea:e4:
42:d7:01:c4:fe:21:1f:d9:b9:c0:54:95:42:81:52:
72:1f:49:64:7a:c8:6c:24:f1:08:70:0b:4d:a5:a0:
32:d1:a0:1c:57:a8:4d:e3:af:a5:8e:05:05:3e:10:
43:a1
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
AD:91:99:0B:C2:2A:B1:F5:17:04:8C:23:B6:65:5A:26:8E:34:5A:63
X509v3 Authority Key Identifier:
keyid:AD:91:99:0B:C2:2A:B1:F5:17:04:8C:23:B6:65:5A:26:8E:34:5A:63
X509v3 Basic Constraints: critical
CA:TRUE
X509v3 Key Usage:
Digital Signature, Certificate Sign, CRL Sign
X509v3 CRL Distribution Points:
Full Name:
URI:http://www.canonical.com/secure-boot-master-ca.crl
Signature Algorithm: sha256WithRSAEncryption
3f:7d:f6:76:a5:b3:83:b4:2b:7a:d0:6d:52:1a:03:83:c4:12:
a7:50:9c:47:92:cc:c0:94:77:82:d2:ae:57:b3:99:04:f5:32:
3a:c6:55:1d:07:db:12:a9:56:fa:d8:d4:76:20:eb:e4:c3:51:
db:9a:5c:9c:92:3f:18:73:da:94:6a:a1:99:38:8c:a4:88:6d:
c1:fc:39:71:d0:74:76:16:03:3e:56:23:35:d5:55:47:5b:1a:
1d:41:c2:d3:12:4c:dc:ff:ae:0a:92:9c:62:0a:17:01:9c:73:
e0:5e:b1:fd:bc:d6:b5:19:11:7a:7e:cd:3e:03:7e:66:db:5b:
a8:c9:39:48:51:ff:53:e1:9c:31:53:91:1b:3b:10:75:03:17:
ba:e6:81:02:80:94:70:4c:46:b7:94:b0:3d:15:cd:1f:8e:02:
e0:68:02:8f:fb:f9:47:1d:7d:a2:01:c6:07:51:c4:9a:cc:ed:
dd:cf:a3:5d:ed:92:bb:be:d1:fd:e6:ec:1f:33:51:73:04:be:
3c:72:b0:7d:08:f8:01:ff:98:7d:cb:9c:e0:69:39:77:25:47:
71:88:b1:8d:27:a5:2e:a8:f7:3f:5f:80:69:97:3e:a9:f4:99:
14:db:ce:03:0e:0b:66:c4:1c:6d:bd:b8:27:77:c1:42:94:bd:
fc:6a:0a:bc
Co ciekawe, na tej powyższej liście były trzy klucze, w tym jeden zdublowany. Prawdopodobnie ten dodatkowy wpis wziął się z zaszytego w shim certyfikatu dystrybucji Debiana. Wychodzi na to, że nie trzeba go ręcznie dodawać (wystarczyłoby dodać certyfikat Canonical'a, a certyfikat Debiana by wskoczył automatycznie).
Jeśli teraz podejrzymy keyring kernela, to zobaczymy tam dwa dodatkowe klucze, z racji, że w tym keyring'u mogą się znajdować klucze wbudowane w shim oraz klucze, które są przechowywane w MOK:
# cat /proc/keys | grep -i asym
...
15c78670 I------ 1 perm 1f010000 0 0 asymmetri Canonical Ltd. Master Certificate Authority: ad91990bc22ab1f517048c23b6655a268e345a63: X509.rsa 8e345a63 []
...
3eee887b I------ 1 perm 1f010000 0 0 asymmetri Debian Secure Boot CA: 6ccece7e4c6c0d1f6149f3dd27dfcc5cbb419ea1: X509.rsa bb419ea1 []
Te klucze dostarczane przez shim i MOK będą jedynie dostępne, gdy shim będzie pośredniczył w
procesie startu systemu. W moim przypadku przed implementacją Secure Boot był wykorzystywany
menadżer rozruchu rEFInd. Jeśli odpalę system bezpośrednio z menu wyboru rEFInd'a, to te powyższe
klucze nie zostaną dodane. Dlatego też jeśli zamierzamy z nich korzystać, to trzeba zainstalować
rEFInd ze wsparciem dla shim (była o tym mowa wcześniej) i zarejestrować wpis z shim w firmware
EFI/UEFI przez efibootmgr
, przykładowo:
# efibootmgr --create --disk /dev/sda --part 3 --label "SHIM" --loader '\EFI\refind\shimx64.efi' --verbose
# efibootmgr -v
BootCurrent: 0019
Timeout: 0 seconds
BootOrder: 0019,0000,0001,0002,0003,001A,0018,001B,0007,0008,0009,000A,000D,000B,000C,000E,000F,0010,0011,0012
...
Boot0018* rEFInd Boot Manager HD(3,GPT,c180ec1b-183f-43e0-853f-3c76950c3c52,0x745fc000,0x100000)/File(\EFI\refind\refind_x64.efi)
Boot0019* SHIM HD(3,GPT,c180ec1b-183f-43e0-853f-3c76950c3c52,0x745fc000,0x100000)/File(\EFI\refind\shimx64.efi)
...
Wpis z rEFInd może zostać ale trzeba będzie zmienić kolejność wpisów czytanych przy starcie systemu, tak by uruchamiany był shim.
Tak czy inaczej, płytki live mające podpisany bootloader/kernel z wykorzystaniem kluczy prywatnych od tych dodanych przez nas certyfikatów bez większego problemu będą u nas się uruchamiać. Podobnie, gdybyśmy chcieli zainstalować sobie na stałe Debiana lub Ubuntu, to one też z powodzeniem będą się uruchamiać w trybie Secure Boot.
Warto tutaj zwrócić uwagę, że może i dodaliśmy klucze do bazy MOK ale zmienna MokList
w dalszym
ciągu nie ma żadnych wpisów:
# efi-readvar -v MokList
Variable MokList has no entries
Test Ubuntu live z włączonym Secure Boot
Zrestartujmy zatem jeszcze raz komputer i podłączmy do portu USB pendrive z wgranym Ubuntu live, by przetestować czy aby na pewno ten system się nam uruchomi.
W menu rEFInd powiły się dwa dodatkowe wpisy (pierwsze dwa z lewej):
Czemu dwa? Bo na pendrive znajdują się dwa bootloader'y: grubx64.efi
(podpisany przez Canonical) i
BOOTx64.EFI
(podpisany przez Microsoft) . Możemy to zweryfikować montując obraz Ubuntu live i
listując sygnatury złożone na tych dwóch plikach przy pomocy sbverify
:
# sbverify --list '/mnt/EFI/BOOT/grubx64.efi'
signature 1
image signature issuers:
- /C=GB/ST=Isle of Man/L=Douglas/O=Canonical Ltd./CN=Canonical Ltd. Master Certificate Authority
image signature certificates:
- subject: /C=GB/ST=Isle of Man/O=Canonical Ltd./OU=Secure Boot/CN=Canonical Ltd. Secure Boot Signing
issuer: /C=GB/ST=Isle of Man/L=Douglas/O=Canonical Ltd./CN=Canonical Ltd. Master Certificate Authority
# sbverify --list '/mnt/EFI/BOOT/BOOTx64.EFI'
warning: data remaining[1171248 vs 1334816]: gaps between PE/COFF sections?
signature 1
image signature issuers:
- /C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Corporation UEFI CA 2011
image signature certificates:
- subject: /C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Windows UEFI Driver Publisher
issuer: /C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Corporation UEFI CA 2011
- subject: /C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Corporation UEFI CA 2011
issuer: /C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Corporation Third Party Marketplace Root
Z racji, że nie mamy kluczy Microsoft'u, to uruchomienie systemu live z tego drugiego wpisu się nie powiedzie. Natomiast dysponując certyfikatem od Canonical bez problemu możemy odpalić Ubuntu z pierwszego wpisu w menu rEFInd:
I jeszcze dowód, że system live działa w trybie Secure Boot:
Wsparcie Secure Boot dla windows
Tworząc własne klucze i podmieniając nimi te wbudowane w firmware EFI/UEFI nie uwzględniliśmy kluczy Microsoft'u, bo w zasadzie jak widać do tej pory wszystko działa bez większego zarzutu i dodawanie tych kluczy jest niepotrzebne. Jeśli jednak zamierzamy kiedyś w przyszłości zainstalować sobie windows'a, to przydałoby się dodać klucze MS bezpośrednio do firmware EFI/UEFI. Można oczywiście na etapie zastępowania kluczy wybrać opcję z uwzględnieniem kluczy Microsoft'u ale istnieje sposób na dodanie tych kluczy manualnie bez potrzeby przełączania systemu w tryb Setup Mode i dlatego postanowiłem go tutaj opisać.
Microsoft dysponuje dwoma kluczami. Pierwszy z nich służy do podpisywania windows'a, a drugi do podpisywania shim'a (i innych aplikacji trzecich). Najlepiej jest dodać oba, choć oczywiście możemy sobie wybrać ten, który nam jest potrzebny. W tym przypadku pobieramy oba certyfikaty i poddajemy je temu samemu procesowi, który przeprowadzaliśmy w przypadku naszych własnych kluczy.
# cd /etc/kernel_key/
Konwertujemy certyfikaty zakodowane w DER na format PEM:
# openssl x509 -inform DER -outform PEM -in MicWinProPCA2011_2011-10-19.crt -out MicWinProPCA2011_2011-10-19.crt.pem
# openssl x509 -inform DER -outform PEM -in MicCorUEFCA2011_2011-06-27.crt -out MicCorUEFCA2011_2011-06-27.crt.pem
Tworzymy listy sygnatur EFI/UEFI z ID Microsoft'u (77fa9abd-0359-4d32-bd60-28f4e78f784b):
# cert-to-efi-sig-list -g 77fa9abd-0359-4d32-bd60-28f4e78f784b MicWinProPCA2011_2011-10-19.crt.pem MS_Win_db.esl
# cert-to-efi-sig-list -g 77fa9abd-0359-4d32-bd60-28f4e78f784b MicCorUEFCA2011_2011-06-27.crt.pem MS_UEFI_db.esl
Dla uproszczenia sobie całego procesu łączymy te dwa pliki .esl
w jeden:
# cat MS_Win_db.esl MS_UEFI_db.esl > MS_db.esl
I na koniec podpisujemy naszym kluczem KEK
aktualizację dla zmiennej db
:
# sign-efi-sig-list -a -g 77fa9abd-0359-4d32-bd60-28f4e78f784b -k KEK.key -c KEK.crt db MS_db.esl add_MS_db.auth
Restartujemy maszynę i przy pomocy KeyTool dodajemy zawartość pliku add_MS_db.auth
do zmiennej
db
:
W keyring'u kernela powinny pojawić się dwie nowe pozycje:
# cat /proc/keys | grep -i asym | grep Mic
11ffee25 I------ 1 perm 1f010000 0 0 asymmetri Microsoft Windows Production PCA 2011: a92902398e16c49778cd90f99e4f9ae17c55af53: X509.rsa 7c55af53 []
35ef3b88 I------ 1 perm 1f010000 0 0 asymmetri Microsoft Corporation UEFI CA 2011: 13adbf4309bd82709c8cd54f316ed522988a1bd4: X509.rsa 988a1bd4 []
Tak samo dwie nowe pozycje powinny zostać dodane do zmiennej db
:
# efi-readvar -v db
Variable db, length 4008
db: List 0, type X509
Signature 0, size 837, owner 1091ff9c-7b84-4df6-8163-ed1b8aa05096
Subject:
CN=morfikov's kernel-signing key
Issuer:
CN=morfikov's kernel-signing key
db: List 1, type X509
Signature 0, size 1515, owner 77fa9abd-0359-4d32-bd60-28f4e78f784b
Subject:
C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Windows Production PCA 2011
Issuer:
C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Root Certificate Authority 2010
db: List 2, type X509
Signature 0, size 1572, owner 77fa9abd-0359-4d32-bd60-28f4e78f784b
Subject:
C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Corporation UEFI CA 2011
Issuer:
C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Corporation Third Party Marketplace Root
Gdy zamierzamy korzystać z kluczy MS, to przydałoby się wgrać na partycję ESP shim w wersji
podpisanej przez Microsoft (w Debianie jest to pakiet shim-signed
). Jeśli mamy wątpliwości czy
shim, który mamy na tej partycji jest podpisany kluczem MS, to zawsze możemy posłużyć się
poniższym poleceniem:
# sbverify --list /efi/EFI/refind/shimx64.efi.signed
warning: data remaining[1159744 vs 1322936]: gaps between PE/COFF sections?
signature 1
image signature issuers:
- /C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Corporation UEFI CA 2011
image signature certificates:
- subject: /C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Windows UEFI Driver Publisher
issuer: /C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Corporation UEFI CA 2011
- subject: /C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Corporation UEFI CA 2011
issuer: /C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Corporation Third Party Marketplace Root
By przetestować czy te klucze działają, możemy posłużyć się obrazem Ubuntu live i wybrać tryb fallback. Jeśli się uruchomi, to znaczy, że klucze spełniają swoją rolę.
Wyłączenie Secure Boot na poziomie shim
Jeśli Secure Boot jest z jakiegoś powodu wymagany i nasz komputer nie chce się uruchomić bez tej opcji włączonej, to zawsze możemy spróbować włączyć Secure Boot w ustawieniach firmware EFI/UEFI i wyłączyć go na poziomie shim. Ten zabieg jest bardzo prosty i w zasadzie sprowadza się do wydania w terminalu jednego polecenia:
# mokutil --disable-validation
password length: 8~16
input password:
input password again:
Teraz wystarczy zrestartować system i dokończyć proces w MokManager:
Po wyłączeniu Secure Boot w taki sposób, przy starcie systemu w lewym górnym rogu będziemy mieć
teraz informację: Booting in insecure mode
:
W logu systemowym będzie informacja, że secureboot: Secure boot disabled
. Różne narzędzia takie
jak np. bootctl
czy mokutil
będą w dalszym ciągu zwracać, że Secure Boot jest włączony:
# bootctl status | grep -i secure
...
Secure Boot: enabled
...
# mokutil --sb-state
SecureBoot enabled
Jak interpretować te powyższe sprzeczne wydawać by się było ze sobą informacje? Secure Boot jest włączony w konfiguracji EFI/UEFI i technicznie rzecz biorąc restrykcje jakie on nakłada będą w mocy pod warunkiem, że system nie zostanie uruchomiony za pośrednictwem shim'a. Jeżeli shim będzie pośredniczył w procesie startu systemu, to wtedy efekt jest podobny do wyłączenia Secure Boot w konfiguracji firmware EFI/UEFI. Niemniej jednak, jeśli nie zamierzamy korzystać z Secure Boot i opcje w firmware umożliwiają wyłączenie tego mechanizmu, to powinniśmy Secure Boot wyłączyć w konfiguracji firmware EFI/UEFI.
Hasło na EFI/UEFI
Zmiany w ustawieniach firmware EFI/UEFI, których dokonywaliśmy, może przeprowadzić praktycznie każdy, kto ma dostęp fizyczny do komputera. Dlatego też przydałoby się ustawić hasło, by czasem ktoś z marszu nie podmienił czy wgrał nam nowych kluczy, które mogą całkowicie skompromitować bezpieczeństwo jakie oferuje Secure Boot.
Podsumowanie
I tak oto dobrnęliśmy do końca. Jak można było zauważyć, mechanizm Secure Boot jest w pełni konfigurowalny, wliczając w to zastąpienie wbudowanych w firmware EFI/UEFI certyfikatów Microsoft i Lenovo naszymi własnymi. Oczywiście trochę wysiłku trzeba było włożyć w ten proces, by wypracować jakieś przyzwoite rozwiązanie, przynajmniej z mojego punktu widzenia, ale też nie wszystkie rzeczy trzeba było robić w taki sposób jak zostały przedstawione w tym artykule. Przeciętny użytkownik linux'a zadowoli się rozwiązaniem jakie oferuje shim podpisany kluczem Microsoft'u i będzie on w stanie uruchomić praktycznie wszystkie dystrybucje linux'a, nawet te, które nie wspierają Secure Boot.
Obawy co do możliwości uruchamiania linux'a na komputerach z firmware EFI/UEFI raczej nie znikną i moim zdaniem nie powinny. Prawie dekadę temu Microsoft zmienił swoje podejście w kwestii wymagań jakie się stawia certyfikowanym maszynom, które mają preinstalowany windows 8. Niemniej jednak, w przypadku komputerów z windows 10 takich obostrzeń, by użytkownik był w stanie Secure Boot wyłączyć, już nie ma. Jest za to jedynie zalecenie, by producenci sprzętu elektronicznego taką możliwość końcowemu użytkownikowi zostawili. Póki co nie słychać, aby producenci komputerów rozważali uniemożliwienie użytkownikom wyłączenia Secure Boot. Należy też pamiętać o shim i PreLoader.
Także moim zdaniem linux bez problemu da sobie radę i nie ma co winić samego mechanizmu Secure Boot za dominującą pozycję Microsoft'u i ewentualny brak wsparcia alternatywnych systemów u producentów sprzętu. Ja oczywiście zamierzam włączyć sobie Secure Boot na moim lapku ze względu na fakt, że ten mechanizm potrafi być bardzo użyteczny i poprawia znacząco ochronę systemu operacyjnego we wczesnym stadium uruchamiania komputera.