SEAndroid安全机制中的进程安全上下文关联分析

发表于 5年以前  | 总阅读数:4008 次

前面一篇文章分析了文件安全上下文关联过程。但是在SEAndroid中,除了要给文件关联安全上下文外,还需要给进程关联安全上下文,因为只有当进程和文件都关联安全上下文之后,SEAndroid安全策略才能发挥作用。也就是说,当一个进程试图访问一个文件时,SEAndroid会将进程和文件的安全上下文提取出来,根据安全策略规则,决定是否允许访问。本文就详细分析SEAndroid的进程安全上下文的关联过程。

在传统的Linux系统中,每一个应用程序都对应有一个可执行文件。在这种情况下,我们就可以在安全策略中设定一个规则:当一个可执行文件加载到一个进程中执行时,该进程的安全上下文就设置为指定的值。也就是说,我们可以在安全策略中静态地为进程设置安全上下文。然而,这种进程安全上下文设置方式不适合于Android系统中的应用程序进程。从前面Android系统进程Zygote启动过程的源代码分析Android应用程序进程启动过程的源代码分析这两篇文章可以知道,Android系统中的应用程序进程都是由Zygote进程fork出来。这些应用程序进程被Zygote进程fork出来之后,不像传统Linux的应用程序进程一样,会通过exec系统调用将对应的可执行文件加载起来执行。这样就会使得Zygote进程及其创建的所有应用程序进程对应的可执行文件均为/system/bin/app_process。由于我们却需要给不同的应用程序设置不同的安全上下文,以便给它们赋予不同的安全权限,因此我们需要在应用程序进程创建出来之后动态地设置它的安全上下文。

根据上面的描述,我们就总结出,在SEAndroid安全机制中,进程的安全上下文设置分为静态和动态两种方式,如图1所示:

图1 SEAndroid安全机制中的进程安全上下文关联方式

接下来,我们就分别描述这两种进程安全上下文设置方式。

1. 为独立进程静态地设置安全上下文

Android系统的第一个进程是init,其它所有的进程都是由init进程直接或者间接fork出来的。我们在前面SEAndroid安全机制中的文件安全上下文关联分析一篇文章提到,一个新创建的文件的安全上下文在默认情况下来自于其父目录。与此类似,一个新创建的进程的安全上下文在默认情况下来自于其父进程。因此,我们就先看看系统中的第一个进程init的安全上下文是如何设置的。

查看init进程的启动脚本system/core/rootdir/init.rc,可以看到以下的内容:

on early-init
        ......

        # Set the security context for the init process.
        # This should occur before anything else (e.g. ueventd) is started.
        setcon u:r:init:s0

        ......

这段脚本的意思是init进程启动之后就马上调用函数setcon将自己的安全上下文设置为"u:r:init:s0",即将init进程的domain指定为init。

接下来我们再看看init这个domain的定义,在external/sepolicy/init.te文件中:

# init switches to init domain (via init.rc).
    type init, domain;
    permissive init;
    # init is unconfined.
    unconfined_domain(init)
    tmpfs_domain(init)
    # add a rule to handle unlabelled mounts
    allow init unlabeled:filesystem mount;

第一个type语句将domain设置为init的属性,这意味着init是用来描述进程的安全上下文的。

第二个permissive语句指定当domain为init的进程违反SEAndroid安全策略访问资源时,只进行日志输出,而不是拒绝执行。由于这里列出来的内容是来自Android 4.3的,而Android 4.3开启的是Permissive的SEAndroid模式,因此这里会看到这样的一个permissive语句。

第三个unconfined_domain语句是一个宏,定义在external/sepolicy/te_macros文件中,用来指定init是一个不受限制的domain,即它可以访问系统中的大部分资源。它的定义如下所示:

