Ubuntu Weekly Recipe

第444回 Ubuntuにおけるセキュアブートの仕組み

この記事を読むのに必要な時間:およそ 11 分

UEFI変数の追加や更新

セキュアブートにはdb/dbxといったUEFI変数の内容が関わってきます。また,この変数を更新するためにはPK/KEK変数に登録した公開鍵証明書と対になる秘密鍵が必要です。言い換えると,これらの変数が簡単に変更できてしまうと,セキュアブートがセキュアではなくなってしまいます。そのためUEFI変数の属性には「書き込み時に認証が必要かどうか」のビットが存在し,そのビットが立っている場合は特定の認証処理を経て認証されないとセキュリティ違反EFI_SECURITY_VIOLATIONと判定します。

$ hd /sys/firmware/efi/efivars/PK-* | head
00000000  27 00 00 00 a1 59 c0 a5  e4 94 a7 4a 87 b5 ab 15  |'....Y.....J....|
00000010  5c 2b f0 72 75 04 00 00  00 00 00 00 59 04 00 00  |\+.ru.......Y...|
00000020  74 24 86 16 a3 fc 52 46  86 1f 93 30 80 10 25 e7  |t$....RF...0..%.|
00000030  30 82 04 45 30 82 03 2d  a0 03 02 01 02 02 09 00  |0..E0..-........|

たとえば上記のPK変数の例だと※8⁠,属性フィールドの値は「0x00000027」です。個々のビットは次のような意味をもっています※9⁠。

0010 0111 = 0x27
        ^- NVRAM領域に保存する
       ^- Boot Service後も読み込み可能にする
      ^- Boot Service後も書き込み可能にする
     ^- Hardware Error Record用の変数である
   ^- 書き込み時にカウンタベースの認証が必要
  ^- 書き込み時に時刻ベースの認証が必要
 ^- 書き込み時に既存のデータの後ろに追記する
※8
LinuxカーネルをUEFIファームウェアから起動した場合,/sys/firmware/efi/efivars/経由でUEFI変数にアクセスできます。変数名の後ろには変数ごとに決まっている「VendorGuid」が付きます。UEFI対応版のGRUB(grub-efi-ARCHパッケージ)からLinuxカーネル起動するか,UEFIファームウェアから直接Linuxカーネルを起動した場合に,カーネルは「UEFIファームウェアから起動した」と判断します。従来のBIOSファームウェア環境にUbuntuをインストールした場合は,grub-efi-ARCHパッケージではなくgrub-pcパッケージがインストールされるため,結果的に/sys/firmware/efi/efivars/は作成されません。
※9
UEFI Spec ⁠7.2 Variable Services」GetVariable()のRelated DefinitionsやSetVariable()のDescription

このうち「認証が必要」なビットが立っている変数がセキュアブートポリシーを持った変数です。カウンタベースか時刻ベースかは認証方法の違いで,現在の仕様では,認証時に更新するかどうかの判断基準としてタイムスタンプを使用する時刻ベースのほうが推奨されています※10⁠。

※10
UEFI Spec ⁠7.2.1 Using the EFI_VARIABLE_AUTHENTICATION_2 descriptor (Recommended)」

