SElinux内核态的实现-数据库部分-class篇

前言

本文适合对SElinux已经有一定了解,能够按需实现.te、.if、.fc文件,对内核中SElinux相关机制实现感兴趣的同事。

此外,本文实现基于4.19内核版本,且本人工作中未使用mls。也将跳过mls代码解析的实现。

SELinux内核态简介

SELinux在内核中分为检查部分以及数据库部分

检查部分也就是我们熟悉的lsm hook机制

Linux Security Modules (LSM) 是Linux内核中的一种框架,用于为操作系统添加可插拔的安全策略。这一机制允许开发者和系统管理员通过插入自定义的安全模块来增强系统的安全性,而不必直接修改内核代码。LSM主要关注访问控制,以防范未授权的活动。

LSM的工作原理围绕“钩子”(hooks)展开。这些钩子是内核中预定义的插入点,分布在各种敏感操作上,比如文件访问、进程创建和信号处理等。每当内核执行这些操作时,都会暂停去调用LSM注册的钩子函数,让安全模块有机会根据其策略来决定是否放行或拒绝请求。

安全模块通过实现这些钩子接口,可以在操作执行前后介入,执行额外的权限验证或审计。例如,在文件打开操作期间,模块可以通过钩子检查请求者是否有权访问目标文件。如果模块判断访问不应被允许,它可以返回一个错误,从而阻止操作。

值得注意的是,LSM是静态编入内核的,意味着要在系统中使用特定的LSM,必须在编译内核时将其包含进去,且同一时间系统只能激活一个LSM模块。这保证了安全策略的统一性和效率,但要求对安全需求有明确的预先规划。

数据库部分则保存的是我们检查过程中需要使用的数据,比如selinux标签对应的编号,class对应的权限类等。

本文章对检查部分涉及较少,有以下两个原因:

一来检查部分除了一个初始化宏较难理解外,其余部分代码部分读懂难度不大。主要难点是在为什么在这里设置检查点以及检查点检查的内容是什么。而这两点更多考察的是对内核各个功能模块实现机制的了解,和安全模块本地检查功能关系不大。

二是如果要细讲,需要将前置技能点《linux内核功能的实现》一一点亮,这与SElinux本身功能实现关联较小。

所以检查部分这块不作为本文重点。本文还是以SElinux数据库部分实现为主。

SELinux数据库的实现

SElinux数据库的核心结构体是selinux_statepolicydb。前者主要保存的是sid与安全上下文的映射关系、检查结果的缓存,policydb主要保存的是检查规则。这些概念看不懂没关系,后续会详细解释这些内容。

不过在此之前,我们需要先了解SELinux数据库结构设计中保存各个数据的最小结构单元:安全上下文、class、

安全上下文

我们应当知道,SELinux的管控是基于扩展属性的标签实现的。其标签格式如下

/var/tinydns/env(/.*)?                             all files          system_u:object_r:svc_conf_t:s0 

这4个字段分别表示user、role、type以及mls级别,而这些元素的组合一起就是安全上下文,所以user_u:role_r:type_t:s0就是一个安全上下文。

SElinux使用结构体为strcut context保存安全上下文,定义如下

/*
 * A security context consists of an authenticated user
 * identity, a role, a type and a MLS range.
 */
struct context {
	u32 user;
	u32 role;
	u32 type;
	u32 len;        /* length of string in bytes */
	struct mls_range range;
	char *str;	/* string representation if context cannot be mapped. */
};

这里其中char str表示这个安全上下文的,也就是上文中的user_u:role_r:type_t:s0,而len很明显是这个字符串的长度。

context_struct_to_string我们可以了解SELinux对context->str的用法

static int context_struct_to_string(struct policydb *p,
				    struct context *context,
				    char **scontext, u32 *scontext_len)
{
	char *scontextp;

	if (scontext)
		*scontext = NULL;
	*scontext_len = 0;
	// 如果安全上下文有设置长度,则直接返回context->str
	if (context->len) {
		*scontext_len = context->len;
		if (scontext) {
			*scontext = kstrdup(context->str, GFP_ATOMIC);
			if (!(*scontext))
				return -ENOMEM;
		}
		return 0;
	}
	// 否则则拼接各个字符串合成完整上下文
...
}

