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