- Android Looper 和 Handler 分析
- Android MediaScanner 详尽分析
- Android 深入浅出之 Binder 机制
- 第一部分 AudioTrack 分析
- 第二部分 AudioFlinger 分析
- Android 深入浅出之 Audio 第三部分 Audio Policy
- Android 深入浅出之 Zygote
- Android 深入浅出之 Surface
- Linux Kernel 系列一 开篇和 Kernel 启动概要
- Linux Kernel 系列二 用户空间的初始化
- Linux Kernel 系列三 Kernel 编译和链接中的 linker script 语法详解
- 第五章 深入理解常见类
- linux kernel 系列四 嵌入式系统中的文件系统以及 MTD
- 随笔之 Android 平台上的进程调度探讨
- Android 4.0 External 下功能库说明
- 随笔之 Android 不吐不快
- Android Rom 移植知识普及
- 深入理解 Android 系列书籍的规划路线图
- Android 4.1 初识 - 7月12号
- Android 4.1 初识 - 7月13号
- Android 4.1 Surface 系统变化说明
- Android BSP 成长计划随笔之虚拟设备搭建和 input 系统
- 深入理解 Android 写作背后的故事
- 随笔之 GoldFish Kernel 启动过程中 arm 汇编分析
- Android Project Butter 分析
- Android Says Bonjour
- MTP in Android
- DRM in Android
- Tieto 公司 Android 多窗口解决方案展示
- 深入理解 SELinux SEAndroid 之二
- 深入理解 SELinux SEAndroid(最后部分)
- 前言
- 附录
- 第一章 准备工作
- 第二章 深入理解 Netd
- 第三章 Wi-Fi 基础知识
- 第四章 深入理解 wpa_supplicant
- 第五章 深入理解 WifiService
- 第六章 深入理解 wi-Fi Simple Configuration
- 第七章 深入理解 Wi-Fi P2P
- 第八章 深入理解 NFC
- 第九章 深入理解 GPS
- Google I/O 2014 之 Android 面面观
- 深入理解 Android 之 Java Security 第一部分
- 深入理解 Android 之 Java Security 第二部分(Final)
- 深入理解 Android 之设备加密 Device Encryption
- 第一章 阅读前的准备工作
- 第二章 深入理解 JNI
- 第三章 深入理解 init
- 第四章 深入理解 Zygote
- 第五章 深入理解常见类
- 第六章 深入理解 Binder
- 第七章 深入理解 Audio 系统
- 第八章 深入理解 Surface 系统
- 第九章 深入理解 Vold 和 Rild
- 第十章 深入理解 MediaScanner
- 第一章 开发环境部署
- 第二章 深入理解 Java Binder 和 MessageQueue
- 第三章 深入理解 SystemServer
- 第四章 深入理解 PackageManagerService
- 第五章 深入理解 PowerManagerService
- 第六章 深入理解 ActivityManagerService
- 第七章 深入理解 ContentProvider
- 第八章 深入理解 ContentService 和 AccountManagerService
- 第一章 开发环境部署
- 第二章 深入理解 Java Binder 和 MessageQueue
- 第三章 深入理解 AudioService
- 第四章 深入理解 WindowManagerService
- 第五章 深入理解 Android 输入系统
- 第六章 深入理解控件(ViewRoot)系统
- 第七章 深入理解 SystemUI
- 第八章 深入理解 Android 壁纸
- 边缘设备、系统及计算杂谈(16)——Apache 学习
- 边缘设备、系统及计算杂谈(17)——Ansible 学习
- ZFS 和 LVM
- Android 4.2 蓝牙介绍
- 了解一下 Android 10 中的 APEX
- 关于 Android 学习的三个终极问题
- 深入理解 Android 之 AOP
- Android 系统性能调优工具介绍
- 深入理解 SELinux SEAndroid(第一部分)
- Android Wi-Fi Display(Miracast)介绍
- 深入理解 Android 之 Gradle
深入理解 SELinux SEAndroid(最后部分)
二 SEAndroid源码分析
有了上文的SELinux的基础知识,本节再来看看Google是如何在Android平台定制SELinux的。如前文所示,Android平台中的SELinux叫SEAndroid。
先来看SEAndroid安全策略文件的编译。
1. 编译sepolicy
Android平台中:
- external/sepolicy:提供了Android平台中的安全策略源文件。同时,该目录下的tools还提供了诸如m4,checkpolicy等编译安全策略文件的工具。注意,这些工具运行于主机(即不是提供给Android系统使用的)
- external/libselinux:提供了Android平台中的libselinux,供Android系统使用。
- external/libsepol:提供了供安全策略文件编译时使用的一个工具checkcon。
对我们而言,最重要的还是external/sepolicy。所以先来看它。
读者还记得上文提到的如何查看make命令的执行情况吗?通过:
mmm external/sepolicy --just-print
,我们可以看到sepolicy编译时都干了些什么。
#以后用SEPOLICY_TEMP代替
# out/target/product/generic/obj/ETC/sepolicy_intermediates字符串
#创建临时目录
mkdir -p out/target/product/generic/obj/ETC/sepolicy_intermediates/
#----->处理一堆输入源文件,最终输出为policy.conf
#执行m4命令,用来生成plicy.conf文件。m4命令将扩展SEAndroid定义的一些宏
m4 -D mls_num_sens=1 -D mls_num_cats=1024 -s
#m4的输入文件。下面标黑体的是SEAndroid一些系统相关的文件,一般不会修改它
security_classes initial_sids access_vectors
global_macros mls_macros mls
policy_capabilities te_macros attributes
#Android系统中的te文件。
adbd.te app.te bluetoothd.te bluetooth.te clatd.te dbusd.te debuggerd.te device.te dhcp.te dnsmasq.te domain.te drmserver.te file.te gpsd.te hci_attach.te healthd.te hostapd.te init_shell.te init.te installd.te isolated_app.te kernel.te keystore.te media_app.te mediaserver.te mtp.te netd.te net.te nfc.te ping.te platform_app.te ppp.te property.te qemud.te racoon.te radio.te release_app.te rild.te runas.te sdcardd.te servicemanager.te shared_app.te shell.te surfaceflinger.te su.te system.te tee.te ueventd.te unconfined.te untrusted_app.te vold.te watchdogd.te wpa_supplicant.te zygote.te
#其他文件
roles users initial_sid_contexts fs_use genfs_contexts port_contexts
#m4:将上述源文件处理完后,生成policy.conf
> SEPOLICY_TEMP/policy.conf
#下面这个命令将根据policy.conf中的内容,再生成一个policy.conf.dontaudit文件
sed '/dontaudit/d'
SEPOLICY_TEMP/policy.conf >
SEPOLICY_TEMP/policy.conf.dontaudit
mkdir -p SEPOLICY_TEMP/
#------>根据policy.conf文件,生成二进制文件。SEAndroid中,它叫sepolicy
#执行checkpolicy,输入是policy.conf,输出是sepolicy
#-M选项表示支持MLS
checkpolicy -M -c 26 -o SEPOLICY_TEMP/sepolicy
SEPOLICY_TEMP/policy.conf
#执行checkpolicy,输入是policy.conf.dontaudit,输出是sepolicy.dontaudit
checkpolicy -M -c 26 -o
SEPOLICY_TEMP/sepolicy.dontaudit
SEPOLICY_TEMP/policy.conf.dontaudit
#--->将sepolicy拷贝到对应目标平台的root目录下
echo "Install: out/target/product/generic/root/sepolicy"
acp -fp SEPOLICY_TEMP/sepolicy
out/target/product/generic/root/sepolicy
#---->生成file_context文件
#用FILE_CONTEXT_TEMP代替
# out/target/product/generic/obj/ETC/file_contexts_intermediates字符串
mkdir -p FILE_CONTEXT_TEMP/
m4 -s external/sepolicy/file_contexts > FILE_CONTEXT_TEMP/file_contexts
checkfc SEPOLICY_TEMP/sepolicy
FILE_CONTEXT_TEMP/file_contexts
echo "Install: out/target/product/generic/root/file_contexts"
acp -fp FILE_CONTEXT_TEMP/file_contexts
out/target/product/generic/root/file_contexts
#--->生成seapp_context文件,这个是Android平台特有的,其作用我们下文再介绍
#用SEAPP_CONTEXT_TEMP代替
# out/target/product/generic/obj/ETC/seapp_contexts_intermediates
mkdir -p SEAPP_CONTEXT_TEMP/
checkseapp -p SEPOLICY_TEMP /sepolicy
-o SEAPP_CONTEXT_TEMP/seapp_contexts SEAPP_CONTEXT_TEMP/seapp_contexts.tmp
echo "Install: out/target/product/generic/root/seapp_contexts"
acp -fp SEAPP_CONTEXT_TEMP/seapp_contexts
out/target/product/generic/root/seapp_contexts
#---->和Android平台中的属性相关。SEAndroid中,设置属性也需要相关权限
#用PROPERTY_CONTEXT_TMP代替:
# out/target/product/generic/obj/ETC/property_contexts_intermediates
mkdir -p PROPERTY_CONTEXT_TMP/
m4 -s external/sepolicy/property_contexts >
PROPERTY_CONTEXT_TMP/property_contexts
checkfc -p TARGET_SEPOLICY_TEMP/sepolicy
PROPERTY_CONTEXT_TMP/property_contexts
echo "Install: out/target/product/generic/root/property_contexts"
acp -fp PROPERTY_CONTEXT_TMP/property_contexts
out/target/product/generic/root/property_contexts
上面展示了sepolicy编译的执行情况,读者最好自己尝试一下。注意,checkfc,checkseapp等都是SEAndroid编译时使用的工具,它们用来做策略检查,看看是否有规则不符合的地方。
总结:
- sepolicy的重头工作是编译sepolicy安全策略文件。这个文件来源于众多的te文件,初始化相关的文件(initial_sid,initial_sid_context,users,roles,fs_context等)。
- file_context:该文件记载了不同目录的初始化SContext,所以它和死货打标签有关。
- seapp_context:和Android中的应用程序打标签有关。
- property_contexts:和Android系统中的属性服务(property_service)有关,它为各种不同的属性打标签。
下面我们来看看和SEAndroid相关的代码,故事从init开始。
2. init的SEAndroid定制
Android平台中,SEAndroid的初始化由进程的祖先init的main函数完成,相关代码如下所示:
[-->init.c:main]
process_kernel_cmdline();
//向SELinux设置两个回调函数,主要是打印log
union selinux_callback cb;
cb.func_log = klog_write;
selinux_set_callback(SELINUX_CB_LOG, cb);
cb.func_audit = audit_callback;
//selinux_set_callback由libselinux提供。读者可google libselinux各个API
//的作用
selinux_set_callback(SELINUX_CB_AUDIT, cb);
//①初始化SEAndroid
selinux_initialize();
//②给下面几个目录打标签!
restorecon("/dev");
restorecon("/dev/socket");
restorecon("/dev/__properties__");
restorecon_recursive("/sys");
上述代码中的两个重要函数:
- selinux_initialize:初始化SEAndroid:
- 一堆的restoercon,全称应该是restore context:就是根据file_contexts中的内容给一些目录打标签。
先来看selinux_initialize:
2.1 selinux_initialize分析
[-->init.c:: selinux_initialize]
static void selinux_initialize(void)
{
/*判断selinux功能是否启用。方法是:
1) /sys/fs/selinux 是否存在。或者
2) ro.boot.selinux 属性不为disabled
*/
if (selinux_is_disabled()) return;
//加载sepolicy文件
if (selinux_android_load_policy() < 0) {......}
selinux_init_all_handles();
/*selinux有两种工作模式,
“permissive”:所有操作都被允许(即没有MAC),但是如果有违反权限的话,会记录日志
“enforcing”:所有操作都会进行权限检查
*/
bool is_enforcing = selinux_is_enforcing();
//设置SELinux的模式
security_setenforce(is_enforcing);
}
来看上述代码中的两个函数:
- selinux_android_load_policy:加载sepolicy文件。
- selinux_init_all_handles:初始化file_context,seapp_context及property_context相关内容。
(1) selinux_android_load_policy
来看selinux_android_load_policy,其代码如下所示:
[-->external/libselinux/src/android.c:: selinux_android_load_policy]
int selinux_android_load_policy(void)
{
char *mnt = SELINUXMNT;// 值为/sys/fs/selinux
int rc;//挂载/sys/fs/selinux,SELINUXFS值为"selinuxfs"
rc = mount(SELINUXFS, mnt, SELINUXFS, 0, NULL);
......
// /sys/fs/selinux为userpace和kernel中的SELinux模块交互的通道
set_selinuxmnt(mnt);//此函数定义在selinux.h中,属于libselinux API.
return selinux_android_reload_policy(); //加载SEAndroid中的policy文件
}
图11展示了Nexus 7上/sys/fs/selinux的内容:
图11 /sys/fs/selinux的内容
用户空间进程可同读写/sys/fs/selinux的各个文件或其中的子目录来通知Kernel中的SELinux完成相关的操作。
我们此处此处举一个例子,如图11下方红框中的booleans文件夹:
- 我们可以SELinux的安全配置文件中写一些类似if/else的语句。if中的是布尔判断条件。比如booleans文件夹下有一个in_qemu文件,这个就是sepolicy安全配置文件中的一个布尔变量。in_qemu定义在domain.te文件中,关键词是bool。
- cat booleans/in_qemu:打印in_qemu布尔变量的取值。读者会发现它的值为“0 0”。为什么有两个零呢,这第一个0是它的当前值,第二个零代表pending取值。即还没有赋值给当前值的一个中间变量。如果我们通过 echo "1" > booleans/in_qemu的话,第二个零将变成1。
- 为什么需要有中间变量呢?读者注意图11的右上方有一个commit_pending_bools文件。原来,通过在布尔变量中设置一个pending变量,我们可以实现批处理操作。即先修改1个或多个布尔变量的pending变量,然后往commit_pending_bools写1,这样这些一个或多个的布尔变量将使用pending变量取代当前值。
接下来看看selinux_android_reload_policy函数:
[-->external/libselinux/src/android.c:: selinux_android_reload_policy]
int selinux_android_reload_policy(void)
{
int fd = -1, rc; struct stat sb; void *map = NULL;
int i = 0;
// sepolicy_file指明sepolicy文件的路径。Android中有两处,第一个是
// /data/security/current/sepolicy。第二个是root目录下的sepolicy文件。
//下面这段逻辑可知,SEAndroid只使用其中的一个,如果/data/目录下有sepolicy文件,则
//优先使用它
while (fd < 0 && sepolicy_file[i]) {
fd = open(sepolicy_file[i], O_RDONLY | O_NOFOLLOW);
i++;
}
......
map = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
......
//假设使用根目录下的sepolicy文件。下面这个函数由selinux.h定义,它将此文件加载到
//内核中
rc = security_load_policy(map, sb.st_size);
......
munmap(map, sb.st_size);
close(fd);
return 0;
}
init通过mmap的方式,将sepolicy文件传递给了kernel。init使用了libselinux提供的API函数来完成相关操作。而libselinux则是通过操作/sys/fs/selinux下的文件来完成和Kernel中SELinux模块的交互。libselinux库的API不是我们研究的重点,感兴趣的兄弟请自己研究源码。
总之,selinux_android_load_policy干得最重要的一件事情就是将sepolicy文件传递给Kernel,这样Kernel就有了安全策略配置文件,后续的MAC才能开展起来。
在此,请读者注意sepolicy文件的位置:
- 首先查看/data/security/current/sepolicy:笔者自定义的策略文件一般放在这里,init优先使用它。
- /sepolicy:root根目录下的sepolicy,如果data目录下没有sepolicy,则使用它。系统默认的sepolicy在此。
(2) selinux_init_all_handles
前面讲过,init要给一些死货和property打标签,为了完成这个工作,根据libselinux的API,init需要先创建两个handler,代码在selinux_init_all_handles中:
[-->init.c:: selinux_init_all_handles]
void selinux_init_all_handles(void)
{
sehandle = selinux_android_file_context_handle();
sehandle_prop = selinux_android_prop_context_handle();
}
创建两个handler,主要为后续做labeling控制。我们来看看prop的context:
[-->init.c::selinux_android_prop_context_handle]
struct selabel_handle* selinux_android_prop_context_handle(void)
{
int i = 0;
struct selabel_handle* sehandle = NULL;
//setopts_prop也有两个值:
//第一个是/data/security/property_contexts。第二个是/property_contexts
while ((sehandle == NULL) && seopts_prop[i].value) {
sehandle = selabel_open(SELABEL_CTX_ANDROID_PROP, &seopts_prop[i], 1);
i++;
}
//假设采用的是根目录下的property_context文件
......
return sehandle;
}
handler其实就是为了打开xxx_context文件。由于它们和restorecon有关,下面直接来看restorecon函数,看看这些handler是怎么个用法。
2.2 restorecon分析
[-->init.c::restorecon]
int restorecon(const char *pathname)
{
char *secontext = NULL;
struct stat sb;
int i;
if (is_selinux_enabled() <= 0 || !sehandle)
return 0;
if (lstat(pathname, &sb) < 0) return -errno;
//查找file_context文件中是否包含有pathname路径的控制选项
if (selabel_lookup(sehandle, &secontext, pathname, sb.st_mode) < 0)
return -errno;
//设置patchname目录的security_context,lsetfilecon的实现非常简单,就是调用
//
if (lsetfilecon(pathname, secontext) < 0) {
freecon(secontext);
return -errno;
}
freecon(secontext);
return 0;
}
想知道selinux是如何labeling一个文件或目录的吗?答案在lsetfilecon中:
[-->external/libselinux/src/lsetfilecon.c:: lsetfilecon]
int lsetfilecon(const char *path, const security_context_t context)
{
//设置文件系统的属性
return lsetxattr(path, XATTR_NAME_SELINUX, context, strlen(context) + 1,0);
}
2.3 property权限检查
一般而言,SELinux权限检查都是由kernel来完成的,不过对于Android平台中的Property而言,这却完全是一个用户空间的内容。所以,我们看看init是如何使用libselinux来完成用户空间的权限检查的。
每当其他进程通过setprop函数设置属性时,property_service中有一个叫check_
[system/core/init/property_service.c:: check_mac_perms]
static int check_mac_perms(const char *name, char *sctx)
{
if (is_selinux_enabled() <= 0) return 1;
char *tctx = NULL;
const char *class = "property_service";
const char *perm = "set";
int result = 0;
......
//检查property_context中是否定义了目标SContext,即tctx。
if (selabel_lookup(sehandle_prop, &tctx, name, 1) != 0) goto err;
//将源SContext和目标SContext进行比较,判断是否有相关权限。name是属性的名字
//源SContext是调用setprop进程的SContext。目标SContext是property_context
//文件中定义的SContext。
if (selinux_check_access(sctx, tctx, class, perm, name) == 0)
result = 1;
freecon(tctx);
err:
return result;
}
怎么样?理解起来并不困难吧?用户空间的权限检查主要就是通过selinux_check_access完成,其输入参数包括:
- 源的SContext:它就是调用setprop的进程的SContext
- 目标的SContext:不同的属性有不同的SContext,这是在property_context中定义的。
- 要检查的Object class(系统所支持的类在external/sepolicy/security_classes文件中定义)。
- 操作名称(perm,由access vector定义。对Property这种Object class而言,其唯一需要做权限检查的操作就是set。读者可参考external/sepolicy/access_vectors这个文件)。
具体的哪一个属性(name参数指定,就是具体指明哪一文件)。
提示:关于这些API的说明,读者请参考http://selinuxproject.org/page/User_Resources中的Manual pages文档。
下面我们来看Android中应用程序是如何使用SELinux的。
3. 应用程序中的SELinux
对应用程序而言,最重要的工作就是管理它们的DT和TT:
- 所有Android的Application对应的进程都是从zygote进程中fork出来的。从前文介绍DT的知识可知,在做DT时,可以根据所执行的不同Type的文件来转换到不同的DT。但这个对Android而言不可行。因为zygote在fork子进程后,并没有执行execv。
- apk在安装后,都会在/data/data/目录下建立自己对于的文件夹,这个工作是由installd来完成的。同样,installd应该给这些不同的文件夹打上对应的label。
我们先来看应用程序的DT。
3.1 Java应用程序的DT
Android中应用进程(就是APK所在的进程)的DT转换其实很简单,它及其具有Android特色:
- 普通的DT是根据所execv文件的Type来设置DT转换条件。
- 而Android中则根据该APK签名信息来讲最终的进程转换到几种预设值的Domain中。
(1) mac_permissions.xml的用途
我们先来看PackageManagerService:
[-->PackageManagerService.java::PackageManageService]
......
/*下面这个函数将尝试解析
1)/data/security/mac_permissions.xml 或
2)/system/etc/security/mac_permissions.xml 中的内容。
*/
mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();
注意,mac_permissions.xml位于external/sepolicy中,图12所示为该文件的原始:
图12 mac_permissions.xml的内容
在系统过程中,图12中的@RELEASE,@PLATFORM等内容会被RELEASE、PLATFORM签名信息替换。图13所示为Nexus 7中该文件的内容。
图13 Nexus7中mac_permissions.xml内容
mac_permissions.xml保存了不同签名所对应的seinfo:如seinfo为platform时的签名是什么,seinfo为media的时候签名又是什么。那么,这些信息有啥用呢?来看下文。
(2) 扫描APK
当APK安装时,也就是APK被PKMGS扫描的时候,有如下的代码:
[-->PackageManagerService.java::ScanPackageLI]
if (mFoundPolicyFile) {
//下面这个函数将根据签名信息赋值seinfo值给对应的apk
SELinuxMMAC.assignSeinfoValue(pkg);
}
[-->SELinuxMMAC.java::assignSeinfoValue]
public static void assignSeinfoValue(PackageParser.Package pkg) {
//对于系统app(预装的,位于system目录下的)
if (((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) ||
((pkg.applicationInfo.flags &
ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0)) {
for (Signature s : pkg.mSignatures) {
if (s == null) continue;
//sSigSeinfo存储了mac_permissions.xml中seinfo标签的内容
if (sSigSeinfo.containsKey(s)) {
String seinfo = pkg.applicationInfo.seinfo = sSigSeinfo.get(s);
return;
}
}
//sPackageSeinfo存储了xml中package标签下seinfo子标签的内容
if (sPackageSeinfo.containsKey(pkg.packageName)) {
String seinfo = pkg.applicationInfo.seinfo =
sPackageSeinfo.get(pkg.packageName);
return;
}
}
//default标签中seinfo的值
String seinfo = pkg.applicationInfo.seinfo = sSigSeinfo.get(null);
......
}
assignSeinfoValue的功能如上代码所示,它根据apk的签名信息来赋值不同的seinfo,也就是诸如"platform",”media“之类的值。
提示:大家能想出为什么要设置seinfo吗?恩,它就是Android为App定义的SContext中的Domain的值。
(3) App的DT转换
ActivityManagerService负责启动目标应用进程,相关代码如下所示:
[-->ActivityManagerService.java:: startProcessLocked]
Process.ProcessStartResult startResult =
Process.start("android.app.ActivityThread",
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, null);
根据《深入理解Android卷I》第4章对zygote的介绍,zygote进程将fork一个子进程,相关函数在:
[-->ZygoteConnection.java::runOnce]
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal,
parsedArgs.seInfo,parsedArgs.niceName);
该函数由JNI实现,代码在dalvik/vm/native/ dalvik_system_Zygote.cpp中,其中最重要的是内部所调用的forkAndSpecializeCommon:
[-->dalvik_system_Zygote.cpp:: forkAndSpecializeCommon]
pid = fork();
if (pid == 0) {
......
err = setSELinuxContext(uid, isSystemServer, seInfo, niceName);
.....}
[-->external/libselinux/android.c::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 = -1;
if (is_selinux_enabled() <= 0) return 0;
//重要函数:seapp_context_init,内部将调用selinux_android_seapp_context_reload
//以加载seapp_contexts文件。
// 1) /data/security/current/seapp_contexts 或者
// 2) /seapp_contexts 本例而言,就是根目录下的这个seapp_context文件
__selinux_once(once, seapp_context_init);
rc = getcon(&ctx_str);
ctx = context_new(ctx_str);
orig_ctx_str = ctx_str;
//从zygote进程fork出来后,最初的SContext取值为u:r:zygote:s0
//下面这个函数将根据uid,pkgname等设置最终的SC。例如u:r:system_app:s0等
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;
......
return rc;
}
图14所示为seapp_context的内容,非常简单:
图14 seapp_context内容
上面代码中的seapp_context_lookup将根据图14的内容,通过不同的apk所对应的seinfo,找到他们的目标domain,然后再设置为它们新的SContext。例如图15的Nexus 7中ps -Z的结果图。
图15 ps -Z查看apk进程的SContext
seapp_context_lookup是完成从seapp_context文件内容映射到具体对应为哪个Domain的关键函数,该函数第一次看起来吓死人,其实蛮简单。这里就不再多说。
anyway,SEAndroid中,不同应用程序将根据它们的签名信息得到对应的SContext(主要是Domain,MLS其实没用上,但以后可以用上,这是通过图14中的levelFrom语句来控制的,具体可参考seapp_context_lookup的实现)。
DT完成后,我们看系统如何为它们的对应文件夹打标签
3.2 App data目录的TT
还是在PackageManagerService的scanPackageLI函数中,
[-->PackageManagerService.java:: scanPackageLI]
int ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid,
pkg.applicationInfo.seinfo);
createDataDirsLI最终会调用installd实现的函数:
[-->installd/commands.c::install]
//内部调用selinux_android_setfilecon2,它和上文的selinux_android_setcontext
//几乎一样。最终它将设置pkgdir的SContext。注意,它主要根据seapp_context文件中的
//type字段来确定最终的Type值。
if (selinux_android_setfilecon2(pkgdir, pkgname, seinfo, uid) < 0) {
......
}
图16展示了ls -Z /data/data目录下的结果。
图16 /data/data目录下ls -Z的结果
是不是和图14中seapp_context文件的type字段描述一样一样的?
4 小试牛刀
下面,笔者将通过修改shell的权限,使其无法设置属性。
先来看shell的te,如下所示:
[external/sepolicy/shell.te]
# Domain for shell processes spawned by ADB
type shell, domain;
type shell_exec, file_type;
#shell属于unconfined_domain,unconfined即是不受限制的意思
unconfined_domain(shell)
# Run app_process.
# XXX Split into its own domain?
app_domain(shell)
unconfied_domain是一个宏,它将shell和如下两个attribute相关联:
[external/sepolicy/te_macros]
#####################################
# unconfined_domain(domain)
# Allow the specified domain to do anything.
#
define(`unconfined_domain', `
typeattribute $1 mlstrustedsubject; #这个和MLS有关
typeattribute $1 unconfineddomain;
')
unconfineddomain权限很多,它的allow语句定义在unconfined.te中:
[external/sepolicy/unconfined.te]
......
allow unconfineddomain property_type:property_service set;
从上面可以看出,shell所关联的unconfineddomain有权限设置属性。所以,我们把它改成:
allow {unconfineddomain -shell} property_type:property_service set;
通过一个“-”号,将shell的权限排除。
然后:
- 我们mmm external/sepolicy,得到sepolicy文件。
- 将其push到/data/security/current/sepolicy目录下
- 接着调用setprop selinux.reload_policy 1,使得init重新加载sepolicy,由于/data目录下有了sepolicy,所以它将使用这个新的。
图17所示为整个测试的例子:
图17 测试结果
根据图17:
- 重新加载sepolicy之前,笔者可通过"setprop wlan.driver.status test_ok"设置该属性的值为test_ok。注意,这个值笔者瞎设的。
- 当通过setprop selinux.reload_policy 1的命令后,init重新加载了sepolicy。
- 从此,笔者再setprop wlan.driver.status 都不能修改该属性的值。
图18所示为dmesg输出,可以看出,当selinux使用了data目录下这个新的sepolicy后,shell的setprop权限就被否了!
图18 dmesg输出
提示:前面曾提到过audit,日志一类的事情。恩,这个日志由kernel输出,可借助诸如audit2allow等host上的工具查看哪些地方有违反权限的地方。系统会将源,目标SContext等信息都打印出来。
三 全文总结
本文对SELinux的核心知识进行了介绍。从入门角度来说,有了这些内容,SELinux大概80%左右的知识都已经介绍,剩下来的工作就是不断去修改和尝试不同的安全配置文件。
然后我们对SEAndroid进行了相关介绍,这部分基本上反映了Android是如何利用这些安全配置文件来构造自己的安全环境的。
从目前AOSP SEAndroid安全配置源文件来看,很多te文件中都使用了如下这样的语句:
图19 permissive定义
其中,permissive关键词表示不用对上述这些type/domain进行MAC监管。permissive一般用于测试某个策略,看是否对整个系统有影响。一旦测验通过,就可以把permissve语句移掉,以真正提升安全。
基于SEAndroid,广大搞机人可以:
- 针对行业,开发更加安全的安全策略。
- 定制MLS,针对企业级或军用级的多层权限管理。
- 不知道google有没有意向实现Modular Policy,这样的话,SELinux灵活性更高。
另外,要提醒读者的是,安全配置需要考虑的东西非常多,稍有不甚,就会影响系统其他模块的运行。比如笔者在研究SELinux时,不小心把Ubuntu的图像界面系统启动不了,后来只能移除SELinux后才解决。这也是为什么SELinux出来这么多年,但是大家好像碰到它的机会很少的原因,因为它的配置实在是太麻烦,很容易出错!
最后,反复提醒读者,一旦修改了策略文件,务必进行全方位,多层面测试。
关于SEAndroid的更多官方说明,请参考
http://source.android.com/devices/tech/security/se-linux.html
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论