其次,我们观察struct context可以发现role,type,user,len的类型是u32,所以我们可以合理推测两点

  1. SELinux保存role,type,user,len是通过id保存的。
  2. 用户空间拿到规则te文件后,编译阶段会给这些role,type,user,len来分配id。

sid

既然role,type,user,len是通过id来保存在内核中的,那么对于role,type,user,len,即安全上下文是不是也可以通过id来保存的呢。

结果是必然的。这个id在SELinux中被称为sid。这里以邮政编码,假设role是湖北省、type是宜昌市、user是夷陵区,那么那么实际的上下文对于邮编地址就是湖北省宜昌市夷陵区,就是sid对应的就是身份证前6位:420505

role:type:user:mls  --> sid
湖北省宜昌市夷陵区 --> 420505(身份证号前6位)

这个sid与context上下文映射保存在selinux_ss->sidtab->sidtab哈希表中。此外,为了快速查询,SElinux还会将查询结果缓存,这份缓存保存在selinux_ss->sidtab->cache中。

根据SELinux生成sid部分的代码(sidtab_context_to_sid函数),可以发现这个sid在系统每次运行时候是不一样的。也就是说,sid与安全上下文的映射关系是实时生成的,当某个安全上下文首次需要被检查时候,SELinux将会给其分配一个sid,然后将这个映射关系保存在sidtab->sidtab哈希表中,接着更新sidtab->cache缓存表。

同理,查询的时候也是先查询sidtab->cache,然后查询sidtab->sidtab,如果还找不到,则给这个安全上下文分配一个新的sid。

具体查询实现代码如下

int sidtab_context_to_sid(struct sidtab *s,
			  struct context *context,
			  u32 *out_sid)
{
	u32 sid;
	int ret = 0;
	unsigned long flags;

	*out_sid = SECSID_NULL;
	// 从sidtab->cache中查询context的sid
	sid  = sidtab_search_cache(s, context);
	if (!sid)
		// 从sidtab->sidtab中查询context的sid
		sid = sidtab_search_context(s, context);
	if (!sid) {
		spin_lock_irqsave(&s->lock, flags);
		/* Rescan now that we hold the lock. */
		sid = sidtab_search_context(s, context);
		if (sid)
			goto unlock_out;
		/* No SID exists for the context.  Allocate a new one. */
		if (s->next_sid == UINT_MAX || s->shutdown) {
			ret = -ENOMEM;
			goto unlock_out;
		}
		// 分配新sid
		sid = s->next_sid++;
		if (context->len)
			pr_info("SELinux:  Context %s is not valid (left unmapped).\n",
			       context->str);
		ret = sidtab_insert(s, sid, context);
		if (ret)
			s->next_sid--;
unlock_out:
		spin_unlock_irqrestore(&s->lock, flags);
	}

	if (ret)
		return ret;

	*out_sid = sid;
	return 0;
}

sid与安全上下文映射关系的保存

sid与上下文的映射关系保存在struct sidtab(&state->ss->sidtab)中.

struct selinux_ss {
	struct sidtab sidtab;
	struct policydb policydb;
	rwlock_t policy_rwlock;
	u32 latest_granting;
	struct selinux_map map;
	struct page *status_page;
	struct mutex status_lock;
};

image

class与permission

到这里,我们已经了解sid的实现,也就是说,内核的SELinux部分这时候已经能获得源目文件的标签(假设是文件访问文件的权限检查),并成功的将标签转换为了一个唯一的sid。

现在SELinux如果要进行源目权限检查的话,还缺少一个源对目的所执行的操作(permission)。

这句话怎么具体解释呢,比如我现在知晓了进程A的需要访问文件B,而且已经知晓了进程A和文件B的sid。那么我还需要知晓进程A具体需要对文件B做什么操作。是写入还是读取还是ioctl还是mmap等。

而这里的“写入”还是“读取”或者 ioctl 亦或 mmap 这个动作在SElinux中被称为permission(操作),而一系列的操作(permission)的集合,在SELinux中被称为class(类)。

