* Re: [RFC] shared credentials with vfs snapshotting
2002-10-24 20:12 [RFC] shared credentials with vfs snapshotting David C. Hansen
@ 2002-10-24 20:24 ` David C. Hansen
0 siblings, 0 replies; 2+ messages in thread
From: David C. Hansen @ 2002-10-24 20:24 UTC (permalink / raw)
Cc: lkml, Trond Myklebust, Dave McCracken
[-- Attachment #1: Type: text/plain, Size: 55 bytes --]
Oh yeah, the code!
--
Dave Hansen
haveblue@us.ibm.com
[-- Attachment #2: cred-sharing-snapshotting-2.5.44-0.patch --]
[-- Type: text/x-patch, Size: 25742 bytes --]
diff -X exclude -urN linux-2.5.44/arch/i386/kernel/init_task.c linux-2.5.44-cred/arch/i386/kernel/init_task.c
--- linux-2.5.44/arch/i386/kernel/init_task.c Fri Oct 18 21:02:34 2002
+++ linux-2.5.44-cred/arch/i386/kernel/init_task.c Thu Oct 24 11:33:14 2002
@@ -11,6 +11,7 @@
static struct fs_struct init_fs = INIT_FS;
static struct files_struct init_files = INIT_FILES;
static struct signal_struct init_signals = INIT_SIGNALS(init_signals);
+static struct cred_struct init_cred = INIT_CRED;
struct mm_struct init_mm = INIT_MM(init_mm);
/*
diff -X exclude -urN linux-2.5.44/arch/s390x/kernel/linux32.c linux-2.5.44-cred/arch/s390x/kernel/linux32.c
--- linux-2.5.44/arch/s390x/kernel/linux32.c Fri Oct 18 21:01:16 2002
+++ linux-2.5.44-cred/arch/s390x/kernel/linux32.c Thu Oct 24 12:14:14 2002
@@ -194,12 +194,10 @@
if (gidsetsize < 0)
return -EINVAL;
- i = current->ngroups;
+ i = getgroups16(NGROUPS, groups);
if (gidsetsize) {
if (i > gidsetsize)
return -EINVAL;
- for(j=0;j<i;j++)
- groups[j] = current->groups[j];
if (copy_to_user(grouplist, groups, sizeof(u16)*i))
return -EFAULT;
}
@@ -217,10 +215,7 @@
return -EINVAL;
if (copy_from_user(groups, grouplist, gidsetsize * sizeof(u16)))
return -EFAULT;
- for (i = 0 ; i < gidsetsize ; i++)
- current->groups[i] = (gid_t)groups[i];
- current->ngroups = gidsetsize;
- return 0;
+ return setgroups16(gidgetsize, grouplist);
}
asmlinkage long sys32_getuid16(void)
Binary files linux-2.5.44/arch/sparc64/kernel/.sys_sparc32.c.swp and linux-2.5.44-cred/arch/sparc64/kernel/.sys_sparc32.c.swp differ
diff -X exclude -urN linux-2.5.44/arch/sparc64/kernel/sys_sparc32.c linux-2.5.44-cred/arch/sparc64/kernel/sys_sparc32.c
--- linux-2.5.44/arch/sparc64/kernel/sys_sparc32.c Fri Oct 18 21:02:00 2002
+++ linux-2.5.44-cred/arch/sparc64/kernel/sys_sparc32.c Thu Oct 24 12:14:48 2002
@@ -211,12 +211,10 @@
if (gidsetsize < 0)
return -EINVAL;
- i = current->ngroups;
+ i = getgroups16(NGROUPS, groups);
if (gidsetsize) {
if (i > gidsetsize)
return -EINVAL;
- for(j=0;j<i;j++)
- groups[j] = current->groups[j];
if (copy_to_user(grouplist, groups, sizeof(u16)*i))
return -EFAULT;
}
@@ -234,10 +232,7 @@
return -EINVAL;
if (copy_from_user(groups, grouplist, gidsetsize * sizeof(u16)))
return -EFAULT;
- for (i = 0 ; i < gidsetsize ; i++)
- current->groups[i] = (gid_t)groups[i];
- current->ngroups = gidsetsize;
- return 0;
+ return setgroups16(gidsetsize, groups);
}
asmlinkage long sys32_getuid16(void)
diff -X exclude -urN linux-2.5.44/fs/exec.c linux-2.5.44-cred/fs/exec.c
--- linux-2.5.44/fs/exec.c Fri Oct 18 21:01:48 2002
+++ linux-2.5.44-cred/fs/exec.c Thu Oct 24 11:47:20 2002
@@ -864,6 +864,11 @@
current->suid = current->euid = current->fsuid = bprm->e_uid;
current->sgid = current->egid = current->fsgid = bprm->e_gid;
+ current->cred->suid = current->cred->euid = bprm->e_uid;
+ setfsuid(bprm->e_uid);
+ current->cred->sgid = current->cred->egid = bprm->e_gid;
+ setfsgid(bprm->e_gid);
+
if(do_unlock)
unlock_kernel();
diff -X exclude -urN linux-2.5.44/include/linux/cred.h linux-2.5.44-cred/include/linux/cred.h
--- linux-2.5.44/include/linux/cred.h Wed Dec 31 16:00:00 1969
+++ linux-2.5.44-cred/include/linux/cred.h Thu Oct 24 11:16:25 2002
@@ -0,0 +1,101 @@
+#ifndef _LINUX_CRED_H
+#define _LINUX_CRED_H
+
+#ifdef __KERNEL__
+
+#include <linux/param.h>
+#include <linux/types.h>
+#include <asm/atomic.h>
+
+
+struct cred_struct {
+ atomic_t count;
+ uid_t uid,euid,suid;
+ gid_t gid,egid,sgid;
+ struct vfs_cred* vfscred;
+ kernel_cap_t cap_effective, cap_inheritable, cap_permitted;
+ int keep_capabilities:1;
+ structuser_struct *user;
+};
+
+
+
+/*
+ * VFS credentials
+ *
+ * This is for use by filesystems, sockets, RPC for user authentication
+ * purposes. It is a direct replacement for the 2.4.x task entries
+ * fsuid/fsgid + groups[].
+ *
+ * The VFS credential may be shared among different threads, interrupts,...
+ * without any fancy locking mechanisms provided one sticks to using
+ * copy on write semantics. The latter is a guarantee that if more than
+ * one object is referencing the vfscred, then the data in the struct will
+ * not change.
+ *
+ * This again means that the recipe for changing one or more of fsuid,
+ * fsgid, ... for the current thread will usually be as follows:
+ * 1) copy the existing vfscred into a new one using clone_current_vfscred()
+ * 2) make all the necessary changes to the clone
+ * 3) use set_current_vfscred() in order to register the change to the
+ * task_struct
+ *
+ */
+struct vfs_cred {
+ atomic_t count;
+
+ uid_t uid;
+ gid_t gid;
+
+ int ngroups;
+ gid_t *groups;
+ /* Default storage for groups */
+ gid_t __group_storage[NGROUPS];
+};
+
+#define NOGID ((gid_t)-1)
+#define NOUID ((uid_t)-1)
+
+extern struct vfs_cred init_vfscred;
+extern void credentials_init(void);
+
+extern void put_vfscred(struct vfs_cred *);
+extern struct vfs_cred *vfscred_create(uid_t, gid_t);
+extern struct vfs_cred *vfscred_clone(struct vfs_cred *);
+extern int vfscred_getgroups(struct vfs_cred *, int, gid_t *);
+extern int vfscred_setgroups(struct vfs_cred *, int, gid_t *);
+extern int vfscred_match_group(struct vfs_cred *, gid_t);
+
+static inline struct vfs_cred *get_vfscred(struct vfs_cred *cred)
+{
+ atomic_inc(&cred->count);
+ return cred;
+}
+
+static inline int vfscred_count(struct vfs_cred *cred)
+{
+ return atomic_read(&cred->count);
+}
+
+#define get_current_vfscred() get_vfscred(current->vfscred)
+
+extern struct vfs_cred *clone_current_vfscred(void);
+extern void set_current_vfscred(struct vfs_cred *);
+extern int setfsuid(uid_t);
+extern int setfsgid(gid_t);
+extern int setgroups(int, gid_t *);
+extern int getgroups(int, gid_t *);
+
+extern struct vfs_cred *get_task_vfscred(struct task_struct *);
+extern void set_task_vfscred(struct task_struct *, struct vfs_cred *);
+
+/* Grrr: Support for oddball functions in security/dummy.c */
+extern int task_setfsuid(struct task_struct *, uid_t);
+
+#if defined(CONFIG_SPARC64) || defined(CONFIG_ARCH_S390X)
+extern int setgroups16(int, gid16_t *);
+extern int getgroups16(int, gid16_t *);
+#endif
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_CRED_H */
diff -X exclude -urN linux-2.5.44/include/linux/init_task.h linux-2.5.44-cred/include/linux/init_task.h
--- linux-2.5.44/include/linux/init_task.h Fri Oct 18 21:01:11 2002
+++ linux-2.5.44-cred/include/linux/init_task.h Thu Oct 24 11:26:13 2002
@@ -50,6 +50,24 @@
.shared_pending = { NULL, &sig.shared_pending.head, {{0}}}, \
}
+
+#define INIT_CRED \
+{ \
+ .count = ATOMIC_INIT(1), \
+ .uid = 0, \
+ .euid = 0, \
+ .suid = 0, \
+ .gid = 0, \
+ .egid = 0, \
+ .sgid = 0, \
+ .vfscred = &init_vfcred \
+ .cap_effective = CAP_INIT_EFF_SET, \
+ .cap_inheritable = CAP_INIT_INH_SET, \
+ .cap_permitted = CAP_FULL_SET, \
+ .keep_capabilities = 0, \
+ .user = INIT_USER \
+}
+
/*
* INIT_TASK is used to set up the first task table, touch at
* your own risk!. Base=0, limit=0x1fffff (=2MB)
@@ -80,17 +98,13 @@
.real_timer = { \
.function = it_real_fn \
}, \
- .cap_effective = CAP_INIT_EFF_SET, \
- .cap_inheritable = CAP_INIT_INH_SET, \
- .cap_permitted = CAP_FULL_SET, \
- .keep_capabilities = 0, \
.rlim = INIT_RLIMITS, \
- .user = INIT_USER, \
.comm = "swapper", \
.thread = INIT_THREAD, \
.fs = &init_fs, \
.files = &init_files, \
.sig = &init_signals, \
+ .cred = &init_cred \
.pending = { NULL, &tsk.pending.head, {{0}}}, \
.blocked = {{0}}, \
.alloc_lock = SPIN_LOCK_UNLOCKED, \
diff -X exclude -urN linux-2.5.44/include/linux/sched.h linux-2.5.44-cred/include/linux/sched.h
--- linux-2.5.44/include/linux/sched.h Fri Oct 18 21:01:11 2002
+++ linux-2.5.44-cred/include/linux/sched.h Thu Oct 24 12:11:33 2002
@@ -29,6 +29,7 @@
#include <linux/compiler.h>
#include <linux/completion.h>
#include <linux/pid.h>
+#include <linux/cred.h>
struct exec_domain;
@@ -51,7 +52,7 @@
#define CLONE_SETTID 0x00100000 /* write the TID back to userspace */
#define CLONE_CLEARTID 0x00200000 /* clear the userspace TID */
#define CLONE_DETACHED 0x00400000 /* parent wants no child-exit signal */
-
+#define CLONE_CRED 0x00800000 /* Share credentials between tasks */
/*
* List of flags we want to share for kernel threads,
* if only because they are not used by them anyway.
@@ -340,13 +341,7 @@
unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap;
int swappable:1;
/* process credentials */
- uid_t uid,euid,suid,fsuid;
- gid_t gid,egid,sgid,fsgid;
- int ngroups;
- gid_t groups[NGROUPS];
- kernel_cap_t cap_effective, cap_inheritable, cap_permitted;
- int keep_capabilities:1;
- struct user_struct *user;
+ struct cred_struct* cred;
/* limits */
struct rlimit rlim[RLIM_NLIMITS];
unsigned short used_math;
diff -X exclude -urN linux-2.5.44/include/linux/slab.h linux-2.5.44-cred/include/linux/slab.h
--- linux-2.5.44/include/linux/slab.h Fri Oct 18 21:02:34 2002
+++ linux-2.5.44-cred/include/linux/slab.h Thu Oct 24 11:30:02 2002
@@ -73,6 +73,8 @@
extern kmem_cache_t *fs_cachep;
extern kmem_cache_t *sigact_cachep;
extern kmem_cache_t *bio_cachep;
+extern kmem_cache_t *cred_cachep;
+extern kmem_cache_t *vfscred_cachep;
#endif /* __KERNEL__ */
diff -X exclude -urN linux-2.5.44/init/main.c linux-2.5.44-cred/init/main.c
--- linux-2.5.44/init/main.c Fri Oct 18 21:01:16 2002
+++ linux-2.5.44-cred/init/main.c Thu Oct 24 11:49:25 2002
@@ -424,6 +424,7 @@
pidhash_init();
pgtable_cache_init();
pte_chain_init();
+ credentials_init();
fork_init(num_physpages);
proc_caches_init();
security_scaffolding_startup();
diff -X exclude -urN linux-2.5.44/kernel/cred.c linux-2.5.44-cred/kernel/cred.c
--- linux-2.5.44/kernel/cred.c Wed Dec 31 16:00:00 1969
+++ linux-2.5.44-cred/kernel/cred.c Thu Oct 24 11:19:49 2002
@@ -0,0 +1,468 @@
+/*
+ * linux/kernel/cred.c
+ *
+ * Copyright (c) 2001 Trond Myklebust <trond.myklebust@fys.uio.no>
+ *
+ * 'cred.c' contains the helper routines for managing task_cred
+ * and vfs_cred structures.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/cred.h>
+
+/*
+ * Creds for the init task
+ */
+struct vfs_cred init_vfscred = {
+ .count = ATOMIC_INIT(2),
+ /* .uid = (uid_t)0, */
+ /* .gid = (gid_t)0, */
+ /* .ngroups = 0, */
+ .groups = init_vfscred.__group_storage,
+};
+
+rwlock_t task_credlock = RW_LOCK_UNLOCKED;
+
+static kmem_cache_t *vfscred_cache;
+
+void __init credentials_init(void)
+{
+ vfscred_cache = kmem_cache_create("vfs_cred",
+ sizeof(struct vfs_cred),
+ 0,
+ SLAB_HWCACHE_ALIGN,
+ NULL, NULL);
+ if (!vfscred_cache)
+ panic("Cannot create vfs credential SLAB cache");
+}
+
+static inline struct vfs_cred *vfscred_alloc(int gfp)
+{
+ struct vfs_cred *cred;
+ cred = (struct vfs_cred *)kmem_cache_alloc(vfscred_cache, gfp);
+ if (cred) {
+ atomic_set(&cred->count, 1);
+ cred->groups = cred->__group_storage;
+ }
+ return cred;
+}
+
+static inline void vfscred_freegroups(struct vfs_cred *cred)
+{
+ if (cred->groups != cred->__group_storage)
+ kfree(cred->groups);
+}
+
+static int vfscred_growgroups(struct vfs_cred *cred, int ngrp)
+{
+ gid_t *buf;
+ int err;
+
+ buf = cred->__group_storage;
+ if (ngrp <= ARRAY_SIZE(cred->__group_storage))
+ goto out;
+ err = -ENOMEM;
+ buf = (gid_t *)kmalloc(ngrp * sizeof(gid_t), GFP_KERNEL);
+ if (!buf)
+ goto out_err;
+out:
+ vfscred_freegroups(cred);
+ cred->groups = buf;
+ return 0;
+out_err:
+ return err;
+}
+
+/**
+ * put_vfscred - Release a user credential
+ * @cred: pointer to vfs_cred
+ */
+void put_vfscred(struct vfs_cred *cred)
+{
+ if (atomic_dec_and_test(&cred->count)) {
+ vfscred_freegroups(cred);
+ kmem_cache_free(vfscred_cache, cred);
+ }
+}
+
+/**
+ * vfscred_create - allocate and initialize a new user credential
+ * @uid:
+ * @gid:
+ */
+struct vfs_cred *vfscred_create(uid_t uid, gid_t gid)
+{
+ struct vfs_cred *cred;
+
+ if (!(cred = vfscred_alloc(SLAB_KERNEL)))
+ return NULL;
+ cred->uid = uid;
+ cred->gid = gid;
+ cred->ngroups = 0;
+ return cred;
+}
+
+/**
+ * vfscred_setgroups - set the supplemental group membership in a vfs_cred
+ * @cred: pointer to vfs_cred
+ * @ngrp: number of gids to copy
+ * @src: pointer to gids
+ *
+ * Note: this function does not COW. If you want to set the groups on
+ * a public vfscred, please ensure that you copy it first...
+ */
+int vfscred_setgroups(struct vfs_cred *cred, int ngrp, gid_t *src)
+{
+ int err;
+
+ err = -EINVAL;
+ if (ngrp < 0)
+ goto out_err;
+ err = vfscred_growgroups(cred, ngrp);
+ if (err)
+ goto out_err;
+ memcpy(cred->groups, src, ngrp * sizeof(gid_t));
+ cred->ngroups = ngrp;
+ return 0;
+out_err:
+ return err;
+}
+
+/**
+ * vfscred_getgroups - return the supplemental groups from a vfs_cred
+ * @cred: pointer to vfs_cred
+ * @ngrp: number of gids to copy
+ * @dst: pointer to dest buffer
+ *
+ */
+int vfscred_getgroups(struct vfs_cred *cred, int ngrp, gid_t *dst)
+{
+ if (ngrp > cred->ngroups)
+ ngrp = cred->ngroups;
+ memcpy(dst, cred->groups, ngrp * sizeof(gid_t));
+ return cred->ngroups;
+}
+
+/**
+ * vfscred_match_group - match a gid to a vfs_cred's supplemental groups
+ * @cred: pointer to vfs_cred
+ * @gid: gid to match
+ */
+int vfscred_match_group(struct vfs_cred *cred, gid_t gid)
+{
+ gid_t *p = cred->groups;
+ int i;
+
+ for (i = cred->ngroups; i != 0 ; i--) {
+ if (gid == *p++)
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * vfscred_clone - duplicate a user credential
+ * @cred: pointer to vfs_cred
+ *
+ * Allocate a new user credential, and copy the entries in cred.
+ */
+struct vfs_cred *vfscred_clone(struct vfs_cred *cred)
+{
+ struct vfs_cred *new;
+ int err;
+
+ new = vfscred_create(cred->uid, cred->gid);
+ if (!new)
+ goto out_nomem;
+ err = vfscred_setgroups(new, cred->ngroups, cred->groups);
+ if (err)
+ goto out_free;
+ return new;
+out_free:
+ put_vfscred(new);
+out_nomem:
+ return NULL;
+}
+
+/**
+ * get_task_vfscred - return a reference to a task's user credentials
+ * @tsk: pointer to task
+ *
+ * Note: the rwlock is needed in order to protect against /proc
+ * grabbing a reference while the task itself is changing
+ * the vfscred.
+ * Once CLONE_CRED is introduced it may get worse...
+ */
+struct vfs_cred *get_task_vfscred(struct task_struct *tsk)
+{
+ struct vfs_cred *cred;
+
+ read_lock(&task_credlock);
+ cred = get_vfscred(tsk->cred->vfscred);
+ read_unlock(&task_credlock);
+ return cred;
+}
+
+/**
+ * set_task_vfscred - replace a task's user credentials
+ * @tsk: pointer to task
+ * @cred: pointer to vfs_cred
+ *
+ * Note: This function does not check capabilities etc.
+ */
+void set_task_vfscred(struct task_struct *tsk, struct vfs_cred *cred)
+{
+ struct vfs_cred *old;
+
+ if (tsk->cred->vfscred == cred)
+ return;
+ write_lock(&task_credlock);
+ old = xchg(&tsk->cred->vfscred, get_vfscred(cred));
+ write_unlock(&task_credlock);
+ if (old)
+ put_vfscred(old);
+}
+
+/**
+ * set_current_vfscred - replace the current task's user credentials
+ * @cred: pointer to vfs_cred
+ */
+void set_current_vfscred(struct vfs_cred *cred)
+{
+ set_task_vfscred(current, cred);
+}
+
+/**
+ * clone_current_vfscred - Clone the current task's vfs_cred
+ */
+struct vfs_cred *clone_current_vfscred(void)
+{
+ struct vfs_cred *cred, *new;
+
+ cred = get_current_vfscred();
+ new = vfscred_clone(cred);
+ put_vfscred(cred);
+ return new;
+}
+
+/*
+ * copy_write_task_vfscred - Conditionally clone a task's vfs_cred.
+ * @tsk: pointer to task
+ *
+ * Clones the task's vfscred if its reference count is > 1, else
+ * just take a reference.
+ * Use this in order to ensure that you are free to write to the
+ * vfscred.
+ *
+ */
+static struct vfs_cred *copy_write_task_vfscred(struct task_struct *tsk)
+{
+ struct vfs_cred *cred;
+
+ cred = get_task_vfscred(tsk);
+ if (vfscred_count(cred) > 2) {
+ struct vfs_cred *new;
+ new = vfscred_clone(cred);
+ put_vfscred(cred);
+ return new;
+ }
+ return cred;
+}
+
+/**
+ * setfsuid - set the current task's vfscred->uid
+ * @uid: new fsuid
+ */
+int setfsuid(uid_t uid)
+{
+ struct vfs_cred *cred;
+
+ if (uid == current->cred->vfscred->uid)
+ goto out;
+ cred = copy_write_task_vfscred(current);
+ if (!cred)
+ return -ENOMEM;
+ cred->uid = uid;
+ set_current_vfscred(cred);
+ put_vfscred(cred);
+out:
+ return 0;
+}
+
+/**
+ * setfsgid - set the current task's vfscred->gid
+ * @gid: new fsgid
+ */
+int setfsgid(gid_t gid)
+{
+ struct vfs_cred *cred;
+
+ if (gid == current->cred->vfscred->gid)
+ goto out;
+ cred = copy_write_task_vfscred(current);
+ if (!cred)
+ return -ENOMEM;
+ cred->gid = gid;
+ set_current_vfscred(cred);
+ put_vfscred(cred);
+out:
+ return 0;
+}
+
+/**
+ * setgroups - set the current task's group list
+ * @ngrp: number of gids to copy
+ * @src: pointer to gids
+ */
+int setgroups(int ngrp, gid_t *src)
+{
+ struct vfs_cred *cred;
+ int ret = -ENOMEM;
+
+ cred = copy_write_task_vfscred(current);
+ if (!cred)
+ goto out;
+ ret = vfscred_setgroups(cred, ngrp, src);
+ if (!ret)
+ set_current_vfscred(cred);
+ put_vfscred(cred);
+out:
+ return ret;
+}
+
+/**
+ * getgroups - return the current task's group list
+ * @ngrp: number of gids to copy
+ * @dst: pointer to dest buffer
+ */
+int getgroups(int ngrp, gid_t *dst)
+{
+ struct vfs_cred *cred;
+ int ret;
+
+ cred = get_current_vfscred();
+ ret = vfscred_getgroups(cred, ngrp, dst);
+ put_vfscred(cred);
+ return ret;
+}
+
+/**
+ * task_setfsuid - set the current task's vfscred uid
+ * @uid: new fsuid
+ *
+ * Doing this for tasks other than 'current' is usually unsafe,
+ * so use of this function is deprecated.
+ */
+int task_setfsuid(struct task_struct *tsk, uid_t uid)
+{
+ struct vfs_cred *cred;
+
+ cred = copy_write_task_vfscred(tsk);
+ if (!cred)
+ return -ENOMEM;
+ cred->uid = uid;
+ set_task_vfscred(tsk, cred);
+ put_vfscred(cred);
+ return 0;
+}
+
+#if defined(CONFIG_SPARC64) || defined(CONFIG_ARCH_S390X)
+/*
+ * vfscred_setgroups16 - set the supplemental group membership in a vfs_cred
+ * @cred: pointer to vfs_cred
+ * @ngrp: number of 16-bit gids to copy
+ * @src: pointer to gids
+ *
+ * Note: this function does not COW. If you want to set the groups on
+ * a public vfscred, please ensure that you copy it first...
+ */
+static int vfscred_setgroups16(struct vfs_cred *cred, int ngrp, gid16_t *src)
+{
+ gid_t *dst;
+ int i, err;
+
+ err = -EINVAL;
+ if (ngrp < 0)
+ goto out_err;
+ err = vfscred_growgroups(cred, ngrp);
+ if (err)
+ goto out_err;
+ dst = cred->groups;
+ for (i = ngrp; i != 0; i--)
+ *dst++ = (gid_t)*src++;
+ cred->ngroups = ngrp;
+ return 0;
+out_err:
+ return err;
+}
+
+/**
+ * vfscred_getgroups16 - return the supplemental groups from a vfs_cred
+ * @cred: pointer to vfs_cred
+ * @ngrp: number of 16-bit gids to copy
+ * @dst: pointer to dest buffer
+ *
+ */
+static int vfscred_getgroups16(struct vfs_cred *cred, int ngrp, gid16_t *dst)
+{
+ gid_t *src = cred->groups;
+ int i;
+ if (ngrp > cred->ngroups)
+ ngrp = cred->ngroups;
+ for (i = ngrp; i != 0; i--)
+ *dst++ = (gid16_t)*src++;
+ return cred->ngroups;
+}
+
+/**
+ * setgroups16 - set the current task's group list
+ * @ngrp: number of 16-bit gids to copy
+ * @src: pointer to 16-bit gids
+ */
+int setgroups16(int ngrp, gid16_t *src)
+{
+ struct vfs_cred *cred;
+ int ret = -ENOMEM;
+
+ cred = copy_write_task_vfscred(current);
+ if (!cred)
+ goto out;
+ ret = vfscred_setgroups16(cred, ngrp, src);
+ if (!ret)
+ set_current_vfscred(cred);
+ put_vfscred(cred);
+out:
+ return ret;
+}
+
+/**
+ * getgroups16 - return the current task's group list
+ * @ngrp: number of 16-bit gids to copy
+ * @dst: pointer to dest buffer
+ */
+int getgroups16(int ngrp, gid16_t *dst)
+{
+ struct vfs_cred *cred;
+ int ret;
+
+ cred = get_current_vfscred();
+ ret = vfscred_getgroups16(cred, ngrp, dst);
+ put_vfscred(cred);
+ return ret;
+}
+
+#endif /* defined(CONFIG_SPARC64) || defined(CONFIG_ARCH_S390X) */
+
+EXPORT_SYMBOL(put_vfscred);
+EXPORT_SYMBOL(vfscred_getgroups);
+EXPORT_SYMBOL(setfsuid);
+EXPORT_SYMBOL(setfsgid);
+EXPORT_SYMBOL(setgroups);
+EXPORT_SYMBOL(getgroups);
+EXPORT_SYMBOL(set_current_vfscred);
diff -X exclude -urN linux-2.5.44/kernel/exit.c linux-2.5.44-cred/kernel/exit.c
--- linux-2.5.44/kernel/exit.c Fri Oct 18 21:02:29 2002
+++ linux-2.5.44-cred/kernel/exit.c Thu Oct 24 12:07:50 2002
@@ -384,6 +384,23 @@
__exit_fs(tsk);
}
+static inline void __exit_cred(struct task_struct *tsk)
+{
+ struct cred_struct *cred = tsk->cred;
+
+ if (atomic_dec_and_test(&cred->count)) {
+ put_vfscred(cred->vfscred);
+ atomic_dec(&cred->user->processes);
+ free_uid(cred->user);
+ kmem_cache_free(cred_cachep, cred);
+ }
+}
+
+void exit_cred(struct task_struct *tsk)
+{
+ __exit_cred(tsk);
+}
+
/*
* We can use these to temporarily drop into
* "lazy TLB" mode and back.
@@ -646,6 +663,7 @@
sem_exit();
__exit_files(tsk);
__exit_fs(tsk);
+ __exit_cred(tsk);
exit_namespace(tsk);
exit_thread();
diff -X exclude -urN linux-2.5.44/kernel/fork.c linux-2.5.44-cred/kernel/fork.c
--- linux-2.5.44/kernel/fork.c Fri Oct 18 21:01:11 2002
+++ linux-2.5.44-cred/kernel/fork.c Thu Oct 24 12:10:49 2002
@@ -618,6 +618,25 @@
goto out;
}
+static inline int copy_cred(unsigned long clone_flags, struct task_struct * tsk)
+{
+ struct cred_struct *cred;
+
+ if (clone_flags & CLONE_CRED) {
+ atomic_inc(¤t->cred->count);
+ return 0;
+ }
+ cred = kmem_cache_alloc(cred_cachep, GFP_KERNEL);
+ tsk->cred = cred;
+ if (!cred)
+ return -1;
+ *cred = *current->cred;
+ cred->vfscred = get_current_vfscred();
+ atomic_set(&cred->count, 1);
+ atomic_inc(&cred->user->__count);
+ return 0;
+}
+
static inline int copy_sighand(unsigned long clone_flags, struct task_struct * tsk)
{
struct signal_struct *sig;
@@ -692,10 +711,15 @@
goto fork_out;
retval = -EAGAIN;
- if (atomic_read(&p->user->processes) >= p->rlim[RLIMIT_NPROC].rlim_cur) {
- if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE))
- goto bad_fork_free;
- }
+
+ /* for now, always copy */
+ if ( 1 || !(clone_flags & CLONE_CRED)) {
+ if (atomic_read(&p->cred->user->processes) >=
+ p->rlim[RLIMIT_NPROC].rlim_cur)
+ if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE))
+ goto bad_fork_free;
+ atomic_inc(&p->cred->user->processes);
+ }
atomic_inc(&p->user->__count);
atomic_inc(&p->user->processes);
@@ -783,6 +807,8 @@
goto bad_fork_cleanup_files;
if (copy_sighand(clone_flags, p))
goto bad_fork_cleanup_fs;
+ if (copy_cred(clone_flags, p))
+ goto bad_fork_cleanup_cred;
if (copy_mm(clone_flags, p))
goto bad_fork_cleanup_sighand;
if (copy_namespace(clone_flags, p))
@@ -913,6 +939,8 @@
exit_namespace(p);
bad_fork_cleanup_mm:
exit_mm(p);
+bad_fork_cleanup_cred:
+ exit_cred(p);
bad_fork_cleanup_sighand:
exit_sighand(p);
bad_fork_cleanup_fs:
@@ -930,8 +958,8 @@
if (p->binfmt && p->binfmt->module)
__MOD_DEC_USE_COUNT(p->binfmt->module);
bad_fork_cleanup_count:
- atomic_dec(&p->user->processes);
- free_uid(p->user);
+ if ( 1 || !(clone_flags & CLONE_CRED))
+ atomic_dec(&p->cred->user->processes);
bad_fork_free:
put_task_struct(p);
goto fork_out;
@@ -977,6 +1005,9 @@
return p;
}
+/* SLAB cache for cred_struct structures (tsk->cred) */
+kmem_cache_t *cred_cachep;
+
/* SLAB cache for signal_struct structures (tsk->sig) */
kmem_cache_t *sigact_cachep;
@@ -994,6 +1025,12 @@
void __init proc_caches_init(void)
{
+ cred_cachep = kmem_cache_create("cred_cache",
+ sizeof(struct cred_struct), 0,
+ SLAB_HWCACHE_ALIGN, NULL, NULL);
+ if (!cred_cachep)
+ panic("Cannot create credential SLAB cache");
+
sigact_cachep = kmem_cache_create("signal_act",
sizeof(struct signal_struct), 0,
SLAB_HWCACHE_ALIGN, NULL, NULL);
diff -X exclude -urN linux-2.5.44/kernel/sched.c linux-2.5.44-cred/kernel/sched.c
--- linux-2.5.44/kernel/sched.c Fri Oct 18 21:02:28 2002
+++ linux-2.5.44-cred/kernel/sched.c Thu Oct 24 11:27:55 2002
@@ -1447,7 +1447,7 @@
if ((policy == SCHED_FIFO || policy == SCHED_RR) &&
!capable(CAP_SYS_NICE))
goto out_unlock;
- if ((current->euid != p->euid) && (current->euid != p->uid) &&
+ if ((current->cred->euid != p->cred->euid) && (current->cred->euid != p->cred->uid) &&
!capable(CAP_SYS_NICE))
goto out_unlock;
@@ -1605,7 +1605,8 @@
read_unlock(&tasklist_lock);
retval = -EPERM;
- if ((current->euid != p->euid) && (current->euid != p->uid) &&
+ if ((current->cred->euid != p->cred->euid) &&
+ (current->cred->euid != p->cred->uid) &&
!capable(CAP_SYS_NICE))
goto out_unlock;
^ permalink raw reply [flat|nested] 2+ messages in thread