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

John Johansen john.johansen at canonical.com
Sat Jun 30 09:34:36 UTC 2012


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         |  210 +++++++++++++++++++++++++++++---
 security/apparmor/include/apparmorfs.h |   10 ++
 security/apparmor/include/policy.h     |   10 ++
 security/apparmor/policy.c             |   30 ++++-
 4 files changed, 244 insertions(+), 16 deletions(-)

diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index 16c15ec..cdcdb6d 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -182,8 +182,179 @@ 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", 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;
+
+	switch (*mode) {
+	case APPARMOR_ENFORCE:
+		seq_printf(seq, "enforce");
+		break;
+	case APPARMOR_COMPLAIN:
+		seq_printf(seq, "complain");
+		break;
+	case APPARMOR_KILL:
+		seq_printf(seq, "kill");
+		break;
+	}
+
+	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;
+
+	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]);
+}
+
+int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
+{
+	struct aa_profile *child;
+	struct dentry *dir;
+	int error, i = 0;
+
+	profile->dents[i++] = securityfs_create_dir(profile->sidstr, parent);
+	if (IS_ERR(profile->dents[i - 1]))
+		goto fail;
+	dir = profile->dents[0];
+
+	profile->dents[i++] = securityfs_create_dir("profiles", dir);
+	if (IS_ERR(profile->dents[i - 1]))
+		goto fail;
+	profile->dents[i++] = securityfs_create_file("name", S_IFREG | 0444,
+						     dir, profile->base.name,
+						     &aa_fs_seq_string_fops);
+	if (IS_ERR(profile->dents[i - 1]))
+		goto fail;
+	profile->dents[i++] = securityfs_create_file("mode", S_IFREG | 0444,
+						     dir, &profile->mode,
+						     &aa_fs_seq_mode_fops);
+	if (IS_ERR(profile->dents[i - 1]))
+		goto fail;
+
+	list_for_each_entry(child, &profile->base.profiles, base.list) {
+		error = __aa_fs_profile_mkdir(child, profile->dents[1]);
+		if (error)
+			goto fail2;
+	}
+
+	return 0;
+
+fail:
+	error = PTR_ERR(profile->dents[i - 1]);
+	profile->dents[i - 1] = NULL;
+
+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;
+
+	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]);
+}
+
+int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent)
+{
+	struct aa_namespace *sub;
+	struct aa_profile *child;
+	struct dentry *dir;
+	int error, i = 0;
+
+	ns->dents[i++] = securityfs_create_dir(ns->base.name, parent);
+	if (IS_ERR(ns->dents[i - 1]))
+		goto fail;
+	dir = ns->dents[0];
+
+	ns->dents[i++] = securityfs_create_dir("profiles", dir);
+	if (IS_ERR(ns->dents[i - 1]))
+		goto fail;
+	ns->dents[i++] = securityfs_create_dir("namespaces", dir);
+	if (IS_ERR(ns->dents[i - 1]))
+		goto fail;
+
+	list_for_each_entry(child, &ns->base.profiles, base.list) {
+		error = __aa_fs_profile_mkdir(child, ns->dents[1]);
+		if (error)
+			goto fail2;
+	}
+
+	list_for_each_entry(sub, &ns->sub_ns, base.list) {
+		error = __aa_fs_namespace_mkdir(sub, ns->dents[2]);
+		if (error)
+			goto fail2;
+	}
+
+	return 0;
+
+fail:
+	error = PTR_ERR(ns->dents[i - 1]);
+	ns->dents[i - 1] = NULL;
+
+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"),
@@ -207,6 +378,7 @@ static struct aa_fs_entry aa_fs_entry_features[] = {
 };
 
 static struct aa_fs_entry aa_fs_entry_apparmor[] = {
+	AA_FS_DIR("policy", NULL),	/* don't move see aacreate_aafs */
 	AA_FS_FILE_FOPS(".load", 0640, &aa_fs_profile_load),
 	AA_FS_FILE_FOPS(".replace", 0640, &aa_fs_profile_replace),
 	AA_FS_FILE_FOPS(".remove", 0640, &aa_fs_profile_remove),
@@ -214,8 +386,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
@@ -240,6 +414,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)
@@ -250,17 +425,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
@@ -272,6 +446,8 @@ static int __init aafs_create_dir(struct aa_fs_entry *fs_dir,
 	return 0;
 
 failed:
+	aafs_remove_dir(fs_dir);
+
 	return error;
 }
 
@@ -296,7 +472,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
@@ -313,7 +489,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);
 }
 
 /**
@@ -330,13 +506,17 @@ 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_apparmor[0].dentry);
 	if (error)
 		goto error;
 
diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h
index 7ea4769..d55954d 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,12 @@ extern const struct file_operations aa_fs_seq_file_ops;
 
 extern void __init aa_destroy_aafs(void);
 
+struct aa_profile;
+struct aa_namespace;
+
+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);
+
 #endif /* __AA_APPARMORFS_H */
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h
index bda4569..1cdc73b 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[3];
 };
 
 /* 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
+ * @sidstr: hex string representation of the sid
+ *
  * 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,8 +201,12 @@ struct aa_profile {
 	struct aa_file_rules file;
 	struct aa_caps caps;
 	struct aa_rlimit rlimits;
+
+	struct dentry *dents[4];
+	char sidstr[9];
 };
 
+
 extern struct aa_namespace *root_ns;
 extern enum profile_mode aa_g_profile_mode;
 
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
index cf5fd22..1e53048 100644
--- a/security/apparmor/policy.c
+++ b/security/apparmor/policy.c
@@ -293,6 +293,7 @@ static struct aa_namespace *alloc_namespace(const char *prefix,
 		goto fail_unconfined;
 
 	ns->unconfined->sid = aa_alloc_sid();
+	sprintf(ns->unconfined->sidstr, "%x", ns->unconfined->sid);
 	ns->unconfined->flags = PFLAG_UNCONFINED | PFLAG_IX_ON_NAME_ERROR |
 	    PFLAG_IMMUTABLE;
 
@@ -421,6 +422,13 @@ static struct aa_namespace *aa_prepare_namespace(const char *name)
 			list_add(&new_ns->base.list, &root->sub_ns);
 			/* add list ref */
 			ns = aa_get_namespace(new_ns);
+
+			/* Fixme: deal with failure */
+			if (__aa_fs_namespace_mkdir(new_ns,
+						    new_ns->parent->dents[1]))
+				AA_ERROR("Added namespace %s but failed to "
+					 "add interface files.\n",
+					 new_ns->base.name);
 		} else {
 			/* raced so free the new one */
 			free_namespace(new_ns);
@@ -498,6 +506,7 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new)
 	new->parent = aa_get_profile(old->parent);
 	new->ns = aa_get_namespace(old->ns);
 	new->sid = old->sid;
+	strcpy(new->sidstr, old->sidstr);
 	__list_add_profile(&policy->profiles, new);
 	/* inherit children */
 	list_for_each_entry_safe(child, tmp, &old->base.profiles, base.list) {
@@ -691,6 +700,7 @@ struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat)
 		goto fail;
 
 	profile->sid = sid;
+	sprintf(profile->sidstr, "%x", sid);
 	profile->mode = APPARMOR_COMPLAIN;
 	profile->flags = PFLAG_NULL;
 	if (hat)
@@ -951,6 +961,7 @@ static void __add_new_profile(struct aa_namespace *ns, struct aa_policy *policy,
 	__list_add_profile(&policy->profiles, profile);
 	/* released on free_profile */
 	profile->sid = aa_alloc_sid();
+	sprintf(profile->sidstr, "%x", profile->sid);
 	profile->ns = aa_get_namespace(ns);
 }
 
@@ -1086,8 +1097,10 @@ audit:
 	error = audit_policy(op, GFP_ATOMIC, name, info, error);
 
 	if (!error) {
-		if (rename_profile)
+		if (rename_profile) {
 			__replace_profile(rename_profile, new_profile);
+			__aa_fs_profile_rmdir(rename_profile);
+		}
 		if (old_profile) {
 			/* when there are both rename and old profiles
 			 * inherit old profiles sid
@@ -1095,9 +1108,22 @@ audit:
 			if (rename_profile)
 				aa_free_sid(new_profile->sid);
 			__replace_profile(old_profile, new_profile);
+			__aa_fs_profile_rmdir(old_profile);
 		}
 		if (!(old_profile || rename_profile))
 			__add_new_profile(ns, policy, new_profile);
+
+		/* FIXME: currently can fail, but old policy is replaced with
+		 * new. We do not want to fail the replacement at this
+		 * point. Problem can't create files & dirs that already
+		 * exist so maybe transfer them?
+		 */
+		if (__aa_fs_profile_mkdir(new_profile,
+					  new_profile->parent ?
+					  new_profile->parent->dents[1] :
+					  new_profile->ns->dents[1]))
+			AA_ERROR("added profile %s but failed to add "
+				 "interface files.\n", new_profile->base.name);
 	}
 	write_unlock(&ns->lock);
 
@@ -1162,6 +1188,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 */
@@ -1174,6 +1201,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.9.5




More information about the AppArmor mailing list