diff options
Diffstat (limited to 'security')
59 files changed, 1250 insertions, 713 deletions
diff --git a/security/Makefile b/security/Makefile index 22e73a3482bd..3baf435de541 100644 --- a/security/Makefile +++ b/security/Makefile @@ -30,7 +30,7 @@ obj-$(CONFIG_SECURITY_YAMA) += yama/ obj-$(CONFIG_SECURITY_LOADPIN) += loadpin/ obj-$(CONFIG_SECURITY_SAFESETID) += safesetid/ obj-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown/ -obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o +obj-$(CONFIG_CGROUPS) += device_cgroup.o obj-$(CONFIG_BPF_LSM) += bpf/ # Object integrity file lists diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index 0fe336860773..03fae1bd48a6 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig @@ -70,8 +70,9 @@ config SECURITY_APPARMOR_DEBUG_MESSAGES the kernel message buffer. config SECURITY_APPARMOR_KUNIT_TEST - bool "Build KUnit tests for policy_unpack.c" + bool "Build KUnit tests for policy_unpack.c" if !KUNIT_ALL_TESTS depends on KUNIT=y && SECURITY_APPARMOR + default KUNIT_ALL_TESTS help This builds the AppArmor KUnit tests. diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 280741fc0f5f..5fd4a64e431f 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -341,38 +341,6 @@ static struct dentry *aafs_create_dir(const char *name, struct dentry *parent) } /** - * aafs_create_symlink - create a symlink in the apparmorfs filesystem - * @name: name of dentry to create - * @parent: parent directory for this dentry - * @target: if symlink, symlink target string - * @private: private data - * @iops: struct of inode_operations that should be used - * - * If @target parameter is %NULL, then the @iops parameter needs to be - * setup to handle .readlink and .get_link inode_operations. - */ -static struct dentry *aafs_create_symlink(const char *name, - struct dentry *parent, - const char *target, - void *private, - const struct inode_operations *iops) -{ - struct dentry *dent; - char *link = NULL; - - if (target) { - if (!link) - return ERR_PTR(-ENOMEM); - } - dent = aafs_create(name, S_IFLNK | 0444, parent, private, link, NULL, - iops); - if (IS_ERR(dent)) - kfree(link); - - return dent; -} - -/** * aafs_remove - removes a file or directory from the apparmorfs filesystem * * @dentry: dentry of the file/directory/symlink to removed. @@ -454,7 +422,7 @@ static ssize_t policy_update(u32 mask, const char __user *buf, size_t size, */ error = aa_may_manage_policy(label, ns, mask); if (error) - return error; + goto end_section; data = aa_simple_write_to_buffer(buf, size, size, pos); error = PTR_ERR(data); @@ -462,6 +430,7 @@ static ssize_t policy_update(u32 mask, const char __user *buf, size_t size, error = aa_replace_profiles(ns, label, mask, data); aa_put_loaddata(data); } +end_section: end_current_label_crit_section(label); return error; @@ -623,7 +592,7 @@ static __poll_t ns_revision_poll(struct file *file, poll_table *pt) void __aa_bump_ns_revision(struct aa_ns *ns) { - WRITE_ONCE(ns->revision, ns->revision + 1); + WRITE_ONCE(ns->revision, READ_ONCE(ns->revision) + 1); wake_up_interruptible(&ns->wait); } @@ -839,7 +808,7 @@ static ssize_t query_label(char *buf, size_t buf_len, struct multi_transaction { struct kref count; ssize_t size; - char data[0]; + char data[]; }; #define MULTI_TRANSACTION_LIMIT (PAGE_SIZE - sizeof(struct multi_transaction)) @@ -1762,25 +1731,25 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent) } if (profile->rawdata) { - dent = aafs_create_symlink("raw_sha1", dir, NULL, - profile->label.proxy, - &rawdata_link_sha1_iops); + dent = aafs_create("raw_sha1", S_IFLNK | 0444, dir, + profile->label.proxy, NULL, NULL, + &rawdata_link_sha1_iops); if (IS_ERR(dent)) goto fail; aa_get_proxy(profile->label.proxy); profile->dents[AAFS_PROF_RAW_HASH] = dent; - dent = aafs_create_symlink("raw_abi", dir, NULL, - profile->label.proxy, - &rawdata_link_abi_iops); + dent = aafs_create("raw_abi", S_IFLNK | 0444, dir, + profile->label.proxy, NULL, NULL, + &rawdata_link_abi_iops); if (IS_ERR(dent)) goto fail; aa_get_proxy(profile->label.proxy); profile->dents[AAFS_PROF_RAW_ABI] = dent; - dent = aafs_create_symlink("raw_data", dir, NULL, - profile->label.proxy, - &rawdata_link_data_iops); + dent = aafs_create("raw_data", S_IFLNK | 0444, dir, + profile->label.proxy, NULL, NULL, + &rawdata_link_data_iops); if (IS_ERR(dent)) goto fail; aa_get_proxy(profile->label.proxy); @@ -2363,6 +2332,8 @@ static struct aa_sfs_entry aa_sfs_entry_versions[] = { static struct aa_sfs_entry aa_sfs_entry_policy[] = { AA_SFS_DIR("versions", aa_sfs_entry_versions), AA_SFS_FILE_BOOLEAN("set_load", 1), + /* number of out of band transitions supported */ + AA_SFS_FILE_U64("outofband", MAX_OOB_SUPPORTED), { } }; diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 5a98661a8b46..597732503815 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -197,8 +197,9 @@ int aa_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule) rule->label = aa_label_parse(&root_ns->unconfined->label, rulestr, GFP_KERNEL, true, false); if (IS_ERR(rule->label)) { + int err = PTR_ERR(rule->label); aa_audit_rule_free(rule); - return PTR_ERR(rule->label); + return err; } *vrule = rule; diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 6ceb74e0f789..1c898055a476 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -320,8 +320,7 @@ static int aa_xattrs_match(const struct linux_binprm *bprm, might_sleep(); /* transition from exec match to xattr set */ - state = aa_dfa_null_transition(profile->xmatch, state); - + state = aa_dfa_outofband_transition(profile->xmatch, state); d = bprm->file->f_path.dentry; for (i = 0; i < profile->xattr_count; i++) { @@ -330,7 +329,13 @@ static int aa_xattrs_match(const struct linux_binprm *bprm, if (size >= 0) { u32 perm; - /* Check the xattr value, not just presence */ + /* + * Check the xattr presence before value. This ensure + * that not present xattr can be distinguished from a 0 + * length value or rule that matches any value + */ + state = aa_dfa_null_transition(profile->xmatch, state); + /* Check xattr value */ state = aa_dfa_match_len(profile->xmatch, state, value, size); perm = dfa_user_allow(profile->xmatch, state); @@ -340,7 +345,7 @@ static int aa_xattrs_match(const struct linux_binprm *bprm, } } /* transition to next element */ - state = aa_dfa_null_transition(profile->xmatch, state); + state = aa_dfa_outofband_transition(profile->xmatch, state); if (size < 0) { /* * No xattr match, so verify if transition to @@ -620,8 +625,6 @@ static struct aa_label *profile_transition(struct aa_profile *profile, bool *secure_exec) { struct aa_label *new = NULL; - struct aa_profile *component; - struct label_it i; const char *info = NULL, *name = NULL, *target = NULL; unsigned int state = profile->file.start; struct aa_perms perms = {}; @@ -670,21 +673,6 @@ static struct aa_label *profile_transition(struct aa_profile *profile, info = "profile transition not found"; /* remove MAY_EXEC to audit as failure */ perms.allow &= ~MAY_EXEC; - } else { - /* verify that each component's xattr requirements are - * met, and fail execution otherwise - */ - label_for_each(i, new, component) { - if (aa_xattrs_match(bprm, component, state) < - 0) { - error = -EACCES; - info = "required xattrs not present"; - perms.allow &= ~MAY_EXEC; - aa_put_label(new); - new = NULL; - goto audit; - } - } } } else if (COMPLAIN_MODE(profile)) { /* no exec permission - learning mode */ @@ -854,14 +842,14 @@ static struct aa_label *handle_onexec(struct aa_label *label, } /** - * apparmor_bprm_set_creds - set the new creds on the bprm struct + * apparmor_bprm_creds_for_exec - Update the new creds on the bprm struct * @bprm: binprm for the exec (NOT NULL) * * Returns: %0 or error on failure * * TODO: once the other paths are done see if we can't refactor into a fn */ -int apparmor_bprm_set_creds(struct linux_binprm *bprm) +int apparmor_bprm_creds_for_exec(struct linux_binprm *bprm) { struct aa_task_ctx *ctx; struct aa_label *label, *new = NULL; @@ -875,9 +863,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) file_inode(bprm->file)->i_mode }; - if (bprm->called_set_creds) - return 0; - ctx = task_ctx(current); AA_BUG(!cred_label(bprm->cred)); AA_BUG(!ctx); @@ -929,7 +914,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) * aways results in a further reduction of permissions. */ if ((bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) && - !unconfined(label) && !aa_label_is_subset(new, ctx->nnp)) { + !unconfined(label) && + !aa_label_is_unconfined_subset(new, ctx->nnp)) { error = -EPERM; info = "no new privs"; goto audit; @@ -1207,7 +1193,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags) * reduce restrictions. */ if (task_no_new_privs(current) && !unconfined(label) && - !aa_label_is_subset(new, ctx->nnp)) { + !aa_label_is_unconfined_subset(new, ctx->nnp)) { /* not an apparmor denial per se, so don't log it */ AA_DEBUG("no_new_privs - change_hat denied"); error = -EPERM; @@ -1228,7 +1214,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags) * reduce restrictions. */ if (task_no_new_privs(current) && !unconfined(label) && - !aa_label_is_subset(previous, ctx->nnp)) { + !aa_label_is_unconfined_subset(previous, ctx->nnp)) { /* not an apparmor denial per se, so don't log it */ AA_DEBUG("no_new_privs - change_hat denied"); error = -EPERM; @@ -1328,6 +1314,7 @@ int aa_change_profile(const char *fqname, int flags) ctx->nnp = aa_get_label(label); if (!fqname || !*fqname) { + aa_put_label(label); AA_DEBUG("no profile name"); return -EINVAL; } @@ -1346,8 +1333,6 @@ int aa_change_profile(const char *fqname, int flags) op = OP_CHANGE_PROFILE; } - label = aa_get_current_label(); - if (*fqname == '&') { stack = true; /* don't have label_parse() do stacking */ @@ -1424,7 +1409,7 @@ check: * reduce restrictions. */ if (task_no_new_privs(current) && !unconfined(label) && - !aa_label_is_subset(new, ctx->nnp)) { + !aa_label_is_unconfined_subset(new, ctx->nnp)) { /* not an apparmor denial per se, so don't log it */ AA_DEBUG("no_new_privs - change_hat denied"); error = -EPERM; diff --git a/security/apparmor/file.c b/security/apparmor/file.c index f1caf3674e1c..9a2d14b7c9f8 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -154,13 +154,13 @@ int aa_audit_file(struct aa_profile *profile, struct aa_perms *perms, * is_deleted - test if a file has been completely unlinked * @dentry: dentry of file to test for deletion (NOT NULL) * - * Returns: %1 if deleted else %0 + * Returns: true if deleted else false */ static inline bool is_deleted(struct dentry *dentry) { if (d_unlinked(dentry) && d_backing_inode(dentry)->i_nlink == 0) - return 1; - return 0; + return true; + return false; } static int path_name(const char *op, struct aa_label *label, @@ -353,15 +353,15 @@ int aa_path_perm(const char *op, struct aa_label *label, * this is done as part of the subset test, where a hardlink must have * a subset of permissions that the target has. * - * Returns: %1 if subset else %0 + * Returns: true if subset else false */ static inline bool xindex_is_subset(u32 link, u32 target) { if (((link & ~AA_X_UNSAFE) != (target & ~AA_X_UNSAFE)) || ((link & AA_X_UNSAFE) && !(target & AA_X_UNSAFE))) - return 0; + return false; - return 1; + return true; } static int profile_path_link(struct aa_profile *profile, diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h index 21b875fe2d37..d14928fe1c6f 100644 --- a/security/apparmor/include/domain.h +++ b/security/apparmor/include/domain.h @@ -30,7 +30,7 @@ struct aa_domain { struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex, const char **name); -int apparmor_bprm_set_creds(struct linux_binprm *bprm); +int apparmor_bprm_creds_for_exec(struct linux_binprm *bprm); void aa_free_domain_entries(struct aa_domain *domain); int aa_change_hat(const char *hats[], int count, u64 token, int flags); diff --git a/security/apparmor/include/label.h b/security/apparmor/include/label.h index 47942c4ba7ca..1e90384b1523 100644 --- a/security/apparmor/include/label.h +++ b/security/apparmor/include/label.h @@ -275,12 +275,14 @@ void aa_labelset_destroy(struct aa_labelset *ls); void aa_labelset_init(struct aa_labelset *ls); void __aa_labelset_update_subtree(struct aa_ns *ns); +void aa_label_destroy(struct aa_label *label); void aa_label_free(struct aa_label *label); void aa_label_kref(struct kref *kref); bool aa_label_init(struct aa_label *label, int size, gfp_t gfp); struct aa_label *aa_label_alloc(int size, struct aa_proxy *proxy, gfp_t gfp); bool aa_label_is_subset(struct aa_label *set, struct aa_label *sub); +bool aa_label_is_unconfined_subset(struct aa_label *set, struct aa_label *sub); struct aa_profile *__aa_label_next_not_in_set(struct label_it *I, struct aa_label *set, struct aa_label *sub); diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h index e23f4aadc1ff..884489590588 100644 --- a/security/apparmor/include/match.h +++ b/security/apparmor/include/match.h @@ -37,6 +37,10 @@ #define YYTH_MAGIC 0x1B5E783D #define YYTH_FLAG_DIFF_ENCODE 1 +#define YYTH_FLAG_OOB_TRANS 2 +#define YYTH_FLAGS (YYTH_FLAG_DIFF_ENCODE | YYTH_FLAG_OOB_TRANS) + +#define MAX_OOB_SUPPORTED 1 struct table_set_header { u32 th_magic; /* YYTH_MAGIC */ @@ -94,6 +98,7 @@ struct table_header { struct aa_dfa { struct kref count; u16 flags; + u32 max_oob; struct table_header *tables[YYTD_ID_TSIZE]; }; @@ -127,6 +132,8 @@ unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start, const char *str); unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state, const char c); +unsigned int aa_dfa_outofband_transition(struct aa_dfa *dfa, + unsigned int state); unsigned int aa_dfa_match_until(struct aa_dfa *dfa, unsigned int start, const char *str, const char **retpos); unsigned int aa_dfa_matchn_until(struct aa_dfa *dfa, unsigned int start, @@ -181,5 +188,9 @@ static inline void aa_put_dfa(struct aa_dfa *dfa) #define MATCH_FLAG_DIFF_ENCODE 0x80000000 #define MARK_DIFF_ENCODE 0x40000000 +#define MATCH_FLAG_OOB_TRANSITION 0x20000000 +#define MATCH_FLAGS_MASK 0xff000000 +#define MATCH_FLAGS_VALID (MATCH_FLAG_DIFF_ENCODE | MATCH_FLAG_OOB_TRANSITION) +#define MATCH_FLAGS_INVALID (MATCH_FLAGS_MASK & ~MATCH_FLAGS_VALID) #endif /* __AA_MATCH_H */ diff --git a/security/apparmor/label.c b/security/apparmor/label.c index 470693239e64..e68bcedca976 100644 --- a/security/apparmor/label.c +++ b/security/apparmor/label.c @@ -309,10 +309,8 @@ out: } -static void label_destroy(struct aa_label *label) +void aa_label_destroy(struct aa_label *label) { - struct aa_label *tmp; - AA_BUG(!label); if (!label_isprofile(label)) { @@ -328,16 +326,13 @@ static void label_destroy(struct aa_label *label) } } - if (rcu_dereference_protected(label->proxy->label, true) == label) - rcu_assign_pointer(label->proxy->label, NULL); - + if (label->proxy) { + if (rcu_dereference_protected(label->proxy->label, true) == label) + rcu_assign_pointer(label->proxy->label, NULL); + aa_put_proxy(label->proxy); + } aa_free_secid(label->secid); - tmp = rcu_dereference_protected(label->proxy->label, true); - if (tmp == label) - rcu_assign_pointer(label->proxy->label, NULL); - - aa_put_proxy(label->proxy); label->proxy = (struct aa_proxy *) PROXY_POISON + 1; } @@ -346,7 +341,7 @@ void aa_label_free(struct aa_label *label) if (!label) return; - label_destroy(label); + aa_label_destroy(label); kfree(label); } @@ -550,6 +545,39 @@ bool aa_label_is_subset(struct aa_label *set, struct aa_label *sub) return __aa_label_next_not_in_set(&i, set, sub) == NULL; } +/** + * aa_label_is_unconfined_subset - test if @sub is a subset of @set + * @set: label to test against + * @sub: label to test if is subset of @set + * + * This checks for subset but taking into account unconfined. IF + * @sub contains an unconfined profile that does not have a matching + * unconfined in @set then this will not cause the test to fail. + * Conversely we don't care about an unconfined in @set that is not in + * @sub + * + * Returns: true if @sub is special_subset of @set + * else false + */ +bool aa_label_is_unconfined_subset(struct aa_label *set, struct aa_label *sub) +{ + struct label_it i = { }; + struct aa_profile *p; + + AA_BUG(!set); + AA_BUG(!sub); + + if (sub == set) + return true; + + do { + p = __aa_label_next_not_in_set(&i, set, sub); + if (p && !profile_unconfined(p)) + break; + } while (p); + + return p == NULL; +} /** @@ -1531,13 +1559,13 @@ static const char *label_modename(struct aa_ns *ns, struct aa_label *label, label_for_each(i, label, profile) { if (aa_ns_visible(ns, profile->ns, flags & FLAG_VIEW_SUBNS)) { - if (profile->mode == APPARMOR_UNCONFINED) + count++; + if (profile == profile->ns->unconfined) /* special case unconfined so stacks with * unconfined don't report as mixed. ie. * profile_foo//&:ns1:unconfined (mixed) */ continue; - count++; if (mode == -1) mode = profile->mode; else if (mode != profile->mode) @@ -1749,13 +1777,13 @@ void aa_label_seq_xprint(struct seq_file *f, struct aa_ns *ns, AA_DEBUG("label print error"); return; } - seq_printf(f, "%s", str); + seq_puts(f, str); kfree(str); } else if (display_mode(ns, label, flags)) seq_printf(f, "%s (%s)", label->hname, label_modename(ns, label, flags)); else - seq_printf(f, "%s", label->hname); + seq_puts(f, label->hname); } void aa_label_xprintk(struct aa_ns *ns, struct aa_label *label, int flags, diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index b621ad74f54a..ffeaee5ed968 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -804,7 +804,12 @@ static void apparmor_sk_clone_security(const struct sock *sk, struct aa_sk_ctx *ctx = SK_CTX(sk); struct aa_sk_ctx *new = SK_CTX(newsk); + if (new->label) + aa_put_label(new->label); new->label = aa_get_label(ctx->label); + + if (new->peer) + aa_put_label(new->peer); new->peer = aa_get_label(ctx->peer); } @@ -1232,7 +1237,7 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(cred_prepare, apparmor_cred_prepare), LSM_HOOK_INIT(cred_transfer, apparmor_cred_transfer), - LSM_HOOK_INIT(bprm_set_creds, apparmor_bprm_set_creds), + LSM_HOOK_INIT(bprm_creds_for_exec, apparmor_bprm_creds_for_exec), LSM_HOOK_INIT(bprm_committing_creds, apparmor_bprm_committing_creds), LSM_HOOK_INIT(bprm_committed_creds, apparmor_bprm_committed_creds), @@ -1696,7 +1701,7 @@ static int __init alloc_buffers(void) #ifdef CONFIG_SYSCTL static int apparmor_dointvec(struct ctl_table *table, int write, - void __user *buffer, size_t *lenp, loff_t *ppos) + void *buffer, size_t *lenp, loff_t *ppos) { if (!policy_admin_capable(NULL)) return -EPERM; diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 525ce22dc0e9..3e9e1eaf990e 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -97,6 +97,9 @@ static struct table_header *unpack_table(char *blob, size_t bsize) th.td_flags == YYTD_DATA8)) goto out; + /* if we have a table it must have some entries */ + if (th.td_lolen == 0) + goto out; tsize = table_size(th.td_lolen, th.td_flags); if (bsize < tsize) goto out; @@ -198,10 +201,32 @@ static int verify_dfa(struct aa_dfa *dfa) state_count = dfa->tables[YYTD_ID_BASE]->td_lolen; trans_count = dfa->tables[YYTD_ID_NXT]->td_lolen; + if (state_count == 0) + goto out; for (i = 0; i < state_count; i++) { if (!(BASE_TABLE(dfa)[i] & MATCH_FLAG_DIFF_ENCODE) && (DEFAULT_TABLE(dfa)[i] >= state_count)) goto out; + if (BASE_TABLE(dfa)[i] & MATCH_FLAGS_INVALID) { + pr_err("AppArmor DFA state with invalid match flags"); + goto out; + } + if ((BASE_TABLE(dfa)[i] & MATCH_FLAG_DIFF_ENCODE)) { + if (!(dfa->flags & YYTH_FLAG_DIFF_ENCODE)) { + pr_err("AppArmor DFA diff encoded transition state without header flag"); + goto out; + } + } + if ((BASE_TABLE(dfa)[i] & MATCH_FLAG_OOB_TRANSITION)) { + if (base_idx(BASE_TABLE(dfa)[i]) < dfa->max_oob) { + pr_err("AppArmor DFA out of bad transition out of range"); + goto out; + } + if (!(dfa->flags & YYTH_FLAG_OOB_TRANS)) { + pr_err("AppArmor DFA out of bad transition state without header flag"); + goto out; + } + } if (base_idx(BASE_TABLE(dfa)[i]) + 255 >= trans_count) { pr_err("AppArmor DFA next/check upper bounds error\n"); goto out; @@ -304,9 +329,23 @@ struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags) goto fail; dfa->flags = ntohs(*(__be16 *) (data + 12)); - if (dfa->flags != 0 && dfa->flags != YYTH_FLAG_DIFF_ENCODE) + if (dfa->flags & ~(YYTH_FLAGS)) goto fail; + /* + * TODO: needed for dfa to support more than 1 oob + * if (dfa->flags & YYTH_FLAGS_OOB_TRANS) { + * if (hsize < 16 + 4) + * goto fail; + * dfa->max_oob = ntol(*(__be32 *) (data + 16)); + * if (dfa->max <= MAX_OOB_SUPPORTED) { + * pr_err("AppArmor DFA OOB greater than supported\n"); + * goto fail; + * } + * } + */ + dfa->max_oob = 1; + data += hsize; size -= hsize; @@ -495,6 +534,23 @@ unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state, return state; } +unsigned int aa_dfa_outofband_transition(struct aa_dfa *dfa, unsigned int state) +{ + u16 *def = DEFAULT_TABLE(dfa); + u32 *base = BASE_TABLE(dfa); + u16 *next = NEXT_TABLE(dfa); + u16 *check = CHECK_TABLE(dfa); + u32 b = (base)[(state)]; + + if (!(b & MATCH_FLAG_OOB_TRANSITION)) + return DFA_NOMATCH; + + /* No Equivalence class remapping for outofband transitions */ + match_char(state, def, base, next, check, -1); + + return state; +} + /** * aa_dfa_match_until - traverse @dfa until accept state or end of input * @dfa: the dfa to match @str against (NOT NULL) diff --git a/security/apparmor/path.c b/security/apparmor/path.c index c6da542de27b..b02dfdbff7cd 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c @@ -142,7 +142,7 @@ static int d_namespace_path(const struct path *path, char *buf, char **name, error = PTR_ERR(res); *name = buf; goto out; - }; + } } else if (!our_mnt(path->mnt)) connected = 0; diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 269f2f53c0b1..af4f50fda9e3 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -242,6 +242,7 @@ void aa_free_profile(struct aa_profile *profile) kzfree(profile->hash); aa_put_loaddata(profile->rawdata); + aa_label_destroy(&profile->label); kzfree(profile); } diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 2d743c004bc4..b67322abcc33 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -243,11 +243,11 @@ fail: static bool unpack_X(struct aa_ext *e, enum aa_code code) { if (!inbounds(e, 1)) - return 0; + return false; if (*(u8 *) e->pos != code) - return 0; + return false; e->pos++; - return 1; + return true; } /** @@ -261,10 +261,10 @@ static bool unpack_X(struct aa_ext *e, enum aa_code code) * name element in the stream. If @name is NULL any name element will be * skipped and only the typecode will be tested. * - * Returns 1 on success (both type code and name tests match) and the read + * Returns true on success (both type code and name tests match) and the read * head is advanced past the headers * - * Returns: 0 if either match fails, the read head does not move + * Returns: false if either match fails, the read head does not move */ static bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name) { @@ -289,11 +289,11 @@ static bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name) /* now check if type code matches */ if (unpack_X(e, code)) - return 1; + return true; fail: e->pos = pos; - return 0; + return false; } static bool unpack_u8(struct aa_ext *e, u8 *data, const char *name) @@ -306,12 +306,12 @@ static bool unpack_u8(struct aa_ext *e, u8 *data, const char *name) if (data) *data = get_unaligned((u8 *)e->pos); e->pos += sizeof(u8); - return 1; + return true; } fail: e->pos = pos; - return 0; + return false; } static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) @@ -324,12 +324,12 @@ static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) if (data) *data = le32_to_cpu(get_unaligned((__le32 *) e->pos)); e->pos += sizeof(u32); - return 1; + return true; } fail: e->pos = pos; - return 0; + return false; } static bool unpack_u64(struct aa_ext *e, u64 *data, const char *name) @@ -342,12 +342,12 @@ static bool unpack_u64(struct aa_ext *e, u64 *data, const char *name) if (data) *data = le64_to_cpu(get_unaligned((__le64 *) e->pos)); e->pos += sizeof(u64); - return 1; + return true; } fail: e->pos = pos; - return 0; + return false; } static size_t unpack_array(struct aa_ext *e, const char *name) @@ -472,7 +472,7 @@ static struct aa_dfa *unpack_dfa(struct aa_ext *e) * @e: serialized data extent information (NOT NULL) * @profile: profile to add the accept table to (NOT NULL) * - * Returns: 1 if table successfully unpacked + * Returns: true if table successfully unpacked */ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile) { @@ -535,12 +535,12 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile) if (!unpack_nameX(e, AA_STRUCTEND, NULL)) goto fail; } - return 1; + return true; fail: aa_free_domain_entries(&profile->file.trans); e->pos = saved_pos; - return 0; + return false; } static bool unpack_xattrs(struct aa_ext *e, struct aa_profile *profile) @@ -565,11 +565,11 @@ static bool unpack_xattrs(struct aa_ext *e, struct aa_profile *profile) goto fail; } - return 1; + return true; fail: e->pos = pos; - return 0; + return false; } static bool unpack_secmark(struct aa_ext *e, struct aa_profile *profile) @@ -601,7 +601,7 @@ static bool unpack_secmark(struct aa_ext *e, struct aa_profile *profile) goto fail; } - return 1; + return true; fail: if (profile->secmark) { @@ -613,7 +613,7 @@ fail: } e->pos = pos; - return 0; + return false; } static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile) @@ -643,11 +643,11 @@ static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile) if (!unpack_nameX(e, AA_STRUCTEND, NULL)) goto fail; } - return 1; + return true; fail: e->pos = pos; - return 0; + return false; } static u32 strhash(const void *data, u32 len, u32 seed) @@ -748,10 +748,14 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) goto fail; if (tmp == PACKED_MODE_COMPLAIN || (e->version & FORCE_COMPLAIN_FLAG)) profile->mode = APPARMOR_COMPLAIN; + else if (tmp == PACKED_MODE_ENFORCE) + profile->mode = APPARMOR_ENFORCE; else if (tmp == PACKED_MODE_KILL) profile->mode = APPARMOR_KILL; else if (tmp == PACKED_MODE_UNCONFINED) profile->mode = APPARMOR_UNCONFINED; + else + goto fail; if (!unpack_u32(e, &tmp, NULL)) goto fail; if (tmp) @@ -990,8 +994,8 @@ static bool verify_xindex(int xindex, int table_size) xtype = xindex & AA_X_TYPE_MASK; index = xindex & AA_X_INDEX_MASK; if (xtype == AA_X_TABLE && index >= table_size) - return 0; - return 1; + return false; + return true; } /* verify dfa xindexes are in range of transition tables */ @@ -1000,11 +1004,11 @@ static bool verify_dfa_xindex(struct aa_dfa *dfa, int table_size) int i; for (i = 0; i < dfa->tables[YYTD_ID_ACCEPT]->td_lolen; i++) { if (!verify_xindex(dfa_user_xindex(dfa, i), table_size)) - return 0; + return false; if (!verify_xindex(dfa_other_xindex(dfa, i), table_size)) - return 0; + return false; } - return 1; + return true; } /** diff --git a/security/commoncap.c b/security/commoncap.c index f4ee0ae106b2..59bf3c1674c8 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -647,7 +647,8 @@ int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data * its xattrs and, if present, apply them to the proposed credentials being * constructed by execve(). */ -static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_fcap) +static int get_file_caps(struct linux_binprm *bprm, struct file *file, + bool *effective, bool *has_fcap) { int rc = 0; struct cpu_vfs_cap_data vcaps; @@ -657,7 +658,7 @@ static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_f if (!file_caps_enabled) return 0; - if (!mnt_may_suid(bprm->file->f_path.mnt)) + if (!mnt_may_suid(file->f_path.mnt)) return 0; /* @@ -665,10 +666,10 @@ static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_f * explicit that capability bits are limited to s_user_ns and its * descendants. */ - if (!current_in_userns(bprm->file->f_path.mnt->mnt_sb->s_user_ns)) + if (!current_in_userns(file->f_path.mnt->mnt_sb->s_user_ns)) return 0; - rc = get_vfs_caps_from_disk(bprm->file->f_path.dentry, &vcaps); + rc = get_vfs_caps_from_disk(file->f_path.dentry, &vcaps); if (rc < 0) { if (rc == -EINVAL) printk(KERN_NOTICE "Invalid argument reading file caps for %s\n", @@ -797,15 +798,17 @@ static inline bool nonroot_raised_pE(struct cred *new, const struct cred *old, } /** - * cap_bprm_set_creds - Set up the proposed credentials for execve(). + * cap_bprm_creds_from_file - Set up the proposed credentials for execve(). * @bprm: The execution parameters, including the proposed creds + * @file: The file to pull the credentials from * * Set up the proposed credentials for a new execution context being * constructed by execve(). The proposed creds in @bprm->cred is altered, * which won't take effect immediately. Returns 0 if successful, -ve on error. */ -int cap_bprm_set_creds(struct linux_binprm *bprm) +int cap_bprm_creds_from_file(struct linux_binprm *bprm, struct file *file) { + /* Process setpcap binaries and capabilities for uid 0 */ const struct cred *old = current_cred(); struct cred *new = bprm->cred; bool effective = false, has_fcap = false, is_setid; @@ -815,7 +818,7 @@ int cap_bprm_set_creds(struct linux_binprm *bprm) if (WARN_ON(!cap_ambient_invariant_ok(old))) return -EPERM; - ret = get_file_caps(bprm, &effective, &has_fcap); + ret = get_file_caps(bprm, file, &effective, &has_fcap); if (ret < 0) return ret; @@ -884,12 +887,11 @@ int cap_bprm_set_creds(struct linux_binprm *bprm) return -EPERM; /* Check for privilege-elevated exec. */ - bprm->cap_elevated = 0; if (is_setid || (!__is_real(root_uid, new) && (effective || __cap_grew(permitted, ambient, new)))) - bprm->cap_elevated = 1; + bprm->secureexec = 1; return 0; } @@ -1346,7 +1348,7 @@ static struct security_hook_list capability_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(ptrace_traceme, cap_ptrace_traceme), LSM_HOOK_INIT(capget, cap_capget), LSM_HOOK_INIT(capset, cap_capset), - LSM_HOOK_INIT(bprm_set_creds, cap_bprm_set_creds), + LSM_HOOK_INIT(bprm_creds_from_file, cap_bprm_creds_from_file), LSM_HOOK_INIT(inode_need_killpriv, cap_inode_need_killpriv), LSM_HOOK_INIT(inode_killpriv, cap_inode_killpriv), LSM_HOOK_INIT(inode_getsecurity, cap_inode_getsecurity), diff --git a/security/device_cgroup.c b/security/device_cgroup.c index 7d0f8f7431ff..43ab0ad45c1b 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -15,6 +15,8 @@ #include <linux/rcupdate.h> #include <linux/mutex.h> +#ifdef CONFIG_CGROUP_DEVICE + static DEFINE_MUTEX(devcgroup_mutex); enum devcg_behavior { @@ -792,7 +794,7 @@ struct cgroup_subsys devices_cgrp_subsys = { }; /** - * __devcgroup_check_permission - checks if an inode operation is permitted + * 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 @@ -801,7 +803,7 @@ struct cgroup_subsys devices_cgrp_subsys = { * * returns 0 on success, -EPERM case the operation is not permitted */ -static int __devcgroup_check_permission(short type, u32 major, u32 minor, +static int devcgroup_legacy_check_permission(short type, u32 major, u32 minor, short access) { struct dev_cgroup *dev_cgroup; @@ -825,6 +827,10 @@ static int __devcgroup_check_permission(short type, u32 major, u32 minor, return 0; } +#endif /* CONFIG_CGROUP_DEVICE */ + +#if defined(CONFIG_CGROUP_DEVICE) || defined(CONFIG_CGROUP_BPF) + int devcgroup_check_permission(short type, u32 major, u32 minor, short access) { int rc = BPF_CGROUP_RUN_PROG_DEVICE_CGROUP(type, major, minor, access); @@ -832,6 +838,13 @@ int devcgroup_check_permission(short type, u32 major, u32 minor, short access) if (rc) return -EPERM; - return __devcgroup_check_permission(type, major, minor, access); + #ifdef CONFIG_CGROUP_DEVICE + return devcgroup_legacy_check_permission(type, major, minor, access); + + #else /* CONFIG_CGROUP_DEVICE */ + return 0; + + #endif /* CONFIG_CGROUP_DEVICE */ } EXPORT_SYMBOL(devcgroup_check_permission); +#endif /* defined(CONFIG_CGROUP_DEVICE) || defined(CONFIG_CGROUP_BPF) */ diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index 35682852ddea..168c3b78ac47 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c @@ -73,7 +73,7 @@ static struct shash_desc *init_desc(char type, uint8_t hash_algo) { long rc; const char *algo; - struct crypto_shash **tfm; + struct crypto_shash **tfm, *tmp_tfm; struct shash_desc *desc; if (type == EVM_XATTR_HMAC) { @@ -91,31 +91,31 @@ static struct shash_desc *init_desc(char type, uint8_t hash_algo) algo = hash_algo_name[hash_algo]; } - if (*tfm == NULL) { - mutex_lock(&mutex); - if (*tfm) - goto out; - *tfm = crypto_alloc_shash(algo, 0, CRYPTO_NOLOAD); - if (IS_ERR(*tfm)) { - rc = PTR_ERR(*tfm); - pr_err("Can not allocate %s (reason: %ld)\n", algo, rc); - *tfm = NULL; + if (*tfm) + goto alloc; + mutex_lock(&mutex); + if (*tfm) + goto unlock; + + tmp_tfm = crypto_alloc_shash(algo, 0, CRYPTO_NOLOAD); + if (IS_ERR(tmp_tfm)) { + pr_err("Can not allocate %s (reason: %ld)\n", algo, + PTR_ERR(tmp_tfm)); + mutex_unlock(&mutex); + return ERR_CAST(tmp_tfm); + } + if (type == EVM_XATTR_HMAC) { + rc = crypto_shash_setkey(tmp_tfm, evmkey, evmkey_len); + if (rc) { + crypto_free_shash(tmp_tfm); mutex_unlock(&mutex); return ERR_PTR(rc); } - if (type == EVM_XATTR_HMAC) { - rc = crypto_shash_setkey(*tfm, evmkey, evmkey_len); - if (rc) { - crypto_free_shash(*tfm); - *tfm = NULL; - mutex_unlock(&mutex); - return ERR_PTR(rc); - } - } -out: - mutex_unlock(&mutex); } - + *tfm = tmp_tfm; +unlock: + mutex_unlock(&mutex); +alloc: desc = kmalloc(sizeof(*desc) + crypto_shash_descsize(*tfm), GFP_KERNEL); if (!desc) @@ -207,7 +207,7 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry, data->hdr.length = crypto_shash_digestsize(desc->tfm); error = -ENODATA; - list_for_each_entry_rcu(xattr, &evm_config_xattrnames, list) { + list_for_each_entry_lockless(xattr, &evm_config_xattrnames, list) { bool is_ima = false; if (strcmp(xattr->name, XATTR_NAME_IMA) == 0) @@ -241,7 +241,7 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry, /* Portable EVM signatures must include an IMA hash */ if (type == EVM_XATTR_PORTABLE_DIGSIG && !ima_present) - return -EPERM; + error = -EPERM; out: kfree(xattr_value); kfree(desc); diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index d361d7fdafc4..0d36259b690d 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -97,7 +97,7 @@ static int evm_find_protected_xattrs(struct dentry *dentry) if (!(inode->i_opflags & IOP_XATTR)) return -EOPNOTSUPP; - list_for_each_entry_rcu(xattr, &evm_config_xattrnames, list) { + list_for_each_entry_lockless(xattr, &evm_config_xattrnames, list) { error = __vfs_getxattr(dentry, inode, xattr->name, NULL, 0); if (error < 0) { if (error == -ENODATA) @@ -228,7 +228,7 @@ static int evm_protected_xattr(const char *req_xattr_name) struct xattr_list *xattr; namelen = strlen(req_xattr_name); - list_for_each_entry_rcu(xattr, &evm_config_xattrnames, list) { + list_for_each_entry_lockless(xattr, &evm_config_xattrnames, list) { if ((strlen(xattr->name) == namelen) && (strncmp(req_xattr_name, xattr->name, namelen) == 0)) { found = 1; diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c index 39ad1038d45d..cfc3075769bb 100644 --- a/security/integrity/evm/evm_secfs.c +++ b/security/integrity/evm/evm_secfs.c @@ -232,7 +232,14 @@ static ssize_t evm_write_xattrs(struct file *file, const char __user *buf, goto out; } - /* Guard against races in evm_read_xattrs */ + /* + * xattr_list_mutex guards against races in evm_read_xattrs(). + * Entries are only added to the evm_config_xattrnames list + * and never deleted. Therefore, the list is traversed + * using list_for_each_entry_lockless() without holding + * the mutex in evm_calc_hmac_or_hash(), evm_find_protected_xattrs() + * and evm_protected_xattr(). + */ mutex_lock(&xattr_list_mutex); list_for_each_entry(tmp, &evm_config_xattrnames, list) { if (strcmp(xattr->name, tmp->name) == 0) { diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 64317d95363e..df93ac258e01 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -36,7 +36,7 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 }; #define IMA_DIGEST_SIZE SHA1_DIGEST_SIZE #define IMA_EVENT_NAME_LEN_MAX 255 -#define IMA_HASH_BITS 9 +#define IMA_HASH_BITS 10 #define IMA_MEASURE_HTABLE_SIZE (1 << IMA_HASH_BITS) #define IMA_TEMPLATE_FIELD_ID_MAX_LEN 16 @@ -45,13 +45,19 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 }; #define IMA_TEMPLATE_IMA_NAME "ima" #define IMA_TEMPLATE_IMA_FMT "d|n" +#define NR_BANKS(chip) ((chip != NULL) ? chip->nr_allocated_banks : 0) + /* current content of the policy */ extern int ima_policy_flag; /* set during initialization */ extern int ima_hash_algo; +extern int ima_sha1_idx __ro_after_init; +extern int ima_hash_algo_idx __ro_after_init; +extern int ima_extra_slots __ro_after_init; extern int ima_appraise; extern struct tpm_chip *ima_tpm_chip; +extern const char boot_aggregate_name[]; /* IMA event related data */ struct ima_event_data { @@ -92,7 +98,7 @@ struct ima_template_desc { struct ima_template_entry { int pcr; - u8 digest[TPM_DIGEST_SIZE]; /* sha1 or md5 measurement hash */ + struct tpm_digest *digests; struct ima_template_desc *template_desc; /* template descriptor */ u32 template_data_len; struct ima_field_data template_data[0]; /* template related data */ @@ -138,9 +144,8 @@ int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash); int ima_calc_buffer_hash(const void *buf, loff_t len, struct ima_digest_data *hash); int ima_calc_field_array_hash(struct ima_field_data *field_data, - struct ima_template_desc *desc, int num_fields, - struct ima_digest_data *hash); -int __init ima_calc_boot_aggregate(struct ima_digest_data *hash); + struct ima_template_entry *entry); +int ima_calc_boot_aggregate(struct ima_digest_data *hash); void ima_add_violation(struct file *file, const unsigned char *filename, struct integrity_iint_cache *iint, const char *op, const char *cause); @@ -175,9 +180,10 @@ struct ima_h_table { }; extern struct ima_h_table ima_htable; -static inline unsigned long ima_hash_key(u8 *digest) +static inline unsigned int ima_hash_key(u8 *digest) { - return hash_long(*digest, IMA_HASH_BITS); + /* there is no point in taking a hash of part of a digest */ + return (digest[0] | digest[1] << 8) % IMA_MEASURE_HTABLE_SIZE; } #define __ima_hooks(hook) \ diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index f6bc00914aa5..bf22de8b7ce0 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -27,6 +27,7 @@ void ima_free_template_entry(struct ima_template_entry *entry) for (i = 0; i < entry->template_desc->num_fields; i++) kfree(entry->template_data[i].data); + kfree(entry->digests); kfree(entry); } @@ -38,6 +39,7 @@ int ima_alloc_init_template(struct ima_event_data *event_data, struct ima_template_desc *desc) { struct ima_template_desc *template_desc; + struct tpm_digest *digests; int i, result = 0; if (desc) @@ -50,6 +52,15 @@ int ima_alloc_init_template(struct ima_event_data *event_data, if (!*entry) return -ENOMEM; + digests = kcalloc(NR_BANKS(ima_tpm_chip) + ima_extra_slots, + sizeof(*digests), GFP_NOFS); + if (!digests) { + kfree(*entry); + *entry = NULL; + return -ENOMEM; + } + + (*entry)->digests = digests; (*entry)->template_desc = template_desc; for (i = 0; i < template_desc->num_fields; i++) { const struct ima_template_field *field = @@ -96,26 +107,16 @@ int ima_store_template(struct ima_template_entry *entry, static const char audit_cause[] = "hashing_error"; char *template_name = entry->template_desc->name; int result; - struct { - struct ima_digest_data hdr; - char digest[TPM_DIGEST_SIZE]; - } hash; if (!violation) { - int num_fields = entry->template_desc->num_fields; - - /* this function uses default algo */ - hash.hdr.algo = HASH_ALGO_SHA1; result = ima_calc_field_array_hash(&entry->template_data[0], - entry->template_desc, - num_fields, &hash.hdr); + entry); if (result < 0) { integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, template_name, op, audit_cause, result, 0); return result; } - memcpy(entry->digest, hash.hdr.digest, hash.hdr.length); } entry->pcr = pcr; result = ima_add_template_entry(entry, violation, op, inode, filename); diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c index 423c84f95a14..220b14920c37 100644 --- a/security/integrity/ima/ima_crypto.c +++ b/security/integrity/ima/ima_crypto.c @@ -57,7 +57,22 @@ MODULE_PARM_DESC(ahash_bufsize, "Maximum ahash buffer size"); static struct crypto_shash *ima_shash_tfm; static struct crypto_ahash *ima_ahash_tfm; -int __init ima_init_crypto(void) +struct ima_algo_desc { + struct crypto_shash *tfm; + enum hash_algo algo; +}; + +int ima_sha1_idx __ro_after_init; +int ima_hash_algo_idx __ro_after_init; +/* + * Additional number of slots reserved, as needed, for SHA1 + * and IMA default algo. + */ +int ima_extra_slots __ro_after_init; + +static struct ima_algo_desc *ima_algo_array; + +static int __init ima_init_ima_crypto(void) { long rc; @@ -76,26 +91,137 @@ int __init ima_init_crypto(void) static struct crypto_shash *ima_alloc_tfm(enum hash_algo algo) { struct crypto_shash *tfm = ima_shash_tfm; - int rc; + int rc, i; if (algo < 0 || algo >= HASH_ALGO__LAST) algo = ima_hash_algo; - if (algo != ima_hash_algo) { - tfm = crypto_alloc_shash(hash_algo_name[algo], 0, 0); - if (IS_ERR(tfm)) { - rc = PTR_ERR(tfm); - pr_err("Can not allocate %s (reason: %d)\n", - hash_algo_name[algo], rc); - } + if (algo == ima_hash_algo) + return tfm; + + for (i = 0; i < NR_BANKS(ima_tpm_chip) + ima_extra_slots; i++) + if (ima_algo_array[i].tfm && ima_algo_array[i].algo == algo) + return ima_algo_array[i].tfm; + + tfm = crypto_alloc_shash(hash_algo_name[algo], 0, 0); + if (IS_ERR(tfm)) { + rc = PTR_ERR(tfm); + pr_err("Can not allocate %s (reason: %d)\n", + hash_algo_name[algo], rc); } return tfm; } +int __init ima_init_crypto(void) +{ + enum hash_algo algo; + long rc; + int i; + + rc = ima_init_ima_crypto(); + if (rc) + return rc; + + ima_sha1_idx = -1; + ima_hash_algo_idx = -1; + + for (i = 0; i < NR_BANKS(ima_tpm_chip); i++) { + algo = ima_tpm_chip->allocated_banks[i].crypto_id; + if (algo == HASH_ALGO_SHA1) + ima_sha1_idx = i; + + if (algo == ima_hash_algo) + ima_hash_algo_idx = i; + } + + if (ima_sha1_idx < 0) { + ima_sha1_idx = NR_BANKS(ima_tpm_chip) + ima_extra_slots++; + if (ima_hash_algo == HASH_ALGO_SHA1) + ima_hash_algo_idx = ima_sha1_idx; + } + + if (ima_hash_algo_idx < 0) + ima_hash_algo_idx = NR_BANKS(ima_tpm_chip) + ima_extra_slots++; + + ima_algo_array = kcalloc(NR_BANKS(ima_tpm_chip) + ima_extra_slots, + sizeof(*ima_algo_array), GFP_KERNEL); + if (!ima_algo_array) { + rc = -ENOMEM; + goto out; + } + + for (i = 0; i < NR_BANKS(ima_tpm_chip); i++) { + algo = ima_tpm_chip->allocated_banks[i].crypto_id; + ima_algo_array[i].algo = algo; + + /* unknown TPM algorithm */ + if (algo == HASH_ALGO__LAST) + continue; + + if (algo == ima_hash_algo) { + ima_algo_array[i].tfm = ima_shash_tfm; + continue; + } + + ima_algo_array[i].tfm = ima_alloc_tfm(algo); + if (IS_ERR(ima_algo_array[i].tfm)) { + if (algo == HASH_ALGO_SHA1) { + rc = PTR_ERR(ima_algo_array[i].tfm); + ima_algo_array[i].tfm = NULL; + goto out_array; + } + + ima_algo_array[i].tfm = NULL; + } + } + + if (ima_sha1_idx >= NR_BANKS(ima_tpm_chip)) { + if (ima_hash_algo == HASH_ALGO_SHA1) { + ima_algo_array[ima_sha1_idx].tfm = ima_shash_tfm; + } else { + ima_algo_array[ima_sha1_idx].tfm = + ima_alloc_tfm(HASH_ALGO_SHA1); + if (IS_ERR(ima_algo_array[ima_sha1_idx].tfm)) { + rc = PTR_ERR(ima_algo_array[ima_sha1_idx].tfm); + goto out_array; + } + } + + ima_algo_array[ima_sha1_idx].algo = HASH_ALGO_SHA1; + } + + if (ima_hash_algo_idx >= NR_BANKS(ima_tpm_chip) && + ima_hash_algo_idx != ima_sha1_idx) { + ima_algo_array[ima_hash_algo_idx].tfm = ima_shash_tfm; + ima_algo_array[ima_hash_algo_idx].algo = ima_hash_algo; + } + + return 0; +out_array: + for (i = 0; i < NR_BANKS(ima_tpm_chip) + ima_extra_slots; i++) { + if (!ima_algo_array[i].tfm || + ima_algo_array[i].tfm == ima_shash_tfm) + continue; + + crypto_free_shash(ima_algo_array[i].tfm); + } +out: + crypto_free_shash(ima_shash_tfm); + return rc; +} + static void ima_free_tfm(struct crypto_shash *tfm) { - if (tfm != ima_shash_tfm) - crypto_free_shash(tfm); + int i; + + if (tfm == ima_shash_tfm) + return; + + for (i = 0; i < NR_BANKS(ima_tpm_chip) + ima_extra_slots; i++) + if (ima_algo_array[i].tfm == tfm) + return; + + crypto_free_shash(tfm); } /** @@ -411,7 +537,7 @@ int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash) loff_t i_size; int rc; struct file *f = file; - bool new_file_instance = false, modified_flags = false; + bool new_file_instance = false, modified_mode = false; /* * For consistency, fail file's opened with the O_DIRECT flag on @@ -431,13 +557,13 @@ int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash) f = dentry_open(&file->f_path, flags, file->f_cred); if (IS_ERR(f)) { /* - * Cannot open the file again, lets modify f_flags + * Cannot open the file again, lets modify f_mode * of original and continue */ pr_info_ratelimited("Unable to reopen file for reading.\n"); f = file; - f->f_flags |= FMODE_READ; - modified_flags = true; + f->f_mode |= FMODE_READ; + modified_mode = true; } else { new_file_instance = true; } @@ -455,8 +581,8 @@ int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash) out: if (new_file_instance) fput(f); - else if (modified_flags) - f->f_flags &= ~FMODE_READ; + else if (modified_mode) + f->f_mode &= ~FMODE_READ; return rc; } @@ -464,17 +590,15 @@ out: * Calculate the hash of template data */ static int ima_calc_field_array_hash_tfm(struct ima_field_data *field_data, - struct ima_template_desc *td, - int num_fields, - struct ima_digest_data *hash, - struct crypto_shash *tfm) + struct ima_template_entry *entry, + int tfm_idx) { - SHASH_DESC_ON_STACK(shash, tfm); + SHASH_DESC_ON_STACK(shash, ima_algo_array[tfm_idx].tfm); + struct ima_template_desc *td = entry->template_desc; + int num_fields = entry->template_desc->num_fields; int rc, i; - shash->tfm = tfm; - - hash->length = crypto_shash_digestsize(tfm); + shash->tfm = ima_algo_array[tfm_idx].tfm; rc = crypto_shash_init(shash); if (rc != 0) @@ -504,27 +628,44 @@ static int ima_calc_field_array_hash_tfm(struct ima_field_data *field_data, } if (!rc) - rc = crypto_shash_final(shash, hash->digest); + rc = crypto_shash_final(shash, entry->digests[tfm_idx].digest); return rc; } int ima_calc_field_array_hash(struct ima_field_data *field_data, - struct ima_template_desc *desc, int num_fields, - struct ima_digest_data *hash) + struct ima_template_entry *entry) { - struct crypto_shash *tfm; - int rc; + u16 alg_id; + int rc, i; - tfm = ima_alloc_tfm(hash->algo); - if (IS_ERR(tfm)) - return PTR_ERR(tfm); + rc = ima_calc_field_array_hash_tfm(field_data, entry, ima_sha1_idx); + if (rc) + return rc; - rc = ima_calc_field_array_hash_tfm(field_data, desc, num_fields, - hash, tfm); + entry->digests[ima_sha1_idx].alg_id = TPM_ALG_SHA1; - ima_free_tfm(tfm); + for (i = 0; i < NR_BANKS(ima_tpm_chip) + ima_extra_slots; i++) { + if (i == ima_sha1_idx) + continue; + + if (i < NR_BANKS(ima_tpm_chip)) { + alg_id = ima_tpm_chip->allocated_banks[i].alg_id; + entry->digests[i].alg_id = alg_id; + } + + /* for unmapped TPM algorithms digest is still a padded SHA1 */ + if (!ima_algo_array[i].tfm) { + memcpy(entry->digests[i].digest, + entry->digests[ima_sha1_idx].digest, + TPM_DIGEST_SIZE); + continue; + } + rc = ima_calc_field_array_hash_tfm(field_data, entry, i); + if (rc) + return rc; + } return rc; } @@ -645,7 +786,7 @@ int ima_calc_buffer_hash(const void *buf, loff_t len, return calc_buffer_shash(buf, len, hash); } -static void __init ima_pcrread(u32 idx, struct tpm_digest *d) +static void ima_pcrread(u32 idx, struct tpm_digest *d) { if (!ima_tpm_chip) return; @@ -655,18 +796,29 @@ static void __init ima_pcrread(u32 idx, struct tpm_digest *d) } /* - * Calculate the boot aggregate hash + * The boot_aggregate is a cumulative hash over TPM registers 0 - 7. With + * TPM 1.2 the boot_aggregate was based on reading the SHA1 PCRs, but with + * TPM 2.0 hash agility, TPM chips could support multiple TPM PCR banks, + * allowing firmware to configure and enable different banks. + * + * Knowing which TPM bank is read to calculate the boot_aggregate digest + * needs to be conveyed to a verifier. For this reason, use the same + * hash algorithm for reading the TPM PCRs as for calculating the boot + * aggregate digest as stored in the measurement list. */ -static int __init ima_calc_boot_aggregate_tfm(char *digest, - struct crypto_shash *tfm) +static int ima_calc_boot_aggregate_tfm(char *digest, u16 alg_id, + struct crypto_shash *tfm) { - struct tpm_digest d = { .alg_id = TPM_ALG_SHA1, .digest = {0} }; + struct tpm_digest d = { .alg_id = alg_id, .digest = {0} }; int rc; u32 i; SHASH_DESC_ON_STACK(shash, tfm); shash->tfm = tfm; + pr_devel("calculating the boot-aggregate based on TPM bank: %04x\n", + d.alg_id); + rc = crypto_shash_init(shash); if (rc != 0) return rc; @@ -675,24 +827,48 @@ static int __init ima_calc_boot_aggregate_tfm(char *digest, for (i = TPM_PCR0; i < TPM_PCR8; i++) { ima_pcrread(i, &d); /* now accumulate with current aggregate */ - rc = crypto_shash_update(shash, d.digest, TPM_DIGEST_SIZE); + rc = crypto_shash_update(shash, d.digest, + crypto_shash_digestsize(tfm)); } if (!rc) crypto_shash_final(shash, digest); return rc; } -int __init ima_calc_boot_aggregate(struct ima_digest_data *hash) +int ima_calc_boot_aggregate(struct ima_digest_data *hash) { struct crypto_shash *tfm; - int rc; + u16 crypto_id, alg_id; + int rc, i, bank_idx = -1; + + for (i = 0; i < ima_tpm_chip->nr_allocated_banks; i++) { + crypto_id = ima_tpm_chip->allocated_banks[i].crypto_id; + if (crypto_id == hash->algo) { + bank_idx = i; + break; + } + + if (crypto_id == HASH_ALGO_SHA256) + bank_idx = i; + + if (bank_idx == -1 && crypto_id == HASH_ALGO_SHA1) + bank_idx = i; + } + + if (bank_idx == -1) { + pr_err("No suitable TPM algorithm for boot aggregate\n"); + return 0; + } + + hash->algo = ima_tpm_chip->allocated_banks[bank_idx].crypto_id; tfm = ima_alloc_tfm(hash->algo); if (IS_ERR(tfm)) return PTR_ERR(tfm); hash->length = crypto_shash_digestsize(tfm); - rc = ima_calc_boot_aggregate_tfm(hash->digest, tfm); + alg_id = ima_tpm_chip->allocated_banks[bank_idx].alg_id; + rc = ima_calc_boot_aggregate_tfm(hash->digest, alg_id, tfm); ima_free_tfm(tfm); diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index a71e822a6e92..e3fcad871861 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -150,7 +150,7 @@ int ima_measurements_show(struct seq_file *m, void *v) ima_putc(m, &pcr, sizeof(e->pcr)); /* 2nd: template digest */ - ima_putc(m, e->digest, TPM_DIGEST_SIZE); + ima_putc(m, e->digests[ima_sha1_idx].digest, TPM_DIGEST_SIZE); /* 3rd: template name size */ namelen = !ima_canonical_fmt ? strlen(template_name) : @@ -233,7 +233,7 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v) seq_printf(m, "%2d ", e->pcr); /* 2nd: SHA1 template hash */ - ima_print_digest(m, e->digest, TPM_DIGEST_SIZE); + ima_print_digest(m, e->digests[ima_sha1_idx].digest, TPM_DIGEST_SIZE); /* 3th: template name */ seq_printf(m, " %s", template_name); @@ -338,8 +338,7 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf, integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL, "policy_update", "signed policy required", 1, 0); - if (ima_appraise & IMA_APPRAISE_ENFORCE) - result = -EACCES; + result = -EACCES; } else { result = ima_parse_add_rule(data); } diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index 567468188a61..4902fe7bd570 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -19,13 +19,13 @@ #include "ima.h" /* name for boot aggregate entry */ -static const char boot_aggregate_name[] = "boot_aggregate"; +const char boot_aggregate_name[] = "boot_aggregate"; struct tpm_chip *ima_tpm_chip; /* Add the boot aggregate to the IMA measurement list and extend * the PCR register. * - * Calculate the boot aggregate, a SHA1 over tpm registers 0-7, + * Calculate the boot aggregate, a hash over tpm registers 0-7, * assuming a TPM chip exists, and zeroes if the TPM chip does not * exist. Add the boot aggregate measurement to the measurement * list and extend the PCR register. @@ -49,15 +49,27 @@ static int __init ima_add_boot_aggregate(void) int violation = 0; struct { struct ima_digest_data hdr; - char digest[TPM_DIGEST_SIZE]; + char digest[TPM_MAX_DIGEST_SIZE]; } hash; memset(iint, 0, sizeof(*iint)); memset(&hash, 0, sizeof(hash)); iint->ima_hash = &hash.hdr; - iint->ima_hash->algo = HASH_ALGO_SHA1; - iint->ima_hash->length = SHA1_DIGEST_SIZE; - + iint->ima_hash->algo = ima_hash_algo; + iint->ima_hash->length = hash_digest_size[ima_hash_algo]; + + /* + * With TPM 2.0 hash agility, TPM chips could support multiple TPM + * PCR banks, allowing firmware to configure and enable different + * banks. The SHA1 bank is not necessarily enabled. + * + * Use the same hash algorithm for reading the TPM PCRs as for + * calculating the boot aggregate digest. Preference is given to + * the configured IMA default hash algorithm. Otherwise, use the + * TCG required banks - SHA256 for TPM 2.0, SHA1 for TPM 1.2. + * Ultimately select SHA1 also for TPM 2.0 if the SHA256 PCR bank + * is not found. + */ if (ima_tpm_chip) { result = ima_calc_boot_aggregate(&hash.hdr); if (result < 0) { diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 9d0abedeae77..c1583d98c5e5 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -394,6 +394,58 @@ int ima_file_mmap(struct file *file, unsigned long prot) } /** + * ima_file_mprotect - based on policy, limit mprotect change + * @prot: contains the protection that will be applied by the kernel. + * + * Files can be mmap'ed read/write and later changed to execute to circumvent + * IMA's mmap appraisal policy rules. Due to locking issues (mmap semaphore + * would be taken before i_mutex), files can not be measured or appraised at + * this point. Eliminate this integrity gap by denying the mprotect + * PROT_EXECUTE change, if an mmap appraise policy rule exists. + * + * On mprotect change success, return 0. On failure, return -EACESS. + */ +int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot) +{ + struct ima_template_desc *template; + struct file *file = vma->vm_file; + char filename[NAME_MAX]; + char *pathbuf = NULL; + const char *pathname = NULL; + struct inode *inode; + int result = 0; + int action; + u32 secid; + int pcr; + + /* Is mprotect making an mmap'ed file executable? */ + if (!(ima_policy_flag & IMA_APPRAISE) || !vma->vm_file || + !(prot & PROT_EXEC) || (vma->vm_flags & VM_EXEC)) + return 0; + + security_task_getsecid(current, &secid); + inode = file_inode(vma->vm_file); + action = ima_get_action(inode, current_cred(), secid, MAY_EXEC, + MMAP_CHECK, &pcr, &template, 0); + + /* Is the mmap'ed file in policy? */ + if (!(action & (IMA_MEASURE | IMA_APPRAISE_SUBMASK))) + return 0; + + if (action & IMA_APPRAISE_SUBMASK) + result = -EPERM; + + file = vma->vm_file; + pathname = ima_d_path(&file->f_path, &pathbuf, filename); + integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, pathname, + "collect_data", "failed-mprotect", result, 0); + if (pathbuf) + __putname(pathbuf); + + return result; +} + +/** * ima_bprm_check - based on policy, collect/store measurement. * @bprm: contains the linux_binprm structure * @@ -792,6 +844,9 @@ static int __init init_ima(void) error = ima_init(); } + if (error) + return error; + error = register_blocking_lsm_notifier(&ima_lsm_policy_notifier); if (error) pr_warn("Couldn't register LSM notifier, error %d\n", error); diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index c334e0dc6083..e493063a3c34 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -204,7 +204,7 @@ static struct ima_rule_entry *arch_policy_entry __ro_after_init; static LIST_HEAD(ima_default_rules); static LIST_HEAD(ima_policy_rules); static LIST_HEAD(ima_temp_rules); -static struct list_head *ima_rules; +static struct list_head *ima_rules = &ima_default_rules; /* Pre-allocated buffer used for matching keyrings. */ static char *ima_keyrings; @@ -644,9 +644,12 @@ static void add_rules(struct ima_rule_entry *entries, int count, list_add_tail(&entry->list, &ima_policy_rules); } if (entries[i].action == APPRAISE) { - temp_ima_appraise |= ima_appraise_flag(entries[i].func); - if (entries[i].func == POLICY_CHECK) - temp_ima_appraise |= IMA_APPRAISE_POLICY; + if (entries != build_appraise_rules) + temp_ima_appraise |= + ima_appraise_flag(entries[i].func); + else + build_ima_appraise |= + ima_appraise_flag(entries[i].func); } } } @@ -765,7 +768,6 @@ void __init ima_init_policy(void) ARRAY_SIZE(default_appraise_rules), IMA_DEFAULT_POLICY); - ima_rules = &ima_default_rules; ima_update_policy_flag(); } diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c index 8753212ddb18..fb4ec270f620 100644 --- a/security/integrity/ima/ima_queue.c +++ b/security/integrity/ima/ima_queue.c @@ -55,7 +55,8 @@ static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value, key = ima_hash_key(digest_value); rcu_read_lock(); hlist_for_each_entry_rcu(qe, &ima_htable.queue[key], hnext) { - rc = memcmp(qe->entry->digest, digest_value, TPM_DIGEST_SIZE); + rc = memcmp(qe->entry->digests[ima_hash_algo_idx].digest, + digest_value, hash_digest_size[ima_hash_algo]); if ((rc == 0) && (qe->entry->pcr == pcr)) { ret = qe; break; @@ -75,7 +76,7 @@ static int get_binary_runtime_size(struct ima_template_entry *entry) int size = 0; size += sizeof(u32); /* pcr */ - size += sizeof(entry->digest); + size += TPM_DIGEST_SIZE; size += sizeof(int); /* template name size field */ size += strlen(entry->template_desc->name); size += sizeof(entry->template_data_len); @@ -107,7 +108,7 @@ static int ima_add_digest_entry(struct ima_template_entry *entry, atomic_long_inc(&ima_htable.len); if (update_htable) { - key = ima_hash_key(entry->digest); + key = ima_hash_key(entry->digests[ima_hash_algo_idx].digest); hlist_add_head_rcu(&qe->hnext, &ima_htable.queue[key]); } @@ -134,18 +135,14 @@ unsigned long ima_get_binary_runtime_size(void) return binary_runtime_size + sizeof(struct ima_kexec_hdr); }; -static int ima_pcr_extend(const u8 *hash, int pcr) +static int ima_pcr_extend(struct tpm_digest *digests_arg, int pcr) { int result = 0; - int i; if (!ima_tpm_chip) return result; - for (i = 0; i < ima_tpm_chip->nr_allocated_banks; i++) - memcpy(digests[i].digest, hash, TPM_DIGEST_SIZE); - - result = tpm_pcr_extend(ima_tpm_chip, pcr, digests); + result = tpm_pcr_extend(ima_tpm_chip, pcr, digests_arg); if (result != 0) pr_err("Error Communicating to TPM chip, result: %d\n", result); return result; @@ -163,7 +160,8 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation, const char *op, struct inode *inode, const unsigned char *filename) { - u8 digest[TPM_DIGEST_SIZE]; + u8 *digest = entry->digests[ima_hash_algo_idx].digest; + struct tpm_digest *digests_arg = entry->digests; const char *audit_cause = "hash_added"; char tpm_audit_cause[AUDIT_CAUSE_LEN_MAX]; int audit_info = 1; @@ -171,7 +169,6 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation, mutex_lock(&ima_extend_list_mutex); if (!violation) { - memcpy(digest, entry->digest, sizeof(digest)); if (ima_lookup_digest_entry(digest, entry->pcr)) { audit_cause = "hash_exists"; result = -EEXIST; @@ -187,9 +184,9 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation, } if (violation) /* invalidate pcr */ - memset(digest, 0xff, sizeof(digest)); + digests_arg = digests; - tpmresult = ima_pcr_extend(digest, entry->pcr); + tpmresult = ima_pcr_extend(digests_arg, entry->pcr); if (tpmresult != 0) { snprintf(tpm_audit_cause, AUDIT_CAUSE_LEN_MAX, "TPM_error(%d)", tpmresult); @@ -215,6 +212,8 @@ int ima_restore_measurement_entry(struct ima_template_entry *entry) int __init ima_init_digests(void) { + u16 digest_size; + u16 crypto_id; int i; if (!ima_tpm_chip) @@ -225,8 +224,17 @@ int __init ima_init_digests(void) if (!digests) return -ENOMEM; - for (i = 0; i < ima_tpm_chip->nr_allocated_banks; i++) + for (i = 0; i < ima_tpm_chip->nr_allocated_banks; i++) { digests[i].alg_id = ima_tpm_chip->allocated_banks[i].alg_id; + digest_size = ima_tpm_chip->allocated_banks[i].digest_size; + crypto_id = ima_tpm_chip->allocated_banks[i].crypto_id; + + /* for unmapped TPM algorithms digest is still a padded SHA1 */ + if (crypto_id == HASH_ALGO__LAST) + digest_size = SHA1_DIGEST_SIZE; + + memset(digests[i].digest, 0xff, digest_size); + } return 0; } diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c index 062d9ad49afb..5a2def40a733 100644 --- a/security/integrity/ima/ima_template.c +++ b/security/integrity/ima/ima_template.c @@ -301,6 +301,7 @@ static int ima_restore_template_data(struct ima_template_desc *template_desc, int template_data_size, struct ima_template_entry **entry) { + struct tpm_digest *digests; int ret = 0; int i; @@ -309,11 +310,21 @@ static int ima_restore_template_data(struct ima_template_desc *template_desc, if (!*entry) return -ENOMEM; + digests = kcalloc(NR_BANKS(ima_tpm_chip) + ima_extra_slots, + sizeof(*digests), GFP_NOFS); + if (!digests) { + kfree(*entry); + return -ENOMEM; + } + + (*entry)->digests = digests; + ret = ima_parse_buf(template_data, template_data + template_data_size, NULL, template_desc->num_fields, (*entry)->template_data, NULL, NULL, ENFORCE_FIELDS | ENFORCE_BUFEND, "template data"); if (ret < 0) { + kfree((*entry)->digests); kfree(*entry); return ret; } @@ -346,6 +357,7 @@ static int ima_restore_template_data(struct ima_template_desc *template_desc, int ima_restore_measurement_list(loff_t size, void *buf) { char template_name[MAX_TEMPLATE_NAME_LEN]; + unsigned char zero[TPM_DIGEST_SIZE] = { 0 }; struct ima_kexec_hdr *khdr = buf; struct ima_field_data hdr[HDR__LAST] = { @@ -445,8 +457,17 @@ int ima_restore_measurement_list(loff_t size, void *buf) if (ret < 0) break; - memcpy(entry->digest, hdr[HDR_DIGEST].data, - hdr[HDR_DIGEST].len); + if (memcmp(hdr[HDR_DIGEST].data, zero, sizeof(zero))) { + ret = ima_calc_field_array_hash( + &entry->template_data[0], + entry); + if (ret < 0) { + pr_err("cannot calculate template digest\n"); + ret = -EINVAL; + break; + } + } + entry->pcr = !ima_canonical_fmt ? *(hdr[HDR_PCR].data) : le32_to_cpu(*(hdr[HDR_PCR].data)); ret = ima_restore_measurement_entry(entry); diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c index 9cd1e50f3ccc..635c6ac05050 100644 --- a/security/integrity/ima/ima_template_lib.c +++ b/security/integrity/ima/ima_template_lib.c @@ -286,6 +286,24 @@ int ima_eventdigest_init(struct ima_event_data *event_data, goto out; } + if ((const char *)event_data->filename == boot_aggregate_name) { + if (ima_tpm_chip) { + hash.hdr.algo = HASH_ALGO_SHA1; + result = ima_calc_boot_aggregate(&hash.hdr); + + /* algo can change depending on available PCR banks */ + if (!result && hash.hdr.algo != HASH_ALGO_SHA1) + result = -EINVAL; + + if (result < 0) + memset(&hash, 0, sizeof(hash)); + } + + cur_digest = hash.hdr.digest; + cur_digestsize = hash_digest_size[HASH_ALGO_SHA1]; + goto out; + } + if (!event_data->file) /* missing info to re-calculate the digest */ return -EINVAL; diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 298b73794d8b..16c1894c29bb 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -107,7 +107,7 @@ struct ima_digest_data { } ng; u8 data[2]; } xattr; - u8 digest[0]; + u8 digest[]; } __packed; /* @@ -119,7 +119,7 @@ struct signature_v2_hdr { uint8_t hash_algo; /* Digest algorithm [enum hash_algo] */ __be32 keyid; /* IMA key identifier - not X509/PGP specific */ __be16 sig_size; /* signature size */ - uint8_t sig[0]; /* signature payload */ + uint8_t sig[]; /* signature payload */ } __packed; /* integrity data associated with an inode */ diff --git a/security/keys/Kconfig b/security/keys/Kconfig index 47c041563d41..83bc23409164 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig @@ -60,9 +60,7 @@ config BIG_KEYS bool "Large payload keys" depends on KEYS depends on TMPFS - select CRYPTO - select CRYPTO_AES - select CRYPTO_GCM + depends on CRYPTO_LIB_CHACHA20POLY1305 = y help This option provides support for holding large keys within the kernel (for example Kerberos ticket caches). The data may be stored out to @@ -116,3 +114,12 @@ config KEY_DH_OPERATIONS in the kernel. If you are unsure as to whether this is required, answer N. + +config KEY_NOTIFICATIONS + bool "Provide key/keyring change notifications" + depends on KEYS && WATCH_QUEUE + help + This option provides support for getting change notifications on keys + and keyrings on which the caller has View permission. This makes use + of the /dev/watch_queue misc device to handle the notification + buffer and provides KEYCTL_WATCH_KEY to enable/disable watches. diff --git a/security/keys/big_key.c b/security/keys/big_key.c index 82008f900930..dd708e8f13c0 100644 --- a/security/keys/big_key.c +++ b/security/keys/big_key.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* Large capacity key type * - * Copyright (C) 2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. + * Copyright (C) 2017-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) */ @@ -12,20 +12,10 @@ #include <linux/file.h> #include <linux/shmem_fs.h> #include <linux/err.h> -#include <linux/scatterlist.h> #include <linux/random.h> -#include <linux/vmalloc.h> #include <keys/user-type.h> #include <keys/big_key-type.h> -#include <crypto/aead.h> -#include <crypto/gcm.h> - -struct big_key_buf { - unsigned int nr_pages; - void *virt; - struct scatterlist *sg; - struct page *pages[]; -}; +#include <crypto/chacha20poly1305.h> /* * Layout of key payload words. @@ -38,14 +28,6 @@ enum { }; /* - * Crypto operation with big_key data - */ -enum big_key_op { - BIG_KEY_ENC, - BIG_KEY_DEC, -}; - -/* * If the data is under this limit, there's no point creating a shm file to * hold it as the permanently resident metadata for the shmem fs will be at * least as large as the data. @@ -53,16 +35,6 @@ enum big_key_op { #define BIG_KEY_FILE_THRESHOLD (sizeof(struct inode) + sizeof(struct dentry)) /* - * Key size for big_key data encryption - */ -#define ENC_KEY_SIZE 32 - -/* - * Authentication tag length - */ -#define ENC_AUTHTAG_SIZE 16 - -/* * big_key defined keys take an arbitrary string as the description and an * arbitrary blob of data as the payload */ @@ -75,136 +47,20 @@ struct key_type key_type_big_key = { .destroy = big_key_destroy, .describe = big_key_describe, .read = big_key_read, - /* no ->update(); don't add it without changing big_key_crypt() nonce */ + .update = big_key_update, }; /* - * Crypto names for big_key data authenticated encryption - */ -static const char big_key_alg_name[] = "gcm(aes)"; -#define BIG_KEY_IV_SIZE GCM_AES_IV_SIZE - -/* - * Crypto algorithms for big_key data authenticated encryption - */ -static struct crypto_aead *big_key_aead; - -/* - * Since changing the key affects the entire object, we need a mutex. - */ -static DEFINE_MUTEX(big_key_aead_lock); - -/* - * Encrypt/decrypt big_key data - */ -static int big_key_crypt(enum big_key_op op, struct big_key_buf *buf, size_t datalen, u8 *key) -{ - int ret; - struct aead_request *aead_req; - /* We always use a zero nonce. The reason we can get away with this is - * because we're using a different randomly generated key for every - * different encryption. Notably, too, key_type_big_key doesn't define - * an .update function, so there's no chance we'll wind up reusing the - * key to encrypt updated data. Simply put: one key, one encryption. - */ - u8 zero_nonce[BIG_KEY_IV_SIZE]; - - aead_req = aead_request_alloc(big_key_aead, GFP_KERNEL); - if (!aead_req) - return -ENOMEM; - - memset(zero_nonce, 0, sizeof(zero_nonce)); - aead_request_set_crypt(aead_req, buf->sg, buf->sg, datalen, zero_nonce); - aead_request_set_callback(aead_req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL); - aead_request_set_ad(aead_req, 0); - - mutex_lock(&big_key_aead_lock); - if (crypto_aead_setkey(big_key_aead, key, ENC_KEY_SIZE)) { - ret = -EAGAIN; - goto error; - } - if (op == BIG_KEY_ENC) - ret = crypto_aead_encrypt(aead_req); - else - ret = crypto_aead_decrypt(aead_req); -error: - mutex_unlock(&big_key_aead_lock); - aead_request_free(aead_req); - return ret; -} - -/* - * Free up the buffer. - */ -static void big_key_free_buffer(struct big_key_buf *buf) -{ - unsigned int i; - - if (buf->virt) { - memset(buf->virt, 0, buf->nr_pages * PAGE_SIZE); - vunmap(buf->virt); - } - - for (i = 0; i < buf->nr_pages; i++) - if (buf->pages[i]) - __free_page(buf->pages[i]); - - kfree(buf); -} - -/* - * Allocate a buffer consisting of a set of pages with a virtual mapping - * applied over them. - */ -static void *big_key_alloc_buffer(size_t len) -{ - struct big_key_buf *buf; - unsigned int npg = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; - unsigned int i, l; - - buf = kzalloc(sizeof(struct big_key_buf) + - sizeof(struct page) * npg + - sizeof(struct scatterlist) * npg, - GFP_KERNEL); - if (!buf) - return NULL; - - buf->nr_pages = npg; - buf->sg = (void *)(buf->pages + npg); - sg_init_table(buf->sg, npg); - - for (i = 0; i < buf->nr_pages; i++) { - buf->pages[i] = alloc_page(GFP_KERNEL); - if (!buf->pages[i]) - goto nomem; - - l = min_t(size_t, len, PAGE_SIZE); - sg_set_page(&buf->sg[i], buf->pages[i], l, 0); - len -= l; - } - - buf->virt = vmap(buf->pages, buf->nr_pages, VM_MAP, PAGE_KERNEL); - if (!buf->virt) - goto nomem; - - return buf; - -nomem: - big_key_free_buffer(buf); - return NULL; -} - -/* * Preparse a big key */ int big_key_preparse(struct key_preparsed_payload *prep) { - struct big_key_buf *buf; struct path *path = (struct path *)&prep->payload.data[big_key_path]; struct file *file; - u8 *enckey; + u8 *buf, *enckey; ssize_t written; - size_t datalen = prep->datalen, enclen = datalen + ENC_AUTHTAG_SIZE; + size_t datalen = prep->datalen; + size_t enclen = datalen + CHACHA20POLY1305_AUTHTAG_SIZE; int ret; if (datalen <= 0 || datalen > 1024 * 1024 || !prep->data) @@ -220,28 +76,28 @@ int big_key_preparse(struct key_preparsed_payload *prep) * to be swapped out if needed. * * File content is stored encrypted with randomly generated key. + * Since the key is random for each file, we can set the nonce + * to zero, provided we never define a ->update() call. */ loff_t pos = 0; - buf = big_key_alloc_buffer(enclen); + buf = kvmalloc(enclen, GFP_KERNEL); if (!buf) return -ENOMEM; - memcpy(buf->virt, prep->data, datalen); /* generate random key */ - enckey = kmalloc(ENC_KEY_SIZE, GFP_KERNEL); + enckey = kmalloc(CHACHA20POLY1305_KEY_SIZE, GFP_KERNEL); if (!enckey) { ret = -ENOMEM; goto error; } - ret = get_random_bytes_wait(enckey, ENC_KEY_SIZE); + ret = get_random_bytes_wait(enckey, CHACHA20POLY1305_KEY_SIZE); if (unlikely(ret)) goto err_enckey; - /* encrypt aligned data */ - ret = big_key_crypt(BIG_KEY_ENC, buf, datalen, enckey); - if (ret) - goto err_enckey; + /* encrypt data */ + chacha20poly1305_encrypt(buf, prep->data, datalen, NULL, 0, + 0, enckey); /* save aligned data to file */ file = shmem_kernel_file_setup("", enclen, 0); @@ -250,11 +106,11 @@ int big_key_preparse(struct key_preparsed_payload *prep) goto err_enckey; } - written = kernel_write(file, buf->virt, enclen, &pos); + written = kernel_write(file, buf, enclen, &pos); if (written != enclen) { ret = written; if (written >= 0) - ret = -ENOMEM; + ret = -EIO; goto err_fput; } @@ -265,7 +121,8 @@ int big_key_preparse(struct key_preparsed_payload *prep) *path = file->f_path; path_get(path); fput(file); - big_key_free_buffer(buf); + memzero_explicit(buf, enclen); + kvfree(buf); } else { /* Just store the data in a buffer */ void *data = kmalloc(datalen, GFP_KERNEL); @@ -283,7 +140,8 @@ err_fput: err_enckey: kzfree(enckey); error: - big_key_free_buffer(buf); + memzero_explicit(buf, enclen); + kvfree(buf); return ret; } @@ -334,6 +192,23 @@ void big_key_destroy(struct key *key) } /* + * Update a big key + */ +int big_key_update(struct key *key, struct key_preparsed_payload *prep) +{ + int ret; + + ret = key_payload_reserve(key, prep->datalen); + if (ret < 0) + return ret; + + if (key_is_positive(key)) + big_key_destroy(key); + + return generic_key_instantiate(key, prep); +} + +/* * describe the big_key key */ void big_key_describe(const struct key *key, struct seq_file *m) @@ -361,14 +236,13 @@ long big_key_read(const struct key *key, char *buffer, size_t buflen) return datalen; if (datalen > BIG_KEY_FILE_THRESHOLD) { - struct big_key_buf *buf; struct path *path = (struct path *)&key->payload.data[big_key_path]; struct file *file; - u8 *enckey = (u8 *)key->payload.data[big_key_data]; - size_t enclen = datalen + ENC_AUTHTAG_SIZE; + u8 *buf, *enckey = (u8 *)key->payload.data[big_key_data]; + size_t enclen = datalen + CHACHA20POLY1305_AUTHTAG_SIZE; loff_t pos = 0; - buf = big_key_alloc_buffer(enclen); + buf = kvmalloc(enclen, GFP_KERNEL); if (!buf) return -ENOMEM; @@ -379,25 +253,28 @@ long big_key_read(const struct key *key, char *buffer, size_t buflen) } /* read file to kernel and decrypt */ - ret = kernel_read(file, buf->virt, enclen, &pos); - if (ret >= 0 && ret != enclen) { - ret = -EIO; + ret = kernel_read(file, buf, enclen, &pos); + if (ret != enclen) { + if (ret >= 0) + ret = -EIO; goto err_fput; } - ret = big_key_crypt(BIG_KEY_DEC, buf, enclen, enckey); - if (ret) + ret = chacha20poly1305_decrypt(buf, buf, enclen, NULL, 0, 0, + enckey) ? 0 : -EBADMSG; + if (unlikely(ret)) goto err_fput; ret = datalen; /* copy out decrypted data */ - memcpy(buffer, buf->virt, datalen); + memcpy(buffer, buf, datalen); err_fput: fput(file); error: - big_key_free_buffer(buf); + memzero_explicit(buf, enclen); + kvfree(buf); } else { ret = datalen; memcpy(buffer, key->payload.data[big_key_data], datalen); @@ -411,39 +288,7 @@ error: */ static int __init big_key_init(void) { - int ret; - - /* init block cipher */ - big_key_aead = crypto_alloc_aead(big_key_alg_name, 0, CRYPTO_ALG_ASYNC); - if (IS_ERR(big_key_aead)) { - ret = PTR_ERR(big_key_aead); - pr_err("Can't alloc crypto: %d\n", ret); - return ret; - } - - if (unlikely(crypto_aead_ivsize(big_key_aead) != BIG_KEY_IV_SIZE)) { - WARN(1, "big key algorithm changed?"); - ret = -EINVAL; - goto free_aead; - } - - ret = crypto_aead_setauthsize(big_key_aead, ENC_AUTHTAG_SIZE); - if (ret < 0) { - pr_err("Can't set crypto auth tag len: %d\n", ret); - goto free_aead; - } - - ret = register_key_type(&key_type_big_key); - if (ret < 0) { - pr_err("Can't register type: %d\n", ret); - goto free_aead; - } - - return 0; - -free_aead: - crypto_free_aead(big_key_aead); - return ret; + return register_key_type(&key_type_big_key); } late_initcall(big_key_init); diff --git a/security/keys/compat.c b/security/keys/compat.c index b975f8f11124..6ee9d8f6a4a5 100644 --- a/security/keys/compat.c +++ b/security/keys/compat.c @@ -156,6 +156,9 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option, case KEYCTL_CAPABILITIES: return keyctl_capabilities(compat_ptr(arg2), arg3); + case KEYCTL_WATCH_KEY: + return keyctl_watch_key(arg2, arg3, arg4); + default: return -EOPNOTSUPP; } diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index f6797ba44bf7..14cf81d1a30b 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -323,19 +323,6 @@ error: return ukey; } -static int calc_hash(struct crypto_shash *tfm, u8 *digest, - const u8 *buf, unsigned int buflen) -{ - SHASH_DESC_ON_STACK(desc, tfm); - int err; - - desc->tfm = tfm; - - err = crypto_shash_digest(desc, buf, buflen, digest); - shash_desc_zero(desc); - return err; -} - static int calc_hmac(u8 *digest, const u8 *key, unsigned int keylen, const u8 *buf, unsigned int buflen) { @@ -351,7 +338,7 @@ static int calc_hmac(u8 *digest, const u8 *key, unsigned int keylen, err = crypto_shash_setkey(tfm, key, keylen); if (!err) - err = calc_hash(tfm, digest, buf, buflen); + err = crypto_shash_tfm_digest(tfm, buf, buflen, digest); crypto_free_shash(tfm); return err; } @@ -381,7 +368,8 @@ static int get_derived_key(u8 *derived_key, enum derived_key_type key_type, memcpy(derived_buf + strlen(derived_buf) + 1, master_key, master_keylen); - ret = calc_hash(hash_tfm, derived_key, derived_buf, derived_buf_len); + ret = crypto_shash_tfm_digest(hash_tfm, derived_buf, derived_buf_len, + derived_key); kzfree(derived_buf); return ret; } diff --git a/security/keys/gc.c b/security/keys/gc.c index 671dd730ecfc..3c90807476eb 100644 --- a/security/keys/gc.c +++ b/security/keys/gc.c @@ -131,6 +131,11 @@ static noinline void key_gc_unused_keys(struct list_head *keys) kdebug("- %u", key->serial); key_check(key); +#ifdef CONFIG_KEY_NOTIFICATIONS + remove_watch_list(key->watchers, key->serial); + key->watchers = NULL; +#endif + /* Throw away the key data if the key is instantiated */ if (state == KEY_IS_POSITIVE && key->type->destroy) key->type->destroy(key); diff --git a/security/keys/internal.h b/security/keys/internal.h index 6d0ca48ae9a5..338a526cbfa5 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -15,6 +15,7 @@ #include <linux/task_work.h> #include <linux/keyctl.h> #include <linux/refcount.h> +#include <linux/watch_queue.h> #include <linux/compat.h> #include <linux/mm.h> #include <linux/vmalloc.h> @@ -99,7 +100,8 @@ extern int __key_link_begin(struct key *keyring, const struct keyring_index_key *index_key, struct assoc_array_edit **_edit); extern int __key_link_check_live_key(struct key *keyring, struct key *key); -extern void __key_link(struct key *key, struct assoc_array_edit **_edit); +extern void __key_link(struct key *keyring, struct key *key, + struct assoc_array_edit **_edit); extern void __key_link_end(struct key *keyring, const struct keyring_index_key *index_key, struct assoc_array_edit *edit); @@ -165,7 +167,6 @@ extern bool lookup_user_key_possessed(const struct key *key, const struct key_match_data *match_data); #define KEY_LOOKUP_CREATE 0x01 #define KEY_LOOKUP_PARTIAL 0x02 -#define KEY_LOOKUP_FOR_UNLINK 0x04 extern long join_session_keyring(const char *name); extern void key_change_session_keyring(struct callback_head *twork); @@ -181,14 +182,32 @@ extern void key_gc_keytype(struct key_type *ktype); extern int key_task_permission(const key_ref_t key_ref, const struct cred *cred, - key_perm_t perm); + enum key_need_perm need_perm); + +static inline void notify_key(struct key *key, + enum key_notification_subtype subtype, u32 aux) +{ +#ifdef CONFIG_KEY_NOTIFICATIONS + struct key_notification n = { + .watch.type = WATCH_TYPE_KEY_NOTIFY, + .watch.subtype = subtype, + .watch.info = watch_sizeof(n), + .key_id = key_serial(key), + .aux = aux, + }; + + post_watch_notification(key->watchers, &n.watch, current_cred(), + n.key_id); +#endif +} /* * Check to see whether permission is granted to use a key in the desired way. */ -static inline int key_permission(const key_ref_t key_ref, unsigned perm) +static inline int key_permission(const key_ref_t key_ref, + enum key_need_perm need_perm) { - return key_task_permission(key_ref, current_cred(), perm); + return key_task_permission(key_ref, current_cred(), need_perm); } extern struct key_type key_type_request_key_auth; @@ -333,6 +352,15 @@ static inline long keyctl_pkey_e_d_s(int op, extern long keyctl_capabilities(unsigned char __user *_buffer, size_t buflen); +#ifdef CONFIG_KEY_NOTIFICATIONS +extern long keyctl_watch_key(key_serial_t, int, int); +#else +static inline long keyctl_watch_key(key_serial_t key_id, int watch_fd, int watch_id) +{ + return -EOPNOTSUPP; +} +#endif + /* * Debugging key validation */ @@ -350,15 +378,4 @@ static inline void key_check(const struct key *key) #define key_check(key) do {} while(0) #endif - -/* - * Helper function to clear and free a kvmalloc'ed memory object. - */ -static inline void __kvzfree(const void *addr, size_t len) -{ - if (addr) { - memset((void *)addr, 0, len); - kvfree(addr); - } -} #endif /* _INTERNAL_H */ diff --git a/security/keys/key.c b/security/keys/key.c index e959b3c96b48..e282c6179b21 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -444,6 +444,7 @@ static int __key_instantiate_and_link(struct key *key, /* mark the key as being instantiated */ atomic_inc(&key->user->nikeys); mark_key_instantiated(key, 0); + notify_key(key, NOTIFY_KEY_INSTANTIATED, 0); if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags)) awaken = 1; @@ -453,7 +454,7 @@ static int __key_instantiate_and_link(struct key *key, if (test_bit(KEY_FLAG_KEEP, &keyring->flags)) set_bit(KEY_FLAG_KEEP, &key->flags); - __key_link(key, _edit); + __key_link(keyring, key, _edit); } /* disable the authorisation key */ @@ -601,6 +602,7 @@ int key_reject_and_link(struct key *key, /* mark the key as being negatively instantiated */ atomic_inc(&key->user->nikeys); mark_key_instantiated(key, -error); + notify_key(key, NOTIFY_KEY_INSTANTIATED, -error); key->expiry = ktime_get_real_seconds() + timeout; key_schedule_gc(key->expiry + key_gc_delay); @@ -611,7 +613,7 @@ int key_reject_and_link(struct key *key, /* and link it into the destination keyring */ if (keyring && link_ret == 0) - __key_link(key, &edit); + __key_link(keyring, key, &edit); /* disable the authorisation key */ if (authkey) @@ -764,9 +766,11 @@ static inline key_ref_t __key_update(key_ref_t key_ref, down_write(&key->sem); ret = key->type->update(key, prep); - if (ret == 0) + if (ret == 0) { /* Updating a negative key positively instantiates it */ mark_key_instantiated(key, 0); + notify_key(key, NOTIFY_KEY_UPDATED, 0); + } up_write(&key->sem); @@ -1023,9 +1027,11 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen) down_write(&key->sem); ret = key->type->update(key, &prep); - if (ret == 0) + if (ret == 0) { /* Updating a negative key positively instantiates it */ mark_key_instantiated(key, 0); + notify_key(key, NOTIFY_KEY_UPDATED, 0); + } up_write(&key->sem); @@ -1057,15 +1063,17 @@ void key_revoke(struct key *key) * instantiated */ down_write_nested(&key->sem, 1); - if (!test_and_set_bit(KEY_FLAG_REVOKED, &key->flags) && - key->type->revoke) - key->type->revoke(key); - - /* set the death time to no more than the expiry time */ - time = ktime_get_real_seconds(); - if (key->revoked_at == 0 || key->revoked_at > time) { - key->revoked_at = time; - key_schedule_gc(key->revoked_at + key_gc_delay); + if (!test_and_set_bit(KEY_FLAG_REVOKED, &key->flags)) { + notify_key(key, NOTIFY_KEY_REVOKED, 0); + if (key->type->revoke) + key->type->revoke(key); + + /* set the death time to no more than the expiry time */ + time = ktime_get_real_seconds(); + if (key->revoked_at == 0 || key->revoked_at > time) { + key->revoked_at = time; + key_schedule_gc(key->revoked_at + key_gc_delay); + } } up_write(&key->sem); @@ -1087,8 +1095,10 @@ void key_invalidate(struct key *key) if (!test_bit(KEY_FLAG_INVALIDATED, &key->flags)) { down_write_nested(&key->sem, 1); - if (!test_and_set_bit(KEY_FLAG_INVALIDATED, &key->flags)) + if (!test_and_set_bit(KEY_FLAG_INVALIDATED, &key->flags)) { + notify_key(key, NOTIFY_KEY_INVALIDATED, 0); key_schedule_gc_links(); + } up_write(&key->sem); } } diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 5e01192e222a..9febd37a168f 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -37,7 +37,9 @@ static const unsigned char keyrings_capabilities[2] = { KEYCTL_CAPS0_MOVE ), [1] = (KEYCTL_CAPS1_NS_KEYRING_NAME | - KEYCTL_CAPS1_NS_KEY_TAG), + KEYCTL_CAPS1_NS_KEY_TAG | + (IS_ENABLED(CONFIG_KEY_NOTIFICATIONS) ? KEYCTL_CAPS1_NOTIFICATIONS : 0) + ), }; static int key_get_type_from_user(char *type, @@ -142,10 +144,7 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type, key_ref_put(keyring_ref); error3: - if (payload) { - memzero_explicit(payload, plen); - kvfree(payload); - } + kvfree_sensitive(payload, plen); error2: kfree(description); error: @@ -360,7 +359,7 @@ long keyctl_update_key(key_serial_t id, key_ref_put(key_ref); error2: - __kvzfree(payload, plen); + kvfree_sensitive(payload, plen); error: return ret; } @@ -432,7 +431,7 @@ long keyctl_invalidate_key(key_serial_t id) /* Root is permitted to invalidate certain special keys */ if (capable(CAP_SYS_ADMIN)) { - key_ref = lookup_user_key(id, 0, 0); + key_ref = lookup_user_key(id, 0, KEY_SYSADMIN_OVERRIDE); if (IS_ERR(key_ref)) goto error; if (test_bit(KEY_FLAG_ROOT_CAN_INVAL, @@ -477,7 +476,8 @@ long keyctl_keyring_clear(key_serial_t ringid) /* Root is permitted to invalidate certain special keyrings */ if (capable(CAP_SYS_ADMIN)) { - keyring_ref = lookup_user_key(ringid, 0, 0); + keyring_ref = lookup_user_key(ringid, 0, + KEY_SYSADMIN_OVERRIDE); if (IS_ERR(keyring_ref)) goto error; if (test_bit(KEY_FLAG_ROOT_CAN_CLEAR, @@ -561,7 +561,7 @@ long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid) goto error; } - key_ref = lookup_user_key(id, KEY_LOOKUP_FOR_UNLINK, 0); + key_ref = lookup_user_key(id, KEY_LOOKUP_PARTIAL, KEY_NEED_UNLINK); if (IS_ERR(key_ref)) { ret = PTR_ERR(key_ref); goto error2; @@ -661,7 +661,7 @@ long keyctl_describe_key(key_serial_t keyid, key_put(instkey); key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, - 0); + KEY_AUTHTOKEN_OVERRIDE); if (!IS_ERR(key_ref)) goto okay; } @@ -831,7 +831,7 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen) size_t key_data_len; /* find the key first */ - key_ref = lookup_user_key(keyid, 0, 0); + key_ref = lookup_user_key(keyid, 0, KEY_DEFER_PERM_CHECK); if (IS_ERR(key_ref)) { ret = -ENOKEY; goto out; @@ -878,7 +878,7 @@ can_read_key: * * Allocating a temporary buffer to hold the keys before * transferring them to user buffer to avoid potential - * deadlock involving page fault and mmap_sem. + * deadlock involving page fault and mmap_lock. * * key_data_len = (buflen <= PAGE_SIZE) * ? buflen : actual length of key data @@ -914,7 +914,7 @@ can_read_key: */ if (ret > key_data_len) { if (unlikely(key_data)) - __kvzfree(key_data, key_data_len); + kvfree_sensitive(key_data, key_data_len); key_data_len = ret; continue; /* Allocate buffer */ } @@ -923,7 +923,7 @@ can_read_key: ret = -EFAULT; break; } - __kvzfree(key_data, key_data_len); + kvfree_sensitive(key_data, key_data_len); key_put_out: key_put(key); @@ -1039,6 +1039,7 @@ long keyctl_chown_key(key_serial_t id, uid_t user, gid_t group) if (group != (gid_t) -1) key->gid = gid; + notify_key(key, NOTIFY_KEY_SETATTR, 0); ret = 0; error_put: @@ -1089,6 +1090,7 @@ long keyctl_setperm_key(key_serial_t id, key_perm_t perm) /* 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())) { key->perm = perm; + notify_key(key, NOTIFY_KEY_SETATTR, 0); ret = 0; } @@ -1225,10 +1227,7 @@ long keyctl_instantiate_key_common(key_serial_t id, keyctl_change_reqkey_auth(NULL); error2: - if (payload) { - memzero_explicit(payload, plen); - kvfree(payload); - } + kvfree_sensitive(payload, plen); error: return ret; } @@ -1467,7 +1466,7 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout) key_put(instkey); key_ref = lookup_user_key(id, KEY_LOOKUP_PARTIAL, - 0); + KEY_AUTHTOKEN_OVERRIDE); if (!IS_ERR(key_ref)) goto okay; } @@ -1480,10 +1479,12 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout) okay: key = key_ref_to_ptr(key_ref); ret = 0; - if (test_bit(KEY_FLAG_KEEP, &key->flags)) + if (test_bit(KEY_FLAG_KEEP, &key->flags)) { ret = -EPERM; - else + } else { key_set_timeout(key, timeout); + notify_key(key, NOTIFY_KEY_SETATTR, 0); + } key_put(key); error: @@ -1573,7 +1574,8 @@ long keyctl_get_security(key_serial_t keyid, return PTR_ERR(instkey); key_put(instkey); - key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, 0); + key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, + KEY_AUTHTOKEN_OVERRIDE); if (IS_ERR(key_ref)) return PTR_ERR(key_ref); } @@ -1757,6 +1759,90 @@ error: return ret; } +#ifdef CONFIG_KEY_NOTIFICATIONS +/* + * Watch for changes to a key. + * + * The caller must have View permission to watch a key or keyring. + */ +long keyctl_watch_key(key_serial_t id, int watch_queue_fd, int watch_id) +{ + struct watch_queue *wqueue; + struct watch_list *wlist = NULL; + struct watch *watch = NULL; + struct key *key; + key_ref_t key_ref; + long ret; + + if (watch_id < -1 || watch_id > 0xff) + return -EINVAL; + + key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE, KEY_NEED_VIEW); + if (IS_ERR(key_ref)) + return PTR_ERR(key_ref); + key = key_ref_to_ptr(key_ref); + + wqueue = get_watch_queue(watch_queue_fd); + if (IS_ERR(wqueue)) { + ret = PTR_ERR(wqueue); + goto err_key; + } + + if (watch_id >= 0) { + ret = -ENOMEM; + if (!key->watchers) { + wlist = kzalloc(sizeof(*wlist), GFP_KERNEL); + if (!wlist) + goto err_wqueue; + init_watch_list(wlist, NULL); + } + + watch = kzalloc(sizeof(*watch), GFP_KERNEL); + if (!watch) + goto err_wlist; + + init_watch(watch, wqueue); + watch->id = key->serial; + watch->info_id = (u32)watch_id << WATCH_INFO_ID__SHIFT; + + ret = security_watch_key(key); + if (ret < 0) + goto err_watch; + + down_write(&key->sem); + if (!key->watchers) { + key->watchers = wlist; + wlist = NULL; + } + + ret = add_watch_to_object(watch, key->watchers); + up_write(&key->sem); + + if (ret == 0) + watch = NULL; + } else { + ret = -EBADSLT; + if (key->watchers) { + down_write(&key->sem); + ret = remove_watch_from_object(key->watchers, + wqueue, key_serial(key), + false); + up_write(&key->sem); + } + } + +err_watch: + kfree(watch); +err_wlist: + kfree(wlist); +err_wqueue: + put_watch_queue(wqueue); +err_key: + key_put(key); + return ret; +} +#endif /* CONFIG_KEY_NOTIFICATIONS */ + /* * Get keyrings subsystem capabilities. */ @@ -1926,6 +2012,9 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3, case KEYCTL_CAPABILITIES: return keyctl_capabilities((unsigned char __user *)arg2, (size_t)arg3); + case KEYCTL_WATCH_KEY: + return keyctl_watch_key((key_serial_t)arg2, (int)arg3, (int)arg4); + default: return -EOPNOTSUPP; } diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 5ca620d31cd3..14abfe765b7e 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -1056,12 +1056,14 @@ int keyring_restrict(key_ref_t keyring_ref, const char *type, down_write(&keyring->sem); down_write(&keyring_serialise_restrict_sem); - if (keyring->restrict_link) + if (keyring->restrict_link) { ret = -EEXIST; - else if (keyring_detect_restriction_cycle(keyring, restrict_link)) + } else if (keyring_detect_restriction_cycle(keyring, restrict_link)) { ret = -EDEADLK; - else + } else { keyring->restrict_link = restrict_link; + notify_key(keyring, NOTIFY_KEY_SETATTR, 0); + } up_write(&keyring_serialise_restrict_sem); up_write(&keyring->sem); @@ -1362,12 +1364,14 @@ int __key_link_check_live_key(struct key *keyring, struct key *key) * holds at most one link to any given key of a particular type+description * combination. */ -void __key_link(struct key *key, struct assoc_array_edit **_edit) +void __key_link(struct key *keyring, struct key *key, + struct assoc_array_edit **_edit) { __key_get(key); assoc_array_insert_set_object(*_edit, keyring_key_to_ptr(key)); assoc_array_apply_edit(*_edit); *_edit = NULL; + notify_key(keyring, NOTIFY_KEY_LINKED, key_serial(key)); } /* @@ -1451,7 +1455,7 @@ int key_link(struct key *keyring, struct key *key) if (ret == 0) ret = __key_link_check_live_key(keyring, key); if (ret == 0) - __key_link(key, &edit); + __key_link(keyring, key, &edit); error_end: __key_link_end(keyring, &key->index_key, edit); @@ -1483,7 +1487,7 @@ static int __key_unlink_begin(struct key *keyring, struct key *key, struct assoc_array_edit *edit; BUG_ON(*_edit != NULL); - + edit = assoc_array_delete(&keyring->keys, &keyring_assoc_array_ops, &key->index_key); if (IS_ERR(edit)) @@ -1503,6 +1507,7 @@ static void __key_unlink(struct key *keyring, struct key *key, struct assoc_array_edit **_edit) { assoc_array_apply_edit(*_edit); + notify_key(keyring, NOTIFY_KEY_UNLINKED, key_serial(key)); *_edit = NULL; key_payload_reserve(keyring, keyring->datalen - KEYQUOTA_LINK_BYTES); } @@ -1621,7 +1626,7 @@ int key_move(struct key *key, goto error; __key_unlink(from_keyring, key, &from_edit); - __key_link(key, &to_edit); + __key_link(to_keyring, key, &to_edit); error: __key_link_end(to_keyring, &key->index_key, to_edit); __key_unlink_end(from_keyring, key, from_edit); @@ -1655,6 +1660,7 @@ int keyring_clear(struct key *keyring) } else { if (edit) assoc_array_apply_edit(edit); + notify_key(keyring, NOTIFY_KEY_CLEARED, 0); key_payload_reserve(keyring, 0); ret = 0; } diff --git a/security/keys/permission.c b/security/keys/permission.c index 085f907b64ac..4a61f804e80f 100644 --- a/security/keys/permission.c +++ b/security/keys/permission.c @@ -13,7 +13,7 @@ * key_task_permission - Check a key can be used * @key_ref: The key to check. * @cred: The credentials to use. - * @perm: The permissions to check for. + * @need_perm: The permission required. * * Check to see whether permission is granted to use a key in the desired way, * but permit the security modules to override. @@ -24,12 +24,30 @@ * permissions bits or the LSM check. */ int key_task_permission(const key_ref_t key_ref, const struct cred *cred, - unsigned perm) + enum key_need_perm need_perm) { struct key *key; - key_perm_t kperm; + key_perm_t kperm, mask; int ret; + switch (need_perm) { + default: + WARN_ON(1); + return -EACCES; + case KEY_NEED_UNLINK: + case KEY_SYSADMIN_OVERRIDE: + case KEY_AUTHTOKEN_OVERRIDE: + case KEY_DEFER_PERM_CHECK: + goto lsm; + + case KEY_NEED_VIEW: mask = KEY_OTH_VIEW; break; + case KEY_NEED_READ: mask = KEY_OTH_READ; break; + case KEY_NEED_WRITE: mask = KEY_OTH_WRITE; break; + case KEY_NEED_SEARCH: mask = KEY_OTH_SEARCH; break; + case KEY_NEED_LINK: mask = KEY_OTH_LINK; break; + case KEY_NEED_SETATTR: mask = KEY_OTH_SETATTR; break; + } + key = key_ref_to_ptr(key_ref); /* use the second 8-bits of permissions for keys the caller owns */ @@ -64,13 +82,12 @@ use_these_perms: if (is_key_possessed(key_ref)) kperm |= key->perm >> 24; - kperm = kperm & perm & KEY_NEED_ALL; - - if (kperm != perm) + if ((kperm & mask) != mask) return -EACCES; /* let LSM be the final arbiter */ - return security_key_permission(key_ref, cred, perm); +lsm: + return security_key_permission(key_ref, cred, need_perm); } EXPORT_SYMBOL(key_task_permission); diff --git a/security/keys/proc.c b/security/keys/proc.c index 415f3f1c2da0..d0cde6685627 100644 --- a/security/keys/proc.c +++ b/security/keys/proc.c @@ -139,6 +139,8 @@ static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos) n = key_serial_next(p, v); if (n) *_pos = key_node_serial(n); + else + (*_pos)++; return n; } diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 09541de31f2f..7e0232db1707 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -609,7 +609,7 @@ bool lookup_user_key_possessed(const struct key *key, * returned key reference. */ key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags, - key_perm_t perm) + enum key_need_perm need_perm) { struct keyring_search_context ctx = { .match_data.cmp = lookup_user_key_possessed, @@ -773,35 +773,33 @@ try_again: /* unlink does not use the nominated key in any way, so can skip all * the permission checks as it is only concerned with the keyring */ - if (lflags & KEY_LOOKUP_FOR_UNLINK) { - ret = 0; - goto error; - } - - if (!(lflags & KEY_LOOKUP_PARTIAL)) { - ret = wait_for_key_construction(key, true); - switch (ret) { - case -ERESTARTSYS: - goto invalid_key; - default: - if (perm) + if (need_perm != KEY_NEED_UNLINK) { + if (!(lflags & KEY_LOOKUP_PARTIAL)) { + ret = wait_for_key_construction(key, true); + switch (ret) { + case -ERESTARTSYS: + goto invalid_key; + default: + if (need_perm != KEY_AUTHTOKEN_OVERRIDE && + need_perm != KEY_DEFER_PERM_CHECK) + goto invalid_key; + case 0: + break; + } + } else if (need_perm != KEY_DEFER_PERM_CHECK) { + ret = key_validate(key); + if (ret < 0) goto invalid_key; - case 0: - break; } - } else if (perm) { - ret = key_validate(key); - if (ret < 0) + + ret = -EIO; + if (!(lflags & KEY_LOOKUP_PARTIAL) && + key_read_state(key) == KEY_IS_UNINSTANTIATED) goto invalid_key; } - ret = -EIO; - if (!(lflags & KEY_LOOKUP_PARTIAL) && - key_read_state(key) == KEY_IS_UNINSTANTIATED) - goto invalid_key; - /* check the permissions */ - ret = key_task_permission(key_ref, ctx.cred, perm); + ret = key_task_permission(key_ref, ctx.cred, need_perm); if (ret < 0) goto invalid_key; diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 957b9e3e1492..e1b9f1a80676 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -418,7 +418,7 @@ static int construct_alloc_key(struct keyring_search_context *ctx, goto key_already_present; if (dest_keyring) - __key_link(key, &edit); + __key_link(dest_keyring, key, &edit); mutex_unlock(&key_construction_mutex); if (dest_keyring) @@ -437,7 +437,7 @@ key_already_present: if (dest_keyring) { ret = __key_link_check_live_key(dest_keyring, key); if (ret == 0) - __key_link(key, &edit); + __key_link(dest_keyring, key, &edit); __key_link_end(dest_keyring, &ctx->index_key, edit); if (ret < 0) goto link_check_failed; diff --git a/security/lockdown/lockdown.c b/security/lockdown/lockdown.c index 5a952617a0eb..87cbdc64d272 100644 --- a/security/lockdown/lockdown.c +++ b/security/lockdown/lockdown.c @@ -150,7 +150,7 @@ static int __init lockdown_secfs_init(void) { struct dentry *dentry; - dentry = securityfs_create_file("lockdown", 0600, NULL, NULL, + dentry = securityfs_create_file("lockdown", 0644, NULL, NULL, &lockdown_ops); return PTR_ERR_OR_ZERO(dentry); } diff --git a/security/min_addr.c b/security/min_addr.c index 94d2b0cf0e7b..88c9a6a21f47 100644 --- a/security/min_addr.c +++ b/security/min_addr.c @@ -30,7 +30,7 @@ static void update_mmap_min_addr(void) * calls update_mmap_min_addr() so non MAP_FIXED hints get rounded properly */ int mmap_min_addr_handler(struct ctl_table *table, int write, - void __user *buffer, size_t *lenp, loff_t *ppos) + void *buffer, size_t *lenp, loff_t *ppos) { int ret; diff --git a/security/security.c b/security/security.c index 7fed24b9d57e..0ce3e73edd42 100644 --- a/security/security.c +++ b/security/security.c @@ -823,9 +823,14 @@ int security_vm_enough_memory_mm(struct mm_struct *mm, long pages) return __vm_enough_memory(mm, pages, cap_sys_admin); } -int security_bprm_set_creds(struct linux_binprm *bprm) +int security_bprm_creds_for_exec(struct linux_binprm *bprm) { - return call_int_hook(bprm_set_creds, 0, bprm); + return call_int_hook(bprm_creds_for_exec, 0, bprm); +} + +int security_bprm_creds_from_file(struct linux_binprm *bprm, struct file *file) +{ + return call_int_hook(bprm_creds_from_file, 0, bprm, file); } int security_bprm_check(struct linux_binprm *bprm) @@ -1459,6 +1464,7 @@ int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { return call_int_hook(file_ioctl, 0, file, cmd, arg); } +EXPORT_SYMBOL_GPL(security_file_ioctl); static inline unsigned long mmap_prot(struct file *file, unsigned long prot) { @@ -1512,7 +1518,12 @@ int security_mmap_addr(unsigned long addr) int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot, unsigned long prot) { - return call_int_hook(file_mprotect, 0, vma, reqprot, prot); + int ret; + + ret = call_int_hook(file_mprotect, 0, vma, reqprot, prot); + if (ret) + return ret; + return ima_file_mprotect(vma, prot); } int security_file_lock(struct file *file, unsigned int cmd) @@ -1685,6 +1696,12 @@ int security_task_fix_setuid(struct cred *new, const struct cred *old, return call_int_hook(task_fix_setuid, 0, new, old, flags); } +int security_task_fix_setgid(struct cred *new, const struct cred *old, + int flags) +{ + return call_int_hook(task_fix_setgid, 0, new, old, flags); +} + int security_task_setpgid(struct task_struct *p, pid_t pgid) { return call_int_hook(task_setpgid, 0, p, pgid); @@ -1965,8 +1982,20 @@ EXPORT_SYMBOL(security_ismaclabel); int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) { - return call_int_hook(secid_to_secctx, -EOPNOTSUPP, secid, secdata, - seclen); + struct security_hook_list *hp; + int rc; + + /* + * Currently, only one LSM can implement secid_to_secctx (i.e this + * LSM hook is not "stackable"). + */ + hlist_for_each_entry(hp, &security_hook_heads.secid_to_secctx, list) { + rc = hp->hook.secid_to_secctx(secid, secdata, seclen); + if (rc != LSM_RET_DEFAULT(secid_to_secctx)) + return rc; + } + + return LSM_RET_DEFAULT(secid_to_secctx); } EXPORT_SYMBOL(security_secid_to_secctx); @@ -2007,6 +2036,22 @@ int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen) } EXPORT_SYMBOL(security_inode_getsecctx); +#ifdef CONFIG_WATCH_QUEUE +int security_post_notification(const struct cred *w_cred, + const struct cred *cred, + struct watch_notification *n) +{ + return call_int_hook(post_notification, 0, w_cred, cred, n); +} +#endif /* CONFIG_WATCH_QUEUE */ + +#ifdef CONFIG_KEY_NOTIFICATIONS +int security_watch_key(struct key *key) +{ + return call_int_hook(watch_key, 0, key); +} +#endif + #ifdef CONFIG_SECURITY_NETWORK int security_unix_stream_connect(struct sock *sock, struct sock *other, struct sock *newsk) @@ -2382,10 +2427,10 @@ void security_key_free(struct key *key) call_void_hook(key_free, key); } -int security_key_permission(key_ref_t key_ref, - const struct cred *cred, unsigned perm) +int security_key_permission(key_ref_t key_ref, const struct cred *cred, + enum key_need_perm need_perm) { - return call_int_hook(key_permission, 0, key_ref, cred, perm); + return call_int_hook(key_permission, 0, key_ref, cred, need_perm); } int security_key_getsecurity(struct key *key, char **_buffer) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 0b4e32161b77..efa6108b1ce9 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2286,7 +2286,7 @@ static int check_nnp_nosuid(const struct linux_binprm *bprm, return -EACCES; } -static int selinux_bprm_set_creds(struct linux_binprm *bprm) +static int selinux_bprm_creds_for_exec(struct linux_binprm *bprm) { const struct task_security_struct *old_tsec; struct task_security_struct *new_tsec; @@ -2297,8 +2297,6 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) /* SELinux context only depends on initial program or script and not * the script interpreter */ - if (bprm->called_set_creds) - return 0; old_tsec = selinux_cred(current_cred()); new_tsec = selinux_cred(bprm->cred); @@ -5842,40 +5840,60 @@ static unsigned int selinux_ipv6_postroute(void *priv, static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb) { - int err = 0; - u32 perm; + int rc = 0; + unsigned int msg_len; + unsigned int data_len = skb->len; + unsigned char *data = skb->data; struct nlmsghdr *nlh; struct sk_security_struct *sksec = sk->sk_security; + u16 sclass = sksec->sclass; + u32 perm; - if (skb->len < NLMSG_HDRLEN) { - err = -EINVAL; - goto out; - } - nlh = nlmsg_hdr(skb); + while (data_len >= nlmsg_total_size(0)) { + nlh = (struct nlmsghdr *)data; + + /* NOTE: the nlmsg_len field isn't reliably set by some netlink + * users which means we can't reject skb's with bogus + * length fields; our solution is to follow what + * netlink_rcv_skb() does and simply skip processing at + * messages with length fields that are clearly junk + */ + if (nlh->nlmsg_len < NLMSG_HDRLEN || nlh->nlmsg_len > data_len) + return 0; - err = selinux_nlmsg_lookup(sksec->sclass, nlh->nlmsg_type, &perm); - if (err) { - if (err == -EINVAL) { + rc = selinux_nlmsg_lookup(sclass, nlh->nlmsg_type, &perm); + if (rc == 0) { + rc = sock_has_perm(sk, perm); + if (rc) + return rc; + } else if (rc == -EINVAL) { + /* -EINVAL is a missing msg/perm mapping */ pr_warn_ratelimited("SELinux: unrecognized netlink" - " message: protocol=%hu nlmsg_type=%hu sclass=%s" - " pid=%d comm=%s\n", - sk->sk_protocol, nlh->nlmsg_type, - secclass_map[sksec->sclass - 1].name, - task_pid_nr(current), current->comm); - if (!enforcing_enabled(&selinux_state) || - security_get_allow_unknown(&selinux_state)) - err = 0; + " message: protocol=%hu nlmsg_type=%hu sclass=%s" + " pid=%d comm=%s\n", + sk->sk_protocol, nlh->nlmsg_type, + secclass_map[sclass - 1].name, + task_pid_nr(current), current->comm); + if (enforcing_enabled(&selinux_state) && + !security_get_allow_unknown(&selinux_state)) + return rc; + rc = 0; + } else if (rc == -ENOENT) { + /* -ENOENT is a missing socket/class mapping, ignore */ + rc = 0; + } else { + return rc; } - /* Ignore */ - if (err == -ENOENT) - err = 0; - goto out; + /* move to the next message after applying netlink padding */ + msg_len = NLMSG_ALIGN(nlh->nlmsg_len); + if (msg_len >= data_len) + return 0; + data_len -= msg_len; + data += msg_len; } - err = sock_has_perm(sk, perm); -out: - return err; + return rc; } static void ipc_init_security(struct ipc_security_struct *isec, u16 sclass) @@ -6385,7 +6403,7 @@ static int selinux_setprocattr(const char *name, void *value, size_t size) /* Permission checking based on the specified context is performed during the actual operation (execve, open/mkdir/...), when we know the full context of the - operation. See selinux_bprm_set_creds for the execve + operation. See selinux_bprm_creds_for_exec for the execve checks and may_create for the file creation checks. The operation will then fail if the context is not permitted. */ tsec = selinux_cred(new); @@ -6541,20 +6559,43 @@ static void selinux_key_free(struct key *k) static int selinux_key_permission(key_ref_t key_ref, const struct cred *cred, - unsigned perm) + enum key_need_perm need_perm) { struct key *key; struct key_security_struct *ksec; - u32 sid; + u32 perm, sid; - /* if no specific permissions are requested, we skip the - permission check. No serious, additional covert channels - appear to be created. */ - if (perm == 0) + switch (need_perm) { + case KEY_NEED_VIEW: + perm = KEY__VIEW; + break; + case KEY_NEED_READ: + perm = KEY__READ; + break; + case KEY_NEED_WRITE: + perm = KEY__WRITE; + break; + case KEY_NEED_SEARCH: + perm = KEY__SEARCH; + break; + case KEY_NEED_LINK: + perm = KEY__LINK; + break; + case KEY_NEED_SETATTR: + perm = KEY__SETATTR; + break; + case KEY_NEED_UNLINK: + case KEY_SYSADMIN_OVERRIDE: + case KEY_AUTHTOKEN_OVERRIDE: + case KEY_DEFER_PERM_CHECK: return 0; + default: + WARN_ON(1); + return -EPERM; - sid = cred_sid(cred); + } + sid = cred_sid(cred); key = key_ref_to_ptr(key_ref); ksec = key->security; @@ -6576,6 +6617,17 @@ static int selinux_key_getsecurity(struct key *key, char **_buffer) *_buffer = context; return rc; } + +#ifdef CONFIG_KEY_NOTIFICATIONS +static int selinux_watch_key(struct key *key) +{ + struct key_security_struct *ksec = key->security; + u32 sid = current_sid(); + + return avc_has_perm(&selinux_state, + sid, ksec->sid, SECCLASS_KEY, KEY__VIEW, NULL); +} +#endif #endif #ifdef CONFIG_SECURITY_INFINIBAND @@ -6914,7 +6966,7 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(netlink_send, selinux_netlink_send), - LSM_HOOK_INIT(bprm_set_creds, selinux_bprm_set_creds), + LSM_HOOK_INIT(bprm_creds_for_exec, selinux_bprm_creds_for_exec), LSM_HOOK_INIT(bprm_committing_creds, selinux_bprm_committing_creds), LSM_HOOK_INIT(bprm_committed_creds, selinux_bprm_committed_creds), @@ -7091,6 +7143,9 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(key_free, selinux_key_free), LSM_HOOK_INIT(key_permission, selinux_key_permission), LSM_HOOK_INIT(key_getsecurity, selinux_key_getsecurity), +#ifdef CONFIG_KEY_NOTIFICATIONS + LSM_HOOK_INIT(watch_key, selinux_watch_key), +#endif #endif #ifdef CONFIG_AUDIT diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index 986f3ac14282..98e1513b608a 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -27,9 +27,9 @@ "audit_control", "setfcap" #define COMMON_CAP2_PERMS "mac_override", "mac_admin", "syslog", \ - "wake_alarm", "block_suspend", "audit_read" + "wake_alarm", "block_suspend", "audit_read", "perfmon", "bpf" -#if CAP_LAST_CAP > CAP_AUDIT_READ +#if CAP_LAST_CAP > CAP_BPF #error New capability defined, please update COMMON_CAP2_PERMS. #endif diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c index 7a92b028f722..0cc7cdd58465 100644 --- a/security/selinux/ss/conditional.c +++ b/security/selinux/ss/conditional.c @@ -424,7 +424,7 @@ int cond_read_list(struct policydb *p, void *fp) p->cond_list = kcalloc(len, sizeof(*p->cond_list), GFP_KERNEL); if (!p->cond_list) - return rc; + return -ENOMEM; rc = avtab_alloc(&(p->te_cond_avtab), p->te_avtab.nel); if (rc) diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index a30cad18931b..98f343005d6b 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -1054,14 +1054,14 @@ static int str_read(char **strp, gfp_t flags, void *fp, u32 len) if (!str) return -ENOMEM; - /* it's expected the caller should free the str */ - *strp = str; - rc = next_entry(str, fp, len); - if (rc) + if (rc) { + kfree(str); return rc; + } str[len] = '\0'; + *strp = str; return 0; } diff --git a/security/smack/smack.h b/security/smack/smack.h index 62529f382942..e9e817d09785 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -109,9 +109,7 @@ struct inode_smack { struct smack_known *smk_inode; /* label of the fso */ struct smack_known *smk_task; /* label of the task */ struct smack_known *smk_mmap; /* label of the mmap domain */ - struct mutex smk_lock; /* initialization lock */ int smk_flags; /* smack inode flags */ - struct rcu_head smk_rcu; /* for freeing inode_smack */ }; struct task_smack { @@ -148,7 +146,6 @@ struct smk_net4addr { struct smack_known *smk_label; /* label */ }; -#if IS_ENABLED(CONFIG_IPV6) /* * An entry in the table identifying IPv6 hosts. */ @@ -159,9 +156,7 @@ struct smk_net6addr { int smk_masks; /* mask size */ struct smack_known *smk_label; /* label */ }; -#endif /* CONFIG_IPV6 */ -#ifdef SMACK_IPV6_PORT_LABELING /* * An entry in the table identifying ports. */ @@ -174,7 +169,6 @@ struct smk_port_label { short smk_sock_type; /* Socket type */ short smk_can_reuse; }; -#endif /* SMACK_IPV6_PORT_LABELING */ struct smack_known_list_elem { struct list_head list; @@ -335,9 +329,7 @@ extern struct smack_known smack_known_web; extern struct mutex smack_known_lock; extern struct list_head smack_known_list; extern struct list_head smk_net4addr_list; -#if IS_ENABLED(CONFIG_IPV6) extern struct list_head smk_net6addr_list; -#endif /* CONFIG_IPV6 */ extern struct mutex smack_onlycap_lock; extern struct list_head smack_onlycap_list; @@ -505,10 +497,6 @@ static inline void smk_ad_setfield_u_fs_path_dentry(struct smk_audit_info *a, struct dentry *d) { } -static inline void smk_ad_setfield_u_fs_path_mnt(struct smk_audit_info *a, - struct vfsmount *m) -{ -} static inline void smk_ad_setfield_u_fs_inode(struct smk_audit_info *a, struct inode *i) { diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 8c61d175e195..8ffbf951b7ed 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -41,6 +41,7 @@ #include <linux/parser.h> #include <linux/fs_context.h> #include <linux/fs_parser.h> +#include <linux/watch_queue.h> #include "smack.h" #define TRANS_TRUE "TRUE" @@ -50,11 +51,8 @@ #define SMK_RECEIVING 1 #define SMK_SENDING 2 -#ifdef SMACK_IPV6_PORT_LABELING -DEFINE_MUTEX(smack_ipv6_lock); +static DEFINE_MUTEX(smack_ipv6_lock); static LIST_HEAD(smk_ipv6_port_list); -#endif -static struct kmem_cache *smack_inode_cache; struct kmem_cache *smack_rule_cache; int smack_enabled; @@ -316,7 +314,6 @@ static void init_inode_smack(struct inode *inode, struct smack_known *skp) isp->smk_inode = skp; isp->smk_flags = 0; - mutex_init(&isp->smk_lock); } /** @@ -891,12 +888,12 @@ static int smack_sb_statfs(struct dentry *dentry) */ /** - * smack_bprm_set_creds - set creds for exec + * smack_bprm_creds_for_exec - Update bprm->cred if needed for exec * @bprm: the exec information * * Returns 0 if it gets a blob, -EPERM if exec forbidden and -ENOMEM otherwise */ -static int smack_bprm_set_creds(struct linux_binprm *bprm) +static int smack_bprm_creds_for_exec(struct linux_binprm *bprm) { struct inode *inode = file_inode(bprm->file); struct task_smack *bsp = smack_cred(bprm->cred); @@ -904,9 +901,6 @@ static int smack_bprm_set_creds(struct linux_binprm *bprm) struct superblock_smack *sbsp; int rc; - if (bprm->called_set_creds) - return 0; - isp = smack_inode(inode); if (isp->smk_task == NULL || isp->smk_task == bsp->smk_task) return 0; @@ -2320,7 +2314,6 @@ static struct smack_known *smack_ipv4host_label(struct sockaddr_in *sip) return NULL; } -#if IS_ENABLED(CONFIG_IPV6) /* * smk_ipv6_localhost - Check for local ipv6 host address * @sip: the address @@ -2388,7 +2381,6 @@ static struct smack_known *smack_ipv6host_label(struct sockaddr_in6 *sip) return NULL; } -#endif /* CONFIG_IPV6 */ /** * smack_netlabel - Set the secattr on a socket @@ -2477,7 +2469,6 @@ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap) return smack_netlabel(sk, sk_lbl); } -#if IS_ENABLED(CONFIG_IPV6) /** * smk_ipv6_check - check Smack access * @subject: subject Smack label @@ -2510,7 +2501,6 @@ static int smk_ipv6_check(struct smack_known *subject, rc = smk_bu_note("IPv6 check", subject, object, MAY_WRITE, rc); return rc; } -#endif /* CONFIG_IPV6 */ #ifdef SMACK_IPV6_PORT_LABELING /** @@ -2599,6 +2589,7 @@ static void smk_ipv6_port_label(struct socket *sock, struct sockaddr *address) mutex_unlock(&smack_ipv6_lock); return; } +#endif /** * smk_ipv6_port_check - check Smack port access @@ -2661,7 +2652,6 @@ static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address, return smk_ipv6_check(skp, object, address, act); } -#endif /* SMACK_IPV6_PORT_LABELING */ /** * smack_inode_setsecurity - set smack xattrs @@ -2836,24 +2826,21 @@ static int smack_socket_connect(struct socket *sock, struct sockaddr *sap, return 0; if (IS_ENABLED(CONFIG_IPV6) && sap->sa_family == AF_INET6) { struct sockaddr_in6 *sip = (struct sockaddr_in6 *)sap; -#ifdef SMACK_IPV6_SECMARK_LABELING - struct smack_known *rsp; -#endif + struct smack_known *rsp = NULL; if (addrlen < SIN6_LEN_RFC2133) return 0; -#ifdef SMACK_IPV6_SECMARK_LABELING - rsp = smack_ipv6host_label(sip); + if (__is_defined(SMACK_IPV6_SECMARK_LABELING)) + rsp = smack_ipv6host_label(sip); if (rsp != NULL) { struct socket_smack *ssp = sock->sk->sk_security; rc = smk_ipv6_check(ssp->smk_out, rsp, sip, SMK_CONNECTING); } -#endif -#ifdef SMACK_IPV6_PORT_LABELING - rc = smk_ipv6_port_check(sock->sk, sip, SMK_CONNECTING); -#endif + if (__is_defined(SMACK_IPV6_PORT_LABELING)) + rc = smk_ipv6_port_check(sock->sk, sip, SMK_CONNECTING); + return rc; } if (sap->sa_family != AF_INET || addrlen < sizeof(struct sockaddr_in)) @@ -3273,13 +3260,12 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) isp = smack_inode(inode); - mutex_lock(&isp->smk_lock); /* * If the inode is already instantiated * take the quick way out */ if (isp->smk_flags & SMK_INODE_INSTANT) - goto unlockandout; + return; sbp = inode->i_sb; sbsp = sbp->s_security; @@ -3330,7 +3316,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) break; } isp->smk_flags |= SMK_INODE_INSTANT; - goto unlockandout; + return; } /* @@ -3465,8 +3451,6 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) isp->smk_flags |= (SMK_INODE_INSTANT | transflag); -unlockandout: - mutex_unlock(&isp->smk_lock); return; } @@ -4230,13 +4214,14 @@ static void smack_key_free(struct key *key) * smack_key_permission - Smack access on a key * @key_ref: gets to the object * @cred: the credentials to use - * @perm: requested key permissions + * @need_perm: requested key permission * * Return 0 if the task has read and write to the object, * an error code otherwise */ static int smack_key_permission(key_ref_t key_ref, - const struct cred *cred, unsigned perm) + const struct cred *cred, + enum key_need_perm need_perm) { struct key *keyp; struct smk_audit_info ad; @@ -4247,8 +4232,26 @@ static int smack_key_permission(key_ref_t key_ref, /* * Validate requested permissions */ - if (perm & ~KEY_NEED_ALL) + switch (need_perm) { + case KEY_NEED_READ: + case KEY_NEED_SEARCH: + case KEY_NEED_VIEW: + request |= MAY_READ; + break; + case KEY_NEED_WRITE: + case KEY_NEED_LINK: + case KEY_NEED_SETATTR: + request |= MAY_WRITE; + break; + case KEY_NEED_UNSPECIFIED: + case KEY_NEED_UNLINK: + case KEY_SYSADMIN_OVERRIDE: + case KEY_AUTHTOKEN_OVERRIDE: + case KEY_DEFER_PERM_CHECK: + return 0; + default: return -EINVAL; + } keyp = key_ref_to_ptr(key_ref); if (keyp == NULL) @@ -4265,7 +4268,7 @@ static int smack_key_permission(key_ref_t key_ref, if (tkp == NULL) return -EACCES; - if (smack_privileged_cred(CAP_MAC_OVERRIDE, cred)) + if (smack_privileged(CAP_MAC_OVERRIDE)) return 0; #ifdef CONFIG_AUDIT @@ -4273,10 +4276,6 @@ static int smack_key_permission(key_ref_t key_ref, ad.a.u.key_struct.key = keyp->serial; ad.a.u.key_struct.key_desc = keyp->description; #endif - if (perm & (KEY_NEED_READ | KEY_NEED_SEARCH | KEY_NEED_VIEW)) - request |= MAY_READ; - if (perm & (KEY_NEED_WRITE | KEY_NEED_LINK | KEY_NEED_SETATTR)) - request |= MAY_WRITE; rc = smk_access(tkp, keyp->security, request, &ad); rc = smk_bu_note("key access", tkp, keyp->security, request, rc); return rc; @@ -4311,8 +4310,81 @@ static int smack_key_getsecurity(struct key *key, char **_buffer) return length; } + +#ifdef CONFIG_KEY_NOTIFICATIONS +/** + * smack_watch_key - Smack access to watch a key for notifications. + * @key: The key to be watched + * + * Return 0 if the @watch->cred has permission to read from the key object and + * an error otherwise. + */ +static int smack_watch_key(struct key *key) +{ + struct smk_audit_info ad; + struct smack_known *tkp = smk_of_current(); + int rc; + + if (key == NULL) + return -EINVAL; + /* + * If the key hasn't been initialized give it access so that + * it may do so. + */ + if (key->security == NULL) + return 0; + /* + * This should not occur + */ + if (tkp == NULL) + return -EACCES; + + if (smack_privileged_cred(CAP_MAC_OVERRIDE, current_cred())) + return 0; + +#ifdef CONFIG_AUDIT + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_KEY); + ad.a.u.key_struct.key = key->serial; + ad.a.u.key_struct.key_desc = key->description; +#endif + rc = smk_access(tkp, key->security, MAY_READ, &ad); + rc = smk_bu_note("key watch", tkp, key->security, MAY_READ, rc); + return rc; +} +#endif /* CONFIG_KEY_NOTIFICATIONS */ #endif /* CONFIG_KEYS */ +#ifdef CONFIG_WATCH_QUEUE +/** + * smack_post_notification - Smack access to post a notification to a queue + * @w_cred: The credentials of the watcher. + * @cred: The credentials of the event source (may be NULL). + * @n: The notification message to be posted. + */ +static int smack_post_notification(const struct cred *w_cred, + const struct cred *cred, + struct watch_notification *n) +{ + struct smk_audit_info ad; + struct smack_known *subj, *obj; + int rc; + + /* Always let maintenance notifications through. */ + if (n->type == WATCH_TYPE_META) + return 0; + + if (!cred) + return 0; + subj = smk_of_task(smack_cred(cred)); + obj = smk_of_task(smack_cred(w_cred)); + + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_NOTIFICATION); + rc = smk_access(subj, obj, MAY_WRITE, &ad); + rc = smk_bu_note("notification", subj, obj, MAY_WRITE, rc); + return rc; +} +#endif /* CONFIG_WATCH_QUEUE */ + /* * Smack Audit hooks * @@ -4598,7 +4670,7 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(sb_statfs, smack_sb_statfs), LSM_HOOK_INIT(sb_set_mnt_opts, smack_set_mnt_opts), - LSM_HOOK_INIT(bprm_set_creds, smack_bprm_set_creds), + LSM_HOOK_INIT(bprm_creds_for_exec, smack_bprm_creds_for_exec), LSM_HOOK_INIT(inode_alloc_security, smack_inode_alloc_security), LSM_HOOK_INIT(inode_init_security, smack_inode_init_security), @@ -4701,8 +4773,15 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(key_free, smack_key_free), LSM_HOOK_INIT(key_permission, smack_key_permission), LSM_HOOK_INIT(key_getsecurity, smack_key_getsecurity), +#ifdef CONFIG_KEY_NOTIFICATIONS + LSM_HOOK_INIT(watch_key, smack_watch_key), +#endif #endif /* CONFIG_KEYS */ +#ifdef CONFIG_WATCH_QUEUE + LSM_HOOK_INIT(post_notification, smack_post_notification), +#endif + /* Audit hooks */ #ifdef CONFIG_AUDIT LSM_HOOK_INIT(audit_rule_init, smack_audit_rule_init), @@ -4760,15 +4839,9 @@ static __init int smack_init(void) struct cred *cred = (struct cred *) current->cred; struct task_smack *tsp; - smack_inode_cache = KMEM_CACHE(inode_smack, 0); - if (!smack_inode_cache) - return -ENOMEM; - smack_rule_cache = KMEM_CACHE(smack_rule, 0); - if (!smack_rule_cache) { - kmem_cache_destroy(smack_inode_cache); + if (!smack_rule_cache) return -ENOMEM; - } /* * Set the security state for the initial task. diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index e3e05c04dbd1..c21b656b3263 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -878,11 +878,21 @@ static ssize_t smk_set_cipso(struct file *file, const char __user *buf, else rule += strlen(skp->smk_known) + 1; + if (rule > data + count) { + rc = -EOVERFLOW; + goto out; + } + ret = sscanf(rule, "%d", &maplevel); if (ret != 1 || maplevel > SMACK_CIPSO_MAXLEVEL) goto out; rule += SMK_DIGITLEN; + if (rule > data + count) { + rc = -EOVERFLOW; + goto out; + } + ret = sscanf(rule, "%d", &catlen); if (ret != 1 || catlen > SMACK_CIPSO_MAXCATNUM) goto out; diff --git a/security/tomoyo/Kconfig b/security/tomoyo/Kconfig index 9221ea506631..b9f867100a9f 100644 --- a/security/tomoyo/Kconfig +++ b/security/tomoyo/Kconfig @@ -43,7 +43,7 @@ config SECURITY_TOMOYO_OMIT_USERSPACE_LOADER bool "Activate without calling userspace policy loader." default n depends on SECURITY_TOMOYO - ---help--- + help Say Y here if you want to activate access control as soon as built-in policy was loaded. This option will be useful for systems where operations which can lead to the hijacking of the boot sequence are @@ -60,7 +60,7 @@ config SECURITY_TOMOYO_POLICY_LOADER default "/sbin/tomoyo-init" depends on SECURITY_TOMOYO depends on !SECURITY_TOMOYO_OMIT_USERSPACE_LOADER - ---help--- + help This is the default pathname of policy loader which is called before activation. You can override this setting via TOMOYO_loader= kernel command line option. @@ -70,7 +70,7 @@ config SECURITY_TOMOYO_ACTIVATION_TRIGGER default "/sbin/init" depends on SECURITY_TOMOYO depends on !SECURITY_TOMOYO_OMIT_USERSPACE_LOADER - ---help--- + help This is the default pathname of activation trigger. You can override this setting via TOMOYO_trigger= kernel command line option. For example, if you pass init=/bin/systemd option, you may diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 1b467381986f..c16b8c1b03e7 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -1025,7 +1025,7 @@ static bool tomoyo_select_domain(struct tomoyo_io_buffer *head, if (domain) head->r.domain = &domain->list; else - head->r.eof = 1; + head->r.eof = true; tomoyo_io_printf(head, "# select %s\n", data); if (domain && domain->is_deleted) tomoyo_io_printf(head, "# This is a deleted domain.\n"); @@ -2662,8 +2662,6 @@ ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head, if (!head->write) return -EINVAL; - if (!access_ok(buffer, buffer_len)) - return -EFAULT; if (mutex_lock_interruptible(&head->io_sem)) return -EINTR; head->read_user_buf_avail = 0; diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c index bf38fc1b59b2..df4798980416 100644 --- a/security/tomoyo/realpath.c +++ b/security/tomoyo/realpath.c @@ -7,6 +7,7 @@ #include "common.h" #include <linux/magic.h> +#include <linux/proc_fs.h> /** * tomoyo_encode2 - Encode binary string to ascii string. @@ -161,9 +162,10 @@ static char *tomoyo_get_local_path(struct dentry *dentry, char * const buffer, if (sb->s_magic == PROC_SUPER_MAGIC && *pos == '/') { char *ep; const pid_t pid = (pid_t) simple_strtoul(pos + 1, &ep, 10); + struct pid_namespace *proc_pidns = proc_pid_ns(sb); if (*ep == '/' && pid && pid == - task_tgid_nr_ns(current, sb->s_fs_info)) { + task_tgid_nr_ns(current, proc_pidns)) { pos = ep - 5; if (pos < buffer) goto out; diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index 716c92ec941a..f9adddc42ac8 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -63,21 +63,15 @@ static void tomoyo_bprm_committed_creds(struct linux_binprm *bprm) #ifndef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER /** - * tomoyo_bprm_set_creds - Target for security_bprm_set_creds(). + * tomoyo_bprm_for_exec - Target for security_bprm_creds_for_exec(). * * @bprm: Pointer to "struct linux_binprm". * * Returns 0. */ -static int tomoyo_bprm_set_creds(struct linux_binprm *bprm) +static int tomoyo_bprm_creds_for_exec(struct linux_binprm *bprm) { /* - * Do only if this function is called for the first time of an execve - * operation. - */ - if (bprm->called_set_creds) - return 0; - /* * Load policy if /sbin/tomoyo-init exists and /sbin/init is requested * for the first time. */ @@ -539,7 +533,7 @@ static struct security_hook_list tomoyo_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(task_alloc, tomoyo_task_alloc), LSM_HOOK_INIT(task_free, tomoyo_task_free), #ifndef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER - LSM_HOOK_INIT(bprm_set_creds, tomoyo_bprm_set_creds), + LSM_HOOK_INIT(bprm_creds_for_exec, tomoyo_bprm_creds_for_exec), #endif LSM_HOOK_INIT(bprm_check_security, tomoyo_bprm_check_security), LSM_HOOK_INIT(file_fcntl, tomoyo_file_fcntl), diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c index 94dc346370b1..536c99646f6a 100644 --- a/security/yama/yama_lsm.c +++ b/security/yama/yama_lsm.c @@ -430,7 +430,7 @@ static struct security_hook_list yama_hooks[] __lsm_ro_after_init = { #ifdef CONFIG_SYSCTL static int yama_dointvec_minmax(struct ctl_table *table, int write, - void __user *buffer, size_t *lenp, loff_t *ppos) + void *buffer, size_t *lenp, loff_t *ppos) { struct ctl_table table_copy; |