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); |
