selinux label的初始化过程

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了selinux label的初始化过程相关的知识,希望对你有一定的参考价值。

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()=>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 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标识。

以上是关于selinux label的初始化过程的主要内容,如果未能解决你的问题,请参考以下文章

Linux关闭SELinux

初始selinux

centos7 一键初始化shell脚本

centos7部署k8s

自己的记录

阿里云部署Django详细过程