File Index
Files and directories
Sign in
FILE

lib.c

File preview with syntax highlighting.

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