dfa module, d_path fixes, chroot hook, path cache

This commit is contained in:
John Johansen 2007-01-31 23:22:10 +00:00
parent 2395fa7d35
commit d84a0cb8ae
10 changed files with 1624 additions and 0 deletions

View file

@ -0,0 +1,39 @@
Index: linux-2.6.19.1/security/apparmor/main.c
===================================================================
--- linux-2.6.19.1.orig/security/apparmor/main.c
+++ linux-2.6.19.1/security/apparmor/main.c
@@ -804,6 +804,7 @@ char *aa_get_name(struct dentry *dentry,
{
char *buffer, *name;
int order = 0;
+ unsigned int pconds;
*addr = NULL;
buffer = (char *)aa_get_path(GFP_KERNEL);
@@ -813,7 +814,8 @@ char *aa_get_name(struct dentry *dentry,
goto out;
}
- name = d_path(dentry, mnt, buffer, PAGE_SIZE << order);
+ name = d_path_flags(dentry, mnt, buffer, PAGE_SIZE << order,
+ DPATH_NSROOT, &pconds);
/* 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.
@@ -826,16 +828,6 @@ char *aa_get_name(struct dentry *dentry,
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;
- size_t size;
- size = strlen(name);
- if (!IS_ROOT(dentry) && d_unhashed(dentry) &&
- size > deleted_size &&
- strcmp(name + size - deleted_size, deleted_str) == 0)
- name[size - deleted_size] = '\0';
- AA_DEBUG("%s: full_path=%s\n", __FUNCTION__, name);
}
out:

View file

@ -0,0 +1,505 @@
Index: linux-2.6.19.1/security/apparmor/match/Kbuild
===================================================================
--- linux-2.6.19.1.orig/security/apparmor/match/Kbuild
+++ linux-2.6.19.1/security/apparmor/match/Kbuild
@@ -1,6 +1,6 @@
# Makefile for AppArmor aamatch submodule
#
-obj-$(CONFIG_SECURITY_APPARMOR) += aamatch_pcre.o
+obj-$(CONFIG_SECURITY_APPARMOR) += aamatch_dfa.o
-aamatch_pcre-y := match_pcre.o pcre_exec.o
+aamatch_dfa-y := match_dfa.o
Index: linux-2.6.19.1/security/apparmor/match/match_dfa.c
===================================================================
--- /dev/null
+++ linux-2.6.19.1/security/apparmor/match/match_dfa.c
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2002-2005 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.
+ *
+ * http://forge.novell.com/modules/xfmod/project/?apparmor
+ *
+ * AppArmor aamatch submodule (w/ pattern expansion).
+ *
+ */
+
+#include <asm/unaligned.h>
+#include <linux/module.h>
+#include "match.h"
+
+static const char *features="literal tailglob pattern=aadfa";
+
+#define YYTH_MAGIC 0x1B5E783D
+
+struct table_set_header {
+ u32 th_magic; /* TH_MAGIC */
+ u32 th_hsize;
+ u32 th_ssize;
+ u16 th_flags;
+ char th_version[];
+};
+
+#define YYTD_ID_ACCEPT 1 /* 1 */
+#define YYTD_ID_BASE 2 /* 2 */
+#define YYTD_ID_CHK 3 /* 3 */
+#define YYTD_ID_DEF 4 /* 4 */
+#define YYTD_ID_EC 5 /* 5 */
+#define YYTD_ID_NXT 6 /* 8 */
+#define YYTD_ID_META 7 /* 6 */
+
+#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))
+
+struct aa_dfa {
+ struct table_header *tables[YYTD_ID_NXT];
+
+ struct table_set_header th;
+};
+
+#define ntohb(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 pad64(size_t i)
+{
+ return (i + (size_t)7) & ~(size_t)7;
+}
+
+static inline size_t table_size(size_t len, size_t el_size)
+{
+ return pad64(sizeof(struct table_header) + len * el_size);
+}
+
+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 = ntohs(get_unaligned((u16 *) (blob)));
+ th.td_flags = ntohs(get_unaligned((u16 *) (blob + 2)));
+ th.td_lolen = ntohl(get_unaligned((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, ntohb);
+ else if (th.td_flags == YYTD_DATA16)
+ UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
+ u16, ntohs);
+ else
+ UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
+ u32, ntohl);
+ }
+
+out:
+ return table;
+}
+
+static int unpack_dfa(struct aa_dfa *dfa, void *blob, size_t size)
+{
+ int i;
+ int error = -ENOMEM;
+
+ /* get dfa table set header */
+ if (size < sizeof(struct table_set_header))
+ goto fail;
+
+ dfa->th.th_magic = ntohl(get_unaligned((u32 *) (blob + 0)));
+ dfa->th.th_hsize = ntohl(get_unaligned((u32 *) (blob + 4)));
+ dfa->th.th_ssize = ntohl(get_unaligned((u32 *) (blob + 8)));
+ dfa->th.th_flags = ntohs(get_unaligned((u16 *) (blob + 12)));
+
+ if (dfa->th.th_magic != YYTH_MAGIC)
+ goto fail;
+
+ if (size < dfa->th.th_hsize)
+ goto fail;
+
+ blob += dfa->th.th_hsize;
+ size -= dfa->th.th_hsize;
+
+ 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_BASE:
+ dfa->tables[table->td_id - 1] = table;
+ if (table->td_flags != YYTD_DATA32)
+ goto fail_proto;
+ 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_proto;
+ break;
+ case YYTD_ID_EC:
+ dfa->tables[table->td_id - 1] = table;
+ if (table->td_flags != YYTD_DATA8)
+ goto fail_proto;
+ break;
+ default:
+ kfree(table);
+ goto fail_proto;
+ }
+
+ blob += table_size(table->td_lolen, table->td_flags);
+ size -= table_size(table->td_lolen, table->td_flags);
+ }
+
+ error = 0;
+
+ return error;
+
+fail_proto:
+ error = -EPROTO;
+fail:
+ for (i = 0; i < YYTD_ID_NXT; 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
+ */
+static 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_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))
+ 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 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;
+ }
+
+ error = 0;
+out:
+ return error;
+}
+
+/**
+ * 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_VALID_PERM_MASK;
+}
+
+/**
+ * aadfa_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
+ *
+ * aadfa_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.
+ */
+static unsigned int aadfa_match(struct aa_dfa *dfa, unsigned int state,
+ const char *path)
+{
+ 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;
+
+ /* current state is <state>, matching character *s */
+ if (dfa->tables[YYTD_ID_EC - 1]) {
+ u8 *equiv = EQUIV_TABLE(dfa);
+ for ( ; *s; ++s) {
+ pos = base[state] + equiv[*s];
+ if (check[pos] == state)
+ state = next[pos];
+ else
+ state = def[state];
+ }
+ } else {
+ for ( ; *s; ++s) {
+ pos = base[state] + *s;
+ if (check[pos] == state)
+ state = next[pos];
+ else
+ state = def[state];
+ }
+ }
+ return state;
+}
+
+void* aamatch_alloc(enum entry_match_type entry_type)
+{
+ void *ptr=NULL;
+
+ if (entry_type == aa_entry_pattern) {
+ ptr = kmalloc(sizeof(struct aa_dfa), GFP_KERNEL);
+ if (ptr)
+ memset(ptr, 0, sizeof(struct aa_dfa));
+ else
+ ptr=ERR_PTR(-ENOMEM);
+ } else if (entry_type != aa_entry_literal &&
+ entry_type != aa_entry_tailglob) {
+ ptr = ERR_PTR(-EINVAL);
+ }
+
+ return ptr;
+}
+
+void aamatch_free(void *ptr)
+{
+ if (ptr) {
+ int i;
+ struct aa_dfa *dfa = (struct aa_dfa *) ptr;
+ for (i = 0; i < YYTD_ID_NXT; i++) {
+ if (dfa->tables[i])
+ kfree(dfa->tables[i]);
+ }
+ }
+ kfree(ptr);
+}
+
+const char *aamatch_features(void)
+{
+ return features;
+}
+
+int aamatch_serialize(void *entry_extradata, struct aa_ext *e,
+ aamatch_serializecb cb)
+{
+ int error = 0;
+ char *blob = NULL;
+ size_t size;
+
+ struct aa_dfa *dfa = (struct aa_dfa *) entry_extradata;
+ if (dfa == NULL)
+ goto done;
+
+ error = -EPROTO;
+ size = cb(e, AA_BLOB_LOC, &blob, "aadfa");
+
+ if (size)
+ error = unpack_dfa(dfa, blob, size);
+
+ if (!error)
+ error = verify_dfa(dfa);
+done:
+ return error;
+}
+
+int aamatch_match(struct aa_entry *entry, const char *pathname)
+{
+ int ret;
+
+ if (entry->type == aa_entry_pattern) {
+ unsigned int state;
+ struct aa_dfa *dfa = (struct aa_dfa *) entry->extradata;
+
+ state = aadfa_match(dfa, 1, pathname);
+
+ /* label returned is the permissions of the matched state */
+ ret = aadfa_label(dfa, state);
+ } else {
+ ret = aamatch_match_common(entry, pathname);
+ }
+
+ return ret;
+}
+
+EXPORT_SYMBOL_GPL(aamatch_alloc);
+EXPORT_SYMBOL_GPL(aamatch_free);
+EXPORT_SYMBOL_GPL(aamatch_features);
+EXPORT_SYMBOL_GPL(aamatch_serialize);
+EXPORT_SYMBOL_GPL(aamatch_match);
+
+MODULE_DESCRIPTION("AppArmor aa_match module [dfa]");
+MODULE_AUTHOR("John Johansen <jjohansen@suse.de>");
+MODULE_LICENSE("GPL");
Index: linux-2.6.19.1/security/apparmor/module_interface.c
===================================================================
--- linux-2.6.19.1.orig/security/apparmor/module_interface.c
+++ linux-2.6.19.1/security/apparmor/module_interface.c
@@ -206,6 +206,7 @@ static void aaconvert(enum aa_code code,
*(u16 *)dest = le16_to_cpu(get_unaligned((u16 *)src));
break;
case AA_U32:
+ case AA_BLOB_LOC:
case AA_STATIC_BLOB:
*(u32 *)dest = le32_to_cpu(get_unaligned((u32 *)src));
break;
@@ -239,7 +240,8 @@ static u32 aa_is_X(struct aa_ext *e, enu
int ret = 0;
if (!aa_inbounds(e, AA_CODE_BYTE + aacode_datasize[code]))
goto fail;
- if (code != *(u8 *)e->pos)
+ if (code != *(u8 *)e->pos &&
+ !(code == AA_BLOB_LOC && AA_STATIC_BLOB == *(u8 *)e->pos))
goto out;
e->pos += AA_CODE_BYTE;
if (code == AA_NAME) {
@@ -271,6 +273,20 @@ static u32 aa_is_X(struct aa_ext *e, enu
}
e->pos += size;
ret = size;
+
+ } else if (code == AA_BLOB_LOC) {
+ u32 size;
+ /* blobs are followed by X bytes */
+ size = le32_to_cpu(get_unaligned((u32 *)e->pos));
+ e->pos += aacode_datasize[AA_STATIC_BLOB];
+ if (!aa_inbounds(e, (size_t) size))
+ goto fail;
+ if (data) {
+ * (char **)data = e->pos;
+ }
+ e->pos += size;
+ ret = size;
+
} else if (code == AA_STATIC_BLOB) {
u32 size;
/* blobs are followed by X bytes, that can be 2^32 */
@@ -434,7 +450,6 @@ static inline struct aa_entry *aa_activa
return entry;
fail:
- aamatch_free(entry->extradata);
free_aa_entry(entry);
return NULL;
}
Index: linux-2.6.19.1/security/apparmor/module_interface.h
===================================================================
--- linux-2.6.19.1.orig/security/apparmor/module_interface.h
+++ linux-2.6.19.1/security/apparmor/module_interface.h
@@ -20,6 +20,7 @@ enum aa_code {
AA_LIST,
AA_LISTEND,
AA_OFFSET,
+ AA_BLOB_LOC,
AA_BAD
};
Index: linux-2.6.19.1/security/apparmor/shared.h
===================================================================
--- linux-2.6.19.1.orig/security/apparmor/shared.h
+++ linux-2.6.19.1/security/apparmor/shared.h
@@ -28,6 +28,9 @@
#define POS_AA_EXEC_UNSAFE (POS_AA_EXEC_MMAP + 1)
#define POS_AA_FILE_MAX POS_AA_EXEC_UNSAFE
+/* Invalid perm permission */
+#define POS_AA_INVALID_POS 31
+
/* Modeled after MAY_READ, MAY_WRITE, MAY_EXEC def'ns */
#define AA_MAY_EXEC (0x01 << POS_AA_MAY_EXEC)
#define AA_MAY_WRITE (0x01 << POS_AA_MAY_WRITE)
@@ -38,9 +41,11 @@
#define AA_EXEC_PROFILE (0x01 << POS_AA_EXEC_PROFILE)
#define AA_EXEC_MMAP (0x01 << POS_AA_EXEC_MMAP)
#define AA_EXEC_UNSAFE (0x01 << POS_AA_EXEC_UNSAFE)
+#define AA_INVALID_PERM (0x01 << POS_AA_INVALID_POS)
#define AA_EXEC_MODIFIERS (AA_EXEC_INHERIT | \
AA_EXEC_UNCONSTRAINED | \
AA_EXEC_PROFILE)
+#define AA_VALID_PERM_MASK ((1 << (POS_AA_FILE_MAX + 1)) - 1)
#endif /* _SHARED_H */

View file

@ -0,0 +1,289 @@
Index: linux-2.6.19.1/security/apparmor/main.c
===================================================================
--- linux-2.6.19.1.orig/security/apparmor/main.c
+++ linux-2.6.19.1/security/apparmor/main.c
@@ -61,7 +61,7 @@ static inline int aa_taskattr_access(con
static inline int aa_file_mode(struct aaprofile *profile, const char *name)
{
struct aa_entry *entry;
- int mode = 0;
+ int perms = 0;
AA_DEBUG("%s: %s\n", __FUNCTION__, name);
if (!name) {
@@ -74,12 +74,10 @@ static inline int aa_file_mode(struct aa
goto out;
}
list_for_each_entry(entry, &profile->file_entry, list) {
- if (aamatch_match(name, entry->filename,
- entry->type, entry->extradata))
- mode |= entry->mode;
+ perms |= aamatch_match(entry, name);
}
out:
- return mode;
+ return perms;
}
/**
@@ -109,8 +107,7 @@ static inline int aa_get_execmode(struct
int *xmod, int *unsafe)
{
struct aa_entry *entry;
- struct aa_entry *match = NULL;
-
+ int entry_perms = 0, match_perms = 0;
int pattern_match_invalid = 0, rc = 0;
/* search list of profiles with 'x' permission
@@ -127,47 +124,44 @@ static inline int aa_get_execmode(struct
listp[POS_AA_MAY_EXEC]) {
if (!pattern_match_invalid &&
entry->type == aa_entry_pattern &&
- aamatch_match(name, entry->filename,
- entry->type, entry->extradata)) {
- if (match &&
- AA_EXEC_UNSAFE_MASK(entry->mode) !=
- AA_EXEC_UNSAFE_MASK(match->mode))
+ (entry_perms = aamatch_match(entry, name))) {
+ if (match_perms &&
+ AA_EXEC_UNSAFE_MASK(entry_perms) !=
+ AA_EXEC_UNSAFE_MASK(match_perms))
pattern_match_invalid = 1;
else
/* keep searching for an exact match */
- match = entry;
+ match_perms = entry_perms;
} else if ((entry->type == aa_entry_literal ||
(!pattern_match_invalid &&
entry->type == aa_entry_tailglob)) &&
- aamatch_match(name, entry->filename,
- entry->type,
- entry->extradata)) {
+ (entry_perms = aamatch_match(entry, name))) {
if (entry->type == aa_entry_literal) {
/* got an exact match -- there can be only
* one, asserted at profile load time
*/
- match = entry;
+ match_perms = entry_perms;
pattern_match_invalid = 0;
break;
} else {
- if (match &&
- AA_EXEC_UNSAFE_MASK(entry->mode) !=
- AA_EXEC_UNSAFE_MASK(match->mode))
+ if (match_perms &&
+ AA_EXEC_UNSAFE_MASK(entry_perms) !=
+ AA_EXEC_UNSAFE_MASK(match_perms))
pattern_match_invalid = 1;
else
/* got a tailglob match, keep searching
* for an exact match
*/
- match = entry;
+ match_perms = entry_perms;
}
}
}
- rc = match && !pattern_match_invalid;
+ rc = match_perms && !pattern_match_invalid;
if (rc) {
- int mode = AA_EXEC_MASK(match->mode);
+ int mode = AA_EXEC_MASK(match_perms);
/* check for qualifiers, if present
* we just return the qualifier
@@ -176,8 +170,8 @@ static inline int aa_get_execmode(struct
mode = mode & ~AA_MAY_EXEC;
*xmod = mode;
- *unsafe = (match->mode & AA_EXEC_UNSAFE);
- } else if (!match) {
+ *unsafe = (match_perms & AA_EXEC_UNSAFE);
+ } else if (!match_perms) {
AA_DEBUG("%s: Unable to find execute entry in profile "
"for image '%s'\n",
__FUNCTION__,
@@ -251,7 +245,8 @@ static inline void aa_permerror2result(i
static unsigned int aa_file_perm(struct aaprofile *active, const char *name,
int mask)
{
- int i, error = 0, mode;
+ int i, error = 0;
+ int perms;
#define PROCPFX "/proc/"
#define PROCLEN sizeof(PROCPFX) - 1
@@ -270,7 +265,7 @@ static unsigned int aa_file_perm(struct
aa_taskattr_access(name + PROCLEN))
goto done;
- mode = 0;
+ perms = 0;
/* iterate over partition, one permission bit at a time */
for (i = 0; i <= POS_AA_FILE_MAX; i++) {
@@ -278,32 +273,27 @@ static unsigned int aa_file_perm(struct
/* do we have to accumulate this bit?
* or have we already accumulated it (shortcut below)? */
- if (!(mask & (1 << i)) || mode & (1 << i))
+ if (!(mask & (1 << i)) || perms & (1 << i))
continue;
list_for_each_entry(entry, &active->file_entryp[i],
listp[i]) {
- if (aamatch_match(name, entry->filename,
- entry->type, entry->extradata)) {
- /* Shortcut, accumulate all bits present */
- mode |= entry->mode;
-
- /* Mask bits are overloaded
- * MAY_{EXEC,WRITE,READ,APPEND} are used by
- * kernel, other values are used locally only.
- */
- if ((mode & mask) == mask) {
- AA_DEBUG("MATCH! %s=0x%x [total mode=0x%x]\n",
- name, mask, mode);
+ perms |= aamatch_match(entry, name);
- goto done;
- }
+ /* Mask bits are overloaded
+ * MAY_{EXEC,WRITE,READ,APPEND} are used by
+ * kernel, other values are used locally only.
+ */
+ if ((perms & mask) == mask) {
+ AA_DEBUG("MATCH! %s=0x%x [total mode=0x%x]\n",
+ name, mask, perms);
+ goto done;
}
}
}
/* return permissions not satisfied */
- error = mask & ~mode;
+ error = mask & ~perms;
done:
return error;
Index: linux-2.6.19.1/security/apparmor/match/match.h
===================================================================
--- linux-2.6.19.1.orig/security/apparmor/match/match.h
+++ linux-2.6.19.1/security/apparmor/match/match.h
@@ -69,16 +69,11 @@ extern int aamatch_serialize(void *entry
/**
* aamatch_match: determine if pathname matches entry
+ * @entry: rule entry to match against
* @pathname: pathname to verify
- * @entry_name: entry name
- * @type: type of entry
- * @entry_extradata: data previously allocated by aamatch_alloc
- * Return value: 1 match, 0 othersise
+ * Return value: permission match, 0 othersise
*/
-extern unsigned int aamatch_match(const char *pathname, const char *entry_name,
- enum entry_match_type type,
- void *entry_extradata);
-
+extern int aamatch_match(struct aa_entry *entry, const char *pathname);
/**
* sd_getmatch_type - return string representation of entry_match_type
@@ -107,26 +102,25 @@ static inline const char *sd_getmatch_ty
* @entry_name: name from aa_entry
* @type: type of entry
*/
-static inline int aamatch_match_common(const char *path,
- const char *entry_name,
- enum entry_match_type type)
+static inline int aamatch_match_common(struct aa_entry *entry,
+ const char *path)
{
int retval;
/* literal, no pattern matching characters */
- if (type == aa_entry_literal) {
- retval = (strcmp(entry_name, path) == 0);
+ if (entry->type == aa_entry_literal) {
+ retval = (strcmp(entry->filename, path) == 0);
/* trailing ** glob pattern */
- } else if (type == aa_entry_tailglob) {
- retval = (strncmp(entry_name, path,
- strlen(entry_name) - 2) == 0);
+ } else if (entry->type == aa_entry_tailglob) {
+ retval = (strncmp(entry->filename, path,
+ strlen(entry->filename) - 2) == 0);
} else {
AA_WARN("%s: Invalid entry_match_type %d\n",
- __FUNCTION__, type);
+ __FUNCTION__, entry->type);
retval = 0;
}
- return retval;
+ return retval ? entry->mode : 0;
}
#endif /* __MATCH_H */
Index: linux-2.6.19.1/security/apparmor/match/match_default.c
===================================================================
--- linux-2.6.19.1.orig/security/apparmor/match/match_default.c
+++ linux-2.6.19.1/security/apparmor/match/match_default.c
@@ -36,12 +36,11 @@ int aamatch_serialize(void *entry_extrad
return 0;
}
-unsigned int aamatch_match(const char *pathname, const char *entry_name,
- enum entry_match_type type, void *entry_extradata)
+int aamatch_match(struct aa_entry *entry, const char *pathname)
{
int ret;
- ret = aamatch_match_common(pathname, entry_name, type);
+ ret = aamatch_match_common(entry, pathname);
return ret;
}
Index: linux-2.6.19.1/security/apparmor/match/match_pcre.c
===================================================================
--- linux-2.6.19.1.orig/security/apparmor/match/match_pcre.c
+++ linux-2.6.19.1/security/apparmor/match/match_pcre.c
@@ -132,27 +132,26 @@ done:
return error;
}
-unsigned int aamatch_match(const char *pathname, const char *entry_name,
- enum entry_match_type entry_type, void *entry_extradata)
+int aamatch_match(struct aa_entry *entry, const char *pathname)
{
int ret;
- if (entry_type == aa_entry_pattern) {
+ if (entry->type == aa_entry_pattern) {
int pcreret;
struct aamatch_entry *ed =
- (struct aamatch_entry *) entry_extradata;
+ (struct aamatch_entry *) entry->extradata;
pcreret = pcre_exec(ed->compiled, NULL,
pathname, strlen(pathname),
0, 0, NULL, 0);
- ret = (pcreret >= 0);
+ ret = (pcreret >= 0) ? entry->mode : 0;
// XXX - this needs access to subdomain_debug, hmmm
//AA_DEBUG("%s(%d): %s %s %d\n", __FUNCTION__,
// ret, pathname, ed->pattern, pcreret);
} else {
- ret = aamatch_match_common(pathname, entry_name, entry_type);
+ ret = aamatch_match_common(entry, pathname);
}
return ret;

View file

@ -0,0 +1,169 @@
Index: linux-2.6.19.1/security/apparmor/apparmor.h
===================================================================
--- linux-2.6.19.1.orig/security/apparmor/apparmor.h
+++ linux-2.6.19.1/security/apparmor/apparmor.h
@@ -255,7 +255,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.19.1/security/apparmor/inline.h
===================================================================
--- linux-2.6.19.1.orig/security/apparmor/inline.h
+++ linux-2.6.19.1/security/apparmor/inline.h
@@ -220,9 +220,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.19.1/security/apparmor/main.c
===================================================================
--- linux-2.6.19.1.orig/security/apparmor/main.c
+++ linux-2.6.19.1/security/apparmor/main.c
@@ -454,8 +454,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);
@@ -468,7 +469,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;
}
@@ -794,27 +795,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;
@@ -828,6 +839,8 @@ char *aa_get_name(struct dentry *dentry,
}
out:
+ if (order > 0)
+ *addr = buffer;
return name;
}
@@ -902,6 +915,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;
@@ -986,9 +1000,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);
@@ -1010,8 +1025,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;
}
@@ -1078,6 +1093,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;
@@ -1090,7 +1106,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;
@@ -1339,7 +1355,7 @@ apply_profile:
}
cleanup:
- aa_put_name(filename);
+ aa_put_name(filename, addr);
put_aaprofile(active);

