SElinux

SElinux是一个linux安全增强功能,其允许用户和管理员对访问控制有更多的控制权。

访问控制可以做到哪个用户可能访问哪些资源的级别。这些资源可以来自文件。标准的Linux存取控制,如文件权限(-rwxr-xr-x)由用户或用户运行的程序修改。相反的,SELinux 的存取权限由系统加载时的policy确定,这个policy不能随意更改。

SELinux同样提供了细粒度的存取控制,不仅仅是对文件的读写执行权限,还可以指定指定谁可以unlink,仅仅append以及移动文件等操作。SELinux同样可以指定除了文件意外的访问权限,如网络资源和进程间通信等。

SELinux是linux上的MAC(mandatory access control)强制访问控制。

SElinux如何工作

SELinux基于类型增强模型,类型指的是资源或者应用程序分类方法。如用户home目录的文件被标签为user_home_t。user_home_t就是类型,这意味这policy对用户家目录下的所有文件起作用。

运行的应用程序同样有标签,如web浏览器也许是firefox_t。类型增强允许用户指定应用程序标签可以访问哪些资源标签。如下SELinux允许应用程序访问一些资源:

allow firefox_t user_home_t : file { read write };

查看SELinux状态

[root@localhost ~]# getenforce
Enforcing

SELinux 禁止

# setenforce 0

可以修改/etc/selinux/config文件使得SELinux进入permissive或者disabled的模式

SELinux核心组件

SELinux核心组件执行enforcement 安全策略是的流程如下图。

SElinux_第1张图片

1.一个请求/获取资源/权限的subject必须存在。

2.一个对象管理器,其知道对特定资源需要的权限,

3.Security Server根据安全规则对请求的访问做安全决策

4.Security Policy描述了使用SELinux的policy语言描述的规则

5.Access Vector Cache(AVC)通过缓存安全策略以提高系统性能。

SElinux_第2张图片

上图从下到上是:

a)当前security server嵌入在linux kernel空间,policy则由libselinux库提供的若干函数从用户空间取得。

OM(object manager)和AVC(access vector cache)驻留在内核空间和用户空间。

内核空间

OM服务于内核服务如文件,目录,套接字,进程间通信等,由嵌入SELinux子系统的Linux Security Module(LSM)的hook函数提供。内核空间的AVC用于缓存加速基于内核OM的访问速度。

用户空间

应用程序或用户空间服务的MAC控制。

b)SELinux 安全策略(上图右侧),其支持/etc/selinux目录下的配置文件。这个目录包括了SELinux主要配置文件。

c)支持模块策略,

d)Policy source是必须的,可以有三种基本方法提供。

i)作为SELinux Policy Language所写的源码。

ii)使用Reference Policy支持高级宏定义安全规则。这是当前SELinux采用的标准方法。

iii)使用CIL(common Intermediate Language)

e)为了能够编译连接规则源文件,以及加载安全服务需要许多工具(上图上半部分)

f)为了使得管理员可以管理策略,SELinux环境和标签文件系统,工具以及修改的linux 命令被使用。

g)为了确保安全事件被记录,audit 服务用于捕捉违反规则的信息。

h)SELinux支持网络服务。

有SELinux情况时的系统调用

SElinux_第3张图片

DAC是标准linux的Discretionary Access Control。

安卓下的SELinux

external/libselinux

提供了SELinux用户空间函数库。

selinux_android_setcontext

当使用setcon加载应用程序时设置正确的上下文domain,seapp_contexts文件内容用于计算正确的内容。当frameworks/base/core/jni/com_android_internal_o
s_Zygote.cpp创建新进程以及system/core/run-as/run-as.c会调用该接口。

selinux_android_setfilecon

使用setfilecon应用的目录/文件,使用到seapp_contexts,这个函数在package install时被用到。

install()和make_user_data函数被frameworks/native/cmds/installd/commands.c调用

selinux_android_restorecon

selinux_android_restorecon_pkgdir

这些函数基于file_context或者seapp_context文件标记文件和目录。他们调用selinux_android_restorecon_common()重新标记请求的文件和目录。

selinux_android_restorecon由

  • frameworks/native/cmds/installd/installd.c安装新app时使用。
  • frameworks/base/core/jni/android_os_SELinux.cpp的native_restorecon方法
  • frameworks/native/cmds/dumpstate/utils.c

