Browse Source

Merge branch 'for-security' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor into next

James Morris 13 years ago
parent
commit
b01d3fb921

+ 3 - 2
security/apparmor/Makefile

@@ -15,7 +15,7 @@ clean-files := capability_names.h rlim_names.h
 # to
 #    [1] = "dac_override",
 quiet_cmd_make-caps = GEN     $@
-cmd_make-caps = echo "static const char *capability_names[] = {" > $@ ;\
+cmd_make-caps = echo "static const char const *capability_names[] = {" > $@ ;\
 	sed $< >>$@ -r -n -e '/CAP_FS_MASK/d' \
 	-e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/[\2] = "\L\1",/p';\
 	echo "};" >> $@
@@ -43,7 +43,8 @@ cmd_make-caps = echo "static const char *capability_names[] = {" > $@ ;\
 # to
 # #define AA_FS_RLIMIT_MASK "fsize stack"
 quiet_cmd_make-rlim = GEN     $@
-cmd_make-rlim = echo "static const char *rlim_names[RLIM_NLIMITS] = {" > $@ ;\
+cmd_make-rlim = echo "static const char const *rlim_names[RLIM_NLIMITS] = {" \
+	> $@ ;\
 	sed $< >> $@ -r -n \
 	    -e 's/^\# ?define[ \t]+(RLIMIT_([A-Z0-9_]+)).*/[\1] = "\L\2",/p';\
 	echo "};" >> $@ ;\

+ 3 - 3
security/apparmor/audit.c

@@ -19,7 +19,7 @@
 #include "include/audit.h"
 #include "include/policy.h"
 
