[apparmor] [PATCH 17/18] apparmor: Add interface files for profiles and namespaces

Seth Arnold seth.arnold at gmail.com
Fri Jul 27 07:54:51 UTC 2012


That ARRAY_LEN macro scares me; there are so many potential uses that could give an incorrect answer that I'd _really_ rather see it replaced with a hardcoded enum or define in the same place as AAFS_PROF_PROFS and related values.
-----Original Message-----
From: John Johansen <john.johansen at canonical.com>
Sender: apparmor-bounces at lists.ubuntu.com
Date: Thu, 26 Jul 2012 21:28:15 
To: <apparmor at lists.ubuntu.com>
Subject: [apparmor] [PATCH 17/18] apparmor: Add interface files for profiles
	and namespaces

Add basic interface files to access namespace and profile information.
The interface files are created when a profile is loaded and removed
when the profile or namespace is removed.

Signed-off-by: John Johansen <john.johansen at canonical.com>
---
 security/apparmor/apparmorfs.c         |  224 +++++++++++++++++++++++++++++---
 security/apparmor/audit.c              |    6 +
 security/apparmor/include/apparmorfs.h |   26 ++++
 security/apparmor/include/audit.h      |    2 +
 security/apparmor/include/policy.h     |    9 ++
 security/apparmor/policy.c             |   83 +++++++++++-
 6 files changed, 330 insertions(+), 20 deletions(-)

diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index b73e59b..76d107f 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -182,8 +182,193 @@ const struct file_operations aa_fs_seq_file_ops = {
 	.release	= single_release,
 };
 
-/** Base file system setup **/
+static int aa_fs_seq_string_show(struct seq_file *seq, void *v)
+{
+	char *string = seq->private;
+
+	if (string)
+		seq_printf(seq, "%s\n", string);
+
+	return 0;
+}
+
+static int aa_fs_seq_string_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, aa_fs_seq_string_show, inode->i_private);
+}
+
+const struct file_operations aa_fs_seq_string_fops = {
+	.owner		= THIS_MODULE,
+	.open		= aa_fs_seq_string_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int aa_fs_seq_mode_show(struct seq_file *seq, void *v)
+{
+	enum profile_mode *mode = seq->private;
+
+	seq_printf(seq, "%s\n", aa_profile_mode_names[*mode]);
+
+	return 0;
+}
+
+static int aa_fs_seq_mode_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, aa_fs_seq_mode_show, inode->i_private);
+}
 
