From owner-svn-src-all@FreeBSD.ORG Fri Jun 19 17:10:36 2009 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 85F7B106564A; Fri, 19 Jun 2009 17:10:36 +0000 (UTC) (envelope-from brooks@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 6FBE98FC08; Fri, 19 Jun 2009 17:10:36 +0000 (UTC) (envelope-from brooks@FreeBSD.org) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id n5JHAawe049528; Fri, 19 Jun 2009 17:10:36 GMT (envelope-from brooks@svn.freebsd.org) Received: (from brooks@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id n5JHAaql049507; Fri, 19 Jun 2009 17:10:36 GMT (envelope-from brooks@svn.freebsd.org) Message-Id: <200906191710.n5JHAaql049507@svn.freebsd.org> From: Brooks Davis Date: Fri, 19 Jun 2009 17:10:36 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: 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... X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 19 Jun 2009 17:10:37 -0000 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 ***