博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
selinux label的初始化过程
阅读量:6247 次
发布时间:2019-06-22

本文共 27746 字,大约阅读时间需要 92 分钟。

selinux的架构中主要分为:subject和object。subject可以认为是进程,object可以认为是文件(linux中一切皆文件)。在系统代码中,通过撰写sepolicy规则文件(.te),在编译阶段预先为每个关键(subject)进程设置了security context,也为object(文件)设置了security context。那么这些文件是如何生效的呢?

第一步:静态sepolicy文件之间不存在冲突,能正常编译通过;
第二步:编译阶段把sepolicy文件编译成binary文件,并传入到内核;
第三步:内核在启动后以传入的sepolicy文件为原材料,构建起selinux在kernel层的工作框架。

默认第一步已经完成的情况下,重点讨论第二步和第三步的工作流程。

sepolicy文件编译到rom里面后,会保存在rom的某个位置,然后在启动阶段把这个文件传入到kernel中。为了完成这一步需要做以下几个步骤:
1)找到selinuxfs挂载点,一般在/sys/fs/selinux,然后把这个文件写入到该目录下的load文件中;
2)然后导入selinux context文件,加载其中定义的object(文件)的security context;

system/core/init/init.cpp

545int main(int argc, char** argv) {546    ...564    bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);565566    if (is_first_stage) {567    ...586        mount("sysfs", "/sys", "sysfs", 0, NULL);587        mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);588        ...618        // Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).619        global_seccomp();620621        // Set up SELinux, loading the SELinux policy.622        SelinuxSetupKernelLogging();623        SelinuxInitialize();624625        // We're in the kernel domain, so re-exec init to transition to the init domain now626        // that the SELinux policy has been loaded.627        if (selinux_android_restorecon("/init", 0) == -1) {628            PLOG(FATAL) << "restorecon failed of /init failed";629        }630631        setenv("INIT_SECOND_STAGE", "true", 1);632        ...644    }645646    ...683    // Now set up SELinux for second stage.684    SelinuxSetupKernelLogging();685    SelabelInitialize();686    SelinuxRestoreContext();687    ...

其中上述代码中SelinuxInitialize函数完成把规则文件导入到/sys/fs/selinux/dload中,流程如下:

SelinuxInitialize=>LoadPolicy=>LoadSplitPolicy=>FindPrecompiledSplitPolicy=>selinux_android_load_policy_from_fd。代码实现如下:
system/core/init/selinux.cpp

