summaryrefslogtreecommitdiff
path: root/security/selinux
diff options
context:
space:
mode:
Diffstat (limited to 'security/selinux')
-rw-r--r--security/selinux/avc.c15
-rw-r--r--security/selinux/hooks.c496
-rw-r--r--security/selinux/include/classmap.h33
-rw-r--r--security/selinux/include/objsec.h9
-rw-r--r--security/selinux/include/security.h3
-rw-r--r--security/selinux/include/xfrm.h2
-rw-r--r--security/selinux/nlmsgtab.c40
-rw-r--r--security/selinux/selinuxfs.c1
-rw-r--r--security/selinux/ss/policydb.c8
-rw-r--r--security/selinux/ss/policydb.h4
-rw-r--r--security/selinux/ss/services.c10
-rw-r--r--security/selinux/xfrm.c8
12 files changed, 493 insertions, 136 deletions
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index 52f3c550abcc..a16c72c2a967 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -348,26 +348,27 @@ static struct avc_xperms_decision_node
struct avc_xperms_decision_node *xpd_node;
struct extended_perms_decision *xpd;
- xpd_node = kmem_cache_zalloc(avc_xperms_decision_cachep, GFP_NOWAIT);
+ xpd_node = kmem_cache_zalloc(avc_xperms_decision_cachep,
+ GFP_NOWAIT | __GFP_NOWARN);
if (!xpd_node)
return NULL;
xpd = &xpd_node->xpd;
if (which & XPERMS_ALLOWED) {
xpd->allowed = kmem_cache_zalloc(avc_xperms_data_cachep,
- GFP_NOWAIT);
+ GFP_NOWAIT | __GFP_NOWARN);
if (!xpd->allowed)
goto error;
}
if (which & XPERMS_AUDITALLOW) {
xpd->auditallow = kmem_cache_zalloc(avc_xperms_data_cachep,
- GFP_NOWAIT);
+ GFP_NOWAIT | __GFP_NOWARN);
if (!xpd->auditallow)
goto error;
}
if (which & XPERMS_DONTAUDIT) {
xpd->dontaudit = kmem_cache_zalloc(avc_xperms_data_cachep,
- GFP_NOWAIT);
+ GFP_NOWAIT | __GFP_NOWARN);
if (!xpd->dontaudit)
goto error;
}
@@ -395,7 +396,7 @@ static struct avc_xperms_node *avc_xperms_alloc(void)
{
struct avc_xperms_node *xp_node;
- xp_node = kmem_cache_zalloc(avc_xperms_cachep, GFP_NOWAIT);
+ xp_node = kmem_cache_zalloc(avc_xperms_cachep, GFP_NOWAIT | __GFP_NOWARN);
if (!xp_node)
return xp_node;
INIT_LIST_HEAD(&xp_node->xpd_head);
@@ -548,7 +549,7 @@ static struct avc_node *avc_alloc_node(void)
{
struct avc_node *node;
- node = kmem_cache_zalloc(avc_node_cachep, GFP_NOWAIT);
+ node = kmem_cache_zalloc(avc_node_cachep, GFP_NOWAIT | __GFP_NOWARN);
if (!node)
goto out;
@@ -865,7 +866,7 @@ static int avc_update_node(u32 event, u32 perms, u8 driver, u8 xperm, u32 ssid,
if (orig->ae.xp_node) {
rc = avc_xperms_populate(node, orig->ae.xp_node);
if (rc) {
- kmem_cache_free(avc_node_cachep, node);
+ avc_node_kill(node);
goto out_unlock;
}
}
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index f7e2cd8c3e53..3bac79428c9b 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -83,6 +83,7 @@
#include <linux/export.h>
#include <linux/msg.h>
#include <linux/shm.h>
+#include <linux/bpf.h>
#include "avc.h"
#include "objsec.h"
@@ -242,6 +243,79 @@ static int inode_alloc_security(struct inode *inode)
return 0;
}
+static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry);
+
+/*
+ * Try reloading inode security labels that have been marked as invalid. The
+ * @may_sleep parameter indicates when sleeping and thus reloading labels is
+ * allowed; when set to false, returns ERR_PTR(-ECHILD) when the label is
+ * invalid. The @opt_dentry parameter should be set to a dentry of the inode;
+ * when no dentry is available, set it to NULL instead.
+ */
+static int __inode_security_revalidate(struct inode *inode,
+ struct dentry *opt_dentry,
+ bool may_sleep)
+{
+ struct inode_security_struct *isec = inode->i_security;
+
+ might_sleep_if(may_sleep);
+
+ if (ss_initialized && isec->initialized != LABEL_INITIALIZED) {
+ if (!may_sleep)
+ return -ECHILD;
+
+ /*
+ * Try reloading the inode security label. This will fail if
+ * @opt_dentry is NULL and no dentry for this inode can be
+ * found; in that case, continue using the old label.
+ */
+ inode_doinit_with_dentry(inode, opt_dentry);
+ }
+ return 0;
+}
+
+static struct inode_security_struct *inode_security_novalidate(struct inode *inode)
+{
+ return inode->i_security;
+}
+
+static struct inode_security_struct *inode_security_rcu(struct inode *inode, bool rcu)
+{
+ int error;
+
+ error = __inode_security_revalidate(inode, NULL, !rcu);
+ if (error)
+ return ERR_PTR(error);
+ return inode->i_security;
+}
+
+/*
+ * Get the security label of an inode.
+ */
+static struct inode_security_struct *inode_security(struct inode *inode)
+{
+ __inode_security_revalidate(inode, NULL, true);
+ 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.
+ */
+static struct inode_security_struct *backing_inode_security(struct dentry *dentry)
+{
+ struct inode *inode = d_backing_inode(dentry);
+
+ __inode_security_revalidate(inode, dentry, true);
+ return inode->i_security;
+}
+
static void inode_free_rcu(struct rcu_head *head)
{
struct inode_security_struct *isec;
@@ -333,8 +407,6 @@ static void superblock_free_security(struct super_block *sb)
kfree(sbsec);
}
-static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry);
-
static inline int inode_doinit(struct inode *inode)
{
return inode_doinit_with_dentry(inode, NULL);
@@ -572,8 +644,8 @@ static int selinux_get_mnt_opts(const struct super_block *sb,
opts->mnt_opts_flags[i++] = DEFCONTEXT_MNT;
}
if (sbsec->flags & ROOTCONTEXT_MNT) {
- struct inode *root = d_backing_inode(sbsec->sb->s_root);
- struct inode_security_struct *isec = root->i_security;
+ struct dentry *root = sbsec->sb->s_root;
+ struct inode_security_struct *isec = backing_inode_security(root);
rc = security_sid_to_context(isec->sid, &context, &len);
if (rc)
@@ -628,8 +700,8 @@ static int selinux_set_mnt_opts(struct super_block *sb,
int rc = 0, i;
struct superblock_security_struct *sbsec = sb->s_security;
const char *name = sb->s_type->name;
- struct inode *inode = d_backing_inode(sbsec->sb->s_root);
- struct inode_security_struct *root_isec = inode->i_security;
+ struct dentry *root = sbsec->sb->s_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;
@@ -672,6 +744,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
@@ -748,7 +822,8 @@ static int selinux_set_mnt_opts(struct super_block *sb,
if (!strcmp(sb->s_type->name, "debugfs") ||
!strcmp(sb->s_type->name, "tracefs") ||
!strcmp(sb->s_type->name, "sysfs") ||
- !strcmp(sb->s_type->name, "pstore"))
+ !strcmp(sb->s_type->name, "pstore") ||
+ !strcmp(sb->s_type->name, "bpf"))
sbsec->flags |= SE_SBGENFS;
if (!sbsec->behavior) {
@@ -810,7 +885,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
goto out;
root_isec->sid = rootcontext_sid;
- root_isec->initialized = 1;
+ root_isec->initialized = LABEL_INITIALIZED;
}
if (defcontext_sid) {
@@ -860,8 +935,8 @@ static int selinux_cmp_sb_context(const struct super_block *oldsb,
if ((oldflags & DEFCONTEXT_MNT) && old->def_sid != new->def_sid)
goto mismatch;
if (oldflags & ROOTCONTEXT_MNT) {
- struct inode_security_struct *oldroot = d_backing_inode(oldsb->s_root)->i_security;
- struct inode_security_struct *newroot = d_backing_inode(newsb->s_root)->i_security;
+ struct inode_security_struct *oldroot = backing_inode_security(oldsb->s_root);
+ struct inode_security_struct *newroot = backing_inode_security(newsb->s_root);
if (oldroot->sid != newroot->sid)
goto mismatch;
}
@@ -911,17 +986,14 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
if (!set_fscontext)
newsbsec->sid = sid;
if (!set_rootcontext) {
- struct inode *newinode = d_backing_inode(newsb->s_root);
- struct inode_security_struct *newisec = newinode->i_security;
+ struct inode_security_struct *newisec = backing_inode_security(newsb->s_root);
newisec->sid = sid;
}
newsbsec->mntpoint_sid = sid;
}
if (set_rootcontext) {
- const struct inode *oldinode = d_backing_inode(oldsb->s_root);
- const struct inode_security_struct *oldisec = oldinode->i_security;
- struct inode *newinode = d_backing_inode(newsb->s_root);
- struct inode_security_struct *newisec = newinode->i_security;
+ const struct inode_security_struct *oldisec = backing_inode_security(oldsb->s_root);
+ struct inode_security_struct *newisec = backing_inode_security(newsb->s_root);
newisec->sid = oldisec->sid;
}
@@ -1303,11 +1375,11 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
unsigned len = 0;
int rc = 0;
- if (isec->initialized)
+ if (isec->initialized == LABEL_INITIALIZED)
goto out;
mutex_lock(&isec->lock);
- if (isec->initialized)
+ if (isec->initialized == LABEL_INITIALIZED)
goto out_unlock;
sbsec = inode->i_sb->s_security;
@@ -1479,7 +1551,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
break;
}
- isec->initialized = 1;
+ isec->initialized = LABEL_INITIALIZED;
out_unlock:
mutex_unlock(&isec->lock);
@@ -1571,7 +1643,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;
@@ -1585,10 +1657,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
@@ -1650,6 +1722,7 @@ static inline int dentry_has_perm(const struct cred *cred,
ad.type = LSM_AUDIT_DATA_DENTRY;
ad.u.dentry = dentry;
+ __inode_security_revalidate(inode, dentry, true);
return inode_has_perm(cred, inode, av, &ad);
}
@@ -1665,6 +1738,7 @@ static inline int path_has_perm(const struct cred *cred,
ad.type = LSM_AUDIT_DATA_PATH;
ad.u.path = *path;
+ __inode_security_revalidate(inode, path->dentry, true);
return inode_has_perm(cred, inode, av, &ad);
}
@@ -1680,6 +1754,10 @@ static inline int file_path_has_perm(const struct cred *cred,
return inode_has_perm(cred, file_inode(file), av, &ad);
}
+#ifdef CONFIG_BPF_SYSCALL
+static int bpf_fd_pass(struct file *file, u32 sid);
+#endif
+
/* 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
@@ -1710,6 +1788,12 @@ static int file_has_perm(const struct cred *cred,
goto out;
}
+#ifdef CONFIG_BPF_SYSCALL
+ rc = bpf_fd_pass(file, cred_sid(cred));
+ if (rc)
+ return rc;
+#endif
+
/* av is zero if only checking access to the descriptor. */
rc = 0;
if (av)
@@ -1722,13 +1806,12 @@ out:
/*
* Determine the label for an inode that might be unioned.
*/
-static int selinux_determine_inode_label(const struct inode *dir,
+static int selinux_determine_inode_label(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 = dir->i_security;
const struct task_security_struct *tsec = current_security();
if ((sbsec->flags & SE_SBINITIALIZED) &&
@@ -1738,6 +1821,7 @@ static int selinux_determine_inode_label(const 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);
}
@@ -1757,7 +1841,7 @@ static int may_create(struct inode *dir,
struct common_audit_data ad;
int rc;
- dsec = dir->i_security;
+ dsec = inode_security(dir);
sbsec = dir->i_sb->s_security;
sid = tsec->sid;
@@ -1810,8 +1894,8 @@ static int may_link(struct inode *dir,
u32 av;
int rc;
- dsec = dir->i_security;
- isec = d_backing_inode(dentry)->i_security;
+ dsec = inode_security(dir);
+ isec = backing_inode_security(dentry);
ad.type = LSM_AUDIT_DATA_DENTRY;
ad.u.dentry = dentry;
@@ -1853,10 +1937,10 @@ static inline int may_rename(struct inode *old_dir,
int old_is_dir, new_is_dir;
int rc;
- old_dsec = old_dir->i_security;
- old_isec = d_backing_inode(old_dentry)->i_security;
+ old_dsec = inode_security(old_dir);
+ old_isec = backing_inode_security(old_dentry);
old_is_dir = d_is_dir(old_dentry);
- new_dsec = new_dir->i_security;
+ new_dsec = inode_security(new_dir);
ad.type = LSM_AUDIT_DATA_DENTRY;
@@ -1884,7 +1968,7 @@ static inline int may_rename(struct inode *old_dir,
if (rc)
return rc;
if (d_is_positive(new_dentry)) {
- new_isec = d_backing_inode(new_dentry)->i_security;
+ new_isec = backing_inode_security(new_dentry);
new_is_dir = d_is_dir(new_dentry);
rc = avc_has_perm(sid, new_isec->sid,
new_isec->sclass,
@@ -1977,21 +2061,18 @@ static inline u32 open_file_to_av(struct file *file)
/* Hook functions begin here. */
-static int selinux_binder_set_context_mgr(struct task_struct *mgr)
+static int selinux_binder_set_context_mgr(const struct cred *mgr)
{
- u32 mysid = current_sid();
- u32 mgrsid = task_sid(mgr);
-
- return avc_has_perm(mysid, mgrsid, SECCLASS_BINDER,
+ return avc_has_perm(current_sid(), cred_sid(mgr), SECCLASS_BINDER,
BINDER__SET_CONTEXT_MGR, NULL);
}
-static int selinux_binder_transaction(struct task_struct *from,
- struct task_struct *to)
+static int selinux_binder_transaction(const struct cred *from,
+ const struct cred *to)
{
u32 mysid = current_sid();
- u32 fromsid = task_sid(from);
- u32 tosid = task_sid(to);
+ u32 fromsid = cred_sid(from);
+ u32 tosid = cred_sid(to);
int rc;
if (mysid != fromsid) {
@@ -2005,24 +2086,22 @@ static int selinux_binder_transaction(struct task_struct *from,
NULL);
}
-static int selinux_binder_transfer_binder(struct task_struct *from,
- struct task_struct *to)
+static int selinux_binder_transfer_binder(const struct cred *from,
+ const struct cred *to)
{
- u32 fromsid = task_sid(from);
- u32 tosid = task_sid(to);
-
- return avc_has_perm(fromsid, tosid, SECCLASS_BINDER, BINDER__TRANSFER,
+ return avc_has_perm(cred_sid(from), cred_sid(to),
+ SECCLASS_BINDER, BINDER__TRANSFER,
NULL);
}
-static int selinux_binder_transfer_file(struct task_struct *from,
- struct task_struct *to,
+static int selinux_binder_transfer_file(const struct cred *from,
+ const struct cred *to,
struct file *file)
{
- u32 sid = task_sid(to);
+ u32 sid = cred_sid(to);
struct file_security_struct *fsec = file->f_security;
- struct inode *inode = d_backing_inode(file->f_path.dentry);
- struct inode_security_struct *isec = inode->i_security;
+ struct dentry *dentry = file->f_path.dentry;
+ struct inode_security_struct *isec;
struct common_audit_data ad;
int rc;
@@ -2038,9 +2117,16 @@ static int selinux_binder_transfer_file(struct task_struct *from,
return rc;
}
- if (unlikely(IS_PRIVATE(inode)))
+#ifdef CONFIG_BPF_SYSCALL
+ rc = bpf_fd_pass(file, sid);
+ if (rc)
+ return rc;
+#endif
+
+ 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);
}
@@ -2089,7 +2175,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)
@@ -2167,7 +2253,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;
@@ -2227,7 +2313,7 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
old_tsec = current_security();
new_tsec = bprm->cred->security;
- isec = inode->i_security;
+ isec = inode_security(inode);
/* Default to the current task SID. */
new_tsec->sid = old_tsec->sid;
@@ -2649,7 +2735,7 @@ static int selinux_sb_remount(struct super_block *sb, void *data)
break;
case ROOTCONTEXT_MNT: {
struct inode_security_struct *root_isec;
- root_isec = d_backing_inode(sb->s_root)->i_security;
+ root_isec = backing_inode_security(sb->s_root);
if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid, sid))
goto out_bad_option;
@@ -2688,7 +2774,7 @@ static int selinux_sb_kern_mount(struct super_block *sb, int flags, void *data)
return rc;
/* Allow all mounts performed by the kernel */
- if (flags & MS_KERNMOUNT)
+ if (flags & (MS_KERNMOUNT | MS_SUBMOUNT))
return 0;
ad.type = LSM_AUDIT_DATA_DENTRY;
@@ -2763,13 +2849,11 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
void **value, size_t *len)
{
const struct task_security_struct *tsec = current_security();
- struct inode_security_struct *dsec;
struct superblock_security_struct *sbsec;
u32 sid, newsid, clen;
int rc;
char *context;
- dsec = dir->i_security;
sbsec = dir->i_sb->s_security;
sid = tsec->sid;
@@ -2787,7 +2871,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
struct inode_security_struct *isec = inode->i_security;
isec->sclass = inode_mode_to_security_class(inode->i_mode);
isec->sid = newsid;
- isec->initialized = 1;
+ isec->initialized = LABEL_INITIALIZED;
}
if (!ss_initialized || !(sbsec->flags & SBLABEL_MNT))
@@ -2868,7 +2952,9 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct inode *inode,
ad.type = LSM_AUDIT_DATA_DENTRY;
ad.u.dentry = dentry;
sid = cred_sid(cred);
- isec = inode->i_security;
+ isec = inode_security_rcu(inode, rcu);
+ if (IS_ERR(isec))
+ return PTR_ERR(isec);
return avc_has_perm_flags(sid, isec->sid, isec->sclass, FILE__READ, &ad,
rcu ? MAY_NOT_BLOCK : 0);
@@ -2920,7 +3006,9 @@ static int selinux_inode_permission(struct inode *inode, int mask)
perms = file_mask_to_av(inode->i_mode, mask);
sid = cred_sid(cred);
- isec = inode->i_security;
+ isec = inode_security_rcu(inode, flags & MAY_NOT_BLOCK);
+ if (IS_ERR(isec))
+ return PTR_ERR(isec);
rc = avc_has_perm_noaudit(sid, isec->sid, isec->sclass, perms, 0, &avd);
audited = avc_audit_required(perms, &avd, rc,
@@ -2993,7 +3081,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 = inode->i_security;
+ struct inode_security_struct *isec;
struct superblock_security_struct *sbsec;
struct common_audit_data ad;
u32 newsid, sid = current_sid();
@@ -3012,6 +3100,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)
@@ -3070,7 +3159,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 = inode->i_security;
+ struct inode_security_struct *isec;
u32 newsid;
int rc;
@@ -3087,9 +3176,10 @@ 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 = 1;
+ isec->initialized = LABEL_INITIALIZED;
return;
}
@@ -3123,12 +3213,12 @@ static int selinux_inode_removexattr(struct dentry *dentry, const char *name)
*
* Permission check is handled by selinux_inode_getxattr hook.
*/
-static int selinux_inode_getsecurity(const struct inode *inode, const char *name, void **buffer, bool alloc)
+static int selinux_inode_getsecurity(struct inode *inode, const char *name, void **buffer, bool alloc)
{
u32 size;
int error;
char *context = NULL;
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec;
if (strcmp(name, XATTR_SELINUX_SUFFIX))
return -EOPNOTSUPP;
@@ -3146,7 +3236,8 @@ static int selinux_inode_getsecurity(const struct inode *inode, const char *name
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);
@@ -3167,7 +3258,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->i_security;
+ struct inode_security_struct *isec = inode_security_novalidate(inode);
u32 newsid;
int rc;
@@ -3183,7 +3274,7 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name,
isec->sclass = inode_mode_to_security_class(inode->i_mode);
isec->sid = newsid;
- isec->initialized = 1;
+ isec->initialized = LABEL_INITIALIZED;
return 0;
}
@@ -3195,9 +3286,9 @@ static int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t
return len;
}
-static void selinux_inode_getsecid(const struct inode *inode, u32 *secid)
+static void selinux_inode_getsecid(struct inode *inode, u32 *secid)
{
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec = inode_security(inode);
*secid = isec->sid;
}
@@ -3220,13 +3311,14 @@ static int selinux_file_permission(struct file *file, int mask)
{
struct inode *inode = file_inode(file);
struct file_security_struct *fsec = file->f_security;
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec;
u32 sid = current_sid();
if (!mask)
/* No permission to check. Existence test. */
return 0;
+ isec = inode_security(inode);
if (sid == fsec->sid && fsec->isid == isec->sid &&
fsec->pseqno == avc_policy_seqno())
/* No change since file_open check. */
@@ -3255,7 +3347,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->i_security;
+ struct inode_security_struct *isec;
struct lsm_ioctlop_audit ioctl;
u32 ssid = cred_sid(cred);
int rc;
@@ -3279,6 +3371,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:
@@ -3320,7 +3413,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
@@ -3519,7 +3612,7 @@ static int selinux_file_open(struct file *file, const struct cred *cred)
struct inode_security_struct *isec;
fsec = file->f_security;
- isec = file_inode(file)->i_security;
+ isec = inode_security(file_inode(file));
/*
* Save inode label and policy sequence number
* at open-time so that selinux_file_permission
@@ -3637,7 +3730,7 @@ static int selinux_kernel_act_as(struct cred *new, u32 secid)
*/
static int selinux_kernel_create_files_as(struct cred *new, struct inode *inode)
{
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec = inode_security(inode);
struct task_security_struct *tsec = new->security;
u32 sid = current_sid();
int ret;
@@ -3681,19 +3774,19 @@ static int selinux_kernel_module_from_file(struct file *file)
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;
}
+ inode = file_inode(file);
+ isec = inode->i_security;
return avc_has_perm(sid, isec->sid, SECCLASS_SYSTEM,
SYSTEM__MODULE_LOAD, &ad);
}
@@ -3793,7 +3886,7 @@ static void selinux_task_to_inode(struct task_struct *p,
u32 sid = task_sid(p);
isec->sid = sid;
- isec->initialized = 1;
+ isec->initialized = LABEL_INITIALIZED;
}
/* Returns error only if unable to parse addresses */
@@ -4112,7 +4205,7 @@ static int selinux_socket_post_create(struct socket *sock, int family,
int type, int protocol, int kern)
{
const struct task_security_struct *tsec = current_security();
- struct inode_security_struct *isec = SOCK_INODE(sock)->i_security;
+ struct inode_security_struct *isec = inode_security_novalidate(SOCK_INODE(sock));
struct sk_security_struct *sksec;
int err = 0;
@@ -4126,7 +4219,7 @@ static int selinux_socket_post_create(struct socket *sock, int family,
return err;
}
- isec->initialized = 1;
+ isec->initialized = LABEL_INITIALIZED;
if (sock->sk) {
sksec = sock->sk->sk_security;
@@ -4320,12 +4413,12 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock)
if (err)
return err;
- newisec = SOCK_INODE(newsock)->i_security;
+ newisec = inode_security_novalidate(SOCK_INODE(newsock));
- isec = SOCK_INODE(sock)->i_security;
+ isec = inode_security_novalidate(SOCK_INODE(sock));
newisec->sclass = isec->sclass;
newisec->sid = isec->sid;
- newisec->initialized = 1;
+ newisec->initialized = LABEL_INITIALIZED;
return 0;
}
@@ -4660,7 +4753,8 @@ static void selinux_sk_getsecid(struct sock *sk, u32 *secid)
static void selinux_sock_graft(struct sock *sk, struct socket *parent)
{
- struct inode_security_struct *isec = SOCK_INODE(parent)->i_security;
+ struct inode_security_struct *isec =
+ inode_security_novalidate(SOCK_INODE(parent));
struct sk_security_struct *sksec = sk->sk_security;
if (sk->sk_family == PF_INET || sk->sk_family == PF_INET6 ||
@@ -4741,9 +4835,9 @@ static void selinux_secmark_refcount_dec(void)
}
static void selinux_req_classify_flow(const struct request_sock *req,
- struct flowi *fl)
+ struct flowi_common *flic)
{
- fl->flowi_secid = req->secid;
+ flic->flowic_secid = req->secid;
}
static int selinux_tun_dev_alloc_security(void **security)
@@ -4826,38 +4920,59 @@ static int selinux_tun_dev_open(void *security)
static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
{
- int err = 0;
- u32 perm;
+ int rc = 0;
+ unsigned int msg_len;
+ unsigned int data_len = skb->len;
+ unsigned char *data = skb->data;
struct nlmsghdr *nlh;
struct sk_security_struct *sksec = sk->sk_security;
+ u16 sclass = sksec->sclass;
+ u32 perm;
- if (skb->len < NLMSG_HDRLEN) {
- err = -EINVAL;
- goto out;
- }
- nlh = nlmsg_hdr(skb);
+ while (data_len >= nlmsg_total_size(0)) {
+ nlh = (struct nlmsghdr *)data;
- err = selinux_nlmsg_lookup(sksec->sclass, nlh->nlmsg_type, &perm);
- if (err) {
- if (err == -EINVAL) {
- printk(KERN_WARNING
- "SELinux: unrecognized netlink message:"
- " protocol=%hu nlmsg_type=%hu sclass=%s\n",
- sk->sk_protocol, nlh->nlmsg_type,
- secclass_map[sksec->sclass - 1].name);
- if (!selinux_enforcing || security_get_allow_unknown())
- err = 0;
+ /* NOTE: the nlmsg_len field isn't reliably set by some netlink
+ * users which means we can't reject skb's with bogus
+ * length fields; our solution is to follow what
+ * netlink_rcv_skb() does and simply skip processing at
+ * messages with length fields that are clearly junk
+ */
+ if (nlh->nlmsg_len < NLMSG_HDRLEN || nlh->nlmsg_len > data_len)
+ return 0;
+
+ rc = selinux_nlmsg_lookup(sclass, nlh->nlmsg_type, &perm);
+ if (rc == 0) {
+ rc = sock_has_perm(current, sk, perm);
+ if (rc)
+ return rc;
+ } else if (rc == -EINVAL) {
+ /* -EINVAL is a missing msg/perm mapping */
+ pr_warn_ratelimited("SELinux: unrecognized netlink"
+ " message: protocol=%hu nlmsg_type=%hu sclass=%s"
+ " pid=%d comm=%s\n",
+ sk->sk_protocol, nlh->nlmsg_type,
+ secclass_map[sclass - 1].name,
+ task_pid_nr(current), current->comm);
+ if (selinux_enforcing && !security_get_allow_unknown())
+ return rc;
+ rc = 0;
+ } else if (rc == -ENOENT) {
+ /* -ENOENT is a missing socket/class mapping, ignore */
+ rc = 0;
+ } else {
+ return rc;
}
- /* Ignore */
- if (err == -ENOENT)
- err = 0;
- goto out;
+ /* move to the next message after applying netlink padding */
+ msg_len = NLMSG_ALIGN(nlh->nlmsg_len);
+ if (msg_len >= data_len)
+ return 0;
+ data_len -= msg_len;
+ data += msg_len;
}
- err = sock_has_perm(current, sk, perm);
-out:
- return err;
+ return rc;
}
#ifdef CONFIG_NETFILTER
@@ -4993,7 +5108,7 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
struct common_audit_data ad;
struct lsm_network_audit net = {0,};
char *addrp;
- u8 proto;
+ u8 proto = 0;
if (sk == NULL)
return NF_ACCEPT;
@@ -5817,6 +5932,15 @@ static void selinux_release_secctx(char *secdata, u32 seclen)
kfree(secdata);
}
+static void selinux_inode_invalidate_secctx(struct inode *inode)
+{
+ struct inode_security_struct *isec = inode->i_security;
+
+ mutex_lock(&isec->lock);
+ isec->initialized = LABEL_INVALID;
+ mutex_unlock(&isec->lock);
+}
+
/*
* called with inode->i_mutex locked
*/
@@ -5911,6 +6035,139 @@ static int selinux_key_getsecurity(struct key *key, char **_buffer)
#endif
+#ifdef CONFIG_BPF_SYSCALL
+static int selinux_bpf(int cmd, union bpf_attr *attr,
+ unsigned int size)
+{
+ u32 sid = current_sid();
+ int ret;
+
+ switch (cmd) {
+ case BPF_MAP_CREATE:
+ ret = avc_has_perm(sid, sid, SECCLASS_BPF, BPF__MAP_CREATE,
+ NULL);
+ break;
+ case BPF_PROG_LOAD:
+ ret = avc_has_perm(sid, sid, SECCLASS_BPF, BPF__PROG_LOAD,
+ NULL);
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+
+ return ret;
+}
+
+static u32 bpf_map_fmode_to_av(fmode_t fmode)
+{
+ u32 av = 0;
+
+ if (fmode & FMODE_READ)
+ av |= BPF__MAP_READ;
+ if (fmode & FMODE_WRITE)
+ av |= BPF__MAP_WRITE;
+ return av;
+}
+
+/* This function will check the file pass through unix socket or binder to see
+ * if it is a bpf related object. And apply correspinding checks on the bpf
+ * object based on the type. The bpf maps and programs, not like other files and
+ * socket, are using a shared anonymous inode inside the kernel as their inode.
+ * So checking that inode cannot identify if the process have privilege to
+ * access the bpf object and that's why we have to add this additional check in
+ * selinux_file_receive and selinux_binder_transfer_files.
+ */
+static int bpf_fd_pass(struct file *file, u32 sid)
+{
+ struct bpf_security_struct *bpfsec;
+ struct bpf_prog *prog;
+ struct bpf_map *map;
+ int ret;
+
+ if (file->f_op == &bpf_map_fops) {
+ map = file->private_data;
+ bpfsec = map->security;
+ ret = avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF,
+ bpf_map_fmode_to_av(file->f_mode), NULL);
+ if (ret)
+ return ret;
+ } else if (file->f_op == &bpf_prog_fops) {
+ prog = file->private_data;
+ bpfsec = prog->aux->security;
+ ret = avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF,
+ BPF__PROG_RUN, NULL);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static int selinux_bpf_map(struct bpf_map *map, fmode_t fmode)
+{
+ u32 sid = current_sid();
+ struct bpf_security_struct *bpfsec;
+
+ bpfsec = map->security;
+ return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF,
+ bpf_map_fmode_to_av(fmode), NULL);
+}
+
+static int selinux_bpf_prog(struct bpf_prog *prog)
+{
+ u32 sid = current_sid();
+ struct bpf_security_struct *bpfsec;
+
+ bpfsec = prog->aux->security;
+ return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF,
+ BPF__PROG_RUN, NULL);
+}
+
+static int selinux_bpf_map_alloc(struct bpf_map *map)
+{
+ struct bpf_security_struct *bpfsec;
+
+ bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
+ if (!bpfsec)
+ return -ENOMEM;
+
+ bpfsec->sid = current_sid();
+ map->security = bpfsec;
+
+ return 0;
+}
+
+static void selinux_bpf_map_free(struct bpf_map *map)
+{
+ struct bpf_security_struct *bpfsec = map->security;
+
+ map->security = NULL;
+ kfree(bpfsec);
+}
+
+static int selinux_bpf_prog_alloc(struct bpf_prog_aux *aux)
+{
+ struct bpf_security_struct *bpfsec;
+
+ bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
+ if (!bpfsec)
+ return -ENOMEM;
+
+ bpfsec->sid = current_sid();
+ aux->security = bpfsec;
+
+ return 0;
+}
+
+static void selinux_bpf_prog_free(struct bpf_prog_aux *aux)
+{
+ struct bpf_security_struct *bpfsec = aux->security;
+
+ aux->security = NULL;
+ kfree(bpfsec);
+}
+#endif
+
static struct security_hook_list selinux_hooks[] = {
LSM_HOOK_INIT(binder_set_context_mgr, selinux_binder_set_context_mgr),
LSM_HOOK_INIT(binder_transaction, selinux_binder_transaction),
@@ -6049,6 +6306,7 @@ static struct security_hook_list selinux_hooks[] = {
LSM_HOOK_INIT(secid_to_secctx, selinux_secid_to_secctx),
LSM_HOOK_INIT(secctx_to_secid, selinux_secctx_to_secid),
LSM_HOOK_INIT(release_secctx, selinux_release_secctx),
+ LSM_HOOK_INIT(inode_invalidate_secctx, selinux_inode_invalidate_secctx),
LSM_HOOK_INIT(inode_notifysecctx, selinux_inode_notifysecctx),
LSM_HOOK_INIT(inode_setsecctx, selinux_inode_setsecctx),
LSM_HOOK_INIT(inode_getsecctx, selinux_inode_getsecctx),
@@ -6121,6 +6379,16 @@ static struct security_hook_list selinux_hooks[] = {
LSM_HOOK_INIT(audit_rule_match, selinux_audit_rule_match),
LSM_HOOK_INIT(audit_rule_free, selinux_audit_rule_free),
#endif
+
+#ifdef CONFIG_BPF_SYSCALL
+ LSM_HOOK_INIT(bpf, selinux_bpf),
+ LSM_HOOK_INIT(bpf_map, selinux_bpf_map),
+ LSM_HOOK_INIT(bpf_prog, selinux_bpf_prog),
+ LSM_HOOK_INIT(bpf_map_alloc_security, selinux_bpf_map_alloc),
+ LSM_HOOK_INIT(bpf_prog_alloc_security, selinux_bpf_prog_alloc),
+ LSM_HOOK_INIT(bpf_map_free_security, selinux_bpf_map_free),
+ LSM_HOOK_INIT(bpf_prog_free_security, selinux_bpf_prog_free),
+#endif
};
static __init int selinux_init(void)
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index d40631150045..d1ee3dfb2555 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".
@@ -34,14 +46,7 @@ struct security_class_mapping secclass_map[] = {
{ "ipc_info", "syslog_read", "syslog_mod",
"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",
@@ -100,7 +105,8 @@ struct security_class_mapping secclass_map[] = {
{ COMMON_IPC_PERMS, NULL } },
{ "netlink_route_socket",
{ COMMON_SOCK_PERMS,
- "nlmsg_read", "nlmsg_write", NULL } },
+ "nlmsg_read", "nlmsg_write", "nlmsg_readpriv", "nlmsg_getneigh",
+ NULL } },
{ "netlink_tcpdiag_socket",
{ COMMON_SOCK_PERMS,
"nlmsg_read", "nlmsg_write", NULL } },
@@ -150,13 +156,18 @@ 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 } },
+ { "bpf",
+ { "map_create", "map_read", "map_write", "prog_load", "prog_run" } },
{ "can_socket",
{ COMMON_SOCK_PERMS, NULL } },
{ NULL }
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index f6027d67a0e6..a18686d4fbf4 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -36,6 +36,11 @@ struct task_security_struct {
u32 sockcreate_sid; /* fscreate SID */
};
+enum label_initialized {
+ LABEL_INVALID, /* invalid or not initialized */
+ LABEL_INITIALIZED /* initialized */
+};
+
struct inode_security_struct {
struct inode *inode; /* back pointer to inode object */
union {
@@ -124,6 +129,10 @@ struct key_security_struct {
u32 sid; /* SID of key */
};
+struct bpf_security_struct {
+ u32 sid; /*SID of bpf obj creater*/
+};
+
extern unsigned int selinux_checkreqprot;
#endif /* _SELINUX_OBJSEC_H_ */
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 0464cbb709cd..efb852e076b6 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -78,6 +78,8 @@ enum {
};
#define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1)
+extern int selinux_android_netlink_route;
+extern int selinux_android_netlink_getneigh;
extern int selinux_policycap_netpeer;
extern int selinux_policycap_openperm;
extern int selinux_policycap_alwaysnetwork;
@@ -263,6 +265,7 @@ extern struct vfsmount *selinuxfs_mount;
extern void selnl_notify_setenforce(int val);
extern void selnl_notify_policyload(u32 seqno);
extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
+extern void selinux_nlmsg_init(void);
#endif /* _SELINUX_SECURITY_H_ */
diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h
index 1450f85b946d..0f929b420059 100644
--- a/security/selinux/include/xfrm.h
+++ b/security/selinux/include/xfrm.h
@@ -25,7 +25,7 @@ int selinux_xfrm_state_delete(struct xfrm_state *x);
int selinux_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir);
int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x,
struct xfrm_policy *xp,
- const struct flowi *fl);
+ const struct flowi_common *flic);
#ifdef CONFIG_SECURITY_NETWORK_XFRM
extern atomic_t selinux_xfrm_refcount;
diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c
index 0714b4c61a8b..c321361af6bf 100644
--- a/security/selinux/nlmsgtab.c
+++ b/security/selinux/nlmsgtab.c
@@ -191,3 +191,43 @@ int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm)
return err;
}
+
+static void nlmsg_set_perm_for_type(u32 perm, u16 type)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(nlmsg_route_perms); i++) {
+ if (nlmsg_route_perms[i].nlmsg_type == type) {
+ nlmsg_route_perms[i].perm = perm;
+ break;
+ }
+ }
+}
+
+/**
+ * Use nlmsg_readpriv as the permission for RTM_GETLINK messages if the
+ * netlink_route_getlink policy capability is set. Otherwise use nlmsg_read.
+ * Similarly, use nlmsg_getneigh for RTM_GETNEIGH and RTM_GETNEIGHTBL if the
+ * netlink_route_getneigh policy capability is set. Otherwise use nlmsg_read.
+ */
+void selinux_nlmsg_init(void)
+{
+ if (selinux_android_netlink_route)
+ nlmsg_set_perm_for_type(NETLINK_ROUTE_SOCKET__NLMSG_READPRIV,
+ RTM_GETLINK);
+ else
+ nlmsg_set_perm_for_type(NETLINK_ROUTE_SOCKET__NLMSG_READ,
+ RTM_GETLINK);
+
+ if (selinux_android_netlink_getneigh) {
+ nlmsg_set_perm_for_type(NETLINK_ROUTE_SOCKET__NLMSG_GETNEIGH,
+ RTM_GETNEIGH);
+ nlmsg_set_perm_for_type(NETLINK_ROUTE_SOCKET__NLMSG_GETNEIGH,
+ RTM_GETNEIGHTBL);
+ } else {
+ nlmsg_set_perm_for_type(NETLINK_ROUTE_SOCKET__NLMSG_READ,
+ RTM_GETNEIGH);
+ nlmsg_set_perm_for_type(NETLINK_ROUTE_SOCKET__NLMSG_READ,
+ RTM_GETNEIGHTBL);
+ }
+}
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index c02da25d7b63..7778e28cce9d 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -1370,6 +1370,7 @@ static struct avc_cache_stats *sel_avc_get_stat_idx(loff_t *idx)
*idx = cpu + 1;
return &per_cpu(avc_cache_stats, cpu);
}
+ (*idx)++;
return NULL;
}
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 01fbbbf89f41..c5b2940f4dab 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -2329,6 +2329,14 @@ int policydb_read(struct policydb *p, void *fp)
p->reject_unknown = !!(le32_to_cpu(buf[1]) & REJECT_UNKNOWN);
p->allow_unknown = !!(le32_to_cpu(buf[1]) & ALLOW_UNKNOWN);
+ if ((le32_to_cpu(buf[1]) & POLICYDB_CONFIG_ANDROID_NETLINK_ROUTE)) {
+ p->android_netlink_route = 1;
+ }
+
+ if ((le32_to_cpu(buf[1]) & POLICYDB_CONFIG_ANDROID_NETLINK_GETNEIGH)) {
+ p->android_netlink_getneigh = 1;
+ }
+
if (p->policyvers >= POLICYDB_VERSION_POLCAP) {
rc = ebitmap_read(&p->policycaps, fp);
if (rc)
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h
index 725d5945a97e..45698f754766 100644
--- a/security/selinux/ss/policydb.h
+++ b/security/selinux/ss/policydb.h
@@ -227,6 +227,8 @@ struct genfs {
/* The policy database */
struct policydb {
int mls_enabled;
+ int android_netlink_route;
+ int android_netlink_getneigh;
/* symbol tables */
struct symtab symtab[SYM_NUM];
@@ -313,6 +315,8 @@ extern int policydb_write(struct policydb *p, void *fp);
#define PERM_SYMTAB_SIZE 32
#define POLICYDB_CONFIG_MLS 1
+#define POLICYDB_CONFIG_ANDROID_NETLINK_ROUTE (1 << 31)
+#define POLICYDB_CONFIG_ANDROID_NETLINK_GETNEIGH (1 << 30)
/* the config flags related to unknown classes/perms are bits 2 and 3 */
#define REJECT_UNKNOWN 0x00000002
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 0a258c0602d1..b615627d1abc 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -70,6 +70,8 @@
#include "ebitmap.h"
#include "audit.h"
+int selinux_android_netlink_route;
+int selinux_android_netlink_getneigh;
int selinux_policycap_netpeer;
int selinux_policycap_openperm;
int selinux_policycap_alwaysnetwork;
@@ -1997,6 +1999,10 @@ static void security_load_policycaps(void)
POLICYDB_CAPABILITY_OPENPERM);
selinux_policycap_alwaysnetwork = ebitmap_get_bit(&policydb.policycaps,
POLICYDB_CAPABILITY_ALWAYSNETWORK);
+
+ selinux_android_netlink_route = policydb.android_netlink_route;
+ selinux_android_netlink_getneigh = policydb.android_netlink_getneigh;
+ selinux_nlmsg_init();
}
static int security_preserve_bools(struct policydb *p);
@@ -2622,8 +2628,12 @@ err:
if (*names) {
for (i = 0; i < *len; i++)
kfree((*names)[i]);
+ kfree(*names);
}
kfree(*values);
+ *len = 0;
+ *names = NULL;
+ *values = NULL;
goto out;
}
diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c
index 56e354fcdfc6..fda680555451 100644
--- a/security/selinux/xfrm.c
+++ b/security/selinux/xfrm.c
@@ -174,9 +174,10 @@ int selinux_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir)
*/
int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x,
struct xfrm_policy *xp,
- const struct flowi *fl)
+ const struct flowi_common *flic)
{
u32 state_sid;
+ u32 flic_sid;
if (!xp->security)
if (x->security)
@@ -195,14 +196,15 @@ int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x,
return 0;
state_sid = x->security->ctx_sid;
+ flic_sid = flic->flowic_secid;
- if (fl->flowi_secid != state_sid)
+ if (flic_sid != state_sid)
return 0;
/* We don't need a separate SA Vs. policy polmatch check since the SA
* is now of the same label as the flow and a flow Vs. policy polmatch
* check had already happened in selinux_xfrm_policy_lookup() above. */
- return (avc_has_perm(fl->flowi_secid, state_sid,
+ return (avc_has_perm(flic_sid, state_sid,
SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO,
NULL) ? 0 : 1);
}