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