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; |