从【SELINUX】策略中学习【LSM】编写规则

 最近涉及到了LSM的编写,在网上基本上搜不到关于LSM的编写规则和使用方法,LSM是我觉得菜鸟非常适合的一种访问控制策略编写,所以今天从SELinux的LSM代码学习。 
 

在内核源码/security/SELinux中hook.c中定义了LSM模块的hook机制。hook主要根据的是struct security_operations结构体,里面提供了各种函数的回调机制。

struct security_operations {
      int (*ptrace) (struct task_struct * parent, struct task_struct * child);
      int (*capget) (struct task_struct * target,
                   kernel_cap_t * effective,
                   kernel_cap_t * inheritable, kernel_cap_t * permitted);
      int (*capset_check) (struct task_struct * target,
                        kernel_cap_t * effective,
                        kernel_cap_t * inheritable,
                        kernel_cap_t * permitted);
      void (*capset_set) (struct task_struct * target,
                       kernel_cap_t * effective,
                       kernel_cap_t * inheritable,
                       kernel_cap_t * permitted);
      int (*capable) (struct task_struct * tsk, int cap);
      int (*acct) (struct file * file);
      int (*sysctl) (struct ctl_table * table, int op);
      int (*quotactl) (int cmds, int type, int id, struct super_block * sb);
      int (*quota_on) (struct dentry * dentry);
      int (*syslog) (int type);
      int (*settime) (struct timespec *ts, struct timezone *tz);
      int (*vm_enough_memory) (long pages);
 
      int (*bprm_alloc_security) (struct linux_binprm * bprm);
      void (*bprm_free_security) (struct linux_binprm * bprm);
      void (*bprm_apply_creds) (struct linux_binprm * bprm, int unsafe);
      void (*bprm_post_apply_creds) (struct linux_binprm * bprm);
      int (*bprm_set_security) (struct linux_binprm * bprm);
      int (*bprm_check_security) (struct linux_binprm * bprm);
      int (*bprm_secureexec) (struct linux_binprm * bprm);
 
      int (*sb_alloc_security) (struct super_block * sb);
      void (*sb_free_security) (struct super_block * sb);
      int (*sb_copy_data)(struct file_system_type *type,
                       void *orig, void *copy);
      int (*sb_kern_mount) (struct super_block *sb, void *data);
      int (*sb_statfs) (struct dentry *dentry);
      int (*sb_mount) (char *dev_name, struct nameidata * nd,
                     char *type, unsigned long flags, void *data);
      int (*sb_check_sb) (struct vfsmount * mnt, struct nameidata * nd);
      int (*sb_umount) (struct vfsmount * mnt, int flags);
      void (*sb_umount_close) (struct vfsmount * mnt);
      void (*sb_umount_busy) (struct vfsmount * mnt);
      void (*sb_post_remount) (struct vfsmount * mnt,
                            unsigned long flags, void *data);
      void (*sb_post_mountroot) (void);
      void (*sb_post_addmount) (struct vfsmount * mnt,
                            struct nameidata * mountpoint_nd);
      int (*sb_pivotroot) (struct nameidata * old_nd,
                        struct nameidata * new_nd);
      void (*sb_post_pivotroot) (struct nameidata * old_nd,
                             struct nameidata * new_nd);
 
