userns: Convert ipc to use kuid and kgid where appropriate
authorEric W. Biederman <ebiederm@xmission.com>
Wed, 8 Feb 2012 00:54:11 +0000 (16:54 -0800)
committerEric W. Biederman <ebiederm@xmission.com>
Fri, 7 Sep 2012 05:17:20 +0000 (22:17 -0700)
- 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>
include/linux/ipc.h
init/Kconfig
ipc/msg.c
ipc/sem.c
ipc/shm.c
ipc/util.c
ipc/util.h

index 30e816148df42c0dd4c7fc0f71755a2a7a448215..ca833fdc3138595ed076af4ba41003e37935e5a8 100644 (file)
@@ -79,6 +79,7 @@ struct ipc_kludge {
 
 #ifdef __KERNEL__
 #include <linux/spinlock.h>
+#include <linux/uidgid.h>
 
 #define IPCMNI 32768  /* <= MAX_INT limit for ipc arrays (including sysctl changes) */
 
@@ -89,10 +90,10 @@ struct kern_ipc_perm
        int             deleted;
        int             id;
        key_t           key;
-       uid_t           uid;
-       gid_t           gid;
-       uid_t           cuid;
-       gid_t           cgid;
+       kuid_t          uid;
+       kgid_t          gid;
+       kuid_t          cuid;
+       kgid_t          cgid;
        umode_t         mode; 
        unsigned long   seq;
        void            *security;
index 7d4422c92ccad347a87390b84dd4d95879bb94ca..d09738dee238c24031c01430dead862f11004d43 100644 (file)
@@ -925,7 +925,6 @@ config UIDGID_CONVERTED
 
        # List of kernel pieces that need user namespace work
        # Features
-       depends on SYSVIPC = n
        depends on IMA = n
        depends on EVM = n
        depends on KEYS = n
index 7385de25788a80c0d9cb51e6a9921997ae135e74..a71af5a65abf2b6a3029c4792d13594317dba8d2 100644 (file)
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -443,9 +443,12 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd,
                        goto out_unlock;
                }
 
+               err = ipc_update_perm(&msqid64.msg_perm, ipcp);
+               if (err)
+                       goto out_unlock;
+
                msq->q_qbytes = msqid64.msg_qbytes;
 
