应用程序进程是由ActivityManagerService请求Zygote进程创建的。ActivityManagerService在请求Zygote进程创建应用程序进程的时候,会传递很多参数,其中就包括seinfo。当ActivityMangerService需要创建应用程序进程的时候,就会调用ActivityMangerService类的成员函数startProcessLocked,查看这个函数(/home/chris/anrom-5.0/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java):
private final void startProcessLocked(ProcessRecord app, StringhostingType,
String hostingNameStr, String abiOverride, String entryPoint,String[] entryPointArgs) {
........
Process.ProcessStartResult startResult =Process.start(entryPoint,
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion,
app.info.seinfo, requiredAbi,instructionSet,
app.info.dataDir, refreshTheme, entryPointArgs);
.........
}
startProcessLocked通过调用Process类的静态成员函数start来创建应用程序进程,包含了要创建的应用程序进程的各种参数。其中app.info.seinfo从mac_permission.xml中获得。这些参数会通过SocketIPC传递给Zygote进程。最后,Zygote进程会通过调用ZygoteConnection类的成员函数runOnce来执行创建应用程序进程的工作。查看ZygoteConnection类的成员函数runOnce中,与selinux有关的代码(frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java):
boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
......
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid,parsedArgs.gids,
parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal,parsedArgs.seInfo,
parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,
parsedArgs.appDataDir);
.........
}
查看同一文件夹下的Zygote.java中的forkAndSpecialize函数,其中调用到:
int pid = nativeForkAndSpecialize(
uid, gid, gids, debugFlags, rlimits, mountExternal, seInfo,niceName, fdsToClose,
instructionSet, appDataDir);
:
int selinux_android_setcontext(uid_t uid,
int isSystemServer,
const char *seinfo,
const char *pkgname)
{
char*orig_ctx_str = NULL, *ctx_str;
context_tctx = NULL;
int rc =-1;
if(is_selinux_enabled() <= 0)
return0;
rc =getcon(&ctx_str);
if(rc)
gotoerr;
ctx =context_new(ctx_str);
orig_ctx_str= ctx_str;
if(!ctx)
gotooom;
rc =seapp_context_lookup(SEAPP_DOMAIN,uid, isSystemServer, seinfo, pkgname, NULL, ctx);
if (rc ==-1)
gotoerr;
else if (rc== -2)
gotooom;
ctx_str =context_str(ctx);
if(!ctx_str)
gotooom;
rc =security_check_context(ctx_str);
if (rc <0)
gotoerr;
if(strcmp(ctx_str, orig_ctx_str)) {
rc =setcon(ctx_str);
if (rc <0)
gotoerr;
}
rc =0;
..........
}
这个函数的实现过程,和设置文件的安全上下文非常相似,也要通过seapp_context_lookup查看并设置进程的domain,再通过security_check_context检查安全上下文的正确性,最后通过setcon来设置安全上下文。
函数setcon通过external/libselinux/src/procattr.c中的三个宏来定义:
#define setselfattr_def(fn, attr) \
intset##fn(const char * c) \
{ \
returnsetprocattrcon(c, 0, #attr); \
}
#define all_selfattr_def(fn, attr) \
getselfattr_def(fn,attr) \
setselfattr_def(fn, attr)
从这三个宏的定义就可以看出,函数setcon最终通过调用另外一个函数setprocattrcon来设置当前进程的安全上下文,其中,第一个参数c描述的是要设置的安全上下文,第三个参数的值等于"current"。
static int setprocattrcon(const char * context,
pid_t pid, const char *attr)
{
char*path;
int fd,rc;
pid_ttid;
ssize_tret;
interrno_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);
} while (ret< 0 && errno == EINTR);
errno_hold =errno;
close(fd);
errno =errno_hold;
if (ret <0)
return-1;
else
return0;
}
函数setcon实际上就是打开proc文件系统中的/proc/self/task/pid/attr/current文件,并且向其写入参数context所描述的安全上下文。其中,描述的是当前线程的id。向/proc/self/task//attr/current文件写入安全上下文实际上就是将进程的安全上下文保存在内核中用来描述进程的结构体task_struct中。