summaryrefslogtreecommitdiff
path: root/security
diff options
context:
space:
mode:
Diffstat (limited to 'security')
-rw-r--r--security/apparmor/audit.c90
-rw-r--r--security/apparmor/domain.c2
-rw-r--r--security/apparmor/include/audit.h6
-rw-r--r--security/apparmor/include/label.h2
-rw-r--r--security/apparmor/include/path.h33
-rw-r--r--security/apparmor/include/secid.h17
-rw-r--r--security/apparmor/label.c15
-rw-r--r--security/apparmor/lib.c2
-rw-r--r--security/apparmor/lsm.c50
-rw-r--r--security/apparmor/match.c2
-rw-r--r--security/apparmor/mount.c2
-rw-r--r--security/apparmor/policy.c7
-rw-r--r--security/apparmor/policy_unpack.c2
-rw-r--r--security/apparmor/resource.c2
-rw-r--r--security/apparmor/secid.c151
-rw-r--r--security/commoncap.c10
-rw-r--r--security/device_cgroup.c2
-rw-r--r--security/integrity/evm/Kconfig11
-rw-r--r--security/integrity/evm/evm.h7
-rw-r--r--security/integrity/evm/evm_crypto.c13
-rw-r--r--security/integrity/evm/evm_main.c79
-rw-r--r--security/integrity/evm/evm_secfs.c206
-rw-r--r--security/integrity/iint.c18
-rw-r--r--security/integrity/ima/ima.h1
-rw-r--r--security/integrity/ima/ima_api.c2
-rw-r--r--security/integrity/ima/ima_fs.c18
-rw-r--r--security/integrity/ima/ima_kexec.c2
-rw-r--r--security/integrity/ima/ima_main.c19
-rw-r--r--security/integrity/ima/ima_policy.c70
-rw-r--r--security/integrity/ima/ima_template_lib.c2
-rw-r--r--security/integrity/integrity.h2
-rw-r--r--security/integrity/integrity_audit.c2
-rw-r--r--security/keys/big_key.c11
-rw-r--r--security/keys/dh.c37
-rw-r--r--security/keys/proc.c34
-rw-r--r--security/keys/trusted.c2
-rw-r--r--security/lsm_audit.c2
-rw-r--r--security/security.c6
-rw-r--r--security/selinux/hooks.c103
-rw-r--r--security/selinux/include/classmap.h4
-rw-r--r--security/selinux/selinuxfs.c6
-rw-r--r--security/selinux/ss/services.c16
-rw-r--r--security/smack/smack_lsm.c35
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