selinux_android_restorecon_pkgdir

  1. frameworks/native/cmds/installd/commands.c的restorecon_data()和make_user_data()函数使用。

selinux_android_seapp_context_reload

 当package installer加载后,为frameworks/native/cmds/installd/installd.c加载seapp_contexts

selinux_android_load_policy

当SELinux是能时挂载SELinux文件系统,然后调用selinux_android_reload_policy将policy加载到内核。由system/core/init/init.c初始化SELinux时使用。

selinux_android_reload_policy

重新加载policy到内核空间,在设置完selinux.reload_policy属性后由system/core/init/init.c调用selinux_reload_policy()。

selinux_android_use_data_policy

system/core/init/init.c调用其决定哪个policy目录加载property_contexts。

selinux_status_updated,is_selinux_enabled,

用于检查SELinux环境是否有任何改变

selinux_check_access

用于检查是否有权限

selinux_label_open;selabel_lookup;selinux_android_file_context_handle;selinux_android_prop_context_handle;setfilecon;setfscreatecon

标签管理

selinux_lookup_best_match

当ueventd创建设备节点时,由system/core/init/devices.c调用

/external/sepolicy


这是SE针对安卓的核心模块。主要是若干te文件。由于SELinux从安卓5.1开始已经强制使用了,涉及到SELinux相关的问题一般修改这里文件。最终这个目录经过m4编译成

./obj/ETC/sepolicy_intermediates/policy.conf
这个policy.conf大部分内容是te(Type Enforcement)文件以及系统相关文件的直接拷贝。

  • sepolicy的重头工作是编译sepolicy安全策略文件。这个文件来源于众多的te文件,初始化相关的文件(initial_sid,initial_sid_context,users,roles,fs_context等)。
  • file_context:该文件记载了不同目录的初始化SContext,它是和标签相关的。
  • seapp_context:和Android中的应用程序打标签有关。
  • property_contexts:和Android系统中的属性服务(property_service)有关,它为各种不同的属性打标签。

frameworks/base

JNI-增加SELinux支持函数,如isSELinuxEnabled和setFSCreateCon。

SELinux java 类以及方法定义。

检查Zygote连接内容。

package manager和墙纸服务的文件权限

安装/运行是MMAC。

system/core

SELinux对服务toolbox的支持(load_policy,runcon)

SELinux系统初始化支持(init,init.rc)

SELinux对auditing avc(auditd)

kernel LSM/SELinux支持

  1. LSM 位于binder的hooks(drivers/staging/android/binder.c,include/linux/security.h)
  2. capability支持(security/capability.c)
  3. LSM安全模块的hook(security/security.c).
  4. binder对象类和权限(security/selinux/include/classmap.h);权限检查security/selinux/hooks.c.

其它信息

  1. selinux版本信息,external/sepolicy/Android.mk

# SELinux policy version.
# Must be <= /sys/fs/selinux/policyvers reported by the Android kernel.
# Must be within the compatibility range reported by checkpolicy -V.
POLICYVERS ?= 30

安卓平台SEAndroid

init

android平台,SEAndroid的初始化由进程的祖先init的main函数完成,相关代码如下:

   
main()
{
process_kernel_cmdline();
// Set up SELinux, including loading the SELinux policy if we're in the kernel domain.
selinux_initialize(is_first_stage);


restorecon("/init") 

    // These directories were necessarily created before initial policy load
    // and therefore need their security context restored to the proper value.
    // This must happen before /dev is populated by ueventd.
    INFO("Running restorecon...\n");
    restorecon("/dev");
    restorecon("/dev/socket");
    restorecon("/dev/__properties__");
    restorecon_recursive("/sys");
} 
main函数比较关键的两点是:

1.调用selinux_initialize(is_first_stage)初始化SELinux Android。

2.restore context,根据file_contexts内容给目录打标签。


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);
//如果disabled,直接返回,但是5.1之后AOSP强制enforcing,一般厂商也是测试基于enforcing模式的。
    if (selinux_is_disabled()) {
        return;
    }

    if (in_kernel_domain) {
        INFO("Loading SELinux policy...\n");
       //加载sepolicy   
        if (selinux_android_load_policy() < 0) {
            ERROR("failed to load policy: %s\n", strerror(errno));
            security_failure();
        }
//设置SELinux模式,disabled,permissive,enforcing,这个可以在parse_cmdline中进行设置。
        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 {
//permissive模式,违规给出warning并记录日志,但依然放行,enforcing违规是不放行的。
       selinux_init_all_handles();
    }
}

