>From dfbd6496936ca2d707441f1dd21a64df846c468b Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 24 May 2012 18:00:49 -0600 Subject: [PATCH] userns: make each net (net_ns) belong to a user_ns The user namespace which creates a new network namespace owns that namespace and all resources created in it. This way we can target capability checks for privileged operations against network resources to the user_ns which created the network namespace in which the resource lives. Privilege to the user namespace which owns the network namespace, or any parent user namespace thereof, provides the same privilege to the network resource. This patch is reworked from a version originally by Serge E. Hallyn Signed-off-by: Eric W. Biederman --- include/net/net_namespace.h | 7 +++++-- kernel/nsproxy.c | 2 +- net/core/net_namespace.c | 22 +++++++++++++++++----- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index ac9195e..688d05c 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -16,18 +16,19 @@ #include #include #include #include #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) #include #endif #include +struct user_namespace; struct proc_dir_entry; struct net_device; struct sock; struct ctl_table_header; struct net_generic; struct sock; struct netns_ipvs; @@ -46,18 +47,20 @@ struct net { * destroy on demand */ #endif spinlock_t rules_mod_lock; struct list_head list; /* list of network namespaces */ struct list_head cleanup_list; /* namespaces on death row */ struct list_head exit_list; /* Use only net_mutex */ + struct user_namespace *user_ns; /* Owning user namespace */ + struct proc_dir_entry *proc_net; struct proc_dir_entry *proc_net_stat; #ifdef CONFIG_SYSCTL struct ctl_table_set sysctls; #endif struct sock *rtnl; /* rtnetlink socket */ struct sock *genl_sock; @@ -104,22 +107,22 @@ struct net { }; #include /* Init's network namespace */ extern struct net init_net; #ifdef CONFIG_NET -extern struct net *copy_net_ns(unsigned long flags, struct net *net_ns); +extern struct net *copy_net_ns(unsigned long flags, struct task_struct *tsk); #else /* CONFIG_NET */ -static inline struct net *copy_net_ns(unsigned long flags, struct net *net_ns) +static inline struct net *copy_net_ns(unsigned long flags, struct task_struct *tsk) { /* There is nothing to copy so this is a noop */ return net_ns; } #endif /* CONFIG_NET */ extern struct list_head net_namespace_list; diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c index b576f7f..bdf5722 100644 --- a/kernel/nsproxy.c +++ b/kernel/nsproxy.c @@ -84,19 +84,19 @@ static struct nsproxy *create_new_namespaces(unsigned long flags, goto out_ipc; } new_nsp->pid_ns = copy_pid_ns(flags, task_active_pid_ns(tsk)); if (IS_ERR(new_nsp->pid_ns)) { err = PTR_ERR(new_nsp->pid_ns); goto out_pid; } - new_nsp->net_ns = copy_net_ns(flags, tsk->nsproxy->net_ns); + new_nsp->net_ns = copy_net_ns(flags, tsk); if (IS_ERR(new_nsp->net_ns)) { err = PTR_ERR(new_nsp->net_ns); goto out_net; } return new_nsp; out_net: if (new_nsp->pid_ns) diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index dddbacb..f9d871b 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -7,18 +7,19 @@ #include #include #include #include #include #include #include #include #include +#include #include #include /* * Our network namespace constructor/destructor lists */ static LIST_HEAD(pernet_list); static struct list_head *first_device = &pernet_list; @@ -137,28 +138,29 @@ static void ops_free_list(const struct pernet_operations *ops, if (ops->size && ops->id) { list_for_each_entry(net, net_exit_list, exit_list) ops_free(ops, net); } } /* * setup_net runs the initializers for the network namespace object. */ -static __net_init int setup_net(struct net *net) +static __net_init int setup_net(struct net *net, struct user_namespace *user_ns) { /* Must be called with net_mutex held */ const struct pernet_operations *ops, *saved_ops; int error = 0; LIST_HEAD(net_exit_list); atomic_set(&net->count, 1); atomic_set(&net->passive, 1); net->dev_base_seq = 1; + net->user_ns = user_ns; #ifdef NETNS_REFCNT_DEBUG atomic_set(&net->use_count, 0); #endif list_for_each_entry(ops, &pernet_list, list) { error = ops_init(ops, net); if (error < 0) goto out_undo; @@ -224,38 +226,46 @@ static void net_free(struct net *net) } void net_drop_ns(void *p) { struct net *ns = p; if (ns && atomic_dec_and_test(&ns->passive)) net_free(ns); } -struct net *copy_net_ns(unsigned long flags, struct net *old_net) +struct net *copy_net_ns(unsigned long flags, struct task_struct *tsk) { + struct net *old_net = tsk->nsproxy->net_ns; struct net *net; + struct user_namespace *user_ns; int rv; if (!(flags & CLONE_NEWNET)) return get_net(old_net); net = net_alloc(); if (!net) return ERR_PTR(-ENOMEM); + + rcu_read_lock(); + user_ns = get_user_ns(__task_cred(tsk)->user_ns); + rcu_read_unlock(); + mutex_lock(&net_mutex); - rv = setup_net(net); + rv = setup_net(net, user_ns); if (rv == 0) { rtnl_lock(); list_add_tail_rcu(&net->list, &net_namespace_list); rtnl_unlock(); } mutex_unlock(&net_mutex); if (rv < 0) { + put_user_ns(user_ns); net_drop_ns(net); return ERR_PTR(rv); } return net; } static DEFINE_SPINLOCK(cleanup_list_lock); static LIST_HEAD(cleanup_list); /* Must hold cleanup_list_lock to touch */ @@ -300,18 +310,19 @@ static void cleanup_net(struct work_struct *work) /* Ensure there are no outstanding rcu callbacks using this * network namespace. */ rcu_barrier(); /* Finally it is safe to free my network namespace structure */ list_for_each_entry_safe(net, tmp, &net_exit_list, exit_list) { list_del_init(&net->exit_list); + put_user_ns(net->user_ns); net_drop_ns(net); } } static DECLARE_WORK(net_cleanup_work, cleanup_net); void __put_net(struct net *net) { /* Cleanup the network namespace in process context */ unsigned long flags; @@ -339,20 +350,21 @@ struct net *get_net_ns_by_fd(int fd) net = get_net(ei->ns); else net = ERR_PTR(-EINVAL); fput(file); return net; } #else -struct net *copy_net_ns(unsigned long flags, struct net *old_net) +struct net *copy_net_ns(unsigned long flags, struct task_struct *tsk) { + struct net *old_net = tsk->nsproxy->net_ns; if (flags & CLONE_NEWNET) return ERR_PTR(-EINVAL); return old_net; } struct net *get_net_ns_by_fd(int fd) { return ERR_PTR(-EINVAL); } @@ -394,19 +406,19 @@ static int __init net_ns_init(void) #endif ng = net_alloc_generic(); if (!ng) panic("Could not allocate generic netns"); rcu_assign_pointer(init_net.gen, ng); mutex_lock(&net_mutex); - if (setup_net(&init_net)) + if (setup_net(&init_net, &init_user_ns)) panic("Could not setup the initial network namespace"); rtnl_lock(); list_add_tail_rcu(&init_net.list, &net_namespace_list); rtnl_unlock(); mutex_unlock(&net_mutex); return 0; -- 1.7.10