SELinux の実装

SELinux は、デフォルト拒否に設定されています。つまり、カーネルにフックを持つすべてのアクセスで、ポリシーによる明示的な許可が必要になります。これは、ポリシー ファイルが、ルール、タイプ、クラス、権限などに関する多量の情報で構成されることを意味します。このドキュメントでは SELinux の詳細は扱っていませんが、新しい Android デバイスを構築するには、ポリシールールの記述方法を理解することが今後は不可欠になります。SELinux については、すでに多くの情報が公開されています。推奨されるリソースについては、サポート ドキュメントをご覧ください。

キーファイル

SELinux を有効にするには、最新の Android カーネルを統合して、system/sepolicy ディレクトリにあるファイルを組み込みます。コンパイルすると、それらのファイルが SELinux カーネルのセキュリティ ポリシーを構成し、アップストリームの Android オペレーティング システムに対応します。

一般的に、system/sepolicy ファイルは直接変更すべきではありません。代わりに、/device/manufacturer/device-name/sepolicy ディレクトリにある独自のデバイス固有のポリシー ファイルを追加または編集します。Android 8.0 以降では、これらのファイルを変更した場合、ベンダー ディレクトリ内のポリシーだけに影響します。Android 8.0 以降のパブリック sepolicy の分離について詳しくは、Android 8.0+ での SEPolicy のカスタマイズをご覧ください。以下のファイルは、Android のバージョンにかかわらず変更できます。

ポリシー ファイル

末尾が *.te であるファイルは、ドメインとそのラベルを定義する SELinux ポリシーのソースファイルです。/device/manufacturer/device-name/sepolicy で新しいポリシー ファイルを作成することが必要になる場合もありますが、可能な限り既存のファイルの更新を試みてください。

コンテキスト ファイル

コンテキスト ファイルでは、オブジェクトのラベルを指定します。

  • file_contexts はファイルにラベルを割り当てます。ユーザー空間のさまざまなコンポーネントで使用されます。新しいポリシーを作成した場合は、このファイルを作成または更新して、ファイルに新しいラベルを割り当てます。新しい file_contexts を適用するには、ファイルシステム イメージを再ビルドするか、再度ラベル付けするファイルで restorecon を実行します。file_contexts への変更は、アップグレード時にアップグレードの一環として、システムとユーザーデータのパーティションに自動的に適用されます。また、アップグレード時に変更を他のパーティションにも自動的に適用するには、それらのパーティションが読み取り / 書き込みでマウントされた後で restorecon_recursive 呼び出しを init.board.rc ファイルに追加します。
  • genfs_contexts は、拡張属性をサポートしていない、procvfat などのラベルをファイルシステムに割り当てます。この構成はカーネル ポリシーの一部として読み込まれますが、変更が in-core inode で有効にならない場合があります。変更を完全に適用するには、再起動またはファイル システムのマウント解除と再マウントが必要になります。context=mount オプションを使用して、特定のマウントに vfat などの特定のラベルを割り当てることもできます。
  • property_contexts は Android システム プロパティにラベルを割り当てて、どのプロセスがそれらのプロパティを設定するかを制御します。この構成は、起動時に init プロセスによって読み取られます。
  • service_contexts は Android バインダー サービスにラベルを割り当てて、どのプロセスがサービスのバインダ リファレンスを追加(登録)および検索(ルックアップ)できるかを制御します。この構成は、起動時に servicemanager プロセスによって読み取られます。
  • seapp_contexts はアプリプロセスと /data/data ディレクトリにラベルを割り当てます。この構成は、アプリを起動するたびに zygote プロセスに、また起動時に installd に読み取られます。
  • mac_permissions.xml は、署名と、必要に応じてパッケージ名に基づいて、アプリに seinfo タグを割り当てます。seinfo タグを seapp_contexts ファイル内でキーとして使用すれば、seinfo タグが付けられたすべてのアプリに特定のラベルを割り当てることができます。この構成は、起動時に system_server によって読み取られます。
  • keystore2_key_contexts は Keystore 2.0 名前空間にラベルを割り当てます。これらの名前空間は keystore2 デーモンによって適用されます。キーストアは UID / AID ベースの名前空間を提供してきました。キーストア 2.0 は、さらに sepolicy で定義された名前空間を適用します。このファイルの形式と規則の詳細については、こちらをご覧ください。

BoardConfig.mk makefile

ポリシー ファイルとコンテキスト ファイルを編集または追加したら、sepolicy サブディレクトリとそれぞれの新しいポリシー ファイルを参照するように /device/manufacturer/device-name/BoardConfig.mk makefile を更新します。 BOARD_SEPOLICY 変数について詳しくは、system/sepolicy/README ファイルをご覧ください。

BOARD_SEPOLICY_DIRS += \
        <root>/device/manufacturer/device-name/sepolicy

BOARD_SEPOLICY_UNION += \
        genfs_contexts \
        file_contexts \
        sepolicy.te

再ビルドすると、デバイスで SELinux が有効になります。これで、カスタマイズの説明に沿って Android オペレーティング システムに追加した内容に応じて SELinux ポリシーをカスタマイズするか、検証の説明に沿って既存の設定を検証できます。

