summaryrefslogtreecommitdiff
path: root/security/selinux
diff options
context:
space:
mode:
Diffstat (limited to 'security/selinux')
-rw-r--r--security/selinux/Kconfig38
-rw-r--r--security/selinux/hooks.c292
-rw-r--r--security/selinux/include/classmap.h30
-rw-r--r--security/selinux/include/conditional.h2
-rw-r--r--security/selinux/include/netlabel.h4
-rw-r--r--security/selinux/include/objsec.h5
-rw-r--r--security/selinux/include/security.h4
-rw-r--r--security/selinux/netlabel.c36
-rw-r--r--security/selinux/selinuxfs.c2
-rw-r--r--security/selinux/ss/conditional.c2
-rw-r--r--security/selinux/ss/ebitmap.c5
-rw-r--r--security/selinux/ss/policydb.c12
-rw-r--r--security/selinux/ss/services.c76
13 files changed, 317 insertions, 191 deletions
diff --git a/security/selinux/Kconfig b/security/selinux/Kconfig
index 8691e92f27e5..ea7e3efbe0f7 100644
--- a/security/selinux/Kconfig
+++ b/security/selinux/Kconfig
@@ -93,41 +93,3 @@ config SECURITY_SELINUX_CHECKREQPROT_VALUE
via /selinux/checkreqprot if authorized by policy.
If you are unsure how to answer this question, answer 0.
-
-config SECURITY_SELINUX_POLICYDB_VERSION_MAX
- bool "NSA SELinux maximum supported policy format version"
- depends on SECURITY_SELINUX
- default n
- help
- This option enables the maximum policy format version supported
- by SELinux to be set to a particular value. This value is reported
- to userspace via /selinux/policyvers and used at policy load time.
- It can be adjusted downward to support legacy userland (init) that
- does not correctly handle kernels that support newer policy versions.
-
- Examples:
- For the Fedora Core 3 or 4 Linux distributions, enable this option
- and set the value via the next option. For Fedora Core 5 and later,
- do not enable this option.
-
- If you are unsure how to answer this question, answer N.
-
-config SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE
- int "NSA SELinux maximum supported policy format version value"
- depends on SECURITY_SELINUX_POLICYDB_VERSION_MAX
- range 15 23
- default 19
- help
- This option sets the value for the maximum policy format version
- supported by SELinux.
-
- Examples:
- For Fedora Core 3, use 18.
- For Fedora Core 4, use 19.
-
- If you are unsure how to answer this question, look for the
- policy format version supported by your policy toolchain, by
- running 'checkpolicy -V'. Or look at what policy you have
- installed under /etc/selinux/$SELINUXTYPE/policy, where
- SELINUXTYPE is defined in your /etc/selinux/config.
-
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 3140efa76a75..2205ea27aa0a 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
@@ -821,6 +830,28 @@ static int selinux_set_mnt_opts(struct super_block *sb,
goto out;
}
}
+
+ /*
+ * If this is a user namespace mount, no contexts are allowed
+ * on the command line and security labels must be ignored.
+ */
+ if (sb->s_user_ns != &init_user_ns) {
+ if (context_sid || fscontext_sid || rootcontext_sid ||
+ defcontext_sid) {
+ rc = -EACCES;
+ goto out;
+ }
+ if (sbsec->behavior == SECURITY_FS_USE_XATTR) {
+ sbsec->behavior = SECURITY_FS_USE_MNTPOINT;
+ rc = security_transition_sid(current_sid(), current_sid(),
+ SECCLASS_FILE, NULL,
+ &sbsec->mntpoint_sid);
+ if (rc)
+ goto out;
+ }
+ goto out_set_opts;
+ }
+
/* sets the context of the superblock for the fs being mounted. */
if (fscontext_sid) {
rc = may_context_mount_sb_relabel(fscontext_sid, sbsec, cred);
@@ -889,6 +920,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
sbsec->def_sid = defcontext_sid;
}
+out_set_opts:
rc = sb_finish_set_opts(sb);
out:
mutex_unlock(&sbsec->lock);
@@ -1623,7 +1655,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 +1669,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
@@ -1729,8 +1761,8 @@ static inline int file_path_has_perm(const struct cred *cred,
{
struct common_audit_data ad;
- ad.type = LSM_AUDIT_DATA_PATH;
- ad.u.path = file->f_path;
+ ad.type = LSM_AUDIT_DATA_FILE;
+ ad.u.file = file;
return inode_has_perm(cred, file_inode(file), av, &ad);
}
@@ -1752,8 +1784,8 @@ static int file_has_perm(const struct cred *cred,
u32 sid = cred_sid(cred);
int rc;
- ad.type = LSM_AUDIT_DATA_PATH;
- ad.u.path = file->f_path;
+ ad.type = LSM_AUDIT_DATA_FILE;
+ ad.u.file = file;
if (sid != fsec->sid) {
rc = avc_has_perm(sid, fsec->sid,
@@ -1776,14 +1808,13 @@ out:
/*
* Determine the label for an inode that might be unioned.
*/
-static int selinux_determine_inode_label(struct inode *dir,
- const struct qstr *name,
- u16 tclass,
- u32 *_new_isid)
+static int
+selinux_determine_inode_label(const struct task_security_struct *tsec,
+ struct inode *dir,
+ const struct qstr *name, u16 tclass,
+ 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) &&
(sbsec->behavior == SECURITY_FS_USE_MNTPOINT)) {
@@ -1792,6 +1823,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);
}
@@ -1825,8 +1857,8 @@ static int may_create(struct inode *dir,
if (rc)
return rc;
- rc = selinux_determine_inode_label(dir, &dentry->d_name, tclass,
- &newsid);
+ rc = selinux_determine_inode_label(current_security(), dir,
+ &dentry->d_name, tclass, &newsid);
if (rc)
return rc;
@@ -2076,7 +2108,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 +2127,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 +2176,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 +2254,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,12 +2263,26 @@ 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)
{
int nnp = (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS);
- int nosuid = (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID);
+ int nosuid = !mnt_may_suid(bprm->file->f_path.mnt);
int rc;
if (!nnp && !nosuid)
@@ -2318,8 +2365,8 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
new_tsec->sid = old_tsec->sid;
}
- ad.type = LSM_AUDIT_DATA_PATH;
- ad.u.path = bprm->file->f_path;
+ ad.type = LSM_AUDIT_DATA_FILE;
+ ad.u.file = bprm->file;
if (new_tsec->sid == old_tsec->sid) {
rc = avc_has_perm(old_tsec->sid, isec->sid,
@@ -2351,18 +2398,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,
@@ -2796,13 +2832,14 @@ static void selinux_inode_free_security(struct inode *inode)
}
static int selinux_dentry_init_security(struct dentry *dentry, int mode,
- struct qstr *name, void **ctx,
+ const struct qstr *name, void **ctx,
u32 *ctxlen)
{
u32 newsid;
int rc;
- rc = selinux_determine_inode_label(d_inode(dentry->d_parent), name,
+ rc = selinux_determine_inode_label(current_security(),
+ d_inode(dentry->d_parent), name,
inode_mode_to_security_class(mode),
&newsid);
if (rc)
@@ -2811,6 +2848,27 @@ static int selinux_dentry_init_security(struct dentry *dentry, int mode,
return security_sid_to_context(newsid, (char **)ctx, ctxlen);
}
+static int selinux_dentry_create_files_as(struct dentry *dentry, int mode,
+ struct qstr *name,
+ const struct cred *old,
+ struct cred *new)
+{
+ u32 newsid;
+ int rc;
+ struct task_security_struct *tsec;
+
+ rc = selinux_determine_inode_label(old->security,
+ d_inode(dentry->d_parent), name,
+ inode_mode_to_security_class(mode),
+ &newsid);
+ if (rc)
+ return rc;
+
+ tsec = new->security;
+ tsec->create_sid = newsid;
+ return 0;
+}
+
static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr,
const char **name,
@@ -2827,7 +2885,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
sid = tsec->sid;
newsid = tsec->create_sid;
- rc = selinux_determine_inode_label(
+ rc = selinux_determine_inode_label(current_security(),
dir, qstr,
inode_mode_to_security_class(inode->i_mode),
&newsid);
@@ -3046,7 +3104,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 +3123,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 +3182,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 +3199,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 +3241,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 +3259,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 +3281,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;
@@ -3254,6 +3315,41 @@ static void selinux_inode_getsecid(struct inode *inode, u32 *secid)
*secid = isec->sid;
}
+static int selinux_inode_copy_up(struct dentry *src, struct cred **new)
+{
+ u32 sid;
+ struct task_security_struct *tsec;
+ struct cred *new_creds = *new;
+
+ if (new_creds == NULL) {
+ new_creds = prepare_creds();
+ if (!new_creds)
+ return -ENOMEM;
+ }
+
+ tsec = new_creds->security;
+ /* Get label from overlay inode and set it in create_sid */
+ selinux_inode_getsecid(d_inode(src), &sid);
+ tsec->create_sid = sid;
+ *new = new_creds;
+ return 0;
+}
+
+static int selinux_inode_copy_up_xattr(const char *name)
+{
+ /* The copy_up hook above sets the initial context on an inode, but we
+ * don't then want to overwrite it by blindly copying all the lower
+ * xattrs up. Instead, we have to filter out SELinux-related xattrs.
+ */
+ if (strcmp(name, XATTR_NAME_SELINUX) == 0)
+ return 1; /* Discard */
+ /*
+ * Any other attribute apart from SELINUX is not claimed, supported
+ * by selinux.
+ */
+ return -EOPNOTSUPP;
+}
+
/* file security operations */
static int selinux_revalidate_file_permission(struct file *file, int mask)
@@ -3309,7 +3405,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 +3429,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 +3471,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 +3560,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 +3818,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_FILE;
+ ad.u.file = file;
+
+ 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);
@@ -3897,7 +4041,7 @@ out:
return ret;
}
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6)
/* Returns error only if unable to parse addresses */
static int selinux_parse_skb_ipv6(struct sk_buff *skb,
@@ -3988,7 +4132,7 @@ static int selinux_parse_skb(struct sk_buff *skb, struct common_audit_data *ad,
&ad->u.net->v4info.daddr);
goto okay;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6)
case PF_INET6:
ret = selinux_parse_skb_ipv6(skb, ad, proto);
if (ret)
@@ -4540,13 +4684,13 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
err = selinux_inet_sys_rcv_skb(sock_net(sk), skb->skb_iif,
addrp, family, peer_sid, &ad);
if (err) {
- selinux_netlbl_err(skb, err, 0);
+ selinux_netlbl_err(skb, family, err, 0);
return err;
}
err = avc_has_perm(sk_sid, peer_sid, SECCLASS_PEER,
PEER__RECV, &ad);
if (err) {
- selinux_netlbl_err(skb, err, 0);
+ selinux_netlbl_err(skb, family, err, 0);
return err;
}
}
@@ -4599,6 +4743,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 +4754,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:
@@ -4912,7 +5058,7 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb,
err = selinux_inet_sys_rcv_skb(dev_net(indev), indev->ifindex,
addrp, family, peer_sid, &ad);
if (err) {
- selinux_netlbl_err(skb, err, 1);
+ selinux_netlbl_err(skb, family, err, 1);
return NF_DROP;
}
}
@@ -4940,7 +5086,7 @@ static unsigned int selinux_ipv4_forward(void *priv,
return selinux_ip_forward(skb, state->in, PF_INET);
}
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6)
static unsigned int selinux_ipv6_forward(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state)
@@ -4998,6 +5144,15 @@ static unsigned int selinux_ipv4_output(void *priv,
return selinux_ip_output(skb, PF_INET);
}
+#if IS_ENABLED(CONFIG_IPV6)
+static unsigned int selinux_ipv6_output(void *priv,
+ struct sk_buff *skb,
+ const struct nf_hook_state *state)
+{
+ return selinux_ip_output(skb, PF_INET6);
+}
+#endif /* IPV6 */
+
static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
int ifindex,
u16 family)
@@ -5175,7 +5330,7 @@ static unsigned int selinux_ipv4_postroute(void *priv,
return selinux_ip_postroute(skb, state->out, PF_INET);
}
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6)
static unsigned int selinux_ipv6_postroute(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state)
@@ -5676,7 +5831,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 +5937,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)
@@ -5971,6 +6119,7 @@ static struct security_hook_list selinux_hooks[] = {
LSM_HOOK_INIT(sb_parse_opts_str, selinux_parse_opts_str),
LSM_HOOK_INIT(dentry_init_security, selinux_dentry_init_security),
+ LSM_HOOK_INIT(dentry_create_files_as, selinux_dentry_create_files_as),
LSM_HOOK_INIT(inode_alloc_security, selinux_inode_alloc_security),
LSM_HOOK_INIT(inode_free_security, selinux_inode_free_security),
@@ -5997,6 +6146,8 @@ static struct security_hook_list selinux_hooks[] = {
LSM_HOOK_INIT(inode_setsecurity, selinux_inode_setsecurity),
LSM_HOOK_INIT(inode_listsecurity, selinux_inode_listsecurity),
LSM_HOOK_INIT(inode_getsecid, selinux_inode_getsecid),
+ LSM_HOOK_INIT(inode_copy_up, selinux_inode_copy_up),
+ LSM_HOOK_INIT(inode_copy_up_xattr, selinux_inode_copy_up_xattr),
LSM_HOOK_INIT(file_permission, selinux_file_permission),
LSM_HOOK_INIT(file_alloc_security, selinux_file_alloc_security),
@@ -6021,6 +6172,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),
@@ -6225,7 +6377,7 @@ static struct nf_hook_ops selinux_nf_ops[] = {
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP_PRI_SELINUX_FIRST,
},
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6)
{
.hook = selinux_ipv6_postroute,
.pf = NFPROTO_IPV6,
@@ -6238,6 +6390,12 @@ static struct nf_hook_ops selinux_nf_ops[] = {
.hooknum = NF_INET_FORWARD,
.priority = NF_IP6_PRI_SELINUX_FIRST,
},
+ {
+ .hook = selinux_ipv6_output,
+ .pf = NFPROTO_IPV6,
+ .hooknum = NF_INET_LOCAL_OUT,
+ .priority = NF_IP6_PRI_SELINUX_FIRST,
+ },
#endif /* IPV6 */
};
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index ef83c4b85a33..1f1f4b2f6018 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -12,6 +12,18 @@
#define COMMON_IPC_PERMS "create", "destroy", "getattr", "setattr", "read", \
"write", "associate", "unix_read", "unix_write"
+#define COMMON_CAP_PERMS "chown", "dac_override", "dac_read_search", \
+ "fowner", "fsetid", "kill", "setgid", "setuid", "setpcap", \
+ "linux_immutable", "net_bind_service", "net_broadcast", \
+ "net_admin", "net_raw", "ipc_lock", "ipc_owner", "sys_module", \
+ "sys_rawio", "sys_chroot", "sys_ptrace", "sys_pacct", "sys_admin", \
+ "sys_boot", "sys_nice", "sys_resource", "sys_time", \
+ "sys_tty_config", "mknod", "lease", "audit_write", \
+ "audit_control", "setfcap"
+
+#define COMMON_CAP2_PERMS "mac_override", "mac_admin", "syslog", \
+ "wake_alarm", "block_suspend", "audit_read"
+
/*
* Note: The name for any socket class should be suffixed by "socket",
* and doesn't contain more than one substr of "socket".
@@ -32,16 +44,9 @@ struct security_class_mapping secclass_map[] = {
"setsockcreate", NULL } },
{ "system",
{ "ipc_info", "syslog_read", "syslog_mod",
- "syslog_console", "module_request", NULL } },
+ "syslog_console", "module_request", "module_load", NULL } },
{ "capability",
- { "chown", "dac_override", "dac_read_search",
- "fowner", "fsetid", "kill", "setgid", "setuid", "setpcap",
- "linux_immutable", "net_bind_service", "net_broadcast",
- "net_admin", "net_raw", "ipc_lock", "ipc_owner", "sys_module",
- "sys_rawio", "sys_chroot", "sys_ptrace", "sys_pacct", "sys_admin",
- "sys_boot", "sys_nice", "sys_resource", "sys_time",
- "sys_tty_config", "mknod", "lease", "audit_write",
- "audit_control", "setfcap", NULL } },
+ { COMMON_CAP_PERMS, NULL } },
{ "filesystem",
{ "mount", "remount", "unmount", "getattr",
"relabelfrom", "relabelto", "associate", "quotamod",
@@ -150,12 +155,15 @@ struct security_class_mapping secclass_map[] = {
{ "memprotect", { "mmap_zero", NULL } },
{ "peer", { "recv", NULL } },
{ "capability2",
- { "mac_override", "mac_admin", "syslog", "wake_alarm", "block_suspend",
- "audit_read", NULL } },
+ { COMMON_CAP2_PERMS, NULL } },
{ "kernel_service", { "use_as_override", "create_files_as", NULL } },
{ "tun_socket",
{ COMMON_SOCK_PERMS, "attach_queue", NULL } },
{ "binder", { "impersonate", "call", "set_context_mgr", "transfer",
NULL } },
+ { "cap_userns",
+ { COMMON_CAP_PERMS, NULL } },
+ { "cap2_userns",
+ { COMMON_CAP2_PERMS, NULL } },
{ NULL }
};
diff --git a/security/selinux/include/conditional.h b/security/selinux/include/conditional.h
index 67ce7a8d8301..ff4fddca9050 100644
--- a/security/selinux/include/conditional.h
+++ b/security/selinux/include/conditional.h
@@ -17,6 +17,6 @@ int security_get_bools(int *len, char ***names, int **values);
int security_set_bools(int len, int *values);
-int security_get_bool_value(int bool);
+int security_get_bool_value(int index);
#endif
diff --git a/security/selinux/include/netlabel.h b/security/selinux/include/netlabel.h
index 8c59b8f150e8..75686d53df07 100644
--- a/security/selinux/include/netlabel.h
+++ b/security/selinux/include/netlabel.h
@@ -40,7 +40,8 @@
#ifdef CONFIG_NETLABEL
void selinux_netlbl_cache_invalidate(void);
-void selinux_netlbl_err(struct sk_buff *skb, int error, int gateway);
+void selinux_netlbl_err(struct sk_buff *skb, u16 family, int error,
+ int gateway);
void selinux_netlbl_sk_security_free(struct sk_security_struct *sksec);
void selinux_netlbl_sk_security_reset(struct sk_security_struct *sksec);
@@ -72,6 +73,7 @@ static inline void selinux_netlbl_cache_invalidate(void)
}
static inline void selinux_netlbl_err(struct sk_buff *skb,
+ u16 family,
int error,
int gateway)
{
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index a2ae05414ba1..c21e135460a5 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -38,9 +38,8 @@ struct task_security_struct {
};
enum label_initialized {
- LABEL_MISSING, /* not initialized */
- LABEL_INITIALIZED, /* inizialized */
- LABEL_INVALID /* invalid */
+ LABEL_INVALID, /* invalid or not initialized */
+ LABEL_INITIALIZED /* initialized */
};
struct inode_security_struct {
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 38feb55d531a..308a286c6cbe 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -39,11 +39,7 @@
/* Range of policy versions we understand*/
#define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE
-#ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX
-#define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE
-#else
#define POLICYDB_VERSION_MAX POLICYDB_VERSION_XPERMS_IOCTL
-#endif
/* Mask for just the mount related flags */
#define SE_MNTMASK 0x0f
diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c
index 1f989a539fd4..aaba6677ee2e 100644
--- a/security/selinux/netlabel.c
+++ b/security/selinux/netlabel.c
@@ -54,6 +54,7 @@
*
*/
static int selinux_netlbl_sidlookup_cached(struct sk_buff *skb,
+ u16 family,
struct netlbl_lsm_secattr *secattr,
u32 *sid)
{
@@ -63,7 +64,7 @@ static int selinux_netlbl_sidlookup_cached(struct sk_buff *skb,
if (rc == 0 &&
(secattr->flags & NETLBL_SECATTR_CACHEABLE) &&
(secattr->flags & NETLBL_SECATTR_CACHE))
- netlbl_cache_add(skb, secattr);
+ netlbl_cache_add(skb, family, secattr);
return rc;
}
@@ -151,9 +152,9 @@ void selinux_netlbl_cache_invalidate(void)
* present on the packet, NetLabel is smart enough to only act when it should.
*
*/
-void selinux_netlbl_err(struct sk_buff *skb, int error, int gateway)
+void selinux_netlbl_err(struct sk_buff *skb, u16 family, int error, int gateway)
{
- netlbl_skbuff_err(skb, error, gateway);
+ netlbl_skbuff_err(skb, family, error, gateway);
}
/**
@@ -214,7 +215,8 @@ int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
netlbl_secattr_init(&secattr);
rc = netlbl_skbuff_getattr(skb, family, &secattr);
if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)
- rc = selinux_netlbl_sidlookup_cached(skb, &secattr, sid);
+ rc = selinux_netlbl_sidlookup_cached(skb, family,
+ &secattr, sid);
else
*sid = SECSID_NULL;
*type = secattr.type;
@@ -284,7 +286,7 @@ int selinux_netlbl_inet_conn_request(struct request_sock *req, u16 family)
int rc;
struct netlbl_lsm_secattr secattr;
- if (family != PF_INET)
+ if (family != PF_INET && family != PF_INET6)
return 0;
netlbl_secattr_init(&secattr);
@@ -333,7 +335,7 @@ int selinux_netlbl_socket_post_create(struct sock *sk, u16 family)
struct sk_security_struct *sksec = sk->sk_security;
struct netlbl_lsm_secattr *secattr;
- if (family != PF_INET)
+ if (family != PF_INET && family != PF_INET6)
return 0;
secattr = selinux_netlbl_sock_genattr(sk);
@@ -382,7 +384,8 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
netlbl_secattr_init(&secattr);
rc = netlbl_skbuff_getattr(skb, family, &secattr);
if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)
- rc = selinux_netlbl_sidlookup_cached(skb, &secattr, &nlbl_sid);
+ rc = selinux_netlbl_sidlookup_cached(skb, family,
+ &secattr, &nlbl_sid);
else
nlbl_sid = SECINITSID_UNLABELED;
netlbl_secattr_destroy(&secattr);
@@ -405,11 +408,26 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
return 0;
if (nlbl_sid != SECINITSID_UNLABELED)
- netlbl_skbuff_err(skb, rc, 0);
+ netlbl_skbuff_err(skb, family, rc, 0);
return rc;
}
/**
+ * selinux_netlbl_option - Is this a NetLabel option
+ * @level: the socket level or protocol
+ * @optname: the socket option name
+ *
+ * Description:
+ * Returns true if @level and @optname refer to a NetLabel option.
+ * Helper for selinux_netlbl_socket_setsockopt().
+ */
+static inline int selinux_netlbl_option(int level, int optname)
+{
+ return (level == IPPROTO_IP && optname == IP_OPTIONS) ||
+ (level == IPPROTO_IPV6 && optname == IPV6_HOPOPTS);
+}
+
+/**
* selinux_netlbl_socket_setsockopt - Do not allow users to remove a NetLabel
* @sock: the socket
* @level: the socket level or protocol
@@ -431,7 +449,7 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock,
struct sk_security_struct *sksec = sk->sk_security;
struct netlbl_lsm_secattr secattr;
- if (level == IPPROTO_IP && optname == IP_OPTIONS &&
+ if (selinux_netlbl_option(level, optname) &&
(sksec->nlbl_state == NLBL_LABELED ||
sksec->nlbl_state == NLBL_CONNLABELED)) {
netlbl_secattr_init(&secattr);
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index 1b1fd27de632..0765c5b053b5 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -1347,7 +1347,7 @@ static ssize_t sel_write_avc_cache_threshold(struct file *file,
{
char *page;
ssize_t ret;
- int new_value;
+ unsigned int new_value;
ret = task_has_security(current, SECURITY__SETSECPARAM);
if (ret)
diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c
index 456e1a9bcfde..34afeadd9e73 100644
--- a/security/selinux/ss/conditional.c
+++ b/security/selinux/ss/conditional.c
@@ -242,6 +242,8 @@ int cond_read_bool(struct policydb *p, struct hashtab *h, void *fp)
goto err;
len = le32_to_cpu(buf[2]);
+ if (((len == 0) || (len == (u32)-1)))
+ goto err;
rc = -ENOMEM;
key = kmalloc(len + 1, GFP_KERNEL);
diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c
index 57644b1dc42e..7d10e5d418bb 100644
--- a/security/selinux/ss/ebitmap.c
+++ b/security/selinux/ss/ebitmap.c
@@ -165,7 +165,7 @@ int ebitmap_netlbl_import(struct ebitmap *ebmap,
e_iter = kzalloc(sizeof(*e_iter), GFP_ATOMIC);
if (e_iter == NULL)
goto netlbl_import_failure;
- e_iter->startbit = offset & ~(EBITMAP_SIZE - 1);
+ e_iter->startbit = offset - (offset % EBITMAP_SIZE);
if (e_prev == NULL)
ebmap->node = e_iter;
else
@@ -374,6 +374,9 @@ int ebitmap_read(struct ebitmap *e, void *fp)
goto ok;
}
+ if (e->highbit && !count)
+ goto bad;
+
for (i = 0; i < count; i++) {
rc = next_entry(&startbit, fp, sizeof(u32));
if (rc < 0) {
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 992a31530825..ace683838d80 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -541,21 +541,21 @@ static int policydb_index(struct policydb *p)
rc = -ENOMEM;
p->class_val_to_struct =
- kmalloc(p->p_classes.nprim * sizeof(*(p->class_val_to_struct)),
+ kzalloc(p->p_classes.nprim * sizeof(*(p->class_val_to_struct)),
GFP_KERNEL);
if (!p->class_val_to_struct)
goto out;
rc = -ENOMEM;
p->role_val_to_struct =
- kmalloc(p->p_roles.nprim * sizeof(*(p->role_val_to_struct)),
+ kzalloc(p->p_roles.nprim * sizeof(*(p->role_val_to_struct)),
GFP_KERNEL);
if (!p->role_val_to_struct)
goto out;
rc = -ENOMEM;
p->user_val_to_struct =
- kmalloc(p->p_users.nprim * sizeof(*(p->user_val_to_struct)),
+ kzalloc(p->p_users.nprim * sizeof(*(p->user_val_to_struct)),
GFP_KERNEL);
if (!p->user_val_to_struct)
goto out;
@@ -964,7 +964,7 @@ int policydb_context_isvalid(struct policydb *p, struct context *c)
* Role must be authorized for the type.
*/
role = p->role_val_to_struct[c->role - 1];
- if (!ebitmap_get_bit(&role->types, c->type - 1))
+ if (!role || !ebitmap_get_bit(&role->types, c->type - 1))
/* role may not be associated with type */
return 0;
@@ -1094,6 +1094,9 @@ static int str_read(char **strp, gfp_t flags, void *fp, u32 len)
int rc;
char *str;
+ if ((len == 0) || (len == (u32)-1))
+ return -EINVAL;
+
str = kmalloc(len + 1, flags);
if (!str)
return -ENOMEM;
@@ -2414,6 +2417,7 @@ int policydb_read(struct policydb *p, void *fp)
} else
tr->tclass = p->process_class;
+ rc = -EINVAL;
if (!policydb_role_isvalid(p, tr->role) ||
!policydb_type_isvalid(p, tr->type) ||
!policydb_class_isvalid(p, tr->tclass) ||
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index ebda97333f1b..082b20c78363 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -543,7 +543,7 @@ static void type_attribute_bounds_av(struct context *scontext,
struct av_decision *avd)
{
struct context lo_scontext;
- struct context lo_tcontext;
+ struct context lo_tcontext, *tcontextp = tcontext;
struct av_decision lo_avd;
struct type_datum *source;
struct type_datum *target;
@@ -553,67 +553,41 @@ static void type_attribute_bounds_av(struct context *scontext,
scontext->type - 1);
BUG_ON(!source);
+ if (!source->bounds)
+ return;
+
target = flex_array_get_ptr(policydb.type_val_to_struct_array,
tcontext->type - 1);
BUG_ON(!target);
- if (source->bounds) {
- memset(&lo_avd, 0, sizeof(lo_avd));
-
- memcpy(&lo_scontext, scontext, sizeof(lo_scontext));
- lo_scontext.type = source->bounds;
+ memset(&lo_avd, 0, sizeof(lo_avd));
- context_struct_compute_av(&lo_scontext,
- tcontext,
- tclass,
- &lo_avd,
- NULL);
- if ((lo_avd.allowed & avd->allowed) == avd->allowed)
- return; /* no masked permission */
- masked = ~lo_avd.allowed & avd->allowed;
- }
+ memcpy(&lo_scontext, scontext, sizeof(lo_scontext));
+ lo_scontext.type = source->bounds;
if (target->bounds) {
- memset(&lo_avd, 0, sizeof(lo_avd));
-
memcpy(&lo_tcontext, tcontext, sizeof(lo_tcontext));
lo_tcontext.type = target->bounds;
-
- context_struct_compute_av(scontext,
- &lo_tcontext,
- tclass,
- &lo_avd,
- NULL);
- if ((lo_avd.allowed & avd->allowed) == avd->allowed)
- return; /* no masked permission */
- masked = ~lo_avd.allowed & avd->allowed;
+ tcontextp = &lo_tcontext;
}
- if (source->bounds && target->bounds) {
- memset(&lo_avd, 0, sizeof(lo_avd));
- /*
- * lo_scontext and lo_tcontext are already
- * set up.
- */
+ context_struct_compute_av(&lo_scontext,
+ tcontextp,
+ tclass,
+ &lo_avd,
+ NULL);
- context_struct_compute_av(&lo_scontext,
- &lo_tcontext,
- tclass,
- &lo_avd,
- NULL);
- if ((lo_avd.allowed & avd->allowed) == avd->allowed)
- return; /* no masked permission */
- masked = ~lo_avd.allowed & avd->allowed;
- }
+ masked = ~lo_avd.allowed & avd->allowed;
- if (masked) {
- /* mask violated permissions */
- avd->allowed &= ~masked;
+ if (likely(!masked))
+ return; /* no masked permission */
- /* audit masked permissions */
- security_dump_masked_av(scontext, tcontext,
- tclass, masked, "bounds");
- }
+ /* mask violated permissions */
+ avd->allowed &= ~masked;
+
+ /* audit masked permissions */
+ security_dump_masked_av(scontext, tcontext,
+ tclass, masked, "bounds");
}
/*
@@ -2696,7 +2670,7 @@ out:
return rc;
}
-int security_get_bool_value(int bool)
+int security_get_bool_value(int index)
{
int rc;
int len;
@@ -2705,10 +2679,10 @@ int security_get_bool_value(int bool)
rc = -EFAULT;
len = policydb.p_bools.nprim;
- if (bool >= len)
+ if (index >= len)
goto out;
- rc = policydb.bool_val_to_struct[bool]->state;
+ rc = policydb.bool_val_to_struct[index]->state;
out:
read_unlock(&policy_rwlock);
return rc;