diff --git a/kernel-patches/for-mainline/apparmor-path_resize.diff b/kernel-patches/for-mainline/apparmor-path_resize.diff deleted file mode 100644 index 809e6e09d..000000000 --- a/kernel-patches/for-mainline/apparmor-path_resize.diff +++ /dev/null @@ -1,169 +0,0 @@ -Index: linux-2.6/security/apparmor/apparmor.h -=================================================================== ---- linux-2.6.orig/security/apparmor/apparmor.h -+++ linux-2.6/security/apparmor/apparmor.h -@@ -218,7 +218,8 @@ extern int aa_audit_message(struct aapro - extern int aa_audit_syscallreject(struct aaprofile *active, gfp_t gfp, - const char *); - extern int aa_audit(struct aaprofile *active, const struct aa_audit *); --extern char *aa_get_name(struct dentry *dentry, struct vfsmount *mnt); -+extern char *aa_get_name(struct dentry *dentry, struct vfsmount *mnt, -+ char **addr); - - extern int aa_attr(struct aaprofile *active, struct dentry *dentry, - struct vfsmount *mnt, struct iattr *iattr); -Index: linux-2.6/security/apparmor/inline.h -=================================================================== ---- linux-2.6.orig/security/apparmor/inline.h -+++ linux-2.6/security/apparmor/inline.h -@@ -214,9 +214,12 @@ static inline struct aaprofile *alloc_aa - * Release space (free_page) allocated to hold pathname - * name may be NULL (checked for by free_page) - */ --static inline void aa_put_name(const char *name) -+static inline void aa_put_name(const char *name, char *addr) - { -- aa_put_path((char *)name); -+ if (addr) -+ kfree(addr); -+ else -+ aa_put_path((char *)name); - } - - /** __aa_find_profile -Index: linux-2.6/security/apparmor/main.c -=================================================================== ---- linux-2.6.orig/security/apparmor/main.c -+++ linux-2.6/security/apparmor/main.c -@@ -318,8 +318,9 @@ static int _aa_perm_vfsmount(struct aapr - struct vfsmount *mnt, struct aa_audit *sa, int mask) - { - int permerror, error; -+ char *addr; - -- sa->name = aa_get_name(dentry, mnt); -+ sa->name = aa_get_name(dentry, mnt, &addr); - - if (IS_ERR(sa->name)) { - permerror = PTR_ERR(sa->name); -@@ -332,7 +333,7 @@ static int _aa_perm_vfsmount(struct aapr - - error = aa_audit(active, sa); - -- aa_put_name(sa->name); -+ aa_put_name(sa->name, addr); - - return error; - } -@@ -658,27 +659,37 @@ out: - * aa_get_name - retrieve fully qualified path name - * @dentry: relative path element - * @mnt: where in tree -- * -+ * @addr: the true start address of returned names buffer if buffer > a -+ * single page - * Returns fully qualified path name on sucess, NULL on failure. - * aa_put_name must be used to free allocated buffer. - */ --char *aa_get_name(struct dentry *dentry, struct vfsmount *mnt) -+char *aa_get_name(struct dentry *dentry, struct vfsmount *mnt, char **addr) - { -- char *page, *name; -+ char *buffer, *name; -+ int order = 0; - -- page = (char *)aa_get_path(GFP_KERNEL); -- if (!page) { -+ *addr = NULL; -+ buffer = (char *)aa_get_path(GFP_KERNEL); -+ retry: -+ if (!buffer) { - name = ERR_PTR(-ENOMEM); - goto out; - } - -- name = d_path(dentry, mnt, page, PAGE_SIZE); -+ name = d_path(dentry, mnt, buffer, PAGE_SIZE << order); - /* check for (deleted) that d_path appends to pathnames if the dentry - * has been removed from the cache. - * The size > deleted_size and strcmp checks are redundant safe guards. - */ - if (IS_ERR(name)) { -- aa_put_path(page); -+ if (order == 0) -+ aa_put_path(buffer); -+ else -+ kfree(buffer); -+ order++; -+ buffer = kmalloc(PAGE_SIZE << order, GFP_KERNEL); -+ goto retry; - } else { - const char deleted_str[] = " (deleted)"; - const size_t deleted_size = sizeof(deleted_str) - 1; -@@ -692,6 +703,8 @@ char *aa_get_name(struct dentry *dentry, - } - - out: -+ if (order > 0) -+ *addr = buffer; - return name; - } - -@@ -766,6 +779,7 @@ int aa_perm(struct aaprofile *active, st - { - int error = 0; - struct aa_audit sa; -+ char *addr; - - if ((mask = aa_filter_mask(mask, dentry->d_inode)) == 0) - goto out; -@@ -850,9 +864,10 @@ int aa_link(struct aaprofile *active, - { - int permerror = -EPERM, error; - struct aa_audit sa; -+ char *addr, *paddr; - -- sa.name = aa_get_name(link, link_mnt); -- sa.pval = aa_get_name(target, target_mnt); -+ sa.name = aa_get_name(link, link_mnt, &addr); -+ sa.pval = aa_get_name(target, target_mnt, &paddr); - - if (IS_ERR(sa.name)) { - permerror = PTR_ERR(sa.name); -@@ -874,8 +889,8 @@ int aa_link(struct aaprofile *active, - - error = aa_audit(active, &sa); - -- aa_put_name(sa.name); -- aa_put_name(sa.pval); -+ aa_put_name(sa.name, addr); -+ aa_put_name(sa.pval, paddr); - - return error; - } -@@ -942,6 +957,7 @@ int aa_fork(struct task_struct *p) - int aa_register(struct linux_binprm *bprm) - { - char *filename; -+ char *addr; - struct file *filp = bprm->file; - struct aaprofile *active; - struct aaprofile *newprofile = NULL, unconstrained_flag; -@@ -954,7 +970,7 @@ int aa_register(struct linux_binprm *bpr - - AA_DEBUG("%s\n", __FUNCTION__); - -- filename = aa_get_name(filp->f_dentry, filp->f_vfsmnt); -+ filename = aa_get_name(filp->f_dentry, filp->f_vfsmnt, &addr); - if (IS_ERR(filename)) { - AA_WARN("%s: Failed to get filename\n", __FUNCTION__); - goto out; -@@ -1206,7 +1222,7 @@ apply_profile: - } - - cleanup: -- aa_put_name(filename); -+ aa_put_name(filename, addr); - - put_aaprofile(active); - diff --git a/kernel-patches/for-mainline/apparmor-percpu_path_cache.diff b/kernel-patches/for-mainline/apparmor-percpu_path_cache.diff deleted file mode 100644 index dd82b80bd..000000000 --- a/kernel-patches/for-mainline/apparmor-percpu_path_cache.diff +++ /dev/null @@ -1,218 +0,0 @@ -Index: linux-2.6/security/apparmor/apparmor.h -=================================================================== ---- linux-2.6.orig/security/apparmor/apparmor.h -+++ linux-2.6/security/apparmor/apparmor.h -@@ -197,7 +197,19 @@ enum aa_xattroptype { - #define BASE_PROFILE(p) ((p)->parent ? (p)->parent : (p)) - #define IN_SUBPROFILE(p) ((p)->parent) - -+/* path name buffer cache */ -+#define AAPATH_CACHE_MAX_COUNT 2 -+ -+struct aa_path_cache_head { -+ unsigned int count; -+ struct list_head list; -+}; -+ - /* main.c */ -+extern void aa_destroy_path_cache(void); -+extern int aa_init_path_cache(void); -+extern char *aa_get_path(gfp_t gfp); -+extern void aa_put_path(char *path); - extern int alloc_null_complain_profile(void); - extern void free_null_complain_profile(void); - extern int attach_nullprofile(struct aaprofile *profile); -Index: linux-2.6/security/apparmor/inline.h -=================================================================== ---- linux-2.6.orig/security/apparmor/inline.h -+++ linux-2.6/security/apparmor/inline.h -@@ -216,7 +216,7 @@ static inline struct aaprofile *alloc_aa - */ - static inline void aa_put_name(const char *name) - { -- free_page((unsigned long)name); -+ aa_put_path((char *)name); - } - - /** __aa_find_profile -Index: linux-2.6/security/apparmor/lsm.c -=================================================================== ---- linux-2.6.orig/security/apparmor/lsm.c -+++ linux-2.6/security/apparmor/lsm.c -@@ -816,6 +816,11 @@ static int __init apparmor_init(void) - goto alloc_out; - } - -+ if ((error = aa_init_path_cache())) { -+ AA_ERROR("Unable to allocate path cache\n"); -+ goto path_out; -+ } -+ - if ((error = register_security(&apparmor_ops))) { - AA_ERROR("Unable to load AppArmor\n"); - goto register_security_out; -@@ -830,6 +835,9 @@ static int __init apparmor_init(void) - return error; - - register_security_out: -+ aa_destroy_path_cache(); -+ -+path_out: - free_null_complain_profile(); - - alloc_out: -Index: linux-2.6/security/apparmor/main.c -=================================================================== ---- linux-2.6.orig/security/apparmor/main.c -+++ linux-2.6/security/apparmor/main.c -@@ -12,11 +12,132 @@ - #include - #include - #include -+#include - - #include "apparmor.h" - - #include "inline.h" - -+static DEFINE_PER_CPU(struct aa_path_cache_head, aa_path_cache); -+ -+/** -+ * aa_destroy_path_cache - destroy the path cache initialized -+ */ -+void aa_destroy_path_cache(void) -+{ -+ int cpu; -+ -+ for_each_possible_cpu(cpu) { -+ struct list_head *n, *tmp; -+ if (per_cpu(aa_path_cache, cpu).count == 0) -+ continue; -+ per_cpu(aa_path_cache, cpu).count = 0; -+ list_for_each_safe(n, tmp, &per_cpu(aa_path_cache, cpu).list) { -+ list_del(n); -+ free_page((unsigned long) n); -+ } -+ } -+} -+ -+#define ALIGN_TO_PAGE(X) (((unsigned long)(X)) & PAGE_MASK) -+ -+static inline int aa_push_path(struct aa_path_cache_head *cache, char *path) -+{ -+ int ret = 0; -+ -+ if (cache->count < AAPATH_CACHE_MAX_COUNT) { -+ list_add((struct list_head *) ALIGN_TO_PAGE(path), -+ &cache->list); -+ cache->count++; -+ ret = 1; -+ } -+ -+ return ret; -+} -+ -+/** -+ * aa_init_path_cache - initialize AA's path cache -+ */ -+int aa_init_path_cache(void) -+{ -+ int cpu; -+ int error = -ENOMEM; -+ -+ for_each_possible_cpu(cpu) { -+ per_cpu(aa_path_cache, cpu).count = 0; -+ INIT_LIST_HEAD(&per_cpu(aa_path_cache, cpu).list); -+ } -+ -+ /* initialize the path cache with 2 pages/cpu */ -+ for_each_possible_cpu(cpu) { -+ char *page; -+ -+ page = (char *) __get_free_page(GFP_KERNEL); -+ if (!page) -+ goto out; -+ aa_push_path(&per_cpu(aa_path_cache, cpu), page); -+ page = (char *) __get_free_page(GFP_KERNEL); -+ if (!page) -+ goto out; -+ aa_push_path(&per_cpu(aa_path_cache, cpu), page); -+ error = 0; -+ } -+ -+out: -+ return error; -+} -+ -+/** -+ * aa_get_path - get a path buffer for use in path lookups -+ * @gfp: the type of memory allocation to perform if no buffer available in -+ * the cache. -+ */ -+char *aa_get_path(gfp_t gfp) -+{ -+ struct aa_path_cache_head *cache; -+ char *path; -+ -+ cache = &get_cpu_var(aa_path_cache); -+ if (cache->count) { -+ struct list_head *node; -+ list_for_each(node, &cache->list) { -+ list_del(node); -+ break; -+ } -+ cache->count--; -+ path = (char *) node; -+ put_cpu_var(aa_path_cache); -+ } else { -+ put_cpu_var(aa_path_cache); -+ path = (char *) __get_free_page(gfp); -+ } -+ return path; -+} -+ -+/** -+ * aa_put_path - put a path buffer obtained with aa_get_path -+ * @path: the path buffer to return to the path buffer cache -+ */ -+void aa_put_path(char *path) -+{ -+ if (!path) -+ goto out; -+ -+ /* does the path buffer belong on the current cpu's node */ -+ if (cpu_to_node(smp_processor_id()) == -+ page_to_nid(virt_to_page(path))) { -+ struct aa_path_cache_head *cache = &get_cpu_var(aa_path_cache); -+ int success = aa_push_path(cache, path); -+ put_cpu_var(aa_path_cache); -+ if (success) -+ goto out; -+ } -+ free_page((unsigned long) path); -+ -+out: -+ return; -+} -+ - /* NULL complain profile - * - * Used when in complain mode, to emit Permitting messages for non-existant -@@ -545,7 +666,7 @@ char *aa_get_name(struct dentry *dentry, - { - char *page, *name; - -- page = (char *)__get_free_page(GFP_KERNEL); -+ page = (char *)aa_get_path(GFP_KERNEL); - if (!page) { - name = ERR_PTR(-ENOMEM); - goto out; -@@ -557,7 +678,7 @@ char *aa_get_name(struct dentry *dentry, - * The size > deleted_size and strcmp checks are redundant safe guards. - */ - if (IS_ERR(name)) { -- free_page((unsigned long)page); -+ aa_put_path(page); - } else { - const char deleted_str[] = " (deleted)"; - const size_t deleted_size = sizeof(deleted_str) - 1; diff --git a/kernel-patches/for-mainline/d_path-lazy-unmounts.diff b/kernel-patches/for-mainline/d_path-lazy-unmounts.diff index c16dcdbb0..c3729d87d 100644 --- a/kernel-patches/for-mainline/d_path-lazy-unmounts.diff +++ b/kernel-patches/for-mainline/d_path-lazy-unmounts.diff @@ -1,43 +1,87 @@ -Index: linux-2.6/fs/dcache.c +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. + +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: +lazy unmounts of bind mounts will produce a path that does start with a +non-slash so we can tell from that, but other lazy unmounts will produce +a path that starts with a slash, just like "ordinary" paths. + +Third, sys_getcwd() shouldn't return disconnected paths. The patch +checks for that, and makes it fail with -ENOENT in that case. + +The attached patch cleans up __d_path() to fix the bug with overlapping +pathname components. It also adds a @fail_deleted argument, which allows +to get rid of some of the mess in sys_getcwd(). We make sure that paths +will only start with a slash if the path leads all the way up to the +root. If the resulting path would otherwise be empty, we return "." +instead so that some users of seq_path for files in /proc won't break. + +The @fail_deleted argument allows sys_getcwd() to be simplified. +Grabbing the dcache_lock can be moved into __d_path(). + +The @fail_deleted argument could be added to d_path() as well: this would +allow callers to recognize deleted files without having to resort to the +ambiguous check for the " (deleted)" string at the end of the pathnames. +This is not currently done, but it might be worthwhile. + +Signed-off-by: Andreas Gruenbacher +Reviewed-by: NeilBrown + +Index: b/fs/dcache.c =================================================================== ---- linux-2.6.orig/fs/dcache.c -+++ linux-2.6/fs/dcache.c -@@ -1739,45 +1739,43 @@ shouldnt_be_hashed: +--- a/fs/dcache.c ++++ b/fs/dcache.c +@@ -1732,52 +1732,51 @@ shouldnt_be_hashed: + } + + /** +- * d_path - return the path of a dentry ++ * __d_path - return the path of a dentry + * @dentry: dentry to report + * @vfsmnt: vfsmnt to which the dentry belongs + * @root: root dentry * @rootmnt: vfsmnt to which the root dentry belongs * @buffer: buffer to return value in * @buflen: buffer length -+ * @fail_deleted: what to return when hitting a deleted dentry ++ * @fail_deleted: what to return for deleted files * - * Convert a dentry into an ASCII path name. If the entry has been deleted -- * the string " (deleted)" is appended. Note that this is ambiguous. + * Convert a dentry into an ASCII path name. If the entry has been deleted, + * then if @fail_deleted is true, ERR_PTR(-ENOENT) is returned. Otherwise, -+ * the the string " (deleted)" is appended. Note that this is ambiguous. + * the string " (deleted)" is appended. Note that this is ambiguous. * - * Returns the buffer or an error code if the path was too long. -- * ++ * If @dentry is not connected to @root, the path returned will be relative ++ * (i.e., it will not start with a slash). + * - * "buflen" should be positive. Caller holds the dcache_lock. + * Returns the buffer or an error code. */ -static char * __d_path( struct dentry *dentry, struct vfsmount *vfsmnt, - struct dentry *root, struct vfsmount *rootmnt, - char *buffer, int buflen) +-{ +- char * end = buffer+buflen; +- char * retval; +- int namelen; +static char *__d_path(struct dentry *dentry, struct vfsmount *vfsmnt, + struct dentry *root, struct vfsmount *rootmnt, + char *buffer, int buflen, int fail_deleted) - { -- char * end = buffer+buflen; -- char * retval; -+ char *end = buffer + buflen - 1; - int namelen; - -- *--end = '\0'; -+ buffer = end; ++{ ++ int namelen, is_slash; ++ + if (buflen < 2) + return ERR_PTR(-ENAMETOOLONG); -+ *end = '\0'; - buflen--; -+ ++ buffer += --buflen; ++ *buffer = '\0'; + +- *--end = '\0'; +- buflen--; + spin_lock(&dcache_lock); if (!IS_ROOT(dentry) && d_unhashed(dentry)) { - buflen -= 10; @@ -72,26 +116,26 @@ Index: linux-2.6/fs/dcache.c spin_lock(&vfsmount_lock); if (vfsmnt->mnt_parent == vfsmnt) { spin_unlock(&vfsmount_lock); -@@ -1791,33 +1789,49 @@ static char * __d_path( struct dentry *d +@@ -1791,33 +1790,63 @@ static char * __d_path( struct dentry *d parent = dentry->d_parent; prefetch(parent); namelen = dentry->d_name.len; - buflen -= namelen + 1; - if (buflen < 0) -+ if (buflen <= namelen) ++ if (buflen < namelen + 1) goto Elong; - end -= namelen; - memcpy(end, dentry->d_name.name, namelen); - *--end = '/'; - retval = end; + buflen -= namelen + 1; -+ buffer -= namelen; ++ buffer -= namelen + 1; + memcpy(buffer, dentry->d_name.name, namelen); -+ *--buffer = '/'; ++ *buffer = '/'; dentry = parent; } -+ /* Get '/' right */ -+ if (buffer == end) ++ /* Get '/' right. */ ++ if (*buffer != '/') + *--buffer = '/'; - return retval; @@ -101,27 +145,40 @@ Index: linux-2.6/fs/dcache.c global_root: + /* -+ * We went past the (vfsmount, dentry) we were loking for and have -+ * either hit a root dentry, a lazily unmounted dentry, or an -+ * unconnected dentry. Make sure we won't return a pathname rooted -+ * in '/'. ++ * We went past the (vfsmount, dentry) we were looking for and have ++ * either hit a root dentry, a lazily unmounted dentry, an ++ * unconnected dentry, or the file is on a pseudo filesystem. + */ namelen = dentry->d_name.len; - buflen -= namelen; - if (buflen < 0) -- goto Elong; ++ is_slash = (namelen == 1 && *dentry->d_name.name == '/'); ++ if (is_slash || (dentry->d_sb->s_flags & MS_NOUSER)) { ++ /* ++ * Make sure we won't return a pathname starting with '/'. ++ * ++ * Historically, we also glue together the root dentry and ++ * remaining name for pseudo filesystems like pipefs, which ++ * have the MS_NOUSER flag set. This results in pathnames ++ * like "pipe:[439336]". ++ */ ++ if (*buffer == '/') { ++ buffer++; ++ buflen++; ++ } ++ if (is_slash) { ++ if (*buffer == '\0') ++ *--buffer = '.'; ++ goto out; ++ } ++ } ++ if (buflen < namelen) + goto Elong; - retval -= namelen-1; /* hit the slash */ - memcpy(retval, dentry->d_name.name, namelen); - return retval; -+ if (namelen == 1 && *dentry->d_name.name == '/') { -+ if (buffer != end) -+ buffer++; -+ } else { -+ if (buflen < namelen) -+ goto Elong; -+ buffer -= namelen; -+ memcpy(buffer, dentry->d_name.name, namelen); -+ } ++ buffer -= namelen; ++ memcpy(buffer, dentry->d_name.name, namelen); + goto out; + Elong: @@ -138,7 +195,7 @@ Index: linux-2.6/fs/dcache.c { char *res; struct vfsmount *rootmnt; -@@ -1827,9 +1841,7 @@ char * d_path(struct dentry *dentry, str +@@ -1827,9 +1856,7 @@ char * d_path(struct dentry *dentry, str rootmnt = mntget(current->fs->rootmnt); root = dget(current->fs->root); read_unlock(¤t->fs->lock); @@ -149,7 +206,7 @@ Index: linux-2.6/fs/dcache.c dput(root); mntput(rootmnt); return res; -@@ -1855,10 +1867,10 @@ char * d_path(struct dentry *dentry, str +@@ -1855,10 +1882,10 @@ char * d_path(struct dentry *dentry, str */ asmlinkage long sys_getcwd(char __user *buf, unsigned long size) { @@ -162,11 +219,15 @@ Index: linux-2.6/fs/dcache.c if (!page) return -ENOMEM; -@@ -1870,29 +1882,18 @@ asmlinkage long sys_getcwd(char __user * +@@ -1870,29 +1897,21 @@ asmlinkage long sys_getcwd(char __user * root = dget(current->fs->root); read_unlock(¤t->fs->lock); -- error = -ENOENT; ++ cwd = __d_path(pwd, pwdmnt, root, rootmnt, page, PAGE_SIZE, 1); ++ error = PTR_ERR(cwd); ++ if (IS_ERR(cwd)) ++ goto out; + error = -ENOENT; - /* Has the current directory has been unlinked? */ - spin_lock(&dcache_lock); - if (pwd->d_parent == pwd || !d_unhashed(pwd)) { @@ -179,7 +240,9 @@ Index: linux-2.6/fs/dcache.c - error = PTR_ERR(cwd); - if (IS_ERR(cwd)) - goto out; -- ++ if (*cwd != '/') ++ goto out; + - error = -ERANGE; - len = PAGE_SIZE + page - cwd; - if (len <= size) { @@ -189,11 +252,6 @@ Index: linux-2.6/fs/dcache.c - } - } else - spin_unlock(&dcache_lock); -+ cwd = __d_path(pwd, pwdmnt, root, rootmnt, page, PAGE_SIZE, 1); -+ error = PTR_ERR(cwd); -+ if (IS_ERR(cwd)) -+ goto out; -+ + error = -ERANGE; + len = PAGE_SIZE + page - cwd; + if (len <= size) { diff --git a/kernel-patches/for-mainline/match-changes.diff b/kernel-patches/for-mainline/match-changes.diff new file mode 100644 index 000000000..7297a29eb --- /dev/null +++ b/kernel-patches/for-mainline/match-changes.diff @@ -0,0 +1,92 @@ +Index: b/security/apparmor/match.c +=================================================================== +--- a/security/apparmor/match.c ++++ b/security/apparmor/match.c +@@ -160,7 +160,7 @@ int verify_dfa(struct aa_dfa *dfa) + if (trans_count != dfa->tables[YYTD_ID_CHK - 1]->td_lolen) + goto out; + +- /* if equivalence classes then its table must be 256 */ ++ /* 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; +@@ -201,67 +201,46 @@ void aa_match_free(struct aa_dfa *dfa) + } + + /** +- * aadfa_label - return the permissions associated with @state +- * @dfa: dfa to get state permission from +- * @state: state in the dfa for which to get a label +- * +- * Assumes that state is a valid state of the dfa +- * +- * Returns the label associated with @state. 0 indicates the state +- * is no-accepting/provides no permissions. +- */ +-inline unsigned int aadfa_label(struct aa_dfa *dfa, int state) +-{ +- return ACCEPT_TABLE(dfa)[state]; +-} +- +-/** + * aa_dfa_match - match @path against @dfa starting in @state + * @dfa: the dfa to match @path against + * @state: the state to start matching in + * @path: the path to match against the dfa + * + * aa_dfa_match will match the full path length and return the state it +- * finished matching in. The final state returned can be used to +- * lookup the accepting label or as a starting point to continue matching +- * with a new string if the path has been broken into multiple components. ++ * finished matching in. The final state is used to look up the accepting ++ * label. + */ +-inline unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int state, +- const char *path) ++inline unsigned int aa_dfa_match(struct aa_dfa *dfa, const char *str) + { +- u8 *s = (u8 *) path; + u16 *def = DEFAULT_TABLE(dfa); + u32 *base = BASE_TABLE(dfa); + u16 *next = NEXT_TABLE(dfa); + u16 *check = CHECK_TABLE(dfa); +- unsigned int pos; ++ unsigned int state = 1, pos; + +- /* current state is , matching character *s */ ++ /* current state is , matching character *str */ + if (dfa->tables[YYTD_ID_EC - 1]) { + u8 *equiv = EQUIV_TABLE(dfa); +- for ( ; *s; ++s) { +- pos = base[state] + equiv[*s]; ++ while (*str) { ++ pos = base[state] + equiv[(u8)*str++]; + if (check[pos] == state) + state = next[pos]; + else + state = def[state]; + } + } else { +- for ( ; *s; ++s) { +- pos = base[state] + *s; ++ while (*str) { ++ pos = base[state] + (u8)*str++; + if (check[pos] == state) + state = next[pos]; + else + state = def[state]; + } + } +- return state; ++ return ACCEPT_TABLE(dfa)[state]; + } + + unsigned int aa_match(struct aa_dfa *dfa, const char *pathname) + { +- if (dfa) +- return aadfa_label(dfa, aa_dfa_match(dfa, 1, pathname)); +- +- return 0; ++ return dfa ? aa_dfa_match(dfa, pathname) : 0; + } diff --git a/kernel-patches/for-mainline/match-features.diff b/kernel-patches/for-mainline/match-features.diff new file mode 100644 index 000000000..9a31048cc --- /dev/null +++ b/kernel-patches/for-mainline/match-features.diff @@ -0,0 +1,52 @@ +Remove some remains of the matcher modules. + +Index: b/security/apparmor/apparmor.h +=================================================================== +--- a/security/apparmor/apparmor.h ++++ b/security/apparmor/apparmor.h +@@ -278,7 +278,6 @@ struct aa_dfa *aa_match_alloc(void); + void aa_match_free(struct aa_dfa *dfa); + int unpack_dfa(struct aa_dfa *dfa, void *blob, size_t size); + int verify_dfa(struct aa_dfa *dfa); +-const char *aa_match_features(void); + unsigned int aa_match(struct aa_dfa *dfa, const char *pathname); + + #endif /* __APPARMOR_H */ +Index: b/security/apparmor/apparmorfs.c +=================================================================== +--- a/security/apparmor/apparmorfs.c ++++ b/security/apparmor/apparmorfs.c +@@ -141,7 +141,7 @@ static int aa_prof_release(struct inode + static ssize_t aa_matching_read(struct file *file, char __user *buf, + size_t size, loff_t *ppos) + { +- const char *matching = aa_match_features(); ++ const char *matching = "pattern=aadfa"; + + return simple_read_from_buffer(buf, size, ppos, matching, + strlen(matching)); +Index: b/security/apparmor/match.c +=================================================================== +--- a/security/apparmor/match.c ++++ b/security/apparmor/match.c +@@ -16,8 +16,6 @@ + #include + #include "match.h" + +-static const char *features="pattern=aadfa"; +- + static struct table_header *unpack_table(void *blob, size_t bsize) + { + struct table_header *table = NULL; +@@ -202,11 +200,6 @@ void aa_match_free(struct aa_dfa *dfa) + kfree(dfa); + } + +-const char *aa_match_features(void) +-{ +- return features; +-} +- + /** + * aadfa_label - return the permissions associated with @state + * @dfa: dfa to get state permission from diff --git a/kernel-patches/for-mainline/no-unreachable-paths.diff b/kernel-patches/for-mainline/no-unreachable-paths.diff new file mode 100644 index 000000000..09ecda30a --- /dev/null +++ b/kernel-patches/for-mainline/no-unreachable-paths.diff @@ -0,0 +1,181 @@ +Hide unreachable mount points in /proc/mounts and /proc/$PID/mountstats + +What's mounted on unreachable mount points isn't interesting to +processes: they can't get there in the first place. This patch hides +unreachable mounts from processes. + +Processes living in the root namespace whill still see all mounts they +were seeing before except for the rootfs mount, which is never reachable +from an "ordinary" process. + +Only the initial initrd init process will actually have access to the +rootfs mount. For this process that mount *is* reachable, and so it will +show in. + +This patch also removes some code duplication between mounts_open() and +mountstats_open(). + +Signed-off-by: Andreas Gruenbacher + +Index: b/fs/namespace.c +=================================================================== +--- a/fs/namespace.c ++++ b/fs/namespace.c +@@ -348,8 +348,16 @@ static inline void mangle(struct seq_fil + seq_escape(m, s, " \t\n\\"); + } + ++/* Keep in sync with fs/proc/base.c! */ ++struct proc_mounts { ++ struct seq_file m; ++ void *page; ++ int event; ++}; ++ + static int show_vfsmnt(struct seq_file *m, void *v) + { ++ void *page = container_of(m, struct proc_mounts, m)->page; + struct vfsmount *mnt = v; + int err = 0; + static struct proc_fs_info { +@@ -372,9 +380,13 @@ static int show_vfsmnt(struct seq_file * + }; + struct proc_fs_info *fs_infop; + ++ char *path = d_path(mnt->mnt_root, mnt, page, PAGE_SIZE); ++ if (IS_ERR(path) || *path != '/') ++ return err; ++ + mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none"); + seq_putc(m, ' '); +- seq_path(m, mnt, mnt->mnt_root, " \t\n\\"); ++ mangle(m, path); + seq_putc(m, ' '); + mangle(m, mnt->mnt_sb->s_type->name); + seq_puts(m, mnt->mnt_sb->s_flags & MS_RDONLY ? " ro" : " rw"); +@@ -401,9 +413,14 @@ struct seq_operations mounts_op = { + + static int show_vfsstat(struct seq_file *m, void *v) + { ++ void *page = container_of(m, struct proc_mounts, m)->page; + struct vfsmount *mnt = v; + int err = 0; + ++ char *path = d_path(mnt->mnt_root, mnt, page, PAGE_SIZE); ++ if (IS_ERR(path) || *path != '/') ++ return err; /* error or path unreachable from chroot */ ++ + /* device */ + if (mnt->mnt_devname) { + seq_puts(m, "device "); +@@ -413,7 +430,7 @@ static int show_vfsstat(struct seq_file + + /* mount point */ + seq_puts(m, " mounted on "); +- seq_path(m, mnt, mnt->mnt_root, " \t\n\\"); ++ mangle(m, path); + seq_putc(m, ' '); + + /* file system type */ +Index: b/fs/proc/base.c +=================================================================== +--- a/fs/proc/base.c ++++ b/fs/proc/base.c +@@ -356,13 +356,16 @@ static const struct inode_operations pro + .setattr = proc_setattr, + }; + ++/* Keep in sync with fs/namespace.c! */ + extern struct seq_operations mounts_op; + struct proc_mounts { + struct seq_file m; ++ void *page; + int event; + }; + +-static int mounts_open(struct inode *inode, struct file *file) ++static int __mounts_open(struct inode *inode, struct file *file, ++ struct seq_operations *seq_ops) + { + struct task_struct *task = get_proc_task(inode); + struct mnt_namespace *ns = NULL; +@@ -385,12 +388,16 @@ static int mounts_open(struct inode *ino + p = kmalloc(sizeof(struct proc_mounts), GFP_KERNEL); + if (p) { + file->private_data = &p->m; +- ret = seq_open(file, &mounts_op); ++ p->page = (void *)__get_free_page(GFP_KERNEL); ++ if (p->page) ++ ret = seq_open(file, seq_ops); + if (!ret) { + p->m.private = ns; + p->event = ns->event; + return 0; + } ++ if (p->page) ++ free_page((unsigned long)p->page); + kfree(p); + } + put_mnt_ns(ns); +@@ -398,17 +405,25 @@ static int mounts_open(struct inode *ino + return ret; + } + ++static int mounts_open(struct inode *inode, struct file *file) ++{ ++ return __mounts_open(inode, file, &mounts_op); ++} ++ + static int mounts_release(struct inode *inode, struct file *file) + { +- struct seq_file *m = file->private_data; +- struct mnt_namespace *ns = m->private; ++ struct proc_mounts *p = ++ container_of(file->private_data, struct proc_mounts, m); ++ struct mnt_namespace *ns = p->m.private; ++ free_page((unsigned long)p->page); + put_mnt_ns(ns); + return seq_release(inode, file); + } + + static unsigned mounts_poll(struct file *file, poll_table *wait) + { +- struct proc_mounts *p = file->private_data; ++ struct proc_mounts *p = ++ container_of(file->private_data, struct proc_mounts, m); + struct mnt_namespace *ns = p->m.private; + unsigned res = 0; + +@@ -435,31 +450,7 @@ static const struct file_operations proc + extern struct seq_operations mountstats_op; + static int mountstats_open(struct inode *inode, struct file *file) + { +- int ret = seq_open(file, &mountstats_op); +- +- if (!ret) { +- struct seq_file *m = file->private_data; +- struct mnt_namespace *mnt_ns = NULL; +- struct task_struct *task = get_proc_task(inode); +- +- if (task) { +- task_lock(task); +- if (task->nsproxy) +- mnt_ns = task->nsproxy->mnt_ns; +- if (mnt_ns) +- get_mnt_ns(mnt_ns); +- task_unlock(task); +- put_task_struct(task); +- } +- +- if (mnt_ns) +- m->private = mnt_ns; +- else { +- seq_release(inode, file); +- ret = -EINVAL; +- } +- } +- return ret; ++ return __mounts_open(inode, file, &mountstats_op); + } + + static const struct file_operations proc_mountstats_operations = { diff --git a/kernel-patches/for-mainline/reintroduce-revalidation.diff b/kernel-patches/for-mainline/reintroduce-revalidation.diff new file mode 100644 index 000000000..b68c5f293 --- /dev/null +++ b/kernel-patches/for-mainline/reintroduce-revalidation.diff @@ -0,0 +1,72 @@ +Reintroduce revalidation. + +Index: b/security/apparmor/lsm.c +=================================================================== +--- a/security/apparmor/lsm.c ++++ b/security/apparmor/lsm.c +@@ -445,6 +445,55 @@ static int apparmor_inode_removexattr(st + MAY_WRITE); + } + ++static int apparmor_file_permission(struct file *file, int mask) ++{ ++ struct aa_profile *profile; ++ struct aa_profile *file_profile = (struct aa_profile*)file->f_security; ++ int error = 0; ++ ++ if (!file_profile) ++ goto out; ++ ++ /* ++ * If this file was opened under a different profile, we ++ * revalidate the access against the current profile. ++ */ ++ profile = aa_get_profile(current); ++ if (profile && file_profile != profile) { ++ struct dentry *dentry = file->f_dentry; ++ struct vfsmount *mnt = file->f_vfsmnt; ++ ++ /* ++ * FIXME: We should remember which profiles we revalidated ++ * against. ++ */ ++ mask &= (MAY_READ | MAY_WRITE | MAY_EXEC); ++ error = aa_permission(dentry->d_inode, dentry, mnt, mask, 1); ++ } ++ aa_put_profile(profile); ++ ++out: ++ return error; ++} ++ ++static int apparmor_file_alloc_security(struct file *file) ++{ ++ struct aa_profile *profile; ++ ++ profile = aa_get_profile(current); ++ if (profile) ++ file->f_security = profile; ++ ++ return 0; ++} ++ ++static void apparmor_file_free_security(struct file *file) ++{ ++ struct aa_profile *file_profile = (struct aa_profile*)file->f_security; ++ ++ aa_put_profile(file_profile); ++} ++ + static inline int aa_mmap(struct file *file, unsigned long prot, + unsigned long flags) + { +@@ -668,6 +717,9 @@ struct security_operations apparmor_ops + .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, + diff --git a/kernel-patches/for-mainline/remove-debug-flag.diff b/kernel-patches/for-mainline/remove-debug-flag.diff new file mode 100644 index 000000000..b32744ead --- /dev/null +++ b/kernel-patches/for-mainline/remove-debug-flag.diff @@ -0,0 +1,25 @@ +Index: b/security/apparmor/module_interface.c +=================================================================== +--- a/security/apparmor/module_interface.c ++++ b/security/apparmor/module_interface.c +@@ -383,7 +383,7 @@ static struct aa_profile *aa_activate_pr + error_string = "Invalid flags"; + /* per profile debug flags (debug, complain, audit) */ + AA_READ_X(e, AA_STRUCT, NULL, "flags"); +- AA_READ_X(e, AA_U32, &(profile->flags.debug), NULL); ++ AA_READ_X(e, AA_U32, NULL, NULL); + AA_READ_X(e, AA_U32, &(profile->flags.complain), NULL); + AA_READ_X(e, AA_U32, &(profile->flags.audit), NULL); + AA_READ_X(e, AA_STRUCTEND, NULL, NULL); +Index: b/security/apparmor/apparmor.h +=================================================================== +--- a/security/apparmor/apparmor.h ++++ b/security/apparmor/apparmor.h +@@ -108,7 +108,6 @@ struct aa_profile { + struct list_head list; + struct list_head sub; + struct { +- int debug; + int complain; + int audit; + } flags; diff --git a/kernel-patches/for-mainline/rename-profile-lock.diff b/kernel-patches/for-mainline/rename-profile-lock.diff new file mode 100644 index 000000000..a7c5509f8 --- /dev/null +++ b/kernel-patches/for-mainline/rename-profile-lock.diff @@ -0,0 +1,112 @@ +Index: b/security/apparmor/list.c +=================================================================== +--- a/security/apparmor/list.c ++++ b/security/apparmor/list.c +@@ -15,7 +15,7 @@ + + /* list of all profiles and lock */ + static LIST_HEAD(profile_list); +-static rwlock_t profile_lock = RW_LOCK_UNLOCKED; ++static rwlock_t profile_list_lock = RW_LOCK_UNLOCKED; + + /* list of all task_contexts and lock */ + static LIST_HEAD(task_context_list); +@@ -32,9 +32,9 @@ struct aa_profile *aa_profilelist_find(c + { + struct aa_profile *p = NULL; + if (name) { +- read_lock(&profile_lock); ++ read_lock(&profile_list_lock); + p = __aa_find_profile(name, &profile_list); +- read_unlock(&profile_lock); ++ read_unlock(&profile_list_lock); + } + return p; + } +@@ -57,7 +57,7 @@ int aa_profilelist_add(struct aa_profile + if (!profile) + goto out; + +- write_lock(&profile_lock); ++ write_lock(&profile_list_lock); + old_profile = __aa_find_profile(profile->name, &profile_list); + if (old_profile) { + aa_put_profile(old_profile); +@@ -67,7 +67,7 @@ int aa_profilelist_add(struct aa_profile + list_add(&profile->list, &profile_list); + ret = 1; + out: +- write_unlock(&profile_lock); ++ write_unlock(&profile_list_lock); + return ret; + } + +@@ -87,7 +87,7 @@ struct aa_profile *aa_profilelist_remove + if (!name) + goto out; + +- write_lock(&profile_lock); ++ write_lock(&profile_list_lock); + list_for_each_entry_safe(p, tmp, &profile_list, list) { + if (!strcmp(p->name, name)) { + list_del_init(&p->list); +@@ -97,7 +97,7 @@ struct aa_profile *aa_profilelist_remove + break; + } + } +- write_unlock(&profile_lock); ++ write_unlock(&profile_list_lock); + + out: + return profile; +@@ -118,7 +118,7 @@ struct aa_profile *aa_profilelist_replac + { + struct aa_profile *oldprofile; + +- write_lock(&profile_lock); ++ write_lock(&profile_list_lock); + oldprofile = __aa_find_profile(profile->name, &profile_list); + if (oldprofile) { + list_del_init(&oldprofile->list); +@@ -130,7 +130,7 @@ struct aa_profile *aa_profilelist_replac + } + + list_add(&profile->list, &profile_list); +- write_unlock(&profile_lock); ++ write_unlock(&profile_list_lock); + + return oldprofile; + } +@@ -142,12 +142,12 @@ void aa_profilelist_release(void) + { + struct aa_profile *p, *tmp; + +- write_lock(&profile_lock); ++ write_lock(&profile_list_lock); + list_for_each_entry_safe(p, tmp, &profile_list, list) { + list_del_init(&p->list); + aa_put_profile(p); + } +- write_unlock(&profile_lock); ++ write_unlock(&profile_list_lock); + } + + /** +@@ -233,7 +233,7 @@ static void *p_start(struct seq_file *f, + struct aa_profile *node; + loff_t l = *pos; + +- read_lock(&profile_lock); ++ read_lock(&profile_list_lock); + list_for_each_entry(node, &profile_list, list) + if (!l--) + return node; +@@ -250,7 +250,7 @@ static void *p_next(struct seq_file *f, + + static void p_stop(struct seq_file *f, void *v) + { +- read_unlock(&profile_lock); ++ read_unlock(&profile_list_lock); + } + + static int seq_show_profile(struct seq_file *f, void *v) diff --git a/kernel-patches/for-mainline/series b/kernel-patches/for-mainline/series index 6579332a4..7b7116c93 100644 --- a/kernel-patches/for-mainline/series +++ b/kernel-patches/for-mainline/series @@ -27,6 +27,7 @@ security-listxattr.diff vfs-removexattr.diff security-removexattr.diff d_path-lazy-unmounts.diff +no-unreachable-paths.diff # security_chroot.diff apparmor-audit.diff apparmor-intree.diff @@ -58,5 +59,9 @@ d_namespace_path.diff apparmor-d_namespace.diff alloc-pathnames.diff # fix-change_hat.diff -# apparmor-percpu_path_cache.diff -# apparmor-path_resize.diff +typo.diff +reintroduce-revalidation.diff +remove-debug-flag.diff +rename-profile-lock.diff +match-features.diff +match-changes.diff diff --git a/kernel-patches/for-mainline/typo.diff b/kernel-patches/for-mainline/typo.diff new file mode 100644 index 000000000..5e0316a57 --- /dev/null +++ b/kernel-patches/for-mainline/typo.diff @@ -0,0 +1,13 @@ +Index: b/security/apparmor/main.c +=================================================================== +--- a/security/apparmor/main.c ++++ b/security/apparmor/main.c +@@ -1087,7 +1087,7 @@ void aa_release(struct task_struct *task + + /** + * do_change_hat - actually switch hats +- * @hat_name: name of hat to swtich to ++ * @hat_name: name of hat to switch to + * @cxt: current aa_task_context + * + * Switch to a new hat. Return %0 on success, error otherwise.