| 1 | |
| 2 | |
| 3 | * AppArmor security module
|
| 4 | *
|
| 5 | * This file contains AppArmor auditing functions
|
| 6 | *
|
| 7 | * Copyright (C) 1998-2008 Novell/SUSE
|
| 8 | * Copyright 2009-2010 Canonical Ltd.
|
| 9 |
|
| 10 |
|
| 11 | #include <linux/audit.h>
|
| 12 | #include <linux/socket.h>
|
| 13 |
|
| 14 | #include "include/apparmor.h"
|
| 15 | #include "include/audit.h"
|
| 16 | #include "include/policy.h"
|
| 17 | #include "include/policy_ns.h"
|
| 18 | #include "include/secid.h"
|
| 19 |
|
| 20 | const char *const audit_mode_names[] = {
|
| 21 | "normal",
|
| 22 | "quiet_denied",
|
| 23 | "quiet.allowed",
|
| 24 | "quiet",
|
| 25 | "noquiet",
|
| 26 | "all"
|
| 27 | };
|
| 28 |
|
| 29 | static const char *const aa_audit_type[] = {
|
| 30 | "AUDIT",
|
| 31 | "ALLOWED",
|
| 32 | "DENIED",
|
| 33 | "HINT",
|
| 34 | "STATUS",
|
| 35 | "ERROR",
|
| 36 | "KILLED",
|
| 37 | "AUTO"
|
| 38 | };
|
| 39 |
|
| 40 | static const char *const aa_class_names[] = {
|
| 41 | "none",
|
| 42 | "unknown",
|
| 43 | "file",
|
| 44 | "cap",
|
| 45 | "net",
|
| 46 | "rlimits",
|
| 47 | "domain",
|
| 48 | "mount",
|
| 49 | "unknown",
|
| 50 | "ptrace",
|
| 51 | "signal",
|
| 52 | "xmatch",
|
| 53 | "unknown",
|
| 54 | "unknown",
|
| 55 | "net",
|
| 56 | "netv9",
|
| 57 | "label",
|
| 58 | "posix_mqueue",
|
| 59 | "io_uring",
|
| 60 | "module",
|
| 61 | "lsm",
|
| 62 | "namespace",
|
| 63 | "io_uring",
|
| 64 | "unknown",
|
| 65 | "unknown",
|
| 66 | "unknown",
|
| 67 | "unknown",
|
| 68 | "unknown",
|
| 69 | "unknown",
|
| 70 | "unknown",
|
| 71 | "netv9_packet",
|
| 72 | "X",
|
| 73 | "dbus",
|
| 74 | };
|
| 75 |
|
| 76 |
|
| 77 | |
| 78 | * Currently AppArmor auditing is fed straight into the audit framework.
|
| 79 | *
|
| 80 | * TODO:
|
| 81 | * netlink interface for complain mode
|
| 82 | * user auditing, - send user auditing to netlink interface
|
| 83 | * system control of whether user audit messages go to system log
|
| 84 |
|
| 85 |
|
| 86 | |
| 87 | * audit_pre() - core AppArmor function.
|
| 88 | * @ab: audit buffer to fill (NOT NULL)
|
| 89 | * @va: audit structure containing data to audit (NOT NULL)
|
| 90 | *
|
| 91 | * Record common AppArmor audit data from @va
|
| 92 |
|
| 93 | static void audit_pre(struct audit_buffer *ab, void *va)
|
| 94 | {
|
| 95 | struct apparmor_audit_data *ad = aad_of_va(va);
|
| 96 |
|
| 97 | if (aa_g_audit_header) {
|
| 98 | audit_log_format(ab, "apparmor=\"%s\"",
|
| 99 | aa_audit_type[ad->type]);
|
| 100 | }
|
| 101 |
|
| 102 | if (ad->op)
|
| 103 | audit_log_format(ab, " operation=\"%s\"", ad->op);
|
| 104 |
|
| 105 | if (ad->class)
|
| 106 | audit_log_format(ab, " class=\"%s\"",
|
| 107 | ad->class <= AA_CLASS_LAST ?
|
| 108 | aa_class_names[ad->class] :
|
| 109 | "unknown");
|
| 110 |
|
| 111 | if (ad->info) {
|
| 112 | audit_log_format(ab, " info=\"%s\"", ad->info);
|
| 113 | if (ad->error)
|
| 114 | audit_log_format(ab, " error=%d", ad->error);
|
| 115 | }
|
| 116 |
|
| 117 | if (ad->subj_label) {
|
| 118 | struct aa_label *label = ad->subj_label;
|
| 119 |
|
| 120 | if (label_isprofile(label)) {
|
| 121 | struct aa_profile *profile = labels_profile(label);
|
| 122 |
|
| 123 | if (profile->ns != root_ns) {
|
| 124 | audit_log_format(ab, " namespace=");
|
| 125 | audit_log_untrustedstring(ab,
|
| 126 | profile->ns->base.hname);
|
| 127 | }
|
| 128 | audit_log_format(ab, " profile=");
|
| 129 | audit_log_untrustedstring(ab, profile->base.hname);
|
| 130 | } else {
|
| 131 | audit_log_format(ab, " label=");
|
| 132 | aa_label_xaudit(ab, root_ns, label, FLAG_VIEW_SUBNS,
|
| 133 | GFP_ATOMIC);
|
| 134 | }
|
| 135 | }
|
| 136 |
|
| 137 | if (ad->name) {
|
| 138 | audit_log_format(ab, " name=");
|
| 139 | audit_log_untrustedstring(ab, ad->name);
|
| 140 | }
|
| 141 | }
|
| 142 |
|
| 143 | int aa_select_audit_type(u32 denied, const struct aa_perms *perms)
|
| 144 | {
|
| 145 | if (likely(!denied))
|
| 146 | return AUDIT_APPARMOR_AUDIT;
|
| 147 | else if (denied & perms->kill)
|
| 148 | return AUDIT_APPARMOR_KILL;
|
| 149 | else if (denied == (denied & perms->complain))
|
| 150 | return AUDIT_APPARMOR_ALLOWED;
|
| 151 | return AUDIT_APPARMOR_DENIED;
|
| 152 | }
|
| 153 |
|
| 154 | |
| 155 | * aa_audit_msg - Log a message to the audit subsystem
|
| 156 | * @type: audit type for the message
|
| 157 | * @ad: audit event structure (NOT NULL)
|
| 158 | * @cb: optional callback fn for type specific fields (MAYBE NULL)
|
| 159 |
|
| 160 | void aa_audit_msg(int type, struct apparmor_audit_data *ad,
|
| 161 | void (*cb) (struct audit_buffer *, void *))
|
| 162 | {
|
| 163 | ad->type = type;
|
| 164 | common_lsm_audit(&ad->common, audit_pre, cb);
|
| 165 | }
|
| 166 |
|
| 167 | int aa_audit_perm_error(struct aa_label *label, u32 request, int error,
|
| 168 | struct apparmor_audit_data *ad,
|
| 169 | void (*cb)(struct audit_buffer *, void *))
|
| 170 | {
|
| 171 | int type = aa_select_audit_type(request, &nullperms);
|
| 172 |
|
| 173 | if (ad) {
|
| 174 | struct aa_profile *profile;
|
| 175 | struct label_it i;
|
| 176 |
|
| 177 | ad->request = request;
|
| 178 | ad->denied = request;
|
| 179 | ad->error = error;
|
| 180 | label_for_each_confined(i, label, profile) {
|
| 181 | ad->subj_label = &profile->label;
|
| 182 | aa_audit_msg(type, ad, cb);
|
| 183 | }
|
| 184 | }
|
| 185 |
|
| 186 | return error;
|
| 187 | }
|
| 188 |
|
| 189 | |
| 190 | * aa_audit - Log a profile based audit event to the audit subsystem
|
| 191 | * @type: audit type for the message
|
| 192 | * @profile: profile to check against (NOT NULL)
|
| 193 | * @ad: audit event (NOT NULL)
|
| 194 | * @cb: optional callback fn for type specific fields (MAYBE NULL)
|
| 195 | *
|
| 196 | * Handle default message switching based off of audit mode flags
|
| 197 | *
|
| 198 | * Returns: error on failure
|
| 199 |
|
| 200 | int aa_audit(int type, struct aa_profile *profile,
|
| 201 | struct apparmor_audit_data *ad,
|
| 202 | void (*cb) (struct audit_buffer *, void *))
|
| 203 | {
|
| 204 | AA_BUG(!profile);
|
| 205 |
|
| 206 | if (type == AUDIT_APPARMOR_AUTO) {
|
| 207 | if (likely(!ad->error)) {
|
| 208 | if (AUDIT_MODE(profile) != AUDIT_ALL)
|
| 209 | return 0;
|
| 210 | type = AUDIT_APPARMOR_AUDIT;
|
| 211 | } else if (COMPLAIN_MODE(profile))
|
| 212 | type = AUDIT_APPARMOR_ALLOWED;
|
| 213 | else
|
| 214 | type = AUDIT_APPARMOR_DENIED;
|
| 215 | }
|
| 216 | if (AUDIT_MODE(profile) == AUDIT_QUIET ||
|
| 217 | (type == AUDIT_APPARMOR_DENIED &&
|
| 218 | AUDIT_MODE(profile) == AUDIT_QUIET_DENIED))
|
| 219 | return ad->error;
|
| 220 |
|
| 221 | if (KILL_MODE(profile) && type == AUDIT_APPARMOR_DENIED)
|
| 222 | type = AUDIT_APPARMOR_KILL;
|
| 223 |
|
| 224 | ad->subj_label = &profile->label;
|
| 225 |
|
| 226 | aa_audit_msg(type, ad, cb);
|
| 227 |
|
| 228 | if (ad->type == AUDIT_APPARMOR_KILL)
|
| 229 | send_sig_info(profile->signal, SEND_SIG_NOINFO,
|
| 230 | ad->common.type == LSM_AUDIT_DATA_TASK &&
|
| 231 | ad->common.u.tsk ? ad->common.u.tsk : current);
|
| 232 |
|
| 233 | if (ad->type == AUDIT_APPARMOR_ALLOWED)
|
| 234 | return complain_error(ad->error);
|
| 235 |
|
| 236 | return ad->error;
|
| 237 | }
|
| 238 |
|
| 239 | struct aa_audit_rule {
|
| 240 | struct aa_label *label;
|
| 241 | };
|
| 242 |
|
| 243 | void aa_audit_rule_free(void *vrule)
|
| 244 | {
|
| 245 | struct aa_audit_rule *rule = vrule;
|
| 246 |
|
| 247 | if (rule) {
|
| 248 | if (!IS_ERR(rule->label))
|
| 249 | aa_put_label(rule->label);
|
| 250 | kfree(rule);
|
| 251 | }
|
| 252 | }
|
| 253 |
|
| 254 | int aa_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule, gfp_t gfp)
|
| 255 | {
|
| 256 | struct aa_audit_rule *rule;
|
| 257 |
|
| 258 | switch (field) {
|
| 259 | case AUDIT_SUBJ_ROLE:
|
| 260 | if (op != Audit_equal && op != Audit_not_equal)
|
| 261 | return -EINVAL;
|
| 262 | break;
|
| 263 | default:
|
| 264 | return -EINVAL;
|
| 265 | }
|
| 266 |
|
| 267 | rule = kzalloc_obj(struct aa_audit_rule, gfp);
|
| 268 |
|
| 269 | if (!rule)
|
| 270 | return -ENOMEM;
|
| 271 |
|
| 272 |
|
| 273 | rule->label = aa_label_parse(&root_ns->unconfined->label, rulestr,
|
| 274 | gfp, true, false);
|
| 275 | if (IS_ERR(rule->label)) {
|
| 276 | int err = PTR_ERR(rule->label);
|
| 277 | aa_audit_rule_free(rule);
|
| 278 | return err;
|
| 279 | }
|
| 280 |
|
| 281 | *vrule = rule;
|
| 282 | return 0;
|
| 283 | }
|
| 284 |
|
| 285 | int aa_audit_rule_known(struct audit_krule *rule)
|
| 286 | {
|
| 287 | int i;
|
| 288 |
|
| 289 | for (i = 0; i < rule->field_count; i++) {
|
| 290 | struct audit_field *f = &rule->fields[i];
|
| 291 |
|
| 292 | switch (f->type) {
|
| 293 | case AUDIT_SUBJ_ROLE:
|
| 294 | return 1;
|
| 295 | }
|
| 296 | }
|
| 297 |
|
| 298 | return 0;
|
| 299 | }
|
| 300 |
|
| 301 | int aa_audit_rule_match(struct lsm_prop *prop, u32 field, u32 op, void *vrule)
|
| 302 | {
|
| 303 | struct aa_audit_rule *rule = vrule;
|
| 304 | struct aa_label *label;
|
| 305 | int found = 0;
|
| 306 |
|
| 307 | label = prop->apparmor.label;
|
| 308 |
|
| 309 | if (!label)
|
| 310 | return -ENOENT;
|
| 311 |
|
| 312 | if (aa_label_is_subset(label, rule->label))
|
| 313 | found = 1;
|
| 314 |
|
| 315 | switch (field) {
|
| 316 | case AUDIT_SUBJ_ROLE:
|
| 317 | switch (op) {
|
| 318 | case Audit_equal:
|
| 319 | return found;
|
| 320 | case Audit_not_equal:
|
| 321 | return !found;
|
| 322 | }
|
| 323 | }
|
| 324 | return 0;
|
| 325 | }
|