为什么会有集合这个概念呢:

  1. 逻辑分组: class将相似的permissions组织在一起,形成逻辑上的分类。这样做可以让安全策略更加清晰和易于理解。例如,所有与文件操作相关的权限(如读、写、执行)被归类在file class下,网络操作的权限可能归在另一个class下。这样,管理员在编写或审查策略时,能更快地把握权限分配的逻辑结构。
  2. 策略一致性与复用: 通过class,可以确保相同类型的操作在不同上下文中具有一致的处理方式。当需要调整或扩展权限时,只需修改或增加class内的permissions,而不需要逐个修改涉及这些权限的规则,提高了策略的一致性和复用性。
  3. 简化表达和解析: 在SELinux策略规则中,使用class作为permissions的载体,使得规则表达更为简洁。规则通常采用allow source target:class permission的形式,这样的结构便于解析器快速识别并处理权限请求,也便于人类阅读和理解。

当源目上下文加上class,我们拿到了一个完整的规则检查需求明细:

源(安全上下文) -> 目的(安全上下文):class(类) 的某些具体permission(操作)。

现在我们可以来对这个明细做检查了:在前面我们已经知晓了如何将安全上下文是转换为sid。现在我们需要知晓如何将class转换为对应的id。

至于怎么检查的,权限检查章节会详细讲解。

这里我们只需要知晓:对于SELinux来说,权限的检查的入参全是一个个id即可。

class的实现

前面我们已经了解,“class” 代表了一类资源或操作(permissions)的集。

在SELinux中,class的存储被分为了两部分:

  • 被多个 class 公用的 class 保存在 symtab[SYM_COMMONS] 中
  • class 自己私有权限 class保存在 symtab[SYM_CLASSES] 中。

而这个 symtab[] 数组,则保存在 policydb->symtab[SYM_NUM] 中,同时 SELinux 提供了两个宏来指向此 symtab[SYM_COMMONS] 和symtab[SYM_CLASSES]

SELinux总共提供了SYM_NUM个symtab,每个sytab的含义可以通过如下的宏定义来窥探一二
我这里只关心p_commons 与 p_classes 至于这个组内的其他内容,将在需要讲解的时候分析

	/* symbol tables */
	struct symtab symtab[SYM_NUM];
#define p_commons symtab[SYM_COMMONS]
#define p_classes symtab[SYM_CLASSES]
#define p_roles symtab[SYM_ROLES]
#define p_types symtab[SYM_TYPES]
#define p_users symtab[SYM_USERS]
#define p_bools symtab[SYM_BOOLS]
#define p_levels symtab[SYM_LEVELS]
#define p_cats symtab[SYM_CATS]
#define p_commons symtab[SYM_COMMONS]
#define p_classes symtab[SYM_CLASSES]

首先, symtab 内包含的是一个哈希表+节点数量,

struct symtab {
	struct hashtab *table;	/* hash table (keyed on a string) */
	u32 nprim;		/* number of primary names in table */
};
struct hashtab {
	struct hashtab_node **htable;	/* hash table 哈希表*/
	u32 size;			/* number of slots in hash table 哈希表内slots数量,即哈希链表数量 */
	u32 nel;			/* number of elements in hash table 哈希表内元素数量 */
	u32 (*hash_value)(struct hashtab *h, const void *key);
					/* hash function */
	int (*keycmp)(struct hashtab *h, const void *key1, const void *key2);
					/* key comparison function */
};

哈希表内存放的元素(内容)为hashtab_node

struct hashtab_node {
	void *key;
	void *datum;
	struct hashtab_node *next;
};

symtab的结构如下

image

而在不同的symtab中,keydatum的含义均不一样。 当讲解具体symtab的时候再来详细分解其keydatum的含义

公用class

p_commons(公用class)中,这个哈希表的keydatum含义如下:

  • key等于公共class的名字,是一个字符串,比如“socket”
  • datum 内保存的一个指向结构体struct common_datum 的指针

struct common_datum 的结构如下:

/* Attributes of a common prefix for access vectors */
struct common_datum {
	u32 value;			/* internal common value */
	struct symtab permissions;	/* common permissions */
};
  • u32 value表示给此class的id
  • permissions表示此class含有的所有操作(permission),

image

这里的变量permissions可以发现其类型是struct symtab,所以permissions变量内保存的又是一个哈希表,这也与一个class内包含多个permission的特性相符

datum->permissions这个哈希表中元素中的keydatum的含义如下

  • key等于操作(permissions)的名字,是一个字符串
  • datum 是结构体struct perm_datum,里面内容很简单,就是一个u32的二进制。表示此操作(permissions)在其class的编号id
/* Permission attributes */
struct perm_datum {
	u32 value;		/* permission bit + 1 */
};

其结构如下
在这里插入图片描述

