Commit b0e77598f87107001a00b8a4ece9c95e4254ccc4
Committed by
Linus Torvalds
1 parent
b515498f5b
Exists in
master
and in
7 other branches
userns: user namespaces: convert several capable() calls
CAP_IPC_OWNER and CAP_IPC_LOCK can be checked against current_user_ns(), because the resource comes from current's own ipc namespace. setuid/setgid are to uids in own namespace, so again checks can be against current_user_ns(). Changelog: Jan 11: Use task_ns_capable() in place of sched_capable(). Jan 11: Use nsown_capable() as suggested by Bastian Blank. Jan 11: Clarify (hopefully) some logic in futex and sched.c Feb 15: use ns_capable for ipc, not nsown_capable Feb 23: let copy_ipcs handle setting ipc_ns->user_ns Feb 23: pass ns down rather than taking it from current [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Serge E. Hallyn <serge.hallyn@canonical.com> Acked-by: "Eric W. Biederman" <ebiederm@xmission.com> Acked-by: Daniel Lezcano <daniel.lezcano@free.fr> Acked-by: David Howells <dhowells@redhat.com> Cc: James Morris <jmorris@namei.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Showing 13 changed files with 75 additions and 45 deletions Side-by-side Diff
include/linux/ipc_namespace.h
... | ... | @@ -5,6 +5,7 @@ |
5 | 5 | #include <linux/idr.h> |
6 | 6 | #include <linux/rwsem.h> |
7 | 7 | #include <linux/notifier.h> |
8 | +#include <linux/nsproxy.h> | |
8 | 9 | |
9 | 10 | /* |
10 | 11 | * ipc namespace events |
... | ... | @@ -93,7 +94,7 @@ |
93 | 94 | |
94 | 95 | #if defined(CONFIG_IPC_NS) |
95 | 96 | extern struct ipc_namespace *copy_ipcs(unsigned long flags, |
96 | - struct ipc_namespace *ns); | |
97 | + struct task_struct *tsk); | |
97 | 98 | static inline struct ipc_namespace *get_ipc_ns(struct ipc_namespace *ns) |
98 | 99 | { |
99 | 100 | if (ns) |
100 | 101 | |
... | ... | @@ -104,12 +105,12 @@ |
104 | 105 | extern void put_ipc_ns(struct ipc_namespace *ns); |
105 | 106 | #else |
106 | 107 | static inline struct ipc_namespace *copy_ipcs(unsigned long flags, |
107 | - struct ipc_namespace *ns) | |
108 | + struct task_struct *tsk) | |
108 | 109 | { |
109 | 110 | if (flags & CLONE_NEWIPC) |
110 | 111 | return ERR_PTR(-EINVAL); |
111 | 112 | |
112 | - return ns; | |
113 | + return tsk->nsproxy->ipc_ns; | |
113 | 114 | } |
114 | 115 | |
115 | 116 | static inline struct ipc_namespace *get_ipc_ns(struct ipc_namespace *ns) |
ipc/msg.c
... | ... | @@ -421,7 +421,7 @@ |
421 | 421 | return -EFAULT; |
422 | 422 | } |
423 | 423 | |
424 | - ipcp = ipcctl_pre_down(&msg_ids(ns), msqid, cmd, | |
424 | + ipcp = ipcctl_pre_down(ns, &msg_ids(ns), msqid, cmd, | |
425 | 425 | &msqid64.msg_perm, msqid64.msg_qbytes); |
426 | 426 | if (IS_ERR(ipcp)) |
427 | 427 | return PTR_ERR(ipcp); |
... | ... | @@ -539,7 +539,7 @@ |
539 | 539 | success_return = 0; |
540 | 540 | } |
541 | 541 | err = -EACCES; |
542 | - if (ipcperms(&msq->q_perm, S_IRUGO)) | |
542 | + if (ipcperms(ns, &msq->q_perm, S_IRUGO)) | |
543 | 543 | goto out_unlock; |
544 | 544 | |
545 | 545 | err = security_msg_queue_msgctl(msq, cmd); |
... | ... | @@ -664,7 +664,7 @@ |
664 | 664 | struct msg_sender s; |
665 | 665 | |
666 | 666 | err = -EACCES; |
667 | - if (ipcperms(&msq->q_perm, S_IWUGO)) | |
667 | + if (ipcperms(ns, &msq->q_perm, S_IWUGO)) | |
668 | 668 | goto out_unlock_free; |
669 | 669 | |
670 | 670 | err = security_msg_queue_msgsnd(msq, msg, msgflg); |
... | ... | @@ -774,7 +774,7 @@ |
774 | 774 | struct list_head *tmp; |
775 | 775 | |
776 | 776 | msg = ERR_PTR(-EACCES); |
777 | - if (ipcperms(&msq->q_perm, S_IRUGO)) | |
777 | + if (ipcperms(ns, &msq->q_perm, S_IRUGO)) | |
778 | 778 | goto out_unlock; |
779 | 779 | |
780 | 780 | msg = ERR_PTR(-EAGAIN); |
ipc/namespace.c
... | ... | @@ -15,7 +15,8 @@ |
15 | 15 | |
16 | 16 | #include "util.h" |
17 | 17 | |
18 | -static struct ipc_namespace *create_ipc_ns(struct ipc_namespace *old_ns) | |
18 | +static struct ipc_namespace *create_ipc_ns(struct task_struct *tsk, | |
19 | + struct ipc_namespace *old_ns) | |
19 | 20 | { |
20 | 21 | struct ipc_namespace *ns; |
21 | 22 | int err; |
22 | 23 | |
23 | 24 | |
24 | 25 | |
... | ... | @@ -44,17 +45,19 @@ |
44 | 45 | ipcns_notify(IPCNS_CREATED); |
45 | 46 | register_ipcns_notifier(ns); |
46 | 47 | |
47 | - ns->user_ns = old_ns->user_ns; | |
48 | - get_user_ns(ns->user_ns); | |
48 | + ns->user_ns = get_user_ns(task_cred_xxx(tsk, user)->user_ns); | |
49 | 49 | |
50 | 50 | return ns; |
51 | 51 | } |
52 | 52 | |
53 | -struct ipc_namespace *copy_ipcs(unsigned long flags, struct ipc_namespace *ns) | |
53 | +struct ipc_namespace *copy_ipcs(unsigned long flags, | |
54 | + struct task_struct *tsk) | |
54 | 55 | { |
56 | + struct ipc_namespace *ns = tsk->nsproxy->ipc_ns; | |
57 | + | |
55 | 58 | if (!(flags & CLONE_NEWIPC)) |
56 | 59 | return get_ipc_ns(ns); |
57 | - return create_ipc_ns(ns); | |
60 | + return create_ipc_ns(tsk, ns); | |
58 | 61 | } |
59 | 62 | |
60 | 63 | /* |
ipc/sem.c
... | ... | @@ -817,7 +817,7 @@ |
817 | 817 | } |
818 | 818 | |
819 | 819 | err = -EACCES; |
820 | - if (ipcperms (&sma->sem_perm, S_IRUGO)) | |
820 | + if (ipcperms(ns, &sma->sem_perm, S_IRUGO)) | |
821 | 821 | goto out_unlock; |
822 | 822 | |
823 | 823 | err = security_sem_semctl(sma, cmd); |
... | ... | @@ -862,7 +862,8 @@ |
862 | 862 | nsems = sma->sem_nsems; |
863 | 863 | |
864 | 864 | err = -EACCES; |
865 | - if (ipcperms (&sma->sem_perm, (cmd==SETVAL||cmd==SETALL)?S_IWUGO:S_IRUGO)) | |
865 | + if (ipcperms(ns, &sma->sem_perm, | |
866 | + (cmd == SETVAL || cmd == SETALL) ? S_IWUGO : S_IRUGO)) | |
866 | 867 | goto out_unlock; |
867 | 868 | |
868 | 869 | err = security_sem_semctl(sma, cmd); |
... | ... | @@ -1047,7 +1048,8 @@ |
1047 | 1048 | return -EFAULT; |
1048 | 1049 | } |
1049 | 1050 | |
1050 | - ipcp = ipcctl_pre_down(&sem_ids(ns), semid, cmd, &semid64.sem_perm, 0); | |
1051 | + ipcp = ipcctl_pre_down(ns, &sem_ids(ns), semid, cmd, | |
1052 | + &semid64.sem_perm, 0); | |
1051 | 1053 | if (IS_ERR(ipcp)) |
1052 | 1054 | return PTR_ERR(ipcp); |
1053 | 1055 | |
... | ... | @@ -1386,7 +1388,7 @@ |
1386 | 1388 | goto out_unlock_free; |
1387 | 1389 | |
1388 | 1390 | error = -EACCES; |
1389 | - if (ipcperms(&sma->sem_perm, alter ? S_IWUGO : S_IRUGO)) | |
1391 | + if (ipcperms(ns, &sma->sem_perm, alter ? S_IWUGO : S_IRUGO)) | |
1390 | 1392 | goto out_unlock_free; |
1391 | 1393 | |
1392 | 1394 | error = security_sem_semop(sma, sops, nsops, alter); |
ipc/shm.c
... | ... | @@ -623,7 +623,8 @@ |
623 | 623 | return -EFAULT; |
624 | 624 | } |
625 | 625 | |
626 | - ipcp = ipcctl_pre_down(&shm_ids(ns), shmid, cmd, &shmid64.shm_perm, 0); | |
626 | + ipcp = ipcctl_pre_down(ns, &shm_ids(ns), shmid, cmd, | |
627 | + &shmid64.shm_perm, 0); | |
627 | 628 | if (IS_ERR(ipcp)) |
628 | 629 | return PTR_ERR(ipcp); |
629 | 630 | |
... | ... | @@ -737,7 +738,7 @@ |
737 | 738 | result = 0; |
738 | 739 | } |
739 | 740 | err = -EACCES; |
740 | - if (ipcperms (&shp->shm_perm, S_IRUGO)) | |
741 | + if (ipcperms(ns, &shp->shm_perm, S_IRUGO)) | |
741 | 742 | goto out_unlock; |
742 | 743 | err = security_shm_shmctl(shp, cmd); |
743 | 744 | if (err) |
... | ... | @@ -773,7 +774,7 @@ |
773 | 774 | |
774 | 775 | audit_ipc_obj(&(shp->shm_perm)); |
775 | 776 | |
776 | - if (!capable(CAP_IPC_LOCK)) { | |
777 | + if (!ns_capable(ns->user_ns, CAP_IPC_LOCK)) { | |
777 | 778 | uid_t euid = current_euid(); |
778 | 779 | err = -EPERM; |
779 | 780 | if (euid != shp->shm_perm.uid && |
... | ... | @@ -888,7 +889,7 @@ |
888 | 889 | } |
889 | 890 | |
890 | 891 | err = -EACCES; |
891 | - if (ipcperms(&shp->shm_perm, acc_mode)) | |
892 | + if (ipcperms(ns, &shp->shm_perm, acc_mode)) | |
892 | 893 | goto out_unlock; |
893 | 894 | |
894 | 895 | err = security_shm_shmat(shp, shmaddr, shmflg); |
ipc/util.c
... | ... | @@ -329,12 +329,14 @@ |
329 | 329 | * |
330 | 330 | * It is called with ipc_ids.rw_mutex and ipcp->lock held. |
331 | 331 | */ |
332 | -static int ipc_check_perms(struct kern_ipc_perm *ipcp, struct ipc_ops *ops, | |
333 | - struct ipc_params *params) | |
332 | +static int ipc_check_perms(struct ipc_namespace *ns, | |
333 | + struct kern_ipc_perm *ipcp, | |
334 | + struct ipc_ops *ops, | |
335 | + struct ipc_params *params) | |
334 | 336 | { |
335 | 337 | int err; |
336 | 338 | |
337 | - if (ipcperms(ipcp, params->flg)) | |
339 | + if (ipcperms(ns, ipcp, params->flg)) | |
338 | 340 | err = -EACCES; |
339 | 341 | else { |
340 | 342 | err = ops->associate(ipcp, params->flg); |
... | ... | @@ -396,7 +398,7 @@ |
396 | 398 | * ipc_check_perms returns the IPC id on |
397 | 399 | * success |
398 | 400 | */ |
399 | - err = ipc_check_perms(ipcp, ops, params); | |
401 | + err = ipc_check_perms(ns, ipcp, ops, params); | |
400 | 402 | } |
401 | 403 | ipc_unlock(ipcp); |
402 | 404 | } |
403 | 405 | |
... | ... | @@ -610,10 +612,12 @@ |
610 | 612 | * |
611 | 613 | * Check user, group, other permissions for access |
612 | 614 | * to ipc resources. return 0 if allowed |
615 | + * | |
616 | + * @flag will most probably be 0 or S_...UGO from <linux/stat.h> | |
613 | 617 | */ |
614 | 618 | |
615 | -int ipcperms (struct kern_ipc_perm *ipcp, short flag) | |
616 | -{ /* flag will most probably be 0 or S_...UGO from <linux/stat.h> */ | |
619 | +int ipcperms(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp, short flag) | |
620 | +{ | |
617 | 621 | uid_t euid = current_euid(); |
618 | 622 | int requested_mode, granted_mode; |
619 | 623 | |
... | ... | @@ -627,7 +631,7 @@ |
627 | 631 | granted_mode >>= 3; |
628 | 632 | /* is there some bit set in requested_mode but not in granted_mode? */ |
629 | 633 | if ((requested_mode & ~granted_mode & 0007) && |
630 | - !capable(CAP_IPC_OWNER)) | |
634 | + !ns_capable(ns->user_ns, CAP_IPC_OWNER)) | |
631 | 635 | return -1; |
632 | 636 | |
633 | 637 | return security_ipc_permission(ipcp, flag); |
... | ... | @@ -765,6 +769,7 @@ |
765 | 769 | |
766 | 770 | /** |
767 | 771 | * ipcctl_pre_down - retrieve an ipc and check permissions for some IPC_XXX cmd |
772 | + * @ids: the ipc namespace | |
768 | 773 | * @ids: the table of ids where to look for the ipc |
769 | 774 | * @id: the id of the ipc to retrieve |
770 | 775 | * @cmd: the cmd to check |
... | ... | @@ -779,7 +784,8 @@ |
779 | 784 | * - returns the ipc with both ipc and rw_mutex locks held in case of success |
780 | 785 | * or an err-code without any lock held otherwise. |
781 | 786 | */ |
782 | -struct kern_ipc_perm *ipcctl_pre_down(struct ipc_ids *ids, int id, int cmd, | |
787 | +struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns, | |
788 | + struct ipc_ids *ids, int id, int cmd, | |
783 | 789 | struct ipc64_perm *perm, int extra_perm) |
784 | 790 | { |
785 | 791 | struct kern_ipc_perm *ipcp; |
... | ... | @@ -799,8 +805,8 @@ |
799 | 805 | perm->gid, perm->mode); |
800 | 806 | |
801 | 807 | euid = current_euid(); |
802 | - if (euid == ipcp->cuid || | |
803 | - euid == ipcp->uid || capable(CAP_SYS_ADMIN)) | |
808 | + if (euid == ipcp->cuid || euid == ipcp->uid || | |
809 | + ns_capable(ns->user_ns, CAP_SYS_ADMIN)) | |
804 | 810 | return ipcp; |
805 | 811 | |
806 | 812 | err = -EPERM; |
ipc/util.h
... | ... | @@ -103,7 +103,7 @@ |
103 | 103 | void ipc_rmid(struct ipc_ids *, struct kern_ipc_perm *); |
104 | 104 | |
105 | 105 | /* must be called with ipcp locked */ |
106 | -int ipcperms(struct kern_ipc_perm *ipcp, short flg); | |
106 | +int ipcperms(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp, short flg); | |
107 | 107 | |
108 | 108 | /* for rare, potentially huge allocations. |
109 | 109 | * both function can sleep |
... | ... | @@ -126,7 +126,8 @@ |
126 | 126 | void kernel_to_ipc64_perm(struct kern_ipc_perm *in, struct ipc64_perm *out); |
127 | 127 | void ipc64_perm_to_ipc_perm(struct ipc64_perm *in, struct ipc_perm *out); |
128 | 128 | void ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out); |
129 | -struct kern_ipc_perm *ipcctl_pre_down(struct ipc_ids *ids, int id, int cmd, | |
129 | +struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns, | |
130 | + struct ipc_ids *ids, int id, int cmd, | |
130 | 131 | struct ipc64_perm *perm, int extra_perm); |
131 | 132 | |
132 | 133 | #ifndef __ARCH_WANT_IPC_PARSE_VERSION |
kernel/futex.c
... | ... | @@ -2418,10 +2418,19 @@ |
2418 | 2418 | goto err_unlock; |
2419 | 2419 | ret = -EPERM; |
2420 | 2420 | pcred = __task_cred(p); |
2421 | + /* If victim is in different user_ns, then uids are not | |
2422 | + comparable, so we must have CAP_SYS_PTRACE */ | |
2423 | + if (cred->user->user_ns != pcred->user->user_ns) { | |
2424 | + if (!ns_capable(pcred->user->user_ns, CAP_SYS_PTRACE)) | |
2425 | + goto err_unlock; | |
2426 | + goto ok; | |
2427 | + } | |
2428 | + /* If victim is in same user_ns, then uids are comparable */ | |
2421 | 2429 | if (cred->euid != pcred->euid && |
2422 | 2430 | cred->euid != pcred->uid && |
2423 | - !capable(CAP_SYS_PTRACE)) | |
2431 | + !ns_capable(pcred->user->user_ns, CAP_SYS_PTRACE)) | |
2424 | 2432 | goto err_unlock; |
2433 | +ok: | |
2425 | 2434 | head = p->robust_list; |
2426 | 2435 | rcu_read_unlock(); |
2427 | 2436 | } |
kernel/futex_compat.c
... | ... | @@ -153,10 +153,19 @@ |
153 | 153 | goto err_unlock; |
154 | 154 | ret = -EPERM; |
155 | 155 | pcred = __task_cred(p); |
156 | + /* If victim is in different user_ns, then uids are not | |
157 | + comparable, so we must have CAP_SYS_PTRACE */ | |
158 | + if (cred->user->user_ns != pcred->user->user_ns) { | |
159 | + if (!ns_capable(pcred->user->user_ns, CAP_SYS_PTRACE)) | |
160 | + goto err_unlock; | |
161 | + goto ok; | |
162 | + } | |
163 | + /* If victim is in same user_ns, then uids are comparable */ | |
156 | 164 | if (cred->euid != pcred->euid && |
157 | 165 | cred->euid != pcred->uid && |
158 | - !capable(CAP_SYS_PTRACE)) | |
166 | + !ns_capable(pcred->user->user_ns, CAP_SYS_PTRACE)) | |
159 | 167 | goto err_unlock; |
168 | +ok: | |
160 | 169 | head = p->compat_robust_list; |
161 | 170 | rcu_read_unlock(); |
162 | 171 | } |
kernel/groups.c
kernel/nsproxy.c
... | ... | @@ -75,15 +75,10 @@ |
75 | 75 | goto out_uts; |
76 | 76 | } |
77 | 77 | |
78 | - new_nsp->ipc_ns = copy_ipcs(flags, tsk->nsproxy->ipc_ns); | |
78 | + new_nsp->ipc_ns = copy_ipcs(flags, tsk); | |
79 | 79 | if (IS_ERR(new_nsp->ipc_ns)) { |
80 | 80 | err = PTR_ERR(new_nsp->ipc_ns); |
81 | 81 | goto out_ipc; |
82 | - } | |
83 | - if (new_nsp->ipc_ns != tsk->nsproxy->ipc_ns) { | |
84 | - put_user_ns(new_nsp->ipc_ns->user_ns); | |
85 | - new_nsp->ipc_ns->user_ns = task_cred_xxx(tsk, user)->user_ns; | |
86 | - get_user_ns(new_nsp->ipc_ns->user_ns); | |
87 | 82 | } |
88 | 83 | |
89 | 84 | new_nsp->pid_ns = copy_pid_ns(flags, task_active_pid_ns(tsk)); |
kernel/sched.c
... | ... | @@ -4892,8 +4892,11 @@ |
4892 | 4892 | |
4893 | 4893 | rcu_read_lock(); |
4894 | 4894 | pcred = __task_cred(p); |
4895 | - match = (cred->euid == pcred->euid || | |
4896 | - cred->euid == pcred->uid); | |
4895 | + if (cred->user->user_ns == pcred->user->user_ns) | |
4896 | + match = (cred->euid == pcred->euid || | |
4897 | + cred->euid == pcred->uid); | |
4898 | + else | |
4899 | + match = false; | |
4897 | 4900 | rcu_read_unlock(); |
4898 | 4901 | return match; |
4899 | 4902 | } |
... | ... | @@ -5221,7 +5224,7 @@ |
5221 | 5224 | goto out_free_cpus_allowed; |
5222 | 5225 | } |
5223 | 5226 | retval = -EPERM; |
5224 | - if (!check_same_owner(p) && !capable(CAP_SYS_NICE)) | |
5227 | + if (!check_same_owner(p) && !task_ns_capable(p, CAP_SYS_NICE)) | |
5225 | 5228 | goto out_unlock; |
5226 | 5229 | |
5227 | 5230 | retval = security_task_setscheduler(p); |
kernel/uid16.c