diff options
Diffstat (limited to 'security')
53 files changed, 528 insertions, 284 deletions
diff --git a/security/Kconfig b/security/Kconfig index 97abeb9b9a19..52c9af08ad35 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -127,16 +127,8 @@ config LSM_MMAP_MIN_ADDR this low address space will need the permission specific to the systems running LSM. -config HAVE_HARDENED_USERCOPY_ALLOCATOR - bool - help - The heap allocator implements __check_heap_object() for - validating memory ranges against heap object sizes in - support of CONFIG_HARDENED_USERCOPY. - config HARDENED_USERCOPY bool "Harden memory copies between kernel and userspace" - depends on HAVE_HARDENED_USERCOPY_ALLOCATOR imply STRICT_DEVMEM help This option checks for obviously wrong memory regions when diff --git a/security/apparmor/crypto.c b/security/apparmor/crypto.c index b498ed302461..6724e2ff6da8 100644 --- a/security/apparmor/crypto.c +++ b/security/apparmor/crypto.c @@ -28,15 +28,15 @@ unsigned int aa_hash_size(void) char *aa_calc_hash(void *data, size_t len) { SHASH_DESC_ON_STACK(desc, apparmor_tfm); - char *hash = NULL; - int error = -ENOMEM; + char *hash; + int error; if (!apparmor_tfm) return NULL; hash = kzalloc(apparmor_hash_size, GFP_KERNEL); if (!hash) - goto fail; + return ERR_PTR(-ENOMEM); desc->tfm = apparmor_tfm; @@ -62,7 +62,7 @@ int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, size_t len) { SHASH_DESC_ON_STACK(desc, apparmor_tfm); - int error = -ENOMEM; + int error; __le32 le32_version = cpu_to_le32(version); if (!aa_g_hash_policy) @@ -73,7 +73,7 @@ int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, profile->hash = kzalloc(apparmor_hash_size, GFP_KERNEL); if (!profile->hash) - goto fail; + return -ENOMEM; desc->tfm = apparmor_tfm; diff --git a/security/apparmor/file.c b/security/apparmor/file.c index 9119ddda6217..698b124e649f 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -161,6 +161,7 @@ static int path_name(const char *op, struct aa_label *label, return 0; } +struct aa_perms default_perms = {}; /** * aa_lookup_fperms - convert dfa compressed perms to internal perms * @dfa: dfa to lookup perms for (NOT NULL) @@ -171,7 +172,6 @@ static int path_name(const char *op, struct aa_label *label, * * Returns: a pointer to a file permission set */ -struct aa_perms default_perms = {}; struct aa_perms *aa_lookup_fperms(struct aa_policydb *file_rules, aa_state_t state, struct path_cond *cond) { diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h index f1a29ab7ea1b..73c8a32c6861 100644 --- a/security/apparmor/include/lib.h +++ b/security/apparmor/include/lib.h @@ -232,7 +232,7 @@ void aa_policy_destroy(struct aa_policy *policy); */ #define fn_label_build(L, P, GFP, FN) \ ({ \ - __label__ __cleanup, __done; \ + __label__ __do_cleanup, __done; \ struct aa_label *__new_; \ \ if ((L)->size > 1) { \ @@ -250,7 +250,7 @@ void aa_policy_destroy(struct aa_policy *policy); __new_ = (FN); \ AA_BUG(!__new_); \ if (IS_ERR(__new_)) \ - goto __cleanup; \ + goto __do_cleanup; \ __lvec[__j++] = __new_; \ } \ for (__j = __count = 0; __j < (L)->size; __j++) \ @@ -272,7 +272,7 @@ void aa_policy_destroy(struct aa_policy *policy); vec_cleanup(profile, __pvec, __count); \ } else \ __new_ = NULL; \ -__cleanup: \ +__do_cleanup: \ vec_cleanup(label, __lvec, (L)->size); \ } else { \ (P) = labels_profile(L); \ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index f431251ffb91..c9463bd0307d 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -46,7 +46,7 @@ int apparmor_initialized; union aa_buffer { struct list_head list; - char buffer[1]; + DECLARE_FLEX_ARRAY(char, buffer); }; #define RESERVE_COUNT 2 @@ -1647,7 +1647,7 @@ retry: list_del(&aa_buf->list); buffer_count--; spin_unlock(&aa_buffers_lock); - return &aa_buf->buffer[0]; + return aa_buf->buffer; } if (in_atomic) { /* @@ -1670,7 +1670,7 @@ retry: pr_warn_once("AppArmor: Failed to allocate a memory buffer.\n"); return NULL; } - return &aa_buf->buffer[0]; + return aa_buf->buffer; } void aa_put_buffer(char *buf) @@ -1747,7 +1747,7 @@ static int __init alloc_buffers(void) destroy_buffers(); return -ENOMEM; } - aa_put_buffer(&aa_buf->buffer[0]); + aa_put_buffer(aa_buf->buffer); } return 0; } diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 51e8184e0fec..b38f7b2a5e1d 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -430,11 +430,9 @@ static struct aa_policy *__lookup_parent(struct aa_ns *ns, * @hname: hierarchical profile name to find parent of (NOT NULL) * @gfp: type of allocation. * - * Returns: NULL on error, parent profile on success - * * Requires: ns mutex lock held * - * Returns: unrefcounted parent policy or NULL if error creating + * Return: unrefcounted parent policy on success or %NULL if error creating * place holder profiles. */ static struct aa_policy *__create_missing_ancestors(struct aa_ns *ns, @@ -591,7 +589,15 @@ struct aa_profile *aa_alloc_null(struct aa_profile *parent, const char *name, profile->label.flags |= FLAG_NULL; rules = list_first_entry(&profile->rules, typeof(*rules), list); rules->file.dfa = aa_get_dfa(nulldfa); + rules->file.perms = kcalloc(2, sizeof(struct aa_perms), GFP_KERNEL); + if (!rules->file.perms) + goto fail; + rules->file.size = 2; rules->policy.dfa = aa_get_dfa(nulldfa); + rules->policy.perms = kcalloc(2, sizeof(struct aa_perms), GFP_KERNEL); + if (!rules->policy.perms) + goto fail; + rules->policy.size = 2; if (parent) { profile->path_flags = parent->path_flags; @@ -602,6 +608,11 @@ struct aa_profile *aa_alloc_null(struct aa_profile *parent, const char *name, } return profile; + +fail: + aa_free_profile(profile); + + return NULL; } /** @@ -828,7 +839,7 @@ bool aa_current_policy_admin_capable(struct aa_ns *ns) /** * aa_may_manage_policy - can the current task manage policy * @label: label to check if it can manage policy - * @op: the policy manipulation operation being done + * @mask: contains the policy manipulation operation being done * * Returns: 0 if the task is allowed to manipulate policy else error */ @@ -883,7 +894,6 @@ static struct aa_profile *__list_lookup_parent(struct list_head *lh, * __replace_profile - replace @old with @new on a list * @old: profile to be replaced (NOT NULL) * @new: profile to replace @old with (NOT NULL) - * @share_proxy: transfer @old->proxy to @new * * Will duplicate and refcount elements that @new inherits from @old * and will inherit @old children. diff --git a/security/apparmor/policy_compat.c b/security/apparmor/policy_compat.c index cc89d1e88fb7..0cb02da8a319 100644 --- a/security/apparmor/policy_compat.c +++ b/security/apparmor/policy_compat.c @@ -146,7 +146,8 @@ static struct aa_perms compute_fperms_other(struct aa_dfa *dfa, * * Returns: remapped perm table */ -static struct aa_perms *compute_fperms(struct aa_dfa *dfa) +static struct aa_perms *compute_fperms(struct aa_dfa *dfa, + u32 *size) { aa_state_t state; unsigned int state_count; @@ -159,6 +160,7 @@ static struct aa_perms *compute_fperms(struct aa_dfa *dfa) table = kvcalloc(state_count * 2, sizeof(struct aa_perms), GFP_KERNEL); if (!table) return NULL; + *size = state_count * 2; for (state = 0; state < state_count; state++) { table[state * 2] = compute_fperms_user(dfa, state); @@ -168,7 +170,8 @@ static struct aa_perms *compute_fperms(struct aa_dfa *dfa) return table; } -static struct aa_perms *compute_xmatch_perms(struct aa_dfa *xmatch) +static struct aa_perms *compute_xmatch_perms(struct aa_dfa *xmatch, + u32 *size) { struct aa_perms *perms; int state; @@ -179,6 +182,9 @@ static struct aa_perms *compute_xmatch_perms(struct aa_dfa *xmatch) state_count = xmatch->tables[YYTD_ID_BASE]->td_lolen; /* DFAs are restricted from having a state_count of less than 2 */ perms = kvcalloc(state_count, sizeof(struct aa_perms), GFP_KERNEL); + if (!perms) + return NULL; + *size = state_count; /* zero init so skip the trap state (state == 0) */ for (state = 1; state < state_count; state++) @@ -239,7 +245,8 @@ static struct aa_perms compute_perms_entry(struct aa_dfa *dfa, return perms; } -static struct aa_perms *compute_perms(struct aa_dfa *dfa, u32 version) +static struct aa_perms *compute_perms(struct aa_dfa *dfa, u32 version, + u32 *size) { unsigned int state; unsigned int state_count; @@ -252,6 +259,7 @@ static struct aa_perms *compute_perms(struct aa_dfa *dfa, u32 version) table = kvcalloc(state_count, sizeof(struct aa_perms), GFP_KERNEL); if (!table) return NULL; + *size = state_count; /* zero init so skip the trap state (state == 0) */ for (state = 1; state < state_count; state++) @@ -286,7 +294,7 @@ static void remap_dfa_accept(struct aa_dfa *dfa, unsigned int factor) /* TODO: merge different dfa mappings into single map_policy fn */ int aa_compat_map_xmatch(struct aa_policydb *policy) { - policy->perms = compute_xmatch_perms(policy->dfa); + policy->perms = compute_xmatch_perms(policy->dfa, &policy->size); if (!policy->perms) return -ENOMEM; @@ -297,7 +305,7 @@ int aa_compat_map_xmatch(struct aa_policydb *policy) int aa_compat_map_policy(struct aa_policydb *policy, u32 version) { - policy->perms = compute_perms(policy->dfa, version); + policy->perms = compute_perms(policy->dfa, version, &policy->size); if (!policy->perms) return -ENOMEM; @@ -308,7 +316,7 @@ int aa_compat_map_policy(struct aa_policydb *policy, u32 version) int aa_compat_map_file(struct aa_policydb *policy) { - policy->perms = compute_fperms(policy->dfa); + policy->perms = compute_fperms(policy->dfa, &policy->size); if (!policy->perms) return -ENOMEM; diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index cf2ceec40b28..694fb7a09962 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -448,7 +448,7 @@ static struct aa_dfa *unpack_dfa(struct aa_ext *e, int flags) /** * unpack_trans_table - unpack a profile transition table * @e: serialized data extent information (NOT NULL) - * @table: str table to unpack to (NOT NULL) + * @strs: str table to unpack to (NOT NULL) * * Returns: true if table successfully unpacked or not present */ @@ -860,10 +860,12 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) } profile->attach.xmatch_len = tmp; profile->attach.xmatch.start[AA_CLASS_XMATCH] = DFA_START; - error = aa_compat_map_xmatch(&profile->attach.xmatch); - if (error) { - info = "failed to convert xmatch permission table"; - goto fail; + if (!profile->attach.xmatch.perms) { + error = aa_compat_map_xmatch(&profile->attach.xmatch); + if (error) { + info = "failed to convert xmatch permission table"; + goto fail; + } } } @@ -983,31 +985,54 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) AA_CLASS_FILE); if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL)) goto fail; - error = aa_compat_map_policy(&rules->policy, e->version); - if (error) { - info = "failed to remap policydb permission table"; - goto fail; + if (!rules->policy.perms) { + error = aa_compat_map_policy(&rules->policy, + e->version); + if (error) { + info = "failed to remap policydb permission table"; + goto fail; + } } - } else + } else { rules->policy.dfa = aa_get_dfa(nulldfa); - + rules->policy.perms = kcalloc(2, sizeof(struct aa_perms), + GFP_KERNEL); + if (!rules->policy.perms) + goto fail; + rules->policy.size = 2; + } /* get file rules */ error = unpack_pdb(e, &rules->file, false, true, &info); if (error) { goto fail; } else if (rules->file.dfa) { - error = aa_compat_map_file(&rules->file); - if (error) { - info = "failed to remap file permission table"; - goto fail; + if (!rules->file.perms) { + error = aa_compat_map_file(&rules->file); + if (error) { + info = "failed to remap file permission table"; + goto fail; + } } } else if (rules->policy.dfa && rules->policy.start[AA_CLASS_FILE]) { rules->file.dfa = aa_get_dfa(rules->policy.dfa); rules->file.start[AA_CLASS_FILE] = rules->policy.start[AA_CLASS_FILE]; - } else + rules->file.perms = kcalloc(rules->policy.size, + sizeof(struct aa_perms), + GFP_KERNEL); + if (!rules->file.perms) + goto fail; + memcpy(rules->file.perms, rules->policy.perms, + rules->policy.size * sizeof(struct aa_perms)); + rules->file.size = rules->policy.size; + } else { rules->file.dfa = aa_get_dfa(nulldfa); - + rules->file.perms = kcalloc(2, sizeof(struct aa_perms), + GFP_KERNEL); + if (!rules->file.perms) + goto fail; + rules->file.size = 2; + } error = -EPROTO; if (aa_unpack_nameX(e, AA_STRUCT, "data")) { info = "out of memory"; @@ -1046,8 +1071,13 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) goto fail; } - rhashtable_insert_fast(profile->data, &data->head, - profile->data->p); + if (rhashtable_insert_fast(profile->data, &data->head, + profile->data->p)) { + kfree_sensitive(data->key); + kfree_sensitive(data); + info = "failed to insert data to table"; + goto fail; + } } if (!aa_unpack_nameX(e, AA_STRUCTEND, NULL)) { @@ -1134,22 +1164,16 @@ static int verify_header(struct aa_ext *e, int required, const char **ns) return 0; } -static bool verify_xindex(int xindex, int table_size) -{ - int index, xtype; - xtype = xindex & AA_X_TYPE_MASK; - index = xindex & AA_X_INDEX_MASK; - if (xtype == AA_X_TABLE && index >= table_size) - return false; - return true; -} - -/* verify dfa xindexes are in range of transition tables */ -static bool verify_dfa_xindex(struct aa_dfa *dfa, int table_size) +/** + * verify_dfa_accept_index - verify accept indexes are in range of perms table + * @dfa: the dfa to check accept indexes are in range + * table_size: the permission table size the indexes should be within + */ +static bool verify_dfa_accept_index(struct aa_dfa *dfa, int table_size) { int i; for (i = 0; i < dfa->tables[YYTD_ID_ACCEPT]->td_lolen; i++) { - if (!verify_xindex(ACCEPT_TABLE(dfa)[i], table_size)) + if (ACCEPT_TABLE(dfa)[i] >= table_size) return false; } return true; @@ -1186,11 +1210,13 @@ static bool verify_perms(struct aa_policydb *pdb) if (!verify_perm(&pdb->perms[i])) return false; /* verify indexes into str table */ - if (pdb->perms[i].xindex >= pdb->trans.size) + if ((pdb->perms[i].xindex & AA_X_TYPE_MASK) == AA_X_TABLE && + (pdb->perms[i].xindex & AA_X_INDEX_MASK) >= pdb->trans.size) return false; - if (pdb->perms[i].tag >= pdb->trans.size) + if (pdb->perms[i].tag && pdb->perms[i].tag >= pdb->trans.size) return false; - if (pdb->perms[i].label >= pdb->trans.size) + if (pdb->perms[i].label && + pdb->perms[i].label >= pdb->trans.size) return false; } @@ -1212,10 +1238,10 @@ static int verify_profile(struct aa_profile *profile) if (!rules) return 0; - if ((rules->file.dfa && !verify_dfa_xindex(rules->file.dfa, - rules->file.trans.size)) || + if ((rules->file.dfa && !verify_dfa_accept_index(rules->file.dfa, + rules->file.size)) || (rules->policy.dfa && - !verify_dfa_xindex(rules->policy.dfa, rules->policy.trans.size))) { + !verify_dfa_accept_index(rules->policy.dfa, rules->policy.size))) { audit_iface(profile, NULL, NULL, "Unpack: Invalid named transition", NULL, -EPROTO); return -EPROTO; diff --git a/security/apparmor/policy_unpack_test.c b/security/apparmor/policy_unpack_test.c index e1bfdab524b7..5c9bde25e56d 100644 --- a/security/apparmor/policy_unpack_test.c +++ b/security/apparmor/policy_unpack_test.c @@ -69,31 +69,30 @@ static struct aa_ext *build_aa_ext_struct(struct policy_unpack_fixture *puf, *buf = AA_NAME; *(buf + 1) = strlen(TEST_STRING_NAME) + 1; - strcpy(buf + 3, TEST_STRING_NAME); + strscpy(buf + 3, TEST_STRING_NAME, e->end - (void *)(buf + 3)); buf = e->start + TEST_STRING_BUF_OFFSET; *buf = AA_STRING; *(buf + 1) = strlen(TEST_STRING_DATA) + 1; - strcpy(buf + 3, TEST_STRING_DATA); - + strscpy(buf + 3, TEST_STRING_DATA, e->end - (void *)(buf + 3)); buf = e->start + TEST_NAMED_U32_BUF_OFFSET; *buf = AA_NAME; *(buf + 1) = strlen(TEST_U32_NAME) + 1; - strcpy(buf + 3, TEST_U32_NAME); + strscpy(buf + 3, TEST_U32_NAME, e->end - (void *)(buf + 3)); *(buf + 3 + strlen(TEST_U32_NAME) + 1) = AA_U32; *((u32 *)(buf + 3 + strlen(TEST_U32_NAME) + 2)) = TEST_U32_DATA; buf = e->start + TEST_NAMED_U64_BUF_OFFSET; *buf = AA_NAME; *(buf + 1) = strlen(TEST_U64_NAME) + 1; - strcpy(buf + 3, TEST_U64_NAME); + strscpy(buf + 3, TEST_U64_NAME, e->end - (void *)(buf + 3)); *(buf + 3 + strlen(TEST_U64_NAME) + 1) = AA_U64; *((u64 *)(buf + 3 + strlen(TEST_U64_NAME) + 2)) = TEST_U64_DATA; buf = e->start + TEST_NAMED_BLOB_BUF_OFFSET; *buf = AA_NAME; *(buf + 1) = strlen(TEST_BLOB_NAME) + 1; - strcpy(buf + 3, TEST_BLOB_NAME); + strscpy(buf + 3, TEST_BLOB_NAME, e->end - (void *)(buf + 3)); *(buf + 3 + strlen(TEST_BLOB_NAME) + 1) = AA_BLOB; *(buf + 3 + strlen(TEST_BLOB_NAME) + 2) = TEST_BLOB_DATA_SIZE; memcpy(buf + 3 + strlen(TEST_BLOB_NAME) + 6, @@ -102,7 +101,7 @@ static struct aa_ext *build_aa_ext_struct(struct policy_unpack_fixture *puf, buf = e->start + TEST_NAMED_ARRAY_BUF_OFFSET; *buf = AA_NAME; *(buf + 1) = strlen(TEST_ARRAY_NAME) + 1; - strcpy(buf + 3, TEST_ARRAY_NAME); + strscpy(buf + 3, TEST_ARRAY_NAME, e->end - (void *)(buf + 3)); *(buf + 3 + strlen(TEST_ARRAY_NAME) + 1) = AA_ARRAY; *((u16 *)(buf + 3 + strlen(TEST_ARRAY_NAME) + 2)) = TEST_ARRAY_SIZE; diff --git a/security/apparmor/secid.c b/security/apparmor/secid.c index 24a0e23f1b2b..83d3d1e6d9dc 100644 --- a/security/apparmor/secid.c +++ b/security/apparmor/secid.c @@ -53,8 +53,7 @@ void aa_secid_update(u32 secid, struct aa_label *label) xa_unlock_irqrestore(&aa_secids, flags); } -/** - * +/* * see label for inverse aa_label_to_secid */ struct aa_label *aa_secid_to_label(u32 secid) diff --git a/security/commoncap.c b/security/commoncap.c index 0b3fc2f3afe7..ab5742ab4362 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -314,7 +314,7 @@ int cap_inode_need_killpriv(struct dentry *dentry) * the vfsmount must be passed through @idmap. This function will then * take care to map the inode according to @idmap before checking * permissions. On non-idmapped mounts or if permission checking is to be - * performed on the raw inode simply passs @nop_mnt_idmap. + * performed on the raw inode simply pass @nop_mnt_idmap. * * Return: 0 if successful, -ve on error. */ @@ -522,7 +522,7 @@ static bool validheader(size_t size, const struct vfs_cap_data *cap) * the vfsmount must be passed through @idmap. This function will then * take care to map the inode according to @idmap before checking * permissions. On non-idmapped mounts or if permission checking is to be - * performed on the raw inode simply passs @nop_mnt_idmap. + * performed on the raw inode simply pass @nop_mnt_idmap. * * Return: On success, return the new size; on error, return < 0. */ @@ -630,7 +630,7 @@ static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps, * the vfsmount must be passed through @idmap. This function will then * take care to map the inode according to @idmap before checking * permissions. On non-idmapped mounts or if permission checking is to be - * performed on the raw inode simply passs @nop_mnt_idmap. + * performed on the raw inode simply pass @nop_mnt_idmap. */ int get_vfs_caps_from_disk(struct mnt_idmap *idmap, const struct dentry *dentry, @@ -1133,7 +1133,7 @@ int cap_task_fix_setuid(struct cred *new, const struct cred *old, int flags) break; case LSM_SETID_FS: - /* juggle the capabilties to follow FSUID changes, unless + /* juggle the capabilities to follow FSUID changes, unless * otherwise suppressed * * FIXME - is fsuser used for all CAP_FS_MASK capabilities? @@ -1184,10 +1184,10 @@ static int cap_safe_nice(struct task_struct *p) } /** - * cap_task_setscheduler - Detemine if scheduler policy change is permitted + * cap_task_setscheduler - Determine if scheduler policy change is permitted * @p: The task to affect * - * Detemine if the requested scheduler policy change is permitted for the + * Determine if the requested scheduler policy change is permitted for the * specified task. * * Return: 0 if permission is granted, -ve if denied. @@ -1198,11 +1198,11 @@ int cap_task_setscheduler(struct task_struct *p) } /** - * cap_task_setioprio - Detemine if I/O priority change is permitted + * cap_task_setioprio - Determine if I/O priority change is permitted * @p: The task to affect * @ioprio: The I/O priority to set * - * Detemine if the requested I/O priority change is permitted for the specified + * Determine if the requested I/O priority change is permitted for the specified * task. * * Return: 0 if permission is granted, -ve if denied. @@ -1213,11 +1213,11 @@ int cap_task_setioprio(struct task_struct *p, int ioprio) } /** - * cap_task_setnice - Detemine if task priority change is permitted + * cap_task_setnice - Determine if task priority change is permitted * @p: The task to affect * @nice: The nice value to set * - * Detemine if the requested task priority change is permitted for the + * Determine if the requested task priority change is permitted for the * specified task. * * Return: 0 if permission is granted, -ve if denied. diff --git a/security/device_cgroup.c b/security/device_cgroup.c index 7507d14eacc7..dc4df7475081 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -421,7 +421,7 @@ static bool verify_new_ex(struct dev_cgroup *dev_cgroup, } else { /* * new exception in the child will add more devices - * that can be acessed, so it can't match any of + * that can be accessed, so it can't match any of * parent's exceptions, even slightly */ match = match_exception_partial(&dev_cgroup->exceptions, @@ -822,7 +822,6 @@ struct cgroup_subsys devices_cgrp_subsys = { /** * devcgroup_legacy_check_permission - checks if an inode operation is permitted - * @dev_cgroup: the dev cgroup to be tested against * @type: device type * @major: device major number * @minor: device minor number diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index 033804f5a5f2..0dae649f3740 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c @@ -40,7 +40,7 @@ static const char evm_hmac[] = "hmac(sha1)"; /** * evm_set_key() - set EVM HMAC key from the kernel * @key: pointer to a buffer with the key data - * @size: length of the key data + * @keylen: length of the key data * * This function allows setting the EVM HMAC key from the kernel * without using the "encrypted" key subsystem keys. It can be used diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index cf24c5255583..c9b6e2a43478 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -318,7 +318,6 @@ int evm_protected_xattr_if_enabled(const char *req_xattr_name) /** * evm_read_protected_xattrs - read EVM protected xattr names, lengths, values * @dentry: dentry of the read xattrs - * @inode: inode of the read xattrs * @buffer: buffer xattr names, lengths or values are copied to * @buffer_size: size of buffer * @type: n: names, l: lengths, v: values @@ -390,6 +389,7 @@ int evm_read_protected_xattrs(struct dentry *dentry, u8 *buffer, * @xattr_name: requested xattr * @xattr_value: requested xattr value * @xattr_value_len: requested xattr value length + * @iint: inode integrity metadata * * Calculate the HMAC for the given dentry and verify it against the stored * security.evm xattr. For performance, use the xattr value and length @@ -795,7 +795,9 @@ static int evm_attr_change(struct mnt_idmap *idmap, /** * evm_inode_setattr - prevent updating an invalid EVM extended attribute + * @idmap: idmap of the mount * @dentry: pointer to the affected dentry + * @attr: iattr structure containing the new file attributes * * Permit update of file attributes when files have a valid EVM signature, * except in the case of them having an immutable portable signature. diff --git a/security/integrity/iint.c b/security/integrity/iint.c index c73858e8c6d5..a462df827de2 100644 --- a/security/integrity/iint.c +++ b/security/integrity/iint.c @@ -43,12 +43,10 @@ static struct integrity_iint_cache *__integrity_iint_find(struct inode *inode) else if (inode > iint->inode) n = n->rb_right; else - break; + return iint; } - if (!n) - return NULL; - return iint; + return NULL; } /* @@ -113,10 +111,15 @@ struct integrity_iint_cache *integrity_inode_get(struct inode *inode) parent = *p; test_iint = rb_entry(parent, struct integrity_iint_cache, rb_node); - if (inode < test_iint->inode) + if (inode < test_iint->inode) { p = &(*p)->rb_left; - else + } else if (inode > test_iint->inode) { p = &(*p)->rb_right; + } else { + write_unlock(&integrity_iint_lock); + kmem_cache_free(iint_cache, iint); + return test_iint; + } } iint->inode = inode; diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index 60a511c6b583..c17660bf5f34 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -248,18 +248,6 @@ config IMA_APPRAISE_MODSIG The modsig keyword can be used in the IMA policy to allow a hook to accept such signatures. -config IMA_TRUSTED_KEYRING - bool "Require all keys on the .ima keyring be signed (deprecated)" - depends on IMA_APPRAISE && SYSTEM_TRUSTED_KEYRING - depends on INTEGRITY_ASYMMETRIC_KEYS - select INTEGRITY_TRUSTED_KEYRING - default y - help - This option requires that all keys added to the .ima - keyring be signed by a key on the system trusted keyring. - - This option is deprecated in favor of INTEGRITY_TRUSTED_KEYRING - config IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY bool "Permit keys validly signed by a built-in or secondary CA cert (EXPERIMENTAL)" depends on SYSTEM_TRUSTED_KEYRING diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index d3662f4acadc..452e80b541e5 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -13,7 +13,6 @@ #include <linux/fs.h> #include <linux/xattr.h> #include <linux/evm.h> -#include <linux/iversion.h> #include <linux/fsverity.h> #include "ima.h" @@ -202,19 +201,19 @@ int ima_get_action(struct mnt_idmap *idmap, struct inode *inode, allowed_algos); } -static int ima_get_verity_digest(struct integrity_iint_cache *iint, - struct ima_max_digest_data *hash) +static bool ima_get_verity_digest(struct integrity_iint_cache *iint, + struct ima_max_digest_data *hash) { - enum hash_algo verity_alg; - int ret; + enum hash_algo alg; + int digest_len; /* * On failure, 'measure' policy rules will result in a file data * hash containing 0's. */ - ret = fsverity_get_digest(iint->inode, hash->digest, &verity_alg); - if (ret) - return ret; + digest_len = fsverity_get_digest(iint->inode, hash->digest, NULL, &alg); + if (digest_len == 0) + return false; /* * Unlike in the case of actually calculating the file hash, in @@ -223,9 +222,9 @@ static int ima_get_verity_digest(struct integrity_iint_cache *iint, * mismatch between the verity algorithm and the xattr signature * algorithm, if one exists, will be detected later. */ - hash->hdr.algo = verity_alg; - hash->hdr.length = hash_digest_size[verity_alg]; - return 0; + hash->hdr.algo = alg; + hash->hdr.length = digest_len; + return true; } /* @@ -246,10 +245,11 @@ int ima_collect_measurement(struct integrity_iint_cache *iint, struct inode *inode = file_inode(file); const char *filename = file->f_path.dentry->d_name.name; struct ima_max_digest_data hash; + struct kstat stat; int result = 0; int length; void *tmpbuf; - u64 i_version; + u64 i_version = 0; /* * Always collect the modsig, because IMA might have already collected @@ -268,7 +268,10 @@ int ima_collect_measurement(struct integrity_iint_cache *iint, * to an initial measurement/appraisal/audit, but was modified to * assume the file changed. */ - i_version = inode_query_iversion(inode); + result = vfs_getattr_nosec(&file->f_path, &stat, STATX_CHANGE_COOKIE, + AT_STATX_SYNC_AS_STAT); + if (!result && (stat.result_mask & STATX_CHANGE_COOKIE)) + i_version = stat.change_cookie; hash.hdr.algo = algo; hash.hdr.length = hash_digest_size[algo]; @@ -276,16 +279,9 @@ int ima_collect_measurement(struct integrity_iint_cache *iint, memset(&hash.digest, 0, sizeof(hash.digest)); if (iint->flags & IMA_VERITY_REQUIRED) { - result = ima_get_verity_digest(iint, &hash); - switch (result) { - case 0: - break; - case -ENODATA: + if (!ima_get_verity_digest(iint, &hash)) { audit_cause = "no-verity-digest"; - break; - default: - audit_cause = "invalid-verity-digest"; - break; + result = -ENODATA; } } else if (buf) { result = ima_calc_buffer_hash(buf, size, &hash.hdr); diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index d66a0a36415e..365db0e43d7c 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -24,7 +24,6 @@ #include <linux/slab.h> #include <linux/xattr.h> #include <linux/ima.h> -#include <linux/iversion.h> #include <linux/fs.h> #include "ima.h" @@ -164,11 +163,16 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint, mutex_lock(&iint->mutex); if (atomic_read(&inode->i_writecount) == 1) { + struct kstat stat; + update = test_and_clear_bit(IMA_UPDATE_XATTR, &iint->atomic_flags); - if (!IS_I_VERSION(inode) || - !inode_eq_iversion(inode, iint->version) || - (iint->flags & IMA_NEW_FILE)) { + if ((iint->flags & IMA_NEW_FILE) || + vfs_getattr_nosec(&file->f_path, &stat, + STATX_CHANGE_COOKIE, + AT_STATX_SYNC_AS_STAT) || + !(stat.result_mask & STATX_CHANGE_COOKIE) || + stat.change_cookie != iint->version) { iint->flags &= ~(IMA_DONE_MASK | IMA_NEW_FILE); iint->measured_pcrs = 0; if (update) diff --git a/security/integrity/ima/ima_modsig.c b/security/integrity/ima/ima_modsig.c index fb25723c65bc..3e7bee30080f 100644 --- a/security/integrity/ima/ima_modsig.c +++ b/security/integrity/ima/ima_modsig.c @@ -89,6 +89,9 @@ int ima_read_modsig(enum ima_hooks func, const void *buf, loff_t buf_len, /** * ima_collect_modsig - Calculate the file hash without the appended signature. + * @modsig: parsed module signature + * @buf: data to verify the signature on + * @size: data size * * Since the modsig is part of the file contents, the hash used in its signature * isn't the same one ordinarily calculated by IMA. Therefore PKCS7 code diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 3ca8b7348c2e..c9b3bd8f1bb9 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -721,6 +721,7 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func) * @secid: LSM secid of the task to be validated * @func: IMA hook identifier * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC) + * @flags: IMA actions to consider (e.g. IMA_MEASURE | IMA_APPRAISE) * @pcr: set the pcr to extend * @template_desc: the template that should be used for this rule * @func_data: func specific data, may be NULL @@ -1915,7 +1916,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) /** * ima_parse_add_rule - add a rule to ima_policy_rules - * @rule - ima measurement policy rule + * @rule: ima measurement policy rule * * Avoid locking by allowing just one writer at a time in ima_write_policy() * Returns the length of the rule parsed, an error code on failure diff --git a/security/integrity/platform_certs/load_powerpc.c b/security/integrity/platform_certs/load_powerpc.c index b9de70b90826..170789dc63d2 100644 --- a/security/integrity/platform_certs/load_powerpc.c +++ b/security/integrity/platform_certs/load_powerpc.c @@ -15,6 +15,9 @@ #include "keyring_handler.h" #include "../integrity.h" +#define extract_esl(db, data, size, offset) \ + do { db = data + offset; size = size - offset; } while (0) + /* * Get a certificate list blob from the named secure variable. * @@ -55,8 +58,9 @@ static __init void *get_cert_list(u8 *key, unsigned long keylen, u64 *size) */ static int __init load_powerpc_certs(void) { - void *db = NULL, *dbx = NULL; - u64 dbsize = 0, dbxsize = 0; + void *db = NULL, *dbx = NULL, *data = NULL; + u64 dsize = 0; + u64 offset = 0; int rc = 0; ssize_t len; char buf[32]; @@ -74,38 +78,46 @@ static int __init load_powerpc_certs(void) return -ENODEV; } + if (strcmp("ibm,plpks-sb-v1", buf) == 0) + /* PLPKS authenticated variables ESL data is prefixed with 8 bytes of timestamp */ + offset = 8; + /* * Get db, and dbx. They might not exist, so it isn't an error if we * can't get them. */ - db = get_cert_list("db", 3, &dbsize); - if (!db) { + data = get_cert_list("db", 3, &dsize); + if (!data) { pr_info("Couldn't get db list from firmware\n"); - } else if (IS_ERR(db)) { - rc = PTR_ERR(db); + } else if (IS_ERR(data)) { + rc = PTR_ERR(data); pr_err("Error reading db from firmware: %d\n", rc); return rc; } else { - rc = parse_efi_signature_list("powerpc:db", db, dbsize, + extract_esl(db, data, dsize, offset); + + rc = parse_efi_signature_list("powerpc:db", db, dsize, get_handler_for_db); if (rc) pr_err("Couldn't parse db signatures: %d\n", rc); - kfree(db); + kfree(data); } - dbx = get_cert_list("dbx", 4, &dbxsize); - if (!dbx) { + data = get_cert_list("dbx", 4, &dsize); + if (!data) { pr_info("Couldn't get dbx list from firmware\n"); - } else if (IS_ERR(dbx)) { - rc = PTR_ERR(dbx); + } else if (IS_ERR(data)) { + rc = PTR_ERR(data); pr_err("Error reading dbx from firmware: %d\n", rc); return rc; } else { - rc = parse_efi_signature_list("powerpc:dbx", dbx, dbxsize, + extract_esl(dbx, data, dsize, offset); + + rc = parse_efi_signature_list("powerpc:dbx", dbx, dsize, get_handler_for_dbx); if (rc) pr_err("Couldn't parse dbx signatures: %d\n", rc); - kfree(dbx); + kfree(data); } return rc; diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index d54f73c558f7..19be69fa4d05 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -980,14 +980,19 @@ long keyctl_chown_key(key_serial_t id, uid_t user, gid_t group) ret = -EACCES; down_write(&key->sem); - if (!capable(CAP_SYS_ADMIN)) { + { + bool is_privileged_op = false; + /* only the sysadmin can chown a key to some other UID */ if (user != (uid_t) -1 && !uid_eq(key->uid, uid)) - goto error_put; + is_privileged_op = true; /* only the sysadmin can set the key's GID to a group other * than one of those that the current process subscribes to */ if (group != (gid_t) -1 && !gid_eq(gid, key->gid) && !in_group_p(gid)) + is_privileged_op = true; + + if (is_privileged_op && !capable(CAP_SYS_ADMIN)) goto error_put; } @@ -1088,7 +1093,7 @@ long keyctl_setperm_key(key_serial_t id, key_perm_t perm) down_write(&key->sem); /* if we're not the sysadmin, we can only change a key that we own */ - if (capable(CAP_SYS_ADMIN) || uid_eq(key->uid, current_fsuid())) { + if (uid_eq(key->uid, current_fsuid()) || capable(CAP_SYS_ADMIN)) { key->perm = perm; notify_key(key, NOTIFY_KEY_SETATTR, 0); ret = 0; diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 07a0ef2baacd..a7673ad86d18 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -401,17 +401,21 @@ static int construct_alloc_key(struct keyring_search_context *ctx, set_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags); if (dest_keyring) { - ret = __key_link_lock(dest_keyring, &ctx->index_key); + ret = __key_link_lock(dest_keyring, &key->index_key); if (ret < 0) goto link_lock_failed; - ret = __key_link_begin(dest_keyring, &ctx->index_key, &edit); - if (ret < 0) - goto link_prealloc_failed; } - /* attach the key to the destination keyring under lock, but we do need + /* + * Attach the key to the destination keyring under lock, but we do need * to do another check just in case someone beat us to it whilst we - * waited for locks */ + * waited for locks. + * + * The caller might specify a comparison function which looks for keys + * that do not exactly match but are still equivalent from the caller's + * perspective. The __key_link_begin() operation must be done only after + * an actual key is determined. + */ mutex_lock(&key_construction_mutex); rcu_read_lock(); @@ -420,12 +424,16 @@ static int construct_alloc_key(struct keyring_search_context *ctx, if (!IS_ERR(key_ref)) goto key_already_present; - if (dest_keyring) + if (dest_keyring) { + ret = __key_link_begin(dest_keyring, &key->index_key, &edit); + if (ret < 0) + goto link_alloc_failed; __key_link(dest_keyring, key, &edit); + } mutex_unlock(&key_construction_mutex); if (dest_keyring) - __key_link_end(dest_keyring, &ctx->index_key, edit); + __key_link_end(dest_keyring, &key->index_key, edit); mutex_unlock(&user->cons_lock); *_key = key; kleave(" = 0 [%d]", key_serial(key)); @@ -438,10 +446,13 @@ key_already_present: mutex_unlock(&key_construction_mutex); key = key_ref_to_ptr(key_ref); if (dest_keyring) { + ret = __key_link_begin(dest_keyring, &key->index_key, &edit); + if (ret < 0) + goto link_alloc_failed_unlocked; ret = __key_link_check_live_key(dest_keyring, key); if (ret == 0) __key_link(dest_keyring, key, &edit); - __key_link_end(dest_keyring, &ctx->index_key, edit); + __key_link_end(dest_keyring, &key->index_key, edit); if (ret < 0) goto link_check_failed; } @@ -456,8 +467,10 @@ link_check_failed: kleave(" = %d [linkcheck]", ret); return ret; -link_prealloc_failed: - __key_link_end(dest_keyring, &ctx->index_key, edit); +link_alloc_failed: + mutex_unlock(&key_construction_mutex); +link_alloc_failed_unlocked: + __key_link_end(dest_keyring, &key->index_key, edit); link_lock_failed: mutex_unlock(&user->cons_lock); key_put(key); diff --git a/security/keys/sysctl.c b/security/keys/sysctl.c index b46b651b3c4c..b348e1679d5d 100644 --- a/security/keys/sysctl.c +++ b/security/keys/sysctl.c @@ -9,7 +9,7 @@ #include <linux/sysctl.h> #include "internal.h" -struct ctl_table key_sysctls[] = { +static struct ctl_table key_sysctls[] = { { .procname = "maxkeys", .data = &key_quota_maxkeys, @@ -68,3 +68,10 @@ struct ctl_table key_sysctls[] = { #endif { } }; + +static int __init init_security_keys_sysctls(void) +{ + register_sysctl_init("kernel/keys", key_sysctls); + return 0; +} +early_initcall(init_security_keys_sysctls); diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c index 2b2c8eb258d5..bc700f85f80b 100644 --- a/security/keys/trusted-keys/trusted_tpm2.c +++ b/security/keys/trusted-keys/trusted_tpm2.c @@ -186,7 +186,7 @@ int tpm2_key_priv(void *context, size_t hdrlen, } /** - * tpm_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer. + * tpm2_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer. * * @buf: an allocated tpm_buf instance * @session_handle: session handle diff --git a/security/landlock/Kconfig b/security/landlock/Kconfig index 8e33c4e8ffb8..c1e862a38410 100644 --- a/security/landlock/Kconfig +++ b/security/landlock/Kconfig @@ -2,7 +2,7 @@ config SECURITY_LANDLOCK bool "Landlock support" - depends on SECURITY && !ARCH_EPHEMERAL_INODES + depends on SECURITY select SECURITY_PATH help Landlock is a sandboxing mechanism that enables processes to restrict diff --git a/security/lsm_audit.c b/security/lsm_audit.c index 368e77ca43c4..849e832719e2 100644 --- a/security/lsm_audit.c +++ b/security/lsm_audit.c @@ -200,7 +200,7 @@ static void dump_common_audit_data(struct audit_buffer *ab, char comm[sizeof(current->comm)]; /* - * To keep stack sizes in check force programers to notice if they + * To keep stack sizes in check force programmers to notice if they * start making this union too large! See struct lsm_network_audit * as an example of how to deal with large data. */ diff --git a/security/safesetid/lsm.c b/security/safesetid/lsm.c index e806739f7868..5be5894aa0ea 100644 --- a/security/safesetid/lsm.c +++ b/security/safesetid/lsm.c @@ -131,7 +131,7 @@ static int safesetid_security_capable(const struct cred *cred, * set*gid() (e.g. setting up userns gid mappings). */ pr_warn("Operation requires CAP_SETGID, which is not available to GID %u for operations besides approved set*gid transitions\n", - __kuid_val(cred->uid)); + __kgid_val(cred->gid)); return -EPERM; default: /* Error, the only capabilities were checking for is CAP_SETUID/GID */ diff --git a/security/security.c b/security/security.c index d5ff7ff45b77..549104a447e3 100644 --- a/security/security.c +++ b/security/security.c @@ -1139,6 +1139,20 @@ void security_bprm_committed_creds(struct linux_binprm *bprm) } /** + * security_fs_context_submount() - Initialise fc->security + * @fc: new filesystem context + * @reference: dentry reference for submount/remount + * + * Fill out the ->security field for a new fs_context. + * + * Return: Returns 0 on success or negative error code on failure. + */ +int security_fs_context_submount(struct fs_context *fc, struct super_block *reference) +{ + return call_int_hook(fs_context_submount, 0, fc, reference); +} + +/** * security_fs_context_dup() - Duplicate a fs_context LSM blob * @fc: destination filesystem context * @src_fc: source filesystem context @@ -2491,7 +2505,7 @@ int security_inode_copy_up_xattr(const char *name) /* * The implementation can return 0 (accept the xattr), 1 (discard the * xattr), -EOPNOTSUPP if it does not know anything about the xattr or - * any other error code incase of an error. + * any other error code in case of an error. */ hlist_for_each_entry(hp, &security_hook_heads.inode_copy_up_xattr, list) { @@ -4667,6 +4681,23 @@ int security_sctp_assoc_established(struct sctp_association *asoc, } EXPORT_SYMBOL(security_sctp_assoc_established); +/** + * security_mptcp_add_subflow() - Inherit the LSM label from the MPTCP socket + * @sk: the owning MPTCP socket + * @ssk: the new subflow + * + * Update the labeling for the given MPTCP subflow, to match the one of the + * owning MPTCP socket. This hook has to be called after the socket creation and + * initialization via the security_socket_create() and + * security_socket_post_create() LSM hooks. + * + * Return: Returns 0 on success or a negative error code on failure. + */ +int security_mptcp_add_subflow(struct sock *sk, struct sock *ssk) +{ + return call_int_hook(mptcp_add_subflow, 0, sk, ssk); +} + #endif /* CONFIG_SECURITY_NETWORK */ #ifdef CONFIG_SECURITY_INFINIBAND @@ -4676,7 +4707,7 @@ EXPORT_SYMBOL(security_sctp_assoc_established); * @subnet_prefix: subnet prefix of the port * @pkey: IB pkey * - * Check permission to access a pkey when modifing a QP. + * Check permission to access a pkey when modifying a QP. * * Return: Returns 0 if permission is granted. */ diff --git a/security/selinux/Makefile b/security/selinux/Makefile index 8b21520bd4b9..836379639058 100644 --- a/security/selinux/Makefile +++ b/security/selinux/Makefile @@ -3,32 +3,38 @@ # Makefile for building the SELinux module as part of the kernel tree. # +# NOTE: There are a number of improvements that can be made to this Makefile +# once the kernel requires make v4.3 or greater; the most important feature +# lacking in older versions of make is support for grouped targets. These +# improvements are noted inline in the Makefile below ... + obj-$(CONFIG_SECURITY_SELINUX) := selinux.o +ccflags-y := -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include + selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.o \ netnode.o netport.o status.o \ ss/ebitmap.o ss/hashtab.o ss/symtab.o ss/sidtab.o ss/avtab.o \ ss/policydb.o ss/services.o ss/conditional.o ss/mls.o ss/context.o selinux-$(CONFIG_SECURITY_NETWORK_XFRM) += xfrm.o - selinux-$(CONFIG_NETLABEL) += netlabel.o - selinux-$(CONFIG_SECURITY_INFINIBAND) += ibpkey.o - selinux-$(CONFIG_IMA) += ima.o -ccflags-y := -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include +genhdrs := flask.h av_permissions.h +# see the note above, replace the dependency rule with the one below: +# $(addprefix $(obj)/,$(selinux-y)): $(addprefix $(obj)/,$(genhdrs)) $(addprefix $(obj)/,$(selinux-y)): $(obj)/flask.h -quiet_cmd_flask = GEN $(obj)/flask.h $(obj)/av_permissions.h - cmd_flask = $< $(obj)/flask.h $(obj)/av_permissions.h +quiet_cmd_genhdrs = GEN $(addprefix $(obj)/,$(genhdrs)) + cmd_genhdrs = $< $(addprefix $(obj)/,$(genhdrs)) -targets += flask.h av_permissions.h -# once make >= 4.3 is required, we can use grouped targets in the rule below, -# which basically involves adding both headers and a '&' before the colon, see -# the example below: -# $(obj)/flask.h $(obj)/av_permissions.h &: scripts/selinux/... +# see the note above, replace the $targets and 'flask.h' rule with the lines +# below: +# targets += $(genhdrs) +# $(addprefix $(obj)/,$(genhdrs)) &: scripts/selinux/... +targets += flask.h $(obj)/flask.h: scripts/selinux/genheaders/genheaders FORCE - $(call if_changed,flask) + $(call if_changed,genhdrs) diff --git a/security/selinux/avc.c b/security/selinux/avc.c index eaed5c2da02b..1074db66e5ff 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -642,7 +642,6 @@ static void avc_insert(u32 ssid, u32 tsid, u16 tclass, hlist_add_head_rcu(&node->list, head); found: spin_unlock_irqrestore(lock, flag); - return; } /** @@ -1203,22 +1202,3 @@ u32 avc_policy_seqno(void) { return selinux_avc.avc_cache.latest_notif; } - -void avc_disable(void) -{ - /* - * If you are looking at this because you have realized that we are - * not destroying the avc_node_cachep it might be easy to fix, but - * I don't know the memory barrier semantics well enough to know. It's - * possible that some other task dereferenced security_ops when - * it still pointed to selinux operations. If that is the case it's - * possible that it is about to use the avc and is about to need the - * avc_node_cachep. I know I could wrap the security.c security_ops call - * in an rcu_lock, but seriously, it's not worth it. Instead I just flush - * the cache and get that memory back. - */ - if (avc_node_cachep) { - avc_flush(); - /* kmem_cache_destroy(avc_node_cachep); */ - } -} diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 79b4890e9936..5c16fe737a27 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -357,7 +357,7 @@ enum { }; #define A(s, has_arg) {#s, sizeof(#s) - 1, Opt_##s, has_arg} -static struct { +static const struct { const char *name; int len; int opt; @@ -605,6 +605,13 @@ static int selinux_set_mnt_opts(struct super_block *sb, u32 defcontext_sid = 0; int rc = 0; + /* + * Specifying internal flags without providing a place to + * place the results is not allowed + */ + if (kern_flags && !set_kern_flags) + return -EINVAL; + mutex_lock(&sbsec->lock); if (!selinux_initialized()) { @@ -612,6 +619,10 @@ static int selinux_set_mnt_opts(struct super_block *sb, /* Defer initialization until selinux_complete_init, after the initial policy is loaded and the security server is ready to handle calls. */ + if (kern_flags & SECURITY_LSM_NATIVE_LABELS) { + sbsec->flags |= SE_SBNATIVE; + *set_kern_flags |= SECURITY_LSM_NATIVE_LABELS; + } goto out; } rc = -EINVAL; @@ -619,12 +630,6 @@ static int selinux_set_mnt_opts(struct super_block *sb, "before the security server is initialized\n"); goto out; } - if (kern_flags && !set_kern_flags) { - /* Specifying internal flags without providing a place to - * place the results is not allowed */ - rc = -EINVAL; - goto out; - } /* * Binary mount data FS will come through this function twice. Once @@ -757,7 +762,17 @@ static int selinux_set_mnt_opts(struct super_block *sb, * sets the label used on all file below the mountpoint, and will set * the superblock context if not already set. */ - if (kern_flags & SECURITY_LSM_NATIVE_LABELS && !context_sid) { + if (sbsec->flags & SE_SBNATIVE) { + /* + * This means we are initializing a superblock that has been + * mounted before the SELinux was initialized and the + * filesystem requested native labeling. We had already + * returned SECURITY_LSM_NATIVE_LABELS in *set_kern_flags + * in the original mount attempt, so now we just need to set + * the SECURITY_FS_USE_NATIVE behavior. + */ + sbsec->behavior = SECURITY_FS_USE_NATIVE; + } else if (kern_flags & SECURITY_LSM_NATIVE_LABELS && !context_sid) { sbsec->behavior = SECURITY_FS_USE_NATIVE; *set_kern_flags |= SECURITY_LSM_NATIVE_LABELS; } @@ -869,31 +884,37 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb, int set_rootcontext = (oldsbsec->flags & ROOTCONTEXT_MNT); /* - * if the parent was able to be mounted it clearly had no special lsm - * mount options. thus we can safely deal with this superblock later - */ - if (!selinux_initialized()) - return 0; - - /* * Specifying internal flags without providing a place to * place the results is not allowed. */ if (kern_flags && !set_kern_flags) return -EINVAL; + mutex_lock(&newsbsec->lock); + + /* + * if the parent was able to be mounted it clearly had no special lsm + * mount options. thus we can safely deal with this superblock later + */ + if (!selinux_initialized()) { + if (kern_flags & SECURITY_LSM_NATIVE_LABELS) { + newsbsec->flags |= SE_SBNATIVE; + *set_kern_flags |= SECURITY_LSM_NATIVE_LABELS; + } + goto out; + } + /* how can we clone if the old one wasn't set up?? */ BUG_ON(!(oldsbsec->flags & SE_SBINITIALIZED)); /* if fs is reusing a sb, make sure that the contexts match */ if (newsbsec->flags & SE_SBINITIALIZED) { + mutex_unlock(&newsbsec->lock); if ((kern_flags & SECURITY_LSM_NATIVE_LABELS) && !set_context) *set_kern_flags |= SECURITY_LSM_NATIVE_LABELS; return selinux_cmp_sb_context(oldsb, newsb); } - mutex_lock(&newsbsec->lock); - newsbsec->flags = oldsbsec->flags; newsbsec->sid = oldsbsec->sid; @@ -937,7 +958,7 @@ out: } /* - * NOTE: the caller is resposible for freeing the memory even if on error. + * NOTE: the caller is responsible for freeing the memory even if on error. */ static int selinux_add_opt(int token, const char *s, void **mnt_opts) { @@ -1394,8 +1415,11 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent spin_unlock(&isec->lock); switch (sbsec->behavior) { + /* + * In case of SECURITY_FS_USE_NATIVE we need to re-fetch the labels + * via xattr when called from delayed_superblock_init(). + */ case SECURITY_FS_USE_NATIVE: - break; case SECURITY_FS_USE_XATTR: if (!(inode->i_opflags & IOP_XATTR)) { sid = sbsec->def_sid; @@ -2721,6 +2745,33 @@ static int selinux_umount(struct vfsmount *mnt, int flags) FILESYSTEM__UNMOUNT, NULL); } +static int selinux_fs_context_submount(struct fs_context *fc, + struct super_block *reference) +{ + const struct superblock_security_struct *sbsec = selinux_superblock(reference); + struct selinux_mnt_opts *opts; + + /* + * Ensure that fc->security remains NULL when no options are set + * as expected by selinux_set_mnt_opts(). + */ + if (!(sbsec->flags & (FSCONTEXT_MNT|CONTEXT_MNT|DEFCONTEXT_MNT))) + return 0; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return -ENOMEM; + + if (sbsec->flags & FSCONTEXT_MNT) + opts->fscontext_sid = sbsec->sid; + if (sbsec->flags & CONTEXT_MNT) + opts->context_sid = sbsec->mntpoint_sid; + if (sbsec->flags & DEFCONTEXT_MNT) + opts->defcontext_sid = sbsec->def_sid; + fc->security = opts; + return 0; +} + static int selinux_fs_context_dup(struct fs_context *fc, struct fs_context *src_fc) { @@ -5379,6 +5430,21 @@ static void selinux_sctp_sk_clone(struct sctp_association *asoc, struct sock *sk selinux_netlbl_sctp_sk_clone(sk, newsk); } +static int selinux_mptcp_add_subflow(struct sock *sk, struct sock *ssk) +{ + struct sk_security_struct *ssksec = ssk->sk_security; + struct sk_security_struct *sksec = sk->sk_security; + + ssksec->sclass = sksec->sclass; + ssksec->sid = sksec->sid; + + /* replace the existing subflow label deleting the existing one + * and re-recreating a new label using the updated context + */ + selinux_netlbl_sk_security_free(ssksec); + return selinux_netlbl_socket_post_create(ssk, ssk->sk_family); +} + static int selinux_inet_conn_request(const struct sock *sk, struct sk_buff *skb, struct request_sock *req) { @@ -7074,6 +7140,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = { LSM_HOOK_INIT(sctp_sk_clone, selinux_sctp_sk_clone), LSM_HOOK_INIT(sctp_bind_connect, selinux_sctp_bind_connect), LSM_HOOK_INIT(sctp_assoc_established, selinux_sctp_assoc_established), + LSM_HOOK_INIT(mptcp_add_subflow, selinux_mptcp_add_subflow), LSM_HOOK_INIT(inet_conn_request, selinux_inet_conn_request), LSM_HOOK_INIT(inet_csk_clone, selinux_inet_csk_clone), LSM_HOOK_INIT(inet_conn_established, selinux_inet_conn_established), @@ -7142,6 +7209,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = { /* * PUT "CLONING" (ACCESSING + ALLOCATING) HOOKS HERE */ + LSM_HOOK_INIT(fs_context_submount, selinux_fs_context_submount), LSM_HOOK_INIT(fs_context_dup, selinux_fs_context_dup), LSM_HOOK_INIT(fs_context_parse_param, selinux_fs_context_parse_param), LSM_HOOK_INIT(sb_eat_lsm_opts, selinux_sb_eat_lsm_opts), diff --git a/security/selinux/ima.c b/security/selinux/ima.c index 7daf59667f59..aa34da9b0aeb 100644 --- a/security/selinux/ima.c +++ b/security/selinux/ima.c @@ -4,7 +4,7 @@ * * Author: Lakshmi Ramasubramanian (nramas@linux.microsoft.com) * - * Measure critical data structures maintainted by SELinux + * Measure critical data structures maintained by SELinux * using IMA subsystem. */ #include <linux/vmalloc.h> diff --git a/security/selinux/include/audit.h b/security/selinux/include/audit.h index 406bceb90c6c..d5495134a5b9 100644 --- a/security/selinux/include/audit.h +++ b/security/selinux/include/audit.h @@ -41,7 +41,7 @@ void selinux_audit_rule_free(void *rule); * selinux_audit_rule_match - determine if a context ID matches a rule. * @sid: the context ID to check * @field: the field this rule refers to - * @op: the operater the rule uses + * @op: the operator the rule uses * @rule: pointer to the audit rule to check against * * Returns 1 if the context id matches the rule, 0 if it does not, and diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index 9301222c8e55..9e055f74daf6 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h @@ -168,9 +168,6 @@ int avc_get_hash_stats(char *page); unsigned int avc_get_cache_threshold(void); void avc_set_cache_threshold(unsigned int cache_threshold); -/* Attempt to free avc node cache */ -void avc_disable(void); - #ifdef CONFIG_SECURITY_SELINUX_AVC_STATS DECLARE_PER_CPU(struct avc_cache_stats, avc_cache_stats); #endif diff --git a/security/selinux/include/ibpkey.h b/security/selinux/include/ibpkey.h index c992f83b0aae..875b055849e1 100644 --- a/security/selinux/include/ibpkey.h +++ b/security/selinux/include/ibpkey.h @@ -15,6 +15,7 @@ #define _SELINUX_IB_PKEY_H #include <linux/types.h> +#include "flask.h" #ifdef CONFIG_SECURITY_INFINIBAND void sel_ib_pkey_flush(void); diff --git a/security/selinux/include/ima.h b/security/selinux/include/ima.h index 05e04172c86d..93c05e97eb7f 100644 --- a/security/selinux/include/ima.h +++ b/security/selinux/include/ima.h @@ -4,7 +4,7 @@ * * Author: Lakshmi Ramasubramanian (nramas@linux.microsoft.com) * - * Measure critical data structures maintainted by SELinux + * Measure critical data structures maintained by SELinux * using IMA subsystem. */ diff --git a/security/selinux/include/initial_sid_to_string.h b/security/selinux/include/initial_sid_to_string.h index 60820517aa43..ecc6e74fa09b 100644 --- a/security/selinux/include/initial_sid_to_string.h +++ b/security/selinux/include/initial_sid_to_string.h @@ -1,4 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ + +#include <linux/stddef.h> + static const char *const initial_sid_to_string[] = { NULL, "kernel", diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 8746fafeb778..3b605f39e040 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -65,6 +65,7 @@ #define SE_SBPROC 0x0200 #define SE_SBGENFS 0x0400 #define SE_SBGENFS_XATTR 0x0800 +#define SE_SBNATIVE 0x1000 #define CONTEXT_STR "context" #define FSCONTEXT_STR "fscontext" @@ -384,7 +385,6 @@ struct selinux_kernel_status { extern void selinux_status_update_setenforce(int enforcing); extern void selinux_status_update_policyload(int seqno); extern void selinux_complete_init(void); -extern void exit_sel_fs(void); extern struct path selinux_null; extern void selnl_notify_setenforce(int val); extern void selnl_notify_policyload(u32 seqno); diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c index 767c670d33ea..528f5186e912 100644 --- a/security/selinux/netlabel.c +++ b/security/selinux/netlabel.c @@ -154,8 +154,12 @@ void selinux_netlbl_err(struct sk_buff *skb, u16 family, int error, int gateway) */ void selinux_netlbl_sk_security_free(struct sk_security_struct *sksec) { - if (sksec->nlbl_secattr != NULL) - netlbl_secattr_free(sksec->nlbl_secattr); + if (!sksec->nlbl_secattr) + return; + + netlbl_secattr_free(sksec->nlbl_secattr); + sksec->nlbl_secattr = NULL; + sksec->nlbl_state = NLBL_UNSET; } /** diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 69a583b91fc5..bad1f6b685fd 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -951,7 +951,7 @@ static ssize_t sel_write_create(struct file *file, char *buf, size_t size) * either whitespace or multibyte characters, they shall be * encoded based on the percentage-encoding rule. * If not encoded, the sscanf logic picks up only left-half - * of the supplied name; splitted by a whitespace unexpectedly. + * of the supplied name; split by a whitespace unexpectedly. */ char *r, *w; int c1, c2; @@ -1649,7 +1649,7 @@ static int sel_make_ss_files(struct dentry *dir) struct super_block *sb = dir->d_sb; struct selinux_fs_info *fsi = sb->s_fs_info; int i; - static struct tree_descr files[] = { + static const struct tree_descr files[] = { { "sidtab_hash_stats", &sel_sidtab_hash_stats_ops, S_IRUGO }, }; diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c index 8480ec6c6e75..6766edc0fe68 100644 --- a/security/selinux/ss/avtab.c +++ b/security/selinux/ss/avtab.c @@ -354,7 +354,7 @@ int avtab_alloc_dup(struct avtab *new, const struct avtab *orig) return avtab_alloc_common(new, orig->nslot); } -void avtab_hash_eval(struct avtab *h, char *tag) +void avtab_hash_eval(struct avtab *h, const char *tag) { int i, chain_len, slots_used, max_chain_len; unsigned long long chain2_len_sum; diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h index d3ebea8d146f..d6742fd9c560 100644 --- a/security/selinux/ss/avtab.h +++ b/security/selinux/ss/avtab.h @@ -92,7 +92,7 @@ int avtab_alloc(struct avtab *, u32); int avtab_alloc_dup(struct avtab *new, const struct avtab *orig); struct avtab_datum *avtab_search(struct avtab *h, const struct avtab_key *k); void avtab_destroy(struct avtab *h); -void avtab_hash_eval(struct avtab *h, char *tag); +void avtab_hash_eval(struct avtab *h, const char *tag); struct policydb; int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c index e11219fdf9f7..b156c181c3c1 100644 --- a/security/selinux/ss/conditional.c +++ b/security/selinux/ss/conditional.c @@ -38,7 +38,7 @@ static int cond_evaluate_expr(struct policydb *p, struct cond_expr *expr) if (sp == (COND_EXPR_MAXDEPTH - 1)) return -1; sp++; - s[sp] = p->bool_val_to_struct[node->bool - 1]->state; + s[sp] = p->bool_val_to_struct[node->boolean - 1]->state; break; case COND_NOT: if (sp < 0) @@ -366,7 +366,7 @@ static int expr_node_isvalid(struct policydb *p, struct cond_expr_node *expr) return 0; } - if (expr->bool > p->p_bools.nprim) { + if (expr->boolean > p->p_bools.nprim) { pr_err("SELinux: conditional expressions uses unknown bool.\n"); return 0; } @@ -401,7 +401,7 @@ static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp) return rc; expr->expr_type = le32_to_cpu(buf[0]); - expr->bool = le32_to_cpu(buf[1]); + expr->boolean = le32_to_cpu(buf[1]); if (!expr_node_isvalid(p, expr)) return -EINVAL; @@ -518,7 +518,7 @@ static int cond_write_node(struct policydb *p, struct cond_node *node, for (i = 0; i < node->expr.len; i++) { buf[0] = cpu_to_le32(node->expr.nodes[i].expr_type); - buf[1] = cpu_to_le32(node->expr.nodes[i].bool); + buf[1] = cpu_to_le32(node->expr.nodes[i].boolean); rc = put_entry(buf, sizeof(u32), 2, fp); if (rc) return rc; diff --git a/security/selinux/ss/conditional.h b/security/selinux/ss/conditional.h index e47ec6ddeaf6..5a7b51278dc6 100644 --- a/security/selinux/ss/conditional.h +++ b/security/selinux/ss/conditional.h @@ -29,7 +29,7 @@ struct cond_expr_node { #define COND_NEQ 7 /* bool != bool */ #define COND_LAST COND_NEQ u32 expr_type; - u32 bool; + u32 boolean; }; struct cond_expr { diff --git a/security/selinux/ss/context.h b/security/selinux/ss/context.h index eda32c3d4c0a..aed704b8c642 100644 --- a/security/selinux/ss/context.h +++ b/security/selinux/ss/context.h @@ -167,6 +167,8 @@ static inline int context_cpy(struct context *dst, const struct context *src) rc = mls_context_cpy(dst, src); if (rc) { kfree(dst->str); + dst->str = NULL; + dst->len = 0; return rc; } return 0; diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index adcfb63b3550..dc904865af58 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -42,7 +42,7 @@ #include "services.h" #ifdef DEBUG_HASHES -static const char *symtab_name[SYM_NUM] = { +static const char *const symtab_name[SYM_NUM] = { "common prefixes", "classes", "roles", @@ -2005,6 +2005,7 @@ static int filename_trans_read_helper(struct policydb *p, void *fp) if (!datum) goto out; + datum->next = NULL; *dst = datum; /* ebitmap_read() will at least init the bitmap */ @@ -2017,7 +2018,6 @@ static int filename_trans_read_helper(struct policydb *p, void *fp) goto out; datum->otype = le32_to_cpu(buf[0]); - datum->next = NULL; dst = &datum->next; } @@ -2257,6 +2257,10 @@ static int ocontext_read(struct policydb *p, const struct policydb_compat_info * if (rc) goto out; + if (i == OCON_FS) + pr_warn("SELinux: void and deprecated fs ocon %s\n", + c->u.name); + rc = context_read_and_validate(&c->context[0], p, fp); if (rc) goto out; diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index ffc4e7bad205..74b63ed1173f 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h @@ -225,7 +225,7 @@ struct genfs { /* object context array indices */ #define OCON_ISID 0 /* initial SIDs */ -#define OCON_FS 1 /* unlabeled file systems */ +#define OCON_FS 1 /* unlabeled file systems (deprecated) */ #define OCON_PORT 2 /* TCP and UDP port numbers */ #define OCON_NETIF 3 /* network interfaces */ #define OCON_NODE 4 /* nodes */ diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index f14d1ffe54c5..78946b71c1c1 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -583,7 +583,7 @@ static void type_attribute_bounds_av(struct policydb *policydb, /* * flag which drivers have permissions - * only looking for ioctl based extended permssions + * only looking for ioctl based extended permissions */ void services_compute_xperms_drivers( struct extended_perms *xperms, @@ -3541,38 +3541,38 @@ int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule) tmprule = kzalloc(sizeof(struct selinux_audit_rule), GFP_KERNEL); if (!tmprule) return -ENOMEM; - context_init(&tmprule->au_ctxt); rcu_read_lock(); policy = rcu_dereference(state->policy); policydb = &policy->policydb; - tmprule->au_seqno = policy->latest_granting; - switch (field) { case AUDIT_SUBJ_USER: case AUDIT_OBJ_USER: - rc = -EINVAL; userdatum = symtab_search(&policydb->p_users, rulestr); - if (!userdatum) - goto out; + if (!userdatum) { + rc = -EINVAL; + goto err; + } tmprule->au_ctxt.user = userdatum->value; break; case AUDIT_SUBJ_ROLE: case AUDIT_OBJ_ROLE: - rc = -EINVAL; roledatum = symtab_search(&policydb->p_roles, rulestr); - if (!roledatum) - goto out; + if (!roledatum) { + rc = -EINVAL; + goto err; + } tmprule->au_ctxt.role = roledatum->value; break; case AUDIT_SUBJ_TYPE: case AUDIT_OBJ_TYPE: - rc = -EINVAL; typedatum = symtab_search(&policydb->p_types, rulestr); - if (!typedatum) - goto out; + if (!typedatum) { + rc = -EINVAL; + goto err; + } tmprule->au_ctxt.type = typedatum->value; break; case AUDIT_SUBJ_SEN: @@ -3582,20 +3582,18 @@ int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule) rc = mls_from_string(policydb, rulestr, &tmprule->au_ctxt, GFP_ATOMIC); if (rc) - goto out; + goto err; break; } - rc = 0; -out: rcu_read_unlock(); - if (rc) { - selinux_audit_rule_free(tmprule); - tmprule = NULL; - } - *rule = tmprule; + return 0; +err: + rcu_read_unlock(); + selinux_audit_rule_free(tmprule); + *rule = NULL; return rc; } diff --git a/security/smack/smack.h b/security/smack/smack.h index e2239be7bd60..aa15ff56ed6e 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -120,6 +120,7 @@ struct inode_smack { struct task_smack { struct smack_known *smk_task; /* label for access control */ struct smack_known *smk_forked; /* label when forked */ + struct smack_known *smk_transmuted;/* label when transmuted */ struct list_head smk_rules; /* per task access rules */ struct mutex smk_rules_lock; /* lock for the rules */ struct list_head smk_relabel; /* transit allowed labels */ diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 7a3e9ab137d8..a8201cf22f20 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -615,6 +615,56 @@ out_opt_err: } /** + * smack_fs_context_submount - Initialise security data for a filesystem context + * @fc: The filesystem context. + * @reference: reference superblock + * + * Returns 0 on success or -ENOMEM on error. + */ +static int smack_fs_context_submount(struct fs_context *fc, + struct super_block *reference) +{ + struct superblock_smack *sbsp; + struct smack_mnt_opts *ctx; + struct inode_smack *isp; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + fc->security = ctx; + + sbsp = smack_superblock(reference); + isp = smack_inode(reference->s_root->d_inode); + + if (sbsp->smk_default) { + ctx->fsdefault = kstrdup(sbsp->smk_default->smk_known, GFP_KERNEL); + if (!ctx->fsdefault) + return -ENOMEM; + } + + if (sbsp->smk_floor) { + ctx->fsfloor = kstrdup(sbsp->smk_floor->smk_known, GFP_KERNEL); + if (!ctx->fsfloor) + return -ENOMEM; + } + + if (sbsp->smk_hat) { + ctx->fshat = kstrdup(sbsp->smk_hat->smk_known, GFP_KERNEL); + if (!ctx->fshat) + return -ENOMEM; + } + + if (isp->smk_flags & SMK_INODE_TRANSMUTE) { + if (sbsp->smk_root) { + ctx->fstransmute = kstrdup(sbsp->smk_root->smk_known, GFP_KERNEL); + if (!ctx->fstransmute) + return -ENOMEM; + } + } + return 0; +} + +/** * smack_fs_context_dup - Duplicate the security data on fs_context duplication * @fc: The new filesystem context. * @src_fc: The source filesystem context being duplicated. @@ -933,8 +983,9 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, const struct qstr *qstr, const char **name, void **value, size_t *len) { + struct task_smack *tsp = smack_cred(current_cred()); struct inode_smack *issp = smack_inode(inode); - struct smack_known *skp = smk_of_current(); + struct smack_known *skp = smk_of_task(tsp); struct smack_known *isp = smk_of_inode(inode); struct smack_known *dsp = smk_of_inode(dir); int may; @@ -943,20 +994,34 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, *name = XATTR_SMACK_SUFFIX; if (value && len) { - rcu_read_lock(); - may = smk_access_entry(skp->smk_known, dsp->smk_known, - &skp->smk_rules); - rcu_read_unlock(); + /* + * If equal, transmuting already occurred in + * smack_dentry_create_files_as(). No need to check again. + */ + if (tsp->smk_task != tsp->smk_transmuted) { + rcu_read_lock(); + may = smk_access_entry(skp->smk_known, dsp->smk_known, + &skp->smk_rules); + rcu_read_unlock(); + } /* - * If the access rule allows transmutation and - * the directory requests transmutation then - * by all means transmute. + * In addition to having smk_task equal to smk_transmuted, + * if the access rule allows transmutation and the directory + * requests transmutation then by all means transmute. * Mark the inode as changed. */ - if (may > 0 && ((may & MAY_TRANSMUTE) != 0) && - smk_inode_transmutable(dir)) { - isp = dsp; + if ((tsp->smk_task == tsp->smk_transmuted) || + (may > 0 && ((may & MAY_TRANSMUTE) != 0) && + smk_inode_transmutable(dir))) { + /* + * The caller of smack_dentry_create_files_as() + * should have overridden the current cred, so the + * inode label was already set correctly in + * smack_inode_alloc_security(). + */ + if (tsp->smk_task != tsp->smk_transmuted) + isp = dsp; issp->smk_flags |= SMK_INODE_CHANGED; } @@ -1463,10 +1528,19 @@ static int smack_inode_getsecurity(struct mnt_idmap *idmap, struct super_block *sbp; struct inode *ip = inode; struct smack_known *isp; + struct inode_smack *ispp; + size_t label_len; + char *label = NULL; - if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) + if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) { isp = smk_of_inode(inode); - else { + } else if (strcmp(name, XATTR_SMACK_TRANSMUTE) == 0) { + ispp = smack_inode(inode); + if (ispp->smk_flags & SMK_INODE_TRANSMUTE) + label = TRANS_TRUE; + else + label = ""; + } else { /* * The rest of the Smack xattrs are only on sockets. */ @@ -1488,13 +1562,18 @@ static int smack_inode_getsecurity(struct mnt_idmap *idmap, return -EOPNOTSUPP; } + if (!label) + label = isp->smk_known; + + label_len = strlen(label); + if (alloc) { - *buffer = kstrdup(isp->smk_known, GFP_KERNEL); + *buffer = kstrdup(label, GFP_KERNEL); if (*buffer == NULL) return -ENOMEM; } - return strlen(isp->smk_known); + return label_len; } @@ -4753,8 +4832,10 @@ static int smack_dentry_create_files_as(struct dentry *dentry, int mode, * providing access is transmuting use the containing * directory label instead of the process label. */ - if (may > 0 && (may & MAY_TRANSMUTE)) + if (may > 0 && (may & MAY_TRANSMUTE)) { ntsp->smk_task = isp->smk_inode; + ntsp->smk_transmuted = ntsp->smk_task; + } } return 0; } @@ -4845,6 +4926,7 @@ static struct security_hook_list smack_hooks[] __ro_after_init = { LSM_HOOK_INIT(ptrace_traceme, smack_ptrace_traceme), LSM_HOOK_INIT(syslog, smack_syslog), + LSM_HOOK_INIT(fs_context_submount, smack_fs_context_submount), LSM_HOOK_INIT(fs_context_dup, smack_fs_context_dup), LSM_HOOK_INIT(fs_context_parse_param, smack_fs_context_parse_param), diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 5590eaad241b..25f67d1b5c73 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -896,7 +896,7 @@ static ssize_t smk_set_cipso(struct file *file, const char __user *buf, } ret = sscanf(rule, "%d", &catlen); - if (ret != 1 || catlen > SMACK_CIPSO_MAXCATNUM) + if (ret != 1 || catlen < 0 || catlen > SMACK_CIPSO_MAXCATNUM) goto out; if (format == SMK_FIXED24_FMT && diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index 31af29f669d2..ac20c0bdff9d 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -916,7 +916,7 @@ bool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos, */ mmap_read_lock(bprm->mm); ret = get_user_pages_remote(bprm->mm, pos, 1, - FOLL_FORCE, &page, NULL, NULL); + FOLL_FORCE, &page, NULL); mmap_read_unlock(bprm->mm); if (ret <= 0) return false; |