这样SELinux通过结构体struct common_datum与结构体struct perm_datum 就将class与操作(permissions)结合在一起。其中公用class以下内容:

一级名称二级名称变量类型
公用class名称-字符串
公用class的id-u32
此class包含的所有permissions(操作)permission的名称字符串
-permission在此class的idu32

公用class的完整结构体布局如下
在这里插入图片描述

私有class

在 p_classes(私有class)中,这个哈希表的keydatum含义如下:

  • key等于私有class的名字,是一个字符串,比如“ioctl”
  • datum 内保存的一个指向结构体struct class_datum 的指针

struct class_datum 的结构如下:

/* Class attributes */
struct class_datum {
	u32 value;			/* class value */
	char *comkey;			/* common name */
	struct common_datum *comdatum;	/* common datum */
	struct symtab permissions;	/* class-specific permission symbol table */
	struct constraint_node *constraints;	/* constraints on class permissions */
	struct constraint_node *validatetrans;	/* special transition rules */
/* Options how a new object user, role, and type should be decided */
#define DEFAULT_SOURCE         1
#define DEFAULT_TARGET         2
	char default_user;
	char default_role;
	char default_type;
/* Options how a new object range should be decided */
#define DEFAULT_SOURCE_LOW     1
#define DEFAULT_SOURCE_HIGH    2
#define DEFAULT_SOURCE_LOW_HIGH        3
#define DEFAULT_TARGET_LOW     4
#define DEFAULT_TARGET_HIGH    5
#define DEFAULT_TARGET_LOW_HIGH        6
	char default_range;
};
  • u32 value表示给此class的id
  • comkey 表示此class依赖的公共class的名称
  • comdatum 指针指向结构体common_datum的地址,此地址内保存了依赖的公用class实际内容。
  • permissions 保存了除了依赖的公共class外的其他的permission(操作)
  • constraint_node 保存了此class的约束信息
  • validatetrans 是此class转换的约束信息
  • default_user、default_role、default_type、default_range 各个类型默认id

image

私有class包含了以下内容:

一级名称二级名称变量类型
class名称-字符串
class id-u32
依赖的公用class名称-字符串
存放依赖公用class内容的结构体地址指针
此class包含的所有permissions(操作)permission的名称字符串
-permission在此class的idu32
class约束信息、转换约束信息指针
class默认role、user、type、mlsu32

好的,现在通过p_common以及p_class,我们可以拿到某个class含有的所有permissions的id了。

但是很明显,这种通过哈希表保存的数据更适合查询的是字符串和id的映射关系,那么如果想快速查询某个class下的所有permission应该怎么做呢。

class与permission的关联–selinux_mapping

SELinux采用struct selinux_mapping来保存class id与permission id的映射关系

/* Mapping for a single class */
struct selinux_mapping {
	u16 value; /* policy value for class */
	unsigned int num_perms; /* number of permissions in class */
	u32 perms[sizeof(u32) * 8]; /* policy values for permissions */
};
  • value : class的id
  • num_perms : class拥有的permission数量
  • perms[] : permission 的id ,class 最多有32个permission

image

而这些selinux_mapping 又被集中保存在selinux_map中,

/* Map for all of the classes, with array size */
struct selinux_map {
	struct selinux_mapping *mapping; /* indexed by class */
	u16 size; /* array size of mapping */
};
  • *mapping :一个指针数组,长度为array_size(secclass_map[x].name)
  • size :class的总数

image

selinux_mapping的初始化

SElinux对selinux_map的初始化函数为selinux_set_mapping

在初始化阶段SELinux将通过依次遍历字典 struct security_class_mapping secclass_map[]

/*
 * Note: The name for any socket class should be suffixed by "socket",
 *	 and doesn't contain more than one substr of "socket".
 */
struct security_class_mapping secclass_map[] = {
	{ "security",
	  { "compute_av", "compute_create", "compute_member",
	    "check_context", "load_policy", "compute_relabel",
	    "compute_user", "setenforce", "setbool", "setsecparam",
	    "setcheckreqprot", "read_policy", "validate_trans", NULL } },
	{ "process",
	  { "fork", "transition", "sigchld", "sigkill",
	    "sigstop", "signull", "signal", "ptrace", "getsched", "setsched",
	    "getsession", "getpgid", "setpgid", "getcap", "setcap", "share",
	    "getattr", "setexec", "setfscreate", "noatsecure", "siginh",
	    "setrlimit", "rlimitinh", "dyntransition", "setcurrent",
	    "execmem", "execstack", "execheap", "setkeycreate",
	    "setsockcreate", "getrlimit", NULL } },
...
}

