Index: share/man/man9/ucred.9 =================================================================== --- share/man/man9/ucred.9 (revision 307332) +++ share/man/man9/ucred.9 (working copy) @@ -32,20 +32,29 @@ .Sh NAME .Nm ucred , .Nm crget , +.Nm ncrget , +.Nm crextend , .Nm crhold , .Nm crfree , .Nm crshared , .Nm crcopy , +.Nm crcopysafe , .Nm crdup , +.Nm crsetgroups , +.Nm crsetgroups_s , .Nm cru2x , -.Nm cred_update_thread +.Nm cred_update_thread , .Nd "functions related to user credentials" .Sh SYNOPSIS .In sys/param.h .In sys/ucred.h .Ft "struct ucred *" -.Fn crget void +.Fn crget "void" .Ft "struct ucred *" +.Fn ncrget "int n" +.Ft void +.Fn crextend "struct ucred *cr, int n" +.Ft "struct ucred *" .Fn crhold "struct ucred *cr" .Ft void .Fn crfree "struct ucred *cr" @@ -60,6 +69,8 @@ .Ft void .Fn crsetgroups "struct ucred *cr" "int ngrp" "gid_t *groups" .Ft void +.Fn crsetgroups_s "struct ucred *cr" "int ngrp" "gid_t *groups" "crgrp_order srt" +.Ft void .Fn cru2x "struct ucred *cr" "struct xucred *xcr" .Ft void .Fn cred_update_thread "struct thread *td" @@ -72,11 +83,30 @@ .Pp The .Fn crget -function allocates memory -for a new structure, sets its reference count to 1, and -initializes its lock. +function is a wrapper around the +.Fn ncrget +function. Use this when you don't know how many groups you will need +storage for. This will call +.Fn ncrget +asking for the default storage size to be allocated for groups. .Pp The +.Fn ncrget +function allocates memory for a new structure, sets its reference +count to 1 and initializes it's lock. A buffer is allocated +for storing groups and it is sized to hold +.Fa n +items. +Values less than 16 result in the minimum group buffer size +being allocated. +.Pp +The +.Fn crextend +function increases the buffer size for holding groups to to +.Vt n +items. Previously stored groups will be lost after this operation. +.Pp +The .Fn crhold function increases the reference count on the credential. .Pp @@ -124,6 +154,12 @@ .Pp The .Fn crsetgroups +function is a wrapper for calling the +.Fn crsetgroups_s +function. Calling this function always ensures that the copied groups are sorted. +.Pp +The +.Fn crsetgroups_s function sets the .Va cr_groups and @@ -130,7 +166,16 @@ .Va cr_ngroups variables and allocates space as needed. It also truncates the group list to the current maximum number of -groups. +groups. The +.Fa srt +parameter indicates whether the supplied groups are sorted or not - it takes the following +values: +.Va CRGRP_UNSORTED +and +.Va CRGRP_SORTED . +Using a value of +.Va CRGRP_UNSORTED +will ensure that the groups are sorted as they are copied. No other mechanism should be used to modify the .Va cr_groups array except for updating the primary group via assignment to Index: sys/compat/linux/linux_misc.c =================================================================== --- sys/compat/linux/linux_misc.c (revision 307332) +++ sys/compat/linux/linux_misc.c (working copy) @@ -1316,8 +1316,7 @@ error = copyin(args->grouplist, linux_gidset, ngrp * sizeof(l_gid_t)); if (error) goto out; - newcred = crget(); - crextend(newcred, ngrp + 1); + newcred = ncrget(ngrp + 1); p = td->td_proc; PROC_LOCK(p); oldcred = p->p_ucred; Index: sys/compat/linux/linux_uid16.c =================================================================== --- sys/compat/linux/linux_uid16.c (revision 307332) +++ sys/compat/linux/linux_uid16.c (working copy) @@ -179,8 +179,8 @@ free(linux_gidset, M_LINUX); return (error); } - newcred = crget(); p = td->td_proc; + newcred = ncrget(td->td_ucred->cr_agroups); PROC_LOCK(p); oldcred = crcopysafe(p, newcred); Index: sys/fs/nfs/nfs_commonport.c =================================================================== --- sys/fs/nfs/nfs_commonport.c (revision 307332) +++ sys/fs/nfs/nfs_commonport.c (working copy) @@ -243,7 +243,7 @@ KASSERT(nfscr->nfsc_ngroups >= 0, ("newnfs_copycred: negative nfsc_ngroups")); cr->cr_uid = nfscr->nfsc_uid; - crsetgroups(cr, nfscr->nfsc_ngroups, nfscr->nfsc_groups); + crsetgroups_s(cr, nfscr->nfsc_ngroups, nfscr->nfsc_groups, CRGRP_UNSORTED); } /* Index: sys/fs/nfs/nfs_commonsubs.c =================================================================== --- sys/fs/nfs/nfs_commonsubs.c (revision 307332) +++ sys/fs/nfs/nfs_commonsubs.c (working copy) @@ -3290,9 +3290,9 @@ * Create a credential just like svc_getcred(), * but using the group list provided. */ - cr = crget(); + cr = ncrget(nidp->nid_ngroup); cr->cr_uid = cr->cr_ruid = cr->cr_svuid = nidp->nid_uid; - crsetgroups(cr, nidp->nid_ngroup, grps); + crsetgroups_s(cr, nidp->nid_ngroup, grps, CRGRP_UNSORTED); cr->cr_rgid = cr->cr_svgid = cr->cr_groups[0]; cr->cr_prison = &prison0; prison_hold(cr->cr_prison); Index: sys/fs/nfsserver/nfs_nfsdport.c =================================================================== --- sys/fs/nfsserver/nfs_nfsdport.c (revision 307332) +++ sys/fs/nfsserver/nfs_nfsdport.c (working copy) @@ -2670,8 +2670,8 @@ (nd->nd_flag & ND_AUTHNONE) != 0) { nd->nd_cred->cr_uid = credanon->cr_uid; nd->nd_cred->cr_gid = credanon->cr_gid; - crsetgroups(nd->nd_cred, credanon->cr_ngroups, - credanon->cr_groups); + crsetgroups_s(nd->nd_cred, credanon->cr_ngroups, + credanon->cr_groups, CRGRP_SORTED); } else if ((nd->nd_flag & ND_GSS) == 0) { /* * If using AUTH_SYS, call nfsrv_getgrpscred() to see Index: sys/kern/init_main.c =================================================================== --- sys/kern/init_main.c (revision 307332) +++ sys/kern/init_main.c (working copy) @@ -517,7 +517,7 @@ callout_init(&td->td_slpcallout, 1); /* Create credentials. */ - newcred = crget(); + newcred = ncrget(1); newcred->cr_ngroups = 1; /* group 0 */ newcred->cr_uidinfo = uifind(0); newcred->cr_ruidinfo = uifind(0); @@ -832,6 +832,7 @@ struct ucred *newcred, *oldcred; struct thread *td; int error; + int ng; bzero(&fr, sizeof(fr)); fr.fr_flags = RFFDG | RFPROC | RFSTOPPED; @@ -841,7 +842,10 @@ panic("cannot fork init: %d\n", error); KASSERT(initproc->p_pid == 1, ("create_init: initproc->p_pid != 1")); /* divorce init's credentials from the kernel's */ - newcred = crget(); + PROC_LOCK(initproc); + ng = initproc->p_ucred->cr_agroups; + PROC_UNLOCK(initproc); + newcred = ncrget(ng); sx_xlock(&proctree_lock); PROC_LOCK(initproc); initproc->p_flag |= P_SYSTEM | P_INMEM; Index: sys/kern/kern_jail.c =================================================================== --- sys/kern/kern_jail.c (revision 307332) +++ sys/kern/kern_jail.c (working copy) @@ -2403,7 +2403,7 @@ if ((error = pwd_chroot(td, pr->pr_root))) goto e_revert_osd; - newcred = crget(); + newcred = ncrget(td->td_ucred->cr_agroups); PROC_LOCK(p); oldcred = crcopysafe(p, newcred); newcred->cr_prison = pr; Index: sys/kern/kern_loginclass.c =================================================================== --- sys/kern/kern_loginclass.c (revision 307332) +++ sys/kern/kern_loginclass.c (working copy) @@ -216,7 +216,7 @@ newlc = loginclass_find(lcname); if (newlc == NULL) return (EINVAL); - newcred = crget(); + newcred = ncrget(td->td_ucred->cr_agroups); PROC_LOCK(p); oldcred = crcopysafe(p, newcred); Index: sys/kern/kern_prot.c =================================================================== --- sys/kern/kern_prot.c (revision 307332) +++ sys/kern/kern_prot.c (working copy) @@ -84,7 +84,7 @@ SYSCTL_NODE(_security, OID_AUTO, bsd, CTLFLAG_RW, 0, "BSD security policy"); static void crsetgroups_locked(struct ucred *cr, int ngrp, - gid_t *groups); + gid_t *groups, crgrp_order srt); #ifndef _SYS_SYSPROTO_H_ struct getpid_args { @@ -492,7 +492,7 @@ uid = uap->uid; AUDIT_ARG_UID(uid); - newcred = crget(); + newcred = ncrget(td->td_ucred->cr_agroups); uip = uifind(uid); PROC_LOCK(p); /* @@ -606,7 +606,7 @@ euid = uap->euid; AUDIT_ARG_EUID(euid); - newcred = crget(); + newcred = ncrget(td->td_ucred->cr_agroups); euip = uifind(euid); PROC_LOCK(p); /* @@ -661,7 +661,7 @@ gid = uap->gid; AUDIT_ARG_GID(gid); - newcred = crget(); + newcred = ncrget(td->td_ucred->cr_agroups); PROC_LOCK(p); oldcred = crcopysafe(p, newcred); @@ -759,7 +759,7 @@ egid = uap->egid; AUDIT_ARG_EGID(egid); - newcred = crget(); + newcred = ncrget(td->td_ucred->cr_agroups); PROC_LOCK(p); oldcred = crcopysafe(p, newcred); @@ -831,8 +831,7 @@ MPASS(ngrp <= ngroups_max + 1); AUDIT_ARG_GROUPSET(groups, ngrp); - newcred = crget(); - crextend(newcred, ngrp); + newcred = ncrget(ngrp); PROC_LOCK(p); oldcred = crcopysafe(p, newcred); @@ -855,7 +854,7 @@ */ newcred->cr_ngroups = 1; } else { - crsetgroups_locked(newcred, ngrp, groups); + crsetgroups_locked(newcred, ngrp, groups, CRGRP_UNSORTED); } setsugid(p); proc_set_cred(p, newcred); @@ -889,7 +888,7 @@ ruid = uap->ruid; AUDIT_ARG_EUID(euid); AUDIT_ARG_RUID(ruid); - newcred = crget(); + newcred = ncrget(td->td_ucred->cr_agroups); euip = uifind(euid); ruip = uifind(ruid); PROC_LOCK(p); @@ -958,7 +957,7 @@ rgid = uap->rgid; AUDIT_ARG_EGID(egid); AUDIT_ARG_RGID(rgid); - newcred = crget(); + newcred = ncrget(td->td_ucred->cr_agroups); PROC_LOCK(p); oldcred = crcopysafe(p, newcred); @@ -1026,7 +1025,7 @@ AUDIT_ARG_EUID(euid); AUDIT_ARG_RUID(ruid); AUDIT_ARG_SUID(suid); - newcred = crget(); + newcred = ncrget(td->td_ucred->cr_agroups); euip = uifind(euid); ruip = uifind(ruid); PROC_LOCK(p); @@ -1107,7 +1106,7 @@ AUDIT_ARG_EGID(egid); AUDIT_ARG_RGID(rgid); AUDIT_ARG_SGID(sgid); - newcred = crget(); + newcred = ncrget(td->td_ucred->cr_agroups); PROC_LOCK(p); oldcred = crcopysafe(p, newcred); @@ -1775,11 +1774,24 @@ } /* - * Allocate a zeroed cred structure. + * Wrapper around ncrget(int). + * Use this function when you don't know how many groups to reserve space for + * and just want to receive the default - smallgroups. */ struct ucred * crget(void) { + /* Any value <= XU_NGROUPS is equivalent here. */ + return (ncrget(1)); +} + +/* + * Allocate a zeroed cred structure. + * If ngroups > cr_smallgroups, use it to extend the group buffer. + */ +struct ucred * +ncrget(int n) +{ register struct ucred *cr; cr = malloc(sizeof(*cr), M_CRED, M_WAITOK | M_ZERO); @@ -1790,9 +1802,12 @@ #ifdef MAC mac_cred_init(cr); #endif - cr->cr_groups = cr->cr_smallgroups; - cr->cr_agroups = - sizeof(cr->cr_smallgroups) / sizeof(cr->cr_smallgroups[0]); + if (n > sizeof (cr->cr_smallgroups) / sizeof (cr->cr_smallgroups[0])) + crextend (cr, n); + else { + cr->cr_groups = cr->cr_smallgroups; + cr->cr_agroups = sizeof(cr->cr_smallgroups) / sizeof(cr->cr_smallgroups[0]); + } return (cr); } @@ -1818,7 +1833,7 @@ KASSERT(cr->cr_ref != 0xdeadc0de, ("dangling reference to ucred")); if (refcount_release(&cr->cr_ref)) { /* - * Some callers of crget(), such as nfs_statfs(), + * Some callers of ncrget(), such as nfs_statfs(), * allocate a temporary credential, but don't * allocate a uidinfo structure. */ @@ -1856,7 +1871,7 @@ 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); + crsetgroups_s(dest, src->cr_ngroups, src->cr_groups, CRGRP_SORTED); uihold(dest->cr_uidinfo); uihold(dest->cr_ruidinfo); prison_hold(dest->cr_prison); @@ -1877,7 +1892,7 @@ { struct ucred *newcr; - newcr = crget(); + newcr = ncrget(cr->cr_agroups); crcopy(newcr, cr); return (newcr); } @@ -1971,6 +1986,9 @@ /* Truncate? */ if (n <= cr->cr_agroups) return; + /* No need to allocate more than we are willing to use? */ + if (n > ngroups_max + 1) + n = ngroups_max + 1; /* * We extend by 2 each time since we're using a power of two @@ -2007,7 +2025,7 @@ * space is available. */ static void -crsetgroups_locked(struct ucred *cr, int ngrp, gid_t *groups) +crsetgroups_locked(struct ucred *cr, int ngrp, gid_t *groups, crgrp_order srt) { int i; int j; @@ -2026,27 +2044,38 @@ * be replaced with shell sort like linux uses or possibly * heap sort. */ - for (i = 2; i < ngrp; i++) { - g = cr->cr_groups[i]; - for (j = i-1; j >= 1 && g < cr->cr_groups[j]; j--) - cr->cr_groups[j + 1] = cr->cr_groups[j]; - cr->cr_groups[j + 1] = g; - } + if (srt == CRGRP_UNSORTED) + for (i = 2; i < ngrp; i++) { + g = cr->cr_groups[i]; + for (j = i-1; j >= 1 && g < cr->cr_groups[j]; j--) + cr->cr_groups[j + 1] = cr->cr_groups[j]; + cr->cr_groups[j + 1] = g; + } } /* - * Copy groups in to a credential after expanding it if required. - * Truncate the list to (ngroups_max + 1) if it is too large. + * A wrapper around crsetgroups_s which adds an srt enum so that the + * sort algorithm in crsetgroups_locked can be avoided when the "groups" + * parameter is taken from a struct ucred - that means it must already be + * pre-sorted, right ? */ void crsetgroups(struct ucred *cr, int ngrp, gid_t *groups) { + crsetgroups_s (cr, ngrp, groups, CRGRP_UNSORTED); +} +/* + * Copy groups in to a credential after expanding it if required. + * Truncate the list to (ngroups_max + 1) if it is too large. + */ +void crsetgroups_s(struct ucred *cr, int ngrp, gid_t *groups, crgrp_order srt) +{ if (ngrp > ngroups_max + 1) ngrp = ngroups_max + 1; crextend(cr, ngrp); - crsetgroups_locked(cr, ngrp, groups); + crsetgroups_locked(cr, ngrp, groups, srt); } /* Index: sys/kern/sys_capability.c =================================================================== --- sys/kern/sys_capability.c (revision 307332) +++ sys/kern/sys_capability.c (working copy) @@ -103,8 +103,8 @@ if (IN_CAPABILITY_MODE(td)) return (0); - newcred = crget(); p = td->td_proc; + newcred = ncrget(td->td_ucred->cr_agroups); PROC_LOCK(p); oldcred = crcopysafe(p, newcred); newcred->cr_flags |= CRED_FLAG_CAPMODE; Index: sys/kern/vfs_export.c =================================================================== --- sys/kern/vfs_export.c (revision 307332) +++ sys/kern/vfs_export.c (working copy) @@ -129,10 +129,10 @@ } np = &nep->ne_defexported; np->netc_exflags = argp->ex_flags; - np->netc_anon = crget(); + np->netc_anon = ncrget(argp->ex_anon.cr_ngroups); np->netc_anon->cr_uid = argp->ex_anon.cr_uid; - crsetgroups(np->netc_anon, argp->ex_anon.cr_ngroups, - argp->ex_anon.cr_groups); + crsetgroups_s(np->netc_anon, argp->ex_anon.cr_ngroups, + argp->ex_anon.cr_groups, CRGRP_SORTED); np->netc_anon->cr_prison = &prison0; prison_hold(np->netc_anon->cr_prison); np->netc_numsecflavors = argp->ex_numsecflavors; @@ -208,10 +208,10 @@ goto out; } np->netc_exflags = argp->ex_flags; - np->netc_anon = crget(); + np->netc_anon = ncrget(argp->ex_anon.cr_ngroups); np->netc_anon->cr_uid = argp->ex_anon.cr_uid; - crsetgroups(np->netc_anon, argp->ex_anon.cr_ngroups, - argp->ex_anon.cr_groups); + crsetgroups_s(np->netc_anon, argp->ex_anon.cr_ngroups, + argp->ex_anon.cr_groups, CRGRP_SORTED); np->netc_anon->cr_prison = &prison0; prison_hold(np->netc_anon->cr_prison); np->netc_numsecflavors = argp->ex_numsecflavors; Index: sys/rpc/rpcsec_gss/svc_rpcsec_gss.c =================================================================== --- sys/rpc/rpcsec_gss/svc_rpcsec_gss.c (revision 307332) +++ sys/rpc/rpcsec_gss/svc_rpcsec_gss.c (working copy) @@ -445,10 +445,10 @@ } uc = &client->cl_ucred; - cr = client->cl_cred = crget(); + cr = client->cl_cred = ncrget(uc->gidlen); cr->cr_uid = cr->cr_ruid = cr->cr_svuid = uc->uid; cr->cr_rgid = cr->cr_svgid = uc->gid; - crsetgroups(cr, uc->gidlen, uc->gidlist); + crsetgroups_s(cr, uc->gidlen, uc->gidlist, CRGRP_UNSORTED); cr->cr_prison = &prison0; prison_hold(cr->cr_prison); *crp = crhold(cr); Index: sys/rpc/svc_auth.c =================================================================== --- sys/rpc/svc_auth.c (revision 307332) +++ sys/rpc/svc_auth.c (working copy) @@ -175,9 +175,9 @@ switch (flavor) { case AUTH_UNIX: xcr = (struct xucred *) rqst->rq_clntcred; - cr = crget(); + cr = ncrget(xcr->cr_ngroups); cr->cr_uid = cr->cr_ruid = cr->cr_svuid = xcr->cr_uid; - crsetgroups(cr, xcr->cr_ngroups, xcr->cr_groups); + crsetgroups_s(cr, xcr->cr_ngroups, xcr->cr_groups, CRGRP_UNSORTED); cr->cr_rgid = cr->cr_svgid = cr->cr_groups[0]; cr->cr_prison = &prison0; prison_hold(cr->cr_prison); Index: sys/security/audit/audit_syscalls.c =================================================================== --- sys/security/audit/audit_syscalls.c (revision 307332) +++ sys/security/audit/audit_syscalls.c (working copy) @@ -445,11 +445,9 @@ return (EINVAL); if (udata.au_aupinfo.ap_pid < 1) return (ESRCH); - newcred = crget(); - if ((tp = pfind(udata.au_aupinfo.ap_pid)) == NULL) { - crfree(newcred); + if ((tp = pfind(udata.au_aupinfo.ap_pid)) == NULL) return (ESRCH); - } + newcred = ncrget(td->td_ucred->cr_agroups); if ((error = p_cansee(td, tp)) != 0) { PROC_UNLOCK(tp); crfree(newcred); @@ -587,7 +585,7 @@ if (error) return (error); audit_arg_auid(id); - newcred = crget(); + newcred = ncrget(td->td_ucred->cr_agroups); PROC_LOCK(td->td_proc); oldcred = td->td_proc->p_ucred; crcopy(newcred, oldcred); @@ -652,7 +650,7 @@ if (error) return (error); audit_arg_auditinfo(&ai); - newcred = crget(); + newcred = ncrget(td->td_ucred->cr_agroups); PROC_LOCK(td->td_proc); oldcred = td->td_proc->p_ucred; crcopy(newcred, oldcred); @@ -715,7 +713,7 @@ if (aia.ai_termid.at_type != AU_IPv6 && aia.ai_termid.at_type != AU_IPv4) return (EINVAL); - newcred = crget(); + newcred = ncrget(td->td_ucred->cr_agroups); PROC_LOCK(td->td_proc); oldcred = td->td_proc->p_ucred; crcopy(newcred, oldcred); Index: sys/security/mac/mac_syscalls.c =================================================================== --- sys/security/mac/mac_syscalls.c (revision 307332) +++ sys/security/mac/mac_syscalls.c (working copy) @@ -192,9 +192,8 @@ if (error) goto out; - newcred = crget(); - p = td->td_proc; + newcred = ncrget(td->td_ucred->cr_agroups); PROC_LOCK(p); oldcred = p->p_ucred; Index: sys/security/mac_lomac/mac_lomac.c =================================================================== --- sys/security/mac_lomac/mac_lomac.c (revision 307332) +++ sys/security/mac_lomac/mac_lomac.c (working copy) @@ -2235,7 +2235,7 @@ if (subj->mac_lomac.ml_flags & MAC_LOMAC_FLAG_UPDATE) { dodrop = 0; mtx_unlock(&subj->mtx); - newcred = crget(); + newcred = ncrget(td->td_ucred->cr_agroups); /* * Prevent a lock order reversal in mac_proc_vm_revoke; * ideally, the other user of subj->mtx wouldn't be holding Index: sys/sys/ucred.h =================================================================== --- sys/sys/ucred.h (revision 307332) +++ sys/sys/ucred.h (working copy) @@ -96,6 +96,11 @@ struct proc; struct thread; +typedef enum { + CRGRP_UNSORTED = 0, + CRGRP_SORTED = 1 +} crgrp_order; + void change_egid(struct ucred *newcred, gid_t egid); void change_euid(struct ucred *newcred, struct uidinfo *euip); void change_rgid(struct ucred *newcred, gid_t rgid); @@ -110,9 +115,11 @@ struct ucred *proc_set_cred(struct proc *p, struct ucred *cr); void crfree(struct ucred *cr); struct ucred *crget(void); +struct ucred *ncrget(int n); struct ucred *crhold(struct ucred *cr); void cru2x(struct ucred *cr, struct xucred *xcr); void crsetgroups(struct ucred *cr, int n, gid_t *groups); +void crsetgroups_s(struct ucred *cr, int n, gid_t *groups, crgrp_order srt); int groupmember(gid_t gid, struct ucred *cred); #endif /* _KERNEL */