该函数主要完成两件事情:

1.selinux_android_load_policy加载sepolicy

2.selinux_init_all_handles初始化file_context,seapp_context以及property_context相关内容

selinux_android_load_policy

external/libselinux/src/android.c
int selinux_android_load_policy(void)
{
//src/policy.h:16:#define SELINUXMNT "/sys/fs/selinux",挂载点
    const char *mnt = SELINUXMNT;
    int rc;
//src/policy.h:20:#define SELINUXFS "selinuxfs"
    rc = mount(SELINUXFS, mnt, SELINUXFS, 0, NULL);
//sys/fs/selinux为userspace和kernel的SELinux模块交互通道
    set_selinuxmnt(mnt);
//加载selinux policy。
    return selinux_android_load_policy_helper(false);
}
 
  SElinux_第4张图片 
  


用户空间进程可以读写/sys/fs/selinux的各个文件或其中的子目录来通知kernel中的SELinux完成相关的操作。

static int selinux_android_load_policy_helper(bool reload)
{
    int fd = -1, rc;
    struct stat sb;
    void *map = NULL;
    int old_policy_index = policy_index;

    /*
     * If reloading policy and there is no /data policy or
     * that /data policy has the wrong version and our prior
     * load was from the / policy, then just return.
     * There is no point in reloading policy from / a second time.
     */
    set_policy_index();
    if (reload && !policy_index && !old_policy_index)
        return 0;
//sepolicy_file指向的是sepolicy文件的路径,即root目录下的sepolicy文件。
    fd = open(sepolicy_file[policy_index], O_RDONLY | O_NOFOLLOW);
    if (fd < 0) {
        selinux_log(SELINUX_ERROR, "SELinux:  Could not open sepolicy:  %s\n",
                strerror(errno));
        return -1;
    }
...
    map = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    if (map == MAP_FAILED) {
        selinux_log(SELINUX_ERROR, "SELinux:  Could not map %s:  %s\n",
                sepolicy_file[policy_index], strerror(errno));
        close(fd);
        return -1;
    }
//将sepolicy文件加载到内核中
    rc = security_load_policy(map, sb.st_size);
    if (rc < 0) {
        selinux_log(SELINUX_ERROR, "SELinux:  Could not load policy:  %s\n",
                strerror(errno));
        munmap(map, sb.st_size);
        close(fd);
        return -1;
    }

    munmap(map, sb.st_size);
    close(fd);
    selinux_log(SELINUX_INFO, "SELinux: Loaded policy from %s\n", sepolicy_file[policy_index]);

    return 0;
}
init通过mmap的方式,将sepolicy文件传递给kernel。init使用了libselinux提供的API完成相关操作。而libselinux则是通过/sys/fs/selinux下的文件来完成和Kernel中SELinux模块的交互。

selinux_init_all_handles



static void selinux_init_all_handles(void)
{
    sehandle = selinux_android_file_context_handle();
    selinux_android_set_sehandle(sehandle);
    sehandle_prop = selinux_android_prop_context_handle();
}

创建并设置文件handler,创建prophandler。

struct selabel_handle* selinux_android_file_context_handle(void)
{
    struct selabel_handle *sehandle;

    set_policy_index();
    sehandle = selabel_open(SELABEL_CTX_FILE, &seopts[policy_index], 1);

    if (!compute_contexts_hash(seopts, fc_digest)) {
        selabel_close(sehandle);
        return NULL;
    }

    return sehandle;
}
加载/file_contexts文件,和文件安全label有关。prop和此类似。

restorecon


int restorecon(const char* pathname)
{
    return selinux_android_restorecon(pathname, 0);
}

 
int selinux_android_restorecon(const char *file, unsigned int flags)
{
    return selinux_android_restorecon_common(file, NULL, -1, flags);
}
设置系统属性
    // Labeling successful. Mark the top level directory as completed.
    if (setrestoreconlast && !nochange && !error)
        setxattr(pathname, RESTORECON_LAST, fc_digest, sizeof fc_digest, 0);

你可能感兴趣的:(linux)