From: jjohansen@suse.de
To: linux-kernel@vger.kernel.org
Cc: linux-security-module@vger.kernel.org,
linux-fsdevel@vger.kernel.org, chrisw@sous-sol.org,
John Johansen <jjohansen@suse.de>,
Andreas Gruenbacher <agruen@suse.de>
Subject: [AppArmor 39/41] AppArmor: Profile loading and manipulation, pathname matching
Date: Thu, 12 Apr 2007 02:08:48 -0700 [thread overview]
Message-ID: <20070412090849.465428000@suse.de> (raw)
In-Reply-To: 20070412090809.917795000@suse.de
[-- Attachment #1: apparmor-module_interface.diff --]
[-- Type: text/plain, Size: 25105 bytes --]
Pathname matching, transition table loading, profile loading and
manipulation.
Signed-off-by: John Johansen <jjohansen@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
---
security/apparmor/match.c | 232 ++++++++++++
security/apparmor/match.h | 83 ++++
security/apparmor/module_interface.c | 641 +++++++++++++++++++++++++++++++++++
3 files changed, 956 insertions(+)
--- /dev/null
+++ b/security/apparmor/match.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2007 Novell/SUSE
+ *
+ * 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.
+ *
+ * Regular expression transition table matching
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include "match.h"
+
+static struct table_header *unpack_table(void *blob, size_t bsize)
+{
+ struct table_header *table = NULL;
+ struct table_header th;
+ size_t tsize;
+
+ if (bsize < sizeof(struct table_header))
+ goto out;
+
+ th.td_id = ntohs(*(u16 *) (blob));
+ th.td_flags = ntohs(*(u16 *) (blob + 2));
+ th.td_lolen = ntohl(*(u32 *) (blob + 8));
+ blob += sizeof(struct table_header);
+
+ if (!(th.td_flags == YYTD_DATA16 || th.td_flags == YYTD_DATA32 ||
+ th.td_flags == YYTD_DATA8))
+ goto out;
+
+ tsize = table_size(th.td_lolen, th.td_flags);
+ if (bsize < tsize)
+ goto out;
+
+ table = kmalloc(tsize, GFP_KERNEL);
+ if (table) {
+ *table = th;
+ if (th.td_flags == YYTD_DATA8)
+ UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
+ u8, ntohb);
+ else if (th.td_flags == YYTD_DATA16)
+ UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
+ u16, ntohs);
+ else
+ UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
+ u32, ntohl);
+ }
+
+out:
+ return table;
+}
+
+int unpack_dfa(struct aa_dfa *dfa, void *blob, size_t size)
+{
+ int hsize, i;
+ int error = -ENOMEM;
+
+ /* get dfa table set header */
+ if (size < sizeof(struct table_set_header))
+ goto fail;
+
+ if (ntohl(*(u32 *)blob) != YYTH_MAGIC)
+ goto fail;
+
+ hsize = ntohl(*(u32 *)(blob + 4));
+ if (size < hsize)
+ goto fail;
+
+ blob += hsize;
+ size -= hsize;
+
+ error = -EPROTO;
+ while (size > 0) {
+ struct table_header *table;
+ table = unpack_table(blob, size);
+ if (!table)
+ goto fail;
+
+ switch(table->td_id) {
+ case YYTD_ID_ACCEPT:
+ case YYTD_ID_BASE:
+ dfa->tables[table->td_id - 1] = table;
+ if (table->td_flags != YYTD_DATA32)
+ goto fail;
+ break;
+ case YYTD_ID_DEF:
+ case YYTD_ID_NXT:
+ case YYTD_ID_CHK:
+ dfa->tables[table->td_id - 1] = table;
+ if (table->td_flags != YYTD_DATA16)
+ goto fail;
+ break;
+ case YYTD_ID_EC:
+ dfa->tables[table->td_id - 1] = table;
+ if (table->td_flags != YYTD_DATA8)
+ goto fail;
+ break;
+ default:
+ kfree(table);
+ goto fail;
+ }
+
+ blob += table_size(table->td_lolen, table->td_flags);
+ size -= table_size(table->td_lolen, table->td_flags);
+ }
+
+ return 0;
+
+fail:
+ for (i = 0; i < ARRAY_SIZE(dfa->tables); i++) {
+ if (dfa->tables[i]) {
+ kfree(dfa->tables[i]);
+ dfa->tables[i] = NULL;
+ }
+ }
+ return error;
+}
+
+/**
+ * verify_dfa - verify that all the transitions and states in the dfa tables
+ * are in bounds.
+ * @dfa: dfa to test
+ *
+ * assumes dfa has gone through the verification done by unpacking
+ */
+int verify_dfa(struct aa_dfa *dfa)
+{
+ size_t i, state_count, trans_count;
+ int error = -EPROTO;
+
+ /* check that required tables exist */
+ if (!(dfa->tables[YYTD_ID_ACCEPT -1 ] &&
+ dfa->tables[YYTD_ID_DEF - 1] &&
+ dfa->tables[YYTD_ID_BASE - 1] &&
+ dfa->tables[YYTD_ID_NXT - 1] &&
+ dfa->tables[YYTD_ID_CHK - 1]))
+ goto out;
+
+ /* accept.size == default.size == base.size */
+ state_count = dfa->tables[YYTD_ID_BASE - 1]->td_lolen;
+ if (!(state_count == dfa->tables[YYTD_ID_DEF - 1]->td_lolen &&
+ state_count == dfa->tables[YYTD_ID_ACCEPT - 1]->td_lolen))
+ goto out;
+
+ /* next.size == chk.size */
+ trans_count = dfa->tables[YYTD_ID_NXT - 1]->td_lolen;
+ if (trans_count != dfa->tables[YYTD_ID_CHK - 1]->td_lolen)
+ goto out;
+
+ /* if equivalence classes then its table size must be 256 */
+ if (dfa->tables[YYTD_ID_EC - 1] &&
+ dfa->tables[YYTD_ID_EC - 1]->td_lolen != 256)
+ goto out;
+
+ for (i = 0; i < state_count; i++) {
+ if (DEFAULT_TABLE(dfa)[i] >= state_count)
+ goto out;
+ if (BASE_TABLE(dfa)[i] >= trans_count + 256)
+ goto out;
+ }
+
+ for (i = 0; i < trans_count ; i++) {
+ if (NEXT_TABLE(dfa)[i] >= state_count)
+ goto out;
+ if (CHECK_TABLE(dfa)[i] >= state_count)
+ goto out;
+ }
+
+ error = 0;
+out:
+ return error;
+}
+
+struct aa_dfa *aa_match_alloc(void)
+{
+ return kzalloc(sizeof(struct aa_dfa), GFP_KERNEL);
+}
+
+void aa_match_free(struct aa_dfa *dfa)
+{
+ if (dfa) {
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dfa->tables); i++)
+ kfree(dfa->tables[i]);
+ }
+ kfree(dfa);
+}
+
+/**
+ * aa_dfa_match - match @path against @dfa starting in @state
+ * @dfa: the dfa to match @path against
+ * @state: the state to start matching in
+ * @path: the path to match against the dfa
+ *
+ * aa_dfa_match will match the full path length and return the state it
+ * finished matching in. The final state is used to look up the accepting
+ * label.
+ */
+unsigned int aa_dfa_match(struct aa_dfa *dfa, const char *str)
+{
+ u16 *def = DEFAULT_TABLE(dfa);
+ u32 *base = BASE_TABLE(dfa);
+ u16 *next = NEXT_TABLE(dfa);
+ u16 *check = CHECK_TABLE(dfa);
+ unsigned int state = 1, pos;
+
+ /* current state is <state>, matching character *str */
+ if (dfa->tables[YYTD_ID_EC - 1]) {
+ u8 *equiv = EQUIV_TABLE(dfa);
+ while (*str) {
+ pos = base[state] + equiv[(u8)*str++];
+ if (check[pos] == state)
+ state = next[pos];
+ else
+ state = def[state];
+ }
+ } else {
+ while (*str) {
+ pos = base[state] + (u8)*str++;
+ if (check[pos] == state)
+ state = next[pos];
+ else
+ state = def[state];
+ }
+ }
+ return ACCEPT_TABLE(dfa)[state];
+}
--- /dev/null
+++ b/security/apparmor/match.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2007 Novell/SUSE
+ *
+ * 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.
+ *
+ * AppArmor submodule (match) prototypes
+ */
+
+#ifndef __MATCH_H
+#define __MATCH_H
+
+/**
+ * The format used for transition tables is based on the GNU flex table
+ * file format (--tables-file option; see Table File Format in the flex
+ * info pages and the flex sources for documentation). The magic number
+ * used in the header is 0x1B5E783D insted of 0xF13C57B1 though, because
+ * the YY_ID_CHK (check) and YY_ID_DEF (default) tables are used
+ * slightly differently (see the apparmor-parser package).
+ */
+
+#define YYTH_MAGIC 0x1B5E783D
+
+struct table_set_header {
+ u32 th_magic; /* YYTH_MAGIC */
+ u32 th_hsize;
+ u32 th_ssize;
+ u16 th_flags;
+ char th_version[];
+};
+
+#define YYTD_ID_ACCEPT 1
+#define YYTD_ID_BASE 2
+#define YYTD_ID_CHK 3
+#define YYTD_ID_DEF 4
+#define YYTD_ID_EC 5
+#define YYTD_ID_META 6
+#define YYTD_ID_NXT 8
+
+
+#define YYTD_DATA8 1
+#define YYTD_DATA16 2
+#define YYTD_DATA32 4
+
+struct table_header {
+ u16 td_id;
+ u16 td_flags;
+ u32 td_hilen;
+ u32 td_lolen;
+ char td_data[];
+};
+
+#define DEFAULT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_DEF - 1]->td_data))
+#define BASE_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_BASE - 1]->td_data))
+#define NEXT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_NXT - 1]->td_data))
+#define CHECK_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_CHK - 1]->td_data))
+#define EQUIV_TABLE(DFA) ((u8 *)((DFA)->tables[YYTD_ID_EC - 1]->td_data))
+#define ACCEPT_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT - 1]->td_data))
+
+struct aa_dfa {
+ struct table_header *tables[YYTD_ID_NXT];
+};
+
+#define ntohb(X) (X)
+
+#define UNPACK_ARRAY(TABLE, BLOB, LEN, TYPE, NTOHX) \
+ do { \
+ typeof(LEN) __i; \
+ TYPE *__t = (TYPE *) TABLE; \
+ TYPE *__b = (TYPE *) BLOB; \
+ for (__i = 0; __i < LEN; __i++) { \
+ __t[__i] = NTOHX(__b[__i]); \
+ } \
+ } while (0)
+
+static inline size_t table_size(size_t len, size_t el_size)
+{
+ return ALIGN(sizeof(struct table_header) + len * el_size, 8);
+}
+
+#endif /* __MATCH_H */
--- /dev/null
+++ b/security/apparmor/module_interface.c
@@ -0,0 +1,641 @@
+/*
+ * Copyright (C) 1998-2007 Novell/SUSE
+ *
+ * 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.
+ *
+ * AppArmor userspace policy interface
+ */
+
+#include <asm/unaligned.h>
+
+#include "apparmor.h"
+#include "inline.h"
+
+/*
+ * This mutex is used to synchronize profile adds, replacements, and
+ * removals: we only allow one of these operations at a time.
+ * We do not use the profile list lock here in order to avoid blocking
+ * exec during those operations. (Exec involves a profile list lookup
+ * for named-profile transitions.)
+ */
+DEFINE_MUTEX(aa_interface_lock);
+
+/*
+ * The AppArmor interface treats data as a type byte followed by the
+ * actual data. The interface has the notion of a a named entry
+ * which has a name (AA_NAME typecode followed by name string) followed by
+ * the entries typecode and data. Named types allow for optional
+ * elements and extensions to be added and tested for without breaking
+ * backwards compatability.
+ */
+
+enum aa_code {
+ AA_U8,
+ AA_U16,
+ AA_U32,
+ AA_U64,
+ AA_NAME, /* same as string except it is items name */
+ AA_STRING,
+ AA_BLOB,
+ AA_STRUCT,
+ AA_STRUCTEND,
+ AA_LIST,
+ AA_LISTEND,
+};
+
+/*
+ * aa_ext is the read of the buffer containing the serialized profile. The
+ * data is copied into a kernel buffer in apparmorfs and then handed off to
+ * the unpack routines.
+ */
+struct aa_ext {
+ void *start;
+ void *end;
+ void *pos; /* pointer to current position in the buffer */
+ u32 version;
+};
+
+static inline int aa_inbounds(struct aa_ext *e, size_t size)
+{
+ return (e->pos + size <= e->end);
+}
+
+/**
+ * aa_u16_chunck - test and do bounds checking for a u16 size based chunk
+ * @e: serialized data read head
+ * @chunk: start address for chunk of data
+ *
+ * return the size of chunk found with the read head at the end of
+ * the chunk.
+ */
+static size_t aa_is_u16_chunk(struct aa_ext *e, char **chunk)
+{
+ void *pos = e->pos;
+ size_t size = 0;
+
+ if (!aa_inbounds(e, sizeof(u16)))
+ goto fail;
+ size = le16_to_cpu(get_unaligned((u16 *)e->pos));
+ e->pos += sizeof(u16);
+ if (!aa_inbounds(e, size))
+ goto fail;
+ *chunk = e->pos;
+ e->pos += size;
+ return size;
+
+fail:
+ e->pos = pos;
+ return 0;
+}
+
+static inline int aa_is_X(struct aa_ext *e, enum aa_code code)
+{
+ if (!aa_inbounds(e, 1))
+ return 0;
+ if (*(u8 *) e->pos != code)
+ return 0;
+ e->pos++;
+ return 1;
+}
+
+/**
+ * aa_is_nameX - check is the next element is of type X with a name of @name
+ * @e: serialized data extent information
+ * @code: type code
+ * @name: name to match to the serialized element.
+ *
+ * check that the next serialized data element is of type X and has a tag
+ * name @name. If @name is specified then there must be a matching
+ * name element in the stream. If @name is NULL any name element will be
+ * skipped and only the typecode will be tested.
+ * returns 1 on success (both type code and name tests match) and the read
+ * head is advanced past the headers
+ * returns %0 if either match failes, the read head does not move
+ */
+static int aa_is_nameX(struct aa_ext *e, enum aa_code code, const char *name)
+{
+ void *pos = e->pos;
+ /*
+ * Check for presence of a tagname, and if present name size
+ * AA_NAME tag value is a u16.
+ */
+ if (aa_is_X(e, AA_NAME)) {
+ char *tag;
+ size_t size = aa_is_u16_chunk(e, &tag);
+ /* if a name is specified it must match. otherwise skip tag */
+ if (name && (!size || strcmp(name, tag)))
+ goto fail;
+ } else if (name) {
+ /* if a name is specified and there is no name tag fail */
+ goto fail;
+ }
+
+ /* now check if type code matches */
+ if (aa_is_X(e, code))
+ return 1;
+
+fail:
+ e->pos = pos;
+ return 0;
+}
+
+static int aa_is_u32(struct aa_ext *e, u32 *data, const char *name)
+{
+ void *pos = e->pos;
+ if (aa_is_nameX(e, AA_U32, name)) {
+ if (!aa_inbounds(e, sizeof(u32)))
+ goto fail;
+ if (data)
+ *data = le32_to_cpu(get_unaligned((u32 *)e->pos));
+ e->pos += sizeof(u32);
+ return 1;
+ }
+fail:
+ e->pos = pos;
+ return 0;
+}
+
+static size_t aa_is_blob(struct aa_ext *e, char **blob, const char *name)
+{
+ void *pos = e->pos;
+ if (aa_is_nameX(e, AA_BLOB, name)) {
+ u32 size;
+ if (!aa_inbounds(e, sizeof(u32)))
+ goto fail;
+ size = le32_to_cpu(get_unaligned((u32 *)e->pos));
+ e->pos += sizeof(u32);
+ if (aa_inbounds(e, (size_t) size)) {
+ * blob = e->pos;
+ e->pos += size;
+ return size;
+ }
+ }
+fail:
+ e->pos = pos;
+ return 0;
+}
+
+static int aa_is_dynstring(struct aa_ext *e, char **string, const char *name)
+{
+ char *src_str;
+ size_t size = 0;
+ void *pos = e->pos;
+ *string = NULL;
+ if (aa_is_nameX(e, AA_STRING, name) &&
+ (size = aa_is_u16_chunk(e, &src_str))) {
+ char *str;
+ if (!(str = kmalloc(size, GFP_KERNEL)))
+ goto fail;
+ memcpy(str, src_str, size);
+ *string = str;
+ }
+
+ return size;
+
+fail:
+ e->pos = pos;
+ return 0;
+}
+
+/**
+ * aa_unpack_dfa - unpack a file rule dfa
+ * @e: serialized data extent information
+ *
+ * returns dfa or ERR_PTR
+ */
+struct aa_dfa *aa_unpack_dfa(struct aa_ext *e)
+{
+ char *blob = NULL;
+ size_t size, error = 0;
+ struct aa_dfa *dfa = NULL;
+
+ size = aa_is_blob(e, &blob, "aadfa");
+ if (size) {
+ dfa = aa_match_alloc();
+ if (dfa) {
+ /*
+ * The dfa is aligned with in the blob to 8 bytes
+ * from the beginning of the stream.
+ */
+ size_t sz = blob - (char *) e->start;
+ size_t pad = ALIGN(sz, 8) - sz;
+ error = unpack_dfa(dfa, blob + pad, size - pad);
+ if (!error)
+ error = verify_dfa(dfa);
+ } else {
+ error = -ENOMEM;
+ }
+
+ if (error) {
+ aa_match_free(dfa);
+ dfa = ERR_PTR(error);
+ }
+ }
+
+ return dfa;
+}
+
+/**
+ * aa_unpack_profile - unpack a serialized profile
+ * @e: serialized data extent information
+ * @error: error code returned if unpacking fails
+ */
+static struct aa_profile *aa_unpack_profile(struct aa_ext *e)
+{
+ struct aa_profile *profile = NULL;
+
+ int error = -EPROTO;
+
+ profile = alloc_aa_profile();
+ if (!profile)
+ return ERR_PTR(-ENOMEM);
+
+ /* check that we have the right struct being passed */
+ if (!aa_is_nameX(e, AA_STRUCT, "profile"))
+ goto fail;
+ if (!aa_is_dynstring(e, &profile->name, NULL))
+ goto fail;
+
+ /* per profile debug flags (complain, audit) */
+ if (!aa_is_nameX(e, AA_STRUCT, "flags"))
+ goto fail;
+ if (!aa_is_u32(e, NULL, NULL))
+ goto fail;
+ if (!aa_is_u32(e, &(profile->flags.complain), NULL))
+ goto fail;
+ if (!aa_is_u32(e, &(profile->flags.audit), NULL))
+ goto fail;
+ if (!aa_is_nameX(e, AA_STRUCTEND, NULL))
+ goto fail;
+
+ if (!aa_is_u32(e, &(profile->capabilities), NULL))
+ goto fail;
+
+ /* get file rules */
+ profile->file_rules = aa_unpack_dfa(e);
+ if (IS_ERR(profile->file_rules)) {
+ error = PTR_ERR(profile->file_rules);
+ profile->file_rules = NULL;
+ goto fail;
+ }
+
+ /* get optional subprofiles */
+ if (aa_is_nameX(e, AA_LIST, "hats")) {
+ while (!aa_is_nameX(e, AA_LISTEND, NULL)) {
+ struct aa_profile *subprofile;
+ subprofile = aa_unpack_profile(e);
+ if (IS_ERR(subprofile)) {
+ error = PTR_ERR(subprofile);
+ goto fail;
+ }
+ subprofile->parent = profile;
+ list_add(&subprofile->list, &profile->sub);
+ }
+ }
+
+ if (!aa_is_nameX(e, AA_STRUCTEND, NULL))
+ goto fail;
+
+ return profile;
+
+fail:
+ AA_WARN(GFP_KERNEL, "Invalid profile %s",
+ profile && profile->name ? profile->name : "unknown");
+
+ if (profile)
+ free_aa_profile(profile);
+
+ return ERR_PTR(error);
+}
+
+/**
+ * aa_unpack_profile_wrapper - unpack a serialized base profile
+ * @e: serialized data extent information
+ *
+ * check interface version unpack a profile and all its hats and patch
+ * in any extra information that the profile needs.
+ */
+static struct aa_profile *aa_unpack_profile_wrapper(struct aa_ext *e)
+{
+ struct aa_profile *profile = aa_unpack_profile(e);
+ if (!IS_ERR(profile) &&
+ (!list_empty(&profile->sub) || profile->flags.complain)) {
+ int error;
+ if ((error = attach_nullprofile(profile))) {
+ aa_put_profile(profile);
+ return ERR_PTR(error);
+ }
+ }
+
+ return profile;
+}
+
+/**
+ * aa_verify_head - unpack serialized stream header
+ * @e: serialized data read head
+ *
+ * returns error or 0 if header is good
+ */
+static int aa_verify_header(struct aa_ext *e)
+{
+ /* get the interface version */
+ if (!aa_is_u32(e, &e->version, "version")) {
+ AA_WARN(GFP_KERNEL, "Interface version missing");
+ return -EPROTONOSUPPORT;
+ }
+
+ /* check that the interface version is currently supported */
+ if (e->version != 3) {
+ AA_WARN(GFP_KERNEL,
+ "Unsupported interface version (%d)", e->version);
+ return -EPROTONOSUPPORT;
+ }
+ return 0;
+}
+
+/**
+ * aa_add_profile - Unpack and add a new profile to the profile list
+ * @data: serialized data stream
+ * @size: size of the serialized data stream
+ */
+ssize_t aa_add_profile(void *data, size_t size)
+{
+ struct aa_profile *profile = NULL;
+ struct aa_ext e = {
+ .start = data,
+ .end = data + size,
+ .pos = data
+ };
+ ssize_t error = aa_verify_header(&e);
+ if (error)
+ return error;
+
+ profile = aa_unpack_profile_wrapper(&e);
+ if (IS_ERR(profile))
+ return PTR_ERR(profile);
+
+ mutex_lock(&aa_interface_lock);
+ write_lock(&profile_list_lock);
+ if (__aa_find_profile(profile->name, &profile_list)) {
+ /* A profile with this name exists already. */
+ write_unlock(&profile_list_lock);
+ mutex_unlock(&aa_interface_lock);
+ aa_put_profile(profile);
+ return -EEXIST;
+ }
+ list_add(&profile->list, &profile_list);
+ write_unlock(&profile_list_lock);
+ mutex_unlock(&aa_interface_lock);
+
+ return size;
+}
+
+/**
+ * task_replace - replace a task's profile
+ * @task: task to replace profile on
+ * @new_cxt: new aa_task_context to do replacement with
+ * @new_profile: new profile
+ */
+static inline void task_replace(struct task_struct *task,
+ struct aa_task_context *new_cxt,
+ struct aa_profile *new_profile)
+{
+ struct aa_task_context *cxt = aa_task_context(task);
+
+ AA_DEBUG("%s: replacing profile for task %s(%d) "
+ "profile=%s (%p) hat=%s (%p)\n",
+ __FUNCTION__,
+ cxt->task->comm, cxt->task->pid,
+ cxt->profile->parent->name, cxt->profile->parent,
+ cxt->profile->name, cxt->profile);
+
+ if (cxt->profile != cxt->profile->parent) {
+ struct aa_profile *hat;
+
+ /*
+ * The old profile was in a hat, check to see if the new
+ * profile has an equivalent hat.
+ */
+ hat = __aa_find_profile(cxt->profile->name, &new_profile->sub);
+
+ if (!hat)
+ hat = aa_dup_profile(new_profile->null_profile);
+
+ aa_change_task_context(task, new_cxt, hat, cxt->hat_magic);
+ aa_put_profile(hat);
+ } else
+ aa_change_task_context(task, new_cxt, new_profile,
+ cxt->hat_magic);
+}
+
+/**
+ * aa_replace_profile - replace a profile on the profile list
+ * @udata: serialized data stream
+ * @size: size of the serialized data stream
+ *
+ * unpack and replace a profile on the profile list and uses of that profile
+ * by any aa_task_context. If the profile does not exist on the profile list
+ * it is added. Return %0 or error.
+ */
+ssize_t aa_replace_profile(void *udata, size_t size)
+{
+ struct aa_profile *old_profile, *new_profile;
+ struct aa_task_context *new_cxt;
+ struct aa_ext e = {
+ .start = udata,
+ .end = udata + size,
+ .pos = udata
+ };
+ ssize_t error = aa_verify_header(&e);
+ if (error)
+ return error;
+
+ new_profile = aa_unpack_profile_wrapper(&e);
+ if (IS_ERR(new_profile))
+ return PTR_ERR(new_profile);
+
+ mutex_lock(&aa_interface_lock);
+ write_lock(&profile_list_lock);
+ old_profile = __aa_find_profile(new_profile->name, &profile_list);
+ if (old_profile) {
+ lock_profile(old_profile);
+ old_profile->isstale = 1;
+ unlock_profile(old_profile);
+ list_del_init(&old_profile->list);
+ }
+ list_add(&new_profile->list, &profile_list);
+ write_unlock(&profile_list_lock);
+
+ if (!old_profile)
+ goto out;
+
+ /*
+ * Replacement needs to allocate a new aa_task_context for each
+ * task confined by old_profile. To do this the profile locks
+ * are only held when the actual switch is done per task. While
+ * looping to allocate a new aa_task_context the old_task list
+ * may get shorter if tasks exit/change their profile but will
+ * not get longer as new task will not use old_profile detecting
+ * that is stale.
+ */
+ do {
+ new_cxt = aa_alloc_task_context(GFP_KERNEL | __GFP_NOFAIL);
+
+ lock_both_profiles(old_profile, new_profile);
+ if (!list_empty(&old_profile->task_contexts)) {
+ struct task_struct *task =
+ list_entry(old_profile->task_contexts.next,
+ struct aa_task_context, list)->task;
+ task_lock(task);
+ task_replace(task, new_cxt, new_profile);
+ task_unlock(task);
+ new_cxt = NULL;
+ }
+ unlock_both_profiles(old_profile, new_profile);
+ } while (!new_cxt);
+ aa_free_task_context(new_cxt);
+ aa_put_profile(old_profile);
+
+out:
+ mutex_unlock(&aa_interface_lock);
+ return size;
+}
+
+/**
+ * aa_remove_profile - remove a profile from the system
+ * @name: name of the profile to remove
+ * @size: size of the name
+ *
+ * remove a profile from the profile list and all aa_task_context references
+ * to said profile.
+ */
+ssize_t aa_remove_profile(const char *name, size_t size)
+{
+ struct aa_profile *profile;
+
+ mutex_lock(&aa_interface_lock);
+ write_lock(&profile_list_lock);
+ profile = __aa_find_profile(name, &profile_list);
+ if (!profile) {
+ write_unlock(&profile_list_lock);
+ mutex_unlock(&aa_interface_lock);
+ return -ENOENT;
+ }
+
+ /* Remove the profile from each task context it is on. */
+ lock_profile(profile);
+ profile->isstale = 1;
+ aa_unconfine_tasks(profile);
+ unlock_profile(profile);
+
+ /* Release the profile itself. */
+ list_del_init(&profile->list);
+ aa_put_profile(profile);
+ write_unlock(&profile_list_lock);
+ mutex_unlock(&aa_interface_lock);
+
+ return size;
+}
+
+/**
+ * free_aa_profile_kref - free aa_profile by kref (called by aa_put_profile)
+ * @kr: kref callback for freeing of a profile
+ */
+void free_aa_profile_kref(struct kref *kref)
+{
+ struct aa_profile *p=container_of(kref, struct aa_profile, count);
+
+ free_aa_profile(p);
+}
+
+/**
+ * alloc_aa_profile - allocate, initialize and return a new profile
+ * Returns NULL on failure.
+ */
+struct aa_profile *alloc_aa_profile(void)
+{
+ struct aa_profile *profile;
+
+ profile = kzalloc(sizeof(*profile), GFP_KERNEL);
+ AA_DEBUG("%s(%p)\n", __FUNCTION__, profile);
+ if (profile) {
+ profile->parent = profile;
+ INIT_LIST_HEAD(&profile->list);
+ INIT_LIST_HEAD(&profile->sub);
+ kref_init(&profile->count);
+ INIT_LIST_HEAD(&profile->task_contexts);
+ spin_lock_init(&profile->lock);
+ }
+ return profile;
+}
+
+/**
+ * free_aa_profile - free a profile
+ * @profile: the profile to free
+ *
+ * Free a profile, its hats and null_profile. All references to the profile,
+ * its hats and null_profile must have been put.
+ *
+ * If the profile was referenced from a task context, free_aa_profile() will
+ * be called from an rcu callback routine, so we must not sleep here.
+ */
+void free_aa_profile(struct aa_profile *profile)
+{
+ struct aa_profile *p, *ptmp;
+
+ AA_DEBUG("%s(%p)\n", __FUNCTION__, profile);
+
+ if (!profile)
+ return;
+
+ /* profile is still on global profile list -- invalid */
+ if (!list_empty(&profile->list)) {
+ AA_ERROR("%s: internal error, "
+ "profile '%s' still on global list\n",
+ __FUNCTION__,
+ profile->name);
+ BUG();
+ }
+
+ aa_match_free(profile->file_rules);
+
+ /*
+ * Use free_aa_profile instead of aa_put_profile to destroy the
+ * null_profile, because the null_profile use the same reference
+ * counting as hats, ie. the count goes to the base profile.
+ */
+ free_aa_profile(profile->null_profile);
+ list_for_each_entry_safe(p, ptmp, &profile->sub, list) {
+ list_del_init(&p->list);
+ p->parent = p;
+ aa_put_profile(p);
+ }
+
+ if (profile->name) {
+ AA_DEBUG("%s: %s\n", __FUNCTION__, profile->name);
+ kfree(profile->name);
+ }
+
+ kfree(profile);
+}
+
+/**
+ * aa_unconfine_tasks - remove tasks on a profile's task context list
+ * @profile: profile to remove tasks from
+ *
+ * Assumes that @profile lock is held.
+ */
+void aa_unconfine_tasks(struct aa_profile *profile)
+{
+ while (!list_empty(&profile->task_contexts)) {
+ struct task_struct *task =
+ list_entry(profile->task_contexts.next,
+ struct aa_task_context, list)->task;
+ task_lock(task);
+ aa_change_task_context(task, NULL, NULL, 0);
+ task_unlock(task);
+ }
+}
--
next prev parent reply other threads:[~2007-04-12 9:08 UTC|newest]
Thread overview: 159+ messages / expand[flat|nested] mbox.gz Atom feed top
2007-04-12 9:08 [AppArmor 00/41] AppArmor security module overview jjohansen
2007-04-12 9:08 ` [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook jjohansen
2007-04-12 10:06 ` Christoph Hellwig
2007-04-16 16:11 ` [nameidata 1/2] Don't pass NULL nameidata to vfs_create Andreas Gruenbacher
2007-04-16 16:21 ` Christoph Hellwig
2007-04-16 16:40 ` Andreas Gruenbacher
2007-04-16 16:45 ` Christoph Hellwig
2007-04-17 12:09 ` Andreas Gruenbacher
2007-05-11 15:59 ` Andreas Gruenbacher
2007-04-16 16:25 ` Matthew Wilcox
2007-04-16 16:29 ` [nameidata 2/2] Pass no useless nameidata to the create, lookup, and permission IOPs Andreas Gruenbacher
2007-04-16 16:39 ` Christoph Hellwig
2007-04-16 16:42 ` Randy Dunlap
2007-04-16 16:44 ` Andreas Gruenbacher
2007-04-16 16:50 ` Randy Dunlap
2007-04-12 10:12 ` [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook Al Viro
2007-05-23 19:06 ` Andreas Gruenbacher
2007-05-24 1:28 ` James Morris
2007-05-24 9:16 ` Andreas Gruenbacher
2007-05-24 12:51 ` [AppArmor 01/41] Pass struct vfsmount to the inode_create LSMhook Tetsuo Handa
[not found] ` <200705241112.41101.agruen@suse.de>
2007-05-24 13:19 ` [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook James Morris
2007-05-24 18:10 ` Andreas Gruenbacher
2007-05-24 18:40 ` Al Viro
2007-05-24 21:56 ` Andreas Gruenbacher
2007-05-24 18:58 ` Casey Schaufler
2007-05-25 4:14 ` Andreas Gruenbacher
2007-05-25 5:17 ` Jeremy Maitin-Shepard
2007-05-25 17:43 ` Casey Schaufler
2007-05-25 18:10 ` Jeremy Maitin-Shepard
2007-05-25 18:13 ` Jeremy Maitin-Shepard
2007-05-25 19:06 ` Casey Schaufler
2007-05-26 1:40 ` Tetsuo Handa
2007-05-26 12:10 ` Andreas Gruenbacher
2007-05-26 22:58 ` Casey Schaufler
2007-05-27 1:33 ` Valdis.Kletnieks
2007-05-25 20:00 ` Andreas Gruenbacher
2007-05-25 20:27 ` Casey Schaufler
2007-05-26 5:27 ` Crispin Cowan
2007-05-26 13:34 ` Alan Cox
2007-05-26 14:05 ` Andreas Gruenbacher
2007-05-26 18:41 ` James Morris
2007-05-26 5:20 ` Kyle Moffett
2007-05-26 11:46 ` Andreas Gruenbacher
2007-05-26 12:09 ` Tetsuo Handa
2007-05-26 13:41 ` Andreas Gruenbacher
2007-05-26 14:44 ` Tetsuo Handa
2007-05-26 16:52 ` Andreas Gruenbacher
2007-05-26 18:16 ` Kyle Moffett
2007-05-26 18:45 ` [AppArmor 01/41] " James Morris
2007-05-26 23:08 ` Toshiharu Harada
2007-05-27 2:10 ` Kyle Moffett
2007-05-27 2:37 ` Valdis.Kletnieks
2007-05-27 5:32 ` Kyle Moffett
2007-05-28 20:38 ` Pavel Machek
2007-05-29 2:00 ` Kyle Moffett
2007-05-27 7:25 ` Toshiharu Harada
2007-05-27 13:35 ` Kyle Moffett
2007-05-28 10:41 ` Toshiharu Harada
2007-05-29 1:54 ` Kyle Moffett
2007-05-29 21:17 ` Valdis.Kletnieks
2007-05-30 5:52 ` Crispin Cowan
2007-05-24 14:40 ` Pavel Machek
2007-05-30 10:06 ` Alan Cox
2007-05-30 2:38 ` Toshiharu Harada
2007-05-27 8:34 ` Cliffe
2007-05-27 13:07 ` Kyle Moffett
2007-05-27 16:12 ` Casey Schaufler
2007-05-25 8:01 ` Toshiharu Harada
2007-04-12 9:08 ` [AppArmor 02/41] Remove redundant check from proc_setattr() jjohansen
2007-04-12 9:08 ` [AppArmor 03/41] Remove redundant check from proc_sys_setattr() jjohansen
2007-04-12 10:10 ` Alan Cox
2007-04-12 9:08 ` [AppArmor 04/41] Pass struct file down to remove_suid and children jjohansen
2007-04-12 9:08 ` [AppArmor 05/41] Add a vfsmount parameter to notify_change() jjohansen
2007-04-12 9:08 ` [AppArmor 06/41] Pass struct vfsmount to the inode_setattr LSM hook jjohansen
2007-04-12 9:08 ` [AppArmor 07/41] Add struct vfsmount parameter to vfs_mkdir() jjohansen
2007-04-12 9:08 ` [AppArmor 08/41] Pass struct vfsmount to the inode_mkdir LSM hook jjohansen
2007-04-12 9:08 ` [AppArmor 09/41] Add a struct vfsmount parameter to vfs_mknod() jjohansen
2007-04-12 9:08 ` [AppArmor 10/41] Pass struct vfsmount to the inode_mknod LSM hook jjohansen
2007-04-12 9:08 ` [AppArmor 11/41] Add a struct vfsmount parameter to vfs_symlink() jjohansen
2007-04-12 9:08 ` [AppArmor 12/41] Pass struct vfsmount to the inode_symlink LSM hook jjohansen
2007-04-12 9:08 ` [AppArmor 13/41] Pass struct vfsmount to the inode_readlink " jjohansen
2007-04-12 9:08 ` [AppArmor 14/41] Add struct vfsmount parameters to vfs_link() jjohansen
2007-04-12 9:08 ` [AppArmor 15/41] Pass the struct vfsmounts to the inode_link LSM hook jjohansen
2007-04-12 9:08 ` [AppArmor 16/41] Add a struct vfsmount parameter to vfs_rmdir() jjohansen
2007-04-12 9:08 ` [AppArmor 17/41] Pass struct vfsmount to the inode_rmdir LSM hook jjohansen
2007-04-12 9:08 ` [AppArmor 18/41] call lsm hook before unhashing dentry in vfs_rmdir() jjohansen
2007-04-12 9:08 ` [AppArmor 19/41] Add a struct vfsmount parameter to vfs_unlink() jjohansen
2007-04-12 9:08 ` [AppArmor 20/41] Pass struct vfsmount to the inode_unlink LSM hook jjohansen
2007-04-12 9:08 ` [AppArmor 21/41] Add struct vfsmount parameters to vfs_rename() jjohansen
2007-04-12 9:08 ` [AppArmor 22/41] Pass struct vfsmount to the inode_rename LSM hook jjohansen
2007-04-12 9:08 ` [AppArmor 23/41] Add a struct vfsmount parameter to vfs_setxattr() jjohansen
2007-04-12 9:08 ` [AppArmor 24/41] Pass struct vfsmount to the inode_setxattr LSM hook jjohansen
2007-04-12 9:08 ` [AppArmor 25/41] Add a struct vfsmount parameter to vfs_getxattr() jjohansen
2007-04-12 9:08 ` [AppArmor 26/41] Pass struct vfsmount to the inode_getxattr LSM hook jjohansen
2007-04-12 9:08 ` [AppArmor 27/41] Add a struct vfsmount parameter to vfs_listxattr() jjohansen
2007-04-12 9:08 ` [AppArmor 28/41] Pass struct vfsmount to the inode_listxattr LSM hook jjohansen
2007-04-12 9:08 ` [AppArmor 29/41] Add a struct vfsmount parameter to vfs_removexattr() jjohansen
2007-04-12 9:08 ` [AppArmor 30/41] Pass struct vfsmount to the inode_removexattr LSM hook jjohansen
2007-04-12 9:08 ` [AppArmor 31/41] Fix __d_path() for lazy unmounts and make it unambiguous; exclude unreachable mount points from /proc/mounts jjohansen
2007-04-12 9:58 ` Alan Cox
2007-04-15 17:40 ` Andreas Gruenbacher
2007-04-16 21:57 ` Alan Cox
2007-04-17 1:35 ` Andreas Gruenbacher
2007-04-17 17:21 ` Alan Cox
2007-04-19 23:23 ` [d_path 0/7] Fixes to d_path: Respin Andreas Gruenbacher
2007-04-19 23:23 ` [d_path 1/7] Fix __d_path() for lazy unmounts and make it unambiguous Andreas Gruenbacher
2007-04-20 9:32 ` Alan Cox
2007-04-19 23:23 ` [d_path 2/7] Make d_path() consistent across mount operations Andreas Gruenbacher
2007-04-19 23:23 ` [d_path 3/7] Add d_namespace_path() to compute namespace relative pathnames Andreas Gruenbacher
2007-04-21 12:57 ` Tetsuo Handa
2007-04-21 16:16 ` Andreas Gruenbacher
2007-04-19 23:23 ` [d_path 4/7] Make getcwd() only return valid paths Andreas Gruenbacher
2007-04-19 23:23 ` [d_path 5/7] Remove duplicate proc code Andreas Gruenbacher
2007-04-19 23:23 ` [d_path 6/7] Filter out disconnected paths from /proc/mounts Andreas Gruenbacher
2007-04-20 9:34 ` Alan Cox
2007-04-19 23:23 ` [d_path 7/7] Distinguish between connected and disconnected paths in d_path() Andreas Gruenbacher
2007-04-20 9:30 ` [d_path 0/7] Fixes to d_path: Respin Alan Cox
2007-04-20 11:45 ` Andreas Gruenbacher
2007-04-20 15:15 ` Ulrich Drepper
2007-04-20 15:21 ` Andreas Gruenbacher
2007-04-20 15:24 ` Ulrich Drepper
2007-04-20 16:40 ` Andreas Gruenbacher
2007-04-20 19:17 ` Ulrich Drepper
2007-04-20 20:44 ` Miklos Szeredi
2007-04-21 19:04 ` Andreas Gruenbacher
2007-04-21 19:46 ` Ulrich Drepper
2007-04-22 9:10 ` Christoph Hellwig
2007-04-22 15:48 ` Andreas Gruenbacher
2007-04-17 6:30 ` [AppArmor 31/41] Fix __d_path() for lazy unmounts and make it unambiguous; exclude unreachable mount points from /proc/mounts Rob Meijer
2007-04-12 9:08 ` [AppArmor 32/41] Make d_path() consistent across mount operations jjohansen
2007-04-12 9:08 ` [AppArmor 33/41] Add d_namespace_path() to obtain namespace relative pathnames jjohansen
2007-04-12 10:49 ` Al Viro
2007-04-12 9:08 ` [AppArmor 34/41] Enable LSM hooks to distinguish operations on file descriptors from operations on pathnames jjohansen
2007-04-12 9:08 ` [AppArmor 35/41] Pass struct file down the inode_*xattr security LSM hooks jjohansen
2007-04-12 9:08 ` [AppArmor 36/41] Export audit subsystem for use by modules jjohansen
2007-04-12 9:08 ` [AppArmor 37/41] AppArmor: Main Part jjohansen
2007-04-12 10:37 ` Alan Cox
2007-04-13 8:17 ` Andreas Gruenbacher
2007-04-13 8:48 ` Andreas Gruenbacher
2007-04-13 8:52 ` Nick Piggin
2007-04-12 9:08 ` [AppArmor 38/41] AppArmor: Module and LSM hooks jjohansen
2007-04-12 10:21 ` Alan Cox
2007-04-16 21:37 ` John Johansen
2007-04-12 9:08 ` jjohansen [this message]
2007-04-12 10:28 ` [AppArmor 39/41] AppArmor: Profile loading and manipulation, pathname matching Alan Cox
2007-04-12 13:46 ` Andi Kleen
2007-04-15 14:21 ` Andreas Gruenbacher
2007-04-16 6:27 ` Andi Kleen
2007-04-16 20:56 ` John Johansen
2007-04-16 7:39 ` Pavel Machek
2007-04-16 22:00 ` Alan Cox
2007-04-16 22:11 ` John Johansen
2007-04-12 9:08 ` [AppArmor 40/41] AppArmor: all the rest jjohansen
2007-04-12 10:32 ` Al Viro
2007-04-12 11:32 ` Al Viro
2007-04-12 9:08 ` [AppArmor 41/41] Add AppArmor LSM to security/Makefile jjohansen
2007-04-12 10:33 ` [AppArmor 00/41] AppArmor security module overview Shaya Potter
2007-04-12 13:50 ` Pavel Machek
2007-04-13 8:04 ` Rob Meijer
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20070412090849.465428000@suse.de \
--to=jjohansen@suse.de \
--cc=agruen@suse.de \
--cc=chrisw@sous-sol.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).