Commit | Line | Data |
---|---|---|
b886d83c | 1 | // SPDX-License-Identifier: GPL-2.0-only |
67012e82 JJ |
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. | |
67012e82 JJ |
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" | |
cff281f6 | 17 | #include "include/policy_ns.h" |
e79c26d0 | 18 | #include "include/secid.h" |
67012e82 | 19 | |
2d4cee7e | 20 | const char *const audit_mode_names[] = { |
67012e82 JJ |
21 | "normal", |
22 | "quiet_denied", | |
23 | "quiet", | |
24 | "noquiet", | |
25 | "all" | |
26 | }; | |
27 | ||
2d4cee7e | 28 | static const char *const aa_audit_type[] = { |
67012e82 JJ |
29 | "AUDIT", |
30 | "ALLOWED", | |
31 | "DENIED", | |
32 | "HINT", | |
33 | "STATUS", | |
34 | "ERROR", | |
b492d50b | 35 | "KILLED", |
ade3ddc0 | 36 | "AUTO" |
67012e82 JJ |
37 | }; |
38 | ||
8c4b785a JJ |
39 | static const char *const aa_class_names[] = { |
40 | "none", | |
41 | "unknown", | |
42 | "file", | |
43 | "cap", | |
44 | "net", | |
45 | "rlimits", | |
46 | "domain", | |
47 | "mount", | |
48 | "unknown", | |
49 | "ptrace", | |
50 | "signal", | |
961f3e3d | 51 | "xmatch", |
8c4b785a JJ |
52 | "unknown", |
53 | "unknown", | |
54 | "net", | |
55 | "unknown", | |
56 | "label", | |
961f3e3d JJ |
57 | "posix_mqueue", |
58 | "io_uring", | |
59 | "module", | |
8c4b785a | 60 | "lsm", |
961f3e3d JJ |
61 | "unknown", |
62 | "unknown", | |
63 | "unknown", | |
64 | "unknown", | |
65 | "unknown", | |
66 | "unknown", | |
67 | "unknown", | |
68 | "unknown", | |
69 | "unknown", | |
70 | "unknown", | |
71 | "X", | |
72 | "dbus", | |
8c4b785a JJ |
73 | }; |
74 | ||
75 | ||
67012e82 JJ |
76 | /* |
77 | * Currently AppArmor auditing is fed straight into the audit framework. | |
78 | * | |
79 | * TODO: | |
80 | * netlink interface for complain mode | |
81 | * user auditing, - send user auditing to netlink interface | |
82 | * system control of whether user audit messages go to system log | |
83 | */ | |
84 | ||
85 | /** | |
64a27ba9 | 86 | * audit_pre() - core AppArmor function. |
67012e82 JJ |
87 | * @ab: audit buffer to fill (NOT NULL) |
88 | * @ca: audit structure containing data to audit (NOT NULL) | |
89 | * | |
90 | * Record common AppArmor audit data from @sa | |
91 | */ | |
92 | static void audit_pre(struct audit_buffer *ab, void *ca) | |
93 | { | |
94 | struct common_audit_data *sa = ca; | |
67012e82 JJ |
95 | |
96 | if (aa_g_audit_header) { | |
f1d9b23c RGB |
97 | audit_log_format(ab, "apparmor=\"%s\"", |
98 | aa_audit_type[aad(sa)->type]); | |
67012e82 JJ |
99 | } |
100 | ||
ef88a7ac | 101 | if (aad(sa)->op) { |
f1d9b23c | 102 | audit_log_format(ab, " operation=\"%s\"", aad(sa)->op); |
67012e82 JJ |
103 | } |
104 | ||
8c4b785a JJ |
105 | if (aad(sa)->class) |
106 | audit_log_format(ab, " class=\"%s\"", | |
107 | aad(sa)->class <= AA_CLASS_LAST ? | |
108 | aa_class_names[aad(sa)->class] : | |
109 | "unknown"); | |
110 | ||
ef88a7ac | 111 | if (aad(sa)->info) { |
f1d9b23c | 112 | audit_log_format(ab, " info=\"%s\"", aad(sa)->info); |
ef88a7ac JJ |
113 | if (aad(sa)->error) |
114 | audit_log_format(ab, " error=%d", aad(sa)->error); | |
67012e82 JJ |
115 | } |
116 | ||
637f688d JJ |
117 | if (aad(sa)->label) { |
118 | struct aa_label *label = aad(sa)->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); | |
67012e82 | 134 | } |
67012e82 JJ |
135 | } |
136 | ||
ef88a7ac | 137 | if (aad(sa)->name) { |
67012e82 | 138 | audit_log_format(ab, " name="); |
ef88a7ac | 139 | audit_log_untrustedstring(ab, aad(sa)->name); |
67012e82 JJ |
140 | } |
141 | } | |
142 | ||
143 | /** | |
144 | * aa_audit_msg - Log a message to the audit subsystem | |
145 | * @sa: audit event structure (NOT NULL) | |
146 | * @cb: optional callback fn for type specific fields (MAYBE NULL) | |
147 | */ | |
148 | void aa_audit_msg(int type, struct common_audit_data *sa, | |
149 | void (*cb) (struct audit_buffer *, void *)) | |
150 | { | |
ef88a7ac | 151 | aad(sa)->type = type; |
b61c37f5 | 152 | common_lsm_audit(sa, audit_pre, cb); |
67012e82 JJ |
153 | } |
154 | ||
155 | /** | |
156 | * aa_audit - Log a profile based audit event to the audit subsystem | |
157 | * @type: audit type for the message | |
158 | * @profile: profile to check against (NOT NULL) | |
67012e82 JJ |
159 | * @sa: audit event (NOT NULL) |
160 | * @cb: optional callback fn for type specific fields (MAYBE NULL) | |
161 | * | |
162 | * Handle default message switching based off of audit mode flags | |
163 | * | |
164 | * Returns: error on failure | |
165 | */ | |
ef88a7ac | 166 | int aa_audit(int type, struct aa_profile *profile, struct common_audit_data *sa, |
67012e82 JJ |
167 | void (*cb) (struct audit_buffer *, void *)) |
168 | { | |
e6bfa25d | 169 | AA_BUG(!profile); |
67012e82 JJ |
170 | |
171 | if (type == AUDIT_APPARMOR_AUTO) { | |
ef88a7ac | 172 | if (likely(!aad(sa)->error)) { |
67012e82 JJ |
173 | if (AUDIT_MODE(profile) != AUDIT_ALL) |
174 | return 0; | |
175 | type = AUDIT_APPARMOR_AUDIT; | |
176 | } else if (COMPLAIN_MODE(profile)) | |
177 | type = AUDIT_APPARMOR_ALLOWED; | |
178 | else | |
179 | type = AUDIT_APPARMOR_DENIED; | |
180 | } | |
181 | if (AUDIT_MODE(profile) == AUDIT_QUIET || | |
182 | (type == AUDIT_APPARMOR_DENIED && | |
68ff8540 | 183 | AUDIT_MODE(profile) == AUDIT_QUIET_DENIED)) |
ef88a7ac | 184 | return aad(sa)->error; |
67012e82 JJ |
185 | |
186 | if (KILL_MODE(profile) && type == AUDIT_APPARMOR_DENIED) | |
187 | type = AUDIT_APPARMOR_KILL; | |
188 | ||
637f688d | 189 | aad(sa)->label = &profile->label; |
67012e82 JJ |
190 | |
191 | aa_audit_msg(type, sa, cb); | |
192 | ||
ef88a7ac | 193 | if (aad(sa)->type == AUDIT_APPARMOR_KILL) |
0972c74e | 194 | (void)send_sig_info(SIGKILL, NULL, |
b6b1b81b JJ |
195 | sa->type == LSM_AUDIT_DATA_TASK && sa->u.tsk ? |
196 | sa->u.tsk : current); | |
67012e82 | 197 | |
ef88a7ac JJ |
198 | if (aad(sa)->type == AUDIT_APPARMOR_ALLOWED) |
199 | return complain_error(aad(sa)->error); | |
67012e82 | 200 | |
ef88a7ac | 201 | return aad(sa)->error; |
67012e82 | 202 | } |
e79c26d0 MG |
203 | |
204 | struct aa_audit_rule { | |
2ab47dae | 205 | struct aa_label *label; |
e79c26d0 MG |
206 | }; |
207 | ||
208 | void aa_audit_rule_free(void *vrule) | |
209 | { | |
210 | struct aa_audit_rule *rule = vrule; | |
211 | ||
212 | if (rule) { | |
2ab47dae JJ |
213 | if (!IS_ERR(rule->label)) |
214 | aa_put_label(rule->label); | |
e79c26d0 MG |
215 | kfree(rule); |
216 | } | |
217 | } | |
218 | ||
219 | int aa_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule) | |
220 | { | |
221 | struct aa_audit_rule *rule; | |
222 | ||
223 | switch (field) { | |
224 | case AUDIT_SUBJ_ROLE: | |
225 | if (op != Audit_equal && op != Audit_not_equal) | |
226 | return -EINVAL; | |
227 | break; | |
228 | default: | |
229 | return -EINVAL; | |
230 | } | |
231 | ||
232 | rule = kzalloc(sizeof(struct aa_audit_rule), GFP_KERNEL); | |
233 | ||
234 | if (!rule) | |
235 | return -ENOMEM; | |
236 | ||
2ab47dae JJ |
237 | /* Currently rules are treated as coming from the root ns */ |
238 | rule->label = aa_label_parse(&root_ns->unconfined->label, rulestr, | |
239 | GFP_KERNEL, true, false); | |
52e8c380 | 240 | if (IS_ERR(rule->label)) { |
c54d481d | 241 | int err = PTR_ERR(rule->label); |
52e8c380 | 242 | aa_audit_rule_free(rule); |
c54d481d | 243 | return err; |
52e8c380 | 244 | } |
e79c26d0 | 245 | |
52e8c380 | 246 | *vrule = rule; |
e79c26d0 MG |
247 | return 0; |
248 | } | |
249 | ||
250 | int aa_audit_rule_known(struct audit_krule *rule) | |
251 | { | |
252 | int i; | |
253 | ||
254 | for (i = 0; i < rule->field_count; i++) { | |
255 | struct audit_field *f = &rule->fields[i]; | |
256 | ||
257 | switch (f->type) { | |
258 | case AUDIT_SUBJ_ROLE: | |
259 | return 1; | |
260 | } | |
261 | } | |
262 | ||
263 | return 0; | |
264 | } | |
265 | ||
90462a5b | 266 | int aa_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule) |
e79c26d0 MG |
267 | { |
268 | struct aa_audit_rule *rule = vrule; | |
269 | struct aa_label *label; | |
e79c26d0 MG |
270 | int found = 0; |
271 | ||
272 | label = aa_secid_to_label(sid); | |
273 | ||
274 | if (!label) | |
275 | return -ENOENT; | |
276 | ||
2ab47dae JJ |
277 | if (aa_label_is_subset(label, rule->label)) |
278 | found = 1; | |
e79c26d0 MG |
279 | |
280 | switch (field) { | |
281 | case AUDIT_SUBJ_ROLE: | |
282 | switch (op) { | |
283 | case Audit_equal: | |
284 | return found; | |
285 | case Audit_not_equal: | |
286 | return !found; | |
287 | } | |
288 | } | |
289 | return 0; | |
290 | } |