      int (*inode_alloc_security) (struct inode *inode);
      void (*inode_free_security) (struct inode *inode);
      int (*inode_init_security) (struct inode *inode, struct inode *dir,
                              char **name, void **value, size_t *len);
      int (*inode_create) (struct inode *dir,
                          struct dentry *dentry, int mode);
      int (*inode_link) (struct dentry *old_dentry,
                        struct inode *dir, struct dentry *new_dentry);
      int (*inode_unlink) (struct inode *dir, struct dentry *dentry);
      int (*inode_symlink) (struct inode *dir,
                           struct dentry *dentry, const char *old_name);
      int (*inode_mkdir) (struct inode *dir, struct dentry *dentry, int mode);
      int (*inode_rmdir) (struct inode *dir, struct dentry *dentry);
      int (*inode_mknod) (struct inode *dir, struct dentry *dentry,
                         int mode, dev_t dev);
      int (*inode_rename) (struct inode *old_dir, struct dentry *old_dentry,
                          struct inode *new_dir, struct dentry *new_dentry);
      int (*inode_readlink) (struct dentry *dentry);
      int (*inode_follow_link) (struct dentry *dentry, struct nameidata *nd);
      int (*inode_permission) (struct inode *inode, int mask, struct nameidata *nd);
      int (*inode_setattr)(struct dentry *dentry, struct iattr *attr);
      int (*inode_getattr) (struct vfsmount *mnt, struct dentry *dentry);
       void (*inode_delete) (struct inode *inode);
      int (*inode_setxattr) (struct dentry *dentry, char *name, void *value,
                          size_t size, int flags);
      void (*inode_post_setxattr) (struct dentry *dentry, char *name, void *value,
                               size_t size, int flags);
      int (*inode_getxattr) (struct dentry *dentry, char *name);
      int (*inode_listxattr) (struct dentry *dentry);
      int (*inode_removexattr) (struct dentry *dentry, char *name);
      const char *(*inode_xattr_getsuffix) (void);
    int (*inode_getsecurity)(const struct inode *inode, const char *name, void *buffer, size_t size, int err);
    int (*inode_setsecurity)(struct inode *inode, const char *name, const void *value, size_t size, int flags);
    int (*inode_listsecurity)(struct inode *inode, char *buffer, size_t buffer_size);
 
      int (*file_permission) (struct file * file, int mask);
      int (*file_alloc_security) (struct file * file);
      void (*file_free_security) (struct file * file);
      int (*file_ioctl) (struct file * file, unsigned int cmd,
                      unsigned long arg);
      int (*file_mmap) (struct file * file,
                     unsigned long reqprot,
                     unsigned long prot, unsigned long flags);
      int (*file_mprotect) (struct vm_area_struct * vma,
                         unsigned long reqprot,
                         unsigned long prot);
      int (*file_lock) (struct file * file, unsigned int cmd);
      int (*file_fcntl) (struct file * file, unsigned int cmd,
                      unsigned long arg);
      int (*file_set_fowner) (struct file * file);
      int (*file_send_sigiotask) (struct task_struct * tsk,
                              struct fown_struct * fown, int sig);
      int (*file_receive) (struct file * file);
 
      int (*task_create) (unsigned long clone_flags);
      int (*task_alloc_security) (struct task_struct * p);
      void (*task_free_security) (struct task_struct * p);
      int (*task_setuid) (uid_t id0, uid_t id1, uid_t id2, int flags);
      int (*task_post_setuid) (uid_t old_ruid /* or fsuid */ ,
                            uid_t old_euid, uid_t old_suid, int flags);
      int (*task_setgid) (gid_t id0, gid_t id1, gid_t id2, int flags);
      int (*task_setpgid) (struct task_struct * p, pid_t pgid);
      int (*task_getpgid) (struct task_struct * p);
      int (*task_getsid) (struct task_struct * p);
      void (*task_getsecid) (struct task_struct * p, u32 * secid);
      int (*task_setgroups) (struct group_info *group_info);
      int (*task_setnice) (struct task_struct * p, int nice);
      int (*task_setioprio) (struct task_struct * p, int ioprio);
      int (*task_getioprio) (struct task_struct * p);
      int (*task_setrlimit) (unsigned int resource, struct rlimit * new_rlim);
      int (*task_setscheduler) (struct task_struct * p, int policy,
                            struct sched_param * lp);
      int (*task_getscheduler) (struct task_struct * p);
      int (*task_movememory) (struct task_struct * p);
      int (*task_kill) (struct task_struct * p,
                     struct siginfo * info, int sig, u32 secid);
      int (*task_wait) (struct task_struct * p);
      int (*task_prctl) (int option, unsigned long arg2,
                      unsigned long arg3, unsigned long arg4,
                      unsigned long arg5);
      void (*task_reparent_to_init) (struct task_struct * p);
      void (*task_to_inode)(struct task_struct *p, struct inode *inode);
 
