diff --git a/kernel-patches/2.6.26/apparmor-2.6.25.diff b/kernel-patches/2.6.26/apparmor-2.6.25.diff new file mode 100644 index 000000000..d224bcf5b --- /dev/null +++ b/kernel-patches/2.6.26/apparmor-2.6.25.diff @@ -0,0 +1,60 @@ +From: John Johansen +Subject: AppArmor: Patch AppArmor for 2.6.25 kernel + +Add 64 bit capabilities support to AppArmor. + +Signed-off-by: John Johansen + +--- + 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 +@@ -394,15 +394,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.26/apparmor-audit.diff b/kernel-patches/2.6.26/apparmor-audit.diff new file mode 100644 index 000000000..6a752a2c8 --- /dev/null +++ b/kernel-patches/2.6.26/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 */ +@@ -546,6 +553,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 +@@ -1226,8 +1226,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; +@@ -1501,3 +1500,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.26/apparmor-intree.diff b/kernel-patches/2.6.26/apparmor-intree.diff new file mode 100644 index 000000000..a8dcbe2a6 --- /dev/null +++ b/kernel-patches/2.6.26/apparmor-intree.diff @@ -0,0 +1,31 @@ +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 +@@ -126,6 +126,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,6 +16,7 @@ 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 + obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o diff --git a/kernel-patches/2.6.26/apparmor-lsm.diff b/kernel-patches/2.6.26/apparmor-lsm.diff new file mode 100644 index 000000000..e2910715e --- /dev/null +++ b/kernel-patches/2.6.26/apparmor-lsm.diff @@ -0,0 +1,910 @@ +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 | 895 ++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 895 insertions(+) + +--- /dev/null ++++ b/security/apparmor/lsm.c +@@ -0,0 +1,895 @@ ++/* ++ * 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 path *path, 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, ++ const char *name, const void *value, ++ size_t size, int flags, struct file *file) ++{ ++ int error = cap_inode_setxattr(dentry, mnt, name, value, size, flags, ++ file); ++ ++ if (!error) ++ error = aa_xattr_permission(dentry, mnt, "xattr set", ++ MAY_WRITE, file); ++ return error; ++} ++ ++static int apparmor_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt, ++ const 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, const 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.26/apparmor-main.diff b/kernel-patches/2.6.26/apparmor-main.diff new file mode 100644 index 000000000..04734fdef --- /dev/null +++ b/kernel-patches/2.6.26/apparmor-main.diff @@ -0,0 +1,1493 @@ +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 | 1478 +++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 1478 insertions(+) + +--- /dev/null ++++ b/security/apparmor/main.c +@@ -0,0 +1,1478 @@ ++/* ++ * 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, int hat, ++ 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)) { ++ aa_free_task_context(new_cxt); ++ return -ENOENT; ++ } ++ new_profile = aa_dup_profile(ns->null_complain_profile); ++ } else if (new_profile && hat && !PROFILE_IS_HAT(new_profile)) { ++ aa_free_task_context(new_cxt); ++ aa_put_profile(new_profile); ++ return error; ++ } ++ ++ 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, 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, 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 (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, 1, &sa); ++ aa_put_name_buffer(name); ++ } else if (previous_profile) ++ error = do_change_profile(profile, profile->ns, ++ previous_profile->name, cookie, 1, 0, ++ &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.26/apparmor-misc.diff b/kernel-patches/2.6.26/apparmor-misc.diff new file mode 100644 index 000000000..3ad738784 --- /dev/null +++ b/kernel-patches/2.6.26/apparmor-misc.diff @@ -0,0 +1,1440 @@ +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 | 371 +++++++++++++++++++++++++++++++++++++++++ + security/apparmor/apparmorfs.c | 280 ++++++++++++++++++++++++++++++ + security/apparmor/inline.h | 250 +++++++++++++++++++++++++++ + security/apparmor/list.c | 174 +++++++++++++++++++ + security/apparmor/locking.txt | 68 +++++++ + security/apparmor/procattr.c | 195 +++++++++++++++++++++ + 8 files changed, 1393 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,371 @@ ++/* ++ * 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)) ++ ++#define PROFILE_IS_HAT(_profile) \ ++ ((_profile) && (_profile)->flags.hat) ++ ++/* ++ * 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 hat; ++ 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.5 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,174 @@ ++/* ++ * 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 struct aa_profile *next_profile(struct aa_profile *profile) ++{ ++ struct aa_profile *next = profile; ++ struct aa_namespace *ns; ++ ++ list_for_each_entry_continue(next, &profile->ns->profiles, list) ++ return next; ++ ++ ns = profile->ns; ++ read_unlock(&ns->lock); ++ list_for_each_entry_continue(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_start(struct seq_file *f, loff_t *pos) ++{ ++ struct aa_namespace *ns; ++ loff_t l = *pos; ++ ++ read_lock(&profile_ns_list_lock); ++ if (!list_empty(&profile_ns_list)) { ++ struct aa_profile *profile = NULL; ++ ns = list_first_entry(&profile_ns_list, typeof(*ns), list); ++ read_lock(&ns->lock); ++ if (!list_empty(&ns->profiles)) ++ profile = list_first_entry(&ns->profiles, ++ typeof(*profile), list); ++ else ++ read_unlock(&ns->lock); ++ for ( ; profile && l > 0; l--) ++ profile = next_profile(profile); ++ return profile; ++ } ++ return NULL; ++} ++ ++static void *p_next(struct seq_file *f, void *p, loff_t *pos) ++{ ++ struct aa_profile *profile = (struct aa_profile *) p; ++ ++ (*pos)++; ++ profile = next_profile(profile); ++ ++ return profile; ++} ++ ++static void p_stop(struct seq_file *f, void *p) ++{ ++ struct aa_profile *profile = (struct aa_profile *) p; ++ ++ if (profile) ++ read_unlock(&profile->ns->lock); ++ read_unlock(&profile_ns_list_lock); ++} ++ ++static int seq_show_profile(struct seq_file *f, void *p) ++{ ++ struct aa_profile *profile = (struct aa_profile *)p; ++ ++ 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.26/apparmor-module_interface.diff b/kernel-patches/2.6.26/apparmor-module_interface.diff new file mode 100644 index 000000000..f373428d6 --- /dev/null +++ b/kernel-patches/2.6.26/apparmor-module_interface.diff @@ -0,0 +1,1350 @@ +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 | 875 +++++++++++++++++++++++++++++++++++ + 3 files changed, 1326 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,875 @@ ++/* ++ * 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; ++ profile->exec_table_size = size; ++ } ++ 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, &(profile->flags.hat), 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.26/apparmor-network.diff b/kernel-patches/2.6.26/apparmor-network.diff new file mode 100644 index 000000000..f3b16974b --- /dev/null +++ b/kernel-patches/2.6.26/apparmor-network.diff @@ -0,0 +1,408 @@ +From: John Johansen +Subject: AppArmor: Simplified network controls for AppArmor + +Simple network control determining which network families a confined +application has access to. + +Signed-off-by: John Johansen + +--- + 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 +@@ -212,6 +214,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; +@@ -258,6 +263,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; + }; + +@@ -319,6 +325,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.26/apparmor-rlimits.diff b/kernel-patches/2.6.26/apparmor-rlimits.diff new file mode 100644 index 000000000..4a7246f84 --- /dev/null +++ b/kernel-patches/2.6.26/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 + +@@ -139,6 +140,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 +@@ -173,6 +186,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 +@@ -210,6 +225,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; +@@ -261,6 +279,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; +@@ -328,6 +347,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.5 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; + } +@@ -1303,6 +1393,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); + +@@ -1481,17 +1577,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) +@@ -1499,8 +1596,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; + } + + /** +@@ -1565,6 +1669,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) { +@@ -1576,6 +1681,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; +@@ -312,6 +328,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 +@@ -355,6 +404,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) +@@ -614,6 +666,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 +@@ -634,6 +688,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); +@@ -656,6 +711,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.26/apparmor-stack_secondary.diff b/kernel-patches/2.6.26/apparmor-stack_secondary.diff new file mode 100644 index 000000000..471fa57a7 --- /dev/null +++ b/kernel-patches/2.6.26/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 +@@ -354,7 +354,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.26/d_namespace_path.diff b/kernel-patches/2.6.26/d_namespace_path.diff new file mode 100644 index 000000000..76aa4815e --- /dev/null +++ b/kernel-patches/2.6.26/d_namespace_path.diff @@ -0,0 +1,59 @@ +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/namespace.c | 29 +++++++++++++++++++++++++++++ + include/linux/mount.h | 2 ++ + 2 files changed, 31 insertions(+) + +--- a/fs/namespace.c ++++ b/fs/namespace.c +@@ -2351,3 +2351,32 @@ 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 = { }; ++ struct path path = { .mnt = vfsmnt, .dentry = dentry }; ++ 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(&path, &ns_root, buf, buflen, ++ D_PATH_FAIL_DELETED | D_PATH_DISCONNECT); ++ 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/mount.h ++++ b/include/linux/mount.h +@@ -113,4 +113,6 @@ 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 /* _LINUX_MOUNT_H */ diff --git a/kernel-patches/2.6.26/do_path_lookup-nameidata.diff b/kernel-patches/2.6.26/do_path_lookup-nameidata.diff new file mode 100644 index 000000000..cc11a0970 --- /dev/null +++ b/kernel-patches/2.6.26/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 +@@ -1153,24 +1153,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.26/file-handle-ops.diff b/kernel-patches/2.6.26/file-handle-ops.diff new file mode 100644 index 000000000..c34a4f4f8 --- /dev/null +++ b/kernel-patches/2.6.26/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 +@@ -412,7 +412,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; + +@@ -420,7 +420,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 + +@@ -446,7 +446,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; +@@ -2111,12 +2111,14 @@ nfsd_set_posix_acl(struct svc_fh *fhp, i + if (error) + goto getout; + 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 +@@ -589,7 +589,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); + +@@ -670,6 +670,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.26/file_permission-nameidata.diff b/kernel-patches/2.6.26/file_permission-nameidata.diff new file mode 100644 index 000000000..03f2395a2 --- /dev/null +++ b/kernel-patches/2.6.26/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 +@@ -318,7 +318,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.26/fix-vfs_rmdir.diff b/kernel-patches/2.6.26/fix-vfs_rmdir.diff new file mode 100644 index 000000000..5049029e0 --- /dev/null +++ b/kernel-patches/2.6.26/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 +@@ -2232,6 +2232,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); +@@ -2239,12 +2243,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.26/fsetattr-reintro-ATTR_FILE.diff b/kernel-patches/2.6.26/fsetattr-reintro-ATTR_FILE.diff new file mode 100644 index 000000000..1da32dcc1 --- /dev/null +++ b/kernel-patches/2.6.26/fsetattr-reintro-ATTR_FILE.diff @@ -0,0 +1,39 @@ +From: John Johansen +Subject: AppArmor: reintroduce ATTR_FILE + +The fsetattr patch removed ATTR_FILE but AppArmor needs it to distinguish +file based writes. + +Note: Now that LSMs must be static, it would be better to add a file +pointer argument to security_operations->inode_setattr() instead. Then +move the fs.h chunk to patches.apparmor/fsetattr-restore-ia_file. -jeffm + +Signed-off-by: John Johansen + +--- + fs/open.c | 3 +++ + include/linux/fs.h | 1 + + 2 files changed, 4 insertions(+) + +--- a/fs/open.c ++++ b/fs/open.c +@@ -208,6 +208,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 +@@ -330,6 +330,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.26/fsetattr-restore-ia_file.diff b/kernel-patches/2.6.26/fsetattr-restore-ia_file.diff new file mode 100644 index 000000000..15a92d3ac --- /dev/null +++ b/kernel-patches/2.6.26/fsetattr-restore-ia_file.diff @@ -0,0 +1,58 @@ +From: Jeff Mahoney +Subject: [PATCH] vfs: restore ia_file for compatibility with external modules +References: bnc#381259 + + patches.apparmor/fsetattr.diff eliminated ia_file and ATTR_FILE in favor + of providing a ->fsetattr call that used a file pointer. Until this + patch is accepted into mainline, this patch provides the backward + compatibility for external file system modules. + +Signed-off-by: Jeff Mahoney +--- + fs/attr.c | 13 ++++++++++++- + include/linux/fs.h | 11 +++++++++++ + 2 files changed, 23 insertions(+), 1 deletion(-) + +--- a/fs/attr.c ++++ b/fs/attr.c +@@ -163,8 +163,19 @@ int fnotify_change(struct dentry *dentry + if (!error) { + if (file && file->f_op && file->f_op->fsetattr) + error = file->f_op->fsetattr(file, attr); +- else ++ else { ++ /* External file system still expect to be ++ * passed a file pointer via ia_file and ++ * have it announced via ATTR_FILE. This ++ * just makes it so they don't need to ++ * change their API just for us. External ++ * callers will have set these themselves. */ ++ if (file) { ++ attr->ia_valid |= ATTR_FILE; ++ attr->ia_file = file; ++ } + error = inode->i_op->setattr(dentry, attr); ++ } + } + } else { + error = inode_change_ok(inode, attr); +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -352,6 +352,17 @@ 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). ++ * ++ * NOTE: With patches.apparmor/fsetattr.diff applied, this is ++ * for compatibility with external file system modules only. There ++ * should not be any in-kernel users left. ++ */ ++ struct file *ia_file; + }; + + /* diff --git a/kernel-patches/2.6.26/fsetattr.diff b/kernel-patches/2.6.26/fsetattr.diff new file mode 100644 index 000000000..b467ece63 --- /dev/null +++ b/kernel-patches/2.6.26/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; +@@ -1158,8 +1159,8 @@ void fuse_release_nowrite(struct inode * + * 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); +@@ -1203,7 +1204,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; +@@ -1273,10 +1274,7 @@ error: + + 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 +@@ -1458,6 +1458,11 @@ static loff_t fuse_file_llseek(struct fi + return retval; + } + ++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 = fuse_file_llseek, + .read = do_sync_read, +@@ -1471,6 +1476,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, + }; + +@@ -1484,6 +1490,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 +@@ -543,6 +543,10 @@ void fuse_truncate(struct address_space + */ + 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 +@@ -207,16 +207,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; + } +@@ -591,7 +587,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_drop_write: +@@ -645,7 +641,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; +@@ -672,7 +668,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; +@@ -689,7 +685,7 @@ asmlinkage long sys_chown(const char __u + error = mnt_want_write(nd.path.mnt); + if (error) + goto out_release; +- error = chown_common(nd.path.dentry, nd.path.mnt, user, group); ++ error = chown_common(nd.path.dentry, nd.path.mnt, user, group, NULL); + mnt_drop_write(nd.path.mnt); + out_release: + path_put(&nd.path); +@@ -714,7 +710,7 @@ asmlinkage long sys_fchownat(int dfd, co + error = mnt_want_write(nd.path.mnt); + if (error) + goto out_release; +- error = chown_common(nd.path.dentry, nd.path.mnt, user, group); ++ error = chown_common(nd.path.dentry, nd.path.mnt, user, group, NULL); + mnt_drop_write(nd.path.mnt); + out_release: + path_put(&nd.path); +@@ -733,7 +729,7 @@ asmlinkage long sys_lchown(const char __ + error = mnt_want_write(nd.path.mnt); + if (error) + goto out_release; +- error = chown_common(nd.path.dentry, nd.path.mnt, user, group); ++ error = chown_common(nd.path.dentry, nd.path.mnt, user, group, NULL); + mnt_drop_write(nd.path.mnt); + out_release: + path_put(&nd.path); +@@ -757,7 +753,7 @@ asmlinkage long sys_fchown(unsigned int + goto out_fput; + 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); + mnt_drop_write(file->f_path.mnt); + out_fput: + fput(file); +--- a/fs/utimes.c ++++ b/fs/utimes.c +@@ -151,7 +151,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); + mnt_drop_write_and_out: + mnt_drop_write(path.mnt); +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -330,7 +330,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) */ + +@@ -352,13 +351,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; + }; + + /* +@@ -1244,6 +1236,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 { +@@ -1752,6 +1745,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.26/parent-permission.diff b/kernel-patches/2.6.26/parent-permission.diff new file mode 100644 index 000000000..3c4857c7c --- /dev/null +++ b/kernel-patches/2.6.26/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 +@@ -1515,6 +1515,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.26/remove_suid.diff b/kernel-patches/2.6.26/remove_suid.diff new file mode 100644 index 000000000..8f4b895e6 --- /dev/null +++ b/kernel-patches/2.6.26/remove_suid.diff @@ -0,0 +1,141 @@ +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/fuse/file.c | 2 +- + fs/ntfs/file.c | 2 +- + fs/splice.c | 4 ++-- + fs/xfs/linux-2.6/xfs_lrw.c | 2 +- + include/linux/fs.h | 2 +- + mm/filemap.c | 16 ++++++++-------- + mm/filemap_xip.c | 2 +- + 7 files changed, 15 insertions(+), 15 deletions(-) + +--- a/fs/fuse/file.c ++++ b/fs/fuse/file.c +@@ -893,7 +893,7 @@ static ssize_t fuse_file_aio_write(struc + if (count == 0) + goto out; + +- err = remove_suid(file->f_path.dentry); ++ err = remove_suid(&file->f_path); + if (err) + goto out; + +--- 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 +@@ -763,7 +763,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; + +@@ -821,7 +821,7 @@ generic_file_splice_write(struct pipe_in + ssize_t ret; + + inode_double_lock(inode, pipe->inode); +- ret = remove_suid(out->f_path.dentry); ++ ret = remove_suid(&out->f_path); + if (likely(!ret)) + ret = __splice_from_pipe(pipe, &sd, pipe_to_file); + inode_double_unlock(inode, pipe->inode); +--- a/fs/xfs/linux-2.6/xfs_lrw.c ++++ b/fs/xfs/linux-2.6/xfs_lrw.c +@@ -711,7 +711,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 +@@ -1815,7 +1815,7 @@ extern void clear_inode(struct inode *); + extern void destroy_inode(struct inode *); + extern struct inode *new_inode(struct super_block *); + 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 +@@ -1660,26 +1660,26 @@ int should_remove_suid(struct dentry *de + } + EXPORT_SYMBOL(should_remove_suid); + +-static int __remove_suid(struct dentry *dentry, int kill) ++static 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; + } +@@ -2394,7 +2394,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.26/security-create.diff b/kernel-patches/2.6.26/security-create.diff new file mode 100644 index 000000000..f1c285373 --- /dev/null +++ b/kernel-patches/2.6.26/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 +@@ -1590,7 +1590,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 +@@ -341,6 +341,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: +@@ -1355,8 +1356,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); +@@ -1628,7 +1629,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); +@@ -1968,6 +1970,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 +@@ -289,7 +289,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 +@@ -388,11 +388,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 +@@ -2507,7 +2507,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.26/security-getxattr.diff b/kernel-patches/2.6.26/security-getxattr.diff new file mode 100644 index 000000000..9af9bb3ce --- /dev/null +++ b/kernel-patches/2.6.26/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 +@@ -141,7 +141,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 +@@ -450,7 +450,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 +@@ -1401,7 +1401,8 @@ struct security_operations { + struct vfsmount *mnt, + const char *name, const void *value, + size_t size, int flags); +- int (*inode_getxattr) (struct dentry *dentry, const char *name); ++ int (*inode_getxattr) (struct dentry *dentry, struct vfsmount *mnt, ++ const char *name); + int (*inode_listxattr) (struct dentry *dentry); + int (*inode_removexattr) (struct dentry *dentry, const char *name); + int (*inode_need_killpriv) (struct dentry *dentry); +@@ -1682,7 +1683,8 @@ int security_inode_setxattr(struct dentr + void security_inode_post_setxattr(struct dentry *dentry, struct vfsmount *mnt, + const char *name, const void *value, + size_t size, int flags); +-int security_inode_getxattr(struct dentry *dentry, const char *name); ++int security_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt, ++ const char *name); + int security_inode_listxattr(struct dentry *dentry); + int security_inode_removexattr(struct dentry *dentry, const char *name); + int security_inode_need_killpriv(struct dentry *dentry); +@@ -2114,7 +2116,8 @@ static inline void security_inode_post_s + { } + + static inline int security_inode_getxattr(struct dentry *dentry, +- const char *name) ++ struct vfsmount *mnt, ++ const char *name) + { + return 0; + } +--- a/security/dummy.c ++++ b/security/dummy.c +@@ -392,7 +392,8 @@ static void dummy_inode_post_setxattr (s + { + } + +-static int dummy_inode_getxattr (struct dentry *dentry, const char *name) ++static int dummy_inode_getxattr (struct dentry *dentry, struct vfsmount *mnt, ++ const char *name) + { + return 0; + } +--- a/security/security.c ++++ b/security/security.c +@@ -520,11 +520,12 @@ void security_inode_post_setxattr(struct + flags); + } + +-int security_inode_getxattr(struct dentry *dentry, const char *name) ++int security_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt, ++ const 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 +@@ -2732,7 +2732,8 @@ static void selinux_inode_post_setxattr( + return; + } + +-static int selinux_inode_getxattr(struct dentry *dentry, const char *name) ++static int selinux_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt, ++ const char *name) + { + return dentry_has_perm(current, NULL, dentry, FILE__GETATTR); + } diff --git a/kernel-patches/2.6.26/security-link.diff b/kernel-patches/2.6.26/security-link.diff new file mode 100644 index 000000000..ecd286710 --- /dev/null +++ b/kernel-patches/2.6.26/security-link.diff @@ -0,0 +1,135 @@ +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 | 18 ++++++++++++------ + security/dummy.c | 6 ++++-- + security/security.c | 8 +++++--- + security/selinux/hooks.c | 9 +++++++-- + 5 files changed, 30 insertions(+), 14 deletions(-) + +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -2507,7 +2507,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 +@@ -347,8 +347,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. +@@ -1363,8 +1365,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); +@@ -1638,8 +1641,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); +@@ -1987,8 +1991,10 @@ static inline int security_inode_create( + } + + static inline int security_inode_link(struct dentry *old_dentry, +- struct inode *dir, +- struct dentry *new_dentry) ++ struct vfsmount *old_mnt, ++ struct inode *dir, ++ struct dentry *new_dentry, ++ struct vfsmount *new_mnt) + { + return 0; + } +--- a/security/dummy.c ++++ b/security/dummy.c +@@ -294,8 +294,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 +@@ -396,12 +396,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 +@@ -2513,11 +2513,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.26/security-listxattr.diff b/kernel-patches/2.6.26/security-listxattr.diff new file mode 100644 index 000000000..03bea3481 --- /dev/null +++ b/kernel-patches/2.6.26/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 +@@ -174,7 +174,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 +@@ -454,7 +454,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 +@@ -1403,7 +1403,7 @@ struct security_operations { + size_t size, int flags); + int (*inode_getxattr) (struct dentry *dentry, struct vfsmount *mnt, + const char *name); +- int (*inode_listxattr) (struct dentry *dentry); ++ int (*inode_listxattr) (struct dentry *dentry, struct vfsmount *mnt); + int (*inode_removexattr) (struct dentry *dentry, const char *name); + int (*inode_need_killpriv) (struct dentry *dentry); + int (*inode_killpriv) (struct dentry *dentry); +@@ -1685,7 +1685,7 @@ void security_inode_post_setxattr(struct + size_t size, int flags); + int security_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt, + const 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, const char *name); + int security_inode_need_killpriv(struct dentry *dentry); + int security_inode_killpriv(struct dentry *dentry); +@@ -2122,7 +2122,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 +@@ -398,7 +398,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 +@@ -528,11 +528,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, const char *name) +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -2738,7 +2738,7 @@ static int selinux_inode_getxattr(struct + 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.26/security-mkdir.diff b/kernel-patches/2.6.26/security-mkdir.diff new file mode 100644 index 000000000..63ea350a8 --- /dev/null +++ b/kernel-patches/2.6.26/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 +@@ -2139,7 +2139,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 +@@ -366,6 +366,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: +@@ -1364,7 +1365,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); +@@ -1638,7 +1640,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, +@@ -2001,6 +2004,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 +@@ -312,7 +312,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 +@@ -419,11 +419,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 +@@ -2538,7 +2538,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.26/security-mknod.diff b/kernel-patches/2.6.26/security-mknod.diff new file mode 100644 index 000000000..cf1fd9ddc --- /dev/null +++ b/kernel-patches/2.6.26/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 +@@ -2041,7 +2041,7 @@ int vfs_mknod(struct inode *dir, struct + if (error) + return error; + +- error = security_inode_mknod(dir, dentry, mode, dev); ++ error = security_inode_mknod(dir, dentry, mnt, mode, dev); + if (error) + return error; + +@@ -2105,11 +2105,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; + } + mnt_drop_write(nd.path.mnt); +--- a/include/linux/security.h ++++ b/include/linux/security.h +@@ -381,6 +381,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. +@@ -1369,7 +1370,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); +@@ -1643,7 +1644,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); +@@ -2018,6 +2020,7 @@ static inline int security_inode_rmdir(s + + 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 +@@ -323,7 +323,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 +@@ -434,11 +434,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 +@@ -2549,11 +2549,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.26/security-readlink.diff b/kernel-patches/2.6.26/security-readlink.diff new file mode 100644 index 000000000..ebe0b4133 --- /dev/null +++ b/kernel-patches/2.6.26/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 +@@ -396,6 +396,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. +@@ -1374,7 +1375,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, +@@ -1649,7 +1650,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, +@@ -2036,7 +2037,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 +@@ -336,7 +336,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 +@@ -452,11 +452,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 +@@ -2568,7 +2568,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.26/security-removexattr.diff b/kernel-patches/2.6.26/security-removexattr.diff new file mode 100644 index 000000000..0df690a24 --- /dev/null +++ b/kernel-patches/2.6.26/security-removexattr.diff @@ -0,0 +1,127 @@ +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 | 14 +++++++++----- + security/commoncap.c | 3 ++- + security/dummy.c | 3 ++- + security/security.c | 5 +++-- + security/selinux/hooks.c | 3 ++- + 6 files changed, 19 insertions(+), 11 deletions(-) + +--- a/fs/xattr.c ++++ b/fs/xattr.c +@@ -202,7 +202,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 +@@ -56,7 +56,8 @@ extern int cap_bprm_secureexec(struct li + extern int cap_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, + const char *name, const void *value, size_t size, + int flags); +-extern int cap_inode_removexattr(struct dentry *dentry, const char *name); ++extern int cap_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt, ++ const 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); +@@ -1404,7 +1405,8 @@ struct security_operations { + int (*inode_getxattr) (struct dentry *dentry, struct vfsmount *mnt, + const char *name); + int (*inode_listxattr) (struct dentry *dentry, struct vfsmount *mnt); +- int (*inode_removexattr) (struct dentry *dentry, const char *name); ++ int (*inode_removexattr) (struct dentry *dentry, struct vfsmount *mnt, ++ const 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); +@@ -1686,7 +1688,8 @@ void security_inode_post_setxattr(struct + int security_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt, + const char *name); + int security_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt); +-int security_inode_removexattr(struct dentry *dentry, const char *name); ++int security_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt, ++ const 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); +@@ -2129,9 +2132,10 @@ static inline int security_inode_listxat + } + + static inline int security_inode_removexattr(struct dentry *dentry, +- const char *name) ++ struct vfsmount *mnt, ++ const 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 +@@ -398,7 +398,8 @@ int cap_inode_setxattr(struct dentry *de + return 0; + } + +-int cap_inode_removexattr(struct dentry *dentry, const char *name) ++int cap_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt, ++ const char *name) + { + if (!strcmp(name, XATTR_NAME_CAPS)) { + if (!capable(CAP_SETFCAP)) +--- a/security/dummy.c ++++ b/security/dummy.c +@@ -403,7 +403,8 @@ static int dummy_inode_listxattr (struct + return 0; + } + +-static int dummy_inode_removexattr (struct dentry *dentry, const char *name) ++static int dummy_inode_removexattr (struct dentry *dentry, struct vfsmount *mnt, ++ const char *name) + { + if (!strncmp(name, XATTR_SECURITY_PREFIX, + sizeof(XATTR_SECURITY_PREFIX) - 1) && +--- a/security/security.c ++++ b/security/security.c +@@ -535,11 +535,12 @@ int security_inode_listxattr(struct dent + return security_ops->inode_listxattr(dentry, mnt); + } + +-int security_inode_removexattr(struct dentry *dentry, const char *name) ++int security_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt, ++ const 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 +@@ -2743,7 +2743,8 @@ static int selinux_inode_listxattr(struc + return dentry_has_perm(current, NULL, dentry, FILE__GETATTR); + } + +-static int selinux_inode_removexattr(struct dentry *dentry, const char *name) ++static int selinux_inode_removexattr(struct dentry *dentry, ++ struct vfsmount *mnt, const char *name) + { + if (strcmp(name, XATTR_NAME_SELINUX)) + return selinux_inode_setotherxattr(dentry, name); diff --git a/kernel-patches/2.6.26/security-rename.diff b/kernel-patches/2.6.26/security-rename.diff new file mode 100644 index 000000000..4b6a66500 --- /dev/null +++ b/kernel-patches/2.6.26/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 +@@ -2636,7 +2636,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; + +@@ -2670,7 +2671,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 +@@ -394,8 +394,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. +@@ -1381,7 +1383,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); +@@ -1659,7 +1663,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); +@@ -2045,8 +2050,10 @@ static inline int security_inode_mknod(s + + 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 +@@ -334,8 +334,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 +@@ -447,13 +447,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 +@@ -2569,8 +2569,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.26/security-rmdir.diff b/kernel-patches/2.6.26/security-rmdir.diff new file mode 100644 index 000000000..9c648235a --- /dev/null +++ b/kernel-patches/2.6.26/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 +@@ -2239,7 +2239,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 +@@ -376,6 +376,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 +@@ -1373,7 +1374,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, +@@ -1649,7 +1651,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, +@@ -2022,7 +2025,8 @@ static inline int security_inode_mkdir(s + } + + 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 +@@ -319,7 +319,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 +@@ -429,11 +429,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 +@@ -2550,7 +2550,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.26/security-setattr.diff b/kernel-patches/2.6.26/security-setattr.diff new file mode 100644 index 000000000..45108fb33 --- /dev/null +++ b/kernel-patches/2.6.26/security-setattr.diff @@ -0,0 +1,121 @@ +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 | 10 +++++++--- + security/dummy.c | 3 ++- + security/security.c | 5 +++-- + security/selinux/hooks.c | 5 +++-- + 5 files changed, 17 insertions(+), 10 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 +@@ -416,6 +416,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: +@@ -1372,7 +1373,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, const char *name, +@@ -1644,7 +1646,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, const char *name, +@@ -2042,7 +2045,8 @@ static inline int security_inode_permiss + } + + static inline int security_inode_setattr(struct dentry *dentry, +- struct iattr *attr) ++ struct vfsmount *mnt, ++ struct iattr *attr) + { + return 0; + } +--- a/security/dummy.c ++++ b/security/dummy.c +@@ -352,7 +352,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 +@@ -471,11 +471,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 +@@ -2598,11 +2598,12 @@ static int selinux_inode_permission(stru + open_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.26/security-setxattr.diff b/kernel-patches/2.6.26/security-setxattr.diff new file mode 100644 index 000000000..7e33aefbd --- /dev/null +++ b/kernel-patches/2.6.26/security-setxattr.diff @@ -0,0 +1,218 @@ +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 | 41 ++++++++++++++++++++++++++--------------- + security/commoncap.c | 5 +++-- + security/dummy.c | 8 +++++--- + security/security.c | 16 ++++++++++------ + security/selinux/hooks.c | 8 +++++--- + 6 files changed, 51 insertions(+), 31 deletions(-) + +--- a/fs/xattr.c ++++ b/fs/xattr.c +@@ -78,7 +78,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; +@@ -86,7 +86,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 +@@ -53,8 +53,9 @@ extern void cap_capset_set(struct task_s + 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, const char *name, +- const void *value, size_t size, int flags); ++extern int cap_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, ++ const char *name, const void *value, size_t size, ++ int flags); + extern int cap_inode_removexattr(struct dentry *dentry, const char *name); + extern int cap_inode_need_killpriv(struct dentry *dentry); + extern int cap_inode_killpriv(struct dentry *dentry); +@@ -442,11 +443,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. +@@ -1393,10 +1394,13 @@ 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, const char *name, +- const void *value, size_t size, int flags); +- void (*inode_post_setxattr) (struct dentry *dentry, const char *name, +- const void *value, size_t size, int flags); ++ int (*inode_setxattr) (struct dentry *dentry, struct vfsmount *mnt, ++ const char *name, const void *value, size_t size, ++ int flags); ++ void (*inode_post_setxattr) (struct dentry *dentry, ++ struct vfsmount *mnt, ++ const char *name, const void *value, ++ size_t size, int flags); + int (*inode_getxattr) (struct dentry *dentry, const char *name); + int (*inode_listxattr) (struct dentry *dentry); + int (*inode_removexattr) (struct dentry *dentry, const char *name); +@@ -1672,10 +1676,12 @@ 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, const char *name, +- const void *value, size_t size, int flags); +-void security_inode_post_setxattr(struct dentry *dentry, const char *name, +- const void *value, size_t size, int flags); ++int security_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, ++ const char *name, const void *value, ++ size_t size, int flags); ++void security_inode_post_setxattr(struct dentry *dentry, struct vfsmount *mnt, ++ const char *name, const void *value, ++ size_t size, int flags); + int security_inode_getxattr(struct dentry *dentry, const char *name); + int security_inode_listxattr(struct dentry *dentry); + int security_inode_removexattr(struct dentry *dentry, const char *name); +@@ -2093,13 +2099,18 @@ static inline void security_inode_delete + { } + + static inline int security_inode_setxattr(struct dentry *dentry, +- const char *name, const void *value, size_t size, int flags) ++ struct vfsmount *mnt, ++ const char *name, const 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, +- const char *name, const void *value, size_t size, int flags) ++ struct vfsmount *mnt, ++ const char *name, ++ const void *value, ++ size_t size, int flags) + { } + + static inline int security_inode_getxattr(struct dentry *dentry, +--- a/security/commoncap.c ++++ b/security/commoncap.c +@@ -383,8 +383,9 @@ int cap_bprm_secureexec (struct linux_bi + current->egid != current->gid); + } + +-int cap_inode_setxattr(struct dentry *dentry, const char *name, +- const void *value, size_t size, int flags) ++int cap_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, ++ const char *name, const 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 +@@ -374,8 +374,9 @@ static void dummy_inode_delete (struct i + return; + } + +-static int dummy_inode_setxattr (struct dentry *dentry, const char *name, +- const void *value, size_t size, int flags) ++static int dummy_inode_setxattr (struct dentry *dentry, struct vfsmount *mnt, ++ const char *name, const void *value, ++ size_t size, int flags) + { + if (!strncmp(name, XATTR_SECURITY_PREFIX, + sizeof(XATTR_SECURITY_PREFIX) - 1) && +@@ -384,7 +385,8 @@ static int dummy_inode_setxattr (struct + return 0; + } + +-static void dummy_inode_post_setxattr (struct dentry *dentry, const char *name, ++static void dummy_inode_post_setxattr (struct dentry *dentry, ++ struct vfsmount *mnt, const char *name, + const void *value, size_t size, + int flags) + { +--- a/security/security.c ++++ b/security/security.c +@@ -500,20 +500,24 @@ void security_inode_delete(struct inode + security_ops->inode_delete(inode); + } + +-int security_inode_setxattr(struct dentry *dentry, const char *name, +- const void *value, size_t size, int flags) ++int security_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, ++ const char *name, const 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, const char *name, +- const void *value, size_t size, int flags) ++void security_inode_post_setxattr(struct dentry *dentry, struct vfsmount *mnt, ++ const char *name, const 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, const char *name) +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -2655,8 +2655,9 @@ static int selinux_inode_setotherxattr(s + return dentry_has_perm(current, NULL, dentry, FILE__SETATTR); + } + +-static int selinux_inode_setxattr(struct dentry *dentry, const char *name, +- const void *value, size_t size, int flags) ++static int selinux_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, ++ const char *name, const void *value, ++ size_t size, int flags) + { + struct task_security_struct *tsec = current->security; + struct inode *inode = dentry->d_inode; +@@ -2705,7 +2706,8 @@ static int selinux_inode_setxattr(struct + &ad); + } + +-static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name, ++static void selinux_inode_post_setxattr(struct dentry *dentry, ++ struct vfsmount *mnt, const char *name, + const void *value, size_t size, + int flags) + { diff --git a/kernel-patches/2.6.26/security-symlink.diff b/kernel-patches/2.6.26/security-symlink.diff new file mode 100644 index 000000000..11098b210 --- /dev/null +++ b/kernel-patches/2.6.26/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 +@@ -2422,7 +2422,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 +@@ -359,6 +359,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: +@@ -1364,8 +1365,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); +@@ -1640,7 +1641,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); +@@ -1999,6 +2000,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 +@@ -306,7 +306,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 +@@ -412,11 +412,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 +@@ -2533,7 +2533,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.26/security-unlink.diff b/kernel-patches/2.6.26/security-unlink.diff new file mode 100644 index 000000000..e4381101f --- /dev/null +++ b/kernel-patches/2.6.26/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 +@@ -2324,7 +2324,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 +@@ -356,6 +356,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. +@@ -1369,7 +1370,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, +@@ -1646,7 +1648,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, +@@ -2003,7 +2006,8 @@ static inline int security_inode_link(st + } + + 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 +@@ -302,7 +302,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 +@@ -406,11 +406,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 +@@ -2528,11 +2528,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.26/security-xattr-file.diff b/kernel-patches/2.6.26/security-xattr-file.diff new file mode 100644 index 000000000..70f646147 --- /dev/null +++ b/kernel-patches/2.6.26/security-xattr-file.diff @@ -0,0 +1,533 @@ +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 | 61 +++++++++++++++++++++++++---------------------- + include/linux/security.h | 38 ++++++++++++++++------------- + include/linux/xattr.h | 8 +++--- + security/commoncap.c | 4 +-- + security/dummy.c | 9 +++--- + security/security.c | 17 ++++++------- + security/selinux/hooks.c | 10 ++++--- + 7 files changed, 81 insertions(+), 66 deletions(-) + +--- a/fs/xattr.c ++++ b/fs/xattr.c +@@ -68,7 +68,7 @@ xattr_permission(struct inode *inode, co + + int + vfs_setxattr(struct dentry *dentry, struct vfsmount *mnt, const char *name, +- const void *value, size_t size, int flags) ++ const void *value, size_t size, int flags, struct file *file) + { + struct inode *inode = dentry->d_inode; + int error; +@@ -78,7 +78,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; +@@ -132,7 +132,7 @@ EXPORT_SYMBOL_GPL(xattr_getsecurity); + + ssize_t + vfs_getxattr(struct dentry *dentry, struct vfsmount *mnt, const char *name, +- void *value, size_t size) ++ void *value, size_t size, struct file *file) + { + struct inode *inode = dentry->d_inode; + int error; +@@ -141,7 +141,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; + +@@ -169,12 +169,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; +@@ -190,7 +190,8 @@ vfs_listxattr(struct dentry *dentry, str + EXPORT_SYMBOL_GPL(vfs_listxattr); + + int +-vfs_removexattr(struct dentry *dentry, struct vfsmount *mnt, const char *name) ++vfs_removexattr(struct dentry *dentry, struct vfsmount *mnt, const char *name, ++ struct file *file) + { + struct inode *inode = dentry->d_inode; + int error; +@@ -202,7 +203,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; + +@@ -222,7 +223,7 @@ EXPORT_SYMBOL_GPL(vfs_removexattr); + */ + static long + setxattr(struct dentry *dentry, struct vfsmount *mnt, const char __user *name, +- const void __user *value, size_t size, int flags) ++ const void __user *value, size_t size, int flags, struct file *file) + { + int error; + void *kvalue = NULL; +@@ -249,7 +250,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; + } +@@ -266,7 +267,8 @@ sys_setxattr(const char __user *path, co + return error; + error = mnt_want_write(nd.path.mnt); + if (!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); + mnt_drop_write(nd.path.mnt); + } + path_put(&nd.path); +@@ -285,7 +287,8 @@ sys_lsetxattr(const char __user *path, c + return error; + error = mnt_want_write(nd.path.mnt); + if (!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); + mnt_drop_write(nd.path.mnt); + } + path_put(&nd.path); +@@ -307,7 +310,8 @@ sys_fsetxattr(int fd, const char __user + audit_inode(NULL, dentry); + error = mnt_want_write(f->f_path.mnt); + if (!error) { +- error = setxattr(dentry, f->f_vfsmnt, name, value, size, flags); ++ error = setxattr(dentry, f->f_vfsmnt, name, value, size, flags, ++ f); + mnt_drop_write(f->f_path.mnt); + } + fput(f); +@@ -319,7 +323,7 @@ sys_fsetxattr(int fd, const char __user + */ + static ssize_t + getxattr(struct dentry *dentry, struct vfsmount *mnt, const char __user *name, +- void __user *value, size_t size) ++ void __user *value, size_t size, struct file *file) + { + ssize_t error; + void *kvalue = NULL; +@@ -339,7 +343,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; +@@ -362,7 +366,7 @@ sys_getxattr(const char __user *path, co + 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; + } +@@ -377,7 +381,7 @@ sys_lgetxattr(const char __user *path, c + 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; + } +@@ -392,7 +396,7 @@ sys_fgetxattr(int fd, const char __user + 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; + } +@@ -402,7 +406,7 @@ sys_fgetxattr(int fd, const char __user + */ + 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; +@@ -415,7 +419,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; +@@ -437,7 +441,7 @@ sys_listxattr(const char __user *path, c + 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; + } +@@ -451,7 +455,7 @@ sys_llistxattr(const char __user *path, + 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; + } +@@ -466,7 +470,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; + } +@@ -475,7 +479,8 @@ sys_flistxattr(int fd, char __user *list + * Extended attribute REMOVE operations + */ + static long +-removexattr(struct dentry *dentry, struct vfsmount *mnt, const char __user *name) ++removexattr(struct dentry *dentry, struct vfsmount *mnt, ++ const char __user *name, struct file *file) + { + int error; + char kname[XATTR_NAME_MAX + 1]; +@@ -486,7 +491,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 +@@ -500,7 +505,7 @@ sys_removexattr(const char __user *path, + return error; + error = mnt_want_write(nd.path.mnt); + if (!error) { +- error = removexattr(nd.path.dentry, nd.path.mnt, name); ++ error = removexattr(nd.path.dentry, nd.path.mnt, name, NULL); + mnt_drop_write(nd.path.mnt); + } + path_put(&nd.path); +@@ -518,7 +523,7 @@ sys_lremovexattr(const char __user *path + return error; + error = mnt_want_write(nd.path.mnt); + if (!error) { +- error = removexattr(nd.path.dentry, nd.path.mnt, name); ++ error = removexattr(nd.path.dentry, nd.path.mnt, name, NULL); + mnt_drop_write(nd.path.mnt); + } + path_put(&nd.path); +@@ -539,7 +544,7 @@ sys_fremovexattr(int fd, const char __us + audit_inode(NULL, dentry); + error = mnt_want_write(f->f_path.mnt); + if (!error) { +- error = removexattr(dentry, f->f_path.mnt, name); ++ error = removexattr(dentry, f->f_path.mnt, name, f); + mnt_drop_write(f->f_path.mnt); + } + fput(f); +--- a/include/linux/security.h ++++ b/include/linux/security.h +@@ -55,9 +55,9 @@ extern void cap_bprm_apply_creds(struct + extern int cap_bprm_secureexec(struct linux_binprm *bprm); + extern int cap_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, + const char *name, const void *value, size_t size, +- int flags); ++ int flags, struct file *file); + extern int cap_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt, +- const char *name); ++ const 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); +@@ -1397,16 +1397,17 @@ struct security_operations { + void (*inode_delete) (struct inode *inode); + int (*inode_setxattr) (struct dentry *dentry, struct vfsmount *mnt, + const char *name, const void *value, size_t size, +- int flags); ++ int flags, struct file *file); + void (*inode_post_setxattr) (struct dentry *dentry, + struct vfsmount *mnt, + const char *name, const void *value, + size_t size, int flags); + int (*inode_getxattr) (struct dentry *dentry, struct vfsmount *mnt, +- const char *name); +- int (*inode_listxattr) (struct dentry *dentry, struct vfsmount *mnt); ++ const 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, +- const char *name); ++ const 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); +@@ -1681,15 +1682,16 @@ int security_inode_getattr(struct vfsmou + void security_inode_delete(struct inode *inode); + int security_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, + const char *name, const void *value, +- size_t size, int flags); ++ size_t size, int flags, struct file *file); + void security_inode_post_setxattr(struct dentry *dentry, struct vfsmount *mnt, + const char *name, const void *value, + size_t size, int flags); + int security_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt, +- const char *name); +-int security_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt); ++ const 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, +- const char *name); ++ const 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); +@@ -2106,9 +2108,10 @@ static inline void security_inode_delete + static inline int security_inode_setxattr(struct dentry *dentry, + struct vfsmount *mnt, + const char *name, const void *value, +- size_t size, int flags) ++ 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, +@@ -2120,22 +2123,25 @@ static inline void security_inode_post_s + + static inline int security_inode_getxattr(struct dentry *dentry, + struct vfsmount *mnt, +- const char *name) ++ const 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, +- const char *name) ++ const 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,10 +47,10 @@ struct xattr_handler { + }; + + ssize_t xattr_getsecurity(struct inode *, const char *, void *, size_t); +-ssize_t vfs_getxattr(struct dentry *, struct vfsmount *, const char *, void *, size_t); +-ssize_t vfs_listxattr(struct dentry *d, struct vfsmount *, char *list, size_t size); +-int vfs_setxattr(struct dentry *, struct vfsmount *, const char *, const void *, size_t, int); +-int vfs_removexattr(struct dentry *, struct vfsmount *mnt, const char *); ++ssize_t vfs_getxattr(struct dentry *, struct vfsmount *, const char *, void *, size_t, struct file *file); ++ssize_t vfs_listxattr(struct dentry *d, struct vfsmount *, char *list, size_t size, struct file *file); ++int vfs_setxattr(struct dentry *, struct vfsmount *, const char *, const void *, size_t, int, struct file *file); ++int vfs_removexattr(struct dentry *, struct vfsmount *mnt, const char *, struct file *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 +@@ -385,7 +385,7 @@ int cap_bprm_secureexec (struct linux_bi + + int cap_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, + const char *name, const void *value, size_t size, +- int flags) ++ int flags, struct file *file) + { + if (!strcmp(name, XATTR_NAME_CAPS)) { + if (!capable(CAP_SETFCAP)) +@@ -399,7 +399,7 @@ int cap_inode_setxattr(struct dentry *de + } + + int cap_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt, +- const char *name) ++ const char *name, struct file *file) + { + if (!strcmp(name, XATTR_NAME_CAPS)) { + if (!capable(CAP_SETFCAP)) +--- a/security/dummy.c ++++ b/security/dummy.c +@@ -376,7 +376,7 @@ static void dummy_inode_delete (struct i + + static int dummy_inode_setxattr (struct dentry *dentry, struct vfsmount *mnt, + const char *name, const void *value, +- size_t size, int flags) ++ size_t size, int flags, struct file *file) + { + if (!strncmp(name, XATTR_SECURITY_PREFIX, + sizeof(XATTR_SECURITY_PREFIX) - 1) && +@@ -393,18 +393,19 @@ static void dummy_inode_post_setxattr (s + } + + static int dummy_inode_getxattr (struct dentry *dentry, struct vfsmount *mnt, +- const char *name) ++ const 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, +- const char *name) ++ const char *name, struct file *file) + { + if (!strncmp(name, XATTR_SECURITY_PREFIX, + sizeof(XATTR_SECURITY_PREFIX) - 1) && +--- a/security/security.c ++++ b/security/security.c +@@ -502,12 +502,12 @@ void security_inode_delete(struct inode + + int security_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, + const char *name, const void *value, size_t size, +- int flags) ++ 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, +@@ -521,26 +521,27 @@ void security_inode_post_setxattr(struct + } + + int security_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt, +- const char *name) ++ const 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, +- const char *name) ++ const 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 +@@ -2657,7 +2657,7 @@ static int selinux_inode_setotherxattr(s + + static int selinux_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, + const char *name, const void *value, +- size_t size, int flags) ++ size_t size, int flags, struct file *file) + { + struct task_security_struct *tsec = current->security; + struct inode *inode = dentry->d_inode; +@@ -2733,18 +2733,20 @@ static void selinux_inode_post_setxattr( + } + + static int selinux_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt, +- const char *name) ++ const 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, const char *name) ++ struct vfsmount *mnt, const char *name, ++ struct file *file) + { + if (strcmp(name, XATTR_NAME_SELINUX)) + return selinux_inode_setotherxattr(dentry, name); diff --git a/kernel-patches/2.6.26/series b/kernel-patches/2.6.26/series new file mode 100644 index 000000000..d9cb21693 --- /dev/null +++ b/kernel-patches/2.6.26/series @@ -0,0 +1,50 @@ +security-create.diff +remove_suid.diff +vfs-notify_change.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 +d_namespace_path.diff +fsetattr.diff +fsetattr-reintro-ATTR_FILE.diff +fsetattr-restore-ia_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 +apparmor-network.diff +apparmor-rlimits.diff +apparmor-2.6.25.diff +apparmor-stack_secondary.diff diff --git a/kernel-patches/2.6.26/sys_fchdir-nameidata.diff b/kernel-patches/2.6.26/sys_fchdir-nameidata.diff new file mode 100644 index 000000000..1c869ac78 --- /dev/null +++ b/kernel-patches/2.6.26/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 +@@ -512,8 +512,8 @@ out: + + asmlinkage long sys_fchdir(unsigned int fd) + { ++ struct nameidata nd = { .flags = 0 }; + struct file *file; +- struct inode *inode; + int error; + + error = -EBADF; +@@ -521,12 +521,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.26/sysctl-pathname.diff b/kernel-patches/2.6.26/sysctl-pathname.diff new file mode 100644 index 000000000..367386066 --- /dev/null +++ b/kernel-patches/2.6.26/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 | 34 +++++----------------------------- + 3 files changed, 34 insertions(+), 29 deletions(-) + +--- a/include/linux/sysctl.h ++++ b/include/linux/sysctl.h +@@ -980,6 +980,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 +@@ -1435,6 +1435,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 + /* Perform the actual read/write of a sysctl table entry. */ + static int do_sysctl_strategy(struct ctl_table_root *root, +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -1736,40 +1736,16 @@ 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.26/unambiguous-__d_path.diff b/kernel-patches/2.6.26/unambiguous-__d_path.diff new file mode 100644 index 000000000..d85f9423c --- /dev/null +++ b/kernel-patches/2.6.26/unambiguous-__d_path.diff @@ -0,0 +1,257 @@ +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 | 126 ++++++++++++++++++++++++++----------------------- + fs/seq_file.c | 4 - + include/linux/dcache.h | 5 + + 3 files changed, 74 insertions(+), 61 deletions(-) + +--- a/fs/dcache.c ++++ b/fs/dcache.c +@@ -1768,44 +1768,46 @@ static int prepend_name(char **buffer, i + * @root: root vfsmnt/dentry (may be modified by this function) + * @buffer: buffer to return value in + * @buflen: buffer length ++ * @flags: flags controling behavior of d_path + * +- * Convert a dentry into an ASCII path name. If the entry has been deleted +- * the string " (deleted)" is appended. Note that this is ambiguous. +- * +- * Returns the buffer or an error code if the path was too long. +- * +- * "buflen" should be positive. Caller holds the dcache_lock. ++ * Convert a dentry into an ASCII path name. If the entry has been deleted, ++ * then if @flags has D_PATH_FAIL_DELETED set, ERR_PTR(-ENOENT) is returned. ++ * Otherwise, the string " (deleted)" is appended. Note that this is ambiguous. + * + * If path is not reachable from the supplied root, then the value of +- * root is changed (without modifying refcounts). ++ * root is changed (without modifying refcounts). The path returned in this ++ * case will be relative (i.e., it will not start with a slash). ++ * ++ * Returns the buffer or an error code if the path was too long. + */ + char *__d_path(const struct path *path, struct path *root, +- char *buffer, int buflen) ++ char *buffer, int buflen, int flags) + { + struct dentry *dentry = path->dentry; + struct vfsmount *vfsmnt = path->mnt; +- char *end = buffer + buflen; +- char *retval; ++ const unsigned char *name; ++ int namelen; ++ ++ buffer += buflen; ++ prepend(&buffer, &buflen, "\0", 1); + + spin_lock(&vfsmount_lock); +- prepend(&end, &buflen, "\0", 1); +- if (!IS_ROOT(dentry) && d_unhashed(dentry) && +- (prepend(&end, &buflen, " (deleted)", 10) != 0)) ++ spin_lock(&dcache_lock); ++ if (!IS_ROOT(dentry) && d_unhashed(dentry)) { ++ if (flags & D_PATH_FAIL_DELETED) { ++ buffer = ERR_PTR(-ENOENT); ++ goto out; ++ } ++ if (prepend(&buffer, &buflen, " (deleted)", 10) != 0) + goto Elong; +- ++ } + 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? */ + if (vfsmnt->mnt_parent == vfsmnt) { + goto global_root; + } +@@ -1815,27 +1817,51 @@ char *__d_path(const struct path *path, + } + parent = dentry->d_parent; + prefetch(parent); +- if ((prepend_name(&end, &buflen, &dentry->d_name) != 0) || +- (prepend(&end, &buflen, "/", 1) != 0)) ++ if ((prepend_name(&buffer, &buflen, &dentry->d_name) != 0) || ++ (prepend(&buffer, &buflen, "/", 1) != 0)) + goto Elong; +- retval = end; + dentry = parent; + } ++ /* Get '/' right. */ ++ if (*buffer != '/' && prepend(&buffer, &buflen, "/", 1)) ++ goto Elong; + + out: ++ spin_unlock(&dcache_lock); + spin_unlock(&vfsmount_lock); +- return retval; ++ return buffer; + + global_root: +- retval += 1; /* hit the slash */ +- if (prepend_name(&retval, &buflen, &dentry->d_name) != 0) ++ /* ++ * 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; ++ name = dentry->d_name.name; ++ ++ /* ++ * If this is a root dentry, then overwrite the slash. This ++ * will also DTRT with pseudo filesystems which have root ++ * dentries named "foo:". ++ */ ++ if (IS_ROOT(dentry) && *buffer == '/') { ++ buffer++; ++ buflen++; ++ } ++ if ((flags & D_PATH_DISCONNECT) && *name == '/') { ++ /* Make sure we won't return a pathname starting with '/' */ ++ name++; ++ namelen--; ++ } ++ if (prepend(&buffer, &buflen, name, namelen)) + goto Elong; + root->mnt = vfsmnt; + root->dentry = dentry; + goto out; + + Elong: +- retval = ERR_PTR(-ENAMETOOLONG); ++ buffer = ERR_PTR(-ENAMETOOLONG); + goto out; + } + +@@ -1872,10 +1898,8 @@ char *d_path(const struct path *path, ch + root = current->fs->root; + path_get(&root); + read_unlock(¤t->fs->lock); +- spin_lock(&dcache_lock); + tmp = root; +- res = __d_path(path, &tmp, buf, buflen); +- spin_unlock(&dcache_lock); ++ res = __d_path(path, &tmp, buf, buflen, 0); + path_put(&root); + return res; + } +@@ -1958,9 +1982,9 @@ Elong: + */ + 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; +@@ -1972,30 +1996,18 @@ asmlinkage long sys_getcwd(char __user * + path_get(&root); + read_unlock(¤t->fs->lock); + +- error = -ENOENT; +- /* Has the current directory has been unlinked? */ +- spin_lock(&dcache_lock); +- if (IS_ROOT(pwd.dentry) || !d_unhashed(pwd.dentry)) { +- unsigned long len; +- struct path tmp = root; +- char * cwd; +- +- cwd = __d_path(&pwd, &tmp, 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, &root, page, PAGE_SIZE, 0); ++ 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); +--- a/fs/seq_file.c ++++ b/fs/seq_file.c +@@ -405,9 +405,7 @@ int seq_path_root(struct seq_file *m, st + char *s = m->buf + m->count; + char *p; + +- spin_lock(&dcache_lock); +- p = __d_path(path, root, s, m->size - m->count); +- spin_unlock(&dcache_lock); ++ p = __d_path(path, root, s, m->size - m->count, 0); + err = PTR_ERR(p); + if (!IS_ERR(p)) { + s = mangle_path(s, p, esc); +--- a/include/linux/dcache.h ++++ b/include/linux/dcache.h +@@ -297,9 +297,12 @@ extern int d_validate(struct dentry *, s + /* + * helper function for dentry_operations.d_dname() members + */ ++#define D_PATH_FAIL_DELETED 1 ++#define D_PATH_DISCONNECT 2 + extern char *dynamic_dname(struct dentry *, char *, int, const char *, ...); + +-extern char *__d_path(const struct path *path, struct path *root, char *, int); ++extern char *__d_path(const struct path *path, struct path *root, char *, int, ++ int); + extern char *d_path(const struct path *, char *, int); + extern char *dentry_path(struct dentry *, char *, int); + diff --git a/kernel-patches/2.6.26/vfs-getxattr.diff b/kernel-patches/2.6.26/vfs-getxattr.diff new file mode 100644 index 000000000..0b14aaa48 --- /dev/null +++ b/kernel-patches/2.6.26/vfs-getxattr.diff @@ -0,0 +1,190 @@ +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 | 15 ++++++++------- + include/linux/nfsd/nfsd.h | 3 ++- + include/linux/xattr.h | 2 +- + 5 files changed, 24 insertions(+), 19 deletions(-) + +--- a/fs/nfsd/nfs4xdr.c ++++ b/fs/nfsd/nfs4xdr.c +@@ -1487,7 +1487,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 +@@ -407,11 +407,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; + +@@ -419,7 +420,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 + +@@ -500,13 +501,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) +@@ -518,14 +519,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)) { +@@ -535,7 +537,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)) { +@@ -2058,7 +2060,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 +@@ -131,7 +131,8 @@ out_noalloc: + EXPORT_SYMBOL_GPL(xattr_getsecurity); + + ssize_t +-vfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size) ++vfs_getxattr(struct dentry *dentry, struct vfsmount *mnt, const char *name, ++ void *value, size_t size) + { + struct inode *inode = dentry->d_inode; + int error; +@@ -315,8 +316,8 @@ sys_fsetxattr(int fd, const char __user + * Extended attribute GET operations + */ + static ssize_t +-getxattr(struct dentry *d, const char __user *name, void __user *value, +- size_t size) ++getxattr(struct dentry *dentry, struct vfsmount *mnt, const char __user *name, ++ void __user *value, size_t size) + { + ssize_t error; + void *kvalue = NULL; +@@ -336,7 +337,7 @@ getxattr(struct dentry *d, const char __ + 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; +@@ -359,7 +360,7 @@ sys_getxattr(const char __user *path, co + 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; + } +@@ -374,7 +375,7 @@ sys_lgetxattr(const char __user *path, c + 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; + } +@@ -389,7 +390,7 @@ sys_fgetxattr(int fd, const char __user + 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 +@@ -85,7 +85,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 *, const char *, void *, size_t); ++ssize_t vfs_getxattr(struct dentry *, struct vfsmount *, const char *, void *, size_t); + ssize_t vfs_listxattr(struct dentry *d, char *list, size_t size); + int vfs_setxattr(struct dentry *, struct vfsmount *, const char *, const void *, size_t, int); + int vfs_removexattr(struct dentry *, const char *); diff --git a/kernel-patches/2.6.26/vfs-link.diff b/kernel-patches/2.6.26/vfs-link.diff new file mode 100644 index 000000000..2d7ff66ea --- /dev/null +++ b/kernel-patches/2.6.26/vfs-link.diff @@ -0,0 +1,91 @@ +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 | 6 ++++-- + fs/nfsd/vfs.c | 3 ++- + include/linux/fs.h | 2 +- + 4 files changed, 14 insertions(+), 6 deletions(-) + +--- a/fs/ecryptfs/inode.c ++++ b/fs/ecryptfs/inode.c +@@ -382,19 +382,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 +@@ -2481,7 +2481,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; +@@ -2562,7 +2562,9 @@ asmlinkage long sys_linkat(int olddfd, c + error = mnt_want_write(nd.path.mnt); + if (error) + goto out_dput; +- 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); + mnt_drop_write(nd.path.mnt); + out_dput: + dput(new_dentry); +--- a/fs/nfsd/vfs.c ++++ b/fs/nfsd/vfs.c +@@ -1629,7 +1629,8 @@ nfsd_link(struct svc_rqst *rqstp, struct + err = nfserrno(host_err); + goto out_dput; + } +- 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 +@@ -1126,7 +1126,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.26/vfs-listxattr.diff b/kernel-patches/2.6.26/vfs-listxattr.diff new file mode 100644 index 000000000..4ff046f7d --- /dev/null +++ b/kernel-patches/2.6.26/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 +@@ -168,18 +168,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; + } +@@ -399,7 +401,8 @@ sys_fgetxattr(int fd, const char __user + * 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; +@@ -412,7 +415,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; +@@ -434,7 +437,7 @@ sys_listxattr(const char __user *path, c + 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; + } +@@ -448,7 +451,7 @@ sys_llistxattr(const char __user *path, + 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; + } +@@ -463,7 +466,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 *, const 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 *, const char *, const void *, size_t, int); + int vfs_removexattr(struct dentry *, const char *); + diff --git a/kernel-patches/2.6.26/vfs-mkdir.diff b/kernel-patches/2.6.26/vfs-mkdir.diff new file mode 100644 index 000000000..c6406be0e --- /dev/null +++ b/kernel-patches/2.6.26/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 +@@ -482,11 +482,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 +@@ -2127,7 +2127,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); + +@@ -2174,7 +2175,7 @@ asmlinkage long sys_mkdirat(int dfd, con + error = mnt_want_write(nd.path.mnt); + if (error) + goto out_dput; +- error = vfs_mkdir(nd.path.dentry->d_inode, dentry, mode); ++ error = vfs_mkdir(nd.path.dentry->d_inode, dentry, nd.path.mnt, mode); + mnt_drop_write(nd.path.mnt); + out_dput: + dput(dentry); +--- a/fs/nfsd/nfs4recover.c ++++ b/fs/nfsd/nfs4recover.c +@@ -158,7 +158,8 @@ nfsd4_create_clid_dir(struct nfs4_client + status = mnt_want_write(rec_dir.path.mnt); + if (status) + 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); + mnt_drop_write(rec_dir.path.mnt); + out_put: + dput(dentry); +--- a/fs/nfsd/vfs.c ++++ b/fs/nfsd/vfs.c +@@ -1185,6 +1185,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; +@@ -1202,6 +1203,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; +@@ -1218,7 +1220,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 { +@@ -1263,7 +1265,7 @@ nfsd_create(struct svc_rqst *rqstp, stru + host_err = mnt_want_write(fhp->fh_export->ex_path.mnt); + if (host_err) + goto out_nfserr; +- 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: +@@ -1284,7 +1286,7 @@ nfsd_create(struct svc_rqst *rqstp, stru + 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 +@@ -1123,7 +1123,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 +@@ -2928,7 +2928,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.26/vfs-mknod.diff b/kernel-patches/2.6.26/vfs-mknod.diff new file mode 100644 index 000000000..13e08bf32 --- /dev/null +++ b/kernel-patches/2.6.26/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 +@@ -533,11 +533,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 +@@ -2023,7 +2023,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); + +@@ -2103,11 +2104,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; + } + mnt_drop_write(nd.path.mnt); +--- a/fs/nfsd/vfs.c ++++ b/fs/nfsd/vfs.c +@@ -1274,7 +1274,8 @@ nfsd_create(struct svc_rqst *rqstp, stru + host_err = mnt_want_write(fhp->fh_export->ex_path.mnt); + if (host_err) + goto out_nfserr; +- 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 +@@ -1124,7 +1124,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 +@@ -829,7 +829,8 @@ static int unix_bind(struct socket *sock + err = mnt_want_write(nd.path.mnt); + if (err) + goto out_mknod_dput; +- 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); + mnt_drop_write(nd.path.mnt); + if (err) + goto out_mknod_dput; diff --git a/kernel-patches/2.6.26/vfs-notify_change.diff b/kernel-patches/2.6.26/vfs-notify_change.diff new file mode 100644 index 000000000..dc8e35247 --- /dev/null +++ b/kernel-patches/2.6.26/vfs-notify_change.diff @@ -0,0 +1,399 @@ +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 | 17 +++++++---------- + include/linux/fs.h | 6 +++--- + mm/filemap.c | 2 +- + mm/tiny-shmem.c | 2 +- + 14 files changed, 45 insertions(+), 42 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 +@@ -843,6 +843,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; +@@ -853,6 +854,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); +@@ -904,7 +906,7 @@ static int ecryptfs_setattr(struct dentr + ia->ia_valid &= ~ATTR_MODE; + + mutex_lock(&lower_dentry->d_inode->i_mutex); +- rc = notify_change(lower_dentry, ia); ++ rc = notify_change(lower_dentry, lower_mnt, ia); + mutex_unlock(&lower_dentry->d_inode->i_mutex); + out: + fsstack_copy_attr_all(inode, lower_inode, NULL); +--- a/fs/exec.c ++++ b/fs/exec.c +@@ -1772,7 +1772,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 +@@ -1666,7 +1666,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 +@@ -387,7 +387,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); + } +@@ -943,13 +943,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); + } + +@@ -1008,7 +1008,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 +@@ -195,8 +195,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; +@@ -216,7 +216,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; + } +@@ -272,7 +272,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: +@@ -327,7 +327,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: +@@ -590,7 +591,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_drop_write: +@@ -627,7 +628,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); + + out_drop_write: +@@ -643,7 +644,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; +@@ -670,7 +672,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; +@@ -687,7 +689,7 @@ asmlinkage long sys_chown(const char __u + error = mnt_want_write(nd.path.mnt); + if (error) + goto out_release; +- error = chown_common(nd.path.dentry, user, group); ++ error = chown_common(nd.path.dentry, nd.path.mnt, user, group); + mnt_drop_write(nd.path.mnt); + out_release: + path_put(&nd.path); +@@ -712,7 +714,7 @@ asmlinkage long sys_fchownat(int dfd, co + error = mnt_want_write(nd.path.mnt); + if (error) + goto out_release; +- error = chown_common(nd.path.dentry, user, group); ++ error = chown_common(nd.path.dentry, nd.path.mnt, user, group); + mnt_drop_write(nd.path.mnt); + out_release: + path_put(&nd.path); +@@ -731,7 +733,7 @@ asmlinkage long sys_lchown(const char __ + error = mnt_want_write(nd.path.mnt); + if (error) + goto out_release; +- error = chown_common(nd.path.dentry, user, group); ++ error = chown_common(nd.path.dentry, nd.path.mnt, user, group); + mnt_drop_write(nd.path.mnt); + out_release: + path_put(&nd.path); +@@ -755,7 +757,7 @@ asmlinkage long sys_fchown(unsigned int + goto out_fput; + 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); + mnt_drop_write(file->f_path.mnt); + out_fput: + fput(file); +--- a/fs/reiserfs/xattr.c ++++ b/fs/reiserfs/xattr.c +@@ -459,7 +459,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; + +@@ -790,7 +790,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; +@@ -834,7 +834,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 +@@ -585,7 +585,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 +@@ -56,11 +56,10 @@ 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; +- struct vfsmount *mnt; + + error = -EINVAL; + if (times && (!nsec_valid(times[0].tv_nsec) || +@@ -80,20 +79,18 @@ long do_utimes(int dfd, char __user *fil + f = fget(dfd); + if (!f) + goto out; +- dentry = f->f_path.dentry; +- mnt = f->f_path.mnt; ++ 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; +- mnt = nd.path.mnt; ++ path = nd.path; + } + +- inode = dentry->d_inode; ++ inode = path.dentry->d_inode; + +- error = mnt_want_write(mnt); ++ error = mnt_want_write(path.mnt); + if (error) + goto dput_and_out; + +@@ -154,10 +151,10 @@ 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); + mnt_drop_write_and_out: +- mnt_drop_write(mnt); ++ mnt_drop_write(path.mnt); + dput_and_out: + if (f) + fput(f); +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -1596,8 +1596,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); +@@ -1751,7 +1751,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 +@@ -1665,7 +1665,7 @@ static int __remove_suid(struct path *pa + 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.26/vfs-removexattr.diff b/kernel-patches/2.6.26/vfs-removexattr.diff new file mode 100644 index 000000000..d7f65cfc1 --- /dev/null +++ b/kernel-patches/2.6.26/vfs-removexattr.diff @@ -0,0 +1,121 @@ +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 | 11 ++++++----- + fs/xattr.c | 12 ++++++------ + include/linux/xattr.h | 2 +- + 3 files changed, 13 insertions(+), 12 deletions(-) + +--- a/fs/nfsd/vfs.c ++++ b/fs/nfsd/vfs.c +@@ -2073,6 +2073,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; +@@ -2105,22 +2106,22 @@ nfsd_set_posix_acl(struct svc_fh *fhp, i + } else + size = 0; + +- error = mnt_want_write(fhp->fh_export->ex_path.mnt); ++ mnt = fhp->fh_export->ex_path.mnt; ++ error = mnt_want_write(mnt); + if (error) + goto getout; + 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; + } + } +- mnt_drop_write(fhp->fh_export->ex_path.mnt); ++ mnt_drop_write(mnt); + + getout: + kfree(value); +--- a/fs/xattr.c ++++ b/fs/xattr.c +@@ -190,7 +190,7 @@ vfs_listxattr(struct dentry *dentry, str + EXPORT_SYMBOL_GPL(vfs_listxattr); + + int +-vfs_removexattr(struct dentry *dentry, const char *name) ++vfs_removexattr(struct dentry *dentry, struct vfsmount *mnt, const char *name) + { + struct inode *inode = dentry->d_inode; + int error; +@@ -475,7 +475,7 @@ sys_flistxattr(int fd, char __user *list + * Extended attribute REMOVE operations + */ + static long +-removexattr(struct dentry *d, const char __user *name) ++removexattr(struct dentry *dentry, struct vfsmount *mnt, const char __user *name) + { + int error; + char kname[XATTR_NAME_MAX + 1]; +@@ -486,7 +486,7 @@ removexattr(struct dentry *d, const char + if (error < 0) + return error; + +- return vfs_removexattr(d, kname); ++ return vfs_removexattr(dentry, mnt, kname); + } + + asmlinkage long +@@ -500,7 +500,7 @@ sys_removexattr(const char __user *path, + return error; + error = mnt_want_write(nd.path.mnt); + if (!error) { +- error = removexattr(nd.path.dentry, name); ++ error = removexattr(nd.path.dentry, nd.path.mnt, name); + mnt_drop_write(nd.path.mnt); + } + path_put(&nd.path); +@@ -518,7 +518,7 @@ sys_lremovexattr(const char __user *path + return error; + error = mnt_want_write(nd.path.mnt); + if (!error) { +- error = removexattr(nd.path.dentry, name); ++ error = removexattr(nd.path.dentry, nd.path.mnt, name); + mnt_drop_write(nd.path.mnt); + } + path_put(&nd.path); +@@ -539,7 +539,7 @@ sys_fremovexattr(int fd, const char __us + audit_inode(NULL, dentry); + error = mnt_want_write(f->f_path.mnt); + if (!error) { +- error = removexattr(dentry, name); ++ error = removexattr(dentry, f->f_path.mnt, name); + mnt_drop_write(f->f_path.mnt); + } + fput(f); +--- a/include/linux/xattr.h ++++ b/include/linux/xattr.h +@@ -50,7 +50,7 @@ ssize_t xattr_getsecurity(struct inode * + ssize_t vfs_getxattr(struct dentry *, struct vfsmount *, const char *, void *, size_t); + ssize_t vfs_listxattr(struct dentry *d, struct vfsmount *, char *list, size_t size); + int vfs_setxattr(struct dentry *, struct vfsmount *, const char *, const void *, size_t, int); +-int vfs_removexattr(struct dentry *, const char *); ++int vfs_removexattr(struct dentry *, struct vfsmount *mnt, const 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.26/vfs-rename.diff b/kernel-patches/2.6.26/vfs-rename.diff new file mode 100644 index 000000000..ddcd9e89e --- /dev/null +++ b/kernel-patches/2.6.26/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 +@@ -571,19 +571,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 +@@ -2620,7 +2620,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; +@@ -2663,7 +2664,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; +@@ -2691,7 +2693,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); +@@ -2720,9 +2723,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, +@@ -2797,8 +2802,8 @@ static int do_rename(int olddfd, const c + error = mnt_want_write(oldnd.path.mnt); + if (error) + 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); + mnt_drop_write(oldnd.path.mnt); + exit5: + dput(new_dentry); +--- a/fs/nfsd/vfs.c ++++ b/fs/nfsd/vfs.c +@@ -1731,7 +1731,8 @@ nfsd_rename(struct svc_rqst *rqstp, stru + if (host_err) + goto out_dput_new; + +- 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 +@@ -1129,7 +1129,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.26/vfs-rmdir.diff b/kernel-patches/2.6.26/vfs-rmdir.diff new file mode 100644 index 000000000..269fe31f2 --- /dev/null +++ b/kernel-patches/2.6.26/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 +@@ -515,14 +515,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 +@@ -2222,7 +2222,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); + +@@ -2289,7 +2289,7 @@ static long do_rmdir(int dfd, const char + error = mnt_want_write(nd.path.mnt); + if (error) + goto exit3; +- error = vfs_rmdir(nd.path.dentry->d_inode, dentry); ++ error = vfs_rmdir(nd.path.dentry->d_inode, dentry, nd.path.mnt); + mnt_drop_write(nd.path.mnt); + exit3: + dput(dentry); +--- a/fs/nfsd/nfs4recover.c ++++ b/fs/nfsd/nfs4recover.c +@@ -279,7 +279,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 +@@ -1769,6 +1769,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; +@@ -1783,6 +1784,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); +@@ -1804,21 +1806,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_drop; +- if (EX_ISSYNC(fhp->fh_export)) ++ if (EX_ISSYNC(exp)) + host_err = nfsd_sync_dir(dentry); + + out_drop: +--- a/fs/reiserfs/xattr.c ++++ b/fs/reiserfs/xattr.c +@@ -746,7 +746,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 +@@ -1127,7 +1127,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.26/vfs-setxattr.diff b/kernel-patches/2.6.26/vfs-setxattr.diff new file mode 100644 index 000000000..698fb32fd --- /dev/null +++ b/kernel-patches/2.6.26/vfs-setxattr.diff @@ -0,0 +1,151 @@ +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 | 2 +- + 3 files changed, 20 insertions(+), 14 deletions(-) + +--- a/fs/nfsd/vfs.c ++++ b/fs/nfsd/vfs.c +@@ -425,7 +425,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; +@@ -444,7 +445,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; +@@ -457,6 +458,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; +@@ -467,6 +469,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; +@@ -477,12 +480,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); +@@ -2101,7 +2106,8 @@ nfsd_set_posix_acl(struct svc_fh *fhp, i + if (error) + goto getout; + 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 +@@ -67,8 +67,8 @@ xattr_permission(struct inode *inode, co + } + + int +-vfs_setxattr(struct dentry *dentry, const char *name, const void *value, +- size_t size, int flags) ++vfs_setxattr(struct dentry *dentry, struct vfsmount *mnt, const char *name, ++ const void *value, size_t size, int flags) + { + struct inode *inode = dentry->d_inode; + int error; +@@ -218,8 +218,8 @@ EXPORT_SYMBOL_GPL(vfs_removexattr); + * Extended attribute SET operations + */ + static long +-setxattr(struct dentry *d, const char __user *name, const void __user *value, +- size_t size, int flags) ++setxattr(struct dentry *dentry, struct vfsmount *mnt, const char __user *name, ++ const void __user *value, size_t size, int flags) + { + int error; + void *kvalue = NULL; +@@ -246,7 +246,7 @@ setxattr(struct dentry *d, const char __ + } + } + +- error = vfs_setxattr(d, kname, kvalue, size, flags); ++ error = vfs_setxattr(dentry, mnt, kname, kvalue, size, flags); + kfree(kvalue); + return error; + } +@@ -263,7 +263,7 @@ sys_setxattr(const char __user *path, co + return error; + error = mnt_want_write(nd.path.mnt); + if (!error) { +- error = setxattr(nd.path.dentry, name, value, size, flags); ++ error = setxattr(nd.path.dentry, nd.path.mnt, name, value, size, flags); + mnt_drop_write(nd.path.mnt); + } + path_put(&nd.path); +@@ -282,7 +282,7 @@ sys_lsetxattr(const char __user *path, c + return error; + error = mnt_want_write(nd.path.mnt); + if (!error) { +- error = setxattr(nd.path.dentry, name, value, size, flags); ++ error = setxattr(nd.path.dentry, nd.path.mnt, name, value, size, flags); + mnt_drop_write(nd.path.mnt); + } + path_put(&nd.path); +@@ -304,7 +304,7 @@ sys_fsetxattr(int fd, const char __user + audit_inode(NULL, dentry); + error = mnt_want_write(f->f_path.mnt); + if (!error) { +- error = setxattr(dentry, name, value, size, flags); ++ error = setxattr(dentry, f->f_vfsmnt, name, value, size, flags); + mnt_drop_write(f->f_path.mnt); + } + fput(f); +--- a/include/linux/xattr.h ++++ b/include/linux/xattr.h +@@ -49,7 +49,7 @@ struct xattr_handler { + ssize_t xattr_getsecurity(struct inode *, const char *, void *, size_t); + ssize_t vfs_getxattr(struct dentry *, const char *, void *, size_t); + ssize_t vfs_listxattr(struct dentry *d, char *list, size_t size); +-int vfs_setxattr(struct dentry *, const char *, const void *, size_t, int); ++int vfs_setxattr(struct dentry *, struct vfsmount *, const char *, const void *, size_t, int); + int vfs_removexattr(struct dentry *, const char *); + + ssize_t generic_getxattr(struct dentry *dentry, const char *name, void *buffer, size_t size); diff --git a/kernel-patches/2.6.26/vfs-symlink.diff b/kernel-patches/2.6.26/vfs-symlink.diff new file mode 100644 index 000000000..552cbacc9 --- /dev/null +++ b/kernel-patches/2.6.26/vfs-symlink.diff @@ -0,0 +1,124 @@ +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 +@@ -443,6 +443,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; +@@ -451,6 +452,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, +@@ -460,7 +462,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 +@@ -2411,7 +2411,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); + +@@ -2460,7 +2461,8 @@ asmlinkage long sys_symlinkat(const char + error = mnt_want_write(nd.path.mnt); + if (error) + goto out_dput; +- 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); + mnt_drop_write(nd.path.mnt); + out_dput: + dput(dentry); +--- a/fs/nfsd/vfs.c ++++ b/fs/nfsd/vfs.c +@@ -1518,6 +1518,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; +@@ -1548,6 +1549,7 @@ nfsd_symlink(struct svc_rqst *rqstp, str + if (host_err) + goto out_nfserr; + ++ exp = fhp->fh_export; + if (unlikely(path[plen] != 0)) { + char *path_alloced = kmalloc(plen+1, GFP_KERNEL); + if (path_alloced == NULL) +@@ -1555,14 +1557,16 @@ 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); +@@ -1570,7 +1574,7 @@ nfsd_symlink(struct svc_rqst *rqstp, str + + mnt_drop_write(fhp->fh_export->ex_path.mnt); + +- 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 +@@ -1125,7 +1125,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.26/vfs-unlink.diff b/kernel-patches/2.6.26/vfs-unlink.diff new file mode 100644 index 000000000..fb6f62a13 --- /dev/null +++ b/kernel-patches/2.6.26/vfs-unlink.diff @@ -0,0 +1,99 @@ +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 +@@ -424,11 +424,12 @@ 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); + struct dentry *lower_dir_dentry; + + lower_dir_dentry = 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 +@@ -2308,7 +2308,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); + +@@ -2376,7 +2376,7 @@ static long do_unlinkat(int dfd, const c + error = mnt_want_write(nd.path.mnt); + if (error) + goto exit2; +- error = vfs_unlink(nd.path.dentry->d_inode, dentry); ++ error = vfs_unlink(nd.path.dentry->d_inode, dentry, nd.path.mnt); + mnt_drop_write(nd.path.mnt); + exit2: + dput(dentry); +--- a/fs/nfsd/nfs4recover.c ++++ b/fs/nfsd/nfs4recover.c +@@ -264,7 +264,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 +@@ -1811,7 +1811,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 +@@ -1128,7 +1128,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 +@@ -756,7 +756,7 @@ asmlinkage long sys_mq_unlink(const char + err = mnt_want_write(mqueue_mnt); + if (err) + goto out_err; +- err = vfs_unlink(dentry->d_parent->d_inode, dentry); ++ err = vfs_unlink(dentry->d_parent->d_inode, dentry, mqueue_mnt); + mnt_drop_write(mqueue_mnt); + out_err: + dput(dentry);