cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

lib.c (13211B)


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