SEAndroid安全机制中的进程安全上下文关联分析模板.docx
《SEAndroid安全机制中的进程安全上下文关联分析模板.docx》由会员分享,可在线阅读,更多相关《SEAndroid安全机制中的进程安全上下文关联分析模板.docx(23页珍藏版)》请在冰豆网上搜索。
SEAndroid安全机制中的进程安全上下文关联分析模板
SEAndroid安全机制中的进程安全上下文关联分析
前面一篇文章分析了文件安全上下文关联过程。
但是在SEAndroid中,除了要给文件关联安全上下文外,还需要给进程关联安全上下文,因为只有当进程和文件都关联安全上下文之后,SEAndroid安全策略才能发挥作用。
也就是说,当一个进程试图访问一个文件时,SEAndroid会将进程和文件的安全上下文提取出来,根据安全策略规则,决定是否允许访问。
本文就详细分析SEAndroid的进程安全上下文的关联过程。
在传统的Linux系统中,每一个应用程序都对应有一个可执行文件。
在这种情况下,我们就可以在安全策略中设定一个规则:
当一个可执行文件加载到一个进程中执行时,该进程的安全上下文就设置为指定的值。
也就是说,我们可以在安全策略中静态地为进程设置安全上下文。
然而,这种进程安全上下文设置方式不适合于Android系统中的应用程序进程。
从前面和这两篇文章可以知道,Android系统中的应用程序进程都是由Zygote进程fork出来。
这些应用程序进程被Zygote进程fork出来之后,不像传统Linux的应用程序进程一样,会通过exec系统调用将对应的可执行文件加载起来执行。
这样就会使得Zygote进程及其创建的所有应用程序进程对应的可执行文件均为/system/bin/app_process。
由于我们却需要给不同的应用程序设置不同的安全上下文,以便给它们赋予不同的安全权限,因此我们需要在应用程序进程创建出来之后动态地设置它的安全上下文。
根据上面的描述,我们就总结出,在SEAndroid安全机制中,进程的安全上下文设置分为静态和动态两种方式,如图1所示:
接下来,我们就分别描述这两种进程安全上下文设置方式。
1.为独立进程静态地设置安全上下文
Android系统的第一个进程是init,其它所有的进程都是由init进程直接或者间接fork出来的。
我们在前面一篇文章提到,一个新创建的文件的安全上下文在默认情况下来自于其父目录。
与此类似,一个新创建的进程的安全上下文在默认情况下来自于其父进程。
因此,我们就先看看系统中的第一个进程init的安全上下文是如何设置的。
查看init进程的启动脚本system/core/rootdir/init.rc,可以看到以下的内容:
[plain]viewplaincopy在CODE上查看代码片派生到我的代码片
onearly-init
......
#Setthesecuritycontextfortheinitprocess.
#Thisshouldoccurbeforeanythingelse(e.g.ueventd)isstarted.
setconu:
r:
init:
s0
......
这段脚本的意思是init进程启动之后就马上调用函数setcon将自己的安全上下文设置为“u:
r:
init:
s0”,即将init进程的domain指定为init。
接下来我们再看看init这个domain的定义,在external/sepolicy/init.te文件中:
[plain]viewplaincopy在CODE上查看代码片派生到我的代码片
#initswitchestoinitdomain(viainit.rc).
typeinit,domain;
permissiveinit;
#initisunconfined.
unconfined_domain(init)
tmpfs_domain(init)
#addaruletohandleunlabelledmounts
allowinitunlabeled:
filesystemmount;
第一个type语句将domain设置为init的属性,这意味着init是用来描述进程的安全上下文的。
第二个permissive语句指定当domain为init的进程违反SEAndroid安全策略访问资源时,只进行日志输出,而不是拒绝执行。
由于这里列出来的内容是来自Android4.3的,而Android4.3开启的是Permissive的SEAndroid模式,因此这里会看到这样的一个permissive语句。
第三个unconfined_domain语句是一个宏,定义在external/sepolicy/te_macros文件中,用来指定init是一个不受限制的domain,即它可以访问系统中的大部分资源。
它的定义如下所示:
[plain]viewplaincopy在CODE上查看代码片派生到我的代码片
#####################################
#unconfined_domain(domain)
#Allowthespecifieddomaintodoanything.
#
define(`unconfined_domain',`
typeattribute$1mlstrustedsubject;
typeattribute$1unconfineddomain;
')
第四个tmpfs_domain语句也是定义在external/sepolicy/te_macros文件中的一个宏,用来指定当domain为init的进程在type为tmpfs的目录中创建文件时,将新创建的文件的type设置为init_tmpfs,并且允许domain为init的进程对它们进行读和执行。
它的定义如下所示:
[plain]viewplaincopy在CODE上查看代码片派生到我的代码片
#####################################
#tmpfs_domain(domain)
#Defineandallowaccesstoauniquetypefor
#thisdomainwhencreatingtmpfs/shmem/ashmemfiles.
define(`tmpfs_domain',`
type$1_tmpfs,file_type;
type_transition$1tmpfs:
file$1_tmpfs;
#MapwithPROT_EXEC.
allow$1$1_tmpfs:
file{readexecuteexecmod};
')
第5个allow语句允许domain为init的进程mount未指定安全上下文的文件系统时,将其安全上下文设置为unlabeled。
上面列出的脚本就指明了init进程的安全上下文,以及它所具有的SEAndroid权限。
接下来我们就再来看看负责创建应用程序进程的Zygote进程的安全上下文的设置过程。
Zygote进程是由init进程创建的,它的启动命令定义在文件system/core/rootdir/init.rc中,如下所示:
[plain]viewplaincopy在CODE上查看代码片派生到我的代码片
servicezygote/system/bin/app_process-Xzygote/system/bin--zygote--start-system-server
classmain
socketzygotestream660rootsystem
onrestartwrite/sys/android_power/request_statewake
onrestartwrite/sys/power/stateon
onrestartrestartmedia
onrestartrestartnetd
这意味着Zygote进程对应的可执行文件为/system/bin/app_process。
通过检查external/sepolicy/file_contexts,我们可以发现文件/system/bin/app_process的安全上下文为“u:
object_r:
zygote_exec:
s0”,如下所示:
[plain]viewplaincopy在CODE上查看代码片派生到我的代码片
/system/bin/app_processu:
object_r:
zygote_exec:
s0
也就是说,文件/system/bin/app_process的type为zygote_exec。
在external/sepolicy/zygote.te文件中,定义了一个名称为zygote的domain,以及名称为zygote_exec的type,如下所示:
[plain]viewplaincopy在CODE上查看代码片派生到我的代码片
#zygote
typezygote,domain;
typezygote_exec,exec_type,file_type;
permissivezygote;
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的权限,它的定义如下所示:
[plain]viewplaincopy在CODE上查看代码片派生到我的代码片
#####################################
#init_daemon_domain(domain)
#Setupatransitionfrominittothedaemondomain
#uponexecutingitsbinary.
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中,如下所示:
[plain]viewplaincopy在CODE上查看代码片派生到我的代码片
#####################################
#domain_auto_trans(olddomain,type,newdomain)
#Automaticallytransitionfromolddomaintonewdomain
#uponexecutingafilelabeledwithtype.
#
define(`domain_auto_trans',`
#Allowthenecessarypermissions.
domain_trans($1,$2,$3)
#Makethetransitionoccurbydefault.
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,它的定义如下所示:
[plain]viewplaincopy在CODE上查看代码片派生到我的代码片
#####################################
#domain_trans(olddomain,type,newdomain)
#Allowatransitionfromolddomaintonewdomain
#uponexecutingafilelabeledwithtype.
#Thisonlyallowsthetransition;itdoesnot
#causeittooccurautomatically-usedomain_auto_trans
#ifthatiswhatyouwant.
#
define(`domain_trans',`
#Olddomainmayexecthefileandtransitiontothenewdomain.
allow$1$2:
file{getattropenreadexecute};
allow$1$3:
processtransition;
#Newdomainisenteredbyexecutingthefile.
allow$3$2:
file{entrypointreadexecute};
#NewdomaincansendSIGCHLDtoitscaller.
allow$3$1:
processsigchld;
#EnableAT_SECURE,i.e.libcsecuremode.
dontaudit$1$3:
processnoatsecure;
#XXXdontauditcandidatebutrequiresfurtherstudy.
allow$1$3:
process{siginhrlimitinh};
')
其中,最重要的是以下两个allow语句:
[plain]viewplaincopy在CODE上查看代码片派生到我的代码片
allow$1$3:
processtransition;
allow$3$2:
file{entrypointreadexecute};
第一个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.为应用程序进程设置安全上下文
从前面一篇文章可以知道,应用程序进程是由ActivityManagerService请求Zygote进程创建的。
ActivityManagerService在请求Zygote进程创建应用程序进程的时候,会传递很多参数,例如应用程序在安装时分配到的uid和gid。
增加了SEAndroid安全机制之后,ActivityManagerService传递给Zygote进程的参数包含了一个seinfo。
这个seinfo与我们在前面一文中介绍的seinfo是一样的,不过它的作用是用来设置应用程序进程的安全上下文,而不是设置应用程序数据文件的安全上下文。
接下来我们就分析应用程序进程的安全上下文设置过程。
从前面一文的Step1可以知道,当ActivityMangerService需要创建应用程序进程的时候,就会调用ActivityMangerService类的成员函数startProcessLocked,它的实现如下所示:
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
publicfinalclassActivityManagerServiceextendsActivityManagerNative
implementsWatchdog.Monitor,BatteryStatsImpl.BatteryCallback{
......
privatefinalvoidstartProcessLocked(ProcessRecordapp,
StringhostingType,StringhostingNameStr){
......
try{
......
//Starttheprocess.Itwilleithersucceedandreturnaresultcontaining
//thePIDofthenewprocess,orelsethrowaRuntimeException.
Process.ProcessStartResultstartResult=Process.start("android.app.ActivityThread",
app.processName,uid,uid,gids,debugFlags,mountExternal,
app.info.targetSdkVersion,app.info.seinfo,null);
......
}catch(RuntimeExceptione){
......
}
}
......
}
这个函数定义在文件frameworks/base/services/java/com/android/server/am/ActivityManagerService.java中。
参数app指向的是一个ProcessRecord对象,用来描述正在创建的应用程序进程。
其中,它的成员变量info指向的是一个ApplicationInfo对象。
从前面一文可以知道,这个ApplicationInfo对象有一个类型为String的成员变量seinfo,是在应用程序安装的时候通过解析文件mac_permissions.xml获得的。
ActivityManagerService类的成员函数startProcessLocked通过调用Process类的静态成员函数start来创建应用程序进程,其中就包含了要创建的应用程序进程的各种参数。
从前面一篇文章可以知道,这些参数会通过SocketIPC传递给Zygote进程。
最后,Zygote进程会通过调用ZygoteConnection类的成员函数runOnce来执行创建应用程序进程的工作。
ZygoteConnection类的成员函数runOnce的实现如下所示:
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
classZygoteConnection{
......
booleanrunOnce()throwsZygoteInit.MethodAndArgsCaller{
......
try{
args=readArgumentList();
......
}catch(IOExceptionex){
......
}
......
try{
parsedArgs=newArguments(args);
......
pid=Zygote.forkAndSpecialize(parsedArgs.uid,parsedArgs.gid,parsedArgs.gids,
parsedArgs.debugFlags,rlimits,parsedArgs.mountExternal,parsedArgs.seInfo,
parsedArgs.niceName);
}catch(IOExceptionex){
......
}catch(ErrnoExceptionex){
......
}catch(IllegalArgumentExceptionex){
......
}catch(ZygoteSecurityExceptionex){
......
}
......
}
......
}
这个函数定义在文件frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java中。
ZygoteConnection类的成员函数runOnce首先是通过调用另外一个成员函数readArgumentList读取ActivityManagerService发送过来的应用程序进程创建参数args,接着再创建一个Arguments对象来解析该参数。
解析后得到的参数传递给Zygote类的静态成员函数forkAndSpecialize,以便后者可以执行创建应用程序进程的