xref: /linux/fs/proc/namespaces.c (revision 8dd06ef34b6e2f41b29fbf5fc1663780f2524285)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
26b4e306aSEric W. Biederman #include <linux/proc_fs.h>
36b4e306aSEric W. Biederman #include <linux/nsproxy.h>
46b4e306aSEric W. Biederman #include <linux/ptrace.h>
56b4e306aSEric W. Biederman #include <linux/namei.h>
66b4e306aSEric W. Biederman #include <linux/file.h>
76b4e306aSEric W. Biederman #include <linux/utsname.h>
86b4e306aSEric W. Biederman #include <net/net_namespace.h>
96b4e306aSEric W. Biederman #include <linux/ipc_namespace.h>
106b4e306aSEric W. Biederman #include <linux/pid_namespace.h>
11cde1975bSEric W. Biederman #include <linux/user_namespace.h>
126b4e306aSEric W. Biederman #include "internal.h"
136b4e306aSEric W. Biederman 
146b4e306aSEric W. Biederman 
156b4e306aSEric W. Biederman static const struct proc_ns_operations *ns_entries[] = {
1613b6f576SEric W. Biederman #ifdef CONFIG_NET_NS
1713b6f576SEric W. Biederman 	&netns_operations,
1813b6f576SEric W. Biederman #endif
1934482e89SEric W. Biederman #ifdef CONFIG_UTS_NS
2034482e89SEric W. Biederman 	&utsns_operations,
2134482e89SEric W. Biederman #endif
22a00eaf11SEric W. Biederman #ifdef CONFIG_IPC_NS
23a00eaf11SEric W. Biederman 	&ipcns_operations,
24a00eaf11SEric W. Biederman #endif
2557e8391dSEric W. Biederman #ifdef CONFIG_PID_NS
2657e8391dSEric W. Biederman 	&pidns_operations,
27eaa0d190SKirill Tkhai 	&pidns_for_children_operations,
2857e8391dSEric W. Biederman #endif
29cde1975bSEric W. Biederman #ifdef CONFIG_USER_NS
30cde1975bSEric W. Biederman 	&userns_operations,
31cde1975bSEric W. Biederman #endif
328823c079SEric W. Biederman 	&mntns_operations,
33a79a908fSAditya Kali #ifdef CONFIG_CGROUPS
34a79a908fSAditya Kali 	&cgroupns_operations,
35a79a908fSAditya Kali #endif
36*769071acSAndrei Vagin #ifdef CONFIG_TIME_NS
37*769071acSAndrei Vagin 	&timens_operations,
38*769071acSAndrei Vagin 	&timens_for_children_operations,
39*769071acSAndrei Vagin #endif
406b4e306aSEric W. Biederman };
416b4e306aSEric W. Biederman 
proc_ns_get_link(struct dentry * dentry,struct inode * inode,struct delayed_call * done)426b255391SAl Viro static const char *proc_ns_get_link(struct dentry *dentry,
43fceef393SAl Viro 				    struct inode *inode,
44fceef393SAl Viro 				    struct delayed_call *done)
45bf056bfaSEric W. Biederman {
463d3d35b1SAl Viro 	const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns_ops;
47bf056bfaSEric W. Biederman 	struct task_struct *task;
48db04dc67SEric W. Biederman 	struct path ns_path;
49ce623f89SAleksa Sarai 	int error = -EACCES;
50bf056bfaSEric W. Biederman 
516b255391SAl Viro 	if (!dentry)
526b255391SAl Viro 		return ERR_PTR(-ECHILD);
536b255391SAl Viro 
54bf056bfaSEric W. Biederman 	task = get_proc_task(inode);
55bf056bfaSEric W. Biederman 	if (!task)
56ce623f89SAleksa Sarai 		return ERR_PTR(-EACCES);
57bf056bfaSEric W. Biederman 
581bc82070SAleksa Sarai 	if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS))
591bc82070SAleksa Sarai 		goto out;
601bc82070SAleksa Sarai 
61e149ed2bSAl Viro 	error = ns_get_path(&ns_path, task, ns_ops);
621bc82070SAleksa Sarai 	if (error)
631bc82070SAleksa Sarai 		goto out;
641bc82070SAleksa Sarai 
651bc82070SAleksa Sarai 	error = nd_jump_link(&ns_path);
661bc82070SAleksa Sarai out:
67bf056bfaSEric W. Biederman 	put_task_struct(task);
68ce623f89SAleksa Sarai 	return ERR_PTR(error);
69bf056bfaSEric W. Biederman }
70bf056bfaSEric W. Biederman 
proc_ns_readlink(struct dentry * dentry,char __user * buffer,int buflen)71bf056bfaSEric W. Biederman static int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int buflen)
72bf056bfaSEric W. Biederman {
732b0143b5SDavid Howells 	struct inode *inode = d_inode(dentry);
743d3d35b1SAl Viro 	const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns_ops;
75bf056bfaSEric W. Biederman 	struct task_struct *task;
76bf056bfaSEric W. Biederman 	char name[50];
775d826c84SAl Viro 	int res = -EACCES;
78bf056bfaSEric W. Biederman 
79bf056bfaSEric W. Biederman 	task = get_proc_task(inode);
80bf056bfaSEric W. Biederman 	if (!task)
81e149ed2bSAl Viro 		return res;
82bf056bfaSEric W. Biederman 
83caaee623SJann Horn 	if (ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) {
84e149ed2bSAl Viro 		res = ns_get_name(name, sizeof(name), task, ns_ops);
85e149ed2bSAl Viro 		if (res >= 0)
865d826c84SAl Viro 			res = readlink_copy(buffer, buflen, name);
87e149ed2bSAl Viro 	}
88bf056bfaSEric W. Biederman 	put_task_struct(task);
895d826c84SAl Viro 	return res;
90bf056bfaSEric W. Biederman }
91bf056bfaSEric W. Biederman 
92bf056bfaSEric W. Biederman static const struct inode_operations proc_ns_link_inode_operations = {
93bf056bfaSEric W. Biederman 	.readlink	= proc_ns_readlink,
946b255391SAl Viro 	.get_link	= proc_ns_get_link,
95bf056bfaSEric W. Biederman 	.setattr	= proc_setattr,
96bf056bfaSEric W. Biederman };
97bf056bfaSEric W. Biederman 
proc_ns_instantiate(struct dentry * dentry,struct task_struct * task,const void * ptr)980168b9e3SAl Viro static struct dentry *proc_ns_instantiate(struct dentry *dentry,
990168b9e3SAl Viro 	struct task_struct *task, const void *ptr)
1006b4e306aSEric W. Biederman {
1016b4e306aSEric W. Biederman 	const struct proc_ns_operations *ns_ops = ptr;
1026b4e306aSEric W. Biederman 	struct inode *inode;
1036b4e306aSEric W. Biederman 	struct proc_inode *ei;
1046b4e306aSEric W. Biederman 
1050168b9e3SAl Viro 	inode = proc_pid_make_inode(dentry->d_sb, task, S_IFLNK | S_IRWXUGO);
1066b4e306aSEric W. Biederman 	if (!inode)
1070168b9e3SAl Viro 		return ERR_PTR(-ENOENT);
1086b4e306aSEric W. Biederman 
1096b4e306aSEric W. Biederman 	ei = PROC_I(inode);
110bf056bfaSEric W. Biederman 	inode->i_op = &proc_ns_link_inode_operations;
1113d3d35b1SAl Viro 	ei->ns_ops = ns_ops;
1121bbc5513SAl Viro 	pid_update_inode(task, inode);
1136b4e306aSEric W. Biederman 
1141b26c9b3SPravin B Shelar 	d_set_d_op(dentry, &pid_dentry_operations);
1150168b9e3SAl Viro 	return d_splice_alias(inode, dentry);
1166b4e306aSEric W. Biederman }
1176b4e306aSEric W. Biederman 
proc_ns_dir_readdir(struct file * file,struct dir_context * ctx)118f0c3b509SAl Viro static int proc_ns_dir_readdir(struct file *file, struct dir_context *ctx)
1196b4e306aSEric W. Biederman {
120f0c3b509SAl Viro 	struct task_struct *task = get_proc_task(file_inode(file));
1216b4e306aSEric W. Biederman 	const struct proc_ns_operations **entry, **last;
1226b4e306aSEric W. Biederman 
1236b4e306aSEric W. Biederman 	if (!task)
124f0c3b509SAl Viro 		return -ENOENT;
1256b4e306aSEric W. Biederman 
126f0c3b509SAl Viro 	if (!dir_emit_dots(file, ctx))
1276b4e306aSEric W. Biederman 		goto out;
128f0c3b509SAl Viro 	if (ctx->pos >= 2 + ARRAY_SIZE(ns_entries))
1296b4e306aSEric W. Biederman 		goto out;
130f0c3b509SAl Viro 	entry = ns_entries + (ctx->pos - 2);
1316b4e306aSEric W. Biederman 	last = &ns_entries[ARRAY_SIZE(ns_entries) - 1];
1326b4e306aSEric W. Biederman 	while (entry <= last) {
133f0c3b509SAl Viro 		const struct proc_ns_operations *ops = *entry;
134f0c3b509SAl Viro 		if (!proc_fill_cache(file, ctx, ops->name, strlen(ops->name),
135f0c3b509SAl Viro 				     proc_ns_instantiate, task, ops))
136f0c3b509SAl Viro 			break;
137f0c3b509SAl Viro 		ctx->pos++;
1386b4e306aSEric W. Biederman 		entry++;
1396b4e306aSEric W. Biederman 	}
1406b4e306aSEric W. Biederman out:
1416b4e306aSEric W. Biederman 	put_task_struct(task);
142f0c3b509SAl Viro 	return 0;
1436b4e306aSEric W. Biederman }
1446b4e306aSEric W. Biederman 
1456b4e306aSEric W. Biederman const struct file_operations proc_ns_dir_operations = {
1466b4e306aSEric W. Biederman 	.read		= generic_read_dir,
147f50752eaSAl Viro 	.iterate_shared	= proc_ns_dir_readdir,
148f50752eaSAl Viro 	.llseek		= generic_file_llseek,
1496b4e306aSEric W. Biederman };
1506b4e306aSEric W. Biederman 
proc_ns_dir_lookup(struct inode * dir,struct dentry * dentry,unsigned int flags)1516b4e306aSEric W. Biederman static struct dentry *proc_ns_dir_lookup(struct inode *dir,
15200cd8dd3SAl Viro 				struct dentry *dentry, unsigned int flags)
1536b4e306aSEric W. Biederman {
1546b4e306aSEric W. Biederman 	struct task_struct *task = get_proc_task(dir);
1556b4e306aSEric W. Biederman 	const struct proc_ns_operations **entry, **last;
1566b4e306aSEric W. Biederman 	unsigned int len = dentry->d_name.len;
1570168b9e3SAl Viro 	struct dentry *res = ERR_PTR(-ENOENT);
1586b4e306aSEric W. Biederman 
1596b4e306aSEric W. Biederman 	if (!task)
1606b4e306aSEric W. Biederman 		goto out_no_task;
1616b4e306aSEric W. Biederman 
1624c619aa0SAndrew Morton 	last = &ns_entries[ARRAY_SIZE(ns_entries)];
1634c619aa0SAndrew Morton 	for (entry = ns_entries; entry < last; entry++) {
1646b4e306aSEric W. Biederman 		if (strlen((*entry)->name) != len)
1656b4e306aSEric W. Biederman 			continue;
1666b4e306aSEric W. Biederman 		if (!memcmp(dentry->d_name.name, (*entry)->name, len))
1676b4e306aSEric W. Biederman 			break;
1686b4e306aSEric W. Biederman 	}
1694c619aa0SAndrew Morton 	if (entry == last)
1706b4e306aSEric W. Biederman 		goto out;
1716b4e306aSEric W. Biederman 
1720168b9e3SAl Viro 	res = proc_ns_instantiate(dentry, task, *entry);
1736b4e306aSEric W. Biederman out:
1746b4e306aSEric W. Biederman 	put_task_struct(task);
1756b4e306aSEric W. Biederman out_no_task:
1760168b9e3SAl Viro 	return res;
1776b4e306aSEric W. Biederman }
1786b4e306aSEric W. Biederman 
1796b4e306aSEric W. Biederman const struct inode_operations proc_ns_dir_inode_operations = {
1806b4e306aSEric W. Biederman 	.lookup		= proc_ns_dir_lookup,
1816b4e306aSEric W. Biederman 	.getattr	= pid_getattr,
1826b4e306aSEric W. Biederman 	.setattr	= proc_setattr,
1836b4e306aSEric W. Biederman };
184