svn commit: r194498 - in head: . lib/libc/rpc lib/libkvm sys/compat/linux sys/fs/nfs sys/fs/nfsclient sys/fs/nfsserver sys/fs/portalfs sys/fs/unionfs sys/i386/ibcs2 sys/kern sys/netinet/ipfw sys/nf...

Brooks Davis brooks at FreeBSD.org
Fri Jun 19 17:10:37 UTC 2009


Author: brooks
Date: Fri Jun 19 17:10:35 2009
New Revision: 194498
URL: http://svn.freebsd.org/changeset/base/194498

Log:
  Rework the credential code to support larger values of NGROUPS and
  NGROUPS_MAX, eliminate ABI dependencies on them, and raise the to 1024
  and 1023 respectively.  (Previously they were equal, but under a close
  reading of POSIX, NGROUPS_MAX was defined to be too large by 1 since it
  is the number of supplemental groups, not total number of groups.)
  
  The bulk of the change consists of converting the struct ucred member
  cr_groups from a static array to a pointer.  Do the equivalent in
  kinfo_proc.
  
  Introduce new interfaces crcopysafe() and crsetgroups() for duplicating
  a process credential before modifying it and for setting group lists
  respectively.  Both interfaces take care for the details of allocating
  groups array. crsetgroups() takes care of truncating the group list
  to the current maximum (NGROUPS) if necessary.  In the future,
  crsetgroups() may be responsible for insuring invariants such as sorting
  the supplemental groups to allow groupmember() to be implemented as a
  binary search.
  
  Because we can not change struct xucred without breaking application
  ABIs, we leave it alone and introduce a new XU_NGROUPS value which is
  always 16 and is to be used or NGRPS as appropriate for things such as
  NFS which need to use no more than 16 groups.  When feasible, truncate
  the group list rather than generating an error.
  
  Minor changes:
    - Reduce the number of hand rolled versions of groupmember().
    - Do not assign to both cr_gid and cr_groups[0].
    - Modify ipfw to cache ucreds instead of part of their contents since
      they are immutable once referenced by more than one entity.
  
  Submitted by:	Isilon Systems (initial implementation)
  X-MFC after:	never
  PR:		bin/113398 kern/133867

Modified:
  head/UPDATING
  head/lib/libc/rpc/netname.c
  head/lib/libc/rpc/netnamer.c
  head/lib/libkvm/kvm_proc.c
  head/sys/compat/linux/linux_misc.c
  head/sys/compat/linux/linux_uid16.c
  head/sys/fs/nfs/nfs_commonport.c
  head/sys/fs/nfsclient/nfs_clport.c
  head/sys/fs/nfsserver/nfs_nfsdport.c
  head/sys/fs/nfsserver/nfs_nfsdstate.c
  head/sys/fs/portalfs/portal.h
  head/sys/fs/portalfs/portal_vnops.c
  head/sys/fs/unionfs/union_vnops.c
  head/sys/i386/ibcs2/ibcs2_misc.c
  head/sys/kern/kern_exec.c
  head/sys/kern/kern_proc.c
  head/sys/kern/kern_prot.c
  head/sys/kern/vfs_export.c
  head/sys/netinet/ipfw/ip_fw2.c
  head/sys/nfsserver/nfs_srvsock.c
  head/sys/nfsserver/nfs_srvsubs.c
  head/sys/rpc/rpcsec_gss/svc_rpcsec_gss.c
  head/sys/rpc/svc_auth.c
  head/sys/rpc/svc_auth_unix.c
  head/sys/sys/param.h
  head/sys/sys/syslimits.h
  head/sys/sys/ucred.h
  head/sys/sys/user.h
  head/sys/ufs/ufs/ufs_vnops.c
  head/usr.sbin/mount_portalfs/portald.h
  head/usr.sbin/mountd/mountd.c

Modified: head/UPDATING
==============================================================================
--- head/UPDATING	Fri Jun 19 17:07:38 2009	(r194497)
+++ head/UPDATING	Fri Jun 19 17:10:35 2009	(r194498)
@@ -22,6 +22,23 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 8.
 	to maximize performance.  (To disable malloc debugging, run
 	ln -s aj /etc/malloc.conf.)
 
+20090619:
+	NGROUPS_MAX and NGROUPS have been increased from 16 to 1023
+	and 1024 respectively.  As long as no more than 16 groups per
+	process are used, no changes should be visible.  When more
+	than 16 groups are used, old binaries may fail if they call
+	getgroups() or getgrouplist() with statically sized storage.
+	Recompiling will work around this, but applications should be
+	modified to use dynamically allocated storage for group arrays
+	as POSIX.1-2008 does not cap an implementation's number of
+	supported groups at NGROUPS_MAX+1 as previous versions did.
+
+	NFS and portalfs mounts may also be affected as the list of
+	groups is truncated to 16.  Users of NFS who use more than 16
+	groups, should take care that negative group permissions are not
+	used on the exported file systems as they will not be reliable
+	unless a GSSAPI based authentication method is used.
+
 20090616:
 	The compiling option ADAPTIVE_LOCKMGRS has been introduced.
 	This option compiles in the support for adaptive spinning for lockmgrs

Modified: head/lib/libc/rpc/netname.c
==============================================================================
--- head/lib/libc/rpc/netname.c	Fri Jun 19 17:07:38 2009	(r194497)
+++ head/lib/libc/rpc/netname.c	Fri Jun 19 17:10:35 2009	(r194498)
@@ -61,9 +61,6 @@ __FBSDID("$FreeBSD$");
 #ifndef MAXHOSTNAMELEN
 #define MAXHOSTNAMELEN 256
 #endif
-#ifndef NGROUPS
-#define NGROUPS 16
-#endif
 
 #define TYPE_BIT(type)  (sizeof (type) * CHAR_BIT)
 

Modified: head/lib/libc/rpc/netnamer.c
==============================================================================
--- head/lib/libc/rpc/netnamer.c	Fri Jun 19 17:07:38 2009	(r194497)
+++ head/lib/libc/rpc/netnamer.c	Fri Jun 19 17:10:35 2009	(r194498)
@@ -66,10 +66,6 @@ static char    *NETIDFILE = "/etc/netid"
 static int getnetid( char *, char * );
 static int _getgroups( char *, gid_t * );
 
