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.imgboot.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_contextsport_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 ビルド変数に基づきます。このプラットフォームがベンダー ポリシーを受け取ることが予期される、すべての前プラットフォーム バージョンのそれぞれに、別個のマッピング ファイルが存在します。詳しくは、互換性をご覧ください。

Android 11 以上

system_ext と product の sepolicy

Android 11 では、system_ext ポリシーと product ポリシーが追加されました。プラットフォームの sepolicy と同様に、system_ext ポリシーと product ポリシーは、パブリック ポリシーとプライベート ポリシーに分割されます。

パブリック ポリシーはベンダーにエクスポートされます。タイプと属性は安定版 API になっており、ベンダー ポリシーはパブリック ポリシーのタイプと属性を参照できます。タイプは PLATFORM_SEPOLICY_VERSION に基づいてバージョニングされ、バージョニングされたポリシーがベンダー ポリシーに含められます。元のポリシーは、system_ext パーティションと product パーティションにそれぞれ含められます。

プライベート ポリシーには、system_ext 専用および product 専用のタイプ、権限、system_ext パーティションと product パーティションの機能に必要な属性が含まれています。プライベート ポリシーはベンダーからは参照できません。つまり、これらのルールは内部的であり、変更可能です。

system_ext と product のマッピング

system_ext と product は、指定されたパブリック タイプをベンダーにエクスポートできます。ただし、互換性を維持する責任は各パートナーにあります。互換性を維持するため、パートナーは各自のマッピング ファイルを提供して、以前のバージョンのバージョニングされた属性を現在のパブリック sepolicy で使用されるコンクリート タイプにマッピングできます。

  • system_ext のマッピング ファイルをインストールするには、目的のマッピング情報を含む cil ファイルを {SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS}/compat/{ver}/{ver}.cil に配置し、system_ext_{ver}.cilPRODUCT_PACKAGES に追加します。
  • product のマッピング ファイルをインストールするには、目的のマッピング情報を含む cil ファイルを {PRODUCT_PRIVATE_SEPOLICY_DIRS}/compat/{ver}/{ver}.cil に配置し、product_{ver}.cilPRODUCT_PACKAGES に追加します。
  • Redbull デバイスの product パーティションのマッピング ファイルを追加するをご覧ください。

    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
    BOARD_ODM_SEPOLICY_DIRS(Android 9 以上) ODM の sepolicy
    SYSTEM_EXT_PUBLIC_SEPOLICY_DIRS(Android 11 以上) system_ext の sepolicy API
    SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS(Android 11 以上) system_ext 実装の詳細(ベンダーは無視することが可能)
    PRODUCT_PUBLIC_SEPOLICY_DIRS(Android 11 以上) product の sepolicy API
    PRODUCT_PRIVATE_SEPOLICY_DIRS(Android 11 以上) product 実装の詳細(ベンダーは無視することが可能)

    ビルドシステムは、このポリシーを取得して、system、system_ext、product、vendor、odm ポリシーの各コンポーネントを、対応するパーティションに生成します。手順は次のとおりです。

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

    プリコンパイル済みの SELinux ポリシー

    init が SELinux を有効にする前に、init はすべての CIL ファイルをパーティション(systemsystem_extproductvendorodm)から収集して、カーネルにロードできるバイナリ形式のポリシーにコンパイルします。コンパイルには時間がかかるため(通常は 1~2 秒)、CIL ファイルはビルド時にプリコンパイルされ、入力 CIL ファイルの SHA256 ハッシュとともに、/vendor/etc/selinux/precompiled_sepolicy または /odm/etc/selinux/precompiled_sepolicy のいずれかに配置されます。init は実行時にハッシュを比較して、いずれかのポリシー ファイルが更新されたかどうかを確認します。何も変更がなければ、init はプリコンパイル済みのポリシーを読み込みます。変更があれば、init は直ちにそのポリシーをコンパイルし、プリコンパイル済みのポリシーの代わりにそれを使用します。

    もっと詳しく説明すると、プリコンパイル済みのポリシーが使用されるのは、以下の条件がすべて満たされる場合です。ここで、{partition} はプリコンパイル済みのポリシーが存在するパーティション(vendor または odm)を表します。

    • /system/etc/selinux/plat_sepolicy_and_mapping.sha256/{partition}/etc/selinux/precompiled_sepolicy.plat_sepolicy_and_mapping.sha256 の両方が存在し、互いに等しい。
    • /system_ext/etc/selinux/system_ext_sepolicy_and_mapping.sha256/{partition}/etc/selinux/precompiled_sepolicy.system_ext_sepolicy_and_mapping.sha256 が両方とも存在しない。または、両方が存在し、互いに等しい。
    • /product/etc/selinux/product_sepolicy_and_mapping.sha256/{partition}/etc/selinux/precompiled_sepolicy.product_sepolicy_and_mapping.sha256 が両方とも存在しない。または、両方が存在し、互いに等しい。

    上記のいずれかが異なる場合、init はデバイス上のコンパイルパスにフォールバックします。詳しくは、system/core/init/selinux.cpp をご覧ください。