Android系統(tǒng)編譯之系統(tǒng)權限

1.引言

android system/etc 中有很多配置文件,例如白名單,例如system-private 權限區(qū)別,還例如 如何配置aapp的內存。這些都是懵逼的。抱著好奇心態(tài),決定深入進去看看一般是怎么設置的。參考鏈接:https://blog.csdn.net/superkris/article/details/7709504/

2.Android的權限規(guī)則

2.1 基于UserID的進程級別的安全機制

進程有獨立的地址空間,進程與進程間默認是不能互相訪問的,Android通過為每一個apk分配唯一的linux userID來實現(xiàn),名稱為"app_"加一個數(shù)字,比如app_43不同的UserID,運行在不同的進程,所以apk之間默認便不能相互訪問。

Android提供了如下的一種機制,可以使兩個apk打破前面講的這種壁壘。 在AndroidManifest.xml中利用sharedUserId屬性給不同的package分配相同的userID,通過這樣做,兩個package可以被當做同一個程序, 系統(tǒng)會分配給兩個程序相同的UserID。當然,基于安全考慮,兩個apk需要相同的簽名,否則沒有驗證也就沒有意義了

2.2 默認apk生成的數(shù)據對外是不可見的

實現(xiàn)方法是:Android會為程序存儲的數(shù)據分配該程序的UserID。借助于Linux嚴格的文件系統(tǒng)訪問權限,便實現(xiàn)了apk之間不能相互訪問似有數(shù)據的機制。 例:我的應用創(chuàng)建的一個文件,默認權限如下,可以看到只有UserID為app_21的程序才能讀寫該文件

2.3 root權限和system權限

  • root權限

    擁有root權限的用戶,其uid=0. 擁有系統(tǒng)約定的最高權限。可以訪問絕大部分文件

  • system 權限
    1. system/app 下面的apk

    2. system/priv-app 下面的apk。

    倆者的區(qū)別在于:倆者雖然都是系統(tǒng)app,但是system/priv-app 中所具有的權限,要大于system/app

  • 特權權限

    特權應用程序是位于/system/priv-app目錄下的系統(tǒng)應用程序 。過去,設備制造商幾乎無法控制可對特權應用授予哪些簽名|特許權限。從 Android 8.0 開始,制造商必須在 /etc/permissions 目錄下的系統(tǒng)配置 XML 文件中明確授予特許權限。從 Android 9 開始,實現(xiàn)人員必須明確授予或拒絕授予所有特許權限,否則設備將無法啟動。

frameworks\base\core\res\AndroidManifest.xml 定義了各種廣播,普通權限,危險權限,特權權限:

<!-- Allows notifications to be colorized
     <p>Not for use by third-party applications. @hide -->
<permission android:name="android.permission.USE_COLORIZED_NOTIFICATIONS"
            android:protectionLevel="signature|setup" /> //setting 具備的

<!-- Allows access to keyguard secure storage.  Only allowed for system processes.
    @hide -->
<permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"
    android:protectionLevel="signature" />  //系統(tǒng)簽名

<!-- Allows managing (adding, removing) fingerprint templates. Reserved for the system. @hide -->
<permission android:name="android.permission.MANAGE_FINGERPRINT"
    android:protectionLevel="signature|privileged" />  //系統(tǒng)簽名且還需要在system/priv-app 下

<!-- Allows an app to reset fingerprint attempt counter. Reserved for the system. @hide -->
<permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT"
    android:protectionLevel="signatureorsystem " />  //系統(tǒng)簽名且還得是系統(tǒng)app(system/priv-app 或system/app)

Android 權限分成四類:

  • 普通權限(normal permission):也叫正常權限,即使擁有了該類權限,用戶的隱私數(shù)據被泄露篡改的風險也很小。例如,設置時區(qū)的權限就是正常權限。如果應用聲明其需要正常權限,系統(tǒng)會自動向應用授予該權限。

  • 敏感權限(dangerous permission):也叫危險權限,運行時權限,跟普通權限相反,一旦某個應該獲取了該類權限,用戶的隱私數(shù)據就面臨被泄露篡改的風險。比如 READ_CONTACTS 權限就屬于危險權限。如果應用聲明其需要危險權限,則用戶必須明確向應用授予該權限。

  • 簽名權限(signature permission):該類權限只對擁有相同簽名的應用開放,比如手機QQ 自定義了一個permission 且在權限標簽中加入 android:protectionLevel=”signature”,而訪問它的某個數(shù)據時,必須要擁有該權限。然后微信和 QQ 發(fā)布時采用相同的簽名,微信就可以申請訪問 QQ 中的此權限,并使用對應權限控制的數(shù)據。其他程序即使知道了這個開放數(shù)據的接口,也在 Manifest 注冊了權限,但由于應用簽名不同,還是無法訪問的對應的數(shù)據。

  • 系統(tǒng)簽名權限(signatureOrSystem permission):與 signature permission類似,但它不光要求簽名相同,還要求是同類的系統(tǒng)級應用,一般手機廠商開發(fā)的預制應用,才會用到該類權限

應用場景1:
前提:app 系統(tǒng)簽過名且放置于system/app/下
操作: 調用 Settings.System.putInt
現(xiàn)象:
Caused by: java.lang.IllegalArgumentException: You cannot keep your settings in the secure settings.
解決方案: 將app 防止于system/priv-app下
問題: 為什么放置于system/priv-app就解決了問題呢?

源碼分析:putInt方法最終會調用: enforceRestrictedSystemSettingsMutationForCallingPackage 方法

