All of lore.kernel.org
 help / color / mirror / Atom feed
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);
+	}
+}

-- 

  parent reply	other threads:[~2007-04-12  9:47 UTC|newest]

Thread overview: 190+ 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  8:34                     ` Cliffe
2007-05-27 13:07                     ` Kyle Moffett
2007-05-27 16:12                     ` Casey Schaufler
2007-05-27 16:12                       ` Casey Schaufler
     [not found]                       ` <465AE46B.4090109@iinet.net.au>
2007-05-28 22:29                         ` Crispin Cowan
2007-05-29 10:46                           ` [AppArmor 01/41] Pass struct vfsmount to the inode_create LSMhook Tetsuo Handa
2007-05-29 15:52                             ` Casey Schaufler
2007-05-29 19:58                               ` James Morris
2007-05-29 17:07                             ` Andreas Gruenbacher
2007-05-29 20:47                               ` Tetsuo Handa
2007-05-29 22:10                                 ` Andreas Gruenbacher
2007-05-30 11:38                                   ` Tetsuo Handa
2007-05-29 14:45                           ` [AppArmor 01/41] Pass struct vfsmount to the inode_create LSM hook Pavel Machek
2007-05-29 23:25                             ` david
2007-05-29 23:30                               ` Pavel Machek
2007-05-30  1:46                                 ` David Wagner
2007-05-24 14:47                                   ` Pavel Machek
2007-06-01 17:44                                     ` Valdis.Kletnieks
2007-06-01 18:00                                       ` david
2007-06-04 11:07                                         ` Pavel Machek
2007-06-02  4:30                                       ` David Wagner
2007-06-02  7:40                                         ` Valdis.Kletnieks
2007-06-02 14:27                                           ` david
2007-06-02 19:14                                             ` Valdis.Kletnieks
2007-06-02 19:51                                               ` david
2007-06-03  4:52                                                 ` Valdis.Kletnieks
2007-06-08 20:24                                         ` Pavel Machek
2007-06-08 21:54                                           ` Casey Schaufler
2007-05-30  5:42                             ` Crispin Cowan
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-20  9:30               ` 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-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-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-17  6:30           ` 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-12 21:13   ` David Wagner
2007-04-16  7:46     ` Pavel Machek
2007-04-16 18:24       ` David Wagner
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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.