策略中学习【LSM】编写规则
2014-08-26 15:53
225 查看
最近涉及到了LSM的编写,在网上基本上搜不到关于LSM的编写规则和使用方法,LSM是我觉得菜鸟非常适合的一种访问控制策略编写,所以今天从SELinux的LSM代码学习。
在内核源码/security/SELinux中hook.c中定义了LSM模块的hook机制。hook主要根据的是struct security_operations结构体,里面提供了各种函数的回调机制。
[cpp] view
plaincopy
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/a7c8e286f463007e2a900848b93dd72c.png)
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/9e12f1d3e499fc949c886e7c9e0484f9)
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的实现机制
[cpp] view
plaincopy
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/a7c8e286f463007e2a900848b93dd72c.png)
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/9e12f1d3e499fc949c886e7c9e0484f9)
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
}
[cpp] view
plaincopy
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/a7c8e286f463007e2a900848b93dd72c.png)
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/9e12f1d3e499fc949c886e7c9e0484f9)
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;
}
[cpp] view
plaincopy
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/a7c8e286f463007e2a900848b93dd72c.png)
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/9e12f1d3e499fc949c886e7c9e0484f9)
u32 secid为进程的sid这是SELinux的有的super id,每一个sid对应了一条安全上下文(也就是user,role,type),如果没有那么将会调用<span style="font-family: 微软雅黑;">current_has_perm获得当前的sid,并调用avc_has_perm确定权限。</span>
[cpp] view
plaincopy
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/a7c8e286f463007e2a900848b93dd72c.png)
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/9e12f1d3e499fc949c886e7c9e0484f9)
<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中。
[cpp] view
plaincopy
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/a7c8e286f463007e2a900848b93dd72c.png)
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/9e12f1d3e499fc949c886e7c9e0484f9)
/**
* 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以便下次使用。
[cpp] view
plaincopy
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/a7c8e286f463007e2a900848b93dd72c.png)
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/9e12f1d3e499fc949c886e7c9e0484f9)
/**
* 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中的策略读取,等一些细节问题,我们下次再说。
在内核源码/security/SELinux中hook.c中定义了LSM模块的hook机制。hook主要根据的是struct security_operations结构体,里面提供了各种函数的回调机制。
[cpp] view
plaincopy
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/a7c8e286f463007e2a900848b93dd72c.png)
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的实现机制
[cpp] view
plaincopy
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/a7c8e286f463007e2a900848b93dd72c.png)
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
}
[cpp] view
plaincopy
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/a7c8e286f463007e2a900848b93dd72c.png)
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;
}
[cpp] view
plaincopy
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/a7c8e286f463007e2a900848b93dd72c.png)
u32 secid为进程的sid这是SELinux的有的super id,每一个sid对应了一条安全上下文(也就是user,role,type),如果没有那么将会调用<span style="font-family: 微软雅黑;">current_has_perm获得当前的sid,并调用avc_has_perm确定权限。</span>
[cpp] view
plaincopy
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/a7c8e286f463007e2a900848b93dd72c.png)
<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中。
[cpp] view
plaincopy
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/a7c8e286f463007e2a900848b93dd72c.png)
/**
* 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以便下次使用。
[cpp] view
plaincopy
![](https://oscdn.geek-share.com/Uploads/Images/Content/201611/a7c8e286f463007e2a900848b93dd72c.png)
/**
* 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中的策略读取,等一些细节问题,我们下次再说。
相关文章推荐
- 从【SELINUX】策略中学习【LSM】编写规则
- 转载便于学习 android jni代码编写规则--整理总结
- MEF框架学习之旅(七)部件的创建规则(策略)
- SELinux策略语言--类型强制(编写TE规则)
- Mayi_XPath编写规则学习
- 编写代码不能看!好好学习华为的编码规则!----项目经理
- 本体(ontology)学习之我见——Jena规则编写——中文显示与owl:inverseOf推理结果不全
- SELinux策略语言--类型强制(编写TE规则)
- XPath编写规则学习总结
- Hibernate实体类编写规则和主键策略
- SELinux策略语言--类型强制(编写TE规则)
- Linux 路由 学习笔记 之七 策略规则的查找
- Linux 路由 学习笔记 之六 策略规则的添加
- Hibernate配置文件的编写二(实体类编写规则、状态、操作和OID生成策略)
- XPath编写规则学习
- strut2 学习之验证框架二编写验证规则
- Linux 路由 学习笔记 之五 策略规则相关的数据结构以及ipv4策略规则的初始化
- SELinux策略语言--类型强制(编写TE规则)
- hibernate的实体类编写规则与主键生成策略
- Linux的Makefile和Kconfig框架学习及编写规则