-               ipc_update_perm(&msqid64.msg_perm, ipcp);
                msq->q_ctime = get_seconds();
                /* sleeping receivers might be excluded by
                 * stricter permissions.
@@ -922,6 +925,7 @@ out:
 #ifdef CONFIG_PROC_FS
 static int sysvipc_msg_proc_show(struct seq_file *s, void *it)
 {
+       struct user_namespace *user_ns = seq_user_ns(s);
        struct msg_queue *msq = it;
 
        return seq_printf(s,
@@ -933,10 +937,10 @@ static int sysvipc_msg_proc_show(struct seq_file *s, void *it)
                        msq->q_qnum,
                        msq->q_lspid,
                        msq->q_lrpid,
-                       msq->q_perm.uid,
-                       msq->q_perm.gid,
-                       msq->q_perm.cuid,
-                       msq->q_perm.cgid,
+                       from_kuid_munged(user_ns, msq->q_perm.uid),
+                       from_kgid_munged(user_ns, msq->q_perm.gid),
+                       from_kuid_munged(user_ns, msq->q_perm.cuid),
+                       from_kgid_munged(user_ns, msq->q_perm.cgid),
                        msq->q_stime,
                        msq->q_rtime,
                        msq->q_ctime);
index 5215a81420df9b1802dd9f6c40c465f1b63a2bbd..58d31f1c1eb59920a558705b677c8db3ff80b6d9 100644 (file)
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -1104,7 +1104,9 @@ static int semctl_down(struct ipc_namespace *ns, int semid,
                freeary(ns, ipcp);
                goto out_up;
        case IPC_SET:
-               ipc_update_perm(&semid64.sem_perm, ipcp);
+               err = ipc_update_perm(&semid64.sem_perm, ipcp);
+               if (err)
+                       goto out_unlock;
                sma->sem_ctime = get_seconds();
                break;
        default:
@@ -1677,6 +1679,7 @@ void exit_sem(struct task_struct *tsk)
 #ifdef CONFIG_PROC_FS
 static int sysvipc_sem_proc_show(struct seq_file *s, void *it)
 {
+       struct user_namespace *user_ns = seq_user_ns(s);
        struct sem_array *sma = it;
 
        return seq_printf(s,
@@ -1685,10 +1688,10 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it)
                          sma->sem_perm.id,
                          sma->sem_perm.mode,
                          sma->sem_nsems,
-                         sma->sem_perm.uid,
-                         sma->sem_perm.gid,
-                         sma->sem_perm.cuid,
-                         sma->sem_perm.cgid,
+                         from_kuid_munged(user_ns, sma->sem_perm.uid),
+                         from_kgid_munged(user_ns, sma->sem_perm.gid),
+                         from_kuid_munged(user_ns, sma->sem_perm.cuid),
+                         from_kgid_munged(user_ns, sma->sem_perm.cgid),
                          sma->sem_otime,
                          sma->sem_ctime);
 }
index 00faa05cf72adf914bc40a41113182da964eec31..dff40c9f73c9df09544c057f7325004726d27715 100644 (file)
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -758,7 +758,9 @@ static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd,
                do_shm_rmid(ns, ipcp);
                goto out_up;
        case IPC_SET:
-               ipc_update_perm(&shmid64.shm_perm, ipcp);
+               err = ipc_update_perm(&shmid64.shm_perm, ipcp);
+               if (err)
+                       goto out_unlock;
                shp->shm_ctim = get_seconds();
                break;
        default:
@@ -893,10 +895,10 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)
                audit_ipc_obj(&(shp->shm_perm));
 
                if (!ns_capable(ns->user_ns, CAP_IPC_LOCK)) {
-                       uid_t euid = current_euid();
+                       kuid_t euid = current_euid();
                        err = -EPERM;
-                       if (euid != shp->shm_perm.uid &&
-                           euid != shp->shm_perm.cuid)
+                       if (!uid_eq(euid, shp->shm_perm.uid) &&
+                           !uid_eq(euid, shp->shm_perm.cuid))
                                goto out_unlock;
                        if (cmd == SHM_LOCK && !rlimit(RLIMIT_MEMLOCK))
                                goto out_unlock;
@@ -1220,6 +1222,7 @@ SYSCALL_DEFINE1(shmdt, char __user *, shmaddr)
 #ifdef CONFIG_PROC_FS
 static int sysvipc_shm_proc_show(struct seq_file *s, void *it)
 {
+       struct user_namespace *user_ns = seq_user_ns(s);
        struct shmid_kernel *shp = it;
        unsigned long rss = 0, swp = 0;
 
@@ -1242,10 +1245,10 @@ static int sysvipc_shm_proc_show(struct seq_file *s, void *it)
                          shp->shm_cprid,
                          shp->shm_lprid,
                          shp->shm_nattch,
-                         shp->shm_perm.uid,
-                         shp->shm_perm.gid,
-                         shp->shm_perm.cuid,
-                         shp->shm_perm.cgid,
+                         from_kuid_munged(user_ns, shp->shm_perm.uid),
+                         from_kgid_munged(user_ns, shp->shm_perm.gid),
+                         from_kuid_munged(user_ns, shp->shm_perm.cuid),
+                         from_kgid_munged(user_ns, shp->shm_perm.cgid),
                          shp->shm_atim,
                          shp->shm_dtim,
                          shp->shm_ctim,
index eb07fd356f2759c74a74206a441bf3928ba95f5b..72fd0785ac948b4c3813a2aac8f463ea41e7955f 100644 (file)
@@ -249,8 +249,8 @@ int ipc_get_maxid(struct ipc_ids *ids)
  
 int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size)
 {
-       uid_t euid;
-       gid_t egid;
+       kuid_t euid;
+       kgid_t egid;
        int id, err;
 
        if (size > IPCMNI)
@@ -606,14 +606,14 @@ void ipc_rcu_putref(void *ptr)
  
 int ipcperms(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp, short flag)
 {
-       uid_t euid = current_euid();
+       kuid_t euid = current_euid();
        int requested_mode, granted_mode;
 
        audit_ipc_obj(ipcp);
        requested_mode = (flag >> 6) | (flag >> 3) | flag;
        granted_mode = ipcp->mode;
-       if (euid == ipcp->cuid ||
-           euid == ipcp->uid)
+       if (uid_eq(euid, ipcp->cuid) ||
+           uid_eq(euid, ipcp->uid))
                granted_mode >>= 6;
        else if (in_group_p(ipcp->cgid) || in_group_p(ipcp->gid))
                granted_mode >>= 3;
@@ -643,10 +643,10 @@ int ipcperms(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp, short flag)
 void kernel_to_ipc64_perm (struct kern_ipc_perm *in, struct ipc64_perm *out)
 {
        out->key        = in->key;
-       out->uid        = in->uid;
-       out->gid        = in->gid;
-       out->cuid       = in->cuid;
-       out->cgid       = in->cgid;
+       out->uid        = from_kuid_munged(current_user_ns(), in->uid);
+       out->gid        = from_kgid_munged(current_user_ns(), in->gid);
+       out->cuid       = from_kuid_munged(current_user_ns(), in->cuid);
+       out->cgid       = from_kgid_munged(current_user_ns(), in->cgid);
        out->mode       = in->mode;
        out->seq        = in->seq;
 }
@@ -747,12 +747,19 @@ int ipcget(struct ipc_namespace *ns, struct ipc_ids *ids,
  * @in:  the permission given as input.
  * @out: the permission of the ipc to set.
  */
