selinux在android中用法以及内核中代码实现

selinux(security enhance linux)是由美国国家安全局开发的,
用来进行强制访问控制,增强linux系统安全性。

传统的linux访问控制采用的是DAC(Discretionary Access Control)模型,
这里面主要用的是uid控制。
在linux系统里,对于文件的操作,只有「所有者」,「所有组」,「其他」这3类的划分。
文件所有者对文件有所有权限,而且可以授予其他用户的访问权限,
但是之后文件的访问就可能不受所有者控制,同时这种模型中没有区分用户和进程,
一个用户下所有进程权限都是等同的。
而selinux采用MAC(Mandatory Access Control),强制访问控制使用的"最小权限集"的方式,
每个进程需要赋予特定的权限,才能进行访问,这样一个用户的不同进程就具有了不同的权限。
例如,可以设定某个网络应用进程net只有socket操作权限,而没有读写proc文件的权限。
这种方式可以对访问操作中的主体和客体进行更精准的权限控制。


使用MAC的关键是主体跟客体都要设定自己的安全上下文,
当访问主体(一般是进程)对访问客体(一般为文件)进行访问时,
需要先通过权限检查,通过之后才能进行下面的访问。

安全上下文实际上就是一个附加在对象上的标签(label)。这个标签实际上就是一个字符串,它由四部分内容组成,分别是SELinux用户、SELinux 角色、类型、安全级别,每一个部分都通过一个冒号来分隔,格式为“user:role:type:rank”。
例如执行 ls -Z
dr-xr-xr-x root     root              u:object_r:proc:s0 proc

在安全上下文中,有用的是类型,用户,角色,安全级别用处不大,一般都设置为u跟object_r,s0
安全级别一般用在多级安全级别环境中,例如军事教育中。
在进行安全检查时,实际上是两个安全上下文的比较。


接下来看下selinux在内核代码中的实现。

使用 SELinux 的强制访问控制系统概述如图:

selinux在android中用法以及内核中代码实现_第1张图片

在内核中定义了一个LSM(linux security module)框架,这个框架定义了安全检查的通用接口,
定义在kernel/security/security.c中,具体安全策略的实现由用户可选,目前linux支持的安全
策略有selinux,yama,tomoyo,smack几种。
对于selinux来说,在hooks.c中会注册selinux 具体安全接口到LSM中,
当主体打算访问客体时,selinux先从AVC(access vector cache)中查找是否有匹配安全上下文的节点,
有的话,直接比较node的安全上下文能否允许访问。
如果在AVC中查找不到,需要从sepolicy数据库中去查找,找到后顺便将安全上下文加入AVC,便于以后的查找。
sepolicy数据库是用户编辑的安全策略文件,android中te文件就是用户定义的安全上下文,最终编译后生成一个sepolicy文件,
android init进程启动时将其加载到内存,最终通过security_load_policy加载到内核中。
我们以mount过程为例看下kernel代码流程。

mount->do_mount->security_sb_mount

int security_sb_mount(const char *dev_name, struct path *path,
                       const char *type, unsigned long flags, void *data)
{
	return security_ops->sb_mount(dev_name, path, type, flags, data);
}
//接下来调用selinux注册的selinux_mount
static int selinux_mount(const char *dev_name,
			 struct path *path,
			 const char *type,
			 unsigned long flags,
			 void *data)
{
	const struct cred *cred = current_cred();

	if (flags & MS_REMOUNT)
		return superblock_has_perm(cred, path->dentry->d_sb,
					   FILESYSTEM__REMOUNT, NULL);
	else
		return path_has_perm(cred, path, FILE__MOUNTON);
}

static inline int path_has_perm(const struct cred *cred,
				struct path *path,
				u32 av)
{
	struct inode *inode = path->dentry->d_inode;
	struct common_audit_data ad;

	ad.type = LSM_AUDIT_DATA_PATH;
	ad.u.path = *path;
	return inode_has_perm(cred, inode, av, &ad, 0);
}
//权限检查
int avc_has_perm_flags(u32 ssid, u32 tsid, u16 tclass,
		       u32 requested, struct common_audit_data *auditdata,
		       unsigned flags)
{
	struct av_decision avd;
	int rc, rc2;
	//用于在不执行审计的情况下检查访问向量缓存来确定是否授予SID对(ssid,tsid)以请求的权限
	rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, &avd);
	//avc 审计检查,
	rc2 = avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata,
			flags);
	if (rc2)
		return rc2;
	return rc;
}
//先从AVC中查找,找不到就去sepolicy数据库中查找,
//最终的关键还是计算allowed值
inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,
			 u16 tclass, u32 requested,
			 unsigned flags,
			 struct av_decision *avd)
{
	struct avc_node *node;
	struct avc_xperms_node xp_node;
	int rc = 0;
	u32 denied;

	BUG_ON(!requested);

	rcu_read_lock();
	//从avc hash list中查找是否有匹配的node
	node = avc_lookup(ssid, tsid, tclass);
	if (unlikely(!node))//hash list查找node失败
		node = avc_compute_av(ssid, tsid, tclass, avd, &xp_node);
	else
		memcpy(avd, &node->ae.avd, sizeof(*avd));

	denied = requested & ~(avd->allowed);//上面检查关键还是确定allowed值
	if (unlikely(denied))
		rc = avc_denied(ssid, tsid, tclass, requested, 0, 0, flags, avd);

	rcu_read_unlock();
	return rc;
}

static noinline struct avc_node *avc_compute_av(u32 ssid, u32 tsid,
			 u16 tclass, struct av_decision *avd,
			 struct avc_xperms_node *xp_node)
{
	rcu_read_unlock();
	INIT_LIST_HEAD(&xp_node->xpd_head);
	//这个函数主要是查找sepolicy数据库,确定安全上下文是否匹配
	security_compute_av(ssid, tsid, tclass, avd, &xp_node->xp);
	rcu_read_lock();
	return avc_insert(ssid, tsid, tclass, avd, xp_node);//将ssid/tsid对应的node插入avc 缓冲区
}
//根据source,target安全上下文计算最终av_decision
void security_compute_av(u32 ssid,
			 u32 tsid,
			 u16 orig_tclass,
			 struct av_decision *avd,
			 struct extended_perms *xperms)
{
	u16 tclass;
	struct context *scontext = NULL, *tcontext = NULL;

	read_lock(&policy_rwlock);
	avd_init(avd);
	xperms->len = 0;
	if (!ss_initialized)
		goto allow;

	scontext = sidtab_search(&sidtab, ssid);//从全局sidtable中查找安全上下文
	if (!scontext) {
		printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
		       __func__, ssid);
		goto out;
	}

	/* permissive domain? */
	if (ebitmap_get_bit(&policydb.permissive_map, scontext->type))
		avd->flags |= AVD_FLAGS_PERMISSIVE;

	tcontext = sidtab_search(&sidtab, tsid);
	if (!tcontext) {
		printk(KERN_ERR "SELinux: %s:  unrecognized SID %d\n",
		       __func__, tsid);
		goto out;
	}

	tclass = unmap_class(orig_tclass);
	if (unlikely(orig_tclass && !tclass)) {
		if (policydb.allow_unknown)
			goto allow;
		goto out;
	}
	//比较source/dst 安全上下文
	context_struct_compute_av(scontext, tcontext, tclass, avd, xperms);
	//用来计算avd 各项值
	map_decision(orig_tclass, avd, policydb.allow_unknown);
out:
	read_unlock(&policy_rwlock);
	return;
allow:
	avd->allowed = 0xffffffff;
	goto out;
}

 //将ssid/tsid对应的node插入avc 缓冲区
static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass,
				struct av_decision *avd,
				struct avc_xperms_node *xp_node)
{
	struct avc_node *pos, *node = NULL;
	int hvalue;
	unsigned long flag;

	if (avc_latest_notif_update(avd->seqno, 1))
		goto out;

	node = avc_alloc_node();//从slub高速缓存申请一个avc_node
	if (node) {
		struct hlist_head *head;
		spinlock_t *lock;
		int rc = 0;

		hvalue = avc_hash(ssid, tsid, tclass);//计算hash值
		avc_node_populate(node, ssid, tsid, tclass, avd);//给node赋值
		rc = avc_xperms_populate(node, xp_node);//填充node扩展属性
		if (rc) {
			kmem_cache_free(avc_node_cachep, node);
			return NULL;
		}
		head = &avc_cache.slots[hvalue];
		lock = &avc_cache.slots_lock[hvalue];

		spin_lock_irqsave(lock, flag);
		hlist_for_each_entry(pos, head, list) {//遍历hlist
			if (pos->ae.ssid == ssid &&
			    pos->ae.tsid == tsid &&
			    pos->ae.tclass == tclass) {
				avc_node_replace(node, pos);
				goto found;
			}
		}
		hlist_add_head_rcu(&node->list, head);//node加入hlist最前
found:
		spin_unlock_irqrestore(lock, flag);
	}
out:
	return node;
} 

//审计授予及拒绝的权限,
//所谓审计就是将权限打印到终端给用户检查
static inline int avc_audit(u32 ssid, u32 tsid,
			    u16 tclass, u32 requested,
			    struct av_decision *avd,
			    int result,
			    struct common_audit_data *a, unsigned flags)
{
	u32 audited, denied;
	//计算待审计及拒绝的权限,并返回待审计的权限
	audited = avc_audit_required(requested, avd, result, 0, &denied);
	if (likely(!audited))
		return 0;
	return slow_avc_audit(ssid, tsid, tclass,
			      requested, audited, denied, result,
			      a, flags);
}

对于android安全策略配置,可以参考下面的文章:

http://www.jianshu.com/p/a3572eee341c

你可能感兴趣的:(selinux在android中用法以及内核中代码实现)