| .. | .. |
|---|
| 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 | | -} |
|---|