diff options
Diffstat (limited to 'security')
43 files changed, 832 insertions, 271 deletions
diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 8f9ecac7f8de..eeaddfe0c0fb 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -19,7 +19,7 @@ #include "include/audit.h" #include "include/policy.h" #include "include/policy_ns.h" - +#include "include/secid.h" const char *const audit_mode_names[] = { "normal", @@ -163,3 +163,91 @@ int aa_audit(int type, struct aa_profile *profile, struct common_audit_data *sa, return aad(sa)->error; } + +struct aa_audit_rule { + struct aa_label *label; +}; + +void aa_audit_rule_free(void *vrule) +{ + struct aa_audit_rule *rule = vrule; + + if (rule) { + if (!IS_ERR(rule->label)) + aa_put_label(rule->label); + kfree(rule); + } +} + +int aa_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule) +{ + struct aa_audit_rule *rule; + + switch (field) { + case AUDIT_SUBJ_ROLE: + if (op != Audit_equal && op != Audit_not_equal) + return -EINVAL; + break; + default: + return -EINVAL; + } + + rule = kzalloc(sizeof(struct aa_audit_rule), GFP_KERNEL); + + if (!rule) + return -ENOMEM; + + /* Currently rules are treated as coming from the root ns */ + rule->label = aa_label_parse(&root_ns->unconfined->label, rulestr, + GFP_KERNEL, true, false); + if (IS_ERR(rule->label)) { + aa_audit_rule_free(rule); + return PTR_ERR(rule->label); + } + + *vrule = rule; + return 0; +} + +int aa_audit_rule_known(struct audit_krule *rule) +{ + int i; + + for (i = 0; i < rule->field_count; i++) { + struct audit_field *f = &rule->fields[i]; + + switch (f->type) { + case AUDIT_SUBJ_ROLE: + return 1; + } + } + + return 0; +} + +int aa_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule, + struct audit_context *actx) +{ + struct aa_audit_rule *rule = vrule; + struct aa_label *label; + int found = 0; + + label = aa_secid_to_label(sid); + + if (!label) + return -ENOENT; + + if (aa_label_is_subset(label, rule->label)) + found = 1; + + switch (field) { + case AUDIT_SUBJ_ROLE: + switch (op) { + case Audit_equal: + return found; + case Audit_not_equal: + return !found; + } + } + return 0; +} diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 590b7e8cd21c..098d546d8253 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -839,7 +839,7 @@ static struct aa_label *handle_onexec(struct aa_label *label, cond, unsafe)); } else { - /* TODO: determine how much we want to losen this */ + /* TODO: determine how much we want to loosen this */ error = fn_for_each_in_ns(label, profile, profile_onexec(profile, onexec, stack, bprm, buffer, cond, unsafe)); diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 9c9be9c98c15..b8c8b1066b0a 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -189,4 +189,10 @@ static inline int complain_error(int error) return error; } +void aa_audit_rule_free(void *vrule); +int aa_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule); +int aa_audit_rule_known(struct audit_krule *rule); +int aa_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule, + struct audit_context *actx); + #endif /* __AA_AUDIT_H */ diff --git a/security/apparmor/include/label.h b/security/apparmor/include/label.h index d871e7ff0952..7ce5fe73ae7f 100644 --- a/security/apparmor/include/label.h +++ b/security/apparmor/include/label.h @@ -281,7 +281,7 @@ void __aa_labelset_update_subtree(struct aa_ns *ns); 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); +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); diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h index e042b994f2b8..b6380c5f0097 100644 --- a/security/apparmor/include/path.h +++ b/security/apparmor/include/path.h @@ -43,10 +43,11 @@ struct aa_buffers { DECLARE_PER_CPU(struct aa_buffers, aa_buffers); -#define ASSIGN(FN, X, N) ((X) = FN(N)) -#define EVAL1(FN, X) ASSIGN(FN, X, 0) /*X = FN(0)*/ -#define EVAL2(FN, X, Y...) do { ASSIGN(FN, X, 1); EVAL1(FN, Y); } while (0) -#define EVAL(FN, X...) CONCATENATE(EVAL, COUNT_ARGS(X))(FN, X) +#define ASSIGN(FN, A, X, N) ((X) = FN(A, N)) +#define EVAL1(FN, A, X) ASSIGN(FN, A, X, 0) /*X = FN(0)*/ +#define EVAL2(FN, A, X, Y...) \ + do { ASSIGN(FN, A, X, 1); EVAL1(FN, A, Y); } while (0) +#define EVAL(FN, A, X...) CONCATENATE(EVAL, COUNT_ARGS(X))(FN, A, X) #define for_each_cpu_buffer(I) for ((I) = 0; (I) < MAX_PATH_BUFFERS; (I)++) @@ -56,26 +57,24 @@ DECLARE_PER_CPU(struct aa_buffers, aa_buffers); #define AA_BUG_PREEMPT_ENABLED(X) /* nop */ #endif -#define __get_buffer(N) ({ \ - struct aa_buffers *__cpu_var; \ +#define __get_buffer(C, N) ({ \ AA_BUG_PREEMPT_ENABLED("__get_buffer without preempt disabled"); \ - __cpu_var = this_cpu_ptr(&aa_buffers); \ - __cpu_var->buf[(N)]; }) + (C)->buf[(N)]; }) -#define __get_buffers(X...) EVAL(__get_buffer, X) +#define __get_buffers(C, X...) EVAL(__get_buffer, C, X) #define __put_buffers(X, Y...) ((void)&(X)) -#define get_buffers(X...) \ -do { \ - preempt_disable(); \ - __get_buffers(X); \ +#define get_buffers(X...) \ +do { \ + struct aa_buffers *__cpu_var = get_cpu_ptr(&aa_buffers); \ + __get_buffers(__cpu_var, X); \ } while (0) -#define put_buffers(X, Y...) \ -do { \ - __put_buffers(X, Y); \ - preempt_enable(); \ +#define put_buffers(X, Y...) \ +do { \ + __put_buffers(X, Y); \ + put_cpu_ptr(&aa_buffers); \ } while (0) #endif /* __AA_PATH_H */ diff --git a/security/apparmor/include/secid.h b/security/apparmor/include/secid.h index 95ed86a0f1e2..dee6fa3b6081 100644 --- a/security/apparmor/include/secid.h +++ b/security/apparmor/include/secid.h @@ -3,7 +3,7 @@ * * This file contains AppArmor security identifier (secid) definitions * - * Copyright 2009-2010 Canonical Ltd. + * Copyright 2009-2018 Canonical Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -14,13 +14,24 @@ #ifndef __AA_SECID_H #define __AA_SECID_H +#include <linux/slab.h> #include <linux/types.h> +struct aa_label; + /* secid value that will not be allocated */ #define AA_SECID_INVALID 0 -#define AA_SECID_ALLOC AA_SECID_INVALID -u32 aa_alloc_secid(void); +struct aa_label *aa_secid_to_label(u32 secid); +int apparmor_secid_to_secctx(u32 secid, char **secdata, u32 *seclen); +int apparmor_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid); +void apparmor_release_secctx(char *secdata, u32 seclen); + + +int aa_alloc_secid(struct aa_label *label, gfp_t gfp); void aa_free_secid(u32 secid); +void aa_secid_update(u32 secid, struct aa_label *label); + +void aa_secids_init(void); #endif /* __AA_SECID_H */ diff --git a/security/apparmor/label.c b/security/apparmor/label.c index 523250e34837..ba11bdf9043a 100644 --- a/security/apparmor/label.c +++ b/security/apparmor/label.c @@ -128,7 +128,7 @@ static int ns_cmp(struct aa_ns *a, struct aa_ns *b) } /** - * profile_cmp - profile comparision for set ordering + * profile_cmp - profile comparison for set ordering * @a: profile to compare (NOT NULL) * @b: profile to compare (NOT NULL) * @@ -157,7 +157,7 @@ static int profile_cmp(struct aa_profile *a, struct aa_profile *b) } /** - * vec_cmp - label comparision for set ordering + * vec_cmp - label comparison for set ordering * @a: label to compare (NOT NULL) * @vec: vector of profiles to compare (NOT NULL) * @n: length of @vec @@ -402,13 +402,12 @@ static void label_free_or_put_new(struct aa_label *label, struct aa_label *new) aa_put_label(new); } -bool aa_label_init(struct aa_label *label, int size) +bool aa_label_init(struct aa_label *label, int size, gfp_t gfp) { AA_BUG(!label); AA_BUG(size < 1); - label->secid = aa_alloc_secid(); - if (label->secid == AA_SECID_INVALID) + if (aa_alloc_secid(label, gfp) < 0) return false; label->size = size; /* doesn't include null */ @@ -441,7 +440,7 @@ struct aa_label *aa_label_alloc(int size, struct aa_proxy *proxy, gfp_t gfp) if (!new) goto fail; - if (!aa_label_init(new, size)) + if (!aa_label_init(new, size, gfp)) goto fail; if (!proxy) { @@ -463,7 +462,7 @@ fail: /** - * label_cmp - label comparision for set ordering + * label_cmp - label comparison for set ordering * @a: label to compare (NOT NULL) * @b: label to compare (NOT NULL) * @@ -2011,7 +2010,7 @@ out: /** * __label_update - insert updated version of @label into labelset - * @label - the label to update/repace + * @label - the label to update/replace * * Returns: new label that is up to date * else NULL on failure diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index 068a9f471f77..a7b3f681b80e 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -408,7 +408,7 @@ int aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target, * @request: requested perms * @deny: Returns: explicit deny set * @sa: initialized audit structure (MAY BE NULL if not auditing) - * @cb: callback fn for tpye specific fields (MAY BE NULL) + * @cb: callback fn for type specific fields (MAY BE NULL) * * Returns: 0 if permission else error code * diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index ce2b89e9ad94..74f17376202b 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -39,6 +39,7 @@ #include "include/policy_ns.h" #include "include/procattr.h" #include "include/mount.h" +#include "include/secid.h" /* Flag indicating whether initialization completed */ int apparmor_initialized; @@ -116,7 +117,8 @@ static int apparmor_ptrace_access_check(struct task_struct *child, tracer = begin_current_label_crit_section(); tracee = aa_get_task_label(child); error = aa_may_ptrace(tracer, tracee, - mode == PTRACE_MODE_READ ? AA_PTRACE_READ : AA_PTRACE_TRACE); + (mode & PTRACE_MODE_READ) ? AA_PTRACE_READ + : AA_PTRACE_TRACE); aa_put_label(tracee); end_current_label_crit_section(tracer); @@ -710,6 +712,13 @@ static void apparmor_bprm_committed_creds(struct linux_binprm *bprm) return; } +static void apparmor_task_getsecid(struct task_struct *p, u32 *secid) +{ + struct aa_label *label = aa_get_task_label(p); + *secid = label->secid; + aa_put_label(label); +} + static int apparmor_task_setrlimit(struct task_struct *task, unsigned int resource, struct rlimit *new_rlim) { @@ -1186,8 +1195,20 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(task_free, apparmor_task_free), LSM_HOOK_INIT(task_alloc, apparmor_task_alloc), + LSM_HOOK_INIT(task_getsecid, apparmor_task_getsecid), LSM_HOOK_INIT(task_setrlimit, apparmor_task_setrlimit), LSM_HOOK_INIT(task_kill, apparmor_task_kill), + +#ifdef CONFIG_AUDIT + LSM_HOOK_INIT(audit_rule_init, aa_audit_rule_init), + LSM_HOOK_INIT(audit_rule_known, aa_audit_rule_known), + LSM_HOOK_INIT(audit_rule_match, aa_audit_rule_match), + LSM_HOOK_INIT(audit_rule_free, aa_audit_rule_free), +#endif + + LSM_HOOK_INIT(secid_to_secctx, apparmor_secid_to_secctx), + LSM_HOOK_INIT(secctx_to_secid, apparmor_secctx_to_secid), + LSM_HOOK_INIT(release_secctx, apparmor_release_secctx), }; /* @@ -1378,14 +1399,12 @@ static int param_set_audit(const char *val, const struct kernel_param *kp) if (apparmor_initialized && !policy_admin_capable(NULL)) return -EPERM; - for (i = 0; i < AUDIT_MAX_INDEX; i++) { - if (strcmp(val, audit_mode_names[i]) == 0) { - aa_g_audit = i; - return 0; - } - } + i = match_string(audit_mode_names, AUDIT_MAX_INDEX, val); + if (i < 0) + return -EINVAL; - return -EINVAL; + aa_g_audit = i; + return 0; } static int param_get_mode(char *buffer, const struct kernel_param *kp) @@ -1409,14 +1428,13 @@ static int param_set_mode(const char *val, const struct kernel_param *kp) if (apparmor_initialized && !policy_admin_capable(NULL)) return -EPERM; - for (i = 0; i < APPARMOR_MODE_NAMES_MAX_INDEX; i++) { - if (strcmp(val, aa_profile_mode_names[i]) == 0) { - aa_g_profile_mode = i; - return 0; - } - } + i = match_string(aa_profile_mode_names, APPARMOR_MODE_NAMES_MAX_INDEX, + val); + if (i < 0) + return -EINVAL; - return -EINVAL; + aa_g_profile_mode = i; + return 0; } /* @@ -1530,6 +1548,8 @@ static int __init apparmor_init(void) return 0; } + aa_secids_init(); + error = aa_setup_dfa_engine(); if (error) { AA_ERROR("Unable to setup dfa engine\n"); diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 280eba082c7b..55f2ee505a01 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -472,7 +472,7 @@ unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start, /** * aa_dfa_next - step one character to the next state in the dfa - * @dfa: the dfa to tranverse (NOT NULL) + * @dfa: the dfa to traverse (NOT NULL) * @state: the state to start in * @c: the input character to transition on * diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c index 6e8c7ac0b33d..c1da22482bfb 100644 --- a/security/apparmor/mount.c +++ b/security/apparmor/mount.c @@ -121,7 +121,7 @@ static void audit_cb(struct audit_buffer *ab, void *va) * @src_name: src_name of object being mediated (MAYBE_NULL) * @type: type of filesystem (MAYBE_NULL) * @trans: name of trans (MAYBE NULL) - * @flags: filesystem idependent mount flags + * @flags: filesystem independent mount flags * @data: filesystem mount flags * @request: permissions requested * @perms: the permissions computed for the request (NOT NULL) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index c07493ce2376..1590e2de4e84 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -268,7 +268,7 @@ struct aa_profile *aa_alloc_profile(const char *hname, struct aa_proxy *proxy, if (!aa_policy_init(&profile->base, NULL, hname, gfp)) goto fail; - if (!aa_label_init(&profile->label, 1)) + if (!aa_label_init(&profile->label, 1, gfp)) goto fail; /* update being set needed by fs interface */ @@ -1008,6 +1008,9 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, audit_policy(label, op, ns_name, ent->new->base.hname, "same as current profile, skipping", error); + /* break refcount cycle with proxy. */ + aa_put_proxy(ent->new->label.proxy); + ent->new->label.proxy = NULL; goto skip; } @@ -1085,7 +1088,7 @@ fail: * Remove a profile or sub namespace from the current namespace, so that * they can not be found anymore and mark them as replaced by unconfined * - * NOTE: removing confinement does not restore rlimits to preconfinemnet values + * NOTE: removing confinement does not restore rlimits to preconfinement values * * Returns: size of data consume else error code if fails */ diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index b9e6b2cafa69..0e566a01d217 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -475,7 +475,7 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile) /* currently 4 exec bits and entries 0-3 are reserved iupcx */ if (size > 16 - 4) goto fail; - profile->file.trans.table = kzalloc(sizeof(char *) * size, + profile->file.trans.table = kcalloc(size, sizeof(char *), GFP_KERNEL); if (!profile->file.trans.table) goto fail; diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c index d022137143b9..95fd26d09757 100644 --- a/security/apparmor/resource.c +++ b/security/apparmor/resource.c @@ -124,7 +124,7 @@ int aa_task_setrlimit(struct aa_label *label, struct task_struct *task, */ if (label != peer && - !aa_capable(label, CAP_SYS_RESOURCE, SECURITY_CAP_NOAUDIT)) + aa_capable(label, CAP_SYS_RESOURCE, SECURITY_CAP_NOAUDIT) != 0) error = fn_for_each(label, profile, audit_resource(profile, resource, new_rlim->rlim_max, peer, diff --git a/security/apparmor/secid.c b/security/apparmor/secid.c index 3a3edbad0b21..f2f22d00db18 100644 --- a/security/apparmor/secid.c +++ b/security/apparmor/secid.c @@ -3,7 +3,7 @@ * * This file contains AppArmor security identifier (secid) manipulation fns * - * Copyright 2009-2010 Canonical Ltd. + * Copyright 2009-2017 Canonical Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -11,38 +11,142 @@ * License. * * - * AppArmor allocates a unique secid for every profile loaded. If a profile - * is replaced it receives the secid of the profile it is replacing. - * - * The secid value of 0 is invalid. + * AppArmor allocates a unique secid for every label used. If a label + * is replaced it receives the secid of the label it is replacing. */ -#include <linux/spinlock.h> #include <linux/errno.h> #include <linux/err.h> +#include <linux/gfp.h> +#include <linux/idr.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include "include/cred.h" +#include "include/lib.h" #include "include/secid.h" +#include "include/label.h" +#include "include/policy_ns.h" + +/* + * secids - do not pin labels with a refcount. They rely on the label + * properly updating/freeing them + */ -/* global counter from which secids are allocated */ -static u32 global_secid; +#define AA_FIRST_SECID 1 + +static DEFINE_IDR(aa_secids); static DEFINE_SPINLOCK(secid_lock); -/* TODO FIXME: add secid to profile mapping, and secid recycling */ +/* + * TODO: allow policy to reserve a secid range? + * TODO: add secid pinning + * TODO: use secid_update in label replace + */ + +/** + * aa_secid_update - update a secid mapping to a new label + * @secid: secid to update + * @label: label the secid will now map to + */ +void aa_secid_update(u32 secid, struct aa_label *label) +{ + unsigned long flags; + + spin_lock_irqsave(&secid_lock, flags); + idr_replace(&aa_secids, label, secid); + spin_unlock_irqrestore(&secid_lock, flags); +} + +/** + * + * see label for inverse aa_label_to_secid + */ +struct aa_label *aa_secid_to_label(u32 secid) +{ + struct aa_label *label; + + rcu_read_lock(); + label = idr_find(&aa_secids, secid); + rcu_read_unlock(); + + return label; +} + +int apparmor_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) +{ + /* TODO: cache secctx and ref count so we don't have to recreate */ + struct aa_label *label = aa_secid_to_label(secid); + int len; + + AA_BUG(!secdata); + AA_BUG(!seclen); + + if (!label) + return -EINVAL; + + if (secdata) + len = aa_label_asxprint(secdata, root_ns, label, + FLAG_SHOW_MODE | FLAG_VIEW_SUBNS | + FLAG_HIDDEN_UNCONFINED | FLAG_ABS_ROOT, + GFP_ATOMIC); + else + len = aa_label_snxprint(NULL, 0, root_ns, label, + FLAG_SHOW_MODE | FLAG_VIEW_SUBNS | + FLAG_HIDDEN_UNCONFINED | FLAG_ABS_ROOT); + if (len < 0) + return -ENOMEM; + + *seclen = len; + + return 0; +} + +int apparmor_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid) +{ + struct aa_label *label; + + label = aa_label_strn_parse(&root_ns->unconfined->label, secdata, + seclen, GFP_KERNEL, false, false); + if (IS_ERR(label)) + return PTR_ERR(label); + *secid = label->secid; + + return 0; +} + +void apparmor_release_secctx(char *secdata, u32 seclen) +{ + kfree(secdata); +} /** * aa_alloc_secid - allocate a new secid for a profile + * @label: the label to allocate a secid for + * @gfp: memory allocation flags + * + * Returns: 0 with @label->secid initialized + * <0 returns error with @label->secid set to AA_SECID_INVALID */ -u32 aa_alloc_secid(void) +int aa_alloc_secid(struct aa_label *label, gfp_t gfp) { - u32 secid; + unsigned long flags; + int ret; + + idr_preload(gfp); + spin_lock_irqsave(&secid_lock, flags); + ret = idr_alloc(&aa_secids, label, AA_FIRST_SECID, 0, GFP_ATOMIC); + spin_unlock_irqrestore(&secid_lock, flags); + idr_preload_end(); - /* - * TODO FIXME: secid recycling - part of profile mapping table - */ - spin_lock(&secid_lock); - secid = (++global_secid); - spin_unlock(&secid_lock); - return secid; + if (ret < 0) { + label->secid = AA_SECID_INVALID; + return ret; + } + + AA_BUG(ret == AA_SECID_INVALID); + label->secid = ret; + return 0; } /** @@ -51,5 +155,14 @@ u32 aa_alloc_secid(void) */ void aa_free_secid(u32 secid) { - ; /* NOP ATM */ + unsigned long flags; + + spin_lock_irqsave(&secid_lock, flags); + idr_remove(&aa_secids, secid); + spin_unlock_irqrestore(&secid_lock, flags); +} + +void aa_secids_init(void) +{ + idr_init_base(&aa_secids, AA_FIRST_SECID); } diff --git a/security/commoncap.c b/security/commoncap.c index 48620c93d697..f4c33abd9959 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -449,6 +449,8 @@ int cap_inode_getsecurity(struct inode *inode, const char *name, void **buffer, magic |= VFS_CAP_FLAGS_EFFECTIVE; memcpy(&cap->data, &nscap->data, sizeof(__le32) * 2 * VFS_CAP_U32); cap->magic_etc = cpu_to_le32(magic); + } else { + size = -ENOMEM; } } kfree(tmpbuf); @@ -917,6 +919,8 @@ int cap_bprm_set_creds(struct linux_binprm *bprm) int cap_inode_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { + struct user_namespace *user_ns = dentry->d_sb->s_user_ns; + /* Ignore non-security xattrs */ if (strncmp(name, XATTR_SECURITY_PREFIX, sizeof(XATTR_SECURITY_PREFIX) - 1) != 0) @@ -929,7 +933,7 @@ int cap_inode_setxattr(struct dentry *dentry, const char *name, if (strcmp(name, XATTR_NAME_CAPS) == 0) return 0; - if (!capable(CAP_SYS_ADMIN)) + if (!ns_capable(user_ns, CAP_SYS_ADMIN)) return -EPERM; return 0; } @@ -947,6 +951,8 @@ int cap_inode_setxattr(struct dentry *dentry, const char *name, */ int cap_inode_removexattr(struct dentry *dentry, const char *name) { + struct user_namespace *user_ns = dentry->d_sb->s_user_ns; + /* Ignore non-security xattrs */ if (strncmp(name, XATTR_SECURITY_PREFIX, sizeof(XATTR_SECURITY_PREFIX) - 1) != 0) @@ -962,7 +968,7 @@ int cap_inode_removexattr(struct dentry *dentry, const char *name) return 0; } - if (!capable(CAP_SYS_ADMIN)) + if (!ns_capable(user_ns, CAP_SYS_ADMIN)) return -EPERM; return 0; } diff --git a/security/device_cgroup.c b/security/device_cgroup.c index c65b39bafdfe..cd97929fac66 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -509,7 +509,7 @@ static inline int may_allow_all(struct dev_cgroup *parent) * This is one of the three key functions for hierarchy implementation. * This function is responsible for re-evaluating all the cgroup's active * exceptions due to a parent's exception change. - * Refer to Documentation/cgroups/devices.txt for more details. + * Refer to Documentation/cgroup-v1/devices.txt for more details. */ static void revalidate_active_exceptions(struct dev_cgroup *devcg) { diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig index e825e0ae78e7..d593346d0bba 100644 --- a/security/integrity/evm/Kconfig +++ b/security/integrity/evm/Kconfig @@ -42,6 +42,17 @@ config EVM_EXTRA_SMACK_XATTRS additional info to the calculation, requires existing EVM labeled file systems to be relabeled. +config EVM_ADD_XATTRS + bool "Add additional EVM extended attributes at runtime" + depends on EVM + default n + help + Allow userland to provide additional xattrs for HMAC calculation. + + When this option is enabled, root can add additional xattrs to the + list used by EVM by writing them into + /sys/kernel/security/integrity/evm/evm_xattrs. + config EVM_LOAD_X509 bool "Load an X509 certificate onto the '.evm' trusted keyring" depends on EVM && INTEGRITY_TRUSTED_KEYRING diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h index 45c4a89c02ff..1257c3c24723 100644 --- a/security/integrity/evm/evm.h +++ b/security/integrity/evm/evm.h @@ -30,6 +30,11 @@ #define EVM_INIT_MASK (EVM_INIT_HMAC | EVM_INIT_X509 | EVM_SETUP_COMPLETE | \ EVM_ALLOW_METADATA_WRITES) +struct xattr_list { + struct list_head list; + char *name; +}; + extern int evm_initialized; #define EVM_ATTR_FSUUID 0x0001 @@ -40,7 +45,7 @@ extern struct crypto_shash *hmac_tfm; extern struct crypto_shash *hash_tfm; /* List of EVM protected security xattrs */ -extern char *evm_config_xattrnames[]; +extern struct list_head evm_config_xattrnames; int evm_init_key(void); int evm_update_evmxattr(struct dentry *dentry, diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index a46fba322340..b60524310855 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c @@ -192,15 +192,16 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry, char type, char *digest) { struct inode *inode = d_backing_inode(dentry); + struct xattr_list *xattr; struct shash_desc *desc; - char **xattrname; size_t xattr_size = 0; char *xattr_value = NULL; int error; int size; bool ima_present = false; - if (!(inode->i_opflags & IOP_XATTR)) + if (!(inode->i_opflags & IOP_XATTR) || + inode->i_sb->s_user_ns != &init_user_ns) return -EOPNOTSUPP; desc = init_desc(type); @@ -208,14 +209,14 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry, return PTR_ERR(desc); error = -ENODATA; - for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) { + list_for_each_entry_rcu(xattr, &evm_config_xattrnames, list) { bool is_ima = false; - if (strcmp(*xattrname, XATTR_NAME_IMA) == 0) + if (strcmp(xattr->name, XATTR_NAME_IMA) == 0) is_ima = true; if ((req_xattr_name && req_xattr_value) - && !strcmp(*xattrname, req_xattr_name)) { + && !strcmp(xattr->name, req_xattr_name)) { error = 0; crypto_shash_update(desc, (const u8 *)req_xattr_value, req_xattr_value_len); @@ -223,7 +224,7 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry, ima_present = true; continue; } - size = vfs_getxattr_alloc(dentry, *xattrname, + size = vfs_getxattr_alloc(dentry, xattr->name, &xattr_value, xattr_size, GFP_NOFS); if (size == -ENOMEM) { error = -ENOMEM; diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index 9ea9c19a545c..f9eff5041e4c 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -35,28 +35,29 @@ static const char * const integrity_status_msg[] = { }; int evm_hmac_attrs; -char *evm_config_xattrnames[] = { +static struct xattr_list evm_config_default_xattrnames[] = { #ifdef CONFIG_SECURITY_SELINUX - XATTR_NAME_SELINUX, + {.name = XATTR_NAME_SELINUX}, #endif #ifdef CONFIG_SECURITY_SMACK - XATTR_NAME_SMACK, + {.name = XATTR_NAME_SMACK}, #ifdef CONFIG_EVM_EXTRA_SMACK_XATTRS - XATTR_NAME_SMACKEXEC, - XATTR_NAME_SMACKTRANSMUTE, - XATTR_NAME_SMACKMMAP, + {.name = XATTR_NAME_SMACKEXEC}, + {.name = XATTR_NAME_SMACKTRANSMUTE}, + {.name = XATTR_NAME_SMACKMMAP}, #endif #endif #ifdef CONFIG_SECURITY_APPARMOR - XATTR_NAME_APPARMOR, + {.name = XATTR_NAME_APPARMOR}, #endif #ifdef CONFIG_IMA_APPRAISE - XATTR_NAME_IMA, + {.name = XATTR_NAME_IMA}, #endif - XATTR_NAME_CAPS, - NULL + {.name = XATTR_NAME_CAPS}, }; +LIST_HEAD(evm_config_xattrnames); + static int evm_fixmode; static int __init evm_set_fixmode(char *str) { @@ -68,6 +69,17 @@ __setup("evm=", evm_set_fixmode); static void __init evm_init_config(void) { + int i, xattrs; + + xattrs = ARRAY_SIZE(evm_config_default_xattrnames); + + pr_info("Initialising EVM extended attributes:\n"); + for (i = 0; i < xattrs; i++) { + pr_info("%s\n", evm_config_default_xattrnames[i].name); + list_add_tail(&evm_config_default_xattrnames[i].list, + &evm_config_xattrnames); + } + #ifdef CONFIG_EVM_ATTR_FSUUID evm_hmac_attrs |= EVM_ATTR_FSUUID; #endif @@ -82,15 +94,15 @@ static bool evm_key_loaded(void) static int evm_find_protected_xattrs(struct dentry *dentry) { struct inode *inode = d_backing_inode(dentry); - char **xattr; + struct xattr_list *xattr; int error; int count = 0; if (!(inode->i_opflags & IOP_XATTR)) return -EOPNOTSUPP; - for (xattr = evm_config_xattrnames; *xattr != NULL; xattr++) { - error = __vfs_getxattr(dentry, inode, *xattr, NULL, 0); + list_for_each_entry_rcu(xattr, &evm_config_xattrnames, list) { + error = __vfs_getxattr(dentry, inode, xattr->name, NULL, 0); if (error < 0) { if (error == -ENODATA) continue; @@ -211,24 +223,25 @@ out: static int evm_protected_xattr(const char *req_xattr_name) { - char **xattrname; int namelen; int found = 0; + struct xattr_list *xattr; namelen = strlen(req_xattr_name); - for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) { - if ((strlen(*xattrname) == namelen) - && (strncmp(req_xattr_name, *xattrname, namelen) == 0)) { + list_for_each_entry_rcu(xattr, &evm_config_xattrnames, list) { + if ((strlen(xattr->name) == namelen) + && (strncmp(req_xattr_name, xattr->name, namelen) == 0)) { found = 1; break; } if (strncmp(req_xattr_name, - *xattrname + XATTR_SECURITY_PREFIX_LEN, + xattr->name + XATTR_SECURITY_PREFIX_LEN, strlen(req_xattr_name)) == 0) { found = 1; break; } } + return found; } @@ -544,35 +557,35 @@ void __init evm_load_x509(void) static int __init init_evm(void) { int error; + struct list_head *pos, *q; + struct xattr_list *xattr; evm_init_config(); error = integrity_init_keyring(INTEGRITY_KEYRING_EVM); if (error) - return error; + goto error; error = evm_init_secfs(); if (error < 0) { pr_info("Error registering secfs\n"); - return error; + goto error; } - return 0; -} - -/* - * evm_display_config - list the EVM protected security extended attributes - */ -static int __init evm_display_config(void) -{ - char **xattrname; +error: + if (error != 0) { + if (!list_empty(&evm_config_xattrnames)) { + list_for_each_safe(pos, q, &evm_config_xattrnames) { + xattr = list_entry(pos, struct xattr_list, + list); + list_del(pos); + } + } + } - for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) - pr_info("%s\n", *xattrname); - return 0; + return error; } -pure_initcall(evm_display_config); late_initcall(init_evm); MODULE_DESCRIPTION("Extended Verification Module"); diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c index feba03bbedae..637eb999e340 100644 --- a/security/integrity/evm/evm_secfs.c +++ b/security/integrity/evm/evm_secfs.c @@ -15,11 +15,21 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/audit.h> #include <linux/uaccess.h> #include <linux/module.h> +#include <linux/mutex.h> #include "evm.h" +static struct dentry *evm_dir; static struct dentry *evm_init_tpm; +static struct dentry *evm_symlink; + +#ifdef CONFIG_EVM_ADD_XATTRS +static struct dentry *evm_xattrs; +static DEFINE_MUTEX(xattr_list_mutex); +static int evm_xattrs_locked; +#endif /** * evm_read_key - read() for <securityfs>/evm @@ -107,13 +117,203 @@ static const struct file_operations evm_key_ops = { .write = evm_write_key, }; +#ifdef CONFIG_EVM_ADD_XATTRS +/** + * evm_read_xattrs - read() for <securityfs>/evm_xattrs + * + * @filp: file pointer, not actually used + * @buf: where to put the result + * @count: maximum to send along + * @ppos: where to start + * + * Returns number of bytes read or error code, as appropriate + */ +static ssize_t evm_read_xattrs(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + char *temp; + int offset = 0; + ssize_t rc, size = 0; + struct xattr_list *xattr; + + if (*ppos != 0) + return 0; + + rc = mutex_lock_interruptible(&xattr_list_mutex); + if (rc) + return -ERESTARTSYS; + + list_for_each_entry(xattr, &evm_config_xattrnames, list) + size += strlen(xattr->name) + 1; + + temp = kmalloc(size + 1, GFP_KERNEL); + if (!temp) { + mutex_unlock(&xattr_list_mutex); + return -ENOMEM; + } + + list_for_each_entry(xattr, &evm_config_xattrnames, list) { + sprintf(temp + offset, "%s\n", xattr->name); + offset += strlen(xattr->name) + 1; + } + + mutex_unlock(&xattr_list_mutex); + rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp)); + + kfree(temp); + + return rc; +} + +/** + * evm_write_xattrs - write() for <securityfs>/evm_xattrs + * @file: file pointer, not actually used + * @buf: where to get the data from + * @count: bytes sent + * @ppos: where to start + * + * Returns number of bytes written or error code, as appropriate + */ +static ssize_t evm_write_xattrs(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + int len, err; + struct xattr_list *xattr, *tmp; + struct audit_buffer *ab; + struct iattr newattrs; + struct inode *inode; + + if (!capable(CAP_SYS_ADMIN) || evm_xattrs_locked) + return -EPERM; + + if (*ppos != 0) + return -EINVAL; + + if (count > XATTR_NAME_MAX) + return -E2BIG; + + ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_EVM_XATTR); + if (IS_ERR(ab)) + return PTR_ERR(ab); + + xattr = kmalloc(sizeof(struct xattr_list), GFP_KERNEL); + if (!xattr) { + err = -ENOMEM; + goto out; + } + + xattr->name = memdup_user_nul(buf, count); + if (IS_ERR(xattr->name)) { + err = PTR_ERR(xattr->name); + xattr->name = NULL; + goto out; + } + + /* Remove any trailing newline */ + len = strlen(xattr->name); + if (len && xattr->name[len-1] == '\n') + xattr->name[len-1] = '\0'; + + if (strcmp(xattr->name, ".") == 0) { + evm_xattrs_locked = 1; + newattrs.ia_mode = S_IFREG | 0440; + newattrs.ia_valid = ATTR_MODE; + inode = evm_xattrs->d_inode; + inode_lock(inode); + err = simple_setattr(evm_xattrs, &newattrs); + inode_unlock(inode); + audit_log_format(ab, "locked"); + if (!err) + err = count; + goto out; + } + + audit_log_format(ab, "xattr="); + audit_log_untrustedstring(ab, xattr->name); + + if (strncmp(xattr->name, XATTR_SECURITY_PREFIX, + XATTR_SECURITY_PREFIX_LEN) != 0) { + err = -EINVAL; + goto out; + } + + /* Guard against races in evm_read_xattrs */ + mutex_lock(&xattr_list_mutex); + list_for_each_entry(tmp, &evm_config_xattrnames, list) { + if (strcmp(xattr->name, tmp->name) == 0) { + err = -EEXIST; + mutex_unlock(&xattr_list_mutex); + goto out; + } + } + list_add_tail_rcu(&xattr->list, &evm_config_xattrnames); + mutex_unlock(&xattr_list_mutex); + + audit_log_format(ab, " res=0"); + audit_log_end(ab); + return count; +out: + audit_log_format(ab, " res=%d", err); + audit_log_end(ab); + if (xattr) { + kfree(xattr->name); + kfree(xattr); + } + return err; +} + +static const struct file_operations evm_xattr_ops = { + .read = evm_read_xattrs, + .write = evm_write_xattrs, +}; + +static int evm_init_xattrs(void) +{ + evm_xattrs = securityfs_create_file("evm_xattrs", 0660, evm_dir, NULL, + &evm_xattr_ops); + if (!evm_xattrs || IS_ERR(evm_xattrs)) + return -EFAULT; + + return 0; +} +#else +static int evm_init_xattrs(void) +{ + return 0; +} +#endif + int __init evm_init_secfs(void) { int error = 0; - evm_init_tpm = securityfs_create_file("evm", S_IRUSR | S_IRGRP, - NULL, NULL, &evm_key_ops); - if (!evm_init_tpm || IS_ERR(evm_init_tpm)) + evm_dir = securityfs_create_dir("evm", integrity_dir); + if (!evm_dir || IS_ERR(evm_dir)) + return -EFAULT; + + evm_init_tpm = securityfs_create_file("evm", 0660, + evm_dir, NULL, &evm_key_ops); + if (!evm_init_tpm || IS_ERR(evm_init_tpm)) { error = -EFAULT; + goto out; + } + + evm_symlink = securityfs_create_symlink("evm", NULL, + "integrity/evm/evm", NULL); + if (!evm_symlink || IS_ERR(evm_symlink)) { + error = -EFAULT; + goto out; + } + + if (evm_init_xattrs() != 0) { + error = -EFAULT; + goto out; + } + + return 0; +out: + securityfs_remove(evm_symlink); + securityfs_remove(evm_init_tpm); + securityfs_remove(evm_dir); return error; } diff --git a/security/integrity/iint.c b/security/integrity/iint.c index f266e4b3b7d4..149faa81f6f0 100644 --- a/security/integrity/iint.c +++ b/security/integrity/iint.c @@ -21,12 +21,15 @@ #include <linux/rbtree.h> #include <linux/file.h> #include <linux/uaccess.h> +#include <linux/security.h> #include "integrity.h" static struct rb_root integrity_iint_tree = RB_ROOT; static DEFINE_RWLOCK(integrity_iint_lock); static struct kmem_cache *iint_cache __read_mostly; +struct dentry *integrity_dir; + /* * __integrity_iint_find - return the iint associated with an inode */ @@ -211,3 +214,18 @@ void __init integrity_load_keys(void) ima_load_x509(); evm_load_x509(); } + +static int __init integrity_fs_init(void) +{ + integrity_dir = securityfs_create_dir("integrity", NULL); + if (IS_ERR(integrity_dir)) { + pr_err("Unable to create integrity sysfs dir: %ld\n", + PTR_ERR(integrity_dir)); + integrity_dir = NULL; + return PTR_ERR(integrity_dir); + } + + return 0; +} + +late_initcall(integrity_fs_init) diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 35fe91aa1fc9..354bb5716ce3 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -53,7 +53,6 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 }; extern int ima_policy_flag; /* set during initialization */ -extern int ima_initialized; extern int ima_used_chip; extern int ima_hash_algo; extern int ima_appraise; diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index bf88236b7a0b..a02c5acfd403 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -326,7 +326,7 @@ void ima_audit_measurement(struct integrity_iint_cache *iint, hex_byte_pack(hash + (i * 2), iint->ima_hash->digest[i]); hash[i * 2] = '\0'; - ab = audit_log_start(current->audit_context, GFP_KERNEL, + ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_INTEGRITY_RULE); if (!ab) goto out; diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index fa540c0469da..ae9d5c766a3c 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -15,6 +15,9 @@ * implemenents security file system for reporting * current measurement list and IMA statistics */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/fcntl.h> #include <linux/slab.h> #include <linux/module.h> @@ -336,7 +339,7 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf, if (data[0] == '/') { result = ima_read_policy(data); } else if (ima_appraise & IMA_APPRAISE_POLICY) { - pr_err("IMA: signed policy file (specified as an absolute pathname) required\n"); + pr_err("signed policy file (specified as an absolute pathname) required\n"); integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL, "policy_update", "signed policy required", 1, 0); @@ -356,6 +359,7 @@ out: } static struct dentry *ima_dir; +static struct dentry *ima_symlink; static struct dentry *binary_runtime_measurements; static struct dentry *ascii_runtime_measurements; static struct dentry *runtime_measurements_count; @@ -417,7 +421,7 @@ static int ima_release_policy(struct inode *inode, struct file *file) valid_policy = 0; } - pr_info("IMA: policy update %s\n", cause); + pr_info("policy update %s\n", cause); integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL, "policy_update", cause, !valid_policy, 0); @@ -434,6 +438,8 @@ static int ima_release_policy(struct inode *inode, struct file *file) ima_policy = NULL; #elif defined(CONFIG_IMA_WRITE_POLICY) clear_bit(IMA_FS_BUSY, &ima_fs_flags); +#elif defined(CONFIG_IMA_READ_POLICY) + inode->i_mode &= ~S_IWUSR; #endif return 0; } @@ -448,10 +454,15 @@ static const struct file_operations ima_measure_policy_ops = { int __init ima_fs_init(void) { - ima_dir = securityfs_create_dir("ima", NULL); + ima_dir = securityfs_create_dir("ima", integrity_dir); if (IS_ERR(ima_dir)) return -1; + ima_symlink = securityfs_create_symlink("ima", NULL, "integrity/ima", + NULL); + if (IS_ERR(ima_symlink)) + goto out; + binary_runtime_measurements = securityfs_create_file("binary_runtime_measurements", S_IRUSR | S_IRGRP, ima_dir, NULL, @@ -491,6 +502,7 @@ out: securityfs_remove(runtime_measurements_count); securityfs_remove(ascii_runtime_measurements); securityfs_remove(binary_runtime_measurements); + securityfs_remove(ima_symlink); securityfs_remove(ima_dir); securityfs_remove(ima_policy); return -1; diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c index e473eee913cb..16bd18747cfa 100644 --- a/security/integrity/ima/ima_kexec.c +++ b/security/integrity/ima/ima_kexec.c @@ -10,6 +10,8 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/seq_file.h> #include <linux/vmalloc.h> #include <linux/kexec.h> diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 74d0bd7e76d7..dca44cf7838e 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -32,8 +32,6 @@ #include "ima.h" -int ima_initialized; - #ifdef CONFIG_IMA_APPRAISE int ima_appraise = IMA_APPRAISE_ENFORCE; #else @@ -61,14 +59,11 @@ static int __init hash_setup(char *str) goto out; } - for (i = 0; i < HASH_ALGO__LAST; i++) { - if (strcmp(str, hash_algo_name[i]) == 0) { - ima_hash_algo = i; - break; - } - } - if (i == HASH_ALGO__LAST) + i = match_string(hash_algo_name, HASH_ALGO__LAST, str); + if (i < 0) return 1; + + ima_hash_algo = i; out: hash_setup_done = 1; return 1; @@ -449,6 +444,7 @@ int ima_read_file(struct file *file, enum kernel_read_file_id read_id) static int read_idmap[READING_MAX_ID] = { [READING_FIRMWARE] = FIRMWARE_CHECK, + [READING_FIRMWARE_PREALLOC_BUFFER] = FIRMWARE_CHECK, [READING_MODULE] = MODULE_CHECK, [READING_KEXEC_IMAGE] = KEXEC_KERNEL_CHECK, [READING_KEXEC_INITRAMFS] = KEXEC_INITRAMFS_CHECK, @@ -517,10 +513,9 @@ static int __init init_ima(void) error = ima_init(); } - if (!error) { - ima_initialized = 1; + if (!error) ima_update_policy_flag(); - } + return error; } diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index d89bebf85421..cdcc9a7b4e24 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -33,6 +33,7 @@ #define IMA_INMASK 0x0040 #define IMA_EUID 0x0080 #define IMA_PCR 0x0100 +#define IMA_FSNAME 0x0200 #define UNKNOWN 0 #define MEASURE 0x0001 /* same as IMA_MEASURE */ @@ -74,6 +75,7 @@ struct ima_rule_entry { void *args_p; /* audit value */ int type; /* audit type */ } lsm[MAX_LSM_RULES]; + char *fsname; }; /* @@ -273,6 +275,9 @@ static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode, if ((rule->flags & IMA_FSMAGIC) && rule->fsmagic != inode->i_sb->s_magic) return false; + if ((rule->flags & IMA_FSNAME) + && strcmp(rule->fsname, inode->i_sb->s_type->name)) + return false; if ((rule->flags & IMA_FSUUID) && !uuid_equal(&rule->fsuuid, &inode->i_sb->s_uuid)) return false; @@ -435,6 +440,17 @@ void ima_update_policy_flag(void) ima_policy_flag &= ~IMA_APPRAISE; } +static int ima_appraise_flag(enum ima_hooks func) +{ + if (func == MODULE_CHECK) + return IMA_APPRAISE_MODULES; + else if (func == FIRMWARE_CHECK) + return IMA_APPRAISE_FIRMWARE; + else if (func == POLICY_CHECK) + return IMA_APPRAISE_POLICY; + return 0; +} + /** * ima_init_policy - initialize the default measure rules. * @@ -473,9 +489,11 @@ void __init ima_init_policy(void) * Insert the appraise rules requiring file signatures, prior to * any other appraise rules. */ - for (i = 0; i < secure_boot_entries; i++) - list_add_tail(&secure_boot_rules[i].list, - &ima_default_rules); + for (i = 0; i < secure_boot_entries; i++) { + list_add_tail(&secure_boot_rules[i].list, &ima_default_rules); + temp_ima_appraise |= + ima_appraise_flag(secure_boot_rules[i].func); + } for (i = 0; i < appraise_entries; i++) { list_add_tail(&default_appraise_rules[i].list, @@ -509,22 +527,9 @@ int ima_check_policy(void) */ void ima_update_policy(void) { - struct list_head *first, *last, *policy; - - /* append current policy with the new rules */ - first = (&ima_temp_rules)->next; - last = (&ima_temp_rules)->prev; - policy = &ima_policy_rules; - - synchronize_rcu(); + struct list_head *policy = &ima_policy_rules; - last->next = policy; - rcu_assign_pointer(list_next_rcu(policy->prev), first); - first->prev = policy->prev; - policy->prev = last; - - /* prepare for the next policy rules addition */ - INIT_LIST_HEAD(&ima_temp_rules); + list_splice_tail_init_rcu(&ima_temp_rules, policy, synchronize_rcu); if (ima_rules != policy) { ima_policy_flag = 0; @@ -540,7 +545,7 @@ enum { Opt_audit, Opt_hash, Opt_dont_hash, Opt_obj_user, Opt_obj_role, Opt_obj_type, Opt_subj_user, Opt_subj_role, Opt_subj_type, - Opt_func, Opt_mask, Opt_fsmagic, + Opt_func, Opt_mask, Opt_fsmagic, Opt_fsname, Opt_fsuuid, Opt_uid_eq, Opt_euid_eq, Opt_fowner_eq, Opt_uid_gt, Opt_euid_gt, Opt_fowner_gt, Opt_uid_lt, Opt_euid_lt, Opt_fowner_lt, @@ -565,6 +570,7 @@ static match_table_t policy_tokens = { {Opt_func, "func=%s"}, {Opt_mask, "mask=%s"}, {Opt_fsmagic, "fsmagic=%s"}, + {Opt_fsname, "fsname=%s"}, {Opt_fsuuid, "fsuuid=%s"}, {Opt_uid_eq, "uid=%s"}, {Opt_euid_eq, "euid=%s"}, @@ -776,6 +782,17 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) if (!result) entry->flags |= IMA_FSMAGIC; break; + case Opt_fsname: + ima_log_string(ab, "fsname", args[0].from); + + entry->fsname = kstrdup(args[0].from, GFP_KERNEL); + if (!entry->fsname) { + result = -ENOMEM; + break; + } + result = 0; + entry->flags |= IMA_FSNAME; + break; case Opt_fsuuid: ima_log_string(ab, "fsuuid", args[0].from); @@ -917,12 +934,9 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) } if (!result && (entry->action == UNKNOWN)) result = -EINVAL; - else if (entry->func == MODULE_CHECK) - temp_ima_appraise |= IMA_APPRAISE_MODULES; - else if (entry->func == FIRMWARE_CHECK) - temp_ima_appraise |= IMA_APPRAISE_FIRMWARE; - else if (entry->func == POLICY_CHECK) - temp_ima_appraise |= IMA_APPRAISE_POLICY; + else if (entry->action == APPRAISE) + temp_ima_appraise |= ima_appraise_flag(entry->func); + audit_log_format(ab, "res=%d", !result); audit_log_end(ab); return result; @@ -1104,6 +1118,12 @@ int ima_policy_show(struct seq_file *m, void *v) seq_puts(m, " "); } + if (entry->flags & IMA_FSNAME) { + snprintf(tbuf, sizeof(tbuf), "%s", entry->fsname); + seq_printf(m, pt(Opt_fsname), tbuf); + seq_puts(m, " "); + } + if (entry->flags & IMA_PCR) { snprintf(tbuf, sizeof(tbuf), "%d", entry->pcr); seq_printf(m, pt(Opt_pcr), tbuf); diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c index 5afaa53decc5..43752002c222 100644 --- a/security/integrity/ima/ima_template_lib.c +++ b/security/integrity/ima/ima_template_lib.c @@ -13,6 +13,8 @@ * Library of supported template fields. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include "ima_template_lib.h" static bool ima_template_hash_algo_allowed(u8 algo) diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 5e58e02ba8dc..0bb372eed62a 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -143,6 +143,8 @@ int integrity_kernel_read(struct file *file, loff_t offset, #define INTEGRITY_KEYRING_MODULE 2 #define INTEGRITY_KEYRING_MAX 3 +extern struct dentry *integrity_dir; + #ifdef CONFIG_INTEGRITY_SIGNATURE int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, diff --git a/security/integrity/integrity_audit.c b/security/integrity/integrity_audit.c index 90987d15b6fe..ab10a25310a1 100644 --- a/security/integrity/integrity_audit.c +++ b/security/integrity/integrity_audit.c @@ -38,7 +38,7 @@ void integrity_audit_msg(int audit_msgno, struct inode *inode, if (!integrity_audit_info && audit_info == 1) /* Skip info messages */ return; - ab = audit_log_start(current->audit_context, GFP_KERNEL, audit_msgno); + ab = audit_log_start(audit_context(), GFP_KERNEL, audit_msgno); audit_log_format(ab, "pid=%d uid=%u auid=%u ses=%u", task_pid_nr(current), from_kuid(&init_user_ns, current_cred()->uid), diff --git a/security/keys/big_key.c b/security/keys/big_key.c index 933623784ccd..2806e70d7f8f 100644 --- a/security/keys/big_key.c +++ b/security/keys/big_key.c @@ -22,6 +22,7 @@ #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; @@ -85,6 +86,7 @@ struct key_type key_type_big_key = { * 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 @@ -109,7 +111,7 @@ static int big_key_crypt(enum big_key_op op, struct big_key_buf *buf, size_t dat * 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[crypto_aead_ivsize(big_key_aead)]; + u8 zero_nonce[BIG_KEY_IV_SIZE]; aead_req = aead_request_alloc(big_key_aead, GFP_KERNEL); if (!aead_req) @@ -425,6 +427,13 @@ static int __init big_key_init(void) 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); diff --git a/security/keys/dh.c b/security/keys/dh.c index d1ea9f325f94..b203f7758f97 100644 --- a/security/keys/dh.c +++ b/security/keys/dh.c @@ -142,6 +142,8 @@ static void kdf_dealloc(struct kdf_sdesc *sdesc) * The src pointer is defined as Z || other info where Z is the shared secret * from DH and other info is an arbitrary string (see SP800-56A section * 5.8.1.2). + * + * 'dlen' must be a multiple of the digest size. */ static int kdf_ctr(struct kdf_sdesc *sdesc, const u8 *src, unsigned int slen, u8 *dst, unsigned int dlen, unsigned int zlen) @@ -162,8 +164,8 @@ static int kdf_ctr(struct kdf_sdesc *sdesc, const u8 *src, unsigned int slen, goto err; if (zlen && h) { - u8 tmpbuffer[h]; - size_t chunk = min_t(size_t, zlen, h); + u8 tmpbuffer[32]; + size_t chunk = min_t(size_t, zlen, sizeof(tmpbuffer)); memset(tmpbuffer, 0, chunk); do { @@ -173,7 +175,7 @@ static int kdf_ctr(struct kdf_sdesc *sdesc, const u8 *src, unsigned int slen, goto err; zlen -= chunk; - chunk = min_t(size_t, zlen, h); + chunk = min_t(size_t, zlen, sizeof(tmpbuffer)); } while (zlen); } @@ -183,24 +185,13 @@ static int kdf_ctr(struct kdf_sdesc *sdesc, const u8 *src, unsigned int slen, goto err; } - if (dlen < h) { - u8 tmpbuffer[h]; - - err = crypto_shash_final(desc, tmpbuffer); - if (err) - goto err; - memcpy(dst, tmpbuffer, dlen); - memzero_explicit(tmpbuffer, h); - return 0; - } else { - err = crypto_shash_final(desc, dst); - if (err) - goto err; + err = crypto_shash_final(desc, dst); + if (err) + goto err; - dlen -= h; - dst += h; - counter = cpu_to_be32(be32_to_cpu(counter) + 1); - } + dlen -= h; + dst += h; + counter = cpu_to_be32(be32_to_cpu(counter) + 1); } return 0; @@ -216,14 +207,16 @@ static int keyctl_dh_compute_kdf(struct kdf_sdesc *sdesc, { uint8_t *outbuf = NULL; int ret; + size_t outbuf_len = roundup(buflen, + crypto_shash_digestsize(sdesc->shash.tfm)); - outbuf = kmalloc(buflen, GFP_KERNEL); + outbuf = kmalloc(outbuf_len, GFP_KERNEL); if (!outbuf) { ret = -ENOMEM; goto err; } - ret = kdf_ctr(sdesc, kbuf, kbuflen, outbuf, buflen, lzero); + ret = kdf_ctr(sdesc, kbuf, kbuflen, outbuf, outbuf_len, lzero); if (ret) goto err; diff --git a/security/keys/proc.c b/security/keys/proc.c index fbc4af5c6c9f..5af2934965d8 100644 --- a/security/keys/proc.c +++ b/security/keys/proc.c @@ -18,7 +18,6 @@ #include <asm/errno.h> #include "internal.h" -static int proc_keys_open(struct inode *inode, struct file *file); static void *proc_keys_start(struct seq_file *p, loff_t *_pos); static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos); static void proc_keys_stop(struct seq_file *p, void *v); @@ -31,14 +30,6 @@ static const struct seq_operations proc_keys_ops = { .show = proc_keys_show, }; -static const struct file_operations proc_keys_fops = { - .open = proc_keys_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - -static int proc_key_users_open(struct inode *inode, struct file *file); static void *proc_key_users_start(struct seq_file *p, loff_t *_pos); static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos); static void proc_key_users_stop(struct seq_file *p, void *v); @@ -51,13 +42,6 @@ static const struct seq_operations proc_key_users_ops = { .show = proc_key_users_show, }; -static const struct file_operations proc_key_users_fops = { - .open = proc_key_users_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - /* * Declare the /proc files. */ @@ -65,11 +49,11 @@ static int __init key_proc_init(void) { struct proc_dir_entry *p; - p = proc_create("keys", 0, NULL, &proc_keys_fops); + p = proc_create_seq("keys", 0, NULL, &proc_keys_ops); if (!p) panic("Cannot create /proc/keys\n"); - p = proc_create("key-users", 0, NULL, &proc_key_users_fops); + p = proc_create_seq("key-users", 0, NULL, &proc_key_users_ops); if (!p) panic("Cannot create /proc/key-users\n"); @@ -96,11 +80,6 @@ static struct rb_node *key_serial_next(struct seq_file *p, struct rb_node *n) return n; } -static int proc_keys_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &proc_keys_ops); -} - static struct key *find_ge_key(struct seq_file *p, key_serial_t id) { struct user_namespace *user_ns = seq_user_ns(p); @@ -293,15 +272,6 @@ static struct rb_node *key_user_first(struct user_namespace *user_ns, struct rb_ return __key_user_next(user_ns, n); } -/* - * Implement "/proc/key-users" to provides a list of the key users and their - * quotas. - */ -static int proc_key_users_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &proc_key_users_ops); -} - static void *proc_key_users_start(struct seq_file *p, loff_t *_pos) __acquires(key_user_lock) { diff --git a/security/keys/trusted.c b/security/keys/trusted.c index 423776682025..b69d3b1777c2 100644 --- a/security/keys/trusted.c +++ b/security/keys/trusted.c @@ -1148,7 +1148,7 @@ static long trusted_read(const struct key *key, char __user *buffer, return -EINVAL; if (buffer && buflen >= 2 * p->blob_len) { - ascii_buf = kmalloc(2 * p->blob_len, GFP_KERNEL); + ascii_buf = kmalloc_array(2, p->blob_len, GFP_KERNEL); if (!ascii_buf) return -ENOMEM; diff --git a/security/lsm_audit.c b/security/lsm_audit.c index 67703dbe29ea..f84001019356 100644 --- a/security/lsm_audit.c +++ b/security/lsm_audit.c @@ -447,7 +447,7 @@ void common_lsm_audit(struct common_audit_data *a, if (a == NULL) return; /* we use GFP_ATOMIC so we won't sleep */ - ab = audit_log_start(current->audit_context, GFP_ATOMIC | __GFP_NOWARN, + ab = audit_log_start(audit_context(), GFP_ATOMIC | __GFP_NOWARN, AUDIT_AVC); if (ab == NULL) diff --git a/security/security.c b/security/security.c index 7bc2fde023a7..68f46d849abe 100644 --- a/security/security.c +++ b/security/security.c @@ -1358,6 +1358,12 @@ int security_socket_post_create(struct socket *sock, int family, protocol, kern); } +int security_socket_socketpair(struct socket *socka, struct socket *sockb) +{ + return call_int_hook(socket_socketpair, 0, socka, sockb); +} +EXPORT_SYMBOL(security_socket_socketpair); + int security_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) { return call_int_hook(socket_bind, 0, sock, address, addrlen); diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 4cafe6a19167..2b5ee5fbd652 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -274,11 +274,10 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent * Try reloading inode security labels that have been marked as invalid. The * @may_sleep parameter indicates when sleeping and thus reloading labels is * allowed; when set to false, returns -ECHILD when the label is - * invalid. The @opt_dentry parameter should be set to a dentry of the inode; - * when no dentry is available, set it to NULL instead. + * invalid. The @dentry parameter should be set to a dentry of the inode. */ static int __inode_security_revalidate(struct inode *inode, - struct dentry *opt_dentry, + struct dentry *dentry, bool may_sleep) { struct inode_security_struct *isec = inode->i_security; @@ -295,7 +294,7 @@ static int __inode_security_revalidate(struct inode *inode, * @opt_dentry is NULL and no dentry for this inode can be * found; in that case, continue using the old label. */ - inode_doinit_with_dentry(inode, opt_dentry); + inode_doinit_with_dentry(inode, dentry); } return 0; } @@ -1471,7 +1470,9 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc return SECCLASS_QIPCRTR_SOCKET; case PF_SMC: return SECCLASS_SMC_SOCKET; -#if PF_MAX > 44 + case PF_XDP: + return SECCLASS_XDP_SOCKET; +#if PF_MAX > 45 #error New address family defined, please update this function. #endif } @@ -1568,8 +1569,15 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent /* Called from d_instantiate or d_splice_alias. */ dentry = dget(opt_dentry); } else { - /* Called from selinux_complete_init, try to find a dentry. */ + /* + * Called from selinux_complete_init, try to find a dentry. + * Some filesystems really want a connected one, so try + * that first. We could split SECURITY_FS_USE_XATTR in + * two, depending upon that... + */ dentry = d_find_alias(inode); + if (!dentry) + dentry = d_find_any_alias(inode); } if (!dentry) { /* @@ -1674,14 +1682,19 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent if ((sbsec->flags & SE_SBGENFS) && !S_ISLNK(inode->i_mode)) { /* We must have a dentry to determine the label on * procfs inodes */ - if (opt_dentry) + if (opt_dentry) { /* Called from d_instantiate or * d_splice_alias. */ dentry = dget(opt_dentry); - else + } else { /* Called from selinux_complete_init, try to - * find a dentry. */ + * find a dentry. Some filesystems really want + * a connected one, so try that first. + */ dentry = d_find_alias(inode); + if (!dentry) + dentry = d_find_any_alias(inode); + } /* * This can be hit on boot when a file is accessed * before the policy is loaded. When we load policy we @@ -3294,7 +3307,8 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name, } else { audit_size = 0; } - ab = audit_log_start(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR); + ab = audit_log_start(audit_context(), + GFP_ATOMIC, AUDIT_SELINUX_ERR); audit_log_format(ab, "op=setxattr invalid_context="); audit_log_n_untrustedstring(ab, value, audit_size); audit_log_end(ab); @@ -4569,6 +4583,18 @@ static int selinux_socket_post_create(struct socket *sock, int family, return err; } +static int selinux_socket_socketpair(struct socket *socka, + struct socket *sockb) +{ + struct sk_security_struct *sksec_a = socka->sk->sk_security; + struct sk_security_struct *sksec_b = sockb->sk->sk_security; + + sksec_a->peer_sid = sksec_b->sid; + sksec_b->peer_sid = sksec_a->sid; + + return 0; +} + /* Range of port numbers used to automatically bind. Need to determine whether we should perform a name_bind permission check between the socket and the port number. */ @@ -4576,6 +4602,7 @@ static int selinux_socket_post_create(struct socket *sock, int family, static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) { struct sock *sk = sock->sk; + struct sk_security_struct *sksec = sk->sk_security; u16 family; int err; @@ -4587,11 +4614,11 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in family = sk->sk_family; if (family == PF_INET || family == PF_INET6) { char *addrp; - struct sk_security_struct *sksec = sk->sk_security; struct common_audit_data ad; struct lsm_network_audit net = {0,}; struct sockaddr_in *addr4 = NULL; struct sockaddr_in6 *addr6 = NULL; + u16 family_sa = address->sa_family; unsigned short snum; u32 sid, node_perm; @@ -4601,11 +4628,20 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in * need to check address->sa_family as it is possible to have * sk->sk_family = PF_INET6 with addr->sa_family = AF_INET. */ - switch (address->sa_family) { + switch (family_sa) { + case AF_UNSPEC: case AF_INET: if (addrlen < sizeof(struct sockaddr_in)) return -EINVAL; addr4 = (struct sockaddr_in *)address; + if (family_sa == AF_UNSPEC) { + /* see __inet_bind(), we only want to allow + * AF_UNSPEC if the address is INADDR_ANY + */ + if (addr4->sin_addr.s_addr != htonl(INADDR_ANY)) + goto err_af; + family_sa = AF_INET; + } snum = ntohs(addr4->sin_port); addrp = (char *)&addr4->sin_addr.s_addr; break; @@ -4617,15 +4653,14 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in addrp = (char *)&addr6->sin6_addr.s6_addr; break; default: - /* Note that SCTP services expect -EINVAL, whereas - * others expect -EAFNOSUPPORT. - */ - if (sksec->sclass == SECCLASS_SCTP_SOCKET) - return -EINVAL; - else - return -EAFNOSUPPORT; + goto err_af; } + ad.type = LSM_AUDIT_DATA_NET; + ad.u.net = &net; + ad.u.net->sport = htons(snum); + ad.u.net->family = family_sa; + if (snum) { int low, high; @@ -4637,10 +4672,6 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in snum, &sid); if (err) goto out; - ad.type = LSM_AUDIT_DATA_NET; - ad.u.net = &net; - ad.u.net->sport = htons(snum); - ad.u.net->family = family; err = avc_has_perm(&selinux_state, sksec->sid, sid, sksec->sclass, @@ -4672,16 +4703,11 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in break; } - err = sel_netnode_sid(addrp, family, &sid); + err = sel_netnode_sid(addrp, family_sa, &sid); if (err) goto out; - ad.type = LSM_AUDIT_DATA_NET; - ad.u.net = &net; - ad.u.net->sport = htons(snum); - ad.u.net->family = family; - - if (address->sa_family == AF_INET) + if (family_sa == AF_INET) ad.u.net->v4info.saddr = addr4->sin_addr.s_addr; else ad.u.net->v6info.saddr = addr6->sin6_addr; @@ -4694,10 +4720,15 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in } out: return err; +err_af: + /* Note that SCTP services expect -EINVAL, others -EAFNOSUPPORT. */ + if (sksec->sclass == SECCLASS_SCTP_SOCKET) + return -EINVAL; + return -EAFNOSUPPORT; } /* This supports connect(2) and SCTP connect services such as sctp_connectx(3) - * and sctp_sendmsg(3) as described in Documentation/security/LSM-sctp.txt + * and sctp_sendmsg(3) as described in Documentation/security/LSM-sctp.rst */ static int selinux_socket_connect_helper(struct socket *sock, struct sockaddr *address, int addrlen) @@ -4771,7 +4802,7 @@ static int selinux_socket_connect_helper(struct socket *sock, ad.type = LSM_AUDIT_DATA_NET; ad.u.net = &net; ad.u.net->dport = htons(snum); - ad.u.net->family = sk->sk_family; + ad.u.net->family = address->sa_family; err = avc_has_perm(&selinux_state, sksec->sid, sid, sksec->sclass, perm, &ad); if (err) @@ -5272,6 +5303,7 @@ static int selinux_sctp_bind_connect(struct sock *sk, int optname, while (walk_size < addrlen) { addr = addr_buf; switch (addr->sa_family) { + case AF_UNSPEC: case AF_INET: len = sizeof(struct sockaddr_in); break; @@ -5279,7 +5311,7 @@ static int selinux_sctp_bind_connect(struct sock *sk, int optname, len = sizeof(struct sockaddr_in6); break; default: - return -EAFNOSUPPORT; + return -EINVAL; } err = -EINVAL; @@ -6431,7 +6463,9 @@ static int selinux_setprocattr(const char *name, void *value, size_t size) audit_size = size - 1; else audit_size = size; - ab = audit_log_start(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR); + ab = audit_log_start(audit_context(), + GFP_ATOMIC, + AUDIT_SELINUX_ERR); audit_log_format(ab, "op=fscreate invalid_context="); audit_log_n_untrustedstring(ab, value, audit_size); audit_log_end(ab); @@ -6999,6 +7033,7 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(socket_create, selinux_socket_create), LSM_HOOK_INIT(socket_post_create, selinux_socket_post_create), + LSM_HOOK_INIT(socket_socketpair, selinux_socket_socketpair), LSM_HOOK_INIT(socket_bind, selinux_socket_bind), LSM_HOOK_INIT(socket_connect, selinux_socket_connect), LSM_HOOK_INIT(socket_listen, selinux_socket_listen), diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index 7f0372426494..bd5fe0d3204a 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -240,9 +240,11 @@ struct security_class_mapping secclass_map[] = { { "manage_subnet", NULL } }, { "bpf", {"map_create", "map_read", "map_write", "prog_load", "prog_run"} }, + { "xdp_socket", + { COMMON_SOCK_PERMS, NULL } }, { NULL } }; -#if PF_MAX > 44 +#if PF_MAX > 45 #error New address family defined, please update secclass_map. #endif diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 19e35dd695d7..79d3709b0671 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -167,7 +167,7 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf, NULL); if (length) goto out; - audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS, + audit_log(audit_context(), GFP_KERNEL, AUDIT_MAC_STATUS, "enforcing=%d old_enforcing=%d auid=%u ses=%u" " enabled=%d old-enabled=%d lsm=selinux res=1", new_value, old_value, @@ -303,7 +303,7 @@ static ssize_t sel_write_disable(struct file *file, const char __user *buf, length = selinux_disable(fsi->state); if (length) goto out; - audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS, + audit_log(audit_context(), GFP_KERNEL, AUDIT_MAC_STATUS, "enforcing=%d old_enforcing=%d auid=%u ses=%u" " enabled=%d old-enabled=%d lsm=selinux res=1", enforcing, enforcing, @@ -575,7 +575,7 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf, length = count; out1: - audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_POLICY_LOAD, + audit_log(audit_context(), GFP_KERNEL, AUDIT_MAC_POLICY_LOAD, "auid=%u ses=%u lsm=selinux res=1", from_kuid(&init_user_ns, audit_get_loginuid(current)), audit_get_sessionid(current)); diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 8057e19dc15f..dd2ceec06fef 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -501,7 +501,7 @@ static void security_dump_masked_av(struct policydb *policydb, goto out; /* audit a message */ - ab = audit_log_start(current->audit_context, + ab = audit_log_start(audit_context(), GFP_ATOMIC, AUDIT_SELINUX_ERR); if (!ab) goto out; @@ -743,7 +743,7 @@ static int security_validtrans_handle_fail(struct selinux_state *state, goto out; if (context_struct_to_string(p, tcontext, &t, &tlen)) goto out; - audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR, + audit_log(audit_context(), GFP_ATOMIC, AUDIT_SELINUX_ERR, "op=security_validate_transition seresult=denied" " oldcontext=%s newcontext=%s taskcontext=%s tclass=%s", o, n, t, sym_name(p, SYM_CLASSES, tclass-1)); @@ -929,7 +929,7 @@ int security_bounded_transition(struct selinux_state *state, &old_name, &length) && !context_struct_to_string(policydb, new_context, &new_name, &length)) { - audit_log(current->audit_context, + audit_log(audit_context(), GFP_ATOMIC, AUDIT_SELINUX_ERR, "op=security_bounded_transition " "seresult=denied " @@ -1494,7 +1494,7 @@ static int security_context_to_sid_core(struct selinux_state *state, scontext_len, &context, def_sid); if (rc == -EINVAL && force) { context.str = str; - context.len = scontext_len; + context.len = strlen(str) + 1; str = NULL; } else if (rc) goto out_unlock; @@ -1586,7 +1586,7 @@ static int compute_sid_handle_invalid_context( goto out; if (context_struct_to_string(policydb, newcontext, &n, &nlen)) goto out; - audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR, + audit_log(audit_context(), GFP_ATOMIC, AUDIT_SELINUX_ERR, "op=security_compute_sid invalid_context=%s" " scontext=%s" " tcontext=%s" @@ -2118,7 +2118,7 @@ int security_load_policy(struct selinux_state *state, void *data, size_t len) int rc = 0; struct policy_file file = { data, len }, *fp = &file; - oldpolicydb = kzalloc(2 * sizeof(*oldpolicydb), GFP_KERNEL); + oldpolicydb = kcalloc(2, sizeof(*oldpolicydb), GFP_KERNEL); if (!oldpolicydb) { rc = -ENOMEM; goto out; @@ -2882,7 +2882,7 @@ int security_set_bools(struct selinux_state *state, int len, int *values) for (i = 0; i < len; i++) { if (!!values[i] != policydb->bool_val_to_struct[i]->state) { - audit_log(current->audit_context, GFP_ATOMIC, + audit_log(audit_context(), GFP_ATOMIC, AUDIT_MAC_CONFIG_CHANGE, "bool=%s val=%d old_val=%d auid=%u ses=%u", sym_name(policydb, SYM_BOOLS, i), @@ -3025,7 +3025,7 @@ int security_sid_mls_copy(struct selinux_state *state, if (rc) { if (!context_struct_to_string(policydb, &newcon, &s, &len)) { - audit_log(current->audit_context, + audit_log(audit_context(), GFP_ATOMIC, AUDIT_SELINUX_ERR, "op=security_sid_mls_copy " "invalid_context=%s", s); diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 0b414836bebd..19de675d4504 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -1545,9 +1545,9 @@ static int smack_inode_listsecurity(struct inode *inode, char *buffer, */ static void smack_inode_getsecid(struct inode *inode, u32 *secid) { - struct inode_smack *isp = inode->i_security; + struct smack_known *skp = smk_of_inode(inode); - *secid = isp->smk_inode->smk_secid; + *secid = skp->smk_secid; } /* @@ -2296,6 +2296,7 @@ static void smack_task_to_inode(struct task_struct *p, struct inode *inode) struct smack_known *skp = smk_of_task_struct(p); isp->smk_inode = skp; + isp->smk_flags |= SMK_INODE_INSTANT; } /* @@ -2842,6 +2843,27 @@ static int smack_socket_post_create(struct socket *sock, int family, return smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET); } +/** + * smack_socket_socketpair - create socket pair + * @socka: one socket + * @sockb: another socket + * + * Cross reference the peer labels for SO_PEERSEC + * + * Returns 0 on success, and error code otherwise + */ +static int smack_socket_socketpair(struct socket *socka, + struct socket *sockb) +{ + struct socket_smack *asp = socka->sk->sk_security; + struct socket_smack *bsp = sockb->sk->sk_security; + + asp->smk_packet = bsp->smk_out; + bsp->smk_packet = asp->smk_out; + + return 0; +} + #ifdef SMACK_IPV6_PORT_LABELING /** * smack_socket_bind - record port binding information. @@ -4538,12 +4560,10 @@ static int smack_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen) static int smack_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen) { - int len = 0; - len = smack_inode_getsecurity(inode, XATTR_SMACK_SUFFIX, ctx, true); + struct smack_known *skp = smk_of_inode(inode); - if (len < 0) - return len; - *ctxlen = len; + *ctx = skp->smk_known; + *ctxlen = strlen(skp->smk_known); return 0; } @@ -4724,6 +4744,7 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(unix_may_send, smack_unix_may_send), LSM_HOOK_INIT(socket_post_create, smack_socket_post_create), + LSM_HOOK_INIT(socket_socketpair, smack_socket_socketpair), #ifdef SMACK_IPV6_PORT_LABELING LSM_HOOK_INIT(socket_bind, smack_socket_bind), #endif |