diff options
| author | Serge E. Hallyn <serge@hallyn.com> | 2011-03-23 19:43:24 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-03-23 22:47:08 -0400 |
| commit | b0e77598f87107001a00b8a4ece9c95e4254ccc4 (patch) | |
| tree | 2738276570e4faa7c92a64521c192f04dca93801 /ipc | |
| parent | b515498f5bb5f38fc0e390b4ff7d00b6077de127 (diff) | |
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>
Diffstat (limited to 'ipc')
| -rw-r--r-- | ipc/msg.c | 8 | ||||
| -rw-r--r-- | ipc/namespace.c | 13 | ||||
| -rw-r--r-- | ipc/sem.c | 10 | ||||
| -rw-r--r-- | ipc/shm.c | 9 | ||||
| -rw-r--r-- | ipc/util.c | 26 | ||||
| -rw-r--r-- | ipc/util.h | 5 |
6 files changed, 42 insertions, 29 deletions
| @@ -421,7 +421,7 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd, | |||
| 421 | return -EFAULT; | 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 | &msqid64.msg_perm, msqid64.msg_qbytes); | 425 | &msqid64.msg_perm, msqid64.msg_qbytes); |
| 426 | if (IS_ERR(ipcp)) | 426 | if (IS_ERR(ipcp)) |
| 427 | return PTR_ERR(ipcp); | 427 | return PTR_ERR(ipcp); |
| @@ -539,7 +539,7 @@ SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf) | |||
| 539 | success_return = 0; | 539 | success_return = 0; |
| 540 | } | 540 | } |
| 541 | err = -EACCES; | 541 | err = -EACCES; |
| 542 | if (ipcperms(&msq->q_perm, S_IRUGO)) | 542 | if (ipcperms(ns, &msq->q_perm, S_IRUGO)) |
| 543 | goto out_unlock; | 543 | goto out_unlock; |
| 544 | 544 | ||
| 545 | err = security_msg_queue_msgctl(msq, cmd); | 545 | err = security_msg_queue_msgctl(msq, cmd); |
| @@ -664,7 +664,7 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext, | |||
| 664 | struct msg_sender s; | 664 | struct msg_sender s; |
| 665 | 665 | ||
| 666 | err = -EACCES; | 666 | err = -EACCES; |
| 667 | if (ipcperms(&msq->q_perm, S_IWUGO)) | 667 | if (ipcperms(ns, &msq->q_perm, S_IWUGO)) |
| 668 | goto out_unlock_free; | 668 | goto out_unlock_free; |
| 669 | 669 | ||
| 670 | err = security_msg_queue_msgsnd(msq, msg, msgflg); | 670 | err = security_msg_queue_msgsnd(msq, msg, msgflg); |
| @@ -774,7 +774,7 @@ long do_msgrcv(int msqid, long *pmtype, void __user *mtext, | |||
| 774 | struct list_head *tmp; | 774 | struct list_head *tmp; |
| 775 | 775 | ||
| 776 | msg = ERR_PTR(-EACCES); | 776 | msg = ERR_PTR(-EACCES); |
| 777 | if (ipcperms(&msq->q_perm, S_IRUGO)) | 777 | if (ipcperms(ns, &msq->q_perm, S_IRUGO)) |
| 778 | goto out_unlock; | 778 | goto out_unlock; |
| 779 | 779 | ||
| 780 | msg = ERR_PTR(-EAGAIN); | 780 | msg = ERR_PTR(-EAGAIN); |
diff --git a/ipc/namespace.c b/ipc/namespace.c index aa1889962693..3c3e5223e7e5 100644 --- a/ipc/namespace.c +++ b/ipc/namespace.c | |||
| @@ -15,7 +15,8 @@ | |||
| 15 | 15 | ||
| 16 | #include "util.h" | 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 | struct ipc_namespace *ns; | 21 | struct ipc_namespace *ns; |
| 21 | int err; | 22 | int err; |
| @@ -44,17 +45,19 @@ static struct ipc_namespace *create_ipc_ns(struct ipc_namespace *old_ns) | |||
| 44 | ipcns_notify(IPCNS_CREATED); | 45 | ipcns_notify(IPCNS_CREATED); |
| 45 | register_ipcns_notifier(ns); | 46 | register_ipcns_notifier(ns); |
| 46 | 47 | ||
| 47 | ns->user_ns = old_ns->user_ns; | 48 | ns->user_ns = get_user_ns(task_cred_xxx(tsk, user)->user_ns); |
| 48 | get_user_ns(ns->user_ns); | ||
| 49 | 49 | ||
| 50 | return ns; | 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 | if (!(flags & CLONE_NEWIPC)) | 58 | if (!(flags & CLONE_NEWIPC)) |
| 56 | return get_ipc_ns(ns); | 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 | /* |
| @@ -817,7 +817,7 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid, | |||
| 817 | } | 817 | } |
| 818 | 818 | ||
| 819 | err = -EACCES; | 819 | err = -EACCES; |
| 820 | if (ipcperms (&sma->sem_perm, S_IRUGO)) | 820 | if (ipcperms(ns, &sma->sem_perm, S_IRUGO)) |
| 821 | goto out_unlock; | 821 | goto out_unlock; |
| 822 | 822 | ||
| 823 | err = security_sem_semctl(sma, cmd); | 823 | err = security_sem_semctl(sma, cmd); |
| @@ -862,7 +862,8 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, | |||
| 862 | nsems = sma->sem_nsems; | 862 | nsems = sma->sem_nsems; |
| 863 | 863 | ||
| 864 | err = -EACCES; | 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 | goto out_unlock; | 867 | goto out_unlock; |
| 867 | 868 | ||
| 868 | err = security_sem_semctl(sma, cmd); | 869 | err = security_sem_semctl(sma, cmd); |
| @@ -1047,7 +1048,8 @@ static int semctl_down(struct ipc_namespace *ns, int semid, | |||
| 1047 | return -EFAULT; | 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 | if (IS_ERR(ipcp)) | 1053 | if (IS_ERR(ipcp)) |
| 1052 | return PTR_ERR(ipcp); | 1054 | return PTR_ERR(ipcp); |
| 1053 | 1055 | ||
| @@ -1386,7 +1388,7 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, | |||
| 1386 | goto out_unlock_free; | 1388 | goto out_unlock_free; |
| 1387 | 1389 | ||
| 1388 | error = -EACCES; | 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 | goto out_unlock_free; | 1392 | goto out_unlock_free; |
| 1391 | 1393 | ||
| 1392 | error = security_sem_semop(sma, sops, nsops, alter); | 1394 | error = security_sem_semop(sma, sops, nsops, alter); |
| @@ -623,7 +623,8 @@ static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd, | |||
| 623 | return -EFAULT; | 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 | if (IS_ERR(ipcp)) | 628 | if (IS_ERR(ipcp)) |
| 628 | return PTR_ERR(ipcp); | 629 | return PTR_ERR(ipcp); |
| 629 | 630 | ||
| @@ -737,7 +738,7 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf) | |||
| 737 | result = 0; | 738 | result = 0; |
| 738 | } | 739 | } |
| 739 | err = -EACCES; | 740 | err = -EACCES; |
| 740 | if (ipcperms (&shp->shm_perm, S_IRUGO)) | 741 | if (ipcperms(ns, &shp->shm_perm, S_IRUGO)) |
| 741 | goto out_unlock; | 742 | goto out_unlock; |
| 742 | err = security_shm_shmctl(shp, cmd); | 743 | err = security_shm_shmctl(shp, cmd); |
| 743 | if (err) | 744 | if (err) |
| @@ -773,7 +774,7 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf) | |||
| 773 | 774 | ||
| 774 | audit_ipc_obj(&(shp->shm_perm)); | 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 | uid_t euid = current_euid(); | 778 | uid_t euid = current_euid(); |
| 778 | err = -EPERM; | 779 | err = -EPERM; |
| 779 | if (euid != shp->shm_perm.uid && | 780 | if (euid != shp->shm_perm.uid && |
| @@ -888,7 +889,7 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr) | |||
| 888 | } | 889 | } |
| 889 | 890 | ||
| 890 | err = -EACCES; | 891 | err = -EACCES; |
| 891 | if (ipcperms(&shp->shm_perm, acc_mode)) | 892 | if (ipcperms(ns, &shp->shm_perm, acc_mode)) |
| 892 | goto out_unlock; | 893 | goto out_unlock; |
| 893 | 894 | ||
| 894 | err = security_shm_shmat(shp, shmaddr, shmflg); | 895 | err = security_shm_shmat(shp, shmaddr, shmflg); |
diff --git a/ipc/util.c b/ipc/util.c index 69a0cc13d966..8fd1b891ec0c 100644 --- a/ipc/util.c +++ b/ipc/util.c | |||
| @@ -329,12 +329,14 @@ retry: | |||
| 329 | * | 329 | * |
| 330 | * It is called with ipc_ids.rw_mutex and ipcp->lock held. | 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, | 332 | static int ipc_check_perms(struct ipc_namespace *ns, |
| 333 | struct ipc_params *params) | 333 | struct kern_ipc_perm *ipcp, |
| 334 | struct ipc_ops *ops, | ||
| 335 | struct ipc_params *params) | ||
| 334 | { | 336 | { |
| 335 | int err; | 337 | int err; |
| 336 | 338 | ||
| 337 | if (ipcperms(ipcp, params->flg)) | 339 | if (ipcperms(ns, ipcp, params->flg)) |
| 338 | err = -EACCES; | 340 | err = -EACCES; |
| 339 | else { | 341 | else { |
| 340 | err = ops->associate(ipcp, params->flg); | 342 | err = ops->associate(ipcp, params->flg); |
| @@ -396,7 +398,7 @@ retry: | |||
| 396 | * ipc_check_perms returns the IPC id on | 398 | * ipc_check_perms returns the IPC id on |
| 397 | * success | 399 | * success |
| 398 | */ | 400 | */ |
| 399 | err = ipc_check_perms(ipcp, ops, params); | 401 | err = ipc_check_perms(ns, ipcp, ops, params); |
| 400 | } | 402 | } |
| 401 | ipc_unlock(ipcp); | 403 | ipc_unlock(ipcp); |
| 402 | } | 404 | } |
| @@ -610,10 +612,12 @@ void ipc_rcu_putref(void *ptr) | |||
| 610 | * | 612 | * |
| 611 | * Check user, group, other permissions for access | 613 | * Check user, group, other permissions for access |
| 612 | * to ipc resources. return 0 if allowed | 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) | 619 | int ipcperms(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp, short flag) |
| 616 | { /* flag will most probably be 0 or S_...UGO from <linux/stat.h> */ | 620 | { |
| 617 | uid_t euid = current_euid(); | 621 | uid_t euid = current_euid(); |
| 618 | int requested_mode, granted_mode; | 622 | int requested_mode, granted_mode; |
| 619 | 623 | ||
| @@ -627,7 +631,7 @@ int ipcperms (struct kern_ipc_perm *ipcp, short flag) | |||
| 627 | granted_mode >>= 3; | 631 | granted_mode >>= 3; |
| 628 | /* is there some bit set in requested_mode but not in granted_mode? */ | 632 | /* is there some bit set in requested_mode but not in granted_mode? */ |
| 629 | if ((requested_mode & ~granted_mode & 0007) && | 633 | if ((requested_mode & ~granted_mode & 0007) && |
| 630 | !capable(CAP_IPC_OWNER)) | 634 | !ns_capable(ns->user_ns, CAP_IPC_OWNER)) |
| 631 | return -1; | 635 | return -1; |
| 632 | 636 | ||
| 633 | return security_ipc_permission(ipcp, flag); | 637 | return security_ipc_permission(ipcp, flag); |
| @@ -765,6 +769,7 @@ void ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out) | |||
| 765 | 769 | ||
| 766 | /** | 770 | /** |
| 767 | * ipcctl_pre_down - retrieve an ipc and check permissions for some IPC_XXX cmd | 771 | * ipcctl_pre_down - retrieve an ipc and check permissions for some IPC_XXX cmd |
| 772 | * @ids: the ipc namespace | ||
| 768 | * @ids: the table of ids where to look for the ipc | 773 | * @ids: the table of ids where to look for the ipc |
| 769 | * @id: the id of the ipc to retrieve | 774 | * @id: the id of the ipc to retrieve |
| 770 | * @cmd: the cmd to check | 775 | * @cmd: the cmd to check |
| @@ -779,7 +784,8 @@ void ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out) | |||
| 779 | * - returns the ipc with both ipc and rw_mutex locks held in case of success | 784 | * - returns the ipc with both ipc and rw_mutex locks held in case of success |
| 780 | * or an err-code without any lock held otherwise. | 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 | struct ipc64_perm *perm, int extra_perm) | 789 | struct ipc64_perm *perm, int extra_perm) |
| 784 | { | 790 | { |
| 785 | struct kern_ipc_perm *ipcp; | 791 | struct kern_ipc_perm *ipcp; |
| @@ -799,8 +805,8 @@ struct kern_ipc_perm *ipcctl_pre_down(struct ipc_ids *ids, int id, int cmd, | |||
| 799 | perm->gid, perm->mode); | 805 | perm->gid, perm->mode); |
| 800 | 806 | ||
| 801 | euid = current_euid(); | 807 | euid = current_euid(); |
| 802 | if (euid == ipcp->cuid || | 808 | if (euid == ipcp->cuid || euid == ipcp->uid || |
| 803 | euid == ipcp->uid || capable(CAP_SYS_ADMIN)) | 809 | ns_capable(ns->user_ns, CAP_SYS_ADMIN)) |
| 804 | return ipcp; | 810 | return ipcp; |
| 805 | 811 | ||
| 806 | err = -EPERM; | 812 | err = -EPERM; |
diff --git a/ipc/util.h b/ipc/util.h index 764b51a37a6a..6f5c20bedaab 100644 --- a/ipc/util.h +++ b/ipc/util.h | |||
| @@ -103,7 +103,7 @@ int ipc_get_maxid(struct ipc_ids *); | |||
| 103 | void ipc_rmid(struct ipc_ids *, struct kern_ipc_perm *); | 103 | void ipc_rmid(struct ipc_ids *, struct kern_ipc_perm *); |
| 104 | 104 | ||
| 105 | /* must be called with ipcp locked */ | 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 | /* for rare, potentially huge allocations. | 108 | /* for rare, potentially huge allocations. |
| 109 | * both function can sleep | 109 | * both function can sleep |
| @@ -126,7 +126,8 @@ struct kern_ipc_perm *ipc_lock(struct ipc_ids *, int); | |||
| 126 | void kernel_to_ipc64_perm(struct kern_ipc_perm *in, struct ipc64_perm *out); | 126 | void kernel_to_ipc64_perm(struct kern_ipc_perm *in, struct ipc64_perm *out); |
| 127 | void ipc64_perm_to_ipc_perm(struct ipc64_perm *in, struct ipc_perm *out); | 127 | void ipc64_perm_to_ipc_perm(struct ipc64_perm *in, struct ipc_perm *out); |
| 128 | void ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out); | 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 | struct ipc64_perm *perm, int extra_perm); | 131 | struct ipc64_perm *perm, int extra_perm); |
| 131 | 132 | ||
| 132 | #ifndef __ARCH_WANT_IPC_PARSE_VERSION | 133 | #ifndef __ARCH_WANT_IPC_PARSE_VERSION |
