2015年7月25日土曜日

Android 5.1 Lollipop SELinux policy injection for system apps.

というわけで久々のエントリですが。完全に自分用。
忘れっぽい自分のために。。

AndroidもlollipopになってからSELinuxが完全にEnforceになったようで、様々なデバイスやファイルに個別の権限を設定し運用されることになりました。たとえば、よくapkのなかにarmの実行形式のバイナリをrawファイルとしておいて、Runtime.execするようなアプリがありますけど、そういったものは完全にブロックされます。アプリ内臓のファイルは実行可能には基本的にはなりません(chmod云々の問題でなくSELinuxでの制御対象)。同様にいままでアプリに許可されていたデバイスへのアクセス権限もpermission以外にSELinuxによりkernelレベルで制御されます。/dev/uinputなんかもネットではいろいろ話題になってますね。JNIからアクセスできねーwwとか。。今回は、そういったエントリです。

最近MTKベースのrootとった端末で遊んでますが、lollipopからのSELinuxでいろんな制限がでてきましたね。いままで動いていたきわどいアプリとか動かないなんてことはふつうにありますね。

自分の体験した例では、/dev/uinputへのアクセスがアプリから完全にできなくなったことで、/dev/uinputを使うようなアプリの実行ができなくなったことです。いわゆる、仮想マウスとか、仮想キーボードを作ってリモートコントロールする、VNCなんかのアプリはこの影響を受けてると思います。

簡単な解決方法があればいいのですが。。いままでのところSELinuxのポリシーをいじるしかないという結論で、その辺を調査してみたということです。

まず、/dev/uinputの状態を見てみましょう。Zオプションでタグが見れます。

ls -lZ /dev/uinput
system net_bt_stack u:object_r:uhid_device:s0 uinput

となってます、基本的にuser=system group=net_bt_stackで、uhid_deviceドメインに属してます。

デバイスがどんなタグをもつかueventd.rcをみたらわかりますが、端末によってはあとから、inir.rcなどでchownとかしてますので、多少注意が必要です。

さて、オーナーがsystemなので、きほんてきにはsystemのユーザのcontextを参照するわけですが。seapp_contextsファイルがだいたい端末のルートにあるので、参照してみます。


user=system domain=system_app type=system_data_file

という感じでsystem_appのドメインを参照するようです。さて、実際のシステムはAOSPにあるようなSELinuxのポリシー以外にも独自のポリシーを適用していることが多く、実際の端末の状態を確認しないと、どんなポリシーが対象のアクセスを制御しているのかわからないことが多いです。なのでAOSPのソースのなかのSELinuxのポリシーだけを見てもダメってことです。もちろんターゲットデバイスのソース一式持ってるなら話は別ですけど。。

実際の端末のポリシーがどんなものかを確認するには。setoolsが必要です。setoolsのAndroid版を開発してくれている方がいますので。githubから落としてndk-buildしてバイナリを作成します。

https://github.com/xmikos/setools-android

非常に使えるツールです。クローンしたら、ディレクトリのトップでndk-buildするだけで、幾つかのCPUアーキテクチャに対応したバイナリが生成されます。詳しいことはgithubのページを参照してください。

git clone https://github.com/xmikos/setools-android.git
cd setools-android
ndk-build

できあがった3つのバイナリを端末に転送してpermissionを実行可能に。

adb push sepolicy-inject /sdcard/
adb push sesearch /sdcard/
adb push seinfo /sdcard/
adb shell
su
cd /data/local/tmp/
cp /sdcard/se* .
chmod 755 ./se*
それから、現在のsystem_appのドメインに定義されているポリシーをダンプしてみます。

/data/local/tmp/sesearch --allow | grep system_app
これで、system_appに許可されているポリシーが表示されます。この中にさきほどlsで確認した。uhid_deviceのアクセス許可が記述されていなければ、アクセスは拒否されます。
あ、ポリシーファイルはルートにあるsepolicyというファイルです。setools-andoidは特に指定しなければこのルートにあるsepolicyをデフォルトで参照してくれます。

さてここからが本番、今回はsystem_appドメインにポリシー追加したいと思いますが、appdomainあたりに追加するには注意が必要です。そもそもいろんなポリシーが不許可なdomainですので。今回追加するポリシーは/dev/uinputへのアクセス許可ですので。

allow system_app uhid_device:chr_file rw_file_perms;
とい感じになります。rw_file_permsはマクロで定義されたパラメータで幾つかのシステムコールの発行が可能になります。気になる人はglobal_macrosの定義を読んでみるといいでしょう。

さてこのポリシーを実行状態の端末に追加するにはsepolicy-injectコマンドを使います。ただし、先ほどのマクロ指定はつかえませんので、ちゃんとaccess_vectorsにあるキーワードをつかって指定しなくてはなりません。rw_file_permsは展開すると。

ioctl read write getattr lock append open
という感じになります。これを指定しますが、この新しいポリシー(TE)を含むポリシーファイルを作成して、それからそれを適用するという感じでやってみます。


cd /data/local/tmp
./sepolicy-inject -s system_app -t uhid_device -c chr_file  ¥
    -p ioctl,read,write,getattr,lock,append,open ¥
    -P /supolicy -o ./supolicy.new
カレントに supolicy.new が生成されればOKです。確認は上記のsesearchコマンドでやります。
./susearch -allow ./supolicy.new | grep system_app | grep uhid_device
追加したものが表示されればOK。次に適用です。/dev/uinputにアクセスするアプリもインストールしておいてください。
cd /
mount -o rw,remount /
cp /data/local/tmp/supolicy.new ./supolicy
sync
load_policy ./supolicy
am restart
最後にam restartすることでsystem_appドメインのアプリに対して、dex2oatなどが再度走ります。それで/data/data/あたりがリフレッシュされて利用可能になる感じ?。よくしらべてないのでカンですが。そんなイメージw

ちなみに端末を再起動してしまうと、sepolicyはもとにもどってしまうので、上記の方法で試してます。sepolicyを入れ替えるのが本来の方法でしょうけど。。ま、確認できればいいので。アプリが/dev/uinputにアクセスできれば仮想キーボードとか作成されますので。リモートからキー入力ができたりと、べんりなvncとかうごきますね。

今回の記事はかなーり適当。ソースをよんで裏取りとかしてませんので。もしやってみるという人は参考程度にね。

あ、ちなみに動かしたいアプリのAndroidManifestのpermissionとかちゃーんとしてないともちろんダメっす。

では。。