201bool FindPrecompiledSplitPolicy(std::string* file) {202    file->clear();203    // If there is an odm partition, precompiled_sepolicy will be in204    // odm/etc/selinux. Otherwise it will be in vendor/etc/selinux.205    static constexpr const char vendor_precompiled_sepolicy[] =206        "/vendor/etc/selinux/precompiled_sepolicy";207    static constexpr const char odm_precompiled_sepolicy[] =208        "/odm/etc/selinux/precompiled_sepolicy";209    if (access(odm_precompiled_sepolicy, R_OK) == 0) {210        *file = odm_precompiled_sepolicy;211    } else if (access(vendor_precompiled_sepolicy, R_OK) == 0) {212        *file = vendor_precompiled_sepolicy;213    } else {214        PLOG(INFO) << "No precompiled sepolicy";215        return false;216    }217    std::string actual_plat_id;218    if (!ReadFirstLine("/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256", &actual_plat_id)) {219        PLOG(INFO) << "Failed to read "220                      "/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256";221        return false;222    }223224    std::string precompiled_plat_id;225    std::string precompiled_sha256 = *file + ".plat_and_mapping.sha256";226    if (!ReadFirstLine(precompiled_sha256.c_str(), &precompiled_plat_id)) {227        PLOG(INFO) << "Failed to read " << precompiled_sha256;228        file->clear();229        return false;230    }231    if ((actual_plat_id.empty()) || (actual_plat_id != precompiled_plat_id)) {232        file->clear();233        return false;234    }235    return true;236}...252bool IsSplitPolicyDevice() {253    return access(plat_policy_cil_file, R_OK) != -1;254}255256bool LoadSplitPolicy() {257    // IMPLEMENTATION NOTE: Split policy consists of three CIL files:258    // * platform -- policy needed due to logic contained in the system image,259    // * non-platform -- policy needed due to logic contained in the vendor image,260    // * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy261    //   with newer versions of platform policy.262    //263    // secilc is invoked to compile the above three policy files into a single monolithic policy264    // file. This file is then loaded into the kernel.265266    // Load precompiled policy from vendor image, if a matching policy is found there. The policy267    // must match the platform policy on the system image.268    std::string precompiled_sepolicy_file;269    if (FindPrecompiledSplitPolicy(&precompiled_sepolicy_file)) {270        unique_fd fd(open(precompiled_sepolicy_file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));271        if (fd != -1) {272            if (selinux_android_load_policy_from_fd(fd, precompiled_sepolicy_file.c_str()) < 0) {273                LOG(ERROR) << "Failed to load SELinux policy from " << precompiled_sepolicy_file;274                return false;275            }276            return true;277        }278    }367}...378bool LoadPolicy() {379    return IsSplitPolicyDevice() ? LoadSplitPolicy() : LoadMonolithicPolicy();380}...384void SelinuxInitialize() {385    Timer t;386387    LOG(INFO) << "Loading SELinux policy";388    if (!LoadPolicy()) {389        LOG(FATAL) << "Unable to load SELinux policy";390    }391    ...400    if (auto result = WriteFile("/sys/fs/selinux/checkreqprot", "0"); !result) {401        LOG(FATAL) << "Unable to write to /sys/fs/selinux/checkreqprot: " << result.error();402    }403}

然后通过SelabelInitialize()和SelinuxRestoreContext()把系统中的selinux context文件,加载其中定义的object(文件)的security context。

其中SelabelInitialize的函数中完成了两个步骤:
1)首先通过selinux_android_file_context_handle完成对selinux context文件的加载,最后生成一个struct selabel_handle对象,该对象封装了selinux context,并向外提供接口查询各个硬盘中文件(File object)【linux中一切皆文件,除了File还有其他object,如socket,property等】的security context;
2)因为生成这个struct selabel_handle对象的需要很多IO操作,比较耗时,所以把这个对象通过selinux_android_set_sehandle缓存到内存中。

其中函数SelabelInitialize()的定义如下:

system/core/init/selinux.cpp

483// selinux_android_file_context_handle() takes on the order of 10+ms to run, so we want to cache484// its value.  selinux_android_restorecon() also needs an sehandle for file context look up.  It485// will create and store its own copy, but selinux_android_set_sehandle() can be used to provide486// one, thus eliminating an extra call to selinux_android_file_context_handle().487void SelabelInitialize() {488    sehandle = selinux_android_file_context_handle();489    selinux_android_set_sehandle(sehandle);490}

其中selinux_android_file_context_handle的函数定义如下,在该函数中遍历file_context文件,读取其中内容,并通过调用selabel_open函数完成struct selabel_handle的构建。

external/selinux/libselinux/src/android/android_platform.c

