Problem z aktualizacją zmiennych PK, KEK, db i dbx via efi-updatevar

Spis treści

Jakiś czas temu opisywałem konfigurację własnych kluczy EFI/UEFI, którymi można zastąpić te wbudowane standardowo w firmware naszego komputera. W tamtym artykule napotkałem jednak dość dziwny problem, za sprawą którego nie można było zaktualizować zmiennych PK , KEK , db i dbx przy pomocy efi-updatevar z poziomu działającego linux'a. Gdy próbowało się te zmienne przepisać, dostawało się błąd typu Operation not permitted . Niby system został uruchomiony w trybie Setup Mode ale z jakiegoś powodu odmawiał on współpracy i trzeba było te zmienne aktualizować bezpośrednio z poziomu firmware EFI/UEFI, co było trochę upierdliwe. Szukając wtedy informacji na ten temat, jedyne co znalazłem, to fakt, że sporo osób ma podobny problem i najwyraźniej firmware mojego laptopa jest ździebko niedorobiony, przez co efi-updatevar nie mógł realizować swojego zdania. Rzeczywistość okazała się nieco inna, a rozwiązanie samego problemu było wręcz banalne.

Czemu efi-updatevar zwraca "Operation not permitted"

Niby własne klucze EFI/UEFI udało się ostatecznie wgrać do firmware, może nieco inną metodą i trochę na około, ale przez cały ten czas ta sytuacja nie dawała mi spokoju. Może nie szukałem aktywnie rozwiązania problem, bo w ostatecznie te klucze udało się wymienić ale nie lubię zostawić niedokończonych spraw samym sobie, bo prędzej czy później one wracają w najmniej oczekiwanym momencie. Jako, że ostatnio sporo się bawię narzędziem strace i przy okazji parę dni temu pojawił się dość poważny błąd w Grub2, który obchodzi mechanizm Secure Boot, to postanowiłem powrócić do tematu i trochę go głębiej podrążyć.

Poniżej jest kawałek logu strace z zarejestrowanym błędem:

# strace -f -o /tmp/strace.txt -s 2000 efi-updatevar -f dbx.esl dbx
...
13488 openat(AT_FDCWD, "/sys/firmware/efi/efivars/dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f", O_RDWR|O_CREAT|O_TRUNC, 0644) = -1 EPERM (Operation not permitted)
13488 write(2, "Failed to update dbx: ", 22) = 22
13488 write(2, "Operation not permitted\n", 24) = 24

Niemniej jednak, jak się podejrzy uprawnienia tego pliku /sys/firmware/efi/efivars/dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f , to mamy coś takiego:

# ls -al /sys/firmware/efi/efivars/dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f
-rw-r--r-- 1 root root 0 2020-07-30 17:42:36 /sys/firmware/efi/efivars/dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f

Więc w czym problem? Niby root ma uprawnienia do zapisu tego pliku ale mimo to operacja zapisu jest niedozwolona.

Z początku nie miałem pojęcia o co może chodzić. Góglając jednak, natrafiłem na pakiet secureboot-db, którego co prawda nie ma w Debianie ale jest za to w Ubuntu. Postanowiłem sobie ten pakiet zbudować, no i do tego celu były mi potrzebne źródła, które można było pozyskać ze wskazanego wyżej linku. Tam z kolei już był przygotowany katalog debian/ , a w nim plik secureboot-db.service , tj. usługa dla systemd. Zajrzawszy do środka, moim oczom ukazała się poniższa zawartość:

...
[Service]
...
ExecStartPre=-/usr/bin/chattr -i /sys/firmware/efi/efivars/KEK-8be4df61-93ca-11d2-aa0d-00e098032b8c
ExecStartPre=-/usr/bin/chattr -i /sys/firmware/efi/efivars/db-d719b2cb-3d3a-4596-a3bc-dad00e67656f
ExecStartPre=-/usr/bin/chattr -i /sys/firmware/efi/efivars/dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f

...

No proszę, a co tu robi chattr ? Przecie to narzędzie zmienia atrybuty pliku. No właśnie atrybuty... A ten i odpowiada za bit odporności (immutable bit) i gdy jest on ustawiony, to taki plik nie może być w żaden sposób modyfikowany, nawet przez użytkownika root.

Postanowiłem zatem sprawdzić atrybuty plików w katalogu /sys/firmware/efi/efivars/ :

# lsattr /sys/firmware/efi/efivars/*
...
----i--------------- /sys/firmware/efi/efivars/KEK-8be4df61-93ca-11d2-aa0d-00e098032b8c
----i--------------- /sys/firmware/efi/efivars/PK-8be4df61-93ca-11d2-aa0d-00e098032b8c
----i--------------- /sys/firmware/efi/efivars/db-d719b2cb-3d3a-4596-a3bc-dad00e67656f
----i--------------- /sys/firmware/efi/efivars/dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f
...

