| 1 | |
| 2 | #include <linux/mount.h>
|
| 3 | #include <linux/pseudo_fs.h>
|
| 4 | #include <linux/file.h>
|
| 5 | #include <linux/fs.h>
|
| 6 | #include <linux/proc_fs.h>
|
| 7 | #include <linux/proc_ns.h>
|
| 8 | #include <linux/magic.h>
|
| 9 | #include <linux/ktime.h>
|
| 10 | #include <linux/seq_file.h>
|
| 11 | #include <linux/pid_namespace.h>
|
| 12 | #include <linux/user_namespace.h>
|
| 13 | #include <linux/nsfs.h>
|
| 14 | #include <linux/uaccess.h>
|
| 15 | #include <linux/mnt_namespace.h>
|
| 16 | #include <linux/ipc_namespace.h>
|
| 17 | #include <linux/time_namespace.h>
|
| 18 | #include <linux/utsname.h>
|
| 19 | #include <linux/exportfs.h>
|
| 20 | #include <linux/nstree.h>
|
| 21 | #include <net/net_namespace.h>
|
| 22 |
|
| 23 | #include "mount.h"
|
| 24 | #include "internal.h"
|
| 25 |
|
| 26 | static struct vfsmount *nsfs_mnt;
|
| 27 |
|
| 28 | static struct path nsfs_root_path = {};
|
| 29 |
|
| 30 | void nsfs_get_root(struct path *path)
|
| 31 | {
|
| 32 | *path = nsfs_root_path;
|
| 33 | path_get(path);
|
| 34 | }
|
| 35 |
|
| 36 | static long ns_ioctl(struct file *filp, unsigned int ioctl,
|
| 37 | unsigned long arg);
|
| 38 | static const struct file_operations ns_file_operations = {
|
| 39 | .unlocked_ioctl = ns_ioctl,
|
| 40 | .compat_ioctl = compat_ptr_ioctl,
|
| 41 | };
|
| 42 |
|
| 43 | static char *ns_dname(struct dentry *dentry, char *buffer, int buflen)
|
| 44 | {
|
| 45 | struct inode *inode = d_inode(dentry);
|
| 46 | struct ns_common *ns = inode->i_private;
|
| 47 | const struct proc_ns_operations *ns_ops = ns->ops;
|
| 48 |
|
| 49 | return dynamic_dname(buffer, buflen, "%s:[%llu]",
|
| 50 | ns_ops->name, inode->i_ino);
|
| 51 | }
|
| 52 |
|
| 53 | const struct dentry_operations ns_dentry_operations = {
|
| 54 | .d_dname = ns_dname,
|
| 55 | .d_prune = stashed_dentry_prune,
|
| 56 | };
|
| 57 |
|
| 58 | static void nsfs_evict(struct inode *inode)
|
| 59 | {
|
| 60 | struct ns_common *ns = inode->i_private;
|
| 61 |
|
| 62 | __ns_ref_active_put(ns);
|
| 63 | clear_inode(inode);
|
| 64 | ns->ops->put(ns);
|
| 65 | }
|
| 66 |
|
| 67 | int ns_get_path_cb(struct path *path, ns_get_path_helper_t *ns_get_cb,
|
| 68 | void *private_data)
|
| 69 | {
|
| 70 | struct ns_common *ns;
|
| 71 |
|
| 72 | ns = ns_get_cb(private_data);
|
| 73 | if (!ns)
|
| 74 | return -ENOENT;
|
| 75 |
|
| 76 | return path_from_stashed(&ns->stashed, nsfs_mnt, ns, path);
|
| 77 | }
|
| 78 |
|
| 79 | struct ns_get_path_task_args {
|
| 80 | const struct proc_ns_operations *ns_ops;
|
| 81 | struct task_struct *task;
|
| 82 | };
|
| 83 |
|
| 84 | static struct ns_common *ns_get_path_task(void *private_data)
|
| 85 | {
|
| 86 | struct ns_get_path_task_args *args = private_data;
|
| 87 |
|
| 88 | return args->ns_ops->get(args->task);
|
| 89 | }
|
| 90 |
|
| 91 | int ns_get_path(struct path *path, struct task_struct *task,
|
| 92 | const struct proc_ns_operations *ns_ops)
|
| 93 | {
|
| 94 | struct ns_get_path_task_args args = {
|
| 95 | .ns_ops = ns_ops,
|
| 96 | .task = task,
|
| 97 | };
|
| 98 |
|
| 99 | return ns_get_path_cb(path, ns_get_path_task, &args);
|
| 100 | }
|
| 101 |
|
| 102 | struct file *open_namespace_file(struct ns_common *ns)
|
| 103 | {
|
| 104 | struct path path __free(path_put) = {};
|
| 105 | int err;
|
| 106 |
|
| 107 |
|
| 108 | err = path_from_stashed(&ns->stashed, nsfs_mnt, ns, &path);
|
| 109 | if (err < 0)
|
| 110 | return ERR_PTR(err);
|
| 111 |
|
| 112 | return dentry_open(&path, O_RDONLY, current_cred());
|
| 113 | }
|
| 114 |
|
| 115 | |
| 116 | * open_namespace - open a namespace
|
| 117 | * @ns: the namespace to open
|
| 118 | *
|
| 119 | * This will consume a reference to @ns indendent of success or failure.
|
| 120 | *
|
| 121 | * Return: A file descriptor on success or a negative error code on failure.
|
| 122 |
|
| 123 | int open_namespace(struct ns_common *ns)
|
| 124 | {
|
| 125 | struct path path __free(path_put) = {};
|
| 126 | int err;
|
| 127 |
|
| 128 |
|
| 129 | err = path_from_stashed(&ns->stashed, nsfs_mnt, ns, &path);
|
| 130 | if (err < 0)
|
| 131 | return err;
|
| 132 |
|
| 133 | return FD_ADD(O_CLOEXEC, dentry_open(&path, O_RDONLY, current_cred()));
|
| 134 | }
|
| 135 |
|
| 136 | int open_related_ns(struct ns_common *ns,
|
| 137 | struct ns_common *(*get_ns)(struct ns_common *ns))
|
| 138 | {
|
| 139 | struct ns_common *relative;
|
| 140 |
|
| 141 | relative = get_ns(ns);
|
| 142 | if (IS_ERR(relative))
|
| 143 | return PTR_ERR(relative);
|
| 144 |
|
| 145 | return open_namespace(relative);
|
| 146 | }
|
| 147 | EXPORT_SYMBOL_GPL(open_related_ns);
|
| 148 |
|
| 149 | static int copy_ns_info_to_user(const struct mnt_namespace *mnt_ns,
|
| 150 | struct mnt_ns_info __user *uinfo, size_t usize,
|
| 151 | struct mnt_ns_info *kinfo)
|
| 152 | {
|
| 153 | |
| 154 | * If userspace and the kernel have the same struct size it can just
|
| 155 | * be copied. If userspace provides an older struct, only the bits that
|
| 156 | * userspace knows about will be copied. If userspace provides a new
|
| 157 | * struct, only the bits that the kernel knows aobut will be copied and
|
| 158 | * the size value will be set to the size the kernel knows about.
|
| 159 |
|
| 160 | kinfo->size = min(usize, sizeof(*kinfo));
|
| 161 | kinfo->mnt_ns_id = mnt_ns->ns.ns_id;
|
| 162 | kinfo->nr_mounts = READ_ONCE(mnt_ns->nr_mounts);
|
| 163 |
|
| 164 | if (kinfo->nr_mounts)
|
| 165 | kinfo->nr_mounts--;
|
| 166 |
|
| 167 | if (copy_to_user(uinfo, kinfo, kinfo->size))
|
| 168 | return -EFAULT;
|
| 169 |
|
| 170 | return 0;
|
| 171 | }
|
| 172 |
|
| 173 | static bool nsfs_ioctl_valid(unsigned int cmd)
|
| 174 | {
|
| 175 | switch (cmd) {
|
| 176 | case NS_GET_USERNS:
|
| 177 | case NS_GET_PARENT:
|
| 178 | case NS_GET_NSTYPE:
|
| 179 | case NS_GET_OWNER_UID:
|
| 180 | case NS_GET_MNTNS_ID:
|
| 181 | case NS_GET_PID_FROM_PIDNS:
|
| 182 | case NS_GET_TGID_FROM_PIDNS:
|
| 183 | case NS_GET_PID_IN_PIDNS:
|
| 184 | case NS_GET_TGID_IN_PIDNS:
|
| 185 | case NS_GET_ID:
|
| 186 | return true;
|
| 187 | }
|
| 188 |
|
| 189 |
|
| 190 | switch (_IOC_NR(cmd)) {
|
| 191 | case _IOC_NR(NS_MNT_GET_INFO):
|
| 192 | return extensible_ioctl_valid(cmd, NS_MNT_GET_INFO, MNT_NS_INFO_SIZE_VER0);
|
| 193 | case _IOC_NR(NS_MNT_GET_NEXT):
|
| 194 | return extensible_ioctl_valid(cmd, NS_MNT_GET_NEXT, MNT_NS_INFO_SIZE_VER0);
|
| 195 | case _IOC_NR(NS_MNT_GET_PREV):
|
| 196 | return extensible_ioctl_valid(cmd, NS_MNT_GET_PREV, MNT_NS_INFO_SIZE_VER0);
|
| 197 | }
|
| 198 |
|
| 199 | return false;
|
| 200 | }
|
| 201 |
|
| 202 | static bool may_use_nsfs_ioctl(unsigned int cmd)
|
| 203 | {
|
| 204 | switch (_IOC_NR(cmd)) {
|
| 205 | case _IOC_NR(NS_MNT_GET_NEXT):
|
| 206 | fallthrough;
|
| 207 | case _IOC_NR(NS_MNT_GET_PREV):
|
| 208 | return may_see_all_namespaces();
|
| 209 | }
|
| 210 | return true;
|
| 211 | }
|
| 212 |
|
| 213 | static long ns_ioctl(struct file *filp, unsigned int ioctl,
|
| 214 | unsigned long arg)
|
| 215 | {
|
| 216 | struct user_namespace *user_ns;
|
| 217 | struct pid_namespace *pid_ns;
|
| 218 | struct task_struct *tsk;
|
| 219 | struct ns_common *ns;
|
| 220 | struct mnt_namespace *mnt_ns;
|
| 221 | bool previous = false;
|
| 222 | uid_t __user *argp;
|
| 223 | uid_t uid;
|
| 224 | int ret;
|
| 225 |
|
| 226 | if (!nsfs_ioctl_valid(ioctl))
|
| 227 | return -ENOIOCTLCMD;
|
| 228 | if (!may_use_nsfs_ioctl(ioctl))
|
| 229 | return -EPERM;
|
| 230 |
|
| 231 | ns = get_proc_ns(file_inode(filp));
|
| 232 | switch (ioctl) {
|
| 233 | case NS_GET_USERNS:
|
| 234 | return open_related_ns(ns, ns_get_owner);
|
| 235 | case NS_GET_PARENT:
|
| 236 | if (!ns->ops->get_parent)
|
| 237 | return -EINVAL;
|
| 238 | return open_related_ns(ns, ns->ops->get_parent);
|
| 239 | case NS_GET_NSTYPE:
|
| 240 | return ns->ns_type;
|
| 241 | case NS_GET_OWNER_UID:
|
| 242 | if (ns->ns_type != CLONE_NEWUSER)
|
| 243 | return -EINVAL;
|
| 244 | user_ns = container_of(ns, struct user_namespace, ns);
|
| 245 | argp = (uid_t __user *) arg;
|
| 246 | uid = from_kuid_munged(current_user_ns(), user_ns->owner);
|
| 247 | return put_user(uid, argp);
|
| 248 | case NS_GET_PID_FROM_PIDNS:
|
| 249 | fallthrough;
|
| 250 | case NS_GET_TGID_FROM_PIDNS:
|
| 251 | fallthrough;
|
| 252 | case NS_GET_PID_IN_PIDNS:
|
| 253 | fallthrough;
|
| 254 | case NS_GET_TGID_IN_PIDNS: {
|
| 255 | if (ns->ns_type != CLONE_NEWPID)
|
| 256 | return -EINVAL;
|
| 257 |
|
| 258 | ret = -ESRCH;
|
| 259 | pid_ns = container_of(ns, struct pid_namespace, ns);
|
| 260 |
|
| 261 | guard(rcu)();
|
| 262 |
|
| 263 | if (ioctl == NS_GET_PID_IN_PIDNS ||
|
| 264 | ioctl == NS_GET_TGID_IN_PIDNS)
|
| 265 | tsk = find_task_by_vpid(arg);
|
| 266 | else
|
| 267 | tsk = find_task_by_pid_ns(arg, pid_ns);
|
| 268 | if (!tsk)
|
| 269 | return ret;
|
| 270 |
|
| 271 | switch (ioctl) {
|
| 272 | case NS_GET_PID_FROM_PIDNS:
|
| 273 | ret = task_pid_vnr(tsk);
|
| 274 | break;
|
| 275 | case NS_GET_TGID_FROM_PIDNS:
|
| 276 | ret = task_tgid_vnr(tsk);
|
| 277 | break;
|
| 278 | case NS_GET_PID_IN_PIDNS:
|
| 279 | ret = task_pid_nr_ns(tsk, pid_ns);
|
| 280 | break;
|
| 281 | case NS_GET_TGID_IN_PIDNS:
|
| 282 | ret = task_tgid_nr_ns(tsk, pid_ns);
|
| 283 | break;
|
| 284 | default:
|
| 285 | ret = 0;
|
| 286 | break;
|
| 287 | }
|
| 288 |
|
| 289 | if (!ret)
|
| 290 | ret = -ESRCH;
|
| 291 | return ret;
|
| 292 | }
|
| 293 | case NS_GET_MNTNS_ID:
|
| 294 | if (ns->ns_type != CLONE_NEWNS)
|
| 295 | return -EINVAL;
|
| 296 | fallthrough;
|
| 297 | case NS_GET_ID: {
|
| 298 | __u64 __user *idp;
|
| 299 | __u64 id;
|
| 300 |
|
| 301 | idp = (__u64 __user *)arg;
|
| 302 | id = ns->ns_id;
|
| 303 | return put_user(id, idp);
|
| 304 | }
|
| 305 | }
|
| 306 |
|
| 307 |
|
| 308 | switch (_IOC_NR(ioctl)) {
|
| 309 | case _IOC_NR(NS_MNT_GET_INFO): {
|
| 310 | struct mnt_ns_info kinfo = {};
|
| 311 | struct mnt_ns_info __user *uinfo = (struct mnt_ns_info __user *)arg;
|
| 312 | size_t usize = _IOC_SIZE(ioctl);
|
| 313 |
|
| 314 | if (ns->ns_type != CLONE_NEWNS)
|
| 315 | return -EINVAL;
|
| 316 |
|
| 317 | if (!uinfo)
|
| 318 | return -EINVAL;
|
| 319 |
|
| 320 | if (usize < MNT_NS_INFO_SIZE_VER0)
|
| 321 | return -EINVAL;
|
| 322 |
|
| 323 | return copy_ns_info_to_user(to_mnt_ns(ns), uinfo, usize, &kinfo);
|
| 324 | }
|
| 325 | case _IOC_NR(NS_MNT_GET_PREV):
|
| 326 | previous = true;
|
| 327 | fallthrough;
|
| 328 | case _IOC_NR(NS_MNT_GET_NEXT): {
|
| 329 | struct mnt_ns_info kinfo = {};
|
| 330 | struct mnt_ns_info __user *uinfo = (struct mnt_ns_info __user *)arg;
|
| 331 | struct path path __free(path_put) = {};
|
| 332 | size_t usize = _IOC_SIZE(ioctl);
|
| 333 |
|
| 334 | if (ns->ns_type != CLONE_NEWNS)
|
| 335 | return -EINVAL;
|
| 336 |
|
| 337 | if (usize < MNT_NS_INFO_SIZE_VER0)
|
| 338 | return -EINVAL;
|
| 339 |
|
| 340 | mnt_ns = get_sequential_mnt_ns(to_mnt_ns(ns), previous);
|
| 341 | if (IS_ERR(mnt_ns))
|
| 342 | return PTR_ERR(mnt_ns);
|
| 343 |
|
| 344 | ns = to_ns_common(mnt_ns);
|
| 345 |
|
| 346 | ret = path_from_stashed(&ns->stashed, nsfs_mnt, ns, &path);
|
| 347 | if (ret)
|
| 348 | return ret;
|
| 349 |
|
| 350 | FD_PREPARE(fdf, O_CLOEXEC, dentry_open(&path, O_RDONLY, current_cred()));
|
| 351 | if (fdf->fd < 0)
|
| 352 | return fdf->fd;
|
| 353 | |
| 354 | * If @uinfo is passed return all information about the
|
| 355 | * mount namespace as well.
|
| 356 |
|
| 357 | ret = copy_ns_info_to_user(to_mnt_ns(ns), uinfo, usize, &kinfo);
|
| 358 | if (ret)
|
| 359 | return ret;
|
| 360 | ret = fd_publish(fdf);
|
| 361 | break;
|
| 362 | }
|
| 363 | default:
|
| 364 | ret = -ENOTTY;
|
| 365 | }
|
| 366 |
|
| 367 | return ret;
|
| 368 | }
|
| 369 |
|
| 370 | int ns_get_name(char *buf, size_t size, struct task_struct *task,
|
| 371 | const struct proc_ns_operations *ns_ops)
|
| 372 | {
|
| 373 | struct ns_common *ns;
|
| 374 | int res = -ENOENT;
|
| 375 | const char *name;
|
| 376 | ns = ns_ops->get(task);
|
| 377 | if (ns) {
|
| 378 | name = ns_ops->real_ns_name ? : ns_ops->name;
|
| 379 | res = snprintf(buf, size, "%s:[%u]", name, ns->inum);
|
| 380 | ns_ops->put(ns);
|
| 381 | }
|
| 382 | return res;
|
| 383 | }
|
| 384 |
|
| 385 | bool proc_ns_file(const struct file *file)
|
| 386 | {
|
| 387 | return file->f_op == &ns_file_operations;
|
| 388 | }
|
| 389 |
|
| 390 | |
| 391 | * ns_match() - Returns true if current namespace matches dev/ino provided.
|
| 392 | * @ns: current namespace
|
| 393 | * @dev: dev_t from nsfs that will be matched against current nsfs
|
| 394 | * @ino: ino_t from nsfs that will be matched against current nsfs
|
| 395 | *
|
| 396 | * Return: true if dev and ino matches the current nsfs.
|
| 397 |
|
| 398 | bool ns_match(const struct ns_common *ns, dev_t dev, ino_t ino)
|
| 399 | {
|
| 400 | return (ns->inum == ino) && (nsfs_mnt->mnt_sb->s_dev == dev);
|
| 401 | }
|
| 402 |
|
| 403 |
|
| 404 | static int nsfs_show_path(struct seq_file *seq, struct dentry *dentry)
|
| 405 | {
|
| 406 | struct inode *inode = d_inode(dentry);
|
| 407 | const struct ns_common *ns = inode->i_private;
|
| 408 | const struct proc_ns_operations *ns_ops = ns->ops;
|
| 409 |
|
| 410 | seq_printf(seq, "%s:[%llu]", ns_ops->name, inode->i_ino);
|
| 411 | return 0;
|
| 412 | }
|
| 413 |
|
| 414 | static const struct super_operations nsfs_ops = {
|
| 415 | .statfs = simple_statfs,
|
| 416 | .evict_inode = nsfs_evict,
|
| 417 | .show_path = nsfs_show_path,
|
| 418 | .drop_inode = inode_just_drop,
|
| 419 | };
|
| 420 |
|
| 421 | static int nsfs_init_inode(struct inode *inode, void *data)
|
| 422 | {
|
| 423 | struct ns_common *ns = data;
|
| 424 |
|
| 425 | inode->i_private = data;
|
| 426 | inode->i_mode |= S_IRUGO;
|
| 427 | inode->i_fop = &ns_file_operations;
|
| 428 | inode->i_ino = ns->inum;
|
| 429 |
|
| 430 | |
| 431 | * Bring the namespace subtree back to life if we have to. This
|
| 432 | * can happen when e.g., all processes using a network namespace
|
| 433 | * and all namespace files or namespace file bind-mounts have
|
| 434 | * died but there are still sockets pinning it. The SIOCGSKNS
|
| 435 | * ioctl on such a socket will resurrect the relevant namespace
|
| 436 | * subtree.
|
| 437 |
|
| 438 | __ns_ref_active_get(ns);
|
| 439 | return 0;
|
| 440 | }
|
| 441 |
|
| 442 | static void nsfs_put_data(void *data)
|
| 443 | {
|
| 444 | struct ns_common *ns = data;
|
| 445 | ns->ops->put(ns);
|
| 446 | }
|
| 447 |
|
| 448 | static const struct stashed_operations nsfs_stashed_ops = {
|
| 449 | .init_inode = nsfs_init_inode,
|
| 450 | .put_data = nsfs_put_data,
|
| 451 | };
|
| 452 |
|
| 453 | #define NSFS_FID_SIZE_U32_VER0 (NSFS_FILE_HANDLE_SIZE_VER0 / sizeof(u32))
|
| 454 | #define NSFS_FID_SIZE_U32_LATEST (NSFS_FILE_HANDLE_SIZE_LATEST / sizeof(u32))
|
| 455 |
|
| 456 | static int nsfs_encode_fh(struct inode *inode, u32 *fh, int *max_len,
|
| 457 | struct inode *parent)
|
| 458 | {
|
| 459 | struct nsfs_file_handle *fid = (struct nsfs_file_handle *)fh;
|
| 460 | struct ns_common *ns = inode->i_private;
|
| 461 | int len = *max_len;
|
| 462 |
|
| 463 | if (parent)
|
| 464 | return FILEID_INVALID;
|
| 465 |
|
| 466 | if (len < NSFS_FID_SIZE_U32_VER0) {
|
| 467 | *max_len = NSFS_FID_SIZE_U32_LATEST;
|
| 468 | return FILEID_INVALID;
|
| 469 | } else if (len > NSFS_FID_SIZE_U32_LATEST) {
|
| 470 | *max_len = NSFS_FID_SIZE_U32_LATEST;
|
| 471 | }
|
| 472 |
|
| 473 | fid->ns_id = ns->ns_id;
|
| 474 | fid->ns_type = ns->ns_type;
|
| 475 | fid->ns_inum = inode->i_ino;
|
| 476 | return FILEID_NSFS;
|
| 477 | }
|
| 478 |
|
| 479 | bool is_current_namespace(struct ns_common *ns)
|
| 480 | {
|
| 481 | switch (ns->ns_type) {
|
| 482 | #ifdef CONFIG_CGROUPS
|
| 483 | case CLONE_NEWCGROUP:
|
| 484 | return current_in_namespace(to_cg_ns(ns));
|
| 485 | #endif
|
| 486 | #ifdef CONFIG_IPC_NS
|
| 487 | case CLONE_NEWIPC:
|
| 488 | return current_in_namespace(to_ipc_ns(ns));
|
| 489 | #endif
|
| 490 | case CLONE_NEWNS:
|
| 491 | return current_in_namespace(to_mnt_ns(ns));
|
| 492 | #ifdef CONFIG_NET_NS
|
| 493 | case CLONE_NEWNET:
|
| 494 | return current_in_namespace(to_net_ns(ns));
|
| 495 | #endif
|
| 496 | #ifdef CONFIG_PID_NS
|
| 497 | case CLONE_NEWPID:
|
| 498 | return current_in_namespace(to_pid_ns(ns));
|
| 499 | #endif
|
| 500 | #ifdef CONFIG_TIME_NS
|
| 501 | case CLONE_NEWTIME:
|
| 502 | return current_in_namespace(to_time_ns(ns));
|
| 503 | #endif
|
| 504 | #ifdef CONFIG_USER_NS
|
| 505 | case CLONE_NEWUSER:
|
| 506 | return current_in_namespace(to_user_ns(ns));
|
| 507 | #endif
|
| 508 | #ifdef CONFIG_UTS_NS
|
| 509 | case CLONE_NEWUTS:
|
| 510 | return current_in_namespace(to_uts_ns(ns));
|
| 511 | #endif
|
| 512 | default:
|
| 513 | VFS_WARN_ON_ONCE(true);
|
| 514 | return false;
|
| 515 | }
|
| 516 | }
|
| 517 |
|
| 518 | static struct dentry *nsfs_fh_to_dentry(struct super_block *sb, struct fid *fh,
|
| 519 | int fh_len, int fh_type)
|
| 520 | {
|
| 521 | struct path path __free(path_put) = {};
|
| 522 | struct nsfs_file_handle *fid = (struct nsfs_file_handle *)fh;
|
| 523 | struct user_namespace *owning_ns = NULL;
|
| 524 | struct ns_common *ns;
|
| 525 | int ret;
|
| 526 |
|
| 527 | if (fh_len < NSFS_FID_SIZE_U32_VER0)
|
| 528 | return NULL;
|
| 529 |
|
| 530 |
|
| 531 | if ((fh_len > NSFS_FID_SIZE_U32_LATEST) &&
|
| 532 | memchr_inv((void *)fid + NSFS_FID_SIZE_U32_LATEST, 0,
|
| 533 | fh_len - NSFS_FID_SIZE_U32_LATEST))
|
| 534 | return NULL;
|
| 535 |
|
| 536 | switch (fh_type) {
|
| 537 | case FILEID_NSFS:
|
| 538 | break;
|
| 539 | default:
|
| 540 | return NULL;
|
| 541 | }
|
| 542 |
|
| 543 | if (!fid->ns_id)
|
| 544 | return NULL;
|
| 545 |
|
| 546 | if (!fid->ns_inum != !fid->ns_type)
|
| 547 | return NULL;
|
| 548 |
|
| 549 | scoped_guard(rcu) {
|
| 550 | ns = ns_tree_lookup_rcu(fid->ns_id, fid->ns_type);
|
| 551 | if (!ns)
|
| 552 | return NULL;
|
| 553 |
|
| 554 | VFS_WARN_ON_ONCE(ns->ns_id != fid->ns_id);
|
| 555 |
|
| 556 | if (fid->ns_inum && (fid->ns_inum != ns->inum))
|
| 557 | return NULL;
|
| 558 | if (fid->ns_type && (fid->ns_type != ns->ns_type))
|
| 559 | return NULL;
|
| 560 |
|
| 561 | |
| 562 | * This is racy because we're not actually taking an
|
| 563 | * active reference. IOW, it could happen that the
|
| 564 | * namespace becomes inactive after this check.
|
| 565 | * We don't care because nsfs_init_inode() will just
|
| 566 | * resurrect the relevant namespace tree for us. If it
|
| 567 | * has been active here we just allow it's resurrection.
|
| 568 | * We could try to take an active reference here and
|
| 569 | * then drop it again. But really, why bother.
|
| 570 |
|
| 571 | if (!ns_get_unless_inactive(ns))
|
| 572 | return NULL;
|
| 573 | }
|
| 574 |
|
| 575 | switch (ns->ns_type) {
|
| 576 | #ifdef CONFIG_CGROUPS
|
| 577 | case CLONE_NEWCGROUP:
|
| 578 | if (!current_in_namespace(to_cg_ns(ns)))
|
| 579 | owning_ns = to_cg_ns(ns)->user_ns;
|
| 580 | break;
|
| 581 | #endif
|
| 582 | #ifdef CONFIG_IPC_NS
|
| 583 | case CLONE_NEWIPC:
|
| 584 | if (!current_in_namespace(to_ipc_ns(ns)))
|
| 585 | owning_ns = to_ipc_ns(ns)->user_ns;
|
| 586 | break;
|
| 587 | #endif
|
| 588 | case CLONE_NEWNS:
|
| 589 | if (!current_in_namespace(to_mnt_ns(ns)))
|
| 590 | owning_ns = to_mnt_ns(ns)->user_ns;
|
| 591 | break;
|
| 592 | #ifdef CONFIG_NET_NS
|
| 593 | case CLONE_NEWNET:
|
| 594 | if (!current_in_namespace(to_net_ns(ns)))
|
| 595 | owning_ns = to_net_ns(ns)->user_ns;
|
| 596 | break;
|
| 597 | #endif
|
| 598 | #ifdef CONFIG_PID_NS
|
| 599 | case CLONE_NEWPID:
|
| 600 | if (!current_in_namespace(to_pid_ns(ns))) {
|
| 601 | owning_ns = to_pid_ns(ns)->user_ns;
|
| 602 | } else if (!READ_ONCE(to_pid_ns(ns)->child_reaper)) {
|
| 603 | ns->ops->put(ns);
|
| 604 | return ERR_PTR(-EPERM);
|
| 605 | }
|
| 606 | break;
|
| 607 | #endif
|
| 608 | #ifdef CONFIG_TIME_NS
|
| 609 | case CLONE_NEWTIME:
|
| 610 | if (!current_in_namespace(to_time_ns(ns)))
|
| 611 | owning_ns = to_time_ns(ns)->user_ns;
|
| 612 | break;
|
| 613 | #endif
|
| 614 | #ifdef CONFIG_USER_NS
|
| 615 | case CLONE_NEWUSER:
|
| 616 | if (!current_in_namespace(to_user_ns(ns)))
|
| 617 | owning_ns = to_user_ns(ns);
|
| 618 | break;
|
| 619 | #endif
|
| 620 | #ifdef CONFIG_UTS_NS
|
| 621 | case CLONE_NEWUTS:
|
| 622 | if (!current_in_namespace(to_uts_ns(ns)))
|
| 623 | owning_ns = to_uts_ns(ns)->user_ns;
|
| 624 | break;
|
| 625 | #endif
|
| 626 | default:
|
| 627 | return ERR_PTR(-EOPNOTSUPP);
|
| 628 | }
|
| 629 |
|
| 630 | if (owning_ns && !may_see_all_namespaces()) {
|
| 631 | ns->ops->put(ns);
|
| 632 | return ERR_PTR(-EPERM);
|
| 633 | }
|
| 634 |
|
| 635 |
|
| 636 | ret = path_from_stashed(&ns->stashed, nsfs_mnt, ns, &path);
|
| 637 | if (ret)
|
| 638 | return ERR_PTR(ret);
|
| 639 |
|
| 640 | return no_free_ptr(path.dentry);
|
| 641 | }
|
| 642 |
|
| 643 | static int nsfs_export_permission(struct handle_to_path_ctx *ctx,
|
| 644 | unsigned int oflags)
|
| 645 | {
|
| 646 |
|
| 647 | return 0;
|
| 648 | }
|
| 649 |
|
| 650 | static struct file *nsfs_export_open(const struct path *path, unsigned int oflags)
|
| 651 | {
|
| 652 | return file_open_root(path, "", oflags, 0);
|
| 653 | }
|
| 654 |
|
| 655 | static const struct export_operations nsfs_export_operations = {
|
| 656 | .encode_fh = nsfs_encode_fh,
|
| 657 | .fh_to_dentry = nsfs_fh_to_dentry,
|
| 658 | .open = nsfs_export_open,
|
| 659 | .permission = nsfs_export_permission,
|
| 660 | };
|
| 661 |
|
| 662 | static int nsfs_init_fs_context(struct fs_context *fc)
|
| 663 | {
|
| 664 | struct pseudo_fs_context *ctx = init_pseudo(fc, NSFS_MAGIC);
|
| 665 | if (!ctx)
|
| 666 | return -ENOMEM;
|
| 667 | ctx->s_d_flags |= DCACHE_DONTCACHE;
|
| 668 | ctx->ops = &nsfs_ops;
|
| 669 | ctx->eops = &nsfs_export_operations;
|
| 670 | ctx->dops = &ns_dentry_operations;
|
| 671 | fc->s_fs_info = (void *)&nsfs_stashed_ops;
|
| 672 | return 0;
|
| 673 | }
|
| 674 |
|
| 675 | static struct file_system_type nsfs = {
|
| 676 | .name = "nsfs",
|
| 677 | .init_fs_context = nsfs_init_fs_context,
|
| 678 | .kill_sb = kill_anon_super,
|
| 679 | };
|
| 680 |
|
| 681 | void __init nsfs_init(void)
|
| 682 | {
|
| 683 | nsfs_mnt = kern_mount(&nsfs);
|
| 684 | if (IS_ERR(nsfs_mnt))
|
| 685 | panic("can't set nsfs up\n");
|
| 686 | nsfs_mnt->mnt_sb->s_flags &= ~SB_NOUSER;
|
| 687 | nsfs_root_path.mnt = nsfs_mnt;
|
| 688 | nsfs_root_path.dentry = nsfs_mnt->mnt_root;
|
| 689 | }
|
| 690 |
|
| 691 | void nsproxy_ns_active_get(struct nsproxy *ns)
|
| 692 | {
|
| 693 | ns_ref_active_get(ns->mnt_ns);
|
| 694 | ns_ref_active_get(ns->uts_ns);
|
| 695 | ns_ref_active_get(ns->ipc_ns);
|
| 696 | ns_ref_active_get(ns->pid_ns_for_children);
|
| 697 | ns_ref_active_get(ns->cgroup_ns);
|
| 698 | ns_ref_active_get(ns->net_ns);
|
| 699 | ns_ref_active_get(ns->time_ns);
|
| 700 | ns_ref_active_get(ns->time_ns_for_children);
|
| 701 | }
|
| 702 |
|
| 703 | void nsproxy_ns_active_put(struct nsproxy *ns)
|
| 704 | {
|
| 705 | ns_ref_active_put(ns->mnt_ns);
|
| 706 | ns_ref_active_put(ns->uts_ns);
|
| 707 | ns_ref_active_put(ns->ipc_ns);
|
| 708 | ns_ref_active_put(ns->pid_ns_for_children);
|
| 709 | ns_ref_active_put(ns->cgroup_ns);
|
| 710 | ns_ref_active_put(ns->net_ns);
|
| 711 | ns_ref_active_put(ns->time_ns);
|
| 712 | ns_ref_active_put(ns->time_ns_for_children);
|
| 713 | }
|