SELinux ポリシーのビルド

この記事では、SELinux ポリシーをビルドする方法を説明します。SELinux ポリシーは、コア AOSP ポリシー(プラットフォーム)とデバイス固有のポリシー(ベンダー)を組み合わせてビルドされます。Android 4.4 から Android 7.0 までの SELinux ポリシーのビルドフローでは、sepolicy のすべてのフラグメントをマージし、ルート ディレクトリにモノリシックなファイルを生成していました。そのため SoC ベンダーや ODM メーカーは、ポリシーが変更されるたびに、boot.img(非 A/B デバイスの場合)または system.img(A/B デバイスの場合)を変更していました。

Android 8.0 以降では、プラットフォームとベンダー ポリシーが個別にビルドされています。 SOC や OEM は、各自の側のポリシーを更新し、イメージ(vendor.imgvendor.img や boot.img など)をビルドして、それらのイメージをプラットフォームの更新とは独立して更新できます。

ただし、SELinux ポリシー ファイルはモジュール化されて /vendor パーティションに保存されているため、init プロセスでは、先にシステムとベンダー パーティションをマウントし、それらのパーティションから SELinux ファイルを読み取り、システム ディレクトリのコア SELinux ファイル(カーネルに読み込まれる前の)にマージできるようにする必要があります。

ソースファイル

SELinux をビルドするためのロジックは、次のファイルにあります。

  • external/selinux: HOST コマンドライン ユーティリティをビルドし、SELinux ポリシーと SELinux ラベルをコンパイルするために使用する、外部 SELinux プロジェクト。
    • external/selinux/libselinux: Android は、外部 libselinux プロジェクトのサブセットのみを、Android 固有のカスタマイズとともに使用します。詳細については、external/selinux/README.android をご覧ください。
    • external/selinux/libsepol:
      • chkcon: 特定のバイナリ ポリシーに対してセキュリティ コンテキストが有効であるかどうかを判断します(ホスト実行可能)。
      • libsepol: バイナリ セキュリティ ポリシーを操作するための SELinux ライブラリ(ホストの静的 / 共有ライブラリ、ターゲットの静的ライブラリ)。
    • external/selinux/checkpolicy: SELinux ポリシー コンパイラ(ホスト実行可能: checkpolicycheckmoduledispol)。libsepol に依存します。
  • system/sepolicy: コンテキスト ファイルとポリシー ファイルを含む、コアの Android SELinux ポリシー構成。主な sepolicy ビルドロジックもこれに含まれています(system/sepolicy/Android.mk)。

system/sepolicy 内のファイルについて詳しくは、SELinux の実装をご覧ください。

Android 7.0 以前

このセクションでは、Android 7.x 以前で SELinux ポリシーがどのようにビルドされているかを説明します。

SELinux ポリシーのビルド

SELinux ポリシーは、コア AOSP ポリシーとデバイス固有のカスタマイズを組み合わせて作成されています。組み合わせたポリシーは、ポリシー コンパイラと各種のチェッカーに渡されます。デバイス固有のカスタマイズは、デバイス固有の Boardconfig.mk ファイルで定義される BOARD_SEPOLICY_DIRS 変数によって行われます。このグローバルなビルド変数には、追加のポリシー ファイルを検索する順序を指定するディレクトリのリストが含まれています。

たとえば、SoC ベンダーと ODM は、それぞれに SoC 固有の設定用、デバイス固有の設定用のディレクトリを追加する場合があります。その場合、そのデバイスに生成される最終的な SELinux 構成は次のようになります。

  • BOARD_SEPOLICY_DIRS += device/SOC/common/sepolicy
  • BOARD_SEPOLICY_DIRS += device/SoC/DEVICE/sepolicy

system/sepolicyBOARD_SEPOLICY_DIRS の file_contexts ファイルのコンテンツを連結して、デバイスに file_contexts.bin が生成されます。

このイメージは、Android 7.x 用の SELinux ビルドロジックを示しています。
図 1. SELinux ビルドロジック

sepolicy ファイルは、複数のソースファイルで構成されています。

  • プレーン テキスト policy.conf は、security_classesinitial_sids*.te ファイル、genfs_contexts および port_contexts をこの順序で連結することで生成されます。
  • 各ファイル(security_classes など)のコンテンツは、system/sepolicy/BOARDS_SEPOLICY_DIRS の名前が同じファイルを連結したものです。
  • policy.conf は SELinux コンパイラに送信されて構文チェックが行われ、デバイスに sepolicy としてバイナリ形式でコンパイルされます。
    このイメージは、Android 7.x 用の SELinux ポリシー ファイルを生成するファイルを示します。
    図 2. SELinux ポリシー ファイル

SELinux ファイル

コンパイル後、7.x 以前を実行する Android デバイスには、一般的に次の SELinux 関連ファイルが含まれることになります。

  • selinux_version
  • sepolicy: ポリシー ファイル(security_classesinitial_sids*.te など)を組み合わせた後のバイナリ出力
  • file_contexts
  • property_contexts
  • seapp_contexts
  • service_contexts
  • system/etc/mac_permissions.xml

詳しくは、SELinux の実装をご覧ください。

SELinux の初期化

システムが起動したときに、SELinux は permissive モード(enforcing モードではなく)になっています。init プロセスが次のタスクを実行します。

  • /sys/fs/selinux/load を通じて ramdisk からカーネルに sepolicy ファイルを読み込みます。
  • SELinux を enforcing モードに切り替えます。
  • re-exec() を実行して、それ自体に SELinux ドメインルールを適用します。

起動時間を短縮するには、init プロセスでできるだけ早く re-exec() を実行します。

