nfs, rpc: Ensure kernel credentials have at least one group

This fixes several bugs where some 'struct ucred' in the kernel,
constructed from user input (via nmount(2)) or obtained from other
servers (e.g., gssd(8)), could have an unfilled 'cr_groups' field and
whose 'cr_groups[0]' (or 'cr_gid', which is an alias) was later
accessed, causing an uninitialized access giving random access rights.

Use crsetgroups_fallback() to enforce a fallback group when possible.
For NFS, the chosen fallback group is that of the NFS server in the
current VNET (NFSD_VNET(nfsrv_defaultgid)).

There does not seem to be any sensible fallback available in rpc code
(sys/rpc/svc_auth.c, svc_getcred()) on AUTH_UNIX (TLS or not), so just
fail credential retrieval there.  Stock NSS sources, rpc.tlsservd(8) or
rpc.tlsclntd(8) provide non-empty group lists, so will not be impacted.

Discussed with: rmacklem (by mail)
Approved by:    markj (mentor)
MFC after:      3 days
Differential Revision:  https://reviews.freebsd.org/D46918
This commit is contained in:
Olivier Certner 2024-10-02 16:28:59 +02:00
parent d4e0d4d92e
commit cfbe7a62dc
No known key found for this signature in database
GPG Key ID: 8CA13040971E2627
7 changed files with 28 additions and 15 deletions

View File

@ -75,6 +75,7 @@ NFSD_VNET_DEFINE(struct nfsstatsv1 *, nfsstatsv1_p);
NFSD_VNET_DECLARE(struct nfssockreq, nfsrv_nfsuserdsock);
NFSD_VNET_DECLARE(nfsuserd_state, nfsrv_nfsuserd);
NFSD_VNET_DECLARE(gid_t, nfsrv_defaultgid);
int nfs_pnfsio(task_fn_t *, void *);
@ -258,7 +259,8 @@ newnfs_copycred(struct nfscred *nfscr, struct ucred *cr)
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_fallback(cr, nfscr->nfsc_ngroups, nfscr->nfsc_groups,
NFSD_VNET(nfsrv_defaultgid));
}
/*

View File

@ -4051,8 +4051,9 @@ nfssvc_idname(struct nfsd_idargs *nidp)
*/
cr = crget();
cr->cr_uid = cr->cr_ruid = cr->cr_svuid = nidp->nid_uid;
crsetgroups(cr, nidp->nid_ngroup, grps);
cr->cr_rgid = cr->cr_svgid = cr->cr_groups[0];
crsetgroups_fallback(cr, nidp->nid_ngroup, grps,
NFSD_VNET(nfsrv_defaultgid));
cr->cr_rgid = cr->cr_svgid = cr->cr_gid;
cr->cr_prison = curthread->td_ucred->cr_prison;
prison_hold(cr->cr_prison);
#ifdef MAC

View File

@ -3311,7 +3311,11 @@ 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
* we don't need a fallback here.
*/
crsetgroups(nd->nd_cred, credanon->cr_ngroups,
credanon->cr_groups);
} else if ((nd->nd_flag & ND_GSS) == 0) {

View File

@ -1422,13 +1422,11 @@ static struct ucred *
nfsrv_createrootcred(void)
{
struct ucred *cr;
gid_t grp;
cr = crget();
cr->cr_uid = cr->cr_ruid = cr->cr_svuid = UID_ROOT;
grp = GID_WHEEL;
crsetgroups(cr, 1, &grp);
cr->cr_rgid = cr->cr_svgid = cr->cr_groups[0];
crsetgroups_fallback(cr, 0, NULL, GID_WHEEL);
cr->cr_rgid = cr->cr_svgid = cr->cr_gid;
cr->cr_prison = curthread->td_ucred->cr_prison;
prison_hold(cr->cr_prison);
#ifdef MAC

View File

@ -61,6 +61,10 @@
#include <rpc/types.h>
#include <rpc/auth.h>
#include <fs/nfs/nfsport.h>
NFSD_VNET_DECLARE(gid_t, nfsrv_defaultgid);
static MALLOC_DEFINE(M_NETADDR, "export_host", "Export host address structure");
#if defined(INET) || defined(INET6)
@ -133,8 +137,8 @@ vfs_hang_addrlist(struct mount *mp, struct netexport *nep,
np->netc_exflags = argp->ex_flags;
np->netc_anon = crget();
np->netc_anon->cr_uid = argp->ex_uid;
crsetgroups(np->netc_anon, argp->ex_ngroups,
argp->ex_groups);
crsetgroups_fallback(np->netc_anon, argp->ex_ngroups,
argp->ex_groups, NFSD_VNET(nfsrv_defaultgid));
np->netc_anon->cr_prison = &prison0;
prison_hold(np->netc_anon->cr_prison);
np->netc_numsecflavors = argp->ex_numsecflavors;
@ -212,8 +216,8 @@ vfs_hang_addrlist(struct mount *mp, struct netexport *nep,
np->netc_exflags = argp->ex_flags;
np->netc_anon = crget();
np->netc_anon->cr_uid = argp->ex_uid;
crsetgroups(np->netc_anon, argp->ex_ngroups,
argp->ex_groups);
crsetgroups_fallback(np->netc_anon, argp->ex_ngroups, argp->ex_groups,
NFSD_VNET(nfsrv_defaultgid));
np->netc_anon->cr_prison = &prison0;
prison_hold(np->netc_anon->cr_prison);
np->netc_numsecflavors = argp->ex_numsecflavors;

View File

@ -537,7 +537,7 @@ rpc_gss_svc_getcred(struct svc_req *req, struct ucred **crp, int *flavorp)
cr = client->cl_cred = crget();
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_fallback(cr, uc->gidlen, uc->gidlist, uc->gid);
cr->cr_prison = curthread->td_ucred->cr_prison;
prison_hold(cr->cr_prison);
*crp = crhold(cr);

View File

@ -187,10 +187,12 @@ svc_getcred(struct svc_req *rqst, struct ucred **crp, int *flavorp)
if ((xprt->xp_tls & (RPCTLS_FLAGS_CERTUSER |
RPCTLS_FLAGS_DISABLED)) == RPCTLS_FLAGS_CERTUSER &&
flavor == AUTH_UNIX) {
if (xprt->xp_ngrps <= 0)
return (FALSE);
cr = crget();
cr->cr_uid = cr->cr_ruid = cr->cr_svuid = xprt->xp_uid;
crsetgroups(cr, xprt->xp_ngrps, xprt->xp_gidp);
cr->cr_rgid = cr->cr_svgid = xprt->xp_gidp[0];
cr->cr_rgid = cr->cr_svgid = cr->cr_gid;
cr->cr_prison = curthread->td_ucred->cr_prison;
prison_hold(cr->cr_prison);
*crp = cr;
@ -200,10 +202,12 @@ svc_getcred(struct svc_req *rqst, struct ucred **crp, int *flavorp)
switch (flavor) {
case AUTH_UNIX:
xcr = (struct xucred *) rqst->rq_clntcred;
if (xcr->cr_ngroups <= 0)
return (FALSE);
cr = crget();
cr->cr_uid = cr->cr_ruid = cr->cr_svuid = xcr->cr_uid;
crsetgroups(cr, xcr->cr_ngroups, xcr->cr_groups);
cr->cr_rgid = cr->cr_svgid = cr->cr_groups[0];
cr->cr_rgid = cr->cr_svgid = cr->cr_gid;
cr->cr_prison = curthread->td_ucred->cr_prison;
prison_hold(cr->cr_prison);
*crp = cr;