summaryrefslogtreecommitdiff
path: root/security/selinux
diff options
context:
space:
mode:
Diffstat (limited to 'security/selinux')
-rw-r--r--security/selinux/Makefile7
-rw-r--r--security/selinux/avc.c63
-rw-r--r--security/selinux/hooks.c448
-rw-r--r--security/selinux/ibpkey.c13
-rw-r--r--security/selinux/include/avc.h9
-rw-r--r--security/selinux/include/classmap.h12
-rw-r--r--security/selinux/include/conditional.h2
-rw-r--r--security/selinux/include/netnode.h2
-rw-r--r--security/selinux/include/objsec.h24
-rw-r--r--security/selinux/include/policycap.h2
-rw-r--r--security/selinux/include/policycap_names.h2
-rw-r--r--security/selinux/include/security.h22
-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.c11
-rw-r--r--security/selinux/ss/avtab.c19
-rw-r--r--security/selinux/ss/avtab.h13
-rw-r--r--security/selinux/ss/conditional.c24
-rw-r--r--security/selinux/ss/conditional.h6
-rw-r--r--security/selinux/ss/context.c2
-rw-r--r--security/selinux/ss/context.h14
-rw-r--r--security/selinux/ss/ebitmap.c12
-rw-r--r--security/selinux/ss/ebitmap.h7
-rw-r--r--security/selinux/ss/hashtab.c3
-rw-r--r--security/selinux/ss/mls.c6
-rw-r--r--security/selinux/ss/mls_types.h2
-rw-r--r--security/selinux/ss/policydb.c154
-rw-r--r--security/selinux/ss/policydb.h24
-rw-r--r--security/selinux/ss/services.c139
-rw-r--r--security/selinux/ss/sidtab.c6
-rw-r--r--security/selinux/xfrm.c2
33 files changed, 703 insertions, 383 deletions
diff --git a/security/selinux/Makefile b/security/selinux/Makefile
index 86f0575f670d..66e56e9011df 100644
--- a/security/selinux/Makefile
+++ b/security/selinux/Makefile
@@ -33,11 +33,10 @@ $(addprefix $(obj)/,$(selinux-y)): $(obj)/flask.h
quiet_cmd_genhdrs = GEN $(addprefix $(obj)/,$(genhdrs))
cmd_genhdrs = $< $(addprefix $(obj)/,$(genhdrs))
-# see the note above, replace the $targets and 'flask.h' rule with the lines
-# below:
-# targets += $(genhdrs)
+targets += $(genhdrs)
+
+# see the note above, replace the 'flask.h' rule with the line below:
# $(addprefix $(obj)/,$(genhdrs)) &: $(obj)/genheaders FORCE
-targets += flask.h
$(obj)/flask.h: $(obj)/genheaders FORCE
$(call if_changed,genhdrs)
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index cc0b0af20296..4b4837a20225 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -174,13 +174,15 @@ int avc_get_hash_stats(char *page)
* using a linked list for extended_perms_decision lookup because the list is
* always small. i.e. less than 5, typically 1
*/
-static struct extended_perms_decision *avc_xperms_decision_lookup(u8 driver,
- struct avc_xperms_node *xp_node)
+static struct extended_perms_decision *
+avc_xperms_decision_lookup(u8 driver, u8 base_perm,
+ struct avc_xperms_node *xp_node)
{
struct avc_xperms_decision_node *xpd_node;
list_for_each_entry(xpd_node, &xp_node->xpd_head, xpd_list) {
- if (xpd_node->xpd.driver == driver)
+ if (xpd_node->xpd.driver == driver &&
+ xpd_node->xpd.base_perm == base_perm)
return &xpd_node->xpd;
}
return NULL;
@@ -205,11 +207,12 @@ avc_xperms_has_perm(struct extended_perms_decision *xpd,
}
static void avc_xperms_allow_perm(struct avc_xperms_node *xp_node,
- u8 driver, u8 perm)
+ u8 driver, u8 base_perm, u8 perm)
{
struct extended_perms_decision *xpd;
security_xperm_set(xp_node->xp.drivers.p, driver);
- xpd = avc_xperms_decision_lookup(driver, xp_node);
+ xp_node->xp.base_perms |= base_perm;
+ xpd = avc_xperms_decision_lookup(driver, base_perm, xp_node);
if (xpd && xpd->allowed)
security_xperm_set(xpd->allowed->p, perm);
}
@@ -245,6 +248,7 @@ static void avc_xperms_free(struct avc_xperms_node *xp_node)
static void avc_copy_xperms_decision(struct extended_perms_decision *dest,
struct extended_perms_decision *src)
{
+ dest->base_perm = src->base_perm;
dest->driver = src->driver;
dest->used = src->used;
if (dest->used & XPERMS_ALLOWED)
@@ -272,6 +276,7 @@ static inline void avc_quick_copy_xperms_decision(u8 perm,
*/
u8 i = perm >> 5;
+ dest->base_perm = src->base_perm;
dest->used = src->used;
if (dest->used & XPERMS_ALLOWED)
dest->allowed->p[i] = src->allowed->p[i];
@@ -357,6 +362,7 @@ static int avc_xperms_populate(struct avc_node *node,
memcpy(dest->xp.drivers.p, src->xp.drivers.p, sizeof(dest->xp.drivers.p));
dest->xp.len = src->xp.len;
+ dest->xp.base_perms = src->xp.base_perms;
/* for each source xpd allocate a destination xpd and copy */
list_for_each_entry(src_xpd, &src->xpd_head, xpd_list) {
@@ -807,6 +813,7 @@ out:
* @event : Updating event
* @perms : Permission mask bits
* @driver: xperm driver information
+ * @base_perm: the base permission associated with the extended permission
* @xperm: xperm permissions
* @ssid: AVC entry source sid
* @tsid: AVC entry target sid
@@ -820,10 +827,9 @@ out:
* otherwise, this function updates the AVC entry. The original AVC-entry object
* will release later by RCU.
*/
-static int avc_update_node(u32 event, u32 perms, u8 driver, u8 xperm, u32 ssid,
- u32 tsid, u16 tclass, u32 seqno,
- struct extended_perms_decision *xpd,
- u32 flags)
+static int avc_update_node(u32 event, u32 perms, u8 driver, u8 base_perm,
+ u8 xperm, u32 ssid, u32 tsid, u16 tclass, u32 seqno,
+ struct extended_perms_decision *xpd, u32 flags)
{
u32 hvalue;
int rc = 0;
@@ -880,7 +886,7 @@ static int avc_update_node(u32 event, u32 perms, u8 driver, u8 xperm, u32 ssid,
case AVC_CALLBACK_GRANT:
node->ae.avd.allowed |= perms;
if (node->ae.xp_node && (flags & AVC_EXTENDED_PERMS))
- avc_xperms_allow_perm(node->ae.xp_node, driver, xperm);
+ avc_xperms_allow_perm(node->ae.xp_node, driver, base_perm, xperm);
break;
case AVC_CALLBACK_TRY_REVOKE:
case AVC_CALLBACK_REVOKE:
@@ -930,7 +936,7 @@ static void avc_flush(void)
spin_lock_irqsave(lock, flag);
/*
- * With preemptable RCU, the outer spinlock does not
+ * With preemptible RCU, the outer spinlock does not
* prevent RCU grace periods from ending.
*/
rcu_read_lock();
@@ -987,10 +993,9 @@ static noinline void avc_compute_av(u32 ssid, u32 tsid, u16 tclass,
avc_insert(ssid, tsid, tclass, avd, xp_node);
}
-static noinline int avc_denied(u32 ssid, u32 tsid,
- u16 tclass, u32 requested,
- u8 driver, u8 xperm, unsigned int flags,
- struct av_decision *avd)
+static noinline int avc_denied(u32 ssid, u32 tsid, u16 tclass, u32 requested,
+ u8 driver, u8 base_perm, u8 xperm,
+ unsigned int flags, struct av_decision *avd)
{
if (flags & AVC_STRICT)
return -EACCES;
@@ -999,7 +1004,7 @@ static noinline int avc_denied(u32 ssid, u32 tsid,
!(avd->flags & AVD_FLAGS_PERMISSIVE))
return -EACCES;
- avc_update_node(AVC_CALLBACK_GRANT, requested, driver,
+ avc_update_node(AVC_CALLBACK_GRANT, requested, driver, base_perm,
xperm, ssid, tsid, tclass, avd->seqno, NULL, flags);
return 0;
}
@@ -1012,7 +1017,8 @@ static noinline int avc_denied(u32 ssid, u32 tsid,
* driver field is used to specify which set contains the permission.
*/
int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested,
- u8 driver, u8 xperm, struct common_audit_data *ad)
+ u8 driver, u8 base_perm, u8 xperm,
+ struct common_audit_data *ad)
{
struct avc_node *node;
struct av_decision avd;
@@ -1047,22 +1053,23 @@ int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested,
local_xpd.auditallow = &auditallow;
local_xpd.dontaudit = &dontaudit;
- xpd = avc_xperms_decision_lookup(driver, xp_node);
+ xpd = avc_xperms_decision_lookup(driver, base_perm, xp_node);
if (unlikely(!xpd)) {
/*
* Compute the extended_perms_decision only if the driver
- * is flagged
+ * is flagged and the base permission is known.
*/
- if (!security_xperm_test(xp_node->xp.drivers.p, driver)) {
+ if (!security_xperm_test(xp_node->xp.drivers.p, driver) ||
+ !(xp_node->xp.base_perms & base_perm)) {
avd.allowed &= ~requested;
goto decision;
}
rcu_read_unlock();
- security_compute_xperms_decision(ssid, tsid, tclass,
- driver, &local_xpd);
+ security_compute_xperms_decision(ssid, tsid, tclass, driver,
+ base_perm, &local_xpd);
rcu_read_lock();
- avc_update_node(AVC_CALLBACK_ADD_XPERMS, requested,
- driver, xperm, ssid, tsid, tclass, avd.seqno,
+ avc_update_node(AVC_CALLBACK_ADD_XPERMS, requested, driver,
+ base_perm, xperm, ssid, tsid, tclass, avd.seqno,
&local_xpd, 0);
} else {
avc_quick_copy_xperms_decision(xperm, &local_xpd, xpd);
@@ -1075,8 +1082,8 @@ int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested,
decision:
denied = requested & ~(avd.allowed);
if (unlikely(denied))
- rc = avc_denied(ssid, tsid, tclass, requested,
- driver, xperm, AVC_EXTENDED_PERMS, &avd);
+ rc = avc_denied(ssid, tsid, tclass, requested, driver,
+ base_perm, xperm, AVC_EXTENDED_PERMS, &avd);
rcu_read_unlock();
@@ -1110,7 +1117,7 @@ static noinline int avc_perm_nonode(u32 ssid, u32 tsid, u16 tclass,
avc_compute_av(ssid, tsid, tclass, avd, &xp_node);
denied = requested & ~(avd->allowed);
if (unlikely(denied))
- return avc_denied(ssid, tsid, tclass, requested, 0, 0,
+ return avc_denied(ssid, tsid, tclass, requested, 0, 0, 0,
flags, avd);
return 0;
}
@@ -1158,7 +1165,7 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,
rcu_read_unlock();
if (unlikely(denied))
- return avc_denied(ssid, tsid, tclass, requested, 0, 0,
+ return avc_denied(ssid, tsid, tclass, requested, 0, 0, 0,
flags, avd);
return 0;
}
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index f5a08f94e094..c95a5874bf7d 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)
@@ -407,7 +414,7 @@ static const struct {
static int match_opt_prefix(char *s, int l, char **arg)
{
- int i;
+ unsigned int i;
for (i = 0; i < ARRAY_SIZE(tokens); i++) {
size_t len = tokens[i].len;
@@ -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);
}
@@ -2869,8 +2880,8 @@ static void selinux_inode_free_security(struct inode *inode)
static int selinux_dentry_init_security(struct dentry *dentry, int mode,
const struct qstr *name,
- const char **xattr_name, void **ctx,
- u32 *ctxlen)
+ const char **xattr_name,
+ struct lsm_context *cp)
{
u32 newsid;
int rc;
@@ -2885,8 +2896,8 @@ static int selinux_dentry_init_security(struct dentry *dentry, int mode,
if (xattr_name)
*xattr_name = XATTR_NAME_SELINUX;
- return security_sid_to_context(newsid, (char **)ctx,
- ctxlen);
+ cp->id = LSM_ID_SELINUX;
+ return security_sid_to_context(newsid, &cp->context, &cp->len);
}
static int selinux_dentry_create_files_as(struct dentry *dentry, int mode,
@@ -3088,44 +3099,152 @@ 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;
+ tsec->avdcache.permissive_neveraudit =
+ (avd->flags == (AVD_FLAGS_PERMISSIVE|AVD_FLAGS_NEVERAUDIT));
+}
+
+/**
+ * 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)))
+ tsec = selinux_cred(current_cred());
+ if (task_avdcache_permnoaudit(tsec))
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);
+ 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;
}
@@ -3135,7 +3254,7 @@ static int selinux_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
const struct cred *cred = current_cred();
struct inode *inode = d_backing_inode(dentry);
unsigned int ia_valid = iattr->ia_valid;
- __u32 av = FILE__WRITE;
+ u32 av = FILE__WRITE;
/* ATTR_FORCE is just used for ATTR_KILL_S[UG]ID. */
if (ia_valid & ATTR_FORCE) {
@@ -3160,6 +3279,13 @@ static int selinux_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
static int selinux_inode_getattr(const struct path *path)
{
+ struct task_security_struct *tsec;
+
+ tsec = selinux_cred(current_cred());
+
+ if (task_avdcache_permnoaudit(tsec))
+ return 0;
+
return path_has_perm(current_cred(), path, FILE__GETATTR);
}
@@ -3366,6 +3492,18 @@ static int selinux_inode_removexattr(struct mnt_idmap *idmap,
return -EACCES;
}
+static int selinux_inode_file_setattr(struct dentry *dentry,
+ struct file_kattr *fa)
+{
+ return dentry_has_perm(current_cred(), dentry, FILE__SETATTR);
+}
+
+static int selinux_inode_file_getattr(struct dentry *dentry,
+ struct file_kattr *fa)
+{
+ return dentry_has_perm(current_cred(), dentry, FILE__GETATTR);
+}
+
static int selinux_path_notify(const struct path *path, u64 mask,
unsigned int obj_type)
{
@@ -3395,6 +3533,9 @@ static int selinux_path_notify(const struct path *path, u64 mask,
case FSNOTIFY_OBJ_TYPE_INODE:
perm = FILE__WATCH;
break;
+ case FSNOTIFY_OBJ_TYPE_MNTNS:
+ perm = FILE__WATCH_MOUNTNS;
+ break;
default:
return -EINVAL;
}
@@ -3404,7 +3545,8 @@ static int selinux_path_notify(const struct path *path, u64 mask,
perm |= FILE__WATCH_WITH_PERM;
/* watches on read-like events need the file:watch_reads permission */
- if (mask & (FS_ACCESS | FS_ACCESS_PERM | FS_CLOSE_NOWRITE))
+ if (mask & (FS_ACCESS | FS_ACCESS_PERM | FS_PRE_ACCESS |
+ FS_CLOSE_NOWRITE))
perm |= FILE__WATCH_READS;
return path_has_perm(current_cred(), path, perm);
@@ -3583,10 +3725,13 @@ static int selinux_kernfs_init_security(struct kernfs_node *kn_dir,
newsid = tsec->create_sid;
} else {
u16 secclass = inode_mode_to_security_class(kn->mode);
+ const char *kn_name;
struct qstr q;
- q.name = kn->name;
- q.hash_len = hashlen_string(kn_dir, kn->name);
+ /* kn is fresh, can't be renamed, name goes not away */
+ kn_name = rcu_dereference_check(kn->name, true);
+ q.name = kn_name;
+ q.hash_len = hashlen_string(kn_dir, kn_name);
rc = security_transition_sid(tsec->sid,
parent_sid, secclass, &q,
@@ -3688,8 +3833,8 @@ static int ioctl_has_perm(const struct cred *cred, struct file *file,
return 0;
isec = inode_security(inode);
- rc = avc_has_extended_perms(ssid, isec->sid, isec->sclass,
- requested, driver, xperm, &ad);
+ rc = avc_has_extended_perms(ssid, isec->sid, isec->sclass, requested,
+ driver, AVC_EXT_IOCTL, xperm, &ad);
out:
return rc;
}
@@ -4095,7 +4240,7 @@ static int selinux_kernel_module_request(char *kmod_name)
SYSTEM__MODULE_REQUEST, &ad);
}
-static int selinux_kernel_module_from_file(struct file *file)
+static int selinux_kernel_load_from_file(struct file *file, u32 requested)
{
struct common_audit_data ad;
struct inode_security_struct *isec;
@@ -4103,12 +4248,8 @@ static int selinux_kernel_module_from_file(struct file *file)
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 */
+ return avc_has_perm(sid, sid, SECCLASS_SYSTEM, requested, NULL);
ad.type = LSM_AUDIT_DATA_FILE;
ad.u.file = file;
@@ -4121,8 +4262,7 @@ static int selinux_kernel_module_from_file(struct file *file)
}
isec = inode_security(file_inode(file));
- return avc_has_perm(sid, isec->sid, SECCLASS_SYSTEM,
- SYSTEM__MODULE_LOAD, &ad);
+ return avc_has_perm(sid, isec->sid, SECCLASS_SYSTEM, requested, &ad);
}
static int selinux_kernel_read_file(struct file *file,
@@ -4131,9 +4271,30 @@ static int selinux_kernel_read_file(struct file *file,
{
int rc = 0;
+ BUILD_BUG_ON_MSG(READING_MAX_ID > 7,
+ "New kernel_read_file_id introduced; update SELinux!");
+
switch (id) {
+ case READING_FIRMWARE:
+ rc = selinux_kernel_load_from_file(file, SYSTEM__FIRMWARE_LOAD);
+ break;
case READING_MODULE:
- rc = selinux_kernel_module_from_file(contents ? file : NULL);
+ rc = selinux_kernel_load_from_file(file, SYSTEM__MODULE_LOAD);
+ break;
+ case READING_KEXEC_IMAGE:
+ rc = selinux_kernel_load_from_file(file,
+ SYSTEM__KEXEC_IMAGE_LOAD);
+ break;
+ case READING_KEXEC_INITRAMFS:
+ rc = selinux_kernel_load_from_file(file,
+ SYSTEM__KEXEC_INITRAMFS_LOAD);
+ break;
+ case READING_POLICY:
+ rc = selinux_kernel_load_from_file(file, SYSTEM__POLICY_LOAD);
+ break;
+ case READING_X509_CERTIFICATE:
+ rc = selinux_kernel_load_from_file(file,
+ SYSTEM__X509_CERTIFICATE_LOAD);
break;
default:
break;
@@ -4146,9 +4307,31 @@ static int selinux_kernel_load_data(enum kernel_load_data_id id, bool contents)
{
int rc = 0;
+ BUILD_BUG_ON_MSG(LOADING_MAX_ID > 7,
+ "New kernel_load_data_id introduced; update SELinux!");
+
switch (id) {
+ case LOADING_FIRMWARE:
+ rc = selinux_kernel_load_from_file(NULL, SYSTEM__FIRMWARE_LOAD);
+ break;
case LOADING_MODULE:
- rc = selinux_kernel_module_from_file(NULL);
+ rc = selinux_kernel_load_from_file(NULL, SYSTEM__MODULE_LOAD);
+ break;
+ case LOADING_KEXEC_IMAGE:
+ rc = selinux_kernel_load_from_file(NULL,
+ SYSTEM__KEXEC_IMAGE_LOAD);
+ break;
+ case LOADING_KEXEC_INITRAMFS:
+ rc = selinux_kernel_load_from_file(NULL,
+ SYSTEM__KEXEC_INITRAMFS_LOAD);
+ break;
+ case LOADING_POLICY:
+ rc = selinux_kernel_load_from_file(NULL,
+ SYSTEM__POLICY_LOAD);
+ break;
+ case LOADING_X509_CERTIFICATE:
+ rc = selinux_kernel_load_from_file(NULL,
+ SYSTEM__X509_CERTIFICATE_LOAD);
break;
default:
break;
@@ -4347,22 +4530,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;
@@ -4441,18 +4608,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;
@@ -4804,10 +4959,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;
@@ -4835,7 +4986,7 @@ out:
return err;
err_af:
/* Note that SCTP services expect -EINVAL, others -EAFNOSUPPORT. */
- if (sksec->sclass == SECCLASS_SCTP_SOCKET)
+ if (sk->sk_protocol == IPPROTO_SCTP)
return -EINVAL;
return -EAFNOSUPPORT;
}
@@ -4863,11 +5014,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,};
@@ -4912,9 +5062,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;
@@ -5738,7 +5885,7 @@ static unsigned int selinux_ip_output(void *priv, struct sk_buff *skb,
/* we do this in the LOCAL_OUT path and not the POST_ROUTING path
* because we want to make sure we apply the necessary labeling
* before IPsec is applied so we can leverage AH protection */
- sk = skb->sk;
+ sk = sk_to_full_sk(skb->sk);
if (sk) {
struct sk_security_struct *sksec;
@@ -5939,20 +6086,20 @@ static int nlmsg_sock_has_extended_perms(struct sock *sk, u32 perms, u16 nlmsg_t
{
struct sk_security_struct *sksec = sk->sk_security;
struct common_audit_data ad;
- struct lsm_network_audit net;
u8 driver;
u8 xperm;
if (sock_skip_has_perm(sksec->sid))
return 0;
- ad_net_init_from_sk(&ad, &net, sk);
+ ad.type = LSM_AUDIT_DATA_NLMSGTYPE;
+ ad.u.nlmsg_type = nlmsg_type;
driver = nlmsg_type >> 8;
xperm = nlmsg_type & 0xff;
return avc_has_extended_perms(current_sid(), sksec->sid, sksec->sclass,
- perms, driver, xperm, &ad);
+ perms, driver, AVC_EXT_NLMSG, xperm, &ad);
}
static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)
@@ -6640,15 +6787,28 @@ static int selinux_ismaclabel(const char *name)
return (strcmp(name, XATTR_SELINUX_SUFFIX) == 0);
}
-static int selinux_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
+static int selinux_secid_to_secctx(u32 secid, struct lsm_context *cp)
{
- return security_sid_to_context(secid, secdata, seclen);
+ u32 seclen;
+ int ret;
+
+ if (cp) {
+ cp->id = LSM_ID_SELINUX;
+ ret = security_sid_to_context(secid, &cp->context, &cp->len);
+ if (ret < 0)
+ return ret;
+ return cp->len;
+ }
+ ret = security_sid_to_context(secid, NULL, &seclen);
+ if (ret < 0)
+ return ret;
+ return seclen;
}
-static int selinux_lsmprop_to_secctx(struct lsm_prop *prop, char **secdata,
- u32 *seclen)
+static int selinux_lsmprop_to_secctx(struct lsm_prop *prop,
+ struct lsm_context *cp)
{
- return selinux_secid_to_secctx(prop->selinux.secid, secdata, seclen);
+ return selinux_secid_to_secctx(prop->selinux.secid, cp);
}
static int selinux_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
@@ -6657,9 +6817,13 @@ static int selinux_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
secid, GFP_KERNEL);
}
-static void selinux_release_secctx(char *secdata, u32 seclen)
+static void selinux_release_secctx(struct lsm_context *cp)
{
- kfree(secdata);
+ if (cp->id == LSM_ID_SELINUX) {
+ kfree(cp->context);
+ cp->context = NULL;
+ cp->id = LSM_ID_UNDEF;
+ }
}
static void selinux_inode_invalidate_secctx(struct inode *inode)
@@ -6691,14 +6855,16 @@ static int selinux_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
ctx, ctxlen, 0, NULL);
}
-static int selinux_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
+static int selinux_inode_getsecctx(struct inode *inode, struct lsm_context *cp)
{
- int len = 0;
+ int len;
len = selinux_inode_getsecurity(&nop_mnt_idmap, inode,
- XATTR_SELINUX_SUFFIX, ctx, true);
+ XATTR_SELINUX_SUFFIX,
+ (void **)&cp->context, true);
if (len < 0)
return len;
- *ctxlen = len;
+ cp->len = len;
+ cp->id = LSM_ID_SELINUX;
return 0;
}
#ifdef CONFIG_KEYS
@@ -6846,7 +7012,7 @@ static int selinux_ib_alloc_security(void *ib_sec)
#ifdef CONFIG_BPF_SYSCALL
static int selinux_bpf(int cmd, union bpf_attr *attr,
- unsigned int size)
+ unsigned int size, bool kernel)
{
u32 sid = current_sid();
int ret;
@@ -6933,7 +7099,7 @@ static int selinux_bpf_prog(struct bpf_prog *prog)
}
static int selinux_bpf_map_create(struct bpf_map *map, union bpf_attr *attr,
- struct bpf_token *token)
+ struct bpf_token *token, bool kernel)
{
struct bpf_security_struct *bpfsec;
@@ -6956,7 +7122,7 @@ static void selinux_bpf_map_free(struct bpf_map *map)
}
static int selinux_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
- struct bpf_token *token)
+ struct bpf_token *token, bool kernel)
{
struct bpf_security_struct *bpfsec;
@@ -7020,7 +7186,7 @@ struct lsm_blob_sizes selinux_blob_sizes __ro_after_init = {
};
#ifdef CONFIG_PERF_EVENTS
-static int selinux_perf_event_open(struct perf_event_attr *attr, int type)
+static int selinux_perf_event_open(int type)
{
u32 requested, sid = current_sid();
@@ -7117,6 +7283,19 @@ static int selinux_uring_cmd(struct io_uring_cmd *ioucmd)
return avc_has_perm(current_sid(), isec->sid,
SECCLASS_IO_URING, IO_URING__CMD, &ad);
}
+
+/**
+ * selinux_uring_allowed - check if io_uring_setup() can be called
+ *
+ * Check to see if the current task is allowed to call io_uring_setup().
+ */
+static int selinux_uring_allowed(void)
+{
+ u32 sid = current_sid();
+
+ return avc_has_perm(sid, sid, SECCLASS_IO_URING, IO_URING__ALLOWED,
+ NULL);
+}
#endif /* CONFIG_IO_URING */
static const struct lsm_id selinux_lsmid = {
@@ -7195,6 +7374,8 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
LSM_HOOK_INIT(inode_getxattr, selinux_inode_getxattr),
LSM_HOOK_INIT(inode_listxattr, selinux_inode_listxattr),
LSM_HOOK_INIT(inode_removexattr, selinux_inode_removexattr),
+ LSM_HOOK_INIT(inode_file_getattr, selinux_inode_file_getattr),
+ LSM_HOOK_INIT(inode_file_setattr, selinux_inode_file_setattr),
LSM_HOOK_INIT(inode_set_acl, selinux_inode_set_acl),
LSM_HOOK_INIT(inode_get_acl, selinux_inode_get_acl),
LSM_HOOK_INIT(inode_remove_acl, selinux_inode_remove_acl),
@@ -7370,6 +7551,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
LSM_HOOK_INIT(uring_override_creds, selinux_uring_override_creds),
LSM_HOOK_INIT(uring_sqpoll, selinux_uring_sqpoll),
LSM_HOOK_INIT(uring_cmd, selinux_uring_cmd),
+ LSM_HOOK_INIT(uring_allowed, selinux_uring_allowed),
#endif
/*
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/avc.h b/security/selinux/include/avc.h
index 96a614d47df8..01b5167fee1a 100644
--- a/security/selinux/include/avc.h
+++ b/security/selinux/include/avc.h
@@ -65,6 +65,10 @@ static inline u32 avc_audit_required(u32 requested, struct av_decision *avd,
int result, u32 auditdeny, u32 *deniedp)
{
u32 denied, audited;
+
+ if (avd->flags & AVD_FLAGS_NEVERAUDIT)
+ return 0;
+
denied = requested & ~avd->allowed;
if (unlikely(denied)) {
audited = denied & avd->auditdeny;
@@ -136,8 +140,11 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid, u16 tclass, u32 requested,
int avc_has_perm(u32 ssid, u32 tsid, u16 tclass, u32 requested,
struct common_audit_data *auditdata);
+#define AVC_EXT_IOCTL (1 << 0) /* Cache entry for an ioctl extended permission */
+#define AVC_EXT_NLMSG (1 << 1) /* Cache entry for an nlmsg extended permission */
int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested,
- u8 driver, u8 perm, struct common_audit_data *ad);
+ u8 driver, u8 base_perm, u8 perm,
+ struct common_audit_data *ad);
u32 avc_policy_seqno(void);
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index 2bc20135324a..5665aa5e7853 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -8,7 +8,7 @@
COMMON_FILE_SOCK_PERMS, "unlink", "link", "rename", "execute", \
"quotaon", "mounton", "audit_access", "open", "execmod", \
"watch", "watch_mount", "watch_sb", "watch_with_perm", \
- "watch_reads"
+ "watch_reads", "watch_mountns"
#define COMMON_SOCK_PERMS \
COMMON_FILE_SOCK_PERMS, "bind", "connect", "listen", "accept", \
@@ -63,7 +63,9 @@ const struct security_class_mapping secclass_map[] = {
{ "process2", { "nnp_transition", "nosuid_transition", NULL } },
{ "system",
{ "ipc_info", "syslog_read", "syslog_mod", "syslog_console",
- "module_request", "module_load", NULL } },
+ "module_request", "module_load", "firmware_load",
+ "kexec_image_load", "kexec_initramfs_load", "policy_load",
+ "x509_certificate_load", NULL } },
{ "capability", { COMMON_CAP_PERMS, NULL } },
{ "filesystem",
{ "mount", "remount", "unmount", "getattr", "relabelfrom",
@@ -125,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 } },
@@ -177,9 +177,9 @@ const struct security_class_mapping secclass_map[] = {
{ "perf_event",
{ "open", "cpu", "kernel", "tracepoint", "read", "write", NULL } },
{ "anon_inode", { COMMON_FILE_PERMS, NULL } },
- { "io_uring", { "override_creds", "sqpoll", "cmd", NULL } },
+ { "io_uring", { "override_creds", "sqpoll", "cmd", "allowed", NULL } },
{ "user_namespace", { "create", NULL } },
- { NULL }
+ /* last one */ { NULL, {} }
};
#ifdef __KERNEL__ /* avoid this check when building host programs */
diff --git a/security/selinux/include/conditional.h b/security/selinux/include/conditional.h
index 5910bb7c2eca..060833e2dba2 100644
--- a/security/selinux/include/conditional.h
+++ b/security/selinux/include/conditional.h
@@ -16,7 +16,7 @@
int security_get_bools(struct selinux_policy *policy, u32 *len, char ***names,
int **values);
-int security_set_bools(u32 len, int *values);
+int security_set_bools(u32 len, const int *values);
int security_get_bool_value(u32 index);
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..1d7ac59015a1 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,8 +43,23 @@ 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 */
+ bool permissive_neveraudit; /* permissive and neveraudit */
+ } avdcache;
} __randomize_layout;
+static inline bool task_avdcache_permnoaudit(struct task_security_struct *tsec)
+{
+ return (tsec->avdcache.permissive_neveraudit &&
+ tsec->sid == tsec->avdcache.sid &&
+ tsec->avdcache.seqno == avc_policy_seqno());
+}
+
enum label_initialized {
LABEL_INVALID, /* invalid or not initialized */
LABEL_INITIALIZED, /* initialized */
@@ -82,7 +104,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 079679fe7254..7405154e6c42 100644
--- a/security/selinux/include/policycap.h
+++ b/security/selinux/include/policycap.h
@@ -15,6 +15,8 @@ enum {
POLICYDB_CAP_IOCTL_SKIP_CLOEXEC,
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 e080827408c4..d8962fcf2ff9 100644
--- a/security/selinux/include/policycap_names.h
+++ b/security/selinux/include/policycap_names.h
@@ -18,6 +18,8 @@ const char *const selinux_policycap_names[__POLICYDB_CAP_MAX] = {
"ioctl_skip_cloexec",
"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 c7f2731abd03..8201e6a3ac0f 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -46,10 +46,12 @@
#define POLICYDB_VERSION_INFINIBAND 31
#define POLICYDB_VERSION_GLBLUB 32
#define POLICYDB_VERSION_COMP_FTRANS 33 /* compressed filename transitions */
+#define POLICYDB_VERSION_COND_XPERMS 34 /* extended permissions in conditional policies */
+#define POLICYDB_VERSION_NEVERAUDIT 35 /* neveraudit types */
/* Range of policy versions we understand*/
#define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE
-#define POLICYDB_VERSION_MAX POLICYDB_VERSION_COMP_FTRANS
+#define POLICYDB_VERSION_MAX POLICYDB_VERSION_NEVERAUDIT
/* Mask for just the mount related flags */
#define SE_MNTMASK 0x0f
@@ -201,6 +203,12 @@ static inline bool selinux_policycap_netlink_xperm(void)
selinux_state.policycap[POLICYDB_CAP_NETLINK_XPERM]);
}
+static inline bool selinux_policycap_netif_wildcard(void)
+{
+ return READ_ONCE(
+ selinux_state.policycap[POLICYDB_CAP_NETIF_WILDCARD]);
+}
+
struct selinux_policy_convert_data;
struct selinux_load_state {
@@ -239,6 +247,7 @@ struct extended_perms_data {
struct extended_perms_decision {
u8 used;
u8 driver;
+ u8 base_perm;
struct extended_perms_data *allowed;
struct extended_perms_data *auditallow;
struct extended_perms_data *dontaudit;
@@ -246,17 +255,20 @@ struct extended_perms_decision {
struct extended_perms {
u16 len; /* length associated decision chain */
+ u8 base_perms; /* which base permissions are covered */
struct extended_perms_data drivers; /* flag drivers that are used */
};
/* definitions of av_decision.flags */
#define AVD_FLAGS_PERMISSIVE 0x0001
+#define AVD_FLAGS_NEVERAUDIT 0x0002
void security_compute_av(u32 ssid, u32 tsid, u16 tclass,
struct av_decision *avd,
struct extended_perms *xperms);
void security_compute_xperms_decision(u32 ssid, u32 tsid, u16 tclass, u8 driver,
+ u8 base_perm,
struct extended_perms_decision *xpermd);
void security_compute_av_user(u32 ssid, u32 tsid, u16 tclass,
@@ -289,7 +301,7 @@ int security_context_to_sid_default(const char *scontext, u32 scontext_len,
int security_context_to_sid_force(const char *scontext, u32 scontext_len,
u32 *sid);
-int security_get_user_sids(u32 callsid, char *username, u32 **sids, u32 *nel);
+int security_get_user_sids(u32 fromsid, const char *username, u32 **sids, u32 *nel);
int security_port_sid(u8 protocol, u16 port, u32 *out_sid);
@@ -297,9 +309,9 @@ int security_ib_pkey_sid(u64 subnet_prefix, u16 pkey_num, u32 *out_sid);
int security_ib_endport_sid(const char *dev_name, u8 port_num, u32 *out_sid);
-int security_netif_sid(char *name, u32 *if_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);
@@ -307,7 +319,7 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
int security_validate_transition_user(u32 oldsid, u32 newsid, u32 tasksid,
u16 tclass);
-int security_bounded_transition(u32 oldsid, u32 newsid);
+int security_bounded_transition(u32 old_sid, u32 new_sid);
int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid);
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 6cd5bb0ba380..9aa1d03ab612 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -1072,6 +1072,7 @@ static ssize_t sel_write_user(struct file *file, char *buf, size_t size)
pr_warn_ratelimited("SELinux: %s (%d) wrote to /sys/fs/selinux/user!"
" This will not be supported in the future; please update your"
" userspace.\n", current->comm, current->pid);
+ ssleep(5);
length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__COMPUTE_USER,
@@ -1515,7 +1516,7 @@ static const struct file_operations sel_avc_hash_stats_ops = {
#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS
static struct avc_cache_stats *sel_avc_get_stat_idx(loff_t *idx)
{
- int cpu;
+ loff_t cpu;
for (cpu = *idx; cpu < nr_cpu_ids; ++cpu) {
if (!cpu_possible(cpu))
@@ -2001,7 +2002,7 @@ static int sel_fill_super(struct super_block *sb, struct fs_context *fc)
[SEL_POLICY] = {"policy", &sel_policy_ops, S_IRUGO},
[SEL_VALIDATE_TRANS] = {"validatetrans", &sel_transition_ops,
S_IWUGO},
- /* last one */ {""}
+ /* last one */ {"", NULL, 0}
};
ret = selinux_fs_info_create(sb);
@@ -2097,8 +2098,6 @@ err:
pr_err("SELinux: %s: failed while creating inodes\n",
__func__);
- selinux_fs_info_free(sb);
-
return ret;
}
@@ -2158,8 +2157,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/avtab.c b/security/selinux/ss/avtab.c
index 8e400dd736b7..c2c31521cace 100644
--- a/security/selinux/ss/avtab.c
+++ b/security/selinux/ss/avtab.c
@@ -336,10 +336,10 @@ static const uint16_t spec_order[] = {
};
/* clang-format on */
-int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
+int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *pol,
int (*insertf)(struct avtab *a, const struct avtab_key *k,
const struct avtab_datum *d, void *p),
- void *p)
+ void *p, bool conditional)
{
__le16 buf16[4];
u16 enabled;
@@ -457,6 +457,13 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
"was specified\n",
vers);
return -EINVAL;
+ } else if ((vers < POLICYDB_VERSION_COND_XPERMS) &&
+ (key.specified & AVTAB_XPERMS) && conditional) {
+ pr_err("SELinux: avtab: policy version %u does not "
+ "support extended permissions rules in conditional "
+ "policies and one was specified\n",
+ vers);
+ return -EINVAL;
} else if (key.specified & AVTAB_XPERMS) {
memset(&xperms, 0, sizeof(struct avtab_extended_perms));
rc = next_entry(&xperms.specified, fp, sizeof(u8));
@@ -500,7 +507,7 @@ static int avtab_insertf(struct avtab *a, const struct avtab_key *k,
return avtab_insert(a, k, d);
}
-int avtab_read(struct avtab *a, void *fp, struct policydb *pol)
+int avtab_read(struct avtab *a, struct policy_file *fp, struct policydb *pol)
{
int rc;
__le32 buf[1];
@@ -523,7 +530,7 @@ int avtab_read(struct avtab *a, void *fp, struct policydb *pol)
goto bad;
for (i = 0; i < nel; i++) {
- rc = avtab_read_item(a, fp, pol, avtab_insertf, NULL);
+ rc = avtab_read_item(a, fp, pol, avtab_insertf, NULL, false);
if (rc) {
if (rc == -ENOMEM)
pr_err("SELinux: avtab: out of memory\n");
@@ -543,7 +550,7 @@ bad:
goto out;
}
-int avtab_write_item(struct policydb *p, const struct avtab_node *cur, void *fp)
+int avtab_write_item(struct policydb *p, const struct avtab_node *cur, struct policy_file *fp)
{
__le16 buf16[4];
__le32 buf32[ARRAY_SIZE(cur->datum.u.xperms->perms.p)];
@@ -579,7 +586,7 @@ int avtab_write_item(struct policydb *p, const struct avtab_node *cur, void *fp)
return 0;
}
-int avtab_write(struct policydb *p, struct avtab *a, void *fp)
+int avtab_write(struct policydb *p, struct avtab *a, struct policy_file *fp)
{
u32 i;
int rc = 0;
diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h
index f4407185401c..850b3453f259 100644
--- a/security/selinux/ss/avtab.h
+++ b/security/selinux/ss/avtab.h
@@ -89,7 +89,7 @@ struct avtab {
};
void avtab_init(struct avtab *h);
-int avtab_alloc(struct avtab *, u32);
+int avtab_alloc(struct avtab *h, u32 nrules);
int avtab_alloc_dup(struct avtab *new, const struct avtab *orig);
void avtab_destroy(struct avtab *h);
@@ -105,15 +105,16 @@ static inline void avtab_hash_eval(struct avtab *h, const char *tag)
#endif
struct policydb;
-int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
+struct policy_file;
+int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *pol,
int (*insert)(struct avtab *a, const struct avtab_key *k,
const struct avtab_datum *d, void *p),
- void *p);
+ void *p, bool conditional);
-int avtab_read(struct avtab *a, void *fp, struct policydb *pol);
+int avtab_read(struct avtab *a, struct policy_file *fp, struct policydb *pol);
int avtab_write_item(struct policydb *p, const struct avtab_node *cur,
- void *fp);
-int avtab_write(struct policydb *p, struct avtab *a, void *fp);
+ struct policy_file *fp);
+int avtab_write(struct policydb *p, struct avtab *a, struct policy_file *fp);
struct avtab_node *avtab_insert_nonunique(struct avtab *h,
const struct avtab_key *key,
diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c
index 64ba95e40a6f..1bebfcb9c6a1 100644
--- a/security/selinux/ss/conditional.c
+++ b/security/selinux/ss/conditional.c
@@ -206,7 +206,7 @@ static int bool_isvalid(struct cond_bool_datum *b)
return 1;
}
-int cond_read_bool(struct policydb *p, struct symtab *s, void *fp)
+int cond_read_bool(struct policydb *p, struct symtab *s, struct policy_file *fp)
{
char *key = NULL;
struct cond_bool_datum *booldatum;
@@ -230,17 +230,11 @@ int cond_read_bool(struct policydb *p, struct symtab *s, 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);
- if (!key)
- goto err;
- rc = next_entry(key, fp, len);
+ rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
goto err;
- key[len] = '\0';
+
rc = symtab_insert(s, key, booldatum);
if (rc)
goto err;
@@ -323,7 +317,7 @@ static int cond_insertf(struct avtab *a, const struct avtab_key *k,
return 0;
}
-static int cond_read_av_list(struct policydb *p, void *fp,
+static int cond_read_av_list(struct policydb *p, struct policy_file *fp,
struct cond_av_list *list,
struct cond_av_list *other)
{
@@ -349,7 +343,7 @@ static int cond_read_av_list(struct policydb *p, void *fp,
for (i = 0; i < len; i++) {
data.dst = &list->nodes[i];
rc = avtab_read_item(&p->te_cond_avtab, fp, p, cond_insertf,
- &data);
+ &data, true);
if (rc) {
kfree(list->nodes);
list->nodes = NULL;
@@ -375,7 +369,7 @@ static int expr_node_isvalid(struct policydb *p, struct cond_expr_node *expr)
return 1;
}
-static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp)
+static int cond_read_node(struct policydb *p, struct cond_node *node, struct policy_file *fp)
{
__le32 buf[2];
u32 i, len;
@@ -415,7 +409,7 @@ static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp)
return cond_read_av_list(p, fp, &node->false_list, &node->true_list);
}
-int cond_read_list(struct policydb *p, void *fp)
+int cond_read_list(struct policydb *p, struct policy_file *fp)
{
__le32 buf[1];
u32 i, len;
@@ -453,7 +447,7 @@ int cond_write_bool(void *vkey, void *datum, void *ptr)
char *key = vkey;
struct cond_bool_datum *booldatum = datum;
struct policy_data *pd = ptr;
- void *fp = pd->fp;
+ struct policy_file *fp = pd->fp;
__le32 buf[3];
u32 len;
int rc;
@@ -536,7 +530,7 @@ static int cond_write_node(struct policydb *p, struct cond_node *node,
return 0;
}
-int cond_write_list(struct policydb *p, void *fp)
+int cond_write_list(struct policydb *p, struct policy_file *fp)
{
u32 i;
__le32 buf[1];
diff --git a/security/selinux/ss/conditional.h b/security/selinux/ss/conditional.h
index 8827715bad75..468e98ad3ea1 100644
--- a/security/selinux/ss/conditional.h
+++ b/security/selinux/ss/conditional.h
@@ -68,10 +68,10 @@ int cond_destroy_bool(void *key, void *datum, void *p);
int cond_index_bool(void *key, void *datum, void *datap);
-int cond_read_bool(struct policydb *p, struct symtab *s, void *fp);
-int cond_read_list(struct policydb *p, void *fp);
+int cond_read_bool(struct policydb *p, struct symtab *s, struct policy_file *fp);
+int cond_read_list(struct policydb *p, struct policy_file *fp);
int cond_write_bool(void *key, void *datum, void *ptr);
-int cond_write_list(struct policydb *p, void *fp);
+int cond_write_list(struct policydb *p, struct policy_file *fp);
void cond_compute_av(struct avtab *ctab, struct avtab_key *key,
struct av_decision *avd, struct extended_perms *xperms);
diff --git a/security/selinux/ss/context.c b/security/selinux/ss/context.c
index e39990f494dd..a528b7f76280 100644
--- a/security/selinux/ss/context.c
+++ b/security/selinux/ss/context.c
@@ -20,7 +20,7 @@ u32 context_compute_hash(const struct context *c)
* context struct with only the len & str set (and vice versa)
* under a given policy. Since context structs from different
* policies should never meet, it is safe to hash valid and
- * invalid contexts differently. The context_cmp() function
+ * invalid contexts differently. The context_equal() function
* already operates under the same assumption.
*/
if (c->len)
diff --git a/security/selinux/ss/context.h b/security/selinux/ss/context.h
index 7ccab2e6965f..dd3b9b5b588e 100644
--- a/security/selinux/ss/context.h
+++ b/security/selinux/ss/context.h
@@ -132,13 +132,13 @@ out:
return rc;
}
-static inline int mls_context_cmp(const struct context *c1,
- const struct context *c2)
+static inline bool mls_context_equal(const struct context *c1,
+ const struct context *c2)
{
return ((c1->range.level[0].sens == c2->range.level[0].sens) &&
- ebitmap_cmp(&c1->range.level[0].cat, &c2->range.level[0].cat) &&
+ ebitmap_equal(&c1->range.level[0].cat, &c2->range.level[0].cat) &&
(c1->range.level[1].sens == c2->range.level[1].sens) &&
- ebitmap_cmp(&c1->range.level[1].cat, &c2->range.level[1].cat));
+ ebitmap_equal(&c1->range.level[1].cat, &c2->range.level[1].cat));
}
static inline void mls_context_destroy(struct context *c)
@@ -188,15 +188,15 @@ static inline void context_destroy(struct context *c)
mls_context_destroy(c);
}
-static inline int context_cmp(const struct context *c1,
- const struct context *c2)
+static inline bool context_equal(const struct context *c1,
+ const struct context *c2)
{
if (c1->len && c2->len)
return (c1->len == c2->len && !strcmp(c1->str, c2->str));
if (c1->len || c2->len)
return 0;
return ((c1->user == c2->user) && (c1->role == c2->role) &&
- (c1->type == c2->type) && mls_context_cmp(c1, c2));
+ (c1->type == c2->type) && mls_context_equal(c1, c2));
}
u32 context_compute_hash(const struct context *c);
diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c
index 99c01be15115..43bc19e21960 100644
--- a/security/selinux/ss/ebitmap.c
+++ b/security/selinux/ss/ebitmap.c
@@ -25,12 +25,12 @@
static struct kmem_cache *ebitmap_node_cachep __ro_after_init;
-int ebitmap_cmp(const struct ebitmap *e1, const struct ebitmap *e2)
+bool ebitmap_equal(const struct ebitmap *e1, const struct ebitmap *e2)
{
const struct ebitmap_node *n1, *n2;
if (e1->highbit != e2->highbit)
- return 0;
+ return false;
n1 = e1->node;
n2 = e2->node;
@@ -41,9 +41,9 @@ int ebitmap_cmp(const struct ebitmap *e1, const struct ebitmap *e2)
}
if (n1 || n2)
- return 0;
+ return false;
- return 1;
+ return true;
}
int ebitmap_cpy(struct ebitmap *dst, const struct ebitmap *src)
@@ -360,7 +360,7 @@ void ebitmap_destroy(struct ebitmap *e)
e->node = NULL;
}
-int ebitmap_read(struct ebitmap *e, void *fp)
+int ebitmap_read(struct ebitmap *e, struct policy_file *fp)
{
struct ebitmap_node *n = NULL;
u32 mapunit, count, startbit, index, i;
@@ -478,7 +478,7 @@ bad:
goto out;
}
-int ebitmap_write(const struct ebitmap *e, void *fp)
+int ebitmap_write(const struct ebitmap *e, struct policy_file *fp)
{
struct ebitmap_node *n;
u32 bit, count, last_bit, last_startbit;
diff --git a/security/selinux/ss/ebitmap.h b/security/selinux/ss/ebitmap.h
index ba2ac3da1153..c9569998f287 100644
--- a/security/selinux/ss/ebitmap.h
+++ b/security/selinux/ss/ebitmap.h
@@ -120,7 +120,7 @@ static inline void ebitmap_node_clr_bit(struct ebitmap_node *n, u32 bit)
(bit) < ebitmap_length(e); \
(bit) = ebitmap_next_positive(e, &(n), bit))
-int ebitmap_cmp(const struct ebitmap *e1, const struct ebitmap *e2);
+bool ebitmap_equal(const struct ebitmap *e1, const struct ebitmap *e2);
int ebitmap_cpy(struct ebitmap *dst, const struct ebitmap *src);
int ebitmap_and(struct ebitmap *dst, const struct ebitmap *e1,
const struct ebitmap *e2);
@@ -129,8 +129,9 @@ int ebitmap_contains(const struct ebitmap *e1, const struct ebitmap *e2,
int ebitmap_get_bit(const struct ebitmap *e, u32 bit);
int ebitmap_set_bit(struct ebitmap *e, u32 bit, int value);
void ebitmap_destroy(struct ebitmap *e);
-int ebitmap_read(struct ebitmap *e, void *fp);
-int ebitmap_write(const struct ebitmap *e, void *fp);
+struct policy_file;
+int ebitmap_read(struct ebitmap *e, struct policy_file *fp);
+int ebitmap_write(const struct ebitmap *e, struct policy_file *fp);
u32 ebitmap_hash(const struct ebitmap *e, u32 hash);
#ifdef CONFIG_NETLABEL
diff --git a/security/selinux/ss/hashtab.c b/security/selinux/ss/hashtab.c
index 383fd2d70878..1382eb3bfde1 100644
--- a/security/selinux/ss/hashtab.c
+++ b/security/selinux/ss/hashtab.c
@@ -40,7 +40,8 @@ int hashtab_init(struct hashtab *h, u32 nel_hint)
h->htable = NULL;
if (size) {
- h->htable = kcalloc(size, sizeof(*h->htable), GFP_KERNEL);
+ h->htable = kcalloc(size, sizeof(*h->htable),
+ GFP_KERNEL | __GFP_NOWARN);
if (!h->htable)
return -ENOMEM;
h->size = size;
diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c
index 989c809d310d..a6e49269f535 100644
--- a/security/selinux/ss/mls.c
+++ b/security/selinux/ss/mls.c
@@ -171,7 +171,7 @@ int mls_level_isvalid(struct policydb *p, struct mls_level *l)
* levdatum->level->cat and no bit in l->cat is larger than
* p->p_cats.nprim.
*/
- return ebitmap_contains(&levdatum->level->cat, &l->cat,
+ return ebitmap_contains(&levdatum->level.cat, &l->cat,
p->p_cats.nprim);
}
@@ -289,7 +289,7 @@ int mls_context_to_sid(struct policydb *pol, char oldc, char *scontext,
levdatum = symtab_search(&pol->p_levels, sensitivity);
if (!levdatum)
return -EINVAL;
- context->range.level[l].sens = levdatum->level->sens;
+ context->range.level[l].sens = levdatum->level.sens;
/* Extract category set. */
while (next_cat != NULL) {
@@ -456,7 +456,7 @@ int mls_convert_context(struct policydb *oldp, struct policydb *newp,
if (!levdatum)
return -EINVAL;
- newc->range.level[l].sens = levdatum->level->sens;
+ newc->range.level[l].sens = levdatum->level.sens;
ebitmap_for_each_positive_bit(&oldc->range.level[l].cat, node,
i)
diff --git a/security/selinux/ss/mls_types.h b/security/selinux/ss/mls_types.h
index 7ef6e8cb0cf4..51df2ebd1211 100644
--- a/security/selinux/ss/mls_types.h
+++ b/security/selinux/ss/mls_types.h
@@ -29,7 +29,7 @@ struct mls_range {
static inline int mls_level_eq(const struct mls_level *l1,
const struct mls_level *l2)
{
- return ((l1->sens == l2->sens) && ebitmap_cmp(&l1->cat, &l2->cat));
+ return ((l1->sens == l2->sens) && ebitmap_equal(&l1->cat, &l2->cat));
}
static inline int mls_level_dom(const struct mls_level *l1,
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 383f3ae82a73..91df3db6a88c 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -155,6 +155,16 @@ static const struct policydb_compat_info policydb_compat[] = {
.sym_num = SYM_NUM,
.ocon_num = OCON_NUM,
},
+ {
+ .version = POLICYDB_VERSION_COND_XPERMS,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM,
+ },
+ {
+ .version = POLICYDB_VERSION_NEVERAUDIT,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM,
+ },
};
static const struct policydb_compat_info *
@@ -296,9 +306,7 @@ static int sens_destroy(void *key, void *datum, void *p)
kfree(key);
if (datum) {
levdatum = datum;
- if (levdatum->level)
- ebitmap_destroy(&levdatum->level->cat);
- kfree(levdatum->level);
+ ebitmap_destroy(&levdatum->level.cat);
}
kfree(datum);
return 0;
@@ -528,6 +536,7 @@ static void policydb_init(struct policydb *p)
ebitmap_init(&p->filename_trans_ttypes);
ebitmap_init(&p->policycaps);
ebitmap_init(&p->permissive_map);
+ ebitmap_init(&p->neveraudit_map);
}
/*
@@ -630,11 +639,11 @@ static int sens_index(void *key, void *datum, void *datap)
p = datap;
if (!levdatum->isalias) {
- if (!levdatum->level->sens ||
- levdatum->level->sens > p->p_levels.nprim)
+ if (!levdatum->level.sens ||
+ levdatum->level.sens > p->p_levels.nprim)
return -EINVAL;
- p->sym_val_to_name[SYM_LEVELS][levdatum->level->sens - 1] = key;
+ p->sym_val_to_name[SYM_LEVELS][levdatum->level.sens - 1] = key;
}
return 0;
@@ -849,6 +858,7 @@ void policydb_destroy(struct policydb *p)
ebitmap_destroy(&p->filename_trans_ttypes);
ebitmap_destroy(&p->policycaps);
ebitmap_destroy(&p->permissive_map);
+ ebitmap_destroy(&p->neveraudit_map);
}
/*
@@ -992,7 +1002,7 @@ int policydb_context_isvalid(struct policydb *p, struct context *c)
* Read a MLS range structure from a policydb binary
* representation file.
*/
-static int mls_read_range_helper(struct mls_range *r, void *fp)
+static int mls_read_range_helper(struct mls_range *r, struct policy_file *fp)
{
__le32 buf[2];
u32 items;
@@ -1052,7 +1062,7 @@ out:
* from a policydb binary representation file.
*/
static int context_read_and_validate(struct context *c, struct policydb *p,
- void *fp)
+ struct policy_file *fp)
{
__le32 buf[3];
int rc;
@@ -1090,7 +1100,7 @@ out:
* binary representation file.
*/
-static int str_read(char **strp, gfp_t flags, void *fp, u32 len)
+int str_read(char **strp, gfp_t flags, struct policy_file *fp, u32 len)
{
int rc;
char *str;
@@ -1113,7 +1123,7 @@ static int str_read(char **strp, gfp_t flags, void *fp, u32 len)
return 0;
}
-static int perm_read(struct policydb *p, struct symtab *s, void *fp)
+static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
{
char *key = NULL;
struct perm_datum *perdatum;
@@ -1146,7 +1156,7 @@ bad:
return rc;
}
-static int common_read(struct policydb *p, struct symtab *s, void *fp)
+static int common_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
{
char *key = NULL;
struct common_datum *comdatum;
@@ -1198,7 +1208,7 @@ static void type_set_init(struct type_set *t)
ebitmap_init(&t->negset);
}
-static int type_set_read(struct type_set *t, void *fp)
+static int type_set_read(struct type_set *t, struct policy_file *fp)
{
__le32 buf[1];
int rc;
@@ -1217,7 +1227,7 @@ static int type_set_read(struct type_set *t, void *fp)
}
static int read_cons_helper(struct policydb *p, struct constraint_node **nodep,
- u32 ncons, int allowxtarget, void *fp)
+ u32 ncons, int allowxtarget, struct policy_file *fp)
{
struct constraint_node *c, *lc;
struct constraint_expr *e, *le;
@@ -1311,7 +1321,7 @@ static int read_cons_helper(struct policydb *p, struct constraint_node **nodep,
return 0;
}
-static int class_read(struct policydb *p, struct symtab *s, void *fp)
+static int class_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
{
char *key = NULL;
struct class_datum *cladatum;
@@ -1408,7 +1418,7 @@ bad:
return rc;
}
-static int role_read(struct policydb *p, struct symtab *s, void *fp)
+static int role_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
{
char *key = NULL;
struct role_datum *role;
@@ -1465,7 +1475,7 @@ bad:
return rc;
}
-static int type_read(struct policydb *p, struct symtab *s, void *fp)
+static int type_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
{
char *key = NULL;
struct type_datum *typdatum;
@@ -1517,7 +1527,7 @@ bad:
* Read a MLS level structure from a policydb binary
* representation file.
*/
-static int mls_read_level(struct mls_level *lp, void *fp)
+static int mls_read_level(struct mls_level *lp, struct policy_file *fp)
{
__le32 buf[1];
int rc;
@@ -1539,7 +1549,7 @@ static int mls_read_level(struct mls_level *lp, void *fp)
return 0;
}
-static int user_read(struct policydb *p, struct symtab *s, void *fp)
+static int user_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
{
char *key = NULL;
struct user_datum *usrdatum;
@@ -1590,7 +1600,7 @@ bad:
return rc;
}
-static int sens_read(struct policydb *p, struct symtab *s, void *fp)
+static int sens_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
{
char *key = NULL;
struct level_datum *levdatum;
@@ -1613,12 +1623,7 @@ static int sens_read(struct policydb *p, struct symtab *s, void *fp)
if (rc)
goto bad;
- rc = -ENOMEM;
- levdatum->level = kmalloc(sizeof(*levdatum->level), GFP_KERNEL);
- if (!levdatum->level)
- goto bad;
-
- rc = mls_read_level(levdatum->level, fp);
+ rc = mls_read_level(&levdatum->level, fp);
if (rc)
goto bad;
@@ -1631,7 +1636,7 @@ bad:
return rc;
}
-static int cat_read(struct policydb *p, struct symtab *s, void *fp)
+static int cat_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
{
char *key = NULL;
struct cat_datum *catdatum;
@@ -1666,7 +1671,7 @@ bad:
/* clang-format off */
static int (*const read_f[SYM_NUM])(struct policydb *p, struct symtab *s,
- void *fp) = {
+ struct policy_file *fp) = {
common_read,
class_read,
role_read,
@@ -1836,7 +1841,7 @@ u32 string_to_av_perm(struct policydb *p, u16 tclass, const char *name)
return 1U << (perdatum->value - 1);
}
-static int range_read(struct policydb *p, void *fp)
+static int range_read(struct policydb *p, struct policy_file *fp)
{
struct range_trans *rt = NULL;
struct mls_range *r = NULL;
@@ -1913,7 +1918,7 @@ out:
return rc;
}
-static int filename_trans_read_helper_compat(struct policydb *p, void *fp)
+static int filename_trans_read_helper_compat(struct policydb *p, struct policy_file *fp)
{
struct filename_trans_key key, *ft = NULL;
struct filename_trans_datum *last, *datum = NULL;
@@ -1998,7 +2003,7 @@ out:
return rc;
}
-static int filename_trans_read_helper(struct policydb *p, void *fp)
+static int filename_trans_read_helper(struct policydb *p, struct policy_file *fp)
{
struct filename_trans_key *ft = NULL;
struct filename_trans_datum **dst, *datum, *first = NULL;
@@ -2087,7 +2092,7 @@ out:
return rc;
}
-static int filename_trans_read(struct policydb *p, void *fp)
+static int filename_trans_read(struct policydb *p, struct policy_file *fp)
{
u32 nel, i;
__le32 buf[1];
@@ -2128,7 +2133,7 @@ static int filename_trans_read(struct policydb *p, void *fp)
return 0;
}
-static int genfs_read(struct policydb *p, void *fp)
+static int genfs_read(struct policydb *p, struct policy_file *fp)
{
int rc;
u32 i, j, nel, nel2, len, len2;
@@ -2242,7 +2247,7 @@ out:
}
static int ocontext_read(struct policydb *p,
- const struct policydb_compat_info *info, void *fp)
+ const struct policydb_compat_info *info, struct policy_file *fp)
{
int rc;
unsigned int i;
@@ -2439,7 +2444,7 @@ out:
* Read the configuration data from a policy database binary
* representation file into a policy database structure.
*/
-int policydb_read(struct policydb *p, void *fp)
+int policydb_read(struct policydb *p, struct policy_file *fp)
{
struct role_allow *ra, *lra;
struct role_trans_key *rtk = NULL;
@@ -2475,24 +2480,18 @@ int policydb_read(struct policydb *p, void *fp)
goto bad;
}
- rc = -ENOMEM;
- policydb_str = kmalloc(len + 1, GFP_KERNEL);
- if (!policydb_str) {
- pr_err("SELinux: unable to allocate memory for policydb "
- "string of length %d\n",
- len);
- goto bad;
- }
-
- rc = next_entry(policydb_str, fp, len);
+ rc = str_read(&policydb_str, GFP_KERNEL, fp, len);
if (rc) {
- pr_err("SELinux: truncated policydb string identifier\n");
- kfree(policydb_str);
+ if (rc == -ENOMEM) {
+ pr_err("SELinux: unable to allocate memory for policydb string of length %d\n",
+ len);
+ } else {
+ pr_err("SELinux: truncated policydb string identifier\n");
+ }
goto bad;
}
rc = -EINVAL;
- policydb_str[len] = '\0';
if (strcmp(policydb_str, POLICYDB_STRING)) {
pr_err("SELinux: policydb string %s does not match "
"my string %s\n",
@@ -2546,6 +2545,12 @@ int policydb_read(struct policydb *p, void *fp)
goto bad;
}
+ if (p->policyvers >= POLICYDB_VERSION_NEVERAUDIT) {
+ rc = ebitmap_read(&p->neveraudit_map, fp);
+ if (rc)
+ goto bad;
+ }
+
rc = -EINVAL;
info = policydb_lookup_compat(p->policyvers);
if (!info) {
@@ -2762,7 +2767,7 @@ bad:
* Write a MLS level structure to a policydb binary
* representation file.
*/
-static int mls_write_level(struct mls_level *l, void *fp)
+static int mls_write_level(struct mls_level *l, struct policy_file *fp)
{
__le32 buf[1];
int rc;
@@ -2783,7 +2788,7 @@ static int mls_write_level(struct mls_level *l, void *fp)
* Write a MLS range structure to a policydb binary
* representation file.
*/
-static int mls_write_range_helper(struct mls_range *r, void *fp)
+static int mls_write_range_helper(struct mls_range *r, struct policy_file *fp)
{
__le32 buf[3];
size_t items;
@@ -2823,7 +2828,7 @@ static int sens_write(void *vkey, void *datum, void *ptr)
char *key = vkey;
struct level_datum *levdatum = datum;
struct policy_data *pd = ptr;
- void *fp = pd->fp;
+ struct policy_file *fp = pd->fp;
__le32 buf[2];
size_t len;
int rc;
@@ -2839,7 +2844,7 @@ static int sens_write(void *vkey, void *datum, void *ptr)
if (rc)
return rc;
- rc = mls_write_level(levdatum->level, fp);
+ rc = mls_write_level(&levdatum->level, fp);
if (rc)
return rc;
@@ -2851,7 +2856,7 @@ static int cat_write(void *vkey, void *datum, void *ptr)
char *key = vkey;
struct cat_datum *catdatum = datum;
struct policy_data *pd = ptr;
- void *fp = pd->fp;
+ struct policy_file *fp = pd->fp;
__le32 buf[3];
size_t len;
int rc;
@@ -2876,7 +2881,7 @@ static int role_trans_write_one(void *key, void *datum, void *ptr)
struct role_trans_key *rtk = key;
struct role_trans_datum *rtd = datum;
struct policy_data *pd = ptr;
- void *fp = pd->fp;
+ struct policy_file *fp = pd->fp;
struct policydb *p = pd->p;
__le32 buf[3];
int rc;
@@ -2896,7 +2901,7 @@ static int role_trans_write_one(void *key, void *datum, void *ptr)
return 0;
}
-static int role_trans_write(struct policydb *p, void *fp)
+static int role_trans_write(struct policydb *p, struct policy_file *fp)
{
struct policy_data pd = { .p = p, .fp = fp };
__le32 buf[1];
@@ -2910,7 +2915,7 @@ static int role_trans_write(struct policydb *p, void *fp)
return hashtab_map(&p->role_tr, role_trans_write_one, &pd);
}
-static int role_allow_write(struct role_allow *r, void *fp)
+static int role_allow_write(struct role_allow *r, struct policy_file *fp)
{
struct role_allow *ra;
__le32 buf[2];
@@ -2938,7 +2943,7 @@ static int role_allow_write(struct role_allow *r, void *fp)
* Write a security context structure
* to a policydb binary representation file.
*/
-static int context_write(struct policydb *p, struct context *c, void *fp)
+static int context_write(struct policydb *p, struct context *c, struct policy_file *fp)
{
int rc;
__le32 buf[3];
@@ -2991,7 +2996,7 @@ static int common_write(void *vkey, void *datum, void *ptr)
char *key = vkey;
struct common_datum *comdatum = datum;
struct policy_data *pd = ptr;
- void *fp = pd->fp;
+ struct policy_file *fp = pd->fp;
__le32 buf[4];
size_t len;
int rc;
@@ -3016,7 +3021,7 @@ static int common_write(void *vkey, void *datum, void *ptr)
return 0;
}
-static int type_set_write(struct type_set *t, void *fp)
+static int type_set_write(struct type_set *t, struct policy_file *fp)
{
int rc;
__le32 buf[1];
@@ -3035,7 +3040,7 @@ static int type_set_write(struct type_set *t, void *fp)
}
static int write_cons_helper(struct policydb *p, struct constraint_node *node,
- void *fp)
+ struct policy_file *fp)
{
struct constraint_node *c;
struct constraint_expr *e;
@@ -3086,7 +3091,7 @@ static int class_write(void *vkey, void *datum, void *ptr)
char *key = vkey;
struct class_datum *cladatum = datum;
struct policy_data *pd = ptr;
- void *fp = pd->fp;
+ struct policy_file *fp = pd->fp;
struct policydb *p = pd->p;
struct constraint_node *c;
__le32 buf[6];
@@ -3171,7 +3176,7 @@ static int role_write(void *vkey, void *datum, void *ptr)
char *key = vkey;
struct role_datum *role = datum;
struct policy_data *pd = ptr;
- void *fp = pd->fp;
+ struct policy_file *fp = pd->fp;
struct policydb *p = pd->p;
__le32 buf[3];
size_t items, len;
@@ -3211,7 +3216,7 @@ static int type_write(void *vkey, void *datum, void *ptr)
struct type_datum *typdatum = datum;
struct policy_data *pd = ptr;
struct policydb *p = pd->p;
- void *fp = pd->fp;
+ struct policy_file *fp = pd->fp;
__le32 buf[4];
int rc;
size_t items, len;
@@ -3252,7 +3257,7 @@ static int user_write(void *vkey, void *datum, void *ptr)
struct user_datum *usrdatum = datum;
struct policy_data *pd = ptr;
struct policydb *p = pd->p;
- void *fp = pd->fp;
+ struct policy_file *fp = pd->fp;
__le32 buf[3];
size_t items, len;
int rc;
@@ -3301,7 +3306,8 @@ static int (*const write_f[SYM_NUM])(void *key, void *datum, void *datap) = {
/* clang-format on */
static int ocontext_write(struct policydb *p,
- const struct policydb_compat_info *info, void *fp)
+ const struct policydb_compat_info *info,
+ struct policy_file *fp)
{
unsigned int i, j;
int rc;
@@ -3437,7 +3443,7 @@ static int ocontext_write(struct policydb *p,
return 0;
}
-static int genfs_write(struct policydb *p, void *fp)
+static int genfs_write(struct policydb *p, struct policy_file *fp)
{
struct genfs *genfs;
struct ocontext *c;
@@ -3495,7 +3501,7 @@ static int range_write_helper(void *key, void *data, void *ptr)
struct range_trans *rt = key;
struct mls_range *r = data;
struct policy_data *pd = ptr;
- void *fp = pd->fp;
+ struct policy_file *fp = pd->fp;
struct policydb *p = pd->p;
int rc;
@@ -3517,7 +3523,7 @@ static int range_write_helper(void *key, void *data, void *ptr)
return 0;
}
-static int range_write(struct policydb *p, void *fp)
+static int range_write(struct policydb *p, struct policy_file *fp)
{
__le32 buf[1];
int rc;
@@ -3544,7 +3550,7 @@ static int filename_write_helper_compat(void *key, void *data, void *ptr)
struct filename_trans_key *ft = key;
struct filename_trans_datum *datum = data;
struct ebitmap_node *node;
- void *fp = ptr;
+ struct policy_file *fp = ptr;
__le32 buf[4];
int rc;
u32 bit, len = strlen(ft->name);
@@ -3581,7 +3587,7 @@ static int filename_write_helper(void *key, void *data, void *ptr)
{
struct filename_trans_key *ft = key;
struct filename_trans_datum *datum;
- void *fp = ptr;
+ struct policy_file *fp = ptr;
__le32 buf[3];
int rc;
u32 ndatum, len = strlen(ft->name);
@@ -3626,7 +3632,7 @@ static int filename_write_helper(void *key, void *data, void *ptr)
return 0;
}
-static int filename_trans_write(struct policydb *p, void *fp)
+static int filename_trans_write(struct policydb *p, struct policy_file *fp)
{
__le32 buf[1];
int rc;
@@ -3658,7 +3664,7 @@ static int filename_trans_write(struct policydb *p, void *fp)
* structure to a policy database binary representation
* file.
*/
-int policydb_write(struct policydb *p, void *fp)
+int policydb_write(struct policydb *p, struct policy_file *fp)
{
unsigned int num_syms;
int rc;
@@ -3730,6 +3736,12 @@ int policydb_write(struct policydb *p, void *fp)
return rc;
}
+ if (p->policyvers >= POLICYDB_VERSION_NEVERAUDIT) {
+ rc = ebitmap_write(&p->neveraudit_map, fp);
+ if (rc)
+ return rc;
+ }
+
num_syms = info->sym_num;
for (i = 0; i < num_syms; i++) {
struct policy_data pd;
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h
index 4bba386264a3..89a180b1742f 100644
--- a/security/selinux/ss/policydb.h
+++ b/security/selinux/ss/policydb.h
@@ -126,7 +126,7 @@ struct user_datum {
/* Sensitivity attributes */
struct level_datum {
- struct mls_level *level; /* sensitivity and associated categories */
+ struct mls_level level; /* sensitivity and associated categories */
unsigned char isalias; /* is this sensitivity an alias for another? */
};
@@ -144,7 +144,7 @@ struct range_trans {
/* Boolean data type */
struct cond_bool_datum {
- __u32 value; /* internal type value */
+ u32 value; /* internal type value */
int state;
};
@@ -300,6 +300,8 @@ struct policydb {
struct ebitmap permissive_map;
+ struct ebitmap neveraudit_map;
+
/* length of this policy when it was loaded */
size_t len;
@@ -312,14 +314,19 @@ struct policydb {
u32 process_trans_perms;
} __randomize_layout;
+struct policy_file {
+ char *data;
+ size_t len;
+};
+
extern void policydb_destroy(struct policydb *p);
extern int policydb_load_isids(struct policydb *p, struct sidtab *s);
extern int policydb_context_isvalid(struct policydb *p, struct context *c);
extern int policydb_class_isvalid(struct policydb *p, unsigned int class);
extern int policydb_type_isvalid(struct policydb *p, unsigned int type);
extern int policydb_role_isvalid(struct policydb *p, unsigned int role);
-extern int policydb_read(struct policydb *p, void *fp);
-extern int policydb_write(struct policydb *p, void *fp);
+extern int policydb_read(struct policydb *p, struct policy_file *fp);
+extern int policydb_write(struct policydb *p, struct policy_file *fp);
extern struct filename_trans_datum *
policydb_filenametr_search(struct policydb *p, struct filename_trans_key *key);
@@ -342,14 +349,9 @@ policydb_roletr_search(struct policydb *p, struct role_trans_key *key);
#define POLICYDB_MAGIC SELINUX_MAGIC
#define POLICYDB_STRING "SE Linux"
-struct policy_file {
- char *data;
- size_t len;
-};
-
struct policy_data {
struct policydb *p;
- void *fp;
+ struct policy_file *fp;
};
static inline int next_entry(void *buf, struct policy_file *fp, size_t bytes)
@@ -386,6 +388,8 @@ static inline char *sym_name(struct policydb *p, unsigned int sym_num,
return p->sym_val_to_name[sym_num][element_nr];
}
+extern int str_read(char **strp, gfp_t flags, struct policy_file *fp, u32 len);
+
extern u16 string_to_security_class(struct policydb *p, const char *name);
extern u32 string_to_av_perm(struct policydb *p, u16 tclass, const char *name);
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 971c45d576ba..713130bd43c4 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -46,6 +46,7 @@
#include <linux/in.h>
#include <linux/sched.h>
#include <linux/audit.h>
+#include <linux/parser.h>
#include <linux/vmalloc.h>
#include <linux/lsm_hooks.h>
#include <net/netlabel.h>
@@ -582,7 +583,7 @@ static void type_attribute_bounds_av(struct policydb *policydb,
}
/*
- * Flag which drivers have permissions.
+ * Flag which drivers have permissions and which base permissions are covered.
*/
void services_compute_xperms_drivers(
struct extended_perms *xperms,
@@ -592,12 +593,19 @@ void services_compute_xperms_drivers(
switch (node->datum.u.xperms->specified) {
case AVTAB_XPERMS_IOCTLDRIVER:
+ xperms->base_perms |= AVC_EXT_IOCTL;
/* if one or more driver has all permissions allowed */
for (i = 0; i < ARRAY_SIZE(xperms->drivers.p); i++)
xperms->drivers.p[i] |= node->datum.u.xperms->perms.p[i];
break;
case AVTAB_XPERMS_IOCTLFUNCTION:
+ xperms->base_perms |= AVC_EXT_IOCTL;
+ /* if allowing permissions within a driver */
+ security_xperm_set(xperms->drivers.p,
+ node->datum.u.xperms->driver);
+ break;
case AVTAB_XPERMS_NLMSG:
+ xperms->base_perms |= AVC_EXT_NLMSG;
/* if allowing permissions within a driver */
security_xperm_set(xperms->drivers.p,
node->datum.u.xperms->driver);
@@ -631,8 +639,7 @@ static void context_struct_compute_av(struct policydb *policydb,
avd->auditallow = 0;
avd->auditdeny = 0xffffffff;
if (xperms) {
- memset(&xperms->drivers, 0, sizeof(xperms->drivers));
- xperms->len = 0;
+ memset(xperms, 0, sizeof(*xperms));
}
if (unlikely(!tclass || tclass > policydb->p_classes.nprim)) {
@@ -946,7 +953,7 @@ static void avd_init(struct selinux_policy *policy, struct av_decision *avd)
}
static void update_xperms_extended_data(u8 specified,
- struct extended_perms_data *from,
+ const struct extended_perms_data *from,
struct extended_perms_data *xp_data)
{
unsigned int i;
@@ -967,38 +974,52 @@ static void update_xperms_extended_data(u8 specified,
void services_compute_xperms_decision(struct extended_perms_decision *xpermd,
struct avtab_node *node)
{
+ u16 specified;
+
switch (node->datum.u.xperms->specified) {
case AVTAB_XPERMS_IOCTLFUNCTION:
- case AVTAB_XPERMS_NLMSG:
- if (xpermd->driver != node->datum.u.xperms->driver)
+ if (xpermd->base_perm != AVC_EXT_IOCTL ||
+ xpermd->driver != node->datum.u.xperms->driver)
return;
break;
case AVTAB_XPERMS_IOCTLDRIVER:
- if (!security_xperm_test(node->datum.u.xperms->perms.p,
- xpermd->driver))
+ if (xpermd->base_perm != AVC_EXT_IOCTL ||
+ !security_xperm_test(node->datum.u.xperms->perms.p,
+ xpermd->driver))
+ return;
+ break;
+ case AVTAB_XPERMS_NLMSG:
+ if (xpermd->base_perm != AVC_EXT_NLMSG ||
+ xpermd->driver != node->datum.u.xperms->driver)
return;
break;
default:
- BUG();
+ pr_warn_once(
+ "SELinux: unknown extended permission (%u) will be ignored\n",
+ node->datum.u.xperms->specified);
+ return;
}
- if (node->key.specified == AVTAB_XPERMS_ALLOWED) {
+ specified = node->key.specified & ~(AVTAB_ENABLED | AVTAB_ENABLED_OLD);
+
+ if (specified == AVTAB_XPERMS_ALLOWED) {
xpermd->used |= XPERMS_ALLOWED;
update_xperms_extended_data(node->datum.u.xperms->specified,
&node->datum.u.xperms->perms,
xpermd->allowed);
- } else if (node->key.specified == AVTAB_XPERMS_AUDITALLOW) {
+ } else if (specified == AVTAB_XPERMS_AUDITALLOW) {
xpermd->used |= XPERMS_AUDITALLOW;
update_xperms_extended_data(node->datum.u.xperms->specified,
&node->datum.u.xperms->perms,
xpermd->auditallow);
- } else if (node->key.specified == AVTAB_XPERMS_DONTAUDIT) {
+ } else if (specified == AVTAB_XPERMS_DONTAUDIT) {
xpermd->used |= XPERMS_DONTAUDIT;
update_xperms_extended_data(node->datum.u.xperms->specified,
&node->datum.u.xperms->perms,
xpermd->dontaudit);
} else {
- BUG();
+ pr_warn_once("SELinux: unknown specified key (%u)\n",
+ node->key.specified);
}
}
@@ -1006,6 +1027,7 @@ void security_compute_xperms_decision(u32 ssid,
u32 tsid,
u16 orig_tclass,
u8 driver,
+ u8 base_perm,
struct extended_perms_decision *xpermd)
{
struct selinux_policy *policy;
@@ -1019,6 +1041,7 @@ void security_compute_xperms_decision(u32 ssid,
struct ebitmap_node *snode, *tnode;
unsigned int i, j;
+ xpermd->base_perm = base_perm;
xpermd->driver = driver;
xpermd->used = 0;
memset(xpermd->allowed->p, 0, sizeof(xpermd->allowed->p));
@@ -1130,6 +1153,14 @@ void security_compute_av(u32 ssid,
if (ebitmap_get_bit(&policydb->permissive_map, scontext->type))
avd->flags |= AVD_FLAGS_PERMISSIVE;
+ /* neveraudit domain? */
+ if (ebitmap_get_bit(&policydb->neveraudit_map, scontext->type))
+ avd->flags |= AVD_FLAGS_NEVERAUDIT;
+
+ /* both permissive and neveraudit => allow */
+ if (avd->flags == (AVD_FLAGS_PERMISSIVE|AVD_FLAGS_NEVERAUDIT))
+ goto allow;
+
tcontext = sidtab_search(sidtab, tsid);
if (!tcontext) {
pr_err("SELinux: %s: unrecognized SID %d\n",
@@ -1149,6 +1180,8 @@ void security_compute_av(u32 ssid,
policydb->allow_unknown);
out:
rcu_read_unlock();
+ if (avd->flags & AVD_FLAGS_NEVERAUDIT)
+ avd->auditallow = avd->auditdeny = 0;
return;
allow:
avd->allowed = 0xffffffff;
@@ -1185,6 +1218,14 @@ void security_compute_av_user(u32 ssid,
if (ebitmap_get_bit(&policydb->permissive_map, scontext->type))
avd->flags |= AVD_FLAGS_PERMISSIVE;
+ /* neveraudit domain? */
+ if (ebitmap_get_bit(&policydb->neveraudit_map, scontext->type))
+ avd->flags |= AVD_FLAGS_NEVERAUDIT;
+
+ /* both permissive and neveraudit => allow */
+ if (avd->flags == (AVD_FLAGS_PERMISSIVE|AVD_FLAGS_NEVERAUDIT))
+ goto allow;
+
tcontext = sidtab_search(sidtab, tsid);
if (!tcontext) {
pr_err("SELinux: %s: unrecognized SID %d\n",
@@ -1202,6 +1243,8 @@ void security_compute_av_user(u32 ssid,
NULL);
out:
rcu_read_unlock();
+ if (avd->flags & AVD_FLAGS_NEVERAUDIT)
+ avd->auditallow = avd->auditdeny = 0;
return;
allow:
avd->allowed = 0xffffffff;
@@ -1886,11 +1929,17 @@ retry:
goto out_unlock;
}
/* Obtain the sid for the context. */
- rc = sidtab_context_to_sid(sidtab, &newcontext, out_sid);
- if (rc == -ESTALE) {
- rcu_read_unlock();
- context_destroy(&newcontext);
- goto retry;
+ if (context_equal(scontext, &newcontext))
+ *out_sid = ssid;
+ else if (context_equal(tcontext, &newcontext))
+ *out_sid = tsid;
+ else {
+ rc = sidtab_context_to_sid(sidtab, &newcontext, out_sid);
+ if (rc == -ESTALE) {
+ rcu_read_unlock();
+ context_destroy(&newcontext);
+ goto retry;
+ }
}
out_unlock:
rcu_read_unlock();
@@ -2550,13 +2599,14 @@ out:
* @name: interface name
* @if_sid: interface SID
*/
-int security_netif_sid(char *name, u32 *if_sid)
+int security_netif_sid(const char *name, u32 *if_sid)
{
struct selinux_policy *policy;
struct policydb *policydb;
struct sidtab *sidtab;
int rc;
struct ocontext *c;
+ bool wildcard_support;
if (!selinux_initialized()) {
*if_sid = SECINITSID_NETIF;
@@ -2569,11 +2619,18 @@ retry:
policy = rcu_dereference(selinux_state.policy);
policydb = &policy->policydb;
sidtab = policy->sidtab;
+ wildcard_support = ebitmap_get_bit(&policydb->policycaps, POLICYDB_CAP_NETIF_WILDCARD);
c = policydb->ocontexts[OCON_NETIF];
while (c) {
- if (strcmp(name, c->u.name) == 0)
- break;
+ if (wildcard_support) {
+ if (match_wildcard(c->u.name, name))
+ break;
+ } else {
+ if (strcmp(c->u.name, name) == 0)
+ break;
+ }
+
c = c->next;
}
@@ -2593,17 +2650,15 @@ out:
return rc;
}
-static int match_ipv6_addrmask(u32 *input, u32 *addr, u32 *mask)
+static bool match_ipv6_addrmask(const u32 input[4], const u32 addr[4], const u32 mask[4])
{
- int i, fail = 0;
+ int i;
for (i = 0; i < 4; i++)
- if (addr[i] != (input[i] & mask[i])) {
- fail = 1;
- break;
- }
+ if (addr[i] != (input[i] & mask[i]))
+ return false;
- return !fail;
+ return true;
}
/**
@@ -2614,7 +2669,7 @@ static int match_ipv6_addrmask(u32 *input, u32 *addr, u32 *mask)
* @out_sid: security identifier
*/
int security_node_sid(u16 domain,
- void *addrp,
+ const void *addrp,
u32 addrlen,
u32 *out_sid)
{
@@ -2643,7 +2698,7 @@ retry:
if (addrlen != sizeof(u32))
goto out;
- addr = *((u32 *)addrp);
+ addr = *((const u32 *)addrp);
c = policydb->ocontexts[OCON_NODE];
while (c) {
@@ -2708,7 +2763,7 @@ out:
*/
int security_get_user_sids(u32 fromsid,
- char *username,
+ const char *username,
u32 **sids,
u32 *nel)
{
@@ -2843,6 +2898,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++;
@@ -2859,11 +2915,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)
@@ -3030,7 +3095,7 @@ err:
}
-int security_set_bools(u32 len, int *values)
+int security_set_bools(u32 len, const int *values)
{
struct selinux_state *state = &selinux_state;
struct selinux_policy *newpolicy, *oldpolicy;
@@ -3329,7 +3394,7 @@ int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type,
__func__, xfrm_sid);
goto out;
}
- rc = (mls_context_cmp(nlbl_ctx, xfrm_ctx) ? 0 : -EACCES);
+ rc = (mls_context_equal(nlbl_ctx, xfrm_ctx) ? 0 : -EACCES);
if (rc)
goto out;
diff --git a/security/selinux/ss/sidtab.c b/security/selinux/ss/sidtab.c
index c8848cbba81f..59f8c09158ef 100644
--- a/security/selinux/ss/sidtab.c
+++ b/security/selinux/ss/sidtab.c
@@ -66,7 +66,7 @@ static u32 context_to_sid(struct sidtab *s, struct context *context, u32 hash)
hash_for_each_possible_rcu(s->context_to_sid, entry, list, hash) {
if (entry->hash != hash)
continue;
- if (context_cmp(&entry->context, context)) {
+ if (context_equal(&entry->context, context)) {
sid = entry->sid;
break;
}
@@ -114,12 +114,12 @@ int sidtab_set_initial(struct sidtab *s, u32 sid, struct context *context)
int sidtab_hash_stats(struct sidtab *sidtab, char *page)
{
- int i;
+ unsigned int i;
int chain_len = 0;
int slots_used = 0;
int entries = 0;
int max_chain_len = 0;
- int cur_bucket = 0;
+ unsigned int cur_bucket = 0;
struct sidtab_entry *entry;
rcu_read_lock();
diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c
index 90ec4ef1b082..61d56b0c2be1 100644
--- a/security/selinux/xfrm.c
+++ b/security/selinux/xfrm.c
@@ -94,7 +94,7 @@ static int selinux_xfrm_alloc_user(struct xfrm_sec_ctx **ctxp,
ctx->ctx_doi = XFRM_SC_DOI_LSM;
ctx->ctx_alg = XFRM_SC_ALG_SELINUX;
- ctx->ctx_len = str_len;
+ ctx->ctx_len = str_len + 1;
memcpy(ctx->ctx_str, &uctx[1], str_len);
ctx->ctx_str[str_len] = '\0';
rc = security_context_to_sid(ctx->ctx_str, str_len,