diff --git a/kernel-patches/2.6.25/__d_path-keep-connected.diff b/kernel-patches/2.6.25/__d_path-keep-connected.diff new file mode 100644 index 000000000..76c265697 --- /dev/null +++ b/kernel-patches/2.6.25/__d_path-keep-connected.diff @@ -0,0 +1,115 @@ +From: John Johansen +Subject: Fix __d_path to allow for old and new behavior bnc#380763 + +Fix __d_path so that it can be told whether or not to connect +disconnect path to the root. This is easier and more efficient +than trying to reconnect these paths for d_path and get_cwd +after the fact. + +Signed-off-by: John Johansen + +--- + fs/dcache.c | 26 +++++++------------------- + fs/namespace.c | 2 +- + include/linux/dcache.h | 2 +- + 3 files changed, 9 insertions(+), 21 deletions(-) + +--- a/fs/dcache.c ++++ b/fs/dcache.c +@@ -1755,6 +1755,7 @@ shouldnt_be_hashed: + * @buffer: buffer to return value in + * @buflen: buffer length + * @fail_deleted: what to return for deleted files ++ * @disconnect: don't return a path starting with / when disconnected + * + * Convert a dentry into an ASCII path name. If the entry has been deleted, + * then if @fail_deleted is true, ERR_PTR(-ENOENT) is returned. Otherwise, +@@ -1768,7 +1769,7 @@ shouldnt_be_hashed: + */ + char *__d_path(struct dentry *dentry, struct vfsmount *vfsmnt, + struct path *root, char *buffer, int buflen, +- int fail_deleted) ++ int fail_deleted, int disconnect) + { + int namelen, is_slash, vfsmount_locked = 0; + +@@ -1833,7 +1834,7 @@ global_root: + */ + namelen = dentry->d_name.len; + is_slash = (namelen == 1 && *dentry->d_name.name == '/'); +- if (is_slash || (dentry->d_sb->s_flags & MS_NOUSER)) { ++ if (disconnect && (is_slash || (dentry->d_sb->s_flags & MS_NOUSER))) { + /* + * Make sure we won't return a pathname starting with '/'. + * +@@ -1848,6 +1849,8 @@ global_root: + } + if (is_slash) + goto out; ++ } else if (is_slash && *buffer == '/') { ++ goto out; + } + if (buflen < namelen) + goto Elong; +@@ -1860,19 +1863,6 @@ Elong: + goto out; + } + +-static char *__connect_d_path(char *path, char *buffer, struct dentry *dentry) +-{ +- if (!IS_ERR(path) && *path != '/' && +- !(dentry->d_sb->s_flags & MS_NOUSER)) { +- /* Pretend that disconnected paths are hanging off the root. */ +- if (path == buffer) +- path = ERR_PTR(-ENAMETOOLONG); +- else +- *--path = '/'; +- } +- return path; +-} +- + /** + * d_path - return the path of a dentry + * @path: path to report +@@ -1905,8 +1895,7 @@ char *d_path(struct path *path, char *bu + root = current->fs->root; + path_get(¤t->fs->root); + read_unlock(¤t->fs->lock); +- res = __d_path(path->dentry, path->mnt, &root, buf, buflen, 0); +- res = __connect_d_path(res, buf, path->dentry); ++ res = __d_path(path->dentry, path->mnt, &root, buf, buflen, 0, 0); + path_put(&root); + return res; + } +@@ -1966,8 +1955,7 @@ asmlinkage long sys_getcwd(char __user * + path_get(¤t->fs->root); + read_unlock(¤t->fs->lock); + +- cwd = __d_path(pwd.dentry, pwd.mnt, &root, page, PAGE_SIZE, 1); +- cwd = __connect_d_path(cwd, page, pwd.dentry); ++ cwd = __d_path(pwd.dentry, pwd.mnt, &root, page, PAGE_SIZE, 1, 0); + error = PTR_ERR(cwd); + if (IS_ERR(cwd)) + goto out; +--- a/fs/namespace.c ++++ b/fs/namespace.c +@@ -1869,7 +1869,7 @@ char *d_namespace_path(struct dentry *de + if (ns_root.mnt) + ns_root.dentry = dget(ns_root.mnt->mnt_root); + spin_unlock(&vfsmount_lock); +- res = __d_path(dentry, vfsmnt, &ns_root, buf, buflen, 1); ++ res = __d_path(dentry, vfsmnt, &ns_root, buf, buflen, 1, 1); + path_put(&root); + path_put(&ns_root); + +--- a/include/linux/dcache.h ++++ b/include/linux/dcache.h +@@ -301,7 +301,7 @@ extern int d_validate(struct dentry *, s + */ + extern char *dynamic_dname(struct dentry *, char *, int, const char *, ...); + extern char *__d_path(struct dentry *, struct vfsmount *, struct path *, +- char *, int, int); ++ char *, int, int, int); + extern char *d_path(struct path *, char *, int); + + /* Allocation counts.. */ diff --git a/kernel-patches/2.6.25/apparmor-2.6.25.diff b/kernel-patches/2.6.25/apparmor-2.6.25.diff new file mode 100644 index 000000000..e1b42d15c --- /dev/null +++ b/kernel-patches/2.6.25/apparmor-2.6.25.diff @@ -0,0 +1,53 @@ +--- + security/apparmor/lsm.c | 3 ++- + security/apparmor/module_interface.c | 22 ++++++++++++++++++---- + 2 files changed, 20 insertions(+), 5 deletions(-) + +--- a/security/apparmor/lsm.c ++++ b/security/apparmor/lsm.c +@@ -462,7 +462,8 @@ static int apparmor_inode_permission(str + /* allow traverse accesses to directories */ + mask &= ~MAY_EXEC; + } +- return aa_permission("inode_permission", inode, nd->dentry, nd->mnt, ++ return aa_permission("inode_permission", inode, nd->path.dentry, ++ nd->path.mnt, + mask, check); + } + +--- a/security/apparmor/module_interface.c ++++ b/security/apparmor/module_interface.c +@@ -356,15 +356,29 @@ static struct aa_profile *aa_unpack_prof + if (!aa_is_nameX(e, AA_STRUCTEND, NULL)) + goto fail; + +- if (!aa_is_u32(e, &(profile->capabilities), NULL)) ++ if (!aa_is_u32(e, &(profile->capabilities.cap[0]), NULL)) + goto fail; +- if (!aa_is_u32(e, &(profile->audit_caps), NULL)) ++ if (!aa_is_u32(e, &(profile->audit_caps.cap[0]), NULL)) + goto fail; +- if (!aa_is_u32(e, &(profile->quiet_caps), NULL)) ++ if (!aa_is_u32(e, &(profile->quiet_caps.cap[0]), NULL)) + goto fail; +- if (!aa_is_u32(e, &(profile->set_caps), NULL)) ++ if (!aa_is_u32(e, &(profile->set_caps.cap[0]), NULL)) + goto fail; + ++ if (aa_is_nameX(e, AA_STRUCT, "caps64")) { ++ /* optional upper half of 64 bit caps */ ++ if (!aa_is_u32(e, &(profile->capabilities.cap[1]), NULL)) ++ goto fail; ++ if (!aa_is_u32(e, &(profile->audit_caps.cap[1]), NULL)) ++ goto fail; ++ if (!aa_is_u32(e, &(profile->quiet_caps.cap[1]), NULL)) ++ goto fail; ++ if (!aa_is_u32(e, &(profile->set_caps.cap[1]), NULL)) ++ goto fail; ++ if (!aa_is_nameX(e, AA_STRUCTEND, NULL)) ++ goto fail; ++ } ++ + if (!aa_unpack_rlimits(e, profile)) + goto fail; + diff --git a/kernel-patches/2.6.25/apparmor-audit.diff b/kernel-patches/2.6.25/apparmor-audit.diff new file mode 100644 index 000000000..0bb75a788 --- /dev/null +++ b/kernel-patches/2.6.25/apparmor-audit.diff @@ -0,0 +1,72 @@ +From: Tony Jones +Subject: Export audit subsystem for use by modules + +Update kenel audit range comments to show AppArmor's registered range of +1500-1599. This range used to be reserved for LSPP but LSPP uses the +SE Linux range and the range was given to AppArmor. +Adds necessary export symbols for audit subsystem routines. +Changes audit_log_vformat to be externally visible (analagous to vprintf) +Patch is not in mainline -- pending AppArmor code submission to lkml + +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + include/linux/audit.h | 12 +++++++++++- + kernel/audit.c | 6 ++++-- + 2 files changed, 15 insertions(+), 3 deletions(-) + +--- a/include/linux/audit.h ++++ b/include/linux/audit.h +@@ -33,7 +33,7 @@ + * 1200 - 1299 messages internal to the audit daemon + * 1300 - 1399 audit event messages + * 1400 - 1499 SE Linux use +- * 1500 - 1599 kernel LSPP events ++ * 1500 - 1599 AppArmor use + * 1600 - 1699 kernel crypto events + * 1700 - 1799 kernel anomaly records + * 1800 - 1999 future kernel use (maybe integrity labels and related events) +@@ -119,6 +119,13 @@ + #define AUDIT_MAC_UNLBL_STCADD 1416 /* NetLabel: add a static label */ + #define AUDIT_MAC_UNLBL_STCDEL 1417 /* NetLabel: del a static label */ + ++#define AUDIT_APPARMOR_AUDIT 1501 /* AppArmor audited grants */ ++#define AUDIT_APPARMOR_ALLOWED 1502 /* Allowed Access for learning */ ++#define AUDIT_APPARMOR_DENIED 1503 ++#define AUDIT_APPARMOR_HINT 1504 /* Process Tracking information */ ++#define AUDIT_APPARMOR_STATUS 1505 /* Changes in config */ ++#define AUDIT_APPARMOR_ERROR 1506 /* Internal AppArmor Errors */ ++ + #define AUDIT_FIRST_KERN_ANOM_MSG 1700 + #define AUDIT_LAST_KERN_ANOM_MSG 1799 + #define AUDIT_ANOM_PROMISCUOUS 1700 /* Device changed promiscuous mode */ +@@ -518,6 +525,9 @@ extern void audit_log(struct audit_ + __attribute__((format(printf,4,5))); + + extern struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, int type); ++extern void audit_log_vformat(struct audit_buffer *ab, ++ const char *fmt, va_list args) ++ __attribute__((format(printf,2,0))); + extern void audit_log_format(struct audit_buffer *ab, + const char *fmt, ...) + __attribute__((format(printf,2,3))); +--- a/kernel/audit.c ++++ b/kernel/audit.c +@@ -1136,8 +1136,7 @@ static inline int audit_expand(struct au + * will be called a second time. Currently, we assume that a printk + * can't format message larger than 1024 bytes, so we don't either. + */ +-static void audit_log_vformat(struct audit_buffer *ab, const char *fmt, +- va_list args) ++void audit_log_vformat(struct audit_buffer *ab, const char *fmt, va_list args) + { + int len, avail; + struct sk_buff *skb; +@@ -1407,3 +1406,6 @@ EXPORT_SYMBOL(audit_log_start); + EXPORT_SYMBOL(audit_log_end); + EXPORT_SYMBOL(audit_log_format); + EXPORT_SYMBOL(audit_log); ++EXPORT_SYMBOL_GPL(audit_log_vformat); ++EXPORT_SYMBOL_GPL(audit_log_untrustedstring); ++EXPORT_SYMBOL_GPL(audit_log_d_path); diff --git a/kernel-patches/2.6.25/apparmor-intree.diff b/kernel-patches/2.6.25/apparmor-intree.diff new file mode 100644 index 000000000..2d5b21b54 --- /dev/null +++ b/kernel-patches/2.6.25/apparmor-intree.diff @@ -0,0 +1,30 @@ +From: John Johansen +Subject: Add AppArmor LSM to security/Makefile + +Signed-off-by: John Johansen +Signed-off-by: Andreas Gruenbacher + +--- + security/Kconfig | 1 + + security/Makefile | 1 + + 2 files changed, 2 insertions(+) + +--- a/security/Kconfig ++++ b/security/Kconfig +@@ -124,6 +124,7 @@ config SECURITY_DEFAULT_MMAP_MIN_ADDR + + source security/selinux/Kconfig + source security/smack/Kconfig ++source security/apparmor/Kconfig + + endmenu + +--- a/security/Makefile ++++ b/security/Makefile +@@ -16,5 +16,6 @@ obj-$(CONFIG_SECURITY) += security.o d + # Must precede capability.o in order to stack properly. + obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o + obj-$(CONFIG_SECURITY_SMACK) += commoncap.o smack/built-in.o ++obj-$(CONFIG_SECURITY_APPARMOR) += commoncap.o apparmor/ + obj-$(CONFIG_SECURITY_CAPABILITIES) += commoncap.o capability.o + obj-$(CONFIG_SECURITY_ROOTPLUG) += commoncap.o root_plug.o diff --git a/kernel-patches/2.6.25/apparmor-lsm.diff b/kernel-patches/2.6.25/apparmor-lsm.diff new file mode 100644 index 000000000..63af28c4f --- /dev/null +++ b/kernel-patches/2.6.25/apparmor-lsm.diff @@ -0,0 +1,904 @@ +From: John Johansen +Subject: AppArmor: Module and LSM hooks + +Module parameters, LSM hooks, initialization and teardown. + +Signed-off-by: John Johansen +Signed-off-by: Andreas Gruenbacher + +--- + security/apparmor/lsm.c | 889 ++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 889 insertions(+) + +--- /dev/null ++++ b/security/apparmor/lsm.c +@@ -0,0 +1,889 @@ ++/* ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "apparmor.h" ++#include "inline.h" ++ ++/* Flag indicating whether initialization completed */ ++int apparmor_initialized = 0; ++ ++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"); ++ ++/* Boot time disable flag */ ++#ifdef CONFIG_SECURITY_APPARMOR_DISABLE ++#define AA_ENABLED_PERMS 0600 ++#else ++#define AA_ENABLED_PERMS 0400 ++#endif ++static int param_set_aa_enabled(const char *val, struct kernel_param *kp); ++unsigned int apparmor_enabled = CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE; ++module_param_call(enabled, param_set_aa_enabled, param_get_aauint, ++ &apparmor_enabled, AA_ENABLED_PERMS); ++MODULE_PARM_DESC(apparmor_enabled, "Enable/Disable Apparmor on boot"); ++ ++static int __init apparmor_enabled_setup(char *str) ++{ ++ apparmor_enabled = simple_strtol(str, NULL, 0); ++ return 1; ++} ++__setup("apparmor=", apparmor_enabled_setup); ++ ++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); ++} ++ ++/* allow run time disabling of apparmor */ ++static int param_set_aa_enabled(const char *val, struct kernel_param *kp) ++{ ++ char *endp; ++ unsigned long l; ++ ++ if (!apparmor_initialized) { ++ apparmor_enabled = 0; ++ return 0; ++ } ++ ++ if (aa_task_context(current)) ++ return -EPERM; ++ ++ if (!apparmor_enabled) ++ return -EINVAL; ++ ++ if (!val) ++ return -EINVAL; ++ ++ l = simple_strtoul(val, &endp, 0); ++ if (endp == val || l != 0) ++ return -EINVAL; ++ ++ apparmor_enabled = 0; ++ apparmor_disable(); ++ return 0; ++} ++ ++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; ++ 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); ++ if (cxt) { ++ if (parent->nsproxy != child->nsproxy) { ++ struct aa_audit sa; ++ memset(&sa, 0, sizeof(sa)); ++ sa.operation = "ptrace"; ++ sa.gfp_mask = GFP_ATOMIC; ++ sa.parent = parent->pid; ++ sa.task = child->pid; ++ sa.info = "different namespaces"; ++ aa_audit_reject(cxt->profile, &sa); ++ error = -EPERM; ++ } else { ++ struct aa_task_context *child_cxt = ++ aa_task_context(child); ++ ++ error = aa_may_ptrace(cxt, child_cxt ? ++ child_cxt->profile : NULL); ++ if (PROFILE_COMPLAIN(cxt->profile)) { ++ struct aa_audit sa; ++ memset(&sa, 0, sizeof(sa)); ++ sa.operation = "ptrace"; ++ sa.gfp_mask = GFP_ATOMIC; ++ sa.parent = parent->pid; ++ sa.task = child->pid; ++ aa_audit_hint(cxt->profile, &sa); ++ } ++ } ++ } ++ rcu_read_unlock(); ++ ++ return error; ++} ++ ++static int apparmor_capable(struct task_struct *task, int cap) ++{ ++ int error; ++ struct aa_task_context *cxt; ++ ++ /* cap_capable returns 0 on success, else -EPERM */ ++ error = cap_capable(task, cap); ++ ++ rcu_read_lock(); ++ cxt = aa_task_context(task); ++ if (cxt && (!error || cap_raised(cxt->profile->set_caps, cap))) ++ 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, "sysctl", name, mask, 0); ++ } ++ free_page((unsigned long)buffer); ++ } ++ ++out: ++ aa_put_profile(profile); ++ 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, "inode_mkdir", dentry, mnt, ++ 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, "inode_rmdir", dentry, mnt, ++ MAY_WRITE); ++ ++ aa_put_profile(profile); ++ ++out: ++ return error; ++} ++ ++static int aa_permission(const char *operation, 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, operation, dentry, mnt, mask, ++ check); ++ aa_put_profile(profile); ++ } ++ return error; ++} ++ ++static inline int aa_mask_permissions(int mask) ++{ ++ if (mask & MAY_APPEND) ++ mask &= (MAY_READ | MAY_APPEND | MAY_EXEC); ++ else ++ mask &= (MAY_READ | MAY_WRITE | MAY_EXEC); ++ return mask; ++} ++ ++static int apparmor_inode_create(struct inode *dir, struct dentry *dentry, ++ struct vfsmount *mnt, int mask) ++{ ++ return aa_permission("inode_create", dir, dentry, mnt, MAY_APPEND, 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("inode_unlink", 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("inode_symlink", 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("inode_mknod", 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, "inode_rename", old_dentry, ++ old_mnt, MAY_READ | MAY_WRITE, check); ++ ++ if (!error && new_mnt) { ++ error = aa_perm(profile, "inode_rename", 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 = aa_mask_permissions(mask); ++ if (S_ISDIR(inode->i_mode)) { ++ check |= AA_CHECK_DIR; ++ /* allow traverse accesses to directories */ ++ mask &= ~MAY_EXEC; ++ } ++ return aa_permission("inode_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, operation, dentry, mnt, ++ 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 aa_file_permission(const char *op, 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 || mask & AA_MAY_LOCK)) { ++ 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; ++ error = aa_permission(op, inode, dentry, mnt, mask, check); ++ } ++ aa_put_profile(profile); ++ ++out: ++ return error; ++} ++ ++static int apparmor_file_permission(struct file *file, int mask) ++{ ++ return aa_file_permission("file_permission", file, ++ aa_mask_permissions(mask)); ++} ++ ++static inline int apparmor_file_lock (struct file *file, unsigned int cmd) ++{ ++ int mask = AA_MAY_LOCK; ++ if (cmd == F_WRLCK) ++ mask |= MAY_WRITE; ++ return aa_file_permission("file_lock", file, mask); ++} ++ ++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, const char *operation, ++ 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(operation, 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, ++ unsigned long addr, unsigned long addr_only) ++{ ++ if ((addr < mmap_min_addr) && !capable(CAP_SYS_RAWIO)) { ++ struct aa_profile *profile = aa_get_profile(current); ++ if (profile) ++ /* future control check here */ ++ return -EACCES; ++ else ++ return -EACCES; ++ aa_put_profile(profile); ++ } ++ ++ return aa_mmap(file, "file_mmap", prot, flags); ++} ++ ++static int apparmor_file_mprotect(struct vm_area_struct *vma, ++ unsigned long reqprot, unsigned long prot) ++{ ++ return aa_mmap(vma->vm_file, "file_mprotect", 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, "changeprofile") == 0) { ++ if (current != task) ++ return -EACCES; ++ error = aa_setprocattr_changeprofile(args); ++ } else if (strcmp(command, "setprofile") == 0) { ++ 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) { ++ struct aa_audit sa; ++ memset(&sa, 0, sizeof(sa)); ++ sa.operation = "profile_set"; ++ sa.gfp_mask = GFP_KERNEL; ++ sa.task = task->pid; ++ sa.info = "from confined process"; ++ aa_audit_reject(profile, &sa); ++ aa_put_profile(profile); ++ return -EACCES; ++ } ++ error = aa_setprocattr_setprofile(task, args); ++ } else { ++ struct aa_audit sa; ++ memset(&sa, 0, sizeof(sa)); ++ sa.operation = "setprocattr"; ++ sa.gfp_mask = GFP_KERNEL; ++ sa.info = "invalid command"; ++ sa.name = command; ++ sa.task = task->pid; ++ aa_audit_reject(NULL, &sa); ++ return -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, ++ .file_lock = apparmor_file_lock, ++ ++ .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, ++}; ++ ++void info_message(const char *str) ++{ ++ struct aa_audit sa; ++ memset(&sa, 0, sizeof(sa)); ++ sa.gfp_mask = GFP_KERNEL; ++ sa.info = str; ++ printk(KERN_INFO "AppArmor: %s\n", str); ++ if (audit_enabled) ++ aa_audit_message(NULL, &sa, AUDIT_APPARMOR_STATUS); ++} ++ ++static int __init apparmor_init(void) ++{ ++ int error; ++ ++ if (!apparmor_enabled) { ++ info_message("AppArmor disabled by boottime parameter\n"); ++ return 0; ++ } ++ ++ if ((error = create_apparmorfs())) { ++ AA_ERROR("Unable to activate AppArmor filesystem\n"); ++ goto createfs_out; ++ } ++ ++ if ((error = alloc_default_namespace())){ ++ AA_ERROR("Unable to allocate default profile namespace\n"); ++ goto alloc_out; ++ } ++ ++ if ((error = register_security(&apparmor_ops))) { ++ AA_ERROR("Unable to register AppArmor\n"); ++ goto register_security_out; ++ } ++ ++ /* Report that AppArmor successfully initialized */ ++ apparmor_initialized = 1; ++ if (apparmor_complain) ++ info_message("AppArmor initialized: complainmode enabled"); ++ else ++ info_message("AppArmor initialized"); ++ ++ return error; ++ ++register_security_out: ++ free_default_namespace(); ++ ++alloc_out: ++ destroy_apparmorfs(); ++ ++createfs_out: ++ return error; ++ ++} ++ ++security_initcall(apparmor_init); ++ ++void apparmor_disable(void) ++{ ++ /* Remove and release all the profiles on the profile list. */ ++ mutex_lock(&aa_interface_lock); ++ aa_profile_ns_list_release(); ++ ++ /* FIXME: cleanup profiles references on files */ ++ free_default_namespace(); ++ ++ /* ++ * 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); ++ ++ apparmor_initialized = 0; ++ ++ info_message("AppArmor protection removed"); ++} ++ ++MODULE_DESCRIPTION("AppArmor process confinement"); ++MODULE_AUTHOR("Novell/Immunix, http://bugs.opensuse.org"); ++MODULE_LICENSE("GPL"); diff --git a/kernel-patches/2.6.25/apparmor-main.diff b/kernel-patches/2.6.25/apparmor-main.diff new file mode 100644 index 000000000..3c5f17d0d --- /dev/null +++ b/kernel-patches/2.6.25/apparmor-main.diff @@ -0,0 +1,1494 @@ +From: John Johansen +Subject: AppArmor: Main Part + +The underlying functions by which the AppArmor LSM hooks are implemented. + +Signed-off-by: John Johansen +Signed-off-by: Andreas Gruenbacher + +--- + security/apparmor/main.c | 1479 +++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 1479 insertions(+) + +--- /dev/null ++++ b/security/apparmor/main.c +@@ -0,0 +1,1479 @@ ++/* ++ * Copyright (C) 2002-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 Core ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "apparmor.h" ++ ++#include "inline.h" ++ ++/* ++ * Table of capability names: we generate it from capabilities.h. ++ */ ++static const char *capability_names[] = { ++#include "capability_names.h" ++}; ++ ++struct aa_namespace *default_namespace; ++ ++static int aa_inode_mode(struct inode *inode) ++{ ++ /* if the inode doesn't exist the user is creating it */ ++ if (!inode || current->fsuid == inode->i_uid) ++ return AA_USER_SHIFT; ++ return AA_OTHER_SHIFT; ++} ++ ++int alloc_default_namespace(void) ++{ ++ struct aa_namespace *ns; ++ char *name = kstrdup("default", GFP_KERNEL); ++ if (!name) ++ return -ENOMEM; ++ ns = alloc_aa_namespace(name); ++ if (!ns) { ++ kfree(name); ++ return -ENOMEM; ++ } ++ ++ write_lock(&profile_ns_list_lock); ++ default_namespace = ns; ++ aa_get_namespace(ns); ++ list_add(&ns->list, &profile_ns_list); ++ write_unlock(&profile_ns_list_lock); ++ ++ return 0; ++} ++ ++void free_default_namespace(void) ++{ ++ write_lock(&profile_ns_list_lock); ++ list_del_init(&default_namespace->list); ++ write_unlock(&profile_ns_list_lock); ++ aa_put_namespace(default_namespace); ++ default_namespace = NULL; ++} ++ ++static void aa_audit_file_sub_mask(struct audit_buffer *ab, char *buffer, ++ int mask) ++{ ++ const char unsafex[] = "upcn"; ++ const char safex[] = "UPCN"; ++ char *m = buffer; ++ ++ if (mask & AA_EXEC_MMAP) ++ *m++ = 'm'; ++ if (mask & MAY_READ) ++ *m++ = 'r'; ++ if (mask & MAY_WRITE) ++ *m++ = 'w'; ++ else if (mask & MAY_APPEND) ++ *m++ = 'a'; ++ if (mask & MAY_EXEC) { ++ int index = AA_EXEC_INDEX(mask); ++ /* all indexes > 4 are also named transitions */ ++ if (index > 4) ++ index = 4; ++ if (index > 0) { ++ if (mask & AA_EXEC_UNSAFE) ++ *m++ = unsafex[index - 1]; ++ else ++ *m++ = safex[index - 1]; ++ } ++ if (mask & AA_EXEC_INHERIT) ++ *m++ = 'i'; ++ *m++ = 'x'; ++ } ++ if (mask & AA_MAY_LINK) ++ *m++ = 'l'; ++ if (mask & AA_MAY_LOCK) ++ *m++ = 'k'; ++ *m++ = '\0'; ++} ++ ++static void aa_audit_file_mask(struct audit_buffer *ab, const char *name, ++ int mask) ++{ ++ char user[10], other[10]; ++ ++ aa_audit_file_sub_mask(ab, user, ++ (mask & AA_USER_PERMS) >> AA_USER_SHIFT); ++ aa_audit_file_sub_mask(ab, other, ++ (mask & AA_OTHER_PERMS) >> AA_OTHER_SHIFT); ++ ++ audit_log_format(ab, " %s=\"%s::%s\"", name, user, other); ++} ++ ++/** ++ * aa_audit - Log an audit event to the audit subsystem ++ * @profile: profile to check against ++ * @sa: audit event ++ * @audit_cxt: audit context to log message to ++ * @type: audit event number ++ */ ++static int aa_audit_base(struct aa_profile *profile, struct aa_audit *sa, ++ struct audit_context *audit_cxt, int type) ++{ ++ struct audit_buffer *ab = NULL; ++ ++ ab = audit_log_start(audit_cxt, sa->gfp_mask, type); ++ ++ if (!ab) { ++ AA_ERROR("Unable to log event (%d) to audit subsys\n", ++ type); ++ /* don't fail operations in complain mode even if logging ++ * fails */ ++ return type == AUDIT_APPARMOR_ALLOWED ? 0 : -ENOMEM; ++ } ++ ++ if (sa->operation) ++ audit_log_format(ab, "operation=\"%s\"", sa->operation); ++ ++ if (sa->info) { ++ audit_log_format(ab, " info=\"%s\"", sa->info); ++ if (sa->error_code) ++ audit_log_format(ab, " error=%d", sa->error_code); ++ } ++ ++ if (sa->request_mask) ++ aa_audit_file_mask(ab, "requested_mask", sa->request_mask); ++ ++ if (sa->denied_mask) ++ aa_audit_file_mask(ab, "denied_mask", sa->denied_mask); ++ ++ if (sa->request_mask) ++ audit_log_format(ab, " fsuid=%d", current->fsuid); ++ ++ if (sa->iattr) { ++ struct iattr *iattr = sa->iattr; ++ ++ audit_log_format(ab, " attribute=\"%s%s%s%s%s%s%s\"", ++ iattr->ia_valid & ATTR_MODE ? "mode," : "", ++ iattr->ia_valid & ATTR_UID ? "uid," : "", ++ iattr->ia_valid & ATTR_GID ? "gid," : "", ++ iattr->ia_valid & ATTR_SIZE ? "size," : "", ++ iattr->ia_valid & (ATTR_ATIME | ATTR_ATIME_SET) ? ++ "atime," : "", ++ iattr->ia_valid & (ATTR_MTIME | ATTR_MTIME_SET) ? ++ "mtime," : "", ++ iattr->ia_valid & ATTR_CTIME ? "ctime," : ""); ++ } ++ ++ if (sa->task) ++ audit_log_format(ab, " task=%d", sa->task); ++ ++ if (sa->parent) ++ audit_log_format(ab, " parent=%d", sa->parent); ++ ++ if (sa->name) { ++ audit_log_format(ab, " name="); ++ audit_log_untrustedstring(ab, sa->name); ++ } ++ ++ if (sa->name2) { ++ audit_log_format(ab, " name2="); ++ audit_log_untrustedstring(ab, sa->name2); ++ } ++ ++ audit_log_format(ab, " pid=%d", current->pid); ++ ++ if (profile) { ++ audit_log_format(ab, " profile="); ++ audit_log_untrustedstring(ab, profile->name); ++ ++ if (profile->ns != default_namespace) { ++ audit_log_format(ab, " namespace="); ++ audit_log_untrustedstring(ab, profile->ns->name); ++ } ++ } ++ ++ audit_log_end(ab); ++ ++ return type == AUDIT_APPARMOR_ALLOWED ? 0 : sa->error_code; ++} ++ ++/** ++ * aa_audit_syscallreject - Log a syscall rejection to the audit subsystem ++ * @profile: profile to check against ++ * @gfp: memory allocation flags ++ * @msg: string describing syscall being rejected ++ */ ++int aa_audit_syscallreject(struct aa_profile *profile, gfp_t gfp, ++ const char *msg) ++{ ++ struct aa_audit sa; ++ memset(&sa, 0, sizeof(sa)); ++ sa.operation = "syscall"; ++ sa.name = msg; ++ sa.gfp_mask = gfp; ++ sa.error_code = -EPERM; ++ ++ return aa_audit_base(profile, &sa, current->audit_context, ++ AUDIT_APPARMOR_DENIED); ++} ++ ++int aa_audit_message(struct aa_profile *profile, struct aa_audit *sa, ++ int type) ++{ ++ struct audit_context *audit_cxt; ++ ++ audit_cxt = apparmor_logsyscall ? current->audit_context : NULL; ++ return aa_audit_base(profile, sa, audit_cxt, type); ++} ++ ++void aa_audit_hint(struct aa_profile *profile, struct aa_audit *sa) ++{ ++ aa_audit_message(profile, sa, AUDIT_APPARMOR_HINT); ++} ++ ++void aa_audit_status(struct aa_profile *profile, struct aa_audit *sa) ++{ ++ aa_audit_message(profile, sa, AUDIT_APPARMOR_STATUS); ++} ++ ++int aa_audit_reject(struct aa_profile *profile, struct aa_audit *sa) ++{ ++ return aa_audit_message(profile, sa, AUDIT_APPARMOR_DENIED); ++} ++ ++/** ++ * aa_audit - Log an audit event to the audit subsystem ++ * @profile: profile to check against ++ * @sa: audit event ++ */ ++int aa_audit(struct aa_profile *profile, struct aa_audit *sa) ++{ ++ int type = AUDIT_APPARMOR_DENIED; ++ struct audit_context *audit_cxt; ++ ++ if (likely(!sa->error_code)) ++ type = AUDIT_APPARMOR_AUDIT; ++ else if (PROFILE_COMPLAIN(profile)) ++ type = AUDIT_APPARMOR_ALLOWED; ++ ++ audit_cxt = apparmor_logsyscall ? current->audit_context : NULL; ++ return aa_audit_base(profile, sa, audit_cxt, type); ++} ++ ++static int aa_audit_file(struct aa_profile *profile, struct aa_audit *sa) ++{ ++ if (likely(!sa->error_code)) { ++ int mask = sa->audit_mask & AUDIT_FILE_MASK; ++ ++ if (unlikely(PROFILE_AUDIT(profile))) ++ mask |= AUDIT_FILE_MASK; ++ ++ if (likely(!(sa->request_mask & mask))) ++ return 0; ++ ++ /* mask off perms that are not being force audited */ ++ sa->request_mask &= mask | ALL_AA_EXEC_TYPE; ++ } else { ++ int mask = AUDIT_QUIET_MASK(sa->audit_mask); ++ ++ if (!(sa->denied_mask & ~mask)) ++ return sa->error_code; ++ ++ /* mask off perms whose denial is being silenced */ ++ sa->denied_mask &= (~mask) | ALL_AA_EXEC_TYPE; ++ } ++ ++ return aa_audit(profile, sa); ++} ++ ++static int aa_audit_caps(struct aa_profile *profile, struct aa_audit *sa, ++ int cap) ++{ ++ if (likely(!sa->error_code)) { ++ if (likely(!PROFILE_AUDIT(profile) && ++ !cap_raised(profile->audit_caps, cap))) ++ return 0; ++ } ++ ++ /* quieting of capabilities is handled the caps_logged cache */ ++ return aa_audit(profile, sa); ++} ++ ++/** ++ * aa_file_denied - check for @mask access on a file ++ * @profile: profile to check against ++ * @name: pathname of file ++ * @mask: permission mask requested for file ++ * @audit_mask: return audit mask for the match ++ * ++ * Return %0 on success, or else the permissions in @mask that the ++ * profile denies. ++ */ ++static int aa_file_denied(struct aa_profile *profile, const char *name, ++ int mask, int *audit_mask) ++{ ++ return (mask & ~aa_match(profile->file_rules, name, audit_mask)); ++} ++ ++/** ++ * aa_link_denied - check for permission to link a file ++ * @profile: profile to check against ++ * @link: pathname of link being created ++ * @target: pathname of target to be linked to ++ * @target_mode: UGO shift for target inode ++ * @request_mask: the permissions subset valid only if link succeeds ++ * @audit_mask: return the audit_mask for the link permission ++ * Return %0 on success, or else the permissions that the profile denies. ++ */ ++static int aa_link_denied(struct aa_profile *profile, const char *link, ++ const char *target, int target_mode, ++ int *request_mask, int *audit_mask) ++{ ++ unsigned int state; ++ int l_mode, t_mode, l_x, t_x, denied_mask = 0; ++ int link_mask = AA_MAY_LINK << target_mode; ++ ++ *request_mask = link_mask; ++ ++ l_mode = aa_match_state(profile->file_rules, DFA_START, link, &state); ++ ++ if (l_mode & link_mask) { ++ int mode; ++ /* test to see if target can be paired with link */ ++ state = aa_dfa_null_transition(profile->file_rules, state); ++ mode = aa_match_state(profile->file_rules, state, target, ++ &state); ++ ++ if (!(mode & link_mask)) ++ denied_mask |= link_mask; ++ ++ *audit_mask = dfa_audit_mask(profile->file_rules, state); ++ ++ /* return if link subset test is not required */ ++ if (!(mode & (AA_LINK_SUBSET_TEST << target_mode))) ++ return denied_mask; ++ } ++ ++ /* Do link perm subset test requiring permission on link are a ++ * subset of the permissions on target. ++ * If a subset test is required a permission subset test of the ++ * perms for the link are done against the user::other of the ++ * target's 'r', 'w', 'x', 'a', 'k', and 'm' permissions. ++ * ++ * If the link has 'x', an exact match of all the execute flags ++ * must match. ++ */ ++ denied_mask |= ~l_mode & link_mask; ++ ++ t_mode = aa_match(profile->file_rules, target, NULL); ++ ++ l_x = l_mode & (ALL_AA_EXEC_TYPE | AA_EXEC_BITS); ++ t_x = t_mode & (ALL_AA_EXEC_TYPE | AA_EXEC_BITS); ++ ++ /* For actual subset test ignore valid-profile-transition flags, ++ * and link bits ++ */ ++ l_mode &= AA_FILE_PERMS & ~AA_LINK_BITS; ++ t_mode &= AA_FILE_PERMS & ~AA_LINK_BITS; ++ ++ *request_mask = l_mode | link_mask; ++ ++ if (l_mode) { ++ int x = l_x | (t_x & ALL_AA_EXEC_UNSAFE); ++ denied_mask |= l_mode & ~t_mode; ++ /* mask off x modes not used by link */ ++ ++ /* handle exec subset ++ * - link safe exec issubset of unsafe exec ++ * - no link x perm is subset of target having x perm ++ */ ++ if ((l_mode & AA_USER_EXEC) && ++ (x & AA_USER_EXEC_TYPE) != (t_x & AA_USER_EXEC_TYPE)) ++ denied_mask = AA_USER_EXEC | (l_x & AA_USER_EXEC_TYPE); ++ if ((l_mode & AA_OTHER_EXEC) && ++ (x & AA_OTHER_EXEC_TYPE) != (t_x & AA_OTHER_EXEC_TYPE)) ++ denied_mask = AA_OTHER_EXEC | (l_x & AA_OTHER_EXEC_TYPE); ++ } ++ ++ return denied_mask; ++} ++ ++/** ++ * aa_get_name - compute the pathname of a file ++ * @dentry: dentry of the file ++ * @mnt: vfsmount of the file ++ * @buffer: buffer that aa_get_name() allocated ++ * @check: AA_CHECK_DIR is set if the file is a directory ++ * ++ * Returns a pointer to the beginning of the pathname (which usually differs ++ * from the beginning of the buffer), or an error code. ++ * ++ * We need @check to indicate whether the file is a directory or not because ++ * the file may not yet exist, and so we cannot check the inode's file type. ++ */ ++static char *aa_get_name(struct dentry *dentry, struct vfsmount *mnt, ++ char **buffer, int check) ++{ ++ char *name; ++ int is_dir, size = 256; ++ ++ is_dir = (check & AA_CHECK_DIR) ? 1 : 0; ++ ++ for (;;) { ++ char *buf = kmalloc(size, GFP_KERNEL); ++ if (!buf) ++ return ERR_PTR(-ENOMEM); ++ ++ name = d_namespace_path(dentry, mnt, buf, size - is_dir); ++ if (!IS_ERR(name)) { ++ if (name[0] != '/') { ++ /* ++ * This dentry is not connected to the ++ * namespace root -- reject access. ++ */ ++ kfree(buf); ++ return ERR_PTR(-ENOENT); ++ } ++ if (is_dir && name[1] != '\0') { ++ /* ++ * Append "/" to the pathname. The root ++ * directory is a special case; it already ++ * ends in slash. ++ */ ++ buf[size - 2] = '/'; ++ buf[size - 1] = '\0'; ++ } ++ ++ *buffer = buf; ++ return name; ++ } ++ if (PTR_ERR(name) != -ENAMETOOLONG) ++ return name; ++ ++ kfree(buf); ++ size <<= 1; ++ if (size > apparmor_path_max) ++ return ERR_PTR(-ENAMETOOLONG); ++ } ++} ++ ++static char *new_compound_name(const char *n1, const char *n2) ++{ ++ char *name = kmalloc(strlen(n1) + strlen(n2) + 3, GFP_KERNEL); ++ if (name) ++ sprintf(name, "%s//%s", n1, n2); ++ return name; ++} ++static inline void aa_put_name_buffer(char *buffer) ++{ ++ kfree(buffer); ++} ++ ++/** ++ * aa_perm_dentry - check if @profile allows @mask for a file ++ * @profile: profile to check against ++ * @dentry: dentry of the file ++ * @mnt: vfsmount o the file ++ * @sa: audit context ++ * @mask: requested profile permissions ++ * @check: kind of check to perform ++ * ++ * Returns 0 upon success, or else an error code. ++ * ++ * @check indicates the file type, and whether the file was accessed through ++ * an open file descriptor (AA_CHECK_FD) or not. ++ */ ++static int aa_perm_dentry(struct aa_profile *profile, struct dentry *dentry, ++ struct vfsmount *mnt, struct aa_audit *sa, int check) ++{ ++ int error; ++ char *buffer = NULL; ++ ++ sa->name = aa_get_name(dentry, mnt, &buffer, check); ++ sa->request_mask <<= aa_inode_mode(dentry->d_inode); ++ if (IS_ERR(sa->name)) { ++ /* ++ * deleted files are given a pass on permission checks when ++ * accessed through a file descriptor. ++ */ ++ if (PTR_ERR(sa->name) == -ENOENT && (check & AA_CHECK_FD)) ++ sa->denied_mask = 0; ++ else { ++ sa->denied_mask = sa->request_mask; ++ sa->error_code = PTR_ERR(sa->name); ++ if (sa->error_code == -ENOENT) ++ sa->info = "Failed name resolution - object not a valid entry"; ++ else if (sa->error_code == -ENAMETOOLONG) ++ sa->info = "Failed name resolution - name too long"; ++ else ++ sa->info = "Failed name resolution"; ++ } ++ sa->name = NULL; ++ } else ++ sa->denied_mask = aa_file_denied(profile, sa->name, ++ sa->request_mask, ++ &sa->audit_mask); ++ ++ if (!sa->denied_mask) ++ sa->error_code = 0; ++ ++ error = aa_audit_file(profile, sa); ++ aa_put_name_buffer(buffer); ++ ++ return error; ++} ++ ++/** ++ * aa_attr - check if attribute change is allowed ++ * @profile: profile to check against ++ * @dentry: dentry of the file to check ++ * @mnt: vfsmount of the file to check ++ * @iattr: attribute changes requested ++ */ ++int aa_attr(struct aa_profile *profile, struct dentry *dentry, ++ struct vfsmount *mnt, struct iattr *iattr) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error, check; ++ struct aa_audit sa; ++ ++ memset(&sa, 0, sizeof(sa)); ++ sa.operation = "setattr"; ++ sa.gfp_mask = GFP_KERNEL; ++ sa.iattr = iattr; ++ sa.request_mask = MAY_WRITE; ++ sa.error_code = -EACCES; ++ ++ check = 0; ++ if (inode && S_ISDIR(inode->i_mode)) ++ check |= AA_CHECK_DIR; ++ if (iattr->ia_valid & ATTR_FILE) ++ check |= AA_CHECK_FD; ++ ++ error = aa_perm_dentry(profile, dentry, mnt, &sa, check); ++ ++ return error; ++} ++ ++/** ++ * aa_perm_xattr - check if xattr attribute change is allowed ++ * @profile: profile to check against ++ * @dentry: dentry of the file to check ++ * @mnt: vfsmount of the file to check ++ * @operation: xattr operation being done ++ * @mask: access mode requested ++ * @check: kind of check to perform ++ */ ++int aa_perm_xattr(struct aa_profile *profile, const char *operation, ++ struct dentry *dentry, struct vfsmount *mnt, int mask, ++ int check) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error; ++ struct aa_audit sa; ++ ++ memset(&sa, 0, sizeof(sa)); ++ sa.operation = operation; ++ sa.gfp_mask = GFP_KERNEL; ++ sa.request_mask = mask; ++ sa.error_code = -EACCES; ++ ++ if (inode && S_ISDIR(inode->i_mode)) ++ check |= AA_CHECK_DIR; ++ ++ error = aa_perm_dentry(profile, dentry, mnt, &sa, check); ++ ++ return error; ++} ++ ++/** ++ * aa_perm - basic apparmor permissions check ++ * @profile: profile to check against ++ * @dentry: dentry of the file to check ++ * @mnt: vfsmount of the file to check ++ * @mask: access mode requested ++ * @check: kind of check to perform ++ * ++ * Determine if access @mask for the file is authorized by @profile. ++ * Returns 0 on success, or else an error code. ++ */ ++int aa_perm(struct aa_profile *profile, const char *operation, ++ struct dentry *dentry, struct vfsmount *mnt, int mask, int check) ++{ ++ struct aa_audit sa; ++ int error = 0; ++ ++ if (mask == 0) ++ goto out; ++ ++ memset(&sa, 0, sizeof(sa)); ++ sa.operation = operation; ++ sa.gfp_mask = GFP_KERNEL; ++ sa.request_mask = mask; ++ sa.error_code = -EACCES; ++ ++ error = aa_perm_dentry(profile, dentry, mnt, &sa, check); ++ ++out: ++ return error; ++} ++ ++/** ++ * aa_perm_dir ++ * @profile: profile to check against ++ * @dentry: dentry of directory to check ++ * @mnt: vfsmount of directory to check ++ * @operation: directory operation being performed ++ * @mask: access mode requested ++ * ++ * Determine if directory operation (make/remove) for dentry is authorized ++ * by @profile. ++ * Returns 0 on success, or else an error code. ++ */ ++int aa_perm_dir(struct aa_profile *profile, const char *operation, ++ struct dentry *dentry, struct vfsmount *mnt, int mask) ++{ ++ struct aa_audit sa; ++ ++ memset(&sa, 0, sizeof(sa)); ++ sa.operation = operation; ++ sa.gfp_mask = GFP_KERNEL; ++ sa.request_mask = mask; ++ sa.error_code = -EACCES; ++ ++ return aa_perm_dentry(profile, dentry, mnt, &sa, AA_CHECK_DIR); ++} ++ ++int aa_perm_path(struct aa_profile *profile, const char *operation, ++ const char *name, int mask, uid_t uid) ++{ ++ struct aa_audit sa; ++ ++ memset(&sa, 0, sizeof(sa)); ++ sa.operation = operation; ++ sa.gfp_mask = GFP_KERNEL; ++ sa.request_mask = mask; ++ sa.name = name; ++ if (current->fsuid == uid) ++ sa.request_mask = mask << AA_USER_SHIFT; ++ else ++ sa.request_mask = mask << AA_OTHER_SHIFT; ++ ++ sa.denied_mask = aa_file_denied(profile, name, sa.request_mask, ++ &sa.audit_mask) ; ++ sa.error_code = sa.denied_mask ? -EACCES : 0; ++ ++ return aa_audit_file(profile, &sa); ++} ++ ++/** ++ * aa_capability - test permission to use capability ++ * @cxt: aa_task_context with profile to check against ++ * @cap: capability to be tested ++ * ++ * Look up capability in profile capability set. ++ * Returns 0 on success, or else an error code. ++ */ ++int aa_capability(struct aa_task_context *cxt, int cap) ++{ ++ int error = cap_raised(cxt->profile->capabilities, cap) ? 0 : -EPERM; ++ struct aa_audit sa; ++ ++ /* test if cap has alread been logged */ ++ if (cap_raised(cxt->caps_logged, cap)) { ++ if (PROFILE_COMPLAIN(cxt->profile)) ++ error = 0; ++ return error; ++ } else ++ /* don't worry about rcu replacement of the cxt here. ++ * caps_logged is a cache to reduce the occurence of ++ * duplicate messages in the log. The worst that can ++ * happen is duplicate capability messages shows up in ++ * the audit log ++ */ ++ cap_raise(cxt->caps_logged, cap); ++ ++ memset(&sa, 0, sizeof(sa)); ++ sa.operation = "capable"; ++ sa.gfp_mask = GFP_ATOMIC; ++ sa.name = capability_names[cap]; ++ sa.error_code = error; ++ ++ error = aa_audit_caps(cxt->profile, &sa, cap); ++ ++ return error; ++} ++ ++/* must be used inside rcu_read_lock or task_lock */ ++int aa_may_ptrace(struct aa_task_context *cxt, struct aa_profile *tracee) ++{ ++ if (!cxt || cxt->profile == tracee) ++ return 0; ++ return aa_capability(cxt, CAP_SYS_PTRACE); ++} ++ ++/** ++ * aa_link - hard link check ++ * @profile: profile to check against ++ * @link: dentry of link being created ++ * @link_mnt: vfsmount of link being created ++ * @target: dentry of link target ++ * @target_mnt: vfsmunt of link target ++ * ++ * Returns 0 on success, or else an error code. ++ */ ++int aa_link(struct aa_profile *profile, ++ struct dentry *link, struct vfsmount *link_mnt, ++ struct dentry *target, struct vfsmount *target_mnt) ++{ ++ int error; ++ struct aa_audit sa; ++ char *buffer = NULL, *buffer2 = NULL; ++ ++ memset(&sa, 0, sizeof(sa)); ++ sa.operation = "inode_link"; ++ sa.gfp_mask = GFP_KERNEL; ++ sa.name = aa_get_name(link, link_mnt, &buffer, 0); ++ sa.name2 = aa_get_name(target, target_mnt, &buffer2, 0); ++ ++ if (IS_ERR(sa.name)) { ++ sa.error_code = PTR_ERR(sa.name); ++ sa.name = NULL; ++ } ++ if (IS_ERR(sa.name2)) { ++ sa.error_code = PTR_ERR(sa.name2); ++ sa.name2 = NULL; ++ } ++ ++ if (sa.name && sa.name2) { ++ sa.denied_mask = aa_link_denied(profile, sa.name, sa.name2, ++ aa_inode_mode(target->d_inode), ++ &sa.request_mask, ++ &sa.audit_mask); ++ sa.error_code = sa.denied_mask ? -EACCES : 0; ++ } ++ ++ error = aa_audit_file(profile, &sa); ++ ++ aa_put_name_buffer(buffer); ++ aa_put_name_buffer(buffer2); ++ ++ return error; ++} ++ ++/******************************* ++ * Global task related functions ++ *******************************/ ++ ++/** ++ * aa_clone - initialize the task context for a new task ++ * @child: task that is being created ++ * ++ * Returns 0 on success, or else an error code. ++ */ ++int aa_clone(struct task_struct *child) ++{ ++ struct aa_task_context *cxt, *child_cxt; ++ struct aa_profile *profile; ++ ++ if (!aa_task_context(current)) ++ return 0; ++ child_cxt = aa_alloc_task_context(GFP_KERNEL); ++ if (!child_cxt) ++ return -ENOMEM; ++ ++repeat: ++ profile = aa_get_profile(current); ++ if (profile) { ++ lock_profile(profile); ++ cxt = aa_task_context(current); ++ if (unlikely(profile->isstale || !cxt || ++ cxt->profile != profile)) { ++ /** ++ * Race with profile replacement or removal, or with ++ * task context removal. ++ */ ++ unlock_profile(profile); ++ aa_put_profile(profile); ++ goto repeat; ++ } ++ ++ /* No need to grab the child's task lock here. */ ++ aa_change_task_context(child, child_cxt, profile, ++ cxt->cookie, cxt->previous_profile); ++ unlock_profile(profile); ++ ++ if (APPARMOR_COMPLAIN(child_cxt) && ++ profile == profile->ns->null_complain_profile) { ++ struct aa_audit sa; ++ memset(&sa, 0, sizeof(sa)); ++ sa.operation = "clone"; ++ sa.gfp_mask = GFP_KERNEL; ++ sa.task = child->pid; ++ aa_audit_hint(profile, &sa); ++ } ++ aa_put_profile(profile); ++ } else ++ aa_free_task_context(child_cxt); ++ ++ return 0; ++} ++ ++static struct aa_profile * ++aa_register_find(struct aa_profile *profile, const char* ns_name, ++ const char *name, int mandatory, int complain, ++ struct aa_audit *sa) ++{ ++ struct aa_namespace *ns; ++ struct aa_profile *new_profile; ++ int ns_ref = 0; ++ ++ if (profile) ++ ns = profile->ns; ++ else ++ ns = default_namespace; ++ ++ if (ns_name) { ++ /* locate the profile namespace */ ++ ns = aa_find_namespace(ns_name); ++ if (!ns) { ++ if (mandatory) { ++ sa->info = "profile namespace not found"; ++ sa->denied_mask = sa->request_mask; ++ sa->error_code = -ENOENT; ++ return ERR_PTR(-ENOENT); ++ } else { ++ return NULL; ++ } ++ } ++ ns_ref++; ++ } ++ ++ /* Locate new profile */ ++ new_profile = aa_find_profile(ns, name); ++ ++ if (new_profile) { ++ AA_DEBUG("%s: setting profile %s\n", ++ __FUNCTION__, new_profile->name); ++ } else if (mandatory && profile) { ++ sa->info = "mandatory profile missing"; ++ sa->denied_mask = sa->request_mask; /* shifted MAY_EXEC */ ++ if (complain) { ++ aa_audit_hint(profile, sa); ++ new_profile = ++ aa_dup_profile(profile->ns->null_complain_profile); ++ } else { ++ sa->error_code = -EACCES; ++ if (ns_ref) ++ aa_put_namespace(ns); ++ return ERR_PTR(-EACCES); ++ } ++ } else { ++ /* Only way we can get into this code is if task ++ * is unconfined, pix, nix. ++ */ ++ AA_DEBUG("%s: No profile found for exec image '%s'\n", ++ __FUNCTION__, ++ name); ++ } ++ if (ns_ref) ++ aa_put_namespace(ns); ++ return new_profile; ++} ++ ++static struct aa_profile * ++aa_x_to_profile(struct aa_profile *profile, const char *filename, int xmode, ++ struct aa_audit *sa, char **child) ++{ ++ struct aa_profile *new_profile = NULL; ++ int ix = xmode & AA_EXEC_INHERIT; ++ int complain = PROFILE_COMPLAIN(profile); ++ int index; ++ ++ *child = NULL; ++ switch (xmode & AA_EXEC_MODIFIERS) { ++ case 0: ++ /* only valid with ix flag */ ++ ix = 1; ++ break; ++ case AA_EXEC_UNCONFINED: ++ /* only valid without ix flag */ ++ ix = 0; ++ break; ++ case AA_EXEC_PROFILE: ++ new_profile = aa_register_find(profile, NULL, filename, !ix, ++ complain, sa); ++ break; ++ case AA_EXEC_CHILD: ++ *child = new_compound_name(profile->name, filename); ++ sa->name2 = *child; ++ if (!*child) { ++ sa->info = "Failed name resolution - exec failed"; ++ sa->error_code = -ENOMEM; ++ new_profile = ERR_PTR(-ENOMEM); ++ } else { ++ new_profile = aa_register_find(profile, NULL, *child, ++ !ix, complain, sa); ++ } ++ break; ++ default: ++ /* all other indexes are named transitions */ ++ index = AA_EXEC_INDEX(xmode); ++ if (index - 4 > profile->exec_table_size) { ++ sa->info = "invalid named transition - exec failed"; ++ sa->error_code = -EACCES; ++ new_profile = ERR_PTR(-EACCES); ++ } else { ++ char *ns_name = NULL; ++ char *name = profile->exec_table[index - 4]; ++ if (*name == ':') { ++ ns_name = name + 1; ++ name = ns_name + strlen(ns_name) + 1; ++ } ++ sa->name2 = name; ++ sa->name3 = ns_name; ++ new_profile = ++ aa_register_find(profile, ns_name, name, ++ !ix, complain, sa); ++ } ++ } ++ if (IS_ERR(new_profile)) ++ /* all these failures must be audited - no quieting */ ++ return ERR_PTR(aa_audit_reject(profile, sa)); ++ return new_profile; ++} ++ ++/** ++ * aa_register - register a new program ++ * @bprm: binprm of program being registered ++ * ++ * Try to register a new program during execve(). This should give the ++ * new program a valid aa_task_context if confined. ++ */ ++int aa_register(struct linux_binprm *bprm) ++{ ++ const char *filename; ++ char *buffer = NULL, *child = NULL; ++ struct file *filp = bprm->file; ++ struct aa_profile *profile, *old_profile, *new_profile = NULL; ++ int exec_mode, complain = 0, shift; ++ struct aa_audit sa; ++ ++ AA_DEBUG("%s\n", __FUNCTION__); ++ ++ profile = aa_get_profile(current); ++ ++ shift = aa_inode_mode(filp->f_dentry->d_inode); ++ memset(&sa, 0, sizeof(sa)); ++ sa.operation = "exec"; ++ sa.gfp_mask = GFP_KERNEL; ++ sa.request_mask = MAY_EXEC << shift; ++ ++ filename = aa_get_name(filp->f_dentry, filp->f_vfsmnt, &buffer, 0); ++ if (IS_ERR(filename)) { ++ if (profile) { ++ sa.info = "Failed name resolution - exec failed"; ++ sa.error_code = PTR_ERR(filename); ++ aa_audit_file(profile, &sa); ++ return sa.error_code; ++ } else ++ return 0; ++ } ++ sa.name = filename; ++ ++ exec_mode = AA_EXEC_UNSAFE << shift; ++ ++repeat: ++ if (profile) { ++ complain = PROFILE_COMPLAIN(profile); ++ ++ /* Confined task, determine what mode inherit, unconfined or ++ * mandatory to load new profile ++ */ ++ exec_mode = aa_match(profile->file_rules, filename, ++ &sa.audit_mask); ++ ++ ++ if (exec_mode & sa.request_mask) { ++ int xm = exec_mode >> shift; ++ new_profile = aa_x_to_profile(profile, filename, ++ xm, &sa, &child); ++ ++ if (!new_profile && (xm & AA_EXEC_INHERIT)) ++ /* (p|c|n|)ix - don't change profile */ ++ goto cleanup; ++ /* error case caught below */ ++ ++ } else if (sa.request_mask & AUDIT_QUIET_MASK(sa.audit_mask)) { ++ /* quiet failed exit */ ++ new_profile = ERR_PTR(-EACCES); ++ } else if (complain) { ++ /* There was no entry in calling profile ++ * describing mode to execute image in. ++ * Drop into null-profile (disabling secure exec). ++ */ ++ new_profile = ++ aa_dup_profile(profile->ns->null_complain_profile); ++ exec_mode |= AA_EXEC_UNSAFE << shift; ++ } else { ++ sa.denied_mask = sa.request_mask; ++ sa.error_code = -EACCES; ++ new_profile = ERR_PTR(aa_audit_file(profile, &sa)); ++ } ++ } else { ++ /* Unconfined task, load profile if it exists */ ++ new_profile = aa_register_find(NULL, NULL, filename, 0, 0, &sa); ++ if (new_profile == NULL) ++ goto cleanup; ++ } ++ ++ if (IS_ERR(new_profile)) ++ goto cleanup; ++ ++ old_profile = __aa_replace_profile(current, new_profile); ++ if (IS_ERR(old_profile)) { ++ aa_put_profile(new_profile); ++ aa_put_profile(profile); ++ if (PTR_ERR(old_profile) == -ESTALE) { ++ profile = aa_get_profile(current); ++ goto repeat; ++ } ++ if (PTR_ERR(old_profile) == -EPERM) { ++ sa.denied_mask = sa.request_mask; ++ sa.info = "unable to set profile due to ptrace"; ++ sa.task = current->parent->pid; ++ aa_audit_reject(profile, &sa); ++ } ++ new_profile = old_profile; ++ goto cleanup; ++ } ++ aa_put_profile(old_profile); ++ aa_put_profile(profile); ++ ++ /* Handle confined exec. ++ * Can be at this point for the following reasons: ++ * 1. unconfined switching to confined ++ * 2. confined switching to different confinement ++ * 3. confined switching to unconfined ++ * ++ * Cases 2 and 3 are marked as requiring secure exec ++ * (unless policy specified "unsafe exec") ++ */ ++ if (!(exec_mode & (AA_EXEC_UNSAFE << shift))) { ++ unsigned long bprm_flags; ++ ++ bprm_flags = AA_SECURE_EXEC_NEEDED; ++ bprm->security = (void*) ++ ((unsigned long)bprm->security | bprm_flags); ++ } ++ ++ if (complain && new_profile && ++ new_profile == new_profile->ns->null_complain_profile) { ++ sa.request_mask = 0; ++ sa.name = NULL; ++ sa.info = "set profile"; ++ aa_audit_hint(new_profile, &sa); ++ } ++ ++cleanup: ++ aa_put_name_buffer(child); ++ aa_put_name_buffer(buffer); ++ if (IS_ERR(new_profile)) ++ return PTR_ERR(new_profile); ++ aa_put_profile(new_profile); ++ return 0; ++} ++ ++/** ++ * aa_release - release a task context ++ * @task: task being released ++ * ++ * This is called after a task has exited and the parent has reaped it. ++ */ ++void aa_release(struct task_struct *task) ++{ ++ struct aa_task_context *cxt; ++ struct aa_profile *profile; ++ /* ++ * While the task context is still on a profile's task context ++ * list, another process could replace the profile under us, ++ * leaving us with a locked profile that is no longer attached ++ * to this task. So after locking the profile, we check that ++ * the profile is still attached. The profile lock is ++ * sufficient to prevent the replacement race so we do not lock ++ * the task. ++ * ++ * Use lock subtyping to avoid lockdep reporting a false irq ++ * possible inversion between the task_lock and profile_lock ++ * ++ * We also avoid taking the task_lock here because lock_dep ++ * would report another false {softirq-on-W} potential irq_lock ++ * inversion. ++ * ++ * If the task does not have a profile attached we are safe; ++ * nothing can race with us at this point. ++ */ ++ ++repeat: ++ profile = aa_get_profile(task); ++ if (profile) { ++ lock_profile_nested(profile, aa_lock_task_release); ++ cxt = aa_task_context(task); ++ if (unlikely(!cxt || cxt->profile != profile)) { ++ unlock_profile(profile); ++ aa_put_profile(profile); ++ goto repeat; ++ } ++ aa_change_task_context(task, NULL, NULL, 0, NULL); ++ unlock_profile(profile); ++ aa_put_profile(profile); ++ } ++} ++ ++static int do_change_profile(struct aa_profile *expected, ++ struct aa_namespace *ns, const char *name, ++ u64 cookie, int restore, struct aa_audit *sa) ++{ ++ struct aa_profile *new_profile = NULL, *old_profile = NULL, ++ *previous_profile = NULL; ++ struct aa_task_context *new_cxt, *cxt; ++ int error = 0; ++ ++ sa->name = name; ++ ++ new_cxt = aa_alloc_task_context(GFP_KERNEL); ++ if (!new_cxt) ++ return -ENOMEM; ++ ++ new_profile = aa_find_profile(ns, name); ++ if (!new_profile && !restore) { ++ if (!PROFILE_COMPLAIN(expected)) ++ return -ENOENT; ++ new_profile = aa_dup_profile(ns->null_complain_profile); ++ } ++ ++ cxt = lock_task_and_profiles(current, new_profile); ++ if (!cxt) { ++ error = -EPERM; ++ goto out; ++ } ++ old_profile = cxt->profile; ++ ++ if (cxt->profile != expected || (new_profile && new_profile->isstale)) { ++ error = -ESTALE; ++ goto out; ++ } ++ ++ if (cxt->previous_profile) { ++ if (cxt->cookie != cookie) { ++ error = -EACCES; ++ sa->info = "killing process"; ++ aa_audit_reject(cxt->profile, sa); ++ /* terminate process */ ++ (void)send_sig_info(SIGKILL, NULL, current); ++ goto out; ++ } ++ ++ if (!restore) ++ previous_profile = cxt->previous_profile; ++ } else ++ previous_profile = cxt->profile; ++ ++ if ((current->ptrace & PT_PTRACED) && aa_may_ptrace(cxt, new_profile)) { ++ error = -EACCES; ++ goto out; ++ } ++ ++ if (new_profile == ns->null_complain_profile) ++ aa_audit_hint(cxt->profile, sa); ++ ++ if (APPARMOR_AUDIT(cxt)) ++ aa_audit_message(cxt->profile, sa, AUDIT_APPARMOR_AUDIT); ++ ++ if (!restore && cookie) ++ aa_change_task_context(current, new_cxt, new_profile, cookie, ++ previous_profile); ++ else ++ /* either return to previous_profile, or a permanent change */ ++ aa_change_task_context(current, new_cxt, new_profile, 0, NULL); ++ ++out: ++ if (aa_task_context(current) != new_cxt) ++ aa_free_task_context(new_cxt); ++ task_unlock(current); ++ unlock_both_profiles(old_profile, new_profile); ++ aa_put_profile(new_profile); ++ return error; ++} ++ ++/** ++ * aa_change_profile - perform a one-way profile transition ++ * @ns_name: name of the profile namespace to change to ++ * @name: name of profile to change to ++ * Change to new profile @name. Unlike with hats, there is no way ++ * to change back. ++ * ++ * Returns %0 on success, error otherwise. ++ */ ++int aa_change_profile(const char *ns_name, const char *name) ++{ ++ struct aa_task_context *cxt; ++ struct aa_profile *profile = NULL; ++ struct aa_namespace *ns = NULL; ++ struct aa_audit sa; ++ unsigned int state; ++ int error = -EINVAL; ++ ++ if (!name) ++ return -EINVAL; ++ ++ memset(&sa, 0, sizeof(sa)); ++ sa.gfp_mask = GFP_ATOMIC; ++ sa.operation = "change_profile"; ++ ++repeat: ++ task_lock(current); ++ cxt = aa_task_context(current); ++ if (cxt) ++ profile = aa_dup_profile(cxt->profile); ++ task_unlock(current); ++ ++ if (ns_name) ++ ns = aa_find_namespace(ns_name); ++ else if (profile) ++ ns = aa_get_namespace(profile->ns); ++ else ++ ns = aa_get_namespace(default_namespace); ++ ++ if (!ns) { ++ aa_put_profile(profile); ++ return -ENOENT; ++ } ++ ++ if (!profile || PROFILE_COMPLAIN(profile) || ++ (ns == profile->ns && ++ (aa_match(profile->file_rules, name, NULL) & AA_CHANGE_PROFILE))) ++ error = do_change_profile(profile, ns, name, 0, 0, &sa); ++ else { ++ /* check for a rule with a namespace prepended */ ++ aa_match_state(profile->file_rules, DFA_START, ns->name, ++ &state); ++ state = aa_dfa_null_transition(profile->file_rules, state); ++ if ((aa_match_state(profile->file_rules, state, name, NULL) & ++ AA_CHANGE_PROFILE)) ++ error = do_change_profile(profile, ns, name, 0, 0, ++ &sa); ++ else ++ /* no permission to transition to profile @name */ ++ error = -EACCES; ++ } ++ ++ aa_put_namespace(ns); ++ aa_put_profile(profile); ++ if (error == -ESTALE) ++ goto repeat; ++ ++ return error; ++} ++ ++/** ++ * aa_change_hat - change hat to/from subprofile ++ * @hat_name: hat to change to ++ * @cookie: magic value to validate the hat change ++ * ++ * Change to new @hat_name, and store the @hat_magic in the current task ++ * context. If the new @hat_name is %NULL and the @cookie matches that ++ * stored in the current task context and is not 0, return to the top level ++ * profile. ++ * Returns %0 on success, error otherwise. ++ */ ++int aa_change_hat(const char *hat_name, u64 cookie) ++{ ++ struct aa_task_context *cxt; ++ struct aa_profile *profile, *previous_profile; ++ struct aa_audit sa; ++ int error = 0; ++ ++ memset(&sa, 0, sizeof(sa)); ++ sa.gfp_mask = GFP_ATOMIC; ++ sa.operation = "change_hat"; ++ ++repeat: ++ task_lock(current); ++ cxt = aa_task_context(current); ++ if (!cxt) { ++ task_unlock(current); ++ return -EPERM; ++ } ++ profile = aa_dup_profile(cxt->profile); ++ previous_profile = aa_dup_profile(cxt->previous_profile); ++ task_unlock(current); ++ ++ if (hat_name) { ++ char *name, *profile_name; ++ if (!PROFILE_COMPLAIN(profile) && ++ !(aa_match(profile->file_rules, hat_name, NULL) ++ & AA_CHANGE_HAT)) { ++ /* missing permission to change_hat is treated the ++ * same as a failed hat search */ ++ error = -ENOENT; ++ goto out; ++ } ++ ++ if (previous_profile) ++ profile_name = previous_profile->name; ++ else ++ profile_name = profile->name; ++ ++ name = new_compound_name(profile_name, hat_name); ++ if (!name) { ++ error = -ENOMEM; ++ goto out; ++ } ++ error = do_change_profile(profile, profile->ns, name, cookie, ++ 0, &sa); ++ aa_put_name_buffer(name); ++ } else if (previous_profile) ++ error = do_change_profile(profile, profile->ns, ++ previous_profile->name, cookie, 1, ++ &sa); ++ /* else ignore restores when there is no saved profile */ ++ ++out: ++ aa_put_profile(previous_profile); ++ aa_put_profile(profile); ++ if (error == -ESTALE) ++ goto repeat; ++ ++ return error; ++} ++ ++/** ++ * __aa_replace_profile - replace a task's profile ++ * @task: task to switch the profile of ++ * @profile: profile to switch to ++ * ++ * Returns a handle to the previous profile upon success, or else an ++ * error code. ++ */ ++struct aa_profile *__aa_replace_profile(struct task_struct *task, ++ struct aa_profile *profile) ++{ ++ struct aa_task_context *cxt, *new_cxt = NULL; ++ struct aa_profile *old_profile = NULL; ++ ++ if (profile) { ++ new_cxt = aa_alloc_task_context(GFP_KERNEL); ++ if (!new_cxt) ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ cxt = lock_task_and_profiles(task, profile); ++ if (unlikely(profile && profile->isstale)) { ++ task_unlock(task); ++ unlock_both_profiles(profile, cxt ? cxt->profile : NULL); ++ aa_free_task_context(new_cxt); ++ return ERR_PTR(-ESTALE); ++ } ++ ++ if ((current->ptrace & PT_PTRACED) && aa_may_ptrace(cxt, profile)) { ++ task_unlock(task); ++ unlock_both_profiles(profile, cxt ? cxt->profile : NULL); ++ aa_free_task_context(new_cxt); ++ return ERR_PTR(-EPERM); ++ } ++ ++ if (cxt) ++ old_profile = aa_dup_profile(cxt->profile); ++ aa_change_task_context(task, new_cxt, profile, 0, NULL); ++ ++ task_unlock(task); ++ unlock_both_profiles(profile, old_profile); ++ return old_profile; ++} ++ ++/** ++ * lock_task_and_profiles - lock the task and confining profiles and @profile ++ * @task: task to lock ++ * @profile: extra profile to lock in addition to the current profile ++ * ++ * Handle the spinning on locking to make sure the task context and ++ * profile are consistent once all locks are aquired. ++ * ++ * return the aa_task_context currently confining the task. The task lock ++ * will be held whether or not the task is confined. ++ */ ++struct aa_task_context * ++lock_task_and_profiles(struct task_struct *task, struct aa_profile *profile) ++{ ++ struct aa_task_context *cxt; ++ struct aa_profile *old_profile = NULL; ++ ++ rcu_read_lock(); ++repeat: ++ cxt = aa_task_context(task); ++ if (cxt) ++ old_profile = cxt->profile; ++ ++ lock_both_profiles(profile, old_profile); ++ task_lock(task); ++ ++ /* check for race with profile transition, replacement or removal */ ++ if (unlikely(cxt != aa_task_context(task))) { ++ task_unlock(task); ++ unlock_both_profiles(profile, old_profile); ++ old_profile = NULL; ++ goto repeat; ++ } ++ rcu_read_unlock(); ++ return cxt; ++} ++ ++static void free_aa_task_context_rcu_callback(struct rcu_head *head) ++{ ++ struct aa_task_context *cxt; ++ ++ cxt = container_of(head, struct aa_task_context, rcu); ++ aa_free_task_context(cxt); ++} ++ ++/** ++ * aa_change_task_context - switch a task to use a new context and profile ++ * @task: task that is having its task context changed ++ * @new_cxt: new task context to use after the switch ++ * @profile: new profile to use after the switch ++ * @cookie: magic value to switch to ++ * @previous_profile: profile the task can return to ++ */ ++void aa_change_task_context(struct task_struct *task, ++ struct aa_task_context *new_cxt, ++ struct aa_profile *profile, u64 cookie, ++ struct aa_profile *previous_profile) ++{ ++ struct aa_task_context *old_cxt = aa_task_context(task); ++ ++ if (old_cxt) { ++ list_del_init(&old_cxt->list); ++ call_rcu(&old_cxt->rcu, free_aa_task_context_rcu_callback); ++ } ++ if (new_cxt) { ++ /* set the caps_logged cache to the quiet_caps mask ++ * this has the effect of quieting caps that are not ++ * supposed to be logged ++ */ ++ new_cxt->caps_logged = profile->quiet_caps; ++ new_cxt->cookie = cookie; ++ new_cxt->task = task; ++ new_cxt->profile = aa_dup_profile(profile); ++ new_cxt->previous_profile = aa_dup_profile(previous_profile); ++ list_move(&new_cxt->list, &profile->task_contexts); ++ } ++ rcu_assign_pointer(task->security, new_cxt); ++} diff --git a/kernel-patches/2.6.25/apparmor-misc.diff b/kernel-patches/2.6.25/apparmor-misc.diff new file mode 100644 index 000000000..34c963b08 --- /dev/null +++ b/kernel-patches/2.6.25/apparmor-misc.diff @@ -0,0 +1,1418 @@ +From: John Johansen +Subject: AppArmor: all the rest + +All the things that didn't nicely fit in a category on their own: kbuild +code, declararions and inline functions, /sys/kernel/security/apparmor +filesystem for controlling apparmor from user space, profile list +functions, locking documentation, /proc/$pid/task/$tid/attr/current +access. + +Signed-off-by: John Johansen +Signed-off-by: Andreas Gruenbacher + +--- + security/apparmor/Kconfig | 42 ++++ + security/apparmor/Makefile | 13 + + security/apparmor/apparmor.h | 367 +++++++++++++++++++++++++++++++++++++++++ + security/apparmor/apparmorfs.c | 280 +++++++++++++++++++++++++++++++ + security/apparmor/inline.h | 250 +++++++++++++++++++++++++++ + security/apparmor/list.c | 156 +++++++++++++++++ + security/apparmor/locking.txt | 68 +++++++ + security/apparmor/procattr.c | 195 +++++++++++++++++++++ + 8 files changed, 1371 insertions(+) + +--- /dev/null ++++ b/security/apparmor/Kconfig +@@ -0,0 +1,42 @@ ++config SECURITY_APPARMOR ++ bool "AppArmor support" ++ depends on SECURITY ++ select AUDIT ++ help ++ This enables the AppArmor security module. ++ Required userspace tools (if they are not included in your ++ distribution) and further information may be found at ++ ++ ++ If you are unsure how to answer this question, answer N. ++ ++config SECURITY_APPARMOR_BOOTPARAM_VALUE ++ int "AppArmor boot parameter default value" ++ depends on SECURITY_APPARMOR ++ range 0 1 ++ default 1 ++ help ++ This option sets the default value for the kernel parameter ++ 'apparmor', which allows AppArmor to be enabled or disabled ++ at boot. If this option is set to 0 (zero), the AppArmor ++ kernel parameter will default to 0, disabling AppArmor at ++ bootup. If this option is set to 1 (one), the AppArmor ++ kernel parameter will default to 1, enabling AppArmor at ++ bootup. ++ ++ If you are unsure how to answer this question, answer 1. ++ ++config SECURITY_APPARMOR_DISABLE ++ bool "AppArmor runtime disable" ++ depends on SECURITY_APPARMOR ++ default n ++ help ++ This option enables writing to a apparmorfs node 'disable', which ++ allows AppArmor to be disabled at runtime prior to the policy load. ++ AppArmor will then remain disabled until the next boot. ++ This option is similar to the apparmor.enabled=0 boot parameter, ++ but is to support runtime disabling of AppArmor, e.g. from ++ /sbin/init, for portability across platforms where boot ++ parameters are difficult to employ. ++ ++ If you are unsure how to answer this question, answer N. +--- /dev/null ++++ b/security/apparmor/Makefile +@@ -0,0 +1,13 @@ ++# Makefile for AppArmor Linux Security Module ++# ++obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o ++ ++apparmor-y := main.o list.o procattr.o lsm.o apparmorfs.o \ ++ module_interface.o match.o ++ ++quiet_cmd_make-caps = GEN $@ ++cmd_make-caps = sed -n -e "/CAP_FS_MASK/d" -e "s/^\#define[ \\t]\\+CAP_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z > $@ ++ ++$(obj)/main.o : $(obj)/capability_names.h ++$(obj)/capability_names.h : $(srctree)/include/linux/capability.h ++ $(call cmd,make-caps) +--- /dev/null ++++ b/security/apparmor/apparmor.h +@@ -0,0 +1,367 @@ ++/* ++ * 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 internal prototypes ++ */ ++ ++#ifndef __APPARMOR_H ++#define __APPARMOR_H ++ ++#include ++#include ++#include ++#include ++ ++/* ++ * We use MAY_READ, MAY_WRITE, MAY_EXEC, MAY_APPEND and the following flags ++ * for profile permissions ++ */ ++#define AA_MAY_LINK 0x0010 ++#define AA_MAY_LOCK 0x0020 ++#define AA_EXEC_MMAP 0x0040 ++#define AA_MAY_MOUNT 0x0080 /* no direct audit mapping */ ++#define AA_EXEC_UNSAFE 0x0100 ++#define AA_EXEC_INHERIT 0x0200 ++#define AA_EXEC_MOD_0 0x0400 ++#define AA_EXEC_MOD_1 0x0800 ++#define AA_EXEC_MOD_2 0x1000 ++#define AA_EXEC_MOD_3 0x2000 ++ ++#define AA_BASE_PERMS (MAY_READ | MAY_WRITE | MAY_EXEC | \ ++ MAY_APPEND | AA_MAY_LINK | \ ++ AA_MAY_LOCK | AA_EXEC_MMAP | \ ++ AA_MAY_MOUNT | AA_EXEC_UNSAFE | \ ++ AA_EXEC_INHERIT | AA_EXEC_MOD_0 | \ ++ AA_EXEC_MOD_1 | AA_EXEC_MOD_2 | \ ++ AA_EXEC_MOD_3) ++ ++#define AA_EXEC_MODIFIERS (AA_EXEC_MOD_0 | AA_EXEC_MOD_1 | \ ++ AA_EXEC_MOD_2 | AA_EXEC_MOD_3) ++ ++#define AA_EXEC_TYPE (AA_EXEC_UNSAFE | AA_EXEC_INHERIT | \ ++ AA_EXEC_MODIFIERS) ++ ++#define AA_EXEC_UNCONFINED AA_EXEC_MOD_0 ++#define AA_EXEC_PROFILE AA_EXEC_MOD_1 ++#define AA_EXEC_CHILD (AA_EXEC_MOD_0 | AA_EXEC_MOD_1) ++/* remaining exec modes are index into profile name table */ ++#define AA_EXEC_INDEX(mode) ((mode & AA_EXEC_MODIFIERS) >> 10) ++ ++#define AA_USER_SHIFT 0 ++#define AA_OTHER_SHIFT 14 ++ ++#define AA_USER_PERMS (AA_BASE_PERMS << AA_USER_SHIFT) ++#define AA_OTHER_PERMS (AA_BASE_PERMS << AA_OTHER_SHIFT) ++ ++#define AA_FILE_PERMS (AA_USER_PERMS | AA_OTHER_PERMS) ++ ++#define AA_LINK_BITS ((AA_MAY_LINK << AA_USER_SHIFT) | \ ++ (AA_MAY_LINK << AA_OTHER_SHIFT)) ++ ++#define AA_USER_EXEC (MAY_EXEC << AA_USER_SHIFT) ++#define AA_OTHER_EXEC (MAY_EXEC << AA_OTHER_SHIFT) ++ ++#define AA_USER_EXEC_TYPE (AA_EXEC_TYPE << AA_USER_SHIFT) ++#define AA_OTHER_EXEC_TYPE (AA_EXEC_TYPE << AA_OTHER_SHIFT) ++ ++#define AA_EXEC_BITS (AA_USER_EXEC | AA_OTHER_EXEC) ++ ++#define ALL_AA_EXEC_UNSAFE ((AA_EXEC_UNSAFE << AA_USER_SHIFT) | \ ++ (AA_EXEC_UNSAFE << AA_OTHER_SHIFT)) ++ ++#define ALL_AA_EXEC_TYPE (AA_USER_EXEC_TYPE | AA_OTHER_EXEC_TYPE) ++ ++/* overloaded permissions for link pairs */ ++#define AA_LINK_SUBSET_TEST 0x0020 ++ ++#define AA_USER_PTRACE 0x10000000 ++#define AA_OTHER_PTRACE 0x20000000 ++#define AA_PTRACE_PERMS (AA_USER_PTRACE | AA_OTHER_PTRACE) ++ ++/* shared permissions that are not duplicated in user::other */ ++#define AA_CHANGE_HAT 0x40000000 ++#define AA_CHANGE_PROFILE 0x80000000 ++ ++#define AA_SHARED_PERMS (AA_CHANGE_HAT | AA_CHANGE_PROFILE) ++ ++#define AA_VALID_PERM_MASK (AA_FILE_PERMS | AA_PTRACE_PERMS | \ ++ AA_SHARED_PERMS) ++ ++/* audit bits for the second accept field */ ++#define AUDIT_FILE_MASK 0x1fc07f ++#define AUDIT_QUIET_MASK(mask) ((mask >> 7) & AUDIT_FILE_MASK) ++#define AA_VALID_PERM2_MASK 0x0fffffff ++ ++#define AA_SECURE_EXEC_NEEDED 1 ++ ++/* Control parameters (0 or 1), settable thru module/boot flags or ++ * via /sys/kernel/security/apparmor/control */ ++extern int apparmor_complain; ++extern int apparmor_debug; ++extern int apparmor_audit; ++extern int apparmor_logsyscall; ++extern unsigned int apparmor_path_max; ++ ++#define PROFILE_COMPLAIN(_profile) \ ++ (apparmor_complain == 1 || ((_profile) && (_profile)->flags.complain)) ++ ++#define APPARMOR_COMPLAIN(_cxt) \ ++ (apparmor_complain == 1 || \ ++ ((_cxt) && (_cxt)->profile && (_cxt)->profile->flags.complain)) ++ ++#define PROFILE_AUDIT(_profile) \ ++ (apparmor_audit == 1 || ((_profile) && (_profile)->flags.audit)) ++ ++#define APPARMOR_AUDIT(_cxt) \ ++ (apparmor_audit == 1 || \ ++ ((_cxt) && (_cxt)->profile && (_cxt)->profile->flags.audit)) ++ ++/* ++ * DEBUG remains global (no per profile flag) since it is mostly used in sysctl ++ * which is not related to profile accesses. ++ */ ++ ++#define AA_DEBUG(fmt, args...) \ ++ do { \ ++ if (apparmor_debug) \ ++ printk(KERN_DEBUG "AppArmor: " fmt, ##args); \ ++ } while (0) ++ ++#define AA_ERROR(fmt, args...) printk(KERN_ERR "AppArmor: " fmt, ##args) ++ ++struct aa_profile; ++ ++/* struct aa_namespace - namespace for a set of profiles ++ * @name: the name of the namespace ++ * @list: list the namespace is on ++ * @profiles: list of profile in the namespace ++ * @profile_count: the number of profiles in the namespace ++ * @null_complain_profile: special profile used for learning in this namespace ++ * @count: reference count on the namespace ++ * @lock: lock for adding/removing profile to the namespace ++ */ ++struct aa_namespace { ++ char *name; ++ struct list_head list; ++ struct list_head profiles; ++ int profile_count; ++ struct aa_profile *null_complain_profile; ++ ++ struct kref count; ++ rwlock_t lock; ++}; ++ ++/* struct aa_profile - basic confinement data ++ * @name: the profiles name ++ * @list: list this profile is on ++ * @ns: namespace the profile is in ++ * @file_rules: dfa containing the profiles file rules ++ * @flags: flags controlling profile behavior ++ * @isstale: flag indicating if profile is stale ++ * @set_caps: capabilities that are being set ++ * @capabilities: capabilities mask ++ * @audit_caps: caps that are to be audited ++ * @quiet_caps: caps that should not be audited ++ * @capabilities: capabilities granted by the process ++ * @count: reference count of the profile ++ * @task_contexts: list of tasks confined by profile ++ * @lock: lock for the task_contexts list ++ * @network_families: basic network permissions ++ * @audit_network: which network permissions to force audit ++ * @quiet_network: which network permissions to quiet rejects ++ * ++ * The AppArmor profile contains the basic confinement data. Each profile ++ * has a name, and all nonstale profile are in a profile namespace. ++ * ++ * The task_contexts list and the isstale flag are protected by the ++ * profile lock. ++ * ++ * If a task context is moved between two profiles, we first need to grab ++ * both profile locks. lock_both_profiles() does that in a deadlock-safe ++ * way. ++ */ ++struct aa_profile { ++ char *name; ++ struct list_head list; ++ struct aa_namespace *ns; ++ ++ int exec_table_size; ++ char **exec_table; ++ struct aa_dfa *file_rules; ++ struct { ++ int complain; ++ int audit; ++ } flags; ++ int isstale; ++ ++ kernel_cap_t set_caps; ++ kernel_cap_t capabilities; ++ kernel_cap_t audit_caps; ++ kernel_cap_t quiet_caps; ++ ++ struct kref count; ++ struct list_head task_contexts; ++ spinlock_t lock; ++ unsigned long int_flags; ++}; ++ ++extern struct list_head profile_ns_list; ++extern rwlock_t profile_ns_list_lock; ++extern struct mutex aa_interface_lock; ++ ++/** ++ * struct aa_task_context - primary label for confined tasks ++ * @profile: the current profile ++ * @previous_profile: profile the task may return to ++ * @cookie: magic value the task must know for returning to @previous_profile ++ * @list: list this aa_task_context is on ++ * @task: task that the aa_task_context confines ++ * @rcu: rcu head used when freeing the aa_task_context ++ * @caps_logged: caps that have previously generated log entries ++ * ++ * Contains the task's current profile (which could change due to ++ * change_hat). Plus the hat_magic needed during change_hat. ++ */ ++struct aa_task_context { ++ struct aa_profile *profile; ++ struct aa_profile *previous_profile; ++ u64 cookie; ++ struct list_head list; ++ struct task_struct *task; ++ struct rcu_head rcu; ++ kernel_cap_t caps_logged; ++}; ++ ++extern struct aa_namespace *default_namespace; ++ ++/* aa_audit - AppArmor auditing structure ++ * Structure is populated by access control code and passed to aa_audit which ++ * provides for a single point of logging. ++ */ ++ ++struct aa_audit { ++ const char *operation; ++ gfp_t gfp_mask; ++ const char *info; ++ const char *name; ++ const char *name2; ++ const char *name3; ++ int request_mask, denied_mask, audit_mask; ++ struct iattr *iattr; ++ pid_t task, parent; ++ int error_code; ++}; ++ ++/* Flags for the permission check functions */ ++#define AA_CHECK_FD 1 /* coming from a file descriptor */ ++#define AA_CHECK_DIR 2 /* file type is directory */ ++ ++/* lock subtypes so lockdep does not raise false dependencies */ ++enum aa_lock_class { ++ aa_lock_normal, ++ aa_lock_nested, ++ aa_lock_task_release ++}; ++ ++/* main.c */ ++extern int alloc_default_namespace(void); ++extern void free_default_namespace(void); ++extern int aa_audit_message(struct aa_profile *profile, struct aa_audit *sa, ++ int type); ++void aa_audit_hint(struct aa_profile *profile, struct aa_audit *sa); ++void aa_audit_status(struct aa_profile *profile, struct aa_audit *sa); ++int aa_audit_reject(struct aa_profile *profile, struct aa_audit *sa); ++extern int aa_audit_syscallreject(struct aa_profile *profile, gfp_t gfp, ++ const char *); ++extern int aa_audit(struct aa_profile *profile, struct aa_audit *); ++ ++extern int aa_attr(struct aa_profile *profile, struct dentry *dentry, ++ struct vfsmount *mnt, struct iattr *iattr); ++extern int aa_perm_xattr(struct aa_profile *profile, const char *operation, ++ struct dentry *dentry, struct vfsmount *mnt, ++ int mask, int check); ++extern int aa_capability(struct aa_task_context *cxt, int cap); ++extern int aa_perm(struct aa_profile *profile, const char *operation, ++ struct dentry *dentry, struct vfsmount *mnt, int mask, ++ int check); ++extern int aa_perm_dir(struct aa_profile *profile, const char *operation, ++ struct dentry *dentry, struct vfsmount *mnt, ++ int mask); ++extern int aa_perm_path(struct aa_profile *, const char *operation, ++ const char *name, int mask, uid_t uid); ++extern int aa_link(struct aa_profile *profile, ++ struct dentry *link, struct vfsmount *link_mnt, ++ struct dentry *target, struct vfsmount *target_mnt); ++extern int aa_clone(struct task_struct *task); ++extern int aa_register(struct linux_binprm *bprm); ++extern void aa_release(struct task_struct *task); ++extern int aa_change_hat(const char *id, u64 hat_magic); ++extern int aa_change_profile(const char *ns_name, const char *name); ++extern struct aa_profile *__aa_replace_profile(struct task_struct *task, ++ struct aa_profile *profile); ++extern struct aa_task_context *lock_task_and_profiles(struct task_struct *task, ++ struct aa_profile *profile); ++extern void unlock_task_and_profiles(struct task_struct *task, ++ struct aa_task_context *cxt, ++ struct aa_profile *profile); ++extern void aa_change_task_context(struct task_struct *task, ++ struct aa_task_context *new_cxt, ++ struct aa_profile *profile, u64 cookie, ++ struct aa_profile *previous_profile); ++extern int aa_may_ptrace(struct aa_task_context *cxt, ++ struct aa_profile *tracee); ++ ++/* lsm.c */ ++extern int apparmor_initialized; ++extern void info_message(const char *str); ++extern void apparmor_disable(void); ++ ++/* list.c */ ++extern struct aa_namespace *__aa_find_namespace(const char *name, ++ struct list_head *list); ++extern struct aa_profile *__aa_find_profile(const char *name, ++ struct list_head *list); ++extern void aa_profile_ns_list_release(void); ++ ++/* module_interface.c */ ++extern ssize_t aa_add_profile(void *, size_t); ++extern ssize_t aa_replace_profile(void *, size_t); ++extern ssize_t aa_remove_profile(char *, size_t); ++extern struct aa_namespace *alloc_aa_namespace(char *name); ++extern void free_aa_namespace(struct aa_namespace *ns); ++extern void free_aa_namespace_kref(struct kref *kref); ++extern struct aa_profile *alloc_aa_profile(void); ++extern void free_aa_profile(struct aa_profile *profile); ++extern void free_aa_profile_kref(struct kref *kref); ++extern void aa_unconfine_tasks(struct aa_profile *profile); ++ ++/* procattr.c */ ++extern int aa_getprocattr(struct aa_profile *profile, char **string, ++ unsigned *len); ++extern int aa_setprocattr_changehat(char *args); ++extern int aa_setprocattr_changeprofile(char *args); ++extern int aa_setprocattr_setprofile(struct task_struct *task, char *args); ++ ++/* apparmorfs.c */ ++extern int create_apparmorfs(void); ++extern void destroy_apparmorfs(void); ++ ++/* match.c */ ++extern struct aa_dfa *aa_match_alloc(void); ++extern void aa_match_free(struct aa_dfa *dfa); ++extern int unpack_dfa(struct aa_dfa *dfa, void *blob, size_t size); ++extern int verify_dfa(struct aa_dfa *dfa); ++extern unsigned int aa_dfa_match(struct aa_dfa *dfa, const char *str, int *); ++extern unsigned int aa_dfa_next_state(struct aa_dfa *dfa, unsigned int start, ++ const char *str); ++extern unsigned int aa_match_state(struct aa_dfa *dfa, unsigned int start, ++ const char *str, unsigned int *final); ++extern unsigned int aa_dfa_null_transition(struct aa_dfa *dfa, ++ unsigned int start); ++ ++#endif /* __APPARMOR_H */ +--- /dev/null ++++ b/security/apparmor/apparmorfs.c +@@ -0,0 +1,280 @@ ++/* ++ * 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 filesystem (part of securityfs) ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "apparmor.h" ++#include "inline.h" ++ ++static char *aa_simple_write_to_buffer(const char __user *userbuf, ++ size_t alloc_size, size_t copy_size, ++ loff_t *pos, const char *operation) ++{ ++ struct aa_profile *profile; ++ char *data; ++ ++ if (*pos != 0) { ++ /* only writes from pos 0, that is complete writes */ ++ data = ERR_PTR(-ESPIPE); ++ goto out; ++ } ++ ++ /* ++ * Don't allow confined processes to load/replace/remove profiles. ++ * No sane person would add rules allowing this to a profile ++ * but we enforce the restriction anyways. ++ */ ++ profile = aa_get_profile(current); ++ if (profile) { ++ struct aa_audit sa; ++ memset(&sa, 0, sizeof(sa)); ++ sa.operation = operation; ++ sa.gfp_mask = GFP_KERNEL; ++ sa.error_code = -EACCES; ++ data = ERR_PTR(aa_audit_reject(profile, &sa)); ++ aa_put_profile(profile); ++ goto out; ++ } ++ ++ data = vmalloc(alloc_size); ++ if (data == NULL) { ++ data = ERR_PTR(-ENOMEM); ++ goto out; ++ } ++ ++ if (copy_from_user(data, userbuf, copy_size)) { ++ vfree(data); ++ data = ERR_PTR(-EFAULT); ++ goto out; ++ } ++ ++out: ++ return data; ++} ++ ++/* apparmor/profiles */ ++extern struct seq_operations apparmorfs_profiles_op; ++ ++static int aa_profiles_open(struct inode *inode, struct file *file) ++{ ++ return seq_open(file, &apparmorfs_profiles_op); ++} ++ ++ ++static int aa_profiles_release(struct inode *inode, struct file *file) ++{ ++ return seq_release(inode, file); ++} ++ ++static struct file_operations apparmorfs_profiles_fops = { ++ .open = aa_profiles_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = aa_profiles_release, ++}; ++ ++/* apparmor/matching */ ++static ssize_t aa_matching_read(struct file *file, char __user *buf, ++ size_t size, loff_t *ppos) ++{ ++ const char *matching = "pattern=aadfa audit perms=rwxamlk/ user::other"; ++ ++ return simple_read_from_buffer(buf, size, ppos, matching, ++ strlen(matching)); ++} ++ ++static struct file_operations apparmorfs_matching_fops = { ++ .read = aa_matching_read, ++}; ++ ++/* apparmor/features */ ++static ssize_t aa_features_read(struct file *file, char __user *buf, ++ size_t size, loff_t *ppos) ++{ ++ const char *features = "file=3.0 capability=2.0 network=1.0 " ++ "change_hat=1.4 change_profile=1.0 " ++ "aanamespaces=1.0"; ++ ++ return simple_read_from_buffer(buf, size, ppos, features, ++ strlen(features)); ++} ++ ++static struct file_operations apparmorfs_features_fops = { ++ .read = aa_features_read, ++}; ++ ++/* apparmor/.load */ ++static ssize_t aa_profile_load(struct file *f, const char __user *buf, ++ size_t size, loff_t *pos) ++{ ++ char *data; ++ ssize_t error; ++ ++ data = aa_simple_write_to_buffer(buf, size, size, pos, "profile_load"); ++ ++ error = PTR_ERR(data); ++ if (!IS_ERR(data)) { ++ error = aa_add_profile(data, size); ++ vfree(data); ++ } ++ ++ return error; ++} ++ ++ ++static struct file_operations apparmorfs_profile_load = { ++ .write = aa_profile_load ++}; ++ ++/* apparmor/.replace */ ++static ssize_t aa_profile_replace(struct file *f, const char __user *buf, ++ size_t size, loff_t *pos) ++{ ++ char *data; ++ ssize_t error; ++ ++ data = aa_simple_write_to_buffer(buf, size, size, pos, ++ "profile_replace"); ++ ++ error = PTR_ERR(data); ++ if (!IS_ERR(data)) { ++ error = aa_replace_profile(data, size); ++ vfree(data); ++ } ++ ++ return error; ++} ++ ++ ++static struct file_operations apparmorfs_profile_replace = { ++ .write = aa_profile_replace ++}; ++ ++/* apparmor/.remove */ ++static ssize_t aa_profile_remove(struct file *f, const char __user *buf, ++ size_t size, loff_t *pos) ++{ ++ char *data; ++ ssize_t error; ++ ++ /* ++ * aa_remove_profile needs a null terminated string so 1 extra ++ * byte is allocated and the copied data is null terminated. ++ */ ++ data = aa_simple_write_to_buffer(buf, size + 1, size, pos, ++ "profile_remove"); ++ ++ error = PTR_ERR(data); ++ if (!IS_ERR(data)) { ++ data[size] = 0; ++ error = aa_remove_profile(data, size); ++ vfree(data); ++ } ++ ++ return error; ++} ++ ++static struct file_operations apparmorfs_profile_remove = { ++ .write = aa_profile_remove ++}; ++ ++static struct dentry *apparmor_dentry; ++ ++static void aafs_remove(const char *name) ++{ ++ struct dentry *dentry; ++ ++ dentry = lookup_one_len(name, apparmor_dentry, strlen(name)); ++ if (!IS_ERR(dentry)) { ++ securityfs_remove(dentry); ++ dput(dentry); ++ } ++} ++ ++static int aafs_create(const char *name, int mask, struct file_operations *fops) ++{ ++ struct dentry *dentry; ++ ++ dentry = securityfs_create_file(name, S_IFREG | mask, apparmor_dentry, ++ NULL, fops); ++ ++ return IS_ERR(dentry) ? PTR_ERR(dentry) : 0; ++} ++ ++void destroy_apparmorfs(void) ++{ ++ if (apparmor_dentry) { ++ aafs_remove(".remove"); ++ aafs_remove(".replace"); ++ aafs_remove(".load"); ++ aafs_remove("matching"); ++ aafs_remove("features"); ++ aafs_remove("profiles"); ++ securityfs_remove(apparmor_dentry); ++ apparmor_dentry = NULL; ++ } ++} ++ ++int create_apparmorfs(void) ++{ ++ int error; ++ ++ if (!apparmor_initialized) ++ return 0; ++ ++ if (apparmor_dentry) { ++ AA_ERROR("%s: AppArmor securityfs already exists\n", ++ __FUNCTION__); ++ return -EEXIST; ++ } ++ ++ apparmor_dentry = securityfs_create_dir("apparmor", NULL); ++ if (IS_ERR(apparmor_dentry)) { ++ error = PTR_ERR(apparmor_dentry); ++ apparmor_dentry = NULL; ++ goto error; ++ } ++ error = aafs_create("profiles", 0440, &apparmorfs_profiles_fops); ++ if (error) ++ goto error; ++ error = aafs_create("matching", 0444, &apparmorfs_matching_fops); ++ if (error) ++ goto error; ++ error = aafs_create("features", 0444, &apparmorfs_features_fops); ++ if (error) ++ goto error; ++ error = aafs_create(".load", 0640, &apparmorfs_profile_load); ++ if (error) ++ goto error; ++ error = aafs_create(".replace", 0640, &apparmorfs_profile_replace); ++ if (error) ++ goto error; ++ error = aafs_create(".remove", 0640, &apparmorfs_profile_remove); ++ if (error) ++ goto error; ++ ++ /* Report that AppArmor fs is enabled */ ++ info_message("AppArmor Filesystem Enabled"); ++ return 0; ++ ++error: ++ destroy_apparmorfs(); ++ AA_ERROR("Error creating AppArmor securityfs\n"); ++ apparmor_disable(); ++ return error; ++} ++ ++fs_initcall(create_apparmorfs); ++ +--- /dev/null ++++ b/security/apparmor/inline.h +@@ -0,0 +1,250 @@ ++/* ++ * 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. ++ */ ++ ++#ifndef __INLINE_H ++#define __INLINE_H ++ ++#include ++ ++#include "match.h" ++ ++static inline int mediated_filesystem(struct inode *inode) ++{ ++ return !(inode->i_sb->s_flags & MS_NOUSER); ++} ++ ++static inline struct aa_task_context *aa_task_context(struct task_struct *task) ++{ ++ return (struct aa_task_context *) rcu_dereference(task->security); ++} ++ ++static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns) ++{ ++ if (ns) ++ kref_get(&(ns->count)); ++ ++ return ns; ++} ++ ++static inline void aa_put_namespace(struct aa_namespace *ns) ++{ ++ if (ns) ++ kref_put(&ns->count, free_aa_namespace_kref); ++} ++ ++ ++static inline struct aa_namespace *aa_find_namespace(const char *name) ++{ ++ struct aa_namespace *ns = NULL; ++ ++ read_lock(&profile_ns_list_lock); ++ ns = aa_get_namespace(__aa_find_namespace(name, &profile_ns_list)); ++ read_unlock(&profile_ns_list_lock); ++ ++ return ns; ++} ++ ++/** ++ * aa_dup_profile - increment refcount on profile @p ++ * @p: profile ++ */ ++static inline struct aa_profile *aa_dup_profile(struct aa_profile *p) ++{ ++ if (p) ++ kref_get(&(p->count)); ++ ++ return p; ++} ++ ++/** ++ * aa_put_profile - decrement refcount on profile @p ++ * @p: profile ++ */ ++static inline void aa_put_profile(struct aa_profile *p) ++{ ++ if (p) ++ kref_put(&p->count, free_aa_profile_kref); ++} ++ ++static inline struct aa_profile *aa_get_profile(struct task_struct *task) ++{ ++ struct aa_task_context *cxt; ++ struct aa_profile *profile = NULL; ++ ++ rcu_read_lock(); ++ cxt = aa_task_context(task); ++ if (cxt) { ++ profile = cxt->profile; ++ aa_dup_profile(profile); ++ } ++ rcu_read_unlock(); ++ ++ return profile; ++} ++ ++static inline struct aa_profile *aa_find_profile(struct aa_namespace *ns, ++ const char *name) ++{ ++ struct aa_profile *profile = NULL; ++ ++ read_lock(&ns->lock); ++ profile = aa_dup_profile(__aa_find_profile(name, &ns->profiles)); ++ read_unlock(&ns->lock); ++ ++ return profile; ++} ++ ++static inline struct aa_task_context *aa_alloc_task_context(gfp_t flags) ++{ ++ struct aa_task_context *cxt; ++ ++ cxt = kzalloc(sizeof(*cxt), flags); ++ if (cxt) { ++ INIT_LIST_HEAD(&cxt->list); ++ INIT_RCU_HEAD(&cxt->rcu); ++ } ++ ++ return cxt; ++} ++ ++static inline void aa_free_task_context(struct aa_task_context *cxt) ++{ ++ if (cxt) { ++ aa_put_profile(cxt->profile); ++ aa_put_profile(cxt->previous_profile); ++ kfree(cxt); ++ } ++} ++ ++/** ++ * lock_profile - lock a profile ++ * @profile: the profile to lock ++ * ++ * While the profile is locked, local interrupts are disabled. This also ++ * gives us RCU reader safety. ++ */ ++static inline void lock_profile_nested(struct aa_profile *profile, ++ enum aa_lock_class lock_class) ++{ ++ /* ++ * Lock the profile. ++ * ++ * Need to disable interrupts here because this lock is used in ++ * the task_free_security hook, which may run in RCU context. ++ */ ++ if (profile) ++ spin_lock_irqsave_nested(&profile->lock, profile->int_flags, ++ lock_class); ++} ++ ++static inline void lock_profile(struct aa_profile *profile) ++{ ++ lock_profile_nested(profile, aa_lock_normal); ++} ++ ++/** ++ * unlock_profile - unlock a profile ++ * @profile: the profile to unlock ++ */ ++static inline void unlock_profile(struct aa_profile *profile) ++{ ++ /* Unlock the profile. */ ++ if (profile) ++ spin_unlock_irqrestore(&profile->lock, profile->int_flags); ++} ++ ++/** ++ * lock_both_profiles - lock two profiles in a deadlock-free way ++ * @profile1: profile to lock (may be NULL) ++ * @profile2: profile to lock (may be NULL) ++ * ++ * The order in which profiles are passed into lock_both_profiles() / ++ * unlock_both_profiles() does not matter. ++ * While the profile is locked, local interrupts are disabled. This also ++ * gives us RCU reader safety. ++ */ ++static inline void lock_both_profiles(struct aa_profile *profile1, ++ struct aa_profile *profile2) ++{ ++ /* ++ * Lock the two profiles. ++ * ++ * We need to disable interrupts because the profile locks are ++ * used in the task_free_security hook, which may run in RCU ++ * context. ++ * ++ * Do not nest spin_lock_irqsave()/spin_unlock_irqresore(): ++ * interrupts only need to be turned off once. ++ */ ++ if (!profile1 || profile1 == profile2) { ++ if (profile2) ++ spin_lock_irqsave_nested(&profile2->lock, ++ profile2->int_flags, ++ aa_lock_normal); ++ } else if (profile1 > profile2) { ++ /* profile1 cannot be NULL here. */ ++ spin_lock_irqsave_nested(&profile1->lock, profile1->int_flags, ++ aa_lock_normal); ++ if (profile2) ++ spin_lock_nested(&profile2->lock, aa_lock_nested); ++ ++ } else { ++ /* profile2 cannot be NULL here. */ ++ spin_lock_irqsave_nested(&profile2->lock, profile2->int_flags, ++ aa_lock_normal); ++ spin_lock_nested(&profile1->lock, aa_lock_nested); ++ } ++} ++ ++/** ++ * unlock_both_profiles - unlock two profiles in a deadlock-free way ++ * @profile1: profile to unlock (may be NULL) ++ * @profile2: profile to unlock (may be NULL) ++ * ++ * The order in which profiles are passed into lock_both_profiles() / ++ * unlock_both_profiles() does not matter. ++ * While the profile is locked, local interrupts are disabled. This also ++ * gives us RCU reader safety. ++ */ ++static inline void unlock_both_profiles(struct aa_profile *profile1, ++ struct aa_profile *profile2) ++{ ++ /* Unlock the two profiles. */ ++ if (!profile1 || profile1 == profile2) { ++ if (profile2) ++ spin_unlock_irqrestore(&profile2->lock, ++ profile2->int_flags); ++ } else if (profile1 > profile2) { ++ /* profile1 cannot be NULL here. */ ++ if (profile2) ++ spin_unlock(&profile2->lock); ++ spin_unlock_irqrestore(&profile1->lock, profile1->int_flags); ++ } else { ++ /* profile2 cannot be NULL here. */ ++ spin_unlock(&profile1->lock); ++ spin_unlock_irqrestore(&profile2->lock, profile2->int_flags); ++ } ++} ++ ++static inline unsigned int aa_match(struct aa_dfa *dfa, const char *pathname, ++ int *audit_mask) ++{ ++ if (dfa) ++ return aa_dfa_match(dfa, pathname, audit_mask); ++ if (audit_mask) ++ *audit_mask = 0; ++ return 0; ++} ++ ++static inline int dfa_audit_mask(struct aa_dfa *dfa, unsigned int state) ++{ ++ return ACCEPT_TABLE2(dfa)[state]; ++} ++ ++#endif /* __INLINE_H__ */ +--- /dev/null ++++ b/security/apparmor/list.c +@@ -0,0 +1,156 @@ ++/* ++ * 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 Profile List Management ++ */ ++ ++#include ++#include "apparmor.h" ++#include "inline.h" ++ ++/* list of profile namespaces and lock */ ++LIST_HEAD(profile_ns_list); ++rwlock_t profile_ns_list_lock = RW_LOCK_UNLOCKED; ++ ++/** ++ * __aa_find_namespace - look up a profile namespace on the namespace list ++ * @name: name of namespace to find ++ * @head: list to search ++ * ++ * Returns a pointer to the namespace on the list, or NULL if no namespace ++ * called @name exists. The caller must hold the profile_ns_list_lock. ++ */ ++struct aa_namespace *__aa_find_namespace(const char *name, ++ struct list_head *head) ++{ ++ struct aa_namespace *ns; ++ ++ list_for_each_entry(ns, head, list) { ++ if (!strcmp(ns->name, name)) ++ return ns; ++ } ++ ++ return NULL; ++} ++ ++/** ++ * __aa_find_profile - look up a profile on the profile list ++ * @name: name of profile to find ++ * @head: list to search ++ * ++ * Returns a pointer to the profile on the list, or NULL if no profile ++ * called @name exists. The caller must hold the profile_list_lock. ++ */ ++struct aa_profile *__aa_find_profile(const char *name, struct list_head *head) ++{ ++ struct aa_profile *profile; ++ ++ list_for_each_entry(profile, head, list) { ++ if (!strcmp(profile->name, name)) ++ return profile; ++ } ++ ++ return NULL; ++} ++ ++static void aa_profile_list_release(struct list_head *head) ++{ ++ struct aa_profile *profile, *tmp; ++ list_for_each_entry_safe(profile, tmp, head, list) { ++ /* Remove the profile from each task context it is on. */ ++ lock_profile(profile); ++ profile->isstale = 1; ++ aa_unconfine_tasks(profile); ++ list_del_init(&profile->list); ++ unlock_profile(profile); ++ aa_put_profile(profile); ++ } ++} ++ ++/** ++ * aa_profilelist_release - Remove all profiles from profile_list ++ */ ++void aa_profile_ns_list_release(void) ++{ ++ struct aa_namespace *ns, *tmp; ++ ++ /* Remove and release all the profiles on namespace profile lists. */ ++ write_lock(&profile_ns_list_lock); ++ list_for_each_entry_safe(ns, tmp, &profile_ns_list, list) { ++ write_lock(&ns->lock); ++ aa_profile_list_release(&ns->profiles); ++ list_del_init(&ns->list); ++ write_unlock(&ns->lock); ++ aa_put_namespace(ns); ++ } ++ write_unlock(&profile_ns_list_lock); ++} ++ ++static void *p_start(struct seq_file *f, loff_t *pos) ++{ ++ struct aa_namespace *ns; ++ struct aa_profile *profile; ++ loff_t l = *pos; ++ read_lock(&profile_ns_list_lock); ++ if (l--) ++ return NULL; ++ list_for_each_entry(ns, &profile_ns_list, list) { ++ read_lock(&ns->lock); ++ list_for_each_entry(profile, &ns->profiles, list) ++ return profile; ++ read_unlock(&ns->lock); ++ } ++ return NULL; ++} ++ ++static void *p_next(struct seq_file *f, void *p, loff_t *pos) ++{ ++ struct aa_profile *profile = (struct aa_profile *) p; ++ struct list_head *lh = profile->list.next; ++ struct aa_namespace *ns; ++ (*pos)++; ++ if (lh != &profile->ns->profiles) ++ return list_entry(lh, struct aa_profile, list); ++ ++ lh = profile->ns->list.next; ++ read_unlock(&profile->ns->lock); ++ while (lh != &profile_ns_list) { ++ ns = list_entry(lh, struct aa_namespace, list); ++ read_lock(&ns->lock); ++ list_for_each_entry(profile, &ns->profiles, list) ++ return profile; ++ read_unlock(&ns->lock); ++ lh = ns->list.next; ++ } ++ return NULL; ++} ++ ++static void p_stop(struct seq_file *f, void *v) ++{ ++ read_unlock(&profile_ns_list_lock); ++} ++ ++static int seq_show_profile(struct seq_file *f, void *v) ++{ ++ struct aa_profile *profile = (struct aa_profile *)v; ++ if (profile->ns == default_namespace) ++ seq_printf(f, "%s (%s)\n", profile->name, ++ PROFILE_COMPLAIN(profile) ? "complain" : "enforce"); ++ else ++ seq_printf(f, ":%s:%s (%s)\n", profile->ns->name, profile->name, ++ PROFILE_COMPLAIN(profile) ? "complain" : "enforce"); ++ return 0; ++} ++ ++/* Used in apparmorfs.c */ ++struct seq_operations apparmorfs_profiles_op = { ++ .start = p_start, ++ .next = p_next, ++ .stop = p_stop, ++ .show = seq_show_profile, ++}; +--- /dev/null ++++ b/security/apparmor/locking.txt +@@ -0,0 +1,68 @@ ++Locking in AppArmor ++=================== ++ ++Lock hierarchy: ++ ++ aa_interface_lock ++ profile_list_lock ++ aa_profile->lock ++ task_lock() ++ ++ ++Which lock protects what? ++ ++ /-----------------------+-------------------------------\ ++ | Variable | Lock | ++ >-----------------------+-------------------------------< ++ | profile_list | profile_list_lock | ++ +-----------------------+-------------------------------+ ++ | aa_profile | (reference count) | ++ +-----------------------+-------------------------------+ ++ | aa_profile-> | aa_profile->lock | ++ | isstale, | | ++ | task_contexts | | ++ +-----------------------+-------------------------------+ ++ | task_struct->security | read: RCU | ++ | | write: task_lock() | ++ +-----------------------+-------------------------------+ ++ | aa_profile->sub | handle on the profile (list | ++ | | is never modified) | ++ \-----------------------+-------------------------------/ ++ ++(Obviously, the list_heads embedded in data structures are always ++protected with the lock that also protects the list.) ++ ++When moving a task context from one profile to another, we grab both ++profile locks with lock_both_profiles(). This ensures that both locks ++are always taken in the same order, and so we won't deadlock. ++ ++Since task_struct->security is RCU protected the aa_task_struct it ++references is only guarenteed to exist for the rcu cycle. Where ++aa_task_context->profile is needed in blocking operations the ++profile's reference count is incremented and the profile reference ++is used. ++ ++Profiles on profile_list are never stale: when a profile becomes stale, ++it is removed from profile_list at the same time (under profile_list_lock ++and aa_profile->lock). ++ ++The aa_interface_lock is taken whenever user-space modifies the profile ++list, and can sleep. This ensures that profile loading/replacement/removal ++won't race with itself. We release the profile_list_lock as soon as ++possible to avoid stalling exec during profile loading/replacement/removal. ++ ++AppArmor uses lock subtyping to avoid false positives from lockdep. The ++profile lock is often taken nested, but it is guaranteed to be in a lock ++safe order and not the same lock when done, so it is safe. ++ ++A third lock type (aa_lock_task_release) is given to the profile lock ++when it is taken in soft irq context during task release (aa_release). ++This is to avoid a false positive between the task lock and the profile ++lock. In task context the profile lock wraps the task lock with irqs ++off, but the kernel takes the task lock with irqs enabled. This won't ++result in a deadlock because for a deadlock to occur the kernel must ++take dead task A's lock (irqs on), the rcu callback hook freeing ++dead task A must be run and AppArmor must be changing the profile on ++dead task A. The kernel should not be taking a dead task's task_lock ++at the same time the task is being freed by task rcu cleanup other wise ++the task would not be out of its quiescent period. +--- /dev/null ++++ b/security/apparmor/procattr.c +@@ -0,0 +1,195 @@ ++/* ++ * 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 /proc/pid/attr handling ++ */ ++ ++#include "apparmor.h" ++#include "inline.h" ++ ++int aa_getprocattr(struct aa_profile *profile, char **string, unsigned *len) ++{ ++ char *str; ++ ++ if (profile) { ++ const char *mode_str = PROFILE_COMPLAIN(profile) ? ++ " (complain)" : " (enforce)"; ++ int mode_len, name_len, ns_len = 0; ++ ++ mode_len = strlen(mode_str); ++ name_len = strlen(profile->name); ++ if (profile->ns != default_namespace) ++ ns_len = strlen(profile->ns->name) + 2; ++ *len = mode_len + ns_len + name_len + 1; ++ str = kmalloc(*len, GFP_ATOMIC); ++ if (!str) ++ return -ENOMEM; ++ ++ if (ns_len) { ++ *str++ = ':'; ++ memcpy(str, profile->ns->name, ns_len - 2); ++ str += ns_len - 2; ++ *str++ = ':'; ++ } ++ memcpy(str, profile->name, name_len); ++ str += name_len; ++ memcpy(str, mode_str, mode_len); ++ str += mode_len; ++ *str++ = '\n'; ++ str -= *len; ++ } else { ++ const char *unconfined_str = "unconfined\n"; ++ ++ *len = strlen(unconfined_str); ++ str = kmalloc(*len, GFP_ATOMIC); ++ if (!str) ++ return -ENOMEM; ++ ++ memcpy(str, unconfined_str, *len); ++ } ++ *string = str; ++ ++ return 0; ++} ++ ++static char *split_token_from_name(const char *op, char *args, u64 *cookie) ++{ ++ char *name; ++ ++ *cookie = simple_strtoull(args, &name, 16); ++ if ((name == args) || *name != '^') { ++ AA_ERROR("%s: Invalid input '%s'", op, args); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ name++; /* skip ^ */ ++ if (!*name) ++ name = NULL; ++ return name; ++} ++ ++int aa_setprocattr_changehat(char *args) ++{ ++ char *hat; ++ u64 cookie; ++ ++ hat = split_token_from_name("change_hat", args, &cookie); ++ if (IS_ERR(hat)) ++ return PTR_ERR(hat); ++ ++ if (!hat && !cookie) { ++ AA_ERROR("change_hat: Invalid input, NULL hat and NULL magic"); ++ return -EINVAL; ++ } ++ ++ AA_DEBUG("%s: Magic 0x%llx Hat '%s'\n", ++ __FUNCTION__, cookie, hat ? hat : NULL); ++ ++ return aa_change_hat(hat, cookie); ++} ++ ++int aa_setprocattr_changeprofile(char *args) ++{ ++ char *name = args, *ns_name = NULL; ++ ++ if (name[0] == ':') { ++ char *split = strchr(&name[1], ':'); ++ if (split) { ++ *split = 0; ++ ns_name = &name[1]; ++ name = split + 1; ++ } ++ } ++ ++ return aa_change_profile(ns_name, name); ++} ++ ++int aa_setprocattr_setprofile(struct task_struct *task, char *args) ++{ ++ struct aa_profile *old_profile, *new_profile; ++ struct aa_namespace *ns; ++ struct aa_audit sa; ++ char *name, *ns_name = NULL; ++ ++ memset(&sa, 0, sizeof(sa)); ++ sa.operation = "profile_set"; ++ sa.gfp_mask = GFP_KERNEL; ++ sa.task = task->pid; ++ ++ AA_DEBUG("%s: current %d\n", ++ __FUNCTION__, current->pid); ++ ++ name = args; ++ if (args[0] != '/') { ++ char *split = strchr(args, ':'); ++ if (split) { ++ *split = 0; ++ ns_name = args; ++ name = split + 1; ++ } ++ } ++ if (ns_name) ++ ns = aa_find_namespace(ns_name); ++ else ++ ns = aa_get_namespace(default_namespace); ++ if (!ns) { ++ sa.name = ns_name; ++ sa.info = "unknown namespace"; ++ aa_audit_reject(NULL, &sa); ++ aa_put_namespace(ns); ++ return -EINVAL; ++ } ++ ++repeat: ++ if (strcmp(name, "unconfined") == 0) ++ new_profile = NULL; ++ else { ++ new_profile = aa_find_profile(ns, name); ++ if (!new_profile) { ++ sa.name = ns_name; ++ sa.name2 = name; ++ sa.info = "unknown profile"; ++ aa_audit_reject(NULL, &sa); ++ aa_put_namespace(ns); ++ return -EINVAL; ++ } ++ } ++ ++ old_profile = __aa_replace_profile(task, new_profile); ++ if (IS_ERR(old_profile)) { ++ int error; ++ ++ aa_put_profile(new_profile); ++ error = PTR_ERR(old_profile); ++ if (error == -ESTALE) ++ goto repeat; ++ aa_put_namespace(ns); ++ return error; ++ } ++ ++ if (new_profile) { ++ sa.name = ns_name; ++ sa.name2 = name; ++ sa.name3 = old_profile ? old_profile->name : ++ "unconfined"; ++ aa_audit_status(NULL, &sa); ++ } else { ++ if (old_profile) { ++ sa.name = "unconfined"; ++ sa.name2 = old_profile->name; ++ aa_audit_status(NULL, &sa); ++ } else { ++ sa.info = "task is unconfined"; ++ aa_audit_status(NULL, &sa); ++ } ++ } ++ aa_put_namespace(ns); ++ aa_put_profile(old_profile); ++ aa_put_profile(new_profile); ++ return 0; ++} diff --git a/kernel-patches/2.6.25/apparmor-module_interface.diff b/kernel-patches/2.6.25/apparmor-module_interface.diff new file mode 100644 index 000000000..105689193 --- /dev/null +++ b/kernel-patches/2.6.25/apparmor-module_interface.diff @@ -0,0 +1,1349 @@ +From: John Johansen +Subject: AppArmor: Profile loading and manipulation, pathname matching + +Pathname matching, transition table loading, profile loading and +manipulation. + +Signed-off-by: John Johansen +Signed-off-by: Andreas Gruenbacher + +--- + security/apparmor/match.c | 364 ++++++++++++++ + security/apparmor/match.h | 87 +++ + security/apparmor/module_interface.c | 874 +++++++++++++++++++++++++++++++++++ + 3 files changed, 1325 insertions(+) + +--- /dev/null ++++ b/security/apparmor/match.c +@@ -0,0 +1,364 @@ ++/* ++ * 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 ++#include ++#include ++#include "apparmor.h" ++#include "match.h" ++#include "inline.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 = be16_to_cpu(*(u16 *) (blob)); ++ th.td_flags = be16_to_cpu(*(u16 *) (blob + 2)); ++ th.td_lolen = be32_to_cpu(*(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, byte_to_byte); ++ else if (th.td_flags == YYTD_DATA16) ++ UNPACK_ARRAY(table->td_data, blob, th.td_lolen, ++ u16, be16_to_cpu); ++ else ++ UNPACK_ARRAY(table->td_data, blob, th.td_lolen, ++ u32, be32_to_cpu); ++ } ++ ++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_ACCEPT2: ++ 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_ACCEPT2 - 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 && ++ state_count == dfa->tables[YYTD_ID_ACCEPT2 - 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; ++ } ++ ++ /* verify accept permissions */ ++ for (i = 0; i < state_count; i++) { ++ int mode = ACCEPT_TABLE(dfa)[i]; ++ ++ if (mode & ~AA_VALID_PERM_MASK) ++ goto out; ++ if (ACCEPT_TABLE2(dfa)[i] & ~AA_VALID_PERM2_MASK) ++ goto out; ++ ++ /* if any exec modifier is set MAY_EXEC must be set */ ++ if ((mode & AA_USER_EXEC_TYPE) && !(mode & AA_USER_EXEC)) ++ goto out; ++ if ((mode & AA_OTHER_EXEC_TYPE) && !(mode & AA_OTHER_EXEC)) ++ 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_next_state_len - traverse @dfa to find state @str stops at ++ * @dfa: the dfa to match @str against ++ * @start: the state of the dfa to start matching in ++ * @str: the string of bytes to match against the dfa ++ * @len: length of the string of bytes to match ++ * ++ * aa_dfa_next_state will match @str against the dfa and return the state it ++ * finished matching in. The final state can be used to look up the accepting ++ * label, or as the start state of a continuing match. ++ * ++ * aa_dfa_next_state could be implement using this function by doing ++ * return aa_dfa_next_state_len(dfa, start, str, strlen(str)); ++ * but that would require traversing the string twice and be slightly ++ * slower. ++ */ ++unsigned int aa_dfa_next_state_len(struct aa_dfa *dfa, unsigned int start, ++ const char *str, int len) ++{ ++ u16 *def = DEFAULT_TABLE(dfa); ++ u32 *base = BASE_TABLE(dfa); ++ u16 *next = NEXT_TABLE(dfa); ++ u16 *check = CHECK_TABLE(dfa); ++ unsigned int state = start, pos; ++ ++ if (state == 0) ++ return 0; ++ ++ /* current state is , matching character *str */ ++ if (dfa->tables[YYTD_ID_EC - 1]) { ++ u8 *equiv = EQUIV_TABLE(dfa); ++ for (; len; len--) { ++ pos = base[state] + equiv[(u8)*str++]; ++ if (check[pos] == state) ++ state = next[pos]; ++ else ++ state = def[state]; ++ } ++ } else { ++ for (; len; len--) { ++ pos = base[state] + (u8)*str++; ++ if (check[pos] == state) ++ state = next[pos]; ++ else ++ state = def[state]; ++ } ++ } ++ return state; ++} ++ ++/** ++ * aa_dfa_next_state - traverse @dfa to find state @str stops at ++ * @dfa: the dfa to match @str against ++ * @start: the state of the dfa to start matching in ++ * @str: the null terminated string of bytes to match against the dfa ++ * ++ * aa_dfa_next_state will match @str against the dfa and return the state it ++ * finished matching in. The final state can be used to look up the accepting ++ * label, or as the start state of a continuing match. ++ */ ++unsigned int aa_dfa_next_state(struct aa_dfa *dfa, unsigned int start, ++ 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 = start, pos; ++ ++ if (state == 0) ++ return 0; ++ ++ /* current state is , 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 state; ++} ++ ++/** ++ * aa_dfa_null_transition - step to next state after null character ++ * @dfa: the dfa to match against ++ * @start: the state of the dfa to start matching in ++ * ++ * aa_dfa_null_transition transitions to the next state after a null ++ * character which is not used in standard matching and is only ++ * used to seperate pairs. ++ */ ++unsigned int aa_dfa_null_transition(struct aa_dfa *dfa, unsigned int start) ++{ ++ return aa_dfa_next_state_len(dfa, start, "", 1); ++} ++ ++/** ++ * aa_dfa_match - find accept perm for @str in @dfa ++ * @dfa: the dfa to match @str against ++ * @str: the string to match against the dfa ++ * @audit_mask: the audit_mask for the final state ++ * ++ * aa_dfa_match will match @str and return the accept perms for the ++ * final state. ++ */ ++unsigned int aa_dfa_match(struct aa_dfa *dfa, const char *str, int *audit_mask) ++{ ++ int state = aa_dfa_next_state(dfa, DFA_START, str); ++ if (audit_mask) ++ *audit_mask = dfa_audit_mask(dfa, state); ++ return ACCEPT_TABLE(dfa)[state]; ++} ++ ++/** ++ * aa_match_state - find accept perm and state for @str in @dfa ++ * @dfa: the dfa to match @str against ++ * @start: the state to start the match from ++ * @str: the string to match against the dfa ++ * @final: the state that the match finished in ++ * ++ * aa_match_state will match @str and return the accept perms, and @final ++ * state, the match occured in. ++ */ ++unsigned int aa_match_state(struct aa_dfa *dfa, unsigned int start, ++ const char *str, unsigned int *final) ++{ ++ unsigned int state; ++ if (dfa) { ++ state = aa_dfa_next_state(dfa, start, str); ++ if (final) ++ *final = state; ++ return ACCEPT_TABLE(dfa)[state]; ++ } ++ if (final) ++ *final = 0; ++ return 0; ++} ++ +--- /dev/null ++++ b/security/apparmor/match.h +@@ -0,0 +1,87 @@ ++/* ++ * 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 ++ ++#define DFA_START 1 ++ ++/** ++ * 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_ACCEPT2 7 ++#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)) ++#define ACCEPT_TABLE2(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT2 -1]->td_data)) ++ ++struct aa_dfa { ++ struct table_header *tables[YYTD_ID_NXT]; ++}; ++ ++#define byte_to_byte(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,874 @@ ++/* ++ * 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 ++ ++#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_ARRAY, ++ AA_ARRAYEND, ++}; ++ ++/* ++ * 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; ++ char *ns_name; ++}; ++ ++static inline int aa_inbounds(struct aa_ext *e, size_t size) ++{ ++ return (size <= e->end - e->pos); ++} ++ ++/** ++ * 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_u16(struct aa_ext *e, u16 *data, const char *name) ++{ ++ void *pos = e->pos; ++ if (aa_is_nameX(e, AA_U16, name)) { ++ if (!aa_inbounds(e, sizeof(u16))) ++ goto fail; ++ if (data) ++ *data = le16_to_cpu(get_unaligned((u16 *)e->pos)); ++ e->pos += sizeof(u16); ++ 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_array(struct aa_ext *e, const char *name) ++{ ++ void *pos = e->pos; ++ if (aa_is_nameX(e, AA_ARRAY, name)) { ++ int size; ++ if (!aa_inbounds(e, sizeof(u16))) ++ goto fail; ++ size = (int) le16_to_cpu(get_unaligned((u16 *)e->pos)); ++ e->pos += sizeof(u16); ++ return size; ++ } ++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 ++ */ ++static 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; ++} ++ ++static int aa_unpack_exec_table(struct aa_ext *e, struct aa_profile *profile) ++{ ++ void *pos = e->pos; ++ ++ /* exec table is optional */ ++ if (aa_is_nameX(e, AA_STRUCT, "xtable")) { ++ int i, size; ++ ++ size = aa_is_array(e, NULL); ++ /* currently 4 exec bits and entries 0-3 are reserved iupcx */ ++ if (size > 16 - 4) ++ goto fail; ++ profile->exec_table = kzalloc(sizeof(char *) * size, ++ GFP_KERNEL); ++ if (!profile->exec_table) ++ goto fail; ++ ++ for (i = 0; i < size; i++) { ++ char *tmp; ++ if (!aa_is_dynstring(e, &tmp, NULL)) ++ goto fail; ++ /* note: strings beginning with a : have an embedded ++ \0 seperating the profile ns name from the profile ++ name */ ++ profile->exec_table[i] = tmp; ++ } ++ if (!aa_is_nameX(e, AA_ARRAYEND, NULL)) ++ goto fail; ++ if (!aa_is_nameX(e, AA_STRUCTEND, NULL)) ++ goto fail; ++ } ++ return 1; ++ ++fail: ++ e->pos = pos; ++ return 0; ++} ++ ++/** ++ * aa_unpack_profile - unpack a serialized profile ++ * @e: serialized data extent information ++ * @sa: audit struct for the operation ++ */ ++static struct aa_profile *aa_unpack_profile(struct aa_ext *e, ++ struct aa_audit *sa) ++{ ++ 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; ++ if (!aa_is_u32(e, &(profile->audit_caps), NULL)) ++ goto fail; ++ if (!aa_is_u32(e, &(profile->quiet_caps), NULL)) ++ goto fail; ++ if (!aa_is_u32(e, &(profile->set_caps), 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; ++ } ++ ++ if (!aa_unpack_exec_table(e, profile)) ++ goto fail; ++ ++ if (!aa_is_nameX(e, AA_STRUCTEND, NULL)) ++ goto fail; ++ ++ return profile; ++ ++fail: ++ sa->name = profile && profile->name ? profile->name : "unknown"; ++ if (!sa->info) ++ sa->info = "failed to unpack profile"; ++ aa_audit_status(NULL, sa); ++ ++ if (profile) ++ free_aa_profile(profile); ++ ++ return ERR_PTR(error); ++} ++ ++/** ++ * aa_verify_head - unpack serialized stream header ++ * @e: serialized data read head ++ * @operation: operation header is being verified for ++ * ++ * returns error or 0 if header is good ++ */ ++static int aa_verify_header(struct aa_ext *e, struct aa_audit *sa) ++{ ++ /* get the interface version */ ++ if (!aa_is_u32(e, &e->version, "version")) { ++ sa->info = "invalid profile format"; ++ aa_audit_status(NULL, sa); ++ return -EPROTONOSUPPORT; ++ } ++ ++ /* check that the interface version is currently supported */ ++ if (e->version != 5) { ++ sa->info = "unsupported interface version"; ++ aa_audit_status(NULL, sa); ++ return -EPROTONOSUPPORT; ++ } ++ ++ /* read the namespace if present */ ++ if (!aa_is_dynstring(e, &e->ns_name, "namespace")) { ++ e->ns_name = NULL; ++ } ++ ++ 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_namespace *ns = NULL; ++ struct aa_ext e = { ++ .start = data, ++ .end = data + size, ++ .pos = data, ++ .ns_name = NULL ++ }; ++ ssize_t error; ++ struct aa_audit sa; ++ memset(&sa, 0, sizeof(sa)); ++ sa.operation = "profile_load"; ++ sa.gfp_mask = GFP_KERNEL; ++ ++ error = aa_verify_header(&e, &sa); ++ if (error) ++ return error; ++ ++ profile = aa_unpack_profile(&e, &sa); ++ if (IS_ERR(profile)) ++ return PTR_ERR(profile); ++ ++ mutex_lock(&aa_interface_lock); ++ write_lock(&profile_ns_list_lock); ++ if (e.ns_name) ++ ns = __aa_find_namespace(e.ns_name, &profile_ns_list); ++ else ++ ns = default_namespace; ++ if (!ns) { ++ struct aa_namespace *new_ns; ++ write_unlock(&profile_ns_list_lock); ++ new_ns = alloc_aa_namespace(e.ns_name); ++ if (!new_ns) { ++ mutex_unlock(&aa_interface_lock); ++ return -ENOMEM; ++ } ++ write_lock(&profile_ns_list_lock); ++ ns = __aa_find_namespace(e.ns_name, &profile_ns_list); ++ if (!ns) { ++ list_add(&new_ns->list, &profile_ns_list); ++ ns = new_ns; ++ } else ++ free_aa_namespace(new_ns); ++ } ++ ++ write_lock(&ns->lock); ++ if (__aa_find_profile(profile->name, &ns->profiles)) { ++ /* A profile with this name exists already. */ ++ write_unlock(&ns->lock); ++ write_unlock(&profile_ns_list_lock); ++ sa.name = profile->name; ++ sa.name2 = ns->name; ++ sa.info = "failed: profile already loaded"; ++ aa_audit_status(NULL, &sa); ++ mutex_unlock(&aa_interface_lock); ++ aa_put_profile(profile); ++ return -EEXIST; ++ } ++ profile->ns = aa_get_namespace(ns); ++ ns->profile_count++; ++ list_add(&profile->list, &ns->profiles); ++ write_unlock(&ns->lock); ++ write_unlock(&profile_ns_list_lock); ++ ++ sa.name = profile->name; ++ sa.name2 = ns->name; ++ aa_audit_status(NULL, &sa); ++ 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 %d " ++ "profile=%s (%p)\n", ++ __FUNCTION__, ++ cxt->task->pid, ++ cxt->profile->name, cxt->profile); ++ ++ aa_change_task_context(task, new_cxt, new_profile, cxt->cookie, ++ cxt->previous_profile); ++} ++ ++/** ++ * 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_namespace *ns; ++ struct aa_task_context *new_cxt; ++ struct aa_ext e = { ++ .start = udata, ++ .end = udata + size, ++ .pos = udata, ++ .ns_name = NULL ++ }; ++ ssize_t error; ++ struct aa_audit sa; ++ memset(&sa, 0, sizeof(sa)); ++ sa.operation = "profile_replace"; ++ sa.gfp_mask = GFP_KERNEL; ++ ++ error = aa_verify_header(&e, &sa); ++ if (error) ++ return error; ++ ++ new_profile = aa_unpack_profile(&e, &sa); ++ if (IS_ERR(new_profile)) ++ return PTR_ERR(new_profile); ++ ++ mutex_lock(&aa_interface_lock); ++ write_lock(&profile_ns_list_lock); ++ if (e.ns_name) ++ ns = __aa_find_namespace(e.ns_name, &profile_ns_list); ++ else ++ ns = default_namespace; ++ if (!ns) { ++ struct aa_namespace *new_ns; ++ write_unlock(&profile_ns_list_lock); ++ new_ns = alloc_aa_namespace(e.ns_name); ++ if (!new_ns) { ++ mutex_unlock(&aa_interface_lock); ++ return -ENOMEM; ++ } ++ write_lock(&profile_ns_list_lock); ++ ns = __aa_find_namespace(e.ns_name, &profile_ns_list); ++ if (!ns) { ++ list_add(&new_ns->list, &profile_ns_list); ++ ns = new_ns; ++ } else ++ free_aa_namespace(new_ns); ++ } ++ ++ write_lock(&ns->lock); ++ old_profile = __aa_find_profile(new_profile->name, &ns->profiles); ++ if (old_profile) { ++ lock_profile(old_profile); ++ old_profile->isstale = 1; ++ list_del_init(&old_profile->list); ++ unlock_profile(old_profile); ++ ns->profile_count--; ++ } ++ new_profile->ns = aa_get_namespace(ns); ++ ns->profile_count++; ++ /* not don't need an extra ref count to keep new_profile as ++ * it is protect by the interface mutex */ ++ list_add(&new_profile->list, &ns->profiles); ++ write_unlock(&ns->lock); ++ write_unlock(&profile_ns_list_lock); ++ ++ if (!old_profile) { ++ sa.operation = "profile_load"; ++ 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: ++ sa.name = new_profile->name; ++ sa.name2 = ns->name; ++ aa_audit_status(NULL, &sa); ++ 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(char *name, size_t size) ++{ ++ struct aa_namespace *ns; ++ struct aa_profile *profile; ++ struct aa_audit sa; ++ memset(&sa, 0, sizeof(sa)); ++ sa.operation = "profile_remove"; ++ sa.gfp_mask = GFP_KERNEL; ++ ++ mutex_lock(&aa_interface_lock); ++ write_lock(&profile_ns_list_lock); ++ ++ if (name[0] == ':') { ++ char *split = strchr(name + 1, ':'); ++ if (!split) ++ goto noent; ++ *split = 0; ++ ns = __aa_find_namespace(name + 1, &profile_ns_list); ++ name = split + 1; ++ } else { ++ ns = default_namespace; ++ } ++ ++ if (!ns) ++ goto noent; ++ sa.name2 = ns->name; ++ write_lock(&ns->lock); ++ profile = __aa_find_profile(name, &ns->profiles); ++ if (!profile) { ++ write_unlock(&ns->lock); ++ goto noent; ++ } ++ sa.name = profile->name; ++ ++ /* Remove the profile from each task context it is on. */ ++ lock_profile(profile); ++ profile->isstale = 1; ++ aa_unconfine_tasks(profile); ++ list_del_init(&profile->list); ++ ns->profile_count--; ++ unlock_profile(profile); ++ /* Release the profile itself. */ ++ write_unlock(&ns->lock); ++ /* check to see if the namespace has become stale */ ++ if (ns != default_namespace && ns->profile_count == 0) { ++ list_del_init(&ns->list); ++ aa_put_namespace(ns); ++ } ++ write_unlock(&profile_ns_list_lock); ++ ++ aa_audit_status(NULL, &sa); ++ mutex_unlock(&aa_interface_lock); ++ aa_put_profile(profile); ++ ++ return size; ++ ++noent: ++ write_unlock(&profile_ns_list_lock); ++ sa.info = "failed: profile does not exist"; ++ aa_audit_status(NULL, &sa); ++ mutex_unlock(&aa_interface_lock); ++ return -ENOENT; ++} ++ ++/** ++ * free_aa_namespace_kref - free aa_namespace by kref (see aa_put_namespace) ++ * @kr: kref callback for freeing of a namespace ++ */ ++void free_aa_namespace_kref(struct kref *kref) ++{ ++ struct aa_namespace *ns=container_of(kref, struct aa_namespace, count); ++ ++ free_aa_namespace(ns); ++} ++ ++/** ++ * alloc_aa_namespace - allocate, initialize and return a new namespace ++ * @name: a preallocated name ++ * Returns NULL on failure. ++ */ ++struct aa_namespace *alloc_aa_namespace(char *name) ++{ ++ struct aa_namespace *ns; ++ ++ ns = kzalloc(sizeof(*ns), GFP_KERNEL); ++ AA_DEBUG("%s(%p)\n", __FUNCTION__, ns); ++ if (ns) { ++ ns->name = name; ++ INIT_LIST_HEAD(&ns->list); ++ INIT_LIST_HEAD(&ns->profiles); ++ kref_init(&ns->count); ++ rwlock_init(&ns->lock); ++ ++ ns->null_complain_profile = alloc_aa_profile(); ++ if (!ns->null_complain_profile) { ++ if (!name) ++ kfree(ns->name); ++ kfree(ns); ++ return NULL; ++ } ++ ns->null_complain_profile->name = ++ kstrdup("null-complain-profile", GFP_KERNEL); ++ if (!ns->null_complain_profile->name) { ++ free_aa_profile(ns->null_complain_profile); ++ if (!name) ++ kfree(ns->name); ++ kfree(ns); ++ return NULL; ++ } ++ ns->null_complain_profile->flags.complain = 1; ++ /* null_complain_profile doesn't contribute to ns ref count */ ++ ns->null_complain_profile->ns = ns; ++ } ++ return ns; ++} ++ ++/** ++ * free_aa_namespace - free a profile namespace ++ * @namespace: the namespace to free ++ * ++ * Free a namespace. All references to the namespace must have been put. ++ * If the namespace was referenced by a profile confining a task, ++ * free_aa_namespace will be called indirectly (through free_aa_profile) ++ * from an rcu callback routine, so we must not sleep here. ++ */ ++void free_aa_namespace(struct aa_namespace *ns) ++{ ++ AA_DEBUG("%s(%p)\n", __FUNCTION__, ns); ++ ++ if (!ns) ++ return; ++ ++ /* namespace still contains profiles -- invalid */ ++ if (!list_empty(&ns->profiles)) { ++ AA_ERROR("%s: internal error, " ++ "namespace '%s' still contains profiles\n", ++ __FUNCTION__, ++ ns->name); ++ BUG(); ++ } ++ if (!list_empty(&ns->list)) { ++ AA_ERROR("%s: internal error, " ++ "namespace '%s' still on list\n", ++ __FUNCTION__, ++ ns->name); ++ BUG(); ++ } ++ /* null_complain_profile doesn't contribute to ns ref counting */ ++ ns->null_complain_profile->ns = NULL; ++ aa_put_profile(ns->null_complain_profile); ++ kfree(ns->name); ++ kfree(ns); ++} ++ ++/** ++ * 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) { ++ INIT_LIST_HEAD(&profile->list); ++ 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) ++{ ++ AA_DEBUG("%s(%p)\n", __FUNCTION__, profile); ++ ++ if (!profile) ++ return; ++ ++ /* profile is still on profile namespace list -- invalid */ ++ if (!list_empty(&profile->list)) { ++ AA_ERROR("%s: internal error, " ++ "profile '%s' still on global list\n", ++ __FUNCTION__, ++ profile->name); ++ BUG(); ++ } ++ aa_put_namespace(profile->ns); ++ ++ aa_match_free(profile->file_rules); ++ ++ 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, NULL); ++ task_unlock(task); ++ } ++} diff --git a/kernel-patches/2.6.25/apparmor-network.diff b/kernel-patches/2.6.25/apparmor-network.diff new file mode 100644 index 000000000..72c1901e0 --- /dev/null +++ b/kernel-patches/2.6.25/apparmor-network.diff @@ -0,0 +1,400 @@ +--- + security/apparmor/Makefile | 7 + + security/apparmor/apparmor.h | 9 ++ + security/apparmor/lsm.c | 129 ++++++++++++++++++++++++++++++++++- + security/apparmor/main.c | 107 ++++++++++++++++++++++++++++- + security/apparmor/module_interface.c | 26 ++++++- + 5 files changed, 271 insertions(+), 7 deletions(-) + +--- a/security/apparmor/Makefile ++++ b/security/apparmor/Makefile +@@ -8,6 +8,11 @@ apparmor-y := main.o list.o procattr.o l + quiet_cmd_make-caps = GEN $@ + cmd_make-caps = sed -n -e "/CAP_FS_MASK/d" -e "s/^\#define[ \\t]\\+CAP_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z > $@ + +-$(obj)/main.o : $(obj)/capability_names.h ++quiet_cmd_make-af = GEN $@ ++cmd_make-af = sed -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e "s/^\#define[ \\t]\\+AF_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z > $@ ++ ++$(obj)/main.o : $(obj)/capability_names.h $(obj)/af_names.h + $(obj)/capability_names.h : $(srctree)/include/linux/capability.h + $(call cmd,make-caps) ++$(obj)/af_names.h : $(srctree)/include/linux/socket.h ++ $(call cmd,make-af) +--- a/security/apparmor/apparmor.h ++++ b/security/apparmor/apparmor.h +@@ -16,6 +16,8 @@ + #include + #include + #include ++#include ++#include + + /* + * We use MAY_READ, MAY_WRITE, MAY_EXEC, MAY_APPEND and the following flags +@@ -208,6 +210,9 @@ struct aa_profile { + struct list_head task_contexts; + spinlock_t lock; + unsigned long int_flags; ++ u16 network_families[AF_MAX]; ++ u16 audit_network[AF_MAX]; ++ u16 quiet_network[AF_MAX]; + }; + + extern struct list_head profile_ns_list; +@@ -254,6 +259,7 @@ struct aa_audit { + int request_mask, denied_mask, audit_mask; + struct iattr *iattr; + pid_t task, parent; ++ int family, type, protocol; + int error_code; + }; + +@@ -315,6 +321,9 @@ extern void aa_change_task_context(struc + struct aa_profile *previous_profile); + extern int aa_may_ptrace(struct aa_task_context *cxt, + struct aa_profile *tracee); ++extern int aa_net_perm(struct aa_profile *profile, char *operation, ++ int family, int type, int protocol); ++extern int aa_revalidate_sk(struct sock *sk, char *operation); + + /* lsm.c */ + extern int apparmor_initialized; +--- a/security/apparmor/lsm.c ++++ b/security/apparmor/lsm.c +@@ -18,6 +18,7 @@ + #include + #include + #include ++#include + + #include "apparmor.h" + #include "inline.h" +@@ -663,6 +664,117 @@ static void apparmor_task_free_security( + aa_release(task); + } + ++static int apparmor_socket_create(int family, int type, int protocol, int kern) ++{ ++ struct aa_profile *profile; ++ int error = 0; ++ ++ if (kern) ++ return 0; ++ ++ profile = aa_get_profile(current); ++ if (profile) ++ error = aa_net_perm(profile, "socket_create", family, ++ type, protocol); ++ aa_put_profile(profile); ++ ++ return error; ++} ++ ++static int apparmor_socket_post_create(struct socket *sock, int family, ++ int type, int protocol, int kern) ++{ ++ struct sock *sk = sock->sk; ++ ++ if (kern) ++ return 0; ++ ++ return aa_revalidate_sk(sk, "socket_post_create"); ++} ++ ++static int apparmor_socket_bind(struct socket *sock, ++ struct sockaddr *address, int addrlen) ++{ ++ struct sock *sk = sock->sk; ++ ++ return aa_revalidate_sk(sk, "socket_bind"); ++} ++ ++static int apparmor_socket_connect(struct socket *sock, ++ struct sockaddr *address, int addrlen) ++{ ++ struct sock *sk = sock->sk; ++ ++ return aa_revalidate_sk(sk, "socket_connect"); ++} ++ ++static int apparmor_socket_listen(struct socket *sock, int backlog) ++{ ++ struct sock *sk = sock->sk; ++ ++ return aa_revalidate_sk(sk, "socket_listen"); ++} ++ ++static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) ++{ ++ struct sock *sk = sock->sk; ++ ++ return aa_revalidate_sk(sk, "socket_accept"); ++} ++ ++static int apparmor_socket_sendmsg(struct socket *sock, ++ struct msghdr *msg, int size) ++{ ++ struct sock *sk = sock->sk; ++ ++ return aa_revalidate_sk(sk, "socket_sendmsg"); ++} ++ ++static int apparmor_socket_recvmsg(struct socket *sock, ++ struct msghdr *msg, int size, int flags) ++{ ++ struct sock *sk = sock->sk; ++ ++ return aa_revalidate_sk(sk, "socket_recvmsg"); ++} ++ ++static int apparmor_socket_getsockname(struct socket *sock) ++{ ++ struct sock *sk = sock->sk; ++ ++ return aa_revalidate_sk(sk, "socket_getsockname"); ++} ++ ++static int apparmor_socket_getpeername(struct socket *sock) ++{ ++ struct sock *sk = sock->sk; ++ ++ return aa_revalidate_sk(sk, "socket_getpeername"); ++} ++ ++static int apparmor_socket_getsockopt(struct socket *sock, int level, ++ int optname) ++{ ++ struct sock *sk = sock->sk; ++ ++ return aa_revalidate_sk(sk, "socket_getsockopt"); ++} ++ ++static int apparmor_socket_setsockopt(struct socket *sock, int level, ++ int optname) ++{ ++ struct sock *sk = sock->sk; ++ ++ return aa_revalidate_sk(sk, "socket_setsockopt"); ++} ++ ++static int apparmor_socket_shutdown(struct socket *sock, int how) ++{ ++ struct sock *sk = sock->sk; ++ ++ return aa_revalidate_sk(sk, "socket_shutdown"); ++} ++ + static int apparmor_getprocattr(struct task_struct *task, char *name, + char **value) + { +@@ -763,9 +875,6 @@ struct security_operations apparmor_ops + .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, +@@ -801,6 +910,20 @@ struct security_operations apparmor_ops + + .getprocattr = apparmor_getprocattr, + .setprocattr = apparmor_setprocattr, ++ ++ .socket_create = apparmor_socket_create, ++ .socket_post_create = apparmor_socket_post_create, ++ .socket_bind = apparmor_socket_bind, ++ .socket_connect = apparmor_socket_connect, ++ .socket_listen = apparmor_socket_listen, ++ .socket_accept = apparmor_socket_accept, ++ .socket_sendmsg = apparmor_socket_sendmsg, ++ .socket_recvmsg = apparmor_socket_recvmsg, ++ .socket_getsockname = apparmor_socket_getsockname, ++ .socket_getpeername = apparmor_socket_getpeername, ++ .socket_getsockopt = apparmor_socket_getsockopt, ++ .socket_setsockopt = apparmor_socket_setsockopt, ++ .socket_shutdown = apparmor_socket_shutdown, + }; + + void info_message(const char *str) +--- a/security/apparmor/main.c ++++ b/security/apparmor/main.c +@@ -14,6 +14,9 @@ + #include + #include + #include ++#include ++#include ++#include + + #include "apparmor.h" + +@@ -116,6 +119,24 @@ static void aa_audit_file_mask(struct au + audit_log_format(ab, " %s=\"%s::%s\"", name, user, other); + } + ++static const char *address_families[] = { ++#include "af_names.h" ++}; ++ ++static const char *sock_types[] = { ++ "unknown(0)", ++ "stream", ++ "dgram", ++ "raw", ++ "rdm", ++ "seqpacket", ++ "dccp", ++ "unknown(7)", ++ "unknown(8)", ++ "unknown(9)", ++ "packet", ++}; ++ + /** + * aa_audit - Log an audit event to the audit subsystem + * @profile: profile to check against +@@ -187,7 +208,25 @@ static int aa_audit_base(struct aa_profi + audit_log_untrustedstring(ab, sa->name2); + } + +- audit_log_format(ab, " pid=%d", current->pid); ++ if (sa->family || sa->type) { ++ if (address_families[sa->family]) ++ audit_log_format(ab, " family=\"%s\"", ++ address_families[sa->family]); ++ else ++ audit_log_format(ab, " family=\"unknown(%d)\"", ++ sa->family); ++ ++ if (sock_types[sa->type]) ++ audit_log_format(ab, " sock_type=\"%s\"", ++ sock_types[sa->type]); ++ else ++ audit_log_format(ab, " sock_type=\"unknown(%d)\"", ++ sa->type); ++ ++ audit_log_format(ab, " protocol=%d", sa->protocol); ++ } ++ ++ audit_log_format(ab, " pid=%d", current->pid); + + if (profile) { + audit_log_format(ab, " profile="); +@@ -768,6 +807,72 @@ int aa_link(struct aa_profile *profile, + return error; + } + ++int aa_net_perm(struct aa_profile *profile, char *operation, ++ int family, int type, int protocol) ++{ ++ struct aa_audit sa; ++ int error = 0; ++ u16 family_mask, audit_mask, quiet_mask; ++ ++ if ((family < 0) || (family >= AF_MAX)) ++ return -EINVAL; ++ ++ if ((type < 0) || (type >= SOCK_MAX)) ++ return -EINVAL; ++ ++ /* unix domain and netlink sockets are handled by ipc */ ++ if (family == AF_UNIX || family == AF_NETLINK) ++ return 0; ++ ++ family_mask = profile->network_families[family]; ++ audit_mask = profile->audit_network[family]; ++ quiet_mask = profile->quiet_network[family]; ++ ++ error = (family_mask & (1 << type)) ? 0 : -EACCES; ++ ++ memset(&sa, 0, sizeof(sa)); ++ sa.operation = operation; ++ sa.gfp_mask = GFP_KERNEL; ++ sa.family = family; ++ sa.type = type; ++ sa.protocol = protocol; ++ sa.error_code = error; ++ ++ if (likely(!error)) { ++ if (!PROFILE_AUDIT(profile) && !(family_mask & audit_mask)) ++ return 0; ++ } else if (!((1 << type) & ~quiet_mask)) { ++ return error; ++ } ++ ++ error = aa_audit(profile, &sa); ++ ++ return error; ++} ++ ++int aa_revalidate_sk(struct sock *sk, char *operation) ++{ ++ struct aa_profile *profile; ++ int error = 0; ++ ++ /* this is some debugging code to flush out the network hooks that ++ that are called in interrupt context */ ++ if (in_interrupt()) { ++ printk("AppArmor Debug: Hook being called from interrupt context\n"); ++ dump_stack(); ++ return 0; ++ } ++ ++ profile = aa_get_profile(current); ++ if (profile) ++ error = aa_net_perm(profile, operation, ++ sk->sk_family, sk->sk_type, ++ sk->sk_protocol); ++ aa_put_profile(profile); ++ ++ return error; ++} ++ + /******************************* + * Global task related functions + *******************************/ +--- a/security/apparmor/module_interface.c ++++ b/security/apparmor/module_interface.c +@@ -320,8 +320,8 @@ static struct aa_profile *aa_unpack_prof + struct aa_audit *sa) + { + struct aa_profile *profile = NULL; +- +- int error = -EPROTO; ++ size_t size = 0; ++ int i, error = -EPROTO; + + profile = alloc_aa_profile(); + if (!profile) +@@ -354,6 +354,28 @@ static struct aa_profile *aa_unpack_prof + if (!aa_is_u32(e, &(profile->set_caps), NULL)) + goto fail; + ++ size = aa_is_array(e, "net_allowed_af"); ++ if (size) { ++ if (size > AF_MAX) ++ goto fail; ++ ++ for (i = 0; i < size; i++) { ++ if (!aa_is_u16(e, &profile->network_families[i], NULL)) ++ goto fail; ++ if (!aa_is_u16(e, &profile->audit_network[i], NULL)) ++ goto fail; ++ if (!aa_is_u16(e, &profile->quiet_network[i], NULL)) ++ goto fail; ++ } ++ if (!aa_is_nameX(e, AA_ARRAYEND, NULL)) ++ goto fail; ++ /* allow unix domain and netlink sockets they are handled ++ * by IPC ++ */ ++ } ++ profile->network_families[AF_UNIX] = 0xffff; ++ profile->network_families[AF_NETLINK] = 0xffff; ++ + /* get file rules */ + profile->file_rules = aa_unpack_dfa(e); + if (IS_ERR(profile->file_rules)) { diff --git a/kernel-patches/2.6.25/apparmor-rlimits.diff b/kernel-patches/2.6.25/apparmor-rlimits.diff new file mode 100644 index 000000000..5895479c4 --- /dev/null +++ b/kernel-patches/2.6.25/apparmor-rlimits.diff @@ -0,0 +1,461 @@ +From: John Johansen +Subject: AppArmor: per profile controls for system rlimits + +Provide contol of rlimits on a per profile basis. Each profile provides +a per limit contol and corresponding hard limit value, such that when a +profile becomes attached to a task it sets the tasks limits to be <= to +the profiles specified limits. Note: the profile limit value will not +raise a tasks limit if it is already less than the profile mandates. + +In addition to setting a tasks limits, the ability to set limits on +a confined task are controlled. AppArmor only controls the raising +of a tasks limits Tasks with CAP_SYS_RESOURCE can have their hard limits +raised up to the value specified by the profile. AppArmor does not +prevent a task for lowering its hard limits, nor does it provide +additional control on soft limits. + +AppArmor only controls the limits specified in a profile so that +any limit not specified is free to be modified subject to standard +linux limitations. + +--- + security/apparmor/apparmor.h | 23 ++++++ + security/apparmor/apparmorfs.c | 2 + security/apparmor/lsm.c | 16 ++++ + security/apparmor/main.c | 132 +++++++++++++++++++++++++++++++---- + security/apparmor/module_interface.c | 56 ++++++++++++++ + 5 files changed, 215 insertions(+), 14 deletions(-) + +--- a/security/apparmor/apparmor.h ++++ b/security/apparmor/apparmor.h +@@ -16,6 +16,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -136,6 +137,18 @@ extern unsigned int apparmor_path_max; + + #define AA_ERROR(fmt, args...) printk(KERN_ERR "AppArmor: " fmt, ##args) + ++/* struct aa_rlimit - rlimits settings for the profile ++ * @mask: which hard limits to set ++ * @limits: rlimit values that override task limits ++ * ++ * AppArmor rlimits are used to set confined task rlimits. Only the ++ * limits specified in @mask will be controlled by apparmor. ++ */ ++struct aa_rlimit { ++ unsigned int mask; ++ struct rlimit limits[RLIM_NLIMITS]; ++}; ++ + struct aa_profile; + + /* struct aa_namespace - namespace for a set of profiles +@@ -170,6 +183,8 @@ struct aa_namespace { + * @audit_caps: caps that are to be audited + * @quiet_caps: caps that should not be audited + * @capabilities: capabilities granted by the process ++ * @rlimits: rlimits for the profile ++ * @task_count: how many tasks the profile is attached to + * @count: reference count of the profile + * @task_contexts: list of tasks confined by profile + * @lock: lock for the task_contexts list +@@ -206,6 +221,9 @@ struct aa_profile { + kernel_cap_t audit_caps; + kernel_cap_t quiet_caps; + ++ struct aa_rlimit rlimits; ++ unsigned int task_count; ++ + struct kref count; + struct list_head task_contexts; + spinlock_t lock; +@@ -257,6 +275,7 @@ struct aa_audit { + const char *name2; + const char *name3; + int request_mask, denied_mask, audit_mask; ++ int rlimit; + struct iattr *iattr; + pid_t task, parent; + int family, type, protocol; +@@ -324,6 +343,10 @@ extern int aa_may_ptrace(struct aa_task_ + extern int aa_net_perm(struct aa_profile *profile, char *operation, + int family, int type, int protocol); + extern int aa_revalidate_sk(struct sock *sk, char *operation); ++extern int aa_task_setrlimit(struct aa_profile *profile, unsigned int resource, ++ struct rlimit *new_rlim); ++extern void aa_set_rlimits(struct task_struct *task, struct aa_profile *profile); ++ + + /* lsm.c */ + extern int apparmor_initialized; +--- a/security/apparmor/apparmorfs.c ++++ b/security/apparmor/apparmorfs.c +@@ -105,7 +105,7 @@ static ssize_t aa_features_read(struct f + { + const char *features = "file=3.0 capability=2.0 network=1.0 " + "change_hat=1.4 change_profile=1.0 " +- "aanamespaces=1.0"; ++ "aanamespaces=1.0 rlimit=1.0"; + + return simple_read_from_buffer(buf, size, ppos, features, + strlen(features)); +--- a/security/apparmor/lsm.c ++++ b/security/apparmor/lsm.c +@@ -866,6 +866,21 @@ static int apparmor_setprocattr(struct t + return error; + } + ++static int apparmor_task_setrlimit(unsigned int resource, ++ struct rlimit *new_rlim) ++{ ++ struct aa_profile *profile; ++ int error = 0; ++ ++ profile = aa_get_profile(current); ++ if (profile) { ++ error = aa_task_setrlimit(profile, resource, new_rlim); ++ } ++ aa_put_profile(profile); ++ ++ return error; ++} ++ + struct security_operations apparmor_ops = { + .ptrace = apparmor_ptrace, + .capget = cap_capget, +@@ -907,6 +922,7 @@ struct security_operations apparmor_ops + .task_free_security = apparmor_task_free_security, + .task_post_setuid = cap_task_post_setuid, + .task_reparent_to_init = cap_task_reparent_to_init, ++ .task_setrlimit = apparmor_task_setrlimit, + + .getprocattr = apparmor_getprocattr, + .setprocattr = apparmor_setprocattr, +--- a/security/apparmor/main.c ++++ b/security/apparmor/main.c +@@ -177,6 +177,9 @@ static int aa_audit_base(struct aa_profi + if (sa->request_mask) + audit_log_format(ab, " fsuid=%d", current->fsuid); + ++ if (sa->rlimit) ++ audit_log_format(ab, " rlimit=%d", sa->rlimit - 1); ++ + if (sa->iattr) { + struct iattr *iattr = sa->iattr; + +@@ -872,6 +875,79 @@ int aa_revalidate_sk(struct sock *sk, ch + + return error; + } ++/** ++ * aa_task_setrlimit - test permission to set an rlimit ++ * @profile - profile confining the task ++ * @resource - the resource being set ++ * @new_rlim - the new resource limit ++ * ++ * Control raising the processes hard limit. ++ */ ++int aa_task_setrlimit(struct aa_profile *profile, unsigned int resource, ++ struct rlimit *new_rlim) ++{ ++ struct aa_audit sa; ++ int error = 0; ++ ++ memset(&sa, 0, sizeof(sa)); ++ sa.operation = "setrlimit"; ++ sa.gfp_mask = GFP_KERNEL; ++ sa.rlimit = resource + 1; ++ ++ if (profile->rlimits.mask & (1 << resource) && ++ new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max) { ++ sa.error_code = -EACCES; ++ ++ error = aa_audit(profile, &sa); ++ } ++ ++ return error; ++} ++ ++static int aa_rlimit_nproc(struct aa_profile *profile) { ++ if (profile && (profile->rlimits.mask & (1 << RLIMIT_NPROC)) && ++ profile->task_count >= profile->rlimits.limits[RLIMIT_NPROC].rlim_max) ++ return -EAGAIN; ++ return 0; ++} ++ ++void aa_set_rlimits(struct task_struct *task, struct aa_profile *profile) ++{ ++ int i, mask; ++ ++ if (!profile) ++ return; ++ ++ if (!profile->rlimits.mask) ++ return; ++ ++ task_lock(task->group_leader); ++ mask = 1; ++ for (i = 0; i < RLIM_NLIMITS; i++, mask <<= 1) { ++ struct rlimit new_rlim, *old_rlim; ++ ++ /* check to see if NPROC which is per profile and handled ++ * in clone/exec or whether this is a limit to be set ++ * can't set cpu limit either right now ++ */ ++ if (i == RLIMIT_NPROC || i == RLIMIT_CPU) ++ continue; ++ ++ old_rlim = task->signal->rlim + i; ++ new_rlim = *old_rlim; ++ ++ if (mask & profile->rlimits.mask && ++ profile->rlimits.limits[i].rlim_max < new_rlim.rlim_max) { ++ new_rlim.rlim_max = profile->rlimits.limits[i].rlim_max; ++ /* soft limit should not exceed hard limit */ ++ if (new_rlim.rlim_cur > new_rlim.rlim_max) ++ new_rlim.rlim_cur = new_rlim.rlim_max; ++ } ++ ++ *old_rlim = new_rlim; ++ } ++ task_unlock(task->group_leader); ++} + + /******************************* + * Global task related functions +@@ -885,6 +961,7 @@ int aa_revalidate_sk(struct sock *sk, ch + */ + int aa_clone(struct task_struct *child) + { ++ struct aa_audit sa; + struct aa_task_context *cxt, *child_cxt; + struct aa_profile *profile; + +@@ -894,6 +971,11 @@ int aa_clone(struct task_struct *child) + if (!child_cxt) + return -ENOMEM; + ++ memset(&sa, 0, sizeof(sa)); ++ sa.operation = "clone"; ++ sa.task = child->pid; ++ sa.gfp_mask = GFP_KERNEL; ++ + repeat: + profile = aa_get_profile(current); + if (profile) { +@@ -910,18 +992,22 @@ repeat: + goto repeat; + } + ++ if (aa_rlimit_nproc(profile)) { ++ sa.info = "rlimit nproc limit exceeded"; ++ unlock_profile(profile); ++ aa_audit_reject(profile, &sa); ++ aa_put_profile(profile); ++ return -EAGAIN; ++ } ++ + /* No need to grab the child's task lock here. */ + aa_change_task_context(child, child_cxt, profile, + cxt->cookie, cxt->previous_profile); ++ + unlock_profile(profile); + + if (APPARMOR_COMPLAIN(child_cxt) && + profile == profile->ns->null_complain_profile) { +- struct aa_audit sa; +- memset(&sa, 0, sizeof(sa)); +- sa.operation = "clone"; +- sa.gfp_mask = GFP_KERNEL; +- sa.task = child->pid; + aa_audit_hint(profile, &sa); + } + aa_put_profile(profile); +@@ -1156,6 +1242,10 @@ repeat: + sa.task = current->parent->pid; + aa_audit_reject(profile, &sa); + } ++ if (PTR_ERR(old_profile) == -EAGAIN) { ++ sa.info = "rlimit nproc limit exceeded"; ++ aa_audit_reject(profile, &sa); ++ } + new_profile = old_profile; + goto cleanup; + } +@@ -1296,6 +1386,12 @@ static int do_change_profile(struct aa_p + goto out; + } + ++ if ((error = aa_rlimit_nproc(new_profile))) { ++ sa->info = "rlimit nproc limit exceeded"; ++ aa_audit_reject(cxt->profile, sa); ++ goto out; ++ } ++ + if (new_profile == ns->null_complain_profile) + aa_audit_hint(cxt->profile, sa); + +@@ -1482,17 +1578,18 @@ struct aa_profile *__aa_replace_profile( + + cxt = lock_task_and_profiles(task, profile); + if (unlikely(profile && profile->isstale)) { +- task_unlock(task); +- unlock_both_profiles(profile, cxt ? cxt->profile : NULL); +- aa_free_task_context(new_cxt); +- return ERR_PTR(-ESTALE); ++ old_profile = ERR_PTR(-ESTALE); ++ goto error; + } + + if ((current->ptrace & PT_PTRACED) && aa_may_ptrace(cxt, profile)) { +- task_unlock(task); +- unlock_both_profiles(profile, cxt ? cxt->profile : NULL); +- aa_free_task_context(new_cxt); +- return ERR_PTR(-EPERM); ++ old_profile = ERR_PTR(-EPERM); ++ goto error; ++ } ++ ++ if (aa_rlimit_nproc(profile)) { ++ old_profile = ERR_PTR(-EAGAIN); ++ goto error; + } + + if (cxt) +@@ -1500,8 +1597,15 @@ struct aa_profile *__aa_replace_profile( + aa_change_task_context(task, new_cxt, profile, 0, NULL); + + task_unlock(task); ++ aa_set_rlimits(task, profile); + unlock_both_profiles(profile, old_profile); + return old_profile; ++ ++error: ++ task_unlock(task); ++ unlock_both_profiles(profile, cxt ? cxt->profile : NULL); ++ aa_free_task_context(new_cxt); ++ return old_profile; + } + + /** +@@ -1566,6 +1670,7 @@ void aa_change_task_context(struct task_ + + if (old_cxt) { + list_del_init(&old_cxt->list); ++ old_cxt->profile->task_count--; + call_rcu(&old_cxt->rcu, free_aa_task_context_rcu_callback); + } + if (new_cxt) { +@@ -1577,6 +1682,7 @@ void aa_change_task_context(struct task_ + new_cxt->cookie = cookie; + new_cxt->task = task; + new_cxt->profile = aa_dup_profile(profile); ++ profile->task_count++; + new_cxt->previous_profile = aa_dup_profile(previous_profile); + list_move(&new_cxt->list, &profile->task_contexts); + } +--- a/security/apparmor/module_interface.c ++++ b/security/apparmor/module_interface.c +@@ -177,6 +177,22 @@ fail: + return 0; + } + ++static int aa_is_u64(struct aa_ext *e, u64 *data, const char *name) ++{ ++ void *pos = e->pos; ++ if (aa_is_nameX(e, AA_U64, name)) { ++ if (!aa_inbounds(e, sizeof(u64))) ++ goto fail; ++ if (data) ++ *data = le64_to_cpu(get_unaligned((u64 *)e->pos)); ++ e->pos += sizeof(u64); ++ return 1; ++ } ++fail: ++ e->pos = pos; ++ return 0; ++} ++ + static size_t aa_is_array(struct aa_ext *e, const char *name) + { + void *pos = e->pos; +@@ -311,6 +327,39 @@ fail: + return 0; + } + ++int aa_unpack_rlimits(struct aa_ext *e, struct aa_profile *profile) ++{ ++ void *pos = e->pos; ++ ++ /* rlimits are optional */ ++ if (aa_is_nameX(e, AA_STRUCT, "rlimits")) { ++ int i, size; ++ u32 tmp = 0; ++ if (!aa_is_u32(e, &tmp, NULL)) ++ goto fail; ++ profile->rlimits.mask = tmp; ++ ++ size = aa_is_array(e, NULL); ++ if (size != RLIM_NLIMITS) ++ goto fail; ++ for (i = 0; i < size; i++) { ++ u64 tmp = 0; ++ if (!aa_is_u64(e, &tmp, NULL)) ++ goto fail; ++ profile->rlimits.limits[i].rlim_max = tmp; ++ } ++ if (!aa_is_nameX(e, AA_ARRAYEND, NULL)) ++ goto fail; ++ if (!aa_is_nameX(e, AA_STRUCTEND, NULL)) ++ goto fail; ++ } ++ return 1; ++ ++fail: ++ e->pos = pos; ++ return 0; ++} ++ + /** + * aa_unpack_profile - unpack a serialized profile + * @e: serialized data extent information +@@ -354,6 +403,9 @@ static struct aa_profile *aa_unpack_prof + if (!aa_is_u32(e, &(profile->set_caps), NULL)) + goto fail; + ++ if (!aa_unpack_rlimits(e, profile)) ++ goto fail; ++ + size = aa_is_array(e, "net_allowed_af"); + if (size) { + if (size > AF_MAX) +@@ -613,6 +665,8 @@ ssize_t aa_replace_profile(void *udata, + sa.operation = "profile_load"; + goto out; + } ++ /* do not fail replacement based off of profile's NPROC rlimit */ ++ + /* + * Replacement needs to allocate a new aa_task_context for each + * task confined by old_profile. To do this the profile locks +@@ -633,6 +687,7 @@ ssize_t aa_replace_profile(void *udata, + task_lock(task); + task_replace(task, new_cxt, new_profile); + task_unlock(task); ++ aa_set_rlimits(task, new_profile); + new_cxt = NULL; + } + unlock_both_profiles(old_profile, new_profile); +@@ -655,6 +710,7 @@ out: + * + * remove a profile from the profile list and all aa_task_context references + * to said profile. ++ * NOTE: removing confinement does not restore rlimits to preconfinemnet values + */ + ssize_t aa_remove_profile(char *name, size_t size) + { diff --git a/kernel-patches/2.6.25/apparmor-stack_secondary.diff b/kernel-patches/2.6.25/apparmor-stack_secondary.diff new file mode 100644 index 000000000..31ece1223 --- /dev/null +++ b/kernel-patches/2.6.25/apparmor-stack_secondary.diff @@ -0,0 +1,212 @@ +From: John Johnansen +Subject: allow apparmor to stack with dazuko +Patch-mainline: no +References: 300965 + +Allow AppArmor to stack with dazuko so that the clamav virus +scanner can be used on an AppArmored machine. + +Signed-off-by: John Johansen + + +--- + security/apparmor/apparmor.h | 2 + security/apparmor/apparmorfs.c | 2 + security/apparmor/lsm.c | 101 ++++++++++++++++++++++++++++++++++++----- + 3 files changed, 92 insertions(+), 13 deletions(-) + +--- a/security/apparmor/apparmor.h ++++ b/security/apparmor/apparmor.h +@@ -347,7 +347,7 @@ extern void aa_set_rlimits(struct task_s + + /* lsm.c */ + extern int apparmor_initialized; +-extern void info_message(const char *str); ++extern void info_message(const char *str, const char *name); + extern void apparmor_disable(void); + + /* list.c */ +--- a/security/apparmor/apparmorfs.c ++++ b/security/apparmor/apparmorfs.c +@@ -266,7 +266,7 @@ int create_apparmorfs(void) + goto error; + + /* Report that AppArmor fs is enabled */ +- info_message("AppArmor Filesystem Enabled"); ++ info_message("AppArmor Filesystem Enabled", ""); + return 0; + + error: +--- a/security/apparmor/lsm.c ++++ b/security/apparmor/lsm.c +@@ -26,6 +26,20 @@ + /* Flag indicating whether initialization completed */ + int apparmor_initialized = 0; + ++/* point to the apparmor module */ ++struct module *aa_module = NULL; ++ ++/* secondary ops if apparmor is stacked */ ++static struct security_operations *aa_secondary_ops = NULL; ++static DEFINE_MUTEX(aa_secondary_lock); ++ ++#define AA_SECONDARY(FN, ARGS...) \ ++ ({ \ ++ struct security_operations *__f1; \ ++ __f1 = rcu_dereference(aa_secondary_ops); \ ++ (unlikely(__f1) && __f1->FN) ? __f1->FN(ARGS) : 0; \ ++ }) ++ + 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) +@@ -452,19 +466,25 @@ out: + static int apparmor_inode_permission(struct inode *inode, int mask, + struct nameidata *nd) + { +- int check = 0; ++ int check = 0, error = 0; + + if (!nd || nd->flags & (LOOKUP_PARENT | LOOKUP_CONTINUE)) +- return 0; ++ goto out; + mask = aa_mask_permissions(mask); + if (S_ISDIR(inode->i_mode)) { + check |= AA_CHECK_DIR; + /* allow traverse accesses to directories */ + mask &= ~MAY_EXEC; + } +- return aa_permission("inode_permission", inode, nd->path.dentry, +- nd->path.mnt, +- mask, check); ++ error = aa_permission("inode_permission", inode, nd->path.dentry, ++ nd->path.mnt, ++ mask, check); ++ ++out: ++ if (!error) ++ error = AA_SECONDARY(inode_permission, inode, mask, nd); ++ ++ return error; + } + + static int apparmor_inode_setattr(struct dentry *dentry, struct vfsmount *mnt, +@@ -882,6 +902,61 @@ static int apparmor_task_setrlimit(unsig + return error; + } + ++int apparmor_register_subsecurity(const char *name, ++ struct security_operations *ops) ++{ ++ int error = 0; ++ ++ if (mutex_lock_interruptible(&aa_secondary_lock)) ++ return -ERESTARTSYS; ++ ++ /* allow dazuko and capability to stack. The stacking with ++ * capability is not needed since apparmor already composes ++ * capability using common cap. ++ */ ++ if (!aa_secondary_ops && (strcmp(name, "dazuko") == 0 || ++ strcmp(name, "capability") == 0)){ ++ /* The apparmor module needs to be pinned while a secondary is ++ * registered ++ */ ++ if (try_module_get(aa_module)) { ++ aa_secondary_ops = ops; ++ info_message("Registered secondary security module", ++ name); ++ } else { ++ error = -EINVAL; ++ } ++ } else { ++ info_message("Unable to register %s as a secondary security " ++ "module", name); ++ error = -EPERM; ++ } ++ mutex_unlock(&aa_secondary_lock); ++ return error; ++} ++ ++int apparmor_unregister_subsecurity(const char *name, ++ struct security_operations *ops) ++{ ++ int error = 0; ++ ++ if (mutex_lock_interruptible(&aa_secondary_lock)) ++ return -ERESTARTSYS; ++ ++ if (aa_secondary_ops && aa_secondary_ops == ops) { ++ rcu_assign_pointer(aa_secondary_ops, NULL); ++ synchronize_rcu(); ++ module_put(aa_module); ++ info_message("Unregistered secondary security module", name); ++ } else { ++ info_message("Unable to unregister secondary security module", ++ name); ++ error = -EPERM; ++ } ++ mutex_unlock(&aa_secondary_lock); ++ return error; ++} ++ + struct security_operations apparmor_ops = { + .ptrace = apparmor_ptrace, + .capget = cap_capget, +@@ -928,6 +1003,8 @@ struct security_operations apparmor_ops + .getprocattr = apparmor_getprocattr, + .setprocattr = apparmor_setprocattr, + ++ .register_security = apparmor_register_subsecurity, ++ + .socket_create = apparmor_socket_create, + .socket_post_create = apparmor_socket_post_create, + .socket_bind = apparmor_socket_bind, +@@ -943,13 +1020,14 @@ struct security_operations apparmor_ops + .socket_shutdown = apparmor_socket_shutdown, + }; + +-void info_message(const char *str) ++void info_message(const char *str, const char *name) + { + struct aa_audit sa; + memset(&sa, 0, sizeof(sa)); + sa.gfp_mask = GFP_KERNEL; + sa.info = str; +- printk(KERN_INFO "AppArmor: %s\n", str); ++ sa.name = name; ++ printk(KERN_INFO "AppArmor: %s %s\n", str, name); + if (audit_enabled) + aa_audit_message(NULL, &sa, AUDIT_APPARMOR_STATUS); + } +@@ -959,7 +1037,7 @@ static int __init apparmor_init(void) + int error; + + if (!apparmor_enabled) { +- info_message("AppArmor disabled by boottime parameter\n"); ++ info_message("AppArmor disabled by boottime parameter", ""); + return 0; + } + +@@ -981,9 +1059,10 @@ static int __init apparmor_init(void) + /* Report that AppArmor successfully initialized */ + apparmor_initialized = 1; + if (apparmor_complain) +- info_message("AppArmor initialized: complainmode enabled"); ++ info_message("AppArmor initialized: complainmode enabled", ++ NULL); + else +- info_message("AppArmor initialized"); ++ info_message("AppArmor initialized", NULL); + + return error; + +@@ -1021,7 +1100,7 @@ void apparmor_disable(void) + + apparmor_initialized = 0; + +- info_message("AppArmor protection removed"); ++ info_message("AppArmor protection removed", NULL); + } + + MODULE_DESCRIPTION("AppArmor process confinement"); diff --git a/kernel-patches/2.6.25/d_namespace_path.diff b/kernel-patches/2.6.25/d_namespace_path.diff new file mode 100644 index 000000000..54149ebcf --- /dev/null +++ b/kernel-patches/2.6.25/d_namespace_path.diff @@ -0,0 +1,87 @@ +From: Andreas Gruenbacher +Subject: Add d_namespace_path() to compute namespace relative pathnames + +In AppArmor, we are interested in pathnames relative to the namespace root. +This is the same as d_path() except for the root where the search ends. Add +a function for computing the namespace-relative path. + +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/dcache.c | 6 +++--- + fs/namespace.c | 27 +++++++++++++++++++++++++++ + include/linux/dcache.h | 3 ++- + include/linux/mount.h | 2 ++ + 4 files changed, 34 insertions(+), 4 deletions(-) + +--- a/fs/dcache.c ++++ b/fs/dcache.c +@@ -1766,9 +1766,9 @@ shouldnt_be_hashed: + * + * Returns the buffer or an error code. + */ +-static char *__d_path(struct dentry *dentry, struct vfsmount *vfsmnt, +- struct path *root, char *buffer, int buflen, +- int fail_deleted) ++char *__d_path(struct dentry *dentry, struct vfsmount *vfsmnt, ++ struct path *root, char *buffer, int buflen, ++ int fail_deleted) + { + int namelen, is_slash, vfsmount_locked = 0; + +--- a/fs/namespace.c ++++ b/fs/namespace.c +@@ -1852,3 +1852,30 @@ void __put_mnt_ns(struct mnt_namespace * + release_mounts(&umount_list); + kfree(ns); + } ++ ++char *d_namespace_path(struct dentry *dentry, struct vfsmount *vfsmnt, ++ char *buf, int buflen) ++{ ++ struct path root, ns_root = { }; ++ char *res; ++ ++ read_lock(¤t->fs->lock); ++ root = current->fs->root; ++ path_get(¤t->fs->root); ++ read_unlock(¤t->fs->lock); ++ spin_lock(&vfsmount_lock); ++ if (root.mnt) ++ ns_root.mnt = mntget(root.mnt->mnt_ns->root); ++ if (ns_root.mnt) ++ ns_root.dentry = dget(ns_root.mnt->mnt_root); ++ spin_unlock(&vfsmount_lock); ++ res = __d_path(dentry, vfsmnt, &ns_root, buf, buflen, 1); ++ path_put(&root); ++ path_put(&ns_root); ++ ++ /* Prevent empty path for lazily unmounted filesystems. */ ++ if (!IS_ERR(res) && *res == '\0') ++ *--res = '.'; ++ return res; ++} ++EXPORT_SYMBOL(d_namespace_path); +--- a/include/linux/dcache.h ++++ b/include/linux/dcache.h +@@ -300,7 +300,8 @@ extern int d_validate(struct dentry *, s + * helper function for dentry_operations.d_dname() members + */ + extern char *dynamic_dname(struct dentry *, char *, int, const char *, ...); +- ++extern char *__d_path(struct dentry *, struct vfsmount *, struct path *, ++ char *, int, int); + extern char *d_path(struct path *, char *, int); + + /* Allocation counts.. */ +--- a/include/linux/mount.h ++++ b/include/linux/mount.h +@@ -103,5 +103,7 @@ extern void mark_mounts_for_expiry(struc + extern spinlock_t vfsmount_lock; + extern dev_t name_to_dev_t(char *name); + ++extern char *d_namespace_path(struct dentry *, struct vfsmount *, char *, int); ++ + #endif + #endif /* _LINUX_MOUNT_H */ diff --git a/kernel-patches/2.6.25/do_path_lookup-nameidata.diff b/kernel-patches/2.6.25/do_path_lookup-nameidata.diff new file mode 100644 index 000000000..d4cb248f2 --- /dev/null +++ b/kernel-patches/2.6.25/do_path_lookup-nameidata.diff @@ -0,0 +1,42 @@ +From: Andreas Gruenbacher +Subject: Switch to vfs_permission() in do_path_lookup() + +Switch from file_permission() to vfs_permission() in do_path_lookup(): +this avoids calling permission() with a NULL nameidata here. + +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/namei.c | 7 ++----- + 1 file changed, 2 insertions(+), 5 deletions(-) + +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -1150,24 +1150,21 @@ static int do_path_lookup(int dfd, const + path_get(&fs->pwd); + read_unlock(&fs->lock); + } else { +- struct dentry *dentry; +- + file = fget_light(dfd, &fput_needed); + retval = -EBADF; + if (!file) + goto out_fail; + +- dentry = file->f_path.dentry; ++ nd->path = file->f_path; + + retval = -ENOTDIR; +- if (!S_ISDIR(dentry->d_inode->i_mode)) ++ if (!S_ISDIR(nd->path.dentry->d_inode->i_mode)) + goto fput_fail; + + retval = file_permission(file, MAY_EXEC); + if (retval) + goto fput_fail; + +- nd->path = file->f_path; + path_get(&file->f_path); + + fput_light(file, fput_needed); diff --git a/kernel-patches/2.6.25/file-handle-ops.diff b/kernel-patches/2.6.25/file-handle-ops.diff new file mode 100644 index 000000000..27432aaf5 --- /dev/null +++ b/kernel-patches/2.6.25/file-handle-ops.diff @@ -0,0 +1,84 @@ +From: Andreas Gruenbacher +Subject: Enable LSM hooks to distinguish operations on file descriptors from operations on pathnames + +Struct iattr already contains ia_file since commit cc4e69de from +Miklos (which is related to commit befc649c). Use this to pass +struct file down the setattr hooks. This allows LSMs to distinguish +operations on file descriptors from operations on paths. + +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen +Cc: Miklos Szeredi + +--- + fs/nfsd/vfs.c | 12 +++++++----- + fs/open.c | 5 ++++- + 2 files changed, 11 insertions(+), 6 deletions(-) + +--- a/fs/nfsd/vfs.c ++++ b/fs/nfsd/vfs.c +@@ -413,7 +413,7 @@ static ssize_t nfsd_getxattr(struct dent + { + ssize_t buflen; + +- buflen = vfs_getxattr(dentry, mnt, key, NULL, 0); ++ buflen = vfs_getxattr(dentry, mnt, key, NULL, 0, NULL); + if (buflen <= 0) + return buflen; + +@@ -421,7 +421,7 @@ static ssize_t nfsd_getxattr(struct dent + if (!*buf) + return -ENOMEM; + +- return vfs_getxattr(dentry, mnt, key, *buf, buflen); ++ return vfs_getxattr(dentry, mnt, key, *buf, buflen, NULL); + } + #endif + +@@ -447,7 +447,7 @@ set_nfsv4_acl_one(struct dentry *dentry, + goto out; + } + +- error = vfs_setxattr(dentry, mnt, key, buf, len, 0); ++ error = vfs_setxattr(dentry, mnt, key, buf, len, 0, NULL); + out: + kfree(buf); + return error; +@@ -2061,12 +2061,14 @@ nfsd_set_posix_acl(struct svc_fh *fhp, i + + mnt = fhp->fh_export->ex_path.mnt; + if (size) +- error = vfs_setxattr(fhp->fh_dentry, mnt, name, value, size,0); ++ error = vfs_setxattr(fhp->fh_dentry, mnt, name, value, size, 0, ++ NULL); + else { + if (!S_ISDIR(inode->i_mode) && type == ACL_TYPE_DEFAULT) + error = 0; + else { +- error = vfs_removexattr(fhp->fh_dentry, mnt, name); ++ error = vfs_removexattr(fhp->fh_dentry, mnt, name, ++ NULL); + if (error == -ENODATA) + error = 0; + } +--- a/fs/open.c ++++ b/fs/open.c +@@ -577,7 +577,7 @@ asmlinkage long sys_fchmod(unsigned int + if (mode == (mode_t) -1) + mode = inode->i_mode; + newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); +- newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; ++ newattrs.ia_valid = ATTR_MODE | ATTR_CTIME | ATTR_FILE; + err = fnotify_change(dentry, file->f_path.mnt, &newattrs, file); + mutex_unlock(&inode->i_mutex); + +@@ -657,6 +657,9 @@ static int chown_common(struct dentry * + if (!S_ISDIR(inode->i_mode)) + newattrs.ia_valid |= + ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV; ++ if (file) ++ newattrs.ia_valid |= ATTR_FILE; ++ + mutex_lock(&inode->i_mutex); + error = fnotify_change(dentry, mnt, &newattrs, file); + mutex_unlock(&inode->i_mutex); diff --git a/kernel-patches/2.6.25/file_permission-nameidata.diff b/kernel-patches/2.6.25/file_permission-nameidata.diff new file mode 100644 index 000000000..85aa578b7 --- /dev/null +++ b/kernel-patches/2.6.25/file_permission-nameidata.diff @@ -0,0 +1,30 @@ +From: Andreas Gruenbacher +Subject: Fix file_permission() + +We cannot easily switch from file_permission() to vfs_permission() +everywhere, so fix file_permission() to not use a NULL nameidata +for the remaining users. + +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/namei.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -313,7 +313,12 @@ int vfs_permission(struct nameidata *nd, + */ + int file_permission(struct file *file, int mask) + { +- return permission(file->f_path.dentry->d_inode, mask, NULL); ++ struct nameidata nd; ++ ++ nd.path = file->f_path; ++ nd.flags = LOOKUP_ACCESS; ++ ++ return permission(nd.path.dentry->d_inode, mask, &nd); + } + + /* diff --git a/kernel-patches/2.6.25/fix-vfs_rmdir.diff b/kernel-patches/2.6.25/fix-vfs_rmdir.diff new file mode 100644 index 000000000..5169b219d --- /dev/null +++ b/kernel-patches/2.6.25/fix-vfs_rmdir.diff @@ -0,0 +1,44 @@ +From: John Johansen +Subject: Call lsm hook before unhashing dentry in vfs_rmdir() + +If we unhash the dentry before calling the security_inode_rmdir hook, +we cannot compute the file's pathname in the hook anymore. AppArmor +needs to know the filename in order to decide whether a file may be +deleted, though. + +Signed-off-by: John Johansen +Signed-off-by: Andreas Gruenbacher + +--- + fs/namei.c | 13 +++++++------ + 1 file changed, 7 insertions(+), 6 deletions(-) + +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -2100,6 +2100,10 @@ int vfs_rmdir(struct inode *dir, struct + if (!dir->i_op || !dir->i_op->rmdir) + return -EPERM; + ++ error = security_inode_rmdir(dir, dentry, mnt); ++ if (error) ++ return error; ++ + DQUOT_INIT(dir); + + mutex_lock(&dentry->d_inode->i_mutex); +@@ -2107,12 +2111,9 @@ int vfs_rmdir(struct inode *dir, struct + if (d_mountpoint(dentry)) + error = -EBUSY; + else { +- error = security_inode_rmdir(dir, dentry, mnt); +- if (!error) { +- error = dir->i_op->rmdir(dir, dentry); +- if (!error) +- dentry->d_inode->i_flags |= S_DEAD; +- } ++ error = dir->i_op->rmdir(dir, dentry); ++ if (!error) ++ dentry->d_inode->i_flags |= S_DEAD; + } + mutex_unlock(&dentry->d_inode->i_mutex); + if (!error) { diff --git a/kernel-patches/2.6.25/fsetattr-reintro-ATTR_FILE.diff b/kernel-patches/2.6.25/fsetattr-reintro-ATTR_FILE.diff new file mode 100644 index 000000000..98891af94 --- /dev/null +++ b/kernel-patches/2.6.25/fsetattr-reintro-ATTR_FILE.diff @@ -0,0 +1,27 @@ +--- + fs/open.c | 3 +++ + include/linux/fs.h | 1 + + 2 files changed, 4 insertions(+) + +--- a/fs/open.c ++++ b/fs/open.c +@@ -207,6 +207,9 @@ int do_truncate(struct dentry *dentry, s + newattrs.ia_size = length; + newattrs.ia_valid = ATTR_SIZE | time_attrs; + ++ if (filp) ++ newattrs.ia_valid |= ATTR_FILE; ++ + /* Remove suid/sgid on truncate too */ + newattrs.ia_valid |= should_remove_suid(dentry); + +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -331,6 +331,7 @@ typedef void (dio_iodone_t)(struct kiocb + #define ATTR_ATTR_FLAG 1024 + #define ATTR_KILL_SUID 2048 + #define ATTR_KILL_SGID 4096 ++#define ATTR_FILE 8192 + #define ATTR_KILL_PRIV 16384 + #define ATTR_OPEN 32768 /* Truncating from open(O_TRUNC) */ + diff --git a/kernel-patches/2.6.25/fsetattr.diff b/kernel-patches/2.6.25/fsetattr.diff new file mode 100644 index 000000000..bea71f727 --- /dev/null +++ b/kernel-patches/2.6.25/fsetattr.diff @@ -0,0 +1,394 @@ +Subject: VFS: new fsetattr() file operation + +From: Miklos Szeredi + +Add a new file operation: f_op->fsetattr(), that is invoked by +ftruncate, fchmod, fchown and utimensat. Fall back to i_op->setattr() +if it is not defined. + +For the reasons why we need this, see patch adding fgetattr(). + +ftruncate() already passed the open file to the filesystem via the +ia_file member of struct iattr. However it is cleaner to have a +separate file operation for this, so remove ia_file, ATTR_FILE and +convert existing users: fuse and AFS. + +Signed-off-by: Miklos Szeredi --- +Signed-off-by: John Johansen --- + +--- + fs/afs/dir.c | 1 + + fs/afs/file.c | 1 + + fs/afs/inode.c | 19 +++++++++++++++---- + fs/afs/internal.h | 1 + + fs/attr.c | 19 +++++++++++++++---- + fs/fuse/dir.c | 20 +++++++++----------- + fs/fuse/file.c | 7 +++++++ + fs/fuse/fuse_i.h | 4 ++++ + fs/open.c | 20 ++++++++------------ + fs/utimes.c | 2 +- + include/linux/fs.h | 10 ++-------- + 11 files changed, 64 insertions(+), 40 deletions(-) + +--- a/fs/afs/dir.c ++++ b/fs/afs/dir.c +@@ -45,6 +45,7 @@ const struct file_operations afs_dir_fil + .release = afs_release, + .readdir = afs_readdir, + .lock = afs_lock, ++ .fsetattr = afs_fsetattr, + }; + + const struct inode_operations afs_dir_inode_operations = { +--- a/fs/afs/file.c ++++ b/fs/afs/file.c +@@ -36,6 +36,7 @@ const struct file_operations afs_file_op + .fsync = afs_fsync, + .lock = afs_lock, + .flock = afs_flock, ++ .fsetattr = afs_fsetattr, + }; + + const struct inode_operations afs_file_inode_operations = { +--- a/fs/afs/inode.c ++++ b/fs/afs/inode.c +@@ -358,7 +358,8 @@ void afs_clear_inode(struct inode *inode + /* + * set the attributes of an inode + */ +-int afs_setattr(struct dentry *dentry, struct iattr *attr) ++static int afs_do_setattr(struct dentry *dentry, struct iattr *attr, ++ struct file *file) + { + struct afs_vnode *vnode = AFS_FS_I(dentry->d_inode); + struct key *key; +@@ -380,8 +381,8 @@ int afs_setattr(struct dentry *dentry, s + afs_writeback_all(vnode); + } + +- if (attr->ia_valid & ATTR_FILE) { +- key = attr->ia_file->private_data; ++ if (file) { ++ key = file->private_data; + } else { + key = afs_request_key(vnode->volume->cell); + if (IS_ERR(key)) { +@@ -391,10 +392,20 @@ int afs_setattr(struct dentry *dentry, s + } + + ret = afs_vnode_setattr(vnode, key, attr); +- if (!(attr->ia_valid & ATTR_FILE)) ++ if (!file) + key_put(key); + + error: + _leave(" = %d", ret); + return ret; + } ++ ++int afs_setattr(struct dentry *dentry, struct iattr *attr) ++{ ++ return afs_do_setattr(dentry, attr, NULL); ++} ++ ++int afs_fsetattr(struct file *file, struct iattr *attr) ++{ ++ return afs_do_setattr(file->f_path.dentry, attr, file); ++} +--- a/fs/afs/internal.h ++++ b/fs/afs/internal.h +@@ -550,6 +550,7 @@ extern void afs_zap_data(struct afs_vnod + extern int afs_validate(struct afs_vnode *, struct key *); + extern int afs_getattr(struct vfsmount *, struct dentry *, struct kstat *); + extern int afs_setattr(struct dentry *, struct iattr *); ++extern int afs_fsetattr(struct file *, struct iattr *); + extern void afs_clear_inode(struct inode *); + + /* +--- a/fs/attr.c ++++ b/fs/attr.c +@@ -100,8 +100,8 @@ int inode_setattr(struct inode * inode, + } + EXPORT_SYMBOL(inode_setattr); + +-int notify_change(struct dentry *dentry, struct vfsmount *mnt, +- struct iattr *attr) ++int fnotify_change(struct dentry *dentry, struct vfsmount *mnt, ++ struct iattr *attr, struct file *file) + { + struct inode *inode = dentry->d_inode; + mode_t mode = inode->i_mode; +@@ -160,8 +160,12 @@ int notify_change(struct dentry *dentry, + + if (inode->i_op && inode->i_op->setattr) { + error = security_inode_setattr(dentry, mnt, attr); +- if (!error) +- error = inode->i_op->setattr(dentry, attr); ++ if (!error) { ++ if (file && file->f_op && file->f_op->fsetattr) ++ error = file->f_op->fsetattr(file, attr); ++ else ++ error = inode->i_op->setattr(dentry, attr); ++ } + } else { + error = inode_change_ok(inode, attr); + if (!error) +@@ -183,5 +187,12 @@ int notify_change(struct dentry *dentry, + + return error; + } ++EXPORT_SYMBOL_GPL(fnotify_change); ++ ++int notify_change(struct dentry *dentry, struct vfsmount *mnt, ++ struct iattr *attr) ++{ ++ return fnotify_change(dentry, mnt, attr, NULL); ++} + + EXPORT_SYMBOL(notify_change); +--- a/fs/fuse/dir.c ++++ b/fs/fuse/dir.c +@@ -1064,21 +1064,22 @@ static int fuse_dir_fsync(struct file *f + return file ? fuse_fsync_common(file, de, datasync, 1) : 0; + } + +-static bool update_mtime(unsigned ivalid) ++static bool update_mtime(unsigned ivalid, bool have_file) + { + /* Always update if mtime is explicitly set */ + if (ivalid & ATTR_MTIME_SET) + return true; + + /* If it's an open(O_TRUNC) or an ftruncate(), don't update */ +- if ((ivalid & ATTR_SIZE) && (ivalid & (ATTR_OPEN | ATTR_FILE))) ++ if ((ivalid & ATTR_SIZE) && ((ivalid & ATTR_OPEN) || have_file)) + return false; + + /* In all other cases update */ + return true; + } + +-static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg) ++static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg, ++ bool have_file) + { + unsigned ivalid = iattr->ia_valid; + +@@ -1097,7 +1098,7 @@ static void iattr_to_fattr(struct iattr + if (!(ivalid & ATTR_ATIME_SET)) + arg->valid |= FATTR_ATIME_NOW; + } +- if ((ivalid & ATTR_MTIME) && update_mtime(ivalid)) { ++ if ((ivalid & ATTR_MTIME) && update_mtime(ivalid, have_file)) { + arg->valid |= FATTR_MTIME; + arg->mtime = iattr->ia_mtime.tv_sec; + arg->mtimensec = iattr->ia_mtime.tv_nsec; +@@ -1114,8 +1115,8 @@ static void iattr_to_fattr(struct iattr + * vmtruncate() doesn't allow for this case, so do the rlimit checking + * and the actual truncation by hand. + */ +-static int fuse_do_setattr(struct dentry *entry, struct iattr *attr, +- struct file *file) ++int fuse_do_setattr(struct dentry *entry, struct iattr *attr, ++ struct file *file) + { + struct inode *inode = entry->d_inode; + struct fuse_conn *fc = get_fuse_conn(inode); +@@ -1153,7 +1154,7 @@ static int fuse_do_setattr(struct dentry + + memset(&inarg, 0, sizeof(inarg)); + memset(&outarg, 0, sizeof(outarg)); +- iattr_to_fattr(attr, &inarg); ++ iattr_to_fattr(attr, &inarg, file != NULL); + if (file) { + struct fuse_file *ff = file->private_data; + inarg.valid |= FATTR_FH; +@@ -1195,10 +1196,7 @@ static int fuse_do_setattr(struct dentry + + static int fuse_setattr(struct dentry *entry, struct iattr *attr) + { +- if (attr->ia_valid & ATTR_FILE) +- return fuse_do_setattr(entry, attr, attr->ia_file); +- else +- return fuse_do_setattr(entry, attr, NULL); ++ return fuse_do_setattr(entry, attr, NULL); + } + + static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry, +--- a/fs/fuse/file.c ++++ b/fs/fuse/file.c +@@ -909,6 +909,11 @@ static sector_t fuse_bmap(struct address + return err ? 0 : outarg.block; + } + ++static int fuse_fsetattr(struct file *file, struct iattr *attr) ++{ ++ return fuse_do_setattr(file->f_path.dentry, attr, file); ++} ++ + static const struct file_operations fuse_file_operations = { + .llseek = generic_file_llseek, + .read = do_sync_read, +@@ -922,6 +927,7 @@ static const struct file_operations fuse + .fsync = fuse_fsync, + .lock = fuse_file_lock, + .flock = fuse_file_flock, ++ .fsetattr = fuse_fsetattr, + .splice_read = generic_file_splice_read, + }; + +@@ -935,6 +941,7 @@ static const struct file_operations fuse + .fsync = fuse_fsync, + .lock = fuse_file_lock, + .flock = fuse_file_flock, ++ .fsetattr = fuse_fsetattr, + /* no mmap and splice_read */ + }; + +--- a/fs/fuse/fuse_i.h ++++ b/fs/fuse/fuse_i.h +@@ -509,6 +509,10 @@ void fuse_change_attributes(struct inode + */ + int fuse_dev_init(void); + ++ ++int fuse_do_setattr(struct dentry *entry, struct iattr *attr, ++ struct file *file); ++ + /** + * Cleanup the client device + */ +--- a/fs/open.c ++++ b/fs/open.c +@@ -206,16 +206,12 @@ int do_truncate(struct dentry *dentry, s + + newattrs.ia_size = length; + newattrs.ia_valid = ATTR_SIZE | time_attrs; +- if (filp) { +- newattrs.ia_file = filp; +- newattrs.ia_valid |= ATTR_FILE; +- } + + /* Remove suid/sgid on truncate too */ + newattrs.ia_valid |= should_remove_suid(dentry); + + mutex_lock(&dentry->d_inode->i_mutex); +- err = notify_change(dentry, mnt, &newattrs); ++ err = fnotify_change(dentry, mnt, &newattrs, filp); + mutex_unlock(&dentry->d_inode->i_mutex); + return err; + } +@@ -579,7 +575,7 @@ asmlinkage long sys_fchmod(unsigned int + mode = inode->i_mode; + newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); + newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; +- err = notify_change(dentry, file->f_path.mnt, &newattrs); ++ err = fnotify_change(dentry, file->f_path.mnt, &newattrs, file); + mutex_unlock(&inode->i_mutex); + + out_putf: +@@ -629,7 +625,7 @@ asmlinkage long sys_chmod(const char __u + } + + static int chown_common(struct dentry * dentry, struct vfsmount *mnt, +- uid_t user, gid_t group) ++ uid_t user, gid_t group, struct file *file) + { + struct inode * inode; + int error; +@@ -659,7 +655,7 @@ static int chown_common(struct dentry * + newattrs.ia_valid |= + ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV; + mutex_lock(&inode->i_mutex); +- error = notify_change(dentry, mnt, &newattrs); ++ error = fnotify_change(dentry, mnt, &newattrs, file); + mutex_unlock(&inode->i_mutex); + out: + return error; +@@ -673,7 +669,7 @@ asmlinkage long sys_chown(const char __u + error = user_path_walk(filename, &nd); + if (error) + goto out; +- error = chown_common(nd.path.dentry, nd.path.mnt, user, group); ++ error = chown_common(nd.path.dentry, nd.path.mnt, user, group, NULL); + path_put(&nd.path); + out: + return error; +@@ -693,7 +689,7 @@ asmlinkage long sys_fchownat(int dfd, co + error = __user_walk_fd(dfd, filename, follow, &nd); + if (error) + goto out; +- error = chown_common(nd.path.dentry, nd.path.mnt, user, group); ++ error = chown_common(nd.path.dentry, nd.path.mnt, user, group, NULL); + path_put(&nd.path); + out: + return error; +@@ -707,7 +703,7 @@ asmlinkage long sys_lchown(const char __ + error = user_path_walk_link(filename, &nd); + if (error) + goto out; +- error = chown_common(nd.path.dentry, nd.path.mnt, user, group); ++ error = chown_common(nd.path.dentry, nd.path.mnt, user, group, NULL); + path_put(&nd.path); + out: + return error; +@@ -726,7 +722,7 @@ asmlinkage long sys_fchown(unsigned int + + dentry = file->f_path.dentry; + audit_inode(NULL, dentry); +- error = chown_common(dentry, file->f_path.mnt, user, group); ++ error = chown_common(dentry, file->f_path.mnt, user, group, file); + fput(file); + out: + return error; +--- a/fs/utimes.c ++++ b/fs/utimes.c +@@ -132,7 +132,7 @@ long do_utimes(int dfd, char __user *fil + } + } + mutex_lock(&inode->i_mutex); +- error = notify_change(path.dentry, path.mnt, &newattrs); ++ error = fnotify_change(path.dentry, path.mnt, &newattrs, f); + mutex_unlock(&inode->i_mutex); + dput_and_out: + if (f) +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -331,7 +331,6 @@ typedef void (dio_iodone_t)(struct kiocb + #define ATTR_ATTR_FLAG 1024 + #define ATTR_KILL_SUID 2048 + #define ATTR_KILL_SGID 4096 +-#define ATTR_FILE 8192 + #define ATTR_KILL_PRIV 16384 + #define ATTR_OPEN 32768 /* Truncating from open(O_TRUNC) */ + +@@ -353,13 +352,6 @@ struct iattr { + struct timespec ia_atime; + struct timespec ia_mtime; + struct timespec ia_ctime; +- +- /* +- * Not an attribute, but an auxilary info for filesystems wanting to +- * implement an ftruncate() like method. NOTE: filesystem should +- * check for (ia_valid & ATTR_FILE), and not for (ia_file != NULL). +- */ +- struct file *ia_file; + }; + + /* +@@ -1196,6 +1188,7 @@ struct file_operations { + ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); + ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); + int (*setlease)(struct file *, long, struct file_lock **); ++ int (*fsetattr)(struct file *, struct iattr *); + }; + + struct inode_operations { +@@ -1715,6 +1708,7 @@ extern int do_remount_sb(struct super_bl + extern sector_t bmap(struct inode *, sector_t); + #endif + extern int notify_change(struct dentry *, struct vfsmount *, struct iattr *); ++extern int fnotify_change(struct dentry *, struct vfsmount *, struct iattr *, struct file *); + extern int permission(struct inode *, int, struct nameidata *); + extern int generic_permission(struct inode *, int, + int (*check_acl)(struct inode *, int)); diff --git a/kernel-patches/2.6.25/mount-consistent-__d_path.diff b/kernel-patches/2.6.25/mount-consistent-__d_path.diff new file mode 100644 index 000000000..2aaed05f2 --- /dev/null +++ b/kernel-patches/2.6.25/mount-consistent-__d_path.diff @@ -0,0 +1,60 @@ +From: Andreas Gruenbacher +Subject: Make d_path() consistent across mount operations + +The path that __d_path() computes can become slightly inconsistent when it +races with mount operations: it grabs the vfsmount_lock when traversing mount +points but immediately drops it again, only to re-grab it when it reaches the +next mount point. The result is that the filename computed is not always +consisent, and the file may never have had that name. (This is unlikely, but +still possible.) + +Fix this by grabbing the vfsmount_lock when the first mount point is reached, +and holding onto it until the d_cache lookup is completed. + +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/dcache.c | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +--- a/fs/dcache.c ++++ b/fs/dcache.c +@@ -1770,7 +1770,7 @@ static char *__d_path(struct dentry *den + struct path *root, char *buffer, int buflen, + int fail_deleted) + { +- int namelen, is_slash; ++ int namelen, is_slash, vfsmount_locked = 0; + + if (buflen < 2) + return ERR_PTR(-ENAMETOOLONG); +@@ -1794,14 +1794,14 @@ static char *__d_path(struct dentry *den + struct dentry * parent; + + if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { +- spin_lock(&vfsmount_lock); +- if (vfsmnt->mnt_parent == vfsmnt) { +- spin_unlock(&vfsmount_lock); +- goto global_root; ++ if (!vfsmount_locked) { ++ spin_lock(&vfsmount_lock); ++ vfsmount_locked = 1; + } ++ if (vfsmnt->mnt_parent == vfsmnt) ++ goto global_root; + dentry = vfsmnt->mnt_mountpoint; + vfsmnt = vfsmnt->mnt_parent; +- spin_unlock(&vfsmount_lock); + continue; + } + parent = dentry->d_parent; +@@ -1820,6 +1820,8 @@ static char *__d_path(struct dentry *den + *--buffer = '/'; + + out: ++ if (vfsmount_locked) ++ spin_unlock(&vfsmount_lock); + spin_unlock(&dcache_lock); + return buffer; + diff --git a/kernel-patches/2.6.25/parent-permission.diff b/kernel-patches/2.6.25/parent-permission.diff new file mode 100644 index 000000000..681d642ee --- /dev/null +++ b/kernel-patches/2.6.25/parent-permission.diff @@ -0,0 +1,24 @@ +From: Andreas Gruenbacher +Subject: Allow permission functions to tell between parent and leaf checks + +Set the LOOKUP_CONTINUE flag when checking parent permissions. This allows +permission functions to tell between parent and leaf checks. + +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/namei.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -1511,6 +1511,8 @@ static inline int may_create(struct inod + return -EEXIST; + if (IS_DEADDIR(dir)) + return -ENOENT; ++ if (nd) ++ nd->flags |= LOOKUP_CONTINUE; + return permission(dir,MAY_WRITE | MAY_EXEC, nd); + } + diff --git a/kernel-patches/2.6.25/remove_suid.diff b/kernel-patches/2.6.25/remove_suid.diff new file mode 100644 index 000000000..ee782808e --- /dev/null +++ b/kernel-patches/2.6.25/remove_suid.diff @@ -0,0 +1,132 @@ +From: Andreas Gruenbacher +Subject: Pass struct path down to remove_suid and children + +Required by a later patch that adds a struct vfsmount parameter to +notify_change(). + +Signed-off-by: Tony Jones +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + + fs/ntfs/file.c | 2 +- + fs/splice.c | 4 ++-- + fs/xfs/linux-2.6/xfs_lrw.c | 2 +- + include/linux/fs.h | 4 ++-- + mm/filemap.c | 16 ++++++++-------- + mm/filemap_xip.c | 2 +- + 6 files changed, 15 insertions(+), 15 deletions(-) + +--- a/fs/ntfs/file.c ++++ b/fs/ntfs/file.c +@@ -2118,7 +2118,7 @@ static ssize_t ntfs_file_aio_write_noloc + goto out; + if (!count) + goto out; +- err = remove_suid(file->f_path.dentry); ++ err = remove_suid(&file->f_path); + if (err) + goto out; + file_update_time(file); +--- a/fs/splice.c ++++ b/fs/splice.c +@@ -781,7 +781,7 @@ generic_file_splice_write_nolock(struct + ssize_t ret; + int err; + +- err = remove_suid(out->f_path.dentry); ++ err = remove_suid(&out->f_path); + if (unlikely(err)) + return err; + +@@ -841,7 +841,7 @@ generic_file_splice_write(struct pipe_in + if (killpriv) + err = security_inode_killpriv(out->f_path.dentry); + if (!err && killsuid) +- err = __remove_suid(out->f_path.dentry, killsuid); ++ err = __remove_suid(&out->f_path, killsuid); + mutex_unlock(&inode->i_mutex); + if (err) + return err; +--- a/fs/xfs/linux-2.6/xfs_lrw.c ++++ b/fs/xfs/linux-2.6/xfs_lrw.c +@@ -716,7 +716,7 @@ start: + !capable(CAP_FSETID)) { + error = xfs_write_clear_setuid(xip); + if (likely(!error)) +- error = -remove_suid(file->f_path.dentry); ++ error = -remove_suid(&file->f_path); + if (unlikely(error)) { + goto out_unlock_internal; + } +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -1776,9 +1776,9 @@ extern void iget_failed(struct inode *); + extern void clear_inode(struct inode *); + extern void destroy_inode(struct inode *); + extern struct inode *new_inode(struct super_block *); +-extern int __remove_suid(struct dentry *, int); ++extern int __remove_suid(struct path *, int); + extern int should_remove_suid(struct dentry *); +-extern int remove_suid(struct dentry *); ++extern int remove_suid(struct path *); + + extern void __insert_inode_hash(struct inode *, unsigned long hashval); + extern void remove_inode_hash(struct inode *); +--- a/mm/filemap.c ++++ b/mm/filemap.c +@@ -1653,26 +1653,26 @@ int should_remove_suid(struct dentry *de + } + EXPORT_SYMBOL(should_remove_suid); + +-int __remove_suid(struct dentry *dentry, int kill) ++int __remove_suid(struct path *path, int kill) + { + struct iattr newattrs; + + newattrs.ia_valid = ATTR_FORCE | kill; +- return notify_change(dentry, &newattrs); ++ return notify_change(path->dentry, &newattrs); + } + +-int remove_suid(struct dentry *dentry) ++int remove_suid(struct path *path) + { +- int killsuid = should_remove_suid(dentry); +- int killpriv = security_inode_need_killpriv(dentry); ++ int killsuid = should_remove_suid(path->dentry); ++ int killpriv = security_inode_need_killpriv(path->dentry); + int error = 0; + + if (killpriv < 0) + return killpriv; + if (killpriv) +- error = security_inode_killpriv(dentry); ++ error = security_inode_killpriv(path->dentry); + if (!error && killsuid) +- error = __remove_suid(dentry, killsuid); ++ error = __remove_suid(path, killsuid); + + return error; + } +@@ -2387,7 +2387,7 @@ __generic_file_aio_write_nolock(struct k + if (count == 0) + goto out; + +- err = remove_suid(file->f_path.dentry); ++ err = remove_suid(&file->f_path); + if (err) + goto out; + +--- a/mm/filemap_xip.c ++++ b/mm/filemap_xip.c +@@ -380,7 +380,7 @@ xip_file_write(struct file *filp, const + if (count == 0) + goto out_backing; + +- ret = remove_suid(filp->f_path.dentry); ++ ret = remove_suid(&filp->f_path); + if (ret) + goto out_backing; + diff --git a/kernel-patches/2.6.25/security-create.diff b/kernel-patches/2.6.25/security-create.diff new file mode 100644 index 000000000..003f59ac8 --- /dev/null +++ b/kernel-patches/2.6.25/security-create.diff @@ -0,0 +1,107 @@ +From: Tony Jones +Subject: Pass struct vfsmount to the inode_create LSM hook + +This is needed for computing pathnames in the AppArmor LSM. + +Signed-off-by: Tony Jones +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/namei.c | 2 +- + include/linux/security.h | 9 ++++++--- + security/dummy.c | 2 +- + security/security.c | 5 +++-- + security/selinux/hooks.c | 3 ++- + 5 files changed, 13 insertions(+), 8 deletions(-) + +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -1586,7 +1586,7 @@ int vfs_create(struct inode *dir, struct + return -EACCES; /* shouldn't it be ENOSYS? */ + mode &= S_IALLUGO; + mode |= S_IFREG; +- error = security_inode_create(dir, dentry, mode); ++ error = security_inode_create(dir, dentry, nd ? nd->path.mnt : NULL, mode); + if (error) + return error; + DQUOT_INIT(dir); +--- a/include/linux/security.h ++++ b/include/linux/security.h +@@ -330,6 +330,7 @@ static inline void security_free_mnt_opt + * Check permission to create a regular file. + * @dir contains inode structure of the parent of the new file. + * @dentry contains the dentry structure for the file to be created. ++ * @mnt is the vfsmount corresponding to @dentry (may be NULL). + * @mode contains the file mode of the file to be created. + * Return 0 if permission is granted. + * @inode_link: +@@ -1286,8 +1287,8 @@ struct security_operations { + void (*inode_free_security) (struct inode *inode); + int (*inode_init_security) (struct inode *inode, struct inode *dir, + char **name, void **value, size_t *len); +- int (*inode_create) (struct inode *dir, +- struct dentry *dentry, int mode); ++ int (*inode_create) (struct inode *dir, struct dentry *dentry, ++ struct vfsmount *mnt, int mode); + int (*inode_link) (struct dentry *old_dentry, + struct inode *dir, struct dentry *new_dentry); + int (*inode_unlink) (struct inode *dir, struct dentry *dentry); +@@ -1549,7 +1550,8 @@ int security_inode_alloc(struct inode *i + void security_inode_free(struct inode *inode); + int security_inode_init_security(struct inode *inode, struct inode *dir, + char **name, void **value, size_t *len); +-int security_inode_create(struct inode *dir, struct dentry *dentry, int mode); ++int security_inode_create(struct inode *dir, struct dentry *dentry, ++ struct vfsmount *mnt, int mode); + int security_inode_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *new_dentry); + int security_inode_unlink(struct inode *dir, struct dentry *dentry); +@@ -1887,6 +1889,7 @@ static inline int security_inode_init_se + + static inline int security_inode_create (struct inode *dir, + struct dentry *dentry, ++ struct vfsmount *mnt, + int mode) + { + return 0; +--- a/security/dummy.c ++++ b/security/dummy.c +@@ -287,7 +287,7 @@ static int dummy_inode_init_security (st + } + + static int dummy_inode_create (struct inode *inode, struct dentry *dentry, +- int mask) ++ struct vfsmount *mnt, int mask) + { + return 0; + } +--- a/security/security.c ++++ b/security/security.c +@@ -352,11 +352,12 @@ int security_inode_init_security(struct + } + EXPORT_SYMBOL(security_inode_init_security); + +-int security_inode_create(struct inode *dir, struct dentry *dentry, int mode) ++int security_inode_create(struct inode *dir, struct dentry *dentry, ++ struct vfsmount *mnt, int mode) + { + if (unlikely(IS_PRIVATE(dir))) + return 0; +- return security_ops->inode_create(dir, dentry, mode); ++ return security_ops->inode_create(dir, dentry, mnt, mode); + } + + int security_inode_link(struct dentry *old_dentry, struct inode *dir, +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -2448,7 +2448,8 @@ static int selinux_inode_init_security(s + return 0; + } + +-static int selinux_inode_create(struct inode *dir, struct dentry *dentry, int mask) ++static int selinux_inode_create(struct inode *dir, struct dentry *dentry, ++ struct vfsmount *mnt, int mask) + { + return may_create(dir, dentry, SECCLASS_FILE); + } diff --git a/kernel-patches/2.6.25/security-getxattr.diff b/kernel-patches/2.6.25/security-getxattr.diff new file mode 100644 index 000000000..aef2bb571 --- /dev/null +++ b/kernel-patches/2.6.25/security-getxattr.diff @@ -0,0 +1,110 @@ +From: Tony Jones +Subject: Pass struct vfsmount to the inode_getxattr LSM hook + +This is needed for computing pathnames in the AppArmor LSM. + +Signed-off-by: Tony Jones +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/xattr.c | 2 +- + include/linux/security.h | 11 +++++++---- + security/dummy.c | 3 ++- + security/security.c | 5 +++-- + security/selinux/hooks.c | 3 ++- + 5 files changed, 15 insertions(+), 9 deletions(-) + +--- a/fs/xattr.c ++++ b/fs/xattr.c +@@ -142,7 +142,7 @@ vfs_getxattr(struct dentry *dentry, stru + if (error) + return error; + +- error = security_inode_getxattr(dentry, name); ++ error = security_inode_getxattr(dentry, mnt, name); + if (error) + return error; + +--- a/include/linux/security.h ++++ b/include/linux/security.h +@@ -438,7 +438,7 @@ static inline void security_free_mnt_opt + * @value identified by @name for @dentry and @mnt. + * @inode_getxattr: + * Check permission before obtaining the extended attributes +- * identified by @name for @dentry. ++ * identified by @name for @dentry and @mnt. + * Return 0 if permission is granted. + * @inode_listxattr: + * Check permission before obtaining the list of extended attribute +@@ -1330,7 +1330,8 @@ struct security_operations { + struct vfsmount *mnt, + char *name, void *value, + size_t size, int flags); +- int (*inode_getxattr) (struct dentry *dentry, char *name); ++ int (*inode_getxattr) (struct dentry *dentry, struct vfsmount *mnt, ++ char *name); + int (*inode_listxattr) (struct dentry *dentry); + int (*inode_removexattr) (struct dentry *dentry, char *name); + int (*inode_need_killpriv) (struct dentry *dentry); +@@ -1600,7 +1601,8 @@ int security_inode_setxattr(struct dentr + void security_inode_post_setxattr(struct dentry *dentry, struct vfsmount *mnt, + char *name, void *value, size_t size, + int flags); +-int security_inode_getxattr(struct dentry *dentry, char *name); ++int security_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt, ++ char *name); + int security_inode_listxattr(struct dentry *dentry); + int security_inode_removexattr(struct dentry *dentry, char *name); + int security_inode_need_killpriv(struct dentry *dentry); +@@ -2028,7 +2030,8 @@ static inline void security_inode_post_s + int flags) + { } + +-static inline int security_inode_getxattr (struct dentry *dentry, char *name) ++static inline int security_inode_getxattr (struct dentry *dentry, ++ struct vfsmount *mnt, char *name) + { + return 0; + } +--- a/security/dummy.c ++++ b/security/dummy.c +@@ -390,7 +390,8 @@ static void dummy_inode_post_setxattr (s + { + } + +-static int dummy_inode_getxattr (struct dentry *dentry, char *name) ++static int dummy_inode_getxattr (struct dentry *dentry, ++ struct vfsmount *mnt, char *name) + { + return 0; + } +--- a/security/security.c ++++ b/security/security.c +@@ -482,11 +482,12 @@ void security_inode_post_setxattr(struct + security_ops->inode_post_setxattr(dentry, mnt, name, value, size, flags); + } + +-int security_inode_getxattr(struct dentry *dentry, char *name) ++int security_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt, ++ char *name) + { + if (unlikely(IS_PRIVATE(dentry->d_inode))) + return 0; +- return security_ops->inode_getxattr(dentry, name); ++ return security_ops->inode_getxattr(dentry, mnt, name); + } + + int security_inode_listxattr(struct dentry *dentry) +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -2673,7 +2673,8 @@ static void selinux_inode_post_setxattr( + return; + } + +-static int selinux_inode_getxattr (struct dentry *dentry, char *name) ++static int selinux_inode_getxattr (struct dentry *dentry, struct vfsmount *mnt, ++ char *name) + { + return dentry_has_perm(current, NULL, dentry, FILE__GETATTR); + } diff --git a/kernel-patches/2.6.25/security-link.diff b/kernel-patches/2.6.25/security-link.diff new file mode 100644 index 000000000..619736d39 --- /dev/null +++ b/kernel-patches/2.6.25/security-link.diff @@ -0,0 +1,134 @@ +From: Tony Jones +Subject: Pass the struct vfsmounts to the inode_link LSM hook + +This is needed for computing pathnames in the AppArmor LSM. + +Signed-off-by: Tony Jones +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/namei.c | 3 ++- + include/linux/security.h | 16 +++++++++++----- + security/dummy.c | 6 ++++-- + security/security.c | 8 +++++--- + security/selinux/hooks.c | 9 +++++++-- + 5 files changed, 29 insertions(+), 13 deletions(-) + +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -2360,7 +2360,8 @@ int vfs_link(struct dentry *old_dentry, + if (S_ISDIR(old_dentry->d_inode->i_mode)) + return -EPERM; + +- error = security_inode_link(old_dentry, dir, new_dentry); ++ error = security_inode_link(old_dentry, old_mnt, dir, new_dentry, ++ new_mnt); + if (error) + return error; + +--- a/include/linux/security.h ++++ b/include/linux/security.h +@@ -336,8 +336,10 @@ static inline void security_free_mnt_opt + * @inode_link: + * Check permission before creating a new hard link to a file. + * @old_dentry contains the dentry structure for an existing link to the file. ++ * @old_mnt is the vfsmount corresponding to @old_dentry (may be NULL). + * @dir contains the inode structure of the parent directory of the new link. + * @new_dentry contains the dentry structure for the new link. ++ * @new_mnt is the vfsmount corresponding to @new_dentry (may be NULL). + * Return 0 if permission is granted. + * @inode_unlink: + * Check the permission to remove a hard link to a file. +@@ -1294,8 +1296,9 @@ struct security_operations { + char **name, void **value, size_t *len); + int (*inode_create) (struct inode *dir, struct dentry *dentry, + struct vfsmount *mnt, int mode); +- int (*inode_link) (struct dentry *old_dentry, +- struct inode *dir, struct dentry *new_dentry); ++ int (*inode_link) (struct dentry *old_dentry, struct vfsmount *old_mnt, ++ struct inode *dir, struct dentry *new_dentry, ++ struct vfsmount *new_mnt); + int (*inode_unlink) (struct inode *dir, struct dentry *dentry); + int (*inode_symlink) (struct inode *dir, struct dentry *dentry, + struct vfsmount *mnt, const char *old_name); +@@ -1559,8 +1562,9 @@ int security_inode_init_security(struct + char **name, void **value, size_t *len); + int security_inode_create(struct inode *dir, struct dentry *dentry, + struct vfsmount *mnt, int mode); +-int security_inode_link(struct dentry *old_dentry, struct inode *dir, +- struct dentry *new_dentry); ++int security_inode_link(struct dentry *old_dentry, struct vfsmount *old_mnt, ++ struct inode *dir, struct dentry *new_dentry, ++ struct vfsmount *new_mnt); + int security_inode_unlink(struct inode *dir, struct dentry *dentry); + int security_inode_symlink(struct inode *dir, struct dentry *dentry, + struct vfsmount *mnt, const char *old_name); +@@ -1906,8 +1910,10 @@ static inline int security_inode_create + } + + static inline int security_inode_link (struct dentry *old_dentry, ++ struct vfsmount *old_mnt, + struct inode *dir, +- struct dentry *new_dentry) ++ struct dentry *new_dentry, ++ struct vfsmount *new_mnt) + { + return 0; + } +--- a/security/dummy.c ++++ b/security/dummy.c +@@ -292,8 +292,10 @@ static int dummy_inode_create (struct in + return 0; + } + +-static int dummy_inode_link (struct dentry *old_dentry, struct inode *inode, +- struct dentry *new_dentry) ++static int dummy_inode_link (struct dentry *old_dentry, ++ struct vfsmount *old_mnt, struct inode *inode, ++ struct dentry *new_dentry, ++ struct vfsmount *new_mnt) + { + return 0; + } +--- a/security/security.c ++++ b/security/security.c +@@ -360,12 +360,14 @@ int security_inode_create(struct inode * + return security_ops->inode_create(dir, dentry, mnt, mode); + } + +-int security_inode_link(struct dentry *old_dentry, struct inode *dir, +- struct dentry *new_dentry) ++int security_inode_link(struct dentry *old_dentry, struct vfsmount *old_mnt, ++ struct inode *dir, struct dentry *new_dentry, ++ struct vfsmount *new_mnt) + { + if (unlikely(IS_PRIVATE(old_dentry->d_inode))) + return 0; +- return security_ops->inode_link(old_dentry, dir, new_dentry); ++ return security_ops->inode_link(old_dentry, old_mnt, dir, ++ new_dentry, new_mnt); + } + + int security_inode_unlink(struct inode *dir, struct dentry *dentry) +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -2454,11 +2454,16 @@ static int selinux_inode_create(struct i + return may_create(dir, dentry, SECCLASS_FILE); + } + +-static int selinux_inode_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry) ++static int selinux_inode_link(struct dentry *old_dentry, ++ struct vfsmount *old_mnt, ++ struct inode *dir, ++ struct dentry *new_dentry, ++ struct vfsmount *new_mnt) + { + int rc; + +- rc = secondary_ops->inode_link(old_dentry,dir,new_dentry); ++ rc = secondary_ops->inode_link(old_dentry, old_mnt, dir, new_dentry, ++ new_mnt); + if (rc) + return rc; + return may_link(dir, old_dentry, MAY_LINK); diff --git a/kernel-patches/2.6.25/security-listxattr.diff b/kernel-patches/2.6.25/security-listxattr.diff new file mode 100644 index 000000000..31bda61a9 --- /dev/null +++ b/kernel-patches/2.6.25/security-listxattr.diff @@ -0,0 +1,105 @@ +From: Tony Jones +Subject: Pass struct vfsmount to the inode_listxattr LSM hook + +This is needed for computing pathnames in the AppArmor LSM. + +Signed-off-by: Tony Jones +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/xattr.c | 2 +- + include/linux/security.h | 9 +++++---- + security/dummy.c | 2 +- + security/security.c | 4 ++-- + security/selinux/hooks.c | 2 +- + 5 files changed, 10 insertions(+), 9 deletions(-) + +--- a/fs/xattr.c ++++ b/fs/xattr.c +@@ -175,7 +175,7 @@ vfs_listxattr(struct dentry *dentry, str + struct inode *inode = dentry->d_inode; + ssize_t error; + +- error = security_inode_listxattr(dentry); ++ error = security_inode_listxattr(dentry, mnt); + if (error) + return error; + error = -EOPNOTSUPP; +--- a/include/linux/security.h ++++ b/include/linux/security.h +@@ -442,7 +442,7 @@ static inline void security_free_mnt_opt + * Return 0 if permission is granted. + * @inode_listxattr: + * Check permission before obtaining the list of extended attribute +- * names for @dentry. ++ * names for @dentry and @mnt. + * Return 0 if permission is granted. + * @inode_removexattr: + * Check permission before removing the extended attribute +@@ -1332,7 +1332,7 @@ struct security_operations { + size_t size, int flags); + int (*inode_getxattr) (struct dentry *dentry, struct vfsmount *mnt, + char *name); +- int (*inode_listxattr) (struct dentry *dentry); ++ int (*inode_listxattr) (struct dentry *dentry, struct vfsmount *mnt); + int (*inode_removexattr) (struct dentry *dentry, char *name); + int (*inode_need_killpriv) (struct dentry *dentry); + int (*inode_killpriv) (struct dentry *dentry); +@@ -1603,7 +1603,7 @@ void security_inode_post_setxattr(struct + int flags); + int security_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt, + char *name); +-int security_inode_listxattr(struct dentry *dentry); ++int security_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt); + int security_inode_removexattr(struct dentry *dentry, char *name); + int security_inode_need_killpriv(struct dentry *dentry); + int security_inode_killpriv(struct dentry *dentry); +@@ -2036,7 +2036,8 @@ static inline int security_inode_getxatt + return 0; + } + +-static inline int security_inode_listxattr (struct dentry *dentry) ++static inline int security_inode_listxattr (struct dentry *dentry, ++ struct vfsmount *mnt) + { + return 0; + } +--- a/security/dummy.c ++++ b/security/dummy.c +@@ -396,7 +396,7 @@ static int dummy_inode_getxattr (struct + return 0; + } + +-static int dummy_inode_listxattr (struct dentry *dentry) ++static int dummy_inode_listxattr (struct dentry *dentry, struct vfsmount *mnt) + { + return 0; + } +--- a/security/security.c ++++ b/security/security.c +@@ -490,11 +490,11 @@ int security_inode_getxattr(struct dentr + return security_ops->inode_getxattr(dentry, mnt, name); + } + +-int security_inode_listxattr(struct dentry *dentry) ++int security_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt) + { + if (unlikely(IS_PRIVATE(dentry->d_inode))) + return 0; +- return security_ops->inode_listxattr(dentry); ++ return security_ops->inode_listxattr(dentry, mnt); + } + + int security_inode_removexattr(struct dentry *dentry, char *name) +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -2679,7 +2679,7 @@ static int selinux_inode_getxattr (struc + return dentry_has_perm(current, NULL, dentry, FILE__GETATTR); + } + +-static int selinux_inode_listxattr (struct dentry *dentry) ++static int selinux_inode_listxattr (struct dentry *dentry, struct vfsmount *mnt) + { + return dentry_has_perm(current, NULL, dentry, FILE__GETATTR); + } diff --git a/kernel-patches/2.6.25/security-mkdir.diff b/kernel-patches/2.6.25/security-mkdir.diff new file mode 100644 index 000000000..71fc8087f --- /dev/null +++ b/kernel-patches/2.6.25/security-mkdir.diff @@ -0,0 +1,106 @@ +From: Tony Jones +Subject: Pass struct vfsmount to the inode_mkdir LSM hook + +This is needed for computing pathnames in the AppArmor LSM. + +Signed-off-by: Tony Jones +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/namei.c | 2 +- + include/linux/security.h | 8 ++++++-- + security/dummy.c | 2 +- + security/security.c | 5 +++-- + security/selinux/hooks.c | 3 ++- + 5 files changed, 13 insertions(+), 7 deletions(-) + +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -2012,7 +2012,7 @@ int vfs_mkdir(struct inode *dir, struct + return -EPERM; + + mode &= (S_IRWXUGO|S_ISVTX); +- error = security_inode_mkdir(dir, dentry, mode); ++ error = security_inode_mkdir(dir, dentry, mnt, mode); + if (error) + return error; + +--- a/include/linux/security.h ++++ b/include/linux/security.h +@@ -355,6 +355,7 @@ static inline void security_free_mnt_opt + * associated with inode strcture @dir. + * @dir containst the inode structure of parent of the directory to be created. + * @dentry contains the dentry structure of new directory. ++ * @mnt is the vfsmount corresponding to @dentry (may be NULL). + * @mode contains the mode of new directory. + * Return 0 if permission is granted. + * @inode_rmdir: +@@ -1295,7 +1296,8 @@ struct security_operations { + int (*inode_unlink) (struct inode *dir, struct dentry *dentry); + int (*inode_symlink) (struct inode *dir, + struct dentry *dentry, const char *old_name); +- int (*inode_mkdir) (struct inode *dir, struct dentry *dentry, int mode); ++ int (*inode_mkdir) (struct inode *dir, struct dentry *dentry, ++ struct vfsmount *mnt, int mode); + int (*inode_rmdir) (struct inode *dir, struct dentry *dentry); + int (*inode_mknod) (struct inode *dir, struct dentry *dentry, + int mode, dev_t dev); +@@ -1559,7 +1561,8 @@ int security_inode_link(struct dentry *o + int security_inode_unlink(struct inode *dir, struct dentry *dentry); + int security_inode_symlink(struct inode *dir, struct dentry *dentry, + const char *old_name); +-int security_inode_mkdir(struct inode *dir, struct dentry *dentry, int mode); ++int security_inode_mkdir(struct inode *dir, struct dentry *dentry, ++ struct vfsmount *mnt, int mode); + int security_inode_rmdir(struct inode *dir, struct dentry *dentry); + int security_inode_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev); + int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry, +@@ -1920,6 +1923,7 @@ static inline int security_inode_symlink + + static inline int security_inode_mkdir (struct inode *dir, + struct dentry *dentry, ++ struct vfsmount *mnt, + int mode) + { + return 0; +--- a/security/dummy.c ++++ b/security/dummy.c +@@ -310,7 +310,7 @@ static int dummy_inode_symlink (struct i + } + + static int dummy_inode_mkdir (struct inode *inode, struct dentry *dentry, +- int mask) ++ struct vfsmount *mnt, int mask) + { + return 0; + } +--- a/security/security.c ++++ b/security/security.c +@@ -383,11 +383,12 @@ int security_inode_symlink(struct inode + return security_ops->inode_symlink(dir, dentry, old_name); + } + +-int security_inode_mkdir(struct inode *dir, struct dentry *dentry, int mode) ++int security_inode_mkdir(struct inode *dir, struct dentry *dentry, ++ struct vfsmount *mnt, int mode) + { + if (unlikely(IS_PRIVATE(dir))) + return 0; +- return security_ops->inode_mkdir(dir, dentry, mode); ++ return security_ops->inode_mkdir(dir, dentry, mnt, mode); + } + + int security_inode_rmdir(struct inode *dir, struct dentry *dentry) +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -2479,7 +2479,8 @@ static int selinux_inode_symlink(struct + return may_create(dir, dentry, SECCLASS_LNK_FILE); + } + +-static int selinux_inode_mkdir(struct inode *dir, struct dentry *dentry, int mask) ++static int selinux_inode_mkdir(struct inode *dir, struct dentry *dentry, ++ struct vfsmount *mnt, int mask) + { + return may_create(dir, dentry, SECCLASS_DIR); + } diff --git a/kernel-patches/2.6.25/security-mknod.diff b/kernel-patches/2.6.25/security-mknod.diff new file mode 100644 index 000000000..7b0b0fea0 --- /dev/null +++ b/kernel-patches/2.6.25/security-mknod.diff @@ -0,0 +1,124 @@ +From: Tony Jones +Subject: Pass struct vfsmount to the inode_mknod LSM hook + +This is needed for computing pathnames in the AppArmor LSM. + +Signed-off-by: Tony Jones +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/namei.c | 6 +++--- + include/linux/security.h | 7 +++++-- + security/dummy.c | 2 +- + security/security.c | 5 +++-- + security/selinux/hooks.c | 5 +++-- + 5 files changed, 15 insertions(+), 10 deletions(-) + +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -1935,7 +1935,7 @@ int vfs_mknod(struct inode *dir, struct + if (!dir->i_op || !dir->i_op->mknod) + return -EPERM; + +- error = security_inode_mknod(dir, dentry, mode, dev); ++ error = security_inode_mknod(dir, dentry, mnt, mode, dev); + if (error) + return error; + +@@ -1975,11 +1975,11 @@ asmlinkage long sys_mknodat(int dfd, con + break; + case S_IFCHR: case S_IFBLK: + error = vfs_mknod(nd.path.dentry->d_inode, dentry, +- nd.path, mode, new_decode_dev(dev)); ++ nd.path.mnt, mode, new_decode_dev(dev)); + break; + case S_IFIFO: case S_IFSOCK: + error = vfs_mknod(nd.path.dentry->d_inode, dentry, +- nd.path, mode, 0); ++ nd.path.mnt, mode, 0); + break; + case S_IFDIR: + error = -EPERM; +--- a/include/linux/security.h ++++ b/include/linux/security.h +@@ -370,6 +370,7 @@ static inline void security_free_mnt_opt + * and not this hook. + * @dir contains the inode structure of parent of the new file. + * @dentry contains the dentry structure of the new file. ++ * @mnt is the vfsmount corresponding to @dentry (may be NULL). + * @mode contains the mode of the new file. + * @dev contains the device number. + * Return 0 if permission is granted. +@@ -1300,7 +1301,7 @@ struct security_operations { + struct vfsmount *mnt, int mode); + int (*inode_rmdir) (struct inode *dir, struct dentry *dentry); + int (*inode_mknod) (struct inode *dir, struct dentry *dentry, +- int mode, dev_t dev); ++ struct vfsmount *mnt, int mode, dev_t dev); + int (*inode_rename) (struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry); + int (*inode_readlink) (struct dentry *dentry); +@@ -1564,7 +1565,8 @@ int security_inode_symlink(struct inode + int security_inode_mkdir(struct inode *dir, struct dentry *dentry, + struct vfsmount *mnt, int mode); + int security_inode_rmdir(struct inode *dir, struct dentry *dentry); +-int security_inode_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev); ++int security_inode_mknod(struct inode *dir, struct dentry *dentry, ++ struct vfsmount *mnt, int mode, dev_t dev); + int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry); + int security_inode_readlink(struct dentry *dentry); +@@ -1937,6 +1939,7 @@ static inline int security_inode_rmdir ( + + static inline int security_inode_mknod (struct inode *dir, + struct dentry *dentry, ++ struct vfsmount *mnt, + int mode, dev_t dev) + { + return 0; +--- a/security/dummy.c ++++ b/security/dummy.c +@@ -321,7 +321,7 @@ static int dummy_inode_rmdir (struct ino + } + + static int dummy_inode_mknod (struct inode *inode, struct dentry *dentry, +- int mode, dev_t dev) ++ struct vfsmount *mnt, int mode, dev_t dev) + { + return 0; + } +--- a/security/security.c ++++ b/security/security.c +@@ -398,11 +398,12 @@ int security_inode_rmdir(struct inode *d + return security_ops->inode_rmdir(dir, dentry); + } + +-int security_inode_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) ++int security_inode_mknod(struct inode *dir, struct dentry *dentry, ++ struct vfsmount *mnt, int mode, dev_t dev) + { + if (unlikely(IS_PRIVATE(dir))) + return 0; +- return security_ops->inode_mknod(dir, dentry, mode, dev); ++ return security_ops->inode_mknod(dir, dentry, mnt, mode, dev); + } + + int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry, +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -2490,11 +2490,12 @@ static int selinux_inode_rmdir(struct in + return may_link(dir, dentry, MAY_RMDIR); + } + +-static int selinux_inode_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) ++static int selinux_inode_mknod(struct inode *dir, struct dentry *dentry, ++ struct vfsmount *mnt, int mode, dev_t dev) + { + int rc; + +- rc = secondary_ops->inode_mknod(dir, dentry, mode, dev); ++ rc = secondary_ops->inode_mknod(dir, dentry, mnt, mode, dev); + if (rc) + return rc; + diff --git a/kernel-patches/2.6.25/security-readlink.diff b/kernel-patches/2.6.25/security-readlink.diff new file mode 100644 index 000000000..2e634974c --- /dev/null +++ b/kernel-patches/2.6.25/security-readlink.diff @@ -0,0 +1,104 @@ +From: Tony Jones +Subject: Pass struct vfsmount to the inode_readlink LSM hook + +This is needed for computing pathnames in the AppArmor LSM. + +Signed-off-by: Tony Jones +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/stat.c | 2 +- + include/linux/security.h | 8 +++++--- + security/dummy.c | 2 +- + security/security.c | 4 ++-- + security/selinux/hooks.c | 2 +- + 5 files changed, 10 insertions(+), 8 deletions(-) + +--- a/fs/stat.c ++++ b/fs/stat.c +@@ -306,7 +306,7 @@ asmlinkage long sys_readlinkat(int dfd, + + error = -EINVAL; + if (inode->i_op && inode->i_op->readlink) { +- error = security_inode_readlink(nd.path.dentry); ++ error = security_inode_readlink(nd.path.dentry, nd.path.mnt); + if (!error) { + touch_atime(nd.path.mnt, nd.path.dentry); + error = inode->i_op->readlink(nd.path.dentry, +--- a/include/linux/security.h ++++ b/include/linux/security.h +@@ -385,6 +385,7 @@ static inline void security_free_mnt_opt + * @inode_readlink: + * Check the permission to read the symbolic link. + * @dentry contains the dentry structure for the file link. ++ * @mnt is the vfsmount corresponding to @dentry (may be NULL). + * Return 0 if permission is granted. + * @inode_follow_link: + * Check permission to follow a symbolic link when looking up a pathname. +@@ -1305,7 +1306,7 @@ struct security_operations { + struct vfsmount *mnt, int mode, dev_t dev); + int (*inode_rename) (struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry); +- int (*inode_readlink) (struct dentry *dentry); ++ int (*inode_readlink) (struct dentry *dentry, struct vfsmount *mnt); + int (*inode_follow_link) (struct dentry *dentry, struct nameidata *nd); + int (*inode_permission) (struct inode *inode, int mask, struct nameidata *nd); + int (*inode_setattr) (struct dentry *dentry, struct vfsmount *mnt, +@@ -1570,7 +1571,7 @@ int security_inode_mknod(struct inode *d + struct vfsmount *mnt, int mode, dev_t dev); + int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry); +-int security_inode_readlink(struct dentry *dentry); ++int security_inode_readlink(struct dentry *dentry, struct vfsmount *mnt); + int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd); + int security_inode_permission(struct inode *inode, int mask, struct nameidata *nd); + int security_inode_setattr(struct dentry *dentry, struct vfsmount *mnt, +@@ -1955,7 +1956,8 @@ static inline int security_inode_rename + return 0; + } + +-static inline int security_inode_readlink (struct dentry *dentry) ++static inline int security_inode_readlink(struct dentry *dentry, ++ struct vfsmount *mnt) + { + return 0; + } +--- a/security/dummy.c ++++ b/security/dummy.c +@@ -334,7 +334,7 @@ static int dummy_inode_rename (struct in + return 0; + } + +-static int dummy_inode_readlink (struct dentry *dentry) ++static int dummy_inode_readlink (struct dentry *dentry, struct vfsmount *mnt) + { + return 0; + } +--- a/security/security.c ++++ b/security/security.c +@@ -416,11 +416,11 @@ int security_inode_rename(struct inode * + new_dir, new_dentry); + } + +-int security_inode_readlink(struct dentry *dentry) ++int security_inode_readlink(struct dentry *dentry, struct vfsmount *mnt) + { + if (unlikely(IS_PRIVATE(dentry->d_inode))) + return 0; +- return security_ops->inode_readlink(dentry); ++ return security_ops->inode_readlink(dentry, mnt); + } + + int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd) +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -2509,7 +2509,7 @@ static int selinux_inode_rename(struct i + return may_rename(old_inode, old_dentry, new_inode, new_dentry); + } + +-static int selinux_inode_readlink(struct dentry *dentry) ++static int selinux_inode_readlink(struct dentry *dentry, struct vfsmount *mnt) + { + return dentry_has_perm(current, NULL, dentry, FILE__READ); + } diff --git a/kernel-patches/2.6.25/security-removexattr.diff b/kernel-patches/2.6.25/security-removexattr.diff new file mode 100644 index 000000000..eab29930b --- /dev/null +++ b/kernel-patches/2.6.25/security-removexattr.diff @@ -0,0 +1,126 @@ +From: Tony Jones +Subject: Pass struct vfsmount to the inode_removexattr LSM hook + +This is needed for computing pathnames in the AppArmor LSM. + +Signed-off-by: Tony Jones +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/xattr.c | 2 +- + include/linux/security.h | 13 ++++++++----- + security/commoncap.c | 3 ++- + security/dummy.c | 3 ++- + security/security.c | 5 +++-- + security/selinux/hooks.c | 3 ++- + 6 files changed, 18 insertions(+), 11 deletions(-) + +--- a/fs/xattr.c ++++ b/fs/xattr.c +@@ -203,7 +203,7 @@ vfs_removexattr(struct dentry *dentry, s + if (error) + return error; + +- error = security_inode_removexattr(dentry, name); ++ error = security_inode_removexattr(dentry, mnt, name); + if (error) + return error; + +--- a/include/linux/security.h ++++ b/include/linux/security.h +@@ -52,7 +52,7 @@ extern int cap_bprm_set_security (struct + extern void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe); + extern int cap_bprm_secureexec(struct linux_binprm *bprm); + extern int cap_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, char *name, void *value, size_t size, int flags); +-extern int cap_inode_removexattr(struct dentry *dentry, char *name); ++extern int cap_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt, char *name); + extern int cap_inode_need_killpriv(struct dentry *dentry); + extern int cap_inode_killpriv(struct dentry *dentry); + extern int cap_task_post_setuid (uid_t old_ruid, uid_t old_euid, uid_t old_suid, int flags); +@@ -1333,7 +1333,8 @@ struct security_operations { + int (*inode_getxattr) (struct dentry *dentry, struct vfsmount *mnt, + char *name); + int (*inode_listxattr) (struct dentry *dentry, struct vfsmount *mnt); +- int (*inode_removexattr) (struct dentry *dentry, char *name); ++ int (*inode_removexattr) (struct dentry *dentry, struct vfsmount *mnt, ++ char *name); + int (*inode_need_killpriv) (struct dentry *dentry); + int (*inode_killpriv) (struct dentry *dentry); + int (*inode_getsecurity)(const struct inode *inode, const char *name, void **buffer, bool alloc); +@@ -1604,7 +1605,8 @@ void security_inode_post_setxattr(struct + int security_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt, + char *name); + int security_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt); +-int security_inode_removexattr(struct dentry *dentry, char *name); ++int security_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt, ++ char *name); + int security_inode_need_killpriv(struct dentry *dentry); + int security_inode_killpriv(struct dentry *dentry); + int security_inode_getsecurity(const struct inode *inode, const char *name, void **buffer, bool alloc); +@@ -2042,9 +2044,10 @@ static inline int security_inode_listxat + return 0; + } + +-static inline int security_inode_removexattr (struct dentry *dentry, char *name) ++static inline int security_inode_removexattr (struct dentry *dentry, ++ struct vfsmount *mnt, char *name) + { +- return cap_inode_removexattr(dentry, name); ++ return cap_inode_removexattr(dentry, mnt, name); + } + + static inline int security_inode_need_killpriv(struct dentry *dentry) +--- a/security/commoncap.c ++++ b/security/commoncap.c +@@ -400,7 +400,8 @@ int cap_inode_setxattr(struct dentry *de + return 0; + } + +-int cap_inode_removexattr(struct dentry *dentry, char *name) ++int cap_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt, ++ char *name) + { + if (!strcmp(name, XATTR_NAME_CAPS)) { + if (!capable(CAP_SETFCAP)) +--- a/security/dummy.c ++++ b/security/dummy.c +@@ -401,7 +401,8 @@ static int dummy_inode_listxattr (struct + return 0; + } + +-static int dummy_inode_removexattr (struct dentry *dentry, char *name) ++static int dummy_inode_removexattr (struct dentry *dentry, struct vfsmount *mnt, ++ char *name) + { + if (!strncmp(name, XATTR_SECURITY_PREFIX, + sizeof(XATTR_SECURITY_PREFIX) - 1) && +--- a/security/security.c ++++ b/security/security.c +@@ -497,11 +497,12 @@ int security_inode_listxattr(struct dent + return security_ops->inode_listxattr(dentry, mnt); + } + +-int security_inode_removexattr(struct dentry *dentry, char *name) ++int security_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt, ++ char *name) + { + if (unlikely(IS_PRIVATE(dentry->d_inode))) + return 0; +- return security_ops->inode_removexattr(dentry, name); ++ return security_ops->inode_removexattr(dentry, mnt, name); + } + + int security_inode_need_killpriv(struct dentry *dentry) +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -2684,7 +2684,8 @@ static int selinux_inode_listxattr (stru + return dentry_has_perm(current, NULL, dentry, FILE__GETATTR); + } + +-static int selinux_inode_removexattr (struct dentry *dentry, char *name) ++static int selinux_inode_removexattr (struct dentry *dentry, ++ struct vfsmount *mnt, char *name) + { + if (strcmp(name, XATTR_NAME_SELINUX)) + return selinux_inode_setotherxattr(dentry, name); diff --git a/kernel-patches/2.6.25/security-rename.diff b/kernel-patches/2.6.25/security-rename.diff new file mode 100644 index 000000000..78339da36 --- /dev/null +++ b/kernel-patches/2.6.25/security-rename.diff @@ -0,0 +1,136 @@ +From: Tony Jones +Subject: Pass struct vfsmount to the inode_rename LSM hook + +This is needed for computing pathnames in the AppArmor LSM. + +Signed-off-by: Tony Jones +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/namei.c | 6 ++++-- + include/linux/security.h | 13 ++++++++++--- + security/dummy.c | 4 +++- + security/security.c | 7 ++++--- + security/selinux/hooks.c | 8 ++++++-- + 5 files changed, 27 insertions(+), 11 deletions(-) + +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -2484,7 +2484,8 @@ static int vfs_rename_dir(struct inode * + return error; + } + +- error = security_inode_rename(old_dir, old_dentry, new_dir, new_dentry); ++ error = security_inode_rename(old_dir, old_dentry, old_mnt, ++ new_dir, new_dentry, new_mnt); + if (error) + return error; + +@@ -2518,7 +2519,8 @@ static int vfs_rename_other(struct inode + struct inode *target; + int error; + +- error = security_inode_rename(old_dir, old_dentry, new_dir, new_dentry); ++ error = security_inode_rename(old_dir, old_dentry, old_mnt, ++ new_dir, new_dentry, new_mnt); + if (error) + return error; + +--- a/include/linux/security.h ++++ b/include/linux/security.h +@@ -383,8 +383,10 @@ static inline void security_free_mnt_opt + * Check for permission to rename a file or directory. + * @old_dir contains the inode structure for parent of the old link. + * @old_dentry contains the dentry structure of the old link. ++ * @old_mnt is the vfsmount corresponding to @old_dentry (may be NULL). + * @new_dir contains the inode structure for parent of the new link. + * @new_dentry contains the dentry structure of the new link. ++ * @new_mnt is the vfsmount corresponding to @new_dentry (may be NULL). + * Return 0 if permission is granted. + * @inode_readlink: + * Check the permission to read the symbolic link. +@@ -1312,7 +1314,9 @@ struct security_operations { + int (*inode_mknod) (struct inode *dir, struct dentry *dentry, + struct vfsmount *mnt, int mode, dev_t dev); + int (*inode_rename) (struct inode *old_dir, struct dentry *old_dentry, +- struct inode *new_dir, struct dentry *new_dentry); ++ struct vfsmount *old_mnt, ++ struct inode *new_dir, struct dentry *new_dentry, ++ struct vfsmount *new_mnt); + int (*inode_readlink) (struct dentry *dentry, struct vfsmount *mnt); + int (*inode_follow_link) (struct dentry *dentry, struct nameidata *nd); + int (*inode_permission) (struct inode *inode, int mask, struct nameidata *nd); +@@ -1580,7 +1584,8 @@ int security_inode_rmdir(struct inode *d + int security_inode_mknod(struct inode *dir, struct dentry *dentry, + struct vfsmount *mnt, int mode, dev_t dev); + int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry, +- struct inode *new_dir, struct dentry *new_dentry); ++ struct vfsmount *old_mnt, struct inode *new_dir, ++ struct dentry *new_dentry, struct vfsmount *new_mnt); + int security_inode_readlink(struct dentry *dentry, struct vfsmount *mnt); + int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd); + int security_inode_permission(struct inode *inode, int mask, struct nameidata *nd); +@@ -1964,8 +1969,10 @@ static inline int security_inode_mknod ( + + static inline int security_inode_rename (struct inode *old_dir, + struct dentry *old_dentry, ++ struct vfsmount *old_mnt, + struct inode *new_dir, +- struct dentry *new_dentry) ++ struct dentry *new_dentry, ++ struct vfsmount *new_mnt) + { + return 0; + } +--- a/security/dummy.c ++++ b/security/dummy.c +@@ -332,8 +332,10 @@ static int dummy_inode_mknod (struct ino + + static int dummy_inode_rename (struct inode *old_inode, + struct dentry *old_dentry, ++ struct vfsmount *old_mnt, + struct inode *new_inode, +- struct dentry *new_dentry) ++ struct dentry *new_dentry, ++ struct vfsmount *new_mnt) + { + return 0; + } +--- a/security/security.c ++++ b/security/security.c +@@ -411,13 +411,14 @@ int security_inode_mknod(struct inode *d + } + + int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry, +- struct inode *new_dir, struct dentry *new_dentry) ++ struct vfsmount *old_mnt, struct inode *new_dir, ++ struct dentry *new_dentry, struct vfsmount *new_mnt) + { + if (unlikely(IS_PRIVATE(old_dentry->d_inode) || + (new_dentry->d_inode && IS_PRIVATE(new_dentry->d_inode)))) + return 0; +- return security_ops->inode_rename(old_dir, old_dentry, +- new_dir, new_dentry); ++ return security_ops->inode_rename(old_dir, old_dentry, old_mnt, ++ new_dir, new_dentry, new_mnt); + } + + int security_inode_readlink(struct dentry *dentry, struct vfsmount *mnt) +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -2510,8 +2510,12 @@ static int selinux_inode_mknod(struct in + return may_create(dir, dentry, inode_mode_to_security_class(mode)); + } + +-static int selinux_inode_rename(struct inode *old_inode, struct dentry *old_dentry, +- struct inode *new_inode, struct dentry *new_dentry) ++static int selinux_inode_rename(struct inode *old_inode, ++ struct dentry *old_dentry, ++ struct vfsmount *old_mnt, ++ struct inode *new_inode, ++ struct dentry *new_dentry, ++ struct vfsmount *new_mnt) + { + return may_rename(old_inode, old_dentry, new_inode, new_dentry); + } diff --git a/kernel-patches/2.6.25/security-rmdir.diff b/kernel-patches/2.6.25/security-rmdir.diff new file mode 100644 index 000000000..a5d148788 --- /dev/null +++ b/kernel-patches/2.6.25/security-rmdir.diff @@ -0,0 +1,109 @@ +From: Tony Jones +Subject: Pass struct vfsmount to the inode_rmdir LSM hook + +This is needed for computing pathnames in the AppArmor LSM. + +Signed-off-by: Tony Jones +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/namei.c | 2 +- + include/linux/security.h | 10 +++++++--- + security/dummy.c | 3 ++- + security/security.c | 5 +++-- + security/selinux/hooks.c | 3 ++- + 5 files changed, 15 insertions(+), 8 deletions(-) + +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -2107,7 +2107,7 @@ int vfs_rmdir(struct inode *dir, struct + if (d_mountpoint(dentry)) + error = -EBUSY; + else { +- error = security_inode_rmdir(dir, dentry); ++ error = security_inode_rmdir(dir, dentry, mnt); + if (!error) { + error = dir->i_op->rmdir(dir, dentry); + if (!error) +--- a/include/linux/security.h ++++ b/include/linux/security.h +@@ -365,6 +365,7 @@ static inline void security_free_mnt_opt + * Check the permission to remove a directory. + * @dir contains the inode structure of parent of the directory to be removed. + * @dentry contains the dentry structure of directory to be removed. ++ * @mnt is the vfsmount corresponding to @dentry (may be NULL). + * Return 0 if permission is granted. + * @inode_mknod: + * Check permissions when creating a special file (or a socket or a fifo +@@ -1304,7 +1305,8 @@ struct security_operations { + struct vfsmount *mnt, const char *old_name); + int (*inode_mkdir) (struct inode *dir, struct dentry *dentry, + struct vfsmount *mnt, int mode); +- int (*inode_rmdir) (struct inode *dir, struct dentry *dentry); ++ int (*inode_rmdir) (struct inode *dir, struct dentry *dentry, ++ struct vfsmount *mnt); + int (*inode_mknod) (struct inode *dir, struct dentry *dentry, + struct vfsmount *mnt, int mode, dev_t dev); + int (*inode_rename) (struct inode *old_dir, struct dentry *old_dentry, +@@ -1570,7 +1572,8 @@ int security_inode_symlink(struct inode + struct vfsmount *mnt, const char *old_name); + int security_inode_mkdir(struct inode *dir, struct dentry *dentry, + struct vfsmount *mnt, int mode); +-int security_inode_rmdir(struct inode *dir, struct dentry *dentry); ++int security_inode_rmdir(struct inode *dir, struct dentry *dentry, ++ struct vfsmount *mnt); + int security_inode_mknod(struct inode *dir, struct dentry *dentry, + struct vfsmount *mnt, int mode, dev_t dev); + int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry, +@@ -1941,7 +1944,8 @@ static inline int security_inode_mkdir ( + } + + static inline int security_inode_rmdir (struct inode *dir, +- struct dentry *dentry) ++ struct dentry *dentry, ++ struct vfsmount *mnt) + { + return 0; + } +--- a/security/dummy.c ++++ b/security/dummy.c +@@ -317,7 +317,8 @@ static int dummy_inode_mkdir (struct ino + return 0; + } + +-static int dummy_inode_rmdir (struct inode *inode, struct dentry *dentry) ++static int dummy_inode_rmdir (struct inode *inode, struct dentry *dentry, ++ struct vfsmount *mnt) + { + return 0; + } +--- a/security/security.c ++++ b/security/security.c +@@ -393,11 +393,12 @@ int security_inode_mkdir(struct inode *d + return security_ops->inode_mkdir(dir, dentry, mnt, mode); + } + +-int security_inode_rmdir(struct inode *dir, struct dentry *dentry) ++int security_inode_rmdir(struct inode *dir, struct dentry *dentry, ++ struct vfsmount *mnt) + { + if (unlikely(IS_PRIVATE(dentry->d_inode))) + return 0; +- return security_ops->inode_rmdir(dir, dentry); ++ return security_ops->inode_rmdir(dir, dentry, mnt); + } + + int security_inode_mknod(struct inode *dir, struct dentry *dentry, +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -2491,7 +2491,8 @@ static int selinux_inode_mkdir(struct in + return may_create(dir, dentry, SECCLASS_DIR); + } + +-static int selinux_inode_rmdir(struct inode *dir, struct dentry *dentry) ++static int selinux_inode_rmdir(struct inode *dir, struct dentry *dentry, ++ struct vfsmount *mnt) + { + return may_link(dir, dentry, MAY_RMDIR); + } diff --git a/kernel-patches/2.6.25/security-setattr.diff b/kernel-patches/2.6.25/security-setattr.diff new file mode 100644 index 000000000..ce2acc861 --- /dev/null +++ b/kernel-patches/2.6.25/security-setattr.diff @@ -0,0 +1,119 @@ +From: Tony Jones +Subject: Pass struct vfsmount to the inode_setattr LSM hook + +This is needed for computing pathnames in the AppArmor LSM. + +Signed-off-by: Tony Jones +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/attr.c | 4 ++-- + include/linux/security.h | 8 ++++++-- + security/dummy.c | 3 ++- + security/security.c | 5 +++-- + security/selinux/hooks.c | 5 +++-- + 5 files changed, 16 insertions(+), 9 deletions(-) + +--- a/fs/attr.c ++++ b/fs/attr.c +@@ -159,13 +159,13 @@ int notify_change(struct dentry *dentry, + down_write(&dentry->d_inode->i_alloc_sem); + + if (inode->i_op && inode->i_op->setattr) { +- error = security_inode_setattr(dentry, attr); ++ error = security_inode_setattr(dentry, mnt, attr); + if (!error) + error = inode->i_op->setattr(dentry, attr); + } else { + error = inode_change_ok(inode, attr); + if (!error) +- error = security_inode_setattr(dentry, attr); ++ error = security_inode_setattr(dentry, mnt, attr); + if (!error) { + if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) || + (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) +--- a/include/linux/security.h ++++ b/include/linux/security.h +@@ -405,6 +405,7 @@ static inline void security_free_mnt_opt + * file attributes change (such as when a file is truncated, chown/chmod + * operations, transferring disk quotas, etc). + * @dentry contains the dentry structure for the file. ++ * @mnt is the vfsmount corresponding to @dentry (may be NULL). + * @attr is the iattr structure containing the new file attributes. + * Return 0 if permission is granted. + * @inode_getattr: +@@ -1303,7 +1304,8 @@ struct security_operations { + int (*inode_readlink) (struct dentry *dentry); + int (*inode_follow_link) (struct dentry *dentry, struct nameidata *nd); + int (*inode_permission) (struct inode *inode, int mask, struct nameidata *nd); +- int (*inode_setattr) (struct dentry *dentry, struct iattr *attr); ++ int (*inode_setattr) (struct dentry *dentry, struct vfsmount *mnt, ++ struct iattr *attr); + int (*inode_getattr) (struct vfsmount *mnt, struct dentry *dentry); + void (*inode_delete) (struct inode *inode); + int (*inode_setxattr) (struct dentry *dentry, char *name, void *value, +@@ -1565,7 +1567,8 @@ int security_inode_rename(struct inode * + int security_inode_readlink(struct dentry *dentry); + int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd); + int security_inode_permission(struct inode *inode, int mask, struct nameidata *nd); +-int security_inode_setattr(struct dentry *dentry, struct iattr *attr); ++int security_inode_setattr(struct dentry *dentry, struct vfsmount *mnt, ++ struct iattr *attr); + int security_inode_getattr(struct vfsmount *mnt, struct dentry *dentry); + void security_inode_delete(struct inode *inode); + int security_inode_setxattr(struct dentry *dentry, char *name, +@@ -1961,6 +1964,7 @@ static inline int security_inode_permiss + } + + static inline int security_inode_setattr (struct dentry *dentry, ++ struct vfsmount *mnt, + struct iattr *attr) + { + return 0; +--- a/security/dummy.c ++++ b/security/dummy.c +@@ -350,7 +350,8 @@ static int dummy_inode_permission (struc + return 0; + } + +-static int dummy_inode_setattr (struct dentry *dentry, struct iattr *iattr) ++static int dummy_inode_setattr (struct dentry *dentry, struct vfsmount *mnt, ++ struct iattr *iattr) + { + return 0; + } +--- a/security/security.c ++++ b/security/security.c +@@ -435,11 +435,12 @@ int security_inode_permission(struct ino + return security_ops->inode_permission(inode, mask, nd); + } + +-int security_inode_setattr(struct dentry *dentry, struct iattr *attr) ++int security_inode_setattr(struct dentry *dentry, struct vfsmount *mnt, ++ struct iattr *attr) + { + if (unlikely(IS_PRIVATE(dentry->d_inode))) + return 0; +- return security_ops->inode_setattr(dentry, attr); ++ return security_ops->inode_setattr(dentry, mnt, attr); + } + + int security_inode_getattr(struct vfsmount *mnt, struct dentry *dentry) +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -2539,11 +2539,12 @@ static int selinux_inode_permission(stru + file_mask_to_av(inode->i_mode, mask), NULL); + } + +-static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr) ++static int selinux_inode_setattr(struct dentry *dentry, struct vfsmount *mnt, ++ struct iattr *iattr) + { + int rc; + +- rc = secondary_ops->inode_setattr(dentry, iattr); ++ rc = secondary_ops->inode_setattr(dentry, mnt, iattr); + if (rc) + return rc; + diff --git a/kernel-patches/2.6.25/security-setxattr.diff b/kernel-patches/2.6.25/security-setxattr.diff new file mode 100644 index 000000000..ef4ebcea8 --- /dev/null +++ b/kernel-patches/2.6.25/security-setxattr.diff @@ -0,0 +1,210 @@ +From: Tony Jones +Subject: Pass struct vfsmount to the inode_setxattr LSM hook + +This is needed for computing pathnames in the AppArmor LSM. + +Signed-off-by: Tony Jones +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/xattr.c | 4 ++-- + include/linux/security.h | 35 +++++++++++++++++++++-------------- + security/commoncap.c | 4 ++-- + security/dummy.c | 9 ++++++--- + security/security.c | 14 ++++++++------ + security/selinux/hooks.c | 8 ++++++-- + 6 files changed, 45 insertions(+), 29 deletions(-) + +--- a/fs/xattr.c ++++ b/fs/xattr.c +@@ -79,7 +79,7 @@ vfs_setxattr(struct dentry *dentry, stru + return error; + + mutex_lock(&inode->i_mutex); +- error = security_inode_setxattr(dentry, name, value, size, flags); ++ error = security_inode_setxattr(dentry, mnt, name, value, size, flags); + if (error) + goto out; + error = -EOPNOTSUPP; +@@ -87,7 +87,7 @@ vfs_setxattr(struct dentry *dentry, stru + error = inode->i_op->setxattr(dentry, name, value, size, flags); + if (!error) { + fsnotify_xattr(dentry); +- security_inode_post_setxattr(dentry, name, value, ++ security_inode_post_setxattr(dentry, mnt, name, value, + size, flags); + } + } else if (!strncmp(name, XATTR_SECURITY_PREFIX, +--- a/include/linux/security.h ++++ b/include/linux/security.h +@@ -51,7 +51,7 @@ extern void cap_capset_set (struct task_ + extern int cap_bprm_set_security (struct linux_binprm *bprm); + extern void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe); + extern int cap_bprm_secureexec(struct linux_binprm *bprm); +-extern int cap_inode_setxattr(struct dentry *dentry, char *name, void *value, size_t size, int flags); ++extern int cap_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, char *name, void *value, size_t size, int flags); + extern int cap_inode_removexattr(struct dentry *dentry, char *name); + extern int cap_inode_need_killpriv(struct dentry *dentry); + extern int cap_inode_killpriv(struct dentry *dentry); +@@ -431,11 +431,11 @@ static inline void security_free_mnt_opt + * inode. + * @inode_setxattr: + * Check permission before setting the extended attributes +- * @value identified by @name for @dentry. ++ * @value identified by @name for @dentry and @mnt. + * Return 0 if permission is granted. + * @inode_post_setxattr: + * Update inode security field after successful setxattr operation. +- * @value identified by @name for @dentry. ++ * @value identified by @name for @dentry and @mnt. + * @inode_getxattr: + * Check permission before obtaining the extended attributes + * identified by @name for @dentry. +@@ -1324,9 +1324,11 @@ struct security_operations { + struct iattr *attr); + int (*inode_getattr) (struct vfsmount *mnt, struct dentry *dentry); + void (*inode_delete) (struct inode *inode); +- int (*inode_setxattr) (struct dentry *dentry, char *name, void *value, +- size_t size, int flags); +- void (*inode_post_setxattr) (struct dentry *dentry, char *name, void *value, ++ int (*inode_setxattr) (struct dentry *dentry, struct vfsmount *mnt, ++ char *name, void *value, size_t size, int flags); ++ void (*inode_post_setxattr) (struct dentry *dentry, ++ struct vfsmount *mnt, ++ char *name, void *value, + size_t size, int flags); + int (*inode_getxattr) (struct dentry *dentry, char *name); + int (*inode_listxattr) (struct dentry *dentry); +@@ -1593,10 +1595,11 @@ int security_inode_setattr(struct dentry + struct iattr *attr); + int security_inode_getattr(struct vfsmount *mnt, struct dentry *dentry); + void security_inode_delete(struct inode *inode); +-int security_inode_setxattr(struct dentry *dentry, char *name, +- void *value, size_t size, int flags); +-void security_inode_post_setxattr(struct dentry *dentry, char *name, +- void *value, size_t size, int flags); ++int security_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, ++ char *name, void *value, size_t size, int flags); ++void security_inode_post_setxattr(struct dentry *dentry, struct vfsmount *mnt, ++ char *name, void *value, size_t size, ++ int flags); + int security_inode_getxattr(struct dentry *dentry, char *name); + int security_inode_listxattr(struct dentry *dentry); + int security_inode_removexattr(struct dentry *dentry, char *name); +@@ -2011,14 +2014,18 @@ static inline int security_inode_getattr + static inline void security_inode_delete (struct inode *inode) + { } + +-static inline int security_inode_setxattr (struct dentry *dentry, char *name, ++static inline int security_inode_setxattr (struct dentry *dentry, ++ struct vfsmount *mnt, char *name, + void *value, size_t size, int flags) + { +- return cap_inode_setxattr(dentry, name, value, size, flags); ++ return cap_inode_setxattr(dentry, mnt, name, value, size, flags); + } + +-static inline void security_inode_post_setxattr (struct dentry *dentry, char *name, +- void *value, size_t size, int flags) ++static inline void security_inode_post_setxattr (struct dentry *dentry, ++ struct vfsmount *mnt, ++ char *name, ++ void *value, size_t size, ++ int flags) + { } + + static inline int security_inode_getxattr (struct dentry *dentry, char *name) +--- a/security/commoncap.c ++++ b/security/commoncap.c +@@ -386,8 +386,8 @@ int cap_bprm_secureexec (struct linux_bi + current->egid != current->gid); + } + +-int cap_inode_setxattr(struct dentry *dentry, char *name, void *value, +- size_t size, int flags) ++int cap_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, char *name, ++ void *value, size_t size, int flags) + { + if (!strcmp(name, XATTR_NAME_CAPS)) { + if (!capable(CAP_SETFCAP)) +--- a/security/dummy.c ++++ b/security/dummy.c +@@ -372,8 +372,9 @@ static void dummy_inode_delete (struct i + return; + } + +-static int dummy_inode_setxattr (struct dentry *dentry, char *name, void *value, +- size_t size, int flags) ++static int dummy_inode_setxattr (struct dentry *dentry, struct vfsmount *mnt, ++ char *name, void *value, size_t size, ++ int flags) + { + if (!strncmp(name, XATTR_SECURITY_PREFIX, + sizeof(XATTR_SECURITY_PREFIX) - 1) && +@@ -382,7 +383,9 @@ static int dummy_inode_setxattr (struct + return 0; + } + +-static void dummy_inode_post_setxattr (struct dentry *dentry, char *name, void *value, ++static void dummy_inode_post_setxattr (struct dentry *dentry, ++ struct vfsmount *mnt, ++ char *name, void *value, + size_t size, int flags) + { + } +--- a/security/security.c ++++ b/security/security.c +@@ -464,20 +464,22 @@ void security_inode_delete(struct inode + security_ops->inode_delete(inode); + } + +-int security_inode_setxattr(struct dentry *dentry, char *name, +- void *value, size_t size, int flags) ++int security_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, ++ char *name, void *value, size_t size, int flags) + { + if (unlikely(IS_PRIVATE(dentry->d_inode))) + return 0; +- return security_ops->inode_setxattr(dentry, name, value, size, flags); ++ return security_ops->inode_setxattr(dentry, mnt, name, value, size, ++ flags); + } + +-void security_inode_post_setxattr(struct dentry *dentry, char *name, +- void *value, size_t size, int flags) ++void security_inode_post_setxattr(struct dentry *dentry, struct vfsmount *mnt, ++ char *name, void *value, size_t size, ++ int flags) + { + if (unlikely(IS_PRIVATE(dentry->d_inode))) + return; +- security_ops->inode_post_setxattr(dentry, name, value, size, flags); ++ security_ops->inode_post_setxattr(dentry, mnt, name, value, size, flags); + } + + int security_inode_getxattr(struct dentry *dentry, char *name) +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -2596,7 +2596,9 @@ static int selinux_inode_setotherxattr(s + return dentry_has_perm(current, NULL, dentry, FILE__SETATTR); + } + +-static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value, size_t size, int flags) ++static int selinux_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, ++ char *name, void *value, size_t size, ++ int flags) + { + struct task_security_struct *tsec = current->security; + struct inode *inode = dentry->d_inode; +@@ -2645,7 +2647,9 @@ static int selinux_inode_setxattr(struct + &ad); + } + +-static void selinux_inode_post_setxattr(struct dentry *dentry, char *name, ++static void selinux_inode_post_setxattr(struct dentry *dentry, ++ struct vfsmount *mnt, ++ char *name, + void *value, size_t size, int flags) + { + struct inode *inode = dentry->d_inode; diff --git a/kernel-patches/2.6.25/security-symlink.diff b/kernel-patches/2.6.25/security-symlink.diff new file mode 100644 index 000000000..6f04a9a2d --- /dev/null +++ b/kernel-patches/2.6.25/security-symlink.diff @@ -0,0 +1,105 @@ +From: Tony Jones +Subject: Pass struct vfsmount to the inode_symlink LSM hook + +This is needed for computing pathnames in the AppArmor LSM. + +Signed-off-by: Tony Jones +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/namei.c | 2 +- + include/linux/security.h | 8 +++++--- + security/dummy.c | 2 +- + security/security.c | 4 ++-- + security/selinux/hooks.c | 3 ++- + 5 files changed, 11 insertions(+), 8 deletions(-) + +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -2281,7 +2281,7 @@ int vfs_symlink(struct inode *dir, struc + if (!dir->i_op || !dir->i_op->symlink) + return -EPERM; + +- error = security_inode_symlink(dir, dentry, oldname); ++ error = security_inode_symlink(dir, dentry, mnt, oldname); + if (error) + return error; + +--- a/include/linux/security.h ++++ b/include/linux/security.h +@@ -348,6 +348,7 @@ static inline void security_free_mnt_opt + * Check the permission to create a symbolic link to a file. + * @dir contains the inode structure of parent directory of the symbolic link. + * @dentry contains the dentry structure of the symbolic link. ++ * @mnt is the vfsmount corresponding to @dentry (may be NULL). + * @old_name contains the pathname of file. + * Return 0 if permission is granted. + * @inode_mkdir: +@@ -1295,8 +1296,8 @@ struct security_operations { + int (*inode_link) (struct dentry *old_dentry, + struct inode *dir, struct dentry *new_dentry); + int (*inode_unlink) (struct inode *dir, struct dentry *dentry); +- int (*inode_symlink) (struct inode *dir, +- struct dentry *dentry, const char *old_name); ++ int (*inode_symlink) (struct inode *dir, struct dentry *dentry, ++ struct vfsmount *mnt, const char *old_name); + int (*inode_mkdir) (struct inode *dir, struct dentry *dentry, + struct vfsmount *mnt, int mode); + int (*inode_rmdir) (struct inode *dir, struct dentry *dentry); +@@ -1561,7 +1562,7 @@ int security_inode_link(struct dentry *o + struct dentry *new_dentry); + int security_inode_unlink(struct inode *dir, struct dentry *dentry); + int security_inode_symlink(struct inode *dir, struct dentry *dentry, +- const char *old_name); ++ struct vfsmount *mnt, const char *old_name); + int security_inode_mkdir(struct inode *dir, struct dentry *dentry, + struct vfsmount *mnt, int mode); + int security_inode_rmdir(struct inode *dir, struct dentry *dentry); +@@ -1918,6 +1919,7 @@ static inline int security_inode_unlink + + static inline int security_inode_symlink (struct inode *dir, + struct dentry *dentry, ++ struct vfsmount *mnt, + const char *old_name) + { + return 0; +--- a/security/dummy.c ++++ b/security/dummy.c +@@ -304,7 +304,7 @@ static int dummy_inode_unlink (struct in + } + + static int dummy_inode_symlink (struct inode *inode, struct dentry *dentry, +- const char *name) ++ struct vfsmount *mnt, const char *name) + { + return 0; + } +--- a/security/security.c ++++ b/security/security.c +@@ -376,11 +376,11 @@ int security_inode_unlink(struct inode * + } + + int security_inode_symlink(struct inode *dir, struct dentry *dentry, +- const char *old_name) ++ struct vfsmount *mnt, const char *old_name) + { + if (unlikely(IS_PRIVATE(dir))) + return 0; +- return security_ops->inode_symlink(dir, dentry, old_name); ++ return security_ops->inode_symlink(dir, dentry, mnt, old_name); + } + + int security_inode_mkdir(struct inode *dir, struct dentry *dentry, +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -2474,7 +2474,8 @@ static int selinux_inode_unlink(struct i + return may_link(dir, dentry, MAY_UNLINK); + } + +-static int selinux_inode_symlink(struct inode *dir, struct dentry *dentry, const char *name) ++static int selinux_inode_symlink(struct inode *dir, struct dentry *dentry, ++ struct vfsmount *mnt, const char *name) + { + return may_create(dir, dentry, SECCLASS_LNK_FILE); + } diff --git a/kernel-patches/2.6.25/security-unlink.diff b/kernel-patches/2.6.25/security-unlink.diff new file mode 100644 index 000000000..bda4a6b0a --- /dev/null +++ b/kernel-patches/2.6.25/security-unlink.diff @@ -0,0 +1,114 @@ +From: Tony Jones +Subject: Pass struct vfsmount to the inode_unlink LSM hook + +This is needed for computing pathnames in the AppArmor LSM. + +Signed-off-by: Tony Jones +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/namei.c | 2 +- + include/linux/security.h | 10 +++++++--- + security/dummy.c | 3 ++- + security/security.c | 5 +++-- + security/selinux/hooks.c | 5 +++-- + 5 files changed, 16 insertions(+), 9 deletions(-) + +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -2187,7 +2187,7 @@ int vfs_unlink(struct inode *dir, struct + if (d_mountpoint(dentry)) + error = -EBUSY; + else { +- error = security_inode_unlink(dir, dentry); ++ error = security_inode_unlink(dir, dentry, mnt); + if (!error) + error = dir->i_op->unlink(dir, dentry); + } +--- a/include/linux/security.h ++++ b/include/linux/security.h +@@ -345,6 +345,7 @@ static inline void security_free_mnt_opt + * Check the permission to remove a hard link to a file. + * @dir contains the inode structure of parent directory of the file. + * @dentry contains the dentry structure for file to be unlinked. ++ * @mnt is the vfsmount corresponding to @dentry (may be NULL). + * Return 0 if permission is granted. + * @inode_symlink: + * Check the permission to create a symbolic link to a file. +@@ -1300,7 +1301,8 @@ struct security_operations { + int (*inode_link) (struct dentry *old_dentry, struct vfsmount *old_mnt, + struct inode *dir, struct dentry *new_dentry, + struct vfsmount *new_mnt); +- int (*inode_unlink) (struct inode *dir, struct dentry *dentry); ++ int (*inode_unlink) (struct inode *dir, struct dentry *dentry, ++ struct vfsmount *mnt); + int (*inode_symlink) (struct inode *dir, struct dentry *dentry, + struct vfsmount *mnt, const char *old_name); + int (*inode_mkdir) (struct inode *dir, struct dentry *dentry, +@@ -1567,7 +1569,8 @@ int security_inode_create(struct inode * + int security_inode_link(struct dentry *old_dentry, struct vfsmount *old_mnt, + struct inode *dir, struct dentry *new_dentry, + struct vfsmount *new_mnt); +-int security_inode_unlink(struct inode *dir, struct dentry *dentry); ++int security_inode_unlink(struct inode *dir, struct dentry *dentry, ++ struct vfsmount *mnt); + int security_inode_symlink(struct inode *dir, struct dentry *dentry, + struct vfsmount *mnt, const char *old_name); + int security_inode_mkdir(struct inode *dir, struct dentry *dentry, +@@ -1922,7 +1925,8 @@ static inline int security_inode_link (s + } + + static inline int security_inode_unlink (struct inode *dir, +- struct dentry *dentry) ++ struct dentry *dentry, ++ struct vfsmount *mnt) + { + return 0; + } +--- a/security/dummy.c ++++ b/security/dummy.c +@@ -300,7 +300,8 @@ static int dummy_inode_link (struct dent + return 0; + } + +-static int dummy_inode_unlink (struct inode *inode, struct dentry *dentry) ++static int dummy_inode_unlink (struct inode *inode, struct dentry *dentry, ++ struct vfsmount *mnt) + { + return 0; + } +--- a/security/security.c ++++ b/security/security.c +@@ -370,11 +370,12 @@ int security_inode_link(struct dentry *o + new_dentry, new_mnt); + } + +-int security_inode_unlink(struct inode *dir, struct dentry *dentry) ++int security_inode_unlink(struct inode *dir, struct dentry *dentry, ++ struct vfsmount *mnt) + { + if (unlikely(IS_PRIVATE(dentry->d_inode))) + return 0; +- return security_ops->inode_unlink(dir, dentry); ++ return security_ops->inode_unlink(dir, dentry, mnt); + } + + int security_inode_symlink(struct inode *dir, struct dentry *dentry, +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -2469,11 +2469,12 @@ static int selinux_inode_link(struct den + return may_link(dir, old_dentry, MAY_LINK); + } + +-static int selinux_inode_unlink(struct inode *dir, struct dentry *dentry) ++static int selinux_inode_unlink(struct inode *dir, struct dentry *dentry, ++ struct vfsmount *mnt) + { + int rc; + +- rc = secondary_ops->inode_unlink(dir, dentry); ++ rc = secondary_ops->inode_unlink(dir, dentry, mnt); + if (rc) + return rc; + return may_link(dir, dentry, MAY_UNLINK); diff --git a/kernel-patches/2.6.25/security-xattr-file.diff b/kernel-patches/2.6.25/security-xattr-file.diff new file mode 100644 index 000000000..2835cc82e --- /dev/null +++ b/kernel-patches/2.6.25/security-xattr-file.diff @@ -0,0 +1,541 @@ +From: Andreas Gruenbacher +Subject: Pass struct file down the inode_*xattr security LSM hooks + +This allows LSMs to also distinguish between file descriptor and path +access for the xattr operations. (The other relevant operations are +covered by the setattr hook.) + +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/xattr.c | 58 ++++++++++++++++++++++++----------------------- + include/linux/security.h | 40 +++++++++++++++++++------------- + include/linux/xattr.h | 10 ++++---- + security/commoncap.c | 4 +-- + security/dummy.c | 10 ++++---- + security/security.c | 21 +++++++++-------- + security/selinux/hooks.c | 10 ++++---- + 7 files changed, 86 insertions(+), 67 deletions(-) + +--- a/fs/xattr.c ++++ b/fs/xattr.c +@@ -69,7 +69,7 @@ xattr_permission(struct inode *inode, co + + int + vfs_setxattr(struct dentry *dentry, struct vfsmount *mnt, char *name, +- void *value, size_t size, int flags) ++ void *value, size_t size, int flags, struct file *file) + { + struct inode *inode = dentry->d_inode; + int error; +@@ -79,7 +79,7 @@ vfs_setxattr(struct dentry *dentry, stru + return error; + + mutex_lock(&inode->i_mutex); +- error = security_inode_setxattr(dentry, mnt, name, value, size, flags); ++ error = security_inode_setxattr(dentry, mnt, name, value, size, flags, file); + if (error) + goto out; + error = -EOPNOTSUPP; +@@ -133,7 +133,7 @@ EXPORT_SYMBOL_GPL(xattr_getsecurity); + + ssize_t + vfs_getxattr(struct dentry *dentry, struct vfsmount *mnt, char *name, +- void *value, size_t size) ++ void *value, size_t size, struct file *file) + { + struct inode *inode = dentry->d_inode; + int error; +@@ -142,7 +142,7 @@ vfs_getxattr(struct dentry *dentry, stru + if (error) + return error; + +- error = security_inode_getxattr(dentry, mnt, name); ++ error = security_inode_getxattr(dentry, mnt, name, file); + if (error) + return error; + +@@ -170,12 +170,12 @@ EXPORT_SYMBOL_GPL(vfs_getxattr); + + ssize_t + vfs_listxattr(struct dentry *dentry, struct vfsmount *mnt, char *list, +- size_t size) ++ size_t size, struct file *file) + { + struct inode *inode = dentry->d_inode; + ssize_t error; + +- error = security_inode_listxattr(dentry, mnt); ++ error = security_inode_listxattr(dentry, mnt, file); + if (error) + return error; + error = -EOPNOTSUPP; +@@ -191,7 +191,8 @@ vfs_listxattr(struct dentry *dentry, str + EXPORT_SYMBOL_GPL(vfs_listxattr); + + int +-vfs_removexattr(struct dentry *dentry, struct vfsmount *mnt, char *name) ++vfs_removexattr(struct dentry *dentry, struct vfsmount *mnt, char *name, ++ struct file *file) + { + struct inode *inode = dentry->d_inode; + int error; +@@ -203,7 +204,7 @@ vfs_removexattr(struct dentry *dentry, s + if (error) + return error; + +- error = security_inode_removexattr(dentry, mnt, name); ++ error = security_inode_removexattr(dentry, mnt, name, file); + if (error) + return error; + +@@ -223,7 +224,7 @@ EXPORT_SYMBOL_GPL(vfs_removexattr); + */ + static long + setxattr(struct dentry *dentry, struct vfsmount *mnt, char __user *name, +- void __user *value, size_t size, int flags) ++ void __user *value, size_t size, int flags, struct file *file) + { + int error; + void *kvalue = NULL; +@@ -250,7 +251,7 @@ setxattr(struct dentry *dentry, struct v + } + } + +- error = vfs_setxattr(dentry, mnt, kname, kvalue, size, flags); ++ error = vfs_setxattr(dentry, mnt, kname, kvalue, size, flags, file); + kfree(kvalue); + return error; + } +@@ -265,7 +266,7 @@ sys_setxattr(char __user *path, char __u + error = user_path_walk(path, &nd); + if (error) + return error; +- error = setxattr(nd.path.dentry, nd.path.mnt, name, value, size, flags); ++ error = setxattr(nd.path.dentry, nd.path.mnt, name, value, size, flags, NULL); + path_put(&nd.path); + return error; + } +@@ -280,7 +281,7 @@ sys_lsetxattr(char __user *path, char __ + error = user_path_walk_link(path, &nd); + if (error) + return error; +- error = setxattr(nd.path.dentry, nd.path.mnt, name, value, size, flags); ++ error = setxattr(nd.path.dentry, nd.path.mnt, name, value, size, flags, NULL); + path_put(&nd.path); + return error; + } +@@ -298,7 +299,7 @@ sys_fsetxattr(int fd, char __user *name, + return error; + dentry = f->f_path.dentry; + audit_inode(NULL, dentry); +- error = setxattr(dentry, f->f_vfsmnt, name, value, size, flags); ++ error = setxattr(dentry, f->f_vfsmnt, name, value, size, flags, f); + fput(f); + return error; + } +@@ -308,7 +309,7 @@ sys_fsetxattr(int fd, char __user *name, + */ + static ssize_t + getxattr(struct dentry *dentry, struct vfsmount *mnt, char __user *name, +- void __user *value, size_t size) ++ void __user *value, size_t size, struct file *file) + { + ssize_t error; + void *kvalue = NULL; +@@ -328,7 +329,7 @@ getxattr(struct dentry *dentry, struct v + return -ENOMEM; + } + +- error = vfs_getxattr(dentry, mnt, kname, kvalue, size); ++ error = vfs_getxattr(dentry, mnt, kname, kvalue, size, file); + if (error > 0) { + if (size && copy_to_user(value, kvalue, error)) + error = -EFAULT; +@@ -351,7 +352,7 @@ sys_getxattr(char __user *path, char __u + error = user_path_walk(path, &nd); + if (error) + return error; +- error = getxattr(nd.path.dentry, nd.path.mnt, name, value, size); ++ error = getxattr(nd.path.dentry, nd.path.mnt, name, value, size, NULL); + path_put(&nd.path); + return error; + } +@@ -366,7 +367,7 @@ sys_lgetxattr(char __user *path, char __ + error = user_path_walk_link(path, &nd); + if (error) + return error; +- error = getxattr(nd.path.dentry, nd.path.mnt, name, value, size); ++ error = getxattr(nd.path.dentry, nd.path.mnt, name, value, size, NULL); + path_put(&nd.path); + return error; + } +@@ -381,7 +382,7 @@ sys_fgetxattr(int fd, char __user *name, + if (!f) + return error; + audit_inode(NULL, f->f_path.dentry); +- error = getxattr(f->f_path.dentry, f->f_path.mnt, name, value, size); ++ error = getxattr(f->f_path.dentry, f->f_path.mnt, name, value, size, f); + fput(f); + return error; + } +@@ -391,7 +392,7 @@ sys_fgetxattr(int fd, char __user *name, + */ + static ssize_t + listxattr(struct dentry *dentry, struct vfsmount *mnt, char __user *list, +- size_t size) ++ size_t size, struct file *file) + { + ssize_t error; + char *klist = NULL; +@@ -404,7 +405,7 @@ listxattr(struct dentry *dentry, struct + return -ENOMEM; + } + +- error = vfs_listxattr(dentry, mnt, klist, size); ++ error = vfs_listxattr(dentry, mnt, klist, size, file); + if (error > 0) { + if (size && copy_to_user(list, klist, error)) + error = -EFAULT; +@@ -426,7 +427,7 @@ sys_listxattr(char __user *path, char __ + error = user_path_walk(path, &nd); + if (error) + return error; +- error = listxattr(nd.path.dentry, nd.path.mnt, list, size); ++ error = listxattr(nd.path.dentry, nd.path.mnt, list, size, NULL); + path_put(&nd.path); + return error; + } +@@ -440,7 +441,7 @@ sys_llistxattr(char __user *path, char _ + error = user_path_walk_link(path, &nd); + if (error) + return error; +- error = listxattr(nd.path.dentry, nd.path.mnt, list, size); ++ error = listxattr(nd.path.dentry, nd.path.mnt, list, size, NULL); + path_put(&nd.path); + return error; + } +@@ -455,7 +456,7 @@ sys_flistxattr(int fd, char __user *list + if (!f) + return error; + audit_inode(NULL, f->f_path.dentry); +- error = listxattr(f->f_path.dentry, f->f_path.mnt, list, size); ++ error = listxattr(f->f_path.dentry, f->f_path.mnt, list, size, f); + fput(f); + return error; + } +@@ -464,7 +465,8 @@ sys_flistxattr(int fd, char __user *list + * Extended attribute REMOVE operations + */ + static long +-removexattr(struct dentry *dentry, struct vfsmount *mnt, char __user *name) ++removexattr(struct dentry *dentry, struct vfsmount *mnt, char __user *name, ++ struct file *file) + { + int error; + char kname[XATTR_NAME_MAX + 1]; +@@ -475,7 +477,7 @@ removexattr(struct dentry *dentry, struc + if (error < 0) + return error; + +- return vfs_removexattr(dentry, mnt, kname); ++ return vfs_removexattr(dentry, mnt, kname, file); + } + + asmlinkage long +@@ -487,7 +489,7 @@ sys_removexattr(char __user *path, char + error = user_path_walk(path, &nd); + if (error) + return error; +- error = removexattr(nd.path.dentry, nd.path.mnt, name); ++ error = removexattr(nd.path.dentry, nd.path.mnt, name, NULL); + path_put(&nd.path); + return error; + } +@@ -501,7 +503,7 @@ sys_lremovexattr(char __user *path, char + error = user_path_walk_link(path, &nd); + if (error) + return error; +- error = removexattr(nd.path.dentry, nd.path.mnt, name); ++ error = removexattr(nd.path.dentry, nd.path.mnt, name, NULL); + path_put(&nd.path); + return error; + } +@@ -518,7 +520,7 @@ sys_fremovexattr(int fd, char __user *na + return error; + dentry = f->f_path.dentry; + audit_inode(NULL, dentry); +- error = removexattr(dentry, f->f_path.mnt, name); ++ error = removexattr(dentry, f->f_path.mnt, name, f); + fput(f); + return error; + } +--- a/include/linux/security.h ++++ b/include/linux/security.h +@@ -51,8 +51,8 @@ extern void cap_capset_set (struct task_ + extern int cap_bprm_set_security (struct linux_binprm *bprm); + extern void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe); + extern int cap_bprm_secureexec(struct linux_binprm *bprm); +-extern int cap_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, char *name, void *value, size_t size, int flags); +-extern int cap_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt, char *name); ++extern int cap_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, char *name, void *value, size_t size, int flags, struct file *file); ++extern int cap_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt, char *name, struct file *file); + extern int cap_inode_need_killpriv(struct dentry *dentry); + extern int cap_inode_killpriv(struct dentry *dentry); + extern int cap_task_post_setuid (uid_t old_ruid, uid_t old_euid, uid_t old_suid, int flags); +@@ -1325,16 +1325,18 @@ struct security_operations { + int (*inode_getattr) (struct vfsmount *mnt, struct dentry *dentry); + void (*inode_delete) (struct inode *inode); + int (*inode_setxattr) (struct dentry *dentry, struct vfsmount *mnt, +- char *name, void *value, size_t size, int flags); ++ char *name, void *value, size_t size, int flags, ++ struct file *file); + void (*inode_post_setxattr) (struct dentry *dentry, + struct vfsmount *mnt, + char *name, void *value, + size_t size, int flags); + int (*inode_getxattr) (struct dentry *dentry, struct vfsmount *mnt, +- char *name); +- int (*inode_listxattr) (struct dentry *dentry, struct vfsmount *mnt); ++ char *name, struct file *file); ++ int (*inode_listxattr) (struct dentry *dentry, struct vfsmount *mnt, ++ struct file *file); + int (*inode_removexattr) (struct dentry *dentry, struct vfsmount *mnt, +- char *name); ++ char *name, struct file *file); + int (*inode_need_killpriv) (struct dentry *dentry); + int (*inode_killpriv) (struct dentry *dentry); + int (*inode_getsecurity)(const struct inode *inode, const char *name, void **buffer, bool alloc); +@@ -1598,15 +1600,17 @@ int security_inode_setattr(struct dentry + int security_inode_getattr(struct vfsmount *mnt, struct dentry *dentry); + void security_inode_delete(struct inode *inode); + int security_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, +- char *name, void *value, size_t size, int flags); ++ char *name, void *value, size_t size, int flags, ++ struct file *file); + void security_inode_post_setxattr(struct dentry *dentry, struct vfsmount *mnt, + char *name, void *value, size_t size, + int flags); + int security_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt, +- char *name); +-int security_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt); ++ char *name, struct file *file); ++int security_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt, ++ struct file *file); + int security_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt, +- char *name); ++ char *name, struct file *file); + int security_inode_need_killpriv(struct dentry *dentry); + int security_inode_killpriv(struct dentry *dentry); + int security_inode_getsecurity(const struct inode *inode, const char *name, void **buffer, bool alloc); +@@ -2020,9 +2024,10 @@ static inline void security_inode_delete + + static inline int security_inode_setxattr (struct dentry *dentry, + struct vfsmount *mnt, char *name, +- void *value, size_t size, int flags) ++ void *value, size_t size, int flags, ++ struct file *file) + { +- return cap_inode_setxattr(dentry, mnt, name, value, size, flags); ++ return cap_inode_setxattr(dentry, mnt, name, value, size, flags, file); + } + + static inline void security_inode_post_setxattr (struct dentry *dentry, +@@ -2033,21 +2038,24 @@ static inline void security_inode_post_s + { } + + static inline int security_inode_getxattr (struct dentry *dentry, +- struct vfsmount *mnt, char *name) ++ struct vfsmount *mnt, char *name, ++ struct file *file) + { + return 0; + } + + static inline int security_inode_listxattr (struct dentry *dentry, +- struct vfsmount *mnt) ++ struct vfsmount *mnt, ++ struct file *file) + { + return 0; + } + + static inline int security_inode_removexattr (struct dentry *dentry, +- struct vfsmount *mnt, char *name) ++ struct vfsmount *mnt, char *name, ++ struct file *file) + { +- return cap_inode_removexattr(dentry, mnt, name); ++ return cap_inode_removexattr(dentry, mnt, name, file); + } + + static inline int security_inode_need_killpriv(struct dentry *dentry) +--- a/include/linux/xattr.h ++++ b/include/linux/xattr.h +@@ -47,11 +47,13 @@ struct xattr_handler { + }; + + ssize_t xattr_getsecurity(struct inode *, const char *, void *, size_t); +-ssize_t vfs_getxattr(struct dentry *, struct vfsmount *, char *, void *, size_t); +-ssize_t vfs_listxattr(struct dentry *d, struct vfsmount *, char *list, size_t size); ++ssize_t vfs_getxattr(struct dentry *, struct vfsmount *, char *, void *, ++ size_t, struct file *); ++ssize_t vfs_listxattr(struct dentry *d, struct vfsmount *, char *list, ++ size_t size, struct file *); + int vfs_setxattr(struct dentry *, struct vfsmount *, char *, void *, size_t, +- int); +-int vfs_removexattr(struct dentry *, struct vfsmount *, char *); ++ int, struct file *); ++int vfs_removexattr(struct dentry *, struct vfsmount *, char *, struct file *); + + ssize_t generic_getxattr(struct dentry *dentry, const char *name, void *buffer, size_t size); + ssize_t generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size); +--- a/security/commoncap.c ++++ b/security/commoncap.c +@@ -387,7 +387,7 @@ int cap_bprm_secureexec (struct linux_bi + } + + int cap_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, char *name, +- void *value, size_t size, int flags) ++ void *value, size_t size, int flags, struct file *file) + { + if (!strcmp(name, XATTR_NAME_CAPS)) { + if (!capable(CAP_SETFCAP)) +@@ -401,7 +401,7 @@ int cap_inode_setxattr(struct dentry *de + } + + int cap_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt, +- char *name) ++ char *name, struct file *file) + { + if (!strcmp(name, XATTR_NAME_CAPS)) { + if (!capable(CAP_SETFCAP)) +--- a/security/dummy.c ++++ b/security/dummy.c +@@ -374,7 +374,7 @@ static void dummy_inode_delete (struct i + + static int dummy_inode_setxattr (struct dentry *dentry, struct vfsmount *mnt, + char *name, void *value, size_t size, +- int flags) ++ int flags, struct file *file) + { + if (!strncmp(name, XATTR_SECURITY_PREFIX, + sizeof(XATTR_SECURITY_PREFIX) - 1) && +@@ -391,18 +391,20 @@ static void dummy_inode_post_setxattr (s + } + + static int dummy_inode_getxattr (struct dentry *dentry, +- struct vfsmount *mnt, char *name) ++ struct vfsmount *mnt, char *name, ++ struct file *file) + { + return 0; + } + +-static int dummy_inode_listxattr (struct dentry *dentry, struct vfsmount *mnt) ++static int dummy_inode_listxattr (struct dentry *dentry, struct vfsmount *mnt, ++ struct file *file) + { + return 0; + } + + static int dummy_inode_removexattr (struct dentry *dentry, struct vfsmount *mnt, +- char *name) ++ char *name, struct file *file) + { + if (!strncmp(name, XATTR_SECURITY_PREFIX, + sizeof(XATTR_SECURITY_PREFIX) - 1) && +--- a/security/security.c ++++ b/security/security.c +@@ -465,12 +465,13 @@ void security_inode_delete(struct inode + } + + int security_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, +- char *name, void *value, size_t size, int flags) ++ char *name, void *value, size_t size, int flags, ++ struct file *file) + { + if (unlikely(IS_PRIVATE(dentry->d_inode))) + return 0; + return security_ops->inode_setxattr(dentry, mnt, name, value, size, +- flags); ++ flags, file); + } + + void security_inode_post_setxattr(struct dentry *dentry, struct vfsmount *mnt, +@@ -479,30 +480,32 @@ void security_inode_post_setxattr(struct + { + if (unlikely(IS_PRIVATE(dentry->d_inode))) + return; +- security_ops->inode_post_setxattr(dentry, mnt, name, value, size, flags); ++ security_ops->inode_post_setxattr(dentry, mnt, name, value, size, ++ flags); + } + + int security_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt, +- char *name) ++ char *name, struct file *file) + { + if (unlikely(IS_PRIVATE(dentry->d_inode))) + return 0; +- return security_ops->inode_getxattr(dentry, mnt, name); ++ return security_ops->inode_getxattr(dentry, mnt, name, file); + } + +-int security_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt) ++int security_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt, ++ struct file *file) + { + if (unlikely(IS_PRIVATE(dentry->d_inode))) + return 0; +- return security_ops->inode_listxattr(dentry, mnt); ++ return security_ops->inode_listxattr(dentry, mnt, file); + } + + int security_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt, +- char *name) ++ char *name, struct file *file) + { + if (unlikely(IS_PRIVATE(dentry->d_inode))) + return 0; +- return security_ops->inode_removexattr(dentry, mnt, name); ++ return security_ops->inode_removexattr(dentry, mnt, name, file); + } + + int security_inode_need_killpriv(struct dentry *dentry) +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -2598,7 +2598,7 @@ static int selinux_inode_setotherxattr(s + + static int selinux_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, + char *name, void *value, size_t size, +- int flags) ++ int flags, struct file *file) + { + struct task_security_struct *tsec = current->security; + struct inode *inode = dentry->d_inode; +@@ -2674,18 +2674,20 @@ static void selinux_inode_post_setxattr( + } + + static int selinux_inode_getxattr (struct dentry *dentry, struct vfsmount *mnt, +- char *name) ++ char *name, struct file *file) + { + return dentry_has_perm(current, NULL, dentry, FILE__GETATTR); + } + +-static int selinux_inode_listxattr (struct dentry *dentry, struct vfsmount *mnt) ++static int selinux_inode_listxattr (struct dentry *dentry, struct vfsmount *mnt, ++ struct file *file) + { + return dentry_has_perm(current, NULL, dentry, FILE__GETATTR); + } + + static int selinux_inode_removexattr (struct dentry *dentry, +- struct vfsmount *mnt, char *name) ++ struct vfsmount *mnt, char *name, ++ struct file *file) + { + if (strcmp(name, XATTR_NAME_SELINUX)) + return selinux_inode_setotherxattr(dentry, name); diff --git a/kernel-patches/2.6.25/series b/kernel-patches/2.6.25/series new file mode 100644 index 000000000..3ff392d00 --- /dev/null +++ b/kernel-patches/2.6.25/series @@ -0,0 +1,59 @@ +#unionfs-2.2.2_for_2.6.24-rc7.diff +#unionfs-2.1.11_for_2.6.24-rc4.diff +security-create.diff +remove_suid.diff +vfs-notify_change.diff +#should_remove_suid.diff +security-setattr.diff +vfs-mkdir.diff +security-mkdir.diff +vfs-mknod.diff +security-mknod.diff +vfs-symlink.diff +security-symlink.diff +security-readlink.diff +vfs-link.diff +security-link.diff +vfs-rmdir.diff +security-rmdir.diff +fix-vfs_rmdir.diff +vfs-unlink.diff +security-unlink.diff +vfs-rename.diff +security-rename.diff +vfs-setxattr.diff +security-setxattr.diff +vfs-getxattr.diff +security-getxattr.diff +vfs-listxattr.diff +security-listxattr.diff +vfs-removexattr.diff +security-removexattr.diff +unambiguous-__d_path.diff +mount-consistent-__d_path.diff +d_namespace_path.diff +__d_path-keep-connected.diff +#fgetattr.diff +fsetattr.diff +#fix-fuse.diff +fsetattr-reintro-ATTR_FILE.diff +file-handle-ops.diff +security-xattr-file.diff +sysctl-pathname.diff +parent-permission.diff +do_path_lookup-nameidata.diff +sys_fchdir-nameidata.diff +file_permission-nameidata.diff +apparmor-audit.diff +apparmor-main.diff +apparmor-lsm.diff +apparmor-module_interface.diff +apparmor-misc.diff +apparmor-intree.diff +#ptrace-bits.diff +#apparmor-ptrace.diff +#apparmor-named-transitions.diff +apparmor-network.diff +apparmor-rlimits.diff +apparmor-2.6.25.diff +apparmor-stack_secondary.diff diff --git a/kernel-patches/2.6.25/sys_fchdir-nameidata.diff b/kernel-patches/2.6.25/sys_fchdir-nameidata.diff new file mode 100644 index 000000000..01ab7c53a --- /dev/null +++ b/kernel-patches/2.6.25/sys_fchdir-nameidata.diff @@ -0,0 +1,40 @@ +From: Andreas Gruenbacher +Subject: Switch to vfs_permission() in sys_fchdir() + +Switch from file_permission() to vfs_permission() in sys_fchdir(): this +avoids calling permission() with a NULL nameidata here. + +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/open.c | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +--- a/fs/open.c ++++ b/fs/open.c +@@ -500,8 +500,8 @@ out: + + asmlinkage long sys_fchdir(unsigned int fd) + { ++ struct nameidata nd = { .flags = 0 }; + struct file *file; +- struct inode *inode; + int error; + + error = -EBADF; +@@ -509,12 +509,11 @@ asmlinkage long sys_fchdir(unsigned int + if (!file) + goto out; + +- inode = file->f_path.dentry->d_inode; +- + error = -ENOTDIR; +- if (!S_ISDIR(inode->i_mode)) ++ if (!S_ISDIR(file->f_path.dentry->d_inode->i_mode)) + goto out_putf; + ++ nd.path = file->f_path; + error = file_permission(file, MAY_EXEC); + if (!error) + set_fs_pwd(current->fs, &file->f_path); diff --git a/kernel-patches/2.6.25/sysctl-pathname.diff b/kernel-patches/2.6.25/sysctl-pathname.diff new file mode 100644 index 000000000..0e6c7d3d9 --- /dev/null +++ b/kernel-patches/2.6.25/sysctl-pathname.diff @@ -0,0 +1,111 @@ +From: Andreas Gruenbacher +Subject: Factor out sysctl pathname code + +Convert the selinux sysctl pathname computation code into a standalone +function. + +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen +Reviewed-by: James Morris + +--- + include/linux/sysctl.h | 2 ++ + kernel/sysctl.c | 27 +++++++++++++++++++++++++++ + security/selinux/hooks.c | 35 +++++------------------------------ + 3 files changed, 34 insertions(+), 30 deletions(-) + +--- a/include/linux/sysctl.h ++++ b/include/linux/sysctl.h +@@ -977,6 +977,8 @@ extern int proc_doulongvec_minmax(struct + extern int proc_doulongvec_ms_jiffies_minmax(struct ctl_table *table, int, + struct file *, void __user *, size_t *, loff_t *); + ++extern char *sysctl_pathname(ctl_table *, char *, int); ++ + extern int do_sysctl (int __user *name, int nlen, + void __user *oldval, size_t __user *oldlenp, + void __user *newval, size_t newlen); +--- a/kernel/sysctl.c ++++ b/kernel/sysctl.c +@@ -1440,6 +1440,33 @@ void register_sysctl_root(struct ctl_tab + spin_unlock(&sysctl_lock); + } + ++char *sysctl_pathname(struct ctl_table *table, char *buffer, int buflen) ++{ ++ if (buflen < 1) ++ return NULL; ++ buffer += --buflen; ++ *buffer = '\0'; ++ ++ while (table) { ++ int namelen = strlen(table->procname); ++ ++ if (buflen < namelen + 1) ++ return NULL; ++ buflen -= namelen + 1; ++ buffer -= namelen; ++ memcpy(buffer, table->procname, namelen); ++ *--buffer = '/'; ++ table = table->parent; ++ } ++ if (buflen < 4) ++ return NULL; ++ buffer -= 4; ++ memcpy(buffer, "/sys", 4); ++ ++ return buffer; ++} ++EXPORT_SYMBOL_GPL(sysctl_pathname); ++ + #ifdef CONFIG_SYSCTL_SYSCALL + int do_sysctl(int __user *name, int nlen, void __user *oldval, size_t __user *oldlenp, + void __user *newval, size_t newlen) +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -1702,40 +1702,15 @@ static int selinux_capable(struct task_s + + static int selinux_sysctl_get_sid(ctl_table *table, u16 tclass, u32 *sid) + { +- int buflen, rc; +- char *buffer, *path, *end; ++ char *buffer, *path; ++ int rc = -ENOMEM; + +- rc = -ENOMEM; + buffer = (char*)__get_free_page(GFP_KERNEL); + if (!buffer) + goto out; +- +- buflen = PAGE_SIZE; +- end = buffer+buflen; +- *--end = '\0'; +- buflen--; +- path = end-1; +- *path = '/'; +- while (table) { +- const char *name = table->procname; +- size_t namelen = strlen(name); +- buflen -= namelen + 1; +- if (buflen < 0) +- goto out_free; +- end -= namelen; +- memcpy(end, name, namelen); +- *--end = '/'; +- path = end; +- table = table->parent; +- } +- buflen -= 4; +- if (buflen < 0) +- goto out_free; +- end -= 4; +- memcpy(end, "/sys", 4); +- path = end; +- rc = security_genfs_sid("proc", path, tclass, sid); +-out_free: ++ path = sysctl_pathname(table, buffer, PAGE_SIZE); ++ if (path) ++ rc = security_genfs_sid("proc", path, tclass, sid); + free_page((unsigned long)buffer); + out: + return rc; diff --git a/kernel-patches/2.6.25/unambiguous-__d_path.diff b/kernel-patches/2.6.25/unambiguous-__d_path.diff new file mode 100644 index 000000000..03ce8bbad --- /dev/null +++ b/kernel-patches/2.6.25/unambiguous-__d_path.diff @@ -0,0 +1,258 @@ +From: Andreas Gruenbacher +Subject: Fix __d_path() for lazy unmounts and make it unambiguous + +First, when __d_path() hits a lazily unmounted mount point, it tries to prepend +the name of the lazily unmounted dentry to the path name. It gets this wrong, +and also overwrites the slash that separates the name from the following +pathname component. This patch fixes that; if a process was in directory +/foo/bar and /foo got lazily unmounted, the old result was ``foobar'' (note the +missing slash), while the new result with this patch is ``foo/bar''. + +Second, it isn't always possible to tell from the __d_path() result whether the +specified root and rootmnt (i.e., the chroot) was reached. We need an +unambiguous result for AppArmor at least though, so we make sure that paths +will only start with a slash if the path leads all the way up to the root. + +We also add a @fail_deleted argument, which allows to get rid of some of the +mess in sys_getcwd(). + +This patch leaves getcwd() and d_path() as they were before for everything +except for bind-mounted directories; for them, it reports ``/foo/bar'' instead +of ``foobar'' in the example described above. + +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen +Acked-by: Alan Cox + +--- + fs/dcache.c | 159 +++++++++++++++++++++++++++++++++++------------------------- + 1 file changed, 95 insertions(+), 64 deletions(-) + +--- a/fs/dcache.c ++++ b/fs/dcache.c +@@ -1747,51 +1747,53 @@ shouldnt_be_hashed: + } + + /** +- * d_path - return the path of a dentry ++ * __d_path - return the path of a dentry + * @dentry: dentry to report + * @vfsmnt: vfsmnt to which the dentry belongs + * @root: root dentry + * @rootmnt: vfsmnt to which the root dentry belongs + * @buffer: buffer to return value in + * @buflen: buffer length ++ * @fail_deleted: what to return for deleted files + * +- * Convert a dentry into an ASCII path name. If the entry has been deleted ++ * Convert a dentry into an ASCII path name. If the entry has been deleted, ++ * then if @fail_deleted is true, ERR_PTR(-ENOENT) is returned. Otherwise, + * the string " (deleted)" is appended. Note that this is ambiguous. + * + * Returns the buffer or an error code if the path was too long. ++ * If @dentry is not connected to @root, the path returned will be relative ++ * (i.e., it will not start with a slash). + * +- * "buflen" should be positive. Caller holds the dcache_lock. ++ * Returns the buffer or an error code. + */ + static char *__d_path(struct dentry *dentry, struct vfsmount *vfsmnt, +- struct path *root, char *buffer, int buflen) ++ struct path *root, char *buffer, int buflen, ++ int fail_deleted) + { +- char * end = buffer+buflen; +- char * retval; +- int namelen; ++ int namelen, is_slash; ++ ++ if (buflen < 2) ++ return ERR_PTR(-ENAMETOOLONG); ++ buffer += --buflen; ++ *buffer = '\0'; + +- *--end = '\0'; +- buflen--; ++ spin_lock(&dcache_lock); + if (!IS_ROOT(dentry) && d_unhashed(dentry)) { +- buflen -= 10; +- end -= 10; +- if (buflen < 0) ++ if (fail_deleted) { ++ buffer = ERR_PTR(-ENOENT); ++ goto out; ++ } ++ if (buflen < 10) + goto Elong; +- memcpy(end, " (deleted)", 10); ++ buflen -= 10; ++ buffer -= 10; ++ memcpy(buffer, " (deleted)", 10); + } + +- if (buflen < 1) +- goto Elong; +- /* Get '/' right */ +- retval = end-1; +- *retval = '/'; +- +- for (;;) { ++ while (dentry != root->dentry || vfsmnt != root->mnt) { + struct dentry * parent; + +- if (dentry == root->dentry && vfsmnt == root->mnt) +- break; + if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { +- /* Global root? */ + spin_lock(&vfsmount_lock); + if (vfsmnt->mnt_parent == vfsmnt) { + spin_unlock(&vfsmount_lock); +@@ -1805,28 +1807,68 @@ static char *__d_path(struct dentry *den + parent = dentry->d_parent; + prefetch(parent); + namelen = dentry->d_name.len; +- buflen -= namelen + 1; +- if (buflen < 0) ++ if (buflen < namelen + 1) + goto Elong; +- end -= namelen; +- memcpy(end, dentry->d_name.name, namelen); +- *--end = '/'; +- retval = end; ++ buflen -= namelen + 1; ++ buffer -= namelen; ++ memcpy(buffer, dentry->d_name.name, namelen); ++ *--buffer = '/'; + dentry = parent; + } ++ /* Get '/' right. */ ++ if (*buffer != '/') ++ *--buffer = '/'; + +- return retval; ++out: ++ spin_unlock(&dcache_lock); ++ return buffer; + + global_root: ++ /* ++ * We went past the (vfsmount, dentry) we were looking for and have ++ * either hit a root dentry, a lazily unmounted dentry, an ++ * unconnected dentry, or the file is on a pseudo filesystem. ++ */ + namelen = dentry->d_name.len; +- buflen -= namelen; +- if (buflen < 0) ++ is_slash = (namelen == 1 && *dentry->d_name.name == '/'); ++ if (is_slash || (dentry->d_sb->s_flags & MS_NOUSER)) { ++ /* ++ * Make sure we won't return a pathname starting with '/'. ++ * ++ * Historically, we also glue together the root dentry and ++ * remaining name for pseudo filesystems like pipefs, which ++ * have the MS_NOUSER flag set. This results in pathnames ++ * like "pipe:[439336]". ++ */ ++ if (*buffer == '/') { ++ buffer++; ++ buflen++; ++ } ++ if (is_slash) ++ goto out; ++ } ++ if (buflen < namelen) + goto Elong; +- retval -= namelen-1; /* hit the slash */ +- memcpy(retval, dentry->d_name.name, namelen); +- return retval; ++ buffer -= namelen; ++ memcpy(buffer, dentry->d_name.name, namelen); ++ goto out; ++ + Elong: +- return ERR_PTR(-ENAMETOOLONG); ++ buffer = ERR_PTR(-ENAMETOOLONG); ++ goto out; ++} ++ ++static char *__connect_d_path(char *path, char *buffer, struct dentry *dentry) ++{ ++ if (!IS_ERR(path) && *path != '/' && ++ !(dentry->d_sb->s_flags & MS_NOUSER)) { ++ /* Pretend that disconnected paths are hanging off the root. */ ++ if (path == buffer) ++ path = ERR_PTR(-ENAMETOOLONG); ++ else ++ *--path = '/'; ++ } ++ return path; + } + + /** +@@ -1861,9 +1903,8 @@ char *d_path(struct path *path, char *bu + root = current->fs->root; + path_get(¤t->fs->root); + read_unlock(¤t->fs->lock); +- spin_lock(&dcache_lock); +- res = __d_path(path->dentry, path->mnt, &root, buf, buflen); +- spin_unlock(&dcache_lock); ++ res = __d_path(path->dentry, path->mnt, &root, buf, buflen, 0); ++ res = __connect_d_path(res, buf, path->dentry); + path_put(&root); + return res; + } +@@ -1909,9 +1950,9 @@ char *dynamic_dname(struct dentry *dentr + */ + asmlinkage long sys_getcwd(char __user *buf, unsigned long size) + { +- int error; ++ int error, len; + struct path pwd, root; +- char *page = (char *) __get_free_page(GFP_USER); ++ char *page = (char *) __get_free_page(GFP_USER), *cwd; + + if (!page) + return -ENOMEM; +@@ -1923,29 +1964,19 @@ asmlinkage long sys_getcwd(char __user * + path_get(¤t->fs->root); + read_unlock(¤t->fs->lock); + +- error = -ENOENT; +- /* Has the current directory has been unlinked? */ +- spin_lock(&dcache_lock); +- if (pwd.dentry->d_parent == pwd.dentry || !d_unhashed(pwd.dentry)) { +- unsigned long len; +- char * cwd; +- +- cwd = __d_path(pwd.dentry, pwd.mnt, &root, page, PAGE_SIZE); +- spin_unlock(&dcache_lock); +- +- error = PTR_ERR(cwd); +- if (IS_ERR(cwd)) +- goto out; +- +- error = -ERANGE; +- len = PAGE_SIZE + page - cwd; +- if (len <= size) { +- error = len; +- if (copy_to_user(buf, cwd, len)) +- error = -EFAULT; +- } +- } else +- spin_unlock(&dcache_lock); ++ cwd = __d_path(pwd.dentry, pwd.mnt, &root, page, PAGE_SIZE, 1); ++ cwd = __connect_d_path(cwd, page, pwd.dentry); ++ error = PTR_ERR(cwd); ++ if (IS_ERR(cwd)) ++ goto out; ++ ++ error = -ERANGE; ++ len = PAGE_SIZE + page - cwd; ++ if (len <= size) { ++ error = len; ++ if (copy_to_user(buf, cwd, len)) ++ error = -EFAULT; ++ } + + out: + path_put(&pwd); diff --git a/kernel-patches/2.6.25/vfs-getxattr.diff b/kernel-patches/2.6.25/vfs-getxattr.diff new file mode 100644 index 000000000..bf14a4fae --- /dev/null +++ b/kernel-patches/2.6.25/vfs-getxattr.diff @@ -0,0 +1,189 @@ +From: Tony Jones +Subject: Add a struct vfsmount parameter to vfs_getxattr() + +The vfsmount will be passed down to the LSM hook so that LSMs can compute +pathnames. + +Signed-off-by: Tony Jones +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/nfsd/nfs4xdr.c | 2 +- + fs/nfsd/vfs.c | 21 ++++++++++++--------- + fs/xattr.c | 14 ++++++++------ + include/linux/nfsd/nfsd.h | 3 ++- + include/linux/xattr.h | 2 +- + 5 files changed, 24 insertions(+), 18 deletions(-) + +--- a/fs/nfsd/nfs4xdr.c ++++ b/fs/nfsd/nfs4xdr.c +@@ -1501,7 +1501,7 @@ nfsd4_encode_fattr(struct svc_fh *fhp, s + } + if (bmval0 & (FATTR4_WORD0_ACL | FATTR4_WORD0_ACLSUPPORT + | FATTR4_WORD0_SUPPORTED_ATTRS)) { +- err = nfsd4_get_nfs4_acl(rqstp, dentry, &acl); ++ err = nfsd4_get_nfs4_acl(rqstp, dentry, exp->ex_path.mnt, &acl); + aclsupport = (err == 0); + if (bmval0 & FATTR4_WORD0_ACL) { + if (err == -EOPNOTSUPP) +--- a/fs/nfsd/vfs.c ++++ b/fs/nfsd/vfs.c +@@ -408,11 +408,12 @@ out_nfserr: + #if defined(CONFIG_NFSD_V2_ACL) || \ + defined(CONFIG_NFSD_V3_ACL) || \ + defined(CONFIG_NFSD_V4) +-static ssize_t nfsd_getxattr(struct dentry *dentry, char *key, void **buf) ++static ssize_t nfsd_getxattr(struct dentry *dentry, struct vfsmount *mnt, ++ char *key, void **buf) + { + ssize_t buflen; + +- buflen = vfs_getxattr(dentry, key, NULL, 0); ++ buflen = vfs_getxattr(dentry, mnt, key, NULL, 0); + if (buflen <= 0) + return buflen; + +@@ -420,7 +421,7 @@ static ssize_t nfsd_getxattr(struct dent + if (!*buf) + return -ENOMEM; + +- return vfs_getxattr(dentry, key, *buf, buflen); ++ return vfs_getxattr(dentry, mnt, key, *buf, buflen); + } + #endif + +@@ -501,13 +502,13 @@ out_nfserr: + } + + static struct posix_acl * +-_get_posix_acl(struct dentry *dentry, char *key) ++_get_posix_acl(struct dentry *dentry, struct vfsmount *mnt, char *key) + { + void *buf = NULL; + struct posix_acl *pacl = NULL; + int buflen; + +- buflen = nfsd_getxattr(dentry, key, &buf); ++ buflen = nfsd_getxattr(dentry, mnt, key, &buf); + if (!buflen) + buflen = -ENODATA; + if (buflen <= 0) +@@ -519,14 +520,15 @@ _get_posix_acl(struct dentry *dentry, ch + } + + int +-nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct nfs4_acl **acl) ++nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, ++ struct vfsmount *mnt, struct nfs4_acl **acl) + { + struct inode *inode = dentry->d_inode; + int error = 0; + struct posix_acl *pacl = NULL, *dpacl = NULL; + unsigned int flags = 0; + +- pacl = _get_posix_acl(dentry, POSIX_ACL_XATTR_ACCESS); ++ pacl = _get_posix_acl(dentry, mnt, POSIX_ACL_XATTR_ACCESS); + if (IS_ERR(pacl) && PTR_ERR(pacl) == -ENODATA) + pacl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL); + if (IS_ERR(pacl)) { +@@ -536,7 +538,7 @@ nfsd4_get_nfs4_acl(struct svc_rqst *rqst + } + + if (S_ISDIR(inode->i_mode)) { +- dpacl = _get_posix_acl(dentry, POSIX_ACL_XATTR_DEFAULT); ++ dpacl = _get_posix_acl(dentry, mnt, POSIX_ACL_XATTR_DEFAULT); + if (IS_ERR(dpacl) && PTR_ERR(dpacl) == -ENODATA) + dpacl = NULL; + else if (IS_ERR(dpacl)) { +@@ -2011,7 +2013,8 @@ nfsd_get_posix_acl(struct svc_fh *fhp, i + return ERR_PTR(-EOPNOTSUPP); + } + +- size = nfsd_getxattr(fhp->fh_dentry, name, &value); ++ size = nfsd_getxattr(fhp->fh_dentry, fhp->fh_export->ex_path.mnt, name, ++ &value); + if (size < 0) + return ERR_PTR(size); + +--- a/fs/xattr.c ++++ b/fs/xattr.c +@@ -132,7 +132,8 @@ out_noalloc: + EXPORT_SYMBOL_GPL(xattr_getsecurity); + + ssize_t +-vfs_getxattr(struct dentry *dentry, char *name, void *value, size_t size) ++vfs_getxattr(struct dentry *dentry, struct vfsmount *mnt, char *name, ++ void *value, size_t size) + { + struct inode *inode = dentry->d_inode; + int error; +@@ -304,7 +305,8 @@ sys_fsetxattr(int fd, char __user *name, + * Extended attribute GET operations + */ + static ssize_t +-getxattr(struct dentry *d, char __user *name, void __user *value, size_t size) ++getxattr(struct dentry *dentry, struct vfsmount *mnt, char __user *name, ++ void __user *value, size_t size) + { + ssize_t error; + void *kvalue = NULL; +@@ -324,7 +326,7 @@ getxattr(struct dentry *d, char __user * + return -ENOMEM; + } + +- error = vfs_getxattr(d, kname, kvalue, size); ++ error = vfs_getxattr(dentry, mnt, kname, kvalue, size); + if (error > 0) { + if (size && copy_to_user(value, kvalue, error)) + error = -EFAULT; +@@ -347,7 +349,7 @@ sys_getxattr(char __user *path, char __u + error = user_path_walk(path, &nd); + if (error) + return error; +- error = getxattr(nd.path.dentry, name, value, size); ++ error = getxattr(nd.path.dentry, nd.path.mnt, name, value, size); + path_put(&nd.path); + return error; + } +@@ -362,7 +364,7 @@ sys_lgetxattr(char __user *path, char __ + error = user_path_walk_link(path, &nd); + if (error) + return error; +- error = getxattr(nd.path.dentry, name, value, size); ++ error = getxattr(nd.path.dentry, nd.path.mnt, name, value, size); + path_put(&nd.path); + return error; + } +@@ -377,7 +379,7 @@ sys_fgetxattr(int fd, char __user *name, + if (!f) + return error; + audit_inode(NULL, f->f_path.dentry); +- error = getxattr(f->f_path.dentry, name, value, size); ++ error = getxattr(f->f_path.dentry, f->f_path.mnt, name, value, size); + fput(f); + return error; + } +--- a/include/linux/nfsd/nfsd.h ++++ b/include/linux/nfsd/nfsd.h +@@ -78,7 +78,8 @@ __be32 nfsd_setattr(struct svc_rqst *, + #ifdef CONFIG_NFSD_V4 + __be32 nfsd4_set_nfs4_acl(struct svc_rqst *, struct svc_fh *, + struct nfs4_acl *); +-int nfsd4_get_nfs4_acl(struct svc_rqst *, struct dentry *, struct nfs4_acl **); ++int nfsd4_get_nfs4_acl(struct svc_rqst *, struct dentry *, ++ struct vfsmount *mnt, struct nfs4_acl **); + #endif /* CONFIG_NFSD_V4 */ + __be32 nfsd_create(struct svc_rqst *, struct svc_fh *, + char *name, int len, struct iattr *attrs, +--- a/include/linux/xattr.h ++++ b/include/linux/xattr.h +@@ -47,7 +47,7 @@ struct xattr_handler { + }; + + ssize_t xattr_getsecurity(struct inode *, const char *, void *, size_t); +-ssize_t vfs_getxattr(struct dentry *, char *, void *, size_t); ++ssize_t vfs_getxattr(struct dentry *, struct vfsmount *, char *, void *, size_t); + ssize_t vfs_listxattr(struct dentry *d, char *list, size_t size); + int vfs_setxattr(struct dentry *, struct vfsmount *, char *, void *, size_t, + int); diff --git a/kernel-patches/2.6.25/vfs-link.diff b/kernel-patches/2.6.25/vfs-link.diff new file mode 100644 index 000000000..935c6d6ae --- /dev/null +++ b/kernel-patches/2.6.25/vfs-link.diff @@ -0,0 +1,90 @@ +From: Tony Jones +Subject: Add struct vfsmount parameters to vfs_link() + +The vfsmount will be passed down to the LSM hook so that LSMs can compute +pathnames. + +Signed-off-by: Tony Jones +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/ecryptfs/inode.c | 9 +++++++-- + fs/namei.c | 5 +++-- + fs/nfsd/vfs.c | 3 ++- + include/linux/fs.h | 2 +- + 4 files changed, 13 insertions(+), 6 deletions(-) + +--- a/fs/ecryptfs/inode.c ++++ b/fs/ecryptfs/inode.c +@@ -388,19 +388,24 @@ static int ecryptfs_link(struct dentry * + struct dentry *new_dentry) + { + struct dentry *lower_old_dentry; ++ struct vfsmount *lower_old_mnt; + struct dentry *lower_new_dentry; ++ struct vfsmount *lower_new_mnt; + struct dentry *lower_dir_dentry; + u64 file_size_save; + int rc; + + file_size_save = i_size_read(old_dentry->d_inode); + lower_old_dentry = ecryptfs_dentry_to_lower(old_dentry); ++ lower_old_mnt = ecryptfs_dentry_to_lower_mnt(old_dentry); + lower_new_dentry = ecryptfs_dentry_to_lower(new_dentry); ++ lower_new_mnt = ecryptfs_dentry_to_lower_mnt(new_dentry); + dget(lower_old_dentry); + dget(lower_new_dentry); + lower_dir_dentry = lock_parent(lower_new_dentry); +- rc = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode, +- lower_new_dentry); ++ rc = vfs_link(lower_old_dentry, lower_old_mnt, ++ lower_dir_dentry->d_inode, lower_new_dentry, ++ lower_new_mnt); + if (rc || !lower_new_dentry->d_inode) + goto out_lock; + rc = ecryptfs_interpose(lower_new_dentry, new_dentry, dir->i_sb, 0); +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -2335,7 +2335,7 @@ asmlinkage long sys_symlink(const char _ + return sys_symlinkat(oldname, AT_FDCWD, newname); + } + +-int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry) ++int vfs_link(struct dentry *old_dentry, struct vfsmount *old_mnt, struct inode *dir, struct dentry *new_dentry, struct vfsmount *new_mnt) + { + struct inode *inode = old_dentry->d_inode; + int error; +@@ -2413,7 +2413,8 @@ asmlinkage long sys_linkat(int olddfd, c + error = PTR_ERR(new_dentry); + if (IS_ERR(new_dentry)) + goto out_unlock; +- error = vfs_link(old_nd.path.dentry, nd.path.dentry->d_inode, new_dentry); ++ error = vfs_link(old_nd.path.dentry, old_nd.path.mnt, nd.path.dentry->d_inode, ++ new_dentry, nd.path.mnt); + dput(new_dentry); + out_unlock: + mutex_unlock(&nd.path.dentry->d_inode->i_mutex); +--- a/fs/nfsd/vfs.c ++++ b/fs/nfsd/vfs.c +@@ -1599,7 +1599,8 @@ nfsd_link(struct svc_rqst *rqstp, struct + dold = tfhp->fh_dentry; + dest = dold->d_inode; + +- host_err = vfs_link(dold, dirp, dnew); ++ host_err = vfs_link(dold, tfhp->fh_export->ex_path.mnt, dirp, ++ dnew, ffhp->fh_export->ex_path.mnt); + if (!host_err) { + if (EX_ISSYNC(ffhp->fh_export)) { + err = nfserrno(nfsd_sync_dir(ddir)); +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -1079,7 +1079,7 @@ extern int vfs_create(struct inode *, st + extern int vfs_mkdir(struct inode *, struct dentry *, struct vfsmount *, int); + extern int vfs_mknod(struct inode *, struct dentry *, struct vfsmount *, int, dev_t); + extern int vfs_symlink(struct inode *, struct dentry *, struct vfsmount *, const char *, int); +-extern int vfs_link(struct dentry *, struct inode *, struct dentry *); ++extern int vfs_link(struct dentry *, struct vfsmount *, struct inode *, struct dentry *, struct vfsmount *); + extern int vfs_rmdir(struct inode *, struct dentry *); + extern int vfs_unlink(struct inode *, struct dentry *); + extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *); diff --git a/kernel-patches/2.6.25/vfs-listxattr.diff b/kernel-patches/2.6.25/vfs-listxattr.diff new file mode 100644 index 000000000..d60c48dd1 --- /dev/null +++ b/kernel-patches/2.6.25/vfs-listxattr.diff @@ -0,0 +1,101 @@ +From: Tony Jones +Subject: Add a struct vfsmount parameter to vfs_listxattr() + +The vfsmount will be passed down to the LSM hook so that LSMs can compute +pathnames. + +Signed-off-by: Tony Jones +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/xattr.c | 25 ++++++++++++++----------- + include/linux/xattr.h | 2 +- + 2 files changed, 15 insertions(+), 12 deletions(-) + +--- a/fs/xattr.c ++++ b/fs/xattr.c +@@ -169,18 +169,20 @@ nolsm: + EXPORT_SYMBOL_GPL(vfs_getxattr); + + ssize_t +-vfs_listxattr(struct dentry *d, char *list, size_t size) ++vfs_listxattr(struct dentry *dentry, struct vfsmount *mnt, char *list, ++ size_t size) + { ++ struct inode *inode = dentry->d_inode; + ssize_t error; + +- error = security_inode_listxattr(d); ++ error = security_inode_listxattr(dentry); + if (error) + return error; + error = -EOPNOTSUPP; +- if (d->d_inode->i_op && d->d_inode->i_op->listxattr) { +- error = d->d_inode->i_op->listxattr(d, list, size); +- } else { +- error = security_inode_listsecurity(d->d_inode, list, size); ++ if (inode->i_op && inode->i_op->listxattr) ++ error = inode->i_op->listxattr(dentry, list, size); ++ else { ++ error = security_inode_listsecurity(inode, list, size); + if (size && error > size) + error = -ERANGE; + } +@@ -388,7 +390,8 @@ sys_fgetxattr(int fd, char __user *name, + * Extended attribute LIST operations + */ + static ssize_t +-listxattr(struct dentry *d, char __user *list, size_t size) ++listxattr(struct dentry *dentry, struct vfsmount *mnt, char __user *list, ++ size_t size) + { + ssize_t error; + char *klist = NULL; +@@ -401,7 +404,7 @@ listxattr(struct dentry *d, char __user + return -ENOMEM; + } + +- error = vfs_listxattr(d, klist, size); ++ error = vfs_listxattr(dentry, mnt, klist, size); + if (error > 0) { + if (size && copy_to_user(list, klist, error)) + error = -EFAULT; +@@ -423,7 +426,7 @@ sys_listxattr(char __user *path, char __ + error = user_path_walk(path, &nd); + if (error) + return error; +- error = listxattr(nd.path.dentry, list, size); ++ error = listxattr(nd.path.dentry, nd.path.mnt, list, size); + path_put(&nd.path); + return error; + } +@@ -437,7 +440,7 @@ sys_llistxattr(char __user *path, char _ + error = user_path_walk_link(path, &nd); + if (error) + return error; +- error = listxattr(nd.path.dentry, list, size); ++ error = listxattr(nd.path.dentry, nd.path.mnt, list, size); + path_put(&nd.path); + return error; + } +@@ -452,7 +455,7 @@ sys_flistxattr(int fd, char __user *list + if (!f) + return error; + audit_inode(NULL, f->f_path.dentry); +- error = listxattr(f->f_path.dentry, list, size); ++ error = listxattr(f->f_path.dentry, f->f_path.mnt, list, size); + fput(f); + return error; + } +--- a/include/linux/xattr.h ++++ b/include/linux/xattr.h +@@ -48,7 +48,7 @@ struct xattr_handler { + + ssize_t xattr_getsecurity(struct inode *, const char *, void *, size_t); + ssize_t vfs_getxattr(struct dentry *, struct vfsmount *, char *, void *, size_t); +-ssize_t vfs_listxattr(struct dentry *d, char *list, size_t size); ++ssize_t vfs_listxattr(struct dentry *d, struct vfsmount *, char *list, size_t size); + int vfs_setxattr(struct dentry *, struct vfsmount *, char *, void *, size_t, + int); + int vfs_removexattr(struct dentry *, char *); diff --git a/kernel-patches/2.6.25/vfs-mkdir.diff b/kernel-patches/2.6.25/vfs-mkdir.diff new file mode 100644 index 000000000..a8c2f5f5e --- /dev/null +++ b/kernel-patches/2.6.25/vfs-mkdir.diff @@ -0,0 +1,137 @@ +From: Tony Jones +Subject: Add struct vfsmount parameter to vfs_mkdir() + +The vfsmount will be passed down to the LSM hook so that LSMs can compute +pathnames. + +Signed-off-by: Tony Jones +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/ecryptfs/inode.c | 5 ++++- + fs/namei.c | 5 +++-- + fs/nfsd/nfs4recover.c | 3 ++- + fs/nfsd/vfs.c | 8 +++++--- + include/linux/fs.h | 2 +- + kernel/cgroup.c | 2 +- + 6 files changed, 16 insertions(+), 9 deletions(-) + +--- a/fs/ecryptfs/inode.c ++++ b/fs/ecryptfs/inode.c +@@ -487,11 +487,14 @@ static int ecryptfs_mkdir(struct inode * + { + int rc; + struct dentry *lower_dentry; ++ struct vfsmount *lower_mnt; + struct dentry *lower_dir_dentry; + + lower_dentry = ecryptfs_dentry_to_lower(dentry); ++ lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry); + lower_dir_dentry = lock_parent(lower_dentry); +- rc = vfs_mkdir(lower_dir_dentry->d_inode, lower_dentry, mode); ++ rc = vfs_mkdir(lower_dir_dentry->d_inode, lower_dentry, lower_mnt, ++ mode); + if (rc || !lower_dentry->d_inode) + goto out; + rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0); +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -2000,7 +2000,8 @@ asmlinkage long sys_mknod(const char __u + return sys_mknodat(AT_FDCWD, filename, mode, dev); + } + +-int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) ++int vfs_mkdir(struct inode *dir, struct dentry *dentry, struct vfsmount *mnt, ++ int mode) + { + int error = may_create(dir, dentry, NULL); + +@@ -2044,7 +2045,7 @@ asmlinkage long sys_mkdirat(int dfd, con + + if (!IS_POSIXACL(nd.path.dentry->d_inode)) + mode &= ~current->fs->umask; +- error = vfs_mkdir(nd.path.dentry->d_inode, dentry, mode); ++ error = vfs_mkdir(nd.path.dentry->d_inode, dentry, nd.path.mnt, mode); + dput(dentry); + out_unlock: + mutex_unlock(&nd.path.dentry->d_inode->i_mutex); +--- a/fs/nfsd/nfs4recover.c ++++ b/fs/nfsd/nfs4recover.c +@@ -154,7 +154,8 @@ nfsd4_create_clid_dir(struct nfs4_client + dprintk("NFSD: nfsd4_create_clid_dir: DIRECTORY EXISTS\n"); + goto out_put; + } +- status = vfs_mkdir(rec_dir.path.dentry->d_inode, dentry, S_IRWXU); ++ status = vfs_mkdir(rec_dir.path.dentry->d_inode, dentry, ++ rec_dir.path.mnt, S_IRWXU); + out_put: + dput(dentry); + out_unlock: +--- a/fs/nfsd/vfs.c ++++ b/fs/nfsd/vfs.c +@@ -1186,6 +1186,7 @@ nfsd_create(struct svc_rqst *rqstp, stru + int type, dev_t rdev, struct svc_fh *resfhp) + { + struct dentry *dentry, *dchild = NULL; ++ struct svc_export *exp; + struct inode *dirp; + __be32 err; + __be32 err2; +@@ -1203,6 +1204,7 @@ nfsd_create(struct svc_rqst *rqstp, stru + goto out; + + dentry = fhp->fh_dentry; ++ exp = fhp->fh_export; + dirp = dentry->d_inode; + + err = nfserr_notdir; +@@ -1219,7 +1221,7 @@ nfsd_create(struct svc_rqst *rqstp, stru + host_err = PTR_ERR(dchild); + if (IS_ERR(dchild)) + goto out_nfserr; +- err = fh_compose(resfhp, fhp->fh_export, dchild, fhp); ++ err = fh_compose(resfhp, exp, dchild, fhp); + if (err) + goto out; + } else { +@@ -1258,7 +1260,7 @@ nfsd_create(struct svc_rqst *rqstp, stru + host_err = vfs_create(dirp, dchild, iap->ia_mode, NULL); + break; + case S_IFDIR: +- host_err = vfs_mkdir(dirp, dchild, iap->ia_mode); ++ host_err = vfs_mkdir(dirp, dchild, exp->ex_path.mnt, iap->ia_mode); + break; + case S_IFCHR: + case S_IFBLK: +@@ -1273,7 +1275,7 @@ nfsd_create(struct svc_rqst *rqstp, stru + if (host_err < 0) + goto out_nfserr; + +- if (EX_ISSYNC(fhp->fh_export)) { ++ if (EX_ISSYNC(exp)) { + err = nfserrno(nfsd_sync_dir(dentry)); + write_inode_now(dchild->d_inode, 1); + } +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -1076,7 +1076,7 @@ extern void unlock_super(struct super_bl + */ + extern int vfs_permission(struct nameidata *, int); + extern int vfs_create(struct inode *, struct dentry *, int, struct nameidata *); +-extern int vfs_mkdir(struct inode *, struct dentry *, int); ++extern int vfs_mkdir(struct inode *, struct dentry *, struct vfsmount *, int); + extern int vfs_mknod(struct inode *, struct dentry *, int, dev_t); + extern int vfs_symlink(struct inode *, struct dentry *, const char *, int); + extern int vfs_link(struct dentry *, struct inode *, struct dentry *); +--- a/kernel/cgroup.c ++++ b/kernel/cgroup.c +@@ -2827,7 +2827,7 @@ int cgroup_clone(struct task_struct *tsk + } + + /* Create the cgroup directory, which also creates the cgroup */ +- ret = vfs_mkdir(inode, dentry, S_IFDIR | 0755); ++ ret = vfs_mkdir(inode, dentry, NULL, S_IFDIR | 0755); + child = __d_cgrp(dentry); + dput(dentry); + if (ret) { diff --git a/kernel-patches/2.6.25/vfs-mknod.diff b/kernel-patches/2.6.25/vfs-mknod.diff new file mode 100644 index 000000000..19fd27a7f --- /dev/null +++ b/kernel-patches/2.6.25/vfs-mknod.diff @@ -0,0 +1,99 @@ +From: Tony Jones +Subject: Add a struct vfsmount parameter to vfs_mknod() + +The vfsmount will be passed down to the LSM hook so that LSMs can compute +pathnames. + +Signed-off-by: Tony Jones +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/ecryptfs/inode.c | 5 ++++- + fs/namei.c | 10 ++++++---- + fs/nfsd/vfs.c | 3 ++- + include/linux/fs.h | 2 +- + net/unix/af_unix.c | 3 ++- + 5 files changed, 15 insertions(+), 8 deletions(-) + +--- a/fs/ecryptfs/inode.c ++++ b/fs/ecryptfs/inode.c +@@ -538,11 +538,14 @@ ecryptfs_mknod(struct inode *dir, struct + { + int rc; + struct dentry *lower_dentry; ++ struct vfsmount *lower_mnt; + struct dentry *lower_dir_dentry; + + lower_dentry = ecryptfs_dentry_to_lower(dentry); ++ lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry); + lower_dir_dentry = lock_parent(lower_dentry); +- rc = vfs_mknod(lower_dir_dentry->d_inode, lower_dentry, mode, dev); ++ rc = vfs_mknod(lower_dir_dentry->d_inode, lower_dentry, lower_mnt, mode, ++ dev); + if (rc || !lower_dentry->d_inode) + goto out; + rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0); +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -1921,7 +1921,8 @@ fail: + } + EXPORT_SYMBOL_GPL(lookup_create); + +-int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) ++int vfs_mknod(struct inode *dir, struct dentry *dentry, struct vfsmount *mnt, ++ int mode, dev_t dev) + { + int error = may_create(dir, dentry, NULL); + +@@ -1973,11 +1974,12 @@ asmlinkage long sys_mknodat(int dfd, con + error = vfs_create(nd.path.dentry->d_inode,dentry,mode,&nd); + break; + case S_IFCHR: case S_IFBLK: +- error = vfs_mknod(nd.path.dentry->d_inode,dentry,mode, +- new_decode_dev(dev)); ++ error = vfs_mknod(nd.path.dentry->d_inode, dentry, ++ nd.path, mode, new_decode_dev(dev)); + break; + case S_IFIFO: case S_IFSOCK: +- error = vfs_mknod(nd.path.dentry->d_inode,dentry,mode,0); ++ error = vfs_mknod(nd.path.dentry->d_inode, dentry, ++ nd.path, mode, 0); + break; + case S_IFDIR: + error = -EPERM; +--- a/fs/nfsd/vfs.c ++++ b/fs/nfsd/vfs.c +@@ -1266,7 +1266,8 @@ nfsd_create(struct svc_rqst *rqstp, stru + case S_IFBLK: + case S_IFIFO: + case S_IFSOCK: +- host_err = vfs_mknod(dirp, dchild, iap->ia_mode, rdev); ++ host_err = vfs_mknod(dirp, dchild, exp->ex_path.mnt, ++ iap->ia_mode, rdev); + break; + default: + printk("nfsd: bad file type %o in nfsd_create\n", type); +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -1077,7 +1077,7 @@ extern void unlock_super(struct super_bl + extern int vfs_permission(struct nameidata *, int); + extern int vfs_create(struct inode *, struct dentry *, int, struct nameidata *); + extern int vfs_mkdir(struct inode *, struct dentry *, struct vfsmount *, int); +-extern int vfs_mknod(struct inode *, struct dentry *, int, dev_t); ++extern int vfs_mknod(struct inode *, struct dentry *, struct vfsmount *, int, dev_t); + extern int vfs_symlink(struct inode *, struct dentry *, const char *, int); + extern int vfs_link(struct dentry *, struct inode *, struct dentry *); + extern int vfs_rmdir(struct inode *, struct dentry *); +--- a/net/unix/af_unix.c ++++ b/net/unix/af_unix.c +@@ -819,7 +819,8 @@ static int unix_bind(struct socket *sock + */ + mode = S_IFSOCK | + (SOCK_INODE(sock)->i_mode & ~current->fs->umask); +- err = vfs_mknod(nd.path.dentry->d_inode, dentry, mode, 0); ++ err = vfs_mknod(nd.path.dentry->d_inode, dentry, nd.path.mnt, ++ mode, 0); + if (err) + goto out_mknod_dput; + mutex_unlock(&nd.path.dentry->d_inode->i_mutex); diff --git a/kernel-patches/2.6.25/vfs-notify_change.diff b/kernel-patches/2.6.25/vfs-notify_change.diff new file mode 100644 index 000000000..4417f114c --- /dev/null +++ b/kernel-patches/2.6.25/vfs-notify_change.diff @@ -0,0 +1,386 @@ +From: Tony Jones +Subject: Add a vfsmount parameter to notify_change() + +The vfsmount parameter must be set appropriately for files visibile +outside the kernel. Files that are only used in a filesystem (e.g., +reiserfs xattr files) will have a NULL vfsmount. + +Signed-off-by: Tony Jones +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/attr.c | 3 ++- + fs/ecryptfs/inode.c | 4 +++- + fs/exec.c | 3 ++- + fs/fat/file.c | 2 +- + fs/hpfs/namei.c | 2 +- + fs/namei.c | 2 +- + fs/nfsd/vfs.c | 8 ++++---- + fs/open.c | 28 +++++++++++++++------------- + fs/reiserfs/xattr.c | 6 +++--- + fs/sysfs/file.c | 2 +- + fs/utimes.c | 10 +++++----- + include/linux/fs.h | 6 +++--- + mm/filemap.c | 2 +- + mm/tiny-shmem.c | 2 +- + 14 files changed, 43 insertions(+), 37 deletions(-) + +--- a/fs/attr.c ++++ b/fs/attr.c +@@ -100,7 +100,8 @@ int inode_setattr(struct inode * inode, + } + EXPORT_SYMBOL(inode_setattr); + +-int notify_change(struct dentry * dentry, struct iattr * attr) ++int notify_change(struct dentry *dentry, struct vfsmount *mnt, ++ struct iattr *attr) + { + struct inode *inode = dentry->d_inode; + mode_t mode = inode->i_mode; +--- a/fs/ecryptfs/inode.c ++++ b/fs/ecryptfs/inode.c +@@ -848,6 +848,7 @@ static int ecryptfs_setattr(struct dentr + { + int rc = 0; + struct dentry *lower_dentry; ++ struct vfsmount *lower_mnt; + struct inode *inode; + struct inode *lower_inode; + struct ecryptfs_crypt_stat *crypt_stat; +@@ -858,6 +859,7 @@ static int ecryptfs_setattr(struct dentr + inode = dentry->d_inode; + lower_inode = ecryptfs_inode_to_lower(inode); + lower_dentry = ecryptfs_dentry_to_lower(dentry); ++ lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry); + mutex_lock(&crypt_stat->cs_mutex); + if (S_ISDIR(dentry->d_inode->i_mode)) + crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED); +@@ -908,7 +910,7 @@ static int ecryptfs_setattr(struct dentr + if (ia->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) + ia->ia_valid &= ~ATTR_MODE; + +- rc = notify_change(lower_dentry, ia); ++ rc = notify_change(lower_dentry, lower_mnt, ia); + out: + fsstack_copy_attr_all(inode, lower_inode, NULL); + return rc; +--- a/fs/exec.c ++++ b/fs/exec.c +@@ -1777,7 +1777,8 @@ int do_coredump(long signr, int exit_cod + goto close_fail; + if (!file->f_op->write) + goto close_fail; +- if (!ispipe && do_truncate(file->f_path.dentry, 0, 0, file) != 0) ++ if (!ispipe && ++ do_truncate(file->f_path.dentry, file->f_path.mnt, 0, 0, file) != 0) + goto close_fail; + + retval = binfmt->core_dump(signr, regs, file, core_limit); +--- a/fs/fat/file.c ++++ b/fs/fat/file.c +@@ -92,7 +92,7 @@ int fat_generic_ioctl(struct inode *inod + } + + /* This MUST be done before doing anything irreversible... */ +- err = notify_change(filp->f_path.dentry, &ia); ++ err = notify_change(filp->f_path.dentry, filp->f_path.mnt, &ia); + if (err) + goto up; + +--- a/fs/hpfs/namei.c ++++ b/fs/hpfs/namei.c +@@ -426,7 +426,7 @@ again: + /*printk("HPFS: truncating file before delete.\n");*/ + newattrs.ia_size = 0; + newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME; +- err = notify_change(dentry, &newattrs); ++ err = notify_change(dentry, NULL, &newattrs); + put_write_access(inode); + if (!err) + goto again; +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -1663,7 +1663,7 @@ int may_open(struct nameidata *nd, int a + if (!error) { + DQUOT_INIT(inode); + +- error = do_truncate(dentry, 0, ++ error = do_truncate(dentry, nd->path.mnt, 0, + ATTR_MTIME|ATTR_CTIME|ATTR_OPEN, + NULL); + } +--- a/fs/nfsd/vfs.c ++++ b/fs/nfsd/vfs.c +@@ -388,7 +388,7 @@ nfsd_setattr(struct svc_rqst *rqstp, str + err = nfserr_notsync; + if (!check_guard || guardtime == inode->i_ctime.tv_sec) { + fh_lock(fhp); +- host_err = notify_change(dentry, iap); ++ host_err = notify_change(dentry, fhp->fh_export->ex_path.mnt, iap); + err = nfserrno(host_err); + fh_unlock(fhp); + } +@@ -944,13 +944,13 @@ out: + return err; + } + +-static void kill_suid(struct dentry *dentry) ++static void kill_suid(struct dentry *dentry, struct vfsmount *mnt) + { + struct iattr ia; + ia.ia_valid = ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV; + + mutex_lock(&dentry->d_inode->i_mutex); +- notify_change(dentry, &ia); ++ notify_change(dentry, mnt, &ia); + mutex_unlock(&dentry->d_inode->i_mutex); + } + +@@ -1009,7 +1009,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, s + + /* clear setuid/setgid flag after write */ + if (host_err >= 0 && (inode->i_mode & (S_ISUID | S_ISGID))) +- kill_suid(dentry); ++ kill_suid(dentry, exp->ex_path.mnt); + + if (host_err >= 0 && stable) { + static ino_t last_ino; +--- a/fs/open.c ++++ b/fs/open.c +@@ -194,8 +194,8 @@ out: + return error; + } + +-int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs, +- struct file *filp) ++int do_truncate(struct dentry *dentry, struct vfsmount *mnt, loff_t length, ++ unsigned int time_attrs, struct file *filp) + { + int err; + struct iattr newattrs; +@@ -215,7 +215,7 @@ int do_truncate(struct dentry *dentry, l + newattrs.ia_valid |= should_remove_suid(dentry); + + mutex_lock(&dentry->d_inode->i_mutex); +- err = notify_change(dentry, &newattrs); ++ err = notify_change(dentry, mnt, &newattrs); + mutex_unlock(&dentry->d_inode->i_mutex); + return err; + } +@@ -271,7 +271,7 @@ static long do_sys_truncate(const char _ + error = locks_verify_truncate(inode, NULL, length); + if (!error) { + DQUOT_INIT(inode); +- error = do_truncate(nd.path.dentry, length, 0, NULL); ++ error = do_truncate(nd.path.dentry, nd.path.mnt, length, 0, NULL); + } + + put_write_and_out: +@@ -324,7 +324,8 @@ static long do_sys_ftruncate(unsigned in + + error = locks_verify_truncate(inode, file, length); + if (!error) +- error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, file); ++ error = do_truncate(dentry, file->f_path.mnt, length, ++ ATTR_MTIME|ATTR_CTIME, file); + out_putf: + fput(file); + out: +@@ -578,7 +579,7 @@ asmlinkage long sys_fchmod(unsigned int + mode = inode->i_mode; + newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); + newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; +- err = notify_change(dentry, &newattrs); ++ err = notify_change(dentry, file->f_path.mnt, &newattrs); + mutex_unlock(&inode->i_mutex); + + out_putf: +@@ -613,7 +614,7 @@ asmlinkage long sys_fchmodat(int dfd, co + mode = inode->i_mode; + newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); + newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; +- error = notify_change(nd.path.dentry, &newattrs); ++ error = notify_change(nd.path.dentry, nd.path.mnt, &newattrs); + mutex_unlock(&inode->i_mutex); + + dput_and_out: +@@ -627,7 +628,8 @@ asmlinkage long sys_chmod(const char __u + return sys_fchmodat(AT_FDCWD, filename, mode); + } + +-static int chown_common(struct dentry * dentry, uid_t user, gid_t group) ++static int chown_common(struct dentry * dentry, struct vfsmount *mnt, ++ uid_t user, gid_t group) + { + struct inode * inode; + int error; +@@ -657,7 +659,7 @@ static int chown_common(struct dentry * + newattrs.ia_valid |= + ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV; + mutex_lock(&inode->i_mutex); +- error = notify_change(dentry, &newattrs); ++ error = notify_change(dentry, mnt, &newattrs); + mutex_unlock(&inode->i_mutex); + out: + return error; +@@ -671,7 +673,7 @@ asmlinkage long sys_chown(const char __u + error = user_path_walk(filename, &nd); + if (error) + goto out; +- error = chown_common(nd.path.dentry, user, group); ++ error = chown_common(nd.path.dentry, nd.path.mnt, user, group); + path_put(&nd.path); + out: + return error; +@@ -691,7 +693,7 @@ asmlinkage long sys_fchownat(int dfd, co + error = __user_walk_fd(dfd, filename, follow, &nd); + if (error) + goto out; +- error = chown_common(nd.path.dentry, user, group); ++ error = chown_common(nd.path.dentry, nd.path.mnt, user, group); + path_put(&nd.path); + out: + return error; +@@ -705,7 +707,7 @@ asmlinkage long sys_lchown(const char __ + error = user_path_walk_link(filename, &nd); + if (error) + goto out; +- error = chown_common(nd.path.dentry, user, group); ++ error = chown_common(nd.path.dentry, nd.path.mnt, user, group); + path_put(&nd.path); + out: + return error; +@@ -724,7 +726,7 @@ asmlinkage long sys_fchown(unsigned int + + dentry = file->f_path.dentry; + audit_inode(NULL, dentry); +- error = chown_common(dentry, user, group); ++ error = chown_common(dentry, file->f_path.mnt, user, group); + fput(file); + out: + return error; +--- a/fs/reiserfs/xattr.c ++++ b/fs/reiserfs/xattr.c +@@ -460,7 +460,7 @@ reiserfs_xattr_set(struct inode *inode, + newattrs.ia_size = buffer_size; + newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME; + mutex_lock_nested(&xinode->i_mutex, I_MUTEX_XATTR); +- err = notify_change(dentry, &newattrs); ++ err = notify_change(dentry, NULL, &newattrs); + if (err) + goto out_filp; + +@@ -791,7 +791,7 @@ reiserfs_chown_xattrs_filler(void *buf, + } + + if (!S_ISDIR(xafile->d_inode->i_mode)) +- err = notify_change(xafile, attrs); ++ err = notify_change(xafile, NULL, attrs); + dput(xafile); + + return err; +@@ -835,7 +835,7 @@ int reiserfs_chown_xattrs(struct inode * + goto out_dir; + } + +- err = notify_change(dir, attrs); ++ err = notify_change(dir, NULL, attrs); + unlock_kernel(); + + out_dir: +--- a/fs/sysfs/file.c ++++ b/fs/sysfs/file.c +@@ -579,7 +579,7 @@ int sysfs_chmod_file(struct kobject *kob + + newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); + newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; +- rc = notify_change(victim, &newattrs); ++ rc = notify_change(victim, NULL, &newattrs); + + if (rc == 0) { + mutex_lock(&sysfs_mutex); +--- a/fs/utimes.c ++++ b/fs/utimes.c +@@ -55,7 +55,7 @@ long do_utimes(int dfd, char __user *fil + { + int error; + struct nameidata nd; +- struct dentry *dentry; ++ struct path path; + struct inode *inode; + struct iattr newattrs; + struct file *f = NULL; +@@ -78,16 +78,16 @@ long do_utimes(int dfd, char __user *fil + f = fget(dfd); + if (!f) + goto out; +- dentry = f->f_path.dentry; ++ path = f->f_path; + } else { + error = __user_walk_fd(dfd, filename, (flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW, &nd); + if (error) + goto out; + +- dentry = nd.path.dentry; ++ path = nd.path; + } + +- inode = dentry->d_inode; ++ inode = path.dentry->d_inode; + + error = -EROFS; + if (IS_RDONLY(inode)) +@@ -132,7 +132,7 @@ long do_utimes(int dfd, char __user *fil + } + } + mutex_lock(&inode->i_mutex); +- error = notify_change(dentry, &newattrs); ++ error = notify_change(path.dentry, path.mnt, &newattrs); + mutex_unlock(&inode->i_mutex); + dput_and_out: + if (f) +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -1559,8 +1559,8 @@ static inline int break_lease(struct ino + + /* fs/open.c */ + +-extern int do_truncate(struct dentry *, loff_t start, unsigned int time_attrs, +- struct file *filp); ++extern int do_truncate(struct dentry *, struct vfsmount *, loff_t start, ++ unsigned int time_attrs, struct file *filp); + extern long do_sys_open(int dfd, const char __user *filename, int flags, + int mode); + extern struct file *filp_open(const char *, int, int); +@@ -1714,7 +1714,7 @@ extern int do_remount_sb(struct super_bl + #ifdef CONFIG_BLOCK + extern sector_t bmap(struct inode *, sector_t); + #endif +-extern int notify_change(struct dentry *, struct iattr *); ++extern int notify_change(struct dentry *, struct vfsmount *, struct iattr *); + extern int permission(struct inode *, int, struct nameidata *); + extern int generic_permission(struct inode *, int, + int (*check_acl)(struct inode *, int)); +--- a/mm/filemap.c ++++ b/mm/filemap.c +@@ -1658,7 +1658,7 @@ int __remove_suid(struct path *path, int + struct iattr newattrs; + + newattrs.ia_valid = ATTR_FORCE | kill; +- return notify_change(path->dentry, &newattrs); ++ return notify_change(path->dentry, path->mnt, &newattrs); + } + + int remove_suid(struct path *path) +--- a/mm/tiny-shmem.c ++++ b/mm/tiny-shmem.c +@@ -80,7 +80,7 @@ struct file *shmem_file_setup(char *name + inode->i_nlink = 0; /* It is unlinked */ + + /* notify everyone as to the change of file size */ +- error = do_truncate(dentry, size, 0, file); ++ error = do_truncate(dentry, file->f_path.mnt, size, 0, file); + if (error < 0) + goto close_file; + diff --git a/kernel-patches/2.6.25/vfs-removexattr.diff b/kernel-patches/2.6.25/vfs-removexattr.diff new file mode 100644 index 000000000..e9bc9e631 --- /dev/null +++ b/kernel-patches/2.6.25/vfs-removexattr.diff @@ -0,0 +1,111 @@ +From: Tony Jones +Subject: Add a struct vfsmount parameter to vfs_removexattr() + +The vfsmount will be passed down to the LSM hook so that LSMs can compute +pathnames. + +Signed-off-by: Tony Jones +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/nfsd/vfs.c | 7 ++++--- + fs/xattr.c | 12 ++++++------ + include/linux/xattr.h | 2 +- + 3 files changed, 11 insertions(+), 10 deletions(-) + +--- a/fs/nfsd/vfs.c ++++ b/fs/nfsd/vfs.c +@@ -2026,6 +2026,7 @@ nfsd_get_posix_acl(struct svc_fh *fhp, i + int + nfsd_set_posix_acl(struct svc_fh *fhp, int type, struct posix_acl *acl) + { ++ struct vfsmount *mnt; + struct inode *inode = fhp->fh_dentry->d_inode; + char *name; + void *value = NULL; +@@ -2058,14 +2059,14 @@ nfsd_set_posix_acl(struct svc_fh *fhp, i + } else + size = 0; + ++ mnt = fhp->fh_export->ex_path.mnt; + if (size) +- error = vfs_setxattr(fhp->fh_dentry, fhp->fh_export->ex_path.mnt, +- name, value, size,0); ++ error = vfs_setxattr(fhp->fh_dentry, mnt, name, value, size,0); + else { + if (!S_ISDIR(inode->i_mode) && type == ACL_TYPE_DEFAULT) + error = 0; + else { +- error = vfs_removexattr(fhp->fh_dentry, name); ++ error = vfs_removexattr(fhp->fh_dentry, mnt, name); + if (error == -ENODATA) + error = 0; + } +--- a/fs/xattr.c ++++ b/fs/xattr.c +@@ -191,7 +191,7 @@ vfs_listxattr(struct dentry *dentry, str + EXPORT_SYMBOL_GPL(vfs_listxattr); + + int +-vfs_removexattr(struct dentry *dentry, char *name) ++vfs_removexattr(struct dentry *dentry, struct vfsmount *mnt, char *name) + { + struct inode *inode = dentry->d_inode; + int error; +@@ -464,7 +464,7 @@ sys_flistxattr(int fd, char __user *list + * Extended attribute REMOVE operations + */ + static long +-removexattr(struct dentry *d, char __user *name) ++removexattr(struct dentry *dentry, struct vfsmount *mnt, char __user *name) + { + int error; + char kname[XATTR_NAME_MAX + 1]; +@@ -475,7 +475,7 @@ removexattr(struct dentry *d, char __use + if (error < 0) + return error; + +- return vfs_removexattr(d, kname); ++ return vfs_removexattr(dentry, mnt, kname); + } + + asmlinkage long +@@ -487,7 +487,7 @@ sys_removexattr(char __user *path, char + error = user_path_walk(path, &nd); + if (error) + return error; +- error = removexattr(nd.path.dentry, name); ++ error = removexattr(nd.path.dentry, nd.path.mnt, name); + path_put(&nd.path); + return error; + } +@@ -501,7 +501,7 @@ sys_lremovexattr(char __user *path, char + error = user_path_walk_link(path, &nd); + if (error) + return error; +- error = removexattr(nd.path.dentry, name); ++ error = removexattr(nd.path.dentry, nd.path.mnt, name); + path_put(&nd.path); + return error; + } +@@ -518,7 +518,7 @@ sys_fremovexattr(int fd, char __user *na + return error; + dentry = f->f_path.dentry; + audit_inode(NULL, dentry); +- error = removexattr(dentry, name); ++ error = removexattr(dentry, f->f_path.mnt, name); + fput(f); + return error; + } +--- a/include/linux/xattr.h ++++ b/include/linux/xattr.h +@@ -51,7 +51,7 @@ ssize_t vfs_getxattr(struct dentry *, st + ssize_t vfs_listxattr(struct dentry *d, struct vfsmount *, char *list, size_t size); + int vfs_setxattr(struct dentry *, struct vfsmount *, char *, void *, size_t, + int); +-int vfs_removexattr(struct dentry *, char *); ++int vfs_removexattr(struct dentry *, struct vfsmount *, char *); + + ssize_t generic_getxattr(struct dentry *dentry, const char *name, void *buffer, size_t size); + ssize_t generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size); diff --git a/kernel-patches/2.6.25/vfs-rename.diff b/kernel-patches/2.6.25/vfs-rename.diff new file mode 100644 index 000000000..a24bfbcee --- /dev/null +++ b/kernel-patches/2.6.25/vfs-rename.diff @@ -0,0 +1,125 @@ +From: Tony Jones +Subject: Add struct vfsmount parameters to vfs_rename() + +The vfsmount will be passed down to the LSM hook so that LSMs can compute +pathnames. + +Signed-off-by: Tony Jones +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/ecryptfs/inode.c | 7 ++++++- + fs/namei.c | 19 ++++++++++++------- + fs/nfsd/vfs.c | 3 ++- + include/linux/fs.h | 2 +- + 4 files changed, 21 insertions(+), 10 deletions(-) + +--- a/fs/ecryptfs/inode.c ++++ b/fs/ecryptfs/inode.c +@@ -576,19 +576,24 @@ ecryptfs_rename(struct inode *old_dir, s + { + int rc; + struct dentry *lower_old_dentry; ++ struct vfsmount *lower_old_mnt; + struct dentry *lower_new_dentry; ++ struct vfsmount *lower_new_mnt; + struct dentry *lower_old_dir_dentry; + struct dentry *lower_new_dir_dentry; + + lower_old_dentry = ecryptfs_dentry_to_lower(old_dentry); ++ lower_old_mnt = ecryptfs_dentry_to_lower_mnt(old_dentry); + lower_new_dentry = ecryptfs_dentry_to_lower(new_dentry); ++ lower_new_mnt = ecryptfs_dentry_to_lower_mnt(new_dentry); + dget(lower_old_dentry); + dget(lower_new_dentry); + lower_old_dir_dentry = dget_parent(lower_old_dentry); + lower_new_dir_dentry = dget_parent(lower_new_dentry); + lock_rename(lower_old_dir_dentry, lower_new_dir_dentry); + rc = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry, +- lower_new_dir_dentry->d_inode, lower_new_dentry); ++ lower_old_mnt, lower_new_dir_dentry->d_inode, ++ lower_new_dentry, lower_new_mnt); + if (rc) + goto out_lock; + fsstack_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode, NULL); +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -2468,7 +2468,8 @@ asmlinkage long sys_link(const char __us + * locking]. + */ + static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry, +- struct inode *new_dir, struct dentry *new_dentry) ++ struct vfsmount *old_mnt, struct inode *new_dir, ++ struct dentry *new_dentry, struct vfsmount *new_mnt) + { + int error = 0; + struct inode *target; +@@ -2511,7 +2512,8 @@ static int vfs_rename_dir(struct inode * + } + + static int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry, +- struct inode *new_dir, struct dentry *new_dentry) ++ struct vfsmount *old_mnt, struct inode *new_dir, ++ struct dentry *new_dentry, struct vfsmount *new_mnt) + { + struct inode *target; + int error; +@@ -2539,7 +2541,8 @@ static int vfs_rename_other(struct inode + } + + int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, +- struct inode *new_dir, struct dentry *new_dentry) ++ struct vfsmount *old_mnt, struct inode *new_dir, ++ struct dentry *new_dentry, struct vfsmount *new_mnt) + { + int error; + int is_dir = S_ISDIR(old_dentry->d_inode->i_mode); +@@ -2568,9 +2571,11 @@ int vfs_rename(struct inode *old_dir, st + old_name = fsnotify_oldname_init(old_dentry->d_name.name); + + if (is_dir) +- error = vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry); ++ error = vfs_rename_dir(old_dir, old_dentry, old_mnt, ++ new_dir, new_dentry, new_mnt); + else +- error = vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry); ++ error = vfs_rename_other(old_dir, old_dentry, old_mnt, ++ new_dir, new_dentry, new_mnt); + if (!error) { + const char *new_name = old_dentry->d_name.name; + fsnotify_move(old_dir, new_dir, old_name, new_name, is_dir, +@@ -2642,8 +2647,8 @@ static int do_rename(int olddfd, const c + if (new_dentry == trap) + goto exit5; + +- error = vfs_rename(old_dir->d_inode, old_dentry, +- new_dir->d_inode, new_dentry); ++ error = vfs_rename(old_dir->d_inode, old_dentry, oldnd.path.mnt, ++ new_dir->d_inode, new_dentry, newnd.path.mnt); + exit5: + dput(new_dentry); + exit4: +--- a/fs/nfsd/vfs.c ++++ b/fs/nfsd/vfs.c +@@ -1693,7 +1693,8 @@ nfsd_rename(struct svc_rqst *rqstp, stru + host_err = -EPERM; + } else + #endif +- host_err = vfs_rename(fdir, odentry, tdir, ndentry); ++ host_err = vfs_rename(fdir, odentry, ffhp->fh_export->ex_path.mnt, ++ tdir, ndentry, tfhp->fh_export->ex_path.mnt); + if (!host_err && EX_ISSYNC(tfhp->fh_export)) { + host_err = nfsd_sync_dir(tdentry); + if (!host_err) +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -1082,7 +1082,7 @@ extern int vfs_symlink(struct inode *, s + extern int vfs_link(struct dentry *, struct vfsmount *, struct inode *, struct dentry *, struct vfsmount *); + extern int vfs_rmdir(struct inode *, struct dentry *, struct vfsmount *); + extern int vfs_unlink(struct inode *, struct dentry *, struct vfsmount *); +-extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *); ++extern int vfs_rename(struct inode *, struct dentry *, struct vfsmount *, struct inode *, struct dentry *, struct vfsmount *); + + /* + * VFS dentry helper functions. diff --git a/kernel-patches/2.6.25/vfs-rmdir.diff b/kernel-patches/2.6.25/vfs-rmdir.diff new file mode 100644 index 000000000..6057aea6e --- /dev/null +++ b/kernel-patches/2.6.25/vfs-rmdir.diff @@ -0,0 +1,135 @@ +From: Tony Jones +Subject: Add a struct vfsmount parameter to vfs_rmdir() + +The vfsmount will be passed down to the LSM hook so that LSMs can compute +pathnames. + +Signed-off-by: Tony Jones +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/ecryptfs/inode.c | 4 +++- + fs/namei.c | 4 ++-- + fs/nfsd/nfs4recover.c | 2 +- + fs/nfsd/vfs.c | 8 +++++--- + fs/reiserfs/xattr.c | 2 +- + include/linux/fs.h | 2 +- + 6 files changed, 13 insertions(+), 9 deletions(-) + +--- a/fs/ecryptfs/inode.c ++++ b/fs/ecryptfs/inode.c +@@ -520,14 +520,16 @@ out: + static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry) + { + struct dentry *lower_dentry; ++ struct vfsmount *lower_mnt; + struct dentry *lower_dir_dentry; + int rc; + + lower_dentry = ecryptfs_dentry_to_lower(dentry); ++ lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry); + dget(dentry); + lower_dir_dentry = lock_parent(lower_dentry); + dget(lower_dentry); +- rc = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry); ++ rc = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry, lower_mnt); + dput(lower_dentry); + if (!rc) + d_delete(lower_dentry); +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -2090,7 +2090,7 @@ void dentry_unhash(struct dentry *dentry + spin_unlock(&dcache_lock); + } + +-int vfs_rmdir(struct inode *dir, struct dentry *dentry) ++int vfs_rmdir(struct inode *dir, struct dentry *dentry,struct vfsmount *mnt) + { + int error = may_delete(dir, dentry, 1); + +@@ -2154,7 +2154,7 @@ static long do_rmdir(int dfd, const char + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) + goto exit2; +- error = vfs_rmdir(nd.path.dentry->d_inode, dentry); ++ error = vfs_rmdir(nd.path.dentry->d_inode, dentry, nd.path.mnt); + dput(dentry); + exit2: + mutex_unlock(&nd.path.dentry->d_inode->i_mutex); +--- a/fs/nfsd/nfs4recover.c ++++ b/fs/nfsd/nfs4recover.c +@@ -274,7 +274,7 @@ nfsd4_clear_clid_dir(struct dentry *dir, + * a kernel from the future.... */ + nfsd4_list_rec_dir(dentry, nfsd4_remove_clid_file); + mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); +- status = vfs_rmdir(dir->d_inode, dentry); ++ status = vfs_rmdir(dir->d_inode, dentry, rec_dir.path.mnt); + mutex_unlock(&dir->d_inode->i_mutex); + return status; + } +--- a/fs/nfsd/vfs.c ++++ b/fs/nfsd/vfs.c +@@ -1729,6 +1729,7 @@ nfsd_unlink(struct svc_rqst *rqstp, stru + char *fname, int flen) + { + struct dentry *dentry, *rdentry; ++ struct svc_export *exp; + struct inode *dirp; + __be32 err; + int host_err; +@@ -1743,6 +1744,7 @@ nfsd_unlink(struct svc_rqst *rqstp, stru + fh_lock_nested(fhp, I_MUTEX_PARENT); + dentry = fhp->fh_dentry; + dirp = dentry->d_inode; ++ exp = fhp->fh_export; + + rdentry = lookup_one_len(fname, dentry, flen); + host_err = PTR_ERR(rdentry); +@@ -1760,21 +1762,21 @@ nfsd_unlink(struct svc_rqst *rqstp, stru + + if (type != S_IFDIR) { /* It's UNLINK */ + #ifdef MSNFS +- if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) && ++ if ((exp->ex_flags & NFSEXP_MSNFS) && + (atomic_read(&rdentry->d_count) > 1)) { + host_err = -EPERM; + } else + #endif + host_err = vfs_unlink(dirp, rdentry); + } else { /* It's RMDIR */ +- host_err = vfs_rmdir(dirp, rdentry); ++ host_err = vfs_rmdir(dirp, rdentry, exp->ex_path.mnt); + } + + dput(rdentry); + + if (host_err) + goto out_nfserr; +- if (EX_ISSYNC(fhp->fh_export)) ++ if (EX_ISSYNC(exp)) + host_err = nfsd_sync_dir(dentry); + + out_nfserr: +--- a/fs/reiserfs/xattr.c ++++ b/fs/reiserfs/xattr.c +@@ -747,7 +747,7 @@ int reiserfs_delete_xattrs(struct inode + if (dir->d_inode->i_nlink <= 2) { + root = get_xa_root(inode->i_sb, XATTR_REPLACE); + reiserfs_write_lock_xattrs(inode->i_sb); +- err = vfs_rmdir(root->d_inode, dir); ++ err = vfs_rmdir(root->d_inode, dir, NULL); + reiserfs_write_unlock_xattrs(inode->i_sb); + dput(root); + } else { +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -1080,7 +1080,7 @@ extern int vfs_mkdir(struct inode *, str + extern int vfs_mknod(struct inode *, struct dentry *, struct vfsmount *, int, dev_t); + extern int vfs_symlink(struct inode *, struct dentry *, struct vfsmount *, const char *, int); + extern int vfs_link(struct dentry *, struct vfsmount *, struct inode *, struct dentry *, struct vfsmount *); +-extern int vfs_rmdir(struct inode *, struct dentry *); ++extern int vfs_rmdir(struct inode *, struct dentry *, struct vfsmount *); + extern int vfs_unlink(struct inode *, struct dentry *); + extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *); + diff --git a/kernel-patches/2.6.25/vfs-setxattr.diff b/kernel-patches/2.6.25/vfs-setxattr.diff new file mode 100644 index 000000000..56795233d --- /dev/null +++ b/kernel-patches/2.6.25/vfs-setxattr.diff @@ -0,0 +1,152 @@ +From: Tony Jones +Subject: Add a struct vfsmount parameter to vfs_setxattr() + +The vfsmount will be passed down to the LSM hook so that LSMs can compute +pathnames. + +Signed-off-by: Tony Jones +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/nfsd/vfs.c | 16 +++++++++++----- + fs/xattr.c | 16 ++++++++-------- + include/linux/xattr.h | 3 ++- + 3 files changed, 21 insertions(+), 14 deletions(-) + +--- a/fs/nfsd/vfs.c ++++ b/fs/nfsd/vfs.c +@@ -426,7 +426,8 @@ static ssize_t nfsd_getxattr(struct dent + + #if defined(CONFIG_NFSD_V4) + static int +-set_nfsv4_acl_one(struct dentry *dentry, struct posix_acl *pacl, char *key) ++set_nfsv4_acl_one(struct dentry *dentry, struct vfsmount *mnt, ++ struct posix_acl *pacl, char *key) + { + int len; + size_t buflen; +@@ -445,7 +446,7 @@ set_nfsv4_acl_one(struct dentry *dentry, + goto out; + } + +- error = vfs_setxattr(dentry, key, buf, len, 0); ++ error = vfs_setxattr(dentry, mnt, key, buf, len, 0); + out: + kfree(buf); + return error; +@@ -458,6 +459,7 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqst + __be32 error; + int host_error; + struct dentry *dentry; ++ struct vfsmount *mnt; + struct inode *inode; + struct posix_acl *pacl = NULL, *dpacl = NULL; + unsigned int flags = 0; +@@ -468,6 +470,7 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqst + return error; + + dentry = fhp->fh_dentry; ++ mnt = fhp->fh_export->ex_path.mnt; + inode = dentry->d_inode; + if (S_ISDIR(inode->i_mode)) + flags = NFS4_ACL_DIR; +@@ -478,12 +481,14 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqst + } else if (host_error < 0) + goto out_nfserr; + +- host_error = set_nfsv4_acl_one(dentry, pacl, POSIX_ACL_XATTR_ACCESS); ++ host_error = set_nfsv4_acl_one(dentry, mnt, pacl, ++ POSIX_ACL_XATTR_ACCESS); + if (host_error < 0) + goto out_release; + + if (S_ISDIR(inode->i_mode)) +- host_error = set_nfsv4_acl_one(dentry, dpacl, POSIX_ACL_XATTR_DEFAULT); ++ host_error = set_nfsv4_acl_one(dentry, mnt, dpacl, ++ POSIX_ACL_XATTR_DEFAULT); + + out_release: + posix_acl_release(pacl); +@@ -2051,7 +2056,8 @@ nfsd_set_posix_acl(struct svc_fh *fhp, i + size = 0; + + if (size) +- error = vfs_setxattr(fhp->fh_dentry, name, value, size, 0); ++ error = vfs_setxattr(fhp->fh_dentry, fhp->fh_export->ex_path.mnt, ++ name, value, size,0); + else { + if (!S_ISDIR(inode->i_mode) && type == ACL_TYPE_DEFAULT) + error = 0; +--- a/fs/xattr.c ++++ b/fs/xattr.c +@@ -68,8 +68,8 @@ xattr_permission(struct inode *inode, co + } + + int +-vfs_setxattr(struct dentry *dentry, char *name, void *value, +- size_t size, int flags) ++vfs_setxattr(struct dentry *dentry, struct vfsmount *mnt, char *name, ++ void *value, size_t size, int flags) + { + struct inode *inode = dentry->d_inode; + int error; +@@ -219,8 +219,8 @@ EXPORT_SYMBOL_GPL(vfs_removexattr); + * Extended attribute SET operations + */ + static long +-setxattr(struct dentry *d, char __user *name, void __user *value, +- size_t size, int flags) ++setxattr(struct dentry *dentry, struct vfsmount *mnt, char __user *name, ++ void __user *value, size_t size, int flags) + { + int error; + void *kvalue = NULL; +@@ -247,7 +247,7 @@ setxattr(struct dentry *d, char __user * + } + } + +- error = vfs_setxattr(d, kname, kvalue, size, flags); ++ error = vfs_setxattr(dentry, mnt, kname, kvalue, size, flags); + kfree(kvalue); + return error; + } +@@ -262,7 +262,7 @@ sys_setxattr(char __user *path, char __u + error = user_path_walk(path, &nd); + if (error) + return error; +- error = setxattr(nd.path.dentry, name, value, size, flags); ++ error = setxattr(nd.path.dentry, nd.path.mnt, name, value, size, flags); + path_put(&nd.path); + return error; + } +@@ -277,7 +277,7 @@ sys_lsetxattr(char __user *path, char __ + error = user_path_walk_link(path, &nd); + if (error) + return error; +- error = setxattr(nd.path.dentry, name, value, size, flags); ++ error = setxattr(nd.path.dentry, nd.path.mnt, name, value, size, flags); + path_put(&nd.path); + return error; + } +@@ -295,7 +295,7 @@ sys_fsetxattr(int fd, char __user *name, + return error; + dentry = f->f_path.dentry; + audit_inode(NULL, dentry); +- error = setxattr(dentry, name, value, size, flags); ++ error = setxattr(dentry, f->f_vfsmnt, name, value, size, flags); + fput(f); + return error; + } +--- a/include/linux/xattr.h ++++ b/include/linux/xattr.h +@@ -49,7 +49,8 @@ struct xattr_handler { + ssize_t xattr_getsecurity(struct inode *, const char *, void *, size_t); + ssize_t vfs_getxattr(struct dentry *, char *, void *, size_t); + ssize_t vfs_listxattr(struct dentry *d, char *list, size_t size); +-int vfs_setxattr(struct dentry *, char *, void *, size_t, int); ++int vfs_setxattr(struct dentry *, struct vfsmount *, char *, void *, size_t, ++ int); + int vfs_removexattr(struct dentry *, char *); + + ssize_t generic_getxattr(struct dentry *dentry, const char *name, void *buffer, size_t size); diff --git a/kernel-patches/2.6.25/vfs-symlink.diff b/kernel-patches/2.6.25/vfs-symlink.diff new file mode 100644 index 000000000..386842844 --- /dev/null +++ b/kernel-patches/2.6.25/vfs-symlink.diff @@ -0,0 +1,122 @@ +From: Tony Jones +Subject: Add a struct vfsmount parameter to vfs_symlink() + +The vfsmount will be passed down to the LSM hook so that LSMs can compute +pathnames. + +Signed-off-by: Tony Jones +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/ecryptfs/inode.c | 4 +++- + fs/namei.c | 6 ++++-- + fs/nfsd/vfs.c | 12 ++++++++---- + include/linux/fs.h | 2 +- + 4 files changed, 16 insertions(+), 8 deletions(-) + +--- a/fs/ecryptfs/inode.c ++++ b/fs/ecryptfs/inode.c +@@ -448,6 +448,7 @@ static int ecryptfs_symlink(struct inode + { + int rc; + struct dentry *lower_dentry; ++ struct vfsmount *lower_mnt; + struct dentry *lower_dir_dentry; + umode_t mode; + char *encoded_symname; +@@ -456,6 +457,7 @@ static int ecryptfs_symlink(struct inode + + lower_dentry = ecryptfs_dentry_to_lower(dentry); + dget(lower_dentry); ++ lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry); + lower_dir_dentry = lock_parent(lower_dentry); + mode = S_IALLUGO; + encoded_symlen = ecryptfs_encode_filename(crypt_stat, symname, +@@ -465,7 +467,7 @@ static int ecryptfs_symlink(struct inode + rc = encoded_symlen; + goto out_lock; + } +- rc = vfs_symlink(lower_dir_dentry->d_inode, lower_dentry, ++ rc = vfs_symlink(lower_dir_dentry->d_inode, lower_dentry, lower_mnt, + encoded_symname, mode); + kfree(encoded_symname); + if (rc || !lower_dentry->d_inode) +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -2270,7 +2270,8 @@ asmlinkage long sys_unlink(const char __ + return do_unlinkat(AT_FDCWD, pathname); + } + +-int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname, int mode) ++int vfs_symlink(struct inode *dir, struct dentry *dentry, struct vfsmount *mnt, ++ const char *oldname, int mode) + { + int error = may_create(dir, dentry, NULL); + +@@ -2316,7 +2317,8 @@ asmlinkage long sys_symlinkat(const char + if (IS_ERR(dentry)) + goto out_unlock; + +- error = vfs_symlink(nd.path.dentry->d_inode, dentry, from, S_IALLUGO); ++ error = vfs_symlink(nd.path.dentry->d_inode, dentry, nd.path.mnt, from, ++ S_IALLUGO); + dput(dentry); + out_unlock: + mutex_unlock(&nd.path.dentry->d_inode->i_mutex); +--- a/fs/nfsd/vfs.c ++++ b/fs/nfsd/vfs.c +@@ -1499,6 +1499,7 @@ nfsd_symlink(struct svc_rqst *rqstp, str + struct iattr *iap) + { + struct dentry *dentry, *dnew; ++ struct svc_export *exp; + __be32 err, cerr; + int host_err; + umode_t mode; +@@ -1525,6 +1526,7 @@ nfsd_symlink(struct svc_rqst *rqstp, str + if (iap && (iap->ia_valid & ATTR_MODE)) + mode = iap->ia_mode & S_IALLUGO; + ++ exp = fhp->fh_export; + if (unlikely(path[plen] != 0)) { + char *path_alloced = kmalloc(plen+1, GFP_KERNEL); + if (path_alloced == NULL) +@@ -1532,20 +1534,22 @@ nfsd_symlink(struct svc_rqst *rqstp, str + else { + strncpy(path_alloced, path, plen); + path_alloced[plen] = 0; +- host_err = vfs_symlink(dentry->d_inode, dnew, path_alloced, mode); ++ host_err = vfs_symlink(dentry->d_inode, dnew, ++ exp->ex_path.mnt, path_alloced, mode); + kfree(path_alloced); + } + } else +- host_err = vfs_symlink(dentry->d_inode, dnew, path, mode); ++ host_err = vfs_symlink(dentry->d_inode, dnew, exp->ex_path.mnt, ++ path, mode); + + if (!host_err) { +- if (EX_ISSYNC(fhp->fh_export)) ++ if (EX_ISSYNC(exp)) + host_err = nfsd_sync_dir(dentry); + } + err = nfserrno(host_err); + fh_unlock(fhp); + +- cerr = fh_compose(resfhp, fhp->fh_export, dnew, fhp); ++ cerr = fh_compose(resfhp, exp, dnew, fhp); + dput(dnew); + if (err==0) err = cerr; + out: +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -1078,7 +1078,7 @@ extern int vfs_permission(struct nameida + extern int vfs_create(struct inode *, struct dentry *, int, struct nameidata *); + extern int vfs_mkdir(struct inode *, struct dentry *, struct vfsmount *, int); + extern int vfs_mknod(struct inode *, struct dentry *, struct vfsmount *, int, dev_t); +-extern int vfs_symlink(struct inode *, struct dentry *, const char *, int); ++extern int vfs_symlink(struct inode *, struct dentry *, struct vfsmount *, const char *, int); + extern int vfs_link(struct dentry *, struct inode *, struct dentry *); + extern int vfs_rmdir(struct inode *, struct dentry *); + extern int vfs_unlink(struct inode *, struct dentry *); diff --git a/kernel-patches/2.6.25/vfs-unlink.diff b/kernel-patches/2.6.25/vfs-unlink.diff new file mode 100644 index 000000000..5793aa434 --- /dev/null +++ b/kernel-patches/2.6.25/vfs-unlink.diff @@ -0,0 +1,98 @@ +From: Tony Jones +Subject: Add a struct vfsmount parameter to vfs_unlink() + +The vfsmount will be passed down to the LSM hook so that LSMs can compute +pathnames. + +Signed-off-by: Tony Jones +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/ecryptfs/inode.c | 3 ++- + fs/namei.c | 4 ++-- + fs/nfsd/nfs4recover.c | 2 +- + fs/nfsd/vfs.c | 2 +- + include/linux/fs.h | 2 +- + ipc/mqueue.c | 2 +- + 6 files changed, 8 insertions(+), 7 deletions(-) + +--- a/fs/ecryptfs/inode.c ++++ b/fs/ecryptfs/inode.c +@@ -430,10 +430,11 @@ static int ecryptfs_unlink(struct inode + { + int rc = 0; + struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); ++ struct vfsmount *lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry); + struct inode *lower_dir_inode = ecryptfs_inode_to_lower(dir); + + lock_parent(lower_dentry); +- rc = vfs_unlink(lower_dir_inode, lower_dentry); ++ rc = vfs_unlink(lower_dir_inode, lower_dentry, lower_mnt); + if (rc) { + printk(KERN_ERR "Error in vfs_unlink; rc = [%d]\n", rc); + goto out_unlock; +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -2171,7 +2171,7 @@ asmlinkage long sys_rmdir(const char __u + return do_rmdir(AT_FDCWD, pathname); + } + +-int vfs_unlink(struct inode *dir, struct dentry *dentry) ++int vfs_unlink(struct inode *dir, struct dentry *dentry, struct vfsmount *mnt) + { + int error = may_delete(dir, dentry, 0); + +@@ -2236,7 +2236,7 @@ static long do_unlinkat(int dfd, const c + inode = dentry->d_inode; + if (inode) + atomic_inc(&inode->i_count); +- error = vfs_unlink(nd.path.dentry->d_inode, dentry); ++ error = vfs_unlink(nd.path.dentry->d_inode, dentry, nd.path.mnt); + exit2: + dput(dentry); + } +--- a/fs/nfsd/nfs4recover.c ++++ b/fs/nfsd/nfs4recover.c +@@ -259,7 +259,7 @@ nfsd4_remove_clid_file(struct dentry *di + return -EINVAL; + } + mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); +- status = vfs_unlink(dir->d_inode, dentry); ++ status = vfs_unlink(dir->d_inode, dentry, rec_dir.path.mnt); + mutex_unlock(&dir->d_inode->i_mutex); + return status; + } +--- a/fs/nfsd/vfs.c ++++ b/fs/nfsd/vfs.c +@@ -1767,7 +1767,7 @@ nfsd_unlink(struct svc_rqst *rqstp, stru + host_err = -EPERM; + } else + #endif +- host_err = vfs_unlink(dirp, rdentry); ++ host_err = vfs_unlink(dirp, rdentry, exp->ex_path.mnt); + } else { /* It's RMDIR */ + host_err = vfs_rmdir(dirp, rdentry, exp->ex_path.mnt); + } +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -1081,7 +1081,7 @@ extern int vfs_mknod(struct inode *, str + extern int vfs_symlink(struct inode *, struct dentry *, struct vfsmount *, const char *, int); + extern int vfs_link(struct dentry *, struct vfsmount *, struct inode *, struct dentry *, struct vfsmount *); + extern int vfs_rmdir(struct inode *, struct dentry *, struct vfsmount *); +-extern int vfs_unlink(struct inode *, struct dentry *); ++extern int vfs_unlink(struct inode *, struct dentry *, struct vfsmount *); + extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *); + + /* +--- a/ipc/mqueue.c ++++ b/ipc/mqueue.c +@@ -743,7 +743,7 @@ asmlinkage long sys_mq_unlink(const char + if (inode) + atomic_inc(&inode->i_count); + +- err = vfs_unlink(dentry->d_parent->d_inode, dentry); ++ err = vfs_unlink(dentry->d_parent->d_inode, dentry, mqueue_mnt); + out_err: + dput(dentry); +