      int (*ipc_permission) (struct kern_ipc_perm * ipcp, short flag);
 
      int (*msg_msg_alloc_security) (struct msg_msg * msg);
      void (*msg_msg_free_security) (struct msg_msg * msg);
 
      int (*msg_queue_alloc_security) (struct msg_queue * msq);
      void (*msg_queue_free_security) (struct msg_queue * msq);
      int (*msg_queue_associate) (struct msg_queue * msq, int msqf
其中每个函数都是可以回调的,也就是hook。下面我以 task_kill为例理解SELinux的实现机制

static int selinux_task_kill(struct task_struct *p, struct siginfo *info,
				int sig, u32 secid)
{
	u32 perm;
	int rc;

	if (!sig)<span style="color: rgb(0, 130, 0); font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace; font-size: 14px; line-height: 15.390625px; background-color: rgb(224, 224, 224);">//sig表示的是信号,首先确定是否定义了信号,然后调用signal_to_av对信号进行分类</span>
		perm = PROCESS__SIGNULL; /* null signal; existence test */
	else
		perm = signal_to_av(sig);//返回perm,perm是本函数对sig的许可,会在下面调用<span style="font-family: 微软雅黑;">avc_has_perm作为参数</span>
	if (secid)
		rc = avc_has_perm(secid, task_sid(p),
				  SECCLASS_PROCESS, perm, NULL);
	else
		rc = current_has_perm(p, perm);
	return rc;//rc就是函数的返回值,0为可以执行,不能执行将会返回-EACCSE
}
static inline u32 signal_to_av(int sig)
{
	u32 perm = 0;

	switch (sig) {//中间两个杀进程的信号做何种处理可以选择,
		//处理的类型返回到perm
	case SIGCHLD:
		/* Commonly granted from child to parent. */
		perm = PROCESS__SIGCHLD;
		break;
	case SIGKILL:
		/* Cannot be caught or ignored */
		perm = PROCESS__SIGKILL;
		break;
	case SIGSTOP:
		/* Cannot be caught or ignored */
		perm = PROCESS__SIGSTOP;
		break;
	default:
		/* All other signals. */
		perm = PROCESS__SIGNAL;
		break;
	}

	return perm;
}
u32 secid为进程的sid这是SELinux的有的super id,每一个sid对应了一条安全上下文(也就是user,role,type),如果没有那么将会调用<span style="font-family: 微软雅黑;">current_has_perm获得当前的sid,并调用avc_has_perm确定权限。</span>
<pre name="code" class="cpp">/*
 * Check permission between current and another task, e.g. signal checks,
 * fork check, ptrace check, etc.
 * current is the actor and tsk2 is the target
 * - this uses current's subjective creds
 */
static int current_has_perm(const struct task_struct *tsk,
			    u32 perms)
{
	u32 sid, tsid;

	sid = current_sid();
	tsid = task_sid(tsk);
	return avc_has_perm(sid, tsid, SECCLASS_PROCESS, perms, NULL);
}


 
 avc就是一个cache,存储着最近使用过的策略,这种cache思想到处都是比如MMU的内存映射。 
 

SELinux/ss中的avc.c 中avc_has_perm 会调用avc_has_perm_noaudit查看是否在cache中。

/**
 * avc_has_perm_noaudit - Check permissions but perform no auditing.
 * @ssid: source security identifier
 * @tsid: target security identifier
 * @tclass: target security class
 * @requested: requested permissions, interpreted based on @tclass
 * @flags:  AVC_STRICT or 0
 * @avd: access vector decisions
 *
 * Check the AVC to determine whether the @requested permissions are granted
 * for the SID pair (@ssid, @tsid), interpreting the permissions
 * based on @tclass, and call the security server on a cache miss to obtain
 * a new decision and add it to the cache.  Return a copy of the decisions
 * in @avd.  Return %0 if all @requested permissions are granted,
 * -%EACCES if any permissions are denied, or another -errno upon
 * other errors.  This function is typically called by avc_has_perm(),
 * but may also be called directly to separate permission checking from
 * auditing, e.g. in cases where a lock must be held for the check but
 * should be released for the auditing.
 */
int avc_has_perm_noaudit(u32 ssid, u32 tsid,
			 u16 tclass, u32 requested,
			 unsigned flags,
			 struct av_decision *avd)//avc_has_perm根据前三个参数得到avd,avd->allow
						//存储的就是许可的掩码,和request与得到答案
{
	struct avc_node *node;
	int rc = 0;
	u32 denied;

	BUG_ON(!requested);

	rcu_read_lock();

	node = avc_lookup(ssid, tsid, tclass);
	if (unlikely(!node)) {//unlikely 不希望括号中的数值成立,在不成立的时候执行
		rcu_read_unlock();//这个就是cache中没有文件,需要读取ss文件的if条件语句
		security_compute_av(ssid, tsid, tclass, avd);
		rcu_read_lock();
		node = avc_insert(ssid, tsid, tclass, avd);
	} else {
		memcpy(avd, &node->ae.avd, sizeof(*avd));
		avd = &node->ae.avd;
	}

	denied = requested & ~(avd->allowed);

	if (denied) {//根据现有访问策略决定时候拒绝
		if (flags & AVC_STRICT)
			rc = -EACCES;
		else if (!selinux_enforcing || (avd->flags & AVD_FLAGS_PERMISSIVE))
			avc_update_node(AVC_CALLBACK_GRANT, requested, ssid,
					tsid, tclass, avd->seqno);
		else
			rc = -EACCES;
	}

	rcu_read_unlock();
	return rc;
}
从上面的代码中我们可以看出,如果cache中miss,那么就会调用 security_compute_av,

这个函数将会访问到SELinux中的策略,作出决定这次行动是否合法,

并且将策略装入cache以便下次使用。

/**
 * security_compute_av - Compute access vector decisions.
 * @ssid: source security identifier
 * @tsid: target security identifier
 * @tclass: target security class
 * @avd: access vector decisions 访问向量策略-听起来吊炸天!!!
 *
 * Compute a set of access vector decisions based on the
 * SID pair (@ssid, @tsid) for the permissions in @tclass.
 */
void security_compute_av(u32 ssid,
			 u32 tsid,
			 u16 orig_tclass,
			 struct av_decision *avd)
{
	u16 tclass;
	struct context *scontext = NULL, *tcontext = NULL;

	read_lock(&policy_rwlock);
	avd_init(avd);
	if (!ss_initialized)
		goto allow;

	scontext = sidtab_search(&sidtab, ssid);
	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;
	}
	context_struct_compute_av(scontext, tcontext, tclass, avd);
	map_decision(orig_tclass, avd, policydb.allow_unknown);
out:
	read_unlock(&policy_rwlock);
	return;
allow:
	avd->allowed = 0xffffffff;
	goto out;
}

void security_compute_av_user(u32 ssid,
			      u32 tsid,
			      u16 tclass,
			      struct av_decision *avd)
{
	struct context *scontext = NULL, *tcontext = NULL;

	read_lock(&policy_rwlock);
	avd_init(avd);
	if (!ss_initialized)
		goto allow;

	scontext = sidtab_search(&sidtab, ssid);
	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;
	}

	if (unlikely(!tclass)) {
		if (policydb.allow_unknown)
			goto allow;
		goto out;
	}

	context_struct_compute_av(scontext, tcontext, tclass, avd);
 out:
	read_unlock(&policy_rwlock);
	return;
allow:
	avd->allowed = 0xffffffff;
	goto out;
}


好了,至此LSM的一次hook就得到了一个结果,如果符合定义的策略就会执行。至于在函数 security_compute_av中的策略读取,等一些细节问题,我们下次再说。

我是坚持在XEN,KVM的领域,最近看了看访问控制,分享分享。

欢迎转载~

你可能感兴趣的:(linux,selinux,访问控制,lsm,策略编写)