From 28ba83a313f4083ff999b6f5422ab601f1eaeeee Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 10 Dec 2008 17:57:41 +0000 Subject: [PATCH] Update kernel patches for 2.6.28 --- .../2.6.28/AppArmor-checkpatch.diff | 506 ++++++ .../2.6.28/AppArmor-misc-cleanups.diff | 260 +++ .../2.6.28/add-path_permission.diff | 201 +++ .../2.6.28/add-security_path_permission | 109 ++ kernel-patches/2.6.28/apparmor-2.6.25.diff | 47 + kernel-patches/2.6.28/apparmor-audit.diff | 72 + kernel-patches/2.6.28/apparmor-intree.diff | 31 + kernel-patches/2.6.28/apparmor-lsm.diff | 910 ++++++++++ kernel-patches/2.6.28/apparmor-main.diff | 1494 +++++++++++++++++ kernel-patches/2.6.28/apparmor-misc.diff | 1441 ++++++++++++++++ .../2.6.28/apparmor-module_interface.diff | 1350 +++++++++++++++ kernel-patches/2.6.28/apparmor-network.diff | 408 +++++ .../2.6.28/apparmor-path_permission | 78 + .../2.6.28/apparmor-ptrace-2.6.27.diff | 113 ++ kernel-patches/2.6.28/apparmor-rlimits.diff | 461 +++++ kernel-patches/2.6.28/aufs/aa-hack | 87 + kernel-patches/2.6.28/aufs/aufs-fsetattr | 179 ++ kernel-patches/2.6.28/d_namespace_path.diff | 60 + .../2.6.28/do_path_lookup-nameidata.diff | 42 + .../export-security_inode_permission-for-aufs | 26 + kernel-patches/2.6.28/file-handle-ops.diff | 84 + kernel-patches/2.6.28/fix-complain.diff | 26 + kernel-patches/2.6.28/fix-config.diff | 14 + .../2.6.28/fix-d_namespace_path.diff | 15 + kernel-patches/2.6.28/fix-security-param.diff | 35 + kernel-patches/2.6.28/fix-vfs_rmdir.diff | 44 + kernel-patches/2.6.28/fork-tracking.diff | 56 + .../2.6.28/fsetattr-reintro-ATTR_FILE.diff | 28 + .../2.6.28/fsetattr-restore-ia_file.diff | 61 + kernel-patches/2.6.28/fsetattr.diff | 411 +++++ kernel-patches/2.6.28/mount-capability.diff | 55 + kernel-patches/2.6.28/remove_suid.diff | 41 + kernel-patches/2.6.28/securit_default.diff | 34 + kernel-patches/2.6.28/security-create.diff | 107 ++ kernel-patches/2.6.28/security-getxattr.diff | 128 ++ kernel-patches/2.6.28/security-link.diff | 149 ++ kernel-patches/2.6.28/security-listxattr.diff | 105 ++ kernel-patches/2.6.28/security-mkdir.diff | 106 ++ kernel-patches/2.6.28/security-mknod.diff | 124 ++ kernel-patches/2.6.28/security-readlink.diff | 104 ++ .../2.6.28/security-removexattr.diff | 143 ++ kernel-patches/2.6.28/security-rename.diff | 160 ++ kernel-patches/2.6.28/security-rmdir.diff | 127 ++ kernel-patches/2.6.28/security-setattr.diff | 139 ++ kernel-patches/2.6.28/security-setxattr.diff | 256 +++ kernel-patches/2.6.28/security-symlink.diff | 105 ++ kernel-patches/2.6.28/security-unlink.diff | 132 ++ .../2.6.28/security-xattr-file.diff | 592 +++++++ kernel-patches/2.6.28/series | 63 + kernel-patches/2.6.28/sysctl-pathname.diff | 111 ++ .../2.6.28/unambiguous-__d_path.diff | 258 +++ kernel-patches/2.6.28/vfs-getxattr.diff | 191 +++ kernel-patches/2.6.28/vfs-link.diff | 91 + kernel-patches/2.6.28/vfs-listxattr.diff | 101 ++ kernel-patches/2.6.28/vfs-mkdir.diff | 137 ++ kernel-patches/2.6.28/vfs-mknod.diff | 99 ++ kernel-patches/2.6.28/vfs-notify_change.diff | 321 ++++ kernel-patches/2.6.28/vfs-removexattr.diff | 121 ++ kernel-patches/2.6.28/vfs-rename.diff | 125 ++ kernel-patches/2.6.28/vfs-rmdir.diff | 135 ++ kernel-patches/2.6.28/vfs-setxattr.diff | 159 ++ kernel-patches/2.6.28/vfs-symlink.diff | 123 ++ kernel-patches/2.6.28/vfs-unlink.diff | 99 ++ 63 files changed, 13390 insertions(+) create mode 100644 kernel-patches/2.6.28/AppArmor-checkpatch.diff create mode 100644 kernel-patches/2.6.28/AppArmor-misc-cleanups.diff create mode 100644 kernel-patches/2.6.28/add-path_permission.diff create mode 100644 kernel-patches/2.6.28/add-security_path_permission create mode 100644 kernel-patches/2.6.28/apparmor-2.6.25.diff create mode 100644 kernel-patches/2.6.28/apparmor-audit.diff create mode 100644 kernel-patches/2.6.28/apparmor-intree.diff create mode 100644 kernel-patches/2.6.28/apparmor-lsm.diff create mode 100644 kernel-patches/2.6.28/apparmor-main.diff create mode 100644 kernel-patches/2.6.28/apparmor-misc.diff create mode 100644 kernel-patches/2.6.28/apparmor-module_interface.diff create mode 100644 kernel-patches/2.6.28/apparmor-network.diff create mode 100644 kernel-patches/2.6.28/apparmor-path_permission create mode 100644 kernel-patches/2.6.28/apparmor-ptrace-2.6.27.diff create mode 100644 kernel-patches/2.6.28/apparmor-rlimits.diff create mode 100644 kernel-patches/2.6.28/aufs/aa-hack create mode 100644 kernel-patches/2.6.28/aufs/aufs-fsetattr create mode 100644 kernel-patches/2.6.28/d_namespace_path.diff create mode 100644 kernel-patches/2.6.28/do_path_lookup-nameidata.diff create mode 100644 kernel-patches/2.6.28/export-security_inode_permission-for-aufs create mode 100644 kernel-patches/2.6.28/file-handle-ops.diff create mode 100644 kernel-patches/2.6.28/fix-complain.diff create mode 100644 kernel-patches/2.6.28/fix-config.diff create mode 100644 kernel-patches/2.6.28/fix-d_namespace_path.diff create mode 100644 kernel-patches/2.6.28/fix-security-param.diff create mode 100644 kernel-patches/2.6.28/fix-vfs_rmdir.diff create mode 100644 kernel-patches/2.6.28/fork-tracking.diff create mode 100644 kernel-patches/2.6.28/fsetattr-reintro-ATTR_FILE.diff create mode 100644 kernel-patches/2.6.28/fsetattr-restore-ia_file.diff create mode 100644 kernel-patches/2.6.28/fsetattr.diff create mode 100644 kernel-patches/2.6.28/mount-capability.diff create mode 100644 kernel-patches/2.6.28/remove_suid.diff create mode 100644 kernel-patches/2.6.28/securit_default.diff create mode 100644 kernel-patches/2.6.28/security-create.diff create mode 100644 kernel-patches/2.6.28/security-getxattr.diff create mode 100644 kernel-patches/2.6.28/security-link.diff create mode 100644 kernel-patches/2.6.28/security-listxattr.diff create mode 100644 kernel-patches/2.6.28/security-mkdir.diff create mode 100644 kernel-patches/2.6.28/security-mknod.diff create mode 100644 kernel-patches/2.6.28/security-readlink.diff create mode 100644 kernel-patches/2.6.28/security-removexattr.diff create mode 100644 kernel-patches/2.6.28/security-rename.diff create mode 100644 kernel-patches/2.6.28/security-rmdir.diff create mode 100644 kernel-patches/2.6.28/security-setattr.diff create mode 100644 kernel-patches/2.6.28/security-setxattr.diff create mode 100644 kernel-patches/2.6.28/security-symlink.diff create mode 100644 kernel-patches/2.6.28/security-unlink.diff create mode 100644 kernel-patches/2.6.28/security-xattr-file.diff create mode 100644 kernel-patches/2.6.28/series create mode 100644 kernel-patches/2.6.28/sysctl-pathname.diff create mode 100644 kernel-patches/2.6.28/unambiguous-__d_path.diff create mode 100644 kernel-patches/2.6.28/vfs-getxattr.diff create mode 100644 kernel-patches/2.6.28/vfs-link.diff create mode 100644 kernel-patches/2.6.28/vfs-listxattr.diff create mode 100644 kernel-patches/2.6.28/vfs-mkdir.diff create mode 100644 kernel-patches/2.6.28/vfs-mknod.diff create mode 100644 kernel-patches/2.6.28/vfs-notify_change.diff create mode 100644 kernel-patches/2.6.28/vfs-removexattr.diff create mode 100644 kernel-patches/2.6.28/vfs-rename.diff create mode 100644 kernel-patches/2.6.28/vfs-rmdir.diff create mode 100644 kernel-patches/2.6.28/vfs-setxattr.diff create mode 100644 kernel-patches/2.6.28/vfs-symlink.diff create mode 100644 kernel-patches/2.6.28/vfs-unlink.diff diff --git a/kernel-patches/2.6.28/AppArmor-checkpatch.diff b/kernel-patches/2.6.28/AppArmor-checkpatch.diff new file mode 100644 index 000000000..616b11770 --- /dev/null +++ b/kernel-patches/2.6.28/AppArmor-checkpatch.diff @@ -0,0 +1,506 @@ + + security/apparmor/apparmor.h | 5 +++ + security/apparmor/apparmorfs.c | 10 ++----- + security/apparmor/inline.h | 2 - + security/apparmor/list.c | 2 - + security/apparmor/lsm.c | 35 ++++++++++++++------------- + security/apparmor/main.c | 21 ++++++++-------- + security/apparmor/match.c | 8 ++---- + security/apparmor/match.h | 2 - + security/apparmor/module_interface.c | 44 +++++++++++++---------------------- + security/apparmor/procattr.c | 5 +-- + 10 files changed, 61 insertions(+), 73 deletions(-) + +--- a/security/apparmor/apparmor.h ++++ b/security/apparmor/apparmor.h +@@ -297,6 +297,9 @@ enum aa_lock_class { + aa_lock_task_release + }; + ++/* apparmor/profiles */ ++extern struct seq_operations apparmorfs_profiles_op; ++ + /* main.c */ + extern int alloc_default_namespace(void); + extern void free_default_namespace(void); +@@ -334,7 +337,7 @@ extern int aa_change_profile(const char + 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); ++ struct aa_profile *profile); + extern void unlock_task_and_profiles(struct task_struct *task, + struct aa_task_context *cxt, + struct aa_profile *profile); +--- a/security/apparmor/apparmorfs.c ++++ b/security/apparmor/apparmorfs.c +@@ -13,7 +13,7 @@ + #include + #include + #include +-#include ++#include + #include + + #include "apparmor.h" +@@ -65,9 +65,6 @@ 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); +@@ -236,8 +233,7 @@ int create_apparmorfs(void) + return 0; + + if (apparmor_dentry) { +- AA_ERROR("%s: AppArmor securityfs already exists\n", +- __FUNCTION__); ++ AA_ERROR("%s: AppArmor securityfs already exists\n", __func__); + return -EEXIST; + } + +@@ -245,7 +241,7 @@ int create_apparmorfs(void) + if (IS_ERR(apparmor_dentry)) { + error = PTR_ERR(apparmor_dentry); + apparmor_dentry = NULL; +- goto error; ++ goto error; + } + error = aafs_create("profiles", 0440, &apparmorfs_profiles_fops); + if (error) +--- a/security/apparmor/inline.h ++++ b/security/apparmor/inline.h +@@ -213,7 +213,7 @@ static inline void lock_both_profiles(st + * gives us RCU reader safety. + */ + static inline void unlock_both_profiles(struct aa_profile *profile1, +- struct aa_profile *profile2) ++ struct aa_profile *profile2) + { + /* Unlock the two profiles. */ + if (!profile1 || profile1 == profile2) { +--- a/security/apparmor/list.c ++++ b/security/apparmor/list.c +@@ -15,7 +15,7 @@ + + /* list of profile namespaces and lock */ + LIST_HEAD(profile_ns_list); +-rwlock_t profile_ns_list_lock = RW_LOCK_UNLOCKED; ++DEFINE_RWLOCK(profile_ns_list_lock); + + /** + * __aa_find_namespace - look up a profile namespace on the namespace list +--- a/security/apparmor/lsm.c ++++ b/security/apparmor/lsm.c +@@ -24,7 +24,7 @@ + #include "inline.h" + + /* Flag indicating whether initialization completed */ +-int apparmor_initialized = 0; ++int apparmor_initialized; + + static int param_set_aabool(const char *val, struct kernel_param *kp); + static int param_get_aabool(char *buffer, struct kernel_param *kp); +@@ -43,22 +43,22 @@ static int param_get_aauint(char *buffer + * Value is also togglable per profile and referenced when global value is + * enforce. + */ +-int apparmor_complain = 0; ++int apparmor_complain; + 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; ++int apparmor_debug; + 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; ++int apparmor_audit; + 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; ++int apparmor_logsyscall; + module_param_named(logsyscall, apparmor_logsyscall, aabool, S_IRUSR | S_IWUSR); + MODULE_PARM_DESC(apparmor_logsyscall, "Toggle AppArmor logsyscall mode"); + +@@ -117,7 +117,6 @@ static int param_get_aauint(char *buffer + /* 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) { +@@ -134,8 +133,7 @@ static int param_set_aa_enabled(const ch + if (!val) + return -EINVAL; + +- l = simple_strtoul(val, &endp, 0); +- if (endp == val || l != 0) ++ if (strict_strtoul(val, 0, &l) || l != 0) + return -EINVAL; + + apparmor_enabled = 0; +@@ -238,7 +236,7 @@ static int apparmor_sysctl(struct ctl_ta + mask |= MAY_WRITE; + + error = -ENOMEM; +- buffer = (char*)__get_free_page(GFP_KERNEL); ++ buffer = (char *)__get_free_page(GFP_KERNEL); + if (!buffer) + goto out; + name = sysctl_pathname(table, buffer, PAGE_SIZE); +@@ -271,7 +269,7 @@ static int apparmor_bprm_secureexec(stru + + if (!ret && (unsigned long)bprm->security & AA_SECURE_EXEC_NEEDED) { + AA_DEBUG("%s: secureexec required for %s\n", +- __FUNCTION__, bprm->filename); ++ __func__, bprm->filename); + ret = 1; + } + +@@ -522,7 +520,7 @@ static int apparmor_inode_removexattr(st + 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; ++ struct aa_profile *file_profile = file->f_security; + int error = 0; + + if (!file_profile) +@@ -559,7 +557,7 @@ static int apparmor_file_permission(stru + aa_mask_permissions(mask)); + } + +-static inline int apparmor_file_lock (struct file *file, unsigned int cmd) ++static inline int apparmor_file_lock(struct file *file, unsigned int cmd) + { + int mask = AA_MAY_LOCK; + if (cmd == F_WRLCK) +@@ -580,7 +578,7 @@ static int apparmor_file_alloc_security( + + static void apparmor_file_free_security(struct file *file) + { +- struct aa_profile *file_profile = (struct aa_profile*)file->f_security; ++ struct aa_profile *file_profile = file->f_security; + + aa_put_profile(file_profile); + } +@@ -967,17 +965,20 @@ static int __init apparmor_init(void) + return 0; + } + +- if ((error = create_apparmorfs())) { ++ error = create_apparmorfs(); ++ if (error) { + AA_ERROR("Unable to activate AppArmor filesystem\n"); + goto createfs_out; + } + +- if ((error = alloc_default_namespace())){ ++ error = alloc_default_namespace(); ++ if (error) { + AA_ERROR("Unable to allocate default profile namespace\n"); + goto alloc_out; + } + +- if ((error = register_security(&apparmor_ops))) { ++ error = register_security(&apparmor_ops); ++ if (error) { + AA_ERROR("Unable to register AppArmor\n"); + goto register_security_out; + } +@@ -995,7 +996,7 @@ register_security_out: + free_default_namespace(); + + alloc_out: +- destroy_apparmorfs(); ++ destroy_apparmorfs(); + + createfs_out: + return error; +--- a/security/apparmor/main.c ++++ b/security/apparmor/main.c +@@ -416,7 +416,7 @@ static int aa_link_denied(struct aa_prof + * + * 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); +@@ -445,8 +445,10 @@ static int aa_link_denied(struct aa_prof + (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); ++ (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; +@@ -866,7 +868,7 @@ int aa_revalidate_sk(struct sock *sk, ch + /* 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"); ++ printk(KERN_WARNING "AppArmor Debug: Hook being called from interrupt context\n"); + dump_stack(); + return 0; + } +@@ -1019,7 +1021,7 @@ repeat: + } + + static struct aa_profile * +-aa_register_find(struct aa_profile *profile, const char* ns_name, ++aa_register_find(struct aa_profile *profile, const char *ns_name, + const char *name, int mandatory, int complain, + struct aa_audit *sa) + { +@@ -1053,7 +1055,7 @@ aa_register_find(struct aa_profile *prof + + if (new_profile) { + AA_DEBUG("%s: setting profile %s\n", +- __FUNCTION__, new_profile->name); ++ __func__, new_profile->name); + } else if (mandatory && profile) { + sa->info = "mandatory profile missing"; + sa->denied_mask = sa->request_mask; /* shifted MAY_EXEC */ +@@ -1072,8 +1074,7 @@ aa_register_find(struct aa_profile *prof + * is unconfined, pix, nix. + */ + AA_DEBUG("%s: No profile found for exec image '%s'\n", +- __FUNCTION__, +- name); ++ __func__, name); + } + if (ns_ref) + aa_put_namespace(ns); +@@ -1158,7 +1159,7 @@ int aa_register(struct linux_binprm *bpr + int exec_mode, complain = 0, shift; + struct aa_audit sa; + +- AA_DEBUG("%s\n", __FUNCTION__); ++ AA_DEBUG("%s\n", __func__); + + profile = aa_get_profile(current); + +@@ -1266,7 +1267,7 @@ repeat: + unsigned long bprm_flags; + + bprm_flags = AA_SECURE_EXEC_NEEDED; +- bprm->security = (void*) ++ bprm->security = (void *) + ((unsigned long)bprm->security | bprm_flags); + } + +--- a/security/apparmor/match.c ++++ b/security/apparmor/match.c +@@ -82,7 +82,7 @@ int unpack_dfa(struct aa_dfa *dfa, void + if (!table) + goto fail; + +- switch(table->td_id) { ++ switch (table->td_id) { + case YYTD_ID_ACCEPT: + case YYTD_ID_ACCEPT2: + case YYTD_ID_BASE: +@@ -115,10 +115,8 @@ int unpack_dfa(struct aa_dfa *dfa, void + + fail: + for (i = 0; i < ARRAY_SIZE(dfa->tables); i++) { +- if (dfa->tables[i]) { +- kfree(dfa->tables[i]); +- dfa->tables[i] = NULL; +- } ++ kfree(dfa->tables[i]); ++ dfa->tables[i] = NULL; + } + return error; + } +--- a/security/apparmor/match.h ++++ b/security/apparmor/match.h +@@ -61,7 +61,7 @@ struct table_header { + #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)) ++#define ACCEPT_TABLE2(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT2 - 1]->td_data)) + + struct aa_dfa { + struct table_header *tables[YYTD_ID_NXT]; +--- a/security/apparmor/module_interface.c ++++ b/security/apparmor/module_interface.c +@@ -219,7 +219,7 @@ static size_t aa_is_blob(struct aa_ext * + size = le32_to_cpu(get_unaligned((u32 *)e->pos)); + e->pos += sizeof(u32); + if (aa_inbounds(e, (size_t) size)) { +- * blob = e->pos; ++ *blob = e->pos; + e->pos += size; + return size; + } +@@ -237,8 +237,8 @@ static int aa_is_dynstring(struct aa_ext + *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))) ++ char *str = kmalloc(size, GFP_KERNEL); ++ if (!str) + goto fail; + memcpy(str, src_str, size); + *string = str; +@@ -495,9 +495,8 @@ static int aa_verify_header(struct aa_ex + } + + /* read the namespace if present */ +- if (!aa_is_dynstring(e, &e->ns_name, "namespace")) { ++ if (!aa_is_dynstring(e, &e->ns_name, "namespace")) + e->ns_name = NULL; +- } + + return 0; + } +@@ -592,11 +591,8 @@ static inline void task_replace(struct t + { + 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_DEBUG("%s: replacing profile for task %d profile=%s (%p)\n", ++ __func__, cxt->task->pid, cxt->profile->name, cxt->profile); + + aa_change_task_context(task, new_cxt, new_profile, cxt->cookie, + cxt->previous_profile); +@@ -797,9 +793,7 @@ noent: + */ + void free_aa_namespace_kref(struct kref *kref) + { +- struct aa_namespace *ns=container_of(kref, struct aa_namespace, count); +- +- free_aa_namespace(ns); ++ free_aa_namespace(container_of(kref, struct aa_namespace, count)); + } + + /** +@@ -812,7 +806,7 @@ struct aa_namespace *alloc_aa_namespace( + struct aa_namespace *ns; + + ns = kzalloc(sizeof(*ns), GFP_KERNEL); +- AA_DEBUG("%s(%p)\n", __FUNCTION__, ns); ++ AA_DEBUG("%s(%p)\n", __func__, ns); + if (ns) { + ns->name = name; + INIT_LIST_HEAD(&ns->list); +@@ -854,7 +848,7 @@ struct aa_namespace *alloc_aa_namespace( + */ + void free_aa_namespace(struct aa_namespace *ns) + { +- AA_DEBUG("%s(%p)\n", __FUNCTION__, ns); ++ AA_DEBUG("%s(%p)\n", __func__, ns); + + if (!ns) + return; +@@ -863,15 +857,12 @@ void free_aa_namespace(struct aa_namespa + if (!list_empty(&ns->profiles)) { + AA_ERROR("%s: internal error, " + "namespace '%s' still contains profiles\n", +- __FUNCTION__, +- ns->name); ++ __func__, ns->name); + BUG(); + } + if (!list_empty(&ns->list)) { +- AA_ERROR("%s: internal error, " +- "namespace '%s' still on list\n", +- __FUNCTION__, +- ns->name); ++ AA_ERROR("%s: internal error, namespace '%s' still on list\n", ++ __func__, ns->name); + BUG(); + } + /* null_complain_profile doesn't contribute to ns ref counting */ +@@ -887,7 +878,7 @@ void free_aa_namespace(struct aa_namespa + */ + void free_aa_profile_kref(struct kref *kref) + { +- struct aa_profile *p=container_of(kref, struct aa_profile, count); ++ struct aa_profile *p = container_of(kref, struct aa_profile, count); + + free_aa_profile(p); + } +@@ -901,7 +892,7 @@ struct aa_profile *alloc_aa_profile(void + struct aa_profile *profile; + + profile = kzalloc(sizeof(*profile), GFP_KERNEL); +- AA_DEBUG("%s(%p)\n", __FUNCTION__, profile); ++ AA_DEBUG("%s(%p)\n", __func__, profile); + if (profile) { + INIT_LIST_HEAD(&profile->list); + kref_init(&profile->count); +@@ -923,7 +914,7 @@ struct aa_profile *alloc_aa_profile(void + */ + void free_aa_profile(struct aa_profile *profile) + { +- AA_DEBUG("%s(%p)\n", __FUNCTION__, profile); ++ AA_DEBUG("%s(%p)\n", __func__, profile); + + if (!profile) + return; +@@ -932,8 +923,7 @@ void free_aa_profile(struct aa_profile * + if (!list_empty(&profile->list)) { + AA_ERROR("%s: internal error, " + "profile '%s' still on global list\n", +- __FUNCTION__, +- profile->name); ++ __func__, profile->name); + BUG(); + } + aa_put_namespace(profile->ns); +@@ -941,7 +931,7 @@ void free_aa_profile(struct aa_profile * + aa_match_free(profile->file_rules); + + if (profile->name) { +- AA_DEBUG("%s: %s\n", __FUNCTION__, profile->name); ++ AA_DEBUG("%s: %s\n", __func__, profile->name); + kfree(profile->name); + } + +--- a/security/apparmor/procattr.c ++++ b/security/apparmor/procattr.c +@@ -88,7 +88,7 @@ int aa_setprocattr_changehat(char *args) + } + + AA_DEBUG("%s: Magic 0x%llx Hat '%s'\n", +- __FUNCTION__, cookie, hat ? hat : NULL); ++ __func__, cookie, hat ? hat : NULL); + + return aa_change_hat(hat, cookie); + } +@@ -121,8 +121,7 @@ int aa_setprocattr_setprofile(struct tas + sa.gfp_mask = GFP_KERNEL; + sa.task = task->pid; + +- AA_DEBUG("%s: current %d\n", +- __FUNCTION__, current->pid); ++ AA_DEBUG("%s: current %d\n", __func__, current->pid); + + name = args; + if (args[0] != '/') { diff --git a/kernel-patches/2.6.28/AppArmor-misc-cleanups.diff b/kernel-patches/2.6.28/AppArmor-misc-cleanups.diff new file mode 100644 index 000000000..6e689afaf --- /dev/null +++ b/kernel-patches/2.6.28/AppArmor-misc-cleanups.diff @@ -0,0 +1,260 @@ + security/apparmor/Kconfig | 1 + + security/apparmor/apparmor.h | 7 +++---- + security/apparmor/inline.h | 4 ++-- + security/apparmor/list.c | 2 ++ + security/apparmor/lsm.c | 22 ++++++---------------- + security/apparmor/main.c | 5 +++-- + security/apparmor/match.c | 5 +++-- + security/apparmor/module_interface.c | 13 ++++++------- + 8 files changed, 26 insertions(+), 33 deletions(-) + +--- a/security/apparmor/Kconfig ++++ b/security/apparmor/Kconfig +@@ -3,6 +3,7 @@ config SECURITY_APPARMOR + depends on SECURITY + depends on SECURITY_NETWORK + select AUDIT ++ default n + help + This enables the AppArmor security module. + Required userspace tools (if they are not included in your +--- a/security/apparmor/apparmor.h ++++ b/security/apparmor/apparmor.h +@@ -214,9 +214,9 @@ struct aa_profile { + char **exec_table; + struct aa_dfa *file_rules; + struct { +- int hat; +- int complain; +- int audit; ++ u32 hat; ++ u32 complain; ++ u32 audit; + } flags; + int isstale; + +@@ -310,7 +310,6 @@ void aa_audit_status(struct aa_profile * + 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); +--- a/security/apparmor/inline.h ++++ b/security/apparmor/inline.h +@@ -21,7 +21,7 @@ static inline int mediated_filesystem(st + + static inline struct aa_task_context *aa_task_context(struct task_struct *task) + { +- return (struct aa_task_context *) rcu_dereference(task->security); ++ return rcu_dereference(task->security); + } + + static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns) +@@ -91,7 +91,7 @@ static inline struct aa_profile *aa_get_ + static inline struct aa_profile *aa_find_profile(struct aa_namespace *ns, + const char *name) + { +- struct aa_profile *profile = NULL; ++ struct aa_profile *profile; + + read_lock(&ns->lock); + profile = aa_dup_profile(__aa_find_profile(name, &ns->profiles)); +--- a/security/apparmor/list.c ++++ b/security/apparmor/list.c +@@ -112,6 +112,7 @@ static struct aa_profile *next_profile(s + } + + static void *p_start(struct seq_file *f, loff_t *pos) ++ __acquires(profile_ns_list_lock) + { + struct aa_namespace *ns; + loff_t l = *pos; +@@ -144,6 +145,7 @@ static void *p_next(struct seq_file *f, + } + + static void p_stop(struct seq_file *f, void *p) ++ __releases(profile_ns_list_lock) + { + struct aa_profile *profile = (struct aa_profile *) p; + +--- a/security/apparmor/lsm.c ++++ b/security/apparmor/lsm.c +@@ -10,7 +10,7 @@ + */ + + #include +-#include ++#include + #include + #include + #include +@@ -45,27 +45,21 @@ static int param_get_aauint(char *buffer + */ + int apparmor_complain; + 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; + 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; + 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; + 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 +@@ -74,10 +68,9 @@ MODULE_PARM_DESC(apparmor_path_max, "Max + #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; ++static 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) + { +@@ -557,7 +550,7 @@ static int apparmor_file_permission(stru + aa_mask_permissions(mask)); + } + +-static inline int apparmor_file_lock(struct file *file, unsigned int cmd) ++static int apparmor_file_lock(struct file *file, unsigned int cmd) + { + int mask = AA_MAY_LOCK; + if (cmd == F_WRLCK) +@@ -583,8 +576,8 @@ static void apparmor_file_free_security( + aa_put_profile(file_profile); + } + +-static inline int aa_mmap(struct file *file, const char *operation, +- unsigned long prot, unsigned long flags) ++static int aa_mmap(struct file *file, const char *operation, ++ unsigned long prot, unsigned long flags) + { + struct dentry *dentry; + int mask = 0; +@@ -883,7 +876,7 @@ static int apparmor_task_setrlimit(unsig + return error; + } + +-struct security_operations apparmor_ops = { ++static struct security_operations apparmor_ops = { + .name = "apparmor", + .ptrace_may_access = apparmor_ptrace_may_access, + .ptrace_traceme = apparmor_ptrace_traceme, +@@ -1029,6 +1022,3 @@ void apparmor_disable(void) + info_message("AppArmor protection removed"); + } + +-MODULE_DESCRIPTION("AppArmor process confinement"); +-MODULE_AUTHOR("Novell/Immunix, http://bugs.opensuse.org"); +-MODULE_LICENSE("GPL"); +--- a/security/apparmor/main.c ++++ b/security/apparmor/main.c +@@ -299,7 +299,7 @@ int aa_audit_reject(struct aa_profile *p + * @profile: profile to check against + * @sa: audit event + */ +-int aa_audit(struct aa_profile *profile, struct aa_audit *sa) ++static int aa_audit(struct aa_profile *profile, struct aa_audit *sa) + { + int type = AUDIT_APPARMOR_DENIED; + struct audit_context *audit_cxt; +@@ -520,7 +520,8 @@ static char *new_compound_name(const cha + sprintf(name, "%s//%s", n1, n2); + return name; + } +-static inline void aa_put_name_buffer(char *buffer) ++ ++static void aa_put_name_buffer(char *buffer) + { + kfree(buffer); + } +--- a/security/apparmor/match.c ++++ b/security/apparmor/match.c +@@ -226,8 +226,9 @@ void aa_match_free(struct aa_dfa *dfa) + * 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) ++static 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); +--- a/security/apparmor/module_interface.c ++++ b/security/apparmor/module_interface.c +@@ -61,7 +61,7 @@ struct aa_ext { + char *ns_name; + }; + +-static inline int aa_inbounds(struct aa_ext *e, size_t size) ++static int aa_inbounds(struct aa_ext *e, size_t size) + { + return (size <= e->end - e->pos); + } +@@ -94,7 +94,7 @@ fail: + return 0; + } + +-static inline int aa_is_X(struct aa_ext *e, enum aa_code code) ++static int aa_is_X(struct aa_ext *e, enum aa_code code) + { + if (!aa_inbounds(e, 1)) + return 0; +@@ -369,7 +369,7 @@ fail: + static struct aa_profile *aa_unpack_profile(struct aa_ext *e, + struct aa_audit *sa) + { +- struct aa_profile *profile = NULL; ++ struct aa_profile *profile; + size_t size = 0; + int i, error = -EPROTO; + +@@ -465,8 +465,7 @@ fail: + sa->info = "failed to unpack profile"; + aa_audit_status(NULL, sa); + +- if (profile) +- free_aa_profile(profile); ++ free_aa_profile(profile); + + return ERR_PTR(error); + } +@@ -508,7 +507,7 @@ static int aa_verify_header(struct aa_ex + */ + ssize_t aa_add_profile(void *data, size_t size) + { +- struct aa_profile *profile = NULL; ++ struct aa_profile *profile; + struct aa_namespace *ns = NULL; + struct aa_ext e = { + .start = data, +@@ -585,7 +584,7 @@ ssize_t aa_add_profile(void *data, size_ + * @new_cxt: new aa_task_context to do replacement with + * @new_profile: new profile + */ +-static inline void task_replace(struct task_struct *task, ++static void task_replace(struct task_struct *task, + struct aa_task_context *new_cxt, + struct aa_profile *new_profile) + { diff --git a/kernel-patches/2.6.28/add-path_permission.diff b/kernel-patches/2.6.28/add-path_permission.diff new file mode 100644 index 000000000..8a12ce7df --- /dev/null +++ b/kernel-patches/2.6.28/add-path_permission.diff @@ -0,0 +1,201 @@ +From: Jeff Mahoney +Subject: [PATCH] vfs: introduce path_permission() + + 2.6.27 eliminated the nameidata parameter from permission and replaced + several call sites with inode_permission. This keeps the information + required by AppArmor from reaching it. + + The following patch factors out the permission assessment part of + inode_permission into __inode_permission and adds a path_permission + function that takes a struct path instead of a struct inode and passes + it to security_path_permission instead of security_inode_permission. + + All of the call sites that had access to a struct path whether by + itself or via a file or nameidata (and used it) in 2.6.26 are changed + to use the path_permission call. + +Signed-off-by: Jeff Mahoney +--- + fs/inotify_user.c | 2 +- + fs/namei.c | 32 ++++++++++++++++++++++++-------- + fs/open.c | 10 +++++----- + include/linux/fs.h | 5 +++++ + 4 files changed, 35 insertions(+), 14 deletions(-) + +--- a/fs/inotify_user.c ++++ b/fs/inotify_user.c +@@ -372,7 +372,7 @@ static int find_inode(const char __user + if (error) + return error; + /* you can only watch an inode if you have read permissions on it */ +- error = inode_permission(path->dentry->d_inode, MAY_READ); ++ error = path_permission(path, MAY_READ); + if (error) + path_put(path); + return error; +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -226,7 +226,7 @@ int generic_permission(struct inode *ino + return -EACCES; + } + +-int inode_permission(struct inode *inode, int mask) ++static int __inode_permission(struct inode *inode, int mask) + { + int retval; + +@@ -256,7 +256,12 @@ int inode_permission(struct inode *inode + if (retval) + return retval; + +- retval = devcgroup_inode_permission(inode, mask); ++ return devcgroup_inode_permission(inode, mask); ++} ++ ++int inode_permission(struct inode *inode, int mask) ++{ ++ int retval = __inode_permission(inode, mask); + if (retval) + return retval; + +@@ -264,6 +269,15 @@ int inode_permission(struct inode *inode + mask & (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND)); + } + ++int path_permission(struct path *path, int mask) ++{ ++ int retval = __inode_permission(path->dentry->d_inode, mask); ++ if (retval) ++ return retval; ++ return security_path_permission(path, ++ mask & (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND)); ++} ++ + /** + * vfs_permission - check for access rights to a given path + * @nd: lookup result that describes the path +@@ -276,7 +290,7 @@ int inode_permission(struct inode *inode + */ + int vfs_permission(struct nameidata *nd, int mask) + { +- return inode_permission(nd->path.dentry->d_inode, mask); ++ return path_permission(&nd->path, mask); + } + + /** +@@ -293,7 +307,7 @@ int vfs_permission(struct nameidata *nd, + */ + int file_permission(struct file *file, int mask) + { +- return inode_permission(file->f_path.dentry->d_inode, mask); ++ return path_permission(&file->f_path, mask); + } + + /* +@@ -434,8 +448,9 @@ static struct dentry * cached_lookup(str + * short-cut DAC fails, then call permission() to do more + * complete permission check. + */ +-static int exec_permission_lite(struct inode *inode) ++static int exec_permission_lite(struct path *path) + { ++ struct inode *inode = path->dentry->d_inode; + umode_t mode = inode->i_mode; + + if (inode->i_op && inode->i_op->permission) +@@ -460,7 +475,7 @@ static int exec_permission_lite(struct i + + return -EACCES; + ok: +- return security_inode_permission(inode, MAY_EXEC); ++ return security_path_permission(path, MAY_EXEC); + } + + /* +@@ -857,7 +872,7 @@ static int __link_path_walk(const char * + unsigned int c; + + nd->flags |= LOOKUP_CONTINUE; +- err = exec_permission_lite(inode); ++ err = exec_permission_lite(&nd->path); + if (err == -EAGAIN) + err = vfs_permission(nd, MAY_EXEC); + if (err) +@@ -1216,7 +1231,7 @@ static struct dentry *lookup_hash(struct + { + int err; + +- err = inode_permission(nd->path.dentry->d_inode, MAY_EXEC); ++ err = path_permission(&nd->path, MAY_EXEC); + if (err) + return ERR_PTR(err); + return __lookup_hash(&nd->last, nd->path.dentry, nd); +@@ -2862,6 +2877,7 @@ EXPORT_SYMBOL(path_lookup); + EXPORT_SYMBOL(kern_path); + EXPORT_SYMBOL(vfs_path_lookup); + EXPORT_SYMBOL(inode_permission); ++EXPORT_SYMBOL(path_permission); + EXPORT_SYMBOL(vfs_permission); + EXPORT_SYMBOL(file_permission); + EXPORT_SYMBOL(unlock_rename); +--- a/fs/open.c ++++ b/fs/open.c +@@ -250,7 +250,7 @@ static long do_sys_truncate(const char _ + if (error) + goto dput_and_out; + +- error = inode_permission(inode, MAY_WRITE); ++ error = path_permission(&path, MAY_WRITE); + if (error) + goto mnt_drop_write_and_out; + +@@ -474,7 +474,7 @@ asmlinkage long sys_faccessat(int dfd, c + goto out_path_release; + } + +- res = inode_permission(inode, mode | MAY_ACCESS); ++ res = path_permission(&path, mode | MAY_ACCESS); + /* SuS v2 requires we report a read only fs too */ + if (res || !(mode & S_IWOTH) || special_file(inode->i_mode)) + goto out_path_release; +@@ -517,7 +517,7 @@ asmlinkage long sys_chdir(const char __u + if (error) + goto out; + +- error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_ACCESS); ++ error = path_permission(&path, MAY_EXEC | MAY_ACCESS); + if (error) + goto dput_and_out; + +@@ -546,7 +546,7 @@ asmlinkage long sys_fchdir(unsigned int + if (!S_ISDIR(inode->i_mode)) + goto out_putf; + +- error = inode_permission(inode, MAY_EXEC | MAY_ACCESS); ++ error = path_permission(&file->f_path, MAY_EXEC | MAY_ACCESS); + if (!error) + set_fs_pwd(current->fs, &file->f_path); + out_putf: +@@ -564,7 +564,7 @@ asmlinkage long sys_chroot(const char __ + if (error) + goto out; + +- error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_ACCESS); ++ error = path_permission(&path, MAY_EXEC | MAY_ACCESS); + if (error) + goto dput_and_out; + +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -1242,6 +1242,11 @@ int fiemap_fill_next_extent(struct fiema + int fiemap_check_flags(struct fiemap_extent_info *fieinfo, u32 fs_flags); + + /* ++ * VFS path helper functions. ++ */ ++extern int path_permission(struct path *, int); ++ ++/* + * File types + * + * NOTE! These match bits 12..15 of stat.st_mode diff --git a/kernel-patches/2.6.28/add-security_path_permission b/kernel-patches/2.6.28/add-security_path_permission new file mode 100644 index 000000000..e5a6b64e0 --- /dev/null +++ b/kernel-patches/2.6.28/add-security_path_permission @@ -0,0 +1,109 @@ +From: Jeff Mahoney +Subject: [PATCH] security: add ->path_permission + + This patch adds a security_ops->path_permission hook that is identical to + security_ops->inode_permission except that it is passed a struct path + instead of a struct inode. + + LSMs which don't implement it will have their ->inode_permission call + used instead. + +Signed-off-by: Jeff Mahoney +--- + + include/linux/security.h | 21 +++++++++++++++++++++ + security/capability.c | 6 ++++++ + security/security.c | 9 +++++++++ + 3 files changed, 36 insertions(+) + +--- a/include/linux/security.h ++++ b/include/linux/security.h +@@ -592,6 +592,20 @@ static inline void security_free_mnt_opt + * file_permission, and recheck access if anything has changed + * since inode_permission. + * ++ * Security hook for path ++ * ++ * @path_permission: ++ * Check permission before accessing a path. This hook is called by the ++ * existing Linux permission function, so a security module can use it to ++ * provide additional checking for existing Linux permission checks. ++ * Notice that this hook is called when a file is opened (as well as many ++ * other operations), whereas the file_security_ops permission hook is ++ * called when the actual read/write operations are performed. This ++ * hook is optional and if absent, inode_permission will be substituted. ++ * @path contains the path structure to check. ++ * @mask contains the permission mask. ++ * Return 0 if permission is granted. ++ + * Security hooks for task operations. + * + * @task_create: +@@ -1434,6 +1448,7 @@ struct security_operations { + struct fown_struct *fown, int sig); + int (*file_receive) (struct file *file); + int (*dentry_open) (struct file *file); ++ int (*path_permission) (struct path *path, int mask); + + int (*task_create) (unsigned long clone_flags); + int (*task_alloc_security) (struct task_struct *p); +@@ -1704,6 +1719,7 @@ int security_file_send_sigiotask(struct + struct fown_struct *fown, int sig); + int security_file_receive(struct file *file); + int security_dentry_open(struct file *file); ++int security_path_permission(struct path *path, int mask); + int security_task_create(unsigned long clone_flags); + int security_task_alloc(struct task_struct *p); + void security_task_free(struct task_struct *p); +@@ -2245,6 +2261,11 @@ static inline int security_dentry_open(s + { + return 0; + } ++ ++static inline int security_path_permission(struct path *path, int mask) ++{ ++ return 0; ++} + + static inline int security_task_create(unsigned long clone_flags) + { +--- a/security/capability.c ++++ b/security/capability.c +@@ -343,6 +343,11 @@ static int cap_dentry_open(struct file * + return 0; + } + ++static int cap_path_permission(struct path *path, int mask) ++{ ++ return security_inode_permission(path->dentry->d_inode, mask); ++} ++ + static int cap_task_create(unsigned long clone_flags) + { + return 0; +@@ -897,6 +902,7 @@ void security_fixup_ops(struct security_ + set_to_cap_if_null(ops, file_send_sigiotask); + set_to_cap_if_null(ops, file_receive); + set_to_cap_if_null(ops, dentry_open); ++ set_to_cap_if_null(ops, path_permission); + set_to_cap_if_null(ops, task_create); + set_to_cap_if_null(ops, task_alloc_security); + set_to_cap_if_null(ops, task_free_security); +--- a/security/security.c ++++ b/security/security.c +@@ -624,6 +624,15 @@ int security_dentry_open(struct file *fi + return security_ops->dentry_open(file); + } + ++int security_path_permission(struct path *path, int mask) ++{ ++ struct inode *inode = path->dentry->d_inode; ++ if (unlikely(IS_PRIVATE(inode))) ++ return 0; ++ ++ return security_ops->path_permission(path, mask); ++} ++ + int security_task_create(unsigned long clone_flags) + { + return security_ops->task_create(clone_flags); diff --git a/kernel-patches/2.6.28/apparmor-2.6.25.diff b/kernel-patches/2.6.28/apparmor-2.6.25.diff new file mode 100644 index 000000000..56bf22a15 --- /dev/null +++ b/kernel-patches/2.6.28/apparmor-2.6.25.diff @@ -0,0 +1,47 @@ +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/module_interface.c | 22 ++++++++++++++++++---- + 1 file changed, 18 insertions(+), 4 deletions(-) + +--- a/security/apparmor/module_interface.c ++++ b/security/apparmor/module_interface.c +@@ -395,15 +395,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.28/apparmor-audit.diff b/kernel-patches/2.6.28/apparmor-audit.diff new file mode 100644 index 000000000..6fe7499b4 --- /dev/null +++ b/kernel-patches/2.6.28/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 */ +@@ -547,6 +554,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 +@@ -1243,8 +1243,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; +@@ -1518,3 +1517,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.28/apparmor-intree.diff b/kernel-patches/2.6.28/apparmor-intree.diff new file mode 100644 index 000000000..c4586ab61 --- /dev/null +++ b/kernel-patches/2.6.28/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 | 3 ++- + 2 files changed, 3 insertions(+), 1 deletion(-) + +--- a/security/Kconfig ++++ b/security/Kconfig +@@ -134,6 +134,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 +@@ -15,5 +15,6 @@ obj-$(CONFIG_SECURITYFS) += inode.o + # Must precede capability.o in order to stack properly. + obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o + obj-$(CONFIG_SECURITY_SMACK) += smack/built-in.o +-obj-$(CONFIG_SECURITY_ROOTPLUG) += root_plug.o ++obj-$(CONFIG_SECURITY_APPARMOR) += commoncap.o apparmor/ ++ obj-$(CONFIG_SECURITY_ROOTPLUG) += root_plug.o + obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o diff --git a/kernel-patches/2.6.28/apparmor-lsm.diff b/kernel-patches/2.6.28/apparmor-lsm.diff new file mode 100644 index 000000000..7907db0c9 --- /dev/null +++ b/kernel-patches/2.6.28/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, unsigned int mode) ++{ ++ 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 (error && 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.28/apparmor-main.diff b/kernel-patches/2.6.28/apparmor-main.diff new file mode 100644 index 000000000..9e50cfee4 --- /dev/null +++ b/kernel-patches/2.6.28/apparmor-main.diff @@ -0,0 +1,1494 @@ +From: John Johansen +Subject: AppArmor: Main Part + +The underlying functions by which the AppArmor LSM hooks are implemented. + +Signed-off-by: John Johansen +Signed-off-by: Andreas Gruenbacher + +--- + security/apparmor/main.c | 1479 +++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 1479 insertions(+) + +--- /dev/null ++++ b/security/apparmor/main.c +@@ -0,0 +1,1479 @@ ++/* ++ * Copyright (C) 2002-2007 Novell/SUSE ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ * ++ * AppArmor Core ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "apparmor.h" ++ ++#include "inline.h" ++ ++/* ++ * Table of capability names: we generate it from capabilities.h. ++ */ ++static const char *capability_names[] = { ++#include "capability_names.h" ++}; ++ ++struct aa_namespace *default_namespace; ++ ++static int aa_inode_mode(struct inode *inode) ++{ ++ /* if the inode doesn't exist the user is creating it */ ++ if (!inode || current->fsuid == inode->i_uid) ++ return AA_USER_SHIFT; ++ return AA_OTHER_SHIFT; ++} ++ ++int alloc_default_namespace(void) ++{ ++ struct aa_namespace *ns; ++ char *name = kstrdup("default", GFP_KERNEL); ++ if (!name) ++ return -ENOMEM; ++ ns = alloc_aa_namespace(name); ++ if (!ns) { ++ kfree(name); ++ return -ENOMEM; ++ } ++ ++ write_lock(&profile_ns_list_lock); ++ default_namespace = ns; ++ aa_get_namespace(ns); ++ list_add(&ns->list, &profile_ns_list); ++ write_unlock(&profile_ns_list_lock); ++ ++ return 0; ++} ++ ++void free_default_namespace(void) ++{ ++ write_lock(&profile_ns_list_lock); ++ list_del_init(&default_namespace->list); ++ write_unlock(&profile_ns_list_lock); ++ aa_put_namespace(default_namespace); ++ default_namespace = NULL; ++} ++ ++static void aa_audit_file_sub_mask(struct audit_buffer *ab, char *buffer, ++ int mask) ++{ ++ const char unsafex[] = "upcn"; ++ const char safex[] = "UPCN"; ++ char *m = buffer; ++ ++ if (mask & AA_EXEC_MMAP) ++ *m++ = 'm'; ++ if (mask & MAY_READ) ++ *m++ = 'r'; ++ if (mask & MAY_WRITE) ++ *m++ = 'w'; ++ else if (mask & MAY_APPEND) ++ *m++ = 'a'; ++ if (mask & MAY_EXEC) { ++ int index = AA_EXEC_INDEX(mask); ++ /* all indexes > 4 are also named transitions */ ++ if (index > 4) ++ index = 4; ++ if (index > 0) { ++ if (mask & AA_EXEC_UNSAFE) ++ *m++ = unsafex[index - 1]; ++ else ++ *m++ = safex[index - 1]; ++ } ++ if (mask & AA_EXEC_INHERIT) ++ *m++ = 'i'; ++ *m++ = 'x'; ++ } ++ if (mask & AA_MAY_LINK) ++ *m++ = 'l'; ++ if (mask & AA_MAY_LOCK) ++ *m++ = 'k'; ++ *m++ = '\0'; ++} ++ ++static void aa_audit_file_mask(struct audit_buffer *ab, const char *name, ++ int mask) ++{ ++ char user[10], other[10]; ++ ++ aa_audit_file_sub_mask(ab, user, ++ (mask & AA_USER_PERMS) >> AA_USER_SHIFT); ++ aa_audit_file_sub_mask(ab, other, ++ (mask & AA_OTHER_PERMS) >> AA_OTHER_SHIFT); ++ ++ audit_log_format(ab, " %s=\"%s::%s\"", name, user, other); ++} ++ ++/** ++ * aa_audit - Log an audit event to the audit subsystem ++ * @profile: profile to check against ++ * @sa: audit event ++ * @audit_cxt: audit context to log message to ++ * @type: audit event number ++ */ ++static int aa_audit_base(struct aa_profile *profile, struct aa_audit *sa, ++ struct audit_context *audit_cxt, int type) ++{ ++ struct audit_buffer *ab = NULL; ++ ++ ab = audit_log_start(audit_cxt, sa->gfp_mask, type); ++ ++ if (!ab) { ++ AA_ERROR("Unable to log event (%d) to audit subsys\n", ++ type); ++ /* don't fail operations in complain mode even if logging ++ * fails */ ++ return type == AUDIT_APPARMOR_ALLOWED ? 0 : -ENOMEM; ++ } ++ ++ if (sa->operation) ++ audit_log_format(ab, "operation=\"%s\"", sa->operation); ++ ++ if (sa->info) { ++ audit_log_format(ab, " info=\"%s\"", sa->info); ++ if (sa->error_code) ++ audit_log_format(ab, " error=%d", sa->error_code); ++ } ++ ++ if (sa->request_mask) ++ aa_audit_file_mask(ab, "requested_mask", sa->request_mask); ++ ++ if (sa->denied_mask) ++ aa_audit_file_mask(ab, "denied_mask", sa->denied_mask); ++ ++ if (sa->request_mask) ++ audit_log_format(ab, " fsuid=%d", current->fsuid); ++ ++ if (sa->iattr) { ++ struct iattr *iattr = sa->iattr; ++ ++ audit_log_format(ab, " attribute=\"%s%s%s%s%s%s%s\"", ++ iattr->ia_valid & ATTR_MODE ? "mode," : "", ++ iattr->ia_valid & ATTR_UID ? "uid," : "", ++ iattr->ia_valid & ATTR_GID ? "gid," : "", ++ iattr->ia_valid & ATTR_SIZE ? "size," : "", ++ iattr->ia_valid & (ATTR_ATIME | ATTR_ATIME_SET) ? ++ "atime," : "", ++ iattr->ia_valid & (ATTR_MTIME | ATTR_MTIME_SET) ? ++ "mtime," : "", ++ iattr->ia_valid & ATTR_CTIME ? "ctime," : ""); ++ } ++ ++ if (sa->task) ++ audit_log_format(ab, " task=%d", sa->task); ++ ++ if (sa->parent) ++ audit_log_format(ab, " parent=%d", sa->parent); ++ ++ if (sa->name) { ++ audit_log_format(ab, " name="); ++ audit_log_untrustedstring(ab, sa->name); ++ } ++ ++ if (sa->name2) { ++ audit_log_format(ab, " name2="); ++ audit_log_untrustedstring(ab, sa->name2); ++ } ++ ++ audit_log_format(ab, " pid=%d", current->pid); ++ ++ if (profile) { ++ audit_log_format(ab, " profile="); ++ audit_log_untrustedstring(ab, profile->name); ++ ++ if (profile->ns != default_namespace) { ++ audit_log_format(ab, " namespace="); ++ audit_log_untrustedstring(ab, profile->ns->name); ++ } ++ } ++ ++ audit_log_end(ab); ++ ++ return type == AUDIT_APPARMOR_ALLOWED ? 0 : sa->error_code; ++} ++ ++/** ++ * aa_audit_syscallreject - Log a syscall rejection to the audit subsystem ++ * @profile: profile to check against ++ * @gfp: memory allocation flags ++ * @msg: string describing syscall being rejected ++ */ ++int aa_audit_syscallreject(struct aa_profile *profile, gfp_t gfp, ++ const char *msg) ++{ ++ struct aa_audit sa; ++ memset(&sa, 0, sizeof(sa)); ++ sa.operation = "syscall"; ++ sa.name = msg; ++ sa.gfp_mask = gfp; ++ sa.error_code = -EPERM; ++ ++ return aa_audit_base(profile, &sa, current->audit_context, ++ AUDIT_APPARMOR_DENIED); ++} ++ ++int aa_audit_message(struct aa_profile *profile, struct aa_audit *sa, ++ int type) ++{ ++ struct audit_context *audit_cxt; ++ ++ audit_cxt = apparmor_logsyscall ? current->audit_context : NULL; ++ return aa_audit_base(profile, sa, audit_cxt, type); ++} ++ ++void aa_audit_hint(struct aa_profile *profile, struct aa_audit *sa) ++{ ++ aa_audit_message(profile, sa, AUDIT_APPARMOR_HINT); ++} ++ ++void aa_audit_status(struct aa_profile *profile, struct aa_audit *sa) ++{ ++ aa_audit_message(profile, sa, AUDIT_APPARMOR_STATUS); ++} ++ ++int aa_audit_reject(struct aa_profile *profile, struct aa_audit *sa) ++{ ++ return aa_audit_message(profile, sa, AUDIT_APPARMOR_DENIED); ++} ++ ++/** ++ * aa_audit - Log an audit event to the audit subsystem ++ * @profile: profile to check against ++ * @sa: audit event ++ */ ++int aa_audit(struct aa_profile *profile, struct aa_audit *sa) ++{ ++ int type = AUDIT_APPARMOR_DENIED; ++ struct audit_context *audit_cxt; ++ ++ if (likely(!sa->error_code)) ++ type = AUDIT_APPARMOR_AUDIT; ++ else if (PROFILE_COMPLAIN(profile)) ++ type = AUDIT_APPARMOR_ALLOWED; ++ ++ audit_cxt = apparmor_logsyscall ? current->audit_context : NULL; ++ return aa_audit_base(profile, sa, audit_cxt, type); ++} ++ ++static int aa_audit_file(struct aa_profile *profile, struct aa_audit *sa) ++{ ++ if (likely(!sa->error_code)) { ++ int mask = sa->audit_mask & AUDIT_FILE_MASK; ++ ++ if (unlikely(PROFILE_AUDIT(profile))) ++ mask |= AUDIT_FILE_MASK; ++ ++ if (likely(!(sa->request_mask & mask))) ++ return 0; ++ ++ /* mask off perms that are not being force audited */ ++ sa->request_mask &= mask | ALL_AA_EXEC_TYPE; ++ } else { ++ int mask = AUDIT_QUIET_MASK(sa->audit_mask); ++ ++ if (!(sa->denied_mask & ~mask)) ++ return sa->error_code; ++ ++ /* mask off perms whose denial is being silenced */ ++ if (!PROFILE_COMPLAIN(profile)) ++ 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.28/apparmor-misc.diff b/kernel-patches/2.6.28/apparmor-misc.diff new file mode 100644 index 000000000..abffd1c48 --- /dev/null +++ b/kernel-patches/2.6.28/apparmor-misc.diff @@ -0,0 +1,1441 @@ +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 | 281 +++++++++++++++++++++++++++++++ + security/apparmor/inline.h | 250 +++++++++++++++++++++++++++ + security/apparmor/list.c | 174 +++++++++++++++++++ + security/apparmor/locking.txt | 68 +++++++ + security/apparmor/procattr.c | 195 +++++++++++++++++++++ + 8 files changed, 1394 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,281 @@ ++/* ++ * 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 ++ ++#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.28/apparmor-module_interface.diff b/kernel-patches/2.6.28/apparmor-module_interface.diff new file mode 100644 index 000000000..f373428d6 --- /dev/null +++ b/kernel-patches/2.6.28/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.28/apparmor-network.diff b/kernel-patches/2.6.28/apparmor-network.diff new file mode 100644 index 000000000..553948198 --- /dev/null +++ b/kernel-patches/2.6.28/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" +@@ -680,6 +681,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) + { +@@ -780,9 +892,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, +@@ -820,6 +929,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 +@@ -321,8 +321,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) +@@ -355,6 +355,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.28/apparmor-path_permission b/kernel-patches/2.6.28/apparmor-path_permission new file mode 100644 index 000000000..41b7ad181 --- /dev/null +++ b/kernel-patches/2.6.28/apparmor-path_permission @@ -0,0 +1,78 @@ +From: Jeff Mahoney +Subject: [PATCH] apparmor: convert apparmor_inode_permission to path + + patches.apparmor/add-security_path_permission added the ->path_permission + call. This patch converts apparmor_inode_permission to + apparmor_path_permission. The former is now a pass-all, which is how + it behaved in 2.6.26 if a NULL nameidata was passed. + +Signed-off-by: Jeff Mahoney +--- + security/apparmor/lsm.c | 41 +++++++++++++++++++++++++++-------------- + 1 file changed, 27 insertions(+), 14 deletions(-) + +--- a/security/apparmor/lsm.c ++++ b/security/apparmor/lsm.c +@@ -448,21 +448,9 @@ out: + return error; + } + +-static int apparmor_inode_permission(struct inode *inode, int mask, +- struct nameidata *nd) ++static int apparmor_inode_permission(struct inode *inode, int mask) + { +- 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); ++ return 0; + } + + static int apparmor_inode_setattr(struct dentry *dentry, struct vfsmount *mnt, +@@ -656,6 +644,29 @@ static int apparmor_file_mprotect(struct + !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); + } + ++static int apparmor_path_permission(struct path *path, int mask) ++{ ++ struct inode *inode; ++ int check = 0; ++ ++ if (!path) ++ return 0; ++ ++ inode = path->dentry->d_inode; ++ ++ mask = aa_mask_permissions(mask); ++ if (S_ISDIR(inode->i_mode)) { ++ check |= AA_CHECK_DIR; ++ /* allow traverse accesses to directories */ ++ mask &= ~MAY_EXEC; ++ if (!mask) ++ return 0; ++ } ++ ++ return aa_permission("inode_permission", inode, path->dentry, ++ path->mnt, mask, check); ++} ++ + static int apparmor_task_alloc_security(struct task_struct *task) + { + return aa_clone(task); +@@ -800,6 +811,8 @@ struct security_operations apparmor_ops + .file_mprotect = apparmor_file_mprotect, + .file_lock = apparmor_file_lock, + ++ .path_permission = apparmor_path_permission, ++ + .task_alloc_security = apparmor_task_alloc_security, + .task_free_security = apparmor_task_free_security, + .task_post_setuid = cap_task_post_setuid, diff --git a/kernel-patches/2.6.28/apparmor-ptrace-2.6.27.diff b/kernel-patches/2.6.28/apparmor-ptrace-2.6.27.diff new file mode 100644 index 000000000..7d80636b8 --- /dev/null +++ b/kernel-patches/2.6.28/apparmor-ptrace-2.6.27.diff @@ -0,0 +1,113 @@ +From: Jeff Mahoney +Subject: apparmor: use new ptrace security_operations + + This patch implements the new ptrace security_operations members. + + ->ptrace was changed to ->ptrace_may_access and ->ptrace_traceme. + + The apparmor versions are really just wrappers for the old function. + +Signed-off-by: Jeff Mahoney +Signed-off-by: John Johansen + +--- + security/apparmor/lsm.c | 51 ++++++++++++++++++++++++++++++------------------ + 1 file changed, 32 insertions(+), 19 deletions(-) + +--- a/security/apparmor/lsm.c ++++ b/security/apparmor/lsm.c +@@ -157,47 +157,47 @@ static int aa_reject_syscall(struct task + return error; + } + +-static int apparmor_ptrace(struct task_struct *parent, +- struct task_struct *child, unsigned int mode) ++static int apparmor_ptrace(struct task_struct *tracer, ++ struct task_struct *tracee) + { + 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 ++ * tracer can ptrace tracee when ++ * - tracer is unconfined ++ * - tracer & tracee are in the same namespace && ++ * - tracer is in complain mode ++ * - tracer and tracee are confined by the same profile ++ * - tracer profile has CAP_SYS_PTRACE + */ + + rcu_read_lock(); +- cxt = aa_task_context(parent); ++ cxt = aa_task_context(tracer); + if (cxt) { +- if (parent->nsproxy != child->nsproxy) { ++ if (tracer->nsproxy != tracee->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.parent = tracer->pid; ++ sa.task = tracee->pid; + sa.info = "different namespaces"; + aa_audit_reject(cxt->profile, &sa); + error = -EPERM; + } else { +- struct aa_task_context *child_cxt = +- aa_task_context(child); ++ struct aa_task_context *tracee_cxt = ++ aa_task_context(tracee); + +- error = aa_may_ptrace(cxt, child_cxt ? +- child_cxt->profile : NULL); ++ error = aa_may_ptrace(cxt, tracee_cxt ? ++ tracee_cxt->profile : NULL); + if (error && 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; ++ sa.parent = tracer->pid; ++ sa.task = tracee->pid; + aa_audit_hint(cxt->profile, &sa); + } + } +@@ -207,6 +207,18 @@ static int apparmor_ptrace(struct task_s + return error; + } + ++static int apparmor_ptrace_may_access(struct task_struct *child, ++ unsigned int mode) ++{ ++ return apparmor_ptrace(current, child); ++} ++ ++ ++static int apparmor_ptrace_traceme(struct task_struct *parent) ++{ ++ return apparmor_ptrace(parent, current); ++} ++ + static int apparmor_capable(struct task_struct *task, int cap) + { + int error; +@@ -899,7 +911,8 @@ static int apparmor_task_setrlimit(unsig + } + + struct security_operations apparmor_ops = { +- .ptrace = apparmor_ptrace, ++ .ptrace_may_access = apparmor_ptrace_may_access, ++ .ptrace_traceme = apparmor_ptrace_traceme, + .capget = cap_capget, + .capset_check = cap_capset_check, + .capset_set = cap_capset_set, diff --git a/kernel-patches/2.6.28/apparmor-rlimits.diff b/kernel-patches/2.6.28/apparmor-rlimits.diff new file mode 100644 index 000000000..366dd8ca2 --- /dev/null +++ b/kernel-patches/2.6.28/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 +@@ -106,7 +106,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 +@@ -883,6 +883,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, +@@ -926,6 +941,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; + +@@ -873,6 +876,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 +@@ -886,6 +962,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; + +@@ -895,6 +972,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) { +@@ -911,18 +993,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); +@@ -1157,6 +1243,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; + } +@@ -1304,6 +1394,12 @@ static int do_change_profile(struct aa_p + goto out; + } + ++ if ((error = aa_rlimit_nproc(new_profile))) { ++ sa->info = "rlimit nproc limit exceeded"; ++ aa_audit_reject(cxt->profile, sa); ++ goto out; ++ } ++ + if (new_profile == ns->null_complain_profile) + aa_audit_hint(cxt->profile, sa); + +@@ -1482,17 +1578,18 @@ struct aa_profile *__aa_replace_profile( + + cxt = lock_task_and_profiles(task, profile); + if (unlikely(profile && profile->isstale)) { +- task_unlock(task); +- unlock_both_profiles(profile, cxt ? cxt->profile : NULL); +- aa_free_task_context(new_cxt); +- return ERR_PTR(-ESTALE); ++ old_profile = ERR_PTR(-ESTALE); ++ goto error; + } + + if ((current->ptrace & PT_PTRACED) && aa_may_ptrace(cxt, profile)) { +- task_unlock(task); +- unlock_both_profiles(profile, cxt ? cxt->profile : NULL); +- aa_free_task_context(new_cxt); +- return ERR_PTR(-EPERM); ++ old_profile = ERR_PTR(-EPERM); ++ goto error; ++ } ++ ++ if (aa_rlimit_nproc(profile)) { ++ old_profile = ERR_PTR(-EAGAIN); ++ goto error; + } + + if (cxt) +@@ -1500,8 +1597,15 @@ struct aa_profile *__aa_replace_profile( + aa_change_task_context(task, new_cxt, profile, 0, NULL); + + task_unlock(task); ++ aa_set_rlimits(task, profile); + unlock_both_profiles(profile, old_profile); + return old_profile; ++ ++error: ++ task_unlock(task); ++ unlock_both_profiles(profile, cxt ? cxt->profile : NULL); ++ aa_free_task_context(new_cxt); ++ return old_profile; + } + + /** +@@ -1566,6 +1670,7 @@ void aa_change_task_context(struct task_ + + if (old_cxt) { + list_del_init(&old_cxt->list); ++ old_cxt->profile->task_count--; + call_rcu(&old_cxt->rcu, free_aa_task_context_rcu_callback); + } + if (new_cxt) { +@@ -1577,6 +1682,7 @@ void aa_change_task_context(struct task_ + new_cxt->cookie = cookie; + new_cxt->task = task; + new_cxt->profile = aa_dup_profile(profile); ++ profile->task_count++; + new_cxt->previous_profile = aa_dup_profile(previous_profile); + list_move(&new_cxt->list, &profile->task_contexts); + } +--- a/security/apparmor/module_interface.c ++++ b/security/apparmor/module_interface.c +@@ -177,6 +177,22 @@ fail: + return 0; + } + ++static int aa_is_u64(struct aa_ext *e, u64 *data, const char *name) ++{ ++ void *pos = e->pos; ++ if (aa_is_nameX(e, AA_U64, name)) { ++ if (!aa_inbounds(e, sizeof(u64))) ++ goto fail; ++ if (data) ++ *data = le64_to_cpu(get_unaligned((u64 *)e->pos)); ++ e->pos += sizeof(u64); ++ return 1; ++ } ++fail: ++ e->pos = pos; ++ return 0; ++} ++ + static size_t aa_is_array(struct aa_ext *e, const char *name) + { + void *pos = e->pos; +@@ -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.28/aufs/aa-hack b/kernel-patches/2.6.28/aufs/aa-hack new file mode 100644 index 000000000..6c0999bfc --- /dev/null +++ b/kernel-patches/2.6.28/aufs/aa-hack @@ -0,0 +1,87 @@ +From: Jeff Mahoney +Subject: aufs: AppArmor compatibility + + This patch adds NULL vfsmounts for AppArmor enabled kernels. + +Signed-off-by: Jeff Mahoney +--- + fs/aufs25/vfsub.c | 18 +++++++++--------- + 1 file changed, 9 insertions(+), 9 deletions(-) + +--- a/fs/aufs25/vfsub.c ++++ b/fs/aufs25/vfsub.c +@@ -127,9 +127,9 @@ int do_vfsub_create(struct inode *dir, s + } + + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) +-#define VfsubSymlinkArgs dir, dentry, symname ++#define VfsubSymlinkArgs dir, dentry, NULL, symname + #else +-#define VfsubSymlinkArgs dir, dentry, symname, mode ++#define VfsubSymlinkArgs dir, dentry, NULL, symname, mode + #endif + + int do_vfsub_symlink(struct inode *dir, struct dentry *dentry, +@@ -158,7 +158,7 @@ int do_vfsub_mknod(struct inode *dir, st + LKTRTrace("i%lu, %.*s, 0x%x\n", dir->i_ino, AuDLNPair(dentry), mode); + IMustLock(dir); + +- err = vfs_mknod(dir, dentry, mode, dev); ++ err = vfs_mknod(dir, dentry, NULL, mode, dev); + if (!err) { + /* dir inode is locked */ + au_update_fuse_h_inode(NULL, dentry->d_parent); /*ignore*/ +@@ -177,7 +177,7 @@ int do_vfsub_link(struct dentry *src_den + IMustLock(dir); + + lockdep_off(); +- err = vfs_link(src_dentry, dir, dentry); ++ err = vfs_link(src_dentry, NULL, dir, dentry, 0); + lockdep_on(); + if (!err) { + LKTRTrace("src_i %p, dst_i %p\n", +@@ -203,7 +203,7 @@ int do_vfsub_rename(struct inode *src_di + IMustLock(src_dir); + + lockdep_off(); +- err = vfs_rename(src_dir, src_dentry, dir, dentry); ++ err = vfs_rename(src_dir, src_dentry, NULL, dir, dentry, 0); + lockdep_on(); + if (!err) { + /* dir inode is locked */ +@@ -221,7 +221,7 @@ int do_vfsub_mkdir(struct inode *dir, st + LKTRTrace("i%lu, %.*s, 0x%x\n", dir->i_ino, AuDLNPair(dentry), mode); + IMustLock(dir); + +- err = vfs_mkdir(dir, dentry, mode); ++ err = vfs_mkdir(dir, dentry, NULL, mode); + if (!err) { + /* dir inode is locked */ + au_update_fuse_h_inode(NULL, dentry->d_parent); /*ignore*/ +@@ -238,7 +238,7 @@ int do_vfsub_rmdir(struct inode *dir, st + IMustLock(dir); + + lockdep_off(); +- err = vfs_rmdir(dir, dentry); ++ err = vfs_rmdir(dir, dentry, 0); + lockdep_on(); + /* dir inode is locked */ + if (!err) +@@ -255,7 +255,7 @@ int do_vfsub_unlink(struct inode *dir, s + + /* vfs_unlink() locks inode */ + lockdep_off(); +- err = vfs_unlink(dir, dentry); ++ err = vfs_unlink(dir, dentry, 0); + lockdep_on(); + /* dir inode is locked */ + if (!err) +@@ -493,7 +493,7 @@ static void call_notify_change(void *arg + if (!IS_IMMUTABLE(h_inode) && !IS_APPEND(h_inode)) { + vfsub_ignore(a->vargs); + lockdep_off(); +- *a->errp = notify_change(a->h_dentry, a->ia); ++ *a->errp = notify_change(a->h_dentry, NULL, a->ia); + lockdep_on(); + if (!*a->errp) + au_update_fuse_h_inode(NULL, a->h_dentry); /*ignore*/ diff --git a/kernel-patches/2.6.28/aufs/aufs-fsetattr b/kernel-patches/2.6.28/aufs/aufs-fsetattr new file mode 100644 index 000000000..88a0872e5 --- /dev/null +++ b/kernel-patches/2.6.28/aufs/aufs-fsetattr @@ -0,0 +1,179 @@ +From: Jeff Mahoney +Subject: Add fsetattr + + An AppArmor patch removed ia_file and ATTR_FILE from struct iattr and + replaced it with the fsetattr file_operation. + + This patch fixes aufs to use it. + +Signed-off-by: Jeff Mahoney +--- + fs/aufs25/dir.c | 1 + + fs/aufs25/f_op.c | 1 + + fs/aufs25/i_op.c | 25 ++++++++++++++----------- + fs/aufs25/inode.h | 1 + + fs/aufs25/misc.c | 5 ++--- + fs/aufs25/vfsub.c | 13 ++++++++++--- + fs/aufs25/vfsub.h | 2 ++ + 7 files changed, 31 insertions(+), 17 deletions(-) + +--- a/fs/aufs25/dir.c ++++ b/fs/aufs25/dir.c +@@ -546,4 +546,5 @@ struct file_operations aufs_dir_fop = { + .release = aufs_release_dir, + .flush = aufs_flush, + .fsync = aufs_fsync_dir, ++ .fsetattr = aufs_fsetattr, + }; +--- a/fs/aufs25/f_op.c ++++ b/fs/aufs25/f_op.c +@@ -665,4 +665,5 @@ struct file_operations aufs_file_fop = { + .splice_write = aufs_splice_write, + .splice_read = aufs_splice_read, + #endif ++ .fsetattr = aufs_fsetattr, + }; +--- a/fs/aufs25/i_op.c ++++ b/fs/aufs25/i_op.c +@@ -727,13 +727,13 @@ static int au_lock_and_icpup(struct dent + return err; + } + +-static int aufs_setattr(struct dentry *dentry, struct iattr *ia) ++static int aufs_do_setattr(struct dentry *dentry, struct iattr *ia, ++ struct file *file) + { + int err; + struct inode *inode; + struct super_block *sb; + __u32 events; +- struct file *file; + loff_t sz; + struct au_icpup_args *a; + +@@ -751,12 +751,8 @@ static int aufs_setattr(struct dentry *d + si_read_lock(sb, AuLock_FLUSH); + vfsub_args_init(&a->vargs, a->ign, au_test_dlgt(au_mntflags(sb)), 0); + +- if (ia->ia_valid & ATTR_FILE) { +- /* currently ftruncate(2) only */ +- file = ia->ia_file; ++ if (file) + fi_write_lock(file); +- ia->ia_file = au_h_fptr(file, au_fbstart(file)); +- } + + sz = -1; + if ((ia->ia_valid & ATTR_SIZE) +@@ -800,11 +796,8 @@ static int aufs_setattr(struct dentry *d + au_unpin(&a->pin); + di_write_unlock(dentry); + out_si: +- if (file) { ++ if (file) + fi_write_unlock(file); +- ia->ia_file = file; +- ia->ia_valid |= ATTR_FILE; +- } + si_read_unlock(sb); + kfree(a); + out: +@@ -812,6 +805,16 @@ static int aufs_setattr(struct dentry *d + return err; + } + ++static int aufs_setattr(struct dentry *dentry, struct iattr *ia) ++{ ++ return aufs_do_setattr(dentry, ia, NULL); ++} ++ ++int aufs_fsetattr(struct file *file, struct iattr *ia) ++{ ++ return aufs_do_setattr(file->f_dentry, ia, file); ++} ++ + /* ---------------------------------------------------------------------- */ + + static int h_readlink(struct dentry *dentry, int bindex, char __user *buf, +--- a/fs/aufs25/inode.h ++++ b/fs/aufs25/inode.h +@@ -84,6 +84,7 @@ int au_test_h_perm_sio(struct inode *h_i + + /* i_op.c */ + extern struct inode_operations aufs_iop, aufs_symlink_iop, aufs_dir_iop; ++int aufs_fsetattr(struct file *file, struct iattr *ia); + + /* au_wr_dir flags */ + #define AuWrDir_ADD_ENTRY 1 +--- a/fs/aufs25/misc.c ++++ b/fs/aufs25/misc.c +@@ -267,13 +267,12 @@ int au_copy_file(struct file *dst, struc + if (err == 1) { + ia = (void *)buf; + ia->ia_size = dst->f_pos; +- ia->ia_valid = ATTR_SIZE | ATTR_FILE; +- ia->ia_file = dst; ++ ia->ia_valid = ATTR_SIZE; + vfsub_args_reinit(vargs); + vfsub_ign_hinode(vargs, vfsub_events_notify_change(ia), + hdir); + mutex_lock_nested(&h_i->i_mutex, AuLsc_I_CHILD2); +- err = vfsub_notify_change(h_d, ia, vargs); ++ err = vfsub_fnotify_change(h_d, ia, vargs, dst); + mutex_unlock(&h_i->i_mutex); + } + } +--- a/fs/aufs25/vfsub.c ++++ b/fs/aufs25/vfsub.c +@@ -477,6 +477,7 @@ struct notify_change_args { + struct dentry *h_dentry; + struct iattr *ia; + struct vfsub_args *vargs; ++ struct file *file; + }; + + static void call_notify_change(void *args) +@@ -493,7 +494,7 @@ static void call_notify_change(void *arg + if (!IS_IMMUTABLE(h_inode) && !IS_APPEND(h_inode)) { + vfsub_ignore(a->vargs); + lockdep_off(); +- *a->errp = notify_change(a->h_dentry, NULL, a->ia); ++ *a->errp = fnotify_change(a->h_dentry, NULL, a->ia, a->file); + lockdep_on(); + if (!*a->errp) + au_update_fuse_h_inode(NULL, a->h_dentry); /*ignore*/ +@@ -525,8 +526,8 @@ static void vfsub_notify_change_dlgt(str + } + #endif + +-int vfsub_notify_change(struct dentry *dentry, struct iattr *ia, +- struct vfsub_args *vargs) ++int vfsub_fnotify_change(struct dentry *dentry, struct iattr *ia, ++ struct vfsub_args *vargs, struct file *file) + { + int err; + struct notify_change_args args = { +@@ -570,6 +571,12 @@ int vfsub_sio_notify_change(struct au_hi + return err; + } + ++int vfsub_notify_change(struct dentry *dentry, struct iattr *ia, ++ struct vfsub_args *vargs) ++{ ++ return vfsub_fnotify_change(dentry, ia, vargs, NULL); ++} ++ + /* ---------------------------------------------------------------------- */ + + struct unlink_args { +--- a/fs/aufs25/vfsub.h ++++ b/fs/aufs25/vfsub.h +@@ -508,6 +508,8 @@ int vfsub_sio_notify_change(struct au_hi + + /* ---------------------------------------------------------------------- */ + ++int vfsub_fnotify_change(struct dentry *dentry, struct iattr *ia, ++ struct vfsub_args *vargs, struct file *file); + int vfsub_notify_change(struct dentry *dentry, struct iattr *ia, + struct vfsub_args *vargs); + int vfsub_unlink(struct inode *dir, struct dentry *dentry, diff --git a/kernel-patches/2.6.28/d_namespace_path.diff b/kernel-patches/2.6.28/d_namespace_path.diff new file mode 100644 index 000000000..e6148fdec --- /dev/null +++ b/kernel-patches/2.6.28/d_namespace_path.diff @@ -0,0 +1,60 @@ +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 | 30 ++++++++++++++++++++++++++++++ + include/linux/mount.h | 2 ++ + 2 files changed, 32 insertions(+) + +--- a/fs/namespace.c ++++ b/fs/namespace.c +@@ -2349,3 +2349,33 @@ 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, tmp, 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); ++ tmp = ns_root; ++ res = __d_path(&path, &tmp, 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 +@@ -112,4 +112,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.28/do_path_lookup-nameidata.diff b/kernel-patches/2.6.28/do_path_lookup-nameidata.diff new file mode 100644 index 000000000..4edb51ecf --- /dev/null +++ b/kernel-patches/2.6.28/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 +@@ -1067,24 +1067,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.28/export-security_inode_permission-for-aufs b/kernel-patches/2.6.28/export-security_inode_permission-for-aufs new file mode 100644 index 000000000..50b3a7337 --- /dev/null +++ b/kernel-patches/2.6.28/export-security_inode_permission-for-aufs @@ -0,0 +1,26 @@ +From: Jeff Mahoney +Subject: [PATCH] LSM: Export security_inode_permission for aufs +Patch-mainline: Never +References: 356902 + + In order for aufs to work with AppArmor, it needs to be able to call + security_inode_permission itself. + + This patch is a _workaround_ since the author will need to find a + mainline-compatible solution moving forward. + +Signed-off-by: Jeff Mahoney +--- + security/security.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/security/security.c ++++ b/security/security.c +@@ -424,6 +424,7 @@ int security_inode_mknod(struct inode *d + return 0; + return security_ops->inode_mknod(dir, dentry, mnt, mode, dev); + } ++EXPORT_SYMBOL_GPL(security_inode_permission); + + int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry, + struct vfsmount *old_mnt, struct inode *new_dir, diff --git a/kernel-patches/2.6.28/file-handle-ops.diff b/kernel-patches/2.6.28/file-handle-ops.diff new file mode 100644 index 000000000..d796ee749 --- /dev/null +++ b/kernel-patches/2.6.28/file-handle-ops.diff @@ -0,0 +1,84 @@ +From: Andreas Gruenbacher +Subject: Enable LSM hooks to distinguish operations on file descriptors from operations on pathnames + +Struct iattr already contains ia_file since commit cc4e69de from +Miklos (which is related to commit befc649c). Use this to pass +struct file down the setattr hooks. This allows LSMs to distinguish +operations on file descriptors from operations on paths. + +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen +Cc: Miklos Szeredi + +--- + fs/nfsd/vfs.c | 12 +++++++----- + fs/open.c | 5 ++++- + 2 files changed, 11 insertions(+), 6 deletions(-) + +--- a/fs/nfsd/vfs.c ++++ b/fs/nfsd/vfs.c +@@ -413,7 +413,7 @@ static ssize_t nfsd_getxattr(struct dent + ssize_t buflen; + ssize_t ret; + +- buflen = vfs_getxattr(dentry, mnt, key, NULL, 0); ++ buflen = vfs_getxattr(dentry, mnt, key, NULL, 0, NULL); + if (buflen <= 0) + return buflen; + +@@ -421,7 +421,7 @@ static ssize_t nfsd_getxattr(struct dent + if (!*buf) + return -ENOMEM; + +- ret = vfs_getxattr(dentry, mnt, key, *buf, buflen); ++ ret = vfs_getxattr(dentry, mnt, key, *buf, buflen, NULL); + if (ret < 0) + kfree(*buf); + return ret; +@@ -450,7 +450,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; +@@ -2212,12 +2212,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 +@@ -604,7 +604,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); + mnt_drop_write(file->f_path.mnt); +@@ -668,6 +668,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.28/fix-complain.diff b/kernel-patches/2.6.28/fix-complain.diff new file mode 100644 index 000000000..85feac101 --- /dev/null +++ b/kernel-patches/2.6.28/fix-complain.diff @@ -0,0 +1,26 @@ +From: John Johansen +Subject: fix enforcement of deny rules in complain mode +Patch-mainline: no +References: bnc#426159 + +Fix enforcement of deny rules so that they are not enforced in complain +mode. This is necessary so that application behavior is not changed by +the presence of the deny rule. + +Signed-off-by: John Johansen + +--- + security/apparmor/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/security/apparmor/main.c ++++ b/security/apparmor/main.c +@@ -325,7 +325,7 @@ static int aa_audit_file(struct aa_profi + } else { + int mask = AUDIT_QUIET_MASK(sa->audit_mask); + +- if (!(sa->denied_mask & ~mask)) ++ if (!(sa->denied_mask & ~mask) && !PROFILE_COMPLAIN(profile)) + return sa->error_code; + + /* mask off perms whose denial is being silenced */ diff --git a/kernel-patches/2.6.28/fix-config.diff b/kernel-patches/2.6.28/fix-config.diff new file mode 100644 index 000000000..f8f5450a7 --- /dev/null +++ b/kernel-patches/2.6.28/fix-config.diff @@ -0,0 +1,14 @@ +--- + security/apparmor/Kconfig | 1 + + 1 file changed, 1 insertion(+) + +--- a/security/apparmor/Kconfig ++++ b/security/apparmor/Kconfig +@@ -1,6 +1,7 @@ + config SECURITY_APPARMOR + bool "AppArmor support" + depends on SECURITY ++ depends on SECURITY_NETWORK + select AUDIT + help + This enables the AppArmor security module. diff --git a/kernel-patches/2.6.28/fix-d_namespace_path.diff b/kernel-patches/2.6.28/fix-d_namespace_path.diff new file mode 100644 index 000000000..5ea93c099 --- /dev/null +++ b/kernel-patches/2.6.28/fix-d_namespace_path.diff @@ -0,0 +1,15 @@ +--- + fs/namespace.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/fs/namespace.c ++++ b/fs/namespace.c +@@ -2362,7 +2362,7 @@ char *d_namespace_path(struct dentry *de + path_get(¤t->fs->root); + read_unlock(¤t->fs->lock); + spin_lock(&vfsmount_lock); +- if (root.mnt) ++ if (root.mnt && root.mnt->mnt_ns) + ns_root.mnt = mntget(root.mnt->mnt_ns->root); + if (ns_root.mnt) + ns_root.dentry = dget(ns_root.mnt->mnt_root); diff --git a/kernel-patches/2.6.28/fix-security-param.diff b/kernel-patches/2.6.28/fix-security-param.diff new file mode 100644 index 000000000..75f0f6923 --- /dev/null +++ b/kernel-patches/2.6.28/fix-security-param.diff @@ -0,0 +1,35 @@ +From: John Johansen +Subject: fix recognition of security= boot parameter +Patch-mainline: no +References: bnc#442668 + +Fix AppArmor to respect the kernel boot parameter security=, so that if a +different lsm is choosen apparmor does not try to register its lsm hooks. + +Signed-off-by: John Johansen + +--- + security/apparmor/lsm.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +--- a/security/apparmor/lsm.c ++++ b/security/apparmor/lsm.c +@@ -911,6 +911,7 @@ static int apparmor_task_setrlimit(unsig + } + + struct security_operations apparmor_ops = { ++ .name = "apparmor", + .ptrace_may_access = apparmor_ptrace_may_access, + .ptrace_traceme = apparmor_ptrace_traceme, + .capget = cap_capget, +@@ -989,8 +990,8 @@ static int __init apparmor_init(void) + { + int error; + +- if (!apparmor_enabled) { +- info_message("AppArmor disabled by boottime parameter\n"); ++ if (!apparmor_enabled || !security_module_enable(&apparmor_ops)) { ++ info_message("AppArmor disabled by boot time parameter\n"); + return 0; + } + diff --git a/kernel-patches/2.6.28/fix-vfs_rmdir.diff b/kernel-patches/2.6.28/fix-vfs_rmdir.diff new file mode 100644 index 000000000..1c5f70533 --- /dev/null +++ b/kernel-patches/2.6.28/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 +@@ -2125,6 +2125,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); +@@ -2132,12 +2136,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.28/fork-tracking.diff b/kernel-patches/2.6.28/fork-tracking.diff new file mode 100644 index 000000000..1b0ab094b --- /dev/null +++ b/kernel-patches/2.6.28/fork-tracking.diff @@ -0,0 +1,56 @@ +From: John Johansen +Subject: fix log messages to enable tools profile learning +Patch-mainline: no +References: bnc#447564 + +The allocation of the child pid is done after the LSM clone hook, which +breaks the AppArmor tools fork tracking, for profiles learning. Output +the parent pid with each log message to enable the tools to handle fork +tracking. + +Signed-off-by: John Johansen + +--- + security/apparmor/main.c | 10 +++++----- + security/apparmor/module_interface.c | 2 +- + 2 files changed, 6 insertions(+), 6 deletions(-) + +--- a/security/apparmor/main.c ++++ b/security/apparmor/main.c +@@ -229,9 +229,13 @@ static int aa_audit_base(struct aa_profi + audit_log_format(ab, " protocol=%d", sa->protocol); + } + +- audit_log_format(ab, " pid=%d", current->pid); ++ audit_log_format(ab, " pid=%d", current->pid); + + if (profile) { ++ if (!sa->parent) ++ audit_log_format(ab, " parent=%d", ++ current->real_parent->pid); ++ + audit_log_format(ab, " profile="); + audit_log_untrustedstring(ab, profile->name); + +@@ -1007,10 +1011,6 @@ repeat: + + unlock_profile(profile); + +- if (APPARMOR_COMPLAIN(child_cxt) && +- profile == profile->ns->null_complain_profile) { +- aa_audit_hint(profile, &sa); +- } + aa_put_profile(profile); + } else + aa_free_task_context(child_cxt); +--- a/security/apparmor/module_interface.c ++++ b/security/apparmor/module_interface.c +@@ -126,7 +126,7 @@ static int aa_is_nameX(struct aa_ext *e, + * AA_NAME tag value is a u16. + */ + if (aa_is_X(e, AA_NAME)) { +- char *tag; ++ char *tag = NULL; + 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))) diff --git a/kernel-patches/2.6.28/fsetattr-reintro-ATTR_FILE.diff b/kernel-patches/2.6.28/fsetattr-reintro-ATTR_FILE.diff new file mode 100644 index 000000000..746ec5aef --- /dev/null +++ b/kernel-patches/2.6.28/fsetattr-reintro-ATTR_FILE.diff @@ -0,0 +1,28 @@ +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 +++ + 1 file changed, 3 insertions(+) + +--- a/fs/open.c ++++ b/fs/open.c +@@ -210,6 +210,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); + diff --git a/kernel-patches/2.6.28/fsetattr-restore-ia_file.diff b/kernel-patches/2.6.28/fsetattr-restore-ia_file.diff new file mode 100644 index 000000000..fbc982bae --- /dev/null +++ b/kernel-patches/2.6.28/fsetattr-restore-ia_file.diff @@ -0,0 +1,61 @@ +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 | 15 +++++++++++++-- + include/linux/fs.h | 11 +++++++++++ + 2 files changed, 24 insertions(+), 2 deletions(-) + +--- a/fs/attr.c ++++ b/fs/attr.c +@@ -168,10 +168,21 @@ int fnotify_change(struct dentry *dentry + down_write(&dentry->d_inode->i_alloc_sem); + + if (inode->i_op && inode->i_op->setattr) { +- if (file && file->f_op && file->f_op->fsetattr) ++ 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); + if (!error) { +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -367,6 +367,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.28/fsetattr.diff b/kernel-patches/2.6.28/fsetattr.diff new file mode 100644 index 000000000..bcebfc5b3 --- /dev/null +++ b/kernel-patches/2.6.28/fsetattr.diff @@ -0,0 +1,411 @@ +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 | 16 +++++++++++++--- + fs/fuse/dir.c | 20 +++++++++----------- + fs/fuse/file.c | 7 +++++++ + fs/fuse/fuse_i.h | 4 ++++ + fs/open.c | 20 ++++++++------------ + fs/utimes.c | 9 +++++---- + include/linux/fs.h | 9 ++------- + 11 files changed, 66 insertions(+), 41 deletions(-) + +--- a/fs/afs/dir.c ++++ b/fs/afs/dir.c +@@ -46,6 +46,7 @@ const struct file_operations afs_dir_fil + .readdir = afs_readdir, + .lock = afs_lock, + .llseek = generic_file_llseek, ++ .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 +@@ -548,6 +548,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; +@@ -168,7 +168,10 @@ int notify_change(struct dentry *dentry, + down_write(&dentry->d_inode->i_alloc_sem); + + if (inode->i_op && inode->i_op->setattr) { +- error = inode->i_op->setattr(dentry, attr); ++ 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) { +@@ -188,5 +191,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 +@@ -1105,21 +1105,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; + +@@ -1138,7 +1139,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; +@@ -1199,8 +1200,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); +@@ -1244,7 +1245,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; +@@ -1314,10 +1315,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 +@@ -1470,6 +1470,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, +@@ -1483,6 +1488,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, + }; + +@@ -1496,6 +1502,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 +@@ -554,6 +554,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 +@@ -209,16 +209,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; + } +@@ -606,7 +602,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); + mnt_drop_write(file->f_path.mnt); + out_putf: +@@ -651,7 +647,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 = dentry->d_inode; + int error; +@@ -670,7 +666,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); + + return error; +@@ -687,7 +683,7 @@ asmlinkage long sys_chown(const char __u + error = mnt_want_write(path.mnt); + if (error) + goto out_release; +- error = chown_common(path.dentry, path.mnt, user, group); ++ error = chown_common(path.dentry, path.mnt, user, group, NULL); + mnt_drop_write(path.mnt); + out_release: + path_put(&path); +@@ -712,7 +708,7 @@ asmlinkage long sys_fchownat(int dfd, co + error = mnt_want_write(path.mnt); + if (error) + goto out_release; +- error = chown_common(path.dentry, path.mnt, user, group); ++ error = chown_common(path.dentry, path.mnt, user, group, NULL); + mnt_drop_write(path.mnt); + out_release: + path_put(&path); +@@ -731,7 +727,7 @@ asmlinkage long sys_lchown(const char __ + error = mnt_want_write(path.mnt); + if (error) + goto out_release; +- error = chown_common(path.dentry, path.mnt, user, group); ++ error = chown_common(path.dentry, path.mnt, user, group, NULL); + mnt_drop_write(path.mnt); + out_release: + path_put(&path); +@@ -755,7 +751,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 +@@ -48,7 +48,8 @@ static bool nsec_valid(long nsec) + return nsec >= 0 && nsec <= 999999999; + } + +-static int utimes_common(struct path *path, struct timespec *times) ++static int utimes_common(struct path *path, struct timespec *times, ++ struct file *f) + { + int error; + struct iattr newattrs; +@@ -102,7 +103,7 @@ static int utimes_common(struct path *pa + } + } + 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: +@@ -149,7 +150,7 @@ long do_utimes(int dfd, char __user *fil + if (!file) + goto out; + +- error = utimes_common(&file->f_path, times); ++ error = utimes_common(&file->f_path, times, file); + fput(file); + } else { + struct path path; +@@ -162,7 +163,7 @@ long do_utimes(int dfd, char __user *fil + if (error) + goto out; + +- error = utimes_common(&path, times); ++ error = utimes_common(&path, times, NULL); + path_put(&path); + } + +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -367,13 +367,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; + }; + + /* +@@ -1306,6 +1299,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 { +@@ -1831,6 +1825,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 inode_permission(struct inode *, int); + extern int generic_permission(struct inode *, int, + int (*check_acl)(struct inode *, int)); diff --git a/kernel-patches/2.6.28/mount-capability.diff b/kernel-patches/2.6.28/mount-capability.diff new file mode 100644 index 000000000..09349ea62 --- /dev/null +++ b/kernel-patches/2.6.28/mount-capability.diff @@ -0,0 +1,55 @@ +--- + security/apparmor/lsm.c | 28 ---------------------------- + 1 file changed, 28 deletions(-) + +--- a/security/apparmor/lsm.c ++++ b/security/apparmor/lsm.c +@@ -143,20 +143,6 @@ static int param_set_aa_enabled(const ch + 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 *tracer, + struct task_struct *tracee) + { +@@ -292,17 +278,6 @@ static int apparmor_bprm_secureexec(stru + 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) + { +@@ -925,9 +900,6 @@ struct security_operations apparmor_ops + .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, diff --git a/kernel-patches/2.6.28/remove_suid.diff b/kernel-patches/2.6.28/remove_suid.diff new file mode 100644 index 000000000..459ac1137 --- /dev/null +++ b/kernel-patches/2.6.28/remove_suid.diff @@ -0,0 +1,41 @@ +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 + +--- + + mm/filemap.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/mm/filemap.c ++++ b/mm/filemap.c +@@ -1773,12 +1773,12 @@ 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 file_remove_suid(struct file *file) +@@ -1793,7 +1793,7 @@ int file_remove_suid(struct file *file) + if (killpriv) + error = security_inode_killpriv(dentry); + if (!error && killsuid) +- error = __remove_suid(dentry, killsuid); ++ error = __remove_suid(&file->f_path, killsuid); + + return error; + } diff --git a/kernel-patches/2.6.28/securit_default.diff b/kernel-patches/2.6.28/securit_default.diff new file mode 100644 index 000000000..8c032d8a5 --- /dev/null +++ b/kernel-patches/2.6.28/securit_default.diff @@ -0,0 +1,34 @@ +--- + security/Kconfig | 9 +++++++++ + security/security.c | 2 +- + 2 files changed, 10 insertions(+), 1 deletion(-) + +--- a/security/Kconfig ++++ b/security/Kconfig +@@ -59,6 +59,15 @@ config SECURITYFS + + If you are unsure how to answer this question, answer N. + ++config SECURITY_DEFAULT ++ string "Default security module" ++ depends on SECURITY ++ default "" ++ help ++ This determines the security module used if the security= ++ boot parmater is not provided. If a security module is not ++ specified the first module to register will be used. ++ + config SECURITY_NETWORK + bool "Socket and Networking Security Hooks" + depends on SECURITY +--- a/security/security.c ++++ b/security/security.c +@@ -18,7 +18,7 @@ + #include + + /* Boot-time LSM user choice */ +-static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1]; ++static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] = CONFIG_SECURITY_DEFAULT; + + /* things that live in capability.c */ + extern struct security_operations default_security_ops; diff --git a/kernel-patches/2.6.28/security-create.diff b/kernel-patches/2.6.28/security-create.diff new file mode 100644 index 000000000..8fa5aa5af --- /dev/null +++ b/kernel-patches/2.6.28/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/capability.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 +@@ -1481,7 +1481,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 +@@ -337,6 +337,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: +@@ -1354,8 +1355,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); +@@ -1618,7 +1619,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); +@@ -1973,6 +1975,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/capability.c ++++ b/security/capability.c +@@ -155,7 +155,7 @@ static int cap_inode_init_security(struc + } + + static int cap_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 +@@ -367,11 +367,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 +@@ -2564,7 +2564,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.28/security-getxattr.diff b/kernel-patches/2.6.28/security-getxattr.diff new file mode 100644 index 000000000..6d9147b1a --- /dev/null +++ b/kernel-patches/2.6.28/security-getxattr.diff @@ -0,0 +1,128 @@ +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/capability.c | 3 ++- + security/security.c | 5 +++-- + security/selinux/hooks.c | 3 ++- + security/smack/smack_lsm.c | 4 +++- + 6 files changed, 18 insertions(+), 10 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 +@@ -446,7 +446,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 +@@ -1400,7 +1400,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); +@@ -1672,7 +1673,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); +@@ -2118,7 +2120,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/capability.c ++++ b/security/capability.c +@@ -241,7 +241,8 @@ static void cap_inode_post_setxattr(stru + { + } + +-static int cap_inode_getxattr(struct dentry *dentry, const char *name) ++static int cap_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt, ++ const char *name) + { + return 0; + } +--- a/security/security.c ++++ b/security/security.c +@@ -500,11 +500,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 +@@ -2794,7 +2794,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); + } +--- a/security/smack/smack_lsm.c ++++ b/security/smack/smack_lsm.c +@@ -671,11 +671,13 @@ static void smack_inode_post_setxattr(st + /* + * smack_inode_getxattr - Smack check on getxattr + * @dentry: the object ++ * @mnt: unused + * @name: unused + * + * Returns 0 if access is permitted, an error code otherwise + */ +-static int smack_inode_getxattr(struct dentry *dentry, const char *name) ++static int smack_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt, ++ const char *name) + { + return smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ); + } diff --git a/kernel-patches/2.6.28/security-link.diff b/kernel-patches/2.6.28/security-link.diff new file mode 100644 index 000000000..e891b9a83 --- /dev/null +++ b/kernel-patches/2.6.28/security-link.diff @@ -0,0 +1,149 @@ +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/capability.c | 5 +++-- + security/security.c | 8 +++++--- + security/selinux/hooks.c | 9 +++++++-- + security/smack/smack_lsm.c | 5 +++-- + 6 files changed, 32 insertions(+), 16 deletions(-) + +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -2391,7 +2391,8 @@ int vfs_link(struct dentry *old_dentry, + if (S_ISDIR(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 +@@ -343,8 +343,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. +@@ -1362,8 +1364,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); +@@ -1628,8 +1631,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); +@@ -1992,8 +1996,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/capability.c ++++ b/security/capability.c +@@ -160,8 +160,9 @@ static int cap_inode_create(struct inode + return 0; + } + +-static int cap_inode_link(struct dentry *old_dentry, struct inode *inode, +- struct dentry *new_dentry) ++static int cap_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 +@@ -375,12 +375,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 +@@ -2570,11 +2570,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); +--- a/security/smack/smack_lsm.c ++++ b/security/smack/smack_lsm.c +@@ -432,8 +432,9 @@ static int smack_inode_init_security(str + * + * Returns 0 if access is permitted, an error code otherwise + */ +-static int smack_inode_link(struct dentry *old_dentry, struct inode *dir, +- struct dentry *new_dentry) ++static int smack_inode_link(struct dentry *old_dentry, struct vfsmount *old_mnt, ++ struct inode *dir, ++ struct dentry *new_dentry, struct vfsmount *new_mnt) + { + int rc; + char *isp; diff --git a/kernel-patches/2.6.28/security-listxattr.diff b/kernel-patches/2.6.28/security-listxattr.diff new file mode 100644 index 000000000..67dd414c2 --- /dev/null +++ b/kernel-patches/2.6.28/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/capability.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 +@@ -450,7 +450,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 +@@ -1402,7 +1402,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); +@@ -1675,7 +1675,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); +@@ -2126,7 +2126,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/capability.c ++++ b/security/capability.c +@@ -247,7 +247,7 @@ static int cap_inode_getxattr(struct den + return 0; + } + +-static int cap_inode_listxattr(struct dentry *dentry) ++static int cap_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt) + { + return 0; + } +--- a/security/security.c ++++ b/security/security.c +@@ -508,11 +508,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 +@@ -2800,7 +2800,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.28/security-mkdir.diff b/kernel-patches/2.6.28/security-mkdir.diff new file mode 100644 index 000000000..b0b605e56 --- /dev/null +++ b/kernel-patches/2.6.28/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/capability.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 +@@ -2037,7 +2037,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 +@@ -362,6 +362,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: +@@ -1363,7 +1364,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); +@@ -1628,7 +1630,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, +@@ -2006,6 +2009,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/capability.c ++++ b/security/capability.c +@@ -178,7 +178,7 @@ static int cap_inode_symlink(struct inod + } + + static int cap_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 +@@ -398,11 +398,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 +@@ -2595,7 +2595,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.28/security-mknod.diff b/kernel-patches/2.6.28/security-mknod.diff new file mode 100644 index 000000000..c5ccb67c8 --- /dev/null +++ b/kernel-patches/2.6.28/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/capability.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 +@@ -1942,7 +1942,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; + +@@ -2004,11 +2004,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 +@@ -377,6 +377,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. +@@ -1368,7 +1369,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); +@@ -1633,7 +1634,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); +@@ -2023,6 +2025,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/capability.c ++++ b/security/capability.c +@@ -189,7 +189,7 @@ static int cap_inode_rmdir(struct inode + } + + static int cap_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 +@@ -413,11 +413,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 +@@ -2606,11 +2606,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.28/security-readlink.diff b/kernel-patches/2.6.28/security-readlink.diff new file mode 100644 index 000000000..5c5d2da6f --- /dev/null +++ b/kernel-patches/2.6.28/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/capability.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(path.dentry); ++ error = security_inode_readlink(path.dentry, path.mnt); + if (!error) { + touch_atime(path.mnt, path.dentry); + error = inode->i_op->readlink(path.dentry, +--- a/include/linux/security.h ++++ b/include/linux/security.h +@@ -392,6 +392,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. +@@ -1373,7 +1374,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); + int (*inode_setattr) (struct dentry *dentry, struct vfsmount *, +@@ -1639,7 +1640,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); + int security_inode_setattr(struct dentry *dentry, struct vfsmount *mnt, +@@ -2041,7 +2042,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/capability.c ++++ b/security/capability.c +@@ -200,7 +200,7 @@ static int cap_inode_rename(struct inode + return 0; + } + +-static int cap_inode_readlink(struct dentry *dentry) ++static int cap_inode_readlink(struct dentry *dentry, struct vfsmount *mnt) + { + return 0; + } +--- a/security/security.c ++++ b/security/security.c +@@ -431,11 +431,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 +@@ -2625,7 +2625,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.28/security-removexattr.diff b/kernel-patches/2.6.28/security-removexattr.diff new file mode 100644 index 000000000..7d0f7705f --- /dev/null +++ b/kernel-patches/2.6.28/security-removexattr.diff @@ -0,0 +1,143 @@ +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/security.c | 5 +++-- + security/selinux/hooks.c | 3 ++- + security/smack/smack_lsm.c | 6 ++++-- + 6 files changed, 21 insertions(+), 12 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 +@@ -57,7 +57,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); +@@ -1403,7 +1404,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); +@@ -1676,7 +1678,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); +@@ -2133,9 +2136,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 +@@ -426,7 +426,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/security.c ++++ b/security/security.c +@@ -515,11 +515,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 +@@ -2805,7 +2805,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); +--- a/security/smack/smack_lsm.c ++++ b/security/smack/smack_lsm.c +@@ -685,13 +685,15 @@ static int smack_inode_getxattr(struct d + /* + * smack_inode_removexattr - Smack check on removexattr + * @dentry: the object ++ * @mnt: unused + * @name: name of the attribute + * + * Removing the Smack attribute requires CAP_MAC_ADMIN + * + * Returns 0 if access is permitted, an error code otherwise + */ +-static int smack_inode_removexattr(struct dentry *dentry, const char *name) ++static int smack_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt, ++ const char *name) + { + int rc = 0; + +@@ -701,7 +703,7 @@ static int smack_inode_removexattr(struc + if (!capable(CAP_MAC_ADMIN)) + rc = -EPERM; + } else +- rc = cap_inode_removexattr(dentry, name); ++ rc = cap_inode_removexattr(dentry, mnt, name); + + if (rc == 0) + rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE); diff --git a/kernel-patches/2.6.28/security-rename.diff b/kernel-patches/2.6.28/security-rename.diff new file mode 100644 index 000000000..76c8d0af9 --- /dev/null +++ b/kernel-patches/2.6.28/security-rename.diff @@ -0,0 +1,160 @@ +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/capability.c | 3 ++- + security/security.c | 7 ++++--- + security/selinux/hooks.c | 8 ++++++-- + security/smack/smack_lsm.c | 6 +++++- + 6 files changed, 31 insertions(+), 12 deletions(-) + +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -2518,7 +2518,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; + +@@ -2552,7 +2553,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 +@@ -390,8 +390,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. +@@ -1380,7 +1382,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); +@@ -1649,7 +1653,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); +@@ -2050,8 +2055,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/capability.c ++++ b/security/capability.c +@@ -198,7 +198,8 @@ static int cap_inode_mknod(struct inode + } + + static int cap_inode_rename(struct inode *old_inode, struct dentry *old_dentry, +- struct inode *new_inode, struct dentry *new_dentry) ++ struct vfsmount *old_mnt, struct inode *new_inode, ++ struct dentry *new_dentry, struct vfsmount *new_mnt) + { + return 0; + } +--- a/security/security.c ++++ b/security/security.c +@@ -426,13 +426,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 +@@ -2626,8 +2626,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); + } +--- a/security/smack/smack_lsm.c ++++ b/security/smack/smack_lsm.c +@@ -509,8 +509,10 @@ static int smack_inode_rmdir(struct inod + * smack_inode_rename - Smack check on rename + * @old_inode: the old directory + * @old_dentry: unused ++ * @old_mnt: unused + * @new_inode: the new directory + * @new_dentry: unused ++ * @new_mnt: unused + * + * Read and write access is required on both the old and + * new directories. +@@ -519,8 +521,10 @@ static int smack_inode_rmdir(struct inod + */ + static int smack_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) + { + int rc; + char *isp; diff --git a/kernel-patches/2.6.28/security-rmdir.diff b/kernel-patches/2.6.28/security-rmdir.diff new file mode 100644 index 000000000..ddfcfa811 --- /dev/null +++ b/kernel-patches/2.6.28/security-rmdir.diff @@ -0,0 +1,127 @@ +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/capability.c | 3 ++- + security/security.c | 5 +++-- + security/selinux/hooks.c | 3 ++- + security/smack/smack_lsm.c | 4 +++- + 6 files changed, 18 insertions(+), 9 deletions(-) + +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -2132,7 +2132,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 +@@ -372,6 +372,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 +@@ -1372,7 +1373,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, +@@ -1639,7 +1641,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, +@@ -2027,7 +2030,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/capability.c ++++ b/security/capability.c +@@ -184,7 +184,8 @@ static int cap_inode_mkdir(struct inode + return 0; + } + +-static int cap_inode_rmdir(struct inode *inode, struct dentry *dentry) ++static int cap_inode_rmdir(struct inode *inode, struct dentry *dentry, ++ struct vfsmount *mnt) + { + return 0; + } +--- a/security/security.c ++++ b/security/security.c +@@ -408,11 +408,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 +@@ -2607,7 +2607,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); + } +--- a/security/smack/smack_lsm.c ++++ b/security/smack/smack_lsm.c +@@ -480,11 +480,13 @@ static int smack_inode_unlink(struct ino + * smack_inode_rmdir - Smack check on directory deletion + * @dir: containing directory object + * @dentry: directory to unlink ++ * @mnt: vfsmount @dentry to unlink + * + * Returns 0 if current can write the containing directory + * and the directory, error code otherwise + */ +-static int smack_inode_rmdir(struct inode *dir, struct dentry *dentry) ++static int smack_inode_rmdir(struct inode *dir, struct dentry *dentry, ++ struct vfsmount *mnt) + { + int rc; + diff --git a/kernel-patches/2.6.28/security-setattr.diff b/kernel-patches/2.6.28/security-setattr.diff new file mode 100644 index 000000000..ebeceb0a9 --- /dev/null +++ b/kernel-patches/2.6.28/security-setattr.diff @@ -0,0 +1,139 @@ +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 | 2 +- + fs/fat/file.c | 2 +- + include/linux/security.h | 10 +++++++--- + security/capability.c | 3 ++- + security/security.c | 5 +++-- + security/selinux/hooks.c | 5 +++-- + security/smack/smack_lsm.c | 3 ++- + 7 files changed, 19 insertions(+), 11 deletions(-) + +--- a/fs/attr.c ++++ b/fs/attr.c +@@ -160,7 +160,7 @@ int notify_change(struct dentry *dentry, + if (!(attr->ia_valid & ~(ATTR_KILL_SUID | ATTR_KILL_SGID))) + return 0; + +- error = security_inode_setattr(dentry, attr); ++ error = security_inode_setattr(dentry, mnt, attr); + if (error) + return error; + +--- a/fs/fat/file.c ++++ b/fs/fat/file.c +@@ -93,7 +93,7 @@ int fat_generic_ioctl(struct inode *inod + * out the RO attribute for checking by the security + * module, just because it maps to a file mode. + */ +- err = security_inode_setattr(filp->f_path.dentry, &ia); ++ err = security_inode_setattr(filp->f_path.dentry, filp->f_path.mnt, &ia); + if (err) + goto up; + +--- a/include/linux/security.h ++++ b/include/linux/security.h +@@ -412,6 +412,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: +@@ -1371,7 +1372,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); +- int (*inode_setattr) (struct dentry *dentry, struct iattr *attr); ++ int (*inode_setattr) (struct dentry *dentry, struct vfsmount *, ++ 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, +@@ -1634,7 +1636,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); +-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, +@@ -2046,7 +2049,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/capability.c ++++ b/security/capability.c +@@ -216,7 +216,8 @@ static int cap_inode_permission(struct i + return 0; + } + +-static int cap_inode_setattr(struct dentry *dentry, struct iattr *iattr) ++static int cap_inode_setattr(struct dentry *dentry, struct vfsmount *mnt, ++ struct iattr *iattr) + { + return 0; + } +--- a/security/security.c ++++ b/security/security.c +@@ -450,11 +450,12 @@ int security_inode_permission(struct ino + return security_ops->inode_permission(inode, mask); + } + +-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); + } + EXPORT_SYMBOL_GPL(security_inode_setattr); + +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -2654,11 +2654,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; + +--- a/security/smack/smack_lsm.c ++++ b/security/smack/smack_lsm.c +@@ -559,7 +559,8 @@ static int smack_inode_permission(struct + * + * Returns 0 if access is permitted, an error code otherwise + */ +-static int smack_inode_setattr(struct dentry *dentry, struct iattr *iattr) ++static int smack_inode_setattr(struct dentry *dentry, struct vfsmount *mnt, ++ struct iattr *iattr) + { + /* + * Need to allow for clearing the setuid bit. diff --git a/kernel-patches/2.6.28/security-setxattr.diff b/kernel-patches/2.6.28/security-setxattr.diff new file mode 100644 index 000000000..af799e8d4 --- /dev/null +++ b/kernel-patches/2.6.28/security-setxattr.diff @@ -0,0 +1,256 @@ +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/capability.c | 3 ++- + security/commoncap.c | 5 +++-- + security/security.c | 16 ++++++++++------ + security/selinux/hooks.c | 8 +++++--- + security/smack/smack_lsm.c | 12 ++++++++---- + 7 files changed, 56 insertions(+), 33 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 +@@ -54,8 +54,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); +@@ -438,11 +439,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. +@@ -1392,10 +1393,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); +@@ -1662,10 +1666,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); +@@ -2097,13 +2103,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/capability.c ++++ b/security/capability.c +@@ -235,7 +235,8 @@ static void cap_inode_delete(struct inod + { + } + +-static void cap_inode_post_setxattr(struct dentry *dentry, const char *name, ++static void cap_inode_post_setxattr(struct dentry *dentry, struct vfsmount *mnt, ++ const char *name, + const void *value, size_t size, int flags) + { + } +--- a/security/commoncap.c ++++ b/security/commoncap.c +@@ -411,8 +411,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/security.c ++++ b/security/security.c +@@ -480,20 +480,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 +@@ -2711,8 +2711,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; +@@ -2766,7 +2767,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) + { +--- a/security/smack/smack_lsm.c ++++ b/security/smack/smack_lsm.c +@@ -595,6 +595,7 @@ static int smack_inode_getattr(struct vf + /** + * smack_inode_setxattr - Smack check for setting xattrs + * @dentry: the object ++ * @mnt: unused + * @name: name of the attribute + * @value: unused + * @size: unused +@@ -604,8 +605,9 @@ static int smack_inode_getattr(struct vf + * + * Returns 0 if access is permitted, an error code otherwise + */ +-static int smack_inode_setxattr(struct dentry *dentry, const char *name, +- const void *value, size_t size, int flags) ++static int smack_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt, ++ const char *name, const void *value, ++ size_t size, int flags) + { + int rc = 0; + +@@ -615,7 +617,7 @@ static int smack_inode_setxattr(struct d + if (!capable(CAP_MAC_ADMIN)) + rc = -EPERM; + } else +- rc = cap_inode_setxattr(dentry, name, value, size, flags); ++ rc = cap_inode_setxattr(dentry, mnt, name, value, size, flags); + + if (rc == 0) + rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE); +@@ -626,6 +628,7 @@ static int smack_inode_setxattr(struct d + /** + * smack_inode_post_setxattr - Apply the Smack update approved above + * @dentry: object ++ * @mnt: unused + * @name: attribute name + * @value: attribute value + * @size: attribute size +@@ -634,7 +637,8 @@ static int smack_inode_setxattr(struct d + * Set the pointer in the inode blob to the entry found + * in the master label list. + */ +-static void smack_inode_post_setxattr(struct dentry *dentry, const char *name, ++static void smack_inode_post_setxattr(struct dentry *dentry, ++ struct vfsmount *mnt, const char *name, + const void *value, size_t size, int flags) + { + struct inode_smack *isp; diff --git a/kernel-patches/2.6.28/security-symlink.diff b/kernel-patches/2.6.28/security-symlink.diff new file mode 100644 index 000000000..22687fb44 --- /dev/null +++ b/kernel-patches/2.6.28/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/capability.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 +@@ -2312,7 +2312,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 +@@ -355,6 +355,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: +@@ -1363,8 +1364,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); +@@ -1630,7 +1631,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); +@@ -2004,6 +2005,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/capability.c ++++ b/security/capability.c +@@ -172,7 +172,7 @@ static int cap_inode_unlink(struct inode + } + + static int cap_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 +@@ -391,11 +391,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 +@@ -2590,7 +2590,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.28/security-unlink.diff b/kernel-patches/2.6.28/security-unlink.diff new file mode 100644 index 000000000..d95d1806d --- /dev/null +++ b/kernel-patches/2.6.28/security-unlink.diff @@ -0,0 +1,132 @@ +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/capability.c | 3 ++- + security/security.c | 5 +++-- + security/selinux/hooks.c | 5 +++-- + security/smack/smack_lsm.c | 4 +++- + 6 files changed, 19 insertions(+), 10 deletions(-) + +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -2215,7 +2215,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 +@@ -352,6 +352,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. +@@ -1368,7 +1369,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, +@@ -1636,7 +1638,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, +@@ -2008,7 +2011,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/capability.c ++++ b/security/capability.c +@@ -167,7 +167,8 @@ static int cap_inode_link(struct dentry + return 0; + } + +-static int cap_inode_unlink(struct inode *inode, struct dentry *dentry) ++static int cap_inode_unlink(struct inode *inode, struct dentry *dentry, ++ struct vfsmount *mnt) + { + return 0; + } +--- a/security/security.c ++++ b/security/security.c +@@ -385,11 +385,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 +@@ -2585,11 +2585,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); +--- a/security/smack/smack_lsm.c ++++ b/security/smack/smack_lsm.c +@@ -454,11 +454,13 @@ static int smack_inode_link(struct dentr + * smack_inode_unlink - Smack check on inode deletion + * @dir: containing directory object + * @dentry: file to unlink ++ * @mnt: vfsmount of file to unlink + * + * Returns 0 if current can write the containing directory + * and the object, error code otherwise + */ +-static int smack_inode_unlink(struct inode *dir, struct dentry *dentry) ++static int smack_inode_unlink(struct inode *dir, struct dentry *dentry, ++ struct vfsmount *mnt) + { + struct inode *ip = dentry->d_inode; + int rc; diff --git a/kernel-patches/2.6.28/security-xattr-file.diff b/kernel-patches/2.6.28/security-xattr-file.diff new file mode 100644 index 000000000..09ac46309 --- /dev/null +++ b/kernel-patches/2.6.28/security-xattr-file.diff @@ -0,0 +1,592 @@ +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 | 59 +++++++++++++++++++++++---------------------- + include/linux/security.h | 38 ++++++++++++++++------------ + include/linux/xattr.h | 9 +++--- + security/capability.c | 5 ++- + security/commoncap.c | 4 +-- + security/security.c | 17 ++++++------ + security/selinux/hooks.c | 10 ++++--- + security/smack/smack_lsm.c | 14 ++++++---- + 8 files changed, 87 insertions(+), 69 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,7 @@ sys_setxattr(const char __user *pathname + return error; + error = mnt_want_write(path.mnt); + if (!error) { +- error = setxattr(path.dentry, path.mnt, name, value, size, flags); ++ error = setxattr(path.dentry, path.mnt, name, value, size, flags, NULL); + mnt_drop_write(path.mnt); + } + path_put(&path); +@@ -285,7 +286,7 @@ sys_lsetxattr(const char __user *pathnam + return error; + error = mnt_want_write(path.mnt); + if (!error) { +- error = setxattr(path.dentry, path.mnt, name, value, size, flags); ++ error = setxattr(path.dentry, path.mnt, name, value, size, flags, NULL); + mnt_drop_write(path.mnt); + } + path_put(&path); +@@ -307,7 +308,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 +321,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 +341,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 +364,7 @@ sys_getxattr(const char __user *pathname + error = user_path(pathname, &path); + if (error) + return error; +- error = getxattr(path.dentry, path.mnt, name, value, size); ++ error = getxattr(path.dentry, path.mnt, name, value, size, NULL); + path_put(&path); + return error; + } +@@ -377,7 +379,7 @@ sys_lgetxattr(const char __user *pathnam + error = user_lpath(pathname, &path); + if (error) + return error; +- error = getxattr(path.dentry, path.mnt, name, value, size); ++ error = getxattr(path.dentry, path.mnt, name, value, size, NULL); + path_put(&path); + return error; + } +@@ -392,7 +394,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 +404,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 +417,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 +439,7 @@ sys_listxattr(const char __user *pathnam + error = user_path(pathname, &path); + if (error) + return error; +- error = listxattr(path.dentry, path.mnt, list, size); ++ error = listxattr(path.dentry, path.mnt, list, size, NULL); + path_put(&path); + return error; + } +@@ -451,7 +453,7 @@ sys_llistxattr(const char __user *pathna + error = user_lpath(pathname, &path); + if (error) + return error; +- error = listxattr(path.dentry, path.mnt, list, size); ++ error = listxattr(path.dentry, path.mnt, list, size, NULL); + path_put(&path); + return error; + } +@@ -466,7 +468,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 +477,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 +489,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 +503,7 @@ sys_removexattr(const char __user *pathn + return error; + error = mnt_want_write(path.mnt); + if (!error) { +- error = removexattr(path.dentry, path.mnt, name); ++ error = removexattr(path.dentry, path.mnt, name, NULL); + mnt_drop_write(path.mnt); + } + path_put(&path); +@@ -518,7 +521,7 @@ sys_lremovexattr(const char __user *path + return error; + error = mnt_want_write(path.mnt); + if (!error) { +- error = removexattr(path.dentry, path.mnt, name); ++ error = removexattr(path.dentry, path.mnt, name, NULL); + mnt_drop_write(path.mnt); + } + path_put(&path); +@@ -539,7 +542,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 +@@ -56,9 +56,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); +@@ -1396,16 +1396,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); +@@ -1671,15 +1672,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); +@@ -2110,9 +2112,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, +@@ -2124,22 +2127,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 +@@ -17,6 +17,7 @@ + + #include + #include ++#include + + /* Namespaces */ + #define XATTR_OS2_PREFIX "os2." +@@ -48,10 +49,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/capability.c ++++ b/security/capability.c +@@ -242,12 +242,13 @@ static void cap_inode_post_setxattr(stru + } + + static int cap_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt, +- const char *name) ++ const char *name, struct file *f) + { + return 0; + } + +-static int cap_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt) ++static int cap_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt, ++ struct file *f) + { + return 0; + } +--- a/security/commoncap.c ++++ b/security/commoncap.c +@@ -413,7 +413,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)) +@@ -427,7 +427,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/security.c ++++ b/security/security.c +@@ -482,12 +482,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, +@@ -501,26 +501,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 +@@ -2713,7 +2713,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; +@@ -2795,18 +2795,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); +--- a/security/smack/smack_lsm.c ++++ b/security/smack/smack_lsm.c +@@ -600,6 +600,7 @@ static int smack_inode_getattr(struct vf + * @value: unused + * @size: unused + * @flags: unused ++ * @file: unused + * + * This protects the Smack attribute explicitly. + * +@@ -607,7 +608,7 @@ static int smack_inode_getattr(struct vf + */ + static int smack_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) + { + int rc = 0; + +@@ -617,7 +618,8 @@ static int smack_inode_setxattr(struct d + if (!capable(CAP_MAC_ADMIN)) + rc = -EPERM; + } else +- rc = cap_inode_setxattr(dentry, mnt, name, value, size, flags); ++ rc = cap_inode_setxattr(dentry, mnt, name, value, size, flags, ++ file); + + if (rc == 0) + rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE); +@@ -673,11 +675,12 @@ static void smack_inode_post_setxattr(st + * @dentry: the object + * @mnt: unused + * @name: unused ++ * @file: unused + * + * Returns 0 if access is permitted, an error code otherwise + */ + static int smack_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt, +- const char *name) ++ const char *name, struct file *file) + { + return smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ); + } +@@ -687,13 +690,14 @@ static int smack_inode_getxattr(struct d + * @dentry: the object + * @mnt: unused + * @name: name of the attribute ++ * @file: unused + * + * Removing the Smack attribute requires CAP_MAC_ADMIN + * + * Returns 0 if access is permitted, an error code otherwise + */ + static int smack_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt, +- const char *name) ++ const char *name, struct file *file) + { + int rc = 0; + +@@ -703,7 +707,7 @@ static int smack_inode_removexattr(struc + if (!capable(CAP_MAC_ADMIN)) + rc = -EPERM; + } else +- rc = cap_inode_removexattr(dentry, mnt, name); ++ rc = cap_inode_removexattr(dentry, mnt, name, file); + + if (rc == 0) + rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE); diff --git a/kernel-patches/2.6.28/series b/kernel-patches/2.6.28/series new file mode 100644 index 000000000..1cae793d9 --- /dev/null +++ b/kernel-patches/2.6.28/series @@ -0,0 +1,63 @@ +securit_default.diff +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 +add-security_path_permission +add-path_permission.diff +do_path_lookup-nameidata.diff +apparmor-audit.diff +apparmor-main.diff +apparmor-lsm.diff +apparmor-path_permission +apparmor-module_interface.diff +apparmor-misc.diff +apparmor-intree.diff +apparmor-network.diff +apparmor-rlimits.diff +apparmor-2.6.25.diff +apparmor-ptrace-2.6.27.diff +export-security_inode_permission-for-aufs + +#aufs/aa-hack +#aufs/aufs-fsetattr +fix-security-param.diff +fix-complain.diff +fix-d_namespace_path.diff +fork-tracking.diff +mount-capability.diff +fix-config.diff +AppArmor-checkpatch.diff +AppArmor-misc-cleanups.diff diff --git a/kernel-patches/2.6.28/sysctl-pathname.diff b/kernel-patches/2.6.28/sysctl-pathname.diff new file mode 100644 index 000000000..e042d3eb8 --- /dev/null +++ b/kernel-patches/2.6.28/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 +@@ -996,6 +996,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 +@@ -1528,6 +1528,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 +@@ -1814,40 +1814,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.28/unambiguous-__d_path.diff b/kernel-patches/2.6.28/unambiguous-__d_path.diff new file mode 100644 index 000000000..325f1326f --- /dev/null +++ b/kernel-patches/2.6.28/unambiguous-__d_path.diff @@ -0,0 +1,258 @@ +From: Andreas Gruenbacher +Subject: Fix __d_path() for lazy unmounts and make it unambiguous + +First, when __d_path() hits a lazily unmounted mount point, it tries to prepend +the name of the lazily unmounted dentry to the path name. It gets this wrong, +and also overwrites the slash that separates the name from the following +pathname component. This patch fixes that; if a process was in directory +/foo/bar and /foo got lazily unmounted, the old result was ``foobar'' (note the +missing slash), while the new result with this patch is ``foo/bar''. + +Second, it isn't always possible to tell from the __d_path() result whether the +specified root and rootmnt (i.e., the chroot) was reached. We need an +unambiguous result for AppArmor at least though, so we make sure that paths +will only start with a slash if the path leads all the way up to the root. + +We also add a @fail_deleted argument, which allows to get rid of some of the +mess in sys_getcwd(). + +This patch leaves getcwd() and d_path() as they were before for everything +except for bind-mounted directories; for them, it reports ``/foo/bar'' instead +of ``foobar'' in the example described above. + +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen +Acked-by: Alan Cox + +--- + fs/dcache.c | 126 +++++++++++++++++++++++++++---------------------- + fs/seq_file.c | 4 - + include/linux/dcache.h | 5 + + 3 files changed, 75 insertions(+), 60 deletions(-) + +--- a/fs/dcache.c ++++ b/fs/dcache.c +@@ -1907,44 +1907,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; + } +@@ -1954,27 +1956,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; + } + +@@ -2011,10 +2037,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; + } +@@ -2097,9 +2121,9 @@ Elong: + */ + asmlinkage long sys_getcwd(char __user *buf, unsigned long size) + { +- int error; +- struct path pwd, root; +- char *page = (char *) __get_free_page(GFP_USER); ++ int error, len; ++ struct path pwd, root, tmp; ++ char *page = (char *) __get_free_page(GFP_USER), *cwd; + + if (!page) + return -ENOMEM; +@@ -2111,30 +2135,20 @@ 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); +- ++ tmp = root; ++ cwd = __d_path(&pwd, &tmp, page, PAGE_SIZE, D_PATH_FAIL_DELETED); ++ if (IS_ERR(cwd)) { + error = PTR_ERR(cwd); +- if (IS_ERR(cwd)) +- goto out; ++ 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); ++ 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 +@@ -412,9 +412,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 +@@ -300,9 +300,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.28/vfs-getxattr.diff b/kernel-patches/2.6.28/vfs-getxattr.diff new file mode 100644 index 000000000..8d88eea6e --- /dev/null +++ b/kernel-patches/2.6.28/vfs-getxattr.diff @@ -0,0 +1,191 @@ +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 +@@ -1458,7 +1458,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,12 +407,13 @@ 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; + ssize_t ret; + +- buflen = vfs_getxattr(dentry, key, NULL, 0); ++ buflen = vfs_getxattr(dentry, mnt, key, NULL, 0); + if (buflen <= 0) + return buflen; + +@@ -420,7 +421,7 @@ static ssize_t nfsd_getxattr(struct dent + if (!*buf) + return -ENOMEM; + +- ret = vfs_getxattr(dentry, key, *buf, buflen); ++ ret = vfs_getxattr(dentry, mnt, key, *buf, buflen); + if (ret < 0) + kfree(*buf); + return ret; +@@ -504,13 +505,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) +@@ -522,14 +523,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)) { +@@ -539,7 +541,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)) { +@@ -2159,7 +2161,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 *pathname + error = user_path(pathname, &path); + if (error) + return error; +- error = getxattr(path.dentry, name, value, size); ++ error = getxattr(path.dentry, path.mnt, name, value, size); + path_put(&path); + return error; + } +@@ -374,7 +375,7 @@ sys_lgetxattr(const char __user *pathnam + error = user_lpath(pathname, &path); + if (error) + return error; +- error = getxattr(path.dentry, name, value, size); ++ error = getxattr(path.dentry, path.mnt, name, value, size); + path_put(&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 +@@ -86,7 +86,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 +@@ -48,7 +48,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.28/vfs-link.diff b/kernel-patches/2.6.28/vfs-link.diff new file mode 100644 index 000000000..5b1f70a3e --- /dev/null +++ b/kernel-patches/2.6.28/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 +@@ -403,19 +403,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 +@@ -2366,7 +2366,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; +@@ -2445,7 +2445,9 @@ asmlinkage long sys_linkat(int olddfd, c + error = mnt_want_write(nd.path.mnt); + if (error) + goto out_dput; +- error = vfs_link(old_path.dentry, nd.path.dentry->d_inode, new_dentry); ++ error = vfs_link(old_path.dentry, old_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 +@@ -1625,7 +1625,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 +@@ -1208,7 +1208,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 *); +-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.28/vfs-listxattr.diff b/kernel-patches/2.6.28/vfs-listxattr.diff new file mode 100644 index 000000000..83c74f48b --- /dev/null +++ b/kernel-patches/2.6.28/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 *pathnam + error = user_path(pathname, &path); + if (error) + return error; +- error = listxattr(path.dentry, list, size); ++ error = listxattr(path.dentry, path.mnt, list, size); + path_put(&path); + return error; + } +@@ -448,7 +451,7 @@ sys_llistxattr(const char __user *pathna + error = user_lpath(pathname, &path); + if (error) + return error; +- error = listxattr(path.dentry, list, size); ++ error = listxattr(path.dentry, path.mnt, list, size); + path_put(&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 +@@ -49,7 +49,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.28/vfs-mkdir.diff b/kernel-patches/2.6.28/vfs-mkdir.diff new file mode 100644 index 000000000..659ea2c21 --- /dev/null +++ b/kernel-patches/2.6.28/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 +@@ -501,11 +501,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 +@@ -2025,7 +2025,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); + +@@ -2068,7 +2069,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.mnt); + if (status) + goto out_put; +- status = vfs_mkdir(rec_dir.dentry->d_inode, dentry, S_IRWXU); ++ status = vfs_mkdir(rec_dir.dentry->d_inode, dentry, rec_dir.mnt, ++ S_IRWXU); + mnt_drop_write(rec_dir.mnt); + out_put: + dput(dentry); +--- a/fs/nfsd/vfs.c ++++ b/fs/nfsd/vfs.c +@@ -1190,6 +1190,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; +@@ -1207,6 +1208,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; +@@ -1223,7 +1225,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 { +@@ -1273,7 +1275,7 @@ nfsd_create(struct svc_rqst *rqstp, stru + host_err = vfs_create(dirp, dchild, iap->ia_mode, NULL); + break; + case S_IFDIR: +- host_err = vfs_mkdir(dirp, dchild, iap->ia_mode); ++ host_err = vfs_mkdir(dirp, dchild, exp->ex_path.mnt, iap->ia_mode); + break; + case S_IFCHR: + case S_IFBLK: +@@ -1287,7 +1289,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 +@@ -1205,7 +1205,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 *); + extern int vfs_link(struct dentry *, struct inode *, struct dentry *); +--- a/kernel/cgroup.c ++++ b/kernel/cgroup.c +@@ -2966,7 +2966,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.28/vfs-mknod.diff b/kernel-patches/2.6.28/vfs-mknod.diff new file mode 100644 index 000000000..a695577a7 --- /dev/null +++ b/kernel-patches/2.6.28/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 +@@ -552,11 +552,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 +@@ -1924,7 +1924,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); + +@@ -2002,11 +2003,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 +@@ -1281,7 +1281,8 @@ nfsd_create(struct svc_rqst *rqstp, stru + case S_IFBLK: + case S_IFIFO: + case S_IFSOCK: +- host_err = vfs_mknod(dirp, dchild, iap->ia_mode, rdev); ++ host_err = vfs_mknod(dirp, dchild, exp->ex_path.mnt, ++ iap->ia_mode, rdev); + break; + } + if (host_err < 0) { +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -1206,7 +1206,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 *); + 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.28/vfs-notify_change.diff b/kernel-patches/2.6.28/vfs-notify_change.diff new file mode 100644 index 000000000..732b90ac4 --- /dev/null +++ b/kernel-patches/2.6.28/vfs-notify_change.diff @@ -0,0 +1,321 @@ +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/hpfs/namei.c | 2 +- + fs/namei.c | 2 +- + fs/nfsd/vfs.c | 8 ++++---- + fs/open.c | 28 +++++++++++++++------------- + fs/reiserfs/xattr.c | 6 +++--- + fs/utimes.c | 2 +- + include/linux/fs.h | 6 +++--- + mm/filemap.c | 2 +- + 11 files changed, 36 insertions(+), 30 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 +@@ -849,6 +849,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; +@@ -859,6 +860,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); +@@ -910,7 +912,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 +@@ -1821,7 +1821,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/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 +@@ -1557,7 +1557,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); + } +@@ -947,13 +947,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); + } + +@@ -1012,7 +1012,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 +@@ -197,8 +197,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; +@@ -218,7 +218,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; + } +@@ -274,7 +274,7 @@ static long do_sys_truncate(const char _ + error = locks_verify_truncate(inode, NULL, length); + if (!error) { + DQUOT_INIT(inode); +- error = do_truncate(path.dentry, length, 0, NULL); ++ error = do_truncate(path.dentry, path.mnt, length, 0, NULL); + } + + put_write_and_out: +@@ -329,7 +329,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: +@@ -605,7 +606,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); + mnt_drop_write(file->f_path.mnt); + out_putf: +@@ -635,7 +636,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(path.dentry, &newattrs); ++ error = notify_change(path.dentry, path.mnt, &newattrs); + mutex_unlock(&inode->i_mutex); + mnt_drop_write(path.mnt); + dput_and_out: +@@ -649,7 +650,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 = dentry->d_inode; + int error; +@@ -668,7 +670,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); + + return error; +@@ -685,7 +687,7 @@ asmlinkage long sys_chown(const char __u + error = mnt_want_write(path.mnt); + if (error) + goto out_release; +- error = chown_common(path.dentry, user, group); ++ error = chown_common(path.dentry, path.mnt, user, group); + mnt_drop_write(path.mnt); + out_release: + path_put(&path); +@@ -710,7 +712,7 @@ asmlinkage long sys_fchownat(int dfd, co + error = mnt_want_write(path.mnt); + if (error) + goto out_release; +- error = chown_common(path.dentry, user, group); ++ error = chown_common(path.dentry, path.mnt, user, group); + mnt_drop_write(path.mnt); + out_release: + path_put(&path); +@@ -729,7 +731,7 @@ asmlinkage long sys_lchown(const char __ + error = mnt_want_write(path.mnt); + if (error) + goto out_release; +- error = chown_common(path.dentry, user, group); ++ error = chown_common(path.dentry, path.mnt, user, group); + mnt_drop_write(path.mnt); + out_release: + path_put(&path); +@@ -753,7 +755,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/utimes.c ++++ b/fs/utimes.c +@@ -102,7 +102,7 @@ static int utimes_common(struct path *pa + } + } + mutex_lock(&inode->i_mutex); +- error = notify_change(path->dentry, &newattrs); ++ error = notify_change(path->dentry, path->mnt, &newattrs); + mutex_unlock(&inode->i_mutex); + + mnt_drop_write_and_out: +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -1669,8 +1669,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); +@@ -1830,7 +1830,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 inode_permission(struct inode *, int); + extern int generic_permission(struct inode *, int, + int (*check_acl)(struct inode *, int)); +--- a/mm/filemap.c ++++ b/mm/filemap.c +@@ -1778,7 +1778,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 file_remove_suid(struct file *file) diff --git a/kernel-patches/2.6.28/vfs-removexattr.diff b/kernel-patches/2.6.28/vfs-removexattr.diff new file mode 100644 index 000000000..f80915600 --- /dev/null +++ b/kernel-patches/2.6.28/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 +@@ -2174,6 +2174,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; +@@ -2206,22 +2207,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 *pathn + return error; + error = mnt_want_write(path.mnt); + if (!error) { +- error = removexattr(path.dentry, name); ++ error = removexattr(path.dentry, path.mnt, name); + mnt_drop_write(path.mnt); + } + path_put(&path); +@@ -518,7 +518,7 @@ sys_lremovexattr(const char __user *path + return error; + error = mnt_want_write(path.mnt); + if (!error) { +- error = removexattr(path.dentry, name); ++ error = removexattr(path.dentry, path.mnt, name); + mnt_drop_write(path.mnt); + } + path_put(&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 +@@ -51,7 +51,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.28/vfs-rename.diff b/kernel-patches/2.6.28/vfs-rename.diff new file mode 100644 index 000000000..36f5bb3fb --- /dev/null +++ b/kernel-patches/2.6.28/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 +@@ -590,19 +590,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 +@@ -2502,7 +2502,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; +@@ -2545,7 +2546,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; +@@ -2573,7 +2575,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); +@@ -2602,9 +2605,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, +@@ -2685,8 +2690,8 @@ asmlinkage long sys_renameat(int olddfd, + 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 +@@ -1727,7 +1727,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 +@@ -1211,7 +1211,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.28/vfs-rmdir.diff b/kernel-patches/2.6.28/vfs-rmdir.diff new file mode 100644 index 000000000..42bfee0a5 --- /dev/null +++ b/kernel-patches/2.6.28/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 +@@ -534,14 +534,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 +@@ -2115,7 +2115,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); + +@@ -2181,7 +2181,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 +@@ -1765,6 +1765,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; +@@ -1779,6 +1780,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); +@@ -1800,21 +1802,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 +@@ -1209,7 +1209,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 *); + 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.28/vfs-setxattr.diff b/kernel-patches/2.6.28/vfs-setxattr.diff new file mode 100644 index 000000000..aced2ccf0 --- /dev/null +++ b/kernel-patches/2.6.28/vfs-setxattr.diff @@ -0,0 +1,159 @@ +From: Tony Jones +Subject: Add a struct vfsmount parameter to vfs_setxattr() + +The vfsmount will be passed down to the LSM hook so that LSMs can compute +pathnames. + +Signed-off-by: Tony Jones +Signed-off-by: Andreas Gruenbacher +Signed-off-by: John Johansen + +--- + fs/nfsd/vfs.c | 16 +++++++++++----- + fs/xattr.c | 16 ++++++++-------- + include/linux/xattr.h | 3 ++- + 3 files changed, 21 insertions(+), 14 deletions(-) + +--- a/fs/nfsd/vfs.c ++++ b/fs/nfsd/vfs.c +@@ -429,7 +429,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; +@@ -448,7 +449,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; +@@ -461,6 +462,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; +@@ -471,6 +473,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; +@@ -481,12 +484,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); +@@ -2202,7 +2207,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 *pathname + return error; + error = mnt_want_write(path.mnt); + if (!error) { +- error = setxattr(path.dentry, name, value, size, flags); ++ error = setxattr(path.dentry, path.mnt, name, value, size, flags); + mnt_drop_write(path.mnt); + } + path_put(&path); +@@ -282,7 +282,7 @@ sys_lsetxattr(const char __user *pathnam + return error; + error = mnt_want_write(path.mnt); + if (!error) { +- error = setxattr(path.dentry, name, value, size, flags); ++ error = setxattr(path.dentry, path.mnt, name, value, size, flags); + mnt_drop_write(path.mnt); + } + path_put(&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 +@@ -16,6 +16,7 @@ + #ifdef __KERNEL__ + + #include ++#include + + /* Namespaces */ + #define XATTR_OS2_PREFIX "os2." +@@ -49,7 +50,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.28/vfs-symlink.diff b/kernel-patches/2.6.28/vfs-symlink.diff new file mode 100644 index 000000000..2c17f0ed3 --- /dev/null +++ b/kernel-patches/2.6.28/vfs-symlink.diff @@ -0,0 +1,123 @@ +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 | 5 +++-- + fs/nfsd/vfs.c | 12 ++++++++---- + include/linux/fs.h | 2 +- + 4 files changed, 15 insertions(+), 8 deletions(-) + +--- a/fs/ecryptfs/inode.c ++++ b/fs/ecryptfs/inode.c +@@ -464,6 +464,7 @@ static int ecryptfs_symlink(struct inode + { + int rc; + struct dentry *lower_dentry; ++ struct vfsmount *lower_mnt; + struct dentry *lower_dir_dentry; + char *encoded_symname; + int encoded_symlen; +@@ -471,6 +472,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); + encoded_symlen = ecryptfs_encode_filename(crypt_stat, symname, + strlen(symname), +@@ -479,7 +481,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); + kfree(encoded_symname); + if (rc || !lower_dentry->d_inode) +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -2301,7 +2301,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 vfs_symlink(struct inode *dir, struct dentry *dentry, struct vfsmount *mnt, ++ const char *oldname) + { + int error = may_create(dir, dentry); + +@@ -2347,7 +2348,7 @@ 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); ++ error = vfs_symlink(nd.path.dentry->d_inode, dentry, nd.path.mnt, from); + mnt_drop_write(nd.path.mnt); + out_dput: + dput(dentry); +--- a/fs/nfsd/vfs.c ++++ b/fs/nfsd/vfs.c +@@ -1520,6 +1520,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; + +@@ -1544,6 +1545,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) +@@ -1551,14 +1553,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); ++ host_err = vfs_symlink(dentry->d_inode, dnew, ++ exp->ex_path.mnt, path_alloced); + kfree(path_alloced); + } + } else +- host_err = vfs_symlink(dentry->d_inode, dnew, path); ++ host_err = vfs_symlink(dentry->d_inode, dnew, exp->ex_path.mnt, ++ path); + + if (!host_err) { +- if (EX_ISSYNC(fhp->fh_export)) ++ if (EX_ISSYNC(exp)) + host_err = nfsd_sync_dir(dentry); + } + err = nfserrno(host_err); +@@ -1566,7 +1570,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 +@@ -1207,7 +1207,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 *); ++extern int vfs_symlink(struct inode *, struct dentry *, struct vfsmount *, const char *); + 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.28/vfs-unlink.diff b/kernel-patches/2.6.28/vfs-unlink.diff new file mode 100644 index 000000000..ee82d2781 --- /dev/null +++ b/kernel-patches/2.6.28/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 +@@ -445,11 +445,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 +@@ -2199,7 +2199,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); + +@@ -2267,7 +2267,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 +@@ -1807,7 +1807,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 +@@ -1210,7 +1210,7 @@ extern int vfs_mknod(struct inode *, str + extern int vfs_symlink(struct inode *, struct dentry *, struct vfsmount *, const char *); + 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 +@@ -753,7 +753,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);