Commit 1efdb69b authored by Eric W. Biederman's avatar Eric W. Biederman

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: default avatarEric W. Biederman <ebiederm@xmission.com>
parent 9582d901
...@@ -79,6 +79,7 @@ struct ipc_kludge { ...@@ -79,6 +79,7 @@ struct ipc_kludge {
#ifdef __KERNEL__ #ifdef __KERNEL__
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/uidgid.h>
#define IPCMNI 32768 /* <= MAX_INT limit for ipc arrays (including sysctl changes) */ #define IPCMNI 32768 /* <= MAX_INT limit for ipc arrays (including sysctl changes) */
...@@ -89,10 +90,10 @@ struct kern_ipc_perm ...@@ -89,10 +90,10 @@ struct kern_ipc_perm
int deleted; int deleted;
int id; int id;
key_t key; key_t key;
uid_t uid; kuid_t uid;
gid_t gid; kgid_t gid;
uid_t cuid; kuid_t cuid;
gid_t cgid; kgid_t cgid;
umode_t mode; umode_t mode;
unsigned long seq; unsigned long seq;
void *security; void *security;
......
...@@ -925,7 +925,6 @@ config UIDGID_CONVERTED ...@@ -925,7 +925,6 @@ config UIDGID_CONVERTED
# List of kernel pieces that need user namespace work # List of kernel pieces that need user namespace work
# Features # Features
depends on SYSVIPC = n
depends on IMA = n depends on IMA = n
depends on EVM = n depends on EVM = n
depends on KEYS = n depends on KEYS = n
......
...@@ -443,9 +443,12 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd, ...@@ -443,9 +443,12 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd,
goto out_unlock; goto out_unlock;
} }
err = ipc_update_perm(&msqid64.msg_perm, ipcp);
if (err)
goto out_unlock;
msq->q_qbytes = msqid64.msg_qbytes; msq->q_qbytes = msqid64.msg_qbytes;
ipc_update_perm(&msqid64.msg_perm, ipcp);
msq->q_ctime = get_seconds(); msq->q_ctime = get_seconds();
/* sleeping receivers might be excluded by /* sleeping receivers might be excluded by
* stricter permissions. * stricter permissions.
...@@ -922,6 +925,7 @@ SYSCALL_DEFINE5(msgrcv, int, msqid, struct msgbuf __user *, msgp, size_t, msgsz, ...@@ -922,6 +925,7 @@ SYSCALL_DEFINE5(msgrcv, int, msqid, struct msgbuf __user *, msgp, size_t, msgsz,
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
static int sysvipc_msg_proc_show(struct seq_file *s, void *it) 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; struct msg_queue *msq = it;
return seq_printf(s, return seq_printf(s,
...@@ -933,10 +937,10 @@ static int sysvipc_msg_proc_show(struct seq_file *s, void *it) ...@@ -933,10 +937,10 @@ static int sysvipc_msg_proc_show(struct seq_file *s, void *it)
msq->q_qnum, msq->q_qnum,
msq->q_lspid, msq->q_lspid,
msq->q_lrpid, msq->q_lrpid,
msq->q_perm.uid, from_kuid_munged(user_ns, msq->q_perm.uid),
msq->q_perm.gid, from_kgid_munged(user_ns, msq->q_perm.gid),
msq->q_perm.cuid, from_kuid_munged(user_ns, msq->q_perm.cuid),
msq->q_perm.cgid, from_kgid_munged(user_ns, msq->q_perm.cgid),
msq->q_stime, msq->q_stime,
msq->q_rtime, msq->q_rtime,
msq->q_ctime); msq->q_ctime);
......
...@@ -1104,7 +1104,9 @@ static int semctl_down(struct ipc_namespace *ns, int semid, ...@@ -1104,7 +1104,9 @@ static int semctl_down(struct ipc_namespace *ns, int semid,
freeary(ns, ipcp); freeary(ns, ipcp);
goto out_up; goto out_up;
case IPC_SET: 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(); sma->sem_ctime = get_seconds();
break; break;
default: default:
...@@ -1677,6 +1679,7 @@ void exit_sem(struct task_struct *tsk) ...@@ -1677,6 +1679,7 @@ void exit_sem(struct task_struct *tsk)
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
static int sysvipc_sem_proc_show(struct seq_file *s, void *it) 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; struct sem_array *sma = it;
return seq_printf(s, return seq_printf(s,
...@@ -1685,10 +1688,10 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it) ...@@ -1685,10 +1688,10 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it)
sma->sem_perm.id, sma->sem_perm.id,
sma->sem_perm.mode, sma->sem_perm.mode,
sma->sem_nsems, sma->sem_nsems,
sma->sem_perm.uid, from_kuid_munged(user_ns, sma->sem_perm.uid),
sma->sem_perm.gid, from_kgid_munged(user_ns, sma->sem_perm.gid),
sma->sem_perm.cuid, from_kuid_munged(user_ns, sma->sem_perm.cuid),
sma->sem_perm.cgid, from_kgid_munged(user_ns, sma->sem_perm.cgid),
sma->sem_otime, sma->sem_otime,
sma->sem_ctime); sma->sem_ctime);
} }
......
...@@ -758,7 +758,9 @@ static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd, ...@@ -758,7 +758,9 @@ static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd,
do_shm_rmid(ns, ipcp); do_shm_rmid(ns, ipcp);
goto out_up; goto out_up;
case IPC_SET: 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(); shp->shm_ctim = get_seconds();
break; break;
default: default:
...@@ -893,10 +895,10 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf) ...@@ -893,10 +895,10 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)
audit_ipc_obj(&(shp->shm_perm)); audit_ipc_obj(&(shp->shm_perm));
if (!ns_capable(ns->user_ns, CAP_IPC_LOCK)) { if (!ns_capable(ns->user_ns, CAP_IPC_LOCK)) {
uid_t euid = current_euid(); kuid_t euid = current_euid();
err = -EPERM; err = -EPERM;
if (euid != shp->shm_perm.uid && if (!uid_eq(euid, shp->shm_perm.uid) &&
euid != shp->shm_perm.cuid) !uid_eq(euid, shp->shm_perm.cuid))
goto out_unlock; goto out_unlock;
if (cmd == SHM_LOCK && !rlimit(RLIMIT_MEMLOCK)) if (cmd == SHM_LOCK && !rlimit(RLIMIT_MEMLOCK))
goto out_unlock; goto out_unlock;
...@@ -1220,6 +1222,7 @@ SYSCALL_DEFINE1(shmdt, char __user *, shmaddr) ...@@ -1220,6 +1222,7 @@ SYSCALL_DEFINE1(shmdt, char __user *, shmaddr)
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
static int sysvipc_shm_proc_show(struct seq_file *s, void *it) 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; struct shmid_kernel *shp = it;
unsigned long rss = 0, swp = 0; unsigned long rss = 0, swp = 0;
...@@ -1242,10 +1245,10 @@ static int sysvipc_shm_proc_show(struct seq_file *s, void *it) ...@@ -1242,10 +1245,10 @@ static int sysvipc_shm_proc_show(struct seq_file *s, void *it)
shp->shm_cprid, shp->shm_cprid,
shp->shm_lprid, shp->shm_lprid,
shp->shm_nattch, shp->shm_nattch,
shp->shm_perm.uid, from_kuid_munged(user_ns, shp->shm_perm.uid),
shp->shm_perm.gid, from_kgid_munged(user_ns, shp->shm_perm.gid),
shp->shm_perm.cuid, from_kuid_munged(user_ns, shp->shm_perm.cuid),
shp->shm_perm.cgid, from_kgid_munged(user_ns, shp->shm_perm.cgid),
shp->shm_atim, shp->shm_atim,
shp->shm_dtim, shp->shm_dtim,
shp->shm_ctim, shp->shm_ctim,
......
...@@ -249,8 +249,8 @@ int ipc_get_maxid(struct ipc_ids *ids) ...@@ -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) int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size)
{ {
uid_t euid; kuid_t euid;
gid_t egid; kgid_t egid;
int id, err; int id, err;
if (size > IPCMNI) if (size > IPCMNI)
...@@ -606,14 +606,14 @@ void ipc_rcu_putref(void *ptr) ...@@ -606,14 +606,14 @@ void ipc_rcu_putref(void *ptr)
int ipcperms(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp, short flag) 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; int requested_mode, granted_mode;
audit_ipc_obj(ipcp); audit_ipc_obj(ipcp);
requested_mode = (flag >> 6) | (flag >> 3) | flag; requested_mode = (flag >> 6) | (flag >> 3) | flag;
granted_mode = ipcp->mode; granted_mode = ipcp->mode;
if (euid == ipcp->cuid || if (uid_eq(euid, ipcp->cuid) ||
euid == ipcp->uid) uid_eq(euid, ipcp->uid))
granted_mode >>= 6; granted_mode >>= 6;
else if (in_group_p(ipcp->cgid) || in_group_p(ipcp->gid)) else if (in_group_p(ipcp->cgid) || in_group_p(ipcp->gid))
granted_mode >>= 3; granted_mode >>= 3;
...@@ -643,10 +643,10 @@ int ipcperms(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp, short flag) ...@@ -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) void kernel_to_ipc64_perm (struct kern_ipc_perm *in, struct ipc64_perm *out)
{ {
out->key = in->key; out->key = in->key;
out->uid = in->uid; out->uid = from_kuid_munged(current_user_ns(), in->uid);
out->gid = in->gid; out->gid = from_kgid_munged(current_user_ns(), in->gid);
out->cuid = in->cuid; out->cuid = from_kuid_munged(current_user_ns(), in->cuid);
out->cgid = in->cgid; out->cgid = from_kgid_munged(current_user_ns(), in->cgid);
out->mode = in->mode; out->mode = in->mode;
out->seq = in->seq; out->seq = in->seq;
} }
...@@ -747,12 +747,19 @@ int ipcget(struct ipc_namespace *ns, struct ipc_ids *ids, ...@@ -747,12 +747,19 @@ int ipcget(struct ipc_namespace *ns, struct ipc_ids *ids,
* @in: the permission given as input. * @in: the permission given as input.
* @out: the permission of the ipc to set. * @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; kuid_t uid = make_kuid(current_user_ns(), in->uid);
out->gid = in->gid; 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) out->mode = (out->mode & ~S_IRWXUGO)
| (in->mode & S_IRWXUGO); | (in->mode & S_IRWXUGO);
return 0;
} }
/** /**
...@@ -777,7 +784,7 @@ struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns, ...@@ -777,7 +784,7 @@ struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns,
struct ipc64_perm *perm, int extra_perm) struct ipc64_perm *perm, int extra_perm)
{ {
struct kern_ipc_perm *ipcp; struct kern_ipc_perm *ipcp;
uid_t euid; kuid_t euid;
int err; int err;
down_write(&ids->rw_mutex); down_write(&ids->rw_mutex);
...@@ -793,7 +800,7 @@ struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns, ...@@ -793,7 +800,7 @@ struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns,
perm->gid, perm->mode); perm->gid, perm->mode);
euid = current_euid(); 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)) ns_capable(ns->user_ns, CAP_SYS_ADMIN))
return ipcp; return ipcp;
......
...@@ -125,7 +125,7 @@ struct kern_ipc_perm *ipc_lock(struct ipc_ids *, int); ...@@ -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 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 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 kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns,
struct ipc_ids *ids, int id, int cmd, struct ipc_ids *ids, int id, int cmd,
struct ipc64_perm *perm, int extra_perm); struct ipc64_perm *perm, int extra_perm);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment