diff options
Diffstat (limited to 'usr/src/uts/common/fs/nfs/nfs_auth.c')
-rw-r--r-- | usr/src/uts/common/fs/nfs/nfs_auth.c | 456 |
1 files changed, 290 insertions, 166 deletions
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); } /* |