Androidは、LinuxのUID(User Id)とGID(Group Id)を使用してアプリごとに異なるアクセス権を設定することができます。
たとえば、アプリに割り当てられているUIDはRootまたはSystemではないので、 /system
フォルダにアクセスすることはできません。
逆に、アプリがGID media_rw
を持っていればsdcardにアクセスすることができます。
この方法で、システムはアプリごとに異なる権限を付与することができ、セキュリティを維持することができます。
UID(User ID)
UIDは、ユーザーのIDです。 Androidはアプリごとに異なるUIDを割り当てます。 このようにする理由は、異なるアプリのデータにアクセスできないように作るためです。 アプリは、自分のデータフォルダのみアクセス可能であり、システムや他のアプリのフォルダにアクセスすることはできません。
たとえば、ルーティングされたデバイスのshellで以下のようにアプリのデータフォルダーの権限とUIDを見ることができます。
generic_x86:/data/data # ls -aln
# Permission [UID] [GID] [File name]
drwx------ 10 10126 10126 4096 2019-08-08 20:17 com.google.android.apps.maps
drwx------ 8 10108 10108 4096 2019-08-08 20:17 com.google.android.apps.messaging
drwx------ 8 10129 10129 4096 2019-08-08 20:17 com.google.android.youtube
左側にあるパーミッションを見ると、Userだけrwx権限があり、GroupとOthersは何の権限もありません。そしてUIDを見ると、アプリごとに異なるUIDが設定されています。 したがって、Rootを除くすべてのアプリは、お互いのファイルにアクセスすることができません。
アプリUIDチェック
adb shellで ps -ef
コマンドを入力すると、プロセスのUIDを確認することができます。
127|generic_x86:/ $ ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 09:02 ? 00:00:01 init second_stage
audioserver 1706 1 0 09:06 ? 00:00:00 android.hardware.audio@2.0-service
cameraserver 1708 1 0 09:06 ? 00:00:00 android.hardware.camera.provider@2.4-service
media 1709 1 0 09:06 ? 00:00:00 android.hardware.cas@1.1-service
system 1710 1 0 09:06 ? 00:00:00 android.hardware.configstore@1.1-service
media 1711 1 0 09:06 ? 00:00:00 android.hardware.drm@1.0-service
tombstoned 1774 1 0 09:07 ? 00:00:00 tombstoned
radio 2110 1701 0 09:12 ? 00:00:07 com.android.phone
u0_a114 2113 1701 0 09:12 ? 00:00:02 com.google.android.inputmethod.latin
u0_a93 2114 1701 0 09:12 ? 00:00:01 com.google.android.apps.nexuslauncher
上に見えるUIDの名前は、それぞれ別の数値に割り当てられています。 UIDの名前ではなく、数字で見ることを望む ps -efn
コマンドを使用します。
GID(Group ID)
GIDは、Group Idです。 Userは、1つのPrimary groupと1つ以上のSupplementary groupを持つことができます。 結論として、Userは複数のGIDを持つことができます。
アプリが特定のファイルへのアクセス権が必要な場合、その権限に関連するGIDを受けています。
例えば、もしアプリが android.permission.INTERNET
パーミッションを取得し、システムは、アプリのGIDinet
を追加します。
アプリは inet
にアクセス可能な領域にアクセスできるようになります。
GIDもUIDのように ls -al
コマンドでファイルのアクセス権を確認することができます。以下は、 /sdcard
ファイルのGID情報です。(GIDを数値として表示するにはls -aln
を使用してください)
generic_x86:/sdcard # ls -al
# Permission [UID] [GID] [File name]
drwxrwx--x 2 root sdcard_rw 4096 2019-07-19 22:04 Alarms
drwxrwx--x 3 root sdcard_rw 4096 2019-07-19 22:04 Android
drwxrwx--x 2 root sdcard_rw 4096 2019-07-19 22:04 DCIM
drwxrwx--x 2 root sdcard_rw 4096 2019-08-01 23:36 Download
drwxrwx--x 2 root sdcard_rw 4096 2019-07-19 22:04 Movies
drwxrwx--x 2 root sdcard_rw 4096 2019-07-19 22:04 Music
....
上記の結果から、左パーミッションを見ると、UserとGroup rwxを権限があります。
したがって、アプリが /sdcard
領域にアクセスするには、rootになったり、sdcard_rw
Groupに属している必要あります。
事実アプリがWRITE_EXTERNAL_STORAGE権限を受け取るsdcard_rw GIDを受けません。実際には、アプリのmountinfo情報が変更され、/sdcardにアクセスできるようになります。
そしてちなみに、AndroidのQは、Scoped Storageにより、外部リポジトリにアクセスする権限、および方法が、以前とは少し変わりました。
アプリGID確認
アプリのGIDを確認することはUIDよりも少し面倒です。まず確認したいのUIDを知る必要があります。
たとえば、LauncherアプリのGIDを確認するには、以下のようなコマンドでUIDを検索します。
generic_x86:/ $ ps -ef | grep launcher
u0_a93 2114 1701 0 22:09:12 ? 00:00:01 com.google.android.apps.nexuslauncher
そして、 /proc/[pid]/status
ファイルを確認するとGIDを知ることができます。
私の場合、PIDが2114であり、確認した結果は以下の通りです。
generic_x86:/ $ cat /proc/2114/status
Name: s.nexuslauncher
Umask: 0077
State: S (sleeping)
Tgid: 2114
Ngid: 0
Pid: 2114
PPid: 1701
TracerPid: 0
Uid: 10093 10093 10093 10093
Gid: 10093 10093 10093 10093
FDSize: 128
Groups: 9997 20093 50093
上記の結果からGidはPrimary groupに1つだけ設定されており、 Groupsは、Supplementary groupに複数のGIDが設定されています。
アプリのUIDは、誰がいつ割り当てか?
アプリのUIDは、アプリがインストールされるときに決定されます。 PackageManager
は、システムにアプリをインストールするときにアプリごとに異なるIDを付与します。
システムアプリではなく、一般的な3rd partyアプリは10000〜20000の間の数でUIDが割り当てられます。 しかしshell、root、installdなどは、固定されたシステムUIDであり、このような予約されたUIDは、10000未満の値に設定されます。
固定された(スケジュールされた)UIDは、Androidのコード android_filesystem_config.h
に定義されています。
#define AID_RADIO 1001 /* telephony subsystem, RIL */
#define AID_BLUETOOTH 1002 /* bluetooth subsystem */
#define AID_GRAPHICS 1003 /* graphics devices */
#define AID_INPUT 1004 /* input devices */
#define AID_AUDIO 1005 /* audio devices */
#define AID_CAMERA 1006 /* camera devices */
#define AID_LOG 1007 /* log devices */
#define AID_COMPASS 1008 /* compass device */
....
しかし、アプリごとにUIDが同じ場合もあります。アンドロイドにSharedUserIdという概念があり、いくつかのアプリが同じUIDを使用できるようにします。
以下のようにアプリのAndroidManifest.xmlに sharedUserId
属性にIDを指定することができます。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.codechacha.sample"
android:sharedUserId="android.uid.codechacha">
複数のアプリを上記と同じようにsharedUserIdを設定すれば、アプリが端末にインストールされたときと同じUIDを割り当てられます。 重要なのは、SharedUserIdを設定するときエプドゥル同士同じSignatureに署名する必要があります。それ以外の場合、アプリがインストールされていません。
アプリのGIDは、誰がいつ割り当てか?
固定的なGIDは、アプリのUIDと同じように設定され、アプリごとにGIDが追加されることがあります。 上で紹介したように、システムは、アプリがどのようなパーミッションを持っているときにどのようGIDを割り当てることか参考にして、テーブルがあります。
アンドロイドコード frameworks/base/data/etc/platform.xml
がそのテーブルです。
以下のようにパーミッションごとにどのようなGIDが割り当てられて書かれています。
<permission name="android.permission.INTERNET" >
<group gid="inet" />
</permission>
<permission name="android.permission.WRITE_MEDIA_STORAGE" >
<group gid="media_rw" />
</permission>
<permission name="android.permission.LOOP_RADIO" >
<group gid="loop_radio" />
</permission>
上記のパーミッションがアプリに割り当てられた場合dumpsysログでGIDが割り当てられたことを確認することができます。
adb shell dumpsys package [package name]
を入力すると、以下のようなログが出力され、ここで gids=[3003]
のように割り当てられているGIDを示しています。
GID 3003はinetです。
Packages:
Package [com.codechacha.sample] (e5cd86f):
userId=10085
pkg=Package{9a1a3a5 com.codechacha.sample}
codePath=/data/app/com.codechacha.sample-UC4OqmKDH97R9vhuk7qtDw==
......
install permissions:
android.permission.INTERNET: granted=true
User 0: ceDataInode=-4294952062 installed=true hidden=false suspended=false stopped=false notLaunched=false enabled=0 instant=false virtual=false
gids=[3003]
セキュリティの観点からUIDとGID
アンドロイドはパーミッションシステムがあります。アプリは、AndroidManifest.xmlにパーミッションを宣言することができ、ユーザーが許可すると、システム内でその権限に関連するAPIを使用することができます。
パーミッションシステムとは別に、Androidはアプリごとに異なる権限を付与するUIDとGIDを使用します。 例えば、いくつかのシステムAPIを呼び出すときに、APIは、関数を呼び出すUIDをチェックしていくつかのUIDが、そのAPIを使用できるようにします。 したがって、任意のAPIは、UIDがsystemであるプロセスのみを使用することができますものもあります。
そのほか、他の権限とセキュリティ(Security)
そのほか、Androidはselinuxを使用します。アプリがファイルにアクセスすることができるUID、GID権限をすべて持っていてもselinux権限がないアクセスがない場合があります。 selinuxファイルごとにlabelをつけ、アプリのプロセスがそのlabelのアクセス権がない場合は読み書きできません。
まとめ
LinuxののUIDとGIDがAndroid上でどのように使用されるかを知ってみました。 また、アプリがどのようにUIDとGIDを得るか、どのような方法での権限を確認することができるか調べました。 そして、セキュリティの面でどのように使用されるかを知ってみました。
参考
Related Posts
- Android - 振動、Vibrator、VibrationEffectの例
- Android - TabLayoutの実装方法(+ ViewPager2)
- Android - PackageManagerにPackage情報を取得する
- Android - ACTION_BOOT_COMPLETEDイベント受信
- Android - FusedLocationProviderClientに位置情報を取得する
- Android - GPS、Network位置情報を取得する(LocationManager)
- Android - Foreground Service実行
- Android - 時間、日付、変更イベント受信
- Android - currentTimeMillis()、elapsedRealtime()、uptimeMillis()
- Android-PowerManager WakeLock
- Android - ファイル入出力の例(Read、Write、内部、外部ストレージ)
- Android - Screen On / Offイベントの受信、状態確認
- Android - 他のアプリのServiceにバインド
- Android - Handler vs Executor
- Android - Darkmode有効にする方法
- Android - hasSystemFeature()、サポートされているFeature確認
- Android - アプリの権限を確認(Permission check)
- Android - インストールされてアプリリストをインポートする
- Android App Shortcuts実装
- Android - ContentProviderを実装、および例
- Android - AIDLを利用して、Remote Serviceの実装
- Android - Uri、Scheme、SSP(Scheme Specific Part)説明
- Android - アプリのインストール、削除、イベントダウンロード(BroadcastReceiverインテントを受け取る)
- Android - SharedPreferencesに簡単なデータを保存する方法
- Android - AlarmManagerにアラームを登録する方法、および例
- Android - Quick SettingsにCustom Tile追加する方法(kotlin)
- Android - Broadcast Receiver登録およびイベントの受信方法
- Android - Runtime permissionリクエスト方法と例(kotlin)
- Android - ネットワーク(WIFI)の接続状態を確認し、変更の検出
- Mockito - static、final methodをmockingする方法
- Andriod - カスタムパーミッションを定義する方法
- RobolectricにUnit Testを作成する(kotlin)
- Android Mockitoのテストコードを作成する(kotlin)
- Android - Handlerの使用方法、および例
- Android - IntentService使用方法
- Android - JobIntentService使用方法