获得每一个class的所有permission的名称,然后将permission的名称作为入参查询p_commonp_classes得到permission的id,将id保存在通过位移的方式转换为位图,保存在selinux_mapping->perm[k]中,有点绕没关系,这里以实际某次code_dump的内存为例。来用实际内存分配的地址来解析

首先我们梳理下SElinux初始化class和permission的流程:

  • 首先,SELinux将从用户空间读取policy规则文件,生成p_common公共class与p_classes私有class(如何生成的将起来有点枯燥,我放在本文的最后部分class的加载中解析)

  • 然后,SELinux将解析字典secclass_map[],获取每个class的名称以及其包含的所有的permissions的名称。

  • 接着,SELinux将class名称以及其permission的名称作为入参,查询p_common、p_classes,获得每个class的id与permission的id,

  • 最后保存class id 与 permission id的映射关系

    • 将class的id存放在selinux_mapping->value中。
    • 然后将生成一个1U << permission的id 的二进制,保存在 selinux_mapping->perms[k]中。k为secclass_map中某个class的permission的序号。

这里以class socket为实际的例子。我们来看看内核是如何存储 class socket的。

首先看看p_common是如何保存class socket的
同时 SELinux也提供了用户空间的查询class信息的方式

[root@localhost ~]# ls /sys/fs/selinux/class/
alg_socket        chr_file       fifo_file           llc_socket                     netlink_netfilter_socket      perf_event      socket              x_drawable
appletalk_socket  context        file                lnk_file                       netlink_nflog_socket          phonet_socket   sock_file           x_event
association       db_blob        filesystem          memprotect                     netlink_rdma_socket           pppox_socket    system              x_extension
atmpvc_socket     db_column      ib_socket           mpls_socket                    netlink_route_socket          process         tcp_socket          x_font
atmsvc_socket     db_database    icmp_socket         msg                            netlink_scsitransport_socket  process2        tipc_socket         x_gc
ax25_socket       db_language    ieee802154_socket   msgq                           netlink_selinux_socket        proxy           tun_socket          x_keyboard
binder            db_procedure   infiniband_endport  netif                          netlink_socket                qipcrtr_socket  udp_socket          x_pointer
blk_file          db_schema      infiniband_pkey     netlink_audit_socket           netlink_tcpdiag_socket        rawip_socket    unix_dgram_socket   x_property
bluetooth_socket  db_sequence    ipc                 netlink_connector_socket       netlink_xfrm_socket           rds_socket      unix_stream_socket  x_resource
bpf               db_table       ipx_socket          netlink_crypto_socket          netrom_socket                 rose_socket     vsock_socket        x_screen
bridge_socket     db_tuple       irda_socket         netlink_dnrt_socket            nfc_socket                    rxrpc_socket    x25_socket          x_selection
caif_socket       dbus           isdn_socket         netlink_fib_lookup_socket      node                          sctp_socket     x_application_data  x_server
can_socket        db_view        iucv_socket         netlink_firewall_socket        nscd                          security        x_client            x_synthetic_event
cap2_userns       dccp_socket    kcm_socket          netlink_generic_socket         packet                        sem             x_colormap
capability        decnet_socket  kernel_service      netlink_ip6fw_socket           packet_socket                 service         x_cursor
capability2       dir            key                 netlink_iscsi_socket           passwd                        shm             x_device
cap_userns        fd             key_socket          netlink_kobject_uevent_socket  peer                          smc_socket      xdp_socket

可以直接访问查看class的id以及其perm的id

[root@localhost ~]# cat /sys/fs/selinux/class/file/index 
6[root@localhost ~]# ls /sys/fs/selinux/class/file/perms/
append            entrypoint        execute_no_trans  link              mounton           read              rename            unlink            
audit_access      execmod           getattr           lock              open              relabelfrom       setattr           write             
create            execute           ioctl             map               quotaon           relabelto         swapon            
[root@localhost ~]# cat /sys/fs/selinux/class/file/perms/append 
10[root@localhost ~]# 

class查询的优化

