[PATCH 2/2] Make /proc/net a symlink and drop proc shadows

From: Pavel Emelyanov
Date: Thu Feb 28 2008 - 10:52:24 EST


Turn /proc/net into a symlink, which behaves similar to /proc/self
link - it points to .netns/<id> directory where the <id> is the id
of net namespace, current task lives in.

# ls -l /proc/net
lrwxrwxrwx 1 root root 8 Feb 28 18:38 /proc/net -> .netns/0

The /proc/.netns dir contains subtrees for all the namespaces in
the system:

# ls -l /proc/.netns/
total 0
dr-xr-xr-x 5 root root 0 Feb 28 18:39 0
dr-xr-xr-x 3 root root 0 Feb 28 18:39 1

To provide some security each /proc/.netns/<id> directory allows
access to tasks that live in the owning namespace only (with the
exception, that init_net tasks can see everything).

Signed-off-by: Pavel Emelyanov <xemul@xxxxxxxxxx>

---

diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index 68971e6..d4103e5 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -227,7 +227,7 @@ static const struct file_operations proc_file_operations = {
.write = proc_file_write,
};

-static int proc_notify_change(struct dentry *dentry, struct iattr *iattr)
+int proc_notify_change(struct dentry *dentry, struct iattr *iattr)
{
struct inode *inode = dentry->d_inode;
struct proc_dir_entry *de = PDE(inode);
@@ -248,7 +248,7 @@ out:
return error;
}

