Contents

Source Code Analysis Android SELinux Application Permission Assignment

Contents

After using the method above (well, this* above * I haven’t sent it yet) After signing my ROM with my own certificate, I encountered a strange bug. After the boot load is complete, it will display “The system interface has stopped running”, and after clicking “Close Application”, an infinite loop of error will be reported. Here, combined with the source code of Android 7.1.1, I will briefly note the solution to this strange problem caused by SELinux.

First of all, of course, you have to check the log. Fortunately, adb is turned on, and logcat can also enter. When an error is reported at the front desk, the following message pops up in the log.

06-30 08:24:21.600  3103  3103 E SELinux : seapp_context_lookup:  No match for app with uid 1001, seinfo default, name com.android.phone
06-30 08:24:21.601  3103  3103 E SELinux : selinux_android_setcontext:  Error setting context for app with uid 1001, seinfo default:privapp: Success
06-30 08:24:21.601  3103  3103 E Zygote  : selinux_android_setcontext(1001, 0, "default:privapp", "com.android.phone") failed
06-30 08:24:21.601  3103  3103 F art     : art/runtime/jni_internal.cc:492] JNI FatalError called: frameworks/base/core/jni/com_android_internal_os_Zygote.cpp:631: selinux_android_setcontext failed
06-30 08:24:21.603  1608  1652 I ActivityManager: Start proc 3103:com.android.phone/1001 for added application com.android.phone
06-30 08:24:21.620  3103  3103 F art     : art/runtime/runtime.cc:422] Runtime aborting...
06-30 08:24:21.620  3103  3103 F art     : art/runtime/runtime.cc:422] Aborting thread:
06-30 08:24:21.620  3103  3103 F art     : art/runtime/runtime.cc:422] "main" prio=5 tid=1 Native
06-30 08:24:21.620  3103  3103 F art     : art/runtime/runtime.cc:422]   | group="" sCount=0 dsCount=0 obj=0x74cc1990 self=0x7fafe96a00
...
06-30 08:24:21.620  3103  3103 F art     : art/runtime/runtime.cc:422]
06-30 08:24:21.621  3103  3103 F libc    : Fatal signal 6 (SIGABRT), code -6 in tid 3103 (main)
06-30 08:24:21.621   458   458 W         : debuggerd: handling request: pid=3103 uid=1001 gid=1001 tid=3103
06-30 08:24:21.629  1608  1652 W DropBoxManagerService: Dropping: system_server_wtf (965 > 0 bytes)
06-30 08:24:21.638  1608  1652 W ActivityManager: Process ProcessRecord{d133a66 2799:com.android.settings/1000} failed to attach
06-30 08:24:21.638  1608  1652 I ActivityManager: Killing 2799:com.android.settings/1000 (adj -10000): start timeout
06-30 08:24:21.685  3108  3108 F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
06-30 08:24:21.685  3108  3108 F DEBUG   : LineageOS Version: '14.1-20170629-UNOFFICIAL-oneplus3'
06-30 08:24:21.685  3108  3108 F DEBUG   : Build fingerprint: 'OnePlus/OnePlus3/OnePlus3:7.1.1/NMF26F/05151830:user/release-keys'
06-30 08:24:21.685  3108  3108 F DEBUG   : Revision: '0'
06-30 08:24:21.685  3108  3108 F DEBUG   : ABI: 'arm64'
06-30 08:24:21.685  3108  3108 F DEBUG   : pid: 3103, tid: 3103, name: main  >>> zygote64 <<<
06-30 08:24:21.685  3108  3108 F DEBUG   : signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
06-30 08:24:21.688  3108  3108 F DEBUG   : Abort message: 'art/runtime/jni_internal.cc:492] JNI FatalError called: frameworks/base/core/jni/com_android_internal_os_Zygote.cpp:631: selinux_android_setcontext failed'
06-30 08:24:21.689  3108  3108 F DEBUG   :     x0   0000000000000000  x1   0000000000000c1f  x2   0000000000000006  x3   0000000000000008
...
06-30 08:24:21.695  3108  3108 F DEBUG   : backtrace:
06-30 08:24:21.696  3108  3108 F DEBUG   :     #00 pc 000000000006c990  /system/lib64/libc.so (tgkill+8)
...
06-30 08:24:21.696  3108  3108 F DEBUG   :     #10 pc 000000007485fe68  /data/dalvik-cache/arm64/system@framework@boot.oat (offset 0x29af000)
06-30 08:24:21.790   458   458 W         : debuggerd: resuming target 3103
06-30 08:24:21.795   751   751 I Zygote  : Process 3103 exited due to signal (6)

At first glance, I was stunned… If the ART exploded when it was running, wouldn’t the whole system explode? However, it is obvious from the log that the error is due to the problem of SELinux. Turn it off, so the temporary solution I used these days is to plug in the computer when it is turned on, adb shell su root setenforce 0… It is also very Trouble so I’d love to get rid of it.

