diff options
| -rw-r--r-- | fs/proc/base.c | 53 | ||||
| -rw-r--r-- | include/linux/user_namespace.h | 7 | ||||
| -rw-r--r-- | kernel/user.c | 1 | ||||
| -rw-r--r-- | kernel/user_namespace.c | 85 |
4 files changed, 146 insertions, 0 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c index 772efa45a452..7dc3ea89ef1a 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c | |||
| @@ -2464,6 +2464,57 @@ static const struct file_operations proc_projid_map_operations = { | |||
| 2464 | .llseek = seq_lseek, | 2464 | .llseek = seq_lseek, |
| 2465 | .release = proc_id_map_release, | 2465 | .release = proc_id_map_release, |
| 2466 | }; | 2466 | }; |
| 2467 | |||
| 2468 | static int proc_setgroups_open(struct inode *inode, struct file *file) | ||
| 2469 | { | ||
| 2470 | struct user_namespace *ns = NULL; | ||
| 2471 | struct task_struct *task; | ||
| 2472 | int ret; | ||
| 2473 | |||
| 2474 | ret = -ESRCH; | ||
| 2475 | task = get_proc_task(inode); | ||
| 2476 | if (task) { | ||
| 2477 | rcu_read_lock(); | ||
| 2478 | ns = get_user_ns(task_cred_xxx(task, user_ns)); | ||
| 2479 | rcu_read_unlock(); | ||
| 2480 | put_task_struct(task); | ||
| 2481 | } | ||
| 2482 | if (!ns) | ||
| 2483 | goto err; | ||
| 2484 | |||
| 2485 | if (file->f_mode & FMODE_WRITE) { | ||
| 2486 | ret = -EACCES; | ||
| 2487 | if (!ns_capable(ns, CAP_SYS_ADMIN)) | ||
| 2488 | goto err_put_ns; | ||
| 2489 | } | ||
| 2490 | |||
| 2491 | ret = single_open(file, &proc_setgroups_show, ns); | ||
| 2492 | if (ret) | ||
| 2493 | goto err_put_ns; | ||
| 2494 | |||
| 2495 | return 0; | ||
| 2496 | err_put_ns: | ||
| 2497 | put_user_ns(ns); | ||
| 2498 | err: | ||
| 2499 | return ret; | ||
| 2500 | } | ||
| 2501 | |||
| 2502 | static int proc_setgroups_release(struct inode *inode, struct file *file) | ||
| 2503 | { | ||
| 2504 | struct seq_file *seq = file->private_data; | ||
| 2505 | struct user_namespace *ns = seq->private; | ||
| 2506 | int ret = single_release(inode, file); | ||
| 2507 | put_user_ns(ns); | ||
| 2508 | return ret; | ||
| 2509 | } | ||
| 2510 | |||
| 2511 | static const struct file_operations proc_setgroups_operations = { | ||
| 2512 | .open = proc_setgroups_open, | ||
| 2513 | .write = proc_setgroups_write, | ||
| 2514 | .read = seq_read, | ||
| 2515 | .llseek = seq_lseek, | ||
| 2516 | .release = proc_setgroups_release, | ||
| 2517 | }; | ||
| 2467 | #endif /* CONFIG_USER_NS */ | 2518 | #endif /* CONFIG_USER_NS */ |
| 2468 | 2519 | ||
| 2469 | static int proc_pid_personality(struct seq_file *m, struct pid_namespace *ns, | 2520 | static int proc_pid_personality(struct seq_file *m, struct pid_namespace *ns, |
| @@ -2572,6 +2623,7 @@ static const struct pid_entry tgid_base_stuff[] = { | |||
| 2572 | REG("uid_map", S_IRUGO|S_IWUSR, proc_uid_map_operations), | 2623 | REG("uid_map", S_IRUGO|S_IWUSR, proc_uid_map_operations), |
| 2573 | REG("gid_map", S_IRUGO|S_IWUSR, proc_gid_map_operations), | 2624 | REG("gid_map", S_IRUGO|S_IWUSR, proc_gid_map_operations), |
| 2574 | REG("projid_map", S_IRUGO|S_IWUSR, proc_projid_map_operations), | 2625 | REG("projid_map", S_IRUGO|S_IWUSR, proc_projid_map_operations), |
| 2626 | REG("setgroups", S_IRUGO|S_IWUSR, proc_setgroups_operations), | ||
| 2575 | #endif | 2627 | #endif |
| 2576 | #ifdef CONFIG_CHECKPOINT_RESTORE | 2628 | #ifdef CONFIG_CHECKPOINT_RESTORE |
| 2577 | REG("timers", S_IRUGO, proc_timers_operations), | 2629 | REG("timers", S_IRUGO, proc_timers_operations), |
| @@ -2913,6 +2965,7 @@ static const struct pid_entry tid_base_stuff[] = { | |||
| 2913 | REG("uid_map", S_IRUGO|S_IWUSR, proc_uid_map_operations), | 2965 | REG("uid_map", S_IRUGO|S_IWUSR, proc_uid_map_operations), |
| 2914 | REG("gid_map", S_IRUGO|S_IWUSR, proc_gid_map_operations), | 2966 | REG("gid_map", S_IRUGO|S_IWUSR, proc_gid_map_operations), |
| 2915 | REG("projid_map", S_IRUGO|S_IWUSR, proc_projid_map_operations), | 2967 | REG("projid_map", S_IRUGO|S_IWUSR, proc_projid_map_operations), |
| 2968 | REG("setgroups", S_IRUGO|S_IWUSR, proc_setgroups_operations), | ||
| 2916 | #endif | 2969 | #endif |
| 2917 | }; | 2970 | }; |
| 2918 | 2971 | ||
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h index 8d493083486a..9f3579ff543d 100644 --- a/include/linux/user_namespace.h +++ b/include/linux/user_namespace.h | |||
| @@ -17,6 +17,10 @@ struct uid_gid_map { /* 64 bytes -- 1 cache line */ | |||
| 17 | } extent[UID_GID_MAP_MAX_EXTENTS]; | 17 | } extent[UID_GID_MAP_MAX_EXTENTS]; |
| 18 | }; | 18 | }; |
| 19 | 19 | ||
| 20 | #define USERNS_SETGROUPS_ALLOWED 1UL | ||
| 21 | |||
| 22 | #define USERNS_INIT_FLAGS USERNS_SETGROUPS_ALLOWED | ||
| 23 | |||
| 20 | struct user_namespace { | 24 | struct user_namespace { |
| 21 | struct uid_gid_map uid_map; | 25 | struct uid_gid_map uid_map; |
| 22 | struct uid_gid_map gid_map; | 26 | struct uid_gid_map gid_map; |
| @@ -27,6 +31,7 @@ struct user_namespace { | |||
| 27 | kuid_t owner; | 31 | kuid_t owner; |
| 28 | kgid_t group; | 32 | kgid_t group; |
| 29 | unsigned int proc_inum; | 33 | unsigned int proc_inum; |
| 34 | unsigned long flags; | ||
| 30 | 35 | ||
| 31 | /* Register of per-UID persistent keyrings for this namespace */ | 36 | /* Register of per-UID persistent keyrings for this namespace */ |
| 32 | #ifdef CONFIG_PERSISTENT_KEYRINGS | 37 | #ifdef CONFIG_PERSISTENT_KEYRINGS |
| @@ -63,6 +68,8 @@ extern const struct seq_operations proc_projid_seq_operations; | |||
| 63 | extern ssize_t proc_uid_map_write(struct file *, const char __user *, size_t, loff_t *); | 68 | extern ssize_t proc_uid_map_write(struct file *, const char __user *, size_t, loff_t *); |
| 64 | extern ssize_t proc_gid_map_write(struct file *, const char __user *, size_t, loff_t *); | 69 | extern ssize_t proc_gid_map_write(struct file *, const char __user *, size_t, loff_t *); |
| 65 | extern ssize_t proc_projid_map_write(struct file *, const char __user *, size_t, loff_t *); | 70 | extern ssize_t proc_projid_map_write(struct file *, const char __user *, size_t, loff_t *); |
| 71 | extern ssize_t proc_setgroups_write(struct file *, const char __user *, size_t, loff_t *); | ||
| 72 | extern int proc_setgroups_show(struct seq_file *m, void *v); | ||
| 66 | extern bool userns_may_setgroups(const struct user_namespace *ns); | 73 | extern bool userns_may_setgroups(const struct user_namespace *ns); |
| 67 | #else | 74 | #else |
| 68 | 75 | ||
diff --git a/kernel/user.c b/kernel/user.c index 4efa39350e44..2d09940c9632 100644 --- a/kernel/user.c +++ b/kernel/user.c | |||
| @@ -51,6 +51,7 @@ struct user_namespace init_user_ns = { | |||
| 51 | .owner = GLOBAL_ROOT_UID, | 51 | .owner = GLOBAL_ROOT_UID, |
| 52 | .group = GLOBAL_ROOT_GID, | 52 | .group = GLOBAL_ROOT_GID, |
| 53 | .proc_inum = PROC_USER_INIT_INO, | 53 | .proc_inum = PROC_USER_INIT_INO, |
| 54 | .flags = USERNS_INIT_FLAGS, | ||
| 54 | #ifdef CONFIG_PERSISTENT_KEYRINGS | 55 | #ifdef CONFIG_PERSISTENT_KEYRINGS |
| 55 | .persistent_keyring_register_sem = | 56 | .persistent_keyring_register_sem = |
| 56 | __RWSEM_INITIALIZER(init_user_ns.persistent_keyring_register_sem), | 57 | __RWSEM_INITIALIZER(init_user_ns.persistent_keyring_register_sem), |
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index 44a555ac6104..6e80f4c1322b 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c | |||
| @@ -100,6 +100,11 @@ int create_user_ns(struct cred *new) | |||
| 100 | ns->owner = owner; | 100 | ns->owner = owner; |
| 101 | ns->group = group; | 101 | ns->group = group; |
| 102 | 102 | ||
| 103 | /* Inherit USERNS_SETGROUPS_ALLOWED from our parent */ | ||
| 104 | mutex_lock(&userns_state_mutex); | ||
| 105 | ns->flags = parent_ns->flags; | ||
| 106 | mutex_unlock(&userns_state_mutex); | ||
| 107 | |||
| 103 | set_cred_user_ns(new, ns); | 108 | set_cred_user_ns(new, ns); |
| 104 | 109 | ||
| 105 | #ifdef CONFIG_PERSISTENT_KEYRINGS | 110 | #ifdef CONFIG_PERSISTENT_KEYRINGS |
| @@ -839,6 +844,84 @@ static bool new_idmap_permitted(const struct file *file, | |||
| 839 | return false; | 844 | return false; |
| 840 | } | 845 | } |
| 841 | 846 | ||
| 847 | int proc_setgroups_show(struct seq_file *seq, void *v) | ||
| 848 | { | ||
| 849 | struct user_namespace *ns = seq->private; | ||
| 850 | unsigned long userns_flags = ACCESS_ONCE(ns->flags); | ||
| 851 | |||
| 852 | seq_printf(seq, "%s\n", | ||
| 853 | (userns_flags & USERNS_SETGROUPS_ALLOWED) ? | ||
| 854 | "allow" : "deny"); | ||
| 855 | return 0; | ||
| 856 | } | ||
| 857 | |||
| 858 | ssize_t proc_setgroups_write(struct file *file, const char __user *buf, | ||
| 859 | size_t count, loff_t *ppos) | ||
| 860 | { | ||
| 861 | struct seq_file *seq = file->private_data; | ||
| 862 | struct user_namespace *ns = seq->private; | ||
| 863 | char kbuf[8], *pos; | ||
| 864 | bool setgroups_allowed; | ||
| 865 | ssize_t ret; | ||
| 866 | |||
| 867 | /* Only allow a very narrow range of strings to be written */ | ||
| 868 | ret = -EINVAL; | ||
| 869 | if ((*ppos != 0) || (count >= sizeof(kbuf))) | ||
| 870 | goto out; | ||
| 871 | |||
| 872 | /* What was written? */ | ||
| 873 | ret = -EFAULT; | ||
| 874 | if (copy_from_user(kbuf, buf, count)) | ||
| 875 | goto out; | ||
| 876 | kbuf[count] = '\0'; | ||
| 877 | pos = kbuf; | ||
| 878 | |||
| 879 | /* What is being requested? */ | ||
| 880 | ret = -EINVAL; | ||
| 881 | if (strncmp(pos, "allow", 5) == 0) { | ||
| 882 | pos += 5; | ||
| 883 | setgroups_allowed = true; | ||
| 884 | } | ||
| 885 | else if (strncmp(pos, "deny", 4) == 0) { | ||
| 886 | pos += 4; | ||
| 887 | setgroups_allowed = false; | ||
| 888 | } | ||
| 889 | else | ||
| 890 | goto out; | ||
| 891 | |||
| 892 | /* Verify there is not trailing junk on the line */ | ||
| 893 | pos = skip_spaces(pos); | ||
| 894 | if (*pos != '\0') | ||
| 895 | goto out; | ||
| 896 | |||
| 897 | ret = -EPERM; | ||
| 898 | mutex_lock(&userns_state_mutex); | ||
| 899 | if (setgroups_allowed) { | ||
| 900 | /* Enabling setgroups after setgroups has been disabled | ||
| 901 | * is not allowed. | ||
| 902 | */ | ||
| 903 | if (!(ns->flags & USERNS_SETGROUPS_ALLOWED)) | ||
| 904 | goto out_unlock; | ||
| 905 | } else { | ||
| 906 | /* Permanently disabling setgroups after setgroups has | ||
| 907 | * been enabled by writing the gid_map is not allowed. | ||
| 908 | */ | ||
| 909 | if (ns->gid_map.nr_extents != 0) | ||
| 910 | goto out_unlock; | ||
| 911 | ns->flags &= ~USERNS_SETGROUPS_ALLOWED; | ||
| 912 | } | ||
| 913 | mutex_unlock(&userns_state_mutex); | ||
| 914 | |||
| 915 | /* Report a successful write */ | ||
| 916 | *ppos = count; | ||
| 917 | ret = count; | ||
| 918 | out: | ||
| 919 | return ret; | ||
| 920 | out_unlock: | ||
| 921 | mutex_unlock(&userns_state_mutex); | ||
| 922 | goto out; | ||
| 923 | } | ||
| 924 | |||
| 842 | bool userns_may_setgroups(const struct user_namespace *ns) | 925 | bool userns_may_setgroups(const struct user_namespace *ns) |
| 843 | { | 926 | { |
| 844 | bool allowed; | 927 | bool allowed; |
| @@ -848,6 +931,8 @@ bool userns_may_setgroups(const struct user_namespace *ns) | |||
| 848 | * the user namespace has been established. | 931 | * the user namespace has been established. |
| 849 | */ | 932 | */ |
| 850 | allowed = ns->gid_map.nr_extents != 0; | 933 | allowed = ns->gid_map.nr_extents != 0; |
| 934 | /* Is setgroups allowed? */ | ||
| 935 | allowed = allowed && (ns->flags & USERNS_SETGROUPS_ALLOWED); | ||
| 851 | mutex_unlock(&userns_state_mutex); | 936 | mutex_unlock(&userns_state_mutex); |
| 852 | 937 | ||
| 853 | return allowed; | 938 | return allowed; |