-void ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out)
+int ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out)
 {
-       out->uid = in->uid;
-       out->gid = in->gid;
+       kuid_t uid = make_kuid(current_user_ns(), in->uid);
+       kgid_t gid = make_kgid(current_user_ns(), in->gid);
+       if (!uid_valid(uid) || !gid_valid(gid))
+               return -EINVAL;
+
+       out->uid = uid;
+       out->gid = gid;
        out->mode = (out->mode & ~S_IRWXUGO)
                | (in->mode & S_IRWXUGO);
+
+       return 0;
 }
 
 /**
@@ -777,7 +784,7 @@ struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns,
                                      struct ipc64_perm *perm, int extra_perm)
 {
        struct kern_ipc_perm *ipcp;
-       uid_t euid;
+       kuid_t euid;
        int err;
 
        down_write(&ids->rw_mutex);
@@ -793,7 +800,7 @@ struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns,
                                         perm->gid, perm->mode);
 
        euid = current_euid();
-       if (euid == ipcp->cuid || euid == ipcp->uid  ||
+       if (uid_eq(euid, ipcp->cuid) || uid_eq(euid, ipcp->uid)  ||
            ns_capable(ns->user_ns, CAP_SYS_ADMIN))
                return ipcp;
 
index 850ef3e962cb36a13b2cdcd560f6bbccb8fcb489..c8fe2f7631e9b616177d69c8ac614e901db669d2 100644 (file)
@@ -125,7 +125,7 @@ struct kern_ipc_perm *ipc_lock(struct ipc_ids *, int);
 
 void kernel_to_ipc64_perm(struct kern_ipc_perm *in, struct ipc64_perm *out);
 void ipc64_perm_to_ipc_perm(struct ipc64_perm *in, struct ipc_perm *out);
-void ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out);
+int ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out);
 struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns,
                                      struct ipc_ids *ids, int id, int cmd,
                                      struct ipc64_perm *perm, int extra_perm);