Android 8.0 以上

Android 8.0 では、互換性を維持しながら、プラットフォーム ポリシーとベンダー ポリシーを独立して更新できるようにするため、SELinux ポリシーはプラットフォーム コンポーネントとベンダー コンポーネントに分割されます。

さらにプラットフォームの sepolicy は、ベンダー ポリシーのライターに対し特定のタイプと属性をエクスポートするために、プラットフォーム プライベート ポリシーと、プラットフォーム パブリック ポリシーに分割されます。プラットフォーム パブリックのタイプと属性は、対応するプラットフォーム バージョンの安定した API として維持されることが保証されます。以前のプラットフォームのパブリック タイプまたは属性との互換性は、プラットフォーム マッピング ファイルを使用することで、複数のバージョンについて保証されます。

プラットフォーム パブリック sepolicy

プラットフォーム パブリック sepolicy には、system/sepolicy/public で定義されているものすべてが含まれています。プラットフォームは、パブリック ポリシーで定義されているタイプと属性が、そのプラットフォーム バージョンの安定した API であると想定できます。これにより、プラットフォームからエクスポートされ、ベンダー(デバイス)ポリシーのデベロッパーがデバイス固有のポリシーを作成する基準となる部分の sepolicy が形成されます。

それぞれのタイプは、PLATFORM_SEPOLICY_VERSION ビルド変数で定義され、ベンダー ファイルの基準になっているポリシーのバージョンに応じてバージョニングされます。バージョニングされたパブリック ポリシーは、ベンダー ポリシーとプラットフォーム ポリシー(元の形式で)に含まれます。したがって最終的なポリシーには、プライベート プラットフォーム ポリシー、現在のプラットフォームのパブリック sepolicy、デバイス固有のポリシー、デバイス ポリシーの基準になったプラットフォーム バージョンに対してバージョニングされたパブリック ポリシーが含まれます。

プラットフォーム プライベート sepolicy

プラットフォーム プライベート sepolicy には、/system/sepolicy/private で定義されたものがすべて含まれます。ポリシーのこの部分は、プラットフォームが機能するために必要な、プラットフォーム固有のタイプ、権限、属性を形成します。それらは vendor/device ポリシーのライターにはエクスポートされません。プラットフォーム以外のポリシー ライターは、プラットフォーム プライベート sepolicy で定義されたタイプ、属性、ルールに基づいてポリシー拡張を作成してはなりません。さらにこれらのルールは、フレームワークのみの更新で変更することが可能であり、削除される場合もあります。

プラットフォーム プライベート マッピング

プラットフォーム プライベート マッピングには、以前のプラットフォーム バージョンのプラットフォーム パブリック ポリシーで公開された属性を、現在のプラットフォーム パブリック sepolicy で使用されているコンクリート タイプにマッピングするポリシー ステートメントが含まれています。それにより、以前のプラットフォーム パブリック sepolicy バージョンのプラットフォーム パブリック属性に基づいて作成されたベンダー ポリシーが、機能を継続できるようになります。バージョニングは、そのプラットフォーム バージョンの AOSP で設定されている PLATFORM_SEPOLICY_VERSION ビルド変数に基づきます。このプラットフォームがベンダー ポリシーを受け取ることが予期される、すべての前プラットフォーム バージョンのそれぞれに、別個のマッピング ファイルが存在します。詳しくは、互換性をご覧ください。

SELinux ポリシーのビルド

Android 8.0 の SELinux ポリシーは、/system/vendor からの部分を組み合わせることで作成されます。これを適切に設定するためのロジックは、/platform/system/sepolicy/Android.mk にあります。

ポリシーは次の場所に存在します。

場所 内容
system/sepolicy/public プラットフォームの sepolicy API
system/sepolicy/private プラットフォーム実装の詳細(ベンダーは無視することが可能)
system/sepolicy/vendor ベンダーが使用できるポリシー ファイルとコンテキスト ファイル(必要な場合、ベンダーは無視することが可能)
BOARD_SEPOLICY_DIRS ベンダーの sepolicy

ビルドシステムはこのポリシーを使用して、プラットフォーム ポリシー コンポーネントとベンダー ポリシー コンポーネントを、それぞれシステム パーティションとベンダー パーティションに作成します。手順は次のようになります。

  1. ポリシーを SELinux Common Intermediate Language(CIL)形式に変換します。具体的には次のようになります。
    1. パブリック プラットフォーム ポリシー
    2. プライベート + パブリック ポリシーの組み合わせ
    3. パブリック + ベンダーおよび BOARD_SEPOLICY_DIRS ポリシー
  2. ベンダー ポリシーの一部としてパブリックから提供されたポリシーのバージョニングを行います。 これは、作成されたパブリック CIL ポリシーを使用して、パブリック + ベンダー + BOARD_SEPOLICY_DIRS ポリシーの組み合わせについて、プラットフォーム ポリシーにリンクされる属性に変換する必要がある部分を通知することによって行われます。
  3. プラットフォーム部分とベンダー部分をリンクするマッピング ファイルを作成します。 最初の段階では、パブリック ポリシーからのタイプが、ベンダー ポリシー内の対応する属性にリンクされるだけです。その後、これは将来のプラットフォーム バージョンで維持されるファイルの基盤となり、このプラットフォーム バージョンを対象とするベンダー ポリシーとの互換性が確保されます。
  4. ポリシー ファイル(デバイス上とプリコンパイルされたソリューションの両方で作成される)を組み合わせます。
    1. マッピング、プラットフォーム、ベンダーのポリシーを組み合わせます。
    2. 出力バイナリ ポリシー ファイルをコンパイルします。