diff options
Diffstat (limited to 'security/selinux')
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, |