summaryrefslogtreecommitdiff
path: root/security/selinux
diff options
context:
space:
mode:
Diffstat (limited to 'security/selinux')
-rw-r--r--security/selinux/hooks.c266
-rw-r--r--security/selinux/ibpkey.c13
-rw-r--r--security/selinux/include/classmap.h2
-rw-r--r--security/selinux/include/netnode.h2
-rw-r--r--security/selinux/include/objsec.h16
-rw-r--r--security/selinux/include/policycap.h1
-rw-r--r--security/selinux/include/policycap_names.h1
-rw-r--r--security/selinux/include/security.h2
-rw-r--r--security/selinux/netif.c6
-rw-r--r--security/selinux/netnode.c15
-rw-r--r--security/selinux/netport.c14
-rw-r--r--security/selinux/nlmsgtab.c1
-rw-r--r--security/selinux/selinuxfs.c4
-rw-r--r--security/selinux/ss/services.c22
14 files changed, 235 insertions, 130 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index e7a7dcab81db..595ceb314aeb 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -65,7 +65,6 @@
#include <net/netlink.h>
#include <linux/tcp.h>
#include <linux/udp.h>
-#include <linux/dccp.h>
#include <linux/sctp.h>
#include <net/sctp/structs.h>
#include <linux/quota.h>
@@ -213,8 +212,10 @@ static void cred_init_security(void)
{
struct task_security_struct *tsec;
+ /* NOTE: the lsm framework zeros out the buffer on allocation */
+
tsec = selinux_cred(unrcu_pointer(current->real_cred));
- tsec->osid = tsec->sid = SECINITSID_KERNEL;
+ tsec->osid = tsec->sid = tsec->avdcache.sid = SECINITSID_KERNEL;
}
/*
@@ -278,27 +279,21 @@ static int __inode_security_revalidate(struct inode *inode,
struct dentry *dentry,
bool may_sleep)
{
- struct inode_security_struct *isec = selinux_inode(inode);
+ if (!selinux_initialized())
+ return 0;
- might_sleep_if(may_sleep);
+ if (may_sleep)
+ might_sleep();
+ else
+ return -ECHILD;
/*
- * The check of isec->initialized below is racy but
- * inode_doinit_with_dentry() will recheck with
- * isec->lock held.
+ * Check to ensure that an inode's SELinux state is valid and try
+ * reloading the inode security label if necessary. This will fail if
+ * @dentry is NULL and no dentry for this inode can be found; in that
+ * case, continue using the old label.
*/
- if (selinux_initialized() &&
- data_race(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, dentry);
- }
+ inode_doinit_with_dentry(inode, dentry);
return 0;
}
@@ -307,41 +302,53 @@ static struct inode_security_struct *inode_security_novalidate(struct inode *ino
return selinux_inode(inode);
}
-static struct inode_security_struct *inode_security_rcu(struct inode *inode, bool rcu)
+static inline struct inode_security_struct *inode_security_rcu(struct inode *inode,
+ bool rcu)
{
- int error;
+ int rc;
+ struct inode_security_struct *isec = selinux_inode(inode);
- error = __inode_security_revalidate(inode, NULL, !rcu);
- if (error)
- return ERR_PTR(error);
- return selinux_inode(inode);
+ /* check below is racy, but revalidate will recheck with lock held */
+ if (data_race(likely(isec->initialized == LABEL_INITIALIZED)))
+ return isec;
+ rc = __inode_security_revalidate(inode, NULL, !rcu);
+ if (rc)
+ return ERR_PTR(rc);
+ return isec;
}
/*
* Get the security label of an inode.
*/
-static struct inode_security_struct *inode_security(struct inode *inode)
+static inline struct inode_security_struct *inode_security(struct inode *inode)
{
+ struct inode_security_struct *isec = selinux_inode(inode);
+
+ /* check below is racy, but revalidate will recheck with lock held */
+ if (data_race(likely(isec->initialized == LABEL_INITIALIZED)))
+ return isec;
__inode_security_revalidate(inode, NULL, true);
- return selinux_inode(inode);
+ return isec;
}
-static struct inode_security_struct *backing_inode_security_novalidate(struct dentry *dentry)
+static inline struct inode_security_struct *backing_inode_security_novalidate(struct dentry *dentry)
{
- struct inode *inode = d_backing_inode(dentry);
-
- return selinux_inode(inode);
+ return selinux_inode(d_backing_inode(dentry));
}
/*
* Get the security label of a dentry's backing inode.
*/
-static struct inode_security_struct *backing_inode_security(struct dentry *dentry)
+static inline struct inode_security_struct *backing_inode_security(struct dentry *dentry)
{
struct inode *inode = d_backing_inode(dentry);
+ struct inode_security_struct *isec = selinux_inode(inode);
+ /* check below is racy, but revalidate will recheck with lock held */
+ if (data_race(likely(isec->initialized == LABEL_INITIALIZED)))
+ return isec;
__inode_security_revalidate(inode, dentry, true);
- return selinux_inode(inode);
+ return isec;
}
static void inode_free_security(struct inode *inode)
@@ -1191,8 +1198,6 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc
return SECCLASS_ICMP_SOCKET;
else
return SECCLASS_RAWIP_SOCKET;
- case SOCK_DCCP:
- return SECCLASS_DCCP_SOCKET;
default:
return SECCLASS_RAWIP_SOCKET;
}
@@ -1683,12 +1688,15 @@ static inline int dentry_has_perm(const struct cred *cred,
struct dentry *dentry,
u32 av)
{
- struct inode *inode = d_backing_inode(dentry);
struct common_audit_data ad;
+ struct inode *inode = d_backing_inode(dentry);
+ struct inode_security_struct *isec = selinux_inode(inode);
ad.type = LSM_AUDIT_DATA_DENTRY;
ad.u.dentry = dentry;
- __inode_security_revalidate(inode, dentry, true);
+ /* check below is racy, but revalidate will recheck with lock held */
+ if (data_race(unlikely(isec->initialized != LABEL_INITIALIZED)))
+ __inode_security_revalidate(inode, dentry, true);
return inode_has_perm(cred, inode, av, &ad);
}
@@ -1699,12 +1707,15 @@ static inline int path_has_perm(const struct cred *cred,
const struct path *path,
u32 av)
{
- struct inode *inode = d_backing_inode(path->dentry);
struct common_audit_data ad;
+ struct inode *inode = d_backing_inode(path->dentry);
+ struct inode_security_struct *isec = selinux_inode(inode);
ad.type = LSM_AUDIT_DATA_PATH;
ad.u.path = *path;
- __inode_security_revalidate(inode, path->dentry, true);
+ /* check below is racy, but revalidate will recheck with lock held */
+ if (data_race(unlikely(isec->initialized != LABEL_INITIALIZED)))
+ __inode_security_revalidate(inode, path->dentry, true);
return inode_has_perm(cred, inode, av, &ad);
}
@@ -3088,44 +3099,147 @@ static noinline int audit_inode_permission(struct inode *inode,
audited, denied, result, &ad);
}
-static int selinux_inode_permission(struct inode *inode, int mask)
+/**
+ * task_avdcache_reset - Reset the task's AVD cache
+ * @tsec: the task's security state
+ *
+ * Clear the task's AVD cache in @tsec and reset it to the current policy's
+ * and task's info.
+ */
+static inline void task_avdcache_reset(struct task_security_struct *tsec)
+{
+ memset(&tsec->avdcache.dir, 0, sizeof(tsec->avdcache.dir));
+ tsec->avdcache.sid = tsec->sid;
+ tsec->avdcache.seqno = avc_policy_seqno();
+ tsec->avdcache.dir_spot = TSEC_AVDC_DIR_SIZE - 1;
+}
+
+/**
+ * task_avdcache_search - Search the task's AVD cache
+ * @tsec: the task's security state
+ * @isec: the inode to search for in the cache
+ * @avdc: matching avd cache entry returned to the caller
+ *
+ * Search @tsec for a AVD cache entry that matches @isec and return it to the
+ * caller via @avdc. Returns 0 if a match is found, negative values otherwise.
+ */
+static inline int task_avdcache_search(struct task_security_struct *tsec,
+ struct inode_security_struct *isec,
+ struct avdc_entry **avdc)
{
+ int orig, iter;
+
+ /* focused on path walk optimization, only cache directories */
+ if (isec->sclass != SECCLASS_DIR)
+ return -ENOENT;
+
+ if (unlikely(tsec->sid != tsec->avdcache.sid ||
+ tsec->avdcache.seqno != avc_policy_seqno())) {
+ task_avdcache_reset(tsec);
+ return -ENOENT;
+ }
+
+ orig = iter = tsec->avdcache.dir_spot;
+ do {
+ if (tsec->avdcache.dir[iter].isid == isec->sid) {
+ /* cache hit */
+ tsec->avdcache.dir_spot = iter;
+ *avdc = &tsec->avdcache.dir[iter];
+ return 0;
+ }
+ iter = (iter - 1) & (TSEC_AVDC_DIR_SIZE - 1);
+ } while (iter != orig);
+
+ return -ENOENT;
+}
+
+/**
+ * task_avdcache_update - Update the task's AVD cache
+ * @tsec: the task's security state
+ * @isec: the inode associated with the cache entry
+ * @avd: the AVD to cache
+ * @audited: the permission audit bitmask to cache
+ *
+ * Update the AVD cache in @tsec with the @avdc and @audited info associated
+ * with @isec.
+ */
+static inline void task_avdcache_update(struct task_security_struct *tsec,
+ struct inode_security_struct *isec,
+ struct av_decision *avd,
+ u32 audited)
+{
+ int spot;
+
+ /* focused on path walk optimization, only cache directories */
+ if (isec->sclass != SECCLASS_DIR)
+ return;
+
+ /* update cache */
+ spot = (tsec->avdcache.dir_spot + 1) & (TSEC_AVDC_DIR_SIZE - 1);
+ tsec->avdcache.dir_spot = spot;
+ tsec->avdcache.dir[spot].isid = isec->sid;
+ tsec->avdcache.dir[spot].audited = audited;
+ tsec->avdcache.dir[spot].allowed = avd->allowed;
+ tsec->avdcache.dir[spot].permissive = avd->flags & AVD_FLAGS_PERMISSIVE;
+}
+
+/**
+ * selinux_inode_permission - Check if the current task can access an inode
+ * @inode: the inode that is being accessed
+ * @requested: the accesses being requested
+ *
+ * Check if the current task is allowed to access @inode according to
+ * @requested. Returns 0 if allowed, negative values otherwise.
+ */
+static int selinux_inode_permission(struct inode *inode, int requested)
+{
+ int mask;
u32 perms;
- bool from_access;
- bool no_block = mask & MAY_NOT_BLOCK;
+ struct task_security_struct *tsec;
struct inode_security_struct *isec;
- u32 sid = current_sid();
- struct av_decision avd;
+ struct avdc_entry *avdc;
int rc, rc2;
u32 audited, denied;
- from_access = mask & MAY_ACCESS;
- mask &= (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND);
+ mask = requested & (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND);
/* No permission to check. Existence test. */
if (!mask)
return 0;
- if (unlikely(IS_PRIVATE(inode)))
- return 0;
-
- perms = file_mask_to_av(inode->i_mode, mask);
-
- isec = inode_security_rcu(inode, no_block);
+ isec = inode_security_rcu(inode, requested & MAY_NOT_BLOCK);
if (IS_ERR(isec))
return PTR_ERR(isec);
+ tsec = selinux_cred(current_cred());
+ perms = file_mask_to_av(inode->i_mode, mask);
+
+ rc = task_avdcache_search(tsec, isec, &avdc);
+ if (likely(!rc)) {
+ /* Cache hit. */
+ audited = perms & avdc->audited;
+ denied = perms & ~avdc->allowed;
+ if (unlikely(denied && enforcing_enabled() &&
+ !avdc->permissive))
+ rc = -EACCES;
+ } else {
+ struct av_decision avd;
+
+ /* Cache miss. */
+ rc = avc_has_perm_noaudit(tsec->sid, isec->sid, isec->sclass,
+ perms, 0, &avd);
+ audited = avc_audit_required(perms, &avd, rc,
+ (requested & MAY_ACCESS) ? FILE__AUDIT_ACCESS : 0,
+ &denied);
+ task_avdcache_update(tsec, isec, &avd, audited);
+ }
- rc = avc_has_perm_noaudit(sid, isec->sid, isec->sclass, perms, 0,
- &avd);
- audited = avc_audit_required(perms, &avd, rc,
- from_access ? FILE__AUDIT_ACCESS : 0,
- &denied);
if (likely(!audited))
return rc;
rc2 = audit_inode_permission(inode, perms, audited, denied, rc);
if (rc2)
return rc2;
+
return rc;
}
@@ -4392,22 +4506,6 @@ static int selinux_parse_skb_ipv4(struct sk_buff *skb,
break;
}
- case IPPROTO_DCCP: {
- struct dccp_hdr _dccph, *dh;
-
- if (ntohs(ih->frag_off) & IP_OFFSET)
- break;
-
- offset += ihlen;
- dh = skb_header_pointer(skb, offset, sizeof(_dccph), &_dccph);
- if (dh == NULL)
- break;
-
- ad->u.net->sport = dh->dccph_sport;
- ad->u.net->dport = dh->dccph_dport;
- break;
- }
-
#if IS_ENABLED(CONFIG_IP_SCTP)
case IPPROTO_SCTP: {
struct sctphdr _sctph, *sh;
@@ -4486,18 +4584,6 @@ static int selinux_parse_skb_ipv6(struct sk_buff *skb,
break;
}
- case IPPROTO_DCCP: {
- struct dccp_hdr _dccph, *dh;
-
- dh = skb_header_pointer(skb, offset, sizeof(_dccph), &_dccph);
- if (dh == NULL)
- break;
-
- ad->u.net->sport = dh->dccph_sport;
- ad->u.net->dport = dh->dccph_dport;
- break;
- }
-
#if IS_ENABLED(CONFIG_IP_SCTP)
case IPPROTO_SCTP: {
struct sctphdr _sctph, *sh;
@@ -4849,10 +4935,6 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
node_perm = UDP_SOCKET__NODE_BIND;
break;
- case SECCLASS_DCCP_SOCKET:
- node_perm = DCCP_SOCKET__NODE_BIND;
- break;
-
case SECCLASS_SCTP_SOCKET:
node_perm = SCTP_SOCKET__NODE_BIND;
break;
@@ -4908,11 +4990,10 @@ static int selinux_socket_connect_helper(struct socket *sock,
return 0;
/*
- * If a TCP, DCCP or SCTP socket, check name_connect permission
+ * If a TCP or SCTP socket, check name_connect permission
* for the port.
*/
if (sksec->sclass == SECCLASS_TCP_SOCKET ||
- sksec->sclass == SECCLASS_DCCP_SOCKET ||
sksec->sclass == SECCLASS_SCTP_SOCKET) {
struct common_audit_data ad;
struct lsm_network_audit net = {0,};
@@ -4957,9 +5038,6 @@ static int selinux_socket_connect_helper(struct socket *sock,
case SECCLASS_TCP_SOCKET:
perm = TCP_SOCKET__NAME_CONNECT;
break;
- case SECCLASS_DCCP_SOCKET:
- perm = DCCP_SOCKET__NAME_CONNECT;
- break;
case SECCLASS_SCTP_SOCKET:
perm = SCTP_SOCKET__NAME_CONNECT;
break;
diff --git a/security/selinux/ibpkey.c b/security/selinux/ibpkey.c
index 48f537b41c58..470481cfe0e8 100644
--- a/security/selinux/ibpkey.c
+++ b/security/selinux/ibpkey.c
@@ -130,7 +130,7 @@ static int sel_ib_pkey_sid_slow(u64 subnet_prefix, u16 pkey_num, u32 *sid)
{
int ret;
struct sel_ib_pkey *pkey;
- struct sel_ib_pkey *new = NULL;
+ struct sel_ib_pkey *new;
unsigned long flags;
spin_lock_irqsave(&sel_ib_pkey_lock, flags);
@@ -146,12 +146,11 @@ static int sel_ib_pkey_sid_slow(u64 subnet_prefix, u16 pkey_num, u32 *sid)
if (ret)
goto out;
- /* If this memory allocation fails still return 0. The SID
- * is valid, it just won't be added to the cache.
- */
- new = kzalloc(sizeof(*new), GFP_ATOMIC);
+ new = kmalloc(sizeof(*new), GFP_ATOMIC);
if (!new) {
- ret = -ENOMEM;
+ /* If this memory allocation fails still return 0. The SID
+ * is valid, it just won't be added to the cache.
+ */
goto out;
}
@@ -184,7 +183,7 @@ int sel_ib_pkey_sid(u64 subnet_prefix, u16 pkey_num, u32 *sid)
rcu_read_lock();
pkey = sel_ib_pkey_find(subnet_prefix, pkey_num);
- if (pkey) {
+ if (likely(pkey)) {
*sid = pkey->psec.sid;
rcu_read_unlock();
return 0;
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index 04a9b480885e..5665aa5e7853 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -127,8 +127,6 @@ const struct security_class_mapping secclass_map[] = {
{ "key",
{ "view", "read", "write", "search", "link", "setattr", "create",
NULL } },
- { "dccp_socket",
- { COMMON_SOCK_PERMS, "node_bind", "name_connect", NULL } },
{ "memprotect", { "mmap_zero", NULL } },
{ "peer", { "recv", NULL } },
{ "capability2", { COMMON_CAP2_PERMS, NULL } },
diff --git a/security/selinux/include/netnode.h b/security/selinux/include/netnode.h
index 9b8b655a8cd3..e4dc904c3585 100644
--- a/security/selinux/include/netnode.h
+++ b/security/selinux/include/netnode.h
@@ -21,6 +21,6 @@
void sel_netnode_flush(void);
-int sel_netnode_sid(void *addr, u16 family, u32 *sid);
+int sel_netnode_sid(const void *addr, u16 family, u32 *sid);
#endif
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index c88cae81ee4c..6ee7dc4dfd6e 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -29,6 +29,13 @@
#include "flask.h"
#include "avc.h"
+struct avdc_entry {
+ u32 isid; /* inode SID */
+ u32 allowed; /* allowed permission bitmask */
+ u32 audited; /* audited permission bitmask */
+ bool permissive; /* AVC permissive flag */
+};
+
struct task_security_struct {
u32 osid; /* SID prior to last execve */
u32 sid; /* current SID */
@@ -36,6 +43,13 @@ struct task_security_struct {
u32 create_sid; /* fscreate SID */
u32 keycreate_sid; /* keycreate SID */
u32 sockcreate_sid; /* fscreate SID */
+#define TSEC_AVDC_DIR_SIZE (1 << 2)
+ struct {
+ u32 sid; /* current SID for cached entries */
+ u32 seqno; /* AVC sequence number */
+ unsigned int dir_spot; /* dir cache index to check first */
+ struct avdc_entry dir[TSEC_AVDC_DIR_SIZE]; /* dir entries */
+ } avdcache;
} __randomize_layout;
enum label_initialized {
@@ -82,7 +96,7 @@ struct ipc_security_struct {
};
struct netif_security_struct {
- struct net *ns; /* network namespace */
+ const struct net *ns; /* network namespace */
int ifindex; /* device index */
u32 sid; /* SID for this interface */
};
diff --git a/security/selinux/include/policycap.h b/security/selinux/include/policycap.h
index bd402d3fd3ae..7405154e6c42 100644
--- a/security/selinux/include/policycap.h
+++ b/security/selinux/include/policycap.h
@@ -16,6 +16,7 @@ enum {
POLICYDB_CAP_USERSPACE_INITIAL_CONTEXT,
POLICYDB_CAP_NETLINK_XPERM,
POLICYDB_CAP_NETIF_WILDCARD,
+ POLICYDB_CAP_GENFS_SECLABEL_WILDCARD,
__POLICYDB_CAP_MAX
};
#define POLICYDB_CAP_MAX (__POLICYDB_CAP_MAX - 1)
diff --git a/security/selinux/include/policycap_names.h b/security/selinux/include/policycap_names.h
index ac1342d6d5bb..d8962fcf2ff9 100644
--- a/security/selinux/include/policycap_names.h
+++ b/security/selinux/include/policycap_names.h
@@ -19,6 +19,7 @@ const char *const selinux_policycap_names[__POLICYDB_CAP_MAX] = {
"userspace_initial_context",
"netlink_xperm",
"netif_wildcard",
+ "genfs_seclabel_wildcard",
};
/* clang-format on */
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index e7827ed7be5f..278c144c22d6 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -309,7 +309,7 @@ int security_ib_endport_sid(const char *dev_name, u8 port_num, u32 *out_sid);
int security_netif_sid(const char *name, u32 *if_sid);
-int security_node_sid(u16 domain, void *addr, u32 addrlen, u32 *out_sid);
+int security_node_sid(u16 domain, const void *addr, u32 addrlen, u32 *out_sid);
int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
u16 tclass);
diff --git a/security/selinux/netif.c b/security/selinux/netif.c
index 43a0d3594b72..78afbecdbe57 100644
--- a/security/selinux/netif.c
+++ b/security/selinux/netif.c
@@ -156,7 +156,11 @@ static int sel_netif_sid_slow(struct net *ns, int ifindex, u32 *sid)
ret = security_netif_sid(dev->name, sid);
if (ret != 0)
goto out;
- new = kzalloc(sizeof(*new), GFP_ATOMIC);
+
+ /* If this memory allocation fails still return 0. The SID
+ * is valid, it just won't be added to the cache.
+ */
+ new = kmalloc(sizeof(*new), GFP_ATOMIC);
if (new) {
new->nsec.ns = ns;
new->nsec.ifindex = ifindex;
diff --git a/security/selinux/netnode.c b/security/selinux/netnode.c
index 5c8c77e50aad..5d0ed08d46e5 100644
--- a/security/selinux/netnode.c
+++ b/security/selinux/netnode.c
@@ -187,7 +187,7 @@ static void sel_netnode_insert(struct sel_netnode *node)
* failure.
*
*/
-static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid)
+static int sel_netnode_sid_slow(const void *addr, u16 family, u32 *sid)
{
int ret;
struct sel_netnode *node;
@@ -201,19 +201,22 @@ static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid)
return 0;
}
- new = kzalloc(sizeof(*new), GFP_ATOMIC);
+ /* If this memory allocation fails still return 0. The SID
+ * is valid, it just won't be added to the cache.
+ */
+ new = kmalloc(sizeof(*new), GFP_ATOMIC);
switch (family) {
case PF_INET:
ret = security_node_sid(PF_INET,
addr, sizeof(struct in_addr), sid);
if (new)
- new->nsec.addr.ipv4 = *(__be32 *)addr;
+ new->nsec.addr.ipv4 = *(const __be32 *)addr;
break;
case PF_INET6:
ret = security_node_sid(PF_INET6,
addr, sizeof(struct in6_addr), sid);
if (new)
- new->nsec.addr.ipv6 = *(struct in6_addr *)addr;
+ new->nsec.addr.ipv6 = *(const struct in6_addr *)addr;
break;
default:
BUG();
@@ -247,13 +250,13 @@ static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid)
* on failure.
*
*/
-int sel_netnode_sid(void *addr, u16 family, u32 *sid)
+int sel_netnode_sid(const void *addr, u16 family, u32 *sid)
{
struct sel_netnode *node;
rcu_read_lock();
node = sel_netnode_find(addr, family);
- if (node != NULL) {
+ if (likely(node != NULL)) {
*sid = node->nsec.sid;
rcu_read_unlock();
return 0;
diff --git a/security/selinux/netport.c b/security/selinux/netport.c
index 2e22ad9c2bd0..6fd7da4b3576 100644
--- a/security/selinux/netport.c
+++ b/security/selinux/netport.c
@@ -47,12 +47,6 @@ struct sel_netport {
struct rcu_head rcu;
};
-/* NOTE: we are using a combined hash table for both IPv4 and IPv6, the reason
- * for this is that I suspect most users will not make heavy use of both
- * address families at the same time so one table will usually end up wasted,
- * if this becomes a problem we can always add a hash table for each address
- * family later */
-
static DEFINE_SPINLOCK(sel_netport_lock);
static struct sel_netport_bkt sel_netport_hash[SEL_NETPORT_HASH_SIZE];
@@ -151,7 +145,11 @@ static int sel_netport_sid_slow(u8 protocol, u16 pnum, u32 *sid)
ret = security_port_sid(protocol, pnum, sid);
if (ret != 0)
goto out;
- new = kzalloc(sizeof(*new), GFP_ATOMIC);
+
+ /* If this memory allocation fails still return 0. The SID
+ * is valid, it just won't be added to the cache.
+ */
+ new = kmalloc(sizeof(*new), GFP_ATOMIC);
if (new) {
new->psec.port = pnum;
new->psec.protocol = protocol;
@@ -186,7 +184,7 @@ int sel_netport_sid(u8 protocol, u16 pnum, u32 *sid)
rcu_read_lock();
port = sel_netport_find(protocol, pnum);
- if (port != NULL) {
+ if (likely(port != NULL)) {
*sid = port->psec.sid;
rcu_read_unlock();
return 0;
diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c
index 3a95986b134f..2c0b07f9fbbd 100644
--- a/security/selinux/nlmsgtab.c
+++ b/security/selinux/nlmsgtab.c
@@ -98,7 +98,6 @@ static const struct nlmsg_perm nlmsg_route_perms[] = {
static const struct nlmsg_perm nlmsg_tcpdiag_perms[] = {
{ TCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ },
- { DCCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ },
{ SOCK_DIAG_BY_FAMILY, NETLINK_TCPDIAG_SOCKET__NLMSG_READ },
{ SOCK_DESTROY, NETLINK_TCPDIAG_SOCKET__NLMSG_WRITE },
};
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index 47480eb2189b..e67a8ce4b64c 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -2158,8 +2158,8 @@ static int __init init_sel_fs(void)
return err;
}
- selinux_null.dentry = d_hash_and_lookup(selinux_null.mnt->mnt_root,
- &null_name);
+ selinux_null.dentry = try_lookup_noperm(&null_name,
+ selinux_null.mnt->mnt_root);
if (IS_ERR(selinux_null.dentry)) {
pr_err("selinuxfs: could not lookup null!\n");
err = PTR_ERR(selinux_null.dentry);
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index e431772c6168..7becf3808818 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -2643,7 +2643,7 @@ static bool match_ipv6_addrmask(const u32 input[4], const u32 addr[4], const u32
* @out_sid: security identifier
*/
int security_node_sid(u16 domain,
- void *addrp,
+ const void *addrp,
u32 addrlen,
u32 *out_sid)
{
@@ -2672,7 +2672,7 @@ retry:
if (addrlen != sizeof(u32))
goto out;
- addr = *((u32 *)addrp);
+ addr = *((const u32 *)addrp);
c = policydb->ocontexts[OCON_NODE];
while (c) {
@@ -2872,6 +2872,7 @@ static inline int __security_genfs_sid(struct selinux_policy *policy,
struct genfs *genfs;
struct ocontext *c;
int cmp = 0;
+ bool wildcard;
while (path[0] == '/' && path[1] == '/')
path++;
@@ -2888,11 +2889,20 @@ static inline int __security_genfs_sid(struct selinux_policy *policy,
if (!genfs || cmp)
return -ENOENT;
+ wildcard = ebitmap_get_bit(&policy->policydb.policycaps,
+ POLICYDB_CAP_GENFS_SECLABEL_WILDCARD);
for (c = genfs->head; c; c = c->next) {
- size_t len = strlen(c->u.name);
- if ((!c->v.sclass || sclass == c->v.sclass) &&
- (strncmp(c->u.name, path, len) == 0))
- break;
+ if (!c->v.sclass || sclass == c->v.sclass) {
+ if (wildcard) {
+ if (match_wildcard(c->u.name, path))
+ break;
+ } else {
+ size_t len = strlen(c->u.name);
+
+ if ((strncmp(c->u.name, path, len)) == 0)
+ break;
+ }
+ }
}
if (!c)