PK変数は「NVRAM領域に保存され⁠⁠,⁠Boot Service後(OSなどから)も読み書き可能で⁠⁠,⁠書き込み時に時刻ベースの認証が必要」であることがわかります。なお上記のPKの値だと,最初の4バイトが属性で,次の16バイトが鍵の種類を示すGUIDになります。さらに4バイトごとに鍵リスト全体のサイズ(0x0475⁠⁠,鍵ヘッダーのサイズ(0x0000⁠⁠,鍵のサイズ(0x0459)です。上記の例では鍵ヘッダーのサイズが0なので鍵ヘッダーはなく,次の16バイトがその鍵の所有者のGUIDとなります。そして,0x00000030番地から鍵の種類にあわせた形式(今回の例だとDERエンコードされたX.509証明書)のデータが始まります。

まずPK変数が空の状態を考えてみます。この状態はSetup Modeと呼ばれ※11⁠,セキュアブートもオフになります(UEFI変数の「SetupMode」に1が,⁠SecureBoot」に0がセットされます⁠⁠。Setup Modeの時,セキュアブートポリシーを持った変数であっても認証自体をスキップします※12⁠。よってPlatform Keyの登録(Enroll)が可能です。Platform KeyをEnrollすると自動的に「User Mode」に移行します。User Modeになると,セキュアブートポリシーを持ったUEFI変数の書き込み時に認証が行われるようになります。また,Runtime Service※13ではない時は,セキュアブートがオンになります。Runtime Serviceの場合は,次回起動時からセキュアブートが有効化されます※14⁠。

※11
UEFI Spec ⁠Figure 77. Secure Boot Modes」
※12
正確には認証処理自体は行われますが,署名の検証部分のみスキップします。
※13
UEFI Spec ⁠7 Services — Runtime Services」
※14
Ubuntuの場合は「GRUBを起動したあと」から「Linuxを起動するより前もしくは起動した後」の間で,Boot ServiceからRuntime Serviceに移行します。移行のタイミングはセキュアブートが有効かどうかとLinuxカーネルが署名済みかどうかに依存します。

一般的にセキュアブート対応のPCを購入した場合,最初からUser Modeになっています。このためdb/dbx変数を更新する場合はKEK変数の鍵に対応した秘密鍵で認証する必要がありますし,KEK変数を更新する場合はPK変数の鍵に対応した秘密鍵で認証する必要があります。この認証処理は,セキュアブートがオンかオフかに関わらず,User Modeである時は常に行われます※15⁠。

※15
UEFI 2.5からはSetup Mode/User Mode以外に「Audit Mode」「Deployed Mode」が追加されました。このモードを使うと,データセンターや政府向けマシンといった,ベンダーやOSのPKではなく独自のPKやKEK/dbなどを複数のマシンへデプロイしたい場合の作業がより簡単になります。

KEKやPKの秘密鍵を持っていない状態でdb/dbxを更新したいとなると,Setup Modeに戻って認証をスキップするしかありません。しかしSetup Modeに戻ってしまうということは,PK/KEKの値に関係なくセキュアブートポリシー変数を変更できてしまうということで,PK/KEKを利用した「Chain of trust」が途切れてしまうことになります。つまり設定済みのPKやKEKが信頼できないということです。そこでPlatform Keyを削除することで,Setup Modeに戻る仕組みになっています。Platform Keyを削除する方法は次の2つです※16⁠。

  • PK変数に空のデータを書き込む
  • プラットフォーム固有のPK変数クリア機能を実行する
※16
UEFI Spec ⁠30.3.2 Clearing The Platform Key」

PK変数に空のデータを書き込む場合は,当然のことながら認証を行う必要があります。つまりPlatform Keyの秘密鍵が必要です。よって一般的な利用者にとって,後者が唯一の方法になります。大抵のUEFIファームウェアであれば,Platform Keyを削除する仕組みが備わっていることでしょう。ものによっては「Setup ModeにするUI」によって,Platform Keyを削除するかもしれません。たとえばOVMFの場合は「Secure Boot Configuration」「Customize Secure Boot」「Customized」にした上で,⁠Custom Secure Boot Options」「PK Options」から「Delete Pk」を選択します※17⁠。

※17
OVMFにはありませんが,ベンダーのファームウェアによっては,ベンダー固有の鍵にリカバリーする機能も存在します。通常のPCのPK変数を変更する場合は,まずリカバリーできるかどうかという点を確認しておきましょう。

このようにセキュアブートに関するUEFI変数の更新には,さまざまな手続きが必要なのです。

著者プロフィール

柴田充也(しばたみつや)

Ubuntu Japanese Team Member株式会社 創夢所属。数年前にLaunchpad上でStellariumの翻訳をしたことがきっかけで,Ubuntuの翻訳にも関わるようになりました。