summaryrefslogtreecommitdiff
path: root/usr/src/uts/common
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common')
-rw-r--r--usr/src/uts/common/conf/param.c6
-rw-r--r--usr/src/uts/common/fs/nfs/nfs_auth.c456
-rw-r--r--usr/src/uts/common/fs/nfs/nfs_auth_xdr.c8
-rw-r--r--usr/src/uts/common/fs/nfs/nfs_server.c23
-rw-r--r--usr/src/uts/common/nfs/auth.h26
-rw-r--r--usr/src/uts/common/nfs/export.h13
-rw-r--r--usr/src/uts/common/nfs/nfs.h5
-rw-r--r--usr/src/uts/common/sys/param.h3
8 files changed, 355 insertions, 185 deletions
diff --git a/usr/src/uts/common/conf/param.c b/usr/src/uts/common/conf/param.c
index 7a7b9a208b..a71be771fd 100644
--- a/usr/src/uts/common/conf/param.c
+++ b/usr/src/uts/common/conf/param.c
@@ -18,7 +18,9 @@
*
* CDDL HEADER END
*/
+
/*
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 1983, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, Joyent, Inc. All rights reserved.
* Copyright 2012 Milan Jurik. All rights reserved.
@@ -594,10 +596,6 @@ param_calc(int platform_max_nprocs)
cmn_err(CE_NOTE, "maxusers limited to %d", MAX_MAXUSERS);
}
- if (ngroups_max > NGROUPS_MAX_DEFAULT)
- cmn_err(CE_WARN, "ngroups_max of %d > %d, NFS AUTH_SYS will"
- " not work properly", ngroups_max, NGROUPS_MAX_DEFAULT);
-
#ifdef DEBUG
/*
* The purpose of maxusers is to prevent memory overcommit.
diff --git a/usr/src/uts/common/fs/nfs/nfs_auth.c b/usr/src/uts/common/fs/nfs/nfs_auth.c
index d20780ebba..18d358795b 100644
--- a/usr/src/uts/common/fs/nfs/nfs_auth.c
+++ b/usr/src/uts/common/fs/nfs/nfs_auth.c
@@ -18,9 +18,10 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#include <sys/param.h>
@@ -177,7 +178,7 @@ void
mountd_args(uint_t did)
{
mutex_enter(&mountd_lock);
- if (mountd_dh)
+ if (mountd_dh != NULL)
door_ki_rele(mountd_dh);
mountd_dh = door_ki_lookup(did);
mutex_exit(&mountd_lock);
@@ -317,11 +318,11 @@ addrmask(struct netbuf *addr, struct netbuf *mask)
*/
int
nfsauth4_access(struct exportinfo *exi, vnode_t *vp, struct svc_req *req,
- cred_t *cr, uid_t *uid, gid_t *gid)
+ cred_t *cr, uid_t *uid, gid_t *gid, uint_t *ngids, gid_t **gids)
{
int access;
- access = nfsauth_access(exi, req, cr, uid, gid);
+ access = nfsauth_access(exi, req, cr, uid, gid, ngids, gids);
/*
* There are cases that the server needs to allow the client
@@ -370,17 +371,14 @@ sys_log(const char *msg)
static bool_t
nfsauth_retrieve(struct exportinfo *exi, char *req_netid, int flavor,
struct netbuf *addr, int *access, uid_t clnt_uid, gid_t clnt_gid,
- uid_t *srv_uid, gid_t *srv_gid)
+ uint_t clnt_gids_cnt, const gid_t *clnt_gids, uid_t *srv_uid,
+ gid_t *srv_gid, uint_t *srv_gids_cnt, gid_t **srv_gids)
{
varg_t varg = {0};
nfsauth_res_t res = {0};
- XDR xdrs_a;
- XDR xdrs_r;
+ XDR xdrs;
size_t absz;
caddr_t abuf;
- size_t rbsz = (size_t)(BYTES_PER_XDR_UNIT * 4);
- char result[BYTES_PER_XDR_UNIT * 4] = {0};
- caddr_t rbuf = (caddr_t)&result;
int last = 0;
door_arg_t da;
door_info_t di;
@@ -392,35 +390,7 @@ nfsauth_retrieve(struct exportinfo *exi, char *req_netid, int flavor,
* so we need to call the nfsauth service in the
* mount daemon.
*/
-retry:
- mutex_enter(&mountd_lock);
- dh = mountd_dh;
- if (dh)
- door_ki_hold(dh);
- mutex_exit(&mountd_lock);
-
- if (dh == NULL) {
- /*
- * The rendezvous point has not been established yet !
- * This could mean that either mountd(1m) has not yet
- * been started or that _this_ routine nuked the door
- * handle after receiving an EINTR for a REVOKED door.
- *
- * Returning NFSAUTH_DROP will cause the NFS client
- * to retransmit the request, so let's try to be more
- * rescillient and attempt for ntries before we bail.
- */
- if (++ntries % NFSAUTH_DR_TRYCNT) {
- delay(hz);
- goto retry;
- }
-
- sys_log("nfsauth: mountd has not established door");
- *access = NFSAUTH_DROP;
- return (FALSE);
- }
- ntries = 0;
varg.vers = V_PROTO;
varg.arg_u.arg.cmd = NFSAUTH_ACCESS;
varg.arg_u.arg.areq.req_client.n_len = addr->len;
@@ -430,6 +400,10 @@ retry:
varg.arg_u.arg.areq.req_flavor = flavor;
varg.arg_u.arg.areq.req_clnt_uid = clnt_uid;
varg.arg_u.arg.areq.req_clnt_gid = clnt_gid;
+ varg.arg_u.arg.areq.req_clnt_gids.len = clnt_gids_cnt;
+ varg.arg_u.arg.areq.req_clnt_gids.val = (gid_t *)clnt_gids;
+
+ DTRACE_PROBE1(nfsserv__func__nfsauth__varg, varg_t *, &varg);
/*
* Setup the XDR stream for encoding the arguments. Notice that
@@ -440,153 +414,175 @@ retry:
* info _or_ properly encode the arguments, there's really no
* point in continuting, so we fail the request.
*/
- DTRACE_PROBE1(nfsserv__func__nfsauth__varg, varg_t *, &varg);
- if ((absz = xdr_sizeof(xdr_varg, (void *)&varg)) == 0) {
- door_ki_rele(dh);
+ if ((absz = xdr_sizeof(xdr_varg, &varg)) == 0) {
*access = NFSAUTH_DENIED;
return (FALSE);
}
abuf = (caddr_t)kmem_alloc(absz, KM_SLEEP);
- xdrmem_create(&xdrs_a, abuf, absz, XDR_ENCODE);
- if (!xdr_varg(&xdrs_a, &varg)) {
- door_ki_rele(dh);
+ xdrmem_create(&xdrs, abuf, absz, XDR_ENCODE);
+ if (!xdr_varg(&xdrs, &varg)) {
+ XDR_DESTROY(&xdrs);
goto fail;
}
- XDR_DESTROY(&xdrs_a);
+ XDR_DESTROY(&xdrs);
/*
- * The result (nfsauth_res_t) is always four int's, so we don't
- * have to dynamically size (or allocate) the results buffer.
- * Now that we've got what we need, we prep the door arguments
- * and place the call.
+ * Prepare the door arguments
+ *
+ * We don't know the size of the message the daemon
+ * will pass back to us. By setting rbuf to NULL,
+ * we force the door code to allocate a buf of the
+ * appropriate size. We must set rsize > 0, however,
+ * else the door code acts as if no response was
+ * expected and doesn't pass the data to us.
*/
da.data_ptr = (char *)abuf;
da.data_size = absz;
da.desc_ptr = NULL;
da.desc_num = 0;
- da.rbuf = (char *)rbuf;
- da.rsize = rbsz;
+ da.rbuf = NULL;
+ da.rsize = 1;
- switch (door_ki_upcall_limited(dh, &da, NULL, SIZE_MAX, 0)) {
- case 0: /* Success */
- if (da.data_ptr != da.rbuf && da.data_size == 0) {
- /*
- * The door_return that contained the data
- * failed ! We're here because of the 2nd
- * door_return (w/o data) such that we can
- * get control of the thread (and exit
- * gracefully).
- */
- DTRACE_PROBE1(nfsserv__func__nfsauth__door__nil,
- door_arg_t *, &da);
- door_ki_rele(dh);
- goto fail;
+retry:
+ mutex_enter(&mountd_lock);
+ dh = mountd_dh;
+ if (dh != NULL)
+ door_ki_hold(dh);
+ mutex_exit(&mountd_lock);
- } else if (rbuf != da.rbuf) {
- /*
- * The only time this should be true
- * is iff userland wanted to hand us
- * a bigger response than what we
- * expect; that should not happen
- * (nfsauth_res_t is only 4 int's),
- * but we check nevertheless.
- */
- rbuf = da.rbuf;
- rbsz = da.rsize;
+ if (dh == NULL) {
+ /*
+ * The rendezvous point has not been established yet!
+ * This could mean that either mountd(1m) has not yet
+ * been started or that _this_ routine nuked the door
+ * handle after receiving an EINTR for a REVOKED door.
+ *
+ * Returning NFSAUTH_DROP will cause the NFS client
+ * to retransmit the request, so let's try to be more
+ * rescillient and attempt for ntries before we bail.
+ */
+ if (++ntries % NFSAUTH_DR_TRYCNT) {
+ delay(hz);
+ goto retry;
+ }
- } else if (rbsz > da.data_size) {
- /*
- * We were expecting four int's; but if
- * userland fails in encoding the XDR
- * stream, we detect that here, since
- * the mountd forces down only one byte
- * in such scenario.
- */
- door_ki_rele(dh);
- goto fail;
- }
- door_ki_rele(dh);
- break;
+ kmem_free(abuf, absz);
+
+ sys_log("nfsauth: mountd has not established door");
+ *access = NFSAUTH_DROP;
+ return (FALSE);
+ }
+
+ ntries = 0;
+
+ /*
+ * Now that we've got what we need, place the call.
+ */
+ switch (door_ki_upcall_limited(dh, &da, NULL, SIZE_MAX, 0)) {
+ case 0: /* Success */
+ door_ki_rele(dh);
- case EAGAIN:
+ if (da.data_ptr == NULL && da.data_size == 0) {
/*
- * Server out of resources; back off for a bit
+ * The door_return that contained the data
+ * failed! We're here because of the 2nd
+ * door_return (w/o data) such that we can
+ * get control of the thread (and exit
+ * gracefully).
*/
+ DTRACE_PROBE1(nfsserv__func__nfsauth__door__nil,
+ door_arg_t *, &da);
+ goto fail;
+ }
+
+ break;
+
+ case EAGAIN:
+ /*
+ * Server out of resources; back off for a bit
+ */
+ door_ki_rele(dh);
+ delay(hz);
+ goto retry;
+ /* NOTREACHED */
+
+ case EINTR:
+ if (!door_ki_info(dh, &di)) {
door_ki_rele(dh);
- kmem_free(abuf, absz);
- delay(hz);
- goto retry;
- /* NOTREACHED */
- case EINTR:
- if (!door_ki_info(dh, &di)) {
- if (di.di_attributes & DOOR_REVOKED) {
- /*
- * The server barfed and revoked
- * the (existing) door on us; we
- * want to wait to give smf(5) a
- * chance to restart mountd(1m)
- * and establish a new door handle.
- */
- mutex_enter(&mountd_lock);
- if (dh == mountd_dh)
- mountd_dh = NULL;
- mutex_exit(&mountd_lock);
- door_ki_rele(dh);
- kmem_free(abuf, absz);
- delay(hz);
- goto retry;
- }
+ if (di.di_attributes & DOOR_REVOKED) {
/*
- * If the door was _not_ revoked on us,
- * then more than likely we took an INTR,
- * so we need to fail the operation.
+ * The server barfed and revoked
+ * the (existing) door on us; we
+ * want to wait to give smf(5) a
+ * chance to restart mountd(1m)
+ * and establish a new door handle.
*/
- door_ki_rele(dh);
- goto fail;
- }
- /*
- * The only failure that can occur from getting
- * the door info is EINVAL, so we let the code
- * below handle it.
- */
- /* FALLTHROUGH */
-
- case EBADF:
- case EINVAL:
- default:
- /*
- * If we have a stale door handle, give smf a last
- * chance to start it by sleeping for a little bit.
- * If we're still hosed, we'll fail the call.
- *
- * Since we're going to reacquire the door handle
- * upon the retry, we opt to sleep for a bit and
- * _not_ to clear mountd_dh. If mountd restarted
- * and was able to set mountd_dh, we should see
- * the new instance; if not, we won't get caught
- * up in the retry/DELAY loop.
- */
- door_ki_rele(dh);
- if (!last) {
+ mutex_enter(&mountd_lock);
+ if (dh == mountd_dh) {
+ door_ki_rele(mountd_dh);
+ mountd_dh = NULL;
+ }
+ mutex_exit(&mountd_lock);
delay(hz);
- last++;
goto retry;
}
- sys_log("nfsauth: stale mountd door handle");
+ /*
+ * If the door was _not_ revoked on us,
+ * then more than likely we took an INTR,
+ * so we need to fail the operation.
+ */
goto fail;
+ }
+ /*
+ * The only failure that can occur from getting
+ * the door info is EINVAL, so we let the code
+ * below handle it.
+ */
+ /* FALLTHROUGH */
+
+ case EBADF:
+ case EINVAL:
+ default:
+ /*
+ * If we have a stale door handle, give smf a last
+ * chance to start it by sleeping for a little bit.
+ * If we're still hosed, we'll fail the call.
+ *
+ * Since we're going to reacquire the door handle
+ * upon the retry, we opt to sleep for a bit and
+ * _not_ to clear mountd_dh. If mountd restarted
+ * and was able to set mountd_dh, we should see
+ * the new instance; if not, we won't get caught
+ * up in the retry/DELAY loop.
+ */
+ door_ki_rele(dh);
+ if (!last) {
+ delay(hz);
+ last++;
+ goto retry;
+ }
+ sys_log("nfsauth: stale mountd door handle");
+ goto fail;
}
+ ASSERT(da.rbuf != NULL);
+
/*
* No door errors encountered; setup the XDR stream for decoding
* the results. If we fail to decode the results, we've got no
* other recourse than to fail the request.
*/
- xdrmem_create(&xdrs_r, rbuf, rbsz, XDR_DECODE);
- if (!xdr_nfsauth_res(&xdrs_r, &res))
+ xdrmem_create(&xdrs, da.rbuf, da.rsize, XDR_DECODE);
+ if (!xdr_nfsauth_res(&xdrs, &res)) {
+ xdr_free(xdr_nfsauth_res, (char *)&res);
+ XDR_DESTROY(&xdrs);
+ kmem_free(da.rbuf, da.rsize);
goto fail;
- XDR_DESTROY(&xdrs_r);
+ }
+ XDR_DESTROY(&xdrs);
+ kmem_free(da.rbuf, da.rsize);
DTRACE_PROBE1(nfsserv__func__nfsauth__results, nfsauth_res_t *, &res);
switch (res.stat) {
@@ -594,13 +590,18 @@ retry:
*access = res.ares.auth_perm;
*srv_uid = res.ares.auth_srv_uid;
*srv_gid = res.ares.auth_srv_gid;
- kmem_free(abuf, absz);
+ *srv_gids_cnt = res.ares.auth_srv_gids.len;
+ *srv_gids = kmem_alloc(*srv_gids_cnt * sizeof (gid_t),
+ KM_SLEEP);
+ bcopy(res.ares.auth_srv_gids.val, *srv_gids,
+ *srv_gids_cnt * sizeof (gid_t));
break;
case NFSAUTH_DR_EFAIL:
case NFSAUTH_DR_DECERR:
case NFSAUTH_DR_BADCMD:
default:
+ xdr_free(xdr_nfsauth_res, (char *)&res);
fail:
*access = NFSAUTH_DENIED;
kmem_free(abuf, absz);
@@ -608,6 +609,9 @@ fail:
/* NOTREACHED */
}
+ xdr_free(xdr_nfsauth_res, (char *)&res);
+ kmem_free(abuf, absz);
+
return (TRUE);
}
@@ -655,6 +659,8 @@ nfsauth_refresh_thread(void)
*/
while ((ran = list_remove_head(&ren->ren_authlist))) {
+ uint_t ngids;
+ gid_t *gids;
struct auth_cache *p = ran->ran_auth;
ASSERT(p != NULL);
@@ -715,7 +721,8 @@ nfsauth_refresh_thread(void)
retrieval = nfsauth_retrieve(exi, p->auth_netid,
p->auth_flavor, &p->auth_addr, &access,
p->auth_clnt_uid, p->auth_clnt_gid,
- &p->auth_srv_uid, &p->auth_srv_gid);
+ p->auth_clnt_ngids, p->auth_clnt_gids,
+ &p->auth_srv_uid, &p->auth_srv_gid, &ngids, &gids);
/*
* This can only be set in one other place
@@ -728,6 +735,8 @@ nfsauth_refresh_thread(void)
if (p->auth_state == NFS_AUTH_INVALID) {
mutex_exit(&p->auth_lock);
nfsauth_remove_dead_entry(p);
+ if (retrieval == TRUE)
+ kmem_free(gids, ngids * sizeof (gid_t));
} else {
/*
* If we got an error, do not reset the
@@ -737,6 +746,12 @@ nfsauth_refresh_thread(void)
*/
if (retrieval == TRUE) {
p->auth_access = access;
+
+ kmem_free(p->auth_srv_gids,
+ p->auth_srv_ngids * sizeof (gid_t));
+ p->auth_srv_ngids = ngids;
+ p->auth_srv_gids = gids;
+
p->auth_freshness = gethrestime_sec();
}
p->auth_state = NFS_AUTH_FRESH;
@@ -761,13 +776,14 @@ nfsauth_refresh_thread(void)
*/
static int
nfsauth_cache_get(struct exportinfo *exi, struct svc_req *req, int flavor,
- cred_t *cr, uid_t *uid, gid_t *gid)
+ cred_t *cr, uid_t *uid, gid_t *gid, uint_t *ngids, gid_t **gids)
{
struct netbuf *taddrmask;
struct netbuf addr;
struct netbuf *claddr;
struct auth_cache **head;
struct auth_cache *p;
+ struct auth_cache *prev = NULL;
int access;
time_t refresh;
@@ -776,6 +792,8 @@ nfsauth_cache_get(struct exportinfo *exi, struct svc_req *req, int flavor,
uid_t tmpuid;
gid_t tmpgid;
+ uint_t tmpngids;
+ gid_t *tmpgids;
ASSERT(cr != NULL);
@@ -805,9 +823,53 @@ nfsauth_cache_get(struct exportinfo *exi, struct svc_req *req, int flavor,
crgetuid(cr) == p->auth_clnt_uid &&
crgetgid(cr) == p->auth_clnt_gid)
break;
+ prev = p;
}
if (p != NULL) {
+ /*
+ * In a case the client's supplemental groups changed we need
+ * to discard the auth_cache entry and re-retrieve it.
+ */
+ mutex_enter(&p->auth_lock);
+ if (p->auth_clnt_ngids != crgetngroups(cr) ||
+ bcmp(p->auth_clnt_gids, crgetgroups(cr),
+ p->auth_clnt_ngids * sizeof (gid_t))) {
+ auth_state_t prev_state = p->auth_state;
+
+ p->auth_state = NFS_AUTH_INVALID;
+ mutex_exit(&p->auth_lock);
+
+ if (prev_state == NFS_AUTH_FRESH) {
+ if (rw_tryupgrade(&exi->exi_cache_lock) == 0) {
+ struct auth_cache *tmp;
+
+ rw_exit(&exi->exi_cache_lock);
+ rw_enter(&exi->exi_cache_lock,
+ RW_WRITER);
+
+ prev = NULL;
+ for (tmp = *head; tmp != NULL;
+ tmp = tmp->auth_next) {
+ if (p == tmp)
+ break;
+ prev = p;
+ }
+ }
+
+ if (prev == NULL)
+ exi->exi_cache[hash(&addr)] =
+ p->auth_next;
+ else
+ prev->auth_next = p->auth_next;
+
+ nfsauth_free_node(p);
+ }
+
+ goto retrieve;
+ }
+ mutex_exit(&p->auth_lock);
+
nfsauth_cache_hit++;
refresh = gethrestime_sec() - p->auth_freshness;
@@ -889,6 +951,11 @@ nfsauth_cache_get(struct exportinfo *exi, struct svc_req *req, int flavor,
*uid = p->auth_srv_uid;
if (gid != NULL)
*gid = p->auth_srv_gid;
+ if (ngids != NULL && gids != NULL) {
+ *ngids = p->auth_srv_ngids;
+ *gids = kmem_alloc(*ngids * sizeof (gid_t), KM_SLEEP);
+ bcopy(p->auth_srv_gids, *gids, *ngids * sizeof (gid_t));
+ }
p->auth_time = gethrestime_sec();
@@ -898,13 +965,19 @@ nfsauth_cache_get(struct exportinfo *exi, struct svc_req *req, int flavor,
return (access);
}
+retrieve:
rw_exit(&exi->exi_cache_lock);
nfsauth_cache_miss++;
if (!nfsauth_retrieve(exi, svc_getnetid(req->rq_xprt), flavor,
- &addr, &access, crgetuid(cr), crgetgid(cr), &tmpuid, &tmpgid)) {
+ &addr, &access, crgetuid(cr), crgetgid(cr), crgetngroups(cr),
+ crgetgroups(cr), &tmpuid, &tmpgid, &tmpngids, &tmpgids)) {
kmem_free(addr.buf, addr.len);
+ if (ngids != NULL && gids != NULL) {
+ *ngids = 0;
+ *gids = NULL;
+ }
return (access);
}
@@ -912,19 +985,39 @@ nfsauth_cache_get(struct exportinfo *exi, struct svc_req *req, int flavor,
*uid = tmpuid;
if (gid != NULL)
*gid = tmpgid;
+ if (ngids != NULL && gids != NULL) {
+ *ngids = tmpngids;
+ *gids = tmpgids;
+
+ /*
+ * We need a copy of gids for the auth_cache entry
+ */
+ tmpgids = kmem_alloc(tmpngids * sizeof (gid_t), KM_NOSLEEP);
+ if (tmpgids != NULL)
+ bcopy(*gids, tmpgids, tmpngids * sizeof (gid_t));
+ }
/*
* Now cache the result on the cache chain
* for this export (if there's enough memory)
*/
p = kmem_cache_alloc(exi_cache_handle, KM_NOSLEEP);
- if (p != NULL) {
+ if (p != NULL)
+ p->auth_clnt_gids = kmem_alloc(
+ crgetngroups(cr) * sizeof (gid_t), KM_NOSLEEP);
+ if (p != NULL && (tmpngids == 0 || tmpgids != NULL) &&
+ (crgetngroups(cr) == 0 || p->auth_clnt_gids != NULL)) {
p->auth_addr = addr;
p->auth_flavor = flavor;
p->auth_clnt_uid = crgetuid(cr);
p->auth_clnt_gid = crgetgid(cr);
+ p->auth_clnt_ngids = crgetngroups(cr);
+ bcopy(crgetgroups(cr), p->auth_clnt_gids,
+ p->auth_clnt_ngids * sizeof (gid_t));
p->auth_srv_uid = tmpuid;
p->auth_srv_gid = tmpgid;
+ p->auth_srv_ngids = tmpngids;
+ p->auth_srv_gids = tmpgids;
p->auth_access = access;
p->auth_time = p->auth_freshness = gethrestime_sec();
p->auth_state = NFS_AUTH_FRESH;
@@ -937,6 +1030,14 @@ nfsauth_cache_get(struct exportinfo *exi, struct svc_req *req, int flavor,
rw_exit(&exi->exi_cache_lock);
} else {
kmem_free(addr.buf, addr.len);
+ if (tmpgids != NULL)
+ kmem_free(tmpgids, tmpngids * sizeof (gid_t));
+ if (p != NULL) {
+ if (p->auth_clnt_gids != NULL)
+ kmem_free(p->auth_clnt_gids,
+ crgetngroups(cr) * sizeof (gid_t));
+ kmem_cache_free(exi_cache_handle, p);
+ }
}
return (access);
@@ -967,14 +1068,15 @@ nfsauth4_secinfo_access(struct exportinfo *exi, struct svc_req *req,
return (NFSAUTH_RW);
}
- access = nfsauth_cache_get(exi, req, flavor, cr, NULL, NULL);
+ access = nfsauth_cache_get(exi, req, flavor, cr, NULL, NULL, NULL,
+ NULL);
return (access);
}
int
nfsauth_access(struct exportinfo *exi, struct svc_req *req, cred_t *cr,
- uid_t *uid, gid_t *gid)
+ uid_t *uid, gid_t *gid, uint_t *ngids, gid_t **gids)
{
int access, mapaccess;
struct secinfo *sp;
@@ -1021,17 +1123,22 @@ nfsauth_access(struct exportinfo *exi, struct svc_req *req, cred_t *cr,
* This might get overriden later in nfsauth_cache_get().
*/
if (crgetuid(cr) == 0) {
- if (uid)
+ if (uid != NULL)
*uid = exi->exi_export.ex_anon;
- if (gid)
+ if (gid != NULL)
*gid = exi->exi_export.ex_anon;
} else {
- if (uid)
+ if (uid != NULL)
*uid = crgetuid(cr);
- if (gid)
+ if (gid != NULL)
*gid = crgetgid(cr);
}
+ if (ngids != NULL)
+ *ngids = 0;
+ if (gids != NULL)
+ *gids = NULL;
+
/*
* If the flavor is in the ex_secinfo list, but not an explicitly
* shared flavor by the user, it is a result of the nfsv4 server
@@ -1054,10 +1161,12 @@ nfsauth_access(struct exportinfo *exi, struct svc_req *req, cred_t *cr,
return (mapaccess | NFSAUTH_RO);
/*
- * Optimize if there are no lists
+ * Optimize if there are no lists.
+ * We cannot optimize for AUTH_SYS with NGRPS (16) supplemental groups.
*/
perm = sp[i].s_flags;
- if ((perm & (M_ROOT | M_NONE | M_MAP)) == 0) {
+ if ((perm & (M_ROOT | M_NONE | M_MAP)) == 0 && (ngroups_max <= NGRPS ||
+ flavor != AUTH_SYS || crgetngroups(cr) < NGRPS)) {
perm &= ~M_4SEC_EXPORTED;
if (perm == M_RO)
return (mapaccess | NFSAUTH_RO);
@@ -1065,7 +1174,19 @@ nfsauth_access(struct exportinfo *exi, struct svc_req *req, cred_t *cr,
return (mapaccess | NFSAUTH_RW);
}
- access = nfsauth_cache_get(exi, req, flavor, cr, uid, gid);
+ access = nfsauth_cache_get(exi, req, flavor, cr, uid, gid, ngids, gids);
+
+ /*
+ * For both NFSAUTH_DENIED and NFSAUTH_WRONGSEC we do not care about
+ * the supplemental groups.
+ */
+ if (access & NFSAUTH_DENIED || access & NFSAUTH_WRONGSEC) {
+ if (ngids != NULL && gids != NULL) {
+ kmem_free(*gids, *ngids * sizeof (gid_t));
+ *ngids = 0;
+ *gids = NULL;
+ }
+ }
/*
* Client's security flavor doesn't match with "ro" or
@@ -1078,7 +1199,7 @@ nfsauth_access(struct exportinfo *exi, struct svc_req *req, cred_t *cr,
if (authnone_entry != -1) {
mapaccess = NFSAUTH_MAPNONE;
access = nfsauth_cache_get(exi, req, AUTH_NONE, cr,
- NULL, NULL);
+ NULL, NULL, NULL, NULL);
} else {
/*
* Check for AUTH_NONE presence.
@@ -1087,7 +1208,8 @@ nfsauth_access(struct exportinfo *exi, struct svc_req *req, cred_t *cr,
if (sp[i].s_secinfo.sc_nfsnum == AUTH_NONE) {
mapaccess = NFSAUTH_MAPNONE;
access = nfsauth_cache_get(exi, req,
- AUTH_NONE, cr, NULL, NULL);
+ AUTH_NONE, cr, NULL, NULL, NULL,
+ NULL);
break;
}
}
@@ -1106,8 +1228,10 @@ nfsauth_free_node(struct auth_cache *p)
if (p->auth_netid != NULL)
kmem_free(p->auth_netid, strlen(p->auth_netid) + 1);
kmem_free(p->auth_addr.buf, p->auth_addr.len);
+ kmem_free(p->auth_clnt_gids, p->auth_clnt_ngids * sizeof (gid_t));
+ kmem_free(p->auth_srv_gids, p->auth_srv_ngids * sizeof (gid_t));
mutex_destroy(&p->auth_lock);
- kmem_cache_free(exi_cache_handle, (void *)p);
+ kmem_cache_free(exi_cache_handle, p);
}
/*
diff --git a/usr/src/uts/common/fs/nfs/nfs_auth_xdr.c b/usr/src/uts/common/fs/nfs/nfs_auth_xdr.c
index 8fcec0e7b7..1ccba37a21 100644
--- a/usr/src/uts/common/fs/nfs/nfs_auth_xdr.c
+++ b/usr/src/uts/common/fs/nfs/nfs_auth_xdr.c
@@ -70,6 +70,10 @@ xdr_nfsauth_arg(XDR *xdrs, nfsauth_arg_t *argp)
return (FALSE);
if (!xdr_gid_t(xdrs, &argp->areq.req_clnt_gid))
return (FALSE);
+ if (!xdr_array(xdrs, (caddr_t *)&argp->areq.req_clnt_gids.val,
+ &argp->areq.req_clnt_gids.len, NGROUPS_UMAX, (uint_t)sizeof (gid_t),
+ xdr_gid_t))
+ return (FALSE);
return (TRUE);
}
@@ -84,5 +88,9 @@ xdr_nfsauth_res(XDR *xdrs, nfsauth_res_t *argp)
return (FALSE);
if (!xdr_gid_t(xdrs, &argp->ares.auth_srv_gid))
return (FALSE);
+ if (!xdr_array(xdrs, (caddr_t *)&argp->ares.auth_srv_gids.val,
+ &argp->ares.auth_srv_gids.len, NGROUPS_UMAX, (uint_t)sizeof (gid_t),
+ xdr_gid_t))
+ return (FALSE);
return (TRUE);
}
diff --git a/usr/src/uts/common/fs/nfs/nfs_server.c b/usr/src/uts/common/fs/nfs/nfs_server.c
index 5621d2efd9..93ccd28122 100644
--- a/usr/src/uts/common/fs/nfs/nfs_server.c
+++ b/usr/src/uts/common/fs/nfs/nfs_server.c
@@ -2020,6 +2020,8 @@ checkauth(struct exportinfo *exi, struct svc_req *req, cred_t *cr, int anon_ok,
uid_t uid;
gid_t gid;
+ uint_t ngids;
+ gid_t *gids;
/*
* Check for privileged port number
@@ -2078,7 +2080,7 @@ checkauth(struct exportinfo *exi, struct svc_req *req, cred_t *cr, int anon_ok,
/*
* Check if the auth flavor is valid for this export
*/
- access = nfsauth_access(exi, req, cr, &uid, &gid);
+ access = nfsauth_access(exi, req, cr, &uid, &gid, &ngids, &gids);
if (access & NFSAUTH_DROP)
return (-1); /* drop the request */
@@ -2114,6 +2116,9 @@ checkauth(struct exportinfo *exi, struct svc_req *req, cred_t *cr, int anon_ok,
return (0);
}
+ if (rpcflavor != AUTH_SYS)
+ kmem_free(gids, ngids * sizeof (gid_t));
+
switch (rpcflavor) {
case AUTH_NONE:
anon_res = crsetugid(cr, exi->exi_export.ex_anon,
@@ -2153,8 +2158,12 @@ checkauth(struct exportinfo *exi, struct svc_req *req, cred_t *cr, int anon_ok,
exi->exi_export.ex_anon,
exi->exi_export.ex_anon);
(void) crsetgroups(cr, 0, NULL);
+ } else if (access & NFSAUTH_GROUPS) {
+ (void) crsetgroups(cr, ngids, gids);
}
+ kmem_free(gids, ngids * sizeof (gid_t));
+
break;
case AUTH_DES:
@@ -2272,6 +2281,8 @@ checkauth4(struct compound_state *cs, struct svc_req *req)
uid_t uid;
gid_t gid;
+ uint_t ngids;
+ gid_t *gids;
exi = cs->exi;
cr = cs->cr;
@@ -2312,7 +2323,8 @@ checkauth4(struct compound_state *cs, struct svc_req *req)
* Check the access right per auth flavor on the vnode of
* this export for the given request.
*/
- access = nfsauth4_access(cs->exi, cs->vp, req, cr, &uid, &gid);
+ access = nfsauth4_access(cs->exi, cs->vp, req, cr, &uid, &gid, &ngids,
+ &gids);
if (access & NFSAUTH_WRONGSEC)
return (-2); /* no access for this security flavor */
@@ -2342,6 +2354,9 @@ checkauth4(struct compound_state *cs, struct svc_req *req)
* return 1 on success or 0 on failure
*/
+ if (rpcflavor != AUTH_SYS)
+ kmem_free(gids, ngids * sizeof (gid_t));
+
switch (rpcflavor) {
case AUTH_NONE:
anon_res = crsetugid(cr, exi->exi_export.ex_anon,
@@ -2381,8 +2396,12 @@ checkauth4(struct compound_state *cs, struct svc_req *req)
exi->exi_export.ex_anon,
exi->exi_export.ex_anon);
(void) crsetgroups(cr, 0, NULL);
+ } if (access & NFSAUTH_GROUPS) {
+ (void) crsetgroups(cr, ngids, gids);
}
+ kmem_free(gids, ngids * sizeof (gid_t));
+
break;
default:
diff --git a/usr/src/uts/common/nfs/auth.h b/usr/src/uts/common/nfs/auth.h
index 365b1ff6d7..5293e3fdd1 100644
--- a/usr/src/uts/common/nfs/auth.h
+++ b/usr/src/uts/common/nfs/auth.h
@@ -18,12 +18,14 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
+
/*
- * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
*/
#ifndef _AUTH_H
@@ -50,6 +52,7 @@
* int req_flavor; # auth flavor
* uid_t req_clnt_uid; # client's uid
* gid_t req_clnt_gid; # client's gid
+ * gid_t req_clnt_gids<>; # client's supplemental groups
* };
*
* const NFSAUTH_DENIED = 0x01; # Access denied
@@ -60,6 +63,7 @@
* # try a different flavor
* const NFSAUTH_UIDMAP = 0x100; # uid mapped
* const NFSAUTH_GIDMAP = 0x200; # gid mapped
+ * const NFSAUTH_GROUPS = 0x400; # translated supplemental groups
* #
* # The following are not part of the protocol.
* #
@@ -68,9 +72,10 @@
* const NFSAUTH_LIMITED = 0x80; # Access limited to visible nodes
*
* struct auth_res {
- * int auth_perm;
- * uid_t auth_srv_uid;
- * gid_t auth_srv_gid;
+ * int auth_perm;
+ * uid_t auth_srv_uid; # translated uid
+ * gid_t auth_srv_gid; # translated gid
+ * gid_t auth_srv_gids<>; # translated supplemental groups
* };
*
* program NFSAUTH_PROG {
@@ -113,6 +118,7 @@ extern "C" {
#define NFSAUTH_LIMITED 0x80
#define NFSAUTH_UIDMAP 0x100
#define NFSAUTH_GIDMAP 0x200
+#define NFSAUTH_GROUPS 0x400
struct auth_req {
netobj req_client;
@@ -121,6 +127,10 @@ struct auth_req {
int req_flavor;
uid_t req_clnt_uid;
gid_t req_clnt_gid;
+ struct {
+ uint_t len;
+ gid_t *val;
+ } req_clnt_gids;
};
typedef struct auth_req auth_req;
@@ -128,6 +138,10 @@ struct auth_res {
int auth_perm;
uid_t auth_srv_uid;
gid_t auth_srv_gid;
+ struct {
+ uint_t len;
+ gid_t *val;
+ } auth_srv_gids;
};
typedef struct auth_res auth_res;
diff --git a/usr/src/uts/common/nfs/export.h b/usr/src/uts/common/nfs/export.h
index 03851c1fb8..143b98fd8e 100644
--- a/usr/src/uts/common/nfs/export.h
+++ b/usr/src/uts/common/nfs/export.h
@@ -18,9 +18,10 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -256,8 +257,12 @@ struct auth_cache {
int auth_flavor;
uid_t auth_clnt_uid;
gid_t auth_clnt_gid;
+ uint_t auth_clnt_ngids;
+ gid_t *auth_clnt_gids;
uid_t auth_srv_uid;
gid_t auth_srv_gid;
+ uint_t auth_srv_ngids;
+ gid_t *auth_srv_gids;
int auth_access;
time_t auth_time;
time_t auth_freshness;
@@ -542,12 +547,12 @@ typedef struct exp_visible exp_visible_t;
#define rdonly4(req, cs) \
(vn_is_readonly((cs)->vp) || \
(nfsauth4_access((cs)->exi, (cs)->vp, (req), (cs)->basecr, NULL, \
- NULL) & (NFSAUTH_RO | NFSAUTH_LIMITED)))
+ NULL, NULL, NULL) & (NFSAUTH_RO | NFSAUTH_LIMITED)))
extern int nfsauth4_access(struct exportinfo *, vnode_t *,
- struct svc_req *, cred_t *, uid_t *, gid_t *);
+ struct svc_req *, cred_t *, uid_t *, gid_t *, uint_t *, gid_t **);
extern int nfsauth4_secinfo_access(struct exportinfo *,
- struct svc_req *, int, int, cred_t *);
+ struct svc_req *, int, int, cred_t *);
extern int nfs_fhbcmp(char *, char *, int);
extern int nfs_exportinit(void);
extern void nfs_exportfini(void);
diff --git a/usr/src/uts/common/nfs/nfs.h b/usr/src/uts/common/nfs/nfs.h
index b85fe98b11..681057e708 100644
--- a/usr/src/uts/common/nfs/nfs.h
+++ b/usr/src/uts/common/nfs/nfs.h
@@ -18,10 +18,11 @@
*
* CDDL HEADER END
*/
+
/*
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013 by Delphix. All rights reserved.
- * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
@@ -938,7 +939,7 @@ extern int nfslookup(vnode_t *, char *, vnode_t **, struct pathname *, int,
vnode_t *, cred_t *, int);
extern void sv_free(struct servinfo *);
extern int nfsauth_access(struct exportinfo *, struct svc_req *, cred_t *,
- uid_t *, gid_t *);
+ uid_t *, gid_t *, uint_t *, gid_t **);
extern void nfsauth_init(void);
extern void nfsauth_fini(void);
extern int nfs_setopts(vnode_t *, model_t, struct nfs_args *);
diff --git a/usr/src/uts/common/sys/param.h b/usr/src/uts/common/sys/param.h
index 657acc869e..12bdc29079 100644
--- a/usr/src/uts/common/sys/param.h
+++ b/usr/src/uts/common/sys/param.h
@@ -18,7 +18,9 @@
*
* CDDL HEADER END
*/
+
/*
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
*/
@@ -131,7 +133,6 @@ extern "C" {
/*
* NGROUPS_MAX_DEFAULT: *MUST* match NGROUPS_MAX value in limits.h.
- * Remember that the NFS protocol must rev. before this can be increased
*/
#define NGROUPS_MAX_DEFAULT 16