新しいポリシー ファイルと BoardConfig.mk の更新を導入すると、新しいポリシー設定は最終的なカーネル ポリシー ファイルに自動的に組み込まれます。デバイスで sepolicy をビルドする方法について詳しくは、sepolicy のビルドをご覧ください。

実装

SELinux を開始するには:

  1. カーネルで SELinux を有効にします: CONFIG_SECURITY_SELINUX=y
  2. kernel_cmdline または bootconfig のパラメータを次のように変更します。
    BOARD_KERNEL_CMDLINE := androidboot.selinux=permissive
    または
    BOARD_BOOTCONFIG := androidboot.selinux=permissive
    これはデバイスのポリシー作成の初期段階でのみ使用します。ブートストラップ ポリシーを最初に作成した後、このパラメータを削除してデバイスを enforcing モードにしないと、CTS に失敗します。
  3. permissive モードでシステムを起動し、起動中にどのような拒否が発生するかを確認します。
    Ubuntu 14.04 以降:
    adb shell su -c dmesg | grep denied | audit2allow -p out/target/product/BOARD/root/sepolicy
    
    Ubuntu 12.04:
    adb pull /sys/fs/selinux/policy
    adb logcat -b all | audit2allow -p policy
    
  4. 警告の出力(例: init: Warning! Service name needs a SELinux domain defined; please fix! )を評価します。手順とツールについては検証をご覧ください。
  5. ラベル付けが必要な、デバイスとその他の新しいファイルを特定します。
  6. オブジェクトに既存のラベルまたは新しいラベルを割り当てます。*_contexts ファイルでそれまでのラベル付けの状況を確認し、ラベルの意味を理解したうえで新しいラベルを割り当てます。これにはポリシーに適合する既存のラベルを使用するのが理想ですが、新しいラベルと、そのラベルへのアクセスに関するルールが必要になる場合があります。適切なコンテキスト ファイルにラベルを追加します。
  7. 独自のセキュリティ ドメインを必要とするドメインまたはプロセスを特定します。 ドメインまたはプロセスを特定した場合は、必要に応じてまったく新しいポリシーを作成する必要があります。たとえば init から生成されたすべてのサービスには、独自のポリシーが必要です。次のコマンドにより、実行され続けているサービスを確認できます(実際には、すべてのサービスが実行され続ける必要があります)。
    adb shell su -c ps -Z | grep init
    
    adb shell su -c dmesg | grep 'avc: '
    
  8. init.device.rc を確認して、ドメインタイプがないドメインを特定します。それらに開発プロセスの早い段階でドメインを与え、init にルールを追加しないようにします。ドメインを与えないと、init のアクセスと独自のポリシーが適用されているものとの区別ができなくなります。
  9. BOARD_SEPOLICY_* 変数を使用するように BOARD_CONFIG.mk を設定します。設定方法について詳しくは、system/sepolicyREADME をご覧ください。
  10. init.device.rc と fstab.device ファイルを調べ、すべての mount の使用が、適切にラベル付けされたファイルシステムに対応しているか、または context= mount オプションが指定されているかを確認します。
  11. すべての拒否を調べ、それぞれを適切に処理できる SELinux ポリシーを作成します。カスタマイズに示す例をご覧ください。

AOSP のポリシーから開始し、それに基づいてカスタマイズを行います。ポリシー戦略といくつかの手順の詳細については、SELinux ポリシーの記述をご覧ください。

ユースケース

独自のソフトウェアと関連する SELinux ポリシーを作成する場合に考慮すべきエクスプロイトの例を示します。

Symlink - symlink はファイルとして現れるため、多くの場合ファイルとして読み取られ、不正利用される可能性があります。たとえば init など、権限のあるコンポーネントが特定のファイルの権限を変更し、それによって過度にアクセスが許可される場合があります。

攻撃者はそれらのファイルを、制御下にあるコードへの symlink に置き換えることで、任意のファイルを上書きできるようになります。しかしアプリケーションが symlink によるアクセスを行わないことがわかっていれば、それを行うことを SELinux を使用して禁止できます。

システム ファイル - システム サーバーだけが変更するべきシステム ファイルのためのクラスを考慮する必要があります。netdinitvold はルートとして実行されるため、そうしたシステム ファイルにアクセスできてしまいます。したがって、netd が侵害されればそれらのファイルも侵害され、さらにはシステム サーバー自体も侵害される可能性があります。

SELinux では、それらのファイルをシステム サーバー データファイルとして識別できます。 そのため、それらのファイルに対する読み取り / 書き込みアクセス権を持つドメインは、システム サーバーだけになります。 netd が侵害されてルートとして実行された場合でも、システム サーバー ドメインにドメインを切り替えてシステム ファイルにアクセスすることはできません。

アプリデータ - もう 1 つの例は、ルートとして実行される必要がある一方で、アプリデータにはアクセスするべきでない機能のクラスです。これは、広範なアサーションを作成できるようになるため、非常に便利です。たとえば、アプリケーション データに関連がない特定のドメインに対して、インターネットへのアクセスを禁止するといった使い方ができます。

setattr - chmodchown などのコマンドには、関連付けられているドメインで setattr を実行できるファイルを、特定しておくことができます。特定されたもの以外のファイルについては、ルートであっても、変更を禁止できます。その場合アプリケーションは、app_data_files とラベル付けされたファイルに対して chmodchown を実行できても、shell_data_files または system_data_files には実行できません。