xref: /openbmc/linux/security/apparmor/lib.c (revision 2d679f3cb0eaa6afa0dc97fe6ad3b797e1c1899a)
1cdff2642SJohn Johansen /*
2cdff2642SJohn Johansen  * AppArmor security module
3cdff2642SJohn Johansen  *
4cdff2642SJohn Johansen  * This file contains basic common functions used in AppArmor
5cdff2642SJohn Johansen  *
6cdff2642SJohn Johansen  * Copyright (C) 1998-2008 Novell/SUSE
7cdff2642SJohn Johansen  * Copyright 2009-2010 Canonical Ltd.
8cdff2642SJohn Johansen  *
9cdff2642SJohn Johansen  * This program is free software; you can redistribute it and/or
10cdff2642SJohn Johansen  * modify it under the terms of the GNU General Public License as
11cdff2642SJohn Johansen  * published by the Free Software Foundation, version 2 of the
12cdff2642SJohn Johansen  * License.
13cdff2642SJohn Johansen  */
14cdff2642SJohn Johansen 
153b0aaf58SJohn Johansen #include <linux/ctype.h>
16b7f080cfSAlexey Dobriyan #include <linux/mm.h>
17cdff2642SJohn Johansen #include <linux/slab.h>
18cdff2642SJohn Johansen #include <linux/string.h>
19cdff2642SJohn Johansen #include <linux/vmalloc.h>
20cdff2642SJohn Johansen 
21cdff2642SJohn Johansen #include "include/audit.h"
2232c3df63SJames Morris #include "include/apparmor.h"
2312557dcbSJohn Johansen #include "include/lib.h"
24fc7e0b26SJohn Johansen #include "include/perms.h"
25fe6bb31fSJohn Johansen #include "include/policy.h"
26cdff2642SJohn Johansen 
27*2d679f3cSJohn Johansen struct aa_perms nullperms;
28aa9aeea8SJohn Johansen struct aa_perms allperms = { .allow = ALL_PERMS_MASK,
29aa9aeea8SJohn Johansen 			     .quiet = ALL_PERMS_MASK,
30aa9aeea8SJohn Johansen 			     .hide = ALL_PERMS_MASK };
31aa9aeea8SJohn Johansen 
32cdff2642SJohn Johansen /**
33cdff2642SJohn Johansen  * aa_split_fqname - split a fqname into a profile and namespace name
34cdff2642SJohn Johansen  * @fqname: a full qualified name in namespace profile format (NOT NULL)
35cdff2642SJohn Johansen  * @ns_name: pointer to portion of the string containing the ns name (NOT NULL)
36cdff2642SJohn Johansen  *
37cdff2642SJohn Johansen  * Returns: profile name or NULL if one is not specified
38cdff2642SJohn Johansen  *
39cdff2642SJohn Johansen  * Split a namespace name from a profile name (see policy.c for naming
40cdff2642SJohn Johansen  * description).  If a portion of the name is missing it returns NULL for
41cdff2642SJohn Johansen  * that portion.
42cdff2642SJohn Johansen  *
43cdff2642SJohn Johansen  * NOTE: may modify the @fqname string.  The pointers returned point
44cdff2642SJohn Johansen  *       into the @fqname string.
45cdff2642SJohn Johansen  */
46cdff2642SJohn Johansen char *aa_split_fqname(char *fqname, char **ns_name)
47cdff2642SJohn Johansen {
48cdff2642SJohn Johansen 	char *name = strim(fqname);
49cdff2642SJohn Johansen 
50cdff2642SJohn Johansen 	*ns_name = NULL;
51cdff2642SJohn Johansen 	if (name[0] == ':') {
52cdff2642SJohn Johansen 		char *split = strchr(&name[1], ':');
5304ccd53fSJohn Johansen 		*ns_name = skip_spaces(&name[1]);
54cdff2642SJohn Johansen 		if (split) {
55cdff2642SJohn Johansen 			/* overwrite ':' with \0 */
562654bfbcSJohn Johansen 			*split++ = 0;
572654bfbcSJohn Johansen 			if (strncmp(split, "//", 2) == 0)
582654bfbcSJohn Johansen 				split += 2;
592654bfbcSJohn Johansen 			name = skip_spaces(split);
60cdff2642SJohn Johansen 		} else
61cdff2642SJohn Johansen 			/* a ns name without a following profile is allowed */
62cdff2642SJohn Johansen 			name = NULL;
63cdff2642SJohn Johansen 	}
64cdff2642SJohn Johansen 	if (name && *name == 0)
65cdff2642SJohn Johansen 		name = NULL;
66cdff2642SJohn Johansen 
67cdff2642SJohn Johansen 	return name;
68cdff2642SJohn Johansen }
69cdff2642SJohn Johansen 
70cdff2642SJohn Johansen /**
713b0aaf58SJohn Johansen  * skipn_spaces - Removes leading whitespace from @str.
723b0aaf58SJohn Johansen  * @str: The string to be stripped.
733b0aaf58SJohn Johansen  *
743b0aaf58SJohn Johansen  * Returns a pointer to the first non-whitespace character in @str.
753b0aaf58SJohn Johansen  * if all whitespace will return NULL
763b0aaf58SJohn Johansen  */
773b0aaf58SJohn Johansen 
78b91deb9dSJohn Johansen const char *skipn_spaces(const char *str, size_t n)
793b0aaf58SJohn Johansen {
803b0aaf58SJohn Johansen 	for (; n && isspace(*str); --n)
813b0aaf58SJohn Johansen 		++str;
823b0aaf58SJohn Johansen 	if (n)
833b0aaf58SJohn Johansen 		return (char *)str;
843b0aaf58SJohn Johansen 	return NULL;
853b0aaf58SJohn Johansen }
863b0aaf58SJohn Johansen 
873b0aaf58SJohn Johansen const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name,
883b0aaf58SJohn Johansen 			     size_t *ns_len)
893b0aaf58SJohn Johansen {
903b0aaf58SJohn Johansen 	const char *end = fqname + n;
913b0aaf58SJohn Johansen 	const char *name = skipn_spaces(fqname, n);
923b0aaf58SJohn Johansen 
933b0aaf58SJohn Johansen 	if (!name)
943b0aaf58SJohn Johansen 		return NULL;
953b0aaf58SJohn Johansen 	*ns_name = NULL;
963b0aaf58SJohn Johansen 	*ns_len = 0;
973b0aaf58SJohn Johansen 	if (name[0] == ':') {
983b0aaf58SJohn Johansen 		char *split = strnchr(&name[1], end - &name[1], ':');
993b0aaf58SJohn Johansen 		*ns_name = skipn_spaces(&name[1], end - &name[1]);
1003b0aaf58SJohn Johansen 		if (!*ns_name)
1013b0aaf58SJohn Johansen 			return NULL;
1023b0aaf58SJohn Johansen 		if (split) {
1033b0aaf58SJohn Johansen 			*ns_len = split - *ns_name;
1043b0aaf58SJohn Johansen 			if (*ns_len == 0)
1053b0aaf58SJohn Johansen 				*ns_name = NULL;
1063b0aaf58SJohn Johansen 			split++;
1073b0aaf58SJohn Johansen 			if (end - split > 1 && strncmp(split, "//", 2) == 0)
1083b0aaf58SJohn Johansen 				split += 2;
1093b0aaf58SJohn Johansen 			name = skipn_spaces(split, end - split);
1103b0aaf58SJohn Johansen 		} else {
1113b0aaf58SJohn Johansen 			/* a ns name without a following profile is allowed */
1123b0aaf58SJohn Johansen 			name = NULL;
1133b0aaf58SJohn Johansen 			*ns_len = end - *ns_name;
1143b0aaf58SJohn Johansen 		}
1153b0aaf58SJohn Johansen 	}
1163b0aaf58SJohn Johansen 	if (name && *name == 0)
1173b0aaf58SJohn Johansen 		name = NULL;
1183b0aaf58SJohn Johansen 
1193b0aaf58SJohn Johansen 	return name;
1203b0aaf58SJohn Johansen }
1213b0aaf58SJohn Johansen 
1223b0aaf58SJohn Johansen /**
123cdff2642SJohn Johansen  * aa_info_message - log a none profile related status message
124cdff2642SJohn Johansen  * @str: message to log
125cdff2642SJohn Johansen  */
126cdff2642SJohn Johansen void aa_info_message(const char *str)
127cdff2642SJohn Johansen {
128cdff2642SJohn Johansen 	if (audit_enabled) {
129ef88a7acSJohn Johansen 		DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, NULL);
130ef88a7acSJohn Johansen 
131ef88a7acSJohn Johansen 		aad(&sa)->info = str;
132cdff2642SJohn Johansen 		aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, NULL);
133cdff2642SJohn Johansen 	}
134cdff2642SJohn Johansen 	printk(KERN_INFO "AppArmor: %s\n", str);
135cdff2642SJohn Johansen }
136cdff2642SJohn Johansen 
137e53cfe6cSJohn Johansen const char aa_file_perm_chrs[] = "xwracd         km l     ";
138e53cfe6cSJohn Johansen const char *aa_file_perm_names[] = {
139e53cfe6cSJohn Johansen 	"exec",
140e53cfe6cSJohn Johansen 	"write",
141e53cfe6cSJohn Johansen 	"read",
142e53cfe6cSJohn Johansen 	"append",
143e53cfe6cSJohn Johansen 
144e53cfe6cSJohn Johansen 	"create",
145e53cfe6cSJohn Johansen 	"delete",
146e53cfe6cSJohn Johansen 	"open",
147e53cfe6cSJohn Johansen 	"rename",
148e53cfe6cSJohn Johansen 
149e53cfe6cSJohn Johansen 	"setattr",
150e53cfe6cSJohn Johansen 	"getattr",
151e53cfe6cSJohn Johansen 	"setcred",
152e53cfe6cSJohn Johansen 	"getcred",
153e53cfe6cSJohn Johansen 
154e53cfe6cSJohn Johansen 	"chmod",
155e53cfe6cSJohn Johansen 	"chown",
156e53cfe6cSJohn Johansen 	"chgrp",
157e53cfe6cSJohn Johansen 	"lock",
158e53cfe6cSJohn Johansen 
159e53cfe6cSJohn Johansen 	"mmap",
160e53cfe6cSJohn Johansen 	"mprot",
161e53cfe6cSJohn Johansen 	"link",
162e53cfe6cSJohn Johansen 	"snapshot",
163e53cfe6cSJohn Johansen 
164e53cfe6cSJohn Johansen 	"unknown",
165e53cfe6cSJohn Johansen 	"unknown",
166e53cfe6cSJohn Johansen 	"unknown",
167e53cfe6cSJohn Johansen 	"unknown",
168e53cfe6cSJohn Johansen 
169e53cfe6cSJohn Johansen 	"unknown",
170e53cfe6cSJohn Johansen 	"unknown",
171e53cfe6cSJohn Johansen 	"unknown",
172e53cfe6cSJohn Johansen 	"unknown",
173e53cfe6cSJohn Johansen 
174e53cfe6cSJohn Johansen 	"stack",
175e53cfe6cSJohn Johansen 	"change_onexec",
176e53cfe6cSJohn Johansen 	"change_profile",
177e53cfe6cSJohn Johansen 	"change_hat",
178e53cfe6cSJohn Johansen };
179e53cfe6cSJohn Johansen 
180e53cfe6cSJohn Johansen /**
181e53cfe6cSJohn Johansen  * aa_perm_mask_to_str - convert a perm mask to its short string
182e53cfe6cSJohn Johansen  * @str: character buffer to store string in (at least 10 characters)
183e53cfe6cSJohn Johansen  * @mask: permission mask to convert
184e53cfe6cSJohn Johansen  */
185e53cfe6cSJohn Johansen void aa_perm_mask_to_str(char *str, const char *chrs, u32 mask)
186e53cfe6cSJohn Johansen {
187e53cfe6cSJohn Johansen 	unsigned int i, perm = 1;
188e53cfe6cSJohn Johansen 
189e53cfe6cSJohn Johansen 	for (i = 0; i < 32; perm <<= 1, i++) {
190e53cfe6cSJohn Johansen 		if (mask & perm)
191e53cfe6cSJohn Johansen 			*str++ = chrs[i];
192e53cfe6cSJohn Johansen 	}
193e53cfe6cSJohn Johansen 	*str = '\0';
194e53cfe6cSJohn Johansen }
195e53cfe6cSJohn Johansen 
196aa9aeea8SJohn Johansen void aa_audit_perm_names(struct audit_buffer *ab, const char **names, u32 mask)
197aa9aeea8SJohn Johansen {
198aa9aeea8SJohn Johansen 	const char *fmt = "%s";
199aa9aeea8SJohn Johansen 	unsigned int i, perm = 1;
200aa9aeea8SJohn Johansen 	bool prev = false;
201aa9aeea8SJohn Johansen 
202aa9aeea8SJohn Johansen 	for (i = 0; i < 32; perm <<= 1, i++) {
203aa9aeea8SJohn Johansen 		if (mask & perm) {
204aa9aeea8SJohn Johansen 			audit_log_format(ab, fmt, names[i]);
205aa9aeea8SJohn Johansen 			if (!prev) {
206aa9aeea8SJohn Johansen 				prev = true;
207aa9aeea8SJohn Johansen 				fmt = " %s";
208aa9aeea8SJohn Johansen 			}
209aa9aeea8SJohn Johansen 		}
210aa9aeea8SJohn Johansen 	}
211aa9aeea8SJohn Johansen }
212aa9aeea8SJohn Johansen 
213aa9aeea8SJohn Johansen void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs,
214aa9aeea8SJohn Johansen 			u32 chrsmask, const char **names, u32 namesmask)
215aa9aeea8SJohn Johansen {
216aa9aeea8SJohn Johansen 	char str[33];
217aa9aeea8SJohn Johansen 
218aa9aeea8SJohn Johansen 	audit_log_format(ab, "\"");
219aa9aeea8SJohn Johansen 	if ((mask & chrsmask) && chrs) {
220aa9aeea8SJohn Johansen 		aa_perm_mask_to_str(str, chrs, mask & chrsmask);
221aa9aeea8SJohn Johansen 		mask &= ~chrsmask;
222aa9aeea8SJohn Johansen 		audit_log_format(ab, "%s", str);
223aa9aeea8SJohn Johansen 		if (mask & namesmask)
224aa9aeea8SJohn Johansen 			audit_log_format(ab, " ");
225aa9aeea8SJohn Johansen 	}
226aa9aeea8SJohn Johansen 	if ((mask & namesmask) && names)
227aa9aeea8SJohn Johansen 		aa_audit_perm_names(ab, names, mask & namesmask);
228aa9aeea8SJohn Johansen 	audit_log_format(ab, "\"");
229aa9aeea8SJohn Johansen }
230aa9aeea8SJohn Johansen 
231aa9aeea8SJohn Johansen /**
232aa9aeea8SJohn Johansen  * aa_apply_modes_to_perms - apply namespace and profile flags to perms
233aa9aeea8SJohn Johansen  * @profile: that perms where computed from
234aa9aeea8SJohn Johansen  * @perms: perms to apply mode modifiers to
235aa9aeea8SJohn Johansen  *
236aa9aeea8SJohn Johansen  * TODO: split into profile and ns based flags for when accumulating perms
237aa9aeea8SJohn Johansen  */
238aa9aeea8SJohn Johansen void aa_apply_modes_to_perms(struct aa_profile *profile, struct aa_perms *perms)
239aa9aeea8SJohn Johansen {
240aa9aeea8SJohn Johansen 	switch (AUDIT_MODE(profile)) {
241aa9aeea8SJohn Johansen 	case AUDIT_ALL:
242aa9aeea8SJohn Johansen 		perms->audit = ALL_PERMS_MASK;
243aa9aeea8SJohn Johansen 		/* fall through */
244aa9aeea8SJohn Johansen 	case AUDIT_NOQUIET:
245aa9aeea8SJohn Johansen 		perms->quiet = 0;
246aa9aeea8SJohn Johansen 		break;
247aa9aeea8SJohn Johansen 	case AUDIT_QUIET:
248aa9aeea8SJohn Johansen 		perms->audit = 0;
249aa9aeea8SJohn Johansen 		/* fall through */
250aa9aeea8SJohn Johansen 	case AUDIT_QUIET_DENIED:
251aa9aeea8SJohn Johansen 		perms->quiet = ALL_PERMS_MASK;
252aa9aeea8SJohn Johansen 		break;
253aa9aeea8SJohn Johansen 	}
254aa9aeea8SJohn Johansen 
255aa9aeea8SJohn Johansen 	if (KILL_MODE(profile))
256aa9aeea8SJohn Johansen 		perms->kill = ALL_PERMS_MASK;
257aa9aeea8SJohn Johansen 	else if (COMPLAIN_MODE(profile))
258aa9aeea8SJohn Johansen 		perms->complain = ALL_PERMS_MASK;
259aa9aeea8SJohn Johansen /*
260aa9aeea8SJohn Johansen  *  TODO:
261aa9aeea8SJohn Johansen  *	else if (PROMPT_MODE(profile))
262aa9aeea8SJohn Johansen  *		perms->prompt = ALL_PERMS_MASK;
263aa9aeea8SJohn Johansen  */
264aa9aeea8SJohn Johansen }
265aa9aeea8SJohn Johansen 
266aa9aeea8SJohn Johansen static u32 map_other(u32 x)
267aa9aeea8SJohn Johansen {
268aa9aeea8SJohn Johansen 	return ((x & 0x3) << 8) |	/* SETATTR/GETATTR */
269aa9aeea8SJohn Johansen 		((x & 0x1c) << 18) |	/* ACCEPT/BIND/LISTEN */
270aa9aeea8SJohn Johansen 		((x & 0x60) << 19);	/* SETOPT/GETOPT */
271aa9aeea8SJohn Johansen }
272aa9aeea8SJohn Johansen 
273aa9aeea8SJohn Johansen void aa_compute_perms(struct aa_dfa *dfa, unsigned int state,
274aa9aeea8SJohn Johansen 		      struct aa_perms *perms)
275aa9aeea8SJohn Johansen {
276aa9aeea8SJohn Johansen 	perms->deny = 0;
277aa9aeea8SJohn Johansen 	perms->kill = perms->stop = 0;
278aa9aeea8SJohn Johansen 	perms->complain = perms->cond = 0;
279aa9aeea8SJohn Johansen 	perms->hide = 0;
280aa9aeea8SJohn Johansen 	perms->prompt = 0;
281aa9aeea8SJohn Johansen 	perms->allow = dfa_user_allow(dfa, state);
282aa9aeea8SJohn Johansen 	perms->audit = dfa_user_audit(dfa, state);
283aa9aeea8SJohn Johansen 	perms->quiet = dfa_user_quiet(dfa, state);
284aa9aeea8SJohn Johansen 
285aa9aeea8SJohn Johansen 	/* for v5 perm mapping in the policydb, the other set is used
286aa9aeea8SJohn Johansen 	 * to extend the general perm set
287aa9aeea8SJohn Johansen 	 */
288aa9aeea8SJohn Johansen 	perms->allow |= map_other(dfa_other_allow(dfa, state));
289aa9aeea8SJohn Johansen 	perms->audit |= map_other(dfa_other_audit(dfa, state));
290aa9aeea8SJohn Johansen 	perms->quiet |= map_other(dfa_other_quiet(dfa, state));
291aa9aeea8SJohn Johansen //	perms->xindex = dfa_user_xindex(dfa, state);
292aa9aeea8SJohn Johansen }
293aa9aeea8SJohn Johansen 
294cdff2642SJohn Johansen /**
295fe6bb31fSJohn Johansen  * aa_policy_init - initialize a policy structure
296fe6bb31fSJohn Johansen  * @policy: policy to initialize  (NOT NULL)
297fe6bb31fSJohn Johansen  * @prefix: prefix name if any is required.  (MAYBE NULL)
298fe6bb31fSJohn Johansen  * @name: name of the policy, init will make a copy of it  (NOT NULL)
299fe6bb31fSJohn Johansen  *
300fe6bb31fSJohn Johansen  * Note: this fn creates a copy of strings passed in
301fe6bb31fSJohn Johansen  *
302fe6bb31fSJohn Johansen  * Returns: true if policy init successful
303fe6bb31fSJohn Johansen  */
304fe6bb31fSJohn Johansen bool aa_policy_init(struct aa_policy *policy, const char *prefix,
305d102d895SJohn Johansen 		    const char *name, gfp_t gfp)
306fe6bb31fSJohn Johansen {
307fe6bb31fSJohn Johansen 	/* freed by policy_free */
308fe6bb31fSJohn Johansen 	if (prefix) {
309fe6bb31fSJohn Johansen 		policy->hname = kmalloc(strlen(prefix) + strlen(name) + 3,
310d102d895SJohn Johansen 					gfp);
311fe6bb31fSJohn Johansen 		if (policy->hname)
312bbe4a7c8SJohn Johansen 			sprintf((char *)policy->hname, "%s//%s", prefix, name);
313fe6bb31fSJohn Johansen 	} else
314d102d895SJohn Johansen 		policy->hname = kstrdup(name, gfp);
315fe6bb31fSJohn Johansen 	if (!policy->hname)
316b9c42ac7Skbuild test robot 		return false;
317fe6bb31fSJohn Johansen 	/* base.name is a substring of fqname */
318d102d895SJohn Johansen 	policy->name = basename(policy->hname);
319fe6bb31fSJohn Johansen 	INIT_LIST_HEAD(&policy->list);
320fe6bb31fSJohn Johansen 	INIT_LIST_HEAD(&policy->profiles);
321fe6bb31fSJohn Johansen 
322b9c42ac7Skbuild test robot 	return true;
323fe6bb31fSJohn Johansen }
324fe6bb31fSJohn Johansen 
325fe6bb31fSJohn Johansen /**
326fe6bb31fSJohn Johansen  * aa_policy_destroy - free the elements referenced by @policy
327fe6bb31fSJohn Johansen  * @policy: policy that is to have its elements freed  (NOT NULL)
328fe6bb31fSJohn Johansen  */
329fe6bb31fSJohn Johansen void aa_policy_destroy(struct aa_policy *policy)
330fe6bb31fSJohn Johansen {
3315fd1b95fSJohn Johansen 	AA_BUG(on_list_rcu(&policy->profiles));
3325fd1b95fSJohn Johansen 	AA_BUG(on_list_rcu(&policy->list));
333fe6bb31fSJohn Johansen 
334fe6bb31fSJohn Johansen 	/* don't free name as its a subset of hname */
335fe6bb31fSJohn Johansen 	kzfree(policy->hname);
336fe6bb31fSJohn Johansen }
337