diff options
author | Eric W. Biederman <ebiederm@xmission.com> | 2012-02-07 19:54:11 -0500 |
---|---|---|
committer | Eric W. Biederman <ebiederm@xmission.com> | 2012-09-07 01:17:20 -0400 |
commit | 1efdb69b0bb41dec8ee3e2cac0a0f167837d0919 (patch) | |
tree | a9eb64c44d773e7b4fead20a7bfa9a354abf3bfa | |
parent | 9582d90196aa879e6acf866f02a1adead08707b5 (diff) |
userns: Convert ipc to use kuid and kgid where appropriate
- Store the ipc owner and creator with a kuid
- Store the ipc group and the crators group with a kgid.
- Add error handling to ipc_update_perms, allowing it to
fail if the uids and gids can not be converted to kuids
or kgids.
- Modify the proc files to display the ipc creator and
owner in the user namespace of the opener of the proc file.
Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
-rw-r--r-- | include/linux/ipc.h | 9 | ||||
-rw-r--r-- | init/Kconfig | 1 | ||||
-rw-r--r-- | ipc/msg.c | 14 | ||||
-rw-r--r-- | ipc/sem.c | 13 | ||||
-rw-r--r-- | ipc/shm.c | 19 | ||||
-rw-r--r-- | ipc/util.c | 35 | ||||
-rw-r--r-- | ipc/util.h | 2 |
7 files changed, 55 insertions, 38 deletions
diff --git a/include/linux/ipc.h b/include/linux/ipc.h index 30e816148df4..ca833fdc3138 100644 --- a/include/linux/ipc.h +++ b/include/linux/ipc.h | |||
@@ -79,6 +79,7 @@ struct ipc_kludge { | |||
79 | 79 | ||
80 | #ifdef __KERNEL__ | 80 | #ifdef __KERNEL__ |
81 | #include <linux/spinlock.h> | 81 | #include <linux/spinlock.h> |
82 | #include <linux/uidgid.h> | ||
82 | 83 | ||
83 | #define IPCMNI 32768 /* <= MAX_INT limit for ipc arrays (including sysctl changes) */ | 84 | #define IPCMNI 32768 /* <= MAX_INT limit for ipc arrays (including sysctl changes) */ |
84 | 85 | ||
@@ -89,10 +90,10 @@ struct kern_ipc_perm | |||
89 | int deleted; | 90 | int deleted; |
90 | int id; | 91 | int id; |
91 | key_t key; | 92 | key_t key; |
92 | uid_t uid; | 93 | kuid_t uid; |
93 | gid_t gid; | 94 | kgid_t gid; |
94 | uid_t cuid; | 95 | kuid_t cuid; |
95 | gid_t cgid; | 96 | kgid_t cgid; |
96 | umode_t mode; | 97 | umode_t mode; |
97 | unsigned long seq; | 98 | unsigned long seq; |
98 | void *security; | 99 | void *security; |
diff --git a/init/Kconfig b/init/Kconfig index 7d4422c92cca..d09738dee238 100644 --- a/init/Kconfig +++ b/init/Kconfig | |||
@@ -925,7 +925,6 @@ config UIDGID_CONVERTED | |||
925 | 925 | ||
926 | # List of kernel pieces that need user namespace work | 926 | # List of kernel pieces that need user namespace work |
927 | # Features | 927 | # Features |
928 | depends on SYSVIPC = n | ||
929 | depends on IMA = n | 928 | depends on IMA = n |
930 | depends on EVM = n | 929 | depends on EVM = n |
931 | depends on KEYS = n | 930 | depends on KEYS = n |
@@ -443,9 +443,12 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd, | |||
443 | goto out_unlock; | 443 | goto out_unlock; |
444 | } | 444 | } |
445 | 445 | ||
446 | err = ipc_update_perm(&msqid64.msg_perm, ipcp); | ||
447 | if (err) | ||
448 | goto out_unlock; | ||
449 | |||
446 | msq->q_qbytes = msqid64.msg_qbytes; | 450 | msq->q_qbytes = msqid64.msg_qbytes; |
447 | 451 | ||
448 | ipc_update_perm(&msqid64.msg_perm, ipcp); | ||
449 | msq->q_ctime = get_seconds(); | 452 | msq->q_ctime = get_seconds(); |
450 | /* sleeping receivers might be excluded by | 453 | /* sleeping receivers might be excluded by |
451 | * stricter permissions. | 454 | * stricter permissions. |
@@ -922,6 +925,7 @@ out: | |||
922 | #ifdef CONFIG_PROC_FS | 925 | #ifdef CONFIG_PROC_FS |
923 | static int sysvipc_msg_proc_show(struct seq_file *s, void *it) | 926 | static int sysvipc_msg_proc_show(struct seq_file *s, void *it) |
924 | { | 927 | { |
928 | struct user_namespace *user_ns = seq_user_ns(s); | ||
925 | struct msg_queue *msq = it; | 929 | struct msg_queue *msq = it; |
926 | 930 | ||
927 | return seq_printf(s, | 931 | return seq_printf(s, |
@@ -933,10 +937,10 @@ static int sysvipc_msg_proc_show(struct seq_file *s, void *it) | |||
933 | msq->q_qnum, | 937 | msq->q_qnum, |
934 | msq->q_lspid, | 938 | msq->q_lspid, |
935 | msq->q_lrpid, | 939 | msq->q_lrpid, |
936 | msq->q_perm.uid, | 940 | from_kuid_munged(user_ns, msq->q_perm.uid), |
937 | msq->q_perm.gid, | 941 | from_kgid_munged(user_ns, msq->q_perm.gid), |
938 | msq->q_perm.cuid, | 942 | from_kuid_munged(user_ns, msq->q_perm.cuid), |
939 | msq->q_perm.cgid, | 943 | from_kgid_munged(user_ns, msq->q_perm.cgid), |
940 | msq->q_stime, | 944 | msq->q_stime, |
941 | msq->q_rtime, | 945 | msq->q_rtime, |
942 | msq->q_ctime); | 946 | msq->q_ctime); |
@@ -1104,7 +1104,9 @@ static int semctl_down(struct ipc_namespace *ns, int semid, | |||
1104 | freeary(ns, ipcp); | 1104 | freeary(ns, ipcp); |
1105 | goto out_up; | 1105 | goto out_up; |
1106 | case IPC_SET: | 1106 | case IPC_SET: |
1107 | ipc_update_perm(&semid64.sem_perm, ipcp); | 1107 | err = ipc_update_perm(&semid64.sem_perm, ipcp); |
1108 | if (err) | ||
1109 | goto out_unlock; | ||
1108 | sma->sem_ctime = get_seconds(); | 1110 | sma->sem_ctime = get_seconds(); |
1109 | break; | 1111 | break; |
1110 | default: | 1112 | default: |
@@ -1677,6 +1679,7 @@ void exit_sem(struct task_struct *tsk) | |||
1677 | #ifdef CONFIG_PROC_FS | 1679 | #ifdef CONFIG_PROC_FS |
1678 | static int sysvipc_sem_proc_show(struct seq_file *s, void *it) | 1680 | static int sysvipc_sem_proc_show(struct seq_file *s, void *it) |
1679 | { | 1681 | { |
1682 | struct user_namespace *user_ns = seq_user_ns(s); | ||
1680 | struct sem_array *sma = it; | 1683 | struct sem_array *sma = it; |
1681 | 1684 | ||
1682 | return seq_printf(s, | 1685 | return seq_printf(s, |
@@ -1685,10 +1688,10 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it) | |||
1685 | sma->sem_perm.id, | 1688 | sma->sem_perm.id, |
1686 | sma->sem_perm.mode, | 1689 | sma->sem_perm.mode, |
1687 | sma->sem_nsems, | 1690 | sma->sem_nsems, |
1688 | sma->sem_perm.uid, | 1691 | from_kuid_munged(user_ns, sma->sem_perm.uid), |
1689 | sma->sem_perm.gid, | 1692 | from_kgid_munged(user_ns, sma->sem_perm.gid), |
1690 | sma->sem_perm.cuid, | 1693 | from_kuid_munged(user_ns, sma->sem_perm.cuid), |
1691 | sma->sem_perm.cgid, | 1694 | from_kgid_munged(user_ns, sma->sem_perm.cgid), |
1692 | sma->sem_otime, | 1695 | sma->sem_otime, |
1693 | sma->sem_ctime); | 1696 | sma->sem_ctime); |
1694 | } | 1697 | } |
@@ -758,7 +758,9 @@ static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd, | |||
758 | do_shm_rmid(ns, ipcp); | 758 | do_shm_rmid(ns, ipcp); |
759 | goto out_up; | 759 | goto out_up; |
760 | case IPC_SET: | 760 | case IPC_SET: |
761 | ipc_update_perm(&shmid64.shm_perm, ipcp); | 761 | err = ipc_update_perm(&shmid64.shm_perm, ipcp); |
762 | if (err) | ||
763 | goto out_unlock; | ||
762 | shp->shm_ctim = get_seconds(); | 764 | shp->shm_ctim = get_seconds(); |
763 | break; | 765 | break; |
764 | default: | 766 | default: |
@@ -893,10 +895,10 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf) | |||
893 | audit_ipc_obj(&(shp->shm_perm)); | 895 | audit_ipc_obj(&(shp->shm_perm)); |
894 | 896 | ||
895 | if (!ns_capable(ns->user_ns, CAP_IPC_LOCK)) { | 897 | if (!ns_capable(ns->user_ns, CAP_IPC_LOCK)) { |
896 | uid_t euid = current_euid(); | 898 | kuid_t euid = current_euid(); |
897 | err = -EPERM; | 899 | err = -EPERM; |
898 | if (euid != shp->shm_perm.uid && | 900 | if (!uid_eq(euid, shp->shm_perm.uid) && |
899 | euid != shp->shm_perm.cuid) | 901 | !uid_eq(euid, shp->shm_perm.cuid)) |
900 | goto out_unlock; | 902 | goto out_unlock; |
901 | if (cmd == SHM_LOCK && !rlimit(RLIMIT_MEMLOCK)) | 903 | if (cmd == SHM_LOCK && !rlimit(RLIMIT_MEMLOCK)) |
902 | goto out_unlock; | 904 | goto out_unlock; |
@@ -1220,6 +1222,7 @@ SYSCALL_DEFINE1(shmdt, char __user *, shmaddr) | |||
1220 | #ifdef CONFIG_PROC_FS | 1222 | #ifdef CONFIG_PROC_FS |
1221 | static int sysvipc_shm_proc_show(struct seq_file *s, void *it) | 1223 | static int sysvipc_shm_proc_show(struct seq_file *s, void *it) |
1222 | { | 1224 | { |
1225 | struct user_namespace *user_ns = seq_user_ns(s); | ||
1223 | struct shmid_kernel *shp = it; | 1226 | struct shmid_kernel *shp = it; |
1224 | unsigned long rss = 0, swp = 0; | 1227 | unsigned long rss = 0, swp = 0; |
1225 | 1228 | ||
@@ -1242,10 +1245,10 @@ static int sysvipc_shm_proc_show(struct seq_file *s, void *it) | |||
1242 | shp->shm_cprid, | 1245 | shp->shm_cprid, |
1243 | shp->shm_lprid, | 1246 | shp->shm_lprid, |
1244 | shp->shm_nattch, | 1247 | shp->shm_nattch, |
1245 | shp->shm_perm.uid, | 1248 | from_kuid_munged(user_ns, shp->shm_perm.uid), |
1246 | shp->shm_perm.gid, | 1249 | from_kgid_munged(user_ns, shp->shm_perm.gid), |
1247 | shp->shm_perm.cuid, | 1250 | from_kuid_munged(user_ns, shp->shm_perm.cuid), |
1248 | shp->shm_perm.cgid, | 1251 | from_kgid_munged(user_ns, shp->shm_perm.cgid), |
1249 | shp->shm_atim, | 1252 | shp->shm_atim, |
1250 | shp->shm_dtim, | 1253 | shp->shm_dtim, |
1251 | shp->shm_ctim, | 1254 | shp->shm_ctim, |
diff --git a/ipc/util.c b/ipc/util.c index eb07fd356f27..72fd0785ac94 100644 --- a/ipc/util.c +++ b/ipc/util.c | |||
@@ -249,8 +249,8 @@ int ipc_get_maxid(struct ipc_ids *ids) | |||
249 | 249 | ||
250 | int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size) | 250 | int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size) |
251 | { | 251 | { |
252 | uid_t euid; | 252 | kuid_t euid; |
253 | gid_t egid; | 253 | kgid_t egid; |
254 | int id, err; | 254 | int id, err; |
255 | 255 | ||
256 | if (size > IPCMNI) | 256 | if (size > IPCMNI) |
@@ -606,14 +606,14 @@ void ipc_rcu_putref(void *ptr) | |||
606 | 606 | ||
607 | int ipcperms(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp, short flag) | 607 | int ipcperms(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp, short flag) |
608 | { | 608 | { |
609 | uid_t euid = current_euid(); | 609 | kuid_t euid = current_euid(); |
610 | int requested_mode, granted_mode; | 610 | int requested_mode, granted_mode; |
611 | 611 | ||
612 | audit_ipc_obj(ipcp); | 612 | audit_ipc_obj(ipcp); |
613 | requested_mode = (flag >> 6) | (flag >> 3) | flag; | 613 | requested_mode = (flag >> 6) | (flag >> 3) | flag; |
614 | granted_mode = ipcp->mode; | 614 | granted_mode = ipcp->mode; |
615 | if (euid == ipcp->cuid || | 615 | if (uid_eq(euid, ipcp->cuid) || |
616 | euid == ipcp->uid) | 616 | uid_eq(euid, ipcp->uid)) |
617 | granted_mode >>= 6; | 617 | granted_mode >>= 6; |
618 | else if (in_group_p(ipcp->cgid) || in_group_p(ipcp->gid)) | 618 | else if (in_group_p(ipcp->cgid) || in_group_p(ipcp->gid)) |
619 | granted_mode >>= 3; | 619 | granted_mode >>= 3; |
@@ -643,10 +643,10 @@ int ipcperms(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp, short flag) | |||
643 | void kernel_to_ipc64_perm (struct kern_ipc_perm *in, struct ipc64_perm *out) | 643 | void kernel_to_ipc64_perm (struct kern_ipc_perm *in, struct ipc64_perm *out) |
644 | { | 644 | { |
645 | out->key = in->key; | 645 | out->key = in->key; |
646 | out->uid = in->uid; | 646 | out->uid = from_kuid_munged(current_user_ns(), in->uid); |
647 | out->gid = in->gid; | 647 | out->gid = from_kgid_munged(current_user_ns(), in->gid); |
648 | out->cuid = in->cuid; | 648 | out->cuid = from_kuid_munged(current_user_ns(), in->cuid); |
649 | out->cgid = in->cgid; | 649 | out->cgid = from_kgid_munged(current_user_ns(), in->cgid); |
650 | out->mode = in->mode; | 650 | out->mode = in->mode; |
651 | out->seq = in->seq; | 651 | out->seq = in->seq; |
652 | } | 652 | } |
@@ -747,12 +747,19 @@ int ipcget(struct ipc_namespace *ns, struct ipc_ids *ids, | |||
747 | * @in: the permission given as input. | 747 | * @in: the permission given as input. |
748 | * @out: the permission of the ipc to set. | 748 | * @out: the permission of the ipc to set. |
749 | */ | 749 | */ |
750 | void ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out) | 750 | int ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out) |
751 | { | 751 | { |
752 | out->uid = in->uid; | 752 | kuid_t uid = make_kuid(current_user_ns(), in->uid); |
753 | out->gid = in->gid; | 753 | kgid_t gid = make_kgid(current_user_ns(), in->gid); |
754 | if (!uid_valid(uid) || !gid_valid(gid)) | ||
755 | return -EINVAL; | ||
756 | |||
757 | out->uid = uid; | ||
758 | out->gid = gid; | ||
754 | out->mode = (out->mode & ~S_IRWXUGO) | 759 | out->mode = (out->mode & ~S_IRWXUGO) |
755 | | (in->mode & S_IRWXUGO); | 760 | | (in->mode & S_IRWXUGO); |
761 | |||
762 | return 0; | ||
756 | } | 763 | } |
757 | 764 | ||
758 | /** | 765 | /** |
@@ -777,7 +784,7 @@ struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns, | |||
777 | struct ipc64_perm *perm, int extra_perm) | 784 | struct ipc64_perm *perm, int extra_perm) |
778 | { | 785 | { |
779 | struct kern_ipc_perm *ipcp; | 786 | struct kern_ipc_perm *ipcp; |
780 | uid_t euid; | 787 | kuid_t euid; |
781 | int err; | 788 | int err; |
782 | 789 | ||
783 | down_write(&ids->rw_mutex); | 790 | down_write(&ids->rw_mutex); |
@@ -793,7 +800,7 @@ struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns, | |||
793 | perm->gid, perm->mode); | 800 | perm->gid, perm->mode); |
794 | 801 | ||
795 | euid = current_euid(); | 802 | euid = current_euid(); |
796 | if (euid == ipcp->cuid || euid == ipcp->uid || | 803 | if (uid_eq(euid, ipcp->cuid) || uid_eq(euid, ipcp->uid) || |
797 | ns_capable(ns->user_ns, CAP_SYS_ADMIN)) | 804 | ns_capable(ns->user_ns, CAP_SYS_ADMIN)) |
798 | return ipcp; | 805 | return ipcp; |
799 | 806 | ||
diff --git a/ipc/util.h b/ipc/util.h index 850ef3e962cb..c8fe2f7631e9 100644 --- a/ipc/util.h +++ b/ipc/util.h | |||
@@ -125,7 +125,7 @@ struct kern_ipc_perm *ipc_lock(struct ipc_ids *, int); | |||
125 | 125 | ||
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 | int ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out); |
129 | struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns, | 129 | struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns, |
130 | struct ipc_ids *ids, int id, int cmd, | 130 | struct ipc_ids *ids, int id, int cmd, |
131 | struct ipc64_perm *perm, int extra_perm); | 131 | struct ipc64_perm *perm, int extra_perm); |