linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC] shared credentials with vfs snapshotting
@ 2002-10-24 20:12 David C. Hansen
  2002-10-24 20:24 ` David C. Hansen
  0 siblings, 1 reply; 2+ messages in thread
From: David C. Hansen @ 2002-10-24 20:12 UTC (permalink / raw)
  To: lkml; +Cc: Trond Myklebust, Dave McCracken

This patch combines the ideas from two others:  Dave McCracken's 
combination of task credentials into a single structure which is 
shared between threads:
http://marc.theaimsgroup.com/?t=102830918300009&r=1&w=2
And Trond Myklebust's snapshotting of the vfs-specific parts of
the cred structure which are passed down into vfs and are strictly
copy-on-write. 
http://marc.theaimsgroup.com/?t=103081191900001&r=1&w=2
http://marc.theaimsgroup.com/?t=103074984200004&r=1&w=2
http://www.fys.uio.no/~trondmy/src/2.5.32-alpha

Implementing the appearance of shared credentials to userspace requires
large amounts of code to be added in the threading libraries.  The
addition of code here is reasonalbly small.  

This patch is by no means complete or correct.  It completely ignores
the credential sharing flag for now.  It is just here to demonstrate the
combination of the two ideas.  Please don't go applying it to anything
;)

I think that the core of what is needed is in the attached patch.  Most
of what is left can be accomplished with s/->uid/->cred->uid/ and
s/->fsuid/->cred->vfscred->uid/

And, as Trond says:
> Unfortunately there's still a bit more to do. I need to get
> the file creation ops (i_op->create()/symlink()/mknod()/mkdir()) to
> take a vfs_cred* argument. If not, you risk having the
> inode->i_uid/i_gid set to values that differ from the ones checked by
> the calls to ->permission().


-- 
Dave Hansen
haveblue@us.ibm.com


^ permalink raw reply	[flat|nested] 2+ messages in thread

* 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(&current->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

end of thread, other threads:[~2002-10-24 20:18 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2002-10-24 20:12 [RFC] shared credentials with vfs snapshotting David C. Hansen
2002-10-24 20:24 ` David C. Hansen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).