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);
565
566    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();
620
621        // Set up SELinux, loading the SELinux policy.
622        SelinuxSetupKernelLogging();
623        SelinuxInitialize();
624
625        // We're in the kernel domain, so re-exec init to transition to the init domain now
626        // that the SELinux policy has been loaded.
627        if (selinux_android_restorecon("/init", 0) == -1) {
628            PLOG(FATAL) << "restorecon failed of /init failed";
629        }
630
631        setenv("INIT_SECOND_STAGE", "true", 1);
632        ...
644    }
645
646    ...
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 in
204    // 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    }
223
224    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}
255
256bool 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 policy
261    //   with newer versions of platform policy.
262    //
263    // secilc is invoked to compile the above three policy files into a single monolithic policy
264    // file. This file is then loaded into the kernel.
265
266    // Load precompiled policy from vendor image, if a matching policy is found there. The policy
267    // 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;
386
387    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 cache
484// its value.  selinux_android_restorecon() also needs an sehandle for file context look up.  It
485// will create and store its own copy, but selinux_android_set_sehandle() can be used to provide
486// 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];
138
139    memcpy(fc_opts, opts, nopts*sizeof(struct selinux_opt));
140    fc_opts[nopts].type = SELABEL_OPT_BASEONLY;
141    fc_opts[nopts].value = (char *)1;
142
143    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    }
153
154    selinux_log(SELINUX_INFO, "SELinux: Loaded file_contexts\n");
155
156    return sehandle;
157}
158
159struct 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);
53
54static 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;
217
218	if (backend >= ARRAY_SIZE(initfuncs)) {
219		errno = EINVAL;
220		goto out;
221	}
222
223	if (!initfuncs[backend]) {
224		errno = ENOTSUP;
225		goto out;
226	}
227
228	rec = (struct selabel_handle *)malloc(sizeof(*rec));
229	if (!rec)
230		goto out;
231
232	memset(rec, 0, sizeof(*rec));
233	rec->backend = backend;
234	rec->validating = selabel_is_validate_set(opts, nopts);
235
236	rec->digest = selabel_is_digest_set(opts, nopts, rec->digest);
237
238	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;
991
992	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;
1173
1174	data = (struct saved_data *)malloc(sizeof(*data));
1175	if (!data)
1176		return -1;
1177	memset(data, 0, sizeof(*data));
1178
1179	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;
1186
1187	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;
205
206    RegenerateUevents();
207
208    ForkSubProcesses();
209
210    DoRestoreCon();
211
212    WaitForSubProcesses();
213
214    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();
274
275    DeviceHandler device_handler = CreateDeviceHandler();
276    UeventListener uevent_listener;
277
278    if (access(COLDBOOT_DONE, F_OK) != 0) {
279        ColdBoot cold_boot(uevent_listener, device_handler);
280        cold_boot.Run();
281    }
...
289
290    uevent_listener.Poll([&device_handler](const Uevent& uevent) {
291        HandleFirmwareEvent(uevent);
292        device_handler.HandleDeviceEvent(uevent);
293        return ListenerAction::kContinue;
294    });
295
296    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()=>device_handler_.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 list
195    // contain, so we prepend it...
196    std::string path = "/sys" + upath;
197
198    for (const auto& s : sysfs_permissions_) {
199        if (s.MatchWithSubsystem(path, subsystem)) s.SetPermissions(path);
200    }
201
202    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    }
380
381    // if it's not a /dev device, nothing to do
382    if (uevent.major < 0 || uevent.minor < 0) return;
383
384    std::string devpath;
385    std::vector<std::string> links;
386    bool block = false;
387
388    if (uevent.subsystem == "block") {
389        block = true;
390        devpath = "/dev/block/" + Basename(uevent.path);
391
392        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 created
404            // 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 events
412        return;
413    } else {
414        devpath = "/dev/" + Basename(uevent.path);
415    }
416
417    mkdir_recursive(Dirname(devpath), 0755);
418
419    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;
1400
1401    if (selabel_lookup(fc_sehandle, &secontext, pathname, sb->st_mode) < 0)
1402        return 0;  /* no match, but not an error */
1403
1404    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    }
1421
1422    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 "/"
1449
1450static 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;
1474
1475    if (!cross_filesystems) {
1476        ftsflags |= FTS_XDEV;
1477    }
1478
1479    if (is_selinux_enabled() <= 0)
1480        return 0;
1481
1482    __selinux_once(fc_once, file_context_init);
1483
1484    if (!fc_sehandle)
1485        return 0;
...
1556
1557    fts = fts_open(paths, ftsflags, NULL);
1558    if (!fts) {
1559        error = -1;
1560        goto cleanup;
1561    }
1562
1563    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            }
1594
1595            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 itself
1601            }
1602
1603            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 itself
1612            }
1613            /* fall through */
1614        default:
1615            error |= restorecon_sb(ftsent->fts_path, ftsent->fts_statp, nochange, verbose, seinfo, uid);
1616            break;
1617        }
1618    }
1619
1620    // 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}
1646
1647int 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;
909
910	if (!data->nspec) {
911		errno = ENOENT;
912		goto finish;
913	}
914
915	/* 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	}
930
931	sub = selabel_sub_key(data, key);
932	if (sub)
933		key = sub;
934
935	buf = key;
936	file_stem = find_stem_from_file(data, &buf);
937	mode &= S_IFMT;
938
939	/*
940	 * Check for matching specifications in reverse order, so that
941	 * 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 same
946		 * stem as the file AND if the spec in question has no mode
947		 * specified or if the mode matches the file mode then we do
948		 * 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			else
956				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;
991
992	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}
27
28hidden_def(lsetfilecon_raw)
29
30int lsetfilecon(const char *path, const char *context)
31{
32	int ret;
33	char * rcontext;
34
35	if (selinux_trans_to_raw_context(context, &rcontext))
36		return -1;
37
38	ret = lsetfilecon_raw(path, rcontext);
39
40	freecon(rcontext);
41
42	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标识。