除了查询class所含有的permissions外,SElinux还会经常访问指定class id的信息,这种场景下,查询的结果一般有两种:

  • 一是基于class id ,查询其结构体信息,即其结构体class_datum内存地址。
  • 二是基于class id, 查询class名称。

同样selinux页提供了这两种场景下的快捷查询方式。

基于class id 查询其结构体class_datum内存地址

SELinux提供了一个指针数组 class_val_to_struct 来协助在知晓class的id的情况下,快速查找存放其信息的结构体的内存地址的场景。

其中,class 的id为指针数组 class_val_to_struct 的下标。以 class socket的id为5,那么*class_val_to_struct[4] 指向的即是保存socket这个class信息的结构体内存地址

image

基于class id 查询 class的名字

selinux提供了一个struct flex_array sym_val_to_name[]方便通过tclass,也就是class的name找到class结构体的内存地址。

sym_val_to_name[SYM_COMMONS]是用于存放公共class的快速查询。

sym_val_to_name[SYM_CLASSES]是用于存放私有class的快速查询。

sym_val_to_name是一个柔性数组,可以简单理解为一个长度可变的数组,用于存放总量不固定的数据。

内核提供了flex_array_put_ptr(fa, nr, src, gfp)src的地址保存在柔性数组中的的下标为nr的位置

#define flex_array_put_ptr(fa, nr, src, gfp) 	flex_array_put(fa, nr, (void *)&(src), gfp)

同时提供利润flex_array_get_ptr(fa, nr)来获取柔性数组中的的下标为nr的位保存的地址。

image

flex_array_get_ptr的基础上,SELinux封装了sym_name函数来快速获取指定内容的指定的下标指向的地址

static inline char *sym_name(struct policydb *p, unsigned int sym_num, unsigned int element_nr)
{
	struct flex_array *fa = p->sym_val_to_name[sym_num];

	return flex_array_get_ptr(fa, element_nr);
}

class的加载

SELinux将从用户空间的policy文件中读取已经编译好的class信息。实现的函数为policydb_read, 这里我们只关心p_common公共class与p_classes私有class

/*
 * Read the configuration data from a policy database binary
 * representation file into a policy database structure.
 */
int policydb_read(struct policydb *p, void *fp)
{
	...
	info = policydb_lookup_compat(p->policyvers);	// 通过版本号拿到此版本的数据结构信息
	...
	for (i = 0; i < info->sym_num; i++) {			// 通过版本号拿到此版本symtab数量,这里我们只关心SYM_COMMONS和SYM_CLASSES,也就是p_common与p_classes
		rc = next_entry(buf, fp, sizeof(u32)*2);
		if (rc)
			goto bad;
		nprim = le32_to_cpu(buf[0]);				// 此symtab中的内容长度,对于p_common与p_classes,表示的是含有的class数量
		nel = le32_to_cpu(buf[1]);					// 对于p_common与p_classes 此值为1
		for (j = 0; j < nel; j++) {
			rc = read_f[i](p, p->symtab[i].table, fp);	// 使用read_f[i],这个函数指针数组的第i个函数要处理read请求
			if (rc)
				goto bad;
		}

		p->symtab[i].nprim = nprim;
	}
...
}

read_f加载函数指针数组如下

static int (*read_f[SYM_NUM]) (struct policydb *p, struct hashtab *h, void *fp) =
{
	common_read,
	class_read,
	role_read,
	type_read,
	user_read,
	cond_read_bool,
	sens_read,
	cat_read,
};

填充 common(公共class)

static int common_read(struct policydb *p, struct hashtab *h, void *fp)
{
	char *key = NULL;
	struct common_datum *comdatum;
	__le32 buf[4];
	u32 len, nel;
	int i, rc;

	comdatum = kzalloc(sizeof(*comdatum), GFP_KERNEL);
	if (!comdatum)
		return -ENOMEM;
	//读取4个32位长度
	rc = next_entry(buf, fp, sizeof buf);
	if (rc)
		goto bad;
	// 
	len = le32_to_cpu(buf[0]);
	// class的名称
	comdatum->value = le32_to_cpu(buf[1]);

	rc = symtab_init(&comdatum->permissions, PERM_SYMTAB_SIZE);
	if (rc)
		goto bad;
	// class含有的permissions的权限条目数量
	comdatum->permissions.nprim = le32_to_cpu(buf[2]);
	nel = le32_to_cpu(buf[3]);
	// 读取len+1的长度 ,将fp移动到len+1位置,然后将结果赋予key
	rc = str_read(&key, GFP_KERNEL, fp, len);
	if (rc)
		goto bad;

	for (i = 0; i < nel; i++) {
		// 从fp读取permissions
		// table内由n个xx组成, xx第一32bit是字符串长度len,第二位32bit是value值,然后是一个len长度的字符串。填充一个struct hashtab_node 然后通过hashtab_insert插入到comdatum->permissions.table
		rc = perm_read(p, comdatum->permissions.table, fp);
		if (rc)
			goto bad;
	}
	
	rc = hashtab_insert(h, key, comdatum);
	if (rc)
		goto bad;
	return 0;
bad:
	common_destroy(key, comdatum, NULL);
	return rc;
}

