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 |