summaryrefslogtreecommitdiff
path: root/security/apparmor/lib.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/apparmor/lib.c')
-rw-r--r--security/apparmor/lib.c323
1 files changed, 154 insertions, 169 deletions
diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c
index 76491e7f4177..82dbb97ad406 100644
--- a/security/apparmor/lib.c
+++ b/security/apparmor/lib.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AppArmor security module
*
@@ -5,11 +6,6 @@
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2010 Canonical Ltd.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2 of the
- * License.
*/
#include <linux/ctype.h>
@@ -29,47 +25,144 @@ struct aa_perms allperms = { .allow = ALL_PERMS_MASK,
.quiet = ALL_PERMS_MASK,
.hide = ALL_PERMS_MASK };
+struct val_table_ent {
+ const char *str;
+ int value;
+};
+
+static struct val_table_ent debug_values_table[] = {
+ { "N", DEBUG_NONE },
+ { "none", DEBUG_NONE },
+ { "n", DEBUG_NONE },
+ { "0", DEBUG_NONE },
+ { "all", DEBUG_ALL },
+ { "Y", DEBUG_ALL },
+ { "y", DEBUG_ALL },
+ { "1", DEBUG_ALL },
+ { "abs_root", DEBUG_LABEL_ABS_ROOT },
+ { "label", DEBUG_LABEL },
+ { "domain", DEBUG_DOMAIN },
+ { "policy", DEBUG_POLICY },
+ { "interface", DEBUG_INTERFACE },
+ { NULL, 0 }
+};
+
+static struct val_table_ent *val_table_find_ent(struct val_table_ent *table,
+ const char *name, size_t len)
+{
+ struct val_table_ent *entry;
+
+ for (entry = table; entry->str != NULL; entry++) {
+ if (strncmp(entry->str, name, len) == 0 &&
+ strlen(entry->str) == len)
+ return entry;
+ }
+ return NULL;
+}
+
+int aa_parse_debug_params(const char *str)
+{
+ struct val_table_ent *ent;
+ const char *next;
+ int val = 0;
+
+ do {
+ size_t n = strcspn(str, "\r\n,");
+
+ next = str + n;
+ ent = val_table_find_ent(debug_values_table, str, next - str);
+ if (ent)
+ val |= ent->value;
+ else
+ AA_DEBUG(DEBUG_INTERFACE, "unknown debug type '%.*s'",
+ (int)(next - str), str);
+ str = next + 1;
+ } while (*next != 0);
+ return val;
+}
+
/**
- * aa_split_fqname - split a fqname into a profile and namespace name
- * @fqname: a full qualified name in namespace profile format (NOT NULL)
- * @ns_name: pointer to portion of the string containing the ns name (NOT NULL)
- *
- * Returns: profile name or NULL if one is not specified
- *
- * Split a namespace name from a profile name (see policy.c for naming
- * description). If a portion of the name is missing it returns NULL for
- * that portion.
- *
- * NOTE: may modify the @fqname string. The pointers returned point
- * into the @fqname string.
+ * val_mask_to_str - convert a perm mask to its short string
+ * @str: character buffer to store string in (at least 10 characters)
+ * @size: size of the @str buffer
+ * @table: NUL-terminated character buffer of permission characters (NOT NULL)
+ * @mask: permission mask to convert
*/
-char *aa_split_fqname(char *fqname, char **ns_name)
+static int val_mask_to_str(char *str, size_t size,
+ const struct val_table_ent *table, u32 mask)
{
- char *name = strim(fqname);
-
- *ns_name = NULL;
- if (name[0] == ':') {
- char *split = strchr(&name[1], ':');
- *ns_name = skip_spaces(&name[1]);
- if (split) {
- /* overwrite ':' with \0 */
- *split++ = 0;
- if (strncmp(split, "//", 2) == 0)
- split += 2;
- name = skip_spaces(split);
- } else
- /* a ns name without a following profile is allowed */
- name = NULL;
+ const struct val_table_ent *ent;
+ int total = 0;
+
+ for (ent = table; ent->str; ent++) {
+ if (ent->value && (ent->value & mask) == ent->value) {
+ int len = scnprintf(str, size, "%s%s", total ? "," : "",
+ ent->str);
+ size -= len;
+ str += len;
+ total += len;
+ mask &= ~ent->value;
+ }
}
- if (name && *name == 0)
- name = NULL;
- return name;
+ return total;
+}
+
+int aa_print_debug_params(char *buffer)
+{
+ if (!aa_g_debug)
+ return sprintf(buffer, "N");
+ return val_mask_to_str(buffer, PAGE_SIZE, debug_values_table,
+ aa_g_debug);
+}
+
+bool aa_resize_str_table(struct aa_str_table *t, int newsize, gfp_t gfp)
+{
+ char **n;
+ int i;
+
+ if (t->size == newsize)
+ return true;
+ n = kcalloc(newsize, sizeof(*n), gfp);
+ if (!n)
+ return false;
+ for (i = 0; i < min(t->size, newsize); i++)
+ n[i] = t->table[i];
+ for (; i < t->size; i++)
+ kfree_sensitive(t->table[i]);
+ if (newsize > t->size)
+ memset(&n[t->size], 0, (newsize-t->size)*sizeof(*n));
+ kfree_sensitive(t->table);
+ t->table = n;
+ t->size = newsize;
+
+ return true;
+}
+
+/**
+ * aa_free_str_table - free entries str table
+ * @t: the string table to free (MAYBE NULL)
+ */
+void aa_free_str_table(struct aa_str_table *t)
+{
+ int i;
+
+ if (t) {
+ if (!t->table)
+ return;
+
+ for (i = 0; i < t->size; i++)
+ kfree_sensitive(t->table[i]);
+ kfree_sensitive(t->table);
+ t->table = NULL;
+ t->size = 0;
+ }
}
/**
* skipn_spaces - Removes leading whitespace from @str.
* @str: The string to be stripped.
+ * @n: length of str to parse, will stop at \0 if encountered before n
*
* Returns a pointer to the first non-whitespace character in @str.
* if all whitespace will return NULL
@@ -128,10 +221,10 @@ const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name,
void aa_info_message(const char *str)
{
if (audit_enabled) {
- DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, NULL);
+ DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_NONE, AA_CLASS_NONE, NULL);
- aad(&sa)->info = str;
- aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, NULL);
+ ad.info = str;
+ aa_audit_msg(AUDIT_APPARMOR_STATUS, &ad, NULL);
}
printk(KERN_INFO "AppArmor: %s\n", str);
}
@@ -140,7 +233,7 @@ __counted char *aa_str_alloc(int size, gfp_t gfp)
{
struct counted_str *str;
- str = kmalloc(sizeof(struct counted_str) + size, gfp);
+ str = kmalloc(struct_size(str, name, size), gfp);
if (!str)
return NULL;
@@ -259,32 +352,6 @@ void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs,
}
/**
- * aa_audit_perms_cb - generic callback fn for auditing perms
- * @ab: audit buffer (NOT NULL)
- * @va: audit struct to audit values of (NOT NULL)
- */
-static void aa_audit_perms_cb(struct audit_buffer *ab, void *va)
-{
- struct common_audit_data *sa = va;
-
- if (aad(sa)->request) {
- audit_log_format(ab, " requested_mask=");
- aa_audit_perm_mask(ab, aad(sa)->request, aa_file_perm_chrs,
- PERMS_CHRS_MASK, aa_file_perm_names,
- PERMS_NAMES_MASK);
- }
- if (aad(sa)->denied) {
- audit_log_format(ab, "denied_mask=");
- aa_audit_perm_mask(ab, aad(sa)->denied, aa_file_perm_chrs,
- PERMS_CHRS_MASK, aa_file_perm_names,
- PERMS_NAMES_MASK);
- }
- audit_log_format(ab, " peer=");
- aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer,
- FLAGS_NONE, GFP_ATOMIC);
-}
-
-/**
* aa_apply_modes_to_perms - apply namespace and profile flags to perms
* @profile: that perms where computed from
* @perms: perms to apply mode modifiers to
@@ -296,13 +363,13 @@ void aa_apply_modes_to_perms(struct aa_profile *profile, struct aa_perms *perms)
switch (AUDIT_MODE(profile)) {
case AUDIT_ALL:
perms->audit = ALL_PERMS_MASK;
- /* fall through */
+ fallthrough;
case AUDIT_NOQUIET:
perms->quiet = 0;
break;
case AUDIT_QUIET:
perms->audit = 0;
- /* fall through */
+ fallthrough;
case AUDIT_QUIET_DENIED:
perms->quiet = ALL_PERMS_MASK;
break;
@@ -312,113 +379,31 @@ void aa_apply_modes_to_perms(struct aa_profile *profile, struct aa_perms *perms)
perms->kill = ALL_PERMS_MASK;
else if (COMPLAIN_MODE(profile))
perms->complain = ALL_PERMS_MASK;
-/*
- * TODO:
- * else if (PROMPT_MODE(profile))
- * perms->prompt = ALL_PERMS_MASK;
- */
+ else if (USER_MODE(profile))
+ perms->prompt = ALL_PERMS_MASK;
}
-static u32 map_other(u32 x)
-{
- return ((x & 0x3) << 8) | /* SETATTR/GETATTR */
- ((x & 0x1c) << 18) | /* ACCEPT/BIND/LISTEN */
- ((x & 0x60) << 19); /* SETOPT/GETOPT */
-}
-
-void aa_compute_perms(struct aa_dfa *dfa, unsigned int state,
- struct aa_perms *perms)
-{
- *perms = (struct aa_perms) {
- .allow = dfa_user_allow(dfa, state),
- .audit = dfa_user_audit(dfa, state),
- .quiet = dfa_user_quiet(dfa, state),
- };
-
- /* for v5 perm mapping in the policydb, the other set is used
- * to extend the general perm set
- */
- perms->allow |= map_other(dfa_other_allow(dfa, state));
- perms->audit |= map_other(dfa_other_audit(dfa, state));
- perms->quiet |= map_other(dfa_other_quiet(dfa, state));
-// perms->xindex = dfa_user_xindex(dfa, state);
-}
-
-/**
- * aa_perms_accum_raw - accumulate perms with out masking off overlapping perms
- * @accum - perms struct to accumulate into
- * @addend - perms struct to add to @accum
- */
-void aa_perms_accum_raw(struct aa_perms *accum, struct aa_perms *addend)
-{
- accum->deny |= addend->deny;
- accum->allow &= addend->allow & ~addend->deny;
- accum->audit |= addend->audit & addend->allow;
- accum->quiet &= addend->quiet & ~addend->allow;
- accum->kill |= addend->kill & ~addend->allow;
- accum->stop |= addend->stop & ~addend->allow;
- accum->complain |= addend->complain & ~addend->allow & ~addend->deny;
- accum->cond |= addend->cond & ~addend->allow & ~addend->deny;
- accum->hide &= addend->hide & ~addend->allow;
- accum->prompt |= addend->prompt & ~addend->allow & ~addend->deny;
-}
-
-/**
- * aa_perms_accum - accumulate perms, masking off overlapping perms
- * @accum - perms struct to accumulate into
- * @addend - perms struct to add to @accum
- */
-void aa_perms_accum(struct aa_perms *accum, struct aa_perms *addend)
-{
- accum->deny |= addend->deny;
- accum->allow &= addend->allow & ~accum->deny;
- accum->audit |= addend->audit & accum->allow;
- accum->quiet &= addend->quiet & ~accum->allow;
- accum->kill |= addend->kill & ~accum->allow;
- accum->stop |= addend->stop & ~accum->allow;
- accum->complain |= addend->complain & ~accum->allow & ~accum->deny;
- accum->cond |= addend->cond & ~accum->allow & ~accum->deny;
- accum->hide &= addend->hide & ~accum->allow;
- accum->prompt |= addend->prompt & ~accum->allow & ~accum->deny;
-}
-
-void aa_profile_match_label(struct aa_profile *profile, struct aa_label *label,
+void aa_profile_match_label(struct aa_profile *profile,
+ struct aa_ruleset *rules,
+ struct aa_label *label,
int type, u32 request, struct aa_perms *perms)
{
/* TODO: doesn't yet handle extended types */
- unsigned int state;
+ aa_state_t state;
- state = aa_dfa_next(profile->policy.dfa,
- profile->policy.start[AA_CLASS_LABEL],
+ state = aa_dfa_next(rules->policy->dfa,
+ rules->policy->start[AA_CLASS_LABEL],
type);
- aa_label_match(profile, label, state, false, request, perms);
+ aa_label_match(profile, rules, label, state, false, request, perms);
}
-/* currently unused */
-int aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target,
- u32 request, int type, u32 *deny,
- struct common_audit_data *sa)
-{
- struct aa_perms perms;
-
- aad(sa)->label = &profile->label;
- aad(sa)->peer = &target->label;
- aad(sa)->request = request;
-
- aa_profile_match_label(profile, &target->label, type, request, &perms);
- aa_apply_modes_to_perms(profile, &perms);
- *deny |= request & perms.deny;
- return aa_check_perms(profile, &perms, request, sa, aa_audit_perms_cb);
-}
-
/**
* aa_check_perms - do audit mode selection based on perms set
* @profile: profile being checked
* @perms: perms computed for the request
* @request: requested perms
- * @deny: Returns: explicit deny set
- * @sa: initialized audit structure (MAY BE NULL if not auditing)
+ * @ad: initialized audit structure (MAY BE NULL if not auditing)
* @cb: callback fn for type specific fields (MAY BE NULL)
*
* Returns: 0 if permission else error code
@@ -431,7 +416,7 @@ int aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target,
* with a positive value.
*/
int aa_check_perms(struct aa_profile *profile, struct aa_perms *perms,
- u32 request, struct common_audit_data *sa,
+ u32 request, struct apparmor_audit_data *ad,
void (*cb)(struct audit_buffer *, void *))
{
int type, error;
@@ -440,7 +425,7 @@ int aa_check_perms(struct aa_profile *profile, struct aa_perms *perms,
if (likely(!denied)) {
/* mask off perms that are not being force audited */
request &= perms->audit;
- if (!request || !sa)
+ if (!request || !ad)
return 0;
type = AUDIT_APPARMOR_AUDIT;
@@ -459,16 +444,16 @@ int aa_check_perms(struct aa_profile *profile, struct aa_perms *perms,
error = -ENOENT;
denied &= ~perms->quiet;
- if (!sa || !denied)
+ if (!ad || !denied)
return error;
}
- if (sa) {
- aad(sa)->label = &profile->label;
- aad(sa)->request = request;
- aad(sa)->denied = denied;
- aad(sa)->error = error;
- aa_audit_msg(type, sa, cb);
+ if (ad) {
+ ad->subj_label = &profile->label;
+ ad->request = request;
+ ad->denied = denied;
+ ad->error = error;
+ aa_audit_msg(type, ad, cb);
}
if (type == AUDIT_APPARMOR_ALLOWED)