📄 hooks.c
字号:
rc = try_context_mount(sb, data); if (rc) goto out; if (sbsec->behavior == SECURITY_FS_USE_XATTR) { /* Make sure that the xattr handler exists and that no error other than -ENODATA is returned by getxattr on the root directory. -ENODATA is ok, as this may be the first boot of the SELinux kernel before we have assigned xattr values to the filesystem. */ if (!inode->i_op->getxattr) { printk(KERN_WARNING "SELinux: (dev %s, type %s) has no " "xattr support\n", sb->s_id, sb->s_type->name); rc = -EOPNOTSUPP; goto out; } rc = inode->i_op->getxattr(root, XATTR_NAME_SELINUX, NULL, 0); if (rc < 0 && rc != -ENODATA) { if (rc == -EOPNOTSUPP) printk(KERN_WARNING "SELinux: (dev %s, type " "%s) has no security xattr handler\n", sb->s_id, sb->s_type->name); else printk(KERN_WARNING "SELinux: (dev %s, type " "%s) getxattr errno %d\n", sb->s_id, sb->s_type->name, -rc); goto out; } } if (strcmp(sb->s_type->name, "proc") == 0) sbsec->proc = 1; sbsec->initialized = 1; if (sbsec->behavior > ARRAY_SIZE(labeling_behaviors)) { printk(KERN_INFO "SELinux: initialized (dev %s, type %s), unknown behavior\n", sb->s_id, sb->s_type->name); } else { printk(KERN_INFO "SELinux: initialized (dev %s, type %s), %s\n", sb->s_id, sb->s_type->name, labeling_behaviors[sbsec->behavior-1]); } /* Initialize the root inode. */ rc = inode_doinit_with_dentry(sb->s_root->d_inode, sb->s_root); /* Initialize any other inodes associated with the superblock, e.g. inodes created prior to initial policy load or inodes created during get_sb by a pseudo filesystem that directly populates itself. */ spin_lock(&sbsec->isec_lock);next_inode: if (!list_empty(&sbsec->isec_head)) { struct inode_security_struct *isec = list_entry(sbsec->isec_head.next, struct inode_security_struct, list); struct inode *inode = isec->inode; spin_unlock(&sbsec->isec_lock); inode = igrab(inode); if (inode) { inode_doinit(inode); iput(inode); } spin_lock(&sbsec->isec_lock); list_del_init(&isec->list); goto next_inode; } spin_unlock(&sbsec->isec_lock);out: up(&sbsec->sem); return rc;}static inline u16 inode_mode_to_security_class(umode_t mode){ switch (mode & S_IFMT) { case S_IFSOCK: return SECCLASS_SOCK_FILE; case S_IFLNK: return SECCLASS_LNK_FILE; case S_IFREG: return SECCLASS_FILE; case S_IFBLK: return SECCLASS_BLK_FILE; case S_IFDIR: return SECCLASS_DIR; case S_IFCHR: return SECCLASS_CHR_FILE; case S_IFIFO: return SECCLASS_FIFO_FILE; } return SECCLASS_FILE;}static inline u16 socket_type_to_security_class(int family, int type, int protocol){ switch (family) { case PF_UNIX: switch (type) { case SOCK_STREAM: return SECCLASS_UNIX_STREAM_SOCKET; case SOCK_DGRAM: return SECCLASS_UNIX_DGRAM_SOCKET; } case PF_INET: case PF_INET6: switch (type) { case SOCK_STREAM: return SECCLASS_TCP_SOCKET; case SOCK_DGRAM: return SECCLASS_UDP_SOCKET; case SOCK_RAW: return SECCLASS_RAWIP_SOCKET; } case PF_NETLINK: switch (protocol) { case NETLINK_ROUTE: return SECCLASS_NETLINK_ROUTE_SOCKET; case NETLINK_FIREWALL: return SECCLASS_NETLINK_FIREWALL_SOCKET; case NETLINK_TCPDIAG: return SECCLASS_NETLINK_TCPDIAG_SOCKET; case NETLINK_NFLOG: return SECCLASS_NETLINK_NFLOG_SOCKET; case NETLINK_XFRM: return SECCLASS_NETLINK_XFRM_SOCKET; case NETLINK_SELINUX: return SECCLASS_NETLINK_SELINUX_SOCKET; case NETLINK_AUDIT: return SECCLASS_NETLINK_AUDIT_SOCKET; case NETLINK_IP6_FW: return SECCLASS_NETLINK_IP6FW_SOCKET; case NETLINK_DNRTMSG: return SECCLASS_NETLINK_DNRT_SOCKET; default: return SECCLASS_NETLINK_SOCKET; } case PF_PACKET: return SECCLASS_PACKET_SOCKET; case PF_KEY: return SECCLASS_KEY_SOCKET; } return SECCLASS_SOCKET;}#ifdef CONFIG_PROC_FSstatic int selinux_proc_get_sid(struct proc_dir_entry *de, u16 tclass, u32 *sid){ int buflen, rc; char *buffer, *path, *end; buffer = (char*)__get_free_page(GFP_KERNEL); if (!buffer) return -ENOMEM; buflen = PAGE_SIZE; end = buffer+buflen; *--end = '\0'; buflen--; path = end-1; *path = '/'; while (de && de != de->parent) { buflen -= de->namelen + 1; if (buflen < 0) break; end -= de->namelen; memcpy(end, de->name, de->namelen); *--end = '/'; path = end; de = de->parent; } rc = security_genfs_sid("proc", path, tclass, sid); free_page((unsigned long)buffer); return rc;}#elsestatic int selinux_proc_get_sid(struct proc_dir_entry *de, u16 tclass, u32 *sid){ return -EINVAL;}#endif/* The inode's security attributes must be initialized before first use. */static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry){ struct superblock_security_struct *sbsec = NULL; struct inode_security_struct *isec = inode->i_security; u32 sid; struct dentry *dentry;#define INITCONTEXTLEN 255 char *context = NULL; unsigned len = 0; int rc = 0; int hold_sem = 0; if (isec->initialized) goto out; down(&isec->sem); hold_sem = 1; if (isec->initialized) goto out; sbsec = inode->i_sb->s_security; if (!sbsec->initialized) { /* Defer initialization until selinux_complete_init, after the initial policy is loaded and the security server is ready to handle calls. */ spin_lock(&sbsec->isec_lock); if (list_empty(&isec->list)) list_add(&isec->list, &sbsec->isec_head); spin_unlock(&sbsec->isec_lock); goto out; } switch (sbsec->behavior) { case SECURITY_FS_USE_XATTR: if (!inode->i_op->getxattr) { isec->sid = sbsec->def_sid; break; } /* Need a dentry, since the xattr API requires one. Life would be simpler if we could just pass the inode. */ if (opt_dentry) { /* Called from d_instantiate or d_splice_alias. */ dentry = dget(opt_dentry); } else { /* Called from selinux_complete_init, try to find a dentry. */ dentry = d_find_alias(inode); } if (!dentry) { printk(KERN_WARNING "%s: no dentry for dev=%s " "ino=%ld\n", __FUNCTION__, inode->i_sb->s_id, inode->i_ino); goto out; } len = INITCONTEXTLEN; context = kmalloc(len, GFP_KERNEL); if (!context) { rc = -ENOMEM; dput(dentry); goto out; } rc = inode->i_op->getxattr(dentry, XATTR_NAME_SELINUX, context, len); if (rc == -ERANGE) { /* Need a larger buffer. Query for the right size. */ rc = inode->i_op->getxattr(dentry, XATTR_NAME_SELINUX, NULL, 0); if (rc < 0) { dput(dentry); goto out; } kfree(context); len = rc; context = kmalloc(len, GFP_KERNEL); if (!context) { rc = -ENOMEM; dput(dentry); goto out; } rc = inode->i_op->getxattr(dentry, XATTR_NAME_SELINUX, context, len); } dput(dentry); if (rc < 0) { if (rc != -ENODATA) { printk(KERN_WARNING "%s: getxattr returned " "%d for dev=%s ino=%ld\n", __FUNCTION__, -rc, inode->i_sb->s_id, inode->i_ino); kfree(context); goto out; } /* Map ENODATA to the default file SID */ sid = sbsec->def_sid; rc = 0; } else { rc = security_context_to_sid(context, rc, &sid); if (rc) { printk(KERN_WARNING "%s: context_to_sid(%s) " "returned %d for dev=%s ino=%ld\n", __FUNCTION__, context, -rc, inode->i_sb->s_id, inode->i_ino); kfree(context); goto out; } } kfree(context); isec->sid = sid; break; case SECURITY_FS_USE_TASK: isec->sid = isec->task_sid; break; case SECURITY_FS_USE_TRANS: /* Default to the fs SID. */ isec->sid = sbsec->sid; /* Try to obtain a transition SID. */ isec->sclass = inode_mode_to_security_class(inode->i_mode); rc = security_transition_sid(isec->task_sid, sbsec->sid, isec->sclass, &sid); if (rc) goto out; isec->sid = sid; break; default: /* Default to the fs SID. */ isec->sid = sbsec->sid; if (sbsec->proc) { struct proc_inode *proci = PROC_I(inode); if (proci->pde) { isec->sclass = inode_mode_to_security_class(inode->i_mode); rc = selinux_proc_get_sid(proci->pde, isec->sclass, &sid); if (rc) goto out; isec->sid = sid; } } break; } isec->initialized = 1;out: if (inode->i_sock) { struct socket *sock = SOCKET_I(inode); if (sock->sk) { isec->sclass = socket_type_to_security_class(sock->sk->sk_family, sock->sk->sk_type, sock->sk->sk_protocol); } else { isec->sclass = SECCLASS_SOCKET; } } else { isec->sclass = inode_mode_to_security_class(inode->i_mode); } if (hold_sem) up(&isec->sem); return rc;}/* Convert a Linux signal to an access vector. */static inline u32 signal_to_av(int sig){ u32 perm = 0; switch (sig) { 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;}/* Check permission betweeen a pair of tasks, e.g. signal checks, fork check, ptrace check, etc. */int task_has_perm(struct task_struct *tsk1, struct task_struct *tsk2, u32 perms){ struct task_security_struct *tsec1, *tsec2; tsec1 = tsk1->security; tsec2 = tsk2->security; return avc_has_perm(tsec1->sid, tsec2->sid, SECCLASS_PROCESS, perms, &tsec2->avcr, NULL);}/* Check whether a task is allowed to use a capability. */int task_has_capability(struct task_struct *tsk, int cap){ struct task_security_struct *tsec; struct avc_audit_data ad; tsec = tsk->security; AVC_AUDIT_DATA_INIT(&ad,CAP); ad.tsk = tsk; ad.u.cap = cap; return avc_has_perm(tsec->sid, tsec->sid, SECCLASS_CAPABILITY, CAP_TO_MASK(cap), NULL, &ad);}/* Check whether a task is allowed to use a system operation. */int task_has_system(struct task_struct *tsk, u32 perms){ struct task_security_struct *tsec; tsec = tsk->security; return avc_has_perm(tsec->sid, SECINITSID_KERNEL, SECCLASS_SYSTEM, perms, NULL, NULL);}/* Check whether a task has a particular permission to an inode. The 'aeref' parameter is optional and allows other AVC entry references to be passed (e.g. the one in the struct file). The 'adp' parameter is optional and allows other audit data to be passed (e.g. the dentry). */int inode_has_perm(struct task_struct *tsk, struct inode *inode, u32 perms, struct avc_entry_ref *aeref, struct avc_audit_data *adp){ struct task_security_struct *tsec; struct inode_security_struct *isec; struct avc_audit_data ad; tsec = tsk->security; isec = inode->i_security; if (!adp) { adp = &ad; AVC_AUDIT_DATA_INIT(&ad, FS); ad.u.fs.inode = inode; } return avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, aeref ? aeref : &isec->avcr, adp);}/* Same as inode_has_perm, but pass explicit audit data containing the dentry to help the auditing code to more easily generate the pathname if needed. */static inline int dentry_has_perm(struct task_struct *tsk, struct vfsmount *mnt, struct dentry *dentry, u32 av){ struct inode *inode = dentry->d_inode; struct avc_audit_data ad; AVC_AUDIT_DATA_INIT(&ad,FS); ad.u.fs.mnt = mnt; ad.u.fs.dentry = dentry; return inode_has_perm(tsk, inode, av, NULL, &ad);}/* Check whether a task can use an open file descriptor to access an inode in a given way. Check access to the descriptor itself, and then use dentry_has_perm to check a particular permission to the file. Access to the descriptor is implicitly granted if it has the same SID as the process. If av is zero, then access to the file is not checked, e.g. for cases where only the descriptor is affected like seek. */static inline int file_has_perm(struct task_struct *tsk, struct file *file, u32 av){ struct task_security_struct *tsec = tsk->security; struct file_security_struct *fsec = file->f_security; struct vfsmount *mnt = file->f_vfsmnt; struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; struct avc_audit_data ad; int rc; AVC_AUDIT_DATA_INIT(&ad, FS); ad.u.fs.mnt = mnt; ad.u.fs.dentry = dentry; if (tsec->sid != fsec->sid) { rc = avc_has_perm(tsec->sid, fsec->sid, SECCLASS_FD, FD__USE, &fsec->avcr, &ad); if (rc) return rc; } /* av is zero if only checking access to the descriptor. */ if (av) return inode_has_perm(tsk, inode, av, &fsec->inode_avcr, &ad); return 0;}/* Check whether a task can create a file. */static int may_create(struct inode *dir, struct dentry *dentry, u16 tclass){ struct task_security_struct *tsec; struct inode_security_struct *dsec; struct superblock_security_struct *sbsec; u32 newsid; struct avc_audit_data ad; int rc; tsec = current->security; dsec = dir->i_security; sbsec = dir->i_sb->s_security; AVC_AUDIT_DATA_INIT(&ad, FS); ad.u.fs.dentry = dentry; rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR, DIR__ADD_NAME | DIR__SEARCH, &dsec->avcr, &ad);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -