Blob Blame History Raw
From 45f06af64e6d81ed917df095f06baff6b4a80404 Mon Sep 17 00:00:00 2001
From: John Johansen <john.johansen@canonical.com>
Date: Fri, 26 May 2017 16:45:48 -0700
Subject: [PATCH 22/65] apparmor: add mkdir/rmdir interface to manage policy
 namespaces
Git-commit: 4ae47f33354a96efb4e4231dec0d72a586b3921c
Patch-mainline: v4.13-rc1
References: FATE#323500

When setting up namespaces for containers its easier for them to use
an fs interface to create the namespace for the containers
policy. Allow mkdir/rmdir under the policy/namespaces/ dir to be used
to create and remove namespaces.

BugLink: http://bugs.launchpad.net/bugs/1611078

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Goldwyn Rodrigues <rgoldwyn@suse.com>
---
 security/apparmor/apparmorfs.c | 95 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 94 insertions(+), 1 deletion(-)

diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index 8c413333726b..7f3049300ce3 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -1322,6 +1322,97 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
 	return error;
 }
 
+static int ns_mkdir_op(struct inode *dir, struct dentry *dentry, umode_t mode)
+{
+	struct aa_ns *ns, *parent;
+	/* TODO: improve permission check */
+	struct aa_profile *profile = aa_current_profile();
+	int error = aa_may_manage_policy(profile, NULL, AA_MAY_LOAD_POLICY);
+
+	if (error)
+		return error;
+
+	parent = aa_get_ns(dir->i_private);
+	AA_BUG(d_inode(ns_subns_dir(parent)) != dir);
+
+	/* we have to unlock and then relock to get locking order right
+	 * for pin_fs
+	 */
+	inode_unlock(dir);
+	error = simple_pin_fs(&aafs_ops, &aafs_mnt, &aafs_count);
+	mutex_lock(&parent->lock);
+	inode_lock_nested(dir, I_MUTEX_PARENT);
+	if (error)
+		goto out;
+
+	error = __aafs_setup_d_inode(dir, dentry, mode | S_IFDIR,  NULL,
+				     NULL, NULL, NULL);
+	if (error)
+		goto out_pin;
+
+	ns = __aa_find_or_create_ns(parent, READ_ONCE(dentry->d_name.name),
+				    dentry);
+	if (IS_ERR(ns)) {
+		error = PTR_ERR(ns);
+		ns = NULL;
+	}
+
+	aa_put_ns(ns);		/* list ref remains */
+out_pin:
+	if (error)
+		simple_release_fs(&aafs_mnt, &aafs_count);
+out:
+	mutex_unlock(&parent->lock);
+	aa_put_ns(parent);
+
+	return error;
+}
+
+static int ns_rmdir_op(struct inode *dir, struct dentry *dentry)
+{
+	struct aa_ns *ns, *parent;
+	/* TODO: improve permission check */
+	struct aa_profile *profile = aa_current_profile();
+	int error = aa_may_manage_policy(profile, NULL, AA_MAY_LOAD_POLICY);
+
+	if (error)
+		return error;
+
+	parent = aa_get_ns(dir->i_private);
+	/* rmdir calls the generic securityfs functions to remove files
+	 * from the apparmor dir. It is up to the apparmor ns locking
+	 * to avoid races.
+	 */
+	inode_unlock(dir);
+	inode_unlock(dentry->d_inode);
+
+	mutex_lock(&parent->lock);
+	ns = aa_get_ns(__aa_findn_ns(&parent->sub_ns, dentry->d_name.name,
+				     dentry->d_name.len));
+	if (!ns) {
+		error = -ENOENT;
+		goto out;
+	}
+	AA_BUG(ns_dir(ns) != dentry);
+
+	__aa_remove_ns(ns);
+	aa_put_ns(ns);
+
+out:
+	mutex_unlock(&parent->lock);
+	inode_lock_nested(dir, I_MUTEX_PARENT);
+	inode_lock(dentry->d_inode);
+	aa_put_ns(parent);
+
+	return error;
+}
+
+static const struct inode_operations ns_dir_inode_operations = {
+	.lookup		= simple_lookup,
+	.mkdir		= ns_mkdir_op,
+	.rmdir		= ns_rmdir_op,
+};
+
 static void __aa_fs_list_remove_rawdata(struct aa_ns *ns)
 {
 	struct aa_loaddata *ent, *tmp;
@@ -1429,7 +1520,9 @@ static int __aafs_ns_mkdir_entries(struct aa_ns *ns, struct dentry *dir)
 	aa_get_ns(ns);
 	ns_subremove(ns) = dent;
 
-	dent = aafs_create_dir("namespaces", dir);
+	  /* use create_dentry so we can supply private data */
+	dent = aafs_create("namespaces", S_IFDIR | 0755, dir, ns, NULL, NULL,
+			   &ns_dir_inode_operations);
 	if (IS_ERR(dent))
 		return PTR_ERR(dent);
 	aa_get_ns(ns);
-- 
2.12.3