From c70c86c421427fd8487867de66c4104b15abd772 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 9 Jun 2017 14:07:02 -0700 Subject: [PATCH] apparmor: move capability checks to using labels Signed-off-by: John Johansen --- security/apparmor/capability.c | 57 +++++++++++++++++--------- security/apparmor/include/capability.h | 6 ++- security/apparmor/ipc.c | 2 +- security/apparmor/lsm.c | 20 ++++++--- security/apparmor/resource.c | 2 +- 5 files changed, 58 insertions(+), 29 deletions(-) diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c index 3bc19843d8df..67e347192a55 100644 --- a/security/apparmor/capability.c +++ b/security/apparmor/capability.c @@ -48,15 +48,16 @@ static DEFINE_PER_CPU(struct audit_cache, audit_cache); static void audit_cb(struct audit_buffer *ab, void *va) { struct common_audit_data *sa = va; + audit_log_format(ab, " capname="); audit_log_untrustedstring(ab, capability_names[sa->u.cap]); } /** * audit_caps - audit a capability + * @sa: audit data * @profile: profile being tested for confinement (NOT NULL) * @cap: capability tested - @audit: whether an audit record should be generated * @error: error code returned by test * * Do auditing of capability and handle, audit/complain/kill modes switching @@ -64,16 +65,13 @@ static void audit_cb(struct audit_buffer *ab, void *va) * * Returns: 0 or sa->error on success, error code on failure */ -static int audit_caps(struct aa_profile *profile, int cap, int audit, - int error) +static int audit_caps(struct common_audit_data *sa, struct aa_profile *profile, + int cap, int error) { struct audit_cache *ent; int type = AUDIT_APPARMOR_AUTO; - DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_CAP, OP_CAPABLE); - sa.u.cap = cap; - aad(&sa)->error = error; - if (audit == SECURITY_CAP_NOAUDIT) - aad(&sa)->info = "optional: no audit"; + + aad(sa)->error = error; if (likely(!error)) { /* test if auditing is being forced */ @@ -105,24 +103,44 @@ static int audit_caps(struct aa_profile *profile, int cap, int audit, } put_cpu_var(audit_cache); - return aa_audit(type, profile, &sa, audit_cb); + return aa_audit(type, profile, sa, audit_cb); } /** * profile_capable - test if profile allows use of capability @cap * @profile: profile being enforced (NOT NULL, NOT unconfined) * @cap: capability to test if allowed + * @audit: whether an audit record should be generated + * @sa: audit data (MAY BE NULL indicating no auditing) * * Returns: 0 if allowed else -EPERM */ -static int profile_capable(struct aa_profile *profile, int cap) +static int profile_capable(struct aa_profile *profile, int cap, int audit, + struct common_audit_data *sa) { - return cap_raised(profile->caps.allow, cap) ? 0 : -EPERM; + int error; + + if (cap_raised(profile->caps.allow, cap) && + !cap_raised(profile->caps.denied, cap)) + error = 0; + else + error = -EPERM; + + if (audit == SECURITY_CAP_NOAUDIT) { + if (!COMPLAIN_MODE(profile)) + return error; + /* audit the cap request in complain mode but note that it + * should be optional. + */ + aad(sa)->info = "optional: no audit"; + } + + return audit_caps(sa, profile, cap, error); } /** * aa_capable - test permission to use capability - * @profile: profile being tested against (NOT NULL) + * @label: label being tested for capability (NOT NULL) * @cap: capability to be tested * @audit: whether an audit record should be generated * @@ -130,14 +148,15 @@ static int profile_capable(struct aa_profile *profile, int cap) * * Returns: 0 on success, or else an error code. */ -int aa_capable(struct aa_profile *profile, int cap, int audit) +int aa_capable(struct aa_label *label, int cap, int audit) { - int error = profile_capable(profile, cap); + struct aa_profile *profile; + int error = 0; + DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_CAP, OP_CAPABLE); - if (audit == SECURITY_CAP_NOAUDIT) { - if (!COMPLAIN_MODE(profile)) - return error; - } + sa.u.cap = cap; + error = fn_for_each_confined(label, profile, + profile_capable(profile, cap, audit, &sa)); - return audit_caps(profile, cap, audit, error); + return error; } diff --git a/security/apparmor/include/capability.h b/security/apparmor/include/capability.h index 1218e95ebe49..e0304e2aeb7f 100644 --- a/security/apparmor/include/capability.h +++ b/security/apparmor/include/capability.h @@ -19,11 +19,12 @@ #include "apparmorfs.h" -struct aa_profile; +struct aa_label; /* aa_caps - confinement data for capabilities * @allowed: capabilities mask * @audit: caps that are to be audited + * @denied: caps that are explicitly denied * @quiet: caps that should not be audited * @kill: caps that when requested will result in the task being killed * @extended: caps that are subject finer grained mediation @@ -31,6 +32,7 @@ struct aa_profile; struct aa_caps { kernel_cap_t allow; kernel_cap_t audit; + kernel_cap_t denied; kernel_cap_t quiet; kernel_cap_t kill; kernel_cap_t extended; @@ -38,7 +40,7 @@ struct aa_caps { extern struct aa_sfs_entry aa_sfs_entry_caps[]; -int aa_capable(struct aa_profile *profile, int cap, int audit); +int aa_capable(struct aa_label *label, int cap, int audit); static inline void aa_free_cap_rules(struct aa_caps *caps) { diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c index fa68cd42bd15..7678d94c4002 100644 --- a/security/apparmor/ipc.c +++ b/security/apparmor/ipc.c @@ -68,7 +68,7 @@ int aa_may_ptrace(struct aa_profile *tracer, struct aa_profile *tracee, if (profile_unconfined(tracer) || tracer == tracee) return 0; /* log this capability request */ - return aa_capable(tracer, CAP_SYS_PTRACE, 1); + return aa_capable(&tracer->label, CAP_SYS_PTRACE, 1); } /** diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index f7f82ce00d73..bcfdcdb3eae2 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -117,20 +117,28 @@ static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted) { struct aa_label *label; - struct aa_profile *profile; const struct cred *cred; rcu_read_lock(); cred = __task_cred(target); label = aa_get_newest_cred_label(cred); - profile = labels_profile(label); + /* * cap_capget is stacked ahead of this and will * initialize effective and permitted. */ - if (!profile_unconfined(profile) && !COMPLAIN_MODE(profile)) { - *effective = cap_intersect(*effective, profile->caps.allow); - *permitted = cap_intersect(*permitted, profile->caps.allow); + if (!unconfined(label)) { + struct aa_profile *profile; + struct label_it i; + + label_for_each_confined(i, label, profile) { + if (COMPLAIN_MODE(profile)) + continue; + *effective = cap_intersect(*effective, + profile->caps.allow); + *permitted = cap_intersect(*permitted, + profile->caps.allow); + } } rcu_read_unlock(); aa_put_label(label); @@ -146,7 +154,7 @@ static int apparmor_capable(const struct cred *cred, struct user_namespace *ns, label = aa_get_newest_cred_label(cred); if (!unconfined(label)) - error = aa_capable(labels_profile(label), cap, audit); + error = aa_capable(label, cap, audit); aa_put_label(label); return error; diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c index ab8e104c1970..2474ee0b3467 100644 --- a/security/apparmor/resource.c +++ b/security/apparmor/resource.c @@ -100,7 +100,7 @@ int aa_task_setrlimit(struct aa_profile *profile, struct task_struct *task, * task has CAP_SYS_RESOURCE. */ if ((profile != labels_profile(task_label) && - aa_capable(profile, CAP_SYS_RESOURCE, 1)) || + aa_capable(&profile->label, CAP_SYS_RESOURCE, 1)) || (profile->rlimits.mask & (1 << resource) && new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max)) error = -EACCES; -- 2.20.1