At first I thought that Zygote lacked the corresponding permissions, so I screened out the audit part in dmesg, and fed it to the audit2allow that came with compiling Android, and the result told me that it needed a lot——

cat ~/dmsg.log | audit2allow -p $OUT/root/sepolicy


#============= audioserver ==============
allow audioserver zygote:binder call;

#============= untrusted_app ==============
allow untrusted_app adbsecure_prop:file { getattr open };
allow untrusted_app adbtcp_prop:file { getattr open };
...

#============= zygote ==============
allow zygote ashmem_device:chr_file execute;
allow zygote audioserver:binder { call transfer };
...
allow zygote sysfs_wake_lock:file { open read write };
allow zygote system_data_file:dir { add_name remove_name write };
allow zygote system_data_file:file { create rename setattr unlink write };
allow zygote system_server:binder { call transfer };
allow zygote system_server:unix_stream_socket { read write };
allow zygote time_daemon:unix_stream_socket connectto;
allow zygote untrusted_app:binder { call transfer };
allow zygote user_profile_data_file:file { getattr lock open read write };
allow zygote wcnss_filter:unix_stream_socket connectto;
allow zygote wcnss_service_exec:file { execute execute_no_trans getattr open read };
allow zygote zygote_tmpfs:file execute;

My first reaction was that it was impossible… The official must have done it long ago with so many permissions missing! I added these extra permissions to a rule file and recompiled, which was really not satisfactory. option excluded.

So the target is focused on the seapp_context_lookup in the first three lines of the log. I guess again, because this call fails to return, Zygote encounters a fatal error and exits directly, because the owner of the third line of error information is Zygote.

06-30 08:24:21.600  3103  3103 E SELinux : seapp_context_lookup:  No match for app with uid 1001, seinfo default, name com.android.phone
06-30 08:24:21.601  3103  3103 E SELinux : selinux_android_setcontext:  Error setting context for app with uid 1001, seinfo default:privapp: Success
06-30 08:24:21.601  3103  3103 E Zygote  : selinux_android_setcontext(1001, 0, "default:privapp", "com.android.phone") failed

Paste this line of error into google and the first result is this snippet in the android source that throws the error. The source of the code below is the same website AndroidXref1, the version is 7.1.1r6. It’s just a small version of mine, no problem.

This error is thrown at /frameworks/base/core/jni/com_android_internal_os_Zygote.cpp#589.

rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str);
if (rc == -1) {
  ALOGE("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid,
        is_system_server, se_info_c_str, se_name_c_str);
  RuntimeAbort(env, __LINE__, "selinux_android_setcontext failed");
}

From the error message, we can see that the parameter se_info_c_str is set to default:privapp, and it is parsed as default when it is passed into seaapp_context_lookup. From this 2 article we can know that the context is read from the /seapp_contexts file in the Android root directory.

oneplus3:/ # cat /seapp_contexts
isSystemServer=true domain=system_server
user=system seinfo=platform domain=system_app type=system_app_data_file
user=bluetooth seinfo=platform domain=bluetooth type=bluetooth_data_file
user=nfc seinfo=platform domain=nfc type=nfc_data_file
user=radio seinfo=platform domain=radio type=radio_data_file
...

The uid correspondence of the system is in /system/core/include/private/android_filesystem_config.h#46 , it can be determined that 1001 is the radio user. The correct seinfo should be the platform to hit the corresponding record in the file. We continue to look for where seinfo is passed in.

The following list shows the call tree in reverse order, so I won’t go into details.

It stops here. Because we don’t know where this Zygote Connection comes from… After supplementing the information 3, we know that this connection is initiated by ActivityManagerService. Ahhh I really don’t want to watch it.

Another way of thinking, the privapp contained in se_info_c_str must be added by a certain function, find this string to locate a class SELinuxMMAC.java . Its annotations are also noticeable:

/**
 * Centralized access to SELinux MMAC (middleware MAC) implementation. This
 * class is responsible for loading the appropriate mac_permissions.xml file
 * as well as providing an interface for assigning seinfo values to apks.
 *
 * {@hide}
 */

That is, this class is responsible for assigning programs their seinfo values. Nice to meet you. Find the reference to the string just now (it is a static member of this class), locate a method named assignSeinfoValue .

public static void assignSeinfoValue(PackageParser.Package pkg) {
    synchronized (sPolicies) {
        for (Policy policy : sPolicies) {
            String seinfo = policy.getMatchedSeinfo(pkg);
            if (seinfo != null) {
                pkg.applicationInfo.seinfo = seinfo;
                break;
            }
        }
    }

    if (pkg.applicationInfo.isAutoPlayApp())
        pkg.applicationInfo.seinfo += AUTOPLAY_APP_STR;

    if (pkg.applicationInfo.isPrivilegedApp())
        pkg.applicationInfo.seinfo += PRIVILEGED_APP_STR;  // PRIVILEGED_APP_STR = ":privapp";

    if (DEBUG_POLICY_INSTALL) {
        Slog.i(TAG, "package (" + pkg.packageName + ") labeled with " +
                "seinfo=" + pkg.applicationInfo.seinfo);
    }
}