1858    private void enforceRestrictedSystemSettingsMutationForCallingPackage(int operation,
1859            String name, int userId) {
1860        // System/root/shell can mutate whatever secure settings they want.
1861        final int callingUid = Binder.getCallingUid();
1862        final int appId = UserHandle.getAppId(callingUid);
1863        if (appId == android.os.Process.SYSTEM_UID
1864                || appId == Process.SHELL_UID
1865                || appId == Process.ROOT_UID) {
1866            return;
1867        }
1868
1869        switch (operation) {
1870            case MUTATION_OPERATION_INSERT:
1871                // Insert updates.
1872            case MUTATION_OPERATION_UPDATE: {
1873                if (Settings.System.PUBLIC_SETTINGS.contains(name)) {
1874                    return;
1875                }
1876
1877                // The calling package is already verified.
1878                PackageInfo packageInfo = getCallingPackageInfoOrThrow(userId);
1879
1880                // Privileged apps can do whatever they want. 只要是特權應用,就可以直接插入數(shù)據
1881                if ((packageInfo.applicationInfo.privateFlags
1882                        & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
1883                    return;
1884                }
1885                // 檢查是否具備插入數(shù)據的資格,沒得就會拋出異常
1886                warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk(
1887                        packageInfo.applicationInfo.targetSdkVersion, name);
1888            } break;
18
1910    }
應用場景2:
前提:國內 app 采用系統(tǒng)簽名,放在/system/priv-app/下,但是沒有添加 android:sharedUserId="android.uid.system
操作:app本身是一個service,需要有讀寫權限
現(xiàn)象:app中直接進行創(chuàng)建/刪除文件,報錯,提示沒得讀寫權限
解決方案:

Y:\aosp\device\google\marlin\default-permissions.xml:

<exceptions>
    <exception package="com.verizon.mips.services">
        <permission name="android.permission.PROCESS_OUTGOING_CALLS" fixed="false"/>
        <permission name="android.permission.READ_PHONE_STATE" fixed="false"/>
        <permission name="android.permission.RECEIVE_SMS" fixed="false"/>
    </exception>
</exceptions>

default-permissions 配置文件會在第一次開機的時候加載。所以假如直接更改開發(fā)版中的default-permissions。需要格式化開發(fā)板,恢復出廠模式

應用場景3:
前提:國內 app 采用系統(tǒng)簽名,放在/system/priv-app/下,但是沒有添加 android:sharedUserId="android.uid.system且android:persistent="false"
操作: 代碼中直接使用startService啟動服務
現(xiàn)象:
Not allowed to start service Intent { flg=0x1000000 cmp=packagename/.servicename (has extras) }: app is in background uid UidRecord{52db80 u2357s1000 TRNB bg:+2m42s199ms idle procs:3 seq(0,0,0)}
問題: app系統(tǒng)簽名且置于/system/app/下 但是為啥startService 還報錯呢?且為什么加上android:persistent="true"就ok了呢
解答:

startService 啟動服務流程中,有一個方法appServicesRestrictedInBackgroundLocked

 int appServicesRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
    // Persistent app?
        if (mPackageManagerInt.isPackagePersistent(packageName)) {
            if (DEBUG_BACKGROUND_CHECK) {
                Slog.i(TAG, "App " + uid + "/" + packageName
                        + " is persistent; not restricted in background");
            }
            return ActivityManager.APP_START_MODE_NORMAL;
        }
        // Non-persistent but background whitelisted?
        if (uidOnBackgroundWhitelist(uid)) {
            if (DEBUG_BACKGROUND_CHECK) {
                Slog.i(TAG, "App " + uid + "/" + packageName
                        + " on background whitelist; not restricted in background");
            }
            return ActivityManager.APP_START_MODE_NORMAL;
        }
        // Is this app on the battery whitelist?
        if (isOnDeviceIdleWhitelistLocked(uid, /*allowExceptIdleToo=*/ false)) {
            if (DEBUG_BACKGROUND_CHECK) {
                Slog.i(TAG, "App " + uid + "/" + packageName
                        + " on idle whitelist; not restricted in background");
            }
            return ActivityManager.APP_START_MODE_NORMAL;
        }
        // None of the service-policy criteria apply, so we apply the common criteria
        return appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk);
    }
應用場景4:
前提:AndroidManifest 中添加android:sharedUserId="android.uid.system"
操作:代碼中調用安裝apk的代碼
現(xiàn)象:彈出如下所示的dialog
問題:為什么會彈出這個框?如何解決這個問題
第一個問題解答:ActivityManagerService#checkGrantUriPermissionLocked
        if ((callingAppId == SYSTEM_UID) || (callingAppId == ROOT_UID)) { 
9715            if ("com.android.settings.files".equals(grantUri.uri.getAuthority())
9716                    || SystemConfig.getInstance().getAuthoriesInPreloadedApks().contains(
9717                    grantUri.uri.getAuthority())) {
9718                // Exempted authority for
9719                // 1. cropping user photos and sharing a generated license html
9720                //    file in Settings app
9721                // 2. sharing a generated license html file in TvSettings app
9722            } else {
9723                Slog.w(TAG, "For security reasons, the system cannot issue a Uri permission"
9724                        + " grant to " + grantUri + "; use startActivityAsCaller() instead");
9725                return -1;
9726            }
9727        }

callUid為SYSTEM_UID,且不滿足倆個if 則return -1 ,于是就出現(xiàn)上述的安裝失敗情況。

解決方式有倆種:

  • 去掉android:sharedUserId
  • 更改SystemConfig.java
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容