Merge branch 'mount.part1' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull vfs mount API prep from Al Viro: "Mount API prereqs. Mostly that's LSM mount options cleanups. There are several minor fixes in there, but nothing earth-shattering (leaks on failure exits, mostly)" * 'mount.part1' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: (27 commits) mount_fs: suppress MAC on MS_SUBMOUNT as well as MS_KERNMOUNT smack: rewrite smack_sb_eat_lsm_opts() smack: get rid of match_token() smack: take the guts of smack_parse_opts_str() into a new helper LSM: new method: ->sb_add_mnt_opt() selinux: rewrite selinux_sb_eat_lsm_opts() selinux: regularize Opt_... names a bit selinux: switch away from match_token() selinux: new helper - selinux_add_opt() LSM: bury struct security_mnt_opts smack: switch to private smack_mnt_opts selinux: switch to private struct selinux_mnt_opts LSM: hide struct security_mnt_opts from any generic code selinux: kill selinux_sb_get_mnt_opts() LSM: turn sb_eat_lsm_opts() into a method nfs_remount(): don't leak, don't ignore LSM options quietly btrfs: sanitize security_mnt_opts use selinux; don't open-code a loop in sb_finish_set_opts() LSM: split ->sb_set_mnt_opts() out of ->sb_kern_mount() new helper: security_sb_eat_lsm_opts() ...
This commit is contained in:
commit
505b050fdf
|
@ -19,6 +19,7 @@
|
|||
#include <linux/of_fdt.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/cache.h>
|
||||
#include <uapi/linux/mount.h>
|
||||
#include <asm/sections.h>
|
||||
#include <asm/arcregs.h>
|
||||
#include <asm/tlb.h>
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <linux/root_dev.h>
|
||||
#include <linux/screen_info.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <uapi/linux/mount.h>
|
||||
|
||||
#include <asm/setup.h>
|
||||
#include <asm/system_info.h>
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/of_fdt.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <uapi/linux/mount.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/elf.h>
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <linux/kdebug.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/start_kernel.h>
|
||||
#include <uapi/linux/mount.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/processor.h>
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/start_kernel.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <uapi/linux/mount.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/processor.h>
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
#include <linux/kvm_para.h>
|
||||
#include <linux/dma-contiguous.h>
|
||||
#include <xen/xen.h>
|
||||
#include <uapi/linux/mount.h>
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <uapi/linux/mount.h>
|
||||
#include "base.h"
|
||||
|
||||
static struct task_struct *thread;
|
||||
|
|
|
@ -1144,9 +1144,6 @@ struct btrfs_fs_info {
|
|||
struct mutex unused_bg_unpin_mutex;
|
||||
struct mutex delete_unused_bgs_mutex;
|
||||
|
||||
/* For btrfs to record security options */
|
||||
struct security_mnt_opts security_opts;
|
||||
|
||||
/*
|
||||
* Chunks that can't be freed yet (under a trim/discard operation)
|
||||
* and will be latter freed. Protected by fs_info->chunk_mutex.
|
||||
|
@ -3021,7 +3018,6 @@ static inline void free_fs_info(struct btrfs_fs_info *fs_info)
|
|||
kfree(fs_info->free_space_root);
|
||||
kfree(fs_info->super_copy);
|
||||
kfree(fs_info->super_for_commit);
|
||||
security_free_mnt_opts(&fs_info->security_opts);
|
||||
kvfree(fs_info);
|
||||
}
|
||||
|
||||
|
|
|
@ -1458,56 +1458,6 @@ out:
|
|||
return root;
|
||||
}
|
||||
|
||||
static int parse_security_options(char *orig_opts,
|
||||
struct security_mnt_opts *sec_opts)
|
||||
{
|
||||
char *secdata = NULL;
|
||||
int ret = 0;
|
||||
|
||||
secdata = alloc_secdata();
|
||||
if (!secdata)
|
||||
return -ENOMEM;
|
||||
ret = security_sb_copy_data(orig_opts, secdata);
|
||||
if (ret) {
|
||||
free_secdata(secdata);
|
||||
return ret;
|
||||
}
|
||||
ret = security_sb_parse_opts_str(secdata, sec_opts);
|
||||
free_secdata(secdata);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int setup_security_options(struct btrfs_fs_info *fs_info,
|
||||
struct super_block *sb,
|
||||
struct security_mnt_opts *sec_opts)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
* Call security_sb_set_mnt_opts() to check whether new sec_opts
|
||||
* is valid.
|
||||
*/
|
||||
ret = security_sb_set_mnt_opts(sb, sec_opts, 0, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
#ifdef CONFIG_SECURITY
|
||||
if (!fs_info->security_opts.num_mnt_opts) {
|
||||
/* first time security setup, copy sec_opts to fs_info */
|
||||
memcpy(&fs_info->security_opts, sec_opts, sizeof(*sec_opts));
|
||||
} else {
|
||||
/*
|
||||
* Since SELinux (the only one supporting security_mnt_opts)
|
||||
* does NOT support changing context during remount/mount of
|
||||
* the same sb, this must be the same or part of the same
|
||||
* security options, just free it.
|
||||
*/
|
||||
security_free_mnt_opts(sec_opts);
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find a superblock for the given device / mount point.
|
||||
*
|
||||
|
@ -1522,16 +1472,15 @@ static struct dentry *btrfs_mount_root(struct file_system_type *fs_type,
|
|||
struct btrfs_device *device = NULL;
|
||||
struct btrfs_fs_devices *fs_devices = NULL;
|
||||
struct btrfs_fs_info *fs_info = NULL;
|
||||
struct security_mnt_opts new_sec_opts;
|
||||
void *new_sec_opts = NULL;
|
||||
fmode_t mode = FMODE_READ;
|
||||
int error = 0;
|
||||
|
||||
if (!(flags & SB_RDONLY))
|
||||
mode |= FMODE_WRITE;
|
||||
|
||||
security_init_mnt_opts(&new_sec_opts);
|
||||
if (data) {
|
||||
error = parse_security_options(data, &new_sec_opts);
|
||||
error = security_sb_eat_lsm_opts(data, &new_sec_opts);
|
||||
if (error)
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
|
@ -1550,7 +1499,6 @@ static struct dentry *btrfs_mount_root(struct file_system_type *fs_type,
|
|||
|
||||
fs_info->super_copy = kzalloc(BTRFS_SUPER_INFO_SIZE, GFP_KERNEL);
|
||||
fs_info->super_for_commit = kzalloc(BTRFS_SUPER_INFO_SIZE, GFP_KERNEL);
|
||||
security_init_mnt_opts(&fs_info->security_opts);
|
||||
if (!fs_info->super_copy || !fs_info->super_for_commit) {
|
||||
error = -ENOMEM;
|
||||
goto error_fs_info;
|
||||
|
@ -1601,16 +1549,12 @@ static struct dentry *btrfs_mount_root(struct file_system_type *fs_type,
|
|||
btrfs_sb(s)->bdev_holder = fs_type;
|
||||
error = btrfs_fill_super(s, fs_devices, data);
|
||||
}
|
||||
if (!error)
|
||||
error = security_sb_set_mnt_opts(s, new_sec_opts, 0, NULL);
|
||||
security_free_mnt_opts(&new_sec_opts);
|
||||
if (error) {
|
||||
deactivate_locked_super(s);
|
||||
goto error_sec_opts;
|
||||
}
|
||||
|
||||
fs_info = btrfs_sb(s);
|
||||
error = setup_security_options(fs_info, s, &new_sec_opts);
|
||||
if (error) {
|
||||
deactivate_locked_super(s);
|
||||
goto error_sec_opts;
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
|
||||
return dget(s->s_root);
|
||||
|
@ -1779,18 +1723,14 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
|
|||
btrfs_remount_prepare(fs_info);
|
||||
|
||||
if (data) {
|
||||
struct security_mnt_opts new_sec_opts;
|
||||
void *new_sec_opts = NULL;
|
||||
|
||||
security_init_mnt_opts(&new_sec_opts);
|
||||
ret = parse_security_options(data, &new_sec_opts);
|
||||
ret = security_sb_eat_lsm_opts(data, &new_sec_opts);
|
||||
if (!ret)
|
||||
ret = security_sb_remount(sb, new_sec_opts);
|
||||
security_free_mnt_opts(&new_sec_opts);
|
||||
if (ret)
|
||||
goto restore;
|
||||
ret = setup_security_options(fs_info, sb,
|
||||
&new_sec_opts);
|
||||
if (ret) {
|
||||
security_free_mnt_opts(&new_sec_opts);
|
||||
goto restore;
|
||||
}
|
||||
}
|
||||
|
||||
ret = btrfs_parse_options(fs_info, data, *flags);
|
||||
|
|
|
@ -705,21 +705,18 @@ out:
|
|||
/*
|
||||
* Read the superblock from the OSD and fill in the fields
|
||||
*/
|
||||
static int exofs_fill_super(struct super_block *sb, void *data, int silent)
|
||||
static int exofs_fill_super(struct super_block *sb,
|
||||
struct exofs_mountopt *opts,
|
||||
struct exofs_sb_info *sbi,
|
||||
int silent)
|
||||
{
|
||||
struct inode *root;
|
||||
struct exofs_mountopt *opts = data;
|
||||
struct exofs_sb_info *sbi; /*extended info */
|
||||
struct osd_dev *od; /* Master device */
|
||||
struct exofs_fscb fscb; /*on-disk superblock info */
|
||||
struct ore_comp comp;
|
||||
unsigned table_count;
|
||||
int ret;
|
||||
|
||||
sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
|
||||
if (!sbi)
|
||||
return -ENOMEM;
|
||||
|
||||
/* use mount options to fill superblock */
|
||||
if (opts->is_osdname) {
|
||||
struct osd_dev_info odi = {.systemid_len = 0};
|
||||
|
@ -863,7 +860,9 @@ static struct dentry *exofs_mount(struct file_system_type *type,
|
|||
int flags, const char *dev_name,
|
||||
void *data)
|
||||
{
|
||||
struct super_block *s;
|
||||
struct exofs_mountopt opts;
|
||||
struct exofs_sb_info *sbi;
|
||||
int ret;
|
||||
|
||||
ret = parse_options(data, &opts);
|
||||
|
@ -872,9 +871,31 @@ static struct dentry *exofs_mount(struct file_system_type *type,
|
|||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
|
||||
if (!sbi) {
|
||||
kfree(opts.dev_name);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
s = sget(type, NULL, set_anon_super, flags, NULL);
|
||||
|
||||
if (IS_ERR(s)) {
|
||||
kfree(opts.dev_name);
|
||||
kfree(sbi);
|
||||
return ERR_CAST(s);
|
||||
}
|
||||
|
||||
if (!opts.dev_name)
|
||||
opts.dev_name = dev_name;
|
||||
return mount_nodev(type, flags, &opts, exofs_fill_super);
|
||||
|
||||
|
||||
ret = exofs_fill_super(s, &opts, sbi, flags & SB_SILENT ? 1 : 0);
|
||||
if (ret) {
|
||||
deactivate_locked_super(s);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
s->s_flags |= SB_ACTIVE;
|
||||
return dget(s->s_root);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
154
fs/namespace.c
154
fs/namespace.c
|
@ -26,6 +26,7 @@
|
|||
#include <linux/memblock.h>
|
||||
#include <linux/task_work.h>
|
||||
#include <linux/sched/task.h>
|
||||
#include <uapi/linux/mount.h>
|
||||
|
||||
#include "pnode.h"
|
||||
#include "internal.h"
|
||||
|
@ -245,13 +246,9 @@ out_free_cache:
|
|||
* mnt_want/drop_write() will _keep_ the filesystem
|
||||
* r/w.
|
||||
*/
|
||||
int __mnt_is_readonly(struct vfsmount *mnt)
|
||||
bool __mnt_is_readonly(struct vfsmount *mnt)
|
||||
{
|
||||
if (mnt->mnt_flags & MNT_READONLY)
|
||||
return 1;
|
||||
if (sb_rdonly(mnt->mnt_sb))
|
||||
return 1;
|
||||
return 0;
|
||||
return (mnt->mnt_flags & MNT_READONLY) || sb_rdonly(mnt->mnt_sb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__mnt_is_readonly);
|
||||
|
||||
|
@ -507,11 +504,12 @@ static int mnt_make_readonly(struct mount *mnt)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void __mnt_unmake_readonly(struct mount *mnt)
|
||||
static int __mnt_unmake_readonly(struct mount *mnt)
|
||||
{
|
||||
lock_mount_hash();
|
||||
mnt->mnt.mnt_flags &= ~MNT_READONLY;
|
||||
unlock_mount_hash();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sb_prepare_remount_readonly(struct super_block *sb)
|
||||
|
@ -2215,21 +2213,91 @@ out:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int change_mount_flags(struct vfsmount *mnt, int ms_flags)
|
||||
/*
|
||||
* Don't allow locked mount flags to be cleared.
|
||||
*
|
||||
* No locks need to be held here while testing the various MNT_LOCK
|
||||
* flags because those flags can never be cleared once they are set.
|
||||
*/
|
||||
static bool can_change_locked_flags(struct mount *mnt, unsigned int mnt_flags)
|
||||
{
|
||||
int error = 0;
|
||||
int readonly_request = 0;
|
||||
unsigned int fl = mnt->mnt.mnt_flags;
|
||||
|
||||
if (ms_flags & MS_RDONLY)
|
||||
readonly_request = 1;
|
||||
if (readonly_request == __mnt_is_readonly(mnt))
|
||||
if ((fl & MNT_LOCK_READONLY) &&
|
||||
!(mnt_flags & MNT_READONLY))
|
||||
return false;
|
||||
|
||||
if ((fl & MNT_LOCK_NODEV) &&
|
||||
!(mnt_flags & MNT_NODEV))
|
||||
return false;
|
||||
|
||||
if ((fl & MNT_LOCK_NOSUID) &&
|
||||
!(mnt_flags & MNT_NOSUID))
|
||||
return false;
|
||||
|
||||
if ((fl & MNT_LOCK_NOEXEC) &&
|
||||
!(mnt_flags & MNT_NOEXEC))
|
||||
return false;
|
||||
|
||||
if ((fl & MNT_LOCK_ATIME) &&
|
||||
((fl & MNT_ATIME_MASK) != (mnt_flags & MNT_ATIME_MASK)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int change_mount_ro_state(struct mount *mnt, unsigned int mnt_flags)
|
||||
{
|
||||
bool readonly_request = (mnt_flags & MNT_READONLY);
|
||||
|
||||
if (readonly_request == __mnt_is_readonly(&mnt->mnt))
|
||||
return 0;
|
||||
|
||||
if (readonly_request)
|
||||
error = mnt_make_readonly(real_mount(mnt));
|
||||
else
|
||||
__mnt_unmake_readonly(real_mount(mnt));
|
||||
return error;
|
||||
return mnt_make_readonly(mnt);
|
||||
|
||||
return __mnt_unmake_readonly(mnt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the user-settable attributes on a mount. The caller must hold
|
||||
* sb->s_umount for writing.
|
||||
*/
|
||||
static void set_mount_attributes(struct mount *mnt, unsigned int mnt_flags)
|
||||
{
|
||||
lock_mount_hash();
|
||||
mnt_flags |= mnt->mnt.mnt_flags & ~MNT_USER_SETTABLE_MASK;
|
||||
mnt->mnt.mnt_flags = mnt_flags;
|
||||
touch_mnt_namespace(mnt->mnt_ns);
|
||||
unlock_mount_hash();
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle reconfiguration of the mountpoint only without alteration of the
|
||||
* superblock it refers to. This is triggered by specifying MS_REMOUNT|MS_BIND
|
||||
* to mount(2).
|
||||
*/
|
||||
static int do_reconfigure_mnt(struct path *path, unsigned int mnt_flags)
|
||||
{
|
||||
struct super_block *sb = path->mnt->mnt_sb;
|
||||
struct mount *mnt = real_mount(path->mnt);
|
||||
int ret;
|
||||
|
||||
if (!check_mnt(mnt))
|
||||
return -EINVAL;
|
||||
|
||||
if (path->dentry != mnt->mnt.mnt_root)
|
||||
return -EINVAL;
|
||||
|
||||
if (!can_change_locked_flags(mnt, mnt_flags))
|
||||
return -EPERM;
|
||||
|
||||
down_write(&sb->s_umount);
|
||||
ret = change_mount_ro_state(mnt, mnt_flags);
|
||||
if (ret == 0)
|
||||
set_mount_attributes(mnt, mnt_flags);
|
||||
up_write(&sb->s_umount);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2243,6 +2311,7 @@ static int do_remount(struct path *path, int ms_flags, int sb_flags,
|
|||
int err;
|
||||
struct super_block *sb = path->mnt->mnt_sb;
|
||||
struct mount *mnt = real_mount(path->mnt);
|
||||
void *sec_opts = NULL;
|
||||
|
||||
if (!check_mnt(mnt))
|
||||
return -EINVAL;
|
||||
|
@ -2250,50 +2319,25 @@ static int do_remount(struct path *path, int ms_flags, int sb_flags,
|
|||
if (path->dentry != path->mnt->mnt_root)
|
||||
return -EINVAL;
|
||||
|
||||
/* Don't allow changing of locked mnt flags.
|
||||
*
|
||||
* No locks need to be held here while testing the various
|
||||
* MNT_LOCK flags because those flags can never be cleared
|
||||
* once they are set.
|
||||
*/
|
||||
if ((mnt->mnt.mnt_flags & MNT_LOCK_READONLY) &&
|
||||
!(mnt_flags & MNT_READONLY)) {
|
||||
if (!can_change_locked_flags(mnt, mnt_flags))
|
||||
return -EPERM;
|
||||
}
|
||||
if ((mnt->mnt.mnt_flags & MNT_LOCK_NODEV) &&
|
||||
!(mnt_flags & MNT_NODEV)) {
|
||||
return -EPERM;
|
||||
}
|
||||
if ((mnt->mnt.mnt_flags & MNT_LOCK_NOSUID) &&
|
||||
!(mnt_flags & MNT_NOSUID)) {
|
||||
return -EPERM;
|
||||
}
|
||||
if ((mnt->mnt.mnt_flags & MNT_LOCK_NOEXEC) &&
|
||||
!(mnt_flags & MNT_NOEXEC)) {
|
||||
return -EPERM;
|
||||
}
|
||||
if ((mnt->mnt.mnt_flags & MNT_LOCK_ATIME) &&
|
||||
((mnt->mnt.mnt_flags & MNT_ATIME_MASK) != (mnt_flags & MNT_ATIME_MASK))) {
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
err = security_sb_remount(sb, data);
|
||||
if (data && !(sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)) {
|
||||
err = security_sb_eat_lsm_opts(data, &sec_opts);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
err = security_sb_remount(sb, sec_opts);
|
||||
security_free_mnt_opts(&sec_opts);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
down_write(&sb->s_umount);
|
||||
if (ms_flags & MS_BIND)
|
||||
err = change_mount_flags(path->mnt, ms_flags);
|
||||
else if (!ns_capable(sb->s_user_ns, CAP_SYS_ADMIN))
|
||||
err = -EPERM;
|
||||
else
|
||||
if (ns_capable(sb->s_user_ns, CAP_SYS_ADMIN)) {
|
||||
err = do_remount_sb(sb, sb_flags, data, 0);
|
||||
if (!err) {
|
||||
lock_mount_hash();
|
||||
mnt_flags |= mnt->mnt.mnt_flags & ~MNT_USER_SETTABLE_MASK;
|
||||
mnt->mnt.mnt_flags = mnt_flags;
|
||||
touch_mnt_namespace(mnt->mnt_ns);
|
||||
unlock_mount_hash();
|
||||
if (!err)
|
||||
set_mount_attributes(mnt, mnt_flags);
|
||||
}
|
||||
up_write(&sb->s_umount);
|
||||
return err;
|
||||
|
@ -2788,7 +2832,9 @@ long do_mount(const char *dev_name, const char __user *dir_name,
|
|||
SB_LAZYTIME |
|
||||
SB_I_VERSION);
|
||||
|
||||
if (flags & MS_REMOUNT)
|
||||
if ((flags & (MS_REMOUNT | MS_BIND)) == (MS_REMOUNT | MS_BIND))
|
||||
retval = do_reconfigure_mnt(&path, mnt_flags);
|
||||
else if (flags & MS_REMOUNT)
|
||||
retval = do_remount(&path, flags, sb_flags, mnt_flags,
|
||||
data_page);
|
||||
else if (flags & MS_BIND)
|
||||
|
|
|
@ -123,7 +123,7 @@ struct nfs_parsed_mount_data {
|
|||
unsigned short protocol;
|
||||
} nfs_server;
|
||||
|
||||
struct security_mnt_opts lsm_opts;
|
||||
void *lsm_opts;
|
||||
struct net *net;
|
||||
};
|
||||
|
||||
|
|
|
@ -929,7 +929,7 @@ static struct nfs_parsed_mount_data *nfs_alloc_parsed_mount_data(void)
|
|||
data->minorversion = 0;
|
||||
data->need_mount = true;
|
||||
data->net = current->nsproxy->net_ns;
|
||||
security_init_mnt_opts(&data->lsm_opts);
|
||||
data->lsm_opts = NULL;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
@ -1206,7 +1206,7 @@ static int nfs_get_option_ul_bound(substring_t args[], unsigned long *option,
|
|||
static int nfs_parse_mount_options(char *raw,
|
||||
struct nfs_parsed_mount_data *mnt)
|
||||
{
|
||||
char *p, *string, *secdata;
|
||||
char *p, *string;
|
||||
int rc, sloppy = 0, invalid_option = 0;
|
||||
unsigned short protofamily = AF_UNSPEC;
|
||||
unsigned short mountfamily = AF_UNSPEC;
|
||||
|
@ -1217,20 +1217,10 @@ static int nfs_parse_mount_options(char *raw,
|
|||
}
|
||||
dfprintk(MOUNT, "NFS: nfs mount opts='%s'\n", raw);
|
||||
|
||||
secdata = alloc_secdata();
|
||||
if (!secdata)
|
||||
goto out_nomem;
|
||||
|
||||
rc = security_sb_copy_data(raw, secdata);
|
||||
rc = security_sb_eat_lsm_opts(raw, &mnt->lsm_opts);
|
||||
if (rc)
|
||||
goto out_security_failure;
|
||||
|
||||
rc = security_sb_parse_opts_str(secdata, &mnt->lsm_opts);
|
||||
if (rc)
|
||||
goto out_security_failure;
|
||||
|
||||
free_secdata(secdata);
|
||||
|
||||
while ((p = strsep(&raw, ",")) != NULL) {
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
unsigned long option;
|
||||
|
@ -1682,7 +1672,6 @@ out_nomem:
|
|||
printk(KERN_INFO "NFS: not enough memory to parse option\n");
|
||||
return 0;
|
||||
out_security_failure:
|
||||
free_secdata(secdata);
|
||||
printk(KERN_INFO "NFS: security options invalid: %d\n", rc);
|
||||
return 0;
|
||||
}
|
||||
|
@ -2081,14 +2070,9 @@ static int nfs23_validate_mount_data(void *options,
|
|||
if (data->context[0]){
|
||||
#ifdef CONFIG_SECURITY_SELINUX
|
||||
int rc;
|
||||
char *opts_str = kmalloc(sizeof(data->context) + 8, GFP_KERNEL);
|
||||
if (!opts_str)
|
||||
return -ENOMEM;
|
||||
strcpy(opts_str, "context=");
|
||||
data->context[NFS_MAX_CONTEXT_LEN] = '\0';
|
||||
strcat(opts_str, &data->context[0]);
|
||||
rc = security_sb_parse_opts_str(opts_str, &args->lsm_opts);
|
||||
kfree(opts_str);
|
||||
rc = security_add_mnt_opt("context", data->context,
|
||||
strlen(data->context), &args->lsm_opts);
|
||||
if (rc)
|
||||
return rc;
|
||||
#else
|
||||
|
@ -2271,7 +2255,7 @@ nfs_remount(struct super_block *sb, int *flags, char *raw_data)
|
|||
options->version <= 6))))
|
||||
return 0;
|
||||
|
||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||
data = nfs_alloc_parsed_mount_data();
|
||||
if (data == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -2310,8 +2294,10 @@ nfs_remount(struct super_block *sb, int *flags, char *raw_data)
|
|||
|
||||
/* compare new mount options with old ones */
|
||||
error = nfs_compare_remount_data(nfss, data);
|
||||
if (!error)
|
||||
error = security_sb_remount(sb, data->lsm_opts);
|
||||
out:
|
||||
kfree(data);
|
||||
nfs_free_parsed_mount_data(data);
|
||||
return error;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_remount);
|
||||
|
@ -2548,7 +2534,7 @@ int nfs_set_sb_security(struct super_block *s, struct dentry *mntroot,
|
|||
if (NFS_SB(s)->caps & NFS_CAP_SECURITY_LABEL)
|
||||
kflags |= SECURITY_LSM_NATIVE_LABELS;
|
||||
|
||||
error = security_sb_set_mnt_opts(s, &mount_info->parsed->lsm_opts,
|
||||
error = security_sb_set_mnt_opts(s, mount_info->parsed->lsm_opts,
|
||||
kflags, &kflags_out);
|
||||
if (error)
|
||||
goto err;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <linux/mount.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/nsproxy.h>
|
||||
#include <uapi/linux/mount.h>
|
||||
#include "internal.h"
|
||||
#include "pnode.h"
|
||||
|
||||
|
|
24
fs/super.c
24
fs/super.c
|
@ -35,6 +35,7 @@
|
|||
#include <linux/fsnotify.h>
|
||||
#include <linux/lockdep.h>
|
||||
#include <linux/user_namespace.h>
|
||||
#include <uapi/linux/mount.h>
|
||||
#include "internal.h"
|
||||
|
||||
static int thaw_super_locked(struct super_block *sb);
|
||||
|
@ -1245,17 +1246,13 @@ mount_fs(struct file_system_type *type, int flags, const char *name, void *data)
|
|||
{
|
||||
struct dentry *root;
|
||||
struct super_block *sb;
|
||||
char *secdata = NULL;
|
||||
int error = -ENOMEM;
|
||||
void *sec_opts = NULL;
|
||||
|
||||
if (data && !(type->fs_flags & FS_BINARY_MOUNTDATA)) {
|
||||
secdata = alloc_secdata();
|
||||
if (!secdata)
|
||||
goto out;
|
||||
|
||||
error = security_sb_copy_data(data, secdata);
|
||||
error = security_sb_eat_lsm_opts(data, &sec_opts);
|
||||
if (error)
|
||||
goto out_free_secdata;
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
|
||||
root = type->mount(type, flags, name, data);
|
||||
|
@ -1276,10 +1273,16 @@ mount_fs(struct file_system_type *type, int flags, const char *name, void *data)
|
|||
smp_wmb();
|
||||
sb->s_flags |= SB_BORN;
|
||||
|
||||
error = security_sb_kern_mount(sb, flags, secdata);
|
||||
error = security_sb_set_mnt_opts(sb, sec_opts, 0, NULL);
|
||||
if (error)
|
||||
goto out_sb;
|
||||
|
||||
if (!(flags & (MS_KERNMOUNT|MS_SUBMOUNT))) {
|
||||
error = security_sb_kern_mount(sb);
|
||||
if (error)
|
||||
goto out_sb;
|
||||
}
|
||||
|
||||
/*
|
||||
* filesystems should never set s_maxbytes larger than MAX_LFS_FILESIZE
|
||||
* but s_maxbytes was an unsigned long long for many releases. Throw
|
||||
|
@ -1290,14 +1293,13 @@ mount_fs(struct file_system_type *type, int flags, const char *name, void *data)
|
|||
"negative value (%lld)\n", type->name, sb->s_maxbytes);
|
||||
|
||||
up_write(&sb->s_umount);
|
||||
free_secdata(secdata);
|
||||
security_free_mnt_opts(&sec_opts);
|
||||
return root;
|
||||
out_sb:
|
||||
dput(root);
|
||||
deactivate_locked_super(sb);
|
||||
out_free_secdata:
|
||||
free_secdata(secdata);
|
||||
out:
|
||||
security_free_mnt_opts(&sec_opts);
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
|
||||
|
|
|
@ -1461,9 +1461,10 @@ union security_list_options {
|
|||
|
||||
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);
|
||||
void (*sb_free_mnt_opts)(void *mnt_opts);
|
||||
int (*sb_eat_lsm_opts)(char *orig, void **mnt_opts);
|
||||
int (*sb_remount)(struct super_block *sb, void *mnt_opts);
|
||||
int (*sb_kern_mount)(struct super_block *sb);
|
||||
int (*sb_show_options)(struct seq_file *m, struct super_block *sb);
|
||||
int (*sb_statfs)(struct dentry *dentry);
|
||||
int (*sb_mount)(const char *dev_name, const struct path *path,
|
||||
|
@ -1471,14 +1472,15 @@ union security_list_options {
|
|||
int (*sb_umount)(struct vfsmount *mnt, int flags);
|
||||
int (*sb_pivotroot)(const struct path *old_path, const struct path *new_path);
|
||||
int (*sb_set_mnt_opts)(struct super_block *sb,
|
||||
struct security_mnt_opts *opts,
|
||||
void *mnt_opts,
|
||||
unsigned long kern_flags,
|
||||
unsigned long *set_kern_flags);
|
||||
int (*sb_clone_mnt_opts)(const struct super_block *oldsb,
|
||||
struct super_block *newsb,
|
||||
unsigned long kern_flags,
|
||||
unsigned long *set_kern_flags);
|
||||
int (*sb_parse_opts_str)(char *options, struct security_mnt_opts *opts);
|
||||
int (*sb_add_mnt_opt)(const char *option, const char *val, int len,
|
||||
void **mnt_opts);
|
||||
int (*dentry_init_security)(struct dentry *dentry, int mode,
|
||||
const struct qstr *name, void **ctx,
|
||||
u32 *ctxlen);
|
||||
|
@ -1800,7 +1802,8 @@ struct security_hook_heads {
|
|||
struct hlist_head bprm_committed_creds;
|
||||
struct hlist_head sb_alloc_security;
|
||||
struct hlist_head sb_free_security;
|
||||
struct hlist_head sb_copy_data;
|
||||
struct hlist_head sb_free_mnt_opts;
|
||||
struct hlist_head sb_eat_lsm_opts;
|
||||
struct hlist_head sb_remount;
|
||||
struct hlist_head sb_kern_mount;
|
||||
struct hlist_head sb_show_options;
|
||||
|
@ -1810,7 +1813,7 @@ struct security_hook_heads {
|
|||
struct hlist_head sb_pivotroot;
|
||||
struct hlist_head sb_set_mnt_opts;
|
||||
struct hlist_head sb_clone_mnt_opts;
|
||||
struct hlist_head sb_parse_opts_str;
|
||||
struct hlist_head sb_add_mnt_opt;
|
||||
struct hlist_head dentry_init_security;
|
||||
struct hlist_head dentry_create_files_as;
|
||||
#ifdef CONFIG_SECURITY_PATH
|
||||
|
|
|
@ -81,7 +81,7 @@ extern void mnt_drop_write_file(struct file *file);
|
|||
extern void mntput(struct vfsmount *mnt);
|
||||
extern struct vfsmount *mntget(struct vfsmount *mnt);
|
||||
extern struct vfsmount *mnt_clone_internal(const struct path *path);
|
||||
extern int __mnt_is_readonly(struct vfsmount *mnt);
|
||||
extern bool __mnt_is_readonly(struct vfsmount *mnt);
|
||||
extern bool mnt_may_suid(struct vfsmount *mnt);
|
||||
|
||||
struct path;
|
||||
|
|
|
@ -182,36 +182,10 @@ static inline const char *kernel_load_data_id_str(enum kernel_load_data_id id)
|
|||
|
||||
#ifdef CONFIG_SECURITY
|
||||
|
||||
struct security_mnt_opts {
|
||||
char **mnt_opts;
|
||||
int *mnt_opts_flags;
|
||||
int num_mnt_opts;
|
||||
};
|
||||
|
||||
int call_lsm_notifier(enum lsm_event event, void *data);
|
||||
int register_lsm_notifier(struct notifier_block *nb);
|
||||
int unregister_lsm_notifier(struct notifier_block *nb);
|
||||
|
||||
static inline void security_init_mnt_opts(struct security_mnt_opts *opts)
|
||||
{
|
||||
opts->mnt_opts = NULL;
|
||||
opts->mnt_opts_flags = NULL;
|
||||
opts->num_mnt_opts = 0;
|
||||
}
|
||||
|
||||
static inline void security_free_mnt_opts(struct security_mnt_opts *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);
|
||||
opts->mnt_opts = NULL;
|
||||
kfree(opts->mnt_opts_flags);
|
||||
opts->mnt_opts_flags = NULL;
|
||||
opts->num_mnt_opts = 0;
|
||||
}
|
||||
|
||||
/* prototypes */
|
||||
extern int security_init(void);
|
||||
|
||||
|
@ -248,9 +222,10 @@ void security_bprm_committing_creds(struct linux_binprm *bprm);
|
|||
void security_bprm_committed_creds(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);
|
||||
void security_free_mnt_opts(void **mnt_opts);
|
||||
int security_sb_eat_lsm_opts(char *options, void **mnt_opts);
|
||||
int security_sb_remount(struct super_block *sb, void *mnt_opts);
|
||||
int security_sb_kern_mount(struct super_block *sb);
|
||||
int security_sb_show_options(struct seq_file *m, struct super_block *sb);
|
||||
int security_sb_statfs(struct dentry *dentry);
|
||||
int security_sb_mount(const char *dev_name, const struct path *path,
|
||||
|
@ -258,14 +233,15 @@ int security_sb_mount(const char *dev_name, const struct path *path,
|
|||
int security_sb_umount(struct vfsmount *mnt, int flags);
|
||||
int security_sb_pivotroot(const struct path *old_path, const struct path *new_path);
|
||||
int security_sb_set_mnt_opts(struct super_block *sb,
|
||||
struct security_mnt_opts *opts,
|
||||
void *mnt_opts,
|
||||
unsigned long kern_flags,
|
||||
unsigned long *set_kern_flags);
|
||||
int security_sb_clone_mnt_opts(const struct super_block *oldsb,
|
||||
struct super_block *newsb,
|
||||
unsigned long kern_flags,
|
||||
unsigned long *set_kern_flags);
|
||||
int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts);
|
||||
int security_add_mnt_opt(const char *option, const char *val,
|
||||
int len, void **mnt_opts);
|
||||
int security_dentry_init_security(struct dentry *dentry, int mode,
|
||||
const struct qstr *name, void **ctx,
|
||||
u32 *ctxlen);
|
||||
|
@ -403,8 +379,6 @@ int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen);
|
|||
int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen);
|
||||
int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen);
|
||||
#else /* CONFIG_SECURITY */
|
||||
struct security_mnt_opts {
|
||||
};
|
||||
|
||||
static inline int call_lsm_notifier(enum lsm_event event, void *data)
|
||||
{
|
||||
|
@ -421,11 +395,7 @@ static inline int unregister_lsm_notifier(struct notifier_block *nb)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline void security_init_mnt_opts(struct security_mnt_opts *opts)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
|
||||
static inline void security_free_mnt_opts(void **mnt_opts)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -555,17 +525,19 @@ static inline int security_sb_alloc(struct super_block *sb)
|
|||
static inline void security_sb_free(struct super_block *sb)
|
||||
{ }
|
||||
|
||||
static inline int security_sb_copy_data(char *orig, char *copy)
|
||||
static inline int security_sb_eat_lsm_opts(char *options,
|
||||
void **mnt_opts)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int security_sb_remount(struct super_block *sb, void *data)
|
||||
static inline int security_sb_remount(struct super_block *sb,
|
||||
void *mnt_opts)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int security_sb_kern_mount(struct super_block *sb, int flags, void *data)
|
||||
static inline int security_sb_kern_mount(struct super_block *sb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
@ -600,7 +572,7 @@ static inline int security_sb_pivotroot(const struct path *old_path,
|
|||
}
|
||||
|
||||
static inline int security_sb_set_mnt_opts(struct super_block *sb,
|
||||
struct security_mnt_opts *opts,
|
||||
void *mnt_opts,
|
||||
unsigned long kern_flags,
|
||||
unsigned long *set_kern_flags)
|
||||
{
|
||||
|
@ -615,7 +587,8 @@ static inline int security_sb_clone_mnt_opts(const struct super_block *oldsb,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts)
|
||||
static inline int security_add_mnt_opt(const char *option, const char *val,
|
||||
int len, void **mnt_opts)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
@ -1820,28 +1793,5 @@ static inline void security_bpf_prog_free(struct bpf_prog_aux *aux)
|
|||
#endif /* CONFIG_SECURITY */
|
||||
#endif /* CONFIG_BPF_SYSCALL */
|
||||
|
||||
#ifdef CONFIG_SECURITY
|
||||
|
||||
static inline char *alloc_secdata(void)
|
||||
{
|
||||
return (char *)get_zeroed_page(GFP_KERNEL);
|
||||
}
|
||||
|
||||
static inline void free_secdata(void *secdata)
|
||||
{
|
||||
free_page((unsigned long)secdata);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline char *alloc_secdata(void)
|
||||
{
|
||||
return (char *)1;
|
||||
}
|
||||
|
||||
static inline void free_secdata(void *secdata)
|
||||
{ }
|
||||
#endif /* CONFIG_SECURITY */
|
||||
|
||||
#endif /* ! __LINUX_SECURITY_H */
|
||||
|
||||
|
|
|
@ -14,6 +14,11 @@
|
|||
#include <linux/ioctl.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/* Use of MS_* flags within the kernel is restricted to core mount(2) code. */
|
||||
#if !defined(__KERNEL__)
|
||||
#include <linux/mount.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* It's silly to have NR_OPEN bigger than NR_FILE, but you can change
|
||||
* the file limit at runtime and only root can increase the per-process
|
||||
|
@ -101,57 +106,6 @@ struct inodes_stat_t {
|
|||
|
||||
#define NR_FILE 8192 /* this can well be larger on a larger system */
|
||||
|
||||
|
||||
/*
|
||||
* These are the fs-independent mount-flags: up to 32 flags are supported
|
||||
*/
|
||||
#define MS_RDONLY 1 /* Mount read-only */
|
||||
#define MS_NOSUID 2 /* Ignore suid and sgid bits */
|
||||
#define MS_NODEV 4 /* Disallow access to device special files */
|
||||
#define MS_NOEXEC 8 /* Disallow program execution */
|
||||
#define MS_SYNCHRONOUS 16 /* Writes are synced at once */
|
||||
#define MS_REMOUNT 32 /* Alter flags of a mounted FS */
|
||||
#define MS_MANDLOCK 64 /* Allow mandatory locks on an FS */
|
||||
#define MS_DIRSYNC 128 /* Directory modifications are synchronous */
|
||||
#define MS_NOATIME 1024 /* Do not update access times. */
|
||||
#define MS_NODIRATIME 2048 /* Do not update directory access times */
|
||||
#define MS_BIND 4096
|
||||
#define MS_MOVE 8192
|
||||
#define MS_REC 16384
|
||||
#define MS_VERBOSE 32768 /* War is peace. Verbosity is silence.
|
||||
MS_VERBOSE is deprecated. */
|
||||
#define MS_SILENT 32768
|
||||
#define MS_POSIXACL (1<<16) /* VFS does not apply the umask */
|
||||
#define MS_UNBINDABLE (1<<17) /* change to unbindable */
|
||||
#define MS_PRIVATE (1<<18) /* change to private */
|
||||
#define MS_SLAVE (1<<19) /* change to slave */
|
||||
#define MS_SHARED (1<<20) /* change to shared */
|
||||
#define MS_RELATIME (1<<21) /* Update atime relative to mtime/ctime. */
|
||||
#define MS_KERNMOUNT (1<<22) /* this is a kern_mount call */
|
||||
#define MS_I_VERSION (1<<23) /* Update inode I_version field */
|
||||
#define MS_STRICTATIME (1<<24) /* Always perform atime updates */
|
||||
#define MS_LAZYTIME (1<<25) /* Update the on-disk [acm]times lazily */
|
||||
|
||||
/* These sb flags are internal to the kernel */
|
||||
#define MS_SUBMOUNT (1<<26)
|
||||
#define MS_NOREMOTELOCK (1<<27)
|
||||
#define MS_NOSEC (1<<28)
|
||||
#define MS_BORN (1<<29)
|
||||
#define MS_ACTIVE (1<<30)
|
||||
#define MS_NOUSER (1<<31)
|
||||
|
||||
/*
|
||||
* Superblock flags that can be altered by MS_REMOUNT
|
||||
*/
|
||||
#define MS_RMT_MASK (MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK|MS_I_VERSION|\
|
||||
MS_LAZYTIME)
|
||||
|
||||
/*
|
||||
* Old magic mount flag and mask
|
||||
*/
|
||||
#define MS_MGC_VAL 0xC0ED0000
|
||||
#define MS_MGC_MSK 0xffff0000
|
||||
|
||||
/*
|
||||
* Structure for FS_IOC_FSGETXATTR[A] and FS_IOC_FSSETXATTR.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
#ifndef _UAPI_LINUX_MOUNT_H
|
||||
#define _UAPI_LINUX_MOUNT_H
|
||||
|
||||
/*
|
||||
* These are the fs-independent mount-flags: up to 32 flags are supported
|
||||
*
|
||||
* Usage of these is restricted within the kernel to core mount(2) code and
|
||||
* callers of sys_mount() only. Filesystems should be using the SB_*
|
||||
* equivalent instead.
|
||||
*/
|
||||
#define MS_RDONLY 1 /* Mount read-only */
|
||||
#define MS_NOSUID 2 /* Ignore suid and sgid bits */
|
||||
#define MS_NODEV 4 /* Disallow access to device special files */
|
||||
#define MS_NOEXEC 8 /* Disallow program execution */
|
||||
#define MS_SYNCHRONOUS 16 /* Writes are synced at once */
|
||||
#define MS_REMOUNT 32 /* Alter flags of a mounted FS */
|
||||
#define MS_MANDLOCK 64 /* Allow mandatory locks on an FS */
|
||||
#define MS_DIRSYNC 128 /* Directory modifications are synchronous */
|
||||
#define MS_NOATIME 1024 /* Do not update access times. */
|
||||
#define MS_NODIRATIME 2048 /* Do not update directory access times */
|
||||
#define MS_BIND 4096
|
||||
#define MS_MOVE 8192
|
||||
#define MS_REC 16384
|
||||
#define MS_VERBOSE 32768 /* War is peace. Verbosity is silence.
|
||||
MS_VERBOSE is deprecated. */
|
||||
#define MS_SILENT 32768
|
||||
#define MS_POSIXACL (1<<16) /* VFS does not apply the umask */
|
||||
#define MS_UNBINDABLE (1<<17) /* change to unbindable */
|
||||
#define MS_PRIVATE (1<<18) /* change to private */
|
||||
#define MS_SLAVE (1<<19) /* change to slave */
|
||||
#define MS_SHARED (1<<20) /* change to shared */
|
||||
#define MS_RELATIME (1<<21) /* Update atime relative to mtime/ctime. */
|
||||
#define MS_KERNMOUNT (1<<22) /* this is a kern_mount call */
|
||||
#define MS_I_VERSION (1<<23) /* Update inode I_version field */
|
||||
#define MS_STRICTATIME (1<<24) /* Always perform atime updates */
|
||||
#define MS_LAZYTIME (1<<25) /* Update the on-disk [acm]times lazily */
|
||||
|
||||
/* These sb flags are internal to the kernel */
|
||||
#define MS_SUBMOUNT (1<<26)
|
||||
#define MS_NOREMOTELOCK (1<<27)
|
||||
#define MS_NOSEC (1<<28)
|
||||
#define MS_BORN (1<<29)
|
||||
#define MS_ACTIVE (1<<30)
|
||||
#define MS_NOUSER (1<<31)
|
||||
|
||||
/*
|
||||
* Superblock flags that can be altered by MS_REMOUNT
|
||||
*/
|
||||
#define MS_RMT_MASK (MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK|MS_I_VERSION|\
|
||||
MS_LAZYTIME)
|
||||
|
||||
/*
|
||||
* Old magic mount flag and mask
|
||||
*/
|
||||
#define MS_MGC_VAL 0xC0ED0000
|
||||
#define MS_MGC_MSK 0xffff0000
|
||||
|
||||
#endif /* _UAPI_LINUX_MOUNT_H */
|
|
@ -22,6 +22,7 @@
|
|||
#include <linux/nfs_fs.h>
|
||||
#include <linux/nfs_fs_sb.h>
|
||||
#include <linux/nfs_mount.h>
|
||||
#include <uapi/linux/mount.h>
|
||||
|
||||
#include "do_mounts.h"
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <linux/sched.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/kmod.h>
|
||||
#include <uapi/linux/mount.h>
|
||||
|
||||
#include "do_mounts.h"
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <linux/netfilter_ipv4.h>
|
||||
#include <linux/netfilter_ipv6.h>
|
||||
#include <net/sock.h>
|
||||
#include <uapi/linux/mount.h>
|
||||
|
||||
#include "include/apparmor.h"
|
||||
#include "include/apparmorfs.h"
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/fs.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/namei.h>
|
||||
#include <uapi/linux/mount.h>
|
||||
|
||||
#include "include/apparmor.h"
|
||||
#include "include/audit.h"
|
||||
|
|
|
@ -384,20 +384,31 @@ void security_sb_free(struct super_block *sb)
|
|||
call_void_hook(sb_free_security, sb);
|
||||
}
|
||||
|
||||
int security_sb_copy_data(char *orig, char *copy)
|
||||
void security_free_mnt_opts(void **mnt_opts)
|
||||
{
|
||||
return call_int_hook(sb_copy_data, 0, orig, copy);
|
||||
if (!*mnt_opts)
|
||||
return;
|
||||
call_void_hook(sb_free_mnt_opts, *mnt_opts);
|
||||
*mnt_opts = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(security_sb_copy_data);
|
||||
EXPORT_SYMBOL(security_free_mnt_opts);
|
||||
|
||||
int security_sb_remount(struct super_block *sb, void *data)
|
||||
int security_sb_eat_lsm_opts(char *options, void **mnt_opts)
|
||||
{
|
||||
return call_int_hook(sb_remount, 0, sb, data);
|
||||
return call_int_hook(sb_eat_lsm_opts, 0, options, mnt_opts);
|
||||
}
|
||||
EXPORT_SYMBOL(security_sb_eat_lsm_opts);
|
||||
|
||||
int security_sb_kern_mount(struct super_block *sb, int flags, void *data)
|
||||
int security_sb_remount(struct super_block *sb,
|
||||
void *mnt_opts)
|
||||
{
|
||||
return call_int_hook(sb_kern_mount, 0, sb, flags, data);
|
||||
return call_int_hook(sb_remount, 0, sb, mnt_opts);
|
||||
}
|
||||
EXPORT_SYMBOL(security_sb_remount);
|
||||
|
||||
int security_sb_kern_mount(struct super_block *sb)
|
||||
{
|
||||
return call_int_hook(sb_kern_mount, 0, sb);
|
||||
}
|
||||
|
||||
int security_sb_show_options(struct seq_file *m, struct super_block *sb)
|
||||
|
@ -427,13 +438,13 @@ int security_sb_pivotroot(const struct path *old_path, const struct path *new_pa
|
|||
}
|
||||
|
||||
int security_sb_set_mnt_opts(struct super_block *sb,
|
||||
struct security_mnt_opts *opts,
|
||||
void *mnt_opts,
|
||||
unsigned long kern_flags,
|
||||
unsigned long *set_kern_flags)
|
||||
{
|
||||
return call_int_hook(sb_set_mnt_opts,
|
||||
opts->num_mnt_opts ? -EOPNOTSUPP : 0, sb,
|
||||
opts, kern_flags, set_kern_flags);
|
||||
mnt_opts ? -EOPNOTSUPP : 0, sb,
|
||||
mnt_opts, kern_flags, set_kern_flags);
|
||||
}
|
||||
EXPORT_SYMBOL(security_sb_set_mnt_opts);
|
||||
|
||||
|
@ -447,11 +458,13 @@ int security_sb_clone_mnt_opts(const struct super_block *oldsb,
|
|||
}
|
||||
EXPORT_SYMBOL(security_sb_clone_mnt_opts);
|
||||
|
||||
int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts)
|
||||
int security_add_mnt_opt(const char *option, const char *val, int len,
|
||||
void **mnt_opts)
|
||||
{
|
||||
return call_int_hook(sb_parse_opts_str, 0, options, opts);
|
||||
return call_int_hook(sb_add_mnt_opt, -EINVAL,
|
||||
option, val, len, mnt_opts);
|
||||
}
|
||||
EXPORT_SYMBOL(security_sb_parse_opts_str);
|
||||
EXPORT_SYMBOL(security_add_mnt_opt);
|
||||
|
||||
int security_inode_alloc(struct inode *inode)
|
||||
{
|
||||
|
|
|
@ -88,6 +88,7 @@
|
|||
#include <linux/msg.h>
|
||||
#include <linux/shm.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <uapi/linux/mount.h>
|
||||
|
||||
#include "avc.h"
|
||||
#include "objsec.h"
|
||||
|
@ -432,6 +433,20 @@ static void superblock_free_security(struct super_block *sb)
|
|||
kfree(sbsec);
|
||||
}
|
||||
|
||||
struct selinux_mnt_opts {
|
||||
const char *fscontext, *context, *rootcontext, *defcontext;
|
||||
};
|
||||
|
||||
static void selinux_free_mnt_opts(void *mnt_opts)
|
||||
{
|
||||
struct selinux_mnt_opts *opts = mnt_opts;
|
||||
kfree(opts->fscontext);
|
||||
kfree(opts->context);
|
||||
kfree(opts->rootcontext);
|
||||
kfree(opts->defcontext);
|
||||
kfree(opts);
|
||||
}
|
||||
|
||||
static inline int inode_doinit(struct inode *inode)
|
||||
{
|
||||
return inode_doinit_with_dentry(inode, NULL);
|
||||
|
@ -443,20 +458,42 @@ enum {
|
|||
Opt_fscontext = 2,
|
||||
Opt_defcontext = 3,
|
||||
Opt_rootcontext = 4,
|
||||
Opt_labelsupport = 5,
|
||||
Opt_nextmntopt = 6,
|
||||
Opt_seclabel = 5,
|
||||
};
|
||||
|
||||
#define NUM_SEL_MNT_OPTS (Opt_nextmntopt - 1)
|
||||
|
||||
static const match_table_t tokens = {
|
||||
{Opt_context, CONTEXT_STR "%s"},
|
||||
{Opt_fscontext, FSCONTEXT_STR "%s"},
|
||||
{Opt_defcontext, DEFCONTEXT_STR "%s"},
|
||||
{Opt_rootcontext, ROOTCONTEXT_STR "%s"},
|
||||
{Opt_labelsupport, LABELSUPP_STR},
|
||||
{Opt_error, NULL},
|
||||
#define A(s, has_arg) {#s, sizeof(#s) - 1, Opt_##s, has_arg}
|
||||
static struct {
|
||||
const char *name;
|
||||
int len;
|
||||
int opt;
|
||||
bool has_arg;
|
||||
} tokens[] = {
|
||||
A(context, true),
|
||||
A(fscontext, true),
|
||||
A(defcontext, true),
|
||||
A(rootcontext, true),
|
||||
A(seclabel, false),
|
||||
};
|
||||
#undef A
|
||||
|
||||
static int match_opt_prefix(char *s, int l, char **arg)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tokens); i++) {
|
||||
size_t len = tokens[i].len;
|
||||
if (len > l || memcmp(s, tokens[i].name, len))
|
||||
continue;
|
||||
if (tokens[i].has_arg) {
|
||||
if (len == l || s[len] != '=')
|
||||
continue;
|
||||
*arg = s + len + 1;
|
||||
} else if (len != l)
|
||||
continue;
|
||||
return tokens[i].opt;
|
||||
}
|
||||
return Opt_error;
|
||||
}
|
||||
|
||||
#define SEL_MOUNT_FAIL_MSG "SELinux: duplicate or incompatible mount options\n"
|
||||
|
||||
|
@ -570,10 +607,9 @@ static int sb_finish_set_opts(struct super_block *sb)
|
|||
during get_sb by a pseudo filesystem that directly
|
||||
populates itself. */
|
||||
spin_lock(&sbsec->isec_lock);
|
||||
next_inode:
|
||||
if (!list_empty(&sbsec->isec_head)) {
|
||||
while (!list_empty(&sbsec->isec_head)) {
|
||||
struct inode_security_struct *isec =
|
||||
list_entry(sbsec->isec_head.next,
|
||||
list_first_entry(&sbsec->isec_head,
|
||||
struct inode_security_struct, list);
|
||||
struct inode *inode = isec->inode;
|
||||
list_del_init(&isec->list);
|
||||
|
@ -585,112 +621,12 @@ next_inode:
|
|||
iput(inode);
|
||||
}
|
||||
spin_lock(&sbsec->isec_lock);
|
||||
goto next_inode;
|
||||
}
|
||||
spin_unlock(&sbsec->isec_lock);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function should allow an FS to ask what it's mount security
|
||||
* options were so it can use those later for submounts, displaying
|
||||
* mount options, or whatever.
|
||||
*/
|
||||
static int selinux_get_mnt_opts(const struct super_block *sb,
|
||||
struct security_mnt_opts *opts)
|
||||
{
|
||||
int rc = 0, i;
|
||||
struct superblock_security_struct *sbsec = sb->s_security;
|
||||
char *context = NULL;
|
||||
u32 len;
|
||||
char tmp;
|
||||
|
||||
security_init_mnt_opts(opts);
|
||||
|
||||
if (!(sbsec->flags & SE_SBINITIALIZED))
|
||||
return -EINVAL;
|
||||
|
||||
if (!selinux_state.initialized)
|
||||
return -EINVAL;
|
||||
|
||||
/* make sure we always check enough bits to cover the mask */
|
||||
BUILD_BUG_ON(SE_MNTMASK >= (1 << NUM_SEL_MNT_OPTS));
|
||||
|
||||
tmp = sbsec->flags & SE_MNTMASK;
|
||||
/* count the number of mount options for this sb */
|
||||
for (i = 0; i < NUM_SEL_MNT_OPTS; i++) {
|
||||
if (tmp & 0x01)
|
||||
opts->num_mnt_opts++;
|
||||
tmp >>= 1;
|
||||
}
|
||||
/* Check if the Label support flag is set */
|
||||
if (sbsec->flags & SBLABEL_MNT)
|
||||
opts->num_mnt_opts++;
|
||||
|
||||
opts->mnt_opts = kcalloc(opts->num_mnt_opts, sizeof(char *), GFP_ATOMIC);
|
||||
if (!opts->mnt_opts) {
|
||||
rc = -ENOMEM;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
opts->mnt_opts_flags = kcalloc(opts->num_mnt_opts, sizeof(int), GFP_ATOMIC);
|
||||
if (!opts->mnt_opts_flags) {
|
||||
rc = -ENOMEM;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
if (sbsec->flags & FSCONTEXT_MNT) {
|
||||
rc = security_sid_to_context(&selinux_state, sbsec->sid,
|
||||
&context, &len);
|
||||
if (rc)
|
||||
goto out_free;
|
||||
opts->mnt_opts[i] = context;
|
||||
opts->mnt_opts_flags[i++] = FSCONTEXT_MNT;
|
||||
}
|
||||
if (sbsec->flags & CONTEXT_MNT) {
|
||||
rc = security_sid_to_context(&selinux_state,
|
||||
sbsec->mntpoint_sid,
|
||||
&context, &len);
|
||||
if (rc)
|
||||
goto out_free;
|
||||
opts->mnt_opts[i] = context;
|
||||
opts->mnt_opts_flags[i++] = CONTEXT_MNT;
|
||||
}
|
||||
if (sbsec->flags & DEFCONTEXT_MNT) {
|
||||
rc = security_sid_to_context(&selinux_state, sbsec->def_sid,
|
||||
&context, &len);
|
||||
if (rc)
|
||||
goto out_free;
|
||||
opts->mnt_opts[i] = context;
|
||||
opts->mnt_opts_flags[i++] = DEFCONTEXT_MNT;
|
||||
}
|
||||
if (sbsec->flags & ROOTCONTEXT_MNT) {
|
||||
struct dentry *root = sbsec->sb->s_root;
|
||||
struct inode_security_struct *isec = backing_inode_security(root);
|
||||
|
||||
rc = security_sid_to_context(&selinux_state, isec->sid,
|
||||
&context, &len);
|
||||
if (rc)
|
||||
goto out_free;
|
||||
opts->mnt_opts[i] = context;
|
||||
opts->mnt_opts_flags[i++] = ROOTCONTEXT_MNT;
|
||||
}
|
||||
if (sbsec->flags & SBLABEL_MNT) {
|
||||
opts->mnt_opts[i] = NULL;
|
||||
opts->mnt_opts_flags[i++] = SBLABEL_MNT;
|
||||
}
|
||||
|
||||
BUG_ON(i != opts->num_mnt_opts);
|
||||
|
||||
return 0;
|
||||
|
||||
out_free:
|
||||
security_free_mnt_opts(opts);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int bad_option(struct superblock_security_struct *sbsec, char flag,
|
||||
u32 old_sid, u32 new_sid)
|
||||
{
|
||||
|
@ -711,31 +647,39 @@ static int bad_option(struct superblock_security_struct *sbsec, char flag,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int parse_sid(struct super_block *sb, const char *s, u32 *sid)
|
||||
{
|
||||
int rc = security_context_str_to_sid(&selinux_state, s,
|
||||
sid, GFP_KERNEL);
|
||||
if (rc)
|
||||
pr_warn("SELinux: security_context_str_to_sid"
|
||||
"(%s) failed for (dev %s, type %s) errno=%d\n",
|
||||
s, sb->s_id, sb->s_type->name, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allow filesystems with binary mount data to explicitly set mount point
|
||||
* 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)
|
||||
{
|
||||
const struct cred *cred = current_cred();
|
||||
int rc = 0, i;
|
||||
struct superblock_security_struct *sbsec = sb->s_security;
|
||||
const char *name = sb->s_type->name;
|
||||
struct dentry *root = sbsec->sb->s_root;
|
||||
struct selinux_mnt_opts *opts = mnt_opts;
|
||||
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;
|
||||
int rc = 0;
|
||||
|
||||
mutex_lock(&sbsec->lock);
|
||||
|
||||
if (!selinux_state.initialized) {
|
||||
if (!num_opts) {
|
||||
if (!opts) {
|
||||
/* Defer initialization until selinux_complete_init,
|
||||
after the initial policy is loaded and the security
|
||||
server is ready to handle calls. */
|
||||
|
@ -765,7 +709,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
|
|||
* will be used for both mounts)
|
||||
*/
|
||||
if ((sbsec->flags & SE_SBINITIALIZED) && (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)
|
||||
&& (num_opts == 0))
|
||||
&& !opts)
|
||||
goto out;
|
||||
|
||||
root_isec = backing_inode_security_novalidate(root);
|
||||
|
@ -775,68 +719,48 @@ static int selinux_set_mnt_opts(struct super_block *sb,
|
|||
* also check if someone is trying to mount the same sb more
|
||||
* than once with different security options.
|
||||
*/
|
||||
for (i = 0; i < num_opts; i++) {
|
||||
u32 sid;
|
||||
|
||||
if (flags[i] == SBLABEL_MNT)
|
||||
continue;
|
||||
rc = security_context_str_to_sid(&selinux_state,
|
||||
mount_options[i], &sid,
|
||||
GFP_KERNEL);
|
||||
if (rc) {
|
||||
pr_warn("SELinux: security_context_str_to_sid"
|
||||
"(%s) failed for (dev %s, type %s) errno=%d\n",
|
||||
mount_options[i], sb->s_id, name, rc);
|
||||
if (opts) {
|
||||
if (opts->fscontext) {
|
||||
rc = parse_sid(sb, opts->fscontext, &fscontext_sid);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
switch (flags[i]) {
|
||||
case FSCONTEXT_MNT:
|
||||
fscontext_sid = sid;
|
||||
|
||||
if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid,
|
||||
fscontext_sid))
|
||||
goto out_double_mount;
|
||||
|
||||
sbsec->flags |= FSCONTEXT_MNT;
|
||||
break;
|
||||
case CONTEXT_MNT:
|
||||
context_sid = sid;
|
||||
|
||||
}
|
||||
if (opts->context) {
|
||||
rc = parse_sid(sb, opts->context, &context_sid);
|
||||
if (rc)
|
||||
goto out;
|
||||
if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid,
|
||||
context_sid))
|
||||
goto out_double_mount;
|
||||
|
||||
sbsec->flags |= CONTEXT_MNT;
|
||||
break;
|
||||
case ROOTCONTEXT_MNT:
|
||||
rootcontext_sid = sid;
|
||||
|
||||
}
|
||||
if (opts->rootcontext) {
|
||||
rc = parse_sid(sb, opts->rootcontext, &rootcontext_sid);
|
||||
if (rc)
|
||||
goto out;
|
||||
if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid,
|
||||
rootcontext_sid))
|
||||
goto out_double_mount;
|
||||
|
||||
sbsec->flags |= ROOTCONTEXT_MNT;
|
||||
|
||||
break;
|
||||
case DEFCONTEXT_MNT:
|
||||
defcontext_sid = sid;
|
||||
|
||||
}
|
||||
if (opts->defcontext) {
|
||||
rc = parse_sid(sb, opts->defcontext, &defcontext_sid);
|
||||
if (rc)
|
||||
goto out;
|
||||
if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid,
|
||||
defcontext_sid))
|
||||
goto out_double_mount;
|
||||
|
||||
sbsec->flags |= DEFCONTEXT_MNT;
|
||||
|
||||
break;
|
||||
default:
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (sbsec->flags & SE_SBINITIALIZED) {
|
||||
/* previously mounted with options, but not on this attempt? */
|
||||
if ((sbsec->flags & SE_MNTMASK) && !num_opts)
|
||||
if ((sbsec->flags & SE_MNTMASK) && !opts)
|
||||
goto out_double_mount;
|
||||
rc = 0;
|
||||
goto out;
|
||||
|
@ -969,7 +893,8 @@ out:
|
|||
out_double_mount:
|
||||
rc = -EINVAL;
|
||||
pr_warn("SELinux: mount invalid. Same superblock, different "
|
||||
"security settings for (dev %s, type %s)\n", sb->s_id, name);
|
||||
"security settings for (dev %s, type %s)\n", sb->s_id,
|
||||
sb->s_type->name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -1081,218 +1006,145 @@ out:
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int selinux_parse_opts_str(char *options,
|
||||
struct security_mnt_opts *opts)
|
||||
static int selinux_add_opt(int token, const char *s, void **mnt_opts)
|
||||
{
|
||||
char *p;
|
||||
char *context = NULL, *defcontext = NULL;
|
||||
char *fscontext = NULL, *rootcontext = NULL;
|
||||
int rc, num_mnt_opts = 0;
|
||||
struct selinux_mnt_opts *opts = *mnt_opts;
|
||||
|
||||
opts->num_mnt_opts = 0;
|
||||
|
||||
/* Standard string-based options. */
|
||||
while ((p = strsep(&options, "|")) != NULL) {
|
||||
int token;
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
|
||||
if (!*p)
|
||||
continue;
|
||||
|
||||
token = match_token(p, tokens, args);
|
||||
|
||||
switch (token) {
|
||||
case Opt_context:
|
||||
if (context || defcontext) {
|
||||
rc = -EINVAL;
|
||||
pr_warn(SEL_MOUNT_FAIL_MSG);
|
||||
goto out_err;
|
||||
}
|
||||
context = match_strdup(&args[0]);
|
||||
if (!context) {
|
||||
rc = -ENOMEM;
|
||||
goto out_err;
|
||||
}
|
||||
break;
|
||||
|
||||
case Opt_fscontext:
|
||||
if (fscontext) {
|
||||
rc = -EINVAL;
|
||||
pr_warn(SEL_MOUNT_FAIL_MSG);
|
||||
goto out_err;
|
||||
}
|
||||
fscontext = match_strdup(&args[0]);
|
||||
if (!fscontext) {
|
||||
rc = -ENOMEM;
|
||||
goto out_err;
|
||||
}
|
||||
break;
|
||||
|
||||
case Opt_rootcontext:
|
||||
if (rootcontext) {
|
||||
rc = -EINVAL;
|
||||
pr_warn(SEL_MOUNT_FAIL_MSG);
|
||||
goto out_err;
|
||||
}
|
||||
rootcontext = match_strdup(&args[0]);
|
||||
if (!rootcontext) {
|
||||
rc = -ENOMEM;
|
||||
goto out_err;
|
||||
}
|
||||
break;
|
||||
|
||||
case Opt_defcontext:
|
||||
if (context || defcontext) {
|
||||
rc = -EINVAL;
|
||||
pr_warn(SEL_MOUNT_FAIL_MSG);
|
||||
goto out_err;
|
||||
}
|
||||
defcontext = match_strdup(&args[0]);
|
||||
if (!defcontext) {
|
||||
rc = -ENOMEM;
|
||||
goto out_err;
|
||||
}
|
||||
break;
|
||||
case Opt_labelsupport:
|
||||
break;
|
||||
default:
|
||||
rc = -EINVAL;
|
||||
pr_warn("SELinux: unknown mount option\n");
|
||||
goto out_err;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
rc = -ENOMEM;
|
||||
opts->mnt_opts = kcalloc(NUM_SEL_MNT_OPTS, sizeof(char *), GFP_KERNEL);
|
||||
if (!opts->mnt_opts)
|
||||
goto out_err;
|
||||
|
||||
opts->mnt_opts_flags = kcalloc(NUM_SEL_MNT_OPTS, sizeof(int),
|
||||
GFP_KERNEL);
|
||||
if (!opts->mnt_opts_flags)
|
||||
goto out_err;
|
||||
|
||||
if (fscontext) {
|
||||
opts->mnt_opts[num_mnt_opts] = fscontext;
|
||||
opts->mnt_opts_flags[num_mnt_opts++] = FSCONTEXT_MNT;
|
||||
}
|
||||
if (context) {
|
||||
opts->mnt_opts[num_mnt_opts] = context;
|
||||
opts->mnt_opts_flags[num_mnt_opts++] = CONTEXT_MNT;
|
||||
}
|
||||
if (rootcontext) {
|
||||
opts->mnt_opts[num_mnt_opts] = rootcontext;
|
||||
opts->mnt_opts_flags[num_mnt_opts++] = ROOTCONTEXT_MNT;
|
||||
}
|
||||
if (defcontext) {
|
||||
opts->mnt_opts[num_mnt_opts] = defcontext;
|
||||
opts->mnt_opts_flags[num_mnt_opts++] = DEFCONTEXT_MNT;
|
||||
}
|
||||
|
||||
opts->num_mnt_opts = num_mnt_opts;
|
||||
if (token == Opt_seclabel) /* eaten and completely ignored */
|
||||
return 0;
|
||||
|
||||
out_err:
|
||||
security_free_mnt_opts(opts);
|
||||
kfree(context);
|
||||
kfree(defcontext);
|
||||
kfree(fscontext);
|
||||
kfree(rootcontext);
|
||||
return rc;
|
||||
if (!opts) {
|
||||
opts = kzalloc(sizeof(struct selinux_mnt_opts), GFP_KERNEL);
|
||||
if (!opts)
|
||||
return -ENOMEM;
|
||||
*mnt_opts = opts;
|
||||
}
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
switch (token) {
|
||||
case Opt_context:
|
||||
if (opts->context || opts->defcontext)
|
||||
goto Einval;
|
||||
opts->context = s;
|
||||
break;
|
||||
case Opt_fscontext:
|
||||
if (opts->fscontext)
|
||||
goto Einval;
|
||||
opts->fscontext = s;
|
||||
break;
|
||||
case Opt_rootcontext:
|
||||
if (opts->rootcontext)
|
||||
goto Einval;
|
||||
opts->rootcontext = s;
|
||||
break;
|
||||
case Opt_defcontext:
|
||||
if (opts->context || opts->defcontext)
|
||||
goto Einval;
|
||||
opts->defcontext = s;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
Einval:
|
||||
pr_warn(SEL_MOUNT_FAIL_MSG);
|
||||
return -EINVAL;
|
||||
}
|
||||
/*
|
||||
* string mount options parsing and call set the sbsec
|
||||
*/
|
||||
static int superblock_doinit(struct super_block *sb, void *data)
|
||||
|
||||
static int selinux_add_mnt_opt(const char *option, const char *val, int len,
|
||||
void **mnt_opts)
|
||||
{
|
||||
int rc = 0;
|
||||
char *options = data;
|
||||
struct security_mnt_opts opts;
|
||||
int token = Opt_error;
|
||||
int rc, i;
|
||||
|
||||
security_init_mnt_opts(&opts);
|
||||
for (i = 0; i < ARRAY_SIZE(tokens); i++) {
|
||||
if (strcmp(option, tokens[i].name) == 0) {
|
||||
token = tokens[i].opt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!data)
|
||||
goto out;
|
||||
if (token == Opt_error)
|
||||
return -EINVAL;
|
||||
|
||||
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, 0, NULL);
|
||||
|
||||
out_err:
|
||||
security_free_mnt_opts(&opts);
|
||||
if (token != Opt_seclabel)
|
||||
val = kmemdup_nul(val, len, GFP_KERNEL);
|
||||
rc = selinux_add_opt(token, val, mnt_opts);
|
||||
if (unlikely(rc)) {
|
||||
kfree(val);
|
||||
if (*mnt_opts) {
|
||||
selinux_free_mnt_opts(*mnt_opts);
|
||||
*mnt_opts = NULL;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void selinux_write_opts(struct seq_file *m,
|
||||
struct security_mnt_opts *opts)
|
||||
static int show_sid(struct seq_file *m, u32 sid)
|
||||
{
|
||||
int i;
|
||||
char *prefix;
|
||||
char *context = NULL;
|
||||
u32 len;
|
||||
int rc;
|
||||
|
||||
for (i = 0; i < opts->num_mnt_opts; i++) {
|
||||
char *has_comma;
|
||||
rc = security_sid_to_context(&selinux_state, sid,
|
||||
&context, &len);
|
||||
if (!rc) {
|
||||
bool has_comma = context && strchr(context, ',');
|
||||
|
||||
if (opts->mnt_opts[i])
|
||||
has_comma = strchr(opts->mnt_opts[i], ',');
|
||||
else
|
||||
has_comma = NULL;
|
||||
|
||||
switch (opts->mnt_opts_flags[i]) {
|
||||
case CONTEXT_MNT:
|
||||
prefix = CONTEXT_STR;
|
||||
break;
|
||||
case FSCONTEXT_MNT:
|
||||
prefix = FSCONTEXT_STR;
|
||||
break;
|
||||
case ROOTCONTEXT_MNT:
|
||||
prefix = ROOTCONTEXT_STR;
|
||||
break;
|
||||
case DEFCONTEXT_MNT:
|
||||
prefix = DEFCONTEXT_STR;
|
||||
break;
|
||||
case SBLABEL_MNT:
|
||||
seq_putc(m, ',');
|
||||
seq_puts(m, LABELSUPP_STR);
|
||||
continue;
|
||||
default:
|
||||
BUG();
|
||||
return;
|
||||
};
|
||||
/* we need a comma before each option */
|
||||
seq_putc(m, ',');
|
||||
seq_puts(m, prefix);
|
||||
if (has_comma)
|
||||
seq_putc(m, '\"');
|
||||
seq_escape(m, opts->mnt_opts[i], "\"\n\\");
|
||||
seq_escape(m, context, "\"\n\\");
|
||||
if (has_comma)
|
||||
seq_putc(m, '\"');
|
||||
}
|
||||
kfree(context);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int selinux_sb_show_options(struct seq_file *m, struct super_block *sb)
|
||||
{
|
||||
struct security_mnt_opts opts;
|
||||
struct superblock_security_struct *sbsec = sb->s_security;
|
||||
int rc;
|
||||
|
||||
rc = selinux_get_mnt_opts(sb, &opts);
|
||||
if (rc) {
|
||||
/* before policy load we may get EINVAL, don't show anything */
|
||||
if (rc == -EINVAL)
|
||||
rc = 0;
|
||||
if (!(sbsec->flags & SE_SBINITIALIZED))
|
||||
return 0;
|
||||
|
||||
if (!selinux_state.initialized)
|
||||
return 0;
|
||||
|
||||
if (sbsec->flags & FSCONTEXT_MNT) {
|
||||
seq_putc(m, ',');
|
||||
seq_puts(m, FSCONTEXT_STR);
|
||||
rc = show_sid(m, sbsec->sid);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
selinux_write_opts(m, &opts);
|
||||
|
||||
security_free_mnt_opts(&opts);
|
||||
|
||||
if (sbsec->flags & CONTEXT_MNT) {
|
||||
seq_putc(m, ',');
|
||||
seq_puts(m, CONTEXT_STR);
|
||||
rc = show_sid(m, sbsec->mntpoint_sid);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
if (sbsec->flags & DEFCONTEXT_MNT) {
|
||||
seq_putc(m, ',');
|
||||
seq_puts(m, DEFCONTEXT_STR);
|
||||
rc = show_sid(m, sbsec->def_sid);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
if (sbsec->flags & ROOTCONTEXT_MNT) {
|
||||
struct dentry *root = sbsec->sb->s_root;
|
||||
struct inode_security_struct *isec = backing_inode_security(root);
|
||||
seq_putc(m, ',');
|
||||
seq_puts(m, ROOTCONTEXT_STR);
|
||||
rc = show_sid(m, isec->sid);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
if (sbsec->flags & SBLABEL_MNT) {
|
||||
seq_putc(m, ',');
|
||||
seq_puts(m, LABELSUPP_STR);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline u16 inode_mode_to_security_class(umode_t mode)
|
||||
|
@ -2747,195 +2599,129 @@ static void selinux_sb_free_security(struct super_block *sb)
|
|||
superblock_free_security(sb);
|
||||
}
|
||||
|
||||
static inline int match_prefix(char *prefix, int plen, char *option, int olen)
|
||||
static inline int opt_len(const char *s)
|
||||
{
|
||||
if (plen > olen)
|
||||
return 0;
|
||||
bool open_quote = false;
|
||||
int len;
|
||||
char c;
|
||||
|
||||
return !memcmp(prefix, option, plen);
|
||||
}
|
||||
|
||||
static inline int selinux_option(char *option, int len)
|
||||
{
|
||||
return (match_prefix(CONTEXT_STR, sizeof(CONTEXT_STR)-1, option, len) ||
|
||||
match_prefix(FSCONTEXT_STR, sizeof(FSCONTEXT_STR)-1, option, len) ||
|
||||
match_prefix(DEFCONTEXT_STR, sizeof(DEFCONTEXT_STR)-1, option, len) ||
|
||||
match_prefix(ROOTCONTEXT_STR, sizeof(ROOTCONTEXT_STR)-1, option, len) ||
|
||||
match_prefix(LABELSUPP_STR, sizeof(LABELSUPP_STR)-1, option, len));
|
||||
}
|
||||
|
||||
static inline void take_option(char **to, char *from, int *first, int len)
|
||||
{
|
||||
if (!*first) {
|
||||
**to = ',';
|
||||
*to += 1;
|
||||
} else
|
||||
*first = 0;
|
||||
memcpy(*to, from, len);
|
||||
*to += len;
|
||||
}
|
||||
|
||||
static inline void take_selinux_option(char **to, char *from, int *first,
|
||||
int len)
|
||||
{
|
||||
int current_size = 0;
|
||||
|
||||
if (!*first) {
|
||||
**to = '|';
|
||||
*to += 1;
|
||||
} else
|
||||
*first = 0;
|
||||
|
||||
while (current_size < len) {
|
||||
if (*from != '"') {
|
||||
**to = *from;
|
||||
*to += 1;
|
||||
}
|
||||
from += 1;
|
||||
current_size += 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int selinux_sb_copy_data(char *orig, char *copy)
|
||||
{
|
||||
int fnosec, fsec, rc = 0;
|
||||
char *in_save, *in_curr, *in_end;
|
||||
char *sec_curr, *nosec_save, *nosec;
|
||||
int open_quote = 0;
|
||||
|
||||
in_curr = orig;
|
||||
sec_curr = copy;
|
||||
|
||||
nosec = (char *)get_zeroed_page(GFP_KERNEL);
|
||||
if (!nosec) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
nosec_save = nosec;
|
||||
fnosec = fsec = 1;
|
||||
in_save = in_end = orig;
|
||||
|
||||
do {
|
||||
if (*in_end == '"')
|
||||
for (len = 0; (c = s[len]) != '\0'; len++) {
|
||||
if (c == '"')
|
||||
open_quote = !open_quote;
|
||||
if ((*in_end == ',' && open_quote == 0) ||
|
||||
*in_end == '\0') {
|
||||
int len = in_end - in_curr;
|
||||
|
||||
if (selinux_option(in_curr, len))
|
||||
take_selinux_option(&sec_curr, in_curr, &fsec, len);
|
||||
else
|
||||
take_option(&nosec, in_curr, &fnosec, len);
|
||||
|
||||
in_curr = in_end + 1;
|
||||
if (c == ',' && !open_quote)
|
||||
break;
|
||||
}
|
||||
} while (*in_end++);
|
||||
|
||||
strcpy(in_save, nosec_save);
|
||||
free_page((unsigned long)nosec_save);
|
||||
out:
|
||||
return rc;
|
||||
return len;
|
||||
}
|
||||
|
||||
static int selinux_sb_remount(struct super_block *sb, void *data)
|
||||
static int selinux_sb_eat_lsm_opts(char *options, void **mnt_opts)
|
||||
{
|
||||
int rc, i, *flags;
|
||||
struct security_mnt_opts opts;
|
||||
char *secdata, **mount_options;
|
||||
char *from = options;
|
||||
char *to = options;
|
||||
bool first = true;
|
||||
|
||||
while (1) {
|
||||
int len = opt_len(from);
|
||||
int token, rc;
|
||||
char *arg = NULL;
|
||||
|
||||
token = match_opt_prefix(from, len, &arg);
|
||||
|
||||
if (token != Opt_error) {
|
||||
char *p, *q;
|
||||
|
||||
/* strip quotes */
|
||||
if (arg) {
|
||||
for (p = q = arg; p < from + len; p++) {
|
||||
char c = *p;
|
||||
if (c != '"')
|
||||
*q++ = c;
|
||||
}
|
||||
arg = kmemdup_nul(arg, q - arg, GFP_KERNEL);
|
||||
}
|
||||
rc = selinux_add_opt(token, arg, mnt_opts);
|
||||
if (unlikely(rc)) {
|
||||
kfree(arg);
|
||||
if (*mnt_opts) {
|
||||
selinux_free_mnt_opts(*mnt_opts);
|
||||
*mnt_opts = NULL;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
} else {
|
||||
if (!first) { // copy with preceding comma
|
||||
from--;
|
||||
len++;
|
||||
}
|
||||
if (to != from)
|
||||
memmove(to, from, len);
|
||||
to += len;
|
||||
first = false;
|
||||
}
|
||||
if (!from[len])
|
||||
break;
|
||||
from += len + 1;
|
||||
}
|
||||
*to = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int selinux_sb_remount(struct super_block *sb, void *mnt_opts)
|
||||
{
|
||||
struct selinux_mnt_opts *opts = mnt_opts;
|
||||
struct superblock_security_struct *sbsec = sb->s_security;
|
||||
u32 sid;
|
||||
int rc;
|
||||
|
||||
if (!(sbsec->flags & SE_SBINITIALIZED))
|
||||
return 0;
|
||||
|
||||
if (!data)
|
||||
if (!opts)
|
||||
return 0;
|
||||
|
||||
if (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)
|
||||
return 0;
|
||||
|
||||
security_init_mnt_opts(&opts);
|
||||
secdata = alloc_secdata();
|
||||
if (!secdata)
|
||||
return -ENOMEM;
|
||||
rc = selinux_sb_copy_data(data, secdata);
|
||||
if (opts->fscontext) {
|
||||
rc = parse_sid(sb, opts->fscontext, &sid);
|
||||
if (rc)
|
||||
goto out_free_secdata;
|
||||
|
||||
rc = selinux_parse_opts_str(secdata, &opts);
|
||||
if (rc)
|
||||
goto out_free_secdata;
|
||||
|
||||
mount_options = opts.mnt_opts;
|
||||
flags = opts.mnt_opts_flags;
|
||||
|
||||
for (i = 0; i < opts.num_mnt_opts; i++) {
|
||||
u32 sid;
|
||||
|
||||
if (flags[i] == SBLABEL_MNT)
|
||||
continue;
|
||||
rc = security_context_str_to_sid(&selinux_state,
|
||||
mount_options[i], &sid,
|
||||
GFP_KERNEL);
|
||||
if (rc) {
|
||||
pr_warn("SELinux: security_context_str_to_sid"
|
||||
"(%s) failed for (dev %s, type %s) errno=%d\n",
|
||||
mount_options[i], sb->s_id, sb->s_type->name, rc);
|
||||
goto out_free_opts;
|
||||
}
|
||||
rc = -EINVAL;
|
||||
switch (flags[i]) {
|
||||
case FSCONTEXT_MNT:
|
||||
return rc;
|
||||
if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid, sid))
|
||||
goto out_bad_option;
|
||||
break;
|
||||
case CONTEXT_MNT:
|
||||
}
|
||||
if (opts->context) {
|
||||
rc = parse_sid(sb, opts->context, &sid);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid, sid))
|
||||
goto out_bad_option;
|
||||
break;
|
||||
case ROOTCONTEXT_MNT: {
|
||||
}
|
||||
if (opts->rootcontext) {
|
||||
struct inode_security_struct *root_isec;
|
||||
root_isec = backing_inode_security(sb->s_root);
|
||||
|
||||
rc = parse_sid(sb, opts->rootcontext, &sid);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid, sid))
|
||||
goto out_bad_option;
|
||||
break;
|
||||
}
|
||||
case DEFCONTEXT_MNT:
|
||||
if (opts->defcontext) {
|
||||
rc = parse_sid(sb, opts->defcontext, &sid);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid, sid))
|
||||
goto out_bad_option;
|
||||
break;
|
||||
default:
|
||||
goto out_free_opts;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
rc = 0;
|
||||
out_free_opts:
|
||||
security_free_mnt_opts(&opts);
|
||||
out_free_secdata:
|
||||
free_secdata(secdata);
|
||||
return rc;
|
||||
out_bad_option:
|
||||
pr_warn("SELinux: unable to change security options "
|
||||
"during remount (dev %s, type=%s)\n", sb->s_id,
|
||||
sb->s_type->name);
|
||||
goto out_free_opts;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int selinux_sb_kern_mount(struct super_block *sb, int flags, void *data)
|
||||
static int selinux_sb_kern_mount(struct super_block *sb)
|
||||
{
|
||||
const struct cred *cred = current_cred();
|
||||
struct common_audit_data ad;
|
||||
int rc;
|
||||
|
||||
rc = superblock_doinit(sb, data);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Allow all mounts performed by the kernel */
|
||||
if (flags & (MS_KERNMOUNT | MS_SUBMOUNT))
|
||||
return 0;
|
||||
|
||||
ad.type = LSM_AUDIT_DATA_DENTRY;
|
||||
ad.u.dentry = sb->s_root;
|
||||
|
@ -6926,7 +6712,8 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
|
|||
|
||||
LSM_HOOK_INIT(sb_alloc_security, selinux_sb_alloc_security),
|
||||
LSM_HOOK_INIT(sb_free_security, selinux_sb_free_security),
|
||||
LSM_HOOK_INIT(sb_copy_data, selinux_sb_copy_data),
|
||||
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),
|
||||
|
@ -6935,7 +6722,7 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
|
|||
LSM_HOOK_INIT(sb_umount, selinux_umount),
|
||||
LSM_HOOK_INIT(sb_set_mnt_opts, selinux_set_mnt_opts),
|
||||
LSM_HOOK_INIT(sb_clone_mnt_opts, selinux_sb_clone_mnt_opts),
|
||||
LSM_HOOK_INIT(sb_parse_opts_str, selinux_parse_opts_str),
|
||||
LSM_HOOK_INIT(sb_add_mnt_opt, selinux_add_mnt_opt),
|
||||
|
||||
LSM_HOOK_INIT(dentry_init_security, selinux_dentry_init_security),
|
||||
LSM_HOOK_INIT(dentry_create_files_as, selinux_dentry_create_files_as),
|
||||
|
@ -7196,7 +6983,7 @@ static __init int selinux_init(void)
|
|||
|
||||
static void delayed_superblock_init(struct super_block *sb, void *unused)
|
||||
{
|
||||
superblock_doinit(sb, NULL);
|
||||
selinux_set_mnt_opts(sb, NULL, 0, NULL);
|
||||
}
|
||||
|
||||
void selinux_complete_init(void)
|
||||
|
|
|
@ -59,14 +59,31 @@ static LIST_HEAD(smk_ipv6_port_list);
|
|||
static struct kmem_cache *smack_inode_cache;
|
||||
int smack_enabled;
|
||||
|
||||
static const match_table_t smk_mount_tokens = {
|
||||
{Opt_fsdefault, SMK_FSDEFAULT "%s"},
|
||||
{Opt_fsfloor, SMK_FSFLOOR "%s"},
|
||||
{Opt_fshat, SMK_FSHAT "%s"},
|
||||
{Opt_fsroot, SMK_FSROOT "%s"},
|
||||
{Opt_fstransmute, SMK_FSTRANS "%s"},
|
||||
{Opt_error, NULL},
|
||||
#define A(s) {"smack"#s, sizeof("smack"#s) - 1, Opt_##s}
|
||||
static struct {
|
||||
const char *name;
|
||||
int len;
|
||||
int opt;
|
||||
} smk_mount_opts[] = {
|
||||
A(fsdefault), A(fsfloor), A(fshat), A(fsroot), A(fstransmute)
|
||||
};
|
||||
#undef A
|
||||
|
||||
static int match_opt_prefix(char *s, int l, char **arg)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(smk_mount_opts); i++) {
|
||||
size_t len = smk_mount_opts[i].len;
|
||||
if (len > l || memcmp(s, smk_mount_opts[i].name, len))
|
||||
continue;
|
||||
if (len == l || s[len] != '=')
|
||||
continue;
|
||||
*arg = s + len + 1;
|
||||
return smk_mount_opts[i].opt;
|
||||
}
|
||||
return Opt_error;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
|
||||
static char *smk_bu_mess[] = {
|
||||
|
@ -567,175 +584,110 @@ static void smack_sb_free_security(struct super_block *sb)
|
|||
sb->s_security = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* smack_sb_copy_data - copy mount options data for processing
|
||||
* @orig: where to start
|
||||
* @smackopts: mount options string
|
||||
*
|
||||
* Returns 0 on success or -ENOMEM on error.
|
||||
*
|
||||
* Copy the Smack specific mount options out of the mount
|
||||
* options list.
|
||||
*/
|
||||
static int smack_sb_copy_data(char *orig, char *smackopts)
|
||||
struct smack_mnt_opts {
|
||||
const char *fsdefault, *fsfloor, *fshat, *fsroot, *fstransmute;
|
||||
};
|
||||
|
||||
static void smack_free_mnt_opts(void *mnt_opts)
|
||||
{
|
||||
char *cp, *commap, *otheropts, *dp;
|
||||
|
||||
otheropts = (char *)get_zeroed_page(GFP_KERNEL);
|
||||
if (otheropts == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
for (cp = orig, commap = orig; commap != NULL; cp = commap + 1) {
|
||||
if (strstr(cp, SMK_FSDEFAULT) == cp)
|
||||
dp = smackopts;
|
||||
else if (strstr(cp, SMK_FSFLOOR) == cp)
|
||||
dp = smackopts;
|
||||
else if (strstr(cp, SMK_FSHAT) == cp)
|
||||
dp = smackopts;
|
||||
else if (strstr(cp, SMK_FSROOT) == cp)
|
||||
dp = smackopts;
|
||||
else if (strstr(cp, SMK_FSTRANS) == cp)
|
||||
dp = smackopts;
|
||||
else
|
||||
dp = otheropts;
|
||||
|
||||
commap = strchr(cp, ',');
|
||||
if (commap != NULL)
|
||||
*commap = '\0';
|
||||
|
||||
if (*dp != '\0')
|
||||
strcat(dp, ",");
|
||||
strcat(dp, cp);
|
||||
}
|
||||
|
||||
strcpy(orig, otheropts);
|
||||
free_page((unsigned long)otheropts);
|
||||
|
||||
return 0;
|
||||
struct smack_mnt_opts *opts = mnt_opts;
|
||||
kfree(opts->fsdefault);
|
||||
kfree(opts->fsfloor);
|
||||
kfree(opts->fshat);
|
||||
kfree(opts->fsroot);
|
||||
kfree(opts->fstransmute);
|
||||
kfree(opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* smack_parse_opts_str - parse Smack specific mount options
|
||||
* @options: mount options string
|
||||
* @opts: where to store converted mount opts
|
||||
*
|
||||
* Returns 0 on success or -ENOMEM on error.
|
||||
*
|
||||
* converts Smack specific mount options to generic security option format
|
||||
*/
|
||||
static int smack_parse_opts_str(char *options,
|
||||
struct security_mnt_opts *opts)
|
||||
static int smack_add_opt(int token, const char *s, void **mnt_opts)
|
||||
{
|
||||
char *p;
|
||||
char *fsdefault = NULL;
|
||||
char *fsfloor = NULL;
|
||||
char *fshat = NULL;
|
||||
char *fsroot = NULL;
|
||||
char *fstransmute = NULL;
|
||||
int rc = -ENOMEM;
|
||||
int num_mnt_opts = 0;
|
||||
int token;
|
||||
struct smack_mnt_opts *opts = *mnt_opts;
|
||||
|
||||
opts->num_mnt_opts = 0;
|
||||
|
||||
if (!options)
|
||||
return 0;
|
||||
|
||||
while ((p = strsep(&options, ",")) != NULL) {
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
|
||||
if (!*p)
|
||||
continue;
|
||||
|
||||
token = match_token(p, smk_mount_tokens, args);
|
||||
if (!opts) {
|
||||
opts = kzalloc(sizeof(struct smack_mnt_opts), GFP_KERNEL);
|
||||
if (!opts)
|
||||
return -ENOMEM;
|
||||
*mnt_opts = opts;
|
||||
}
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
switch (token) {
|
||||
case Opt_fsdefault:
|
||||
if (fsdefault)
|
||||
if (opts->fsdefault)
|
||||
goto out_opt_err;
|
||||
fsdefault = match_strdup(&args[0]);
|
||||
if (!fsdefault)
|
||||
goto out_err;
|
||||
opts->fsdefault = s;
|
||||
break;
|
||||
case Opt_fsfloor:
|
||||
if (fsfloor)
|
||||
if (opts->fsfloor)
|
||||
goto out_opt_err;
|
||||
fsfloor = match_strdup(&args[0]);
|
||||
if (!fsfloor)
|
||||
goto out_err;
|
||||
opts->fsfloor = s;
|
||||
break;
|
||||
case Opt_fshat:
|
||||
if (fshat)
|
||||
if (opts->fshat)
|
||||
goto out_opt_err;
|
||||
fshat = match_strdup(&args[0]);
|
||||
if (!fshat)
|
||||
goto out_err;
|
||||
opts->fshat = s;
|
||||
break;
|
||||
case Opt_fsroot:
|
||||
if (fsroot)
|
||||
if (opts->fsroot)
|
||||
goto out_opt_err;
|
||||
fsroot = match_strdup(&args[0]);
|
||||
if (!fsroot)
|
||||
goto out_err;
|
||||
opts->fsroot = s;
|
||||
break;
|
||||
case Opt_fstransmute:
|
||||
if (fstransmute)
|
||||
if (opts->fstransmute)
|
||||
goto out_opt_err;
|
||||
fstransmute = match_strdup(&args[0]);
|
||||
if (!fstransmute)
|
||||
goto out_err;
|
||||
opts->fstransmute = s;
|
||||
break;
|
||||
default:
|
||||
rc = -EINVAL;
|
||||
pr_warn("Smack: unknown mount option\n");
|
||||
goto out_err;
|
||||
}
|
||||
}
|
||||
|
||||
opts->mnt_opts = kcalloc(NUM_SMK_MNT_OPTS, sizeof(char *), GFP_KERNEL);
|
||||
if (!opts->mnt_opts)
|
||||
goto out_err;
|
||||
|
||||
opts->mnt_opts_flags = kcalloc(NUM_SMK_MNT_OPTS, sizeof(int),
|
||||
GFP_KERNEL);
|
||||
if (!opts->mnt_opts_flags)
|
||||
goto out_err;
|
||||
|
||||
if (fsdefault) {
|
||||
opts->mnt_opts[num_mnt_opts] = fsdefault;
|
||||
opts->mnt_opts_flags[num_mnt_opts++] = FSDEFAULT_MNT;
|
||||
}
|
||||
if (fsfloor) {
|
||||
opts->mnt_opts[num_mnt_opts] = fsfloor;
|
||||
opts->mnt_opts_flags[num_mnt_opts++] = FSFLOOR_MNT;
|
||||
}
|
||||
if (fshat) {
|
||||
opts->mnt_opts[num_mnt_opts] = fshat;
|
||||
opts->mnt_opts_flags[num_mnt_opts++] = FSHAT_MNT;
|
||||
}
|
||||
if (fsroot) {
|
||||
opts->mnt_opts[num_mnt_opts] = fsroot;
|
||||
opts->mnt_opts_flags[num_mnt_opts++] = FSROOT_MNT;
|
||||
}
|
||||
if (fstransmute) {
|
||||
opts->mnt_opts[num_mnt_opts] = fstransmute;
|
||||
opts->mnt_opts_flags[num_mnt_opts++] = FSTRANS_MNT;
|
||||
}
|
||||
|
||||
opts->num_mnt_opts = num_mnt_opts;
|
||||
return 0;
|
||||
|
||||
out_opt_err:
|
||||
rc = -EINVAL;
|
||||
pr_warn("Smack: duplicate mount options\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
out_err:
|
||||
kfree(fsdefault);
|
||||
kfree(fsfloor);
|
||||
kfree(fshat);
|
||||
kfree(fsroot);
|
||||
kfree(fstransmute);
|
||||
static int smack_sb_eat_lsm_opts(char *options, void **mnt_opts)
|
||||
{
|
||||
char *from = options, *to = options;
|
||||
bool first = true;
|
||||
|
||||
while (1) {
|
||||
char *next = strchr(from, ',');
|
||||
int token, len, rc;
|
||||
char *arg = NULL;
|
||||
|
||||
if (next)
|
||||
len = next - from;
|
||||
else
|
||||
len = strlen(from);
|
||||
|
||||
token = match_opt_prefix(from, len, &arg);
|
||||
if (token != Opt_error) {
|
||||
arg = kmemdup_nul(arg, from + len - arg, GFP_KERNEL);
|
||||
rc = smack_add_opt(token, arg, mnt_opts);
|
||||
if (unlikely(rc)) {
|
||||
kfree(arg);
|
||||
if (*mnt_opts)
|
||||
smack_free_mnt_opts(*mnt_opts);
|
||||
*mnt_opts = NULL;
|
||||
return rc;
|
||||
}
|
||||
} else {
|
||||
if (!first) { // copy with preceding comma
|
||||
from--;
|
||||
len++;
|
||||
}
|
||||
if (to != from)
|
||||
memmove(to, from, len);
|
||||
to += len;
|
||||
first = false;
|
||||
}
|
||||
if (!from[len])
|
||||
break;
|
||||
from += len + 1;
|
||||
}
|
||||
*to = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -751,7 +703,7 @@ out_err:
|
|||
* labels.
|
||||
*/
|
||||
static int smack_set_mnt_opts(struct super_block *sb,
|
||||
struct security_mnt_opts *opts,
|
||||
void *mnt_opts,
|
||||
unsigned long kern_flags,
|
||||
unsigned long *set_kern_flags)
|
||||
{
|
||||
|
@ -760,9 +712,8 @@ static int smack_set_mnt_opts(struct super_block *sb,
|
|||
struct superblock_smack *sp = sb->s_security;
|
||||
struct inode_smack *isp;
|
||||
struct smack_known *skp;
|
||||
int i;
|
||||
int num_opts = opts->num_mnt_opts;
|
||||
int transmute = 0;
|
||||
struct smack_mnt_opts *opts = mnt_opts;
|
||||
bool transmute = false;
|
||||
|
||||
if (sp->smk_flags & SMK_SB_INITIALIZED)
|
||||
return 0;
|
||||
|
@ -771,7 +722,7 @@ static int smack_set_mnt_opts(struct super_block *sb,
|
|||
/*
|
||||
* Unprivileged mounts don't get to specify Smack values.
|
||||
*/
|
||||
if (num_opts)
|
||||
if (opts)
|
||||
return -EPERM;
|
||||
/*
|
||||
* Unprivileged mounts get root and default from the caller.
|
||||
|
@ -787,48 +738,44 @@ static int smack_set_mnt_opts(struct super_block *sb,
|
|||
if (sb->s_user_ns != &init_user_ns &&
|
||||
sb->s_magic != SYSFS_MAGIC && sb->s_magic != TMPFS_MAGIC &&
|
||||
sb->s_magic != RAMFS_MAGIC) {
|
||||
transmute = 1;
|
||||
transmute = true;
|
||||
sp->smk_flags |= SMK_SB_UNTRUSTED;
|
||||
}
|
||||
}
|
||||
|
||||
sp->smk_flags |= SMK_SB_INITIALIZED;
|
||||
|
||||
for (i = 0; i < num_opts; i++) {
|
||||
switch (opts->mnt_opts_flags[i]) {
|
||||
case FSDEFAULT_MNT:
|
||||
skp = smk_import_entry(opts->mnt_opts[i], 0);
|
||||
if (opts) {
|
||||
if (opts->fsdefault) {
|
||||
skp = smk_import_entry(opts->fsdefault, 0);
|
||||
if (IS_ERR(skp))
|
||||
return PTR_ERR(skp);
|
||||
sp->smk_default = skp;
|
||||
break;
|
||||
case FSFLOOR_MNT:
|
||||
skp = smk_import_entry(opts->mnt_opts[i], 0);
|
||||
}
|
||||
if (opts->fsfloor) {
|
||||
skp = smk_import_entry(opts->fsfloor, 0);
|
||||
if (IS_ERR(skp))
|
||||
return PTR_ERR(skp);
|
||||
sp->smk_floor = skp;
|
||||
break;
|
||||
case FSHAT_MNT:
|
||||
skp = smk_import_entry(opts->mnt_opts[i], 0);
|
||||
}
|
||||
if (opts->fshat) {
|
||||
skp = smk_import_entry(opts->fshat, 0);
|
||||
if (IS_ERR(skp))
|
||||
return PTR_ERR(skp);
|
||||
sp->smk_hat = skp;
|
||||
break;
|
||||
case FSROOT_MNT:
|
||||
skp = smk_import_entry(opts->mnt_opts[i], 0);
|
||||
}
|
||||
if (opts->fsroot) {
|
||||
skp = smk_import_entry(opts->fsroot, 0);
|
||||
if (IS_ERR(skp))
|
||||
return PTR_ERR(skp);
|
||||
sp->smk_root = skp;
|
||||
break;
|
||||
case FSTRANS_MNT:
|
||||
skp = smk_import_entry(opts->mnt_opts[i], 0);
|
||||
}
|
||||
if (opts->fstransmute) {
|
||||
skp = smk_import_entry(opts->fstransmute, 0);
|
||||
if (IS_ERR(skp))
|
||||
return PTR_ERR(skp);
|
||||
sp->smk_root = skp;
|
||||
transmute = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
transmute = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -850,37 +797,6 @@ static int smack_set_mnt_opts(struct super_block *sb,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* smack_sb_kern_mount - Smack specific mount processing
|
||||
* @sb: the file system superblock
|
||||
* @flags: the mount flags
|
||||
* @data: the smack mount options
|
||||
*
|
||||
* Returns 0 on success, an error code on failure
|
||||
*/
|
||||
static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data)
|
||||
{
|
||||
int rc = 0;
|
||||
char *options = data;
|
||||
struct security_mnt_opts opts;
|
||||
|
||||
security_init_mnt_opts(&opts);
|
||||
|
||||
if (!options)
|
||||
goto out;
|
||||
|
||||
rc = smack_parse_opts_str(options, &opts);
|
||||
if (rc)
|
||||
goto out_err;
|
||||
|
||||
out:
|
||||
rc = smack_set_mnt_opts(sb, &opts, 0, NULL);
|
||||
|
||||
out_err:
|
||||
security_free_mnt_opts(&opts);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* smack_sb_statfs - Smack check on statfs
|
||||
* @dentry: identifies the file system in question
|
||||
|
@ -4673,11 +4589,10 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
|
|||
|
||||
LSM_HOOK_INIT(sb_alloc_security, smack_sb_alloc_security),
|
||||
LSM_HOOK_INIT(sb_free_security, smack_sb_free_security),
|
||||
LSM_HOOK_INIT(sb_copy_data, smack_sb_copy_data),
|
||||
LSM_HOOK_INIT(sb_kern_mount, smack_sb_kern_mount),
|
||||
LSM_HOOK_INIT(sb_free_mnt_opts, smack_free_mnt_opts),
|
||||
LSM_HOOK_INIT(sb_eat_lsm_opts, smack_sb_eat_lsm_opts),
|
||||
LSM_HOOK_INIT(sb_statfs, smack_sb_statfs),
|
||||
LSM_HOOK_INIT(sb_set_mnt_opts, smack_set_mnt_opts),
|
||||
LSM_HOOK_INIT(sb_parse_opts_str, smack_parse_opts_str),
|
||||
|
||||
LSM_HOOK_INIT(bprm_set_creds, smack_bprm_set_creds),
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <uapi/linux/mount.h>
|
||||
#include "common.h"
|
||||
|
||||
/* String table for special mount operations. */
|
||||
|
|
Loading…
Reference in New Issue