SEandroid 安全机制---整体框架

前言:

在Java层和native守护进程socket通信 一文中,新建了一个名为myguard的socket。然后修改了5个权限相关的文件,

1,在device/qcom/sepolicy/common/ 路径下添加myguard.te文件,内容如下,

#myguard
allow myguard_socket myguard_socket:sock_file create_file_perms; 
type_transition myguard_socket socket_device:sock_file myguard_socket;

2,在device/qcom/sepolicy/common/的file_contexts文件中添加

/dev/socket/myguard                             u:object_r:myguard_socket:s0

3,在device/qcom/sepolicy/common/file.te中添加

type myguard_socket, file_type;

4,在device/qcom/sepolicy/common/system_app.te 中添加

allow system_app myguard_socket:sock_file rw_file_perms;

5,在device/qcom/sepolicy/common/system_server.te中添加

allow system_server myguard_socket:sock_file rw_file_perms;

编译刷入版本开机启动之后,在dev/ socket路径下出现myguard


2个进程读写一个socket为什么非要搞得这么艰难?这是为啥呢?

还不是SEAndroid安全机制!

在引进SEAndroid安全机制之前,Android系统的安全机制分为应用程序和内核两个级别。

应用程序级别的安全机制就是通常说的Permission机制。一个应用如果需要访问一些系统敏感或者特权资源,

那么就必须要在AndroidManifest.xml配置文件中进行申请,并且在安装的时候由用户决定是否赋予相应的权限(android 5.0),

或者在apk运行的时候由用户决定是否赋予相应的权限(android 6.0)。

内核级别的安全机制就是传统的Linux UID/GID机制。在Linux中,每一个用户都拥有一个用户ID,并且也有一个用户组ID,

分别简称为UID和GID。此外,Linux系统的进程和文件也有UID和GID的概念。Linux就是通过用户、进程、

文件的UID/GID属性来进行权限管理的。文件的权限控制在所有者的手中。因此,这种权限控制方式就称为自主式的,

正式的英文名称为Discretionary Access Control,简称为DAC。

例如, myguard设置为660(二进制110  110  000) 对应 rw- rw----

对于root用户,该socket可读可写;对于同组的其他用户, 该socket可读可写;对于其他用户, 该socket不可读也不可写。

为什么会有这2个机制呢,因为一些别有用心的为达目的不择手段;为什么还要有SEAndroid安全机制呢?

因为前面2个机制已经无法阻止一些素丧心病狂的开发人员了。

SEAndroid安全机制与传统的Linux UID/GID安全机制是并存关系的,也就是说,它们同时用来约束进程的权限。

当一个进程访问一个文件的时候,首先要通过基于UID/GID的DAC安全检查,接着才有资格进入到基于SEAndroid的MAC安全检查。

只要其中的一个检查不通过,那么进程访问文件的请求就会被拒绝。上述的安全检查过程如图所示:

SEandroid 安全机制---整体框架_第1张图片

1, SEAndroid安全机制的整体框架

SEAndroid安全机制的整体框架如下,

SEandroid 安全机制---整体框架_第2张图片

以SELinux文件系统接口为边界,SEAndroid安全机制包含有内核空间和用户空间两部分支持。

在内核空间,主要涉及到一个称为SELinux LSM的模块。而在用户空间中,涉及到Security Context、Security Server

和SEAndroid Policy等模块。这些内核空间模块和用户空间模块的作用以及交互如下所示:

 1. 内核空间的SELinux LSM模块负责内核资源的安全访问控制。

2. 用户空间的SEAndroid Policy描述的是资源安全访问策略。系统在启动的时候,用户空间的Security Server

需要将这些安全访问策略加载内核空间的SELinux LSM模块中去。这是通过SELinux文件系统接口实现的。

3. 用户空间的Security Context描述的是资源安全上下文。SEAndroid的安全访问策略就是在资源的安全上下文基础上实现的。

4. 用户空间的Security Server一方面需要到用户空间的Security Context去检索对象的安全上下文,另一方面也需要到内核空间去操作对象的安全上下文。

5. 用户空间的selinux库封装了对SELinux文件系统接口的读写操作。用户空间的Security Server访问内核空间的SELinux LSM模块时,