9static const struct selinux_opt seopts_file_plat[] = {10    { SELABEL_OPT_PATH, "/system/etc/selinux/plat_file_contexts" },11    { SELABEL_OPT_PATH, "/plat_file_contexts" }12};13static const struct selinux_opt seopts_file_vendor[] = {14    { SELABEL_OPT_PATH, "/vendor/etc/selinux/vendor_file_contexts" },15    { SELABEL_OPT_PATH, "/vendor_file_contexts" },16    // TODO: remove nonplat* when no need to retain backward compatibility.17    { SELABEL_OPT_PATH, "/vendor/etc/selinux/nonplat_file_contexts" },18    { SELABEL_OPT_PATH, "/nonplat_file_contexts" }19};20static const struct selinux_opt seopts_file_odm[] = {21    { SELABEL_OPT_PATH, "/odm/etc/selinux/odm_file_contexts" },22    { SELABEL_OPT_PATH, "/odm_file_contexts" }...133static struct selabel_handle* selinux_android_file_context(const struct selinux_opt *opts,134                                                    unsigned nopts)135{136    struct selabel_handle *sehandle;137    struct selinux_opt fc_opts[nopts + 1];138139    memcpy(fc_opts, opts, nopts*sizeof(struct selinux_opt));140    fc_opts[nopts].type = SELABEL_OPT_BASEONLY;141    fc_opts[nopts].value = (char *)1;142143    sehandle = selabel_open(SELABEL_CTX_FILE, fc_opts, ARRAY_SIZE(fc_opts));144    if (!sehandle) {145        selinux_log(SELINUX_ERROR, "%s: Error getting file context handle (%s)\n",146                __FUNCTION__, strerror(errno));147        return NULL;148    }149    if (!compute_file_contexts_hash(fc_digest, opts, nopts)) {150        selabel_close(sehandle);151        return NULL;152    }153154    selinux_log(SELINUX_INFO, "SELinux: Loaded file_contexts\n");155156    return sehandle;157}158159struct selabel_handle* selinux_android_file_context_handle(void)160{161    struct selinux_opt seopts_file[MAX_FILE_CONTEXT_SIZE];162    int size = 0;163    unsigned int i;164    for (i = 0; i < ARRAY_SIZE(seopts_file_plat); i++) {165        if (access(seopts_file_plat[i].value, R_OK) != -1) {166            seopts_file[size++] = seopts_file_plat[i];167            break;168        }169    }170    for (i = 0; i < ARRAY_SIZE(seopts_file_vendor); i++) {171        if (access(seopts_file_vendor[i].value, R_OK) != -1) {172            seopts_file[size++] = seopts_file_vendor[i];173            break;174        }175    }176    for (i = 0; i < ARRAY_SIZE(seopts_file_odm); i++) {177        if (access(seopts_file_odm[i].value, R_OK) != -1) {178            seopts_file[size++] = seopts_file_odm[i];179            break;180        }181    }182    return selinux_android_file_context(seopts_file, size);183}

其中对selabel_open的定义如下,其中根据入餐unsigned int backend选择对不同类型的selinux context文件的操作,在此通过传入SELABEL_CTX_FILE选择了通过函数selabel_file_init导入file类型的object的security context。

external/selinux/libselinux/src/label.c

50typedef int (*selabel_initfunc)(struct selabel_handle *rec,51              const struct selinux_opt *opts,52              unsigned nopts);5354static selabel_initfunc initfuncs[] = {55  CONFIG_FILE_BACKEND(selabel_file_init),56  CONFIG_MEDIA_BACKEND(selabel_media_init),57  CONFIG_X_BACKEND(selabel_x_init),58  CONFIG_DB_BACKEND(selabel_db_init),59  CONFIG_ANDROID_BACKEND(selabel_property_init),60  CONFIG_ANDROID_BACKEND(selabel_service_init),61};...212struct selabel_handle *selabel_open(unsigned int backend,213                 const struct selinux_opt *opts,214                 unsigned nopts)215{216 struct selabel_handle *rec = NULL;217218 if (backend >= ARRAY_SIZE(initfuncs)) {219     errno = EINVAL;220     goto out;221 }222223 if (!initfuncs[backend]) {224     errno = ENOTSUP;225     goto out;226 }227228 rec = (struct selabel_handle *)malloc(sizeof(*rec));229 if (!rec)230     goto out;231232 memset(rec, 0, sizeof(*rec));233 rec->backend = backend;234 rec->validating = selabel_is_validate_set(opts, nopts);235236 rec->digest = selabel_is_digest_set(opts, nopts, rec->digest);237238 if ((*initfuncs[backend])(rec, opts, nopts)) {239     selabel_close(rec);240     rec = NULL;241 }242out:243 return rec;244}

其中selabel_file_init的函数定义如下,注册了对具体文件security context查询的接口func_lookup=lookup函数:

987static struct selabel_lookup_rec *lookup(struct selabel_handle *rec,988                  const char *key, int type)989{990 struct spec *spec;991992 spec = lookup_common(rec, key, type, false);993 if (spec)994     return &spec->lr;995 return NULL;996}...1168int selabel_file_init(struct selabel_handle *rec,1169                    const struct selinux_opt *opts,1170                    unsigned nopts)1171{1172    struct saved_data *data;11731174    data = (struct saved_data *)malloc(sizeof(*data));1175    if (!data)1176        return -1;1177    memset(data, 0, sizeof(*data));11781179    rec->data = data;1180    rec->func_close = &closef;1181    rec->func_stats = &stats;1182    rec->func_lookup = &lookup;1183    rec->func_partial_match = &partial_match;1184    rec->func_lookup_best_match = &lookup_best_match;1185    rec->func_cmp = &cmp;11861187    return init(rec, opts, nopts);1188}

当完成struct selabel_handle对象的初始化以后,就需要通过selinux_android_set_sehandle缓存起来,在后续对系统文件设置security context时会用到这个缓存的对象。函数定义如下:

external/selinux/libselinux/src/android/android_platform.c

1661void selinux_android_set_sehandle(const struct selabel_handle *hndl)1662{1663      fc_sehandle = (struct selabel_handle *) hndl;1664}

至此完成了SelabelInitialize()的定义,实现了把文件(File object)security context缓存到了内存(用户态进程Init)中,并对外提供了查询接口。

那么在完成文件(File Object)security context导入后,而在kernel mode中尚未对这部分内存做任何处理,当在用户态用某个binary文件作为entry pointer启动一个进程的时候,内核中无法对这个进程做selinux context标识。所以还需要在kernel中利用这部分内存的内容,方便以后在内核态对新建进程进行selinux context标识,以及文件访问时进行内核态的约束。

完成这一步需要在各个分区挂载的时候为分区内的文件标识其对应的security context。系统启动过程中分区挂载的消息是通过netlink的方式通知到ueventd,然后在ueventd进程内处理分区挂载的消息。所以为静态的文件(File Object)标识security context的工作是在ueventd进程中实现的(有关netlink和ueventd的启动问题不在此展开)。参考ueventd代码可以看到对静态文件(File Object)标识其security context的流程如下:
1)ueventd通过ColdBoot类,注册DeviceHandler对象处理uevent,当有uevent从kernel发出来的时候,DeviceHandler::HandleDeviceEvent;
2)当监听到有分区挂载的uevent时,会触发下述函数调用流程完成对正在挂载分区内的文件(File Object)标识其security context:DeviceHandler::HandleDeviceEvent=>DeviceHandler::FixupSysPermissions=>selinux_android_restorecon(XXX, SELINUX_ANDROID_RESTORECON_RECURSE);
上述流程对应的代码如下:

