diff options
author | Rick Macklem <rmacklem@FreeBSD.org> | 2023-04-07 19:49:23 +0000 |
---|---|---|
committer | Rick Macklem <rmacklem@FreeBSD.org> | 2023-04-07 19:49:23 +0000 |
commit | ff2f1f691cdb376be70b9579d2f9c91b06ac839d (patch) | |
tree | da930f7c995b72a5dd381f1bd3f56fc9de828d13 | |
parent | 66fbc19fbd7c75fae619ead787d90242d96cc002 (diff) | |
download | src-ff2f1f691cdb.tar.gz src-ff2f1f691cdb.zip |
nfsd: Add support for the SP4_MACH_CRED case in ExchangeID
Commit f4179ad46fa4 added support for operation bitmaps for
NFSv4.1/4.2. This commit uses those to implement the SP4_MACH_CRED
case for the NFSv4.1/4.2 ExchangeID operation since the Linux
NFSv4.1/4.2 client is now using this for Kerberized mounts.
The Linux Kerberized NFSv4.1/4.2 mounts currently work without
support for this because Linux will fall back to SP4_NONE,
but there is no guarantee this fallback will work forever.
This commit only affects Kerberized NFSv4.1/4.2 mounts from
Linux at this time.
MFC after: 3 months
-rw-r--r-- | sys/fs/nfs/nfs_var.h | 2 | ||||
-rw-r--r-- | sys/fs/nfs/nfsrvstate.h | 2 | ||||
-rw-r--r-- | sys/fs/nfsserver/nfs_nfsdkrpc.c | 58 | ||||
-rw-r--r-- | sys/fs/nfsserver/nfs_nfsdserv.c | 29 | ||||
-rw-r--r-- | sys/fs/nfsserver/nfs_nfsdsocket.c | 55 | ||||
-rw-r--r-- | sys/fs/nfsserver/nfs_nfsdstate.c | 90 |
6 files changed, 215 insertions, 21 deletions
diff --git a/sys/fs/nfs/nfs_var.h b/sys/fs/nfs/nfs_var.h index ad1eb73b1090..8fabe6f92fc6 100644 --- a/sys/fs/nfs/nfs_var.h +++ b/sys/fs/nfs/nfs_var.h @@ -99,7 +99,7 @@ int nfsrv_setclient(struct nfsrv_descript *, struct nfsclient **, nfsquad_t *, nfsquad_t *, NFSPROC_T *); int nfsrv_getclient(nfsquad_t, int, struct nfsclient **, struct nfsdsession *, nfsquad_t, uint32_t, struct nfsrv_descript *, NFSPROC_T *); -int nfsrv_destroyclient(nfsquad_t, NFSPROC_T *); +int nfsrv_destroyclient(struct nfsrv_descript *, nfsquad_t, NFSPROC_T *); int nfsrv_destroysession(struct nfsrv_descript *, uint8_t *); int nfsrv_bindconnsess(struct nfsrv_descript *, uint8_t *, int *); int nfsrv_freestateid(struct nfsrv_descript *, nfsv4stateid_t *, NFSPROC_T *); diff --git a/sys/fs/nfs/nfsrvstate.h b/sys/fs/nfs/nfsrvstate.h index 4dc9729a952f..5a2c9496804d 100644 --- a/sys/fs/nfs/nfsrvstate.h +++ b/sys/fs/nfs/nfsrvstate.h @@ -105,6 +105,8 @@ struct nfsclient { time_t lc_delegtime; /* Old deleg expiry (sec) */ nfsquad_t lc_clientid; /* 64 bit clientid */ nfsquad_t lc_confirm; /* 64 bit confirm value */ + nfsopbit_t lc_mustops; /* Must ops SP4_MACH_CRED */ + nfsopbit_t lc_allowops; /* Allowed ops SP4_MACH_CRED */ u_int32_t lc_program; /* RPC Program # */ u_int32_t lc_callback; /* Callback id */ u_int32_t lc_stateindex; /* Current state index# */ diff --git a/sys/fs/nfsserver/nfs_nfsdkrpc.c b/sys/fs/nfsserver/nfs_nfsdkrpc.c index 45459d115346..5999727fccb6 100644 --- a/sys/fs/nfsserver/nfs_nfsdkrpc.c +++ b/sys/fs/nfsserver/nfs_nfsdkrpc.c @@ -108,6 +108,7 @@ extern time_t nfsdev_time; extern int nfsrv_writerpc[NFS_NPROCS]; extern volatile int nfsrv_devidcnt; extern struct nfsv4_opflag nfsv4_opflag[NFSV42_NOPS]; +extern int nfsd_debuglevel; NFSD_VNET_DECLARE(struct proc *, nfsd_master_proc); @@ -126,7 +127,9 @@ nfssvc_program(struct svc_req *rqst, SVCXPRT *xprt) { struct nfsrv_descript nd; struct nfsrvcache *rp = NULL; - int cacherep, credflavor; + rpc_gss_rawcred_t *rcredp; + int cacherep, credflavor, i, j; + u_char *p; #ifdef KERN_TLS u_int maxlen; #endif @@ -245,6 +248,58 @@ nfssvc_program(struct svc_req *rqst, SVCXPRT *xprt) goto out; } + /* Acquire the principal name for the RPCSEC_GSS cases. */ + if ((nd.nd_flag & (ND_NFSV4 | ND_GSS)) == (ND_NFSV4 | ND_GSS)) { + rcredp = NULL; + rpc_gss_getcred_call(rqst, &rcredp, NULL, NULL); + /* + * The exported principal name consists of: + * - TOK_ID bytes with value of 4 and 1. + * - 2 byte mech length + * - mech + * - 4 byte principal name length + * - principal name + * A call to gss_import_name() would be an + * upcall to the gssd, so parse it here. + * See lib/libgssapi/gss_import_name.c for the + * above format. + */ + if (rcredp != NULL && + rcredp->client_principal->len > 4 && + rcredp->client_principal->name[0] == 4 && + rcredp->client_principal->name[1] == 1) { + /* Skip over the mech. */ + p = &rcredp->client_principal->name[2]; + i = (p[0] << 8) | p[1]; + p += i + 2; + i += 4; + /* + * Set "j" to a bogus length so that the + * "i + j" check will fail unless the below + * code sets "j" correctly. + */ + j = rcredp->client_principal->len; + if (rcredp->client_principal->len > i + 4) { + j = (p[0] << 24) | (p[1] << 16) | + (p[2] << 8) | p[3]; + i += 4; + p += 4; + } + if (i + j == rcredp->client_principal->len) { + nd.nd_principal = malloc(j + 1, M_TEMP, + M_WAITOK); + nd.nd_princlen = j; + memcpy(nd.nd_principal, p, j); + nd.nd_principal[j] = '\0'; + NFSD_DEBUG(1, "nfssvc_program: " + "principal=%s\n", nd.nd_principal); + } + } + if (nd.nd_princlen == 0) + printf("nfssvc_program: cannot get RPCSEC_GSS " + "principal name\n"); + } + if ((xprt->xp_tls & RPCTLS_FLAGS_HANDSHAKE) != 0) { nd.nd_flag |= ND_TLS; if ((xprt->xp_tls & RPCTLS_FLAGS_VERIFIED) != 0) @@ -334,6 +389,7 @@ nfssvc_program(struct svc_req *rqst, SVCXPRT *xprt) svc_freereq(rqst); out: + free(nd.nd_principal, M_TEMP); NFSD_CURVNET_RESTORE(); ast_kclear(curthread); NFSEXITCODE(0); diff --git a/sys/fs/nfsserver/nfs_nfsdserv.c b/sys/fs/nfsserver/nfs_nfsdserv.c index 01ecf0b09ebd..a4fa3a934090 100644 --- a/sys/fs/nfsserver/nfs_nfsdserv.c +++ b/sys/fs/nfsserver/nfs_nfsdserv.c @@ -4282,6 +4282,7 @@ nfsrvd_exchangeid(struct nfsrv_descript *nd, __unused int isdgram, uint8_t *verf; uint32_t sp4type, v41flags; struct timespec verstime; + nfsopbit_t mustops, allowops; #ifdef INET struct sockaddr_in *sin, *rin; #endif @@ -4376,7 +4377,22 @@ nfsrvd_exchangeid(struct nfsrv_descript *nd, __unused int isdgram, else v41flags = NFSV4EXCH_USEPNFSMDS; sp4type = fxdr_unsigned(uint32_t, *tl); - if (sp4type != NFSV4EXCH_SP4NONE) { + if (sp4type == NFSV4EXCH_SP4MACHCRED) { + if ((nd->nd_flag & (ND_GSSINTEGRITY | ND_GSSPRIVACY)) == 0 || + nd->nd_princlen == 0) + nd->nd_repstat = (NFSERR_AUTHERR | AUTH_TOOWEAK); + if (nd->nd_repstat == 0) + nd->nd_repstat = nfsrv_getopbits(nd, &mustops, NULL); + if (nd->nd_repstat == 0) + nd->nd_repstat = nfsrv_getopbits(nd, &allowops, NULL); + if (nd->nd_repstat != 0) + goto nfsmout; + NFSOPBIT_CLRNOTMUST(&mustops); + NFSSET_OPBIT(&clp->lc_mustops, &mustops); + NFSOPBIT_CLRNOTALLOWED(&allowops); + NFSSET_OPBIT(&clp->lc_allowops, &allowops); + clp->lc_flags |= LCL_MACHCRED; + } else if (sp4type != NFSV4EXCH_SP4NONE) { nd->nd_repstat = NFSERR_NOTSUPP; goto nfsmout; } @@ -4398,12 +4414,17 @@ nfsrvd_exchangeid(struct nfsrv_descript *nd, __unused int isdgram, if (nd->nd_repstat == 0) { if (confirm.lval[1] != 0) v41flags |= NFSV4EXCH_CONFIRMEDR; - NFSM_BUILD(tl, uint32_t *, 2 * NFSX_HYPER + 3 * NFSX_UNSIGNED); + NFSM_BUILD(tl, uint32_t *, NFSX_HYPER + 3 * NFSX_UNSIGNED); *tl++ = clientid.lval[0]; /* ClientID */ *tl++ = clientid.lval[1]; *tl++ = txdr_unsigned(confirm.lval[0]); /* SequenceID */ *tl++ = txdr_unsigned(v41flags); /* Exch flags */ - *tl++ = txdr_unsigned(NFSV4EXCH_SP4NONE); /* No SSV */ + *tl = txdr_unsigned(sp4type); /* No SSV */ + if (sp4type == NFSV4EXCH_SP4MACHCRED) { + nfsrv_putopbit(nd, &mustops); + nfsrv_putopbit(nd, &allowops); + } + NFSM_BUILD(tl, uint32_t *, NFSX_HYPER); txdr_hyper(nfsrv_owner_minor, tl); /* Owner Minor */ if (nfsrv_owner_major[0] != 0) s = nfsrv_owner_major; @@ -4642,7 +4663,7 @@ nfsrvd_destroyclientid(struct nfsrv_descript *nd, __unused int isdgram, NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED); clientid.lval[0] = *tl++; clientid.lval[1] = *tl; - nd->nd_repstat = nfsrv_destroyclient(clientid, p); + nd->nd_repstat = nfsrv_destroyclient(nd, clientid, p); nfsmout: NFSEXITCODE2(error, nd); return (error); diff --git a/sys/fs/nfsserver/nfs_nfsdsocket.c b/sys/fs/nfsserver/nfs_nfsdsocket.c index 6b64b3ff98eb..75e20b6c3f2f 100644 --- a/sys/fs/nfsserver/nfs_nfsdsocket.c +++ b/sys/fs/nfsserver/nfs_nfsdsocket.c @@ -42,6 +42,8 @@ __FBSDID("$FreeBSD$"); #include <fs/nfs/nfsport.h> +#include <security/mac/mac_framework.h> + extern struct nfsrvfh nfs_pubfh; extern int nfs_pubfhset; extern struct nfsv4lock nfsv4rootfs_lock; @@ -466,6 +468,8 @@ static int nfsv3to4op[NFS_V3NPROCS] = { static struct mtx nfsrvd_statmtx; MTX_SYSINIT(nfsst, &nfsrvd_statmtx, "NFSstat", MTX_DEF); +static struct ucred *nfsrv_createrootcred(void); + static void nfsrvd_statstart(int op, struct bintime *now) { @@ -715,7 +719,7 @@ nfsrvd_compound(struct nfsrv_descript *nd, int isdgram, u_char *tag, vnode_t vp, nvp, savevp; struct nfsrvfh fh; mount_t new_mp, temp_mp = NULL; - struct ucred *credanon; + struct ucred *credanon, *rootcred, *savecred; struct nfsexstuff nes, vpnes, savevpnes; fsid_t cur_fsid, save_fsid; static u_int64_t compref = 0; @@ -726,6 +730,7 @@ nfsrvd_compound(struct nfsrv_descript *nd, int isdgram, u_char *tag, int bextpg, bextpgsiz; p = curthread; + rootcred = savecred = NULL; /* Check for and optionally clear the no space flags for DSs. */ nfsrv_checknospc(); @@ -967,6 +972,30 @@ nfsrvd_compound(struct nfsrv_descript *nd, int isdgram, u_char *tag, retops++; break; } + + /* + * Check for the case of SP4_MACH_CRED and an operation in + * the allow set. For these operations, replace nd_cred with + * root credentials so that the operation will not fail due + * to credentials. + * NB: ND_MACHCRED is set by Sequence when the ClientID + * specifies LCL_MACHCRED and the RPC is being performed + * via krb5i or krb5p using the machine principal. + */ + if ((nd->nd_flag & ND_MACHCRED) != 0) { + if (NFSISSET_OPBIT(&nd->nd_allowops, op)) { + /* Replace nd_cred with root creds. */ + if (rootcred == NULL) + rootcred = nfsrv_createrootcred(); + if (savecred == NULL) + savecred = nd->nd_cred; + nd->nd_cred = rootcred; + } else if (savecred != NULL) { + nd->nd_cred = savecred; + savecred = NULL; + } + } + if (nfsv4_opflag[op].savereply) nd->nd_flag |= ND_SAVEREPLY; switch (op) { @@ -1379,9 +1408,33 @@ nfsmout: vrele(vp); if (savevp) vrele(savevp); + if (savecred != NULL) + nd->nd_cred = savecred; + if (rootcred != NULL) + crfree(rootcred); NFSLOCKV4ROOTMUTEX(); nfsv4_relref(&nfsv4rootfs_lock); NFSUNLOCKV4ROOTMUTEX(); NFSEXITCODE2(0, nd); } + +/* Create a credential for "root". */ +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]; + cr->cr_prison = curthread->td_ucred->cr_prison; + prison_hold(cr->cr_prison); +#ifdef MAC + mac_cred_associate_nfsd(cr); +#endif + return (cr); +} diff --git a/sys/fs/nfsserver/nfs_nfsdstate.c b/sys/fs/nfsserver/nfs_nfsdstate.c index 4a552298671e..caadfd7c9422 100644 --- a/sys/fs/nfsserver/nfs_nfsdstate.c +++ b/sys/fs/nfsserver/nfs_nfsdstate.c @@ -184,7 +184,7 @@ static int nfsrv_delegconflict(struct nfsstate *stp, int *haslockp, NFSPROC_T *p, vnode_t vp); static int nfsrv_cleandeleg(vnode_t vp, struct nfslockfile *lfp, struct nfsclient *clp, int *haslockp, NFSPROC_T *p); -static int nfsrv_notsamecredname(struct nfsrv_descript *nd, +static int nfsrv_notsamecredname(int op, struct nfsrv_descript *nd, struct nfsclient *clp); static time_t nfsrv_leaseexpiry(void); static void nfsrv_delaydelegtimeout(struct nfsstate *stp); @@ -205,7 +205,8 @@ static void nfsrv_locallock_commit(struct nfslockfile *lfp, int flags, static void nfsrv_locklf(struct nfslockfile *lfp); static void nfsrv_unlocklf(struct nfslockfile *lfp); static struct nfsdsession *nfsrv_findsession(uint8_t *sessionid); -static int nfsrv_freesession(struct nfsdsession *sep, uint8_t *sessionid); +static int nfsrv_freesession(struct nfsrv_descript *nd, struct nfsdsession *sep, + uint8_t *sessionid); static int nfsv4_setcbsequence(struct nfsrv_descript *nd, struct nfsclient *clp, int dont_replycache, struct nfsdsession **sepp, int *slotposp); static int nfsv4_getcbsession(struct nfsclient *clp, struct nfsdsession **sepp); @@ -239,6 +240,8 @@ static int nfsrv_createdsfile(vnode_t vp, fhandle_t *fhp, struct pnfsdsfile *pf, vnode_t dvp, struct nfsdevice *ds, struct ucred *cred, NFSPROC_T *p, vnode_t *tvpp); static struct nfsdevice *nfsrv_findmirroredds(struct nfsmount *nmp); +static int nfsrv_checkmachcred(int op, struct nfsrv_descript *nd, + struct nfsclient *clp); /* * Scan the client list for a match and either return the current one, @@ -378,7 +381,7 @@ nfsrv_setclient(struct nfsrv_descript *nd, struct nfsclient **new_clpp, /* * Now, handle the cases where the id is already issued. */ - if (nfsrv_notsamecredname(nd, clp)) { + if (nfsrv_notsamecredname(NFSV4OP_EXCHANGEID, nd, clp)) { /* * Check to see if there is expired state that should go away. */ @@ -447,7 +450,7 @@ nfsrv_setclient(struct nfsrv_descript *nd, struct nfsclient **new_clpp, /* Get rid of all sessions on this clientid. */ LIST_FOREACH_SAFE(sep, &clp->lc_session, sess_list, nsep) { - ret = nfsrv_freesession(sep, NULL); + ret = nfsrv_freesession(NULL, sep, NULL); if (ret != 0) printf("nfsrv_setclient: verifier changed free" " session failed=%d\n", ret); @@ -706,7 +709,8 @@ nfsrv_getclient(nfsquad_t clientid, int opflags, struct nfsclient **clpp, } else if ((nd->nd_flag & ND_NFSV41) == 0 && clp->lc_confirm.qval != confirm.qval) error = NFSERR_STALECLIENTID; - if (error == 0 && nfsrv_notsamecredname(nd, clp)) + if (error == 0 && nfsrv_notsamecredname(NFSV4OP_CREATESESSION, + nd, clp)) error = NFSERR_CLIDINUSE; if (!error) { @@ -779,7 +783,7 @@ nfsrv_getclient(nfsquad_t clientid, int opflags, struct nfsclient **clpp, * If called by the Renew Op, we must check the principal. */ if (!error && (opflags & CLOPS_RENEWOP)) { - if (nfsrv_notsamecredname(nd, clp)) { + if (nfsrv_notsamecredname(0, nd, clp)) { doneok = 0; for (i = 0; i < nfsrv_statehashsize && doneok == 0; i++) { LIST_FOREACH(stp, &clp->lc_stateid[i], ls_hash) { @@ -819,7 +823,7 @@ out: * Perform the NFSv4.1 destroy clientid. */ int -nfsrv_destroyclient(nfsquad_t clientid, NFSPROC_T *p) +nfsrv_destroyclient(struct nfsrv_descript *nd, nfsquad_t clientid, NFSPROC_T *p) { struct nfsclient *clp; struct nfsclienthashhead *hp; @@ -852,6 +856,15 @@ nfsrv_destroyclient(nfsquad_t clientid, NFSPROC_T *p) goto out; } + /* Check for the SP4_MACH_CRED case. */ + error = nfsrv_checkmachcred(NFSV4OP_DESTROYCLIENTID, nd, clp); + if (error != 0) { + NFSLOCKV4ROOTMUTEX(); + nfsv4_unlock(&nfsv4rootfs_lock, 1); + NFSUNLOCKV4ROOTMUTEX(); + goto out; + } + /* * Free up all layouts on the clientid. Should the client return the * layouts? @@ -1374,7 +1387,7 @@ nfsrv_cleanclient(struct nfsclient *clp, NFSPROC_T *p) nfsrv_freeopenowner(stp, 1, p); if ((clp->lc_flags & LCL_ADMINREVOKED) == 0) LIST_FOREACH_SAFE(sep, &clp->lc_session, sess_list, nsep) - (void)nfsrv_freesession(sep, NULL); + (void)nfsrv_freesession(NULL, sep, NULL); } /* @@ -4605,7 +4618,7 @@ nfsrv_docallback(struct nfsclient *clp, int procnum, nfsv4stateid_t *stateidp, if (procnum != NFSV4PROC_CBNULL) nfsv4_freeslot(&sep->sess_cbsess, slotpos, true); - nfsrv_freesession(sep, NULL); + nfsrv_freesession(NULL, sep, NULL); } else if (nd->nd_procnum == NFSV4PROC_CBNULL) error = newnfs_connect(NULL, &clp->lc_req, cred, NULL, 1, dotls, &clp->lc_req.nr_client); @@ -4654,7 +4667,7 @@ nfsrv_docallback(struct nfsclient *clp, int procnum, nfsv4stateid_t *stateidp, nfsv4_freeslot(&sep->sess_cbsess, slotpos, true); } - nfsrv_freesession(sep, NULL); + nfsrv_freesession(NULL, sep, NULL); } else error = newnfs_request(nd, NULL, clp, &clp->lc_req, NULL, NULL, cred, clp->lc_program, @@ -5877,12 +5890,18 @@ nfsrv_throwawayopens(NFSPROC_T *p) /* * This function checks to see if the credentials are the same. * The check for same credentials is needed for state management operations - * for NFSv4.0 where 1 is returned if not same, 0 is returned otherwise. + * for NFSv4.0 or NFSv4.1/4.2 when SP4_MACH_CRED is configured via + * ExchangeID. + * Returns 1 for not same, 0 otherwise. */ static int -nfsrv_notsamecredname(struct nfsrv_descript *nd, struct nfsclient *clp) +nfsrv_notsamecredname(int op, struct nfsrv_descript *nd, struct nfsclient *clp) { + /* Check for the SP4_MACH_CRED case. */ + if (op != 0 && nfsrv_checkmachcred(op, nd, clp) != 0) + return (1); + /* For NFSv4.1/4.2, SP4_NONE always allows this. */ if ((nd->nd_flag & ND_NFSV41) != 0) return (0); @@ -6301,6 +6320,16 @@ nfsrv_checksequence(struct nfsrv_descript *nd, uint32_t sequenceid, nd->nd_clientid.qval = sep->sess_clp->lc_clientid.qval; nd->nd_flag |= ND_IMPLIEDCLID; + /* Handle the SP4_MECH_CRED case for NFSv4.1/4.2. */ + if ((sep->sess_clp->lc_flags & LCL_MACHCRED) != 0 && + (nd->nd_flag & (ND_GSSINTEGRITY | ND_GSSPRIVACY)) != 0 && + nd->nd_princlen == sep->sess_clp->lc_namelen && + !NFSBCMP(sep->sess_clp->lc_name, nd->nd_principal, + nd->nd_princlen)) { + nd->nd_flag |= ND_MACHCRED; + NFSSET_OPBIT(&nd->nd_allowops, &sep->sess_clp->lc_allowops); + } + /* Save maximum request and reply sizes. */ nd->nd_maxreq = sep->sess_maxreq; nd->nd_maxresp = sep->sess_maxresp; @@ -6458,7 +6487,7 @@ nfsrv_destroysession(struct nfsrv_descript *nd, uint8_t *sessionid) } while (igotlock == 0); NFSUNLOCKV4ROOTMUTEX(); - error = nfsrv_freesession(NULL, sessionid); + error = nfsrv_freesession(nd, NULL, sessionid); if (error == 0 && samesess != 0) nd->nd_flag &= ~ND_HASSEQUENCE; @@ -6491,6 +6520,9 @@ nfsrv_bindconnsess(struct nfsrv_descript *nd, uint8_t *sessionid, int *foreaftp) sep = nfsrv_findsession(sessionid); if (sep != NULL) { clp = sep->sess_clp; + error = nfsrv_checkmachcred(NFSV4OP_BINDCONNTOSESS, nd, clp); + if (error != 0) + goto out; if (*foreaftp == NFSCDFC4_BACK || *foreaftp == NFSCDFC4_BACK_OR_BOTH || *foreaftp == NFSCDFC4_FORE_OR_BOTH) { @@ -6538,6 +6570,7 @@ nfsrv_bindconnsess(struct nfsrv_descript *nd, uint8_t *sessionid, int *foreaftp) } } else error = NFSERR_BADSESSION; +out: NFSUNLOCKSESSION(shp); NFSUNLOCKSTATE(); if (savxprt != NULL) @@ -6549,7 +6582,8 @@ nfsrv_bindconnsess(struct nfsrv_descript *nd, uint8_t *sessionid, int *foreaftp) * Free up a session structure. */ static int -nfsrv_freesession(struct nfsdsession *sep, uint8_t *sessionid) +nfsrv_freesession(struct nfsrv_descript *nd, struct nfsdsession *sep, + uint8_t *sessionid) { struct nfssessionhash *shp; int i; @@ -6564,6 +6598,14 @@ nfsrv_freesession(struct nfsdsession *sep, uint8_t *sessionid) NFSLOCKSESSION(shp); } if (sep != NULL) { + /* Check for the SP4_MACH_CRED case. */ + if (nd != NULL && nfsrv_checkmachcred(NFSV4OP_DESTROYSESSION, + nd, sep->sess_clp) != 0) { + NFSUNLOCKSESSION(shp); + NFSUNLOCKSTATE(); + return (NFSERR_AUTHERR | AUTH_TOOWEAK); + } + sep->sess_refcnt--; if (sep->sess_refcnt > 0) { NFSUNLOCKSESSION(shp); @@ -8883,3 +8925,23 @@ nfsrv_marknospc(char *devid, bool setit) NFSUNLOCKLAYOUT(lhyp); } } + +/* + * Check to see if SP4_MACH_CRED is in use and, if it is, check that the + * correct machine credential is being used. + */ +static int +nfsrv_checkmachcred(int op, struct nfsrv_descript *nd, struct nfsclient *clp) +{ + + if ((clp->lc_flags & LCL_MACHCRED) == 0 || + !NFSISSET_OPBIT(&clp->lc_mustops, op)) + return (0); + KASSERT((nd->nd_flag & ND_NFSV41) != 0, + ("nfsrv_checkmachcred: MachCred for NFSv4.0")); + if ((nd->nd_flag & (ND_GSSINTEGRITY | ND_GSSPRIVACY)) != 0 && + nd->nd_princlen == clp->lc_namelen && + !NFSBCMP(nd->nd_principal, clp->lc_name, nd->nd_princlen)) + return (0); + return (NFSERR_AUTHERR | AUTH_TOOWEAK); +} |