[RFC][PATCH 6/11] security: AppArmor - Userspace interface

From: Tony Jones
Date: Wed Apr 19 2006 - 13:57:34 EST


This patch implements the interface between the userspace policy loader
and the kernel module. It is called by the .load, .remove and .replace
file_operations hooks implemented in apparmorfs.c.

The code is reponsible for serializing data in a platform independant
manner from userspace and creating/activating the necessary apparmor
profiles.

Certain aspects are delegated to the sub matching module which implements
the aamatch_* functions.


Signed-off-by: Tony Jones <tonyj@xxxxxxx>

---
security/apparmor/module_interface.c | 840 +++++++++++++++++++++++++++++++++++
security/apparmor/module_interface.h | 37 +
2 files changed, 877 insertions(+)

--- /dev/null
+++ linux-2.6.17-rc1/security/apparmor/module_interface.c
@@ -0,0 +1,840 @@
+/*
+ * Copyright (C) 1998-2005 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"
+#include "module_interface.h"
+#include "match/match.h"
+
+/* aa_code defined in module_interface.h */
+
+const int aacode_datasize[] = { 1, 2, 4, 8, 2, 2, 4, 0, 0, 0, 0, 0, 0 };
+
+struct aa_taskreplace_data {
+ struct aaprofile *old_profile;
+ struct aaprofile *new_profile;
+};
+
+/* inlines must be forward of there use in newer version of gcc,
+ just forward declaring with a prototype won't work anymore */
+
+static inline void free_aa_entry(struct aa_entry *entry)
+{
+ if (entry) {
+ kfree(entry->filename);
+ aamatch_free(entry->extradata);
+ kfree(entry);
+ }
+}
+
+/**
+ * alloc_aa_entry - create new empty aa_entry
+ * This routine allocates, initializes, and returns a new aa_entry
+ * file entry structure. Structure is zeroed. Returns new structure on
+ * success, %NULL on failure.
+ */
+static inline struct aa_entry *alloc_aa_entry(void)
+{
+ struct aa_entry *entry;
+
+ AA_DEBUG("%s\n", __FUNCTION__);
+ entry = kzalloc(sizeof(struct aa_entry), GFP_KERNEL);
+ if (entry) {
+ int i;
+ INIT_LIST_HEAD(&entry->list);
+ for (i = 0; i <= POS_AA_FILE_MAX; i++) {
+ INIT_LIST_HEAD(&entry->listp[i]);
+ }
+ }
+ return entry;
+}
+
+/**
+ * free_aaprofile_rcu - rcu callback for free profiles
+ * @head: rcu_head struct of the profile whose reference is being put.
+ *
+ * the rcu callback routine, which delays the freeing of a profile when
+ * its last reference is put.
+ */
+static void free_aaprofile_rcu(struct rcu_head *head)
+{
+ struct aaprofile *p = container_of(head, struct aaprofile, rcu);
+ free_aaprofile(p);
+}
+
+/**
+ * task_remove - remove profile from a task's subdomain
+ * @sd: task's subdomain
+ *
+ * remove the active profile from a task's subdomain, switching the task
+ * to an unconfined state.
+ */
+static inline void task_remove(struct subdomain *sd)
+{
+ /* spin_lock(&sd_lock) held here */
+ AA_DEBUG("%s: removing profile from task %s(%d) profile %s active %s\n",
+ __FUNCTION__,
+ sd->task->comm,
+ sd->task->pid,
+ BASE_PROFILE(sd->active)->name,
+ sd->active->name);
+
+ aa_switch_unconfined(sd);
+}
+
+/** taskremove_iter - Iterator to unconfine subdomains which match cookie
+ * @sd: subdomain to consider for profile removal
+ * @cookie: pointer to the oldprofile which is being removed
+ *
+ * If the subdomain's active profile matches old_profile, then call
+ * task_remove() to remove the profile leaving the task (subdomain) unconfined.
+ */
+static int taskremove_iter(struct subdomain *sd, void *cookie)
+{
+ struct aaprofile *old_profile = (struct aaprofile *)cookie;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sd_lock, flags);
+
+ if (__aa_is_confined(sd) && BASE_PROFILE(sd->active) == old_profile) {
+ task_remove(sd);
+ }
+
+ spin_unlock_irqrestore(&sd_lock, flags);
+
+ return 0;
+}
+
+/** task_replace - replace subdomain's current profile with a new profile
+ * @sd: subdomain to replace the profile on
+ * @new: new profile
+ *
+ * Replace a task's (subdomain's) active profile with a new profile. If
+ * task was in a hat then the new profile will also be in the equivalent
+ * hat in the new profile if it exists. If it doesn't exist the
+ * task will be placed in the special null_profile state.
+ */
+static inline void task_replace(struct subdomain *sd, struct aaprofile *new)
+{
+ AA_DEBUG("%s: replacing profile for task %s(%d) "
+ "profile=%s (%p) active=%s (%p)\n",
+ __FUNCTION__,
+ sd->task->comm, sd->task->pid,
+ BASE_PROFILE(sd->active)->name, BASE_PROFILE(sd->active),
+ sd->active->name, sd->active);
+
+ if (!sd->active)
+ goto out;
+
+ if (IN_SUBPROFILE(sd->active)) {
+ struct aaprofile *nactive;
+
+ /* The old profile was in a hat, check to see if the new
+ * profile has an equivalent hat */
+ nactive = __aa_find_profile(sd->active->name, &new->sub);
+
+ if (!nactive)
+ nactive = get_aaprofile(new->null_profile);
+
+ aa_switch(sd, nactive);
+ put_aaprofile(nactive);
+ } else {
+ aa_switch(sd, new);
+ }
+
+ out:
+ return;
+}
+
+/** taskreplace_iter - Iterator to replace a subdomain's profile
+ * @sd: subdomain to consider for profile replacement
+ * @cookie: pointer to the old profile which is being replaced.
+ *
+ * If the subdomain's active profile matches old_profile call
+ * task_replace() to replace with the subdomain's active profile with
+ * the new profile.
+ */
+static int taskreplace_iter(struct subdomain *sd, void *cookie)
+{
+ struct aa_taskreplace_data *data = (struct aa_taskreplace_data *)cookie;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sd_lock, flags);
+
+ if (__aa_is_confined(sd) &&
+ BASE_PROFILE(sd->active) == data->old_profile)
+ task_replace(sd, data->new_profile);
+
+ spin_unlock_irqrestore(&sd_lock, flags);
+
+ return 0;
+}
+
+static inline int aa_inbounds(struct aa_ext *e, size_t size)
+{
+ return (e->pos + size <= e->end);
+}
+
+/**
+ * aaconvert - convert trailing values of serialized type codes
+ * @code: type code
+ * @dest: pointer to object to receive the converted value
+ * @src: pointer to value to convert
+ *
+ * for serialized type codes which have a trailing value, convert it
+ * and place it in @dest. If a code does not have a trailing value nop.
+ */
+static void aaconvert(enum aa_code code, void *dest, void *src)
+{
+ switch (code) {
+ case AA_U8:
+ *(u8 *)dest = *(u8 *) src;
+ break;
+ case AA_U16:
+ case AA_NAME:
+ case AA_DYN_STRING:
+ *(u16 *)dest = le16_to_cpu(get_unaligned((u16 *)src));
+ break;
+ case AA_U32:
+ case AA_STATIC_BLOB:
+ *(u32 *)dest = le32_to_cpu(get_unaligned((u32 *)src));
+ break;
+ case AA_U64:
+ *(u64 *)dest = le64_to_cpu(get_unaligned((u64 *)src));
+ break;
+ default:
+ /* nop - all other type codes do not have a trailing value */
+ ;
+ }
+}
+
+/**
+ * aa_is_X - check if the next element is of type X
+ * @e: serialized data extent information
+ * @code: type code
+ * @data: object located at @e->pos (of type @code) is written into @data
+ * if @data is non-null. if data is null it means skip this
+ * entry
+ * check to see if the next element in the serialized data stream is of type
+ * X and check that it is with in bounds, if so put the associated value in
+ * @data.
+ * return the size of bytes associated with the returned data
+ * for complex object like blob and string a pointer to the allocated
+ * data is returned in data, but the size of the blob or string is
+ * returned.
+ */
+static u32 aa_is_X(struct aa_ext *e, enum aa_code code, void *data)
+{
+ void *pos = e->pos;
+ int ret = 0;
+ if (!aa_inbounds(e, AA_CODE_BYTE + aacode_datasize[code]))
+ goto fail;
+ if (code != *(u8 *)e->pos)
+ goto out;
+ e->pos += AA_CODE_BYTE;
+ if (code == AA_NAME) {
+ u16 size;
+ /* name codes are followed by X bytes */
+ size = le16_to_cpu(get_unaligned((u16 *)e->pos));
+ if (!aa_inbounds(e, (size_t) size))
+ goto fail;
+ if (data)
+ *(u16 *)data = size;
+ e->pos += aacode_datasize[code];
+ ret = 1 + aacode_datasize[code];
+ } else if (code == AA_DYN_STRING) {
+ u16 size;
+ char *str;
+ /* strings codes are followed by X bytes */
+ size = le16_to_cpu(get_unaligned((u16 *)e->pos));
+ e->pos += aacode_datasize[code];
+ if (!aa_inbounds(e, (size_t) size))
+ goto fail;
+ if (data) {
+ * (char **)data = NULL;
+ str = kmalloc(size, GFP_KERNEL);
+ if (!str)
+ goto fail;
+ memcpy(str, e->pos, (size_t) size);
+ str[size-1] = '\0';
+ * (char **)data = str;
+ }
+ e->pos += size;
+ ret = size;
+ } else if (code == AA_STATIC_BLOB) {
+ u32 size;
+ /* blobs are followed by X bytes, that can be 2^32 */
+ size = le32_to_cpu(get_unaligned((u32 *)e->pos));
+ e->pos += aacode_datasize[code];
+ if (!aa_inbounds(e, (size_t) size))
+ goto fail;
+ if (data)
+ memcpy(data, e->pos, (size_t) size);
+ e->pos += size;
+ ret = size;
+ } else {
+ if (data)
+ aaconvert(code, data, e->pos);
+ e->pos += aacode_datasize[code];
+ ret = 1 + aacode_datasize[code];
+ }
+out:
+ return ret;
+fail:
+ e->pos = pos;
+ return 0;
+}
+
+/**
+ * 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
+ * @data: location to store deserialized data if match isX criteria
+ * @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 the code matches and name (if specified) matches then
+ * the packed data is unpacked into *data. (Note for strings this is the
+ * size, and the next data in the stream is the string data)
+ * returns %0 if either match failes
+ */
+static int aa_is_nameX(struct aa_ext *e, enum aa_code code, void *data,
+ const char *name)
+{
+ void *pos = e->pos;
+ u16 size;
+ u32 ret;
+ /* 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, &size)) {
+ /* if a name is specified it must match. otherwise skip tag */
+ if (name && ((strlen(name) != size-1) ||
+ strncmp(name, (char *)e->pos, (size_t)size-1)))
+ goto fail;
+ e->pos += size;
+ }
+ /* now check if data actually matches */
+ ret = aa_is_X(e, code, data);
+ if (!ret)
+ goto fail;
+ return ret;
+
+fail:
+ e->pos = pos;
+ return 0;
+}
+
+/* macro to wrap error case to make a block of reads look nicer */
+#define AA_READ_X(E, C, D, N) \
+ do { \
+ u32 __ret; \
+ __ret = aa_is_nameX((E), (C), (D), (N)); \
+ if (!__ret) \
+ goto fail; \
+ } while (0)
+
+/**
+ * aa_activate_net_entry - unpacked serialized net entries
+ * @e: serialized data extent information
+ *
+ * Ignore/skips net entries if they are present in the serialized data
+ * stream. Network confinement rules are currently unsupported but some
+ * user side tools can generate them so they are currently ignored.
+ */
+static inline int aa_activate_net_entry(struct aa_ext *e)
+{
+ AA_READ_X(e, AA_STRUCT, NULL, "ne");
+ AA_READ_X(e, AA_U32, NULL, NULL);
+ AA_READ_X(e, AA_U32, NULL, NULL);
+ AA_READ_X(e, AA_U32, NULL, NULL);
+ AA_READ_X(e, AA_U16, NULL, NULL);
+ AA_READ_X(e, AA_U16, NULL, NULL);
+ AA_READ_X(e, AA_U32, NULL, NULL);
+ AA_READ_X(e, AA_U32, NULL, NULL);
+ AA_READ_X(e, AA_U16, NULL, NULL);
+ AA_READ_X(e, AA_U16, NULL, NULL);
+ /* interface name is optional so just ignore return code */
+ aa_is_nameX(e, AA_DYN_STRING, NULL, NULL);
+ AA_READ_X(e, AA_STRUCTEND, NULL, NULL);
+
+ return 1;
+fail:
+ return 0;
+}
+
+/**
+ * aa_activate_file_entry - unpack serialized file entry
+ * @e: serialized data extent information
+ *
+ * unpack the information used for a file ACL entry.
+ */
+static inline struct aa_entry *aa_activate_file_entry(struct aa_ext *e)
+{
+ struct aa_entry *entry = NULL;
+
+ if (!(entry = alloc_aa_entry()))
+ goto fail;
+
+ AA_READ_X(e, AA_STRUCT, NULL, "fe");
+ AA_READ_X(e, AA_DYN_STRING, &entry->filename, NULL);
+ AA_READ_X(e, AA_U32, &entry->mode, "file.mode");
+ AA_READ_X(e, AA_U32, &entry->type, "file.pattern_type");
+
+ entry->extradata = aamatch_alloc(entry->type);
+ if (IS_ERR(entry->extradata)) {
+ entry->extradata = NULL;
+ goto fail;
+ }
+
+ if (entry->extradata &&
+ aamatch_serialize(entry->extradata, e, aa_is_nameX) != 0) {
+ goto fail;
+ }
+ AA_READ_X(e, AA_STRUCTEND, NULL, NULL);
+
+ switch (entry->type) {
+ case aa_entry_literal:
+ AA_DEBUG("%s: %s [no pattern] mode=0x%x\n",
+ __FUNCTION__,
+ entry->filename,
+ entry->mode);
+ break;
+ case aa_entry_tailglob:
+ AA_DEBUG("%s: %s [tailglob] mode=0x%x\n",
+ __FUNCTION__,
+ entry->filename,
+ entry->mode);
+ break;
+ case aa_entry_pattern:
+ AA_DEBUG("%s: %s mode=0x%x\n",
+ __FUNCTION__,
+ entry->filename,
+ entry->mode);
+ break;
+ default:
+ AA_WARN("%s: INVALID entry_match_type %d\n",
+ __FUNCTION__,
+ (int)entry->type);
+ goto fail;
+ }
+
+ return entry;
+
+fail:
+ aamatch_free(entry->extradata);
+ free_aa_entry(entry);
+ return NULL;
+}
+
+/**
+ * check_rule_and_add - check a file rule is valid and add to a profile
+ * @file_entry: file rule to add
+ * @profile: profile to add the rule to
+ * @message: error message returned if the addition failes.
+ *
+ * perform consistency check to ensure that a file rule entry is valid.
+ * If the rule is valid it is added to the profile.
+ */
+static inline int check_rule_and_add(struct aa_entry *file_entry,
+ struct aaprofile *profile,
+ const char **message)
+{
+ /* verify consistency of x, px, ix, ux for entry against
+ possible duplicates for this entry */
+ int mode = AA_EXEC_MODIFIER_MASK(file_entry->mode);
+ int i;
+
+ if (mode && !(AA_MAY_EXEC & file_entry->mode)) {
+ *message = "inconsistent rule, x modifiers without x";
+ goto out;
+ }
+
+ /* check that only 1 of the modifiers is set */
+ if (mode && (mode & (mode - 1))) {
+ *message = "inconsistent rule, multiple x modifiers";
+ goto out;
+ }
+
+ list_add(&file_entry->list, &profile->file_entry);
+ profile->num_file_entries++;
+
+ mode = file_entry->mode;
+
+ /* Handle partitioned lists
+ * Chain entries onto sublists based on individual
+ * permission bits. This allows more rapid searching.
+ */
+ for (i = 0; i <= POS_AA_FILE_MAX; i++) {
+ if (mode & (1 << i))
+ /* profile->file_entryp[i] initially set to
+ * NULL in alloc_aaprofile() */
+ list_add(&file_entry->listp[i],
+ &profile->file_entryp[i]);
+ }
+
+ return 1;
+
+out:
+ free_aa_entry(file_entry);
+ return 0;
+}
+
+#define AA_ENTRY_LIST(NAME) \
+ do { \
+ if (aa_is_nameX(e, AA_LIST, NULL, (NAME))) { \
+ rulename = ""; \
+ error_string = "Invalid file entry"; \
+ while (!aa_is_nameX(e, AA_LISTEND, NULL, NULL)) { \
+ struct aa_entry *file_entry; \
+ file_entry = aa_activate_file_entry(e); \
+ if (!file_entry) \
+ goto fail; \
+ if (!check_rule_and_add(file_entry, profile, \
+ &error_string)) { \
+ rulename = file_entry->filename; \
+ goto fail; \
+ } \
+ } \
+ } \
+ } while (0)
+
+/**
+ * aa_activate_profile - unpack a serialized profile
+ * @e: serialized data extent information
+ * @error: error code returned if unpacking fails
+ */
+static struct aaprofile *aa_activate_profile(struct aa_ext *e, ssize_t *error)
+{
+ struct aaprofile *profile = NULL;
+ const char *rulename = "";
+ const char *error_string = "Invalid Profile";
+
+ *error = -EPROTO;
+
+ profile = alloc_aaprofile();
+ if (!profile) {
+ error_string = "Could not allocate profile";
+ *error = -ENOMEM;
+ goto fail;
+ }
+
+ /* check that we have the right struct being passed */
+ AA_READ_X(e, AA_STRUCT, NULL, "profile");
+ AA_READ_X(e, AA_DYN_STRING, &profile->name, NULL);
+
+ error_string = "Invalid flags";
+ /* per profile debug flags (debug, complain, audit) */
+ AA_READ_X(e, AA_STRUCT, NULL, "flags");
+ AA_READ_X(e, AA_U32, &(profile->flags.debug), "profile.flags.debug");
+ AA_READ_X(e, AA_U32, &(profile->flags.complain),
+ "profile.flags.complain");
+ AA_READ_X(e, AA_U32, &(profile->flags.audit), "profile.flags.audit");
+ AA_READ_X(e, AA_STRUCTEND, NULL, NULL);
+
+ error_string = "Invalid capabilities";
+ AA_READ_X(e, AA_U32, &(profile->capabilities), "profile.capabilities");
+
+ /* get the file entries. */
+ AA_ENTRY_LIST("pgent"); /* pcre rules */
+ AA_ENTRY_LIST("sgent"); /* simple globs */
+ AA_ENTRY_LIST("fent"); /* regular file entries */
+
+ /* get the net entries */
+ if (aa_is_nameX(e, AA_LIST, NULL, "net")) {
+ error_string = "Invalid net entry";
+ while (!aa_is_nameX(e, AA_LISTEND, NULL, NULL)) {
+ if (!aa_activate_net_entry(e))
+ goto fail;
+ }
+ }
+ rulename = "";
+
+ /* get subprofiles */
+ if (aa_is_nameX(e, AA_LIST, NULL, "hats")) {
+ error_string = "Invalid profile hat";
+ while (!aa_is_nameX(e, AA_LISTEND, NULL, NULL)) {
+ struct aaprofile *subprofile;
+ subprofile = aa_activate_profile(e, error);
+ if (!subprofile)
+ goto fail;
+ subprofile->parent = profile;
+ list_add(&subprofile->list, &profile->sub);
+ }
+ }
+
+ error_string = "Invalid end of profile";
+ AA_READ_X(e, AA_STRUCTEND, NULL, NULL);
+
+ return profile;
+
+fail:
+ AA_WARN("%s: %s %s in profile %s\n", INTERFACE_ID, rulename,
+ error_string, profile && profile->name ? profile->name
+ : "unknown");
+
+ if (profile) {
+ free_aaprofile(profile);
+ profile = NULL;
+ }
+
+ return NULL;
+}
+
+/**
+ * aa_activate_top_profile - unpack a serialized base profile
+ * @e: serialized data extent information
+ * @error: error code returned if unpacking fails
+ *
+ * check interface version unpack a profile and all its hats and patch
+ * in any extra information that the profile needs.
+ */
+static void *aa_activate_top_profile(struct aa_ext *e, ssize_t *error)
+{
+ struct aaprofile *profile = NULL;
+
+ /* get the interface version */
+ if (!aa_is_nameX(e, AA_U32, &e->version, "version")) {
+ AA_WARN("%s: version missing\n", INTERFACE_ID);
+ *error = -EPROTONOSUPPORT;
+ goto fail;
+ }
+
+ /* check that the interface version is currently supported */
+ if (e->version != 2) {
+ AA_WARN("%s: unsupported interface version (%d)\n",
+ INTERFACE_ID, e->version);
+ *error = -EPROTONOSUPPORT;
+ goto fail;
+ }
+
+ profile = aa_activate_profile(e, error);
+ if (!profile)
+ goto fail;
+
+ if (!list_empty(&profile->sub) || profile->flags.complain) {
+ if (attach_nullprofile(profile))
+ goto fail;
+ }
+ return profile;
+
+fail:
+ free_aaprofile(profile);
+ return NULL;
+}
+
+/**
+ * aa_file_prof_add - add a new profile to the profile list
+ * @data: serialized data stream
+ * @size: size of the serialized data stream
+ *
+ * unpack and add a profile to the profile list. Return %0 or error
+ */
+ssize_t aa_file_prof_add(void *data, size_t size)
+{
+ struct aaprofile *profile = NULL;
+
+ struct aa_ext e = {
+ .start = data,
+ .end = data + size,
+ .pos = data
+ };
+ ssize_t error;
+
+ profile = aa_activate_top_profile(&e, &error);
+ if (!profile) {
+ AA_DEBUG("couldn't activate profile\n");
+ goto out;
+ }
+
+ /* aa_activate_top_profile allocates profile with initial 1 count
+ * aa_profilelist_add transfers that ref to profile list without
+ * further incrementing
+ */
+ if (aa_profilelist_add(profile)) {
+ error = size;
+ } else {
+ AA_WARN("trying to add profile (%s) that already exists.\n",
+ profile->name);
+ put_aaprofile(profile);
+ error = -EEXIST;
+ }
+
+out:
+ return error;
+}
+
+/**
+ * aa_file_prof_repl - 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 subdomain. If the profile does not exist on the profile list
+ * it is added. Return %0 or error.
+ */
+ssize_t aa_file_prof_repl(void *udata, size_t size)
+{
+ struct aa_taskreplace_data data;
+ struct aa_ext e = {
+ .start = udata,
+ .end = udata + size,
+ .pos = udata
+ };
+
+ ssize_t error;
+
+ data.new_profile = aa_activate_top_profile(&e, &error);
+ if (!data.new_profile) {
+ AA_DEBUG("couldn't activate profile\n");
+ goto out;
+ }
+
+ /* Refcount on data.new_profile is 1 (aa_activate_top_profile).
+ *
+ * This reference will be inherited by aa_profilelist_replace for it's
+ * profile list reference but this isn't sufficient.
+ *
+ * Another replace (*for-same-profile*) may race us here.
+ * Task A calls aa_profilelist_replace(new_profile) and is interrupted.
+ * Task B old_profile = aa_profilelist_replace() will return task A's
+ * new_profile with the count of 1. If task B proceeeds to put this
+ * profile it will dissapear from under task A.
+ *
+ * Grab extra reference on new_profile to prevent this
+ */
+
+ get_aaprofile(data.new_profile);
+
+ data.old_profile = aa_profilelist_replace(data.new_profile);
+
+ /* If there was an old profile, find all currently executing tasks
+ * using this profile and replace the old profile with the new.
+ */
+ if (data.old_profile) {
+ AA_DEBUG("%s: try to replace profile (%p)%s\n",
+ __FUNCTION__,
+ data.old_profile,
+ data.old_profile->name);
+
+ aa_subdomainlist_iterate(taskreplace_iter, (void *)&data);
+
+ /* it's off global list, and we are done replacing */
+ put_aaprofile(data.old_profile);
+ }
+
+ /* release extra reference obtained above (race) */
+ put_aaprofile(data.new_profile);
+
+ error = size;
+
+out:
+ return error;
+}
+
+/**
+ * aa_file_prof_remove - 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 subdomain references
+ * to said profile. Return %0 on success, else error.
+ */
+ssize_t aa_file_prof_remove(const char *name, size_t size)
+{
+ struct aaprofile *old_profile;
+
+ /* if the old profile exists it will be removed from the list and
+ * a reference is returned.
+ */
+ old_profile = aa_profilelist_remove(name);
+
+ if (old_profile) {
+ /* remove profile from any tasks using it */
+ aa_subdomainlist_iterate(taskremove_iter, (void *)old_profile);
+
+ /* drop reference obtained by aa_profilelist_remove */
+ put_aaprofile(old_profile);
+ } else {
+ AA_WARN("%s: trying to remove profile (%s) that "
+ "doesn't exist - skipping.\n", __FUNCTION__, name);
+ return -ENOENT;
+ }
+
+ return size;
+}
+
+/**
+ * free_aaprofile_kref - free aaprofile by kref (called by put_aaprofile)
+ * @kr: kref callback for freeing of a profile
+ */
+void free_aaprofile_kref(struct kref *kr)
+{
+ struct aaprofile *p=container_of(kr, struct aaprofile, count);
+
+ call_rcu(&p->rcu, free_aaprofile_rcu);
+}
+
+/**
+ * free_aaprofile - free aaprofile structure
+ * @profile: the profile to free
+ *
+ * free a profile, its file entries hats and null_profile. All references
+ * to the profile, its hats and null_profile must have been put.
+ * If the profile was referenced by a subdomain free_aaprofile should be
+ * called from an rcu callback routine.
+ */
+void free_aaprofile(struct aaprofile *profile)
+{
+ struct aa_entry *ent, *tmp;
+ struct aaprofile *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();
+ }
+
+ list_for_each_entry_safe(ent, tmp, &profile->file_entry, list) {
+ if (ent->filename)
+ AA_DEBUG("freeing aa_entry: %p %s\n",
+ ent->filename, ent->filename);
+ list_del_init(&ent->list);
+ free_aa_entry(ent);
+ }
+
+ /* use free_aaprofile instead of put_aaprofile 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_aaprofile(profile->null_profile);
+ list_for_each_entry_safe(p, ptmp, &profile->sub, list) {
+ list_del_init(&p->list);
+ p->parent = NULL;
+ put_aaprofile(p);
+ }
+
+ if (profile->name) {
+ AA_DEBUG("%s: %s\n", __FUNCTION__, profile->name);
+ kfree(profile->name);
+ }
+
+ kfree(profile);
+}
--- /dev/null
+++ linux-2.6.17-rc1/security/apparmor/module_interface.h
@@ -0,0 +1,37 @@
+#ifndef __MODULEINTERFACE_H
+#define __MODULEINTERFACE_H
+
+/* Codes of the types of basic structures that are understood */
+#define AA_CODE_BYTE (sizeof(u8))
+#define INTERFACE_ID "INTERFACE"
+
+#define SUBDOMAIN_INTERFACE_VERSION 2
+
+enum aa_code {
+ AA_U8,
+ AA_U16,
+ AA_U32,
+ AA_U64,
+ AA_NAME, /* same as string except it is items name */
+ AA_DYN_STRING,
+ AA_STATIC_BLOB,
+ AA_STRUCT,
+ AA_STRUCTEND,
+ AA_LIST,
+ AA_LISTEND,
+ AA_OFFSET,
+ AA_BAD
+};
+
+/* aa_ext tracks the kernel buffer and read position in it. The interface
+ * data is copied into a kernel buffer in apparmorfs and then handed off to
+ * the activate routines.
+ */
+struct aa_ext {
+ void *start;
+ void *end;
+ void *pos; /* pointer to current position in the buffer */
+ u32 version;
+};
+
+#endif /* __MODULEINTERFACE_H */
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/