Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
6b4e306a EB |
2 | #include <linux/proc_fs.h> |
3 | #include <linux/nsproxy.h> | |
6b4e306a | 4 | #include <linux/ptrace.h> |
6b4e306a EB |
5 | #include <linux/namei.h> |
6 | #include <linux/file.h> | |
7 | #include <linux/utsname.h> | |
8 | #include <net/net_namespace.h> | |
6b4e306a EB |
9 | #include <linux/ipc_namespace.h> |
10 | #include <linux/pid_namespace.h> | |
cde1975b | 11 | #include <linux/user_namespace.h> |
6b4e306a EB |
12 | #include "internal.h" |
13 | ||
14 | ||
15 | static const struct proc_ns_operations *ns_entries[] = { | |
13b6f576 EB |
16 | #ifdef CONFIG_NET_NS |
17 | &netns_operations, | |
18 | #endif | |
34482e89 EB |
19 | #ifdef CONFIG_UTS_NS |
20 | &utsns_operations, | |
21 | #endif | |
a00eaf11 EB |
22 | #ifdef CONFIG_IPC_NS |
23 | &ipcns_operations, | |
24 | #endif | |
57e8391d EB |
25 | #ifdef CONFIG_PID_NS |
26 | &pidns_operations, | |
eaa0d190 | 27 | &pidns_for_children_operations, |
cde1975b EB |
28 | #endif |
29 | #ifdef CONFIG_USER_NS | |
30 | &userns_operations, | |
57e8391d | 31 | #endif |
8823c079 | 32 | &mntns_operations, |
a79a908f AK |
33 | #ifdef CONFIG_CGROUPS |
34 | &cgroupns_operations, | |
35 | #endif | |
769071ac AV |
36 | #ifdef CONFIG_TIME_NS |
37 | &timens_operations, | |
38 | &timens_for_children_operations, | |
39 | #endif | |
6b4e306a EB |
40 | }; |
41 | ||
6b255391 | 42 | static const char *proc_ns_get_link(struct dentry *dentry, |
fceef393 AV |
43 | struct inode *inode, |
44 | struct delayed_call *done) | |
bf056bfa | 45 | { |
3d3d35b1 | 46 | const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns_ops; |
bf056bfa | 47 | struct task_struct *task; |
db04dc67 | 48 | struct path ns_path; |
ce623f89 | 49 | int error = -EACCES; |
bf056bfa | 50 | |
6b255391 AV |
51 | if (!dentry) |
52 | return ERR_PTR(-ECHILD); | |
53 | ||
bf056bfa EB |
54 | task = get_proc_task(inode); |
55 | if (!task) | |
ce623f89 | 56 | return ERR_PTR(-EACCES); |
bf056bfa | 57 | |
1bc82070 AS |
58 | if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) |
59 | goto out; | |
60 | ||
61 | error = ns_get_path(&ns_path, task, ns_ops); | |
62 | if (error) | |
63 | goto out; | |
64 | ||
65 | error = nd_jump_link(&ns_path); | |
66 | out: | |
bf056bfa | 67 | put_task_struct(task); |
ce623f89 | 68 | return ERR_PTR(error); |
bf056bfa EB |
69 | } |
70 | ||
71 | static int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int buflen) | |
72 | { | |
2b0143b5 | 73 | struct inode *inode = d_inode(dentry); |
3d3d35b1 | 74 | const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns_ops; |
bf056bfa | 75 | struct task_struct *task; |
bf056bfa | 76 | char name[50]; |
5d826c84 | 77 | int res = -EACCES; |
bf056bfa EB |
78 | |
79 | task = get_proc_task(inode); | |
80 | if (!task) | |
e149ed2b | 81 | return res; |
bf056bfa | 82 | |
caaee623 | 83 | if (ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) { |
e149ed2b AV |
84 | res = ns_get_name(name, sizeof(name), task, ns_ops); |
85 | if (res >= 0) | |
86 | res = readlink_copy(buffer, buflen, name); | |
87 | } | |
bf056bfa | 88 | put_task_struct(task); |
5d826c84 | 89 | return res; |
bf056bfa EB |
90 | } |
91 | ||
92 | static const struct inode_operations proc_ns_link_inode_operations = { | |
93 | .readlink = proc_ns_readlink, | |
6b255391 | 94 | .get_link = proc_ns_get_link, |
bf056bfa EB |
95 | .setattr = proc_setattr, |
96 | }; | |
97 | ||
0168b9e3 AV |
98 | static struct dentry *proc_ns_instantiate(struct dentry *dentry, |
99 | struct task_struct *task, const void *ptr) | |
6b4e306a EB |
100 | { |
101 | const struct proc_ns_operations *ns_ops = ptr; | |
102 | struct inode *inode; | |
103 | struct proc_inode *ei; | |
6b4e306a | 104 | |
0168b9e3 | 105 | inode = proc_pid_make_inode(dentry->d_sb, task, S_IFLNK | S_IRWXUGO); |
6b4e306a | 106 | if (!inode) |
0168b9e3 | 107 | return ERR_PTR(-ENOENT); |
6b4e306a EB |
108 | |
109 | ei = PROC_I(inode); | |
bf056bfa | 110 | inode->i_op = &proc_ns_link_inode_operations; |
3d3d35b1 | 111 | ei->ns_ops = ns_ops; |
1bbc5513 | 112 | pid_update_inode(task, inode); |
6b4e306a | 113 | |
1b26c9b3 | 114 | d_set_d_op(dentry, &pid_dentry_operations); |
0168b9e3 | 115 | return d_splice_alias(inode, dentry); |
6b4e306a EB |
116 | } |
117 | ||
f0c3b509 | 118 | static int proc_ns_dir_readdir(struct file *file, struct dir_context *ctx) |
6b4e306a | 119 | { |
f0c3b509 | 120 | struct task_struct *task = get_proc_task(file_inode(file)); |
6b4e306a | 121 | const struct proc_ns_operations **entry, **last; |
6b4e306a | 122 | |
6b4e306a | 123 | if (!task) |
f0c3b509 | 124 | return -ENOENT; |
6b4e306a | 125 | |
f0c3b509 AV |
126 | if (!dir_emit_dots(file, ctx)) |
127 | goto out; | |
128 | if (ctx->pos >= 2 + ARRAY_SIZE(ns_entries)) | |
129 | goto out; | |
130 | entry = ns_entries + (ctx->pos - 2); | |
131 | last = &ns_entries[ARRAY_SIZE(ns_entries) - 1]; | |
132 | while (entry <= last) { | |
133 | const struct proc_ns_operations *ops = *entry; | |
134 | if (!proc_fill_cache(file, ctx, ops->name, strlen(ops->name), | |
135 | proc_ns_instantiate, task, ops)) | |
136 | break; | |
137 | ctx->pos++; | |
138 | entry++; | |
139 | } | |
6b4e306a EB |
140 | out: |
141 | put_task_struct(task); | |
f0c3b509 | 142 | return 0; |
6b4e306a EB |
143 | } |
144 | ||
145 | const struct file_operations proc_ns_dir_operations = { | |
146 | .read = generic_read_dir, | |
f50752ea AV |
147 | .iterate_shared = proc_ns_dir_readdir, |
148 | .llseek = generic_file_llseek, | |
6b4e306a EB |
149 | }; |
150 | ||
151 | static struct dentry *proc_ns_dir_lookup(struct inode *dir, | |
00cd8dd3 | 152 | struct dentry *dentry, unsigned int flags) |
6b4e306a | 153 | { |
6b4e306a EB |
154 | struct task_struct *task = get_proc_task(dir); |
155 | const struct proc_ns_operations **entry, **last; | |
156 | unsigned int len = dentry->d_name.len; | |
0168b9e3 | 157 | struct dentry *res = ERR_PTR(-ENOENT); |
6b4e306a EB |
158 | |
159 | if (!task) | |
160 | goto out_no_task; | |
161 | ||
4c619aa0 AM |
162 | last = &ns_entries[ARRAY_SIZE(ns_entries)]; |
163 | for (entry = ns_entries; entry < last; entry++) { | |
6b4e306a EB |
164 | if (strlen((*entry)->name) != len) |
165 | continue; | |
166 | if (!memcmp(dentry->d_name.name, (*entry)->name, len)) | |
167 | break; | |
168 | } | |
4c619aa0 | 169 | if (entry == last) |
6b4e306a EB |
170 | goto out; |
171 | ||
0168b9e3 | 172 | res = proc_ns_instantiate(dentry, task, *entry); |
6b4e306a EB |
173 | out: |
174 | put_task_struct(task); | |
175 | out_no_task: | |
0168b9e3 | 176 | return res; |
6b4e306a EB |
177 | } |
178 | ||
179 | const struct inode_operations proc_ns_dir_inode_operations = { | |
180 | .lookup = proc_ns_dir_lookup, | |
181 | .getattr = pid_getattr, | |
182 | .setattr = proc_setattr, | |
183 | }; |