aboutsummaryrefslogtreecommitdiffstats
path: root/fs/proc/namespaces.c
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2011-06-18 20:48:18 -0400
committerEric W. Biederman <ebiederm@xmission.com>2012-11-20 07:19:48 -0500
commitbf056bfa80596a5d14b26b17276a56a0dcb080e5 (patch)
tree116db3255e1b75a92b30b72afe416aa941109e9f /fs/proc/namespaces.c
parent33d6dce607573b5fd7a43168e0d91221b3ca532b (diff)
proc: Fix the namespace inode permission checks.
Change the proc namespace files into symlinks so that we won't cache the dentries for the namespace files which can bypass the ptrace_may_access checks. To support the symlinks create an additional namespace inode with it's own set of operations distinct from the proc pid inode and dentry methods as those no longer make sense. Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
Diffstat (limited to 'fs/proc/namespaces.c')
-rw-r--r--fs/proc/namespaces.c169
1 files changed, 148 insertions, 21 deletions
diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c
index 030250c27d70..7a6d8d69cdb8 100644
--- a/fs/proc/namespaces.c
+++ b/fs/proc/namespaces.c
@@ -38,6 +38,151 @@ static const struct file_operations ns_file_operations = {
38 .llseek = no_llseek, 38 .llseek = no_llseek,
39}; 39};
40 40
41static const struct inode_operations ns_inode_operations = {
42 .setattr = proc_setattr,
43};
44
45static int ns_delete_dentry(const struct dentry *dentry)
46{
47 /* Don't cache namespace inodes when not in use */
48 return 1;
49}
50
51static char *ns_dname(struct dentry *dentry, char *buffer, int buflen)
52{
53 struct inode *inode = dentry->d_inode;
54 const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns_ops;
55
56 return dynamic_dname(dentry, buffer, buflen, "%s:[%lu]",
57 ns_ops->name, inode->i_ino);
58}
59
60const struct dentry_operations ns_dentry_operations =
61{
62 .d_delete = ns_delete_dentry,
63 .d_dname = ns_dname,
64};
65
66static struct dentry *proc_ns_get_dentry(struct super_block *sb,
67 struct task_struct *task, const struct proc_ns_operations *ns_ops)
68{
69 struct dentry *dentry, *result;
70 struct inode *inode;
71 struct proc_inode *ei;
72 struct qstr qname = { .name = "", };
73 void *ns;
74
75 ns = ns_ops->get(task);
76 if (!ns)
77 return ERR_PTR(-ENOENT);
78
79 dentry = d_alloc_pseudo(sb, &qname);
80 if (!dentry) {
81 ns_ops->put(ns);
82 return ERR_PTR(-ENOMEM);
83 }
84
85 inode = new_inode(sb);
86 if (!inode) {
87 dput(dentry);
88 ns_ops->put(ns);
89 return ERR_PTR(-ENOMEM);
90 }
91
92 ei = PROC_I(inode);
93 inode->i_ino = get_next_ino();
94 inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
95 inode->i_op = &ns_inode_operations;
96 inode->i_mode = S_IFREG | S_IRUGO;
97 inode->i_fop = &ns_file_operations;
98 ei->ns_ops = ns_ops;
99 ei->ns = ns;
100
101 d_set_d_op(dentry, &ns_dentry_operations);
102 result = d_instantiate_unique(dentry, inode);
103 if (result) {
104 dput(dentry);
105 dentry = result;
106 }
107
108 return dentry;
109}
110
111static void *proc_ns_follow_link(struct dentry *dentry, struct nameidata *nd)
112{
113 struct inode *inode = dentry->d_inode;
114 struct super_block *sb = inode->i_sb;
115 struct proc_inode *ei = PROC_I(inode);
116 struct task_struct *task;
117 struct dentry *ns_dentry;
118 void *error = ERR_PTR(-EACCES);
119
120 task = get_proc_task(inode);
121 if (!task)
122 goto out;
123
124 if (!ptrace_may_access(task, PTRACE_MODE_READ))
125 goto out_put_task;
126
127 ns_dentry = proc_ns_get_dentry(sb, task, ei->ns_ops);
128 if (IS_ERR(ns_dentry)) {
129 error = ERR_CAST(ns_dentry);
130 goto out_put_task;
131 }
132
133 dput(nd->path.dentry);
134 nd->path.dentry = ns_dentry;
135 error = NULL;
136
137out_put_task:
138 put_task_struct(task);
139out:
140 return error;
141}
142
143static int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int buflen)
144{
145 struct inode *inode = dentry->d_inode;
146 struct proc_inode *ei = PROC_I(inode);
147 const struct proc_ns_operations *ns_ops = ei->ns_ops;
148 struct task_struct *task;
149 void *ns;
150 char name[50];
151 int len = -EACCES;
152
153 task = get_proc_task(inode);
154 if (!task)
155 goto out;
156
157 if (!ptrace_may_access(task, PTRACE_MODE_READ))
158 goto out_put_task;
159
160 len = -ENOENT;
161 ns = ns_ops->get(task);
162 if (!ns)
163 goto out_put_task;
164
165 snprintf(name, sizeof(name), "%s", ns_ops->name);
166 len = strlen(name);
167
168 if (len > buflen)
169 len = buflen;
170 if (copy_to_user(buffer, ns_ops->name, len))
171 len = -EFAULT;
172
173 ns_ops->put(ns);
174out_put_task:
175 put_task_struct(task);
176out:
177 return len;
178}
179
180static const struct inode_operations proc_ns_link_inode_operations = {
181 .readlink = proc_ns_readlink,
182 .follow_link = proc_ns_follow_link,
183 .setattr = proc_setattr,
184};
185
41static struct dentry *proc_ns_instantiate(struct inode *dir, 186static struct dentry *proc_ns_instantiate(struct inode *dir,
42 struct dentry *dentry, struct task_struct *task, const void *ptr) 187 struct dentry *dentry, struct task_struct *task, const void *ptr)
43{ 188{
@@ -45,21 +190,15 @@ static struct dentry *proc_ns_instantiate(struct inode *dir,
45 struct inode *inode; 190 struct inode *inode;
46 struct proc_inode *ei; 191 struct proc_inode *ei;
47 struct dentry *error = ERR_PTR(-ENOENT); 192 struct dentry *error = ERR_PTR(-ENOENT);
48 void *ns;
49 193
50 inode = proc_pid_make_inode(dir->i_sb, task); 194 inode = proc_pid_make_inode(dir->i_sb, task);
51 if (!inode) 195 if (!inode)
52 goto out; 196 goto out;
53 197
54 ns = ns_ops->get(task);
55 if (!ns)
56 goto out_iput;
57
58 ei = PROC_I(inode); 198 ei = PROC_I(inode);
59 inode->i_mode = S_IFREG|S_IRUSR; 199 inode->i_mode = S_IFLNK|S_IRWXUGO;
60 inode->i_fop = &ns_file_operations; 200 inode->i_op = &proc_ns_link_inode_operations;
61 ei->ns_ops = ns_ops; 201 ei->ns_ops = ns_ops;
62 ei->ns = ns;
63 202
64 d_set_d_op(dentry, &pid_dentry_operations); 203 d_set_d_op(dentry, &pid_dentry_operations);
65 d_add(dentry, inode); 204 d_add(dentry, inode);
@@ -68,9 +207,6 @@ static struct dentry *proc_ns_instantiate(struct inode *dir,
68 error = NULL; 207 error = NULL;
69out: 208out:
70 return error; 209 return error;
71out_iput:
72 iput(inode);
73 goto out;
74} 210}
75 211
76static int proc_ns_fill_cache(struct file *filp, void *dirent, 212static int proc_ns_fill_cache(struct file *filp, void *dirent,
@@ -97,10 +233,6 @@ static int proc_ns_dir_readdir(struct file *filp, void *dirent,
97 if (!task) 233 if (!task)
98 goto out_no_task; 234 goto out_no_task;
99 235
100 ret = -EPERM;
101 if (!ptrace_may_access(task, PTRACE_MODE_READ))
102 goto out;
103
104 ret = 0; 236 ret = 0;
105 i = filp->f_pos; 237 i = filp->f_pos;
106 switch (i) { 238 switch (i) {
@@ -160,10 +292,6 @@ static struct dentry *proc_ns_dir_lookup(struct inode *dir,
160 if (!task) 292 if (!task)
161 goto out_no_task; 293 goto out_no_task;
162 294
163 error = ERR_PTR(-EPERM);
164 if (!ptrace_may_access(task, PTRACE_MODE_READ))
165 goto out;
166
167 last = &ns_entries[ARRAY_SIZE(ns_entries)]; 295 last = &ns_entries[ARRAY_SIZE(ns_entries)];
168 for (entry = ns_entries; entry < last; entry++) { 296 for (entry = ns_entries; entry < last; entry++) {
169 if (strlen((*entry)->name) != len) 297 if (strlen((*entry)->name) != len)
@@ -171,7 +299,6 @@ static struct dentry *proc_ns_dir_lookup(struct inode *dir,
171 if (!memcmp(dentry->d_name.name, (*entry)->name, len)) 299 if (!memcmp(dentry->d_name.name, (*entry)->name, len))
172 break; 300 break;
173 } 301 }
174 error = ERR_PTR(-ENOENT);
175 if (entry == last) 302 if (entry == last)
176 goto out; 303 goto out;
177 304