I zgodnie z moimi podejrzeniami, wszystkie te wyżej widoczne pliki (każdy odpowiada innej zmiennej EFI/UEFI) mają ustawiony bit odporności. Usunąłem zatem ten bit odporności:

# chattr -i /sys/firmware/efi/efivars/KEK-8be4df61-93ca-11d2-aa0d-00e098032b8c
# chattr -i /sys/firmware/efi/efivars/PK-8be4df61-93ca-11d2-aa0d-00e098032b8c
# chattr -i /sys/firmware/efi/efivars/db-d719b2cb-3d3a-4596-a3bc-dad00e67656f
# chattr -i /sys/firmware/efi/efivars/dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f

I co się okazało? Teraz bez większego problemu idzie te zmienne zapisać przy pomocy narzędzia efi-updatevar .

Czyszczenie zmiennych PK, KEK, db i dbx

Drugi problem, jaki napotkałem, związany był z wyczyszczeniem zmiennych PK , KEK , db i dbx . Do tej pory informacje w tych zmiennych były zastępowane w procesie ich aktualizacji. Czasami jednak przydałoby się te zmienne kompletnie wyczyścić, tak by nie zawierały żadnych informacji. Można tego dokonać przez stworzenie pustego pliku z listą sygnatur EFI/UEFI:

# > null.esl
# ls -al null.esl
-rw-r--r-- 1 root root 0 2020-07-30 18:32:47 null.esl

Tak stworzony plik trzeba podpisać w standardowy sposób kluczem PK , w którego to posiadaniu powinniśmy już być (jeśli nie mamy tego klucza, to odsyłam do artykułu podlinkowanego we wstępie):

# sign-efi-sig-list -c PK.crt -k PK.key PK null.esl PKnull.auth
Timestamp is 2020-7-30 18:35:21
Authentication Payload size 40
Signature of size 1211
Signature at: 40

# ls -al PKnull.auth
-rw------- 1 root root 1251 2020-07-30 18:35:21 PKnull.auth

Ten plik może zostać teraz wykorzystany do wyczyszczenia zmiennych PK , KEK , db i dbx . Robimy to w poniższy sposób:

# efi-updatevar -f PKnull.auth PK
# efi-updatevar -f PKnull.auth KEK
# efi-updatevar -f PKnull.auth db
# efi-updatevar -f PKnull.auth dbx

Sprawdźmy czy zadziałało:

# efi-readvar

Variable PK has no entries
Variable KEK has no entries
Variable db has no entries
Variable dbx has no entries
Variable MokList has no entries

No proszę, zmienne udało się wyczyścić bez żadnego problemu, a sam błąd Operation not permitted już się nie pojawia.

Po wyczyszczeniu zmiennej PK system automatycznie przejdzie w tryb Setup Mode , o czym możemy się przekonać wydając jedno z tych dwóch poniższych poleceń:

# mokutil --sb-state
SecureBoot disabled
Platform is in Setup Mode

# bootctl | grep Setup

   Setup Mode: setup

Możemy teraz wgrać nową zawartość do tych czterech zmiennych dokładnie w taki sam sposób jak to było robione z poziomu firmware EFI/UEFI, z tą różnicą, że przy pomocy efi-updatevar :

# efi-updatevar -f old_dbx.auth dbx
# efi-updatevar -f compound_db.auth db
# efi-updatevar -f compound_KEK.auth KEK
# efi-updatevar -f PK.auth PK

I sprawdźmy czy udało się je uzupełnić:

# 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
	...
Variable MokList has no entries

Jak tylko zapiszemy zmienną PK , to system z Setup Mode automatycznie przeskoczy do User Mode , co możemy zaobserwować w wyjściu bootctl :

# bootctl | grep Setup

   Setup Mode: user

Warto w tym miejscu zaznaczyć, że z racji podpisania pliku null.esl kluczem PK , zmiany, które ten plik wprowadza (czyści zmienne) są zawsze autoryzowane. Dlatego dobrze jest ten plik po zakończeniu prac usunąć albo chociaż trzymać go w bezpiecznym miejscu razem z samym kluczem PK , tak by nikt nam zmiennych EFI/UEFI nie przepisał bez naszej świadomej i dobrowolnej zgody.

Mikhail Morfikov avatar
Mikhail Morfikov
Po ponad 10 latach spędzonych z różnej maści linux'ami (Debian/Ubuntu, OpenWRT, Android) mogę śmiało powiedzieć, że nie ma rzeczy niemożliwych i problemów, których nie da się rozwiązać. Jedną umiejętność, którą ludzki umysł musi posiąść, by wybrnąć nawet z tej najbardziej nieprzyjemniej sytuacji, to zdolność logicznego rozumowania.