summaryrefslogtreecommitdiff
path: root/security/selinux/hooks.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-05-19 16:21:36 (GMT)
committerLinus Torvalds <torvalds@linux-foundation.org>2016-05-19 16:21:36 (GMT)
commitf4f27d0028aabce57e44c16c2fdefccd6310d2f3 (patch)
tree09f25601316d22b64165c19042da51c101bde3c4 /security/selinux/hooks.c
parent2600a46ee0ed57c0e0a382c2a37ebac64d374d20 (diff)
parentb937190c40de0f6f07f592042e3097b16c6b0130 (diff)
downloadlinux-f4f27d0028aabce57e44c16c2fdefccd6310d2f3.tar.xz
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull security subsystem updates from James Morris: "Highlights: - A new LSM, "LoadPin", from Kees Cook is added, which allows forcing of modules and firmware to be loaded from a specific device (this is from ChromeOS, where the device as a whole is verified cryptographically via dm-verity). This is disabled by default but can be configured to be enabled by default (don't do this if you don't know what you're doing). - Keys: allow authentication data to be stored in an asymmetric key. Lots of general fixes and updates. - SELinux: add restrictions for loading of kernel modules via finit_module(). Distinguish non-init user namespace capability checks. Apply execstack check on thread stacks" * 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (48 commits) LSM: LoadPin: provide enablement CONFIG Yama: use atomic allocations when reporting seccomp: Fix comment typo ima: add support for creating files using the mknodat syscall ima: fix ima_inode_post_setattr vfs: forbid write access when reading a file into memory fs: fix over-zealous use of "const" selinux: apply execstack check on thread stacks selinux: distinguish non-init user namespace capability checks LSM: LoadPin for kernel file loading restrictions fs: define a string representation of the kernel_read_file_id enumeration Yama: consolidate error reporting string_helpers: add kstrdup_quotable_file string_helpers: add kstrdup_quotable_cmdline string_helpers: add kstrdup_quotable selinux: check ss_initialized before revalidating an inode label selinux: delay inode label lookup as long as possible selinux: don't revalidate an inode's label when explicitly setting it selinux: Change bool variable name to index. KEYS: Add KEYCTL_DH_COMPUTE command ...
Diffstat (limited to 'security/selinux/hooks.c')
-rw-r--r--security/selinux/hooks.c144
1 files changed, 102 insertions, 42 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 3140efa..a86d537 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -259,7 +259,7 @@ static int __inode_security_revalidate(struct inode *inode,
might_sleep_if(may_sleep);
- if (isec->initialized == LABEL_INVALID) {
+ if (ss_initialized && isec->initialized != LABEL_INITIALIZED) {
if (!may_sleep)
return -ECHILD;
@@ -297,6 +297,13 @@ static struct inode_security_struct *inode_security(struct inode *inode)
return inode->i_security;
}
+static struct inode_security_struct *backing_inode_security_novalidate(struct dentry *dentry)
+{
+ struct inode *inode = d_backing_inode(dentry);
+
+ return inode->i_security;
+}
+
/*
* Get the security label of a dentry's backing inode.
*/
@@ -687,7 +694,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
struct superblock_security_struct *sbsec = sb->s_security;
const char *name = sb->s_type->name;
struct dentry *root = sbsec->sb->s_root;
- struct inode_security_struct *root_isec = backing_inode_security(root);
+ struct inode_security_struct *root_isec;
u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0;
u32 defcontext_sid = 0;
char **mount_options = opts->mnt_opts;
@@ -730,6 +737,8 @@ static int selinux_set_mnt_opts(struct super_block *sb,
&& (num_opts == 0))
goto out;
+ root_isec = backing_inode_security_novalidate(root);
+
/*
* parse the mount options, check if they are valid sids.
* also check if someone is trying to mount the same sb more
@@ -1623,7 +1632,7 @@ static int current_has_perm(const struct task_struct *tsk,
/* Check whether a task is allowed to use a capability. */
static int cred_has_capability(const struct cred *cred,
- int cap, int audit)
+ int cap, int audit, bool initns)
{
struct common_audit_data ad;
struct av_decision avd;
@@ -1637,10 +1646,10 @@ static int cred_has_capability(const struct cred *cred,
switch (CAP_TO_INDEX(cap)) {
case 0:
- sclass = SECCLASS_CAPABILITY;
+ sclass = initns ? SECCLASS_CAPABILITY : SECCLASS_CAP_USERNS;
break;
case 1:
- sclass = SECCLASS_CAPABILITY2;
+ sclass = initns ? SECCLASS_CAPABILITY2 : SECCLASS_CAP2_USERNS;
break;
default:
printk(KERN_ERR
@@ -1782,7 +1791,6 @@ static int selinux_determine_inode_label(struct inode *dir,
u32 *_new_isid)
{
const struct superblock_security_struct *sbsec = dir->i_sb->s_security;
- const struct inode_security_struct *dsec = inode_security(dir);
const struct task_security_struct *tsec = current_security();
if ((sbsec->flags & SE_SBINITIALIZED) &&
@@ -1792,6 +1800,7 @@ static int selinux_determine_inode_label(struct inode *dir,
tsec->create_sid) {
*_new_isid = tsec->create_sid;
} else {
+ const struct inode_security_struct *dsec = inode_security(dir);
return security_transition_sid(tsec->sid, dsec->sid, tclass,
name, _new_isid);
}
@@ -2076,7 +2085,7 @@ static int selinux_binder_transfer_file(struct task_struct *from,
u32 sid = task_sid(to);
struct file_security_struct *fsec = file->f_security;
struct dentry *dentry = file->f_path.dentry;
- struct inode_security_struct *isec = backing_inode_security(dentry);
+ struct inode_security_struct *isec;
struct common_audit_data ad;
int rc;
@@ -2095,6 +2104,7 @@ static int selinux_binder_transfer_file(struct task_struct *from,
if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return 0;
+ isec = backing_inode_security(dentry);
return avc_has_perm(sid, isec->sid, isec->sclass, file_to_av(file),
&ad);
}
@@ -2143,7 +2153,7 @@ static int selinux_capset(struct cred *new, const struct cred *old,
static int selinux_capable(const struct cred *cred, struct user_namespace *ns,
int cap, int audit)
{
- return cred_has_capability(cred, cap, audit);
+ return cred_has_capability(cred, cap, audit, ns == &init_user_ns);
}
static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb)
@@ -2221,7 +2231,7 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
int rc, cap_sys_admin = 0;
rc = cred_has_capability(current_cred(), CAP_SYS_ADMIN,
- SECURITY_CAP_NOAUDIT);
+ SECURITY_CAP_NOAUDIT, true);
if (rc == 0)
cap_sys_admin = 1;
@@ -2230,6 +2240,20 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
/* binprm security operations */
+static u32 ptrace_parent_sid(struct task_struct *task)
+{
+ u32 sid = 0;
+ struct task_struct *tracer;
+
+ rcu_read_lock();
+ tracer = ptrace_parent(task);
+ if (tracer)
+ sid = task_sid(tracer);
+ rcu_read_unlock();
+
+ return sid;
+}
+
static int check_nnp_nosuid(const struct linux_binprm *bprm,
const struct task_security_struct *old_tsec,
const struct task_security_struct *new_tsec)
@@ -2351,18 +2375,7 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
* changes its SID has the appropriate permit */
if (bprm->unsafe &
(LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
- struct task_struct *tracer;
- struct task_security_struct *sec;
- u32 ptsid = 0;
-
- rcu_read_lock();
- tracer = ptrace_parent(current);
- if (likely(tracer != NULL)) {
- sec = __task_cred(tracer)->security;
- ptsid = sec->sid;
- }
- rcu_read_unlock();
-
+ u32 ptsid = ptrace_parent_sid(current);
if (ptsid != 0) {
rc = avc_has_perm(ptsid, new_tsec->sid,
SECCLASS_PROCESS,
@@ -3046,7 +3059,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
struct inode *inode = d_backing_inode(dentry);
- struct inode_security_struct *isec = backing_inode_security(dentry);
+ struct inode_security_struct *isec;
struct superblock_security_struct *sbsec;
struct common_audit_data ad;
u32 newsid, sid = current_sid();
@@ -3065,6 +3078,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
ad.type = LSM_AUDIT_DATA_DENTRY;
ad.u.dentry = dentry;
+ isec = backing_inode_security(dentry);
rc = avc_has_perm(sid, isec->sid, isec->sclass,
FILE__RELABELFROM, &ad);
if (rc)
@@ -3123,7 +3137,7 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
int flags)
{
struct inode *inode = d_backing_inode(dentry);
- struct inode_security_struct *isec = backing_inode_security(dentry);
+ struct inode_security_struct *isec;
u32 newsid;
int rc;
@@ -3140,6 +3154,7 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
return;
}
+ isec = backing_inode_security(dentry);
isec->sclass = inode_mode_to_security_class(inode->i_mode);
isec->sid = newsid;
isec->initialized = LABEL_INITIALIZED;
@@ -3181,7 +3196,7 @@ static int selinux_inode_getsecurity(struct inode *inode, const char *name, void
u32 size;
int error;
char *context = NULL;
- struct inode_security_struct *isec = inode_security(inode);
+ struct inode_security_struct *isec;
if (strcmp(name, XATTR_SELINUX_SUFFIX))
return -EOPNOTSUPP;
@@ -3199,7 +3214,8 @@ static int selinux_inode_getsecurity(struct inode *inode, const char *name, void
SECURITY_CAP_NOAUDIT);
if (!error)
error = cred_has_capability(current_cred(), CAP_MAC_ADMIN,
- SECURITY_CAP_NOAUDIT);
+ SECURITY_CAP_NOAUDIT, true);
+ isec = inode_security(inode);
if (!error)
error = security_sid_to_context_force(isec->sid, &context,
&size);
@@ -3220,7 +3236,7 @@ out_nofree:
static int selinux_inode_setsecurity(struct inode *inode, const char *name,
const void *value, size_t size, int flags)
{
- struct inode_security_struct *isec = inode_security(inode);
+ struct inode_security_struct *isec = inode_security_novalidate(inode);
u32 newsid;
int rc;
@@ -3309,7 +3325,7 @@ static int ioctl_has_perm(const struct cred *cred, struct file *file,
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_security(inode);
+ struct inode_security_struct *isec;
struct lsm_ioctlop_audit ioctl;
u32 ssid = cred_sid(cred);
int rc;
@@ -3333,6 +3349,7 @@ static int ioctl_has_perm(const struct cred *cred, struct file *file,
if (unlikely(IS_PRIVATE(inode)))
return 0;
+ isec = inode_security(inode);
rc = avc_has_extended_perms(ssid, isec->sid, isec->sclass,
requested, driver, xperm, &ad);
out:
@@ -3374,7 +3391,7 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd,
case KDSKBENT:
case KDSKBSENT:
error = cred_has_capability(cred, CAP_SYS_TTY_CONFIG,
- SECURITY_CAP_AUDIT);
+ SECURITY_CAP_AUDIT, true);
break;
/* default case assumes that the command will go
@@ -3463,8 +3480,9 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
vma->vm_end <= vma->vm_mm->brk) {
rc = cred_has_perm(cred, cred, PROCESS__EXECHEAP);
} else if (!vma->vm_file &&
- vma->vm_start <= vma->vm_mm->start_stack &&
- vma->vm_end >= vma->vm_mm->start_stack) {
+ ((vma->vm_start <= vma->vm_mm->start_stack &&
+ vma->vm_end >= vma->vm_mm->start_stack) ||
+ vma_is_stack_for_task(vma, current))) {
rc = current_has_perm(current, PROCESS__EXECSTACK);
} else if (vma->vm_file && vma->anon_vma) {
/*
@@ -3720,6 +3738,52 @@ 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;
+ 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;
+
+ fsec = file->f_security;
+ if (sid != fsec->sid) {
+ rc = avc_has_perm(sid, fsec->sid, SECCLASS_FD, FD__USE, &ad);
+ if (rc)
+ return rc;
+ }
+
+ isec = inode_security(file_inode(file));
+ return avc_has_perm(sid, isec->sid, SECCLASS_SYSTEM,
+ SYSTEM__MODULE_LOAD, &ad);
+}
+
+static int selinux_kernel_read_file(struct file *file,
+ enum kernel_read_file_id id)
+{
+ int rc = 0;
+
+ switch (id) {
+ case READING_MODULE:
+ rc = selinux_kernel_module_from_file(file);
+ break;
+ default:
+ break;
+ }
+
+ return rc;
+}
+
static int selinux_task_setpgid(struct task_struct *p, pid_t pgid)
{
return current_has_perm(p, PROCESS__SETPGID);
@@ -4599,6 +4663,7 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *
{
u32 peer_secid = SECSID_NULL;
u16 family;
+ struct inode_security_struct *isec;
if (skb && skb->protocol == htons(ETH_P_IP))
family = PF_INET;
@@ -4609,9 +4674,10 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *
else
goto out;
- if (sock && family == PF_UNIX)
- selinux_inode_getsecid(SOCK_INODE(sock), &peer_secid);
- else if (skb)
+ if (sock && family == PF_UNIX) {
+ isec = inode_security_novalidate(SOCK_INODE(sock));
+ peer_secid = isec->sid;
+ } else if (skb)
selinux_skb_peerlbl_sid(skb, family, &peer_secid);
out:
@@ -5676,7 +5742,6 @@ static int selinux_setprocattr(struct task_struct *p,
char *name, void *value, size_t size)
{
struct task_security_struct *tsec;
- struct task_struct *tracer;
struct cred *new;
u32 sid = 0, ptsid;
int error;
@@ -5783,14 +5848,8 @@ static int selinux_setprocattr(struct task_struct *p,
/* Check for ptracing, and update the task SID if ok.
Otherwise, leave SID unchanged and fail. */
- ptsid = 0;
- rcu_read_lock();
- tracer = ptrace_parent(p);
- if (tracer)
- ptsid = task_sid(tracer);
- rcu_read_unlock();
-
- if (tracer) {
+ ptsid = ptrace_parent_sid(p);
+ if (ptsid != 0) {
error = avc_has_perm(ptsid, sid, SECCLASS_PROCESS,
PROCESS__PTRACE, NULL);
if (error)
@@ -6021,6 +6080,7 @@ static struct security_hook_list selinux_hooks[] = {
LSM_HOOK_INIT(kernel_act_as, selinux_kernel_act_as),
LSM_HOOK_INIT(kernel_create_files_as, selinux_kernel_create_files_as),
LSM_HOOK_INIT(kernel_module_request, selinux_kernel_module_request),
+ LSM_HOOK_INIT(kernel_read_file, selinux_kernel_read_file),
LSM_HOOK_INIT(task_setpgid, selinux_task_setpgid),
LSM_HOOK_INIT(task_getpgid, selinux_task_getpgid),
LSM_HOOK_INIT(task_getsid, selinux_task_getsid),