void nfsauth_func(void *cookie, char *dataptr, size_t arg_size, door_desc_t *dp, uint_t n_desc) { nfsauth_arg_t *ap; nfsauth_res_t res = {0}; nfsauth_res_t *rp = &res; XDR xdrs_a; XDR xdrs_r; caddr_t abuf = dataptr; size_t absz = arg_size; size_t rbsz = (size_t)(BYTES_PER_XDR_UNIT * 2); char result[BYTES_PER_XDR_UNIT * 2]; caddr_t rbuf = (caddr_t)&result; varg_t varg = {0}; /* * Decode the inbound door data, so we can look at the cmd. */ xdrmem_create(&xdrs_a, abuf, absz, XDR_DECODE); if (!xdr_varg(&xdrs_a, &varg)) { /* * If the arguments can't be decoded, bail. */ if (varg.vers == V_ERROR) syslog(LOG_ERR, gettext("Arg version mismatch")); res.stat = NFSAUTH_DR_DECERR; goto encres; } /* * Now set the args pointer to the proper version of the args */ switch (varg.vers) { case V_PROTO: ap = &varg.arg_u.arg; break; /* Additional arguments versions go here */ default: syslog(LOG_ERR, gettext("Invalid args version")); goto encres; } /* * Call the specified cmd */ switch (ap->cmd) { case NFSAUTH_ACCESS: nfsauth_access(&ap->areq, &rp->ares); rp->stat = NFSAUTH_DR_OKAY; break; default: rp->stat = NFSAUTH_DR_BADCMD; break; } encres: /* * Free space used to decode the args */ xdrs_a.x_op = XDR_FREE; (void) xdr_varg(&xdrs_a, &varg); xdr_destroy(&xdrs_a); /* * Encode the results before passing thru door. * * The result (nfsauth_res_t) is always two int's, so we don't * have to dynamically size (or allocate) the results buffer. */ xdrmem_create(&xdrs_r, rbuf, rbsz, XDR_ENCODE); if (!xdr_nfsauth_res(&xdrs_r, rp)) { /* * return only the status code */ rp->stat = NFSAUTH_DR_EFAIL; rbsz = sizeof (uint_t); *rbuf = (uint_t)rp->stat; } xdr_destroy(&xdrs_r); (void) door_return((char *)rbuf, rbsz, NULL, 0); (void) door_return(NULL, 0, NULL, 0); /* NOTREACHED */ }
/* * Get the access information from the cache or callup to the mountd * to get and cache the access information in the kernel. */ int nfsauth_cache_get(struct exportinfo *exi, struct svc_req *req, int flavor) { struct netbuf addr; struct netbuf *claddr; struct auth_cache **head; struct auth_cache *ap; int access; varg_t varg = {0}; nfsauth_res_t res = {0}; XDR xdrs_a; XDR xdrs_r; size_t absz; caddr_t abuf; size_t rbsz = (size_t)(BYTES_PER_XDR_UNIT * 2); char result[BYTES_PER_XDR_UNIT * 2] = {0}; caddr_t rbuf = (caddr_t)&result; int last = 0; door_arg_t da; door_info_t di; door_handle_t dh; uint_t ntries = 0; /* * Now check whether this client already * has an entry for this flavor in the cache * for this export. * Get the caller's address, mask off the * parts of the address that do not identify * the host (port number, etc), and then hash * it to find the chain of cache entries. */ claddr = svc_getrpccaller(req->rq_xprt); addr = *claddr; addr.buf = kmem_alloc(addr.len, KM_SLEEP); bcopy(claddr->buf, addr.buf, claddr->len); addrmask(&addr, svc_getaddrmask(req->rq_xprt)); head = &exi->exi_cache[hash(&addr)]; rw_enter(&exi->exi_cache_lock, RW_READER); for (ap = *head; ap; ap = ap->auth_next) { if (EQADDR(&addr, &ap->auth_addr) && flavor == ap->auth_flavor) break; } if (ap) { /* cache hit */ access = ap->auth_access; ap->auth_time = gethrestime_sec(); nfsauth_cache_hit++; } rw_exit(&exi->exi_cache_lock); if (ap) { kmem_free(addr.buf, addr.len); return (access); } nfsauth_cache_miss++; /* * No entry in the cache for this client/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"); kmem_free(addr.buf, addr.len); return (NFSAUTH_DROP); } ntries = 0; varg.vers = V_PROTO; varg.arg_u.arg.cmd = NFSAUTH_ACCESS; varg.arg_u.arg.areq.req_client.n_len = addr.len; varg.arg_u.arg.areq.req_client.n_bytes = addr.buf; varg.arg_u.arg.areq.req_netid = svc_getnetid(req->rq_xprt); varg.arg_u.arg.areq.req_path = exi->exi_export.ex_path; varg.arg_u.arg.areq.req_flavor = flavor; /* * Setup the XDR stream for encoding the arguments. Notice that * in addition to the args having variable fields (req_netid and * req_path), the argument data structure is itself versioned, * so we need to make sure we can size the arguments buffer * appropriately to encode all the args. If we can't get sizing * 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); kmem_free(addr.buf, addr.len); return (NFSAUTH_DENIED); } 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); goto fail; } XDR_DESTROY(&xdrs_a); /* * The result (nfsauth_res_t) is always two 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. */ da.data_ptr = (char *)abuf; da.data_size = absz; da.desc_ptr = NULL; da.desc_num = 0; da.rbuf = (char *)rbuf; da.rsize = rbsz; 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; } else if (rbuf != da.rbuf) {
/* * Callup to the mountd to get access information in the kernel. */ 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, 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; size_t absz; caddr_t abuf; int last = 0; door_arg_t da; door_info_t di; door_handle_t dh; uint_t ntries = 0; /* * No entry in the cache for this client/flavor * so we need to call the nfsauth service in the * mount daemon. */ varg.vers = V_PROTO; varg.arg_u.arg.cmd = NFSAUTH_ACCESS; varg.arg_u.arg.areq.req_client.n_len = addr->len; varg.arg_u.arg.areq.req_client.n_bytes = addr->buf; varg.arg_u.arg.areq.req_netid = req_netid; varg.arg_u.arg.areq.req_path = exi->exi_export.ex_path; 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 * in addition to the args having variable fields (req_netid and * req_path), the argument data structure is itself versioned, * so we need to make sure we can size the arguments buffer * appropriately to encode all the args. If we can't get sizing * info _or_ properly encode the arguments, there's really no * point in continuting, so we fail the request. */ if ((absz = xdr_sizeof(xdr_varg, &varg)) == 0) { *access = NFSAUTH_DENIED; return (FALSE); } abuf = (caddr_t)kmem_alloc(absz, KM_SLEEP); xdrmem_create(&xdrs, abuf, absz, XDR_ENCODE); if (!xdr_varg(&xdrs, &varg)) { XDR_DESTROY(&xdrs); goto fail; } XDR_DESTROY(&xdrs); /* * 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 = NULL; da.rsize = 1; retry: mutex_enter(&mountd_lock); dh = mountd_dh; if (dh != NULL) 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; } 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); if (da.data_ptr == NULL && 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); 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); 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) { door_ki_rele(mountd_dh); mountd_dh = NULL; } mutex_exit(&mountd_lock); delay(hz); goto retry; } /* * 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; }