以system.img为例,生成system.img的命令在build/core/Makefile文件中:
BUILT_SYSTEMIMAGE := $(systemimage_intermediates)/system.img
..........
$(BUILT_SYSTEMIMAGE): $(FULL_SYSTEMIMAGE_DEPS)$(INSTALLED_FILES_FILE)
$(call
build-systemimage-target,$@)
可见system.img是由build-systemimage-target命令生成的。
再看build-systemimage-target的定义:
define build-systemimage-target
@echo "Target system fs image: $(1)"
$(call create-system-vendor-symlink)
@mkdir -p $(dir $(1))$(systemimage_intermediates) && rm -rf$(systemimage_intermediates)/system_image_info.txt
$(call
generate-userimage-prop-dictionary,$(systemimage_intermediates)/system_image_info.txt, \
skip_fsck=true)
$(hide) PATH=$(foreachp,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH \
./build/tools/releasetools/build_image.py \
$(TARGET_OUT) $(systemimage_intermediates)/system_image_info.txt$(1) \
|| ( echo "Out of space? the tree size of $(TARGET_OUT) is (MB): "1>&2 ;\
du -sm $(TARGET_OUT) 1>&2;\
echo "The max is $$(( $(BOARD_SYSTEMIMAGE_PARTITION_SIZE) / 1048576)) MB." 1>&2 ;\
mkdir -p $(DIST_DIR); cp $(INSTALLED_FILES_FILE)$(DIST_DIR)/installed-files-rescued.txt; \
exit 1 )
endef
这里执行了两个命令:
第一个命令:
generate-userimage-prop-dictionary,用来生成一个属性文件/system_image_info.txt。它的第一个参数就是system_image_info.txt。
查看这个命令的定义,可以发现这个命令依靠一系列的echo命令来将keyvalue格式的配置写入到文件system_image_info.txt当中。该命令的定义中有一行:
$(hide) echo "selinux_fc=$(SELINUX_FC)" >> $(1)
查看变量SELINUX_FC的赋值:
SELINUX_FC :=$(TARGET_ROOT_OUT)/file_contexts。即OUT/root/file_context,这个文件就是根据external/sepolicy/file_contexts来生成的。
第二个命令:
./build/tools/releasetools/build_image.py,是一个python脚本,用来制作system.img镜像文件。而且这个命令将会使用到第一个命令里生成的属性文件/system_image_info.txt。查看该命令的入口函数:
def main(argv):
if len(argv) != 3:
print__doc__
sys.exit(1)
in_dir = argv[0]
glob_dict_file =argv[1]
out_file =argv[2]
glob_dict =LoadGlobalDict(glob_dict_file)
image_filename =os.path.basename(out_file)
mount_point = ""
if image_filename == "system.img":
mount_point= "system"
elif image_filename == "userdata.img":
mount_point= "data"
elif image_filename == "cache.img":
mount_point= "cache"
elif image_filename == "vendor.img":
mount_point= "vendor"
elif image_filename == "oem.img":
mount_point= "oem"
else:
print>> sys.stderr, "error: unknown image file name ",image_filename
exit(1)
image_properties =
ImagePropFromGlobalDict(glob_dict,mount_point)
if not
BuildImage(in_dir,image_properties, out_file):
print>> sys.stderr, "error: failed to build %s from %s" %(out_file, in_dir)
exit(1)
参数argv[1]指向的就是我们上面提到的属性文件system_image_info.txt,最终保存在本地变量glob_dict_file中。另外一个参数argv[2]指向的要输出的system.img文件路径,最终保存在本地变量out_file中。
函数LoadGlobalDict用来打开属性文件system_image_info.txt,并且将它每一行的key和value提取出来,并且保在字典glob_dict中。注意,这个字典glob_dict包含有一个key等于selinux_fc、value等于file_contexts文件路径的项。
接下来再通过os.path.basename将输出的文件路径out_file的最后一项提取出来,就可以得到image_filename的值为”system.img“,因此再接下来就会得到本地变量mount_point的值为”system“,表示我们现在正在打包的是system.img文件。
函数ImagePropFromGlobalDict用来从字典glob_dict中提取与安装点mount_point相关的项,并且保存在另外一个字典中返回给调用者,在它的实现中有:
common_props = (
"extfs_sparse_flag",
"mkyaffs2_extra_flags",
"selinux_fc",
"skip_fsck",
"verity",
"verity_key",
"verity_signer_cmd",
"transparent_compression_method"
)
for p in common_props:
copy_prop(p,p)
也就是说,该函数返回给调用者的字典包含一个以selinux_fc为key值的项,它的值指向上述分析的file_contexts文件。
main函数的最后调用了函数BuildImage来生成最终的system.img文件,在它的实现中有:
if fc_config is not None:
build_command.append(fc_config)
elif"selinux_fc" in prop_dict:
build_command.append(prop_dict["selinux_fc"])
..............
if "selinux_fc" in prop_dict:
build_command.append(prop_dict["selinux_fc"])
build_command.append(prop_dict["mount_point"])
通过这些命令,系统会编译出一个关联有安全上下文的system.img镜像文件。
二.设置虚拟文件系统的安全上下文
以selinux虚拟文件系统的安装过程为例。在SEAndroid的安全策略,是由external/sepolicy/目录下的文件生成的。查看编译脚本Android.mk,其中有:
sepolicy_build_files := security_classes \
initial_sids \
access_vectors \
global_macros \
mls_macros \
mls \
policy_capabilities \
te_macros \
attributes \
*.te \
roles \
users \
initial_sid_contexts \
fs_use \
genfs_contexts \
port_contexts
查看genfs_contexts,其中有:
genfscon selinuxfs / u:object_r:selinuxfs:s0
可见,selinuxfs的安全上下文,会写在genfs_contexts文件中,并根据Android.mk编译进系统中。
三.设置APP的安全上下文
在Android系统中,使用PackageManagerService安装应用程序,查看frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
1.PackageManagerService()函数中有:
mFoundPolicyFile =SELinuxMMAC.readInstallPolicy();
查看SELinuxMMAC.java文件中的readInstallPolicy()函数:
publicstatic boolean readInstallPolicy() {
// Temp structures to hold the rules while we parse the xmlfile.
// We add all the rules together once we know there's no structuralproblems.
HashMap sigSeinfo = new HashMap();
String defaultSeinfo = null;
FileReader policyFile = null;
try {
policyFile = new FileReader(MAC_PERMISSIONS);
Slog.d(NTAG, "Using policy file " + MAC_PERMISSIONS);
XmlPullParser parser = Xml.newPullParser();
parser.setInput(policyFile);
XmlUtils.beginDocument(parser, "policy");
while (true) {
XmlUtils.nextElement(parser);
if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
break;
}
String tagName = parser.getName();
if ("signer".equals(tagName)) {
String cert = parser.getAttributeValue(null, "signature");
if (cert == null) {
Slog.w(TAG, " without signature at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
Signature signature;
try {
signature = new Signature(cert);
} catch (IllegalArgumentException
e) {
Slog.w(TAG, " with bad signature at "
+ parser.getPositionDescription(), e);
XmlUtils.skipCurrentTag(parser);
continue;
}
Policy policy = readPolicyTags(parser);
if (policy.isValid()) {
Slog.w(NTAG,"'s cert is " + cert + "
is " + policy);
sigSeinfo.put(signature, policy);
}
} else if ("default".equals(tagName)) {
// Value is null if default tag is absent or seinfo tag ismalformed.
defaultSeinfo = readSeinfoTag(parser);
if (DEBUG_POLICY_INSTALL)
Slog.i(TAG, " tag assigned seinfo=" + defaultSeinfo);
} else {
XmlUtils.skipCurrentTag(parser);
}
}
} catch (XmlPullParserException xpe) {
Slog.w(TAG, "Got exception parsing " + MAC_PERMISSIONS, xpe);
return false;
} catch (IOException ioe) {
Slog.w(TAG, "Got exception parsing " + MAC_PERMISSIONS, ioe);
return false;
} finally {
IoUtils.closeQuietly(policyFile);
}
flushInstallPolicy();
sSigSeinfo = sigSeinfo;
sDefaultSeinfo = defaultSeinfo;
return true;
}
这个函数的作用是解析mac_permissions.xml文件。mac_permissions.xml的内容如下:
<signer signature="@ANROM">
<seinfo value="anrom"/>
<package name="com.anrom.aaa">
<seinfo value="aaa"/>
</package>
<package name="com.anrom.aaa">
<seinfo value="bbb"/>
</package>
</signer>
<default>
<seinfo value="default"/>
</default>
readInstallPolicy函数在解析时,会将default标签内的内容解析成一个String类型的defaultSeinfo对象。
而每个signer标签内包含的内容,会被解析成一个HashMap。其中,Signature类主要依靠signature的内容,来标识一个签名。而标签中剩下的部分通过readPolicyTags函数,解析为一个policy对象,这个对象一定包括一个seinfo。还可能包括(可以没有)一些package,seinfo对。查看readPolicyTags的实现:
private static Policy readPolicyTags(XmlPullParser parser)throws
IOException, XmlPullParserException {
int type;
int outerDepth = parser.getDepth();
Policy policy = new Policy();
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
|| parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG
|| type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if ("seinfo".equals(tagName)) {
String seinfo = parseSeinfo(parser);
if (seinfo != null) {
policy.putSeinfo(seinfo);
}
XmlUtils.skipCurrentTag(parser);
} else if ("package".equals(tagName)) {
String pkg = parser.getAttributeValue(null, "name");
if (!validatePackageName(pkg)) {
XmlUtils.skipCurrentTag(parser);
continue;
}
String seinfo = readSeinfoTag(parser);
if (seinfo != null) {
policy.putPkg(pkg, seinfo);
}
} else {
XmlUtils.skipCurrentTag(parser);
}
}
return policy;
}
2.
scanPackageDirtyLI()函数用来解析应用程序的信息,在这个过程中,会分配给应用程序一个seinfo字符串,查看代码,其中有:
if (mFoundPolicyFile) {
SELinuxMMAC.assignSeinfoValue(pkg);
}
查看assignSeinfoValue():
publicstatic boolean assignSeinfoValue(PackageParser.Package pkg) {
// We just want one of the signatures to match.
for (Signature s : pkg.mSignatures) {
if (s == null)
continue;
Policy policy = sSigSeinfo.get(s);
if (policy != null) {
String seinfo = policy.checkPolicy(pkg.packageName);
if (seinfo != null) {
pkg.applicationInfo.seinfo = seinfo;
if (DEBUG_POLICY_INSTALL)
Slog.i(TAG, "package (" + pkg.packageName +
") labeled with seinfo=" + seinfo);
return true;
}
}
}
// If we have a default seinfo value then great, otherwise
// we set a null object and that is what we started with.
pkg.applicationInfo.seinfo = sDefaultSeinfo;
if (DEBUG_POLICY_INSTALL)
Slog.i(TAG, "package (" + pkg.packageName + ") labeled withseinfo="
+ (sDefaultSeinfo == null ? "null" : sDefaultSeinfo));
return (sDefaultSeinfo != null);
}
首先获取应用程序的signature,根据signature来查询policy,如果policy存在,再根据packagename来查询seinfo,如果seinfo存在,则将这个seinfo赋给pkg.applicationInfo.seinfo。如果不存在,则使用policy对应的seinfo。如果policy不存在,则使用defaultSeinfo。
3.在获得seinfo之后,scanPackageDirtyLI()会继续调用createDataDirsLI函数,来创建APP的数据文件,查看这个函数:
private intcreateDataDirsLI(String packageName, int uid, String seinfo){
int[] users = sUserManager.getUserIds();
int res = mInstaller.install(packageName, uid, uid, seinfo);
if (res < 0) {
return res;
}
for (int user : users) {
if (user != 0) {
res =
mInstaller.createUserData(packageName,
UserHandle.getUid(user, uid), user, seinfo);
if (res < 0) {
return res;
}
}
}
return res;
}
查看Installer类的createUserData函数:
public intcreateUserData(String name, int uid, int userId, String seinfo){
StringBuilder builder = new StringBuilder("mkuserdata");
builder.append(' ');
builder.append(name);
builder.append(' ');
builder.append(uid);
builder.append(' ');
builder.append(userId);
builder.append(' ');
builder.append(seinfo != null ? seinfo : "!");
return mInstaller.execute(builder.toString());
}
可见,其实这个函数是通过Installer类的成员函数mInstall通过socket向守护进程installd发送一个install命令,命令的内容为:mkuserdataname uid userIdseinfo。这条命令请求为正在安装的应用程序创建数据目录,并且会将包名、uid和seinfo等信息传递给它。
查看守护进程installd的源码(frameworks/native/cmds/installd/commands.c),其中有install函数,查看其中和selinux有关的部分:
int install(const char *pkgname, uid_t uid, gid_t gid, const char*seinfo)
{
charpkgdir[PKG_PATH_MAX];
if(create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, 0)) {
ALOGE("cannot create package path\n");
return -1;
}
..........
if ((pkgdir,pkgname, seinfo, uid) < 0) {
ALOGE("cannot setfilecon dir '%s': %s\n", pkgdir,strerror(errno));
unlink(libsymlink);
unlink(pkgdir);
return -errno;
}
............
return0;
}
这个函数首先在/data/data目录下创建APP的数据文件目录,然后调用selinux_android_setfilecon来为该目录设置安全上下文。查看selinux_android_setfilecon的实现(external/libselinux/src/android.c):
int selinux_android_setfilecon(const char *pkgdir,
const char*pkgname,
const char*seinfo,
uid_tuid)
{
selinux_log(LOG_TAG, "pkgdir is %s, pkgname is %s,seinfo is%s",pkgdir,pkgname,seinfo);
char*orig_ctx_str = NULL;
char*ctx_str = NULL;
context_tctx = NULL;
int rc =-1;
if(is_selinux_enabled() <= 0)
return0;
rc =(pkgdir, &ctx_str);
if (rc <0)
gotoerr;
ctx =context_new(ctx_str);
orig_ctx_str= ctx_str;
if(!ctx)
gotooom;
rc =seapp_context_lookup(SEAPP_TYPE, uid, 0, 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;
selinux_log(LOG_TAG, "ctx_str is %s, orig_ctx_str is%s",ctx_str,orig_ctx_str);
if(strcmp(ctx_str, orig_ctx_str)) {
rc=
setfilecon(pkgdir, ctx_str);
if (rc <0)
gotoerr;
}
rc =0;
out:
freecon(orig_ctx_str);
context_free(ctx);
returnrc;
err:
selinux_log(SELINUX_ERROR, "%s:
Error settingcontext for pkgdir %s, uid %d: %s\n",
__FUNCTION__, pkgdir, uid, strerror(errno));
rc =-1;
gotoout;
oom:
selinux_log(SELINUX_ERROR, "%s:
Out of memory\n",__FUNCTION__);
rc =-1;
gotoout;
}
该函数的主要流程是:
- 调用函数getfilecon获得要设置新的安全上下文的目录pkgdir原来已有的安全上下文的字符串描述,保存在变量ctx_str。文件或者目录在创建的时候,默认设置的是父目录的安全上下文。
- 调用context_new(ctx_str),建立一个内容为ctx_str的安全上下文ctx。
- 调用seapp_context_lookup,根据seinfo,在seapp_contexts文件中找到对应的type,并将它赋给安全上下文中的type部分。
- 调用security_check_context来验证当前安全上下文ctx的正确性。
- 比较原本的安全上下文和新创建的安全上下文,如果不一致,就调用setfilecon将目录pkgdir的安全上下文设置为新创建的安全上下文。
查看其中调用的几个重要的函数:
- static int seapp_context_lookup(enum seapp_kind kind,
uid_tuid,
intisSystemServer,
const char*seinfo,
const char*pkgname,
const char*path,
context_tctx)
{
const char*username = NULL;
structseapp_context *cur = NULL;
int i;
size_tn;
uid_tuserid;
uid_tappid;
__selinux_once(once,seapp_context_init);
userid = uid / AID_USER;
appid = uid% AID_USER;
if(appid < AID_APP) {
for (n = 0;n < android_id_count; n++) {
if(android_ids[n].aid == appid) {
username =android_ids[n].name;
break;
}
}
if(!username)
gotoerr;
} else if(appid < AID_ISOLATED_START) {
username ="_app";
appid -=AID_APP;
} else{
username ="_isolated";
appid -=AID_ISOLATED_START;
}
for (i = 0;i < ; i++) {
cur =seapp_contexts[i];
if(cur->isSystemServer != isSystemServer)
continue;
if(cur->user.str) {
if(cur->user.is_prefix) {
if(strncasecmp(username, cur->user.str, cur->user.len-1))
continue;
} else{
if(strcasecmp(username, cur->user.str))
continue;
}
}
if(cur->seinfo) {
if (!seinfo|| strcasecmp(seinfo, cur->seinfo))
continue;
}
if(cur->name.str) {
if(!pkgname)
continue;
if(cur->name.is_prefix) {
if(strncasecmp(pkgname, cur->name.str, cur->name.len-1))
continue;
} else{
if(strcasecmp(pkgname, cur->name.str))
continue;
}
}
if(cur->path.str) {
if(!path)
continue;
if(cur->path.is_prefix) {
if(strncmp(path, cur->path.str, cur->path.len-1))
continue;
} else{
if(strcmp(path, cur->path.str))
continue;
}
}
if (kind ==SEAPP_TYPE && !cur->type)
continue;
else if(kind == SEAPP_DOMAIN && !cur->domain)
continue;
if(cur->sebool) {
int value =security_get_boolean_active(cur->sebool);
if (value ==0)
continue;
else if(value == -1) {
selinux_log(SELINUX_ERROR, \
"Could notfind boolean: %s ", cur->sebool);
gotoerr;
}
}
if (kind ==SEAPP_TYPE) {
if(context_type_set(ctx,cur->type))
gotooom;
} else if(kind == SEAPP_DOMAIN) {
if(context_type_set(ctx, cur->domain))
gotooom;
}
if(cur->levelFrom != LEVELFROM_NONE) {
charlevel[255];
switch(cur->levelFrom) {
caseLEVELFROM_APP:
snprintf(level, sizeof level, "s0:c%u,c%u",
appid & 0xff,
256 + (appid>>8 & 0xff));
break;
caseLEVELFROM_USER:
snprintf(level, sizeof level, "s0:c%u,c%u",
512 + (userid & 0xff),
768 + (userid>>8 & 0xff));
break;
caseLEVELFROM_ALL:
snprintf(level, sizeof level, "s0:c%u,c%u,c%u,c%u",
appid & 0xff,
256 + (appid>>8 & 0xff),
512 + (userid & 0xff),
768 + (userid>>8 & 0xff));
break;
default:
gotoerr;
}
if(context_range_set(ctx, level))
gotooom;
} else if(cur->level) {
if(context_range_set(ctx, cur->level))
gotooom;
}
break;
}
if (kind ==SEAPP_DOMAIN && i == nspec) {
selinux_log(SELINUX_ERROR,
"%s: No match for app with uid %d, seinfo %s,name %s\n",
__FUNCTION__, uid, seinfo, pkgname);
if(security_getenforce() == 1)
gotoerr;
}
return0;
err:
return-1;
oom:
return-2;
}
红色部分__selinux_once(once,seapp_context_init)是一个宏,它实际上是利用了pthread库提供的函数pthread_once保证函数seapp_context_init在进程内有且仅有一次会被调用到,适合用来执行初始化工作。它通过调用另外一个函数selinux_android_seapp_context_reload来读取和解析seapp_contexts文件。
int (void)
{
FILE *fp =NULL;
charline_buf[BUFSIZ];
.......
structseapp_context *cur;
set_policy_index();
fp =fopen(seapp_contexts_file[policy_index], "r");
free_seapp_contexts();
.......
seapp_contexts = (struct seapp_context **) calloc(nspec,sizeof(struct seapp_context *));
.........
cur =(struct seapp_context *) calloc(1, sizeof(structseapp_context));
.........
while (1){
name =token;
value =strchr(name, '=');
if (!value){
free_seapp_context(cur);
gotoerr;
}
*value++ =0;
if(!strcasecmp(name, "isSystemServer")){
if(!strcasecmp(value, "true"))
cur->isSystemServer = 1;
else if(!strcasecmp(value, "false"))
cur->isSystemServer = 0;
else {
free_seapp_context(cur);
gotoerr;
}
} else if(!strcasecmp(name, "user")) {
cur->user.str = strdup(value);
if(!cur->user.str) {
free_seapp_context(cur);
gotooom;
}
cur->user.len = strlen(cur->user.str);
if(cur->user.str[cur->user.len-1] == '*')
cur->user.is_prefix = 1;
} else if(!strcasecmp(name, "seinfo")) {
cur->seinfo = strdup(value);
if(!cur->seinfo) {
free_seapp_context(cur);
gotooom;
}
} else if(!strcasecmp(name, "name")) {
cur->name.str = strdup(value);
if(!cur->name.str) {
free_seapp_context(cur);
gotooom;
}
cur->name.len = strlen(cur->name.str);
if(cur->name.str[cur->name.len-1] == '*')
cur->name.is_prefix = 1;
} else if(!strcasecmp(name, "domain")) {
cur->domain = strdup(value);
if(!cur->domain) {
free_seapp_context(cur);
gotooom;
}
} else if(!strcasecmp(name, "type")) {
cur->type= strdup(value);
if(!cur->type) {
free_seapp_context(cur);
gotooom;
}
} else if(!strcasecmp(name, "levelFromUid")){
if(!strcasecmp(value, "true"))
cur->levelFrom = LEVELFROM_APP;
else if(!strcasecmp(value, "false"))
cur->levelFrom = LEVELFROM_NONE;
else {
free_seapp_context(cur);
gotoerr;
}
} else if(!strcasecmp(name, "levelFrom")){
if(!strcasecmp(value, "none"))
cur->levelFrom = LEVELFROM_NONE;
else if(!strcasecmp(value, "app"))
cur->levelFrom = LEVELFROM_APP;
else if(!strcasecmp(value, "user"))
cur->levelFrom = LEVELFROM_USER;
else if(!strcasecmp(value, "all"))
cur->levelFrom = LEVELFROM_ALL;
else {
free_seapp_context(cur);
gotoerr;
}
} else if(!strcasecmp(name, "level")) {
cur->level = strdup(value);
if(!cur->level) {
free_seapp_context(cur);
gotooom;
}
} else if(!strcasecmp(name, "path")) {
cur->path.str = strdup(value);
if(!cur->path.str) {
free_seapp_context(cur);
gotooom;
}
cur->path.len = strlen(cur->path.str);
if(cur->path.str[cur->path.len-1] == '*')
cur->path.is_prefix = 1;
} else if(!strcasecmp(name, "sebool")) {
cur->sebool = strdup(value);
if(!cur->sebool) {
free_seapp_context(cur);
gotooom;
}
} else{
free_seapp_context(cur);
gotoerr;
}
token =strtok_r(NULL, " \t", &saveptr);
if(!token)
break;
}
............
}
绿色部分可以看出android系统中,应用程序在安装的时候分配到的UID由两部分组成,一部分是UserId,另一部分是App Id。常量AID_USER的值定义为100000,用UID除以该值获得的整数就为UserId,而用UID对该值进行取模得到的就为App Id。
AppId小于AID_APP(10000)是保留给系统使用的,它们对应的username保存在数组android_ids中,具体可以参考文件system/core/include/private/android_filesystem_config.h。
AppId大于等于AID_APP(10000)并且小于AID_ISOLATED_START(99000)是分配给应用程序使用的,它们对应的username统一设置为“_app”。
AppId大于等于AID_ISOLATED_START(99000)是分配给运行在独立沙箱(没有任何自己的Permission)的应用程序使用的,它们对应的username统一设置为“_isolated”。
接下来就根据username、isSystemServer、seinfo和pkgname等查找关键字在全局变量seapp_contexts中寻找目标文件的Type或者目标进程的Domain,这是由参数kind的值决定的。如果kind的值等于SEAPP_TYPE,那么就表明要寻找的是文件的Type。如果kind的值等于SEAPP_DOMAIN,那么就表明要查找的是文件的Domain。
最后调用setfilecon设置type。