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>
This commit is contained in:
parent
9582d90196
commit
1efdb69b0b
|
@ -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
|
||||||
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
|
||||||
|
|
||||||
# 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
|
||||||
|
|
14
ipc/msg.c
14
ipc/msg.c
|
@ -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 @@ out:
|
||||||
#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)
|
||||||
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);
|
||||||
|
|
13
ipc/sem.c
13
ipc/sem.c
|
@ -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)
|
||||||
#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)
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
19
ipc/shm.c
19
ipc/shm.c
|
@ -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)
|
||||||
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)
|
||||||
#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)
|
||||||
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,
|
||||||
|
|
35
ipc/util.c
35
ipc/util.c
|
@ -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)
|
||||||
|
|
||||||
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)
|
||||||
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,
|
||||||
* @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,
|
||||||
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,
|
||||||
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);
|
||||||
|
|
||||||
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);
|
||||||
|
|
Reference in New Issue