-const char *op_table[] = {
+const char *const op_table[] = {
 	"null",
 
 	"sysctl",
@@ -73,7 +73,7 @@ const char *op_table[] = {
 	"profile_remove"
 };
 
-const char *audit_mode_names[] = {
+const char *const audit_mode_names[] = {
 	"normal",
 	"quiet_denied",
 	"quiet",
@@ -81,7 +81,7 @@ const char *audit_mode_names[] = {
 	"all"
 };
 
-static char *aa_audit_type[] = {
+static const char *const aa_audit_type[] = {
 	"AUDIT",
 	"ALLOWED",
 	"DENIED",

+ 2 - 3
security/apparmor/domain.c

@@ -372,13 +372,12 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
 	state = profile->file.start;
 
 	/* buffer freed below, name is pointer into buffer */
-	error = aa_get_name(&bprm->file->f_path, profile->path_flags, &buffer,
-			    &name);
+	error = aa_path_name(&bprm->file->f_path, profile->path_flags, &buffer,
+			     &name, &info);
 	if (error) {
 		if (profile->flags &
 		    (PFLAG_IX_ON_NAME_ERROR | PFLAG_UNCONFINED))
 			error = 0;
-		info = "Exec failed name resolution";
 		name = bprm->filename;
 		goto audit;
 	}

+ 7 - 11
security/apparmor/file.c

@@ -278,22 +278,16 @@ int aa_path_perm(int op, struct aa_profile *profile, struct path *path,
 	int error;
 
 	flags |= profile->path_flags | (S_ISDIR(cond->mode) ? PATH_IS_DIR : 0);
-	error = aa_get_name(path, flags, &buffer, &name);
+	error = aa_path_name(path, flags, &buffer, &name, &info);
 	if (error) {
 		if (error == -ENOENT && is_deleted(path->dentry)) {
 			/* Access to open files that are deleted are
 			 * give a pass (implicit delegation)
 			 */
 			error = 0;
+			info = NULL;
 			perms.allow = request;
-		} else if (error == -ENOENT)
-			info = "Failed name lookup - deleted entry";
-		else if (error == -ESTALE)
-			info = "Failed name lookup - disconnected path";
-		else if (error == -ENAMETOOLONG)
-			info = "Failed name lookup - name too long";
-		else
-			info = "Failed name lookup";
+		}
 	} else {
 		aa_str_perms(profile->file.dfa, profile->file.start, name, cond,
 			     &perms);
@@ -364,12 +358,14 @@ int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry,
 	lperms = nullperms;
 
 	/* buffer freed below, lname is pointer in buffer */
-	error = aa_get_name(&link, profile->path_flags, &buffer, &lname);
+	error = aa_path_name(&link, profile->path_flags, &buffer, &lname,
+			     &info);
 	if (error)
 		goto audit;
 
 	/* buffer2 freed below, tname is pointer in buffer2 */
-	error = aa_get_name(&target, profile->path_flags, &buffer2, &tname);
+	error = aa_path_name(&target, profile->path_flags, &buffer2, &tname,
+			     &info);
 	if (error)
 		goto audit;
 

+ 14 - 1
security/apparmor/include/apparmor.h

@@ -19,6 +19,19 @@
 
 #include "match.h"
 
+/*
+ * Class of mediation types in the AppArmor policy db
+ */
+#define AA_CLASS_ENTRY		0
+#define AA_CLASS_UNKNOWN	1
+#define AA_CLASS_FILE		2
+#define AA_CLASS_CAP		3
+#define AA_CLASS_NET		4
+#define AA_CLASS_RLIMITS	5
+#define AA_CLASS_DOMAIN		6
+
+#define AA_CLASS_LAST		AA_CLASS_DOMAIN
+
 /* Control parameters settable through module/boot flags */
 extern enum audit_mode aa_g_audit;
 extern bool aa_g_audit_header;
@@ -81,7 +94,7 @@ static inline unsigned int aa_dfa_null_transition(struct aa_dfa *dfa,
 						  unsigned int start)
 {
 	/* the null transition only needs the string's null terminator byte */
-	return aa_dfa_match_len(dfa, start, "", 1);
+	return aa_dfa_next(dfa, start, 0);
 }
 
 static inline bool mediated_filesystem(struct inode *inode)

+ 2 - 2
security/apparmor/include/audit.h

@@ -25,7 +25,7 @@
 
 struct aa_profile;
 
-extern const char *audit_mode_names[];
+extern const char *const audit_mode_names[];
 #define AUDIT_MAX_INDEX 5
 
 enum audit_mode {
@@ -47,7 +47,7 @@ enum audit_type {
 	AUDIT_APPARMOR_AUTO
 };
 
-extern const char *op_table[];
+extern const char *const op_table[];
 enum aa_ops {
 	OP_NULL,
 

+ 3 - 0
security/apparmor/include/match.h

@@ -116,6 +116,9 @@ unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start,
 			      const char *str, int len);
 unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start,
 			  const char *str);
+unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state,
+			 const char c);
+
 void aa_dfa_free_kref(struct kref *kref);
 
 /**

+ 2 - 1
security/apparmor/include/path.h

@@ -26,6 +26,7 @@ enum path_flags {
 	PATH_MEDIATE_DELETED = 0x10000,	/* mediate deleted paths */
 };
 
-int aa_get_name(struct path *path, int flags, char **buffer, const char **name);
+int aa_path_name(struct path *path, int flags, char **buffer,
+		 const char **name, const char **info);
 
 #endif /* __AA_PATH_H */

+ 14 - 1
security/apparmor/include/policy.h

@@ -29,7 +29,7 @@
 #include "file.h"
 #include "resource.h"
 
-extern const char *profile_mode_names[];
+extern const char *const profile_mode_names[];
 #define APPARMOR_NAMES_MAX_INDEX 3
 
 #define COMPLAIN_MODE(_profile)	\
@@ -129,6 +129,17 @@ struct aa_namespace {
 	struct list_head sub_ns;
 };
 
+/* struct aa_policydb - match engine for a policy
+ * dfa: dfa pattern match
+ * start: set of start states for the different classes of data
+ */
+struct aa_policydb {
+	/* Generic policy DFA specific rule types will be subsections of it */
+	struct aa_dfa *dfa;
+	unsigned int start[AA_CLASS_LAST + 1];
+
+};
+
 /* struct aa_profile - basic confinement data
  * @base - base components of the profile (name, refcount, lists, lock ...)
  * @parent: parent of profile
@@ -143,6 +154,7 @@ struct aa_namespace {
  * @flags: flags controlling profile behavior
  * @path_flags: flags controlling path generation behavior
  * @size: the memory consumed by this profiles rules
+ * @policy: general match rules governing policy
  * @file: The set of rules governing basic file access and domain transitions
  * @caps: capabilities for the profile
  * @rlimits: rlimits for the profile
@@ -179,6 +191,7 @@ struct aa_profile {
 	u32 path_flags;
 	int size;
 
+	struct aa_policydb policy;
 	struct aa_file_rules file;
 	struct aa_caps caps;
 	struct aa_rlimit rlimits;

+ 77 - 3
security/apparmor/match.c

@@ -335,12 +335,12 @@ unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start,
 }
 
 /**
- * aa_dfa_next_state - traverse @dfa to find state @str stops at
+ * aa_dfa_match - traverse @dfa to find state @str stops at
  * @dfa: the dfa to match @str against  (NOT NULL)
  * @start: the state of the dfa to start matching in
  * @str: the null terminated string of bytes to match against the dfa (NOT NULL)
  *
- * aa_dfa_next_state will match @str against the dfa and return the state it
+ * aa_dfa_match 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.
  *
@@ -349,5 +349,79 @@ unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start,
 unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start,
 			  const char *str)
 {
-	return aa_dfa_match_len(dfa, start, str, strlen(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 <state>, matching character *str */
+	if (dfa->tables[YYTD_ID_EC]) {
+		/* Equivalence class table defined */
+		u8 *equiv = EQUIV_TABLE(dfa);
+		/* default is direct to next state */
+		while (*str) {
+			pos = base[state] + equiv[(u8) *str++];
+			if (check[pos] == state)
+				state = next[pos];
+			else
+				state = def[state];
+		}
+	} else {
+		/* default is direct to next state */
+		while (*str) {
+			pos = base[state] + (u8) *str++;
+			if (check[pos] == state)
+				state = next[pos];
+			else
+				state = def[state];
+		}
+	}
+
+	return state;
+}
+
+/**
+ * aa_dfa_next - step one character to the next state in the dfa
+ * @dfa: the dfa to tranverse (NOT NULL)
+ * @state: the state to start in
+ * @c: the input character to transition on
+ *
+ * aa_dfa_match will step through the dfa by one input character @c
+ *
+ * Returns: state reach after input @c
+ */
+unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state,
+			  const char c)
+{
+	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 *str */
+	if (dfa->tables[YYTD_ID_EC]) {
+		/* Equivalence class table defined */
+		u8 *equiv = EQUIV_TABLE(dfa);
+		/* default is direct to next state */
+
+		pos = base[state] + equiv[(u8) c];
+		if (check[pos] == state)
+			state = next[pos];
+		else
+			state = def[state];
+	} else {
+		/* default is direct to next state */
+		pos = base[state] + (u8) c;
+		if (check[pos] == state)
+			state = next[pos];
+		else
+			state = def[state];
+	}
+
+	return state;
 }

+ 34 - 21
security/apparmor/path.c

@@ -83,30 +83,29 @@ static int d_namespace_path(struct path *path, char *buf, int buflen,
 		struct path root;
 		get_fs_root(current->fs, &root);
 		res = __d_path(path, &root, buf, buflen);
-		if (res && !IS_ERR(res)) {
-			/* everything's fine */
-			*name = res;
-			path_put(&root);
-			goto ok;
-		}
 		path_put(&root);
-		connected = 0;
-	} else
+	} else {
 		res = d_absolute_path(path, buf, buflen);
+		if (!our_mnt(path->mnt))
+			connected = 0;
+	}
 
-	*name = res;
 	/* handle error conditions - and still allow a partial path to
 	 * be returned.
 	 */
-	if (IS_ERR(res)) {
-		error = PTR_ERR(res);
-		*name = buf;
-		goto out;
-	}
-	if (!our_mnt(path->mnt))
+	if (!res || IS_ERR(res)) {
 		connected = 0;
+		res = dentry_path_raw(path->dentry, buf, buflen);
+		if (IS_ERR(res)) {
+			error = PTR_ERR(res);
+			*name = buf;
+			goto out;
+		};
+	} else if (!our_mnt(path->mnt))
+		connected = 0;
+
+	*name = res;
 
-ok:
 	/* Handle two cases:
 	 * 1. A deleted dentry && profile is not allowing mediation of deleted
 	 * 2. On some filesystems, newly allocated dentries appear to the
@@ -137,7 +136,7 @@ ok:
 			/* disconnected path, don't return pathname starting
 			 * with '/'
 			 */
-			error = -ESTALE;
+			error = -EACCES;
 			if (*res == '/')
 				*name = res + 1;
 		}
@@ -158,7 +157,7 @@ out:
  * Returns: %0 else error on failure
  */
 static int get_name_to_buffer(struct path *path, int flags, char *buffer,
-			      int size, char **name)
+			      int size, char **name, const char **info)
 {
 	int adjust = (flags & PATH_IS_DIR) ? 1 : 0;
 	int error = d_namespace_path(path, buffer, size - adjust, name, flags);
@@ -170,15 +169,27 @@ static int get_name_to_buffer(struct path *path, int flags, char *buffer,
 		 */
 		strcpy(&buffer[size - 2], "/");
 
+	if (info && error) {
+		if (error == -ENOENT)
+			*info = "Failed name lookup - deleted entry";
+		else if (error == -ESTALE)
+			*info = "Failed name lookup - disconnected path";
+		else if (error == -ENAMETOOLONG)
+			*info = "Failed name lookup - name too long";
+		else
+			*info = "Failed name lookup";
+	}
+
 	return error;
 }
 
 /**
- * aa_get_name - compute the pathname of a file
+ * aa_path_name - compute the pathname of a file
  * @path: path the file  (NOT NULL)
  * @flags: flags controlling path name generation
  * @buffer: buffer that aa_get_name() allocated  (NOT NULL)
  * @name: Returns - the generated path name if !error (NOT NULL)
+ * @info: Returns - information on why the path lookup failed (MAYBE NULL)
  *
  * @name is a pointer to the beginning of the pathname (which usually differs
  * from the beginning of the buffer), or NULL.  If there is an error @name
@@ -191,7 +202,8 @@ static int get_name_to_buffer(struct path *path, int flags, char *buffer,
  *
  * Returns: %0 else error code if could retrieve name
  */
-int aa_get_name(struct path *path, int flags, char **buffer, const char **name)
+int aa_path_name(struct path *path, int flags, char **buffer, const char **name,
+		 const char **info)
 {
 	char *buf, *str = NULL;
 	int size = 256;
@@ -205,7 +217,7 @@ int aa_get_name(struct path *path, int flags, char **buffer, const char **name)
 		if (!buf)
 			return -ENOMEM;
 
-		error = get_name_to_buffer(path, flags, buf, size, &str);
+		error = get_name_to_buffer(path, flags, buf, size, &str, info);
 		if (error != -ENAMETOOLONG)
 			break;
 
@@ -213,6 +225,7 @@ int aa_get_name(struct path *path, int flags, char **buffer, const char **name)
 		size <<= 1;
 		if (size > aa_g_path_max)
 			return -ENAMETOOLONG;
+		*info = NULL;
 	}
 	*buffer = buf;
 	*name = str;

+ 2 - 1
security/apparmor/policy.c

@@ -93,7 +93,7 @@
 /* root profile namespace */
 struct aa_namespace *root_ns;
 
-const char *profile_mode_names[] = {
+const char *const profile_mode_names[] = {
 	"enforce",
 	"complain",
 	"kill",
@@ -749,6 +749,7 @@ static void free_profile(struct aa_profile *profile)
 
 	aa_free_sid(profile->sid);
 	aa_put_dfa(profile->xmatch);
+	aa_put_dfa(profile->policy.dfa);
 
 	aa_put_profile(profile->replacedby);
 

+ 26 - 3
security/apparmor/policy_unpack.c

@@ -84,7 +84,7 @@ static void audit_cb(struct audit_buffer *ab, void *va)
  * @new: profile if it has been allocated (MAYBE NULL)
  * @name: name of the profile being manipulated (MAYBE NULL)
  * @info: any extra info about the failure (MAYBE NULL)
- * @e: buffer position info (NOT NULL)
+ * @e: buffer position info
  * @error: error code
  *
  * Returns: %0 or error
@@ -95,7 +95,8 @@ static int audit_iface(struct aa_profile *new, const char *name,
 	struct aa_profile *profile = __aa_current_profile();
 	struct common_audit_data sa;
 	COMMON_AUDIT_DATA_INIT(&sa, NONE);
-	sa.aad.iface.pos = e->pos - e->start;
+	if (e)
+		sa.aad.iface.pos = e->pos - e->start;
 	sa.aad.iface.target = new;
 	sa.aad.name = name;
 	sa.aad.info = info;
@@ -468,7 +469,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
 {
 	struct aa_profile *profile = NULL;
 	const char *name = NULL;
-	int error = -EPROTO;
+	int i, error = -EPROTO;
 	kernel_cap_t tmpcap;
 	u32 tmp;
 
@@ -561,6 +562,28 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
 	if (!unpack_rlimits(e, profile))
 		goto fail;
 
+	if (unpack_nameX(e, AA_STRUCT, "policydb")) {
+		/* generic policy dfa - optional and may be NULL */
+		profile->policy.dfa = unpack_dfa(e);
+		if (IS_ERR(profile->policy.dfa)) {
+			error = PTR_ERR(profile->policy.dfa);
+			profile->policy.dfa = NULL;
+			goto fail;
+		}
+		if (!unpack_u32(e, &profile->policy.start[0], "start"))
+			/* default start state */
+			profile->policy.start[0] = DFA_START;
+		/* setup class index */
+		for (i = AA_CLASS_FILE; i <= AA_CLASS_LAST; i++) {
+			profile->policy.start[i] =
+				aa_dfa_next(profile->policy.dfa,
+					    profile->policy.start[0],
+					    i);
+		}
+		if (!unpack_nameX(e, AA_STRUCTEND, NULL))
+			goto fail;
+	}
+
 	/* get file rules */
 	profile->file.dfa = unpack_dfa(e);
 	if (IS_ERR(profile->file.dfa)) {