填充class表(私有class)

static int class_read(struct policydb *p, struct hashtab *h, void *fp)
{
	char *key = NULL;
	struct class_datum *cladatum;
	__le32 buf[6];
	u32 len, len2, ncons, nel;
	int i, rc;

	cladatum = kzalloc(sizeof(*cladatum), GFP_KERNEL);
	if (!cladatum)
		return -ENOMEM;
	// 读取头 6个32bit
	rc = next_entry(buf, fp, sizeof(u32)*6);
	if (rc)
		goto bad;
	// class名字的长度
	len = le32_to_cpu(buf[0]);
	// comkey的名字长度
	len2 = le32_to_cpu(buf[1]);
	// class的编号
	cladatum->value = le32_to_cpu(buf[2]);

	rc = symtab_init(&cladatum->permissions, PERM_SYMTAB_SIZE);
	if (rc)
		goto bad;
	// class的permission的长度
	cladatum->permissions.nprim = le32_to_cpu(buf[3]);
	nel = le32_to_cpu(buf[4]);
	// calss的约束
	ncons = le32_to_cpu(buf[5]);
	// 读取class的名称
	rc = str_read(&key, GFP_KERNEL, fp, len);
	if (rc)
		goto bad;
	// 是否有公共class
	if (len2) {
		// 读取公共class的名字
		rc = str_read(&cladatum->comkey, GFP_KERNEL, fp, len2);
		if (rc)
			goto bad;

		rc = -EINVAL;
		// 找到通过公共class名称找到公共class地址并保存
		cladatum->comdatum = hashtab_search(p->p_commons.table, cladatum->comkey);
		if (!cladatum->comdatum) {
			pr_err("SELinux:  unknown common %s\n", cladatum->comkey);
			goto bad;
		}
	}
	// 获取私有class的perm 并填充到 permission->table中
	for (i = 0; i < nel; i++) {
		rc = perm_read(p, cladatum->permissions.table, fp);
		if (rc)
			goto bad;
	}
	// 读取class的约束
	rc = read_cons_helper(p, &cladatum->constraints, ncons, 0, fp);
	if (rc)
		goto bad;
	// 是否支持validatetrans 支持则载入
	if (p->policyvers >= POLICYDB_VERSION_VALIDATETRANS) {
		/* grab the validatetrans rules */
		rc = next_entry(buf, fp, sizeof(u32));
		if (rc)
			goto bad;
		ncons = le32_to_cpu(buf[0]);
		rc = read_cons_helper(p, &cladatum->validatetrans,
				ncons, 1, fp);
		if (rc)
			goto bad;
	}
	// 是否支持 default object
	if (p->policyvers >= POLICYDB_VERSION_NEW_OBJECT_DEFAULTS) {
		rc = next_entry(buf, fp, sizeof(u32) * 3);
		if (rc)
			goto bad;

		cladatum->default_user = le32_to_cpu(buf[0]);
		cladatum->default_role = le32_to_cpu(buf[1]);
		cladatum->default_range = le32_to_cpu(buf[2]);
	}
	// 是否支持 default_type
	if (p->policyvers >= POLICYDB_VERSION_DEFAULT_TYPE) {
		rc = next_entry(buf, fp, sizeof(u32) * 1);
		if (rc)
			goto bad;
		cladatum->default_type = le32_to_cpu(buf[0]);
	}
	// 插入到
	rc = hashtab_insert(h, key, cladatum);
	if (rc)
		goto bad;

	return 0;
bad:
	cls_destroy(key, cladatum, NULL);
	return rc;
}
  • 29
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值