Gotcha. The Policy class is also in this file, so we can find it getMatchedSeinfo .

public String getMatchedSeinfo(PackageParser.Package pkg) {
    // Check for exact signature matches across all certs.
    Signature[] certs = mCerts.toArray(new Signature[0]);
    if (!Signature.areExactMatch(certs, pkg.mSignatures)) {
        return null;
    }

    // Check for inner package name matches given that the
    // signature checks already passed.
    String seinfoValue = mPkgMap.get(pkg.packageName);
    if (seinfoValue != null) {
        return seinfoValue;
    }

    // Return the global seinfo value.
    return mSeinfo;
}

It compares policy.mCerts and pkg.mSignatures members in the two arrays. If the signature matches, it takes out the corresponding seinfo value from policy.mPkgMap. If there is no mPkgMap, it returns the default seinfo that matches this policy. mPkgMap is the Context specified for individual software packages when reading SELinux policy files in XML format (including /system/etc/security/mac_permissions.xml, etc.). For example, Taobao and Alipay have their own context.

And because the default value of pkg.applicationInfo.seinfo is default ( ApplicationInfo.java#618 ), then we can infer that the permission of the application signed by the platform must be reduced to default because the elements of the two arrays of policy.mCerts and pkg.mSignatures are different. Go ahead and find out where these two arrays come from.

policy.mCerts is all signature certificates contained in each policy object, which are added to the mCerts array through PolicyBuilder.addSignature(cert); And this function is called by the function that parses the signer tag in readInstallPolicy. The file read by readInstallPolicy is /system/etc/security/mac_permissions.xml ( SELinuxMMAC.java#62 ), we get the source of the mCerts data.

The content of the pkg.mSignatures object is also read from XML, and the pkg.mSignatures object is created PackageSignatures.java#readXml method at the outermost level by Settings.java#readLPw exist PackageManagerService Called during initialization, the read file is hard-coded in the properties of the Settings object mSettingsFilename In, that is /data/system/packages.xml. Well, we also have the source of mSignatures.

Finally, to sum up, when the system initializes the instance of PackageManagerService for the first time (this Service may be started by any other Service, such as OtaDexoptService ), read packages.xml and load all applications and their signatures and other information. When running the program, ActivityManagerService tells Zygote to fork and start a program. Zygote lets SELinux read mac_permissions.xml (and /system/etc /permission/A lot of rules below) and grant the appropriate permission to run, if there is nothing wrong with the old iron, go up and do it.

Then it will be easy to fix my bug.

Find the correct program signature in mac_permissions.xml, in this case, seinfo is the signature of platform, and copy it.

<?xml version="1.0" encoding="iso-8859-1"?>
<!-- AUTOGENERATED FILE DO NOT MODIFY -->
<policy>
    <signer signature=“308203...">
        <seinfo value="platform"/>
    </signer>
    <signer signature=“308202...">
        <package name="com.eg.android.AlipayGphone">
            <seinfo value="alipay"/>
        </package>
    </signer>
    <signer signature=“308202...">
        <package name="com.taobao.taobao">
            <seinfo value="taobao"/>
        </package>
    </signer>
    <signer signature=“308204...">
        <allow-all/>
        <seinfo value="release"/>
    </signer>
    <signer signature=“308202...">
        <allow-all/>
        <seinfo value="release"/>
    </signer>
    <signer signature=“308203...">
        <package name="com.cyanogenmod.updater">
            <seinfo value="cmupdater"/>
        </package>
    </signer>
    <signer signature=“308203...>
        <package name="org.cyanogenmod.themeservice">
            <seinfo value="themeservice"/>
        </package>
    </signer>
</policy>

Find the cert we want to modify on the package com.android.phone in packages.xml. In order to save space in Android, a cert is only stored once, so like this, we may find the cert we want to replace in the information of other packages:

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<packages>
    <version sdkVersion="25" databaseVersion="3" fingerprint="OnePlus/OnePlus3/OnePlus3:7.1.1/NMF26F/05151830:user/release-keys" />
    <package name="com.android.cts.priv.ctsshim" codePath="/system/priv-app/CtsShimPrivPrebuilt” ... userId="10007" isOrphaned="true">
        <sigs count="1">
            <cert index="1" key=“308203…………" />
        </sigs>
        <proper-signing-keyset identifier="3" />
    </package>
    <package name="com.android.phone" codePath="/system/priv-app/TeleService" ... sharedUserId="1001" isOrphaned="true">
        <sigs count="1">
            <cert index="1" />
        </sigs>
        <perms>
            <item name="android.permission.SEND_RECEIVE_STK_INTENT" granted="true" flags="0" />
            ...

Finally, overwrite the key field of cert with the signature we just copied, and the problem is solved.

Just such a question? Wow. I’m so free!

References: