1. 程式人生 > >Android 使用者組許可權,SELinux心得總結

Android 使用者組許可權,SELinux心得總結

這裡要分兩個部分來說,一個是Linux許可權組的設定,一個是SELinux。

我們先不考慮SELinux。先單獨來說Linux許可權組。

因為要想對某個檔案進行操作(read,write,execute等),必須先滿足Linux許可權組的規則,然後再滿足SeLinux的allow條件,才能成功操作。

我們最好找一臺userdebug軟體的手機來進行除錯學習。我這邊是在Android O上進行示範。

Linux許可權組:

我們知道每開一個程序,都對應一個虛擬機器例項。它有一個對應uid。

adb shell

EDA51-1:/ # ps -A |grep com

system       26267   899 4378628  82036 SyS_epoll_wait 6ffcd7b420 S com.android.settings

u0_a30        1669   899 4418756 118956 SyS_epoll_wait 6ffcd7b420 S com.android.systemui

system       26333   899 4338916  63952 SyS_epoll_wait 6ffcd7b420 S com.potter.honeysigapk

nfc           2456   899 4345896  56824 SyS_epoll_wait 6ffcd7b420 S com.android.nfc

radio         3856   899 4317736  37988 SyS_epoll_wait 6ffcd7b420 S com.qualcomm.simcontacts

這裡的system,u0_a30,nfc等等就是uid。

首先,這些uid是怎麼來的,在哪定義的?

system/core/include/private/android_filesystem_config.h

#define AID_ROOT 0 /* traditional unix root user */

#define AID_SYSTEM 1000 /* system server */

#define AID_RADIO 1001           /* telephony subsystem, RIL */

#define AID_BLUETOOTH 1002       /* bluetooth subsystem */

值的關注的有

#define AID_ROOT 0

#define AID_SYSTEM 1000

#define AID_APP 10000       /* TODO: switch users over to AID_APP_START */

#define AID_APP_START 10000 /* first app user */

#define AID_APP_END 19999   /* last app user */

#define AID_USER 100000

這裡只有大寫的,你可以會問,應該是一一對應的,比如init,rc裡面也有用到root,system等,要對應小寫的才對呀?

上面那個.h檔案底部有以下注釋。在Android O上已經是自動生成的了。在早些版本是宣告的。

/*

 * android_ids has moved to pwd/grp functionality.

 * If you need to add one, the structure is now

 * auto-generated based on the AID_ constraints

 * documented at the top of this header file.

 * Also see build/tools/fs_config for more details.

 */

早些版本宣告類似:

static const struct android_id_info android_ids[] = {

    { "root",          AID_ROOT, },

    { "system",        AID_SYSTEM, },

    { "radio",         AID_RADIO, },

{ "bluetooth",     AID_BLUETOOTH, },

….

如果你和我一樣好奇,可能還會問

u0_a30        1669   899 4418756 118956 SyS_epoll_wait 6ffcd7b420 S com.android.systemui

這個u0_a30,還有其他的u0_axx都是怎麼來的,不可能全部定義吧,這個規則在哪?

上一個問題裡,我們知道其實android_ids是自動生成的。詳細步驟參考:

app的uid/100000的結果為userid,填到ux的x處。

app的uid減去10000為appid,填到axx的xx處。

例如某個app的uid是10022,經過計算,userid為10022/100000=0,appid為10022-10000=22,則那麼最終通過ps列印得到uid字串就是u0_a22

可能你還會問,Settings和SystemUI都是系統apk,為什麼一個是system,一個是u0_axx?

這個其實很簡單,Settings的AndroidManifest.xml裡面聲明瞭android:sharedUserId="android.uid.system"

我們知道sharedUserID一致,同時簽名一致的兩個apk可以共享資料。這裡多說一句,其實

SystemUI和Keyguard在android高版本里面也是同一個uid。

frameworks/base/packages/Keyguard/AndroidManifest.xml:21:    android:sharedUserId="android.uid.systemui"

frameworks/base/packages/SystemUI/AndroidManifest.xml:22:        android:sharedUserId="android.uid.systemui"

這裡我們對uid的定義,來由有了一些瞭解。下面繼續看為什麼需要uid,它有什麼用。

下面的ipsm分割槽是我司自己定製的,大家可以忽略。

EDA51-1:/ # ls -l

drwxrwx--x  47 system   system      4096 1970-01-07 12:06 data

drwxr-xr-x  14 root     root        3800 2018-11-08 15:11 dev

drwxrwx--x   4 system   system      4096 1970-01-07 12:05 ipsm

lrw-r--r--   1 root     root          21 2018-11-08 17:12 sdcard -> /storage/self/primary

drwxr-xr-x   5 root     root         100 2018-11-08 15:06 storage

dr-xr-xr-x  13 root     root           0 1970-01-07 12:12 sys

drwxr-xr-x  16 root     root        4096 2018-11-08 17:12 system

drwxr-xr-x  17 root     root        4096 1970-01-01 08:00 vendor

第一列 共有10位,從左到右邊意思如下

   1  位 表示檔案的型別

   2~4位 表示檔案所有者的許可權就是系統對該檔案所擁有的許可權

   5~7位 表示檔案所在群組的許可權

   8~10  表示檔案的其它使用者許可權

第二列 純數字 表示檔案連結個數

第三列 表示檔案所有者

第四列 表示檔案所在的群組

第五列 表示檔案的大小

第六列 表示檔案最後更新的時間

第七列 表示檔案的名稱

b就是block

d就是directory

l就是link

舉個例子,我們知道某個apk的SharedPreference資料是放在/data/data/com.xxx.xxx/files/裡的

EDA51-1:/data/data # ls -l

drwx------ 4 system    system    4096 1970-01-07 12:06 android

drwxr-x--x 4 u0_a78    u0_a78    4096 2018-11-08 15:06 com.action.ext.servicie

drwx------ 4 u0_a29    u0_a29    4096 2018-11-08 15:06 com.android.apps.tag

drwxr-x--x 4 system    system    4096 2018-11-08 15:06 com.android.backup

drwx------ 4 u0_a49    u0_a49    4096 2018-11-08 15:06 com.android.egg

drwx------ 7 u0_a50    u0_a50    4096 2018-11-08 15:07 com.android.email

drwx------ 4 u0_a13    u0_a13    4096 2018-11-08 15:06 com.android.emergency

可以看到/data/data/com.xxx.xxx的目錄,對應的apk是具有讀寫許可權的,而其他組則沒有。

我們以檔案管理器再來舉例子:

EDA51-1:/data/data # ps -A|grep file

u0_a38       26659   899 4364576 100916 SyS_epoll_wait 6ffcd7b420 S com.cyanogenmod.filemanager

從我們的經驗來看,檔案管理是可以操作外部儲存的(我們插入的sdcard以及storage/emulated/0/)

我們先去storage/emlulated/0目錄下去ls -l看一下

EDA51-1:/storage/emulated # ls -l

drwxrwx--x 16 root sdcard_rw 4096 2018-11-09 07:47 0

EDA51-1:/storage/emulated/0 # ls -l

drwxrwx--x 2 root sdcard_rw    4096 2018-11-08 15:07 Alarms

drwxrwx--x 3 root sdcard_rw    4096 2018-11-08 15:07 Android

drwxrwx--x 2 root sdcard_rw    4096 2018-11-08 15:07 DCIM

這裡可以看到,root和sdcard_rw這兩個id都可以寫storage/emulated/0這個目錄的,可是我們的檔案管理器既不是root也不是sdcard_rw啊,而是u0_a38 ,怎麼做到的?

這個問題的解答涉及到一個檔案:frameworks/base/data/etc/platform.xml

它定義了宣告哪些許可權可以得到哪些對應組的操作許可權。

    <permission name="android.permission.WRITE_MEDIA_STORAGE" >

        <group gid="media_rw" />

        <group gid="sdcard_rw" />

    </permission>

也就是說我的FileManager只需要在AndroidManifest.xml裡面聲明瞭android.permission.WRITE_MEDIA_STORAGE就ok了。

這裡又衍生出一個問題?

為什麼我們經常是用的是

<permission name="android.permission.READ_EXTERNAL_STORAGE" />

<permission name="android.permission.WRITE_EXTERNAL_STORAGE" />而不是android.permission.WRITE_MEDIA_STORAGE?

是否他們都在一個許可權組呢?我們知道動態許可權給了其中一個,整個組的許可權都會給到

frameworks\base\core\res\AndroidManifest.xml

裡面有

<!-- Allows an application to write to external storage.

{@link android.content.Context#getExternalFilesDir} and

         {@link android.content.Context#getExternalCacheDir}.

<permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"

        android:permissionGroup="android.permission-group.STORAGE"

<permission android:name="android.permission.READ_EXTERNAL_STORAGE"

        android:permissionGroup="android.permission-group.STORAGE"

<!-- @SystemApi Allows an application to write to internal media storage

         @hide  -->

<permission android:name="android.permission.WRITE_MEDIA_STORAGE"

        android:protectionLevel="signature|privileged" />

可以看到,WRITE_EXTERNAL_STORAGE和READ_EXTERNAL_STORAGE在同一個許可權組。而WRITE_MEDIA_STORAGE不在裡面。那為什麼給了WRITE_EXTERNAL_STORAGE許可權,就可以讀外部儲存呢(雖然註釋寫了可以讀外部儲存,但是我需要確鑿的證據)?

那應該有個地方宣告:

    <permission name="android.permission.WRITE_EXTERNAL_STORAGE" >

        <group gid="sdcard_rw" />

    </permission>

為什麼在framework目錄沒找到呢,frameworks/base/data/etc/platform.xml裡面也沒有啊?

frameworks/base/data/etc/platform.xml裡面是這個寫的:

    <!-- These are permissions that were mapped to gids but we need

         to keep them here until an upgrade from L to the current

         version is to be supported. These permissions are built-in

         and in L were not stored in packages.xml as a result if they

         are not defined here while parsing packages.xml we would

         ignore these permissions being granted to apps and not

         propagate the granted state. From N we are storing the

         built-in permissions in packages.xml as the saved storage

         is negligible (one tag with the permission) compared to

         the fragility as one can remove a built-in permission which

         no longer needs to be mapped to gids and break grant propagation. -->

    <permission name="android.permission.READ_EXTERNAL_STORAGE" />

    <permission name="android.permission.WRITE_EXTERNAL_STORAGE" />

好吧,這段我也沒理解太懂,我去找了下packages.xml,它生成在/data/system/packages.xml,還有個/data/system/packages.list,兩個都pull出來,也沒找到sdcard_rw.

簡單看no longer needs to be mapped to gids就可以了.這一塊具體在哪我就沒再深究了。

這一部分,意思就是說我們可以通過申請許可權來加入到一些gid裡面去。擴充套件我們apk的檔案操作範圍。

再多講一個我這邊遇到的問題

這個例子強調的對檔案的操作(如把檔案A拷貝到另一個目錄),拷貝後的檔案一定要關注它開放給其他組的許可權。

客戶有個需求,是動態替換開機動畫。他們傳入一個路徑字串。我們提供介面。

實現的方式是在BootAnimation.cpp擴充套件一個目錄,預設是/system/media/bootanimation.zip嘛。

然後通過我們定義的api(我們的一個系統apk,sharedUserID是system的)把客戶的zip檔案通過程式碼拷貝到我們的目標路徑。

當然,這中間涉及到我司的ipsm分割槽和一些Selinux對應的te檔案修改,這些不細說。

只講,程式碼拷貝過來的檔案的許可權問題,就是拷貝過來的檔案,哪些組可以對他進行rwe.

我們嘗試adb的方法(在userdebug上)

adb push bootanimation.zip /ipsm/media/honeywell

重啟手機可以看到生效額。

但是先把bootanimation.zip放在sdcard,然後用程式碼拷貝過去卻不行?

adb push後:

EDA51-1:/ipsm/media/honeywell # ls -l

-rw-rw-rw- 1 root     root     7649510 2018-08-22 23:55 bootanimation.zip

程式碼拷貝後:

EDA51-1:/ipsm/media/honeywell # ls -l

-rw------- 1 system   system   7649510 2018-11-08 14:09 bootanimation.zip

發現push的是root,而且其他使用者是可以rw的。

而程式碼拷貝是system,其他使用者是不能讀寫的。

EDA51-1:/ipsm/media/honeywell # chmod -R 777 bootanimation.zip後

EDA51-1:/ipsm/media/honeywell # ls -l

-rwxrwxrwx 1 system   system   7649510 2018-11-08 14:09 bootanimation.zip

重啟可以了。

那麼程式碼裡面如何修改許可權呢?

程式碼裡面通過Runtime去做肯定不行了,貌似android M以後的user版本就沒有su了。

不是su的話,shell裡面也是不行的,這種方式更別提程式碼裡面了。

EDA51-1:/ipsm/media/honeywell $ chmod -R 777 bootanimation.zip

chmod: chmod 'bootanimation.zip' to 100777: Operation not permitted提示沒有許可權。

可以通過以下方式:

private void changeFolderPermission(File dirFile) throws IOException {

    Set<PosixFilePermission> perms = new HashSet<PosixFilePermission>();

    perms.add(PosixFilePermission.OWNER_READ);

    perms.add(PosixFilePermission.OWNER_WRITE);

    perms.add(PosixFilePermission.OWNER_EXECUTE);

    perms.add(PosixFilePermission.GROUP_READ);

    perms.add(PosixFilePermission.GROUP_WRITE);

    perms.add(PosixFilePermission.GROUP_EXECUTE);

    perms.add(PosixFilePermission.OTHERS_READ);

    perms.add(PosixFilePermission.OTHERS_WRITE);

    perms.add(PosixFilePermission.OTHERS_EXECUTE);

    try {

        Path path = Paths.get(dirFile.getAbsolutePath());

        Files.setPosixFilePermissions(path, perms);

    } catch (Exception e) {

        //logger.log(Level.SEVERE, "Change folder " + dirFile.getAbsolutePath() + " permission failed.", e);

    }

}

這個api是AndroidO才有的。那麼Android N及以下怎麼處理呢?

我們可以在bootanimation.cpp裡面加上system("chmod 777 /ipsm/media/bootanimation.zip");當然,這樣一開始也是不會生效的,因為會因為Selinux報avc。

avc: denied { execute } for name="sh" dev="mmcblk0p32" ino=995 scontext=u:r:bootanim:s0 tcontext=u:object_r:shell_exec:s0 tclass=file permissive=0

這個需要去bootanim.te裡面去allow一下就可以了。

總結來說:

判斷一個apk是否能對某個目錄或者檔案進行對應的操作?

在不考慮SELinux的情況下,需要考慮以下幾點:

1.ps -A|grep  com.xxx.xx 獲取這個apk的uid

2.cd 到對應目錄,ls -l,看這個目錄或者檔案的對應操作許可權對那些uid開放。

3.可以通過修改sharedUserID,在AndroidManifest.xml增加對應許可權加入到對應gid組裡面去,獲取對檔案or目錄的操作許可權。

SELinux:

selinux這個大家一般都用的比較多了。網上資料也比較多。

我個人的心得是兩點:

1.userdebug軟體上可以通過adb shell 然後getenforce,setenforce(0或者1)去進行除錯

Permissive說明是關閉(寬容)的。這種模式也會列印avc denied的log,但是實際沒做限制。

Enforcing說明是開啟的。即列印log,也實際生效,預設高版本的Android Selinux是開啟的。

需要注意一點,Selinux的狀態在重啟後會重置。比如開機的時候從預設的Enforcing改為Permissive,重啟後又變成Enforcing。這一點在除錯重啟階段的問題會有點蛋疼。

2.因為修改SELinux對應的te檔案可能會導致gms測試有fail項,一般來說我們修改

device\xxx\sepolicy\common\下的te檔案,不要去動到system/sepolicy目錄下的te檔案的neverallow,就不會有什麼問題。如果allow和neverallow衝突了,編譯是會報錯的。