设置文件安全上下文的代码实现 ---- SEAndroid in android 5.x

一.设置ROM中的文件的安全上下文
 
          以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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值