LSM: Pass -o remount options to the LSM
authorEric Paris <eparis@redhat.com>
Thu, 3 Mar 2011 21:09:14 +0000 (16:09 -0500)
committerEric Paris <eparis@redhat.com>
Thu, 3 Mar 2011 21:12:27 +0000 (16:12 -0500)
The VFS mount code passes the mount options to the LSM.  The LSM will remove
options it understands from the data and the VFS will then pass the remaining
options onto the underlying filesystem.  This is how options like the
SELinux context= work.  The problem comes in that -o remount never calls
into LSM code.  So if you include an LSM specific option it will get passed
to the filesystem and will cause the remount to fail.  An example of where
this is a problem is the 'seclabel' option.  The SELinux LSM hook will
print this word in /proc/mounts if the filesystem is being labeled using
xattrs.  If you pass this word on mount it will be silently stripped and
ignored.  But if you pass this word on remount the LSM never gets called
and it will be passed to the FS.  The FS doesn't know what seclabel means
and thus should fail the mount.  For example an ext3 fs mounted over loop

# mount -o loop /tmp/fs /mnt/tmp
# cat /proc/mounts | grep /mnt/tmp
/dev/loop0 /mnt/tmp ext3 rw,seclabel,relatime,errors=continue,barrier=0,data=ordered 0 0
# mount -o remount /mnt/tmp
mount: /mnt/tmp not mounted already, or bad option
# dmesg
EXT3-fs (loop0): error: unrecognized mount option "seclabel" or missing value

This patch passes the remount mount options to an new LSM hook.

Signed-off-by: Eric Paris <eparis@redhat.com>
Reviewed-by: James Morris <jmorris@namei.org>
fs/namespace.c
include/linux/security.h
security/capability.c
security/security.c

index 3ddfd9046c449199a8f75ffcaf54761dde37fa73..1b3f2ac59c5e9bf94131746fd82f1f734c04f60d 100644 (file)
@@ -1800,6 +1800,10 @@ static int do_remount(struct path *path, int flags, int mnt_flags,
        if (path->dentry != path->mnt->mnt_root)
                return -EINVAL;
 
+       err = security_sb_remount(sb, data);
+       if (err)
+               return err;
+
        down_write(&sb->s_umount);
        if (flags & MS_BIND)
                err = change_mount_flags(path->mnt, flags);
index 14167f2eb35a93737d51702a7c5b44540f00c90a..d11ac43ecc49b2ea1e0cd77b4b8e46cc855a6bbb 100644 (file)
@@ -268,6 +268,12 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *     @orig the original mount data copied from userspace.
  *     @copy copied data which will be passed to the security module.
  *     Returns 0 if the copy was successful.
+ * @sb_remount:
+ *     Extracts security system specifc mount options and verifys no changes
+ *     are being made to those options.
+ *     @sb superblock being remounted
+ *     @data contains the filesystem-specific data.
+ *     Return 0 if permission is granted.
  * @sb_umount:
  *     Check permission before the @mnt file system is unmounted.
  *     @mnt contains the mounted file system.
@@ -1394,6 +1400,7 @@ struct security_operations {
        int (*sb_alloc_security) (struct super_block *sb);
        void (*sb_free_security) (struct super_block *sb);
        int (*sb_copy_data) (char *orig, char *copy);
+       int (*sb_remount) (struct super_block *sb, void *data);
        int (*sb_kern_mount) (struct super_block *sb, int flags, void *data);
        int (*sb_show_options) (struct seq_file *m, struct super_block *sb);
        int (*sb_statfs) (struct dentry *dentry);
@@ -1676,6 +1683,7 @@ int security_bprm_secureexec(struct linux_binprm *bprm);
 int security_sb_alloc(struct super_block *sb);
 void security_sb_free(struct super_block *sb);
 int security_sb_copy_data(char *orig, char *copy);
+int security_sb_remount(struct super_block *sb, void *data);
 int security_sb_kern_mount(struct super_block *sb, int flags, void *data);
 int security_sb_show_options(struct seq_file *m, struct super_block *sb);
 int security_sb_statfs(struct dentry *dentry);
@@ -1955,6 +1963,11 @@ static inline int security_sb_copy_data(char *orig, char *copy)
        return 0;
 }
 
+static inline int security_sb_remount(struct super_block *sb, void *data)
+{
+       return 0;
+}
+
 static inline int security_sb_kern_mount(struct super_block *sb, int flags, void *data)
 {
        return 0;
index 85b67c8632dfd4e42a2a22421c78c5676137e95b..ab3d807accc3b43ccd26b13e881d1e6ce51a8290 100644 (file)
@@ -54,6 +54,11 @@ static int cap_sb_copy_data(char *orig, char *copy)
        return 0;
 }
 
+static int cap_sb_remount(struct super_block *sb, void *data)
+{
+       return 0;
+}
+
 static int cap_sb_kern_mount(struct super_block *sb, int flags, void *data)
 {
        return 0;
@@ -887,6 +892,7 @@ void __init security_fixup_ops(struct security_operations *ops)
        set_to_cap_if_null(ops, sb_alloc_security);
        set_to_cap_if_null(ops, sb_free_security);
        set_to_cap_if_null(ops, sb_copy_data);
+       set_to_cap_if_null(ops, sb_remount);
        set_to_cap_if_null(ops, sb_kern_mount);
        set_to_cap_if_null(ops, sb_show_options);
        set_to_cap_if_null(ops, sb_statfs);
index 8f28685ee0d9cdb7e5a4bc4aa58e43638d4ee6bb..b1d6134548bc249ce33150f55deb245dee5e23eb 100644 (file)
@@ -267,6 +267,11 @@ int security_sb_copy_data(char *orig, char *copy)
 }
 EXPORT_SYMBOL(security_sb_copy_data);
 
+int security_sb_remount(struct super_block *sb, void *data)
+{
+       return security_ops->sb_remount(sb, data);
+}
+
 int security_sb_kern_mount(struct super_block *sb, int flags, void *data)
 {
        return security_ops->sb_kern_mount(sb, flags, data);