.. | .. |
---|
19 | 19 | #include <linux/module.h> |
---|
20 | 20 | #include <linux/bitops.h> |
---|
21 | 21 | #include <linux/user_namespace.h> |
---|
| 22 | +#include <linux/fs_context.h> |
---|
22 | 23 | #include <linux/mount.h> |
---|
23 | 24 | #include <linux/pid_namespace.h> |
---|
24 | | -#include <linux/parser.h> |
---|
| 25 | +#include <linux/fs_parser.h> |
---|
25 | 26 | #include <linux/cred.h> |
---|
| 27 | +#include <linux/magic.h> |
---|
| 28 | +#include <linux/slab.h> |
---|
26 | 29 | |
---|
27 | 30 | #include "internal.h" |
---|
28 | 31 | |
---|
29 | | -static int proc_test_super(struct super_block *sb, void *data) |
---|
| 32 | +struct proc_fs_context { |
---|
| 33 | + struct pid_namespace *pid_ns; |
---|
| 34 | + unsigned int mask; |
---|
| 35 | + enum proc_hidepid hidepid; |
---|
| 36 | + int gid; |
---|
| 37 | + enum proc_pidonly pidonly; |
---|
| 38 | +}; |
---|
| 39 | + |
---|
| 40 | +enum proc_param { |
---|
| 41 | + Opt_gid, |
---|
| 42 | + Opt_hidepid, |
---|
| 43 | + Opt_subset, |
---|
| 44 | +}; |
---|
| 45 | + |
---|
| 46 | +static const struct fs_parameter_spec proc_fs_parameters[] = { |
---|
| 47 | + fsparam_u32("gid", Opt_gid), |
---|
| 48 | + fsparam_string("hidepid", Opt_hidepid), |
---|
| 49 | + fsparam_string("subset", Opt_subset), |
---|
| 50 | + {} |
---|
| 51 | +}; |
---|
| 52 | + |
---|
| 53 | +static inline int valid_hidepid(unsigned int value) |
---|
30 | 54 | { |
---|
31 | | - return sb->s_fs_info == data; |
---|
| 55 | + return (value == HIDEPID_OFF || |
---|
| 56 | + value == HIDEPID_NO_ACCESS || |
---|
| 57 | + value == HIDEPID_INVISIBLE || |
---|
| 58 | + value == HIDEPID_NOT_PTRACEABLE); |
---|
32 | 59 | } |
---|
33 | 60 | |
---|
34 | | -static int proc_set_super(struct super_block *sb, void *data) |
---|
| 61 | +static int proc_parse_hidepid_param(struct fs_context *fc, struct fs_parameter *param) |
---|
35 | 62 | { |
---|
36 | | - int err = set_anon_super(sb, NULL); |
---|
37 | | - if (!err) { |
---|
38 | | - struct pid_namespace *ns = (struct pid_namespace *)data; |
---|
39 | | - sb->s_fs_info = get_pid_ns(ns); |
---|
| 63 | + struct proc_fs_context *ctx = fc->fs_private; |
---|
| 64 | + struct fs_parameter_spec hidepid_u32_spec = fsparam_u32("hidepid", Opt_hidepid); |
---|
| 65 | + struct fs_parse_result result; |
---|
| 66 | + int base = (unsigned long)hidepid_u32_spec.data; |
---|
| 67 | + |
---|
| 68 | + if (param->type != fs_value_is_string) |
---|
| 69 | + return invalf(fc, "proc: unexpected type of hidepid value\n"); |
---|
| 70 | + |
---|
| 71 | + if (!kstrtouint(param->string, base, &result.uint_32)) { |
---|
| 72 | + if (!valid_hidepid(result.uint_32)) |
---|
| 73 | + return invalf(fc, "proc: unknown value of hidepid - %s\n", param->string); |
---|
| 74 | + ctx->hidepid = result.uint_32; |
---|
| 75 | + return 0; |
---|
40 | 76 | } |
---|
41 | | - return err; |
---|
| 77 | + |
---|
| 78 | + if (!strcmp(param->string, "off")) |
---|
| 79 | + ctx->hidepid = HIDEPID_OFF; |
---|
| 80 | + else if (!strcmp(param->string, "noaccess")) |
---|
| 81 | + ctx->hidepid = HIDEPID_NO_ACCESS; |
---|
| 82 | + else if (!strcmp(param->string, "invisible")) |
---|
| 83 | + ctx->hidepid = HIDEPID_INVISIBLE; |
---|
| 84 | + else if (!strcmp(param->string, "ptraceable")) |
---|
| 85 | + ctx->hidepid = HIDEPID_NOT_PTRACEABLE; |
---|
| 86 | + else |
---|
| 87 | + return invalf(fc, "proc: unknown value of hidepid - %s\n", param->string); |
---|
| 88 | + |
---|
| 89 | + return 0; |
---|
42 | 90 | } |
---|
43 | 91 | |
---|
44 | | -enum { |
---|
45 | | - Opt_gid, Opt_hidepid, Opt_err, |
---|
46 | | -}; |
---|
47 | | - |
---|
48 | | -static const match_table_t tokens = { |
---|
49 | | - {Opt_hidepid, "hidepid=%u"}, |
---|
50 | | - {Opt_gid, "gid=%u"}, |
---|
51 | | - {Opt_err, NULL}, |
---|
52 | | -}; |
---|
53 | | - |
---|
54 | | -static int proc_parse_options(char *options, struct pid_namespace *pid) |
---|
| 92 | +static int proc_parse_subset_param(struct fs_context *fc, char *value) |
---|
55 | 93 | { |
---|
56 | | - char *p; |
---|
57 | | - substring_t args[MAX_OPT_ARGS]; |
---|
58 | | - int option; |
---|
| 94 | + struct proc_fs_context *ctx = fc->fs_private; |
---|
59 | 95 | |
---|
60 | | - if (!options) |
---|
61 | | - return 1; |
---|
| 96 | + while (value) { |
---|
| 97 | + char *ptr = strchr(value, ','); |
---|
62 | 98 | |
---|
63 | | - while ((p = strsep(&options, ",")) != NULL) { |
---|
64 | | - int token; |
---|
65 | | - if (!*p) |
---|
66 | | - continue; |
---|
| 99 | + if (ptr != NULL) |
---|
| 100 | + *ptr++ = '\0'; |
---|
67 | 101 | |
---|
68 | | - args[0].to = args[0].from = NULL; |
---|
69 | | - token = match_token(p, tokens, args); |
---|
70 | | - switch (token) { |
---|
71 | | - case Opt_gid: |
---|
72 | | - if (match_int(&args[0], &option)) |
---|
73 | | - return 0; |
---|
74 | | - pid->pid_gid = make_kgid(current_user_ns(), option); |
---|
75 | | - break; |
---|
76 | | - case Opt_hidepid: |
---|
77 | | - if (match_int(&args[0], &option)) |
---|
78 | | - return 0; |
---|
79 | | - if (option < HIDEPID_OFF || |
---|
80 | | - option > HIDEPID_INVISIBLE) { |
---|
81 | | - pr_err("proc: hidepid value must be between 0 and 2.\n"); |
---|
82 | | - return 0; |
---|
| 102 | + if (*value != '\0') { |
---|
| 103 | + if (!strcmp(value, "pid")) { |
---|
| 104 | + ctx->pidonly = PROC_PIDONLY_ON; |
---|
| 105 | + } else { |
---|
| 106 | + return invalf(fc, "proc: unsupported subset option - %s\n", value); |
---|
83 | 107 | } |
---|
84 | | - pid->hide_pid = option; |
---|
85 | | - break; |
---|
86 | | - default: |
---|
87 | | - pr_err("proc: unrecognized mount option \"%s\" " |
---|
88 | | - "or missing value\n", p); |
---|
89 | | - return 0; |
---|
90 | 108 | } |
---|
| 109 | + value = ptr; |
---|
91 | 110 | } |
---|
92 | 111 | |
---|
93 | | - return 1; |
---|
| 112 | + return 0; |
---|
94 | 113 | } |
---|
95 | 114 | |
---|
96 | | -int proc_remount(struct super_block *sb, int *flags, char *data) |
---|
| 115 | +static int proc_parse_param(struct fs_context *fc, struct fs_parameter *param) |
---|
97 | 116 | { |
---|
98 | | - struct pid_namespace *pid = sb->s_fs_info; |
---|
| 117 | + struct proc_fs_context *ctx = fc->fs_private; |
---|
| 118 | + struct fs_parse_result result; |
---|
| 119 | + int opt; |
---|
| 120 | + |
---|
| 121 | + opt = fs_parse(fc, proc_fs_parameters, param, &result); |
---|
| 122 | + if (opt < 0) |
---|
| 123 | + return opt; |
---|
| 124 | + |
---|
| 125 | + switch (opt) { |
---|
| 126 | + case Opt_gid: |
---|
| 127 | + ctx->gid = result.uint_32; |
---|
| 128 | + break; |
---|
| 129 | + |
---|
| 130 | + case Opt_hidepid: |
---|
| 131 | + if (proc_parse_hidepid_param(fc, param)) |
---|
| 132 | + return -EINVAL; |
---|
| 133 | + break; |
---|
| 134 | + |
---|
| 135 | + case Opt_subset: |
---|
| 136 | + if (proc_parse_subset_param(fc, param->string) < 0) |
---|
| 137 | + return -EINVAL; |
---|
| 138 | + break; |
---|
| 139 | + |
---|
| 140 | + default: |
---|
| 141 | + return -EINVAL; |
---|
| 142 | + } |
---|
| 143 | + |
---|
| 144 | + ctx->mask |= 1 << opt; |
---|
| 145 | + return 0; |
---|
| 146 | +} |
---|
| 147 | + |
---|
| 148 | +static void proc_apply_options(struct proc_fs_info *fs_info, |
---|
| 149 | + struct fs_context *fc, |
---|
| 150 | + struct user_namespace *user_ns) |
---|
| 151 | +{ |
---|
| 152 | + struct proc_fs_context *ctx = fc->fs_private; |
---|
| 153 | + |
---|
| 154 | + if (ctx->mask & (1 << Opt_gid)) |
---|
| 155 | + fs_info->pid_gid = make_kgid(user_ns, ctx->gid); |
---|
| 156 | + if (ctx->mask & (1 << Opt_hidepid)) |
---|
| 157 | + fs_info->hide_pid = ctx->hidepid; |
---|
| 158 | + if (ctx->mask & (1 << Opt_subset)) |
---|
| 159 | + fs_info->pidonly = ctx->pidonly; |
---|
| 160 | +} |
---|
| 161 | + |
---|
| 162 | +static int proc_fill_super(struct super_block *s, struct fs_context *fc) |
---|
| 163 | +{ |
---|
| 164 | + struct proc_fs_context *ctx = fc->fs_private; |
---|
| 165 | + struct inode *root_inode; |
---|
| 166 | + struct proc_fs_info *fs_info; |
---|
| 167 | + int ret; |
---|
| 168 | + |
---|
| 169 | + fs_info = kzalloc(sizeof(*fs_info), GFP_KERNEL); |
---|
| 170 | + if (!fs_info) |
---|
| 171 | + return -ENOMEM; |
---|
| 172 | + |
---|
| 173 | + fs_info->pid_ns = get_pid_ns(ctx->pid_ns); |
---|
| 174 | + proc_apply_options(fs_info, fc, current_user_ns()); |
---|
| 175 | + |
---|
| 176 | + /* User space would break if executables or devices appear on proc */ |
---|
| 177 | + s->s_iflags |= SB_I_USERNS_VISIBLE | SB_I_NOEXEC | SB_I_NODEV; |
---|
| 178 | + s->s_flags |= SB_NODIRATIME | SB_NOSUID | SB_NOEXEC; |
---|
| 179 | + s->s_blocksize = 1024; |
---|
| 180 | + s->s_blocksize_bits = 10; |
---|
| 181 | + s->s_magic = PROC_SUPER_MAGIC; |
---|
| 182 | + s->s_op = &proc_sops; |
---|
| 183 | + s->s_time_gran = 1; |
---|
| 184 | + s->s_fs_info = fs_info; |
---|
| 185 | + |
---|
| 186 | + /* |
---|
| 187 | + * procfs isn't actually a stacking filesystem; however, there is |
---|
| 188 | + * too much magic going on inside it to permit stacking things on |
---|
| 189 | + * top of it |
---|
| 190 | + */ |
---|
| 191 | + s->s_stack_depth = FILESYSTEM_MAX_STACK_DEPTH; |
---|
| 192 | + |
---|
| 193 | + /* procfs dentries and inodes don't require IO to create */ |
---|
| 194 | + s->s_shrink.seeks = 0; |
---|
| 195 | + |
---|
| 196 | + pde_get(&proc_root); |
---|
| 197 | + root_inode = proc_get_inode(s, &proc_root); |
---|
| 198 | + if (!root_inode) { |
---|
| 199 | + pr_err("proc_fill_super: get root inode failed\n"); |
---|
| 200 | + return -ENOMEM; |
---|
| 201 | + } |
---|
| 202 | + |
---|
| 203 | + s->s_root = d_make_root(root_inode); |
---|
| 204 | + if (!s->s_root) { |
---|
| 205 | + pr_err("proc_fill_super: allocate dentry failed\n"); |
---|
| 206 | + return -ENOMEM; |
---|
| 207 | + } |
---|
| 208 | + |
---|
| 209 | + ret = proc_setup_self(s); |
---|
| 210 | + if (ret) { |
---|
| 211 | + return ret; |
---|
| 212 | + } |
---|
| 213 | + return proc_setup_thread_self(s); |
---|
| 214 | +} |
---|
| 215 | + |
---|
| 216 | +static int proc_reconfigure(struct fs_context *fc) |
---|
| 217 | +{ |
---|
| 218 | + struct super_block *sb = fc->root->d_sb; |
---|
| 219 | + struct proc_fs_info *fs_info = proc_sb_info(sb); |
---|
99 | 220 | |
---|
100 | 221 | sync_filesystem(sb); |
---|
101 | | - return !proc_parse_options(data, pid); |
---|
| 222 | + |
---|
| 223 | + proc_apply_options(fs_info, fc, current_user_ns()); |
---|
| 224 | + return 0; |
---|
102 | 225 | } |
---|
103 | 226 | |
---|
104 | | -static struct dentry *proc_mount(struct file_system_type *fs_type, |
---|
105 | | - int flags, const char *dev_name, void *data) |
---|
| 227 | +static int proc_get_tree(struct fs_context *fc) |
---|
106 | 228 | { |
---|
107 | | - int err; |
---|
108 | | - struct super_block *sb; |
---|
109 | | - struct pid_namespace *ns; |
---|
110 | | - char *options; |
---|
| 229 | + return get_tree_nodev(fc, proc_fill_super); |
---|
| 230 | +} |
---|
111 | 231 | |
---|
112 | | - if (flags & SB_KERNMOUNT) { |
---|
113 | | - ns = (struct pid_namespace *)data; |
---|
114 | | - options = NULL; |
---|
115 | | - } else { |
---|
116 | | - ns = task_active_pid_ns(current); |
---|
117 | | - options = data; |
---|
| 232 | +static void proc_fs_context_free(struct fs_context *fc) |
---|
| 233 | +{ |
---|
| 234 | + struct proc_fs_context *ctx = fc->fs_private; |
---|
118 | 235 | |
---|
119 | | - /* Does the mounter have privilege over the pid namespace? */ |
---|
120 | | - if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN)) |
---|
121 | | - return ERR_PTR(-EPERM); |
---|
122 | | - } |
---|
| 236 | + put_pid_ns(ctx->pid_ns); |
---|
| 237 | + kfree(ctx); |
---|
| 238 | +} |
---|
123 | 239 | |
---|
124 | | - sb = sget(fs_type, proc_test_super, proc_set_super, flags, ns); |
---|
125 | | - if (IS_ERR(sb)) |
---|
126 | | - return ERR_CAST(sb); |
---|
| 240 | +static const struct fs_context_operations proc_fs_context_ops = { |
---|
| 241 | + .free = proc_fs_context_free, |
---|
| 242 | + .parse_param = proc_parse_param, |
---|
| 243 | + .get_tree = proc_get_tree, |
---|
| 244 | + .reconfigure = proc_reconfigure, |
---|
| 245 | +}; |
---|
127 | 246 | |
---|
128 | | - if (!proc_parse_options(options, ns)) { |
---|
129 | | - deactivate_locked_super(sb); |
---|
130 | | - return ERR_PTR(-EINVAL); |
---|
131 | | - } |
---|
| 247 | +static int proc_init_fs_context(struct fs_context *fc) |
---|
| 248 | +{ |
---|
| 249 | + struct proc_fs_context *ctx; |
---|
132 | 250 | |
---|
133 | | - if (!sb->s_root) { |
---|
134 | | - err = proc_fill_super(sb); |
---|
135 | | - if (err) { |
---|
136 | | - deactivate_locked_super(sb); |
---|
137 | | - return ERR_PTR(err); |
---|
138 | | - } |
---|
| 251 | + ctx = kzalloc(sizeof(struct proc_fs_context), GFP_KERNEL); |
---|
| 252 | + if (!ctx) |
---|
| 253 | + return -ENOMEM; |
---|
139 | 254 | |
---|
140 | | - sb->s_flags |= MS_ACTIVE; |
---|
141 | | - /* User space would break if executables appear on proc */ |
---|
142 | | - sb->s_iflags |= SB_I_NOEXEC; |
---|
143 | | - } |
---|
144 | | - |
---|
145 | | - return dget(sb->s_root); |
---|
| 255 | + ctx->pid_ns = get_pid_ns(task_active_pid_ns(current)); |
---|
| 256 | + put_user_ns(fc->user_ns); |
---|
| 257 | + fc->user_ns = get_user_ns(ctx->pid_ns->user_ns); |
---|
| 258 | + fc->fs_private = ctx; |
---|
| 259 | + fc->ops = &proc_fs_context_ops; |
---|
| 260 | + return 0; |
---|
146 | 261 | } |
---|
147 | 262 | |
---|
148 | 263 | static void proc_kill_sb(struct super_block *sb) |
---|
149 | 264 | { |
---|
150 | | - struct pid_namespace *ns; |
---|
| 265 | + struct proc_fs_info *fs_info = proc_sb_info(sb); |
---|
151 | 266 | |
---|
152 | | - ns = (struct pid_namespace *)sb->s_fs_info; |
---|
153 | | - if (ns->proc_self) |
---|
154 | | - dput(ns->proc_self); |
---|
155 | | - if (ns->proc_thread_self) |
---|
156 | | - dput(ns->proc_thread_self); |
---|
| 267 | + if (!fs_info) { |
---|
| 268 | + kill_anon_super(sb); |
---|
| 269 | + return; |
---|
| 270 | + } |
---|
| 271 | + |
---|
| 272 | + dput(fs_info->proc_self); |
---|
| 273 | + dput(fs_info->proc_thread_self); |
---|
| 274 | + |
---|
157 | 275 | kill_anon_super(sb); |
---|
158 | | - put_pid_ns(ns); |
---|
| 276 | + put_pid_ns(fs_info->pid_ns); |
---|
| 277 | + kfree(fs_info); |
---|
159 | 278 | } |
---|
160 | 279 | |
---|
161 | 280 | static struct file_system_type proc_fs_type = { |
---|
162 | | - .name = "proc", |
---|
163 | | - .mount = proc_mount, |
---|
164 | | - .kill_sb = proc_kill_sb, |
---|
165 | | - .fs_flags = FS_USERNS_MOUNT, |
---|
| 281 | + .name = "proc", |
---|
| 282 | + .init_fs_context = proc_init_fs_context, |
---|
| 283 | + .parameters = proc_fs_parameters, |
---|
| 284 | + .kill_sb = proc_kill_sb, |
---|
| 285 | + .fs_flags = FS_USERNS_MOUNT | FS_DISALLOW_NOTIFY_PERM, |
---|
166 | 286 | }; |
---|
167 | 287 | |
---|
168 | 288 | void __init proc_root_init(void) |
---|
.. | .. |
---|
174 | 294 | proc_symlink("mounts", NULL, "self/mounts"); |
---|
175 | 295 | |
---|
176 | 296 | proc_net_init(); |
---|
177 | | - proc_uid_init(); |
---|
178 | 297 | proc_mkdir("fs", NULL); |
---|
179 | 298 | proc_mkdir("driver", NULL); |
---|
180 | 299 | proc_create_mount_point("fs/nfsd"); /* somewhere for the nfsd filesystem to be mounted */ |
---|
.. | .. |
---|
199 | 318 | |
---|
200 | 319 | static struct dentry *proc_root_lookup(struct inode * dir, struct dentry * dentry, unsigned int flags) |
---|
201 | 320 | { |
---|
202 | | - if (!proc_pid_lookup(dir, dentry, flags)) |
---|
| 321 | + if (!proc_pid_lookup(dentry, flags)) |
---|
203 | 322 | return NULL; |
---|
204 | | - |
---|
| 323 | + |
---|
205 | 324 | return proc_lookup(dir, dentry, flags); |
---|
206 | 325 | } |
---|
207 | 326 | |
---|
.. | .. |
---|
246 | 365 | .nlink = 2, |
---|
247 | 366 | .refcnt = REFCOUNT_INIT(1), |
---|
248 | 367 | .proc_iops = &proc_root_inode_operations, |
---|
249 | | - .proc_fops = &proc_root_operations, |
---|
| 368 | + .proc_dir_ops = &proc_root_operations, |
---|
250 | 369 | .parent = &proc_root, |
---|
251 | 370 | .subdir = RB_ROOT, |
---|
252 | 371 | .name = "/proc", |
---|
253 | 372 | }; |
---|
254 | | - |
---|
255 | | -int pid_ns_prepare_proc(struct pid_namespace *ns) |
---|
256 | | -{ |
---|
257 | | - struct vfsmount *mnt; |
---|
258 | | - |
---|
259 | | - mnt = kern_mount_data(&proc_fs_type, ns); |
---|
260 | | - if (IS_ERR(mnt)) |
---|
261 | | - return PTR_ERR(mnt); |
---|
262 | | - |
---|
263 | | - ns->proc_mnt = mnt; |
---|
264 | | - return 0; |
---|
265 | | -} |
---|
266 | | - |
---|
267 | | -void pid_ns_release_proc(struct pid_namespace *ns) |
---|
268 | | -{ |
---|
269 | | - kern_unmount(ns->proc_mnt); |
---|
270 | | -} |
---|