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的初始化过程的主要内容,如果未能解决你的问题,请参考以下文章