diff options
Diffstat (limited to 'security/selinux/hooks.c')
-rw-r--r-- | security/selinux/hooks.c | 130 |
1 files changed, 106 insertions, 24 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 280235cc3a98..781f88b125c6 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -452,6 +452,13 @@ static int sb_finish_set_opts(struct super_block *sb) if (selinux_is_sblabel_mnt(sb)) sbsec->flags |= SBLABEL_MNT; + /* + * Special handling for rootfs. Is genfs but supports + * setting SELinux context on in-core inodes. + */ + if (strncmp(sb->s_type->name, "rootfs", sizeof("rootfs")) == 0) + sbsec->flags |= SBLABEL_MNT; + /* Initialize the root inode. */ rc = inode_doinit_with_dentry(root_inode, root); @@ -725,7 +732,12 @@ static int selinux_set_mnt_opts(struct super_block *sb, } if (strcmp(sb->s_type->name, "proc") == 0) - sbsec->flags |= SE_SBPROC; + sbsec->flags |= SE_SBPROC | SE_SBGENFS; + + if (!strcmp(sb->s_type->name, "debugfs") || + !strcmp(sb->s_type->name, "sysfs") || + !strcmp(sb->s_type->name, "pstore")) + sbsec->flags |= SE_SBGENFS; if (!sbsec->behavior) { /* @@ -1221,12 +1233,13 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc return SECCLASS_SOCKET; } -#ifdef CONFIG_PROC_FS -static int selinux_proc_get_sid(struct dentry *dentry, - u16 tclass, - u32 *sid) +static int selinux_genfs_get_sid(struct dentry *dentry, + u16 tclass, + u16 flags, + u32 *sid) { int rc; + struct super_block *sb = dentry->d_inode->i_sb; char *buffer, *path; buffer = (char *)__get_free_page(GFP_KERNEL); @@ -1237,26 +1250,20 @@ static int selinux_proc_get_sid(struct dentry *dentry, if (IS_ERR(path)) rc = PTR_ERR(path); else { - /* each process gets a /proc/PID/ entry. Strip off the - * PID part to get a valid selinux labeling. - * e.g. /proc/1/net/rpc/nfs -> /net/rpc/nfs */ - while (path[1] >= '0' && path[1] <= '9') { - path[1] = '/'; - path++; + if (flags & SE_SBPROC) { + /* each process gets a /proc/PID/ entry. Strip off the + * PID part to get a valid selinux labeling. + * e.g. /proc/1/net/rpc/nfs -> /net/rpc/nfs */ + while (path[1] >= '0' && path[1] <= '9') { + path[1] = '/'; + path++; + } } - rc = security_genfs_sid("proc", path, tclass, sid); + rc = security_genfs_sid(sb->s_type->name, path, tclass, sid); } free_page((unsigned long)buffer); return rc; } -#else -static int selinux_proc_get_sid(struct dentry *dentry, - 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) @@ -1413,7 +1420,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent /* Default to the fs superblock SID. */ isec->sid = sbsec->sid; - if ((sbsec->flags & SE_SBPROC) && !S_ISLNK(inode->i_mode)) { + if ((sbsec->flags & SE_SBGENFS) && !S_ISLNK(inode->i_mode)) { /* We must have a dentry to determine the label on * procfs inodes */ if (opt_dentry) @@ -1436,7 +1443,8 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent if (!dentry) goto out_unlock; isec->sclass = inode_mode_to_security_class(inode->i_mode); - rc = selinux_proc_get_sid(dentry, isec->sclass, &sid); + rc = selinux_genfs_get_sid(dentry, isec->sclass, + sbsec->flags, &sid); dput(dentry); if (rc) goto out_unlock; @@ -2948,7 +2956,8 @@ static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr) ATTR_ATIME_SET | ATTR_MTIME_SET | ATTR_TIMES_SET)) return dentry_has_perm(cred, dentry, FILE__SETATTR); - if (selinux_policycap_openperm && (ia_valid & ATTR_SIZE)) + if (selinux_policycap_openperm && (ia_valid & ATTR_SIZE) + && !(ia_valid & ATTR_FILE)) av |= FILE__OPEN; return dentry_has_perm(cred, dentry, av); @@ -3233,6 +3242,46 @@ static void selinux_file_free_security(struct file *file) file_free_security(file); } +/* + * Check whether a task has the ioctl permission and cmd + * operation to an inode. + */ +int ioctl_has_perm(const struct cred *cred, struct file *file, + u32 requested, u16 cmd) +{ + struct common_audit_data ad; + struct file_security_struct *fsec = file->f_security; + struct inode *inode = file_inode(file); + struct inode_security_struct *isec = inode->i_security; + struct lsm_ioctlop_audit ioctl; + u32 ssid = cred_sid(cred); + int rc; + u8 driver = cmd >> 8; + u8 xperm = cmd & 0xff; + + ad.type = LSM_AUDIT_DATA_IOCTL_OP; + ad.u.op = &ioctl; + ad.u.op->cmd = cmd; + ad.u.op->path = file->f_path; + + if (ssid != fsec->sid) { + rc = avc_has_perm(ssid, fsec->sid, + SECCLASS_FD, + FD__USE, + &ad); + if (rc) + goto out; + } + + if (unlikely(IS_PRIVATE(inode))) + return 0; + + rc = avc_has_extended_perms(ssid, isec->sid, isec->sclass, + requested, driver, xperm, &ad); +out: + return rc; +} + static int selinux_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -3275,7 +3324,7 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd, * to the file's ioctl() function. */ default: - error = file_has_perm(cred, file, FILE__IOCTL); + error = ioctl_has_perm(cred, file, FILE__IOCTL, (u16) cmd); } return error; } @@ -3619,6 +3668,38 @@ static int selinux_kernel_module_request(char *kmod_name) SYSTEM__MODULE_REQUEST, &ad); } +static int selinux_kernel_module_from_file(struct file *file) +{ + struct common_audit_data ad; + struct inode_security_struct *isec; + struct file_security_struct *fsec; + struct inode *inode; + u32 sid = current_sid(); + int rc; + + /* init_module */ + if (file == NULL) + return avc_has_perm(sid, sid, SECCLASS_SYSTEM, + SYSTEM__MODULE_LOAD, NULL); + + /* finit_module */ + ad.type = LSM_AUDIT_DATA_PATH; + ad.u.path = file->f_path; + + inode = file_inode(file); + isec = inode->i_security; + fsec = file->f_security; + + if (sid != fsec->sid) { + rc = avc_has_perm(sid, fsec->sid, SECCLASS_FD, FD__USE, &ad); + if (rc) + return rc; + } + + return avc_has_perm(sid, isec->sid, SECCLASS_SYSTEM, + SYSTEM__MODULE_LOAD, &ad); +} + static int selinux_task_setpgid(struct task_struct *p, pid_t pgid) { return current_has_perm(p, PROCESS__SETPGID); @@ -5931,6 +6012,7 @@ static struct security_operations selinux_ops = { .kernel_act_as = selinux_kernel_act_as, .kernel_create_files_as = selinux_kernel_create_files_as, .kernel_module_request = selinux_kernel_module_request, + .kernel_module_from_file = selinux_kernel_module_from_file, .task_setpgid = selinux_task_setpgid, .task_getpgid = selinux_task_getpgid, .task_getsid = selinux_task_getsid, |