-static int proc_getattr(struct vfsmount *mnt, struct dentry *dentry,
+int proc_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat)
{
struct inode *inode = dentry->d_inode;
@@ -393,8 +393,6 @@ struct dentry *proc_lookup(struct inode * dir, struct dentry *dentry, struct nam
if (!memcmp(dentry->d_name.name, de->name, de->namelen)) {
unsigned int ino;

- if (de->shadow_proc)
- de = de->shadow_proc(current, de);
ino = de->low_ino;
de_get(de);
spin_unlock(&proc_subdir_lock);
diff --git a/fs/proc/proc_net.c b/fs/proc/proc_net.c
index 14e9b5a..8000ea4 100644
--- a/fs/proc/proc_net.c
+++ b/fs/proc/proc_net.c
@@ -83,14 +83,6 @@ struct net *get_proc_net(const struct inode *inode)
}
EXPORT_SYMBOL_GPL(get_proc_net);

-static struct proc_dir_entry *shadow_pde;
-
-static struct proc_dir_entry *proc_net_shadow(struct task_struct *task,
- struct proc_dir_entry *de)
-{
- return task->nsproxy->net_ns->proc_net;
-}
-
struct proc_dir_entry *proc_net_mkdir(struct net *net, const char *name,
struct proc_dir_entry *parent)
{
@@ -102,47 +94,61 @@ struct proc_dir_entry *proc_net_mkdir(struct net *net, const char *name,
}
EXPORT_SYMBOL_GPL(proc_net_mkdir);

+static int proc_netns_id_permission(struct inode *inode, int mask,
+ struct nameidata *nd)
+{
+ struct net *net = current->nsproxy->net_ns;
+
+ if (net == &init_net || net == PDE(inode)->data)
+ return generic_permission(inode, mask, NULL);
+ else
+ return -EACCES;
+}
+
+static const struct inode_operations proc_netns_id_ops = {
+ .lookup = proc_lookup,
+ .getattr = proc_getattr,
+ .setattr = proc_notify_change,
+ .permission = proc_netns_id_permission,
+};
+
+static struct proc_dir_entry *netns_dir;
+
static __net_init int proc_net_ns_init(struct net *net)
{
- struct proc_dir_entry *root, *netd, *net_statd;
- int err;
+ struct proc_dir_entry *netd, *net_statd;
+ char id[PROC_NUMBUF];

- err = -ENOMEM;
- root = kzalloc(sizeof(*root), GFP_KERNEL);
- if (!root)
- goto out;
+ snprintf(id, sizeof(id), "%d", net->id);

- err = -EEXIST;
- netd = proc_net_mkdir(net, "net", root);
+ netd = proc_net_mkdir(net, id, netns_dir);
if (!netd)
- goto free_root;
+ goto out;
+
+ netd->proc_iops = &proc_netns_id_ops;

- err = -EEXIST;
net_statd = proc_net_mkdir(net, "stat", netd);
if (!net_statd)
goto free_net;

- root->data = net;
-
- net->proc_net_root = root;
net->proc_net = netd;
net->proc_net_stat = net_statd;
- err = 0;
+ return 0;

-out:
- return err;
free_net:
- remove_proc_entry("net", root);
-free_root:
- kfree(root);
- goto out;
+ remove_proc_entry(id, netns_dir);
+out:
+ return -ENOMEM;
}

static __net_exit void proc_net_ns_exit(struct net *net)
{
+ char id[PROC_NUMBUF];
+
+ snprintf(id, sizeof(id), "%d", net->id);
+
remove_proc_entry("stat", net->proc_net);
- remove_proc_entry("net", net->proc_net_root);
- kfree(net->proc_net_root);
+ remove_proc_entry(id, netns_dir);
}

static struct pernet_operations __net_initdata proc_net_ns_ops = {
@@ -150,10 +156,36 @@ static struct pernet_operations __net_initdata proc_net_ns_ops = {
.exit = proc_net_ns_exit,
};

+static int proc_net_readlink(struct dentry *dentry, char __user *buffer,
+ int buflen)
+{
+ char tmp[PROC_NUMBUF];
+
+ sprintf(tmp, ".netns/%d", current->nsproxy->net_ns->id);
+ return vfs_readlink(dentry, buffer, buflen, tmp);
+}
+
+static void *proc_net_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+ char tmp[PROC_NUMBUF];
+
+ sprintf(tmp, ".netns/%d", current->nsproxy->net_ns->id);
+ return ERR_PTR(vfs_follow_link(nd, tmp));
+}
+
+static const struct inode_operations proc_net_link_ops = {
+ .readlink = proc_net_readlink,
+ .follow_link = proc_net_follow_link,
+};
+
int __init proc_net_init(void)
{
- shadow_pde = proc_mkdir("net", NULL);
- shadow_pde->shadow_proc = proc_net_shadow;
+ struct proc_dir_entry *nnet;
+
+ netns_dir = proc_mkdir(".netns", NULL);
+ nnet = proc_symlink("net", NULL, ".netns/0");
+
+ nnet->proc_iops = &proc_net_link_ops;

return register_pernet_subsys(&proc_net_ns_ops);
}
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
index d9a9e71..be0c9b7 100644
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -82,7 +82,6 @@ struct proc_dir_entry {
int pde_users; /* number of callers into module in progress */
spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */
struct completion *pde_unload_completion;
- shadow_proc_t *shadow_proc;
};

struct kcore_list {
@@ -145,6 +144,9 @@ extern struct inode *proc_get_inode(struct super_block *, unsigned int, struct p
*/
extern int proc_readdir(struct file *, void *, filldir_t);
extern struct dentry *proc_lookup(struct inode *, struct dentry *, struct nameidata *);
+extern int proc_getattr(struct vfsmount *mnt, struct dentry *dentry,
+ struct kstat *stat);
+extern int proc_notify_change(struct dentry *dentry, struct iattr *iattr);

extern const struct file_operations proc_kcore_operations;
extern const struct file_operations proc_kmsg_operations;
diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h
index 28738b7..8aedfd6 100644
--- a/include/net/net_namespace.h
+++ b/include/net/net_namespace.h
@@ -32,7 +32,6 @@ struct net {

struct proc_dir_entry *proc_net;
struct proc_dir_entry *proc_net_stat;
- struct proc_dir_entry *proc_net_root;

struct list_head sysctl_table_headers;

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/