+const struct file_operations aa_fs_seq_mode_fops = {
+	.owner		= THIS_MODULE,
+	.open		= aa_fs_seq_mode_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+/** fns to setup dynamic per profile/namespace files **/
+void __aa_fs_profile_rmdir(struct aa_profile *profile)
+{
+	struct aa_profile *child;
+	int i;
+
+	if (!profile)
+		return;
+
+	list_for_each_entry(child, &profile->base.profiles, base.list)
+		__aa_fs_profile_rmdir(child);
+
+	for (i = ARRAY_LEN(profile->dents) - 1; i >= 0; --i) {
+		securityfs_remove(profile->dents[i]);
+		profile->dents[i] = NULL;
+	}
+}
+
+/* requires lock be held */
+int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
+{
+	struct aa_profile *child;
+	struct dentry *dent, *dir;
+	int error;
+
+	if (!parent) {
+		dent = profile->parent->dents[AAFS_PROF_DIR];
+		/* adding to parent that previously didn't have children */
+		dent = securityfs_create_dir("profiles", dent);
+		if (!dent)
+			goto fail;
+		profile->parent->dents[AAFS_PROF_PROFS] = parent = dent;
+	}
+
+	dent = securityfs_create_dir(profile->dirname, parent);
+	if (IS_ERR(dent))
+		goto fail;
+	profile->dents[AAFS_PROF_DIR] = dir = dent;
+
+	dent = securityfs_create_file("name", S_IFREG | 0444, dir,
+				      profile->base.name,
+				      &aa_fs_seq_string_fops);
+	if (IS_ERR(dent))
+		goto fail;
+	profile->dents[AAFS_PROF_NAME] = dent;
+
+	dent = securityfs_create_file("mode", S_IFREG | 0444, dir,
+				      &profile->mode, &aa_fs_seq_mode_fops);
+	if (IS_ERR(dent))
+		goto fail;
+	profile->dents[AAFS_PROF_MODE] = dent;
+
+	list_for_each_entry(child, &profile->base.profiles, base.list) {
+		error = __aa_fs_profile_mkdir(child, profile->dents[AAFS_PROF_PROFS]);
+		if (error)
+			goto fail2;
+	}
+
+	return 0;
+
+fail:
+	error = PTR_ERR(dent);
+
+fail2:
+	__aa_fs_profile_rmdir(profile);
+
+	return error;
+}
+
+void __aa_fs_namespace_rmdir(struct aa_namespace *ns)
+{
+	struct aa_namespace *sub;
+	struct aa_profile *child;
+	int i;
+
+	if (!ns)
+		return;
+
+	list_for_each_entry(child, &ns->base.profiles, base.list)
+		__aa_fs_profile_rmdir(child);
+
+	list_for_each_entry(sub, &ns->sub_ns, base.list)
+		__aa_fs_namespace_rmdir(sub);
+
+	for (i = ARRAY_LEN(ns->dents) - 1; i >= 0 ; --i) {
+		securityfs_remove(ns->dents[i]);
+		ns->dents[i] = NULL;
+	}
+}
+
+int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent,
+			    const char *name)
+{
+	struct aa_namespace *sub;
+	struct aa_profile *child;
+	struct dentry *dent, *dir;
+	int error;
+
+	if (!name)
+		name = ns->base.name;
+
+	dent = securityfs_create_dir(name, parent);
+	if (IS_ERR(dent))
+		goto fail;
+	ns->dents[AAFS_NS_DIR] = dir = dent;
+
+	dent = securityfs_create_dir("profiles", dir);
+	if (IS_ERR(dent))
+		goto fail;
+	ns->dents[AAFS_NS_PROFS] = dent;
+
+	dent = securityfs_create_dir("namespaces", dir);
+	if (IS_ERR(dent))
+		goto fail;
+	ns->dents[AAFS_NS_NS] = dent;
+
+	list_for_each_entry(child, &ns->base.profiles, base.list) {
+		error = __aa_fs_profile_mkdir(child, ns->dents[AAFS_NS_PROFS]);
+		if (error)
+			goto fail2;
+	}
+
+	list_for_each_entry(sub, &ns->sub_ns, base.list) {
+		error = __aa_fs_namespace_mkdir(sub, ns->dents[AAFS_NS_NS],
+						NULL);
+		if (error)
+			goto fail2;
+	}
+
+	return 0;
+
+fail:
+	error = PTR_ERR(dent);
+
+fail2:
+	__aa_fs_namespace_rmdir(ns);
+
+	return error;
+}
+
+
+/** Base file system setup **/
 static struct aa_fs_entry aa_fs_entry_file[] = {
 	AA_FS_FILE_STRING("mask", "create read write exec append mmap_exec " \
 				  "link lock"),
@@ -221,8 +406,10 @@ static struct aa_fs_entry aa_fs_entry_apparmor[] = {
 	{ }
 };
 
-static struct aa_fs_entry aa_fs_entry =
-	AA_FS_DIR("apparmor", aa_fs_entry_apparmor);
+static struct aa_fs_entry aa_fs_entry[] = {
+	AA_FS_DIR("apparmor", aa_fs_entry_apparmor),
+	{ }
+};
 
 /**
  * aafs_create_file - create a file entry in the apparmor securityfs
@@ -247,6 +434,7 @@ static int __init aafs_create_file(struct aa_fs_entry *fs_file,
 	return error;
 }
 
+static void __init aafs_remove_dir(struct aa_fs_entry *fs_dir);
 /**
  * aafs_create_dir - recursively create a directory entry in the securityfs
  * @fs_dir: aa_fs_entry (and all child entries) to build (NOT NULL)
@@ -257,17 +445,16 @@ static int __init aafs_create_file(struct aa_fs_entry *fs_file,
 static int __init aafs_create_dir(struct aa_fs_entry *fs_dir,
 				  struct dentry *parent)
 {
-	int error;
 	struct aa_fs_entry *fs_file;
+	struct dentry *dir;
+	int error;
 
-	fs_dir->dentry = securityfs_create_dir(fs_dir->name, parent);
-	if (IS_ERR(fs_dir->dentry)) {
-		error = PTR_ERR(fs_dir->dentry);
-		fs_dir->dentry = NULL;
-		goto failed;
-	}
+	dir = securityfs_create_dir(fs_dir->name, parent);
+	if (IS_ERR(dir))
+		return PTR_ERR(dir);
+	fs_dir->dentry = dir;
 
-	for (fs_file = fs_dir->v.files; fs_file->name; ++fs_file) {
+	for (fs_file = fs_dir->v.files; fs_file && fs_file->name; ++fs_file) {
 		if (fs_file->v_type == AA_FS_TYPE_DIR)
 			error = aafs_create_dir(fs_file, fs_dir->dentry);
 		else
@@ -279,6 +466,8 @@ static int __init aafs_create_dir(struct aa_fs_entry *fs_dir,
 	return 0;
 
 failed:
+	aafs_remove_dir(fs_dir);
+
 	return error;
 }
 
@@ -303,7 +492,7 @@ static void __init aafs_remove_dir(struct aa_fs_entry *fs_dir)
 {
 	struct aa_fs_entry *fs_file;
 
-	for (fs_file = fs_dir->v.files; fs_file->name; ++fs_file) {
+	for (fs_file = fs_dir->v.files; fs_file && fs_file->name; ++fs_file) {
 		if (fs_file->v_type == AA_FS_TYPE_DIR)
 			aafs_remove_dir(fs_file);
 		else
@@ -320,7 +509,7 @@ static void __init aafs_remove_dir(struct aa_fs_entry *fs_dir)
  */
 void __init aa_destroy_aafs(void)
 {
-	aafs_remove_dir(&aa_fs_entry);
+	aafs_remove_dir(aa_fs_entry);
 }
 
 /**
@@ -337,13 +526,18 @@ static int __init aa_create_aafs(void)
 	if (!apparmor_initialized)
 		return 0;
 
-	if (aa_fs_entry.dentry) {
+	if (aa_fs_entry[0].dentry) {
 		AA_ERROR("%s: AppArmor securityfs already exists\n", __func__);
 		return -EEXIST;
 	}
 
 	/* Populate fs tree. */
-	error = aafs_create_dir(&aa_fs_entry, NULL);
+	error = aafs_create_dir(aa_fs_entry, NULL);
+	if (error)
+		goto error;
+
+	error = __aa_fs_namespace_mkdir(root_ns, aa_fs_entry[0].dentry,
+					"policy");
 	if (error)
 		goto error;
 
diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c
index 3ae28db..9ada1bd 100644
--- a/security/apparmor/audit.c
+++ b/security/apparmor/audit.c
@@ -73,6 +73,12 @@ const char *const op_table[] = {
 	"profile_remove"
 };
 
+const char *const aa_profile_mode_names[] = {
+	"enforce",
+	"complain",
+	"kill"
+};
+
 const char *const audit_mode_names[] = {
 	"normal",
 	"quiet_denied",
diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h
index 7ea4769..e08869a 100644
--- a/security/apparmor/include/apparmorfs.h
+++ b/security/apparmor/include/apparmorfs.h
@@ -15,6 +15,8 @@
 #ifndef __AA_APPARMORFS_H
 #define __AA_APPARMORFS_H
 
+#define ARRAY_LEN(ARRAY) (sizeof(ARRAY)/sizeof(*(ARRAY)))
+
 enum aa_fs_type {
 	AA_FS_TYPE_BOOLEAN,
 	AA_FS_TYPE_STRING,
@@ -61,4 +63,28 @@ extern const struct file_operations aa_fs_seq_file_ops;
 
 extern void __init aa_destroy_aafs(void);
 
+struct aa_profile;
+struct aa_namespace;
+
+enum aafs_ns_type {
+	AAFS_NS_DIR,
+	AAFS_NS_PROFS,
+	AAFS_NS_NS,
+	AAFS_NS_LEN,
+};
+
+enum aafs_prof_type {
+	AAFS_PROF_DIR,
+	AAFS_PROF_PROFS,
+	AAFS_PROF_NAME,
+	AAFS_PROF_MODE,
+	AAFS_PROF_LEN,
+};	
+
+void __aa_fs_profile_rmdir(struct aa_profile *profile);
+int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent);
+void __aa_fs_namespace_rmdir(struct aa_namespace *ns);
+int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent,
+			    const char *name);
+
 #endif /* __AA_APPARMORFS_H */
diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h
index 4b7e189..f551329 100644
--- a/security/apparmor/include/audit.h
+++ b/security/apparmor/include/audit.h
@@ -28,6 +28,8 @@ struct aa_profile;
 extern const char *const audit_mode_names[];
 #define AUDIT_MAX_INDEX 5
 
+extern const char *const aa_profile_mode_names[];
+
 enum audit_mode {
 	AUDIT_NORMAL,		/* follow normal auditing of accesses */
 	AUDIT_QUIET_DENIED,	/* quiet all denied access messages */
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h
index 5798135..6dc51c9 100644
--- a/security/apparmor/include/policy.h
+++ b/security/apparmor/include/policy.h
@@ -105,6 +105,7 @@ struct aa_ns_acct {
  * @acct: accounting for the namespace
  * @unconfined: special unconfined profile for the namespace
  * @sub_ns: list of namespaces under the current namespace.
+ * @dents: dentries for the namespaces file entries in apparmorfs
  *
  * An aa_namespace defines the set profiles that are searched to determine
  * which profile to attach to a task.  Profiles can not be shared between
@@ -127,6 +128,8 @@ struct aa_namespace {
 	struct aa_ns_acct acct;
 	struct aa_profile *unconfined;
 	struct list_head sub_ns;
+
+	struct dentry *dents[AAFS_NS_LEN];
 };
 
 /* struct aa_policydb - match engine for a policy
@@ -159,6 +162,9 @@ struct aa_policydb {
  * @caps: capabilities for the profile
  * @rlimits: rlimits for the profile
  *
+ * @dents: dentries for the profiles file entries in apparmorfs
+ * @dirname: name of the profile dir in apparmorfs
+ *
  * The AppArmor profile contains the basic confinement data.  Each profile
  * has a name, and exists in a namespace.  The @name and @exec_match are
  * used to determine profile attachment against unconfined tasks.  All other
@@ -195,6 +201,9 @@ struct aa_profile {
 	struct aa_file_rules file;
 	struct aa_caps caps;
 	struct aa_rlimit rlimits;
+
+	char *dirname;
+	struct dentry *dents[AAFS_PROF_LEN];
 };
 
 extern struct aa_namespace *root_ns;
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
index e4d2dc3..78df425 100644
--- a/security/apparmor/policy.c
+++ b/security/apparmor/policy.c
@@ -73,6 +73,7 @@
  * FIXME: move profile lists to using rcu_lists
  */
 
+#include <linux/ctype.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/string.h>
@@ -116,6 +117,38 @@ static const char *hname_tail(const char *hname)
 }
 
 /**
+ * aa_mangle_name - mangle a profile name to std profile layout form
+ * @name: profile name to mangle  (NOT NULL)
+ * @target: buffer to store mangled name, same length as @name (MAYBE NULL)
+ *
+ * Returns: length of mangled name
+ */
+static int mangle_name(char *name, char *target)
+{
+	char *t = target;
+
+	while (*name == '/' || *name == '.')
+		name++;
+
+	for (; *name; name++) {
+		if (strchr("\"\'(){}[]", *name))
+			continue;
+		else if (*name == '/')
+		  target ? *(t)++ = '.' : t++;
+		else if (isspace(*name))
+			target ? *(t)++ = '_' : t++;
+		else if (strchr("*?^$\\", *name))
+			target ? *(t)++ = 'X' : t++;
+		else if (isgraph(*name))
+			target ? *(t)++ = *name : t++;
+	}
+	if (target)
+		*t = 0;
+
+	return t - target;
+}
+
+/**
  * policy_init - initialize a policy structure
  * @policy: policy to initialize  (NOT NULL)
  * @prefix: prefix name if any is required.  (MAYBE NULL)
@@ -417,6 +450,17 @@ static struct aa_namespace *aa_prepare_namespace(const char *name)
 			/* add parent ref */
 			new_ns->parent = aa_get_namespace(root);
 
+			if (__aa_fs_namespace_mkdir(new_ns,
+						    root->dents[AAFS_NS_PROFS],
+						    NULL)) {
+				free_namespace(new_ns);
+				AA_ERROR("Added namespace %s but failed to "
+					 "add interface files.\n",
+					 new_ns->base.name);
+				ns = NULL;
+				goto out;
+			}
+
 			list_add(&new_ns->base.list, &root->sub_ns);
 			/* add list ref */
 			ns = aa_get_namespace(new_ns);
@@ -666,6 +710,7 @@ static void free_profile(struct aa_profile *profile)
 	aa_free_rlimit_rules(&profile->rlimits);
 
 	aa_free_sid(profile->sid);
+	kzfree(profile->dirname);
 	aa_put_dfa(profile->xmatch);
 	aa_put_dfa(profile->policy.dfa);
 
@@ -696,6 +741,7 @@ void aa_free_profile_kref(struct kref *kref)
 struct aa_profile *aa_alloc_profile(const char *hname, u32 sid)
 {
 	struct aa_profile *profile;
+	int len;
 
 	/* freed by free_profile - usually through aa_put_profile */
 	profile = kzalloc(sizeof(*profile), GFP_KERNEL);
@@ -711,6 +757,15 @@ struct aa_profile *aa_alloc_profile(const char *hname, u32 sid)
 		sid = aa_alloc_sid();
 	profile->sid = sid;
 
+	len = mangle_name(profile->base.name, NULL);
+	profile->dirname = kmalloc(len + 12, GFP_KERNEL);
+	if (!profile->dirname) {
+		free_profile(profile);
+		return NULL;
+	}
+	len = sprintf(profile->dirname, "%d-", sid);
+	mangle_name(profile->base.name, profile->dirname + len);
+
 	/* refcount released by caller */
 	return profile;
 }
@@ -986,11 +1041,11 @@ bool aa_may_manage_policy(int op)
  * @ns - namespace the lookup occurs in
  * @new - profile to lookup who it is replacing
  * @noreplace - true if not replacing an existing profile
- * @old - Returns: pointer to profile to replace (NO REFCOUNT)
- * @rename - Returns: pointer to profile to rename (NO REFCOUNT)
+ * @old - Returns: pointer to profile to replace (ref counted)
+ * @rename - Returns: pointer to profile to rename (ref counted)
  * @info - Returns: info string on why lookup failed
  *
- * Returns: policy (no ref) profile is in on success else ptr error
+ * Returns: policy (no ref count) profile is in on success else ptr error
  */
 static struct aa_policy *__lookup_replace(struct aa_namespace *ns,
 					  struct aa_profile *new,
@@ -1093,6 +1148,18 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
 			new->parent = aa_get_profile((struct aa_profile *) policy);
 	}
 
+	list_for_each_entry(new, &lh, base.list) {
+		/* make new profiles available for introspection */
+		error = __aa_fs_profile_mkdir(new,
+			new->parent ? new->parent->dents[AAFS_PROF_PROFS] :
+				      new->ns->dents[AAFS_NS_PROFS]);
+		if (error) {
+			info = "failed to create ";
+			write_unlock(&ns->lock);
+			goto fail;
+		}
+	}
+
 	/* do actual replacement */
 	list_for_each_entry_safe(new, tmp, &lh, base.list) {
 		struct aa_profile *old, *rename;
@@ -1104,10 +1171,14 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace)
 
 		audit_policy(op, GFP_ATOMIC, new->base.name, NULL, error);
 
-		if (rename)
+		if (rename) {
 			__replace_profile(rename, new);
-		if (old)
+			__aa_fs_profile_rmdir(rename);
+		}
+		if (old) {
 			__replace_profile(old, new);
+			__aa_fs_profile_rmdir(old);
+		}
 		if (!(old || rename))
 			__list_add_profile(&policy->profiles, new);
 
@@ -1182,6 +1253,7 @@ ssize_t aa_remove_profiles(char *fqname, size_t size)
 		/* remove namespace - can only happen if fqname[0] == ':' */
 		write_lock(&ns->parent->lock);
 		__remove_namespace(ns);
+		__aa_fs_namespace_rmdir(ns);
 		write_unlock(&ns->parent->lock);
 	} else {
 		/* remove profile */
@@ -1194,6 +1266,7 @@ ssize_t aa_remove_profiles(char *fqname, size_t size)
 		}
 		name = profile->base.hname;
 		__remove_profile(profile);
+		__aa_fs_profile_rmdir(profile);
 		write_unlock(&ns->lock);
 	}
 
-- 
1.7.10.4


-- 
AppArmor mailing list
AppArmor at lists.ubuntu.com
Modify settings or unsubscribe at: https://lists.ubuntu.com/mailman/listinfo/apparmor


More information about the AppArmor mailing list