|
@@ -443,8 +443,7 @@ out:
|
|
|
* mount options, or whatever.
|
|
|
*/
|
|
|
static int selinux_get_mnt_opts(const struct super_block *sb,
|
|
|
- char ***mount_options, int **mnt_opts_flags,
|
|
|
- int *num_opts)
|
|
|
+ struct security_mnt_opts *opts)
|
|
|
{
|
|
|
int rc = 0, i;
|
|
|
struct superblock_security_struct *sbsec = sb->s_security;
|
|
@@ -452,9 +451,7 @@ static int selinux_get_mnt_opts(const struct super_block *sb,
|
|
|
u32 len;
|
|
|
char tmp;
|
|
|
|
|
|
- *num_opts = 0;
|
|
|
- *mount_options = NULL;
|
|
|
- *mnt_opts_flags = NULL;
|
|
|
+ security_init_mnt_opts(opts);
|
|
|
|
|
|
if (!sbsec->initialized)
|
|
|
return -EINVAL;
|
|
@@ -470,18 +467,18 @@ static int selinux_get_mnt_opts(const struct super_block *sb,
|
|
|
/* count the number of mount options for this sb */
|
|
|
for (i = 0; i < 8; i++) {
|
|
|
if (tmp & 0x01)
|
|
|
- (*num_opts)++;
|
|
|
+ opts->num_mnt_opts++;
|
|
|
tmp >>= 1;
|
|
|
}
|
|
|
|
|
|
- *mount_options = kcalloc(*num_opts, sizeof(char *), GFP_ATOMIC);
|
|
|
- if (!*mount_options) {
|
|
|
+ opts->mnt_opts = kcalloc(opts->num_mnt_opts, sizeof(char *), GFP_ATOMIC);
|
|
|
+ if (!opts->mnt_opts) {
|
|
|
rc = -ENOMEM;
|
|
|
goto out_free;
|
|
|
}
|
|
|
|
|
|
- *mnt_opts_flags = kcalloc(*num_opts, sizeof(int), GFP_ATOMIC);
|
|
|
- if (!*mnt_opts_flags) {
|
|
|
+ opts->mnt_opts_flags = kcalloc(opts->num_mnt_opts, sizeof(int), GFP_ATOMIC);
|
|
|
+ if (!opts->mnt_opts_flags) {
|
|
|
rc = -ENOMEM;
|
|
|
goto out_free;
|
|
|
}
|
|
@@ -491,22 +488,22 @@ static int selinux_get_mnt_opts(const struct super_block *sb,
|
|
|
rc = security_sid_to_context(sbsec->sid, &context, &len);
|
|
|
if (rc)
|
|
|
goto out_free;
|
|
|
- (*mount_options)[i] = context;
|
|
|
- (*mnt_opts_flags)[i++] = FSCONTEXT_MNT;
|
|
|
+ opts->mnt_opts[i] = context;
|
|
|
+ opts->mnt_opts_flags[i++] = FSCONTEXT_MNT;
|
|
|
}
|
|
|
if (sbsec->flags & CONTEXT_MNT) {
|
|
|
rc = security_sid_to_context(sbsec->mntpoint_sid, &context, &len);
|
|
|
if (rc)
|
|
|
goto out_free;
|
|
|
- (*mount_options)[i] = context;
|
|
|
- (*mnt_opts_flags)[i++] = CONTEXT_MNT;
|
|
|
+ opts->mnt_opts[i] = context;
|
|
|
+ opts->mnt_opts_flags[i++] = CONTEXT_MNT;
|
|
|
}
|
|
|
if (sbsec->flags & DEFCONTEXT_MNT) {
|
|
|
rc = security_sid_to_context(sbsec->def_sid, &context, &len);
|
|
|
if (rc)
|
|
|
goto out_free;
|
|
|
- (*mount_options)[i] = context;
|
|
|
- (*mnt_opts_flags)[i++] = DEFCONTEXT_MNT;
|
|
|
+ opts->mnt_opts[i] = context;
|
|
|
+ opts->mnt_opts_flags[i++] = DEFCONTEXT_MNT;
|
|
|
}
|
|
|
if (sbsec->flags & ROOTCONTEXT_MNT) {
|
|
|
struct inode *root = sbsec->sb->s_root->d_inode;
|
|
@@ -515,24 +512,16 @@ static int selinux_get_mnt_opts(const struct super_block *sb,
|
|
|
rc = security_sid_to_context(isec->sid, &context, &len);
|
|
|
if (rc)
|
|
|
goto out_free;
|
|
|
- (*mount_options)[i] = context;
|
|
|
- (*mnt_opts_flags)[i++] = ROOTCONTEXT_MNT;
|
|
|
+ opts->mnt_opts[i] = context;
|
|
|
+ opts->mnt_opts_flags[i++] = ROOTCONTEXT_MNT;
|
|
|
}
|
|
|
|
|
|
- BUG_ON(i != *num_opts);
|
|
|
+ BUG_ON(i != opts->num_mnt_opts);
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
out_free:
|
|
|
- /* don't leak context string if security_sid_to_context had an error */
|
|
|
- if (*mount_options && i)
|
|
|
- for (; i > 0; i--)
|
|
|
- kfree((*mount_options)[i-1]);
|
|
|
- kfree(*mount_options);
|
|
|
- *mount_options = NULL;
|
|
|
- kfree(*mnt_opts_flags);
|
|
|
- *mnt_opts_flags = NULL;
|
|
|
- *num_opts = 0;
|
|
|
+ security_free_mnt_opts(opts);
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
@@ -553,12 +542,13 @@ static int bad_option(struct superblock_security_struct *sbsec, char flag,
|
|
|
return 1;
|
|
|
return 0;
|
|
|
}
|
|
|
+
|
|
|
/*
|
|
|
* Allow filesystems with binary mount data to explicitly set mount point
|
|
|
* labeling information.
|
|
|
*/
|
|
|
-static int selinux_set_mnt_opts(struct super_block *sb, char **mount_options,
|
|
|
- int *flags, int num_opts)
|
|
|
+static int selinux_set_mnt_opts(struct super_block *sb,
|
|
|
+ struct security_mnt_opts *opts)
|
|
|
{
|
|
|
int rc = 0, i;
|
|
|
struct task_security_struct *tsec = current->security;
|
|
@@ -568,6 +558,9 @@ static int selinux_set_mnt_opts(struct super_block *sb, char **mount_options,
|
|
|
struct inode_security_struct *root_isec = inode->i_security;
|
|
|
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;
|
|
|
|
|
|
mutex_lock(&sbsec->lock);
|
|
|
|
|
@@ -588,6 +581,21 @@ static int selinux_set_mnt_opts(struct super_block *sb, char **mount_options,
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
|
+ /*
|
|
|
+ * Binary mount data FS will come through this function twice. Once
|
|
|
+ * from an explicit call and once from the generic calls from the vfs.
|
|
|
+ * Since the generic VFS calls will not contain any security mount data
|
|
|
+ * we need to skip the double mount verification.
|
|
|
+ *
|
|
|
+ * This does open a hole in which we will not notice if the first
|
|
|
+ * mount using this sb set explict options and a second mount using
|
|
|
+ * this sb does not set any security options. (The first options
|
|
|
+ * will be used for both mounts)
|
|
|
+ */
|
|
|
+ if (sbsec->initialized && (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)
|
|
|
+ && (num_opts == 0))
|
|
|
+ goto out;
|
|
|
+
|
|
|
/*
|
|
|
* parse the mount options, check if they are valid sids.
|
|
|
* also check if someone is trying to mount the same sb more
|
|
@@ -792,43 +800,14 @@ static void selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
|
|
|
mutex_unlock(&newsbsec->lock);
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- * string mount options parsing and call set the sbsec
|
|
|
- */
|
|
|
-static int superblock_doinit(struct super_block *sb, void *data)
|
|
|
+int selinux_parse_opts_str(char *options, struct security_mnt_opts *opts)
|
|
|
{
|
|
|
+ char *p;
|
|
|
char *context = NULL, *defcontext = NULL;
|
|
|
char *fscontext = NULL, *rootcontext = NULL;
|
|
|
- int rc = 0;
|
|
|
- char *p, *options = data;
|
|
|
- /* selinux only know about a fixed number of mount options */
|
|
|
- char *mnt_opts[NUM_SEL_MNT_OPTS];
|
|
|
- int mnt_opts_flags[NUM_SEL_MNT_OPTS], num_mnt_opts = 0;
|
|
|
-
|
|
|
- if (!data)
|
|
|
- goto out;
|
|
|
+ int rc, num_mnt_opts = 0;
|
|
|
|
|
|
- /* with the nfs patch this will become a goto out; */
|
|
|
- if (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA) {
|
|
|
- const char *name = sb->s_type->name;
|
|
|
- /* NFS we understand. */
|
|
|
- if (!strcmp(name, "nfs")) {
|
|
|
- struct nfs_mount_data *d = data;
|
|
|
-
|
|
|
- if (d->version != NFS_MOUNT_VERSION)
|
|
|
- goto out;
|
|
|
-
|
|
|
- if (d->context[0]) {
|
|
|
- context = kstrdup(d->context, GFP_KERNEL);
|
|
|
- if (!context) {
|
|
|
- rc = -ENOMEM;
|
|
|
- goto out;
|
|
|
- }
|
|
|
- }
|
|
|
- goto build_flags;
|
|
|
- } else
|
|
|
- goto out;
|
|
|
- }
|
|
|
+ opts->num_mnt_opts = 0;
|
|
|
|
|
|
/* Standard string-based options. */
|
|
|
while ((p = strsep(&options, "|")) != NULL) {
|
|
@@ -901,26 +880,37 @@ static int superblock_doinit(struct super_block *sb, void *data)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-build_flags:
|
|
|
+ rc = -ENOMEM;
|
|
|
+ opts->mnt_opts = kcalloc(NUM_SEL_MNT_OPTS, sizeof(char *), GFP_ATOMIC);
|
|
|
+ if (!opts->mnt_opts)
|
|
|
+ goto out_err;
|
|
|
+
|
|
|
+ opts->mnt_opts_flags = kcalloc(NUM_SEL_MNT_OPTS, sizeof(int), GFP_ATOMIC);
|
|
|
+ if (!opts->mnt_opts_flags) {
|
|
|
+ kfree(opts->mnt_opts);
|
|
|
+ goto out_err;
|
|
|
+ }
|
|
|
+
|
|
|
if (fscontext) {
|
|
|
- mnt_opts[num_mnt_opts] = fscontext;
|
|
|
- mnt_opts_flags[num_mnt_opts++] = FSCONTEXT_MNT;
|
|
|
+ opts->mnt_opts[num_mnt_opts] = fscontext;
|
|
|
+ opts->mnt_opts_flags[num_mnt_opts++] = FSCONTEXT_MNT;
|
|
|
}
|
|
|
if (context) {
|
|
|
- mnt_opts[num_mnt_opts] = context;
|
|
|
- mnt_opts_flags[num_mnt_opts++] = CONTEXT_MNT;
|
|
|
+ opts->mnt_opts[num_mnt_opts] = context;
|
|
|
+ opts->mnt_opts_flags[num_mnt_opts++] = CONTEXT_MNT;
|
|
|
}
|
|
|
if (rootcontext) {
|
|
|
- mnt_opts[num_mnt_opts] = rootcontext;
|
|
|
- mnt_opts_flags[num_mnt_opts++] = ROOTCONTEXT_MNT;
|
|
|
+ opts->mnt_opts[num_mnt_opts] = rootcontext;
|
|
|
+ opts->mnt_opts_flags[num_mnt_opts++] = ROOTCONTEXT_MNT;
|
|
|
}
|
|
|
if (defcontext) {
|
|
|
- mnt_opts[num_mnt_opts] = defcontext;
|
|
|
- mnt_opts_flags[num_mnt_opts++] = DEFCONTEXT_MNT;
|
|
|
+ opts->mnt_opts[num_mnt_opts] = defcontext;
|
|
|
+ opts->mnt_opts_flags[num_mnt_opts++] = DEFCONTEXT_MNT;
|
|
|
}
|
|
|
|
|
|
-out:
|
|
|
- rc = selinux_set_mnt_opts(sb, mnt_opts, mnt_opts_flags, num_mnt_opts);
|
|
|
+ opts->num_mnt_opts = num_mnt_opts;
|
|
|
+ return 0;
|
|
|
+
|
|
|
out_err:
|
|
|
kfree(context);
|
|
|
kfree(defcontext);
|
|
@@ -928,6 +918,33 @@ out_err:
|
|
|
kfree(rootcontext);
|
|
|
return rc;
|
|
|
}
|
|
|
+/*
|
|
|
+ * string mount options parsing and call set the sbsec
|
|
|
+ */
|
|
|
+static int superblock_doinit(struct super_block *sb, void *data)
|
|
|
+{
|
|
|
+ int rc = 0;
|
|
|
+ char *options = data;
|
|
|
+ struct security_mnt_opts opts;
|
|
|
+
|
|
|
+ security_init_mnt_opts(&opts);
|
|
|
+
|
|
|
+ if (!data)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ BUG_ON(sb->s_type->fs_flags & FS_BINARY_MOUNTDATA);
|
|
|
+
|
|
|
+ rc = selinux_parse_opts_str(options, &opts);
|
|
|
+ if (rc)
|
|
|
+ goto out_err;
|
|
|
+
|
|
|
+out:
|
|
|
+ rc = selinux_set_mnt_opts(sb, &opts);
|
|
|
+
|
|
|
+out_err:
|
|
|
+ security_free_mnt_opts(&opts);
|
|
|
+ return rc;
|
|
|
+}
|
|
|
|
|
|
static inline u16 inode_mode_to_security_class(umode_t mode)
|
|
|
{
|
|
@@ -2253,7 +2270,7 @@ static inline void take_selinux_option(char **to, char *from, int *first,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static int selinux_sb_copy_data(struct file_system_type *type, void *orig, void *copy)
|
|
|
+static int selinux_sb_copy_data(char *orig, char *copy)
|
|
|
{
|
|
|
int fnosec, fsec, rc = 0;
|
|
|
char *in_save, *in_curr, *in_end;
|
|
@@ -2263,12 +2280,6 @@ static int selinux_sb_copy_data(struct file_system_type *type, void *orig, void
|
|
|
in_curr = orig;
|
|
|
sec_curr = copy;
|
|
|
|
|
|
- /* Binary mount data: just copy */
|
|
|
- if (type->fs_flags & FS_BINARY_MOUNTDATA) {
|
|
|
- copy_page(sec_curr, in_curr);
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
nosec = (char *)get_zeroed_page(GFP_KERNEL);
|
|
|
if (!nosec) {
|
|
|
rc = -ENOMEM;
|
|
@@ -5251,6 +5262,8 @@ static struct security_operations selinux_ops = {
|
|
|
.sb_get_mnt_opts = selinux_get_mnt_opts,
|
|
|
.sb_set_mnt_opts = selinux_set_mnt_opts,
|
|
|
.sb_clone_mnt_opts = selinux_sb_clone_mnt_opts,
|
|
|
+ .sb_parse_opts_str = selinux_parse_opts_str,
|
|
|
+
|
|
|
|
|
|
.inode_alloc_security = selinux_inode_alloc_security,
|
|
|
.inode_free_security = selinux_inode_free_security,
|