/* * Returns NULL if there was some system error. * Returns "" if the address was not bound, i.e the server crashed. * Returns the merged address otherwise. */ char * mergeaddr(SVCXPRT *xprt, char *netid, char *uaddr, char *saddr) { struct fdlist *fdl; char *c_uaddr, *s_uaddr, *m_uaddr, *allocated_uaddr = NULL; for (fdl = fdhead; fdl; fdl = fdl->next) if (strcmp(fdl->nconf->nc_netid, netid) == 0) break; if (fdl == NULL) return (NULL); if (check_bound(fdl, uaddr) == FALSE) /* that server died */ return strdup(emptystring); /* * If saddr is not NULL, the remote client may have included the * address by which it contacted us. Use that for the "client" uaddr, * otherwise use the info from the SVCXPRT. */ if (saddr != NULL) { c_uaddr = saddr; } else { c_uaddr = taddr2uaddr(fdl->nconf, svc_getrpccaller(xprt)); if (c_uaddr == NULL) { syslog(LOG_ERR, "taddr2uaddr failed for %s", fdl->nconf->nc_netid); return (NULL); } allocated_uaddr = c_uaddr; } #ifdef RPCBIND_DEBUG if (debugging) { if (saddr == NULL) { fprintf(stderr, "mergeaddr: client uaddr = %s\n", c_uaddr); } else { fprintf(stderr, "mergeaddr: contact uaddr = %s\n", c_uaddr); } } #endif s_uaddr = uaddr; /* * This is all we should need for IP 4 and 6 */ m_uaddr = addrmerge(svc_getrpccaller(xprt), s_uaddr, c_uaddr, netid); #ifdef RPCBIND_DEBUG if (debugging) fprintf(stderr, "mergeaddr: uaddr = %s, merged uaddr = %s\n", uaddr, m_uaddr); #endif if (allocated_uaddr != NULL) free(allocated_uaddr); return (m_uaddr); }
int check_access(SVCXPRT *xprt, rpcproc_t proc, rpcprog_t prog, unsigned int rpcbvers) { struct netbuf *caller = svc_getrpccaller(xprt); struct sockaddr *addr = (struct sockaddr *)caller->buf; #ifdef LIBWRAP struct request_info req; #endif /* * The older PMAP_* equivalents have the same numbers, so * they are accounted for here as well. */ switch (proc) { case RPCBPROC_SET: case RPCBPROC_UNSET: if (!insecure && !is_loopback(caller)) { #ifdef RPCBIND_DEBUG if (debugging) fprintf(stderr, " declined (non-loopback sender) \n"); #endif if (verboselog) logit(log_severity, addr, proc, prog, " declined (non-loopback sender)"); return 0; } break; case RPCBPROC_GETADDR: case RPCBPROC_CALLIT: case RPCBPROC_INDIRECT: case RPCBPROC_DUMP: case RPCBPROC_GETTIME: case RPCBPROC_UADDR2TADDR: case RPCBPROC_TADDR2UADDR: case RPCBPROC_GETVERSADDR: case RPCBPROC_GETADDRLIST: case RPCBPROC_GETSTAT: default: break; } #ifdef LIBWRAP if (addr->sa_family == AF_LOCAL) return 1; request_init(&req, RQ_DAEMON, "rpcbind", RQ_CLIENT_SIN, addr, 0); sock_methods(&req); if(!hosts_access(&req)) { logit(deny_severity, addr, proc, prog, ": request from unauthorized host"); return 0; } #endif if (verboselog) logit(log_severity, addr, proc, prog, ""); return 1; }
/* * find the address of the caller of an RPC procedure. */ struct sockaddr_in * amu_svc_getcaller(SVCXPRT *xprt) { struct netbuf *nbp = (struct netbuf *) NULL; if ((nbp = svc_getrpccaller(xprt)) != NULL) return (struct sockaddr_in *) nbp->buf; /* all OK */ return NULL; /* failed */ }
/* * Get the client's hostname from the transport handle * If the name is not available then return "(anon)". */ void getclientsnames(SVCXPRT *transp, struct netbuf **nbuf, struct nd_hostservlist **serv) { struct netconfig *nconf; char tmp[MAXIPADDRLEN]; char *host = NULL; nconf = getnetconfigent(transp->xp_netid); if (nconf == NULL) { syslog(LOG_ERR, "%s: getnetconfigent failed", transp->xp_netid); *serv = anon_client(host); return; } *nbuf = svc_getrpccaller(transp); if (*nbuf == NULL) { freenetconfigent(nconf); *serv = anon_client(host); return; } /* * Use the this API instead of the netdir_getbyaddr() * to avoid service lookup. */ if (__netdir_getbyaddr_nosrv(nconf, serv, *nbuf)) { host = &tmp[0]; if (strcmp(nconf->nc_protofmly, NC_INET) == 0) { struct sockaddr_in *sa; /* LINTED pointer alignment */ sa = (struct sockaddr_in *)((*nbuf)->buf); (void) inet_ntoa_r(sa->sin_addr, tmp); *serv = anon_client(host); freenetconfigent(nconf); return; } else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) { struct sockaddr_in6 *sa; /* LINTED pointer alignment */ sa = (struct sockaddr_in6 *)((*nbuf)->buf); (void) inet_ntop(AF_INET6, sa->sin6_addr.s6_addr, tmp, INET6_ADDRSTRLEN); *serv = anon_client(host); freenetconfigent(nconf); return; } freenetconfigent(nconf); *serv = anon_client(host); return; } freenetconfigent(nconf); }
/* * NLM_UNSHARE, NLM4_UNSHARE * * Release a DOS-style share reservation */ void nlm_do_unshare(nlm4_shareargs *argp, nlm4_shareres *resp, struct svc_req *sr) { struct nlm_globals *g; struct nlm_host *host; struct netbuf *addr; vnode_t *vp = NULL; char *netid; int error; struct shrlock shr; nlm_copy_netobj(&resp->cookie, &argp->cookie); netid = svc_getnetid(sr->rq_xprt); addr = svc_getrpccaller(sr->rq_xprt); g = zone_getspecific(nlm_zone_key, curzone); host = nlm_host_find(g, netid, addr); if (host == NULL) { resp->stat = nlm4_denied_nolocks; return; } DTRACE_PROBE3(unshare__start, struct nlm_globals *, g, struct nlm_host *, host, nlm4_shareargs *, argp); if (NLM_IN_GRACE(g)) { resp->stat = nlm4_denied_grace_period; goto out; } vp = nlm_fh_to_vp(&argp->share.fh); if (vp == NULL) { resp->stat = nlm4_stale_fh; goto out; } /* Convert to local form. */ nlm_init_shrlock(&shr, &argp->share, host); error = VOP_SHRLOCK(vp, F_UNSHARE, &shr, FREAD | FWRITE, CRED(), NULL); (void) error; resp->stat = nlm4_granted; out: DTRACE_PROBE3(unshare__end, struct nlm_globals *, g, struct nlm_host *, host, nlm4_shareres *, resp); if (vp != NULL) VN_RELE(vp); nlm_host_release(g, host); }
/* * Purpose: Log name of function called and source address * Returns: Nothing * Notes: Extracts the source address from the transport handle * passed in as part of the called procedure specification */ static void log_from_addr(const char *fun_name, struct svc_req *req) { struct sockaddr *addr; char hostname_buf[NI_MAXHOST]; addr = svc_getrpccaller(req->rq_xprt)->buf; if (getnameinfo(addr , addr->sa_len, hostname_buf, sizeof hostname_buf, NULL, 0, 0) != 0) return; syslog(LOG_DEBUG, "%s from %s", fun_name, hostname_buf); }
void * nlm_test_msg_1_svc(nlm_testargs *arg, struct svc_req *rqstp) { nlm_testres res; static char dummy; struct sockaddr *addr; CLIENT *cli; int success; struct timeval timeo; struct nlm4_lock arg4; struct nlm4_holder *holder; nlmtonlm4(&arg->alock, &arg4); if (debug_level) log_from_addr("nlm_test_msg", rqstp); holder = testlock(&arg4, arg->exclusive, 0); res.cookie = arg->cookie; if (holder == NULL) { res.stat.stat = nlm_granted; } else { res.stat.stat = nlm_denied; memcpy(&res.stat.nlm_testrply_u.holder, holder, sizeof(struct nlm_holder)); res.stat.nlm_testrply_u.holder.l_offset = holder->l_offset; res.stat.nlm_testrply_u.holder.l_len = holder->l_len; } /* * nlm_test has different result type to the other operations, so * can't use transmit_result() in this case */ addr = svc_getrpccaller(rqstp->rq_xprt)->buf; if ((cli = get_client(addr, NLM_VERS)) != NULL) { timeo.tv_sec = 0; /* No timeout - not expecting response */ timeo.tv_usec = 0; success = clnt_call(cli, NLM_TEST_RES, (xdrproc_t)xdr_nlm_testres, &res, (xdrproc_t)xdr_void, &dummy, timeo); if (debug_level > 2) syslog(LOG_DEBUG, "clnt_call returns %d", success); } return (NULL); }
/* ARGSUSED */ static void * rpcbproc_getaddr_4_local(void *arg, struct svc_req *rqstp, SVCXPRT *transp, rpcvers_t rpcbversnum /*__unused*/) { RPCB *regp = (RPCB *)arg; #ifdef RPCBIND_DEBUG if (debugging) { char *uaddr; uaddr = taddr2uaddr(rpcbind_get_conf(transp->xp_netid), svc_getrpccaller(transp)); fprintf(stderr, "RPCB_GETADDR req for (%lu, %lu, %s) from %s: ", (unsigned long)regp->r_prog, (unsigned long)regp->r_vers, regp->r_netid, uaddr); free(uaddr); } #endif return (rpcbproc_getaddr_com(regp, rqstp, transp, RPCBVERS4, RPCB_ALLVERS)); }
static int root_auth(SVCXPRT *trans, struct svc_req *rqstp) { uid_t uid; struct sockaddr *remote; remote = svc_getrpccaller(trans)->buf; if (remote->sa_family != AF_UNIX) { if (debugging) fprintf(stderr, "client didn't use AF_UNIX\n"); return (0); } if (__rpc_get_local_uid(trans, &uid) < 0) { if (debugging) fprintf(stderr, "__rpc_get_local_uid failed\n"); return (0); } if (debugging) fprintf(stderr, "local_uid %ld\n", uid); if (uid == 0) return (1); if (rqstp->rq_cred.oa_flavor == AUTH_SYS) { if (((uid_t) ((struct authunix_parms *) rqstp->rq_clntcred)->aup_uid) == uid) { return (1); } else { if (debugging) fprintf(stderr, "local_uid %ld mismatches auth %ld\n", uid, ((uid_t) ((struct authunix_parms *)rqstp->rq_clntcred)->aup_uid)); return (0); } } else { if (debugging) fprintf(stderr, "Not auth sys\n"); return (0); } }
/* * Handles server to client callbacks. */ static void nfscb_program(struct svc_req *rqst, SVCXPRT *xprt) { struct nfsrv_descript nd; int cacherep, credflavor; memset(&nd, 0, sizeof(nd)); if (rqst->rq_proc != NFSPROC_NULL && rqst->rq_proc != NFSV4PROC_CBCOMPOUND) { svcerr_noproc(rqst); svc_freereq(rqst); return; } nd.nd_procnum = rqst->rq_proc; nd.nd_flag = (ND_NFSCB | ND_NFSV4); /* * Note: we want rq_addr, not svc_getrpccaller for nd_nam2 - * NFS_SRVMAXDATA uses a NULL value for nd_nam2 to detect TCP * mounts. */ nd.nd_mrep = rqst->rq_args; rqst->rq_args = NULL; newnfs_realign(&nd.nd_mrep, M_WAITOK); nd.nd_md = nd.nd_mrep; nd.nd_dpos = mtod(nd.nd_md, caddr_t); nd.nd_nam = svc_getrpccaller(rqst); nd.nd_nam2 = rqst->rq_addr; nd.nd_mreq = NULL; nd.nd_cred = NULL; NFSCL_DEBUG(1, "cbproc=%d\n",nd.nd_procnum); if (nd.nd_procnum != NFSPROC_NULL) { if (!svc_getcred(rqst, &nd.nd_cred, &credflavor)) { svcerr_weakauth(rqst); svc_freereq(rqst); m_freem(nd.nd_mrep); return; } /* For now, I don't care what credential flavor was used. */ #ifdef notyet #ifdef MAC mac_cred_associate_nfsd(nd.nd_cred); #endif #endif cacherep = nfs_cbproc(&nd, rqst->rq_xid); } else { NFSMGET(nd.nd_mreq); nd.nd_mreq->m_len = 0; cacherep = RC_REPLY; } if (nd.nd_mrep != NULL) m_freem(nd.nd_mrep); if (nd.nd_cred != NULL) crfree(nd.nd_cred); if (cacherep == RC_DROPIT) { if (nd.nd_mreq != NULL) m_freem(nd.nd_mreq); svc_freereq(rqst); return; } if (nd.nd_mreq == NULL) { svcerr_decode(rqst); svc_freereq(rqst); return; } if (nd.nd_repstat & NFSERR_AUTHERR) { svcerr_auth(rqst, nd.nd_repstat & ~NFSERR_AUTHERR); if (nd.nd_mreq != NULL) m_freem(nd.nd_mreq); } else if (!svc_sendreply_mbuf(rqst, nd.nd_mreq)) svcerr_systemerr(rqst); else NFSCL_DEBUG(1, "cbrep sent\n"); svc_freereq(rqst); }
/* * Check for special multihomed host cookie in the key. If there, * collect the addresses from the comma separated list and return * the one that's nearest the client. */ static int multihomed(struct ypreq_key req, struct ypresp_val *resp, SVCXPRT *xprt, DBM *fdb) { char *cp, *bp; ulong_t bestaddr, call_addr; struct netbuf *nbuf; char name[PATH_MAX]; static char localbuf[_PBLKSIZ]; /* buffer for multihomed IPv6 addr */ if (strcmp(req.map, "hosts.byname") && strcmp(req.map, "ipnodes.byname")) /* default status is YP_NOKEY */ return (0); if (strncmp(req.keydat.dptr, "YP_MULTI_", 9)) { datum tmpname; strncpy(name, "YP_MULTI_", 9); strncpy(name + 9, req.keydat.dptr, req.keydat.dsize); tmpname.dsize = req.keydat.dsize + 9; tmpname.dptr = name; resp->valdat = dbm_fetch(fdb, tmpname); } else { /* * Return whole line (for debugging) if YP_MULTI_hostnam * is specified. */ resp->valdat = dbm_fetch(fdb, req.keydat); if (resp->valdat.dptr != NULL) return (1); } if (resp->valdat.dptr == NULL) return (0); strncpy(name, req.keydat.dptr, req.keydat.dsize); name[req.keydat.dsize] = NULL; if (strcmp(req.map, "ipnodes.byname") == 0) { /* * This section handles multihomed IPv6 addresses. * It returns all the IPv6 addresses one per line and only * the requested hostname is returned. NO aliases will be * returned. This is done exactly the same way DNS forwarding * daemon handles multihomed hosts. * New IPv6 enabled clients should be able to handle this * information returned. The sorting is also the client's * responsibility. */ char *buf, *endbuf; if ((buf = strdup(resp->valdat.dptr)) == NULL) /* no memory */ return (0); if ((bp = strtok(buf, " \t")) == NULL) { /* no address field */ free(buf); return (0); } if ((cp = strtok(NULL, "")) == NULL) { /* no host field */ free(buf); return (0); } if ((cp = strtok(bp, ",")) != NULL) { /* multihomed host */ int bsize; localbuf[0] = '\0'; bsize = sizeof (localbuf); endbuf = localbuf; while (cp) { if ((strlen(cp) + strlen(name)) >= bsize) { /* out of range */ break; } sprintf(endbuf, "%s %s\n", cp, name); cp = strtok(NULL, ","); endbuf = &endbuf[strlen(endbuf)]; bsize = &localbuf[sizeof (localbuf)] - endbuf; } resp->valdat.dptr = localbuf; resp->valdat.dsize = strlen(localbuf); } free(buf); /* remove trailing newline */ if (resp->valdat.dsize && resp->valdat.dptr[resp->valdat.dsize-1] == '\n') { resp->valdat.dptr[resp->valdat.dsize-1] = '\0'; resp->valdat.dsize -= 1; } resp->status = YP_TRUE; return (1); } nbuf = svc_getrpccaller(xprt); /* * OK, now I have a netbuf structure which I'm supposed to * treat as opaque... I hate transport independance! * So, we're just gonna doit wrong... By wrong I mean that * we assume that the buf part of the netbuf structure is going * to be a sockaddr_in. We'll then check the assumed family * member and hope that we find AF_INET in there... if not * then we can't continue. */ if (((struct sockaddr_in *)(nbuf->buf))->sin_family != AF_INET) return (0); call_addr = ((struct sockaddr_in *)(nbuf->buf))->sin_addr.s_addr; cp = resp->valdat.dptr; if ((bp = strtok(cp, " \t")) == NULL) /* no address field */ return (0); if ((cp = strtok(NULL, "")) == NULL) /* no host field */ return (0); bp = strtok(bp, ","); bestaddr = inet_addr(bp); while (cp = strtok(NULL, ",")) { ulong_t taddr; taddr = inet_addr(cp); if (ntohl(call_addr ^ taddr) < ntohl(call_addr ^ bestaddr)) bestaddr = taddr; } cp = resp->valdat.dptr; sprintf(cp, "%s %s", inet_ntoa(*(struct in_addr *)&bestaddr), name); resp->valdat.dsize = strlen(cp); resp->status = YP_TRUE; return (1); }
static int omultihomed(struct yprequest req, struct ypresponse *resp, SVCXPRT *xprt, DBM *fdb) { char *cp, *bp; char name[PATH_MAX]; struct netbuf *nbuf; ulong_t bestaddr, call_addr; if (strcmp(req.ypmatch_req_map, "hosts.byname")) return (0); if (strncmp(req.ypmatch_req_keyptr, "YP_MULTI_", 9)) { datum tmpname; strncpy(name, "YP_MULTI_", 9); strncpy(name + 9, req.ypmatch_req_keyptr, req.ypmatch_req_keysize); tmpname.dsize = req.ypmatch_req_keysize + 9; tmpname.dptr = name; resp->ypmatch_resp_valdat = dbm_fetch(fdb, tmpname); } else { resp->ypmatch_resp_valdat = dbm_fetch(fdb, req.ypmatch_req_keydat); if (resp->ypmatch_resp_valptr != NULL) return (1); } if (resp->ypmatch_resp_valptr == NULL) return (0); strncpy(name, req.ypmatch_req_keyptr, req.ypmatch_req_keysize); name[req.ypmatch_req_keysize] = NULL; nbuf = svc_getrpccaller(xprt); /* * OK, now I have a netbuf structure which I'm supposed to treat * as opaque... I hate transport independance! So, we're just * gonna doit wrong... By wrong I mean that we assume that the * buf part of the netbuf structure is going to be a sockaddr_in. * We'll then check the assumed family member and hope that we * find AF_INET in there... if not then we can't continue. */ if (((struct sockaddr_in *)(nbuf->buf))->sin_family != AF_INET) return (0); call_addr = ((struct sockaddr_in *)(nbuf->buf))->sin_addr.s_addr; cp = resp->ypmatch_resp_valptr; if ((bp = strtok(cp, "\t")) == NULL) /* No address field */ return (0); if ((cp = strtok(NULL, "")) == NULL) /* No host field */ return (0); bp = strtok(bp, ","); bestaddr = inet_addr(bp); while (cp = strtok(NULL, ",")) { ulong_t taddr; taddr = inet_addr(cp); if (ntohl(call_addr ^ taddr) < ntohl(call_addr ^ bestaddr)) bestaddr = taddr; } cp = resp->ypmatch_resp_valptr; sprintf(cp, "%s %s", inet_ntoa(*(struct in_addr *)&bestaddr), name); resp->ypmatch_resp_valsize = strlen(cp); resp->ypmatch_resp_status = YP_TRUE; return (1); }
/* * This determines whether or not a passed domain is served by this * server, and returns a boolean. Used by both old and new protocol * versions. */ void ypdomain(SVCXPRT *transp, bool always_respond) { char domain_name[YPMAXDOMAIN + 1]; char *pdomain_name = domain_name; bool isserved; char *fun = "ypdomain"; struct netbuf *nbuf; sa_family_t af; memset(domain_name, 0, sizeof (domain_name)); if (!svc_getargs(transp, (xdrproc_t)xdr_ypdomain_wrap_string, (caddr_t)&pdomain_name)) { svcerr_decode(transp); return; } /* * If the file /var/yp/securenets is present on the server, and if * the hostname is present in the file, then let the client bind to * the server. */ nbuf = svc_getrpccaller(transp); af = ((struct sockaddr_storage *)nbuf->buf)->ss_family; if (af != AF_INET && af != AF_INET6) { logprintf("Protocol incorrect\n"); return; } if (!(check_secure_net_ti(nbuf, fun))) { MAP_ERR; return; } isserved = ypcheck_domain(domain_name); if (isserved || always_respond) { if (!svc_sendreply(transp, xdr_bool, (char *)&isserved)) { RESPOND_ERR; } if (!isserved) logprintf("Domain %s not supported\n", domain_name); } else { /* * This case is the one in which the domain is not * supported, and in which we are not to respond in the * unsupported case. We are going to make an error happen * to allow the portmapper to end his wait without the * normal timeout period. The assumption here is that * the only process in the world which is using the function * in its no-answer-if-nack form is the portmapper, which is * doing the krock for pseudo-broadcast. If some poor fool * calls this function as a single-cast message, the nack * case will look like an incomprehensible error. Sigh... * (The traditional Unix disclaimer) */ svcerr_decode(transp); logprintf("Domain %s not supported (broadcast)\n", domain_name); } }
int check_callit(SVCXPRT *xprt, struct r_rmtcall_args *args, int versnum /*__unused*/) { struct sockaddr *sa = (struct sockaddr *)svc_getrpccaller(xprt)->buf; /* * Always allow calling NULLPROC */ if (args->rmt_proc == 0) return 1; /* * XXX - this special casing sucks. */ switch (args->rmt_prog) { case RPCBPROG: /* * Allow indirect calls to ourselves in insecure mode. * The is_loopback checks aren't useful then anyway. */ if (!insecure) goto deny; break; case MOUNTPROG: if (args->rmt_proc != MOUNTPROC_MNT && args->rmt_proc != MOUNTPROC_UMNT) break; goto deny; case YPBINDPROG: if (args->rmt_proc != YPBINDPROC_SETDOM) break; /* FALLTHROUGH */ case YPPASSWDPROG: case NFS_PROGRAM: case RQUOTAPROG: goto deny; case YPPROG: switch (args->rmt_proc) { case YPPROC_ALL: case YPPROC_MATCH: case YPPROC_FIRST: case YPPROC_NEXT: goto deny; default: break; } default: break; } return 1; deny: #ifdef LIBWRAP logit(deny_severity, sa, args->rmt_proc, args->rmt_prog, ": indirect call not allowed"); #else logit(0, sa, args->rmt_proc, args->rmt_prog, ": indirect call not allowed"); #endif return 0; }
/* * NLM_LOCK, NLM_LOCK_MSG, NLM_NM_LOCK * NLM4_LOCK, NLM4_LOCK_MSG, NLM4_NM_LOCK * * Client request to set a lock, possibly blocking. * * If the lock needs to block, we return status blocked to * this RPC call, and then later call back the client with * a "granted" callback. Tricky aspects of this include: * sending a reply before this function returns, and then * borrowing this thread from the RPC service pool for the * wait on the lock and doing the later granted callback. * * We also have to keep a list of locks (pending + granted) * both to handle retransmitted requests, and to keep the * vnodes for those locks active. */ void nlm_do_lock(nlm4_lockargs *argp, nlm4_res *resp, struct svc_req *sr, nlm_reply_cb reply_cb, nlm_res_cb res_cb, nlm_testargs_cb grant_cb) { struct nlm_globals *g; struct flock64 fl; struct nlm_host *host = NULL; struct netbuf *addr; struct nlm_vhold *nvp = NULL; nlm_rpc_t *rpcp = NULL; char *netid; char *name; int error, flags; bool_t do_blocking = FALSE; bool_t do_mon_req = FALSE; enum nlm4_stats status; nlm_copy_netobj(&resp->cookie, &argp->cookie); name = argp->alock.caller_name; netid = svc_getnetid(sr->rq_xprt); addr = svc_getrpccaller(sr->rq_xprt); g = zone_getspecific(nlm_zone_key, curzone); host = nlm_host_findcreate(g, name, netid, addr); if (host == NULL) { DTRACE_PROBE4(no__host, struct nlm_globals *, g, char *, name, char *, netid, struct netbuf *, addr); status = nlm4_denied_nolocks; goto doreply; } DTRACE_PROBE3(start, struct nlm_globals *, g, struct nlm_host *, host, nlm4_lockargs *, argp); /* * If we may need to do _msg_ call needing an RPC * callback, get the RPC client handle now, * so we know if we can bind to the NLM service on * this client. * * Note: host object carries transport type. * One client using multiple transports gets * separate sysids for each of its transports. */ if (res_cb != NULL || (grant_cb != NULL && argp->block == TRUE)) { error = nlm_host_get_rpc(host, sr->rq_vers, &rpcp); if (error != 0) { status = nlm4_denied_nolocks; goto doreply; } } /* * During the "grace period", only allow reclaim. */ if (argp->reclaim == 0 && NLM_IN_GRACE(g)) { status = nlm4_denied_grace_period; goto doreply; } /* * Check whether we missed host shutdown event */ if (nlm_host_get_state(host) != argp->state) nlm_host_notify_server(host, argp->state); /* * Get a hold on the vnode for a lock operation. * Only lock() and share() need vhold objects. */ nvp = nlm_fh_to_vhold(host, &argp->alock.fh); if (nvp == NULL) { status = nlm4_stale_fh; goto doreply; } /* Convert to local form. */ error = nlm_init_flock(&fl, &argp->alock, host, sr->rq_vers, (argp->exclusive) ? F_WRLCK : F_RDLCK); if (error) { status = nlm4_failed; goto doreply; } /* * Try to lock non-blocking first. If we succeed * getting the lock, we can reply with the granted * status directly and avoid the complications of * making the "granted" RPC callback later. * * This also let's us find out now about some * possible errors like EROFS, etc. */ flags = F_REMOTELOCK | FREAD | FWRITE; error = nlm_vop_frlock(nvp->nv_vp, F_SETLK, &fl, flags, (u_offset_t)0, NULL, CRED(), NULL); DTRACE_PROBE3(setlk__res, struct flock64 *, &fl, int, flags, int, error); switch (error) { case 0: /* Got it without waiting! */ status = nlm4_granted; do_mon_req = TRUE; break; /* EINPROGRESS too? */ case EAGAIN: /* We did not get the lock. Should we block? */ if (argp->block == FALSE || grant_cb == NULL) { status = nlm4_denied; break; } /* * Should block. Try to reserve this thread * so we can use it to wait for the lock and * later send the granted message. If this * reservation fails, say "no resources". */ if (!svc_reserve_thread(sr->rq_xprt)) { status = nlm4_denied_nolocks; break; } /* * OK, can detach this thread, so this call * will block below (after we reply). */ status = nlm4_blocked; do_blocking = TRUE; do_mon_req = TRUE; break; case ENOLCK: /* Failed for lack of resources. */ status = nlm4_denied_nolocks; break; case EROFS: /* read-only file system */ status = nlm4_rofs; break; case EFBIG: /* file too big */ status = nlm4_fbig; break; case EDEADLK: /* dead lock condition */ status = nlm4_deadlck; break; default: status = nlm4_denied; break; } doreply: resp->stat.stat = status; /* * We get one of two function pointers; one for a * normal RPC reply, and another for doing an RPC * "callback" _res reply for a _msg function. * Use either of those to send the reply now. * * If sending this reply fails, just leave the * lock in the list for retransmitted requests. * Cleanup is via unlock or host rele (statmon). */ if (reply_cb != NULL) { /* i.e. nlm_lock_1_reply */ if (!(*reply_cb)(sr->rq_xprt, resp)) svcerr_systemerr(sr->rq_xprt); } if (res_cb != NULL && rpcp != NULL) NLM_INVOKE_CALLBACK("lock", rpcp, resp, res_cb); /* * The reply has been sent to the client. * Start monitoring this client (maybe). * * Note that the non-monitored (NM) calls pass grant_cb=NULL * indicating that the client doesn't support RPC callbacks. * No monitoring for these (lame) clients. */ if (do_mon_req && grant_cb != NULL) nlm_host_monitor(g, host, argp->state); if (do_blocking) { /* * We need to block on this lock, and when that * completes, do the granted RPC call. Note that * we "reserved" this thread above, so we can now * "detach" it from the RPC SVC pool, allowing it * to block indefinitely if needed. */ ASSERT(rpcp != NULL); (void) svc_detach_thread(sr->rq_xprt); nlm_block(argp, host, nvp, rpcp, &fl, grant_cb); } DTRACE_PROBE3(lock__end, struct nlm_globals *, g, struct nlm_host *, host, nlm4_res *, resp); if (rpcp != NULL) nlm_host_rele_rpc(host, rpcp); nlm_vhold_release(host, nvp); nlm_host_release(g, host); }
/* * NLM_TEST, NLM_TEST_MSG, * NLM4_TEST, NLM4_TEST_MSG, * Client inquiry about locks, non-blocking. */ void nlm_do_test(nlm4_testargs *argp, nlm4_testres *resp, struct svc_req *sr, nlm_testres_cb cb) { struct nlm_globals *g; struct nlm_host *host; struct nlm4_holder *lh; struct nlm_owner_handle *oh; nlm_rpc_t *rpcp = NULL; vnode_t *vp = NULL; struct netbuf *addr; char *netid; char *name; int error; struct flock64 fl; nlm_copy_netobj(&resp->cookie, &argp->cookie); name = argp->alock.caller_name; netid = svc_getnetid(sr->rq_xprt); addr = svc_getrpccaller(sr->rq_xprt); g = zone_getspecific(nlm_zone_key, curzone); host = nlm_host_findcreate(g, name, netid, addr); if (host == NULL) { resp->stat.stat = nlm4_denied_nolocks; return; } if (cb != NULL) { error = nlm_host_get_rpc(host, sr->rq_vers, &rpcp); if (error != 0) { resp->stat.stat = nlm4_denied_nolocks; goto out; } } vp = nlm_fh_to_vp(&argp->alock.fh); if (vp == NULL) { resp->stat.stat = nlm4_stale_fh; goto out; } if (NLM_IN_GRACE(g)) { resp->stat.stat = nlm4_denied_grace_period; goto out; } /* Convert to local form. */ error = nlm_init_flock(&fl, &argp->alock, host, sr->rq_vers, (argp->exclusive) ? F_WRLCK : F_RDLCK); if (error) { resp->stat.stat = nlm4_failed; goto out; } /* BSD: VOP_ADVLOCK(nv->nv_vp, NULL, F_GETLK, &fl, F_REMOTE); */ error = nlm_vop_frlock(vp, F_GETLK, &fl, F_REMOTELOCK | FREAD | FWRITE, (u_offset_t)0, NULL, CRED(), NULL); if (error) { resp->stat.stat = nlm4_failed; goto out; } if (fl.l_type == F_UNLCK) { resp->stat.stat = nlm4_granted; goto out; } resp->stat.stat = nlm4_denied; /* * This lock "test" fails due to a conflicting lock. * * If this is a v1 client, make sure the conflicting * lock range we report can be expressed with 32-bit * offsets. The lock range requested was expressed * as 32-bit offset and length, so at least part of * the conflicting lock should lie below MAX_UOFF32. * If the conflicting lock extends past that, we'll * trim the range to end at MAX_UOFF32 so this lock * can be represented in a 32-bit response. Check * the start also (paranoid, but a low cost check). */ if (sr->rq_vers < NLM4_VERS) { uint64 maxlen; if (fl.l_start > MAX_UOFF32) fl.l_start = MAX_UOFF32; maxlen = MAX_UOFF32 + 1 - fl.l_start; if (fl.l_len > maxlen) fl.l_len = maxlen; } /* * Build the nlm4_holder result structure. * * Note that lh->oh is freed via xdr_free, * xdr_nlm4_holder, xdr_netobj, xdr_bytes. */ oh = kmem_zalloc(sizeof (*oh), KM_SLEEP); oh->oh_sysid = (sysid_t)fl.l_sysid; lh = &resp->stat.nlm4_testrply_u.holder; lh->exclusive = (fl.l_type == F_WRLCK); lh->svid = fl.l_pid; lh->oh.n_len = sizeof (*oh); lh->oh.n_bytes = (void *)oh; lh->l_offset = fl.l_start; lh->l_len = fl.l_len; out: /* * If we have a callback function, use that to * deliver the response via another RPC call. */ if (cb != NULL && rpcp != NULL) NLM_INVOKE_CALLBACK("test", rpcp, resp, cb); if (vp != NULL) VN_RELE(vp); if (rpcp != NULL) nlm_host_rele_rpc(host, rpcp); nlm_host_release(g, host); }
/* * NLM_SHARE, NLM4_SHARE * * Request a DOS-style share reservation */ void nlm_do_share(nlm4_shareargs *argp, nlm4_shareres *resp, struct svc_req *sr) { struct nlm_globals *g; struct nlm_host *host; struct netbuf *addr; struct nlm_vhold *nvp = NULL; char *netid; char *name; int error; struct shrlock shr; nlm_copy_netobj(&resp->cookie, &argp->cookie); name = argp->share.caller_name; netid = svc_getnetid(sr->rq_xprt); addr = svc_getrpccaller(sr->rq_xprt); g = zone_getspecific(nlm_zone_key, curzone); host = nlm_host_findcreate(g, name, netid, addr); if (host == NULL) { resp->stat = nlm4_denied_nolocks; return; } DTRACE_PROBE3(share__start, struct nlm_globals *, g, struct nlm_host *, host, nlm4_shareargs *, argp); if (argp->reclaim == 0 && NLM_IN_GRACE(g)) { resp->stat = nlm4_denied_grace_period; goto out; } /* * Get holded vnode when on lock operation. * Only lock() and share() need vhold objects. */ nvp = nlm_fh_to_vhold(host, &argp->share.fh); if (nvp == NULL) { resp->stat = nlm4_stale_fh; goto out; } /* Convert to local form. */ nlm_init_shrlock(&shr, &argp->share, host); error = VOP_SHRLOCK(nvp->nv_vp, F_SHARE, &shr, FREAD | FWRITE, CRED(), NULL); if (error == 0) { resp->stat = nlm4_granted; nlm_host_monitor(g, host, 0); } else { resp->stat = nlm4_denied; } out: DTRACE_PROBE3(share__end, struct nlm_globals *, g, struct nlm_host *, host, nlm4_shareres *, resp); nlm_vhold_release(host, nvp); nlm_host_release(g, host); }
static void nfssvc_program(struct svc_req *rqst, SVCXPRT *xprt) { struct nfsrv_descript nd; struct nfsrvcache *rp = NULL; int cacherep, credflavor; memset(&nd, 0, sizeof(nd)); if (rqst->rq_vers == NFS_VER2) { if (rqst->rq_proc > NFSV2PROC_STATFS) { svcerr_noproc(rqst); svc_freereq(rqst); goto out; } nd.nd_procnum = newnfs_nfsv3_procid[rqst->rq_proc]; nd.nd_flag = ND_NFSV2; } else if (rqst->rq_vers == NFS_VER3) { if (rqst->rq_proc >= NFS_V3NPROCS) { svcerr_noproc(rqst); svc_freereq(rqst); goto out; } nd.nd_procnum = rqst->rq_proc; nd.nd_flag = ND_NFSV3; } else { if (rqst->rq_proc != NFSPROC_NULL && rqst->rq_proc != NFSV4PROC_COMPOUND) { svcerr_noproc(rqst); svc_freereq(rqst); goto out; } nd.nd_procnum = rqst->rq_proc; nd.nd_flag = ND_NFSV4; } /* * Note: we want rq_addr, not svc_getrpccaller for nd_nam2 - * NFS_SRVMAXDATA uses a NULL value for nd_nam2 to detect TCP * mounts. */ nd.nd_mrep = rqst->rq_args; rqst->rq_args = NULL; newnfs_realign(&nd.nd_mrep, M_WAITOK); nd.nd_md = nd.nd_mrep; nd.nd_dpos = mtod(nd.nd_md, caddr_t); nd.nd_nam = svc_getrpccaller(rqst); nd.nd_nam2 = rqst->rq_addr; nd.nd_mreq = NULL; nd.nd_cred = NULL; if (nfs_privport && (nd.nd_flag & ND_NFSV4) == 0) { /* Check if source port is privileged */ u_short port; struct sockaddr *nam = nd.nd_nam; struct sockaddr_in *sin; sin = (struct sockaddr_in *)nam; /* * INET/INET6 - same code: * sin_port and sin6_port are at same offset */ port = ntohs(sin->sin_port); if (port >= IPPORT_RESERVED && nd.nd_procnum != NFSPROC_NULL) { #ifdef INET6 char b6[INET6_ADDRSTRLEN]; #if defined(KLD_MODULE) /* Do not use ip6_sprintf: the nfs module should work without INET6. */ #define ip6_sprintf(buf, a) \ (sprintf((buf), "%x:%x:%x:%x:%x:%x:%x:%x", \ (a)->s6_addr16[0], (a)->s6_addr16[1], \ (a)->s6_addr16[2], (a)->s6_addr16[3], \ (a)->s6_addr16[4], (a)->s6_addr16[5], \ (a)->s6_addr16[6], (a)->s6_addr16[7]), \ (buf)) #endif #endif printf("NFS request from unprivileged port (%s:%d)\n", #ifdef INET6 sin->sin_family == AF_INET6 ? ip6_sprintf(b6, &satosin6(sin)->sin6_addr) : #if defined(KLD_MODULE) #undef ip6_sprintf #endif #endif inet_ntoa(sin->sin_addr), port); svcerr_weakauth(rqst); svc_freereq(rqst); m_freem(nd.nd_mrep); goto out; } } if (nd.nd_procnum != NFSPROC_NULL) { if (!svc_getcred(rqst, &nd.nd_cred, &credflavor)) { svcerr_weakauth(rqst); svc_freereq(rqst); m_freem(nd.nd_mrep); goto out; } /* Set the flag based on credflavor */ if (credflavor == RPCSEC_GSS_KRB5) { nd.nd_flag |= ND_GSS; } else if (credflavor == RPCSEC_GSS_KRB5I) { nd.nd_flag |= (ND_GSS | ND_GSSINTEGRITY); } else if (credflavor == RPCSEC_GSS_KRB5P) { nd.nd_flag |= (ND_GSS | ND_GSSPRIVACY); } else if (credflavor != AUTH_SYS) { svcerr_weakauth(rqst); svc_freereq(rqst); m_freem(nd.nd_mrep); goto out; } #ifdef MAC mac_cred_associate_nfsd(nd.nd_cred); #endif /* * Get a refcnt (shared lock) on nfsd_suspend_lock. * NFSSVC_SUSPENDNFSD will take an exclusive lock on * nfsd_suspend_lock to suspend these threads. * This must be done here, before the check of * nfsv4root exports by nfsvno_v4rootexport(). */ NFSLOCKV4ROOTMUTEX(); nfsv4_getref(&nfsd_suspend_lock, NULL, NFSV4ROOTLOCKMUTEXPTR, NULL); NFSUNLOCKV4ROOTMUTEX(); if ((nd.nd_flag & ND_NFSV4) != 0) { nd.nd_repstat = nfsvno_v4rootexport(&nd); if (nd.nd_repstat != 0) { NFSLOCKV4ROOTMUTEX(); nfsv4_relref(&nfsd_suspend_lock); NFSUNLOCKV4ROOTMUTEX(); svcerr_weakauth(rqst); svc_freereq(rqst); m_freem(nd.nd_mrep); goto out; } } cacherep = nfs_proc(&nd, rqst->rq_xid, xprt, &rp); NFSLOCKV4ROOTMUTEX(); nfsv4_relref(&nfsd_suspend_lock); NFSUNLOCKV4ROOTMUTEX(); } else { NFSMGET(nd.nd_mreq); nd.nd_mreq->m_len = 0; cacherep = RC_REPLY; } if (nd.nd_mrep != NULL) m_freem(nd.nd_mrep); if (nd.nd_cred != NULL) crfree(nd.nd_cred); if (cacherep == RC_DROPIT) { if (nd.nd_mreq != NULL) m_freem(nd.nd_mreq); svc_freereq(rqst); goto out; } if (nd.nd_mreq == NULL) { svcerr_decode(rqst); svc_freereq(rqst); goto out; } if (nd.nd_repstat & NFSERR_AUTHERR) { svcerr_auth(rqst, nd.nd_repstat & ~NFSERR_AUTHERR); if (nd.nd_mreq != NULL) m_freem(nd.nd_mreq); } else if (!svc_sendreply_mbuf(rqst, nd.nd_mreq)) { svcerr_systemerr(rqst); } if (rp != NULL) { nfsrvd_sentcache(rp, (rqst->rq_reply_seq != 0 || SVC_ACK(xprt, NULL)), rqst->rq_reply_seq); } svc_freereq(rqst); out: NFSEXITCODE(0); }
int resolv_req(bool *fwding, CLIENT **client, int *pid, char *tp, SVCXPRT *xprt, struct ypreq_key *req, char *map) { enum clnt_stat stat; struct timeval tv; struct ypfwdreq_key4 fwd_req4; struct ypfwdreq_key6 fwd_req6; struct in6_addr in6; int byname, byaddr; int byname_v6, byaddr_v6; #ifdef TDRPC struct sockaddr_in *addrp; #else struct netbuf *nb; char *uaddr; char *cp; int i; sa_family_t caller_af = AF_UNSPEC; struct sockaddr_in *sin4; struct sockaddr_in6 *sin6; #endif if (! *fwding) return (FALSE); byname = strcmp(map, "hosts.byname") == 0; byaddr = strcmp(map, "hosts.byaddr") == 0; byname_v6 = strcmp(map, "ipnodes.byname") == 0; byaddr_v6 = strcmp(map, "ipnodes.byaddr") == 0; if ((!byname && !byaddr && !byname_v6 && !byaddr_v6) || req->keydat.dsize == 0 || req->keydat.dptr[0] == '\0' || !isascii(req->keydat.dptr[0]) || !isgraph(req->keydat.dptr[0])) { /* default status is YP_NOKEY */ return (FALSE); } #ifdef TDRPC fwd_req4.map = map; fwd_req4.keydat = req->keydat; fwd_req4.xid = svc_getxid(xprt); addrp = svc_getcaller(xprt); fwd_req4.ip = addrp->sin_addr.s_addr; fwd_req4.port = addrp->sin_port; #else /* * In order to tell if we have an IPv4 or IPv6 caller address, * we must know that nb->buf is a (sockaddr_in *) or a * (sockaddr_in6 *). Hence, we might as well dispense with the * conversion to uaddr and parsing of same that this section * of the code previously involved itself in. */ nb = svc_getrpccaller(xprt); if (nb != 0) caller_af = ((struct sockaddr_storage *)nb->buf)->ss_family; if (caller_af == AF_INET6) { fwd_req6.map = map; fwd_req6.keydat = req->keydat; fwd_req6.xid = svc_getxid(xprt); sin6 = (struct sockaddr_in6 *)nb->buf; fwd_req6.addr = (uint32_t *)&in6; memcpy(fwd_req6.addr, sin6->sin6_addr.s6_addr, sizeof (in6)); fwd_req6.port = ntohs(sin6->sin6_port); } else if (caller_af == AF_INET) { fwd_req4.map = map; fwd_req4.keydat = req->keydat; fwd_req4.xid = svc_getxid(xprt); sin4 = (struct sockaddr_in *)nb->buf; fwd_req4.ip = ntohl(sin4->sin_addr.s_addr); fwd_req4.port = ntohs(sin4->sin_port); } else { syslog(LOG_ERR, "unknown caller IP address family %d", caller_af); return (FALSE); } #endif /* Restart resolver if it died. (possible overkill) */ if (kill(*pid, 0)) { syslog(LOG_INFO, "Restarting resolv server: old one (pid %d) died.\n", *pid); if (*client != NULL) clnt_destroy (*client); setup_resolv(fwding, pid, client, tp, 0 /* transient p# */); if (!*fwding) { syslog(LOG_ERR, "can't restart resolver: ending resolv service.\n"); return (FALSE); } } /* may need to up timeout */ tv.tv_sec = 10; tv.tv_usec = 0; if (caller_af == AF_INET6) { stat = clnt_call(*client, YPDNSPROC6, xdr_ypfwdreq_key6, (char *)&fwd_req6, xdr_void, 0, tv); } else { stat = clnt_call(*client, YPDNSPROC4, xdr_ypfwdreq_key4, (char *)&fwd_req4, xdr_void, 0, tv); } if (stat == RPC_SUCCESS) /* expected */ return (TRUE); else { /* Over kill error recovery */ /* make one attempt to restart service before turning off */ syslog(LOG_INFO, "Restarting resolv server: old one not responding.\n"); if (!kill(*pid, 0)) kill (*pid, SIGINT); /* cleanup old one */ if (*client != NULL) clnt_destroy (*client); setup_resolv(fwding, pid, client, tp, 0 /* transient p# */); if (!*fwding) { syslog(LOG_ERR, "can't restart resolver: ending resolv service.\n"); return (FALSE); } if (caller_af == AF_INET6) { stat = clnt_call(*client, YPDNSPROC6, xdr_ypfwdreq_key6, (char *)&fwd_req6, xdr_void, 0, tv); } else { stat = clnt_call(*client, YPDNSPROC4, xdr_ypfwdreq_key4, (char *)&fwd_req4, xdr_void, 0, tv); } if (stat == RPC_SUCCESS) /* expected */ return (TRUE); else { /* no more restarts */ clnt_destroy (*client); *fwding = FALSE; /* turn off fwd'ing */ syslog(LOG_ERR, "restarted resolver not responding: ending resolv service.\n"); return (FALSE); } } }
/* * NLM_UNLOCK, NLM_UNLOCK_MSG, * NLM4_UNLOCK, NLM4_UNLOCK_MSG, * Client removes one of their locks. */ void nlm_do_unlock(nlm4_unlockargs *argp, nlm4_res *resp, struct svc_req *sr, nlm_res_cb cb) { struct nlm_globals *g; struct nlm_host *host; struct netbuf *addr; nlm_rpc_t *rpcp = NULL; vnode_t *vp = NULL; char *netid; char *name; int error; struct flock64 fl; nlm_copy_netobj(&resp->cookie, &argp->cookie); netid = svc_getnetid(sr->rq_xprt); addr = svc_getrpccaller(sr->rq_xprt); name = argp->alock.caller_name; /* * NLM_UNLOCK operation doesn't have an error code * denoting that operation failed, so we always * return nlm4_granted except when the server is * in a grace period. */ resp->stat.stat = nlm4_granted; g = zone_getspecific(nlm_zone_key, curzone); host = nlm_host_findcreate(g, name, netid, addr); if (host == NULL) return; if (cb != NULL) { error = nlm_host_get_rpc(host, sr->rq_vers, &rpcp); if (error != 0) goto out; } DTRACE_PROBE3(start, struct nlm_globals *, g, struct nlm_host *, host, nlm4_unlockargs *, argp); if (NLM_IN_GRACE(g)) { resp->stat.stat = nlm4_denied_grace_period; goto out; } vp = nlm_fh_to_vp(&argp->alock.fh); if (vp == NULL) goto out; /* Convert to local form. */ error = nlm_init_flock(&fl, &argp->alock, host, sr->rq_vers, F_UNLCK); if (error) goto out; /* BSD: VOP_ADVLOCK(nv->nv_vp, NULL, F_UNLCK, &fl, F_REMOTE); */ error = nlm_vop_frlock(vp, F_SETLK, &fl, F_REMOTELOCK | FREAD | FWRITE, (u_offset_t)0, NULL, CRED(), NULL); DTRACE_PROBE1(unlock__res, int, error); out: /* * If we have a callback function, use that to * deliver the response via another RPC call. */ if (cb != NULL && rpcp != NULL) NLM_INVOKE_CALLBACK("unlock", rpcp, resp, cb); DTRACE_PROBE3(unlock__end, struct nlm_globals *, g, struct nlm_host *, host, nlm4_res *, resp); if (vp != NULL) VN_RELE(vp); if (rpcp != NULL) nlm_host_rele_rpc(host, rpcp); nlm_host_release(g, host); }
/* * NLM_CANCEL, NLM_CANCEL_MSG, * NLM4_CANCEL, NLM4_CANCEL_MSG, * Client gives up waiting for a blocking lock. */ void nlm_do_cancel(nlm4_cancargs *argp, nlm4_res *resp, struct svc_req *sr, nlm_res_cb cb) { struct nlm_globals *g; struct nlm_host *host; struct netbuf *addr; struct nlm_vhold *nvp = NULL; nlm_rpc_t *rpcp = NULL; char *netid; char *name; int error; struct flock64 fl; nlm_copy_netobj(&resp->cookie, &argp->cookie); netid = svc_getnetid(sr->rq_xprt); addr = svc_getrpccaller(sr->rq_xprt); name = argp->alock.caller_name; g = zone_getspecific(nlm_zone_key, curzone); host = nlm_host_findcreate(g, name, netid, addr); if (host == NULL) { resp->stat.stat = nlm4_denied_nolocks; return; } if (cb != NULL) { error = nlm_host_get_rpc(host, sr->rq_vers, &rpcp); if (error != 0) { resp->stat.stat = nlm4_denied_nolocks; goto out; } } DTRACE_PROBE3(start, struct nlm_globals *, g, struct nlm_host *, host, nlm4_cancargs *, argp); if (NLM_IN_GRACE(g)) { resp->stat.stat = nlm4_denied_grace_period; goto out; } nvp = nlm_fh_to_vhold(host, &argp->alock.fh); if (nvp == NULL) { resp->stat.stat = nlm4_stale_fh; goto out; } /* Convert to local form. */ error = nlm_init_flock(&fl, &argp->alock, host, sr->rq_vers, (argp->exclusive) ? F_WRLCK : F_RDLCK); if (error) { resp->stat.stat = nlm4_failed; goto out; } error = nlm_slreq_unregister(host, nvp, &fl); if (error != 0) { /* * There's no sleeping lock request corresponding * to the lock. Then requested sleeping lock * doesn't exist. */ resp->stat.stat = nlm4_denied; goto out; } fl.l_type = F_UNLCK; error = nlm_vop_frlock(nvp->nv_vp, F_SETLK, &fl, F_REMOTELOCK | FREAD | FWRITE, (u_offset_t)0, NULL, CRED(), NULL); resp->stat.stat = (error == 0) ? nlm4_granted : nlm4_denied; out: /* * If we have a callback function, use that to * deliver the response via another RPC call. */ if (cb != NULL && rpcp != NULL) NLM_INVOKE_CALLBACK("cancel", rpcp, resp, cb); DTRACE_PROBE3(cancel__end, struct nlm_globals *, g, struct nlm_host *, host, nlm4_res *, resp); if (rpcp != NULL) nlm_host_rele_rpc(host, rpcp); nlm_vhold_release(host, nvp); nlm_host_release(g, host); }
/* * 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) {