前提:因为不用linux用户有不一样的权限,所以需要为不用Linux用户映射为不同权限的SELinux的用户权限。又基于平台不支持semanage等工具,因此需要把SELinux用户权限直接编译到固件种。
主要通过添加Linux用户与SELinux用户为相同名字,并为SELinux用户匹配不同的权限完成映射。
在SELinux模式下登录用户先进行linux用户名及密码的验证,然后再为Linux用户名匹配SELinux用户的安全上下问,最后对此安全上下文是否有相关policy去允许。Linux用户与SELinux用户匹配规则通过分析libselinux.so源码(即get_context_list.c中的get_ordered_context_list函数)得知。
- int get_ordered_context_list(const char *user,
- const char *fromcon,
- char *** list)
- {
- char **reachable = NULL;
- int rc = 0;
- unsigned nreachable = 0;
- char *backup_fromcon = NULL;
- FILE *fp;
- char *fname = NULL;
- size_t fname_len;
- const char *user_contexts_path = selinux_user_contexts_path();
-
- if (!fromcon) {
- /* Get the current context and use it for the starting context */
- rc = getcon(&backup_fromcon);
- if (rc < 0)
- return rc;
- fromcon = backup_fromcon;
- }
-
- /* Determine the ordering to apply from the optional per-user config
- and from the global config. */
- fname_len = strlen(user_contexts_path) + strlen(user) + 2;
- fname = malloc(fname_len);
- if (!fname)
- goto failsafe;
- snprintf(fname, fname_len, "%s%s", user_contexts_path, user);
- fp = fopen(fname, "re");
- if (fp) {
- __fsetlocking(fp, FSETLOCKING_BYCALLER);
- rc = get_context_user(fp, fromcon, user, &reachable, &nreachable);
-
- fclose(fp);
- if (rc < 0 && errno != ENOENT) {
- fprintf(stderr,
- "%s: error in processing configuration file %s\n",
- __FUNCTION__, fname);
- /* Fall through, try global config */
- }
- }
- free(fname);
- fp = fopen(selinux_default_context_path(), "re");
- if (fp) {
- __fsetlocking(fp, FSETLOCKING_BYCALLER);
- rc = get_context_user(fp, fromcon, user, &reachable, &nreachable);
- fclose(fp);
- if (rc < 0 && errno != ENOENT) {
- fprintf(stderr,
- "%s: error in processing configuration file %s\n",
- __FUNCTION__, selinux_default_context_path());
- /* Fall through */
- }
- }
-
- if (!nreachable)
- goto failsafe;
-
- out:
- if (nreachable > 0) {
- *list = reachable;
- rc = nreachable;
- }
- else
- freeconary(reachable);
-
- freecon(backup_fromcon);
-
- return rc;
-
- failsafe:
- /* Unable to determine a reachable context list, try to fall back to
- the "failsafe" context to at least permit root login
- for emergency recovery if possible. */
- freeconary(reachable);
- reachable = malloc(2 * sizeof(char *));
- if (!reachable) {
- rc = -1;
- goto out;
- }
- reachable[0] = reachable[1] = 0;
- rc = get_failsafe_context(user, &reachable[0]);
- if (rc < 0) {
- freeconary(reachable);
- reachable = NULL;
- goto out;
- }
- nreachable = 1; /* one context in the list */
- goto out;
- }
首先通过user_contexts_path去获取SELinux的安全上下文,如果没有获取到会再通过selinux_default_context_path路径去获取安全上下文,最后如果两个地方都没有成功会直接使用get_failsafe_context中的安全上下文。
user_contexts_path对应的是/etc/selinux/targeted/contexts/users/,在这个目录下的查看与登录用户名相同的文件中去获取安全上下文。
selinux_default_context_path对应的是/etc/selinux/targeted/contexts/default_contexts文件。
cget_failsafe_context对应的是/etc/selinux/targeted/contexts/failsafe_context文件。
除failsafe_context文件文件外,文件内容有两部分,前面一部分匹配当前登录时系统使用的规则(rule)及类型(type)。
当匹配前面部分后,就为当前用户匹配为后面部分的规则(rule)及类型(type)。当前面两个文件中都没有匹配成功则直接使用failsafe_context文件中的规则(rule)及类型(type),因此failsafe_context文件中没有前面部分,只有后面部分的内容。匹配成功后会与policy.xx(不同的selinux版本xx的值不同)中的规则进行验证(如user root roles { system_r };),如果没有roles规则与之匹配则报错can't get SID for xxx,若有规则则登录成功。
注意,匹配SELinux安全上下文之前Linux用户密码输入错误时是另一个报错需要与之区分。
总结:1.在refpolicy-2.20200229/config/appconfig-standard/目录下新增XXX_default_contexts文件(XXX未新增的用户名),并在文件中配置需要的安全上下文权限(若已存在用户则直接修改或添加需要的安全上下文权限)。或在refpolicy-2.20200229/config/appconfig-standard/default_contexts文件中直接修改或添加需要的安全上下文权限。
2.在refpolicy-2.20200229/policy/users源码中添加对应的gen_user规则。
例如:添加用户名为ethan的安全上下文为ethan:system_r:kernel_t的SELinux用户
修改并编译后登录验证: