xref: /linux/security/apparmor/lib.c (revision 7f71507851fc7764b36a3221839607d3a45c2025)
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_free_str_table - free entries str table
30  * @t: the string table to free  (MAYBE NULL)
31  */
32 void aa_free_str_table(struct aa_str_table *t)
33 {
34 	int i;
35 
36 	if (t) {
37 		if (!t->table)
38 			return;
39 
40 		for (i = 0; i < t->size; i++)
41 			kfree_sensitive(t->table[i]);
42 		kfree_sensitive(t->table);
43 		t->table = NULL;
44 		t->size = 0;
45 	}
46 }
47 
48 /**
49  * skipn_spaces - Removes leading whitespace from @str.
50  * @str: The string to be stripped.
51  * @n: length of str to parse, will stop at \0 if encountered before n
52  *
53  * Returns a pointer to the first non-whitespace character in @str.
54  * if all whitespace will return NULL
55  */
56 
57 const char *skipn_spaces(const char *str, size_t n)
58 {
59 	for (; n && isspace(*str); --n)
60 		++str;
61 	if (n)
62 		return (char *)str;
63 	return NULL;
64 }
65 
66 const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name,
67 			     size_t *ns_len)
68 {
69 	const char *end = fqname + n;
70 	const char *name = skipn_spaces(fqname, n);
71 
72 	*ns_name = NULL;
73 	*ns_len = 0;
74 
75 	if (!name)
76 		return NULL;
77 
78 	if (name[0] == ':') {
79 		char *split = strnchr(&name[1], end - &name[1], ':');
80 		*ns_name = skipn_spaces(&name[1], end - &name[1]);
81 		if (!*ns_name)
82 			return NULL;
83 		if (split) {
84 			*ns_len = split - *ns_name;
85 			if (*ns_len == 0)
86 				*ns_name = NULL;
87 			split++;
88 			if (end - split > 1 && strncmp(split, "//", 2) == 0)
89 				split += 2;
90 			name = skipn_spaces(split, end - split);
91 		} else {
92 			/* a ns name without a following profile is allowed */
93 			name = NULL;
94 			*ns_len = end - *ns_name;
95 		}
96 	}
97 	if (name && *name == 0)
98 		name = NULL;
99 
100 	return name;
101 }
102 
103 /**
104  * aa_info_message - log a none profile related status message
105  * @str: message to log
106  */
107 void aa_info_message(const char *str)
108 {
109 	if (audit_enabled) {
110 		DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_NONE, AA_CLASS_NONE, NULL);
111 
112 		ad.info = str;
113 		aa_audit_msg(AUDIT_APPARMOR_STATUS, &ad, NULL);
114 	}
115 	printk(KERN_INFO "AppArmor: %s\n", str);
116 }
117 
118 __counted char *aa_str_alloc(int size, gfp_t gfp)
119 {
120 	struct counted_str *str;
121 
122 	str = kmalloc(struct_size(str, name, size), gfp);
123 	if (!str)
124 		return NULL;
125 
126 	kref_init(&str->count);
127 	return str->name;
128 }
129 
130 void aa_str_kref(struct kref *kref)
131 {
132 	kfree(container_of(kref, struct counted_str, count));
133 }
134 
135 
136 const char aa_file_perm_chrs[] = "xwracd         km l     ";
137 const char *aa_file_perm_names[] = {
138 	"exec",
139 	"write",
140 	"read",
141 	"append",
142 
143 	"create",
144 	"delete",
145 	"open",
146 	"rename",
147 
148 	"setattr",
149 	"getattr",
150 	"setcred",
151 	"getcred",
152 
153 	"chmod",
154 	"chown",
155 	"chgrp",
156 	"lock",
157 
158 	"mmap",
159 	"mprot",
160 	"link",
161 	"snapshot",
162 
163 	"unknown",
164 	"unknown",
165 	"unknown",
166 	"unknown",
167 
168 	"unknown",
169 	"unknown",
170 	"unknown",
171 	"unknown",
172 
173 	"stack",
174 	"change_onexec",
175 	"change_profile",
176 	"change_hat",
177 };
178 
179 /**
180  * aa_perm_mask_to_str - convert a perm mask to its short string
181  * @str: character buffer to store string in (at least 10 characters)
182  * @str_size: size of the @str buffer
183  * @chrs: NUL-terminated character buffer of permission characters
184  * @mask: permission mask to convert
185  */
186 void aa_perm_mask_to_str(char *str, size_t str_size, const char *chrs, u32 mask)
187 {
188 	unsigned int i, perm = 1;
189 	size_t num_chrs = strlen(chrs);
190 
191 	for (i = 0; i < num_chrs; perm <<= 1, i++) {
192 		if (mask & perm) {
193 			/* Ensure that one byte is left for NUL-termination */
194 			if (WARN_ON_ONCE(str_size <= 1))
195 				break;
196 
197 			*str++ = chrs[i];
198 			str_size--;
199 		}
200 	}
201 	*str = '\0';
202 }
203 
204 void aa_audit_perm_names(struct audit_buffer *ab, const char * const *names,
205 			 u32 mask)
206 {
207 	const char *fmt = "%s";
208 	unsigned int i, perm = 1;
209 	bool prev = false;
210 
211 	for (i = 0; i < 32; perm <<= 1, i++) {
212 		if (mask & perm) {
213 			audit_log_format(ab, fmt, names[i]);
214 			if (!prev) {
215 				prev = true;
216 				fmt = " %s";
217 			}
218 		}
219 	}
220 }
221 
222 void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs,
223 			u32 chrsmask, const char * const *names, u32 namesmask)
224 {
225 	char str[33];
226 
227 	audit_log_format(ab, "\"");
228 	if ((mask & chrsmask) && chrs) {
229 		aa_perm_mask_to_str(str, sizeof(str), chrs, mask & chrsmask);
230 		mask &= ~chrsmask;
231 		audit_log_format(ab, "%s", str);
232 		if (mask & namesmask)
233 			audit_log_format(ab, " ");
234 	}
235 	if ((mask & namesmask) && names)
236 		aa_audit_perm_names(ab, names, mask & namesmask);
237 	audit_log_format(ab, "\"");
238 }
239 
240 /**
241  * aa_apply_modes_to_perms - apply namespace and profile flags to perms
242  * @profile: that perms where computed from
243  * @perms: perms to apply mode modifiers to
244  *
245  * TODO: split into profile and ns based flags for when accumulating perms
246  */
247 void aa_apply_modes_to_perms(struct aa_profile *profile, struct aa_perms *perms)
248 {
249 	switch (AUDIT_MODE(profile)) {
250 	case AUDIT_ALL:
251 		perms->audit = ALL_PERMS_MASK;
252 		fallthrough;
253 	case AUDIT_NOQUIET:
254 		perms->quiet = 0;
255 		break;
256 	case AUDIT_QUIET:
257 		perms->audit = 0;
258 		fallthrough;
259 	case AUDIT_QUIET_DENIED:
260 		perms->quiet = ALL_PERMS_MASK;
261 		break;
262 	}
263 
264 	if (KILL_MODE(profile))
265 		perms->kill = ALL_PERMS_MASK;
266 	else if (COMPLAIN_MODE(profile))
267 		perms->complain = ALL_PERMS_MASK;
268 	else if (USER_MODE(profile))
269 		perms->prompt = ALL_PERMS_MASK;
270 }
271 
272 void aa_profile_match_label(struct aa_profile *profile,
273 			    struct aa_ruleset *rules,
274 			    struct aa_label *label,
275 			    int type, u32 request, struct aa_perms *perms)
276 {
277 	/* TODO: doesn't yet handle extended types */
278 	aa_state_t state;
279 
280 	state = aa_dfa_next(rules->policy->dfa,
281 			    rules->policy->start[AA_CLASS_LABEL],
282 			    type);
283 	aa_label_match(profile, rules, label, state, false, request, perms);
284 }
285 
286 
287 /**
288  * aa_check_perms - do audit mode selection based on perms set
289  * @profile: profile being checked
290  * @perms: perms computed for the request
291  * @request: requested perms
292  * @ad: initialized audit structure (MAY BE NULL if not auditing)
293  * @cb: callback fn for type specific fields (MAY BE NULL)
294  *
295  * Returns: 0 if permission else error code
296  *
297  * Note: profile audit modes need to be set before calling by setting the
298  *       perm masks appropriately.
299  *
300  *       If not auditing then complain mode is not enabled and the
301  *       error code will indicate whether there was an explicit deny
302  *	 with a positive value.
303  */
304 int aa_check_perms(struct aa_profile *profile, struct aa_perms *perms,
305 		   u32 request, struct apparmor_audit_data *ad,
306 		   void (*cb)(struct audit_buffer *, void *))
307 {
308 	int type, error;
309 	u32 denied = request & (~perms->allow | perms->deny);
310 
311 	if (likely(!denied)) {
312 		/* mask off perms that are not being force audited */
313 		request &= perms->audit;
314 		if (!request || !ad)
315 			return 0;
316 
317 		type = AUDIT_APPARMOR_AUDIT;
318 		error = 0;
319 	} else {
320 		error = -EACCES;
321 
322 		if (denied & perms->kill)
323 			type = AUDIT_APPARMOR_KILL;
324 		else if (denied == (denied & perms->complain))
325 			type = AUDIT_APPARMOR_ALLOWED;
326 		else
327 			type = AUDIT_APPARMOR_DENIED;
328 
329 		if (denied == (denied & perms->hide))
330 			error = -ENOENT;
331 
332 		denied &= ~perms->quiet;
333 		if (!ad || !denied)
334 			return error;
335 	}
336 
337 	if (ad) {
338 		ad->subj_label = &profile->label;
339 		ad->request = request;
340 		ad->denied = denied;
341 		ad->error = error;
342 		aa_audit_msg(type, ad, cb);
343 	}
344 
345 	if (type == AUDIT_APPARMOR_ALLOWED)
346 		error = 0;
347 
348 	return error;
349 }
350 
351 
352 /**
353  * aa_policy_init - initialize a policy structure
354  * @policy: policy to initialize  (NOT NULL)
355  * @prefix: prefix name if any is required.  (MAYBE NULL)
356  * @name: name of the policy, init will make a copy of it  (NOT NULL)
357  * @gfp: allocation mode
358  *
359  * Note: this fn creates a copy of strings passed in
360  *
361  * Returns: true if policy init successful
362  */
363 bool aa_policy_init(struct aa_policy *policy, const char *prefix,
364 		    const char *name, gfp_t gfp)
365 {
366 	char *hname;
367 
368 	/* freed by policy_free */
369 	if (prefix) {
370 		hname = aa_str_alloc(strlen(prefix) + strlen(name) + 3, gfp);
371 		if (hname)
372 			sprintf(hname, "%s//%s", prefix, name);
373 	} else {
374 		hname = aa_str_alloc(strlen(name) + 1, gfp);
375 		if (hname)
376 			strcpy(hname, name);
377 	}
378 	if (!hname)
379 		return false;
380 	policy->hname = hname;
381 	/* base.name is a substring of fqname */
382 	policy->name = basename(policy->hname);
383 	INIT_LIST_HEAD(&policy->list);
384 	INIT_LIST_HEAD(&policy->profiles);
385 
386 	return true;
387 }
388 
389 /**
390  * aa_policy_destroy - free the elements referenced by @policy
391  * @policy: policy that is to have its elements freed  (NOT NULL)
392  */
393 void aa_policy_destroy(struct aa_policy *policy)
394 {
395 	AA_BUG(on_list_rcu(&policy->profiles));
396 	AA_BUG(on_list_rcu(&policy->list));
397 
398 	/* don't free name as its a subset of hname */
399 	aa_put_str(policy->hname);
400 }
401