#####################################
    # unconfined_domain(domain)
    # Allow the specified domain to do anything.
    #
    define(`unconfined_domain', `
    typeattribute $1 mlstrustedsubject;
    typeattribute $1 unconfineddomain;
    ')

第四个tmpfs_domain语句也是定义在external/sepolicy/te_macros文件中的一个宏,用来指定当domain为init的进程在type为tmpfs的目录中创建文件时,将新创建的文件的type设置为init_tmpfs,并且允许domain为init的进程对它们进行读和执行。它的定义如下所示:

#####################################
    # tmpfs_domain(domain)
    # Define and allow access to a unique type for
    # this domain when creating tmpfs / shmem / ashmem files.
    define(`tmpfs_domain', `
    type $1_tmpfs, file_type;
    type_transition $1 tmpfs:file $1_tmpfs;
    # Map with PROT_EXEC.
    allow $1 $1_tmpfs:file { read execute execmod };
    ')

第5个allow语句允许domain为init的进程mount未指定安全上下文的文件系统时,将其安全上下文设置为unlabeled。

上面列出的脚本就指明了init进程的安全上下文,以及它所具有的SEAndroid权限。接下来我们就再来看看负责创建应用程序进程的Zygote进程的安全上下文的设置过程。

Zygote进程是由init进程创建的,它的启动命令定义在文件system/core/rootdir/init.rc中,如下所示:

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
        class main
        socket zygote stream 660 root system
        onrestart write /sys/android_power/request_state wake
        onrestart write /sys/power/state on
        onrestart restart media
        onrestart restart netd

这意味着Zygote进程对应的可执行文件为/system/bin/app_process。

通过检查external/sepolicy/file_contexts,我们可以发现文件/system/bin/app_process的安全上下文为"u:object_r:zygote_exec:s0",如下所示:

/system/bin/app_process u:object_r:zygote_exec:s0

也就是说,文件/system/bin/app_process的type为zygote_exec。

在external/sepolicy/zygote.te文件中,定义了一个名称为zygote的domain,以及名称为zygote_exec的type,如下所示:

# zygote
    type zygote, domain;
    type zygote_exec, exec_type, file_type;

    permissive zygote;
    init_daemon_domain(zygote)
    unconfined_domain(zygote)

第一个type语句将domain设置为zygote的属性,表明zygote是用来描述进程的安全上下文的。

第二个type语句将exec_type和file_type设置为zygote_exec的属性,表明zygote_exec是用来描述可执行文件的安全上下文的。

第三个permissive语句同样是表明当domain为zygote的进程违反SEAndroid安全策略访问资源时,只进行日志输出,而不是拒绝执行。

第四个init_daemon_domain语句是一个宏,定义在文件external/sepolicy/te_macros中,用来设置zygote这个domain的权限,它的定义如下所示:

#####################################
    # init_daemon_domain(domain)
    # Set up a transition from init to the daemon domain
    # upon executing its binary.
    define(`init_daemon_domain', `
    domain_auto_trans(init, $1_exec, $1)
    tmpfs_domain($1)
    ')

宏init_daemon_domain由另外两个宏tmpfs_domain和domain_auto_trans组成。宏tmpfs_domain的作用在前面已经分析过了,接下来我们重点关注宏domain_auto_trans的定义,也是在文件external/sepolicy/te_macros中,如下所示:

#####################################
    # domain_auto_trans(olddomain, type, newdomain)
    # Automatically transition from olddomain to newdomain
    # upon executing a file labeled with type.
    #
    define(`domain_auto_trans', `
    # Allow the necessary permissions.
    domain_trans($1,$2,$3)
    # Make the transition occur by default.
    type_transition $1 $2:process $3;
    ')

第二个type_transition语句指定当一个domain为init的进程创建一个子进程执行一个type为zygote_exec的文件时,将该子进程的domain设置为zygote,而不是继承父进程的domain。

第一个domain_trans语句是一个宏,也是定义在external/sepolicy/te_macros中,用来允许进程的domain从init修改为zygote,它的定义如下所示:

#####################################
    # domain_trans(olddomain, type, newdomain)
    # Allow a transition from olddomain to newdomain
    # upon executing a file labeled with type.
    # This only allows the transition; it does not
    # cause it to occur automatically - use domain_auto_trans
    # if that is what you want.
    #
    define(`domain_trans', `
    # Old domain may exec the file and transition to the new domain.
    allow $1 $2:file { getattr open read execute };
    allow $1 $3:process transition;
    # New domain is entered by executing the file.
    allow $3 $2:file { entrypoint read execute };
    # New domain can send SIGCHLD to its caller.
    allow $3 $1:process sigchld;
    # Enable AT_SECURE, i.e. libc secure mode.
    dontaudit $1 $3:process noatsecure;
    # XXX dontaudit candidate but requires further study.
    allow $1 $3:process { siginh rlimitinh };
    ')

其中,最重要的是以下两个allow语句:

allow $1 $3:process transition;
    allow $3 $2:file { entrypoint read execute };

第一个allow语句允许domain为init的进程将domain修改为zygote。

第二个allow语句允许type为zygote_exec的可执行文件作为进入zygote这个domain的入口点。

概括来说,在external/sepolicy/zygote.te文件中,通过init_daemon_domain指明了Zygote进程的domain为zygote。我们可以从Zygote进程的创建过程来理解这些安全策略。首先, Zygote进程是由init进程fork出来的。在fork出来的时候,Zygote进程的domain来自于父进程init的domain,即此时Zygote进程的domain为init。接下来,刚刚fork出来的Zygote进程会通过系统接口exec将文件/system/bin/app_process加载进来执行。由于上面提到的allow和type_transition规则的存在,使得文件/system/bin/app_process被exec到刚刚fork出来的Zygote进程的时候,它的domain自动地从init转换为zygote。这样我们就可以给init进程和Zygote进程设置不同的domain,以便可以给它们赋予不同的SEAndroid安全权限。

回到external/sepolicy/zygote.te文件中,最后一个unconfined_domain语句同样是将zygote这个domain设置为一个不受限的domain,以便它可以访问系统中的大部分资源。

这样,我们就以init和Zygote进程的安全上下文设置过程为例,说明了那些对应有不同可执行文件的进程的安全上下文的关联过程了。这些进程的安全上下文的设置方式与传统的Linux系统的应用程序进程的设置方式是一致的。接下来我们就再来分析Android系统的应用程序进程的安全上下文的关联过程。

2. 为应用程序进程设置安全上下文

从前面Android应用程序进程启动过程的源代码分析一篇文章可以知道,应用程序进程是由ActivityManagerService请求Zygote进程创建的。ActivityManagerService在请求Zygote进程创建应用程序进程的时候,会传递很多参数,例如应用程序在安装时分配到的uid和gid。增加了SEAndroid安全机制之后,ActivityManagerService传递给Zygote进程的参数包含了一个seinfo。这个seinfo与我们在前面SEAndroid安全机制中的文件安全上下文关联分析一文中介绍的seinfo是一样的,不过它的作用是用来设置应用程序进程的安全上下文,而不是设置应用程序数据文件的安全上下文。接下来我们就分析应用程序进程的安全上下文设置过程。

从前面Android应用程序进程启动过程的源代码分析一文的Step 1可以知道,当ActivityMangerService需要创建应用程序进程的时候,就会调用ActivityMangerService类的成员函数startProcessLocked,它的实现如下所示:

public final class ActivityManagerService  extends ActivityManagerNative
            implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
        ......

        private final void startProcessLocked(ProcessRecord app,
                String hostingType, String hostingNameStr) {
            ......

            try {
                ......

                // Start the process.  It will either succeed and return a result containing
                // the PID of the new process, or else throw a RuntimeException.
                Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread",
                        app.processName, uid, uid, gids, debugFlags, mountExternal,
                        app.info.targetSdkVersion, app.info.seinfo, null);

                ......
            } catch (RuntimeException e) {
                ......
            }
        }

        ......
    }

这个函数定义在文件frameworks/base/services/java/com/android/server/am/ActivityManagerService.java中。

参数app指向的是一个ProcessRecord对象,用来描述正在创建的应用程序进程。其中,它的成员变量info指向的是一个ApplicationInfo对象。从前面SEAndroid安全机制中的文件安全上下文关联分析一文可以知道,这个ApplicationInfo对象有一个类型为String的成员变量seinfo,是在应用程序安装的时候通过解析文件mac_permissions.xml获得的。

ActivityManagerService类的成员函数startProcessLocked通过调用Process类的静态成员函数start来创建应用程序进程,其中就包含了要创建的应用程序进程的各种参数。从前面Android应用程序进程启动过程的源代码分析一篇文章可以知道,这些参数会通过Socket IPC传递给Zygote进程。最后,Zygote进程会通过调用ZygoteConnection类的成员函数runOnce来执行创建应用程序进程的工作。

ZygoteConnection类的成员函数runOnce的实现如下所示:

class ZygoteConnection {
        ......

        boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
            ......

            try {
                args = readArgumentList();
                ......
            } catch (IOException ex) {
                ......
            }

            ......

            try {
                parsedArgs = new Arguments(args);
                ......

                pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
                        parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
                        parsedArgs.niceName);
            } catch (IOException ex) {
                ......
            } catch (ErrnoException ex) {
                ......
            } catch (IllegalArgumentException ex) {
                ......
            } catch (ZygoteSecurityException ex) {
                ......
            }

        ......
        }

        ......
    }

这个函数定义在文件frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java中。

ZygoteConnection类的成员函数runOnce首先是通过调用另外一个成员函数readArgumentList读取ActivityManagerService发送过来的应用程序进程创建参数args,接着再创建一个Arguments对象来解析该参数。解析后得到的参数传递给Zygote类的静态成员函数forkAndSpecialize,以便后者可以执行创建应用程序进程的工作。

Zygote类的静态成员函数forkAndSpecialize的实现如下所示:

public class Zygote {
        ......

        public static int forkAndSpecialize(int uid, int gid, int[] gids, int debugFlags,
                int[][] rlimits, int mountExternal, String seInfo, String niceName) {
            preFork();
            int pid = nativeForkAndSpecialize(
                    uid, gid, gids, debugFlags, rlimits, mountExternal, seInfo, niceName);
            postFork();
            return pid;
        }

        native public static int nativeForkAndSpecialize(int uid, int gid, int[] gids, int debugFlags,
                int[][] rlimits, int mountExternal, String seInfo, String niceName);

        ......
    }

这个函数定义在文件libcore/dalvik/src/main/java/dalvik/system/Zygote.java中。

Zygote类的静态成员函数forkAndSpecialize的实现很简单,它通过调用另外一个JNI函数nativeForkAndSpecialize来执行创建应用程序进程的工作。

Zygote类的JNI函数nativeForkAndSpecialize的由C++层的函数Dalvik_dalvik_system_Zygote_forkAndSpecialize来实现,如下所示:

static void Dalvik_dalvik_system_Zygote_forkAndSpecialize(const u4* args,
        JValue* pResult)
    {
        pid_t pid;

        pid = forkAndSpecializeCommon(args, false);

        RETURN_INT(pid);
    }

这个函数定义在文件dalvik/vm/native/dalvik_system_Zygote.cpp中。

注意,Zygote类的JNI函数nativeForkAndSpecialize在调用的过程中,传递进来的参数都被保存在函数Dalvik_dalvik_system_Zygote_forkAndSpecialize的参数args指向的一块内存中。

函数Dalvik_dalvik_system_Zygote_forkAndSpecialize通过调用另外一个函数forkAndSpecializeCommon来执行创建应用程序进程的工作,它的实现如下所示:

static pid_t forkAndSpecializeCommon(const u4* args, bool isSystemServer)
    {
        pid_t pid;

        uid_t uid = (uid_t) args[0];
        gid_t gid = (gid_t) args[1];
        ......
        char *seInfo = NULL;
        char *niceName = NULL;

        if (isSystemServer) {
            ......
        } else {
            ......
            StringObject* seInfoObj = (StringObject*)args[6];
            if (seInfoObj) {
                seInfo = dvmCreateCstrFromString(seInfoObj);
                ......
            }
            StringObject* niceNameObj = (StringObject*)args[7];
            if (niceNameObj) {
                niceName = dvmCreateCstrFromString(niceNameObj);
                ......
            }

            ......
        }

        ......

        pid = fork();

        if (pid == 0) {
            ......

           err = setSELinuxContext(uid, isSystemServer, seInfo, niceName);
           ......
        }

        ...... 

        return pid;
    }

这个函数定义在文件dalvik/vm/native/dalvik_system_Zygote.cpp中。

参数isSystemServer表示当前创建的是System Server进程还是应用程序进程。在我们这个场景中,它的值等于false,表示要创建的是应用程序进程。从参数args指向的内存可以获得各种各样的参数,例如uid、gid、seinfo和nice name等。

获得了要创建的进程的各种参数之后,函数forkAndSpecializeCommon就通过系统调用fork创建出了一个子进程。注意,这时候函数forkAndSpecializeCommon是在Zygote进程中执行的。因此,这里创建出来的子进程的安全上下文继承于Zygote进程。从前面的分析可以知道,这个安全上下文为"u:r:zygote:s0"。

如果这时候我们什么也不做的话,那么创建出来的应用程序进程的安全上下文就会一直被设置为"u:r:zygote:s0",这样会使得应用程序具有Zygote进程一样的SEAndroid安全权限。这是不允许的,因此,接下来需要通过调用函数setSELinuxContext来修改刚刚创建出来的应用程序进程的安全上下文。

函数setSELinuxContext的实现如下所示:

static int setSELinuxContext(uid_t uid, bool isSystemServer,
                                 const char *seInfo, const char *niceName)
    {
    #ifdef HAVE_ANDROID_OS
        return selinux_android_setcontext(uid, isSystemServer, seInfo, niceName);
    #else
        return 0;
    #endif
    }

这个函数定义在文件dalvik/vm/native/dalvik_system_Zygote.cpp中。

函数setSELinuxContext的实现很简单,它通过调用libselinux提供的函数selinux_android_setcontext来设置刚刚创建出来的应用程序进程的安全上下文。

函数selinux_android_setcontext的实现如下所示:

int selinux_android_setcontext(uid_t uid,
                       int isSystemServer,
                       const char *seinfo,
                       const char *pkgname)
    {
        char *orig_ctx_str = NULL, *ctx_str;
        context_t ctx = NULL;
        int rc;

        if (is_selinux_enabled() <= 0)
            return 0;

        __selinux_once(once, seapp_context_init);

        rc = getcon(&ctx_str);
        ......

        ctx = context_new(ctx_str);
        orig_ctx_str = ctx_str;
        ......

        rc = seapp_context_lookup(SEAPP_DOMAIN, uid, isSystemServer, seinfo, pkgname, ctx);
        ......

        ctx_str = context_str(ctx);
        ......

        rc = security_check_context(ctx_str);
        ......

        if (strcmp(ctx_str, orig_ctx_str)) {
            rc = setcon(ctx_str);
            ......
        }

        rc = 0;
    out:
        ......
        return rc;

        ......
    }

这个函数定义在文件external/libselinux/src/android.c中。

参数isSystemServer表示当前创建的是System Server进程还是应用程序进程。在我们这个场景中,它的值等于false,表示要创建的是应用程序进程。从参数args指向的内存可以获得各种各样的参数,例如uid、gid、seinfo和nice name等。

获得了要创建的进程的各种参数之后,函数forkAndSpecializeCommon就通过系统调用fork创建出了一个子进程。注意,这时候函数forkAndSpecializeCommon是在Zygote进程中执行的。因此,这里创建出来的子进程的安全上下文来继承于Zygote进程。从前面的分析可以知道,这个安全上下文为"u:r:zygote:s0"。

如果这时候我们什么也不做的话,那么创建出来的应用程序进程的安全上下文就会一直被设置为"u:r:zygote:s0",这样会使得应用程序具有Zygote进程一样的SEAndroid安全权限。这是不允许的,因此,接下来需要通过调用函数setSELinuxContext来修改刚刚创建出来的应用程序进程的安全上下文。

函数setSELinuxContext的实现如下所示:

static int setSELinuxContext(uid_t uid, bool isSystemServer,
                                 const char *seInfo, const char *niceName)
    {
    #ifdef HAVE_ANDROID_OS
        return selinux_android_setcontext(uid, isSystemServer, seInfo, niceName);
    #else
        return 0;
    #endif
    }

这个函数定义在文件dalvik/vm/native/dalvik_system_Zygote.cpp中。

函数setSELinuxContext的实现很简单,它通过调用libselinux提供的函数selinux_android_setcontext来设置刚刚创建出来的应用程序进程的安全上下文。

函数selinux_android_setcontext的实现如下所示:

int selinux_android_setcontext(uid_t uid,
                       int isSystemServer,
                       const char *seinfo,
                       const char *pkgname)
    {
        char *orig_ctx_str = NULL, *ctx_str;
        context_t ctx = NULL;
        int rc;

        if (is_selinux_enabled() <= 0)
            return 0;

        __selinux_once(once, seapp_context_init);

        rc = getcon(&ctx_str);
        ......

        ctx = context_new(ctx_str);
        orig_ctx_str = ctx_str;
        ......

        rc = seapp_context_lookup(SEAPP_DOMAIN, uid, isSystemServer, seinfo, pkgname, ctx);
        ......

        ctx_str = context_str(ctx);
        ......

        rc = security_check_context(ctx_str);
        ......

        if (strcmp(ctx_str, orig_ctx_str)) {
            rc = setcon(ctx_str);
            ......
        }

        rc = 0;
    out:
        ......
        return rc;

        ......
    }

函数selinux_android_setcontext的执行过程如下所示:

A. 调用函数is_selinux_enabled检查系统是否启用了SELinux。如果没有启用的话,那就什么也不用做就返回。否则的话,就继续往下执行。

B. 调用函数seapp_context_init读取和解析我们在前面SEAndroid安全机制框架分析一文提到的seapp_contexts文件。注意,__selinux_once是一个宏,它实际上是利用了pthread库提供的函数pthread_once保证函数seapp_context_init在进程内有且仅有一次会被调用到,适合用来执行初始化工作。

C. 调用函数getcon获得当前进程的安全上下文,保存在变量ctx_str。

D. 调用函数context_new在原来的安全上下文的基础上创建一个新的安全上下文ctx,以便可以获得原来安全上下文的SELinux用户、角色和安全级别等信息。

E. 调用函数seapp_context_lookup根据传进来的参数seinfo在seapp_contexts文件中找到对应的Domain,并且将其设置为新的安全上下文ctr的Domain。

F. 调用函数context_str获得新创建的安全上下文的字符串描述,以便可以调用函数security_check_context来验证新创建的安全上下文的正确性。如果不正确的话,就出错返回。否则的话,继续往下执行。

G. 比较原来的安全上下文和新创建的安全上下文。如果不一致的话,那么就调用函数setcon将当前进程的安全上下文设置为新创建的安全上下文。

在上面的几个步骤中,最重要的就是B、E和G三步。其中,B和E这两步在前面SEAndroid安全机制中的文件安全上下文关联分析一文中已经分析过了,因此接下来我们主要分析函数setcon的实现。

函数setcon通过以下三个宏来定义,如下所示:

#define setselfattr_def(fn, attr) \
        int set##fn(const security_context_t c) \
        { \
            return setprocattrcon(c, 0, #attr); \
        }

    #define all_selfattr_def(fn, attr) \
        getselfattr_def(fn, attr)    \
        setselfattr_def(fn, attr)

    all_selfattr_def(con, current)

这三个宏定义在文件external/libselinux/src/procattr.c中。

从这三个宏的定义就可以看出,函数setcon最终通过调用另外一个函数setprocattrcon来设置当前进程的安全上下文,其中,第一个参数c描述的是要设置的安全上下文,第三个参数的值等于"current"。

函数setprocattrcon的实现如下所示:

static int setprocattrcon(security_context_t context,
                  pid_t pid, const char *attr)
    {
        char *path;
        int fd, rc;
        pid_t tid;
        ssize_t ret;
        int errno_hold;

        if (pid > 0)
            rc = asprintf(&path, "/proc/%d/attr/%s", pid, attr);
        else {
            tid = gettid();
            rc = asprintf(&path, "/proc/self/task/%d/attr/%s", tid, attr);
        }
        if (rc < 0)
            return -1;

        fd = open(path, O_RDWR);
        free(path);
        if (fd < 0)
            return -1;
        if (context)
            do {
                ret = write(fd, context, strlen(context) + 1);
            } while (ret < 0 && errno == EINTR);
        else
            do {
                ret = write(fd, NULL, 0);   /* clear */
            } while (ret < 0 && errno == EINTR);
        errno_hold = errno;
        close(fd);
        errno = errno_hold;
        if (ret < 0)
            return -1;
        else
            return 0;
    }

这个函数定义在文件external/libselinux/src/procattr.c中。

函数setcon实际上就是打开proc文件系统中的/proc/self/task//attr/current文件,并且向其写入参数context所描述的安全上下文。其中,描述的是当前线程的id。向/proc/self/task//attr/current文件写入安全上下文实际上就是将进程的安全上下文保存在内核中用来描述进程的结构体task_struct中。这与文件的安全上下文是保存在文件扩展属性中是不一样的。

这样,新创建的应用程序进程的安全上下文就设置好了。

这个系列的文章学习到这里,我们就分析完成进程和文件的安全上下文的设置过程了。回忆前面SEAndroid安全机制简要介绍和学习计划一文,我们提出了一个问题,如何将设备上的/system/bin/gpsd文件下载到PC上来。

通过常用的adb pull命令是无法将开启了SEAndroid安全机制的设备上将/system/bin/gpsd文件读取下来的。这是因为adb pull命令是通过运行在设备上的adbd守护进程来读取/system/bin/gpsd文件,并且传回来给PC的,而守护进程adbd没有权限读取文件/system/bin/gpsd。

我们通过ls -Z和ps -Z命令可以来观察/system/bin/gpsd文件和adbd守护进程的安全上下文:

./adb shell ls -Z /system/bin/gpsd  
    -rwxr-xr-x root     shell             u:object_r:gpsd_exec:s0 gpsd  
    $ ./adb shell ps -Z | grep 'adbd'  
    u:r:adbd:s0                    shell     1978  1     /sbin/adbd 

这意味着domain为adbd的进程无法读取type为gpsd_exec的文件。

实际上我们是可以在PC上通过执行adb shell cat命令来读取设备上的/system/bin/gpsd文件的,如下所示:

./adb shell cat /system/bin/gpsd > ./gpsd

上面的命令实际上是通过shell启动cat程序,并且通过这个cat进程来读取文件/system/bin/gpsd的内容。

从前面的分析可以知道,cat进程的安全上下文来自于其父进程shell,因此,我们通过在设备上执行ps -Z命令观察一下shell进程的安全上下文:

$ ps -Z 
    u:r:shell:s0                   shell     23486 1978  /system/bin/sh

这意味着domain为shell的进程可以读取type为gpsd_exec的文件。

在SEAndroid中,shell和adbd这两个domain的区别是什么呢?我们可以通过一个称为apol的工具做一个简要分析。

首先我们要从设备上读取一个sepolicy文件,如下所示:

./adb pull /sepolicy  ./sepolicy
    2372 KB/s (558936 bytes in 0.230s)

读取出来的sepolicy文件实际上描述的就是设备使用的SEAndroid安全策略,我们可以通过apol工具对它进行分析。

接着我们打开apol工具,并且从File菜单中点击Open项打开上述sepolicy文件。切换到Policy Components选项卡,在左则的Types列表中找到adbd项,双击查看它的属性,如图2所示:

图2 adbd属性

这意味着adbd这个domain具有两个属性domain和mlstrustedsubject。

用同样的方法查看domain为shell的属性,如图3所示:

图3 shell属性

这意味着shell这个domain有三个属性appdomain、domain和mlstructedsubject。与adbd相比,shell多了一个appdomain属性。这又意味着具有appdomain属性的domain具有读取type为gpsd_exec的文件的权限。

再用同样的方法查看type为gpsd_exec的属性,如图4所示:

图4 gpsd_exec属性

这意味着gpsd_exec这个type有两个属性exec_type和file_type。

我们将apol工具切换到Policy Rules选项卡,在Source type/attribute和Target type/attribute编辑框中分别填入"appdomain"和"gpsd_exec",检查具有appdomain属性的进程是否可以读取具有exec_file属性的文件的权限,点击右边的New Search按钮,结果如图5所示:

图5 具有属性appdomain的进程对具有属性exec_type的文件的SEAndroid安全权限

这意味着具有属性appdomain的进程对具有属性exec_type的文件具有读取的权限。也就是说,设备上的shell进程能读取/system/bin/gpsd文件的内容,是因为它的domain具有appdomain属性,而设备上的adbd进程的domain是不具有这个属性的。

通过回答前面SEAndroid安全机制简要介绍和学习计划一文提出的问题,我们介绍了如何通过apol工具来分析设备上的SEAndroid安全策略。这是一个非常有用的工具,希望大家可以掌握。在接下来的两篇文章中,我们就继续学习SEAndroid安全机制是如何支持Android系统的属性访问以及Binder IPC调用的,敬请关注!更多信息可以关注老罗的新浪微博:http://weibo.com/shengyangluo

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:1年以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:1年以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:1年以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:1年以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:1年以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:1年以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:1年以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:1年以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:1年以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:1年以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:1年以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:1年以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:1年以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:1年以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:1年以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:1年以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:1年以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:1年以前  |  398次阅读  |  详细内容 »
 目录