|  | // SPDX-License-Identifier: GPL-2.0-only | 
|  | /* | 
|  | * AppArmor security module | 
|  | * | 
|  | * This file contains AppArmor auditing functions | 
|  | * | 
|  | * Copyright (C) 1998-2008 Novell/SUSE | 
|  | * Copyright 2009-2010 Canonical Ltd. | 
|  | */ | 
|  |  | 
|  | #include <linux/audit.h> | 
|  | #include <linux/socket.h> | 
|  |  | 
|  | #include "include/apparmor.h" | 
|  | #include "include/audit.h" | 
|  | #include "include/policy.h" | 
|  | #include "include/policy_ns.h" | 
|  | #include "include/secid.h" | 
|  |  | 
|  | const char *const audit_mode_names[] = { | 
|  | "normal", | 
|  | "quiet_denied", | 
|  | "quiet", | 
|  | "noquiet", | 
|  | "all" | 
|  | }; | 
|  |  | 
|  | static const char *const aa_audit_type[] = { | 
|  | "AUDIT", | 
|  | "ALLOWED", | 
|  | "DENIED", | 
|  | "HINT", | 
|  | "STATUS", | 
|  | "ERROR", | 
|  | "KILLED", | 
|  | "AUTO" | 
|  | }; | 
|  |  | 
|  | /* | 
|  | * Currently AppArmor auditing is fed straight into the audit framework. | 
|  | * | 
|  | * TODO: | 
|  | * netlink interface for complain mode | 
|  | * user auditing, - send user auditing to netlink interface | 
|  | * system control of whether user audit messages go to system log | 
|  | */ | 
|  |  | 
|  | /** | 
|  | * audit_base - core AppArmor function. | 
|  | * @ab: audit buffer to fill (NOT NULL) | 
|  | * @ca: audit structure containing data to audit (NOT NULL) | 
|  | * | 
|  | * Record common AppArmor audit data from @sa | 
|  | */ | 
|  | static void audit_pre(struct audit_buffer *ab, void *ca) | 
|  | { | 
|  | struct common_audit_data *sa = ca; | 
|  |  | 
|  | if (aa_g_audit_header) { | 
|  | audit_log_format(ab, "apparmor=\"%s\"", | 
|  | aa_audit_type[aad(sa)->type]); | 
|  | } | 
|  |  | 
|  | if (aad(sa)->op) { | 
|  | audit_log_format(ab, " operation=\"%s\"", aad(sa)->op); | 
|  | } | 
|  |  | 
|  | if (aad(sa)->info) { | 
|  | audit_log_format(ab, " info=\"%s\"", aad(sa)->info); | 
|  | if (aad(sa)->error) | 
|  | audit_log_format(ab, " error=%d", aad(sa)->error); | 
|  | } | 
|  |  | 
|  | if (aad(sa)->label) { | 
|  | struct aa_label *label = aad(sa)->label; | 
|  |  | 
|  | if (label_isprofile(label)) { | 
|  | struct aa_profile *profile = labels_profile(label); | 
|  |  | 
|  | if (profile->ns != root_ns) { | 
|  | audit_log_format(ab, " namespace="); | 
|  | audit_log_untrustedstring(ab, | 
|  | profile->ns->base.hname); | 
|  | } | 
|  | audit_log_format(ab, " profile="); | 
|  | audit_log_untrustedstring(ab, profile->base.hname); | 
|  | } else { | 
|  | audit_log_format(ab, " label="); | 
|  | aa_label_xaudit(ab, root_ns, label, FLAG_VIEW_SUBNS, | 
|  | GFP_ATOMIC); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (aad(sa)->name) { | 
|  | audit_log_format(ab, " name="); | 
|  | audit_log_untrustedstring(ab, aad(sa)->name); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * aa_audit_msg - Log a message to the audit subsystem | 
|  | * @sa: audit event structure (NOT NULL) | 
|  | * @cb: optional callback fn for type specific fields (MAYBE NULL) | 
|  | */ | 
|  | void aa_audit_msg(int type, struct common_audit_data *sa, | 
|  | void (*cb) (struct audit_buffer *, void *)) | 
|  | { | 
|  | aad(sa)->type = type; | 
|  | common_lsm_audit(sa, audit_pre, cb); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * aa_audit - Log a profile based audit event to the audit subsystem | 
|  | * @type: audit type for the message | 
|  | * @profile: profile to check against (NOT NULL) | 
|  | * @sa: audit event (NOT NULL) | 
|  | * @cb: optional callback fn for type specific fields (MAYBE NULL) | 
|  | * | 
|  | * Handle default message switching based off of audit mode flags | 
|  | * | 
|  | * Returns: error on failure | 
|  | */ | 
|  | int aa_audit(int type, struct aa_profile *profile, struct common_audit_data *sa, | 
|  | void (*cb) (struct audit_buffer *, void *)) | 
|  | { | 
|  | AA_BUG(!profile); | 
|  |  | 
|  | if (type == AUDIT_APPARMOR_AUTO) { | 
|  | if (likely(!aad(sa)->error)) { | 
|  | if (AUDIT_MODE(profile) != AUDIT_ALL) | 
|  | return 0; | 
|  | type = AUDIT_APPARMOR_AUDIT; | 
|  | } else if (COMPLAIN_MODE(profile)) | 
|  | type = AUDIT_APPARMOR_ALLOWED; | 
|  | else | 
|  | type = AUDIT_APPARMOR_DENIED; | 
|  | } | 
|  | if (AUDIT_MODE(profile) == AUDIT_QUIET || | 
|  | (type == AUDIT_APPARMOR_DENIED && | 
|  | AUDIT_MODE(profile) == AUDIT_QUIET_DENIED)) | 
|  | return aad(sa)->error; | 
|  |  | 
|  | if (KILL_MODE(profile) && type == AUDIT_APPARMOR_DENIED) | 
|  | type = AUDIT_APPARMOR_KILL; | 
|  |  | 
|  | aad(sa)->label = &profile->label; | 
|  |  | 
|  | aa_audit_msg(type, sa, cb); | 
|  |  | 
|  | if (aad(sa)->type == AUDIT_APPARMOR_KILL) | 
|  | (void)send_sig_info(SIGKILL, NULL, | 
|  | sa->type == LSM_AUDIT_DATA_TASK && sa->u.tsk ? | 
|  | sa->u.tsk : current); | 
|  |  | 
|  | if (aad(sa)->type == AUDIT_APPARMOR_ALLOWED) | 
|  | return complain_error(aad(sa)->error); | 
|  |  | 
|  | 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, gfp_t gfp) | 
|  | { | 
|  | 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); | 
|  |  | 
|  | 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, true, false); | 
|  | if (IS_ERR(rule->label)) { | 
|  | int err = PTR_ERR(rule->label); | 
|  | aa_audit_rule_free(rule); | 
|  | return err; | 
|  | } | 
|  |  | 
|  | *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 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; | 
|  | } |