Ubuntu Weekly Recipe

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

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

UEFIの機能の一つに,検証されたブートローダーやOSのみを起動する「セキュアブート」という仕組みが存在します。今回はセキュアブートが有効化された環境について説明し,Ubuntuがどのように起動するのかを紹介します。

あなたのブートローダーとカーネルを見守るセキュアブート

「セキュアブート(Secure Boot)」はUEFIで定義されているプロトコルの一つで,UEFIファームウェアに保存された「鍵」で検証できたバイナリのみを起動する仕組みです。この仕組みを使うことで,検証されていないブートローダーやカーネル,その他のEFIアプリケーションの不用意な起動を「ある程度」防ぐことができます。検証済みのOSだけ起動するようにしておけば,UEFIファームウェアへの不正なアクセスも防ぐことができるのです。

セキュアブートが機能しているかどうかは,Ubuntuであればdmesgコマンドの内容で確認できます※1)。次のように「Secure boot enabled」と表示されていたら,有効化されています。無効化されている場合は,何も表示されません。

$ dmesg | grep Secure
[    0.000000] Secure boot enabled
※1
この出力を行うコードはUbuntuのカーネルには存在するものの,アップストリームのカーネルには存在しません。Ubuntu以外のディストリビューションでも汎用的に確認したいとなると,たとえば/sys/firmware/efi/efivars/SecureBoot*の5バイト目が1かどうかで判断するという方法があります。

セキュアブートの仕組み

Ubuntuのセキュアブート対応を解説する前に,セキュアブートの仕組みについて簡単に説明しておきましょう。今回の記事ではUEFI Specification 2.6を元に説明します。注釈にいくつかセクションタイトルを付けていますが,UEFIのバージョンによってはセクション番号が異なることもありますので,その点は注意してください。

セキュアブートを有効化すると,UEFIファームウェアはあらかじめ登録されていた「公開鍵証明書」※2やハッシュ値を用いて,実行しようとしているバイナリが正当なものであるかどうかを検証します。この時に使われる「鍵」がどこに保存されているかというと,第441回にも出てきたNVRAM領域です。さらにNVRAM変数を安全に更新するための鍵も必要です※3)。鍵は用途に応じていくつかのUEFI変数※4に保存されます。

  • Platform Key (PK)
  • Key Exchange Key (KEK)
  • db/dbx
※2
以降は単に「鍵」と記述し,対になる秘密鍵については明示的に「秘密鍵」と表現します。
※3
UEFI Spec 「30.3 Firmware/OS Key Exchange: creating trust relationships」
※4
UEFI Spec 「7.2 Variable Services」

「Platform Key」変数は,そのプラットフォーム(マシン)のオーナーの鍵を保存する変数です。この変数に鍵として保存されている証明書と対になる秘密鍵を保持しているオーナーのみが,後述のKEK変数を更新できます。市販されているPCなら大抵の場合,PK変数にはPCベンダーが作成した鍵が保存されています。当然のことながら一般ユーザーはPCベンダーの秘密鍵を持っていないはずなので,PCベンダーの秘密鍵で署名されたKEK変数を更新することはできません。KEK変数を更新したいならPK変数を削除し,独自のPlatform Keyを作成した上で,0から各種変数を設定し直す必要があります。

「Key Exchange Key」変数は,db/dbxの更新に使用する鍵を保存する変数です。複数の鍵を登録可能です。市販されているPCならたいていの場合,KEK変数にはOSベンダーが作成した鍵が保存されています。また,プラットフォームによってはPKと同じくPCベンダーが作成した鍵も一緒に保存されている場合があります。つまりdb/dbxの更新をするなら,やはりOSベンダーやPCベンダーの秘密鍵を持っている必要があります。

「db」変数と「dbx」変数はそれぞれ「署名データベース」「失効した署名データベース」であり,実行バイナリの署名を検証する際の鍵や署名そのもの,もしくは実行バイナリのハッシュを保存する変数です※5)。セキュアブートを起動するときは,基本的にこのdbやdbxの内容を参照してバイナリイメージの検証を行います。市販されているPCならOSベンダーの鍵に加えて,他にもいくつかの鍵が入っている場合もあります。

※5
プラットフォームによっては「dbr」「dbt」といった変数もあります。「dbr」はOSリカバリー時に参照する署名データベースです。「dbt」はタイムスタンプを保存する変数でUEFI 2.4で追加されました。「dbt」を使うと「特定の鍵に対してこのタイムスタンプ以降に署名されたバイナリのみ実行を不許可にする」ことが実現できます。ちなみにいずれの変数にも「PKDefault」「dbDefault」といった,ファームウェアの初期化時に設定する変数も存在します。

セキュアブートによる起動の流れ

UEFIファームウェアの実装の一つであるOVMFを参考に,セキュアブートの大まかな流れを見ていきましょう。OVMFではDxeImageVerificationHandler()において,署名の検証を行っています。

  1. 実行するバイナリから署名データを取り出す
  2. 署名データがない場合はバイナリのSHA256ハッシュを計算する
  3. dbxにハッシュもしくは署名データ,署名した人の公開鍵のいずれかが存在したら実行を拒否する
  4. dbにハッシュもしくは署名データ,署名した人の公開鍵のいずれかが存在したら実行を許可する

つまりセキュアブートが有効化された環境で起動時に参照するUEFI変数はdbxとdbのみです。この変数が正しく設定されていれば,バイナリを起動できることになります。ちなみにバイナリが未署名の場合も,そのハッシュ値が登録されていれば実行は可能です。しかしながら,バイナリの内容が変わる度にUEFI変数を更新しなくてはならないため,頻繁に更新が行われるようなOSの起動にはあまり使われません。

UEFIの実行バイナリはMicrosoft Windowsと同じPE/COFF形式です。このバイナリのPEヘッダーのOptional Data Directoryの5番目には「The Attribute Certificate Table」へのアドレスが入っています。このアドレスの先に,署名データが存在するわけです※6)。大抵の場合はバイナリの末尾です。fileコマンドを使えば,PE/COFF形式かどうかわかります※7)。PE形式ならファイルの末尾の方にあるバイナリデータの息遣いを感じて,X.509形式っぽい文字列が見えてきたなら署名済みと判断できることでしょう。

$ file /usr/lib/shim/shim.efi.signed
/usr/lib/shim/shim.efi.signed: PE32+ executable (EFI application) x86-64 (stripped to external PDB), for MS Windows
※6
UEFI Spec 「30.2.2 Embedded Signatures」
※7
PEヘッダー領域にある「Optional Header Windows-Specific Fields」のSubsystemフィールドを見ることで,どのタイプのWindows向けのバイナリかを判定できます。このタイプの中にはEFIアプリケーションやEFIドライバー,Xboxなども含まれているのです。fileコマンドではこのフィールドからバイナリが「EFI application」用であると判定しています。

著者プロフィール

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

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

コメント

コメントの記入