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
This commit is contained in:
Rick Macklem 2015-11-30 21:54:27 +00:00
parent fe5f4c93fe
commit 84be7e0952
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=291527
7 changed files with 547 additions and 219 deletions

View File

@ -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 {

View File

@ -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);

View File

@ -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 @@ nfsv4_uidtostr(uid_t uid, u_char **cpp, int *retlenp, NFSPROC_T *p)
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 @@ nfsv4_uidtostr(uid_t uid, u_char **cpp, int *retlenp, NFSPROC_T *p)
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 @@ nfsv4_uidtostr(uid_t uid, u_char **cpp, int *retlenp, NFSPROC_T *p)
*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();
}
/*
@ -2646,6 +2651,52 @@ nfsv4_uidtostr(uid_t uid, u_char **cpp, int *retlenp, NFSPROC_T *p)
return;
}
/*
* 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
@ -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 @@ nfsv4_gidtostr(gid_t gid, u_char **cpp, int *retlenp, NFSPROC_T *p)
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 @@ nfsv4_gidtostr(gid_t gid, u_char **cpp, int *retlenp, NFSPROC_T *p)
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 @@ nfsv4_gidtostr(gid_t gid, u_char **cpp, int *retlenp, NFSPROC_T *p)
*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) {
/*
* Free up all the old stuff and reinitialize hash lists.
*/
TAILQ_FOREACH_SAFE(usrp, &nfsuserlruhead, lug_lru, nusrp) {
nfsrv_removeuser(usrp);
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;
}
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]);
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);
/*
* Put name in "DNS" string.
*/
if (!error) {
/*
* Put name in "DNS" string.
*/
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 @@ nfssvc_idname(struct nfsd_idargs *nidp)
* Remove a user/group name element.
*/
static void
nfsrv_removeuser(struct nfsusrgrp *usrp)
nfsrv_removeuser(struct nfsusrgrp *usrp, int isuser)
{
struct nfsrv_lughash *hp;
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);
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);
}
/*

View File

@ -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);

View File

@ -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 */
};

View File

@ -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:

View File

@ -69,6 +69,7 @@
#define NFSSVC_SUSPENDNFSD 0x04000000
#define NFSSVC_RESUMENFSD 0x08000000
#define NFSSVC_DUMPMNTOPTS 0x10000000
#define NFSSVC_NEWSTRUCT 0x20000000
/* Argument structure for NFSSVC_DUMPMNTOPTS. */
struct nfscl_dumpmntopts {