summaryrefslogtreecommitdiffstats
path: root/sys/fs
diff options
context:
space:
mode:
authorrmacklem <rmacklem@FreeBSD.org>2015-11-30 21:54:27 +0000
committerrmacklem <rmacklem@FreeBSD.org>2015-11-30 21:54:27 +0000
commit493738a552c2193ff56c48f7c396646355f12399 (patch)
tree3413b8998a4ed8104618ba268341afb5a4ed9b07 /sys/fs
parent64ba6ed15d650e7c9ca0bc58c06af83419b03bdb (diff)
downloadFreeBSD-src-493738a552c2193ff56c48f7c396646355f12399.zip
FreeBSD-src-493738a552c2193ff56c48f7c396646355f12399.tar.gz
Add kernel support to the NFS server for the "-manage-gids"
option that will be added to the nfsuserd daemon in a future commit. It modifies the cache used by NFSv4 for name<-->id translation (both username/uid and group/gid) to support this. When "-manage-gids" is set, the server looks up each uid for the RPC and uses the list of groups cached in the server instead of the list of groups provided in the RPC request. The cached group list is acquired for the cache by the nfsuserd daemon via getgrouplist(3). This avoids the 16 groups limit for the list in the RPC request. Since the cache is now used for every RPC when "-manage-gids" is enabled, the code also modifies the cache to use a separate mutex for each hash list instead of a single global mutex. Suggested by: jpaetzel Tested by: jpaetzel MFC after: 2 weeks
Diffstat (limited to 'sys/fs')
-rw-r--r--sys/fs/nfs/nfs.h18
-rw-r--r--sys/fs/nfs/nfs_commonport.c22
-rw-r--r--sys/fs/nfs/nfs_commonsubs.c683
-rw-r--r--sys/fs/nfs/nfs_var.h1
-rw-r--r--sys/fs/nfs/nfsrvstate.h17
-rw-r--r--sys/fs/nfsserver/nfs_nfsdport.c26
6 files changed, 547 insertions, 220 deletions
diff --git a/sys/fs/nfs/nfs.h b/sys/fs/nfs/nfs.h
index be60c1c..5781459 100644
--- a/sys/fs/nfs/nfs.h
+++ b/sys/fs/nfs/nfs.h
@@ -96,12 +96,6 @@
#define NFSSESSIONHASHSIZE 20 /* Size of server session hash table */
#endif
#define NFSSTATEHASHSIZE 10 /* Size of server stateid hash table */
-#ifndef NFSUSERHASHSIZE
-#define NFSUSERHASHSIZE 30 /* Size of user id hash table */
-#endif
-#ifndef NFSGROUPHASHSIZE
-#define NFSGROUPHASHSIZE 5 /* Size of group id hash table */
-#endif
#ifndef NFSCLDELEGHIGHWATER
#define NFSCLDELEGHIGHWATER 10000 /* limit for client delegations */
#endif
@@ -204,6 +198,18 @@ struct nfsd_idargs {
int nid_usertimeout;/* User name timeout (minutes) */
u_char *nid_name; /* Name */
int nid_namelen; /* and its length */
+ gid_t *nid_grps; /* and the list */
+ int nid_ngroup; /* Size of groups list */
+};
+
+struct nfsd_oidargs {
+ int nid_flag; /* Flags (see below) */
+ uid_t nid_uid; /* user/group id */
+ gid_t nid_gid;
+ int nid_usermax; /* Upper bound on user name cache */
+ int nid_usertimeout;/* User name timeout (minutes) */
+ u_char *nid_name; /* Name */
+ int nid_namelen; /* and its length */
};
struct nfsd_clid {
diff --git a/sys/fs/nfs/nfs_commonport.c b/sys/fs/nfs/nfs_commonport.c
index bc5d9de..a4a4b7b 100644
--- a/sys/fs/nfs/nfs_commonport.c
+++ b/sys/fs/nfs/nfs_commonport.c
@@ -63,6 +63,7 @@ int nfs_numnfscbd = 0;
int nfscl_debuglevel = 0;
char nfsv4_callbackaddr[INET6_ADDRSTRLEN];
struct callout newnfsd_callout;
+int nfsrv_lughashsize = 100;
void (*nfsd_call_servertimer)(void) = NULL;
void (*ncl_call_invalcaches)(struct vnode *) = NULL;
@@ -79,6 +80,9 @@ SYSCTL_STRING(_vfs_nfs, OID_AUTO, callback_addr, CTLFLAG_RW,
"NFSv4 callback addr for server to use");
SYSCTL_INT(_vfs_nfs, OID_AUTO, debuglevel, CTLFLAG_RW, &nfscl_debuglevel,
0, "Debug level for NFS client");
+TUNABLE_INT("vfs.nfs.userhashsize", &nfsrv_lughashsize);
+SYSCTL_INT(_vfs_nfs, OID_AUTO, userhashsize, CTLFLAG_RDTUN, &nfsrv_lughashsize,
+ 0, "Size of hash tables for uid/name mapping");
/*
* Defines for malloc
@@ -445,9 +449,25 @@ nfssvc_call(struct thread *p, struct nfssvc_args *uap, struct ucred *cred)
{
int error = EINVAL;
struct nfsd_idargs nid;
+ struct nfsd_oidargs onid;
if (uap->flag & NFSSVC_IDNAME) {
- error = copyin(uap->argp, (caddr_t)&nid, sizeof (nid));
+ if ((uap->flag & NFSSVC_NEWSTRUCT) != 0)
+ error = copyin(uap->argp, &nid, sizeof(nid));
+ else {
+ error = copyin(uap->argp, &onid, sizeof(onid));
+ if (error == 0) {
+ nid.nid_flag = onid.nid_flag;
+ nid.nid_uid = onid.nid_uid;
+ nid.nid_gid = onid.nid_gid;
+ nid.nid_usermax = onid.nid_usermax;
+ nid.nid_usertimeout = onid.nid_usertimeout;
+ nid.nid_name = onid.nid_name;
+ nid.nid_namelen = onid.nid_namelen;
+ nid.nid_ngroup = 0;
+ nid.nid_grps = NULL;
+ }
+ }
if (error)
goto out;
error = nfssvc_idname(&nid);
diff --git a/sys/fs/nfs/nfs_commonsubs.c b/sys/fs/nfs/nfs_commonsubs.c
index 0f6db32..11db5a5 100644
--- a/sys/fs/nfs/nfs_commonsubs.c
+++ b/sys/fs/nfs/nfs_commonsubs.c
@@ -44,6 +44,8 @@ __FBSDID("$FreeBSD$");
#include <fs/nfs/nfsport.h>
+#include <security/mac/mac_framework.h>
+
/*
* Data items converted to xdr at startup, since they are constant
* This is kinda hokey, but may save a little time doing byte swaps
@@ -68,6 +70,7 @@ int ncl_mbuf_mlen = MLEN;
int nfsd_enable_stringtouid = 0;
NFSNAMEIDMUTEX;
NFSSOCKMUTEX;
+extern int nfsrv_lughashsize;
/*
* This array of structures indicates, for V4:
@@ -154,11 +157,14 @@ static int nfsrv_usercnt = 0;
static int nfsrv_dnsnamelen;
static u_char *nfsrv_dnsname = NULL;
static int nfsrv_usermax = 999999999;
-static struct nfsuserhashhead nfsuserhash[NFSUSERHASHSIZE];
-static struct nfsuserhashhead nfsusernamehash[NFSUSERHASHSIZE];
-static struct nfsuserhashhead nfsgrouphash[NFSGROUPHASHSIZE];
-static struct nfsuserhashhead nfsgroupnamehash[NFSGROUPHASHSIZE];
-static struct nfsuserlruhead nfsuserlruhead;
+struct nfsrv_lughash {
+ struct mtx mtx;
+ struct nfsuserhashhead lughead;
+};
+static struct nfsrv_lughash *nfsuserhash;
+static struct nfsrv_lughash *nfsusernamehash;
+static struct nfsrv_lughash *nfsgrouphash;
+static struct nfsrv_lughash *nfsgroupnamehash;
/*
* This static array indicates whether or not the RPC generates a large
@@ -177,7 +183,7 @@ static void nfsv4_wanted(struct nfsv4lock *lp);
static int nfsrv_cmpmixedcase(u_char *cp, u_char *cp2, int len);
static int nfsrv_getuser(int procnum, uid_t uid, gid_t gid, char *name,
NFSPROC_T *p);
-static void nfsrv_removeuser(struct nfsusrgrp *usrp);
+static void nfsrv_removeuser(struct nfsusrgrp *usrp, int isuser);
static int nfsrv_getrefstr(struct nfsrv_descript *, u_char **, u_char **,
int *, int *);
static void nfsrv_refstrbigenough(int, u_char **, u_char **, int *);
@@ -2548,18 +2554,17 @@ nfsv4_uidtostr(uid_t uid, u_char **cpp, int *retlenp, NFSPROC_T *p)
u_char *cp = *cpp;
uid_t tmp;
int cnt, hasampersand, len = NFSV4_SMALLSTR, ret;
+ struct nfsrv_lughash *hp;
cnt = 0;
tryagain:
- NFSLOCKNAMEID();
- if (nfsrv_dnsname) {
+ if (nfsrv_dnsnamelen > 0) {
/*
* Always map nfsrv_defaultuid to "nobody".
*/
if (uid == nfsrv_defaultuid) {
i = nfsrv_dnsnamelen + 7;
if (i > len) {
- NFSUNLOCKNAMEID();
if (len > NFSV4_SMALLSTR)
free(cp, M_NFSSTRING);
cp = malloc(i, M_NFSSTRING, M_WAITOK);
@@ -2571,11 +2576,12 @@ tryagain:
NFSBCOPY("nobody@", cp, 7);
cp += 7;
NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
- NFSUNLOCKNAMEID();
return;
}
hasampersand = 0;
- LIST_FOREACH(usrp, NFSUSERHASH(uid), lug_numhash) {
+ hp = NFSUSERHASH(uid);
+ mtx_lock(&hp->mtx);
+ TAILQ_FOREACH(usrp, &hp->lughead, lug_numhash) {
if (usrp->lug_uid == uid) {
if (usrp->lug_expiry < NFSD_MONOSEC)
break;
@@ -2595,7 +2601,7 @@ tryagain:
i = usrp->lug_namelen +
nfsrv_dnsnamelen + 1;
if (i > len) {
- NFSUNLOCKNAMEID();
+ mtx_unlock(&hp->mtx);
if (len > NFSV4_SMALLSTR)
free(cp, M_NFSSTRING);
cp = malloc(i, M_NFSSTRING, M_WAITOK);
@@ -2610,20 +2616,19 @@ tryagain:
*cp++ = '@';
NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
}
- TAILQ_REMOVE(&nfsuserlruhead, usrp, lug_lru);
- TAILQ_INSERT_TAIL(&nfsuserlruhead, usrp, lug_lru);
- NFSUNLOCKNAMEID();
+ TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
+ TAILQ_INSERT_TAIL(&hp->lughead, usrp,
+ lug_numhash);
+ mtx_unlock(&hp->mtx);
return;
}
}
- NFSUNLOCKNAMEID();
+ mtx_unlock(&hp->mtx);
cnt++;
ret = nfsrv_getuser(RPCNFSUSERD_GETUID, uid, (gid_t)0,
NULL, p);
if (ret == 0 && cnt < 2)
goto tryagain;
- } else {
- NFSUNLOCKNAMEID();
}
/*
@@ -2647,6 +2652,52 @@ tryagain:
}
/*
+ * Get a credential for the uid with the server's group list.
+ * If none is found, just return the credential passed in after
+ * logging a warning message.
+ */
+struct ucred *
+nfsrv_getgrpscred(struct ucred *oldcred)
+{
+ struct nfsusrgrp *usrp;
+ struct ucred *newcred;
+ int cnt, ret;
+ uid_t uid;
+ struct nfsrv_lughash *hp;
+
+ cnt = 0;
+ uid = oldcred->cr_uid;
+tryagain:
+ if (nfsrv_dnsnamelen > 0) {
+ hp = NFSUSERHASH(uid);
+ mtx_lock(&hp->mtx);
+ TAILQ_FOREACH(usrp, &hp->lughead, lug_numhash) {
+ if (usrp->lug_uid == uid) {
+ if (usrp->lug_expiry < NFSD_MONOSEC)
+ break;
+ if (usrp->lug_cred != NULL) {
+ newcred = crhold(usrp->lug_cred);
+ crfree(oldcred);
+ } else
+ newcred = oldcred;
+ TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
+ TAILQ_INSERT_TAIL(&hp->lughead, usrp,
+ lug_numhash);
+ mtx_unlock(&hp->mtx);
+ return (newcred);
+ }
+ }
+ mtx_unlock(&hp->mtx);
+ cnt++;
+ ret = nfsrv_getuser(RPCNFSUSERD_GETUID, uid, (gid_t)0,
+ NULL, curthread);
+ if (ret == 0 && cnt < 2)
+ goto tryagain;
+ }
+ return (oldcred);
+}
+
+/*
* Convert a string to a uid.
* If no conversion is possible return NFSERR_BADOWNER, otherwise
* return 0.
@@ -2664,6 +2715,7 @@ nfsv4_strtouid(struct nfsrv_descript *nd, u_char *str, int len, uid_t *uidp,
int cnt, ret;
int error = 0;
uid_t tuid;
+ struct nfsrv_lughash *hp, *hp2;
if (len == 0) {
error = NFSERR_BADOWNER;
@@ -2693,49 +2745,55 @@ nfsv4_strtouid(struct nfsrv_descript *nd, u_char *str, int len, uid_t *uidp,
cnt = 0;
tryagain:
- NFSLOCKNAMEID();
- /*
- * If an '@' is found and the domain name matches, search for the name
- * with dns stripped off.
- * Mixed case alpahbetics will match for the domain name, but all
- * upper case will not.
- */
- if (cnt == 0 && i < len && i > 0 && nfsrv_dnsname &&
- (len - 1 - i) == nfsrv_dnsnamelen &&
- !nfsrv_cmpmixedcase(cp, nfsrv_dnsname, nfsrv_dnsnamelen)) {
- len -= (nfsrv_dnsnamelen + 1);
- *(cp - 1) = '\0';
- }
-
- /*
- * Check for the special case of "nobody".
- */
- if (len == 6 && !NFSBCMP(str, "nobody", 6)) {
- *uidp = nfsrv_defaultuid;
- NFSUNLOCKNAMEID();
- error = 0;
- goto out;
- }
-
- LIST_FOREACH(usrp, NFSUSERNAMEHASH(str, len), lug_namehash) {
- if (usrp->lug_namelen == len &&
- !NFSBCMP(usrp->lug_name, str, len)) {
- if (usrp->lug_expiry < NFSD_MONOSEC)
- break;
- *uidp = usrp->lug_uid;
- TAILQ_REMOVE(&nfsuserlruhead, usrp, lug_lru);
- TAILQ_INSERT_TAIL(&nfsuserlruhead, usrp, lug_lru);
- NFSUNLOCKNAMEID();
+ if (nfsrv_dnsnamelen > 0) {
+ /*
+ * If an '@' is found and the domain name matches, search for
+ * the name with dns stripped off.
+ * Mixed case alpahbetics will match for the domain name, but
+ * all upper case will not.
+ */
+ if (cnt == 0 && i < len && i > 0 &&
+ (len - 1 - i) == nfsrv_dnsnamelen &&
+ !nfsrv_cmpmixedcase(cp, nfsrv_dnsname, nfsrv_dnsnamelen)) {
+ len -= (nfsrv_dnsnamelen + 1);
+ *(cp - 1) = '\0';
+ }
+
+ /*
+ * Check for the special case of "nobody".
+ */
+ if (len == 6 && !NFSBCMP(str, "nobody", 6)) {
+ *uidp = nfsrv_defaultuid;
error = 0;
goto out;
}
+
+ hp = NFSUSERNAMEHASH(str, len);
+ mtx_lock(&hp->mtx);
+ TAILQ_FOREACH(usrp, &hp->lughead, lug_namehash) {
+ if (usrp->lug_namelen == len &&
+ !NFSBCMP(usrp->lug_name, str, len)) {
+ if (usrp->lug_expiry < NFSD_MONOSEC)
+ break;
+ hp2 = NFSUSERHASH(usrp->lug_uid);
+ mtx_lock(&hp2->mtx);
+ TAILQ_REMOVE(&hp2->lughead, usrp, lug_numhash);
+ TAILQ_INSERT_TAIL(&hp2->lughead, usrp,
+ lug_numhash);
+ *uidp = usrp->lug_uid;
+ mtx_unlock(&hp2->mtx);
+ mtx_unlock(&hp->mtx);
+ error = 0;
+ goto out;
+ }
+ }
+ mtx_unlock(&hp->mtx);
+ cnt++;
+ ret = nfsrv_getuser(RPCNFSUSERD_GETUSER, (uid_t)0, (gid_t)0,
+ str, p);
+ if (ret == 0 && cnt < 2)
+ goto tryagain;
}
- NFSUNLOCKNAMEID();
- cnt++;
- ret = nfsrv_getuser(RPCNFSUSERD_GETUSER, (uid_t)0, (gid_t)0,
- str, p);
- if (ret == 0 && cnt < 2)
- goto tryagain;
error = NFSERR_BADOWNER;
out:
@@ -2758,18 +2816,17 @@ nfsv4_gidtostr(gid_t gid, u_char **cpp, int *retlenp, NFSPROC_T *p)
u_char *cp = *cpp;
gid_t tmp;
int cnt, hasampersand, len = NFSV4_SMALLSTR, ret;
+ struct nfsrv_lughash *hp;
cnt = 0;
tryagain:
- NFSLOCKNAMEID();
- if (nfsrv_dnsname) {
+ if (nfsrv_dnsnamelen > 0) {
/*
* Always map nfsrv_defaultgid to "nogroup".
*/
if (gid == nfsrv_defaultgid) {
i = nfsrv_dnsnamelen + 8;
if (i > len) {
- NFSUNLOCKNAMEID();
if (len > NFSV4_SMALLSTR)
free(cp, M_NFSSTRING);
cp = malloc(i, M_NFSSTRING, M_WAITOK);
@@ -2781,11 +2838,12 @@ tryagain:
NFSBCOPY("nogroup@", cp, 8);
cp += 8;
NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
- NFSUNLOCKNAMEID();
return;
}
hasampersand = 0;
- LIST_FOREACH(usrp, NFSGROUPHASH(gid), lug_numhash) {
+ hp = NFSGROUPHASH(gid);
+ mtx_lock(&hp->mtx);
+ TAILQ_FOREACH(usrp, &hp->lughead, lug_numhash) {
if (usrp->lug_gid == gid) {
if (usrp->lug_expiry < NFSD_MONOSEC)
break;
@@ -2805,7 +2863,7 @@ tryagain:
i = usrp->lug_namelen +
nfsrv_dnsnamelen + 1;
if (i > len) {
- NFSUNLOCKNAMEID();
+ mtx_unlock(&hp->mtx);
if (len > NFSV4_SMALLSTR)
free(cp, M_NFSSTRING);
cp = malloc(i, M_NFSSTRING, M_WAITOK);
@@ -2820,20 +2878,19 @@ tryagain:
*cp++ = '@';
NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
}
- TAILQ_REMOVE(&nfsuserlruhead, usrp, lug_lru);
- TAILQ_INSERT_TAIL(&nfsuserlruhead, usrp, lug_lru);
- NFSUNLOCKNAMEID();
+ TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
+ TAILQ_INSERT_TAIL(&hp->lughead, usrp,
+ lug_numhash);
+ mtx_unlock(&hp->mtx);
return;
}
}
- NFSUNLOCKNAMEID();
+ mtx_unlock(&hp->mtx);
cnt++;
ret = nfsrv_getuser(RPCNFSUSERD_GETGID, (uid_t)0, gid,
NULL, p);
if (ret == 0 && cnt < 2)
goto tryagain;
- } else {
- NFSUNLOCKNAMEID();
}
/*
@@ -2874,6 +2931,7 @@ nfsv4_strtogid(struct nfsrv_descript *nd, u_char *str, int len, gid_t *gidp,
int cnt, ret;
int error = 0;
gid_t tgid;
+ struct nfsrv_lughash *hp, *hp2;
if (len == 0) {
error = NFSERR_BADOWNER;
@@ -2903,47 +2961,53 @@ nfsv4_strtogid(struct nfsrv_descript *nd, u_char *str, int len, gid_t *gidp,
cnt = 0;
tryagain:
- NFSLOCKNAMEID();
- /*
- * If an '@' is found and the dns name matches, search for the name
- * with the dns stripped off.
- */
- if (cnt == 0 && i < len && i > 0 && nfsrv_dnsname &&
- (len - 1 - i) == nfsrv_dnsnamelen &&
- !nfsrv_cmpmixedcase(cp, nfsrv_dnsname, nfsrv_dnsnamelen)) {
- len -= (nfsrv_dnsnamelen + 1);
- *(cp - 1) = '\0';
- }
-
- /*
- * Check for the special case of "nogroup".
- */
- if (len == 7 && !NFSBCMP(str, "nogroup", 7)) {
- *gidp = nfsrv_defaultgid;
- NFSUNLOCKNAMEID();
- error = 0;
- goto out;
- }
-
- LIST_FOREACH(usrp, NFSGROUPNAMEHASH(str, len), lug_namehash) {
- if (usrp->lug_namelen == len &&
- !NFSBCMP(usrp->lug_name, str, len)) {
- if (usrp->lug_expiry < NFSD_MONOSEC)
- break;
- *gidp = usrp->lug_gid;
- TAILQ_REMOVE(&nfsuserlruhead, usrp, lug_lru);
- TAILQ_INSERT_TAIL(&nfsuserlruhead, usrp, lug_lru);
- NFSUNLOCKNAMEID();
+ if (nfsrv_dnsnamelen > 0) {
+ /*
+ * If an '@' is found and the dns name matches, search for the
+ * name with the dns stripped off.
+ */
+ if (cnt == 0 && i < len && i > 0 &&
+ (len - 1 - i) == nfsrv_dnsnamelen &&
+ !nfsrv_cmpmixedcase(cp, nfsrv_dnsname, nfsrv_dnsnamelen)) {
+ len -= (nfsrv_dnsnamelen + 1);
+ *(cp - 1) = '\0';
+ }
+
+ /*
+ * Check for the special case of "nogroup".
+ */
+ if (len == 7 && !NFSBCMP(str, "nogroup", 7)) {
+ *gidp = nfsrv_defaultgid;
error = 0;
goto out;
}
+
+ hp = NFSGROUPNAMEHASH(str, len);
+ mtx_lock(&hp->mtx);
+ TAILQ_FOREACH(usrp, &hp->lughead, lug_namehash) {
+ if (usrp->lug_namelen == len &&
+ !NFSBCMP(usrp->lug_name, str, len)) {
+ if (usrp->lug_expiry < NFSD_MONOSEC)
+ break;
+ hp2 = NFSGROUPHASH(usrp->lug_gid);
+ mtx_lock(&hp2->mtx);
+ TAILQ_REMOVE(&hp2->lughead, usrp, lug_numhash);
+ TAILQ_INSERT_TAIL(&hp2->lughead, usrp,
+ lug_numhash);
+ *gidp = usrp->lug_gid;
+ mtx_unlock(&hp2->mtx);
+ mtx_unlock(&hp->mtx);
+ error = 0;
+ goto out;
+ }
+ }
+ mtx_unlock(&hp->mtx);
+ cnt++;
+ ret = nfsrv_getuser(RPCNFSUSERD_GETGROUP, (uid_t)0, (gid_t)0,
+ str, p);
+ if (ret == 0 && cnt < 2)
+ goto tryagain;
}
- NFSUNLOCKNAMEID();
- cnt++;
- ret = nfsrv_getuser(RPCNFSUSERD_GETGROUP, (uid_t)0, (gid_t)0,
- str, p);
- if (ret == 0 && cnt < 2)
- goto tryagain;
error = NFSERR_BADOWNER;
out:
@@ -3101,111 +3165,218 @@ APPLESTATIC int
nfssvc_idname(struct nfsd_idargs *nidp)
{
struct nfsusrgrp *nusrp, *usrp, *newusrp;
- struct nfsuserhashhead *hp;
- int i;
+ struct nfsrv_lughash *hp_name, *hp_idnum, *thp;
+ int i, group_locked, groupname_locked, user_locked, username_locked;
int error = 0;
u_char *cp;
+ gid_t *grps;
+ struct ucred *cr;
+ static int onethread = 0;
+ static time_t lasttime = 0;
if (nidp->nid_flag & NFSID_INITIALIZE) {
- cp = (u_char *)malloc(nidp->nid_namelen + 1,
- M_NFSSTRING, M_WAITOK);
- error = copyin(CAST_USER_ADDR_T(nidp->nid_name), cp,
- nidp->nid_namelen);
- NFSLOCKNAMEID();
- if (nfsrv_dnsname) {
+ cp = malloc(nidp->nid_namelen + 1, M_NFSSTRING, M_WAITOK);
+ error = copyin(CAST_USER_ADDR_T(nidp->nid_name), cp,
+ nidp->nid_namelen);
+ if (error != 0) {
+ free(cp, M_NFSSTRING);
+ goto out;
+ }
+ if (atomic_cmpset_acq_int(&nfsrv_dnsnamelen, 0, 0) == 0) {
+ /*
+ * Free up all the old stuff and reinitialize hash
+ * lists. All mutexes for both lists must be locked,
+ * with the user/group name ones before the uid/gid
+ * ones, to avoid a LOR.
+ */
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_lock(&nfsusernamehash[i].mtx);
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_lock(&nfsuserhash[i].mtx);
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ TAILQ_FOREACH_SAFE(usrp,
+ &nfsuserhash[i].lughead, lug_numhash, nusrp)
+ nfsrv_removeuser(usrp, 1);
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_unlock(&nfsuserhash[i].mtx);
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_unlock(&nfsusernamehash[i].mtx);
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_lock(&nfsgroupnamehash[i].mtx);
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_lock(&nfsgrouphash[i].mtx);
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ TAILQ_FOREACH_SAFE(usrp,
+ &nfsgrouphash[i].lughead, lug_numhash,
+ nusrp)
+ nfsrv_removeuser(usrp, 0);
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_unlock(&nfsgrouphash[i].mtx);
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_unlock(&nfsgroupnamehash[i].mtx);
+ free(nfsrv_dnsname, M_NFSSTRING);
+ nfsrv_dnsname = NULL;
+ }
+ if (nfsuserhash == NULL) {
+ /* Allocate the hash tables. */
+ nfsuserhash = malloc(sizeof(struct nfsrv_lughash) *
+ nfsrv_lughashsize, M_NFSUSERGROUP, M_WAITOK |
+ M_ZERO);
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_init(&nfsuserhash[i].mtx, "nfsuidhash",
+ NULL, MTX_DEF | MTX_DUPOK);
+ nfsusernamehash = malloc(sizeof(struct nfsrv_lughash) *
+ nfsrv_lughashsize, M_NFSUSERGROUP, M_WAITOK |
+ M_ZERO);
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_init(&nfsusernamehash[i].mtx,
+ "nfsusrhash", NULL, MTX_DEF |
+ MTX_DUPOK);
+ nfsgrouphash = malloc(sizeof(struct nfsrv_lughash) *
+ nfsrv_lughashsize, M_NFSUSERGROUP, M_WAITOK |
+ M_ZERO);
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_init(&nfsgrouphash[i].mtx, "nfsgidhash",
+ NULL, MTX_DEF | MTX_DUPOK);
+ nfsgroupnamehash = malloc(sizeof(struct nfsrv_lughash) *
+ nfsrv_lughashsize, M_NFSUSERGROUP, M_WAITOK |
+ M_ZERO);
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_init(&nfsgroupnamehash[i].mtx,
+ "nfsgrphash", NULL, MTX_DEF | MTX_DUPOK);
+ }
+ /* (Re)initialize the list heads. */
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ TAILQ_INIT(&nfsuserhash[i].lughead);
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ TAILQ_INIT(&nfsusernamehash[i].lughead);
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ TAILQ_INIT(&nfsgrouphash[i].lughead);
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ TAILQ_INIT(&nfsgroupnamehash[i].lughead);
+
/*
- * Free up all the old stuff and reinitialize hash lists.
+ * Put name in "DNS" string.
*/
- TAILQ_FOREACH_SAFE(usrp, &nfsuserlruhead, lug_lru, nusrp) {
- nfsrv_removeuser(usrp);
- }
- free(nfsrv_dnsname, M_NFSSTRING);
- nfsrv_dnsname = NULL;
- }
- TAILQ_INIT(&nfsuserlruhead);
- for (i = 0; i < NFSUSERHASHSIZE; i++)
- LIST_INIT(&nfsuserhash[i]);
- for (i = 0; i < NFSGROUPHASHSIZE; i++)
- LIST_INIT(&nfsgrouphash[i]);
- for (i = 0; i < NFSUSERHASHSIZE; i++)
- LIST_INIT(&nfsusernamehash[i]);
- for (i = 0; i < NFSGROUPHASHSIZE; i++)
- LIST_INIT(&nfsgroupnamehash[i]);
-
- /*
- * Put name in "DNS" string.
- */
- if (!error) {
nfsrv_dnsname = cp;
- nfsrv_dnsnamelen = nidp->nid_namelen;
nfsrv_defaultuid = nidp->nid_uid;
nfsrv_defaultgid = nidp->nid_gid;
nfsrv_usercnt = 0;
nfsrv_usermax = nidp->nid_usermax;
- }
- NFSUNLOCKNAMEID();
- if (error)
- free(cp, M_NFSSTRING);
- goto out;
+ atomic_store_rel_int(&nfsrv_dnsnamelen, nidp->nid_namelen);
+ goto out;
}
/*
* malloc the new one now, so any potential sleep occurs before
* manipulation of the lists.
*/
- MALLOC(newusrp, struct nfsusrgrp *, sizeof (struct nfsusrgrp) +
- nidp->nid_namelen, M_NFSUSERGROUP, M_WAITOK);
+ newusrp = malloc(sizeof(struct nfsusrgrp) + nidp->nid_namelen,
+ M_NFSUSERGROUP, M_WAITOK | M_ZERO);
error = copyin(CAST_USER_ADDR_T(nidp->nid_name), newusrp->lug_name,
nidp->nid_namelen);
+ if (error == 0 && nidp->nid_ngroup > 0 &&
+ (nidp->nid_flag & NFSID_ADDUID) != 0) {
+ grps = malloc(sizeof(gid_t) * nidp->nid_ngroup, M_TEMP,
+ M_WAITOK);
+ error = copyin(CAST_USER_ADDR_T(nidp->nid_grps), grps,
+ sizeof(gid_t) * nidp->nid_ngroup);
+ if (error == 0) {
+ /*
+ * Create a credential just like svc_getcred(),
+ * but using the group list provided.
+ */
+ 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];
+ cr->cr_prison = &prison0;
+ prison_hold(cr->cr_prison);
+#ifdef MAC
+ mac_cred_associate_nfsd(cr);
+#endif
+ newusrp->lug_cred = cr;
+ }
+ free(grps, M_TEMP);
+ }
if (error) {
- free((caddr_t)newusrp, M_NFSUSERGROUP);
+ free(newusrp, M_NFSUSERGROUP);
goto out;
}
newusrp->lug_namelen = nidp->nid_namelen;
- NFSLOCKNAMEID();
+ /*
+ * The lock order is username[0]->[nfsrv_lughashsize - 1] followed
+ * by uid[0]->[nfsrv_lughashsize - 1], with the same for group.
+ * The flags user_locked, username_locked, group_locked and
+ * groupname_locked are set to indicate all of those hash lists are
+ * locked. hp_name != NULL and hp_idnum != NULL indicates that
+ * the respective one mutex is locked.
+ */
+ user_locked = username_locked = group_locked = groupname_locked = 0;
+ hp_name = hp_idnum = NULL;
+
/*
* Delete old entries, as required.
*/
if (nidp->nid_flag & (NFSID_DELUID | NFSID_ADDUID)) {
- hp = NFSUSERHASH(nidp->nid_uid);
- LIST_FOREACH_SAFE(usrp, hp, lug_numhash, nusrp) {
+ /* Must lock all username hash lists first, to avoid a LOR. */
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_lock(&nfsusernamehash[i].mtx);
+ username_locked = 1;
+ hp_idnum = NFSUSERHASH(nidp->nid_uid);
+ mtx_lock(&hp_idnum->mtx);
+ TAILQ_FOREACH_SAFE(usrp, &hp_idnum->lughead, lug_numhash,
+ nusrp) {
if (usrp->lug_uid == nidp->nid_uid)
- nfsrv_removeuser(usrp);
+ nfsrv_removeuser(usrp, 1);
}
- }
- if (nidp->nid_flag & (NFSID_DELUSERNAME | NFSID_ADDUSERNAME)) {
- hp = NFSUSERNAMEHASH(newusrp->lug_name, newusrp->lug_namelen);
- LIST_FOREACH_SAFE(usrp, hp, lug_namehash, nusrp) {
+ } else if (nidp->nid_flag & (NFSID_DELUSERNAME | NFSID_ADDUSERNAME)) {
+ hp_name = NFSUSERNAMEHASH(newusrp->lug_name,
+ newusrp->lug_namelen);
+ mtx_lock(&hp_name->mtx);
+ TAILQ_FOREACH_SAFE(usrp, &hp_name->lughead, lug_namehash,
+ nusrp) {
if (usrp->lug_namelen == newusrp->lug_namelen &&
!NFSBCMP(usrp->lug_name, newusrp->lug_name,
- usrp->lug_namelen))
- nfsrv_removeuser(usrp);
+ usrp->lug_namelen)) {
+ thp = NFSUSERHASH(usrp->lug_uid);
+ mtx_lock(&thp->mtx);
+ nfsrv_removeuser(usrp, 1);
+ mtx_unlock(&thp->mtx);
+ }
}
- }
- if (nidp->nid_flag & (NFSID_DELGID | NFSID_ADDGID)) {
- hp = NFSGROUPHASH(nidp->nid_gid);
- LIST_FOREACH_SAFE(usrp, hp, lug_numhash, nusrp) {
+ hp_idnum = NFSUSERHASH(nidp->nid_uid);
+ mtx_lock(&hp_idnum->mtx);
+ } else if (nidp->nid_flag & (NFSID_DELGID | NFSID_ADDGID)) {
+ /* Must lock all groupname hash lists first, to avoid a LOR. */
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_lock(&nfsgroupnamehash[i].mtx);
+ groupname_locked = 1;
+ hp_idnum = NFSGROUPHASH(nidp->nid_gid);
+ mtx_lock(&hp_idnum->mtx);
+ TAILQ_FOREACH_SAFE(usrp, &hp_idnum->lughead, lug_numhash,
+ nusrp) {
if (usrp->lug_gid == nidp->nid_gid)
- nfsrv_removeuser(usrp);
+ nfsrv_removeuser(usrp, 0);
}
- }
- if (nidp->nid_flag & (NFSID_DELGROUPNAME | NFSID_ADDGROUPNAME)) {
- hp = NFSGROUPNAMEHASH(newusrp->lug_name, newusrp->lug_namelen);
- LIST_FOREACH_SAFE(usrp, hp, lug_namehash, nusrp) {
+ } else if (nidp->nid_flag & (NFSID_DELGROUPNAME | NFSID_ADDGROUPNAME)) {
+ hp_name = NFSGROUPNAMEHASH(newusrp->lug_name,
+ newusrp->lug_namelen);
+ mtx_lock(&hp_name->mtx);
+ TAILQ_FOREACH_SAFE(usrp, &hp_name->lughead, lug_namehash,
+ nusrp) {
if (usrp->lug_namelen == newusrp->lug_namelen &&
!NFSBCMP(usrp->lug_name, newusrp->lug_name,
- usrp->lug_namelen))
- nfsrv_removeuser(usrp);
+ usrp->lug_namelen)) {
+ thp = NFSGROUPHASH(usrp->lug_gid);
+ mtx_lock(&thp->mtx);
+ nfsrv_removeuser(usrp, 0);
+ mtx_unlock(&thp->mtx);
+ }
}
- }
- TAILQ_FOREACH_SAFE(usrp, &nfsuserlruhead, lug_lru, nusrp) {
- if (usrp->lug_expiry < NFSD_MONOSEC)
- nfsrv_removeuser(usrp);
- }
- while (nfsrv_usercnt >= nfsrv_usermax) {
- usrp = TAILQ_FIRST(&nfsuserlruhead);
- nfsrv_removeuser(usrp);
+ hp_idnum = NFSGROUPHASH(nidp->nid_gid);
+ mtx_lock(&hp_idnum->mtx);
}
/*
@@ -3217,23 +3388,129 @@ nfssvc_idname(struct nfsd_idargs *nidp)
newusrp->lug_expiry = NFSD_MONOSEC + 5;
if (nidp->nid_flag & (NFSID_ADDUID | NFSID_ADDUSERNAME)) {
newusrp->lug_uid = nidp->nid_uid;
- LIST_INSERT_HEAD(NFSUSERHASH(newusrp->lug_uid), newusrp,
- lug_numhash);
- LIST_INSERT_HEAD(NFSUSERNAMEHASH(newusrp->lug_name,
- newusrp->lug_namelen), newusrp, lug_namehash);
- TAILQ_INSERT_TAIL(&nfsuserlruhead, newusrp, lug_lru);
- nfsrv_usercnt++;
+ thp = NFSUSERHASH(newusrp->lug_uid);
+ mtx_assert(&thp->mtx, MA_OWNED);
+ TAILQ_INSERT_TAIL(&thp->lughead, newusrp, lug_numhash);
+ thp = NFSUSERNAMEHASH(newusrp->lug_name, newusrp->lug_namelen);
+ mtx_assert(&thp->mtx, MA_OWNED);
+ TAILQ_INSERT_TAIL(&thp->lughead, newusrp, lug_namehash);
+ atomic_add_int(&nfsrv_usercnt, 1);
} else if (nidp->nid_flag & (NFSID_ADDGID | NFSID_ADDGROUPNAME)) {
newusrp->lug_gid = nidp->nid_gid;
- LIST_INSERT_HEAD(NFSGROUPHASH(newusrp->lug_gid), newusrp,
- lug_numhash);
- LIST_INSERT_HEAD(NFSGROUPNAMEHASH(newusrp->lug_name,
- newusrp->lug_namelen), newusrp, lug_namehash);
- TAILQ_INSERT_TAIL(&nfsuserlruhead, newusrp, lug_lru);
- nfsrv_usercnt++;
- } else
- FREE((caddr_t)newusrp, M_NFSUSERGROUP);
- NFSUNLOCKNAMEID();
+ thp = NFSGROUPHASH(newusrp->lug_gid);
+ mtx_assert(&thp->mtx, MA_OWNED);
+ TAILQ_INSERT_TAIL(&thp->lughead, newusrp, lug_numhash);
+ thp = NFSGROUPNAMEHASH(newusrp->lug_name, newusrp->lug_namelen);
+ mtx_assert(&thp->mtx, MA_OWNED);
+ TAILQ_INSERT_TAIL(&thp->lughead, newusrp, lug_namehash);
+ atomic_add_int(&nfsrv_usercnt, 1);
+ } else {
+ if (newusrp->lug_cred != NULL)
+ crfree(newusrp->lug_cred);
+ free(newusrp, M_NFSUSERGROUP);
+ }
+
+ /*
+ * Once per second, allow one thread to trim the cache.
+ */
+ if (lasttime < NFSD_MONOSEC &&
+ atomic_cmpset_acq_int(&onethread, 0, 1) != 0) {
+ /*
+ * First, unlock the single mutexes, so that all entries
+ * can be locked and any LOR is avoided.
+ */
+ if (hp_name != NULL) {
+ mtx_unlock(&hp_name->mtx);
+ hp_name = NULL;
+ }
+ if (hp_idnum != NULL) {
+ mtx_unlock(&hp_idnum->mtx);
+ hp_idnum = NULL;
+ }
+
+ if ((nidp->nid_flag & (NFSID_DELUID | NFSID_ADDUID |
+ NFSID_DELUSERNAME | NFSID_ADDUSERNAME)) != 0) {
+ if (username_locked == 0) {
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_lock(&nfsusernamehash[i].mtx);
+ username_locked = 1;
+ }
+ KASSERT(user_locked == 0,
+ ("nfssvc_idname: user_locked"));
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_lock(&nfsuserhash[i].mtx);
+ user_locked = 1;
+ for (i = 0; i < nfsrv_lughashsize; i++) {
+ TAILQ_FOREACH_SAFE(usrp,
+ &nfsuserhash[i].lughead, lug_numhash,
+ nusrp)
+ if (usrp->lug_expiry < NFSD_MONOSEC)
+ nfsrv_removeuser(usrp, 1);
+ }
+ for (i = 0; i < nfsrv_lughashsize; i++) {
+ /*
+ * Trim the cache using an approximate LRU
+ * algorithm. This code deletes the least
+ * recently used entry on each hash list.
+ */
+ if (nfsrv_usercnt <= nfsrv_usermax)
+ break;
+ usrp = TAILQ_FIRST(&nfsuserhash[i].lughead);
+ if (usrp != NULL)
+ nfsrv_removeuser(usrp, 1);
+ }
+ } else {
+ if (groupname_locked == 0) {
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_lock(&nfsgroupnamehash[i].mtx);
+ groupname_locked = 1;
+ }
+ KASSERT(group_locked == 0,
+ ("nfssvc_idname: group_locked"));
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_lock(&nfsgrouphash[i].mtx);
+ group_locked = 1;
+ for (i = 0; i < nfsrv_lughashsize; i++) {
+ TAILQ_FOREACH_SAFE(usrp,
+ &nfsgrouphash[i].lughead, lug_numhash,
+ nusrp)
+ if (usrp->lug_expiry < NFSD_MONOSEC)
+ nfsrv_removeuser(usrp, 0);
+ }
+ for (i = 0; i < nfsrv_lughashsize; i++) {
+ /*
+ * Trim the cache using an approximate LRU
+ * algorithm. This code deletes the least
+ * recently user entry on each hash list.
+ */
+ if (nfsrv_usercnt <= nfsrv_usermax)
+ break;
+ usrp = TAILQ_FIRST(&nfsgrouphash[i].lughead);
+ if (usrp != NULL)
+ nfsrv_removeuser(usrp, 0);
+ }
+ }
+ lasttime = NFSD_MONOSEC;
+ atomic_store_rel_int(&onethread, 0);
+ }
+
+ /* Now, unlock all locked mutexes. */
+ if (hp_idnum != NULL)
+ mtx_unlock(&hp_idnum->mtx);
+ if (hp_name != NULL)
+ mtx_unlock(&hp_name->mtx);
+ if (user_locked != 0)
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_unlock(&nfsuserhash[i].mtx);
+ if (username_locked != 0)
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_unlock(&nfsusernamehash[i].mtx);
+ if (group_locked != 0)
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_unlock(&nfsgrouphash[i].mtx);
+ if (groupname_locked != 0)
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_unlock(&nfsgroupnamehash[i].mtx);
out:
NFSEXITCODE(error);
return (error);
@@ -3243,15 +3520,29 @@ out:
* Remove a user/group name element.
*/
static void
-nfsrv_removeuser(struct nfsusrgrp *usrp)
+nfsrv_removeuser(struct nfsusrgrp *usrp, int isuser)
{
-
- NFSNAMEIDREQUIRED();
- LIST_REMOVE(usrp, lug_numhash);
- LIST_REMOVE(usrp, lug_namehash);
- TAILQ_REMOVE(&nfsuserlruhead, usrp, lug_lru);
- nfsrv_usercnt--;
- FREE((caddr_t)usrp, M_NFSUSERGROUP);
+ struct nfsrv_lughash *hp;
+
+ if (isuser != 0) {
+ hp = NFSUSERHASH(usrp->lug_uid);
+ mtx_assert(&hp->mtx, MA_OWNED);
+ TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
+ hp = NFSUSERNAMEHASH(usrp->lug_name, usrp->lug_namelen);
+ mtx_assert(&hp->mtx, MA_OWNED);
+ TAILQ_REMOVE(&hp->lughead, usrp, lug_namehash);
+ } else {
+ hp = NFSGROUPHASH(usrp->lug_gid);
+ mtx_assert(&hp->mtx, MA_OWNED);
+ TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
+ hp = NFSGROUPNAMEHASH(usrp->lug_name, usrp->lug_namelen);
+ mtx_assert(&hp->mtx, MA_OWNED);
+ TAILQ_REMOVE(&hp->lughead, usrp, lug_namehash);
+ }
+ atomic_add_int(&nfsrv_usercnt, -1);
+ if (usrp->lug_cred != NULL)
+ crfree(usrp->lug_cred);
+ free(usrp, M_NFSUSERGROUP);
}
/*
diff --git a/sys/fs/nfs/nfs_var.h b/sys/fs/nfs/nfs_var.h
index ce77157..e08835c 100644
--- a/sys/fs/nfs/nfs_var.h
+++ b/sys/fs/nfs/nfs_var.h
@@ -296,6 +296,7 @@ void nfsv4_setsequence(struct nfsmount *, struct nfsrv_descript *,
int nfsv4_sequencelookup(struct nfsmount *, struct nfsclsession *, int *,
int *, uint32_t *, uint8_t *);
void nfsv4_freeslot(struct nfsclsession *, int);
+struct ucred *nfsrv_getgrpscred(struct ucred *);
/* nfs_clcomsubs.c */
void nfsm_uiombuf(struct nfsrv_descript *, struct uio *, int);
diff --git a/sys/fs/nfs/nfsrvstate.h b/sys/fs/nfs/nfsrvstate.h
index 972fffe..6d32244 100644
--- a/sys/fs/nfs/nfsrvstate.h
+++ b/sys/fs/nfs/nfsrvstate.h
@@ -48,23 +48,22 @@ LIST_HEAD(nfssessionhashhead, nfsdsession);
/*
* List head for nfsusrgrp.
*/
-LIST_HEAD(nfsuserhashhead, nfsusrgrp);
-TAILQ_HEAD(nfsuserlruhead, nfsusrgrp);
+TAILQ_HEAD(nfsuserhashhead, nfsusrgrp);
#define NFSCLIENTHASH(id) \
(&nfsclienthash[(id).lval[1] % nfsrv_clienthashsize])
#define NFSSTATEHASH(clp, id) \
(&((clp)->lc_stateid[(id).other[2] % nfsrv_statehashsize]))
#define NFSUSERHASH(id) \
- (&nfsuserhash[(id) % NFSUSERHASHSIZE])
+ (&nfsuserhash[(id) % nfsrv_lughashsize])
#define NFSUSERNAMEHASH(p, l) \
(&nfsusernamehash[((l)>=4?(*(p)+*((p)+1)+*((p)+2)+*((p)+3)):*(p)) \
- % NFSUSERHASHSIZE])
+ % nfsrv_lughashsize])
#define NFSGROUPHASH(id) \
- (&nfsgrouphash[(id) % NFSGROUPHASHSIZE])
+ (&nfsgrouphash[(id) % nfsrv_lughashsize])
#define NFSGROUPNAMEHASH(p, l) \
(&nfsgroupnamehash[((l)>=4?(*(p)+*((p)+1)+*((p)+2)+*((p)+3)):*(p)) \
- % NFSGROUPHASHSIZE])
+ % nfsrv_lughashsize])
struct nfssessionhash {
struct mtx mtx;
@@ -264,14 +263,14 @@ struct nfslockfile {
* names.
*/
struct nfsusrgrp {
- TAILQ_ENTRY(nfsusrgrp) lug_lru; /* LRU list */
- LIST_ENTRY(nfsusrgrp) lug_numhash; /* Hash by id# */
- LIST_ENTRY(nfsusrgrp) lug_namehash; /* and by name */
+ TAILQ_ENTRY(nfsusrgrp) lug_numhash; /* Hash by id# */
+ TAILQ_ENTRY(nfsusrgrp) lug_namehash; /* and by name */
time_t lug_expiry; /* Expiry time in sec */
union {
uid_t un_uid; /* id# */
gid_t un_gid;
} lug_un;
+ struct ucred *lug_cred; /* Cred. with groups list */
int lug_namelen; /* Name length */
u_char lug_name[1]; /* malloc'd correct length */
};
diff --git a/sys/fs/nfsserver/nfs_nfsdport.c b/sys/fs/nfsserver/nfs_nfsdport.c
index 85b4466..edbcb35 100644
--- a/sys/fs/nfsserver/nfs_nfsdport.c
+++ b/sys/fs/nfsserver/nfs_nfsdport.c
@@ -2649,14 +2649,24 @@ nfsd_excred(struct nfsrv_descript *nd, struct nfsexstuff *exp,
* Fsinfo RPC. If set for anything else, this code might need
* to change.)
*/
- if (NFSVNO_EXPORTED(exp) &&
- ((!(nd->nd_flag & ND_GSS) && nd->nd_cred->cr_uid == 0) ||
- NFSVNO_EXPORTANON(exp) ||
- (nd->nd_flag & ND_AUTHNONE))) {
- 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);
+ if (NFSVNO_EXPORTED(exp)) {
+ if (((nd->nd_flag & ND_GSS) == 0 && nd->nd_cred->cr_uid == 0) ||
+ 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;
+ crsetgroups(nd->nd_cred, credanon->cr_ngroups,
+ credanon->cr_groups);
+ } else if ((nd->nd_flag & ND_GSS) == 0) {
+ /*
+ * If using AUTH_SYS, call nfsrv_getgrpscred() to see
+ * if there is a replacement credential with a group
+ * list set up by "nfsuserd -manage-gids".
+ * If there is no replacement, nfsrv_getgrpscred()
+ * simply returns its argument.
+ */
+ nd->nd_cred = nfsrv_getgrpscred(nd->nd_cred);
+ }
}
out:
OpenPOWER on IntegriCloud