-#ifndef NGROUPS
-#define NGROUPS 16
-#endif
-
 /*
  * Convert network-name into unix credential
  */
@@ -104,7 +100,7 @@ netname2user(netname, uidp, gidp, gidlen
 			return (0);
 		}
 		*gidp = (gid_t) atol(p);
-		for (gidlen = 0; gidlen < NGROUPS; gidlen++) {
+		for (gidlen = 0; gidlen < NGRPS; gidlen++) {
 			p = strsep(&res, "\n,");
 			if (p == NULL)
 				break;
@@ -157,7 +153,7 @@ netname2user(netname, uidp, gidp, gidlen
 static int
 _getgroups(uname, groups)
 	char           *uname;
-	gid_t          groups[NGROUPS];
+	gid_t          groups[NGRPS];
 {
 	gid_t           ngroups = 0;
 	struct group *grp;
@@ -169,7 +165,7 @@ _getgroups(uname, groups)
 	while ((grp = getgrent())) {
 		for (i = 0; grp->gr_mem[i]; i++)
 			if (!strcmp(grp->gr_mem[i], uname)) {
-				if (ngroups == NGROUPS) {
+				if (ngroups == NGRPS) {
 #ifdef DEBUG
 					fprintf(stderr,
 				"initgroups: %s is in too many groups\n", uname);

Modified: head/lib/libkvm/kvm_proc.c
==============================================================================
--- head/lib/libkvm/kvm_proc.c	Fri Jun 19 17:07:38 2009	(r194497)
+++ head/lib/libkvm/kvm_proc.c	Fri Jun 19 17:10:35 2009	(r194498)
@@ -146,8 +146,7 @@ kvm_proclist(kd, what, arg, p, bp, maxcn
 			kp->ki_rgid = ucred.cr_rgid;
 			kp->ki_svgid = ucred.cr_svgid;
 			kp->ki_ngroups = ucred.cr_ngroups;
-			bcopy(ucred.cr_groups, kp->ki_groups,
-			    NGROUPS * sizeof(gid_t));
+			kp->ki_groups = ucred.cr_groups;
 			kp->ki_uid = ucred.cr_uid;
 			if (ucred.cr_prison != NULL) {
 				if (KREAD(kd, (u_long)ucred.cr_prison, &pr)) {

Modified: head/sys/compat/linux/linux_misc.c
==============================================================================
--- head/sys/compat/linux/linux_misc.c	Fri Jun 19 17:07:38 2009	(r194497)
+++ head/sys/compat/linux/linux_misc.c	Fri Jun 19 17:10:35 2009	(r194498)
@@ -1132,7 +1132,7 @@ int
 linux_setgroups(struct thread *td, struct linux_setgroups_args *args)
 {
 	struct ucred *newcred, *oldcred;
-	l_gid_t linux_gidset[NGROUPS];
+	l_gid_t *linux_gidset;
 	gid_t *bsd_gidset;
 	int ngrp, error;
 	struct proc *p;
@@ -1140,13 +1140,14 @@ linux_setgroups(struct thread *td, struc
 	ngrp = args->gidsetsize;
 	if (ngrp < 0 || ngrp >= NGROUPS)
 		return (EINVAL);
+	linux_gidset = malloc(ngrp * sizeof(*linux_gidset), M_TEMP, M_WAITOK);
 	error = copyin(args->grouplist, linux_gidset, ngrp * sizeof(l_gid_t));
 	if (error)
-		return (error);
+		goto out;
 	newcred = crget();
 	p = td->td_proc;
 	PROC_LOCK(p);
-	oldcred = p->p_ucred;
+	oldcred = crcopysafe(p, newcred);
 
 	/*
 	 * cr_groups[0] holds egid. Setting the whole set from
@@ -1157,10 +1158,9 @@ linux_setgroups(struct thread *td, struc
 	if ((error = priv_check_cred(oldcred, PRIV_CRED_SETGROUPS, 0)) != 0) {
 		PROC_UNLOCK(p);
 		crfree(newcred);
-		return (error);
+		goto out;
 	}
 
-	crcopy(newcred, oldcred);
 	if (ngrp > 0) {
 		newcred->cr_ngroups = ngrp + 1;
 
@@ -1177,14 +1177,17 @@ linux_setgroups(struct thread *td, struc
 	p->p_ucred = newcred;
 	PROC_UNLOCK(p);
 	crfree(oldcred);
-	return (0);
+	error = 0;
+out:
+	free(linux_gidset, M_TEMP);
+	return (error);
 }
 
 int
 linux_getgroups(struct thread *td, struct linux_getgroups_args *args)
 {
 	struct ucred *cred;
-	l_gid_t linux_gidset[NGROUPS];
+	l_gid_t *linux_gidset;
 	gid_t *bsd_gidset;
 	int bsd_gidsetsz, ngrp, error;
 
@@ -1207,13 +1210,16 @@ linux_getgroups(struct thread *td, struc
 		return (EINVAL);
 
 	ngrp = 0;
+	linux_gidset = malloc(bsd_gidsetsz * sizeof(*linux_gidset),
+	    M_TEMP, M_WAITOK);
 	while (ngrp < bsd_gidsetsz) {
 		linux_gidset[ngrp] = bsd_gidset[ngrp + 1];
 		ngrp++;
 	}
 
-	if ((error = copyout(linux_gidset, args->grouplist,
-	    ngrp * sizeof(l_gid_t))))
+	error = copyout(linux_gidset, args->grouplist, ngrp * sizeof(l_gid_t));
+	free(linux_gidset, M_TEMP);
+	if (error)
 		return (error);
 
 	td->td_retval[0] = ngrp;

Modified: head/sys/compat/linux/linux_uid16.c
==============================================================================
--- head/sys/compat/linux/linux_uid16.c	Fri Jun 19 17:07:38 2009	(r194497)
+++ head/sys/compat/linux/linux_uid16.c	Fri Jun 19 17:10:35 2009	(r194498)
@@ -98,7 +98,7 @@ int
 linux_setgroups16(struct thread *td, struct linux_setgroups16_args *args)
 {
 	struct ucred *newcred, *oldcred;
-	l_gid16_t linux_gidset[NGROUPS];
+	l_gid16_t *linux_gidset;
 	gid_t *bsd_gidset;
 	int ngrp, error;
 	struct proc *p;
@@ -111,13 +111,14 @@ linux_setgroups16(struct thread *td, str
 	ngrp = args->gidsetsize;
 	if (ngrp < 0 || ngrp >= NGROUPS)
 		return (EINVAL);
+	linux_gidset = malloc(ngrp * sizeof(*linux_gidset), M_TEMP, M_WAITOK);
 	error = copyin(args->gidset, linux_gidset, ngrp * sizeof(l_gid16_t));
 	if (error)
 		return (error);
 	newcred = crget();
 	p = td->td_proc;
 	PROC_LOCK(p);
-	oldcred = p->p_ucred;
+	oldcred = crcopysafe(p, newcred);
 
 	/*
 	 * cr_groups[0] holds egid. Setting the whole set from
@@ -128,10 +129,9 @@ linux_setgroups16(struct thread *td, str
 	if ((error = priv_check_cred(oldcred, PRIV_CRED_SETGROUPS, 0)) != 0) {
 		PROC_UNLOCK(p);
 		crfree(newcred);
-		return (error);
+		goto out;
 	}
 
-	crcopy(newcred, oldcred);
 	if (ngrp > 0) {
 		newcred->cr_ngroups = ngrp + 1;
 
@@ -149,14 +149,17 @@ linux_setgroups16(struct thread *td, str
 	p->p_ucred = newcred;
 	PROC_UNLOCK(p);
 	crfree(oldcred);
-	return (0);
+	error = 0;
+out:
+	free(linux_gidset, M_TEMP);
+	return (error);
 }
 
 int
 linux_getgroups16(struct thread *td, struct linux_getgroups16_args *args)
 {
 	struct ucred *cred;
-	l_gid16_t linux_gidset[NGROUPS];
+	l_gid16_t *linux_gidset;
 	gid_t *bsd_gidset;
 	int bsd_gidsetsz, ngrp, error;
 
@@ -184,12 +187,15 @@ linux_getgroups16(struct thread *td, str
 		return (EINVAL);
 
 	ngrp = 0;
+	linux_gidset = malloc(bsd_gidsetsz * sizeof(*linux_gidset),
+	    M_TEMP, M_WAITOK);
 	while (ngrp < bsd_gidsetsz) {
 		linux_gidset[ngrp] = bsd_gidset[ngrp + 1];
 		ngrp++;
 	}
 
 	error = copyout(linux_gidset, args->gidset, ngrp * sizeof(l_gid16_t));
+	free(linux_gidset, M_TEMP);
 	if (error)
 		return (error);
 

Modified: head/sys/fs/nfs/nfs_commonport.c
==============================================================================
--- head/sys/fs/nfs/nfs_commonport.c	Fri Jun 19 17:07:38 2009	(r194497)
+++ head/sys/fs/nfs/nfs_commonport.c	Fri Jun 19 17:10:35 2009	(r194498)
@@ -220,14 +220,9 @@ nfsrv_lookupfilename(struct nameidata *n
 void
 newnfs_copycred(struct nfscred *nfscr, struct ucred *cr)
 {
-	int ngroups, i;
 
 	cr->cr_uid = nfscr->nfsc_uid;
-	ngroups = (nfscr->nfsc_ngroups < NGROUPS) ?
-	    nfscr->nfsc_ngroups : NGROUPS;
-	for (i = 0; i < ngroups; i++)
-		cr->cr_groups[i] = nfscr->nfsc_groups[i];
-	cr->cr_ngroups = ngroups;
+	crsetgroups(cr, nfscr->nfsc_ngroups, nfscr->nfsc_groups);
 }
 
 /*

Modified: head/sys/fs/nfsclient/nfs_clport.c
==============================================================================
--- head/sys/fs/nfsclient/nfs_clport.c	Fri Jun 19 17:07:38 2009	(r194497)
+++ head/sys/fs/nfsclient/nfs_clport.c	Fri Jun 19 17:10:35 2009	(r194498)
@@ -976,14 +976,12 @@ nfscl_getmyip(struct nfsmount *nmp, int 
 void
 newnfs_copyincred(struct ucred *cr, struct nfscred *nfscr)
 {
-	int ngroups, i;
+	int i;
 
 	nfscr->nfsc_uid = cr->cr_uid;
-	ngroups = (cr->cr_ngroups > NGROUPS) ? NGROUPS :
-	    cr->cr_ngroups;
-	for (i = 0; i < ngroups; i++)
+	nfscr->nfsc_ngroups = MIN(cr->cr_ngroups, XU_NGROUPS);
+	for (i = 0; i < nfscr->nfsc_ngroups; i++)
 		nfscr->nfsc_groups[i] = cr->cr_groups[i];
-	nfscr->nfsc_ngroups = ngroups;
 }
 
 

Modified: head/sys/fs/nfsserver/nfs_nfsdport.c
==============================================================================
--- head/sys/fs/nfsserver/nfs_nfsdport.c	Fri Jun 19 17:07:38 2009	(r194497)
+++ head/sys/fs/nfsserver/nfs_nfsdport.c	Fri Jun 19 17:10:35 2009	(r194498)
@@ -2360,7 +2360,6 @@ int
 nfsd_excred(struct nfsrv_descript *nd, struct nfsexstuff *exp,
     struct ucred *credanon)
 {
-	int i;
 	int error = 0;
 
 	/*
@@ -2403,9 +2402,8 @@ nfsd_excred(struct nfsrv_descript *nd, s
 	     (nd->nd_flag & ND_AUTHNONE))) {
 		nd->nd_cred->cr_uid = credanon->cr_uid;
 		nd->nd_cred->cr_gid = credanon->cr_gid;
-		for (i = 0; i < credanon->cr_ngroups && i < NGROUPS; i++)
-			nd->nd_cred->cr_groups[i] = credanon->cr_groups[i];
-		nd->nd_cred->cr_ngroups = i;
+		crsetgroups(nd->nd_cred, credanon->cr_ngroups,
+		    credanon->cr_groups);
 	}
 	return (0);
 }

Modified: head/sys/fs/nfsserver/nfs_nfsdstate.c
==============================================================================
--- head/sys/fs/nfsserver/nfs_nfsdstate.c	Fri Jun 19 17:07:38 2009	(r194497)
+++ head/sys/fs/nfsserver/nfs_nfsdstate.c	Fri Jun 19 17:10:35 2009	(r194498)
@@ -3577,7 +3577,6 @@ nfsrv_docallback(struct nfsclient *clp, 
 	nd->nd_repstat = 0;
 	cred->cr_uid = clp->lc_uid;
 	cred->cr_gid = clp->lc_gid;
-	cred->cr_groups[0] = clp->lc_gid;
 	callback = clp->lc_callback;
 	NFSUNLOCKSTATE();
 	cred->cr_ngroups = 1;

Modified: head/sys/fs/portalfs/portal.h
==============================================================================
--- head/sys/fs/portalfs/portal.h	Fri Jun 19 17:07:38 2009	(r194497)
+++ head/sys/fs/portalfs/portal.h	Fri Jun 19 17:10:35 2009	(r194498)
@@ -43,7 +43,7 @@ struct portal_cred {
 	int		pcr_flag;		/* File open mode */
 	uid_t		pcr_uid;		/* From ucred */
 	short		pcr_ngroups;		/* From ucred */
-	gid_t		pcr_groups[NGROUPS];	/* From ucred */
+	gid_t		pcr_groups[XU_NGROUPS];	/* From ucred */
 };
 
 #ifdef _KERNEL

Modified: head/sys/fs/portalfs/portal_vnops.c
==============================================================================
--- head/sys/fs/portalfs/portal_vnops.c	Fri Jun 19 17:07:38 2009	(r194497)
+++ head/sys/fs/portalfs/portal_vnops.c	Fri Jun 19 17:10:35 2009	(r194498)
@@ -311,8 +311,9 @@ portal_open(ap)
 
 	pcred.pcr_flag = ap->a_mode;
 	pcred.pcr_uid = ap->a_cred->cr_uid;
-	pcred.pcr_ngroups = ap->a_cred->cr_ngroups;
-	bcopy(ap->a_cred->cr_groups, pcred.pcr_groups, NGROUPS * sizeof(gid_t));
+	pcred.pcr_ngroups = MIN(ap->a_cred->cr_ngroups, XU_NGROUPS);
+	bcopy(ap->a_cred->cr_groups, pcred.pcr_groups,
+	    pcred.pcr_ngroups * sizeof(gid_t));
 	aiov[0].iov_base = (caddr_t) &pcred;
 	aiov[0].iov_len = sizeof(pcred);
 	aiov[1].iov_base = pt->pt_arg;

Modified: head/sys/fs/unionfs/union_vnops.c
==============================================================================
--- head/sys/fs/unionfs/union_vnops.c	Fri Jun 19 17:07:38 2009	(r194497)
+++ head/sys/fs/unionfs/union_vnops.c	Fri Jun 19 17:10:35 2009	(r194498)
@@ -638,7 +638,6 @@ unionfs_check_corrected_access(accmode_t
 	uid_t		uid;	/* upper side vnode's uid */
 	gid_t		gid;	/* upper side vnode's gid */
 	u_short		vmode;	/* upper side vnode's mode */
-	gid_t          *gp;
 	u_short		mask;
 
 	mask = 0;
@@ -659,17 +658,14 @@ unionfs_check_corrected_access(accmode_t
 
 	/* check group */
 	count = 0;
-	gp = cred->cr_groups;
-	for (; count < cred->cr_ngroups; count++, gp++) {
-		if (gid == *gp) {
-			if (accmode & VEXEC)
-				mask |= S_IXGRP;
-			if (accmode & VREAD)
-				mask |= S_IRGRP;
-			if (accmode & VWRITE)
-				mask |= S_IWGRP;
-			return ((vmode & mask) == mask ? 0 : EACCES);
-		}
+	if (groupmember(gid, cred)) {
+		if (accmode & VEXEC)
+			mask |= S_IXGRP;
+		if (accmode & VREAD)
+			mask |= S_IRGRP;
+		if (accmode & VWRITE)
+			mask |= S_IWGRP;
+		return ((vmode & mask) == mask ? 0 : EACCES);
 	}
 
 	/* check other */

Modified: head/sys/i386/ibcs2/ibcs2_misc.c
==============================================================================
--- head/sys/i386/ibcs2/ibcs2_misc.c	Fri Jun 19 17:07:38 2009	(r194497)
+++ head/sys/i386/ibcs2/ibcs2_misc.c	Fri Jun 19 17:10:35 2009	(r194498)
@@ -657,24 +657,29 @@ ibcs2_getgroups(td, uap)
 	struct thread *td;
 	struct ibcs2_getgroups_args *uap;
 {
-	ibcs2_gid_t iset[NGROUPS_MAX];
-	gid_t gp[NGROUPS_MAX];
+	ibcs2_gid_t *iset;
+	gid_t *gp;
 	u_int i, ngrp;
 	int error;
 
 	if (uap->gidsetsize < 0)
 		return (EINVAL);
 	ngrp = MIN(uap->gidsetsize, NGROUPS_MAX);
+	gp = malloc(ngrp * sizeof(*gp), M_TEMP, M_WAITOK);
 	error = kern_getgroups(td, &ngrp, gp);
 	if (error)
-		return (error);
+		goto out;
 	if (uap->gidsetsize > 0) {
+		iset = malloc(ngrp * sizeof(*iset), M_TEMP, M_WAITOK);
 		for (i = 0; i < ngrp; i++)
 			iset[i] = (ibcs2_gid_t)gp[i];
 		error = copyout(iset, uap->gidset, ngrp * sizeof(ibcs2_gid_t));
+		free(iset, M_TEMP);
 	}
 	if (error == 0)
 		td->td_retval[0] = ngrp;
+out:
+	free(gp, M_TEMP);
 	return (error);
 }
 
@@ -683,21 +688,31 @@ ibcs2_setgroups(td, uap)
 	struct thread *td;
 	struct ibcs2_setgroups_args *uap;
 {
-	ibcs2_gid_t iset[NGROUPS_MAX];
-	gid_t gp[NGROUPS_MAX];
+	ibcs2_gid_t *iset;
+	gid_t *gp;
 	int error, i;
 
 	if (uap->gidsetsize < 0 || uap->gidsetsize > NGROUPS_MAX)
 		return (EINVAL);
-	if (uap->gidsetsize && uap->gidset) {
+	if (uap->gidsetsize && uap->gidset == NULL)
+		return (EINVAL);
+	gp = malloc(uap->gidsetsize * sizeof(*gp), M_TEMP, M_WAITOK);
+	if (uap->gidsetsize) {
+		iset = malloc(uap->gidsetsize * sizeof(*iset), M_TEMP, M_WAITOK);
 		error = copyin(uap->gidset, iset, sizeof(ibcs2_gid_t) *
 		    uap->gidsetsize);
-		if (error)
-			return (error);
+		if (error) {
+			free(iset, M_TEMP);
+			goto out;
+		}
 		for (i = 0; i < uap->gidsetsize; i++)
 			gp[i] = (gid_t)iset[i];
 	}
-	return (kern_setgroups(td, uap->gidsetsize, gp));
+
+	error = kern_setgroups(td, uap->gidsetsize, gp);
+out:
+	free(gp, M_TEMP);
+	return (error);
 }
 
 int

Modified: head/sys/kern/kern_exec.c
==============================================================================
--- head/sys/kern/kern_exec.c	Fri Jun 19 17:07:38 2009	(r194497)
+++ head/sys/kern/kern_exec.c	Fri Jun 19 17:10:35 2009	(r194498)
@@ -579,6 +579,7 @@ interpret:
 	 * reset.
 	 */
 	PROC_LOCK(p);
+	oldcred = crcopysafe(p, newcred);
 	if (sigacts_shared(p->p_sigacts)) {
 		oldsigacts = p->p_sigacts;
 		PROC_UNLOCK(p);
@@ -629,7 +630,6 @@ interpret:
 	 * XXXMAC: For the time being, use NOSUID to also prohibit
 	 * transitions on the file system.
 	 */
-	oldcred = p->p_ucred;
 	credential_changing = 0;
 	credential_changing |= (attr.va_mode & S_ISUID) && oldcred->cr_uid !=
 	    attr.va_uid;
@@ -683,7 +683,6 @@ interpret:
 		/*
 		 * Set the new credentials.
 		 */
-		crcopy(newcred, oldcred);
 		if (attr.va_mode & S_ISUID)
 			change_euid(newcred, euip);
 		if (attr.va_mode & S_ISGID)
@@ -723,7 +722,6 @@ interpret:
 		 */
 		if (oldcred->cr_svuid != oldcred->cr_uid ||
 		    oldcred->cr_svgid != oldcred->cr_gid) {
-			crcopy(newcred, oldcred);
 			change_svuid(newcred, newcred->cr_uid);
 			change_svgid(newcred, newcred->cr_gid);
 			p->p_ucred = newcred;

Modified: head/sys/kern/kern_proc.c
==============================================================================
--- head/sys/kern/kern_proc.c	Fri Jun 19 17:07:38 2009	(r194497)
+++ head/sys/kern/kern_proc.c	Fri Jun 19 17:10:35 2009	(r194498)
@@ -730,10 +730,8 @@ fill_kinfo_proc_only(struct proc *p, str
 		kp->ki_uid = cred->cr_uid;
 		kp->ki_ruid = cred->cr_ruid;
 		kp->ki_svuid = cred->cr_svuid;
-		/* XXX bde doesn't like KI_NGROUPS */
-		kp->ki_ngroups = min(cred->cr_ngroups, KI_NGROUPS);
-		bcopy(cred->cr_groups, kp->ki_groups,
-		    kp->ki_ngroups * sizeof(gid_t));
+		kp->ki_ngroups = cred->cr_ngroups;
+		kp->ki_groups = cred->cr_groups;
 		kp->ki_rgid = cred->cr_rgid;
 		kp->ki_svgid = cred->cr_svgid;
 		kp->ki_cr_flags = cred->cr_flags;

Modified: head/sys/kern/kern_prot.c
==============================================================================
--- head/sys/kern/kern_prot.c	Fri Jun 19 17:07:38 2009	(r194497)
+++ head/sys/kern/kern_prot.c	Fri Jun 19 17:10:35 2009	(r194498)
@@ -82,6 +82,11 @@ static MALLOC_DEFINE(M_CRED, "cred", "cr
 
 SYSCTL_NODE(_security, OID_AUTO, bsd, CTLFLAG_RW, 0, "BSD security policy");
 
+static void crextend(struct ucred *cr, int n);
+static void crsetgroups_locked(struct ucred *cr, int ngrp,
+    gid_t *groups);
+
+
 #ifndef _SYS_SYSPROTO_H_
 struct getpid_args {
 	int	dummy;
@@ -276,18 +281,21 @@ struct getgroups_args {
 int
 getgroups(struct thread *td, register struct getgroups_args *uap)
 {
-	gid_t groups[NGROUPS];
+	gid_t *groups;
 	u_int ngrp;
 	int error;
 
 	ngrp = MIN(uap->gidsetsize, NGROUPS);
+	groups = malloc(ngrp * sizeof(*groups), M_TEMP, M_WAITOK);
 	error = kern_getgroups(td, &ngrp, groups);
 	if (error)
-		return (error);
+		goto out;
 	if (uap->gidsetsize > 0)
 		error = copyout(groups, uap->gidset, ngrp * sizeof(gid_t));
 	if (error == 0)
 		td->td_retval[0] = ngrp;
+out:
+	free(groups, M_TEMP);
 	return (error);
 }
 
@@ -486,7 +494,10 @@ setuid(struct thread *td, struct setuid_
 	newcred = crget();
 	uip = uifind(uid);
 	PROC_LOCK(p);
-	oldcred = p->p_ucred;
+	/*
+	 * Copy credentials so other references do not see our changes.
+	 */
+	oldcred = crcopysafe(p, newcred);
 
 #ifdef MAC
 	error = mac_cred_check_setuid(oldcred, uid);
@@ -521,10 +532,6 @@ setuid(struct thread *td, struct setuid_
 	    (error = priv_check_cred(oldcred, PRIV_CRED_SETUID, 0)) != 0)
 		goto fail;
 
-	/*
-	 * Copy credentials so other references do not see our changes.
-	 */
-	crcopy(newcred, oldcred);
 #ifdef _POSIX_SAVED_IDS
 	/*
 	 * Do we have "appropriate privileges" (are we root or uid == euid)
@@ -598,7 +605,10 @@ seteuid(struct thread *td, struct seteui
 	newcred = crget();
 	euip = uifind(euid);
 	PROC_LOCK(p);
-	oldcred = p->p_ucred;
+	/*
+	 * Copy credentials so other references do not see our changes.
+	 */
+	oldcred = crcopysafe(p, newcred);
 
 #ifdef MAC
 	error = mac_cred_check_seteuid(oldcred, euid);
@@ -612,8 +622,7 @@ seteuid(struct thread *td, struct seteui
 		goto fail;
 
 	/*
-	 * Everything's okay, do it.  Copy credentials so other references do
-	 * not see our changes.
+	 * Everything's okay, do it.
 	 */
 	crcopy(newcred, oldcred);
 	if (oldcred->cr_uid != euid) {
@@ -651,7 +660,7 @@ setgid(struct thread *td, struct setgid_
 	AUDIT_ARG(gid, gid);
 	newcred = crget();
 	PROC_LOCK(p);
-	oldcred = p->p_ucred;
+	oldcred = crcopysafe(p, newcred);
 
 #ifdef MAC
 	error = mac_cred_check_setgid(oldcred, gid);
@@ -680,7 +689,6 @@ setgid(struct thread *td, struct setgid_
 	    (error = priv_check_cred(oldcred, PRIV_CRED_SETGID, 0)) != 0)
 		goto fail;
 
-	crcopy(newcred, oldcred);
 #ifdef _POSIX_SAVED_IDS
 	/*
 	 * Do we have "appropriate privileges" (are we root or gid == egid)
@@ -750,7 +758,7 @@ setegid(struct thread *td, struct setegi
 	AUDIT_ARG(egid, egid);
 	newcred = crget();
 	PROC_LOCK(p);
-	oldcred = p->p_ucred;
+	oldcred = crcopysafe(p, newcred);
 
 #ifdef MAC
 	error = mac_cred_check_setegid(oldcred, egid);
@@ -763,7 +771,6 @@ setegid(struct thread *td, struct setegi
 	    (error = priv_check_cred(oldcred, PRIV_CRED_SETEGID, 0)) != 0)
 		goto fail;
 
-	crcopy(newcred, oldcred);
 	if (oldcred->cr_groups[0] != egid) {
 		change_egid(newcred, egid);
 		setsugid(p);
@@ -789,15 +796,19 @@ struct setgroups_args {
 int
 setgroups(struct thread *td, struct setgroups_args *uap)
 {
-	gid_t groups[NGROUPS];
+	gid_t *groups = NULL;
 	int error;
 
 	if (uap->gidsetsize > NGROUPS)
 		return (EINVAL);
+	groups = malloc(uap->gidsetsize * sizeof(gid_t), M_TEMP, M_WAITOK);
 	error = copyin(uap->gidset, groups, uap->gidsetsize * sizeof(gid_t));
 	if (error)
-		return (error);
-	return (kern_setgroups(td, uap->gidsetsize, groups));
+		goto out;
+	error = kern_setgroups(td, uap->gidsetsize, groups);
+out:
+	free(groups, M_TEMP);
+	return (error);
 }
 
 int
@@ -811,8 +822,9 @@ kern_setgroups(struct thread *td, u_int 
 		return (EINVAL);
 	AUDIT_ARG(groupset, groups, ngrp);
 	newcred = crget();
+	crextend(newcred, ngrp);
 	PROC_LOCK(p);
-	oldcred = p->p_ucred;
+	oldcred = crcopysafe(p, newcred);
 
 #ifdef MAC
 	error = mac_cred_check_setgroups(oldcred, ngrp, groups);
@@ -824,11 +836,6 @@ kern_setgroups(struct thread *td, u_int 
 	if (error)
 		goto fail;
 
-	/*
-	 * XXX A little bit lazy here.  We could test if anything has
-	 * changed before crcopy() and setting P_SUGID.
-	 */
-	crcopy(newcred, oldcred);
 	if (ngrp < 1) {
 		/*
 		 * setgroups(0, NULL) is a legitimate way of clearing the
@@ -838,8 +845,7 @@ kern_setgroups(struct thread *td, u_int 
 		 */
 		newcred->cr_ngroups = 1;
 	} else {
-		bcopy(groups, newcred->cr_groups, ngrp * sizeof(gid_t));
-		newcred->cr_ngroups = ngrp;
+		crsetgroups_locked(newcred, ngrp, groups);
 	}
 	setsugid(p);
 	p->p_ucred = newcred;
@@ -877,7 +883,7 @@ setreuid(register struct thread *td, str
 	euip = uifind(euid);
 	ruip = uifind(ruid);
 	PROC_LOCK(p);
-	oldcred = p->p_ucred;
+	oldcred = crcopysafe(p, newcred);
 
 #ifdef MAC
 	error = mac_cred_check_setreuid(oldcred, ruid, euid);
@@ -892,7 +898,6 @@ setreuid(register struct thread *td, str
 	    (error = priv_check_cred(oldcred, PRIV_CRED_SETREUID, 0)) != 0)
 		goto fail;
 
-	crcopy(newcred, oldcred);
 	if (euid != (uid_t)-1 && oldcred->cr_uid != euid) {
 		change_euid(newcred, euip);
 		setsugid(p);
@@ -942,7 +947,7 @@ setregid(register struct thread *td, str
 	AUDIT_ARG(rgid, rgid);
 	newcred = crget();
 	PROC_LOCK(p);
-	oldcred = p->p_ucred;
+	oldcred = crcopysafe(p, newcred);
 
 #ifdef MAC
 	error = mac_cred_check_setregid(oldcred, rgid, egid);
@@ -957,7 +962,6 @@ setregid(register struct thread *td, str
 	    (error = priv_check_cred(oldcred, PRIV_CRED_SETREGID, 0)) != 0)
 		goto fail;
 
-	crcopy(newcred, oldcred);
 	if (egid != (gid_t)-1 && oldcred->cr_groups[0] != egid) {
 		change_egid(newcred, egid);
 		setsugid(p);
@@ -1013,7 +1017,7 @@ setresuid(register struct thread *td, st
 	euip = uifind(euid);
 	ruip = uifind(ruid);
 	PROC_LOCK(p);
-	oldcred = p->p_ucred;
+	oldcred = crcopysafe(p, newcred);
 
 #ifdef MAC
 	error = mac_cred_check_setresuid(oldcred, ruid, euid, suid);
@@ -1033,7 +1037,6 @@ setresuid(register struct thread *td, st
 	    (error = priv_check_cred(oldcred, PRIV_CRED_SETRESUID, 0)) != 0)
 		goto fail;
 
-	crcopy(newcred, oldcred);
 	if (euid != (uid_t)-1 && oldcred->cr_uid != euid) {
 		change_euid(newcred, euip);
 		setsugid(p);
@@ -1090,7 +1093,7 @@ setresgid(register struct thread *td, st
 	AUDIT_ARG(sgid, sgid);
 	newcred = crget();
 	PROC_LOCK(p);
-	oldcred = p->p_ucred;
+	oldcred = crcopysafe(p, newcred);
 
 #ifdef MAC
 	error = mac_cred_check_setresgid(oldcred, rgid, egid, sgid);
@@ -1110,7 +1113,6 @@ setresgid(register struct thread *td, st
 	    (error = priv_check_cred(oldcred, PRIV_CRED_SETRESGID, 0)) != 0)
 		goto fail;
 
-	crcopy(newcred, oldcred);
 	if (egid != (gid_t)-1 && oldcred->cr_groups[0] != egid) {
 		change_egid(newcred, egid);
 		setsugid(p);
@@ -1780,6 +1782,7 @@ crget(void)
 #ifdef MAC
 	mac_cred_init(cr);
 #endif
+	crextend(cr, XU_NGROUPS);
 	return (cr);
 }
 
@@ -1829,6 +1832,7 @@ crfree(struct ucred *cr)
 #ifdef MAC
 		mac_cred_destroy(cr);
 #endif
+		free(cr->cr_groups, M_CRED);
 		free(cr, M_CRED);
 	}
 }
@@ -1854,6 +1858,7 @@ crcopy(struct ucred *dest, struct ucred 
 	bcopy(&src->cr_startcopy, &dest->cr_startcopy,
 	    (unsigned)((caddr_t)&src->cr_endcopy -
 		(caddr_t)&src->cr_startcopy));
+	crsetgroups(dest, src->cr_ngroups, src->cr_groups);
 	uihold(dest->cr_uidinfo);
 	uihold(dest->cr_ruidinfo);
 	prison_hold(dest->cr_prison);
@@ -1888,12 +1893,16 @@ crdup(struct ucred *cr)
 void
 cru2x(struct ucred *cr, struct xucred *xcr)
 {
+	int ngroups;
 
 	bzero(xcr, sizeof(*xcr));
 	xcr->cr_version = XUCRED_VERSION;
 	xcr->cr_uid = cr->cr_uid;
-	xcr->cr_ngroups = cr->cr_ngroups;
-	bcopy(cr->cr_groups, xcr->cr_groups, sizeof(cr->cr_groups));
+
+	ngroups = MIN(cr->cr_ngroups, XU_NGROUPS);
+	xcr->cr_ngroups = ngroups;
+	bcopy(cr->cr_groups, xcr->cr_groups,
+	    ngroups * sizeof(*cr->cr_groups));
 }
 
 /*
@@ -1915,6 +1924,97 @@ cred_update_thread(struct thread *td)
 		crfree(cred);
 }
 
+struct ucred *
+crcopysafe(struct proc *p, struct ucred *cr)
+{
+	struct ucred *oldcred;
+	int groups;
+
+	PROC_LOCK_ASSERT(p, MA_OWNED);
+
+	oldcred = p->p_ucred;
+	while (cr->cr_agroups < oldcred->cr_agroups) {
+		groups = oldcred->cr_agroups;
+		PROC_UNLOCK(p);
+		crextend(cr, groups);
+		PROC_LOCK(p);
+		oldcred = p->p_ucred;
+	}
+	crcopy(cr, oldcred);
+
+	return (oldcred);
+}
+
+/*
+ * Extend the passed in credential to hold n items.
+ */
+static void
+crextend(struct ucred *cr, int n)
+{
+	int cnt;
+
+	/* Truncate? */
+	if (n <= cr->cr_agroups)
+		return;
+
+	/*
+	 * We extend by 2 each time since we're using a power of two
+	 * allocator until we need enough groups to fill a page.
+	 * Once we're allocating multiple pages, only allocate as many
+	 * as we actually need.  The case of processes needing a
+	 * non-power of two number of pages seems more likely than
+	 * a real world process that adds thousands of groups one at a
+	 * time.
+	 */
+	if ( n < PAGE_SIZE / sizeof(gid_t) ) {
+		if (cr->cr_agroups == 0)
+			cnt = MINALLOCSIZE / sizeof(gid_t);
+		else
+			cnt = cr->cr_agroups * 2;
+
+		while (cnt < n)
+			cnt *= 2;
+	} else
+		cnt = roundup2(n, PAGE_SIZE / sizeof(gid_t));
+
+	/* Free the old array. */
+	if (cr->cr_groups)
+		free(cr->cr_groups, M_CRED);
+
+	cr->cr_groups = malloc(cnt * sizeof(gid_t), M_CRED, M_WAITOK | M_ZERO);
+	cr->cr_agroups = cnt;
+}
+
+/*
+ * Copy groups in to a credential, preserving any necessicary invariants
+ * (i.e. sorting in the future).  crextend() must have been called
+ * before hand to ensure sufficient space is available.  If 
+ */
+static void
+crsetgroups_locked(struct ucred *cr, int ngrp, gid_t *groups)
+{
+	
+	KASSERT(cr->cr_agroups >= ngrp, ("cr_ngroups is too small"));
+
+	bcopy(groups, cr->cr_groups, ngrp * sizeof(gid_t));
+	cr->cr_ngroups = ngrp;
+}
+
+/*
+ * Copy groups in to a credential after expanding it if required.
+ * Truncate the list to NGROUPS if it is too large.
+ */
+void
+crsetgroups(struct ucred *cr, int ngrp, gid_t *groups)
+{
+
+	if (ngrp > NGROUPS)
+		ngrp = NGROUPS;
+
+	crextend(cr, ngrp);
+	crsetgroups_locked(cr, ngrp, groups);
+}
+
 /*
  * Get login name, if available.
  */

Modified: head/sys/kern/vfs_export.c
==============================================================================
--- head/sys/kern/vfs_export.c	Fri Jun 19 17:07:38 2009	(r194497)
+++ head/sys/kern/vfs_export.c	Fri Jun 19 17:10:35 2009	(r194498)
@@ -120,9 +120,8 @@ vfs_hang_addrlist(struct mount *mp, stru
 		np->netc_exflags = argp->ex_flags;
 		np->netc_anon = crget();
 		np->netc_anon->cr_uid = argp->ex_anon.cr_uid;
-		np->netc_anon->cr_ngroups = argp->ex_anon.cr_ngroups;
-		bcopy(argp->ex_anon.cr_groups, np->netc_anon->cr_groups,
-		    sizeof(np->netc_anon->cr_groups));
+		crsetgroups(np->netc_anon, argp->ex_anon.cr_ngroups,
+		    argp->ex_anon.cr_groups);
 		np->netc_numsecflavors = argp->ex_numsecflavors;
 		bcopy(argp->ex_secflavors, np->netc_secflavors,
 		    sizeof(np->netc_secflavors));
@@ -205,9 +204,8 @@ vfs_hang_addrlist(struct mount *mp, stru
 	np->netc_exflags = argp->ex_flags;
 	np->netc_anon = crget();
 	np->netc_anon->cr_uid = argp->ex_anon.cr_uid;
-	np->netc_anon->cr_ngroups = argp->ex_anon.cr_ngroups;
-	bcopy(argp->ex_anon.cr_groups, np->netc_anon->cr_groups,
-	    sizeof(np->netc_anon->cr_groups));
+	crsetgroups(np->netc_anon, argp->ex_anon.cr_ngroups,
+	    np->netc_anon->cr_groups);
 	np->netc_numsecflavors = argp->ex_numsecflavors;
 	bcopy(argp->ex_secflavors, np->netc_secflavors,
 	    sizeof(np->netc_secflavors));

Modified: head/sys/netinet/ipfw/ip_fw2.c
==============================================================================
--- head/sys/netinet/ipfw/ip_fw2.c	Fri Jun 19 17:07:38 2009	(r194497)
+++ head/sys/netinet/ipfw/ip_fw2.c	Fri Jun 19 17:10:35 2009	(r194498)
@@ -135,19 +135,6 @@ static uma_zone_t ipfw_dyn_rule_zone;
 struct ip_fw *ip_fw_default_rule;
 
 /*
- * Data structure to cache our ucred related
- * information. This structure only gets used if
- * the user specified UID/GID based constraints in
- * a firewall rule.
- */
-struct ip_fw_ugid {
-	gid_t		fw_groups[NGROUPS];
-	int		fw_ngroups;
-	uid_t		fw_uid;
-	int		fw_prid;
-};
-
-/*
  * list of rules for layer 3
  */
 #ifdef VIMAGE_GLOBALS
@@ -2009,22 +1996,10 @@ dump_table(struct ip_fw_chain *ch, ipfw_
 	return (0);
 }
 
-static void
-fill_ugid_cache(struct inpcb *inp, struct ip_fw_ugid *ugp)
-{
-	struct ucred *cr;
-
-	cr = inp->inp_cred;
-	ugp->fw_prid = jailed(cr) ? cr->cr_prison->pr_id : -1;
-	ugp->fw_uid = cr->cr_uid;
-	ugp->fw_ngroups = cr->cr_ngroups;
-	bcopy(cr->cr_groups, ugp->fw_groups, sizeof(ugp->fw_groups));
-}
-
 static int
 check_uidgid(ipfw_insn_u32 *insn, int proto, struct ifnet *oif,
     struct in_addr dst_ip, u_int16_t dst_port, struct in_addr src_ip,
-    u_int16_t src_port, struct ip_fw_ugid *ugp, int *ugid_lookupp,
+    u_int16_t src_port, struct ucred **uc, int *ugid_lookupp,
     struct inpcb *inp)
 {
 	INIT_VNET_INET(curvnet);
@@ -2032,7 +2007,6 @@ check_uidgid(ipfw_insn_u32 *insn, int pr
 	int wildcard;
 	struct inpcb *pcb;
 	int match;
-	gid_t *gp;
 
 	/*
 	 * Check to see if the UDP or TCP stack supplied us with
@@ -2042,7 +2016,7 @@ check_uidgid(ipfw_insn_u32 *insn, int pr
 	if (inp && *ugid_lookupp == 0) {
 		INP_LOCK_ASSERT(inp);
 		if (inp->inp_socket != NULL) {
-			fill_ugid_cache(inp, ugp);
+			*uc = crhold(inp->inp_cred);
 			*ugid_lookupp = 1;
 		} else
 			*ugid_lookupp = -1;
@@ -2075,7 +2049,7 @@ check_uidgid(ipfw_insn_u32 *insn, int pr
 				dst_ip, htons(dst_port),
 				wildcard, NULL);
 		if (pcb != NULL) {
-			fill_ugid_cache(pcb, ugp);
+			*uc = crhold(inp->inp_cred);
 			*ugid_lookupp = 1;
 		}
 		INP_INFO_RUNLOCK(pi);
@@ -2091,16 +2065,11 @@ check_uidgid(ipfw_insn_u32 *insn, int pr
 		}
 	} 
 	if (insn->o.opcode == O_UID)
-		match = (ugp->fw_uid == (uid_t)insn->d[0]);
-	else if (insn->o.opcode == O_GID) {
-		for (gp = ugp->fw_groups;
-			gp < &ugp->fw_groups[ugp->fw_ngroups]; gp++)
-			if (*gp == (gid_t)insn->d[0]) {
-				match = 1;
-				break;
-			}
-	} else if (insn->o.opcode == O_JAIL)
-		match = (ugp->fw_prid == (int)insn->d[0]);
+		match = ((*uc)->cr_uid == (uid_t)insn->d[0]);
+	else if (insn->o.opcode == O_GID)
+		match = groupmember((gid_t)insn->d[0], *uc);
+	else if (insn->o.opcode == O_JAIL)
+		match = ((*uc)->cr_prison->pr_id == (int)insn->d[0]);
 	return match;
 }
 
@@ -2178,8 +2147,8 @@ ipfw_chk(struct ip_fw_args *args)

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-all mailing list