lkml.org 
[lkml]   [2007]   [May]   [14]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[AppArmor 38/45] AppArmor: Module and LSM hooks
    Module parameters, LSM hooks, initialization and teardown.

    Signed-off-by: John Johansen <jjohansen@suse.de>
    Signed-off-by: Andreas Gruenbacher <agruen@suse.de>

    Index: b/security/apparmor/lsm.c
    ===================================================================
    --- /dev/null
    +++ b/security/apparmor/lsm.c
    @@ -0,0 +1,790 @@
    +/*
    + * 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 LSM interface
    + */
    +
    +#include <linux/security.h>
    +#include <linux/module.h>
    +#include <linux/mm.h>
    +#include <linux/mman.h>
    +#include <linux/mount.h>
    +#include <linux/namei.h>
    +#include <linux/ctype.h>
    +#include <linux/sysctl.h>
    +
    +#include "apparmor.h"
    +#include "inline.h"
    +
    +static int param_set_aabool(const char *val, struct kernel_param *kp);
    +static int param_get_aabool(char *buffer, struct kernel_param *kp);
    +#define param_check_aabool(name, p) __param_check(name, p, int)
    +
    +static int param_set_aauint(const char *val, struct kernel_param *kp);
    +static int param_get_aauint(char *buffer, struct kernel_param *kp);
    +#define param_check_aauint(name, p) __param_check(name, p, int)
    +
    +/* Flag values, also controllable via /sys/module/apparmor/parameters
    + * We define special types as we want to do additional mediation.
    + *
    + * Complain mode -- in complain mode access failures result in auditing only
    + * and task is allowed access. audit events are processed by userspace to
    + * generate policy. Default is 'enforce' (0).
    + * Value is also togglable per profile and referenced when global value is
    + * enforce.
    + */
    +int apparmor_complain = 0;
    +module_param_named(complain, apparmor_complain, aabool, S_IRUSR | S_IWUSR);
    +MODULE_PARM_DESC(apparmor_complain, "Toggle AppArmor complain mode");
    +
    +/* Debug mode */
    +int apparmor_debug = 0;
    +module_param_named(debug, apparmor_debug, aabool, S_IRUSR | S_IWUSR);
    +MODULE_PARM_DESC(apparmor_debug, "Toggle AppArmor debug mode");
    +
    +/* Audit mode */
    +int apparmor_audit = 0;
    +module_param_named(audit, apparmor_audit, aabool, S_IRUSR | S_IWUSR);
    +MODULE_PARM_DESC(apparmor_audit, "Toggle AppArmor audit mode");
    +
    +/* Syscall logging mode */
    +int apparmor_logsyscall = 0;
    +module_param_named(logsyscall, apparmor_logsyscall, aabool, S_IRUSR | S_IWUSR);
    +MODULE_PARM_DESC(apparmor_logsyscall, "Toggle AppArmor logsyscall mode");
    +
    +/* Maximum pathname length before accesses will start getting rejected */
    +unsigned int apparmor_path_max = 2 * PATH_MAX;
    +module_param_named(path_max, apparmor_path_max, aauint, S_IRUSR | S_IWUSR);
    +MODULE_PARM_DESC(apparmor_path_max, "Maximum pathname length allowed");
    +
    +static int param_set_aabool(const char *val, struct kernel_param *kp)
    +{
    + if (aa_task_context(current))
    + return -EPERM;
    + return param_set_bool(val, kp);
    +}
    +
    +static int param_get_aabool(char *buffer, struct kernel_param *kp)
    +{
    + if (aa_task_context(current))
    + return -EPERM;
    + return param_get_bool(buffer, kp);
    +}
    +
    +static int param_set_aauint(const char *val, struct kernel_param *kp)
    +{
    + if (aa_task_context(current))
    + return -EPERM;
    + return param_set_uint(val, kp);
    +}
    +
    +static int param_get_aauint(char *buffer, struct kernel_param *kp)
    +{
    + if (aa_task_context(current))
    + return -EPERM;
    + return param_get_uint(buffer, kp);
    +}
    +
    +static int aa_reject_syscall(struct task_struct *task, gfp_t flags,
    + const char *name)
    +{
    + struct aa_profile *profile = aa_get_profile(task);
    + int error = 0;
    +
    + if (profile) {
    + error = aa_audit_syscallreject(profile, flags, name);
    + aa_put_profile(profile);
    + }
    +
    + return error;
    +}
    +
    +static int apparmor_ptrace(struct task_struct *parent,
    + struct task_struct *child)
    +{
    + struct aa_task_context *cxt;
    + struct aa_task_context *child_cxt;
    + struct aa_profile *child_profile;
    + int error = 0;
    +
    + /*
    + * parent can ptrace child when
    + * - parent is unconfined
    + * - parent & child are in the same namespace &&
    + * - parent is in complain mode
    + * - parent and child are confined by the same profile
    + * - parent profile has CAP_SYS_PTRACE
    + */
    +
    + rcu_read_lock();
    + cxt = aa_task_context(parent);
    + child_cxt = aa_task_context(child);
    + child_profile = child_cxt ? child_cxt->profile : NULL;
    + if (cxt && (parent->nsproxy != child->nsproxy)) {
    + aa_audit_message(NULL, GFP_ATOMIC, "REJECTING ptrace across "
    + "namespace of %d by %d",
    + parent->pid, child->pid);
    + error = -EPERM;
    + } else {
    + error = aa_may_ptrace(cxt, child_profile);
    + if (cxt && PROFILE_COMPLAIN(cxt->profile)) {
    + aa_audit_message(cxt->profile, GFP_ATOMIC,
    + "LOGPROF-HINT ptrace pid=%d child=%d "
    + "(%d profile %s active %s)",
    + current->pid, child->pid, current->pid,
    + cxt->profile->parent->name,
    + cxt->profile->name);
    + }
    + }
    + rcu_read_unlock();
    +
    + return error;
    +}
    +
    +static int apparmor_capable(struct task_struct *task, int cap)
    +{
    + int error;
    +
    + /* cap_capable returns 0 on success, else -EPERM */
    + error = cap_capable(task, cap);
    +
    + if (!error) {
    + struct aa_task_context *cxt;
    +
    + rcu_read_lock();
    + cxt = aa_task_context(task);
    + if (cxt)
    + error = aa_capability(cxt, cap);
    + rcu_read_unlock();
    + }
    +
    + return error;
    +}
    +
    +static int apparmor_sysctl(struct ctl_table *table, int op)
    +{
    + struct aa_profile *profile = aa_get_profile(current);
    + int error = 0;
    +
    + if (profile) {
    + char *buffer, *name;
    + int mask;
    +
    + mask = 0;
    + if (op & 4)
    + mask |= MAY_READ;
    + if (op & 2)
    + mask |= MAY_WRITE;
    +
    + error = -ENOMEM;
    + buffer = (char*)__get_free_page(GFP_KERNEL);
    + if (!buffer)
    + goto out;
    + name = sysctl_pathname(table, buffer, PAGE_SIZE);
    + if (name && name - buffer >= 5) {
    + name -= 5;
    + memcpy(name, "/proc", 5);
    + error = aa_perm_path(profile, name, mask);
    + }
    + free_page((unsigned long)buffer);
    + }
    +
    +out:
    + return error;
    +}
    +
    +static int apparmor_bprm_set_security(struct linux_binprm *bprm)
    +{
    + /* handle capability bits with setuid, etc */
    + cap_bprm_set_security(bprm);
    + /* already set based on script name */
    + if (bprm->sh_bang)
    + return 0;
    + return aa_register(bprm);
    +}
    +
    +static int apparmor_bprm_secureexec(struct linux_binprm *bprm)
    +{
    + int ret = cap_bprm_secureexec(bprm);
    +
    + if (!ret && (unsigned long)bprm->security & AA_SECURE_EXEC_NEEDED) {
    + AA_DEBUG("%s: secureexec required for %s\n",
    + __FUNCTION__, bprm->filename);
    + ret = 1;
    + }
    +
    + return ret;
    +}
    +
    +static int apparmor_sb_mount(char *dev_name, struct nameidata *nd, char *type,
    + unsigned long flags, void *data)
    +{
    + return aa_reject_syscall(current, GFP_KERNEL, "mount");
    +}
    +
    +static int apparmor_umount(struct vfsmount *mnt, int flags)
    +{
    + return aa_reject_syscall(current, GFP_KERNEL, "umount");
    +}
    +
    +static int apparmor_inode_mkdir(struct inode *dir, struct dentry *dentry,
    + struct vfsmount *mnt, int mask)
    +{
    + struct aa_profile *profile;
    + int error = 0;
    +
    + if (!mnt || !mediated_filesystem(dir))
    + goto out;
    +
    + profile = aa_get_profile(current);
    +
    + if (profile)
    + error = aa_perm_dir(profile, dentry, mnt, "mkdir", MAY_WRITE);
    +
    + aa_put_profile(profile);
    +
    +out:
    + return error;
    +}
    +
    +static int apparmor_inode_rmdir(struct inode *dir, struct dentry *dentry,
    + struct vfsmount *mnt)
    +{
    + struct aa_profile *profile;
    + int error = 0;
    +
    + if (!mnt || !mediated_filesystem(dir))
    + goto out;
    +
    + profile = aa_get_profile(current);
    +
    + if (profile)
    + error = aa_perm_dir(profile, dentry, mnt, "rmdir", MAY_WRITE);
    +
    + aa_put_profile(profile);
    +
    +out:
    + return error;
    +}
    +
    +static int aa_permission(struct inode *inode, struct dentry *dentry,
    + struct vfsmount *mnt, int mask, int check)
    +{
    + int error = 0;
    +
    + if (mnt && mediated_filesystem(inode)) {
    + struct aa_profile *profile;
    +
    + profile = aa_get_profile(current);
    + if (profile)
    + error = aa_perm(profile, dentry, mnt, mask, check);
    + aa_put_profile(profile);
    + }
    + return error;
    +}
    +
    +static int apparmor_inode_create(struct inode *dir, struct dentry *dentry,
    + struct vfsmount *mnt, int mask)
    +{
    + return aa_permission(dir, dentry, mnt, MAY_WRITE, 0);
    +}
    +
    +static int apparmor_inode_link(struct dentry *old_dentry,
    + struct vfsmount *old_mnt, struct inode *dir,
    + struct dentry *new_dentry,
    + struct vfsmount *new_mnt)
    +{
    + int error = 0;
    + struct aa_profile *profile;
    +
    + if (!old_mnt || !new_mnt || !mediated_filesystem(dir))
    + goto out;
    +
    + profile = aa_get_profile(current);
    +
    + if (profile)
    + error = aa_link(profile, new_dentry, new_mnt,
    + old_dentry, old_mnt);
    +
    + aa_put_profile(profile);
    +
    +out:
    + return error;
    +}
    +
    +static int apparmor_inode_unlink(struct inode *dir, struct dentry *dentry,
    + struct vfsmount *mnt)
    +{
    + int check = 0;
    +
    + if (S_ISDIR(dentry->d_inode->i_mode))
    + check |= AA_CHECK_DIR;
    + return aa_permission(dir, dentry, mnt, MAY_WRITE, check);
    +}
    +
    +static int apparmor_inode_symlink(struct inode *dir, struct dentry *dentry,
    + struct vfsmount *mnt, const char *old_name)
    +{
    + return aa_permission(dir, dentry, mnt, MAY_WRITE, 0);
    +}
    +
    +static int apparmor_inode_mknod(struct inode *dir, struct dentry *dentry,
    + struct vfsmount *mnt, int mode, dev_t dev)
    +{
    + return aa_permission(dir, dentry, mnt, MAY_WRITE, 0);
    +}
    +
    +static int apparmor_inode_rename(struct inode *old_dir,
    + struct dentry *old_dentry,
    + struct vfsmount *old_mnt,
    + struct inode *new_dir,
    + struct dentry *new_dentry,
    + struct vfsmount *new_mnt)
    +{
    + struct aa_profile *profile;
    + int error = 0;
    +
    + if ((!old_mnt && !new_mnt) || !mediated_filesystem(old_dir))
    + goto out;
    +
    + profile = aa_get_profile(current);
    +
    + if (profile) {
    + struct inode *inode = old_dentry->d_inode;
    + int check = 0;
    +
    + if (inode && S_ISDIR(inode->i_mode))
    + check |= AA_CHECK_DIR;
    + if (old_mnt)
    + error = aa_perm(profile, old_dentry, old_mnt,
    + MAY_READ | MAY_WRITE, check);
    +
    + if (!error && new_mnt) {
    + error = aa_perm(profile, new_dentry, new_mnt,
    + MAY_WRITE, check);
    + }
    + }
    +
    + aa_put_profile(profile);
    +
    +out:
    + return error;
    +}
    +
    +static int apparmor_inode_permission(struct inode *inode, int mask,
    + struct nameidata *nd)
    +{
    + int check = 0;
    +
    + if (!nd || nd->flags & (LOOKUP_PARENT | LOOKUP_CONTINUE))
    + return 0;
    + mask &= (MAY_READ | MAY_WRITE | MAY_EXEC);
    + if (S_ISDIR(inode->i_mode)) {
    + check |= AA_CHECK_DIR;
    + /* allow traverse accesses to directories */
    + mask &= ~MAY_EXEC;
    + }
    + return aa_permission(inode, nd->dentry, nd->mnt, mask, check);
    +}
    +
    +static int apparmor_inode_setattr(struct dentry *dentry, struct vfsmount *mnt,
    + struct iattr *iattr)
    +{
    + int error = 0;
    +
    + if (!mnt)
    + goto out;
    +
    + if (mediated_filesystem(dentry->d_inode)) {
    + struct aa_profile *profile;
    +
    + profile = aa_get_profile(current);
    + /*
    + * Mediate any attempt to change attributes of a file
    + * (chmod, chown, chgrp, etc)
    + */
    + if (profile)
    + error = aa_attr(profile, dentry, mnt, iattr);
    +
    + aa_put_profile(profile);
    + }
    +
    +out:
    + return error;
    +}
    +
    +static int aa_xattr_permission(struct dentry *dentry, struct vfsmount *mnt,
    + const char *operation, int mask,
    + struct file *file)
    +{
    + int error = 0;
    +
    + if (mnt && mediated_filesystem(dentry->d_inode)) {
    + struct aa_profile *profile = aa_get_profile(current);
    + int check = file ? AA_CHECK_FD : 0;
    +
    + if (profile)
    + error = aa_perm_xattr(profile, dentry, mnt, operation,
    + mask, check);
    + aa_put_profile(profile);
    + }
    +
    + return error;
    +}
    +
    +static int apparmor_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt,
    + char *name, void *value, size_t size,
    + int flags, struct file *file)
    +{
    + return aa_xattr_permission(dentry, mnt, "xattr set", MAY_WRITE, file);
    +}
    +
    +static int apparmor_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt,
    + char *name, struct file *file)
    +{
    + return aa_xattr_permission(dentry, mnt, "xattr get", MAY_READ, file);
    +}
    +
    +static int apparmor_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt,
    + struct file *file)
    +{
    + return aa_xattr_permission(dentry, mnt, "xattr list", MAY_READ, file);
    +}
    +
    +static int apparmor_inode_removexattr(struct dentry *dentry,
    + struct vfsmount *mnt, char *name,
    + struct file *file)
    +{
    + return aa_xattr_permission(dentry, mnt, "xattr remove", MAY_WRITE,
    + file);
    +}
    +
    +static int apparmor_file_permission(struct file *file, int mask)
    +{
    + struct aa_profile *profile;
    + struct aa_profile *file_profile = (struct aa_profile*)file->f_security;
    + int error = 0;
    +
    + if (!file_profile)
    + goto out;
    +
    + /*
    + * If this file was opened under a different profile, we
    + * revalidate the access against the current profile.
    + */
    + profile = aa_get_profile(current);
    + if (profile && file_profile != profile) {
    + struct dentry *dentry = file->f_dentry;
    + struct vfsmount *mnt = file->f_vfsmnt;
    + struct inode *inode = dentry->d_inode;
    + int check = AA_CHECK_FD;
    +
    + /*
    + * FIXME: We should remember which profiles we revalidated
    + * against.
    + */
    + if (S_ISDIR(inode->i_mode))
    + check |= AA_CHECK_DIR;
    + mask &= (MAY_READ | MAY_WRITE | MAY_EXEC);
    + error = aa_permission(inode, dentry, mnt, mask, check);
    + }
    + aa_put_profile(profile);
    +
    +out:
    + return error;
    +}
    +
    +static int apparmor_file_alloc_security(struct file *file)
    +{
    + struct aa_profile *profile;
    +
    + profile = aa_get_profile(current);
    + if (profile)
    + file->f_security = profile;
    +
    + return 0;
    +}
    +
    +static void apparmor_file_free_security(struct file *file)
    +{
    + struct aa_profile *file_profile = (struct aa_profile*)file->f_security;
    +
    + aa_put_profile(file_profile);
    +}
    +
    +static inline int aa_mmap(struct file *file, unsigned long prot,
    + unsigned long flags)
    +{
    + struct dentry *dentry;
    + int mask = 0;
    +
    + if (!file || !file->f_security)
    + return 0;
    +
    + if (prot & PROT_READ)
    + mask |= MAY_READ;
    + /* Private mappings don't require write perms since they don't
    + * write back to the files */
    + if ((prot & PROT_WRITE) && !(flags & MAP_PRIVATE))
    + mask |= MAY_WRITE;
    + if (prot & PROT_EXEC)
    + mask |= AA_EXEC_MMAP;
    +
    + dentry = file->f_dentry;
    + return aa_permission(dentry->d_inode, dentry, file->f_vfsmnt, mask,
    + AA_CHECK_FD);
    +}
    +
    +static int apparmor_file_mmap(struct file *file, unsigned long reqprot,
    + unsigned long prot, unsigned long flags)
    +{
    + return aa_mmap(file, prot, flags);
    +}
    +
    +static int apparmor_file_mprotect(struct vm_area_struct *vma,
    + unsigned long reqprot, unsigned long prot)
    +{
    + return aa_mmap(vma->vm_file, prot,
    + !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0);
    +}
    +
    +static int apparmor_task_alloc_security(struct task_struct *task)
    +{
    + return aa_clone(task);
    +}
    +
    +/*
    + * Called from IRQ context from RCU callback.
    + */
    +static void apparmor_task_free_security(struct task_struct *task)
    +{
    + aa_release(task);
    +}
    +
    +static int apparmor_getprocattr(struct task_struct *task, char *name,
    + char **value)
    +{
    + unsigned len;
    + int error;
    + struct aa_profile *profile;
    +
    + /* AppArmor only supports the "current" process attribute */
    + if (strcmp(name, "current") != 0)
    + return -EINVAL;
    +
    + /* must be task querying itself or admin */
    + if (current != task && !capable(CAP_SYS_ADMIN))
    + return -EPERM;
    +
    + profile = aa_get_profile(task);
    + error = aa_getprocattr(profile, value, &len);
    + aa_put_profile(profile);
    + if (!error)
    + error = len;
    +
    + return error;
    +}
    +
    +static int apparmor_setprocattr(struct task_struct *task, char *name,
    + void *value, size_t size)
    +{
    + char *command, *args;
    + int error;
    +
    + if (strcmp(name, "current") != 0 || size == 0 || size >= PAGE_SIZE)
    + return -EINVAL;
    + args = value;
    + args[size] = '\0';
    + args = strstrip(args);
    + command = strsep(&args, " ");
    + if (!args)
    + return -EINVAL;
    + while (isspace(*args))
    + args++;
    + if (!*args)
    + return -EINVAL;
    +
    + if (strcmp(command, "changehat") == 0) {
    + if (current != task)
    + return -EACCES;
    + error = aa_setprocattr_changehat(args);
    + } else if (strcmp(command, "setprofile")) {
    + struct aa_profile *profile;
    +
    + /* Only an unconfined process with admin capabilities
    + * may change the profile of another task.
    + */
    +
    + if (!capable(CAP_SYS_ADMIN))
    + return -EACCES;
    +
    + profile = aa_get_profile(current);
    + if (profile) {
    + aa_put_profile(profile);
    + aa_audit_message(NULL, GFP_KERNEL, "Attempt by "
    + "confined task %d [user %d] to "
    + "assign profile to task %d",
    + current->pid, current->uid,
    + task->pid);
    + return -EACCES;
    + }
    + error = aa_setprocattr_setprofile(task, args);
    + } else {
    + AA_ERROR("Unknown setprocattr command '%.*s' "
    + "by task %d [user %d] for task %d",
    + size < 16 ? (int)size : 16,
    + command,
    + current->pid,
    + current->uid,
    + task->pid);
    + error = -EINVAL;
    + }
    +
    + if (!error)
    + error = size;
    + return error;
    +}
    +
    +struct security_operations apparmor_ops = {
    + .ptrace = apparmor_ptrace,
    + .capget = cap_capget,
    + .capset_check = cap_capset_check,
    + .capset_set = cap_capset_set,
    + .sysctl = apparmor_sysctl,
    + .capable = apparmor_capable,
    + .syslog = cap_syslog,
    +
    + .netlink_send = cap_netlink_send,
    + .netlink_recv = cap_netlink_recv,
    +
    + .bprm_apply_creds = cap_bprm_apply_creds,
    + .bprm_set_security = apparmor_bprm_set_security,
    + .bprm_secureexec = apparmor_bprm_secureexec,
    +
    + .sb_mount = apparmor_sb_mount,
    + .sb_umount = apparmor_umount,
    +
    + .inode_mkdir = apparmor_inode_mkdir,
    + .inode_rmdir = apparmor_inode_rmdir,
    + .inode_create = apparmor_inode_create,
    + .inode_link = apparmor_inode_link,
    + .inode_unlink = apparmor_inode_unlink,
    + .inode_symlink = apparmor_inode_symlink,
    + .inode_mknod = apparmor_inode_mknod,
    + .inode_rename = apparmor_inode_rename,
    + .inode_permission = apparmor_inode_permission,
    + .inode_setattr = apparmor_inode_setattr,
    + .inode_setxattr = apparmor_inode_setxattr,
    + .inode_getxattr = apparmor_inode_getxattr,
    + .inode_listxattr = apparmor_inode_listxattr,
    + .inode_removexattr = apparmor_inode_removexattr,
    + .file_permission = apparmor_file_permission,
    + .file_alloc_security = apparmor_file_alloc_security,
    + .file_free_security = apparmor_file_free_security,
    + .file_mmap = apparmor_file_mmap,
    + .file_mprotect = apparmor_file_mprotect,
    +
    + .task_alloc_security = apparmor_task_alloc_security,
    + .task_free_security = apparmor_task_free_security,
    + .task_post_setuid = cap_task_post_setuid,
    + .task_reparent_to_init = cap_task_reparent_to_init,
    +
    + .getprocattr = apparmor_getprocattr,
    + .setprocattr = apparmor_setprocattr,
    +};
    +
    +static void info_message(const char *str)
    +{
    + printk(KERN_INFO "AppArmor: %s", str);
    + aa_audit_message(NULL, GFP_KERNEL, "%s", str);
    +}
    +
    +static int __init apparmor_init(void)
    +{
    + int error;
    +
    + if ((error = create_apparmorfs())) {
    + AA_ERROR("Unable to activate AppArmor filesystem\n");
    + goto createfs_out;
    + }
    +
    + if ((error = alloc_null_complain_profile())){
    + AA_ERROR("Unable to allocate null complain profile\n");
    + goto alloc_out;
    + }
    +
    + if ((error = register_security(&apparmor_ops))) {
    + AA_ERROR("Unable to load AppArmor\n");
    + goto register_security_out;
    + }
    +
    + if (apparmor_complain)
    + info_message("AppArmor initialized: complainmode enabled");
    + else
    + info_message("AppArmor initialized");
    +
    + return error;
    +
    +register_security_out:
    + free_null_complain_profile();
    +
    +alloc_out:
    + destroy_apparmorfs();
    +
    +createfs_out:
    + return error;
    +
    +}
    +
    +static void __exit apparmor_exit(void)
    +{
    + /* Remove and release all the profiles on the profile list. */
    + mutex_lock(&aa_interface_lock);
    + write_lock(&profile_list_lock);
    + while (!list_empty(&profile_list)) {
    + struct aa_profile *profile =
    + list_entry(profile_list.next, struct aa_profile, list);
    +
    + /* 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);
    +
    + /* FIXME: cleanup profiles references on files */
    +
    + free_null_complain_profile();
    +
    + /*
    + * Delay for an rcu cycle to make sure that all active task
    + * context readers have finished, and all profiles have been
    + * freed by their rcu callbacks.
    + */
    + synchronize_rcu();
    +
    + destroy_apparmorfs();
    + mutex_unlock(&aa_interface_lock);
    +
    + if (unregister_security(&apparmor_ops))
    + info_message("Unable to properly unregister AppArmor");
    +
    + info_message("AppArmor protection removed");
    +}
    +
    +module_init(apparmor_init);
    +module_exit(apparmor_exit);
    +
    +MODULE_DESCRIPTION("AppArmor process confinement");
    +MODULE_AUTHOR("Novell/Immunix, http://bugs.opensuse.org");
    +MODULE_LICENSE("GPL");
    --
    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

    \
     
     \ /
      Last update: 2007-05-14 13:53    [W:0.063 / U:31.484 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site