1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * AppArmor security module
4 *
5 * This file contains basic common functions used in AppArmor
6 *
7 * Copyright (C) 1998-2008 Novell/SUSE
8 * Copyright 2009-2010 Canonical Ltd.
9 */
10
11#include <linux/ctype.h>
12#include <linux/mm.h>
13#include <linux/slab.h>
14#include <linux/string.h>
15#include <linux/vmalloc.h>
16
17#include "include/audit.h"
18#include "include/apparmor.h"
19#include "include/lib.h"
20#include "include/perms.h"
21#include "include/policy.h"
22
23struct aa_perms nullperms;
24struct aa_perms allperms = { .allow = ALL_PERMS_MASK,
25 .quiet = ALL_PERMS_MASK,
26 .hide = ALL_PERMS_MASK };
27
28/**
29 * aa_free_str_table - free entries str table
30 * @t: the string table to free (MAYBE NULL)
31 */
32void aa_free_str_table(struct aa_str_table *t)
33{
34 int i;
35
36 if (t) {
37 if (!t->table)
38 return;
39
40 for (i = 0; i < t->size; i++)
41 kfree_sensitive(objp: t->table[i]);
42 kfree_sensitive(objp: t->table);
43 t->table = NULL;
44 t->size = 0;
45 }
46}
47
48/**
49 * aa_split_fqname - split a fqname into a profile and namespace name
50 * @fqname: a full qualified name in namespace profile format (NOT NULL)
51 * @ns_name: pointer to portion of the string containing the ns name (NOT NULL)
52 *
53 * Returns: profile name or NULL if one is not specified
54 *
55 * Split a namespace name from a profile name (see policy.c for naming
56 * description). If a portion of the name is missing it returns NULL for
57 * that portion.
58 *
59 * NOTE: may modify the @fqname string. The pointers returned point
60 * into the @fqname string.
61 */
62char *aa_split_fqname(char *fqname, char **ns_name)
63{
64 char *name = strim(fqname);
65
66 *ns_name = NULL;
67 if (name[0] == ':') {
68 char *split = strchr(&name[1], ':');
69 *ns_name = skip_spaces(&name[1]);
70 if (split) {
71 /* overwrite ':' with \0 */
72 *split++ = 0;
73 if (strncmp(split, "//", 2) == 0)
74 split += 2;
75 name = skip_spaces(split);
76 } else
77 /* a ns name without a following profile is allowed */
78 name = NULL;
79 }
80 if (name && *name == 0)
81 name = NULL;
82
83 return name;
84}
85
86/**
87 * skipn_spaces - Removes leading whitespace from @str.
88 * @str: The string to be stripped.
89 * @n: length of str to parse, will stop at \0 if encountered before n
90 *
91 * Returns a pointer to the first non-whitespace character in @str.
92 * if all whitespace will return NULL
93 */
94
95const char *skipn_spaces(const char *str, size_t n)
96{
97 for (; n && isspace(*str); --n)
98 ++str;
99 if (n)
100 return (char *)str;
101 return NULL;
102}
103
104const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name,
105 size_t *ns_len)
106{
107 const char *end = fqname + n;
108 const char *name = skipn_spaces(str: fqname, n);
109
110 *ns_name = NULL;
111 *ns_len = 0;
112
113 if (!name)
114 return NULL;
115
116 if (name[0] == ':') {
117 char *split = strnchr(&name[1], end - &name[1], ':');
118 *ns_name = skipn_spaces(str: &name[1], n: end - &name[1]);
119 if (!*ns_name)
120 return NULL;
121 if (split) {
122 *ns_len = split - *ns_name;
123 if (*ns_len == 0)
124 *ns_name = NULL;
125 split++;
126 if (end - split > 1 && strncmp(split, "//", 2) == 0)
127 split += 2;
128 name = skipn_spaces(str: split, n: end - split);
129 } else {
130 /* a ns name without a following profile is allowed */
131 name = NULL;
132 *ns_len = end - *ns_name;
133 }
134 }
135 if (name && *name == 0)
136 name = NULL;
137
138 return name;
139}
140
141/**
142 * aa_info_message - log a none profile related status message
143 * @str: message to log
144 */
145void aa_info_message(const char *str)
146{
147 if (audit_enabled) {
148 DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_NONE, AA_CLASS_NONE, NULL);
149
150 ad.info = str;
151 aa_audit_msg(type: AUDIT_APPARMOR_STATUS, ad: &ad, NULL);
152 }
153 printk(KERN_INFO "AppArmor: %s\n", str);
154}
155
156__counted char *aa_str_alloc(int size, gfp_t gfp)
157{
158 struct counted_str *str;
159
160 str = kmalloc(struct_size(str, name, size), flags: gfp);
161 if (!str)
162 return NULL;
163
164 kref_init(kref: &str->count);
165 return str->name;
166}
167
168void aa_str_kref(struct kref *kref)
169{
170 kfree(container_of(kref, struct counted_str, count));
171}
172
173
174const char aa_file_perm_chrs[] = "xwracd km l ";
175const char *aa_file_perm_names[] = {
176 "exec",
177 "write",
178 "read",
179 "append",
180
181 "create",
182 "delete",
183 "open",
184 "rename",
185
186 "setattr",
187 "getattr",
188 "setcred",
189 "getcred",
190
191 "chmod",
192 "chown",
193 "chgrp",
194 "lock",
195
196 "mmap",
197 "mprot",
198 "link",
199 "snapshot",
200
201 "unknown",
202 "unknown",
203 "unknown",
204 "unknown",
205
206 "unknown",
207 "unknown",
208 "unknown",
209 "unknown",
210
211 "stack",
212 "change_onexec",
213 "change_profile",
214 "change_hat",
215};
216
217/**
218 * aa_perm_mask_to_str - convert a perm mask to its short string
219 * @str: character buffer to store string in (at least 10 characters)
220 * @str_size: size of the @str buffer
221 * @chrs: NUL-terminated character buffer of permission characters
222 * @mask: permission mask to convert
223 */
224void aa_perm_mask_to_str(char *str, size_t str_size, const char *chrs, u32 mask)
225{
226 unsigned int i, perm = 1;
227 size_t num_chrs = strlen(chrs);
228
229 for (i = 0; i < num_chrs; perm <<= 1, i++) {
230 if (mask & perm) {
231 /* Ensure that one byte is left for NUL-termination */
232 if (WARN_ON_ONCE(str_size <= 1))
233 break;
234
235 *str++ = chrs[i];
236 str_size--;
237 }
238 }
239 *str = '\0';
240}
241
242void aa_audit_perm_names(struct audit_buffer *ab, const char * const *names,
243 u32 mask)
244{
245 const char *fmt = "%s";
246 unsigned int i, perm = 1;
247 bool prev = false;
248
249 for (i = 0; i < 32; perm <<= 1, i++) {
250 if (mask & perm) {
251 audit_log_format(ab, fmt, names[i]);
252 if (!prev) {
253 prev = true;
254 fmt = " %s";
255 }
256 }
257 }
258}
259
260void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs,
261 u32 chrsmask, const char * const *names, u32 namesmask)
262{
263 char str[33];
264
265 audit_log_format(ab, fmt: "\"");
266 if ((mask & chrsmask) && chrs) {
267 aa_perm_mask_to_str(str, str_size: sizeof(str), chrs, mask: mask & chrsmask);
268 mask &= ~chrsmask;
269 audit_log_format(ab, fmt: "%s", str);
270 if (mask & namesmask)
271 audit_log_format(ab, fmt: " ");
272 }
273 if ((mask & namesmask) && names)
274 aa_audit_perm_names(ab, names, mask: mask & namesmask);
275 audit_log_format(ab, fmt: "\"");
276}
277
278/**
279 * aa_audit_perms_cb - generic callback fn for auditing perms
280 * @ab: audit buffer (NOT NULL)
281 * @va: audit struct to audit values of (NOT NULL)
282 */
283static void aa_audit_perms_cb(struct audit_buffer *ab, void *va)
284{
285 struct common_audit_data *sa = va;
286 struct apparmor_audit_data *ad = aad(sa);
287
288 if (ad->request) {
289 audit_log_format(ab, fmt: " requested_mask=");
290 aa_audit_perm_mask(ab, mask: ad->request, chrs: aa_file_perm_chrs,
291 PERMS_CHRS_MASK, names: aa_file_perm_names,
292 PERMS_NAMES_MASK);
293 }
294 if (ad->denied) {
295 audit_log_format(ab, fmt: "denied_mask=");
296 aa_audit_perm_mask(ab, mask: ad->denied, chrs: aa_file_perm_chrs,
297 PERMS_CHRS_MASK, names: aa_file_perm_names,
298 PERMS_NAMES_MASK);
299 }
300 audit_log_format(ab, fmt: " peer=");
301 aa_label_xaudit(ab, labels_ns(ad->subj_label), label: ad->peer,
302 FLAGS_NONE, GFP_ATOMIC);
303}
304
305/**
306 * aa_apply_modes_to_perms - apply namespace and profile flags to perms
307 * @profile: that perms where computed from
308 * @perms: perms to apply mode modifiers to
309 *
310 * TODO: split into profile and ns based flags for when accumulating perms
311 */
312void aa_apply_modes_to_perms(struct aa_profile *profile, struct aa_perms *perms)
313{
314 switch (AUDIT_MODE(profile)) {
315 case AUDIT_ALL:
316 perms->audit = ALL_PERMS_MASK;
317 fallthrough;
318 case AUDIT_NOQUIET:
319 perms->quiet = 0;
320 break;
321 case AUDIT_QUIET:
322 perms->audit = 0;
323 fallthrough;
324 case AUDIT_QUIET_DENIED:
325 perms->quiet = ALL_PERMS_MASK;
326 break;
327 }
328
329 if (KILL_MODE(profile))
330 perms->kill = ALL_PERMS_MASK;
331 else if (COMPLAIN_MODE(profile))
332 perms->complain = ALL_PERMS_MASK;
333 else if (USER_MODE(profile))
334 perms->prompt = ALL_PERMS_MASK;
335}
336
337void aa_profile_match_label(struct aa_profile *profile,
338 struct aa_ruleset *rules,
339 struct aa_label *label,
340 int type, u32 request, struct aa_perms *perms)
341{
342 /* TODO: doesn't yet handle extended types */
343 aa_state_t state;
344
345 state = aa_dfa_next(dfa: rules->policy->dfa,
346 state: rules->policy->start[AA_CLASS_LABEL],
347 c: type);
348 aa_label_match(profile, rules, label, state, subns: false, request, perms);
349}
350
351
352/* currently unused */
353int aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target,
354 u32 request, int type, u32 *deny,
355 struct apparmor_audit_data *ad)
356{
357 struct aa_ruleset *rules = list_first_entry(&profile->rules,
358 typeof(*rules), list);
359 struct aa_perms perms;
360
361 ad->peer = &target->label;
362 ad->request = request;
363
364 aa_profile_match_label(profile, rules, label: &target->label, type, request,
365 perms: &perms);
366 aa_apply_modes_to_perms(profile, perms: &perms);
367 *deny |= request & perms.deny;
368 return aa_check_perms(profile, perms: &perms, request, ad, cb: aa_audit_perms_cb);
369}
370
371/**
372 * aa_check_perms - do audit mode selection based on perms set
373 * @profile: profile being checked
374 * @perms: perms computed for the request
375 * @request: requested perms
376 * @ad: initialized audit structure (MAY BE NULL if not auditing)
377 * @cb: callback fn for type specific fields (MAY BE NULL)
378 *
379 * Returns: 0 if permission else error code
380 *
381 * Note: profile audit modes need to be set before calling by setting the
382 * perm masks appropriately.
383 *
384 * If not auditing then complain mode is not enabled and the
385 * error code will indicate whether there was an explicit deny
386 * with a positive value.
387 */
388int aa_check_perms(struct aa_profile *profile, struct aa_perms *perms,
389 u32 request, struct apparmor_audit_data *ad,
390 void (*cb)(struct audit_buffer *, void *))
391{
392 int type, error;
393 u32 denied = request & (~perms->allow | perms->deny);
394
395 if (likely(!denied)) {
396 /* mask off perms that are not being force audited */
397 request &= perms->audit;
398 if (!request || !ad)
399 return 0;
400
401 type = AUDIT_APPARMOR_AUDIT;
402 error = 0;
403 } else {
404 error = -EACCES;
405
406 if (denied & perms->kill)
407 type = AUDIT_APPARMOR_KILL;
408 else if (denied == (denied & perms->complain))
409 type = AUDIT_APPARMOR_ALLOWED;
410 else
411 type = AUDIT_APPARMOR_DENIED;
412
413 if (denied == (denied & perms->hide))
414 error = -ENOENT;
415
416 denied &= ~perms->quiet;
417 if (!ad || !denied)
418 return error;
419 }
420
421 if (ad) {
422 ad->subj_label = &profile->label;
423 ad->request = request;
424 ad->denied = denied;
425 ad->error = error;
426 aa_audit_msg(type, ad, cb);
427 }
428
429 if (type == AUDIT_APPARMOR_ALLOWED)
430 error = 0;
431
432 return error;
433}
434
435
436/**
437 * aa_policy_init - initialize a policy structure
438 * @policy: policy to initialize (NOT NULL)
439 * @prefix: prefix name if any is required. (MAYBE NULL)
440 * @name: name of the policy, init will make a copy of it (NOT NULL)
441 * @gfp: allocation mode
442 *
443 * Note: this fn creates a copy of strings passed in
444 *
445 * Returns: true if policy init successful
446 */
447bool aa_policy_init(struct aa_policy *policy, const char *prefix,
448 const char *name, gfp_t gfp)
449{
450 char *hname;
451
452 /* freed by policy_free */
453 if (prefix) {
454 hname = aa_str_alloc(strlen(prefix) + strlen(name) + 3, gfp);
455 if (hname)
456 sprintf(buf: hname, fmt: "%s//%s", prefix, name);
457 } else {
458 hname = aa_str_alloc(strlen(name) + 1, gfp);
459 if (hname)
460 strcpy(p: hname, q: name);
461 }
462 if (!hname)
463 return false;
464 policy->hname = hname;
465 /* base.name is a substring of fqname */
466 policy->name = basename(hname: policy->hname);
467 INIT_LIST_HEAD(list: &policy->list);
468 INIT_LIST_HEAD(list: &policy->profiles);
469
470 return true;
471}
472
473/**
474 * aa_policy_destroy - free the elements referenced by @policy
475 * @policy: policy that is to have its elements freed (NOT NULL)
476 */
477void aa_policy_destroy(struct aa_policy *policy)
478{
479 AA_BUG(on_list_rcu(&policy->profiles));
480 AA_BUG(on_list_rcu(&policy->list));
481
482 /* don't free name as its a subset of hname */
483 aa_put_str(str: policy->hname);
484}
485

source code of linux/security/apparmor/lib.c