xref: /openbmc/linux/security/apparmor/lib.c (revision a1bd627b46d169268a0ee5960899fb5be960a317)
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 __counted char *aa_str_alloc(int size, gfp_t gfp)
138 {
139 	struct counted_str *str;
140 
141 	str = kmalloc(sizeof(struct counted_str) + size, gfp);
142 	if (!str)
143 		return NULL;
144 
145 	kref_init(&str->count);
146 	return str->name;
147 }
148 
149 void aa_str_kref(struct kref *kref)
150 {
151 	kfree(container_of(kref, struct counted_str, count));
152 }
153 
154 
155 const char aa_file_perm_chrs[] = "xwracd         km l     ";
156 const char *aa_file_perm_names[] = {
157 	"exec",
158 	"write",
159 	"read",
160 	"append",
161 
162 	"create",
163 	"delete",
164 	"open",
165 	"rename",
166 
167 	"setattr",
168 	"getattr",
169 	"setcred",
170 	"getcred",
171 
172 	"chmod",
173 	"chown",
174 	"chgrp",
175 	"lock",
176 
177 	"mmap",
178 	"mprot",
179 	"link",
180 	"snapshot",
181 
182 	"unknown",
183 	"unknown",
184 	"unknown",
185 	"unknown",
186 
187 	"unknown",
188 	"unknown",
189 	"unknown",
190 	"unknown",
191 
192 	"stack",
193 	"change_onexec",
194 	"change_profile",
195 	"change_hat",
196 };
197 
198 /**
199  * aa_perm_mask_to_str - convert a perm mask to its short string
200  * @str: character buffer to store string in (at least 10 characters)
201  * @mask: permission mask to convert
202  */
203 void aa_perm_mask_to_str(char *str, const char *chrs, u32 mask)
204 {
205 	unsigned int i, perm = 1;
206 
207 	for (i = 0; i < 32; perm <<= 1, i++) {
208 		if (mask & perm)
209 			*str++ = chrs[i];
210 	}
211 	*str = '\0';
212 }
213 
214 void aa_audit_perm_names(struct audit_buffer *ab, const char **names, u32 mask)
215 {
216 	const char *fmt = "%s";
217 	unsigned int i, perm = 1;
218 	bool prev = false;
219 
220 	for (i = 0; i < 32; perm <<= 1, i++) {
221 		if (mask & perm) {
222 			audit_log_format(ab, fmt, names[i]);
223 			if (!prev) {
224 				prev = true;
225 				fmt = " %s";
226 			}
227 		}
228 	}
229 }
230 
231 void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs,
232 			u32 chrsmask, const char **names, u32 namesmask)
233 {
234 	char str[33];
235 
236 	audit_log_format(ab, "\"");
237 	if ((mask & chrsmask) && chrs) {
238 		aa_perm_mask_to_str(str, chrs, mask & chrsmask);
239 		mask &= ~chrsmask;
240 		audit_log_format(ab, "%s", str);
241 		if (mask & namesmask)
242 			audit_log_format(ab, " ");
243 	}
244 	if ((mask & namesmask) && names)
245 		aa_audit_perm_names(ab, names, mask & namesmask);
246 	audit_log_format(ab, "\"");
247 }
248 
249 /**
250  * aa_apply_modes_to_perms - apply namespace and profile flags to perms
251  * @profile: that perms where computed from
252  * @perms: perms to apply mode modifiers to
253  *
254  * TODO: split into profile and ns based flags for when accumulating perms
255  */
256 void aa_apply_modes_to_perms(struct aa_profile *profile, struct aa_perms *perms)
257 {
258 	switch (AUDIT_MODE(profile)) {
259 	case AUDIT_ALL:
260 		perms->audit = ALL_PERMS_MASK;
261 		/* fall through */
262 	case AUDIT_NOQUIET:
263 		perms->quiet = 0;
264 		break;
265 	case AUDIT_QUIET:
266 		perms->audit = 0;
267 		/* fall through */
268 	case AUDIT_QUIET_DENIED:
269 		perms->quiet = ALL_PERMS_MASK;
270 		break;
271 	}
272 
273 	if (KILL_MODE(profile))
274 		perms->kill = ALL_PERMS_MASK;
275 	else if (COMPLAIN_MODE(profile))
276 		perms->complain = ALL_PERMS_MASK;
277 /*
278  *  TODO:
279  *	else if (PROMPT_MODE(profile))
280  *		perms->prompt = ALL_PERMS_MASK;
281  */
282 }
283 
284 static u32 map_other(u32 x)
285 {
286 	return ((x & 0x3) << 8) |	/* SETATTR/GETATTR */
287 		((x & 0x1c) << 18) |	/* ACCEPT/BIND/LISTEN */
288 		((x & 0x60) << 19);	/* SETOPT/GETOPT */
289 }
290 
291 void aa_compute_perms(struct aa_dfa *dfa, unsigned int state,
292 		      struct aa_perms *perms)
293 {
294 	perms->deny = 0;
295 	perms->kill = perms->stop = 0;
296 	perms->complain = perms->cond = 0;
297 	perms->hide = 0;
298 	perms->prompt = 0;
299 	perms->allow = dfa_user_allow(dfa, state);
300 	perms->audit = dfa_user_audit(dfa, state);
301 	perms->quiet = dfa_user_quiet(dfa, state);
302 
303 	/* for v5 perm mapping in the policydb, the other set is used
304 	 * to extend the general perm set
305 	 */
306 	perms->allow |= map_other(dfa_other_allow(dfa, state));
307 	perms->audit |= map_other(dfa_other_audit(dfa, state));
308 	perms->quiet |= map_other(dfa_other_quiet(dfa, state));
309 //	perms->xindex = dfa_user_xindex(dfa, state);
310 }
311 
312 /**
313  * aa_policy_init - initialize a policy structure
314  * @policy: policy to initialize  (NOT NULL)
315  * @prefix: prefix name if any is required.  (MAYBE NULL)
316  * @name: name of the policy, init will make a copy of it  (NOT NULL)
317  * @gfp: allocation mode
318  *
319  * Note: this fn creates a copy of strings passed in
320  *
321  * Returns: true if policy init successful
322  */
323 bool aa_policy_init(struct aa_policy *policy, const char *prefix,
324 		    const char *name, gfp_t gfp)
325 {
326 	char *hname;
327 
328 	/* freed by policy_free */
329 	if (prefix) {
330 		hname = aa_str_alloc(strlen(prefix) + strlen(name) + 3, gfp);
331 		if (hname)
332 			sprintf(hname, "%s//%s", prefix, name);
333 	} else {
334 		hname = aa_str_alloc(strlen(name) + 1, gfp);
335 		if (hname)
336 			strcpy(hname, name);
337 	}
338 	if (!hname)
339 		return false;
340 	policy->hname = hname;
341 	/* base.name is a substring of fqname */
342 	policy->name = basename(policy->hname);
343 	INIT_LIST_HEAD(&policy->list);
344 	INIT_LIST_HEAD(&policy->profiles);
345 
346 	return true;
347 }
348 
349 /**
350  * aa_policy_destroy - free the elements referenced by @policy
351  * @policy: policy that is to have its elements freed  (NOT NULL)
352  */
353 void aa_policy_destroy(struct aa_policy *policy)
354 {
355 	AA_BUG(on_list_rcu(&policy->profiles));
356 	AA_BUG(on_list_rcu(&policy->list));
357 
358 	/* don't free name as its a subset of hname */
359 	aa_put_str(policy->hname);
360 }
361