View file

@ -0,0 +1,219 @@
Index: linux-2.6.19.1/security/apparmor/apparmor.h
===================================================================
--- linux-2.6.19.1.orig/security/apparmor/apparmor.h
+++ linux-2.6.19.1/security/apparmor/apparmor.h
@@ -234,7 +234,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.19.1/security/apparmor/inline.h
===================================================================
--- linux-2.6.19.1.orig/security/apparmor/inline.h
+++ linux-2.6.19.1/security/apparmor/inline.h
@@ -222,7 +222,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.19.1/security/apparmor/lsm.c
===================================================================
--- linux-2.6.19.1.orig/security/apparmor/lsm.c
+++ linux-2.6.19.1/security/apparmor/lsm.c
@@ -814,6 +814,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;
@@ -828,6 +833,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.19.1/security/apparmor/main.c
===================================================================
--- linux-2.6.19.1.orig/security/apparmor/main.c
+++ linux-2.6.19.1/security/apparmor/main.c
@@ -12,12 +12,133 @@
#include <linux/security.h>
#include <linux/namei.h>
#include <linux/audit.h>
+#include <linux/mm.h>
#include "apparmor.h"
#include "match/match.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
@@ -681,7 +802,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;
@@ -693,7 +814,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;

View file

@ -0,0 +1,101 @@
Index: linux-2.6.19.1/fs/dcache.c
===================================================================
--- linux-2.6.19.1.orig/fs/dcache.c
+++ linux-2.6.19.1/fs/dcache.c
@@ -1731,9 +1731,11 @@ shouldnt_be_hashed:
* @rootmnt: vfsmnt to which the root dentry belongs
* @buffer: buffer to return value in
* @buflen: buffer length
+ * @flags: flags indicating what should be in the 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.
+ * and the DPATH_DELETED flag is set the string " (deleted)" is appended.
+ * Note that this is ambiguous.
*
* Returns the buffer or an error code if the path was too long.
*
@@ -1741,7 +1743,7 @@ shouldnt_be_hashed:
*/
static char * __d_path( struct dentry *dentry, struct vfsmount *vfsmnt,
struct dentry *root, struct vfsmount *rootmnt,
- char *buffer, int buflen)
+ char *buffer, int buflen, unsigned int flags)
{
char * end = buffer+buflen;
char * retval;
@@ -1749,7 +1751,8 @@ static char * __d_path( struct dentry *d
*--end = '\0';
buflen--;
- if (!IS_ROOT(dentry) && d_unhashed(dentry)) {
+ if ((flags & DPATH_DELETED) &&
+ !IS_ROOT(dentry) && d_unhashed(dentry)) {
buflen -= 10;
end -= 10;
if (buflen < 0)
@@ -1808,8 +1811,8 @@ Elong:
}
/* write full pathname into buffer and return start of pathname */
-char * d_path(struct dentry *dentry, struct vfsmount *vfsmnt,
- char *buf, int buflen)
+char * d_path_flags(struct dentry *dentry, struct vfsmount *vfsmnt,
+ char *buf, int buflen, unsigned int flags)
{
char *res;
struct vfsmount *rootmnt;
@@ -1820,7 +1823,7 @@ char * d_path(struct dentry *dentry, str
root = dget(current->fs->root);
read_unlock(&current->fs->lock);
spin_lock(&dcache_lock);
- res = __d_path(dentry, vfsmnt, root, rootmnt, buf, buflen);
+ res = __d_path(dentry, vfsmnt, root, rootmnt, buf, buflen, flags);
spin_unlock(&dcache_lock);
dput(root);
mntput(rootmnt);
@@ -1869,7 +1872,8 @@ asmlinkage long sys_getcwd(char __user *
unsigned long len;
char * cwd;
- cwd = __d_path(pwd, pwdmnt, root, rootmnt, page, PAGE_SIZE);
+ cwd = __d_path(pwd, pwdmnt, root, rootmnt, page, PAGE_SIZE,
+ DPATH_DELETED);
spin_unlock(&dcache_lock);
error = PTR_ERR(cwd);
@@ -2119,7 +2123,7 @@ EXPORT_SYMBOL(d_invalidate);
EXPORT_SYMBOL(d_lookup);
EXPORT_SYMBOL(d_move);
EXPORT_SYMBOL_GPL(d_materialise_unique);
-EXPORT_SYMBOL(d_path);
+EXPORT_SYMBOL(d_path_flags);
EXPORT_SYMBOL(d_prune_aliases);
EXPORT_SYMBOL(d_rehash);
EXPORT_SYMBOL(d_splice_alias);
Index: linux-2.6.19.1/include/linux/dcache.h
===================================================================
--- linux-2.6.19.1.orig/include/linux/dcache.h
+++ linux-2.6.19.1/include/linux/dcache.h
@@ -176,6 +176,9 @@ d_iput: no no no yes
#define DCACHE_INOTIFY_PARENT_WATCHED 0x0020 /* Parent inode is watched */
+/* d_path flags */
+#define DPATH_DELETED 0x0001 /* append " (deleted)" */
+
extern spinlock_t dcache_lock;
/**
@@ -293,7 +296,10 @@ extern struct dentry * d_hash_and_lookup
/* validate "insecure" dentry pointer */
extern int d_validate(struct dentry *, struct dentry *);
-extern char * d_path(struct dentry *, struct vfsmount *, char *, int);
+#define d_path(D, V, B, BL) d_path_flags(D, V, B, BL, DPATH_DELETED)
+
+extern char * d_path_flags(struct dentry *, struct vfsmount *, char *, int,
+ unsigned int);
/* Allocation counts.. */

View file

@ -0,0 +1,74 @@
Index: linux-2.6.19.1/fs/dcache.c
===================================================================
--- linux-2.6.19.1.orig/fs/dcache.c
+++ linux-2.6.19.1/fs/dcache.c
@@ -1733,10 +1733,12 @@ shouldnt_be_hashed:
* @buflen: buffer length
* @flags: flags indicating what should be in the path
*
- * Convert a dentry into an ASCII path name. If the entry has been deleted
- * and the DPATH_DELETED flag is set the string " (deleted)" is appended.
- * Note that this is ambiguous.
- *
+ * Convert a dentry into an ASCII path name.
+ * If the entry has been deleted and the DPATH_DELETED flag is set the
+ * string " (deleted)" is appended. Note that this is ambiguous.
+ * If the DPATH_NSROOT flag is set the path returned will walk past the
+ * chroot.
+ *
* Returns the buffer or an error code if the path was too long.
*
* "buflen" should be positive. Caller holds the dcache_lock.
@@ -1769,7 +1771,8 @@ static char * __d_path( struct dentry *d
for (;;) {
struct dentry * parent;
- if (dentry == root && vfsmnt == rootmnt)
+ if (!(flags & DPATH_NSROOT) &&
+ dentry == root && vfsmnt == rootmnt)
break;
if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
/* Global root? */
@@ -1815,18 +1818,22 @@ char * d_path_flags(struct dentry *dentr
char *buf, int buflen, unsigned int flags)
{
char *res;
- struct vfsmount *rootmnt;
- struct dentry *root;
+ struct vfsmount *rootmnt = NULL;
+ struct dentry *root = NULL;
- read_lock(&current->fs->lock);
- rootmnt = mntget(current->fs->rootmnt);
- root = dget(current->fs->root);
- read_unlock(&current->fs->lock);
+ if (!(flags & DPATH_NSROOT)) {
+ read_lock(&current->fs->lock);
+ rootmnt = mntget(current->fs->rootmnt);
+ root = dget(current->fs->root);
+ read_unlock(&current->fs->lock);
+ }
spin_lock(&dcache_lock);
res = __d_path(dentry, vfsmnt, root, rootmnt, buf, buflen, flags);
spin_unlock(&dcache_lock);
- dput(root);
- mntput(rootmnt);
+ if (!(flags & DPATH_NSROOT)) {
+ dput(root);
+ mntput(rootmnt);
+ }
return res;
}
Index: linux-2.6.19.1/include/linux/dcache.h
===================================================================
--- linux-2.6.19.1.orig/include/linux/dcache.h
+++ linux-2.6.19.1/include/linux/dcache.h
@@ -178,6 +178,7 @@ d_iput: no no no yes
/* d_path flags */
#define DPATH_DELETED 0x0001 /* append " (deleted)" */
+#define DPATH_NSROOT 0x0002 /* continue past fsroot (chroot) */
extern spinlock_t dcache_lock;

View file

@ -0,0 +1,134 @@
Index: linux-2.6.19.1/fs/dcache.c
===================================================================
--- linux-2.6.19.1.orig/fs/dcache.c
+++ linux-2.6.19.1/fs/dcache.c
@@ -1732,6 +1732,8 @@ shouldnt_be_hashed:
* @buffer: buffer to return value in
* @buflen: buffer length
* @flags: flags indicating what should be in the path
+ * @pcons: if none null will contain flags indicating path lookup conditions
+ * that may affect the path name.
*
* Convert a dentry into an ASCII path name.
* If the entry has been deleted and the DPATH_DELETED flag is set the
@@ -1745,21 +1747,25 @@ shouldnt_be_hashed:
*/
static char * __d_path( struct dentry *dentry, struct vfsmount *vfsmnt,
struct dentry *root, struct vfsmount *rootmnt,
- char *buffer, int buflen, unsigned int flags)
+ char *buffer, int buflen, unsigned int flags,
+ unsigned int *pconds)
{
char * end = buffer+buflen;
char * retval;
+ unsigned int lpconds = 0;
int namelen;
*--end = '\0';
buflen--;
- if ((flags & DPATH_DELETED) &&
- !IS_ROOT(dentry) && d_unhashed(dentry)) {
- buflen -= 10;
- end -= 10;
- if (buflen < 0)
- goto Elong;
- memcpy(end, " (deleted)", 10);
+ if (!IS_ROOT(dentry) && d_unhashed(dentry)) {
+ if (flags & DPATH_DELETED) {
+ buflen -= 10;
+ end -= 10;
+ if (buflen < 0)
+ goto Elong;
+ memcpy(end, " (deleted)", 10);
+ }
+ lpconds |= DPATH_DELETED;
}
if (buflen < 1)
@@ -1771,9 +1777,11 @@ static char * __d_path( struct dentry *d
for (;;) {
struct dentry * parent;
- if (!(flags & DPATH_NSROOT) &&
- dentry == root && vfsmnt == rootmnt)
- break;
+ if (dentry == root && vfsmnt == rootmnt) {
+ if (!(flags & DPATH_NSROOT))
+ break;
+ lpconds |= DPATH_NSROOT;
+ }
if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
/* Global root? */
spin_lock(&vfsmount_lock);
@@ -1799,6 +1807,8 @@ static char * __d_path( struct dentry *d
dentry = parent;
}
+ if (lpconds)
+ *pconds = lpconds;
return retval;
global_root:
@@ -1808,6 +1818,8 @@ global_root:
goto Elong;
retval -= namelen-1; /* hit the slash */
memcpy(retval, dentry->d_name.name, namelen);
+ if (lpconds)
+ *pconds = lpconds;
return retval;
Elong:
return ERR_PTR(-ENAMETOOLONG);
@@ -1815,7 +1827,8 @@ Elong:
/* write full pathname into buffer and return start of pathname */
char * d_path_flags(struct dentry *dentry, struct vfsmount *vfsmnt,
- char *buf, int buflen, unsigned int flags)
+ char *buf, int buflen, unsigned int flags,
+ unsigned int *pconds)
{
char *res;
struct vfsmount *rootmnt = NULL;
@@ -1828,7 +1841,8 @@ char * d_path_flags(struct dentry *dentr
read_unlock(&current->fs->lock);
}
spin_lock(&dcache_lock);
- res = __d_path(dentry, vfsmnt, root, rootmnt, buf, buflen, flags);
+ res = __d_path(dentry, vfsmnt, root, rootmnt, buf, buflen, flags,
+ pconds);
spin_unlock(&dcache_lock);
if (!(flags & DPATH_NSROOT)) {
dput(root);
@@ -1858,6 +1872,7 @@ char * d_path_flags(struct dentry *dentr
asmlinkage long sys_getcwd(char __user *buf, unsigned long size)
{
int error;
+ unsigned int conds;
struct vfsmount *pwdmnt, *rootmnt;
struct dentry *pwd, *root;
char *page = (char *) __get_free_page(GFP_USER);
@@ -1880,7 +1895,7 @@ asmlinkage long sys_getcwd(char __user *
char * cwd;
cwd = __d_path(pwd, pwdmnt, root, rootmnt, page, PAGE_SIZE,
- DPATH_DELETED);
+ DPATH_DELETED, &conds);
spin_unlock(&dcache_lock);
error = PTR_ERR(cwd);
Index: linux-2.6.19.1/include/linux/dcache.h
===================================================================
--- linux-2.6.19.1.orig/include/linux/dcache.h
+++ linux-2.6.19.1/include/linux/dcache.h
@@ -297,10 +297,10 @@ extern struct dentry * d_hash_and_lookup
/* validate "insecure" dentry pointer */
extern int d_validate(struct dentry *, struct dentry *);
-#define d_path(D, V, B, BL) d_path_flags(D, V, B, BL, DPATH_DELETED)
+#define d_path(D, V, B, BL) d_path_flags(D, V, B, BL, DPATH_DELETED, NULL)
extern char * d_path_flags(struct dentry *, struct vfsmount *, char *, int,
- unsigned int);
+ unsigned int, unsigned int *);
/* Allocation counts.. */

View file

@ -0,0 +1,85 @@
Index: linux-2.6.19.1/fs/open.c
===================================================================
--- linux-2.6.19.1.orig/fs/open.c
+++ linux-2.6.19.1/fs/open.c
@@ -483,6 +483,10 @@ asmlinkage long sys_chroot(const char __
if (!capable(CAP_SYS_CHROOT))
goto dput_and_out;
+ error = security_chroot(&nd);
+ if (error)
+ goto dput_and_out;
+
set_fs_root(current->fs, nd.mnt, nd.dentry);
set_fs_altroot();
error = 0;
Index: linux-2.6.19.1/include/linux/security.h
===================================================================
--- linux-2.6.19.1.orig/include/linux/security.h
+++ linux-2.6.19.1/include/linux/security.h
@@ -247,6 +247,9 @@ struct request_sock;
* Update module state after a successful pivot.
* @old_nd contains the nameidata structure for the old root.
* @new_nd contains the nameidata structure for the new root.
+ * @sb_chroot:
+ * Check permission before chroot to chroot to point named by @nd
+ * @nd contains the nameidata object for the new root
*
* Security hooks for inode operations.
*
@@ -1219,6 +1222,7 @@ struct security_operations {
struct nameidata * new_nd);
void (*sb_post_pivotroot) (struct nameidata * old_nd,
struct nameidata * new_nd);
+ int (*sb_chroot) (struct nameidata * nd);
int (*inode_alloc_security) (struct inode *inode);
void (*inode_free_security) (struct inode *inode);
@@ -1617,6 +1621,11 @@ static inline void security_sb_post_pivo
security_ops->sb_post_pivotroot (old_nd, new_nd);
}
+static inline int security_chroot (struct nameidata *nd)
+{
+ return security_ops->sb_chroot (nd);
+}
+
static inline int security_inode_alloc (struct inode *inode)
{
inode->i_security = NULL;
@@ -2367,6 +2376,11 @@ static inline void security_sb_post_pivo
struct nameidata *new_nd)
{ }
+static inline int security_sb_chroot (struct nameidata *nd)
+{
+ return 0;
+}
+
static inline int security_inode_alloc (struct inode *inode)
{
return 0;
Index: linux-2.6.19.1/security/dummy.c
===================================================================
--- linux-2.6.19.1.orig/security/dummy.c
+++ linux-2.6.19.1/security/dummy.c
@@ -248,6 +248,11 @@ static void dummy_sb_post_pivotroot (str
return;
}
+static int dummy_sb_chroot (struct nameidata *nd)
+{
+ return 0;
+}
+
static int dummy_inode_alloc_security (struct inode *inode)
{
return 0;
@@ -1003,6 +1008,7 @@ void security_fixup_ops (struct security
set_to_dummy_if_null(ops, sb_post_addmount);
set_to_dummy_if_null(ops, sb_pivotroot);
set_to_dummy_if_null(ops, sb_post_pivotroot);
+ set_to_dummy_if_null(ops, sb_chroot);
set_to_dummy_if_null(ops, inode_alloc_security);
set_to_dummy_if_null(ops, inode_free_security);
set_to_dummy_if_null(ops, inode_init_security);

View file

@ -26,6 +26,10 @@ vfs-listxattr.diff
security-listxattr.diff
vfs-removexattr.diff
security-removexattr.diff
d_path_flags.diff
d_path_namespace_root.diff
d_path_return_flags.diff
security_chroot.diff
apparmor-audit.diff
apparmor-intree.diff
apparmor.diff
@ -34,3 +38,8 @@ apparmor-builtinonly.diff
apparmor-bootdisable.diff
apparmor-twophaseinit.diff
apparmor-novalidfstype.diff
apparmor-match_perms.diff
apparmor-dfa.diff
apparmor-percpu_path_cache.diff
apparmor-path_resize.diff
apparmor-d_path_flags.diff