aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRick Macklem <rmacklem@FreeBSD.org>2023-04-07 19:49:23 +0000
committerRick Macklem <rmacklem@FreeBSD.org>2023-04-07 19:49:23 +0000
commitff2f1f691cdb376be70b9579d2f9c91b06ac839d (patch)
treeda930f7c995b72a5dd381f1bd3f56fc9de828d13
parent66fbc19fbd7c75fae619ead787d90242d96cc002 (diff)
downloadsrc-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.h2
-rw-r--r--sys/fs/nfs/nfsrvstate.h2
-rw-r--r--sys/fs/nfsserver/nfs_nfsdkrpc.c58
-rw-r--r--sys/fs/nfsserver/nfs_nfsdserv.c29
-rw-r--r--sys/fs/nfsserver/nfs_nfsdsocket.c55
-rw-r--r--sys/fs/nfsserver/nfs_nfsdstate.c90
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);
+}