Browse Source

Merge branch 'proc-per-ns'

Dmitry Torokhov says:

====================
Make /proc per net namespace objects belong to container

Currently [almost] all /proc objects belong to the global root, even if
data belongs to a given namespace within a container and (at least for
sysctls) we work around permssions checks to allow container's root to
access the data.

This series changes ownership of net namespace /proc objects
(/proc/net/self/* and /proc/sys/net/*) to be container's root and not
global root when there exists mapping for container's root in user
namespace.

This helps when running Android CTS in a container, but I think it makes
sense regardless.

Changes from V1:

- added fix for crash when !CONFIG_NET_NS (new patch #1)
- addressed Eric'c comments for error handling style in patch #3 and
  added his Ack
- adjusted patch #2 to use the same style of erro handling
- sent out as series instead of separate patches
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
David S. Miller 9 years ago
parent
commit
a878c02017
6 changed files with 61 additions and 13 deletions
  1. 2 0
      fs/proc/generic.c
  2. 13 0
      fs/proc/proc_net.c
  3. 5 0
      fs/proc/proc_sysctl.c
  4. 4 0
      include/linux/sysctl.h
  5. 17 4
      net/core/net_namespace.c
  6. 20 9
      net/sysctl_net.c

+ 2 - 0
fs/proc/generic.c

@@ -390,6 +390,8 @@ static struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent,
 	atomic_set(&ent->count, 1);
 	atomic_set(&ent->count, 1);
 	spin_lock_init(&ent->pde_unload_lock);
 	spin_lock_init(&ent->pde_unload_lock);
 	INIT_LIST_HEAD(&ent->pde_openers);
 	INIT_LIST_HEAD(&ent->pde_openers);
+	proc_set_user(ent, (*parent)->uid, (*parent)->gid);
+
 out:
 out:
 	return ent;
 	return ent;
 }
 }

+ 13 - 0
fs/proc/proc_net.c

@@ -21,6 +21,7 @@
 #include <linux/bitops.h>
 #include <linux/bitops.h>
 #include <linux/mount.h>
 #include <linux/mount.h>
 #include <linux/nsproxy.h>
 #include <linux/nsproxy.h>
+#include <linux/uidgid.h>
 #include <net/net_namespace.h>
 #include <net/net_namespace.h>
 #include <linux/seq_file.h>
 #include <linux/seq_file.h>
 
 
@@ -185,6 +186,8 @@ const struct file_operations proc_net_operations = {
 static __net_init int proc_net_ns_init(struct net *net)
 static __net_init int proc_net_ns_init(struct net *net)
 {
 {
 	struct proc_dir_entry *netd, *net_statd;
 	struct proc_dir_entry *netd, *net_statd;
+	kuid_t uid;
+	kgid_t gid;
 	int err;
 	int err;
 
 
 	err = -ENOMEM;
 	err = -ENOMEM;
@@ -199,6 +202,16 @@ static __net_init int proc_net_ns_init(struct net *net)
 	netd->parent = &proc_root;
 	netd->parent = &proc_root;
 	memcpy(netd->name, "net", 4);
 	memcpy(netd->name, "net", 4);
 
 
+	uid = make_kuid(net->user_ns, 0);
+	if (!uid_valid(uid))
+		uid = netd->uid;
+
+	gid = make_kgid(net->user_ns, 0);
+	if (!gid_valid(gid))
+		gid = netd->gid;
+
+	proc_set_user(netd, uid, gid);
+
 	err = -EEXIST;
 	err = -EEXIST;
 	net_statd = proc_net_mkdir(net, "stat", netd);
 	net_statd = proc_net_mkdir(net, "stat", netd);
 	if (!net_statd)
 	if (!net_statd)

+ 5 - 0
fs/proc/proc_sysctl.c

@@ -430,6 +430,7 @@ static int sysctl_perm(struct ctl_table_header *head, struct ctl_table *table, i
 static struct inode *proc_sys_make_inode(struct super_block *sb,
 static struct inode *proc_sys_make_inode(struct super_block *sb,
 		struct ctl_table_header *head, struct ctl_table *table)
 		struct ctl_table_header *head, struct ctl_table *table)
 {
 {
+	struct ctl_table_root *root = head->root;
 	struct inode *inode;
 	struct inode *inode;
 	struct proc_inode *ei;
 	struct proc_inode *ei;
 
 
@@ -457,6 +458,10 @@ static struct inode *proc_sys_make_inode(struct super_block *sb,
 		if (is_empty_dir(head))
 		if (is_empty_dir(head))
 			make_empty_dir_inode(inode);
 			make_empty_dir_inode(inode);
 	}
 	}
+
+	if (root->set_ownership)
+		root->set_ownership(head, table, &inode->i_uid, &inode->i_gid);
+
 out:
 out:
 	return inode;
 	return inode;
 }
 }

+ 4 - 0
include/linux/sysctl.h

@@ -25,6 +25,7 @@
 #include <linux/rcupdate.h>
 #include <linux/rcupdate.h>
 #include <linux/wait.h>
 #include <linux/wait.h>
 #include <linux/rbtree.h>
 #include <linux/rbtree.h>
+#include <linux/uidgid.h>
 #include <uapi/linux/sysctl.h>
 #include <uapi/linux/sysctl.h>
 
 
 /* For the /proc/sys support */
 /* For the /proc/sys support */
@@ -157,6 +158,9 @@ struct ctl_table_root {
 	struct ctl_table_set default_set;
 	struct ctl_table_set default_set;
 	struct ctl_table_set *(*lookup)(struct ctl_table_root *root,
 	struct ctl_table_set *(*lookup)(struct ctl_table_root *root,
 					   struct nsproxy *namespaces);
 					   struct nsproxy *namespaces);
+	void (*set_ownership)(struct ctl_table_header *head,
+			      struct ctl_table *table,
+			      kuid_t *uid, kgid_t *gid);
 	int (*permissions)(struct ctl_table_header *head, struct ctl_table *table);
 	int (*permissions)(struct ctl_table_header *head, struct ctl_table *table);
 };
 };
 
 

+ 17 - 4
net/core/net_namespace.c

@@ -37,6 +37,8 @@ struct net init_net = {
 };
 };
 EXPORT_SYMBOL(init_net);
 EXPORT_SYMBOL(init_net);
 
 
+static bool init_net_initialized;
+
 #define INITIAL_NET_GEN_PTRS	13 /* +1 for len +2 for rcu_head */
 #define INITIAL_NET_GEN_PTRS	13 /* +1 for len +2 for rcu_head */
 
 
 static unsigned int max_gen_ptrs = INITIAL_NET_GEN_PTRS;
 static unsigned int max_gen_ptrs = INITIAL_NET_GEN_PTRS;
@@ -750,6 +752,8 @@ static int __init net_ns_init(void)
 	if (setup_net(&init_net, &init_user_ns))
 	if (setup_net(&init_net, &init_user_ns))
 		panic("Could not setup the initial network namespace");
 		panic("Could not setup the initial network namespace");
 
 
+	init_net_initialized = true;
+
 	rtnl_lock();
 	rtnl_lock();
 	list_add_tail_rcu(&init_net.list, &net_namespace_list);
 	list_add_tail_rcu(&init_net.list, &net_namespace_list);
 	rtnl_unlock();
 	rtnl_unlock();
@@ -811,15 +815,24 @@ static void __unregister_pernet_operations(struct pernet_operations *ops)
 static int __register_pernet_operations(struct list_head *list,
 static int __register_pernet_operations(struct list_head *list,
 					struct pernet_operations *ops)
 					struct pernet_operations *ops)
 {
 {
+	if (!init_net_initialized) {
+		list_add_tail(&ops->list, list);
+		return 0;
+	}
+
 	return ops_init(ops, &init_net);
 	return ops_init(ops, &init_net);
 }
 }
 
 
 static void __unregister_pernet_operations(struct pernet_operations *ops)
 static void __unregister_pernet_operations(struct pernet_operations *ops)
 {
 {
-	LIST_HEAD(net_exit_list);
-	list_add(&init_net.exit_list, &net_exit_list);
-	ops_exit_list(ops, &net_exit_list);
-	ops_free_list(ops, &net_exit_list);
+	if (!init_net_initialized) {
+		list_del(&ops->list);
+	} else {
+		LIST_HEAD(net_exit_list);
+		list_add(&init_net.exit_list, &net_exit_list);
+		ops_exit_list(ops, &net_exit_list);
+		ops_free_list(ops, &net_exit_list);
+	}
 }
 }
 
 
 #endif /* CONFIG_NET_NS */
 #endif /* CONFIG_NET_NS */

+ 20 - 9
net/sysctl_net.c

@@ -42,26 +42,37 @@ static int net_ctl_permissions(struct ctl_table_header *head,
 			       struct ctl_table *table)
 			       struct ctl_table *table)
 {
 {
 	struct net *net = container_of(head->set, struct net, sysctls);
 	struct net *net = container_of(head->set, struct net, sysctls);
-	kuid_t root_uid = make_kuid(net->user_ns, 0);
-	kgid_t root_gid = make_kgid(net->user_ns, 0);
 
 
 	/* Allow network administrator to have same access as root. */
 	/* Allow network administrator to have same access as root. */
-	if (ns_capable_noaudit(net->user_ns, CAP_NET_ADMIN) ||
-	    uid_eq(root_uid, current_euid())) {
+	if (ns_capable(net->user_ns, CAP_NET_ADMIN)) {
 		int mode = (table->mode >> 6) & 7;
 		int mode = (table->mode >> 6) & 7;
 		return (mode << 6) | (mode << 3) | mode;
 		return (mode << 6) | (mode << 3) | mode;
 	}
 	}
-	/* Allow netns root group to have the same access as the root group */
-	if (in_egroup_p(root_gid)) {
-		int mode = (table->mode >> 3) & 7;
-		return (mode << 3) | mode;
-	}
+
 	return table->mode;
 	return table->mode;
 }
 }
 
 
+static void net_ctl_set_ownership(struct ctl_table_header *head,
+				  struct ctl_table *table,
+				  kuid_t *uid, kgid_t *gid)
+{
+	struct net *net = container_of(head->set, struct net, sysctls);
+	kuid_t ns_root_uid;
+	kgid_t ns_root_gid;
+
+	ns_root_uid = make_kuid(net->user_ns, 0);
+	if (uid_valid(ns_root_uid))
+		*uid = ns_root_uid;
+
+	ns_root_gid = make_kgid(net->user_ns, 0);
+	if (gid_valid(ns_root_gid))
+		*gid = ns_root_gid;
+}
+
 static struct ctl_table_root net_sysctl_root = {
 static struct ctl_table_root net_sysctl_root = {
 	.lookup = net_ctl_header_lookup,
 	.lookup = net_ctl_header_lookup,
 	.permissions = net_ctl_permissions,
 	.permissions = net_ctl_permissions,
+	.set_ownership = net_ctl_set_ownership,
 };
 };
 
 
 static int __net_init sysctl_net_init(struct net *net)
 static int __net_init sysctl_net_init(struct net *net)