都是间接地通过selinux进行的。这样可以将对SELinux文件系统接口的读写操作封装成更有意义的函数调用。

6. 用户空间的Security Server到用户空间的Security Context去检索对象的安全上下文时,同样也是通过selinux库来进行的。

1.1 内核空间

内核中的资源在访问的过程中,一般需要获得三次检查通过:

1. 一般性错误检查,例如访问的对象是否存在、访问参数是否正确等。

2. DAC检查,即基于Linux UID/GID的安全检查。

3. SELinux检查,即基于安全上下文和安全策略的安全检查。

1.2 用户空间

在用户空间中,SEAndroid包含有三个主要的模块,分别是安全上下文(Security Context)、

安全策略(SEAndroid Policy)和安全服务(Security Server)。

1.2.1 安全上下文

SEAndroid是一种基于安全策略的MAC安全机制。这种安全策略又是建立在对象的安全上下文的基础上的。

这里所说的对象分为两种类型,一种称主体(Subject),一种称为客体(Object)。主体通常就是指进程,

而客观就是指进程所要访问的资源,例如文件、系统属性等。

安全上下文实际上就是一个附加在对象上的标签(Tag)。这个标签实际上就是一个字符串,它由四部

type init, domain, mlstrustedsubject;

分内容组成,

分别是SELinux用户、SELinux角色、类型、安全级别,每一个部分都通过一个冒号来分隔,格式为“user:role:type:sensitivity”。

例如,


myguard socket的安全上下文是 u:object_r: myguard_socket:s0

这表明文件myguard的SELinux用户、SELinux角色、类型和安全级别分别为u、object_r、myguard_socket和s0。

其他三个不重要,主要看SELinux类型。

资源的SELinux类型和进程的SELinux类型就像映射关系,当然并不止一一映射。

只有符合资源规定的SELinux类型的进程才可以访问资源。

例如下面这段话的意思是只有系统服务才可以读写myguard_socket类型的文件,即是读写myguard socket。

allow system_server myguard_socket:sock_file rw_file_perms;

    在android系统中, 安全上下文的SELinux类型声明一般有2种,描述进程的是domain,描述文件的是file_type。

但是自己写的守护进程myguardservice的SELinux类型怎么是init? myguard socket怎么是myguard_socket类型?


external/sepolicy/init.te文件的init类型定义如下,

type init, domain, mlstrustedsubject;

原来init 还是domain类型的,就像狗是动物一样。

同样的, 在device/qcom/sepolicy/common/file.te中myguard_socket也是file_type类型的。

type myguard_socket, file_type;

类似于继承关系,一方面维护独立性,另一方面又维护统一性。

了解了SEAndroid安全机制的安全上下文之后,就可以继续分析Android系统中的对象的安全上下文是如何定义。

这里只讨论四种类型的对象的安全上下文,分别是系统进程、数据文件、系统文件和系统属性。

这些描述安全上下文的文件一般位于external/sepolicy目录中,当然各大厂商可能不一样。

反正就是一条, 安全上下文的SELinux类型规定了资源文件只能由特定进程访问。

1.2.2. 安全策略

    上面分析了SEAndroid安全机制中的对象安全上下文,接下来就继续分析SEAndroid安全机制中的安全策略。

SEAndroid安全机制中的安全策略是在安全上下文的基础上进行描述的,也就是说,它通过主体和客体的安全上下文,定义主体是否有权限访问客体。

在external/sepolicy目录中(或者其他目录),所有以.te为后缀的文件经过编译之后,就会生成一个sepolicy文件。

这个sepolicy文件会打包在ROM中,并且保存在设备上的根目录下,即它在设备上的路径为/sepolicy。

    保存在这个sepolicy文件中的安全策略是不会自动加载到内核空间的SELinux LSM模块(内核空间)去的。

它需要在系统启动的过程中进行加载。

系统中第一个启动的进程是init进程。知道,Init进程在启动的过程中,执行了很多的系统初始化工作,

其中就包括加载SEAndroid安全策略的工作,init.cpp文件中的main方法有关selinux代码如下,

selinux_initialize(is_first_stage); //加载selinx策略

selinux_initialize方法如下,

