sysfs: Restrict mounting sysfs
authorEric W. Biederman <ebiederm@xmission.com>
Tue, 26 Mar 2013 03:07:01 +0000 (20:07 -0700)
committerEric W. Biederman <ebiederm@xmission.com>
Thu, 29 Aug 2013 04:35:14 +0000 (21:35 -0700)
Don't allow mounting sysfs unless the caller has CAP_SYS_ADMIN rights
over the net namespace.  The principle here is if you create or have
capabilities over it you can mount it, otherwise you get to live with
what other people have mounted.

Instead of testing this with a straight forward ns_capable call,
perform this check the long and torturous way with kobject helpers,
this keeps direct knowledge of namespaces out of sysfs, and preserves
the existing sysfs abstractions.

Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
fs/sysfs/mount.c
include/linux/kobject_ns.h
lib/kobject.c
net/core/net-sysfs.c

index 4a2da3a4b1b11effd60dc54a80ceca5ca0d2a9d6..8c69ef49c7f39bc6c3e013d10e9614378075f4bb 100644 (file)
@@ -112,9 +112,15 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type,
        struct super_block *sb;
        int error;
 
-       if (!(flags & MS_KERNMOUNT) && !capable(CAP_SYS_ADMIN) &&
-           !fs_fully_visible(fs_type))
-               return ERR_PTR(-EPERM);
+       if (!(flags & MS_KERNMOUNT)) {
+               if (!capable(CAP_SYS_ADMIN) && !fs_fully_visible(fs_type))
+                       return ERR_PTR(-EPERM);
+
+               for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) {
+                       if (!kobj_ns_current_may_mount(type))
+                               return ERR_PTR(-EPERM);
+               }
+       }
 
        info = kzalloc(sizeof(*info), GFP_KERNEL);
        if (!info)
index f66b065a8b5ff1af1efdc698aadc248a79175917..df32d2508290aae03f539d0434030bf8a355bbd9 100644 (file)
@@ -39,6 +39,7 @@ enum kobj_ns_type {
  */
 struct kobj_ns_type_operations {
        enum kobj_ns_type type;
+       bool (*current_may_mount)(void);
        void *(*grab_current_ns)(void);
        const void *(*netlink_ns)(struct sock *sk);
        const void *(*initial_ns)(void);
@@ -50,6 +51,7 @@ int kobj_ns_type_registered(enum kobj_ns_type type);
 const struct kobj_ns_type_operations *kobj_child_ns_ops(struct kobject *parent);
 const struct kobj_ns_type_operations *kobj_ns_ops(struct kobject *kobj);
 
+bool kobj_ns_current_may_mount(enum kobj_ns_type type);
 void *kobj_ns_grab_current(enum kobj_ns_type type);
 const void *kobj_ns_netlink(enum kobj_ns_type type, struct sock *sk);
 const void *kobj_ns_initial(enum kobj_ns_type type);
index 4a1f33d435483c850d7b8b28f2409e19fc8ebf8c..3bbde222c90f743928674eb52a131850b4162905 100644 (file)
@@ -915,6 +915,21 @@ const struct kobj_ns_type_operations *kobj_ns_ops(struct kobject *kobj)
        return kobj_child_ns_ops(kobj->parent);
 }
 
+bool kobj_ns_current_may_mount(enum kobj_ns_type type)
+{
+       bool may_mount = false;
+
+       if (type == KOBJ_NS_TYPE_NONE)
+               return true;
+
+       spin_lock(&kobj_ns_type_lock);
+       if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) &&
+           kobj_ns_ops_tbl[type])
+               may_mount = kobj_ns_ops_tbl[type]->current_may_mount();
+       spin_unlock(&kobj_ns_type_lock);
+
+       return may_mount;
+}
 
 void *kobj_ns_grab_current(enum kobj_ns_type type)
 {
index 981fed397d1d8af3b0d607a1ac260ad0cc847ade..9bd9ae16adf5ae589b545a2fe18e6b2337b32122 100644 (file)
@@ -1157,6 +1157,13 @@ static void remove_queue_kobjects(struct net_device *net)
 #endif
 }
 
+static bool net_current_may_mount(void)
+{
+       struct net *net = current->nsproxy->net_ns;
+
+       return ns_capable(net->user_ns, CAP_SYS_ADMIN);
+}
+
 static void *net_grab_current_ns(void)
 {
        struct net *ns = current->nsproxy->net_ns;
@@ -1179,6 +1186,7 @@ static const void *net_netlink_ns(struct sock *sk)
 
 struct kobj_ns_type_operations net_ns_type_operations = {
        .type = KOBJ_NS_TYPE_NET,
+       .current_may_mount = net_current_may_mount,
        .grab_current_ns = net_grab_current_ns,
        .netlink_ns = net_netlink_ns,
        .initial_ns = net_initial_ns,