Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Serge E. Hallyn and committed by
Linus Torvalds
b0e77598 b515498f

+75 -45
+4 -3
include/linux/ipc_namespace.h
··· 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 ··· 94 93 95 94 #if defined(CONFIG_IPC_NS) 96 95 extern struct ipc_namespace *copy_ipcs(unsigned long flags, 97 - struct ipc_namespace *ns); 96 + struct task_struct *tsk); 98 97 static inline struct ipc_namespace *get_ipc_ns(struct ipc_namespace *ns) 99 98 { 100 99 if (ns) ··· 105 104 extern void put_ipc_ns(struct ipc_namespace *ns); 106 105 #else 107 106 static inline struct ipc_namespace *copy_ipcs(unsigned long flags, 108 - struct ipc_namespace *ns) 107 + struct task_struct *tsk) 109 108 { 110 109 if (flags & CLONE_NEWIPC) 111 110 return ERR_PTR(-EINVAL); 112 111 113 - return ns; 112 + return tsk->nsproxy->ipc_ns; 114 113 } 115 114 116 115 static inline struct ipc_namespace *get_ipc_ns(struct ipc_namespace *ns)
+4 -4
ipc/msg.c
··· 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 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 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 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);
+8 -5
ipc/namespace.c
··· 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; ··· 45 44 ipcns_notify(IPCNS_CREATED); 46 45 register_ipcns_notifier(ns); 47 46 48 - ns->user_ns = old_ns->user_ns; 49 - get_user_ns(ns->user_ns); 47 + ns->user_ns = get_user_ns(task_cred_xxx(tsk, user)->user_ns); 50 48 51 49 return ns; 52 50 } 53 51 54 - struct ipc_namespace *copy_ipcs(unsigned long flags, struct ipc_namespace *ns) 52 + struct ipc_namespace *copy_ipcs(unsigned long flags, 53 + struct task_struct *tsk) 55 54 { 55 + struct ipc_namespace *ns = tsk->nsproxy->ipc_ns; 56 + 56 57 if (!(flags & CLONE_NEWIPC)) 57 58 return get_ipc_ns(ns); 58 - return create_ipc_ns(ns); 59 + return create_ipc_ns(tsk, ns); 59 60 } 60 61 61 62 /*
+6 -4
ipc/sem.c
··· 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 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); ··· 1048 1047 return -EFAULT; 1049 1048 } 1050 1049 1051 - ipcp = ipcctl_pre_down(&sem_ids(ns), semid, cmd, &semid64.sem_perm, 0); 1050 + ipcp = ipcctl_pre_down(ns, &sem_ids(ns), semid, cmd, 1051 + &semid64.sem_perm, 0); 1052 1052 if (IS_ERR(ipcp)) 1053 1053 return PTR_ERR(ipcp); 1054 1054 ··· 1388 1386 goto out_unlock_free; 1389 1387 1390 1388 error = -EACCES; 1391 - if (ipcperms(&sma->sem_perm, alter ? S_IWUGO : S_IRUGO)) 1389 + if (ipcperms(ns, &sma->sem_perm, alter ? S_IWUGO : S_IRUGO)) 1392 1390 goto out_unlock_free; 1393 1391 1394 1392 error = security_sem_semop(sma, sops, nsops, alter);
+5 -4
ipc/shm.c
··· 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 ··· 738 737 result = 0; 739 738 } 740 739 err = -EACCES; 741 - if (ipcperms (&shp->shm_perm, S_IRUGO)) 740 + if (ipcperms(ns, &shp->shm_perm, S_IRUGO)) 742 741 goto out_unlock; 743 742 err = security_shm_shmctl(shp, cmd); 744 743 if (err) ··· 774 773 775 774 audit_ipc_obj(&(shp->shm_perm)); 776 775 777 - if (!capable(CAP_IPC_LOCK)) { 776 + if (!ns_capable(ns->user_ns, CAP_IPC_LOCK)) { 778 777 uid_t euid = current_euid(); 779 778 err = -EPERM; 780 779 if (euid != shp->shm_perm.uid && ··· 889 888 } 890 889 891 890 err = -EACCES; 892 - if (ipcperms(&shp->shm_perm, acc_mode)) 891 + if (ipcperms(ns, &shp->shm_perm, acc_mode)) 893 892 goto out_unlock; 894 893 895 894 err = security_shm_shmat(shp, shmaddr, shmflg);
+16 -10
ipc/util.c
··· 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); ··· 398 396 * ipc_check_perms returns the IPC id on 399 397 * success 400 398 */ 401 - err = ipc_check_perms(ipcp, ops, params); 399 + err = ipc_check_perms(ns, ipcp, ops, params); 402 400 } 403 401 ipc_unlock(ipcp); 404 402 } ··· 612 610 * 613 611 * Check user, group, other permissions for access 614 612 * to ipc resources. return 0 if allowed 613 + * 614 + * @flag will most probably be 0 or S_...UGO from <linux/stat.h> 615 615 */ 616 616 617 - int ipcperms (struct kern_ipc_perm *ipcp, short flag) 618 - { /* flag will most probably be 0 or S_...UGO from <linux/stat.h> */ 617 + int ipcperms(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp, short flag) 618 + { 619 619 uid_t euid = current_euid(); 620 620 int requested_mode, granted_mode; 621 621 ··· 631 627 granted_mode >>= 3; 632 628 /* is there some bit set in requested_mode but not in granted_mode? */ 633 629 if ((requested_mode & ~granted_mode & 0007) && 634 - !capable(CAP_IPC_OWNER)) 630 + !ns_capable(ns->user_ns, CAP_IPC_OWNER)) 635 631 return -1; 636 632 637 633 return security_ipc_permission(ipcp, flag); ··· 769 765 770 766 /** 771 767 * ipcctl_pre_down - retrieve an ipc and check permissions for some IPC_XXX cmd 768 + * @ids: the ipc namespace 772 769 * @ids: the table of ids where to look for the ipc 773 770 * @id: the id of the ipc to retrieve 774 771 * @cmd: the cmd to check ··· 784 779 * - returns the ipc with both ipc and rw_mutex locks held in case of success 785 780 * or an err-code without any lock held otherwise. 786 781 */ 787 - struct kern_ipc_perm *ipcctl_pre_down(struct ipc_ids *ids, int id, int cmd, 782 + struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns, 783 + struct ipc_ids *ids, int id, int cmd, 788 784 struct ipc64_perm *perm, int extra_perm) 789 785 { 790 786 struct kern_ipc_perm *ipcp; ··· 805 799 perm->gid, perm->mode); 806 800 807 801 euid = current_euid(); 808 - if (euid == ipcp->cuid || 809 - euid == ipcp->uid || capable(CAP_SYS_ADMIN)) 802 + if (euid == ipcp->cuid || euid == ipcp->uid || 803 + ns_capable(ns->user_ns, CAP_SYS_ADMIN)) 810 804 return ipcp; 811 805 812 806 err = -EPERM;
+3 -2
ipc/util.h
··· 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 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
+10 -1
kernel/futex.c
··· 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 }
+10 -1
kernel/futex_compat.c
··· 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 }
+1 -1
kernel/groups.c
··· 233 233 struct group_info *group_info; 234 234 int retval; 235 235 236 - if (!capable(CAP_SETGID)) 236 + if (!nsown_capable(CAP_SETGID)) 237 237 return -EPERM; 238 238 if ((unsigned)gidsetsize > NGROUPS_MAX) 239 239 return -EINVAL;
+1 -6
kernel/nsproxy.c
··· 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));
+6 -3
kernel/sched.c
··· 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 } ··· 5224 5221 goto out_free_cpus_allowed; 5225 5222 } 5226 5223 retval = -EPERM; 5227 - if (!check_same_owner(p) && !capable(CAP_SYS_NICE)) 5224 + if (!check_same_owner(p) && !task_ns_capable(p, CAP_SYS_NICE)) 5228 5225 goto out_unlock; 5229 5226 5230 5227 retval = security_task_setscheduler(p);
+1 -1
kernel/uid16.c
··· 189 189 struct group_info *group_info; 190 190 int retval; 191 191 192 - if (!capable(CAP_SETGID)) 192 + if (!nsown_capable(CAP_SETGID)) 193 193 return -EPERM; 194 194 if ((unsigned)gidsetsize > NGROUPS_MAX) 195 195 return -EINVAL;