static void selinux_initialize(bool in_kernel_domain) {
    Timer t;

    selinux_callback cb;
    cb.func_log = selinux_klog_callback;
    selinux_set_callback(SELINUX_CB_LOG, cb);
    cb.func_audit = audit_callback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);

    if (selinux_is_disabled()) {
        return;
    }

    if (in_kernel_domain) {
        INFO("Loading SELinux policy...\n");
        if (selinux_android_load_policy() < 0) {
            ERROR("failed to load policy: %s\n", strerror(errno));
            security_failure();
        }

        bool is_enforcing = selinux_is_enforcing();
        security_setenforce(is_enforcing);

        if (write_file("/sys/fs/selinux/checkreqprot", "0") == -1) {
            security_failure();
        }

        NOTICE("(Initializing SELinux %s took %.2fs.)\n",
               is_enforcing ? "enforcing" : "non-enforcing", t.duration());
    } else {
        selinux_init_all_handles();
    }
}

selinux_set_callback设置SEAndroid日志和审计回调方法,

selinux_android_load_policy加载安全策略到内核空间的SELinux LSM模块;

selinux_init_all_handles打开前面分析file_contexts和property_contexts文件,以便可以用来查询系统文件和系统属性的安全上下文。

selinux_android_load_policy方法在libselinux.so库中继续执行。

android.c的selinux_android_load_policy方法如下,

int selinux_android_load_policy(void)
{
	const char *mnt = SELINUXMNT;
	int rc;
	rc = mount(SELINUXFS, mnt, SELINUXFS, 0, NULL);
	if (rc < 0) {
		if (errno == ENODEV) {
			/* SELinux not enabled in kernel */
			return -1;
		}
		if (errno == ENOENT) {
			/* Fall back to legacy mountpoint. */
			mnt = OLDSELINUXMNT;
			rc = mkdir(mnt, 0755);
			if (rc == -1 && errno != EEXIST) {
			selinux_log(SELINUX_ERROR,"SELinux:  Could not mkdir:  %s\n",
					strerror(errno));
				return -1;
			}
			rc = mount(SELINUXFS, mnt, SELINUXFS, 0, NULL);
		}
	}
	if (rc < 0) {
		selinux_log(SELINUX_ERROR,"SELinux:  Could not mount selinuxfs:  %s\n",
				strerror(errno));
		return -1;
	}
	set_selinuxmnt(mnt);

    return selinux_android_load_policy_helper(false);
}

SELINUXMNT、OLDSELINUXMNT和SELINUXFS是三个宏,它们定义在文件external/libselinux/src/policy.h文件中,如下所示:

/* Preferred selinuxfs mount point directory paths. */
#define SELINUXMNT "/sys/fs/selinux"
#define OLDSELINUXMNT "/selinux"

/* selinuxfs filesystem type string. */
#define SELINUXFS "selinuxfs"

selinux_android_load_policy方法主要逻辑如下,

A. 以/sys/fs/selinux为安装点,安装一个类型为selinuxfs的文件系统,也就是SELinux文件系统,

用来与内核空间的SELinux LSM模块通信。

B. 如果不能在/sys/fs/selinux这个安装点安装SELinux文件系统,那么再以/selinux为安装点,安装SELinux文件系统。

C. 成功安装SELinux文件系统之后,接下来就调用另外一个函数selinux_android_load_policy_helper来将

SEAndroid安全策略加载到内核空间的SELinux LSM模块中去。

 

security_load_policy的实现很简单,它首先打开/sys/fs/selinux/load文件,然后将参数data所描述的安全策略

写入到这个文件中去。由于/sys/fs/selinux是由内核空间的SELinux LSM模块导出来的文件系统接口,

因此当将安全策略写入到位于该文件系统中的load文件时,就相当于是将安全策略从用户空间加载到SELinux LSM模块中去了。

以后SELinux LSM模块中的Security Server就可以通过它来进行安全检查。

1.2.3. Security Server

用户空间的Security Server主要是用来保护用户空间资源的,以及用来操作内核空间对象的安全上下文的,

它由应用程序安装服务PackageManagerService、应用程序安装守护进程installd、应用程序进程孵化器

Zygote进程以及init进程组成。其中,PackageManagerService和installd负责创建App数据目录的安全上下文,

Zygote进程负责创建App进程的安全上下文,而init进程负责控制系统属性的安全访问。

你可能感兴趣的:(SEandroid 安全机制---整体框架)