LSM: hide struct security_mnt_opts from any generic code

Keep void * instead, allocate on demand (in parse_str_opts, at the
moment).  Eventually both selinux and smack will be better off
with private structures with several strings in those, rather than
this "counter and two pointers to dynamically allocated arrays"
ugliness.  This commit allows to do that at leisure, without
disrupting anything outside of given module.

Changes:
	* instead of struct security_mnt_opt use an opaque pointer
initialized to NULL.
	* security_sb_eat_lsm_opts(), security_sb_parse_opts_str() and
security_free_mnt_opts() take it as var argument (i.e. as void **);
call sites are unchanged.
	* security_sb_set_mnt_opts() and security_sb_remount() take
it by value (i.e. as void *).
	* new method: ->sb_free_mnt_opts().  Takes void *, does
whatever freeing that needs to be done.
	* ->sb_set_mnt_opts() and ->sb_remount() might get NULL as
mnt_opts argument, meaning "empty".

Reviewed-by: David Howells <dhowells@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 11cf2feb..caf7ca7 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -433,6 +433,19 @@
 	kfree(sbsec);
 }
 
+static void selinux_free_mnt_opts(void *mnt_opts)
+{
+	struct security_mnt_opts *opts = mnt_opts;
+	int i;
+
+	if (opts->mnt_opts)
+		for (i = 0; i < opts->num_mnt_opts; i++)
+			kfree(opts->mnt_opts[i]);
+	kfree(opts->mnt_opts);
+	kfree(opts->mnt_opts_flags);
+	kfree(opts);
+}
+
 static inline int inode_doinit(struct inode *inode)
 {
 	return inode_doinit_with_dentry(inode, NULL);
@@ -616,7 +629,7 @@
  * labeling information.
  */
 static int selinux_set_mnt_opts(struct super_block *sb,
-				struct security_mnt_opts *opts,
+				void *mnt_opts,
 				unsigned long kern_flags,
 				unsigned long *set_kern_flags)
 {
@@ -628,9 +641,10 @@
 	struct inode_security_struct *root_isec;
 	u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0;
 	u32 defcontext_sid = 0;
-	char **mount_options = opts->mnt_opts;
-	int *flags = opts->mnt_opts_flags;
-	int num_opts = opts->num_mnt_opts;
+	struct security_mnt_opts *opts = mnt_opts;
+	char **mount_options = opts ? opts->mnt_opts : NULL;
+	int *flags = opts ? opts->mnt_opts_flags : NULL;
+	int num_opts = opts ? opts->num_mnt_opts : 0;
 
 	mutex_lock(&sbsec->lock);
 
@@ -982,12 +996,20 @@
 }
 
 static int selinux_parse_opts_str(char *options,
-				  struct security_mnt_opts *opts)
+				  void **mnt_opts)
 {
 	char *p;
 	char *context = NULL, *defcontext = NULL;
 	char *fscontext = NULL, *rootcontext = NULL;
 	int rc, num_mnt_opts = 0;
+	struct security_mnt_opts *opts = *mnt_opts;
+
+	if (!opts) {
+		opts = kzalloc(sizeof(struct security_mnt_opts), GFP_KERNEL);
+		*mnt_opts = opts;
+		if (!opts)
+			return -ENOMEM;
+	}
 
 	opts->num_mnt_opts = 0;
 
@@ -1094,7 +1116,7 @@
 	return 0;
 
 out_err:
-	security_free_mnt_opts(opts);
+	security_free_mnt_opts(mnt_opts);
 	kfree(context);
 	kfree(defcontext);
 	kfree(fscontext);
@@ -2714,7 +2736,7 @@
 	return rc;
 }
 
-static int selinux_sb_eat_lsm_opts(char *options, struct security_mnt_opts *opts)
+static int selinux_sb_eat_lsm_opts(char *options, void **mnt_opts)
 {
 	char *s = (char *)get_zeroed_page(GFP_KERNEL);
 	int err;
@@ -2723,14 +2745,14 @@
 		return -ENOMEM;
 	err = selinux_sb_copy_data(options, s);
 	if (!err)
-		err = selinux_parse_opts_str(s, opts);
+		err = selinux_parse_opts_str(s, mnt_opts);
 	free_page((unsigned long)s);
 	return err;
 }
 
-static int selinux_sb_remount(struct super_block *sb,
-			      struct security_mnt_opts *opts)
+static int selinux_sb_remount(struct super_block *sb, void *mnt_opts)
 {
+	struct security_mnt_opts *opts = mnt_opts;
 	int i, *flags;
 	char **mount_options;
 	struct superblock_security_struct *sbsec = sb->s_security;
@@ -2738,6 +2760,9 @@
 	if (!(sbsec->flags & SE_SBINITIALIZED))
 		return 0;
 
+	if (!opts)
+		return 0;
+
 	mount_options = opts->mnt_opts;
 	flags = opts->mnt_opts_flags;
 
@@ -6782,6 +6807,7 @@
 	LSM_HOOK_INIT(sb_alloc_security, selinux_sb_alloc_security),
 	LSM_HOOK_INIT(sb_free_security, selinux_sb_free_security),
 	LSM_HOOK_INIT(sb_eat_lsm_opts, selinux_sb_eat_lsm_opts),
+	LSM_HOOK_INIT(sb_free_mnt_opts, selinux_free_mnt_opts),
 	LSM_HOOK_INIT(sb_remount, selinux_sb_remount),
 	LSM_HOOK_INIT(sb_kern_mount, selinux_sb_kern_mount),
 	LSM_HOOK_INIT(sb_show_options, selinux_sb_show_options),
@@ -7051,11 +7077,7 @@
 
 static void delayed_superblock_init(struct super_block *sb, void *unused)
 {
-	struct security_mnt_opts opts;
-
-	security_init_mnt_opts(&opts);
-	selinux_set_mnt_opts(sb, &opts, 0, NULL);
-	security_free_mnt_opts(&opts);
+	selinux_set_mnt_opts(sb, NULL, 0, NULL);
 }
 
 void selinux_complete_init(void)