xref: /openbmc/linux/security/apparmor/lib.c (revision 55fd7e02)
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(sizeof(struct counted_str) + 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 		/* fall through */
296 	case AUDIT_NOQUIET:
297 		perms->quiet = 0;
298 		break;
299 	case AUDIT_QUIET:
300 		perms->audit = 0;
301 		/* fall through */
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 static u32 map_other(u32 x)
319 {
320 	return ((x & 0x3) << 8) |	/* SETATTR/GETATTR */
321 		((x & 0x1c) << 18) |	/* ACCEPT/BIND/LISTEN */
322 		((x & 0x60) << 19);	/* SETOPT/GETOPT */
323 }
324 
325 void aa_compute_perms(struct aa_dfa *dfa, unsigned int state,
326 		      struct aa_perms *perms)
327 {
328 	*perms = (struct aa_perms) {
329 		.allow = dfa_user_allow(dfa, state),
330 		.audit = dfa_user_audit(dfa, state),
331 		.quiet = dfa_user_quiet(dfa, state),
332 	};
333 
334 	/* for v5 perm mapping in the policydb, the other set is used
335 	 * to extend the general perm set
336 	 */
337 	perms->allow |= map_other(dfa_other_allow(dfa, state));
338 	perms->audit |= map_other(dfa_other_audit(dfa, state));
339 	perms->quiet |= map_other(dfa_other_quiet(dfa, state));
340 //	perms->xindex = dfa_user_xindex(dfa, state);
341 }
342 
343 /**
344  * aa_perms_accum_raw - accumulate perms with out masking off overlapping perms
345  * @accum - perms struct to accumulate into
346  * @addend - perms struct to add to @accum
347  */
348 void aa_perms_accum_raw(struct aa_perms *accum, struct aa_perms *addend)
349 {
350 	accum->deny |= addend->deny;
351 	accum->allow &= addend->allow & ~addend->deny;
352 	accum->audit |= addend->audit & addend->allow;
353 	accum->quiet &= addend->quiet & ~addend->allow;
354 	accum->kill |= addend->kill & ~addend->allow;
355 	accum->stop |= addend->stop & ~addend->allow;
356 	accum->complain |= addend->complain & ~addend->allow & ~addend->deny;
357 	accum->cond |= addend->cond & ~addend->allow & ~addend->deny;
358 	accum->hide &= addend->hide & ~addend->allow;
359 	accum->prompt |= addend->prompt & ~addend->allow & ~addend->deny;
360 }
361 
362 /**
363  * aa_perms_accum - accumulate perms, masking off overlapping perms
364  * @accum - perms struct to accumulate into
365  * @addend - perms struct to add to @accum
366  */
367 void aa_perms_accum(struct aa_perms *accum, struct aa_perms *addend)
368 {
369 	accum->deny |= addend->deny;
370 	accum->allow &= addend->allow & ~accum->deny;
371 	accum->audit |= addend->audit & accum->allow;
372 	accum->quiet &= addend->quiet & ~accum->allow;
373 	accum->kill |= addend->kill & ~accum->allow;
374 	accum->stop |= addend->stop & ~accum->allow;
375 	accum->complain |= addend->complain & ~accum->allow & ~accum->deny;
376 	accum->cond |= addend->cond & ~accum->allow & ~accum->deny;
377 	accum->hide &= addend->hide & ~accum->allow;
378 	accum->prompt |= addend->prompt & ~accum->allow & ~accum->deny;
379 }
380 
381 void aa_profile_match_label(struct aa_profile *profile, struct aa_label *label,
382 			    int type, u32 request, struct aa_perms *perms)
383 {
384 	/* TODO: doesn't yet handle extended types */
385 	unsigned int state;
386 
387 	state = aa_dfa_next(profile->policy.dfa,
388 			    profile->policy.start[AA_CLASS_LABEL],
389 			    type);
390 	aa_label_match(profile, label, state, false, request, perms);
391 }
392 
393 
394 /* currently unused */
395 int aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target,
396 			  u32 request, int type, u32 *deny,
397 			  struct common_audit_data *sa)
398 {
399 	struct aa_perms perms;
400 
401 	aad(sa)->label = &profile->label;
402 	aad(sa)->peer = &target->label;
403 	aad(sa)->request = request;
404 
405 	aa_profile_match_label(profile, &target->label, type, request, &perms);
406 	aa_apply_modes_to_perms(profile, &perms);
407 	*deny |= request & perms.deny;
408 	return aa_check_perms(profile, &perms, request, sa, aa_audit_perms_cb);
409 }
410 
411 /**
412  * aa_check_perms - do audit mode selection based on perms set
413  * @profile: profile being checked
414  * @perms: perms computed for the request
415  * @request: requested perms
416  * @deny: Returns: explicit deny set
417  * @sa: initialized audit structure (MAY BE NULL if not auditing)
418  * @cb: callback fn for type specific fields (MAY BE NULL)
419  *
420  * Returns: 0 if permission else error code
421  *
422  * Note: profile audit modes need to be set before calling by setting the
423  *       perm masks appropriately.
424  *
425  *       If not auditing then complain mode is not enabled and the
426  *       error code will indicate whether there was an explicit deny
427  *	 with a positive value.
428  */
429 int aa_check_perms(struct aa_profile *profile, struct aa_perms *perms,
430 		   u32 request, struct common_audit_data *sa,
431 		   void (*cb)(struct audit_buffer *, void *))
432 {
433 	int type, error;
434 	u32 denied = request & (~perms->allow | perms->deny);
435 
436 	if (likely(!denied)) {
437 		/* mask off perms that are not being force audited */
438 		request &= perms->audit;
439 		if (!request || !sa)
440 			return 0;
441 
442 		type = AUDIT_APPARMOR_AUDIT;
443 		error = 0;
444 	} else {
445 		error = -EACCES;
446 
447 		if (denied & perms->kill)
448 			type = AUDIT_APPARMOR_KILL;
449 		else if (denied == (denied & perms->complain))
450 			type = AUDIT_APPARMOR_ALLOWED;
451 		else
452 			type = AUDIT_APPARMOR_DENIED;
453 
454 		if (denied == (denied & perms->hide))
455 			error = -ENOENT;
456 
457 		denied &= ~perms->quiet;
458 		if (!sa || !denied)
459 			return error;
460 	}
461 
462 	if (sa) {
463 		aad(sa)->label = &profile->label;
464 		aad(sa)->request = request;
465 		aad(sa)->denied = denied;
466 		aad(sa)->error = error;
467 		aa_audit_msg(type, sa, cb);
468 	}
469 
470 	if (type == AUDIT_APPARMOR_ALLOWED)
471 		error = 0;
472 
473 	return error;
474 }
475 
476 
477 /**
478  * aa_policy_init - initialize a policy structure
479  * @policy: policy to initialize  (NOT NULL)
480  * @prefix: prefix name if any is required.  (MAYBE NULL)
481  * @name: name of the policy, init will make a copy of it  (NOT NULL)
482  * @gfp: allocation mode
483  *
484  * Note: this fn creates a copy of strings passed in
485  *
486  * Returns: true if policy init successful
487  */
488 bool aa_policy_init(struct aa_policy *policy, const char *prefix,
489 		    const char *name, gfp_t gfp)
490 {
491 	char *hname;
492 
493 	/* freed by policy_free */
494 	if (prefix) {
495 		hname = aa_str_alloc(strlen(prefix) + strlen(name) + 3, gfp);
496 		if (hname)
497 			sprintf(hname, "%s//%s", prefix, name);
498 	} else {
499 		hname = aa_str_alloc(strlen(name) + 1, gfp);
500 		if (hname)
501 			strcpy(hname, name);
502 	}
503 	if (!hname)
504 		return false;
505 	policy->hname = hname;
506 	/* base.name is a substring of fqname */
507 	policy->name = basename(policy->hname);
508 	INIT_LIST_HEAD(&policy->list);
509 	INIT_LIST_HEAD(&policy->profiles);
510 
511 	return true;
512 }
513 
514 /**
515  * aa_policy_destroy - free the elements referenced by @policy
516  * @policy: policy that is to have its elements freed  (NOT NULL)
517  */
518 void aa_policy_destroy(struct aa_policy *policy)
519 {
520 	AA_BUG(on_list_rcu(&policy->profiles));
521 	AA_BUG(on_list_rcu(&policy->list));
522 
523 	/* don't free name as its a subset of hname */
524 	aa_put_str(policy->hname);
525 }
526