system/core/init/ueventd.cpp

165void ColdBoot::DoRestoreCon() {166    selinux_android_restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);167    device_handler_.set_skip_restorecon(false);168}...203void ColdBoot::Run() {204    android::base::Timer cold_boot_timer;205206    RegenerateUevents();207208    ForkSubProcesses();209210    DoRestoreCon();211212    WaitForSubProcesses();213214    close(open(COLDBOOT_DONE, O_WRONLY | O_CREAT | O_CLOEXEC, 0000));215    LOG(INFO) << "Coldboot took " << cold_boot_timer.duration().count() / 1000.0f << " seconds";216}...260int ueventd_main(int argc, char** argv) {...273    SelabelInitialize();274275    DeviceHandler device_handler = CreateDeviceHandler();276    UeventListener uevent_listener;277278    if (access(COLDBOOT_DONE, F_OK) != 0) {279        ColdBoot cold_boot(uevent_listener, device_handler);280        cold_boot.Run();281    }...289290    uevent_listener.Poll([&device_handler](const Uevent& uevent) {291        HandleFirmwareEvent(uevent);292        device_handler.HandleDeviceEvent(uevent);293        return ListenerAction::kContinue;294    });295296    return 0;297}

根据上述代码可以看到代码逻辑如下:

1)在ueventd_main函数中首先调用了SelabelInitialize;
2)然后创建了DeviceHandler对象device_handler,然后创建了ColdBoot对象cold_boot,将device_handler注册到cold_boot中;
3)然后通过cold_boot.Run()=>cold_boot.DoRestoreCon()=>devicehandler.set_skip_restorecon(false);
4)注册devicehandler.HandleDeviceEvent(uevent)来处理uevent事件。

