mirror of
https://github.com/opnsense/src.git
synced 2026-06-11 01:30:30 -04:00
kern: start tracking cr_gid outside of cr_groups[]
This is the (mostly) kernel side of de-conflating cr_gid and the supplemental groups. The pre-existing behavior for getgroups() and setgroups() is retained to keep the user <-> kernel boundary functionally the same while we audit use of these syscalls, but we can remove a lot of the internal special-casing just by reorganizing ucred like this. struct xucred has been altered because the cr_gid macro becomes problematic if ucred has a real cr_gid member but xucred does not. Most notably, they both also have cr_groups[] members, so the definition means that we could easily have situations where we end up using the first supplemental group as the egid in some places. We really can't change the ABI of xucred, so instead we alias the first member to the `cr_gid` name and maintain the status quo. This also fixes the Linux setgroups(2)/getgroups(2) implementation to more cleanly preserve the group set, now that we don't need to special case cr_groups[0]. __FreeBSD_version bumped for the `struct ucred` ABI break. For relnotes: downstreams and out-of-tree modules absolutely must fix any references to cr_groups[0] in their code. These are almost exclusively incorrect in the new world, and cr_gid should be used instead. There is a cr_gid macro available in earlier FreeBSD versions that can be used to avoid having version-dependant conditionals to refer to the effective group id. Surrounding code may need adjusted if it peels off the first element of cr_groups and uses the others as the supplemental groups, since the supplemental groups start at cr_groups[0] now if &cr_groups[0] != &cr_gid. Relnotes: yes (see last paragraph) Co-authored-by: olce Differential Revision: https://reviews.freebsd.org/D51489
This commit is contained in:
parent
763a097c15
commit
be1f7435ef
12 changed files with 164 additions and 179 deletions
|
|
@ -24,7 +24,7 @@
|
|||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
.\" DAMAGE.
|
||||
.\"
|
||||
.Dd January 23, 2019
|
||||
.Dd July 29, 2025
|
||||
.Dt UCRED 9
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
|
@ -119,8 +119,7 @@ It also truncates the group list to the current maximum number of
|
|||
groups.
|
||||
No other mechanism should be used to modify the
|
||||
.Va cr_groups
|
||||
array except for updating the primary group via assignment to
|
||||
.Va cr_groups[0] .
|
||||
array.
|
||||
.Pp
|
||||
The
|
||||
.Fn cru2x
|
||||
|
|
|
|||
|
|
@ -1030,47 +1030,32 @@ linux_setgroups(struct thread *td, struct linux_setgroups_args *args)
|
|||
{
|
||||
struct ucred *newcred, *oldcred;
|
||||
l_gid_t *linux_gidset;
|
||||
gid_t *bsd_gidset;
|
||||
int ngrp, error;
|
||||
struct proc *p;
|
||||
|
||||
ngrp = args->gidsetsize;
|
||||
if (ngrp < 0 || ngrp >= ngroups_max + 1)
|
||||
if (ngrp < 0 || ngrp >= ngroups_max)
|
||||
return (EINVAL);
|
||||
linux_gidset = malloc(ngrp * sizeof(*linux_gidset), M_LINUX, M_WAITOK);
|
||||
error = copyin(args->grouplist, linux_gidset, ngrp * sizeof(l_gid_t));
|
||||
if (error)
|
||||
goto out;
|
||||
newcred = crget();
|
||||
crextend(newcred, ngrp + 1);
|
||||
crextend(newcred, ngrp);
|
||||
p = td->td_proc;
|
||||
PROC_LOCK(p);
|
||||
oldcred = p->p_ucred;
|
||||
crcopy(newcred, oldcred);
|
||||
|
||||
/*
|
||||
* cr_groups[0] holds egid. Setting the whole set from
|
||||
* the supplied set will cause egid to be changed too.
|
||||
* Keep cr_groups[0] unchanged to prevent that.
|
||||
*/
|
||||
|
||||
if ((error = priv_check_cred(oldcred, PRIV_CRED_SETGROUPS)) != 0) {
|
||||
PROC_UNLOCK(p);
|
||||
crfree(newcred);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ngrp > 0) {
|
||||
newcred->cr_ngroups = ngrp + 1;
|
||||
|
||||
bsd_gidset = newcred->cr_groups;
|
||||
ngrp--;
|
||||
while (ngrp >= 0) {
|
||||
bsd_gidset[ngrp + 1] = linux_gidset[ngrp];
|
||||
ngrp--;
|
||||
}
|
||||
} else
|
||||
newcred->cr_ngroups = 1;
|
||||
newcred->cr_ngroups = ngrp;
|
||||
for (int i = 0; i < ngrp; i++)
|
||||
newcred->cr_groups[i] = linux_gidset[i];
|
||||
|
||||
setsugid(p);
|
||||
proc_set_cred(p, newcred);
|
||||
|
|
@ -1092,13 +1077,7 @@ linux_getgroups(struct thread *td, struct linux_getgroups_args *args)
|
|||
|
||||
cred = td->td_ucred;
|
||||
bsd_gidset = cred->cr_groups;
|
||||
bsd_gidsetsz = cred->cr_ngroups - 1;
|
||||
|
||||
/*
|
||||
* cr_groups[0] holds egid. Returning the whole set
|
||||
* here will cause a duplicate. Exclude cr_groups[0]
|
||||
* to prevent that.
|
||||
*/
|
||||
bsd_gidsetsz = cred->cr_ngroups;
|
||||
|
||||
if ((ngrp = args->gidsetsize) == 0) {
|
||||
td->td_retval[0] = bsd_gidsetsz;
|
||||
|
|
@ -1112,7 +1091,7 @@ linux_getgroups(struct thread *td, struct linux_getgroups_args *args)
|
|||
linux_gidset = malloc(bsd_gidsetsz * sizeof(*linux_gidset),
|
||||
M_LINUX, M_WAITOK);
|
||||
while (ngrp < bsd_gidsetsz) {
|
||||
linux_gidset[ngrp] = bsd_gidset[ngrp + 1];
|
||||
linux_gidset[ngrp] = bsd_gidset[ngrp];
|
||||
ngrp++;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -87,12 +87,11 @@ linux_setgroups16(struct thread *td, struct linux_setgroups16_args *args)
|
|||
{
|
||||
struct ucred *newcred, *oldcred;
|
||||
l_gid16_t *linux_gidset;
|
||||
gid_t *bsd_gidset;
|
||||
int ngrp, error;
|
||||
struct proc *p;
|
||||
|
||||
ngrp = args->gidsetsize;
|
||||
if (ngrp < 0 || ngrp >= ngroups_max + 1)
|
||||
if (ngrp < 0 || ngrp >= ngroups_max)
|
||||
return (EINVAL);
|
||||
linux_gidset = malloc(ngrp * sizeof(*linux_gidset), M_LINUX, M_WAITOK);
|
||||
error = copyin(args->gidset, linux_gidset, ngrp * sizeof(l_gid16_t));
|
||||
|
|
@ -106,12 +105,6 @@ linux_setgroups16(struct thread *td, struct linux_setgroups16_args *args)
|
|||
PROC_LOCK(p);
|
||||
oldcred = crcopysafe(p, newcred);
|
||||
|
||||
/*
|
||||
* cr_groups[0] holds egid. Setting the whole set from
|
||||
* the supplied set will cause egid to be changed too.
|
||||
* Keep cr_groups[0] unchanged to prevent that.
|
||||
*/
|
||||
|
||||
if ((error = priv_check_cred(oldcred, PRIV_CRED_SETGROUPS)) != 0) {
|
||||
PROC_UNLOCK(p);
|
||||
crfree(newcred);
|
||||
|
|
@ -121,18 +114,9 @@ linux_setgroups16(struct thread *td, struct linux_setgroups16_args *args)
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (ngrp > 0) {
|
||||
newcred->cr_ngroups = ngrp + 1;
|
||||
|
||||
bsd_gidset = newcred->cr_groups;
|
||||
ngrp--;
|
||||
while (ngrp >= 0) {
|
||||
bsd_gidset[ngrp + 1] = linux_gidset[ngrp];
|
||||
ngrp--;
|
||||
}
|
||||
}
|
||||
else
|
||||
newcred->cr_ngroups = 1;
|
||||
newcred->cr_ngroups = ngrp;
|
||||
for (int i = 0; i < ngrp; i++)
|
||||
newcred->cr_groups[i] = linux_gidset[i];
|
||||
|
||||
setsugid(td->td_proc);
|
||||
proc_set_cred(p, newcred);
|
||||
|
|
@ -155,13 +139,7 @@ linux_getgroups16(struct thread *td, struct linux_getgroups16_args *args)
|
|||
|
||||
cred = td->td_ucred;
|
||||
bsd_gidset = cred->cr_groups;
|
||||
bsd_gidsetsz = cred->cr_ngroups - 1;
|
||||
|
||||
/*
|
||||
* cr_groups[0] holds egid. Returning the whole set
|
||||
* here will cause a duplicate. Exclude cr_groups[0]
|
||||
* to prevent that.
|
||||
*/
|
||||
bsd_gidsetsz = cred->cr_ngroups;
|
||||
|
||||
if ((ngrp = args->gidsetsize) == 0) {
|
||||
td->td_retval[0] = bsd_gidsetsz;
|
||||
|
|
@ -175,7 +153,7 @@ linux_getgroups16(struct thread *td, struct linux_getgroups16_args *args)
|
|||
linux_gidset = malloc(bsd_gidsetsz * sizeof(*linux_gidset),
|
||||
M_LINUX, M_WAITOK);
|
||||
while (ngrp < bsd_gidsetsz) {
|
||||
linux_gidset[ngrp] = bsd_gidset[ngrp + 1];
|
||||
linux_gidset[ngrp] = bsd_gidset[ngrp];
|
||||
ngrp++;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -380,8 +380,7 @@ newnfs_setroot(struct ucred *cred)
|
|||
|
||||
cred->cr_uid = 0;
|
||||
cred->cr_gid = 0;
|
||||
/* XXXKE Fix this if cr_gid gets separated out. */
|
||||
cred->cr_ngroups = 1;
|
||||
cred->cr_ngroups = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -6934,8 +6934,7 @@ nfscl_dofflayoutio(vnode_t vp, struct uio *uiop, int *iomode, int *must_commit,
|
|||
tcred = NFSNEWCRED(cred);
|
||||
tcred->cr_uid = flp->nfsfl_ffm[mirror].user;
|
||||
tcred->cr_gid = flp->nfsfl_ffm[mirror].group;
|
||||
/* XXXKE Fix this if cr_gid gets separated out. */
|
||||
tcred->cr_ngroups = 1;
|
||||
tcred->cr_ngroups = 0;
|
||||
} else
|
||||
tcred = cred;
|
||||
if (rwflag == NFSV4OPEN_ACCESSREAD)
|
||||
|
|
|
|||
|
|
@ -3463,6 +3463,7 @@ nfsd_excred(struct nfsrv_descript *nd, struct nfsexstuff *exp,
|
|||
NFSVNO_EXPORTANON(exp) ||
|
||||
(nd->nd_flag & ND_AUTHNONE) != 0) {
|
||||
nd->nd_cred->cr_uid = credanon->cr_uid;
|
||||
nd->nd_cred->cr_gid = credanon->cr_gid;
|
||||
/*
|
||||
* 'credanon' is already a 'struct ucred' that was built
|
||||
* internally with calls to crsetgroups_fallback(), so
|
||||
|
|
|
|||
|
|
@ -99,12 +99,11 @@ static inline void
|
|||
groups_check_positive_len(int ngrp)
|
||||
{
|
||||
MPASS2(ngrp >= 0, "negative number of groups");
|
||||
MPASS2(ngrp != 0, "at least one group expected (effective GID)");
|
||||
}
|
||||
static inline void
|
||||
groups_check_max_len(int ngrp)
|
||||
{
|
||||
MPASS2(ngrp <= ngroups_max + 1, "too many groups");
|
||||
MPASS2(ngrp <= ngroups_max, "too many supplementary groups");
|
||||
}
|
||||
|
||||
static void groups_normalize(int *ngrp, gid_t *groups);
|
||||
|
|
@ -321,10 +320,17 @@ int
|
|||
sys_getgroups(struct thread *td, struct getgroups_args *uap)
|
||||
{
|
||||
struct ucred *cred;
|
||||
gid_t *ugidset;
|
||||
int ngrp, error;
|
||||
|
||||
cred = td->td_ucred;
|
||||
ngrp = cred->cr_ngroups;
|
||||
|
||||
/*
|
||||
* cr_gid has been moved out of cr_groups, but we'll continue exporting
|
||||
* the egid as groups[0] for the time being until we audit userland for
|
||||
* any surprises.
|
||||
*/
|
||||
ngrp = cred->cr_ngroups + 1;
|
||||
|
||||
if (uap->gidsetsize == 0) {
|
||||
error = 0;
|
||||
|
|
@ -333,7 +339,14 @@ sys_getgroups(struct thread *td, struct getgroups_args *uap)
|
|||
if (uap->gidsetsize < ngrp)
|
||||
return (EINVAL);
|
||||
|
||||
error = copyout(cred->cr_groups, uap->gidset, ngrp * sizeof(gid_t));
|
||||
ugidset = uap->gidset;
|
||||
error = copyout(&cred->cr_gid, ugidset, sizeof(*ugidset));
|
||||
if (error != 0)
|
||||
goto out;
|
||||
|
||||
if (ngrp > 1)
|
||||
error = copyout(cred->cr_groups, ugidset + 1,
|
||||
(ngrp - 1) * sizeof(*ugidset));
|
||||
out:
|
||||
td->td_retval[0] = ngrp;
|
||||
return (error);
|
||||
|
|
@ -499,8 +512,8 @@ gidp_cmp(const void *p1, const void *p2)
|
|||
}
|
||||
|
||||
/*
|
||||
* Final storage for groups (including the effective GID) will be returned via
|
||||
* 'groups'. '*groups' must be NULL on input, and if not equal to 'smallgroups'
|
||||
* Final storage for supplementary groups will be returned via 'groups'.
|
||||
* '*groups' must be NULL on input, and if not equal to 'smallgroups'
|
||||
* on output, must be freed (M_TEMP) *even if* an error is returned.
|
||||
*/
|
||||
static int
|
||||
|
|
@ -525,15 +538,15 @@ kern_setcred_copyin_supp_groups(struct setcred *const wcred,
|
|||
* now, to avoid having to allocate and copy again the
|
||||
* supplementary groups.
|
||||
*/
|
||||
*groups = wcred->sc_supp_groups_nb < CRED_SMALLGROUPS_NB ?
|
||||
smallgroups : malloc((wcred->sc_supp_groups_nb + 1) *
|
||||
*groups = wcred->sc_supp_groups_nb <= CRED_SMALLGROUPS_NB ?
|
||||
smallgroups : malloc(wcred->sc_supp_groups_nb *
|
||||
sizeof(*groups), M_TEMP, M_WAITOK);
|
||||
|
||||
error = copyin(wcred->sc_supp_groups, *groups + 1,
|
||||
error = copyin(wcred->sc_supp_groups, *groups,
|
||||
wcred->sc_supp_groups_nb * sizeof(*groups));
|
||||
if (error != 0)
|
||||
return (error);
|
||||
wcred->sc_supp_groups = *groups + 1;
|
||||
wcred->sc_supp_groups = *groups;
|
||||
} else {
|
||||
wcred->sc_supp_groups_nb = 0;
|
||||
wcred->sc_supp_groups = NULL;
|
||||
|
|
@ -652,9 +665,8 @@ sys_setcred(struct thread *td, struct setcred_args *uap)
|
|||
* CAUTION: This function normalizes groups in 'wcred'.
|
||||
*
|
||||
* If 'preallocated_groups' is non-NULL, it must be an already allocated array
|
||||
* of size 'wcred->sc_supp_groups_nb + 1', with the supplementary groups
|
||||
* starting at index 1, and 'wcred->sc_supp_groups' then must point to the first
|
||||
* supplementary group.
|
||||
* of size 'wcred->sc_supp_groups_nb' containing the supplementary groups, and
|
||||
* 'wcred->sc_supp_groups' then must point to it.
|
||||
*/
|
||||
int
|
||||
kern_setcred(struct thread *const td, const u_int flags,
|
||||
|
|
@ -685,13 +697,14 @@ kern_setcred(struct thread *const td, const u_int flags,
|
|||
return (EINVAL);
|
||||
if (preallocated_groups != NULL) {
|
||||
groups = preallocated_groups;
|
||||
MPASS(preallocated_groups + 1 == wcred->sc_supp_groups);
|
||||
MPASS(preallocated_groups == wcred->sc_supp_groups);
|
||||
} else {
|
||||
groups = wcred->sc_supp_groups_nb < CRED_SMALLGROUPS_NB ?
|
||||
smallgroups :
|
||||
malloc((wcred->sc_supp_groups_nb + 1) *
|
||||
sizeof(*groups), M_TEMP, M_WAITOK);
|
||||
memcpy(groups + 1, wcred->sc_supp_groups,
|
||||
if (wcred->sc_supp_groups_nb <= CRED_SMALLGROUPS_NB)
|
||||
groups = smallgroups;
|
||||
else
|
||||
groups = malloc(wcred->sc_supp_groups_nb *
|
||||
sizeof(*groups), M_TEMP, M_WAITOK);
|
||||
memcpy(groups, wcred->sc_supp_groups,
|
||||
wcred->sc_supp_groups_nb * sizeof(*groups));
|
||||
}
|
||||
}
|
||||
|
|
@ -726,16 +739,12 @@ kern_setcred(struct thread *const td, const u_int flags,
|
|||
if (flags & SETCREDF_SVGID)
|
||||
AUDIT_ARG_SGID(wcred->sc_svgid);
|
||||
if (flags & SETCREDF_SUPP_GROUPS) {
|
||||
int ngrp = wcred->sc_supp_groups_nb;
|
||||
|
||||
/*
|
||||
* Output the raw supplementary groups array for better
|
||||
* traceability.
|
||||
*/
|
||||
AUDIT_ARG_GROUPSET(groups + 1, ngrp);
|
||||
++ngrp;
|
||||
groups_normalize(&ngrp, groups);
|
||||
wcred->sc_supp_groups_nb = ngrp - 1;
|
||||
AUDIT_ARG_GROUPSET(groups, wcred->sc_supp_groups_nb);
|
||||
groups_normalize(&wcred->sc_supp_groups_nb, groups);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -746,7 +755,7 @@ kern_setcred(struct thread *const td, const u_int flags,
|
|||
new_cred = crget();
|
||||
to_free_cred = new_cred;
|
||||
if (flags & SETCREDF_SUPP_GROUPS)
|
||||
crextend(new_cred, wcred->sc_supp_groups_nb + 1);
|
||||
crextend(new_cred, wcred->sc_supp_groups_nb);
|
||||
|
||||
#ifdef MAC
|
||||
mac_cred_setcred_enter();
|
||||
|
|
@ -773,16 +782,11 @@ kern_setcred(struct thread *const td, const u_int flags,
|
|||
|
||||
/*
|
||||
* Change groups.
|
||||
*
|
||||
* crsetgroups_internal() changes both the effective and supplementary
|
||||
* ones.
|
||||
*/
|
||||
if (flags & SETCREDF_SUPP_GROUPS) {
|
||||
groups[0] = flags & SETCREDF_GID ? wcred->sc_gid :
|
||||
new_cred->cr_gid;
|
||||
crsetgroups_internal(new_cred, wcred->sc_supp_groups_nb + 1,
|
||||
if (flags & SETCREDF_SUPP_GROUPS)
|
||||
crsetgroups_internal(new_cred, wcred->sc_supp_groups_nb,
|
||||
groups);
|
||||
} else if (flags & SETCREDF_GID)
|
||||
if (flags & SETCREDF_GID)
|
||||
change_egid(new_cred, wcred->sc_gid);
|
||||
if (flags & SETCREDF_RGID)
|
||||
change_rgid(new_cred, wcred->sc_rgid);
|
||||
|
|
@ -1206,6 +1210,7 @@ sys_setgroups(struct thread *td, struct setgroups_args *uap)
|
|||
* setgroups() differ.
|
||||
*/
|
||||
gidsetsize = uap->gidsetsize;
|
||||
/* XXXKE Limit to ngroups_max when we change the userland interface. */
|
||||
if (gidsetsize > ngroups_max + 1 || gidsetsize < 0)
|
||||
return (EINVAL);
|
||||
|
||||
|
|
@ -1233,29 +1238,49 @@ kern_setgroups(struct thread *td, int *ngrpp, gid_t *groups)
|
|||
struct proc *p = td->td_proc;
|
||||
struct ucred *newcred, *oldcred;
|
||||
int ngrp, error;
|
||||
gid_t egid;
|
||||
|
||||
ngrp = *ngrpp;
|
||||
/* Sanity check size. */
|
||||
/* XXXKE Limit to ngroups_max when we change the userland interface. */
|
||||
if (ngrp < 0 || ngrp > ngroups_max + 1)
|
||||
return (EINVAL);
|
||||
|
||||
AUDIT_ARG_GROUPSET(groups, ngrp);
|
||||
/*
|
||||
* setgroups(0, NULL) is a legitimate way of clearing the groups vector
|
||||
* on non-BSD systems (which generally do not have the egid in the
|
||||
* groups[0]). We risk security holes when running non-BSD software if
|
||||
* we do not do the same. So we allow and treat 0 for 'ngrp' specially
|
||||
* below (twice).
|
||||
*/
|
||||
if (ngrp != 0) {
|
||||
/* We allow and treat 0 specially below. */
|
||||
groups_normalize(ngrpp, groups);
|
||||
ngrp = *ngrpp;
|
||||
/*
|
||||
* To maintain userland compat for now, we use the first group
|
||||
* as our egid and we'll use the rest as our supplemental
|
||||
* groups.
|
||||
*/
|
||||
egid = groups[0];
|
||||
ngrp--;
|
||||
groups++;
|
||||
|
||||
groups_normalize(&ngrp, groups);
|
||||
*ngrpp = ngrp;
|
||||
}
|
||||
newcred = crget();
|
||||
if (ngrp != 0)
|
||||
crextend(newcred, ngrp);
|
||||
crextend(newcred, ngrp);
|
||||
PROC_LOCK(p);
|
||||
oldcred = crcopysafe(p, newcred);
|
||||
|
||||
#ifdef MAC
|
||||
error = ngrp == 0 ?
|
||||
/* If 'ngrp' is 0, we'll keep just the current effective GID. */
|
||||
mac_cred_check_setgroups(oldcred, 1, oldcred->cr_groups) :
|
||||
mac_cred_check_setgroups(oldcred, ngrp, groups);
|
||||
/*
|
||||
* We pass NULL here explicitly if we don't have any supplementary
|
||||
* groups mostly for the sake of normalization, but also to avoid/detect
|
||||
* a situation where a MAC module has some assumption about the layout
|
||||
* of `groups` matching historical behavior.
|
||||
*/
|
||||
error = mac_cred_check_setgroups(oldcred, ngrp,
|
||||
ngrp == 0 ? NULL : groups);
|
||||
if (error)
|
||||
goto fail;
|
||||
#endif
|
||||
|
|
@ -1264,16 +1289,14 @@ kern_setgroups(struct thread *td, int *ngrpp, gid_t *groups)
|
|||
if (error)
|
||||
goto fail;
|
||||
|
||||
if (ngrp == 0) {
|
||||
/*
|
||||
* setgroups(0, NULL) is a legitimate way of clearing the
|
||||
* groups vector on non-BSD systems (which generally do not
|
||||
* have the egid in the groups[0]). We risk security holes
|
||||
* when running non-BSD software if we do not do the same.
|
||||
*/
|
||||
newcred->cr_ngroups = 1;
|
||||
} else
|
||||
crsetgroups_internal(newcred, ngrp, groups);
|
||||
/*
|
||||
* If some groups were passed, the first one is currently the desired
|
||||
* egid. This code is to be removed (along with some commented block
|
||||
* above) when setgroups() is changed to take only supplementary groups.
|
||||
*/
|
||||
if (ngrp != 0)
|
||||
newcred->cr_gid = egid;
|
||||
crsetgroups_internal(newcred, ngrp, groups);
|
||||
|
||||
setsugid(p);
|
||||
proc_set_cred(p, newcred);
|
||||
|
|
@ -1693,11 +1716,11 @@ groups_check_normalized(int ngrp, const gid_t *groups)
|
|||
groups_check_positive_len(ngrp);
|
||||
groups_check_max_len(ngrp);
|
||||
|
||||
if (ngrp == 1)
|
||||
if (ngrp <= 1)
|
||||
return;
|
||||
|
||||
prev_g = groups[1];
|
||||
for (int i = 2; i < ngrp; ++i) {
|
||||
prev_g = groups[0];
|
||||
for (int i = 1; i < ngrp; ++i) {
|
||||
const gid_t g = groups[i];
|
||||
|
||||
if (prev_g >= g)
|
||||
|
|
@ -1723,7 +1746,7 @@ group_is_supplementary(const gid_t gid, const struct ucred *const cred)
|
|||
* Perform a binary search of the supplementary groups. This is
|
||||
* possible because we sort the groups in crsetgroups().
|
||||
*/
|
||||
return (bsearch(&gid, cred->cr_groups + 1, cred->cr_ngroups - 1,
|
||||
return (bsearch(&gid, cred->cr_groups, cred->cr_ngroups,
|
||||
sizeof(gid), gidp_cmp) != NULL);
|
||||
}
|
||||
|
||||
|
|
@ -2588,11 +2611,6 @@ void
|
|||
crcopy(struct ucred *dest, struct ucred *src)
|
||||
{
|
||||
|
||||
/*
|
||||
* Ideally, 'cr_ngroups' should be moved out of 'struct ucred''s bcopied
|
||||
* area, but this would break the ABI, so is deferred until there is
|
||||
* a compelling need to change it.
|
||||
*/
|
||||
bcopy(&src->cr_startcopy, &dest->cr_startcopy,
|
||||
(unsigned)((caddr_t)&src->cr_endcopy -
|
||||
(caddr_t)&src->cr_startcopy));
|
||||
|
|
@ -2634,11 +2652,17 @@ cru2x(struct ucred *cr, struct xucred *xcr)
|
|||
bzero(xcr, sizeof(*xcr));
|
||||
xcr->cr_version = XUCRED_VERSION;
|
||||
xcr->cr_uid = cr->cr_uid;
|
||||
xcr->cr_gid = cr->cr_gid;
|
||||
|
||||
ngroups = MIN(cr->cr_ngroups, XU_NGROUPS);
|
||||
/*
|
||||
* We use a union to alias cr_gid to cr_groups[0] in the xucred, so
|
||||
* this is kind of ugly; cr_ngroups still includes the egid for our
|
||||
* purposes to avoid bumping the xucred version.
|
||||
*/
|
||||
ngroups = MIN(cr->cr_ngroups + 1, nitems(xcr->cr_groups));
|
||||
xcr->cr_ngroups = ngroups;
|
||||
bcopy(cr->cr_groups, xcr->cr_groups,
|
||||
ngroups * sizeof(*cr->cr_groups));
|
||||
bcopy(cr->cr_groups, xcr->cr_sgroups,
|
||||
(ngroups - 1) * sizeof(*cr->cr_groups));
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -2809,12 +2833,8 @@ crextend(struct ucred *cr, int n)
|
|||
/*
|
||||
* Normalizes a set of groups to be applied to a 'struct ucred'.
|
||||
*
|
||||
* The set of groups is an array that must comprise the effective GID as its
|
||||
* first element (so its length cannot be 0).
|
||||
*
|
||||
* Normalization ensures that elements after the first, which stand for the
|
||||
* supplementary groups, are sorted in ascending order and do not contain
|
||||
* duplicates.
|
||||
* Normalization ensures that the supplementary groups are sorted in ascending
|
||||
* order and do not contain duplicates.
|
||||
*/
|
||||
static void
|
||||
groups_normalize(int *ngrp, gid_t *groups)
|
||||
|
|
@ -2825,15 +2845,15 @@ groups_normalize(int *ngrp, gid_t *groups)
|
|||
groups_check_positive_len(*ngrp);
|
||||
groups_check_max_len(*ngrp);
|
||||
|
||||
if (*ngrp == 1)
|
||||
if (*ngrp <= 1)
|
||||
return;
|
||||
|
||||
qsort(groups + 1, *ngrp - 1, sizeof(*groups), gidp_cmp);
|
||||
qsort(groups, *ngrp, sizeof(*groups), gidp_cmp);
|
||||
|
||||
/* Remove duplicates. */
|
||||
prev_g = groups[1];
|
||||
ins_idx = 2;
|
||||
for (int i = 2; i < *ngrp; ++i) {
|
||||
prev_g = groups[0];
|
||||
ins_idx = 1;
|
||||
for (int i = ins_idx; i < *ngrp; ++i) {
|
||||
const gid_t g = groups[i];
|
||||
|
||||
if (g != prev_g) {
|
||||
|
|
@ -2876,7 +2896,7 @@ crsetgroups_internal(struct ucred *cr, int ngrp, const gid_t *groups)
|
|||
* Copy groups in to a credential after expanding it if required.
|
||||
*
|
||||
* May sleep in order to allocate memory (except if, e.g., crextend() was called
|
||||
* before with 'ngrp' or greater). Truncates the list to (ngroups_max + 1) if
|
||||
* before with 'ngrp' or greater). Truncates the list to ngroups_max if
|
||||
* it is too large. Array 'groups' doesn't need to be sorted. 'ngrp' must be
|
||||
* strictly positive.
|
||||
*/
|
||||
|
|
@ -2884,8 +2904,8 @@ void
|
|||
crsetgroups(struct ucred *cr, int ngrp, const gid_t *groups)
|
||||
{
|
||||
|
||||
if (ngrp > ngroups_max + 1)
|
||||
ngrp = ngroups_max + 1;
|
||||
if (ngrp > ngroups_max)
|
||||
ngrp = ngroups_max;
|
||||
/*
|
||||
* crextend() asserts that groups are not set, as it may allocate a new
|
||||
* backing storage without copying the content of the old one. Since we
|
||||
|
|
@ -2893,6 +2913,9 @@ crsetgroups(struct ucred *cr, int ngrp, const gid_t *groups)
|
|||
* consider the old ones thrown away.
|
||||
*/
|
||||
cr->cr_ngroups = 0;
|
||||
if (ngrp == 0)
|
||||
return;
|
||||
|
||||
crextend(cr, ngrp);
|
||||
crsetgroups_internal(cr, ngrp, groups);
|
||||
groups_normalize(&cr->cr_ngroups, cr->cr_groups);
|
||||
|
|
@ -2902,18 +2925,22 @@ crsetgroups(struct ucred *cr, int ngrp, const gid_t *groups)
|
|||
* Same as crsetgroups() but accepts an empty groups array.
|
||||
*
|
||||
* This function ensures that an effective GID is always present in credentials.
|
||||
* An empty array is treated as a one-size one holding the passed effective GID
|
||||
* fallback.
|
||||
* An empty array will only set the effective GID to the fallback, while a
|
||||
* non-empty array will peel off groups[0] to set as the effective GID and use
|
||||
* the remainder, if any, as supplementary groups.
|
||||
*/
|
||||
void
|
||||
crsetgroups_fallback(struct ucred *cr, int ngrp, const gid_t *groups,
|
||||
const gid_t fallback)
|
||||
{
|
||||
if (ngrp == 0)
|
||||
/* Shortcut. */
|
||||
crsetgroups_internal(cr, 1, &fallback);
|
||||
else
|
||||
crsetgroups(cr, ngrp, groups);
|
||||
if (ngrp == 0) {
|
||||
cr->cr_gid = fallback;
|
||||
cr->cr_ngroups = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
crsetgroups(cr, ngrp - 1, groups + 1);
|
||||
cr->cr_gid = groups[0];
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -96,8 +96,12 @@ xdr_authunix_parms(XDR *xdrs, uint32_t *time, struct xucred *cred)
|
|||
if (!xdr_uint32_t(xdrs, &cred->cr_gid))
|
||||
return (FALSE);
|
||||
|
||||
/* XXXKE Fix this is cr_gid gets separated out. */
|
||||
if (xdrs->x_op == XDR_ENCODE) {
|
||||
/*
|
||||
* Note that this is a `struct xucred`, which maintains its
|
||||
* historical layout of preserving the egid in cr_ngroups and
|
||||
* cr_groups[0] == egid.
|
||||
*/
|
||||
ngroups = cred->cr_ngroups - 1;
|
||||
if (ngroups > NGRPS)
|
||||
ngroups = NGRPS;
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@
|
|||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/proc.h>
|
||||
|
|
@ -191,7 +192,7 @@ svc_getcred(struct svc_req *rqst, struct ucred **crp, int *flavorp)
|
|||
return (FALSE);
|
||||
cr = crget();
|
||||
cr->cr_uid = cr->cr_ruid = cr->cr_svuid = xprt->xp_uid;
|
||||
crsetgroups(cr, xprt->xp_ngrps, xprt->xp_gidp);
|
||||
crsetgroups_fallback(cr, xprt->xp_ngrps, xprt->xp_gidp, GID_NOGROUP);
|
||||
cr->cr_rgid = cr->cr_svgid = cr->cr_gid;
|
||||
cr->cr_prison = curthread->td_ucred->cr_prison;
|
||||
prison_hold(cr->cr_prison);
|
||||
|
|
@ -206,7 +207,7 @@ svc_getcred(struct svc_req *rqst, struct ucred **crp, int *flavorp)
|
|||
return (FALSE);
|
||||
cr = crget();
|
||||
cr->cr_uid = cr->cr_ruid = cr->cr_svuid = xcr->cr_uid;
|
||||
crsetgroups(cr, xcr->cr_ngroups, xcr->cr_groups);
|
||||
crsetgroups_fallback(cr, xcr->cr_ngroups, xcr->cr_groups, GID_NOGROUP);
|
||||
cr->cr_rgid = cr->cr_svgid = cr->cr_gid;
|
||||
cr->cr_prison = curthread->td_ucred->cr_prison;
|
||||
prison_hold(cr->cr_prison);
|
||||
|
|
|
|||
|
|
@ -89,8 +89,12 @@ _svcauth_unix(struct svc_req *rqst, struct rpc_msg *msg)
|
|||
stat = AUTH_BADCRED;
|
||||
goto done;
|
||||
}
|
||||
/* XXXKE Fix this if cr_gid gets separated out. */
|
||||
for (i = 0; i < gid_len; i++) {
|
||||
/*
|
||||
* Note that this is a `struct xucred`, which maintains
|
||||
* its historical layout of preserving the egid in
|
||||
* cr_ngroups and cr_groups[0] == egid.
|
||||
*/
|
||||
if (i + 1 < XU_NGROUPS)
|
||||
xcr->cr_groups[i + 1] = IXDR_GET_INT32(buf);
|
||||
else
|
||||
|
|
|
|||
|
|
@ -76,15 +76,12 @@ struct ucred {
|
|||
u_int cr_users; /* (c) proc + thread using this cred */
|
||||
u_int cr_flags; /* credential flags */
|
||||
struct auditinfo_addr cr_audit; /* Audit properties. */
|
||||
int cr_ngroups; /* number of supplementary groups */
|
||||
#define cr_startcopy cr_uid
|
||||
uid_t cr_uid; /* effective user id */
|
||||
uid_t cr_ruid; /* real user id */
|
||||
uid_t cr_svuid; /* saved user id */
|
||||
/*
|
||||
* XXXOC: On the next ABI change, please move 'cr_ngroups' out of the
|
||||
* copied area (crcopy() already copes with this change).
|
||||
*/
|
||||
int cr_ngroups; /* number of groups */
|
||||
gid_t cr_gid; /* effective group id */
|
||||
gid_t cr_rgid; /* real group id */
|
||||
gid_t cr_svgid; /* saved group id */
|
||||
struct uidinfo *cr_uidinfo; /* per euid resource consumption */
|
||||
|
|
@ -111,8 +108,20 @@ struct ucred {
|
|||
struct xucred {
|
||||
u_int cr_version; /* structure layout version */
|
||||
uid_t cr_uid; /* effective user id */
|
||||
short cr_ngroups; /* number of groups */
|
||||
gid_t cr_groups[XU_NGROUPS]; /* groups */
|
||||
short cr_ngroups; /* number of groups (incl. cr_gid). */
|
||||
union {
|
||||
/*
|
||||
* Special little hack to avoid needing a cr_gid macro, which
|
||||
* would cause problems if one were to use it with struct ucred
|
||||
* which also has a cr_groups member.
|
||||
*/
|
||||
struct {
|
||||
gid_t cr_gid; /* effective group id */
|
||||
gid_t cr_sgroups[XU_NGROUPS - 1];
|
||||
};
|
||||
|
||||
gid_t cr_groups[XU_NGROUPS]; /* groups */
|
||||
};
|
||||
union {
|
||||
void *_cr_unused1; /* compatibility with old ucred */
|
||||
pid_t cr_pid;
|
||||
|
|
@ -120,9 +129,6 @@ struct xucred {
|
|||
};
|
||||
#define XUCRED_VERSION 0
|
||||
|
||||
/* This can be used for both ucred and xucred structures. */
|
||||
#define cr_gid cr_groups[0]
|
||||
|
||||
struct mac;
|
||||
/*
|
||||
* Structure to pass as an argument to the setcred() system call.
|
||||
|
|
|
|||
|
|
@ -2038,7 +2038,6 @@ ufs_mkdir(
|
|||
{
|
||||
#ifdef QUOTA
|
||||
struct ucred ucred, *ucp;
|
||||
gid_t ucred_group;
|
||||
ucp = cnp->cn_cred;
|
||||
#endif
|
||||
/*
|
||||
|
|
@ -2065,13 +2064,8 @@ ufs_mkdir(
|
|||
*/
|
||||
ucred.cr_ref = 1;
|
||||
ucred.cr_uid = ip->i_uid;
|
||||
|
||||
/*
|
||||
* XXXKE Fix this is cr_gid gets separated out
|
||||
*/
|
||||
ucred.cr_ngroups = 1;
|
||||
ucred.cr_groups = &ucred_group;
|
||||
ucred.cr_gid = ucred_group = dp->i_gid;
|
||||
ucred.cr_gid = dp->i_gid;
|
||||
ucred.cr_ngroups = 0;
|
||||
ucp = &ucred;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -2802,7 +2796,6 @@ ufs_makeinode(int mode, struct vnode *dvp, struct vnode **vpp,
|
|||
{
|
||||
#ifdef QUOTA
|
||||
struct ucred ucred, *ucp;
|
||||
gid_t ucred_group;
|
||||
ucp = cnp->cn_cred;
|
||||
#endif
|
||||
/*
|
||||
|
|
@ -2828,13 +2821,8 @@ ufs_makeinode(int mode, struct vnode *dvp, struct vnode **vpp,
|
|||
*/
|
||||
ucred.cr_ref = 1;
|
||||
ucred.cr_uid = ip->i_uid;
|
||||
|
||||
/*
|
||||
* XXXKE Fix this is cr_gid gets separated out
|
||||
*/
|
||||
ucred.cr_ngroups = 1;
|
||||
ucred.cr_groups = &ucred_group;
|
||||
ucred.cr_gid = ucred_group = pdir->i_gid;
|
||||
ucred.cr_gid = pdir->i_gid;
|
||||
ucred.cr_ngroups = 0;
|
||||
ucp = &ucred;
|
||||
#endif
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Reference in a new issue