至此完成了对各个分区内部各个文件(File Object)的进行security context标识所需要的所有准备工作,一旦有uevent事件触发,会进入DeviceHandler::HandleDeviceEvent处理该事件。处理uevent事件的代码逻辑如下:

system/core/init/devices.cpp

192void DeviceHandler::FixupSysPermissions(const std::string& upath,193                                        const std::string& subsystem) const {194    // upaths omit the "/sys" that paths in this list195    // contain, so we prepend it...196    std::string path = "/sys" + upath;197198    for (const auto& s : sysfs_permissions_) {199        if (s.MatchWithSubsystem(path, subsystem)) s.SetPermissions(path);200    }201202    if (!skip_restorecon_ && access(path.c_str(), F_OK) == 0) {203        LOG(VERBOSE) << "restorecon_recursive: " << path;204        if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {205            PLOG(ERROR) << "selinux_android_restorecon(" << path << ") failed";206        }207    }208}...376void DeviceHandler::HandleDeviceEvent(const Uevent& uevent) {377    if (uevent.action == "add" || uevent.action == "change" || uevent.action == "online") {378        FixupSysPermissions(uevent.path, uevent.subsystem);379    }380381    // if it's not a /dev device, nothing to do382    if (uevent.major < 0 || uevent.minor < 0) return;383384    std::string devpath;385    std::vector
links;386 bool block = false;387388 if (uevent.subsystem == "block") {389 block = true;390 devpath = "/dev/block/" + Basename(uevent.path);391392 if (StartsWith(uevent.path, "/devices")) {393 links = GetBlockDeviceSymlinks(uevent);394 }395 } else if (const auto subsystem =396 std::find(subsystems_.cbegin(), subsystems_.cend(), uevent.subsystem);397 subsystem != subsystems_.cend()) {398 devpath = subsystem->ParseDevPath(uevent);399 } else if (uevent.subsystem == "usb") {400 if (!uevent.device_name.empty()) {401 devpath = "/dev/" + uevent.device_name;402 } else {403 // This imitates the file system that would be created404 // if we were using devfs instead.405 // Minors are broken up into groups of 128, starting at "001"406 int bus_id = uevent.minor / 128 + 1;407 int device_id = uevent.minor % 128 + 1;408 devpath = StringPrintf("/dev/bus/usb/%03d/%03d", bus_id, device_id);409 }410 } else if (StartsWith(uevent.subsystem, "usb")) {411 // ignore other USB events412 return;413 } else {414 devpath = "/dev/" + Basename(uevent.path);415 }416417 mkdir_recursive(Dirname(devpath), 0755);418419 HandleDevice(uevent.action, devpath, block, uevent.major, uevent.minor, links);420}

如上述代码中的system/core/init/devices.cpp:204行进入selinux_android_restorecon函数对该分区下的所有文件(File Object)进行security context标识。selinux_android_restorecon的函数调用流程如下:

external/selinux/libselinux/src/android/android_platform.c

1393static int restorecon_sb(const char *pathname, const struct stat *sb,1394                         bool nochange, bool verbose,1395                         const char *seinfo, uid_t uid)1396{1397    char *secontext = NULL;1398    char *oldsecontext = NULL;1399    int rc = 0;14001401    if (selabel_lookup(fc_sehandle, &secontext, pathname, sb->st_mode) < 0)1402        return 0;  /* no match, but not an error */14031404    if (lgetfilecon(pathname, &oldsecontext) < 0)1405        goto err;...1413    if (!strncmp(pathname, DATA_DATA_PREFIX, sizeof(DATA_DATA_PREFIX)-1) ||1414        !strncmp(pathname, DATA_USER_PREFIX, sizeof(DATA_USER_PREFIX)-1) ||1415        !strncmp(pathname, DATA_USER_DE_PREFIX, sizeof(DATA_USER_DE_PREFIX)-1) ||1416        !fnmatch(EXPAND_USER_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME) ||1417        !fnmatch(EXPAND_USER_DE_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME)) {1418        if (pkgdir_selabel_lookup(pathname, seinfo, uid, &secontext) < 0)1419            goto err;1420    }14211422    if (strcmp(oldsecontext, secontext) != 0) {1423        if (verbose)1424            selinux_log(SELINUX_INFO,1425                        "SELinux:  Relabeling %s from %s to %s.\n", pathname, oldsecontext, secontext);1426        if (!nochange) {1427            if (lsetfilecon(pathname, secontext) < 0)1428                goto err;1429        }1430    }...1445}1447#define SYS_PATH "/sys"1448#define SYS_PREFIX SYS_PATH "/"14491450static int selinux_android_restorecon_common(const char* pathname_orig,1451                                             const char *seinfo,1452                                             uid_t uid,1453                                             unsigned int flags)1454{1455    bool nochange = (flags & SELINUX_ANDROID_RESTORECON_NOCHANGE) ? true : false;1456    bool verbose = (flags & SELINUX_ANDROID_RESTORECON_VERBOSE) ? true : false;1457    bool recurse = (flags & SELINUX_ANDROID_RESTORECON_RECURSE) ? true : false;1458    bool force = (flags & SELINUX_ANDROID_RESTORECON_FORCE) ? true : false;1459    bool datadata = (flags & SELINUX_ANDROID_RESTORECON_DATADATA) ? true : false;1460    bool skipce = (flags & SELINUX_ANDROID_RESTORECON_SKIPCE) ? true : false;1461    bool cross_filesystems = (flags & SELINUX_ANDROID_RESTORECON_CROSS_FILESYSTEMS) ? true : false;1462    bool issys;1463    bool setrestoreconlast = true;1464    struct stat sb;1465    struct statfs sfsb;1466    FTS *fts;1467    FTSENT *ftsent;1468    char *pathname = NULL, *pathdnamer = NULL, *pathdname, *pathbname;1469    char * paths[2] = { NULL , NULL };1470    int ftsflags = FTS_NOCHDIR | FTS_PHYSICAL;1471    int error, sverrno;1472    char xattr_value[FC_DIGEST_SIZE];1473    ssize_t size;14741475    if (!cross_filesystems) {1476        ftsflags |= FTS_XDEV;1477    }14781479    if (is_selinux_enabled() <= 0)1480        return 0;14811482    __selinux_once(fc_once, file_context_init);14831484    if (!fc_sehandle)1485        return 0;...15561557    fts = fts_open(paths, ftsflags, NULL);1558    if (!fts) {1559        error = -1;1560        goto cleanup;1561    }15621563    error = 0;1564    while ((ftsent = fts_read(fts)) != NULL) {1565        switch (ftsent->fts_info) {1566        case FTS_DC:1567            selinux_log(SELINUX_ERROR,1568                        "SELinux:  Directory cycle on %s.\n", ftsent->fts_path);1569            errno = ELOOP;1570            error = -1;1571            goto out;1572        case FTS_DP:1573            continue;1574        case FTS_DNR:1575            selinux_log(SELINUX_ERROR,1576                        "SELinux:  Could not read %s: %s.\n", ftsent->fts_path, strerror(errno));1577            fts_set(fts, ftsent, FTS_SKIP);1578            continue;1579        case FTS_NS:1580            selinux_log(SELINUX_ERROR,1581                        "SELinux:  Could not stat %s: %s.\n", ftsent->fts_path, strerror(errno));1582            fts_set(fts, ftsent, FTS_SKIP);1583            continue;1584        case FTS_ERR:1585            selinux_log(SELINUX_ERROR,1586                        "SELinux:  Error on %s: %s.\n", ftsent->fts_path, strerror(errno));1587            fts_set(fts, ftsent, FTS_SKIP);1588            continue;1589        case FTS_D:1590            if (issys && !selabel_partial_match(fc_sehandle, ftsent->fts_path)) {1591                fts_set(fts, ftsent, FTS_SKIP);1592                continue;1593            }15941595            if (skipce &&1596                (!strncmp(ftsent->fts_path, DATA_SYSTEM_CE_PREFIX, sizeof(DATA_SYSTEM_CE_PREFIX)-1) ||1597                 !strncmp(ftsent->fts_path, DATA_MISC_CE_PREFIX, sizeof(DATA_MISC_CE_PREFIX)-1))) {1598                // Don't label anything below this directory.1599                fts_set(fts, ftsent, FTS_SKIP);1600                // but fall through and make sure we label the directory itself1601            }16021603            if (!datadata &&1604                (!strcmp(ftsent->fts_path, DATA_DATA_PATH) ||1605                 !strncmp(ftsent->fts_path, DATA_USER_PREFIX, sizeof(DATA_USER_PREFIX)-1) ||1606                 !strncmp(ftsent->fts_path, DATA_USER_DE_PREFIX, sizeof(DATA_USER_DE_PREFIX)-1) ||1607                 !fnmatch(EXPAND_USER_PATH, ftsent->fts_path, FNM_LEADING_DIR|FNM_PATHNAME) ||1608                 !fnmatch(EXPAND_USER_DE_PATH, ftsent->fts_path, FNM_LEADING_DIR|FNM_PATHNAME))) {1609                // Don't label anything below this directory.1610                fts_set(fts, ftsent, FTS_SKIP);1611                // but fall through and make sure we label the directory itself1612            }1613            /* fall through */1614        default:1615            error |= restorecon_sb(ftsent->fts_path, ftsent->fts_statp, nochange, verbose, seinfo, uid);1616            break;1617        }1618    }16191620    // Labeling successful. Mark the top level directory as completed.1621    if (setrestoreconlast && !nochange && !error)1622        setxattr(pathname, RESTORECON_LAST, fc_digest, sizeof fc_digest, 0);...1645}16461647int selinux_android_restorecon(const char *file, unsigned int flags)1648{1649    return selinux_android_restorecon_common(file, NULL, -1, flags);1650}

由上述代码可以看到,当进入到selinux_android_restorecon函数以后,会通过restorecon_sb=>selabel_lookup=>selabel_lookup_common=>selabel_handle::func_lookup,而selabel_handle::func_lookup被初始化为一个静态函数,其函数定义如下:

external/selinux/libselinux/src/label_file.c

894static struct spec *lookup_common(struct selabel_handle *rec,895                      const char *key,896                      int type,897                      bool partial)898{899 struct saved_data *data = (struct saved_data *)rec->data;900 struct spec *spec_arr = data->spec_arr;901 int i, rc, file_stem;902 mode_t mode = (mode_t)type;903 const char *buf;904 struct spec *ret = NULL;905 char *clean_key = NULL;906 const char *prev_slash, *next_slash;907 unsigned int sofar = 0;908 char *sub = NULL;909910 if (!data->nspec) {911     errno = ENOENT;912     goto finish;913 }914915 /* Remove duplicate slashes */916 if ((next_slash = strstr(key, "//"))) {917     clean_key = (char *) malloc(strlen(key) + 1);918     if (!clean_key)919         goto finish;920     prev_slash = key;921     while (next_slash) {922         memcpy(clean_key + sofar, prev_slash, next_slash - prev_slash);923         sofar += next_slash - prev_slash;924         prev_slash = next_slash + 1;925         next_slash = strstr(prev_slash, "//");926     }927     strcpy(clean_key + sofar, prev_slash);928     key = clean_key;929 }930931 sub = selabel_sub_key(data, key);932 if (sub)933     key = sub;934935 buf = key;936 file_stem = find_stem_from_file(data, &buf);937 mode &= S_IFMT;938939 /*940  * Check for matching specifications in reverse order, so that941  * the last matching specification is used.942  */943 for (i = data->nspec - 1; i >= 0; i--) {944     struct spec *spec = &spec_arr[i];945     /* if the spec in question matches no stem or has the same946      * stem as the file AND if the spec in question has no mode947      * specified or if the mode matches the file mode then we do948      * a regex check        */949     if ((spec->stem_id == -1 || spec->stem_id == file_stem) &&950         (!mode || !spec->mode || mode == spec->mode)) {951         if (compile_regex(data, spec, NULL) < 0)952             goto finish;953         if (spec->stem_id == -1)954             rc = regex_match(spec->regex, key, partial);955         else956             rc = regex_match(spec->regex, buf, partial);957         if (rc == REGEX_MATCH) {958             spec->matches++;959             break;960         } else if (partial && rc == REGEX_MATCH_PARTIAL)961             break;970 }...985}987static struct selabel_lookup_rec *lookup(struct selabel_handle *rec,988                  const char *key, int type)989{990 struct spec *spec;991992 spec = lookup_common(rec, key, type, false);993 if (spec)994     return &spec->lr;995 return NULL;996}

从上述代码可以看到,对分区内文件(File Object)进行security context标识时,最终会查询selabel_handle::data中的数据,这些数据是之前通过SelabelInitialize函数调用时,遍历系统中的对文件(File Object)的selinux security context定义的file_contxt文件得到结果。最终返回的是file_context文件中为一些文件(File Object)定义的security context(一个字符串)。

在得到这个security context以后,然后通过lsetfilecon调用将这个security context和文件路径相关联。对lsetfilecon的函数定义如下:

external/selinux/libselinux/src/lsetfilecon.c

10int lsetfilecon_raw(const char *path, const char * context)11{12  int rc = lsetxattr(path, XATTR_NAME_SELINUX, context, strlen(context) + 1,13           0);14  if (rc < 0 && errno == ENOTSUP) {15      char * ccontext = NULL;16      int err = errno;17      if ((lgetfilecon_raw(path, &ccontext) >= 0) &&18          (strcmp(context,ccontext) == 0)) {19          rc = 0;20      } else {21          errno = err;22      }23      freecon(ccontext);24  }25  return rc;26}2728hidden_def(lsetfilecon_raw)2930int lsetfilecon(const char *path, const char *context)31{32  int ret;33  char * rcontext;3435  if (selinux_trans_to_raw_context(context, &rcontext))36      return -1;3738  ret = lsetfilecon_raw(path, rcontext);3940  freecon(rcontext);4142  return ret;43}

由上述代码可见,最终是通过lsetxattr系统调用进入kernel mode完成了对具体文件的security context的初始化。调用流程如下:

lsetxattr=>__NR_lsetxattr=>系统调用lsetxattr=>
path_setxattr=>setxattr=>vfs_setxattr=>security_inode_setxattr
该函数“security_inode_setxattr”函数调用被kernel中的selinux模块hook成了selinux_inode_setxattr,而selinux_inode_setxattr在kernel中的被定义,实现了对这个文件节点对象中inode_security_struct对象的初始化。这个对象会在打开这个文件时使用,其中的inode_security_struct其中包含了一个security id,被用作该文件节点在内核中的selinux security context。

当以该文件作为entry point启动进程时,会以该security id作为新建进程的的security domain id。

如果当其他进程访问这个文件时,会拿该文件的security id与caller进程的domain 的security id为参数,查看现有的selinux规则中是否允许这个文件操作。

至此完成了在系统启动过程中对各个分区中的每个文件(File Object)的security context标识。

转载于:https://blog.51cto.com/2559640/2410131

你可能感兴趣的文章
Vijos1404 遭遇战 最短路,dijkstra,堆
查看>>
svn解决与优化帮助
查看>>
SQL update select结合语句详解及应用
查看>>
[转]阿里要走102年,阿里的工程师能走多远呢?
查看>>
《算法导论》读书笔记之第15章 动态规划—最长公共子序列
查看>>
从$a_n=f(n)$的角度理解数列中的表达式$a_{n+1}=\frac{k}{a_n}$
查看>>
Redis以及Redis的php扩展安装无错版
查看>>
总结性博客作业
查看>>
Windows Phone 8初学者开发—第11部分:设置SounBoard应用程序
查看>>
欧拉图和哈密顿图
查看>>
解线性方程组
查看>>
Python:pandas之DataFrame常用操作
查看>>
Appium移动自动化测试之—基于java的iOS环境搭建
查看>>
NOIP前的刷题记录
查看>>
洛谷P1973 [NOI2011]Noi嘉年华(决策单调性)
查看>>
书签(Bookmarks)
查看>>
Java 信号量 Semaphore 介绍
查看>>
Ubuntu常用软件安装与使用
查看>>
Anroid开发中常用快捷键
查看>>
RecyclerView分隔线定制
查看>>