/* * rpcb_log - log request for service */ void rpcb_log(boolean_t verdict, SVCXPRT *transp, rpcproc_t proc, rpcprog_t prog, boolean_t pm) { struct netconfig *conf; const char *client = "unknown"; char *uaddr; char buf[BUFSIZ]; /* * Transform the transport address into something printable. */ if ((conf = rpcbind_get_conf(transp->xp_netid)) == 0) { syslog(LOG_WARNING, "unknown transport (rpcbind_get_conf failed)"); } else if (strcmp(conf->nc_protofmly, "inet") == 0 || strcmp(conf->nc_protofmly, "inet6") == 0) { client = sgen_toa(svc_getgencaller(transp)); } else if ((uaddr = taddr2uaddr(conf, &(transp->xp_rtaddr))) == NULL) { syslog(LOG_WARNING, "unknown address (taddr2uaddr failed)"); } else { (void) snprintf(buf, sizeof (buf), "%s(%s)", conf->nc_protofmly, uaddr); free(uaddr); client = buf; } qsyslog(verdict ? allow_severity : deny_severity, "%sconnect from %s to %s(%s)", verdict ? "" : "refused ", client, find_procname(proc, pm), find_progname(prog)); }
void rpcbs_rmtcall(rpcvers_t rtype, rpcproc_t rpcbproc, rpcprog_t prog, rpcvers_t vers, rpcproc_t proc, char *netid, rpcblist_ptr rbl) { rpcbs_rmtcalllist *rl; struct netconfig *nconf; if (rtype > RPCBVERS_STAT) return; for (rl = inf[rtype].rmtinfo; rl; rl = rl->next) { if(rl->netid == NULL) return; if ((rl->prog == prog) && (rl->vers == vers) && (rl->proc == proc) && (strcmp(rl->netid, netid) == 0)) { if ((rbl == NULL) || (rbl->rpcb_map.r_vers != vers)) rl->failure++; else rl->success++; if (rpcbproc == RPCBPROC_INDIRECT) rl->indirect++; return; } } nconf = rpcbind_get_conf(netid); if (nconf == NULL) { return; } rl = (rpcbs_rmtcalllist *) malloc(sizeof (rpcbs_rmtcalllist)); if (rl == NULL) { return; } rl->prog = prog; rl->vers = vers; rl->proc = proc; rl->netid = nconf->nc_netid; if ((rbl == NULL) || (rbl->rpcb_map.r_vers != vers)) { rl->failure = 1; rl->success = 0; } else { rl->failure = 0; rl->success = 1; } rl->indirect = 1; rl->next = inf[rtype].rmtinfo; inf[rtype].rmtinfo = rl; return; }
void rpcbs_getaddr(rpcvers_t rtype, rpcprog_t prog, rpcvers_t vers, char *netid, char *uaddr) { rpcbs_addrlist *al; struct netconfig *nconf; if (rtype >= RPCBVERS_STAT) return; for (al = inf[rtype].addrinfo; al; al = al->next) { if(al->netid == NULL) return; if ((al->prog == prog) && (al->vers == vers) && (strcmp(al->netid, netid) == 0)) { if ((uaddr == NULL) || (uaddr[0] == 0)) al->failure++; else al->success++; return; } } nconf = rpcbind_get_conf(netid); if (nconf == NULL) { return; } al = (rpcbs_addrlist *) malloc(sizeof (rpcbs_addrlist)); if (al == NULL) { return; } al->prog = prog; al->vers = vers; al->netid = nconf->nc_netid; if ((uaddr == NULL) || (uaddr[0] == 0)) { al->failure = 1; al->success = 0; } else { al->failure = 0; al->success = 1; } al->next = inf[rtype].addrinfo; inf[rtype].addrinfo = al; }
/* 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)); }
/* * rpcb_check; the rpcbind/portmap access check function. */ boolean_t rpcb_check(SVCXPRT *transp, rpcproc_t procnum, boolean_t ispmap) { struct netconfig *conf; boolean_t res = B_TRUE; if ((conf = rpcbind_get_conf(transp->xp_netid)) == 0) { syslog(LOG_ERR, "rpcbind_get_conf failed: no client address checks"); return (B_TRUE); } /* * Require IPv4 for pmap calls; they're not defined for anything else. */ if (ispmap && strcmp(conf->nc_protofmly, "inet") != 0) { res = B_FALSE; } else if (strcmp(conf->nc_protofmly, "inet") == 0 || strcmp(conf->nc_protofmly, "inet6") == 0) { const char *addr_string = sgen_toa(svc_getgencaller(transp)); if (!localxprt(transp, ispmap) && (local_only || hosts_ctl("rpcbind", addr_string, addr_string, "") == 0)) { res = B_FALSE; } } out: if (!res) svcerr_auth(transp, AUTH_FAILED); if (verboselog || !res) rpcb_log(res, transp, procnum, 0, ispmap); return (res); }
/* ARGSUSED */ static void * rpcbproc_getaddrlist_4_local(void *arg, struct svc_req *rqstp /*__unused*/, SVCXPRT *transp, rpcvers_t versnum /*__unused*/) { RPCB *regp = (RPCB *)arg; static rpcb_entry_list_ptr rlist; register rpcblist_ptr rbl; rpcb_entry_list_ptr rp, tail; rpcprog_t prog; rpcvers_t vers; rpcb_entry *a; struct netconfig *nconf; struct netconfig *reg_nconf; char *saddr, *maddr = NULL; free_rpcb_entry_list(&rlist); tail = NULL; prog = regp->r_prog; vers = regp->r_vers; reg_nconf = rpcbind_get_conf(transp->xp_netid); if (reg_nconf == NULL) return (NULL); if (*(regp->r_addr) != '\0') { saddr = regp->r_addr; } else { saddr = NULL; } #ifdef RPCBIND_DEBUG if (debugging) { fprintf(stderr, "r_addr: %s r_netid: %s nc_protofmly: %s\n", regp->r_addr, regp->r_netid, reg_nconf->nc_protofmly); } #endif for (rbl = list_rbl; rbl != NULL; rbl = rbl->rpcb_next) { if ((rbl->rpcb_map.r_prog == prog) && (rbl->rpcb_map.r_vers == vers)) { nconf = rpcbind_get_conf(rbl->rpcb_map.r_netid); if (nconf == NULL) goto fail; if (strcmp(nconf->nc_protofmly, reg_nconf->nc_protofmly) != 0) { continue; /* not same proto family */ } #ifdef RPCBIND_DEBUG if (debugging) fprintf(stderr, "\tmerge with: %s\n", rbl->rpcb_map.r_addr); #endif if ((maddr = mergeaddr(transp, rbl->rpcb_map.r_netid, rbl->rpcb_map.r_addr, saddr)) == NULL) { #ifdef RPCBIND_DEBUG if (debugging) fprintf(stderr, " FAILED\n"); #endif continue; } else if (!maddr[0]) { #ifdef RPCBIND_DEBUG if (debugging) fprintf(stderr, " SUCCEEDED, but port died - maddr: nullstring\n"); #endif /* The server died. Unset this combination */ delete_prog(regp->r_prog); continue; } #ifdef RPCBIND_DEBUG if (debugging) fprintf(stderr, " SUCCEEDED maddr: %s\n", maddr); #endif /* * Add it to rlist. */ rp = malloc(sizeof (rpcb_entry_list)); if (rp == NULL) goto fail; a = &rp->rpcb_entry_map; a->r_maddr = maddr; a->r_nc_netid = nconf->nc_netid; a->r_nc_semantics = nconf->nc_semantics; a->r_nc_protofmly = nconf->nc_protofmly; a->r_nc_proto = nconf->nc_proto; rp->rpcb_entry_next = NULL; if (rlist == NULL) { rlist = rp; tail = rp; } else { tail->rpcb_entry_next = rp; tail = rp; } rp = NULL; } } #ifdef RPCBIND_DEBUG if (debugging) { for (rp = rlist; rp; rp = rp->rpcb_entry_next) { fprintf(stderr, "\t%s %s\n", rp->rpcb_entry_map.r_maddr, rp->rpcb_entry_map.r_nc_proto); } } #endif /* * XXX: getaddrlist info is also being stuffed into getaddr. * Perhaps wrong, but better than it not getting counted at all. */ rpcbs_getaddr(RPCBVERS4 - 2, prog, vers, transp->xp_netid, maddr); return (void *)&rlist; fail: free_rpcb_entry_list(&rlist); return (NULL); }
/* * Find a server address that can be used by `caller' to contact * the local service specified by `serv_uaddr'. If `clnt_uaddr' is * non-NULL, it is used instead of `caller' as a hint suggesting * the best address (e.g. the `r_addr' field of an rpc, which * contains the rpcbind server address that the caller used). * * Returns the best server address as a malloc'd "universal address" * string which should be freed by the caller. On error, returns NULL. */ char * addrmerge(struct netbuf *caller, const char *serv_uaddr, const char *clnt_uaddr, const char *netid) { struct ifaddrs *ifap, *ifp = NULL, *bestif; struct netbuf *serv_nbp = NULL, *hint_nbp = NULL, tbuf; struct sockaddr *caller_sa, *hint_sa, *ifsa, *ifmasksa, *serv_sa; struct sockaddr_storage ss; struct netconfig *nconf; char *caller_uaddr = NULL; const char *hint_uaddr = NULL; char *ret = NULL; int bestif_goodness; #ifdef ND_DEBUG if (debugging) fprintf(stderr, "addrmerge(caller, %s, %s, %s\n", serv_uaddr, clnt_uaddr == NULL ? "NULL" : clnt_uaddr, netid); #endif caller_sa = caller->buf; if ((nconf = rpcbind_get_conf(netid)) == NULL) goto freeit; if ((caller_uaddr = taddr2uaddr(nconf, caller)) == NULL) goto freeit; /* * Use `clnt_uaddr' as the hint if non-NULL, but ignore it if its * address family is different from that of the caller. */ hint_sa = NULL; if (clnt_uaddr != NULL) { hint_uaddr = clnt_uaddr; if ((hint_nbp = uaddr2taddr(nconf, clnt_uaddr)) == NULL) goto freeit; hint_sa = hint_nbp->buf; } if (hint_sa == NULL || hint_sa->sa_family != caller_sa->sa_family) { hint_uaddr = caller_uaddr; hint_sa = caller->buf; } #ifdef ND_DEBUG if (debugging) fprintf(stderr, "addrmerge: hint %s\n", hint_uaddr); #endif /* Local caller, just return the server address. */ if (strncmp(caller_uaddr, "0.0.0.0.", 8) == 0 || strncmp(caller_uaddr, "::.", 3) == 0 || caller_uaddr[0] == '/') { ret = strdup(serv_uaddr); goto freeit; } if (getifaddrs(&ifp) < 0) goto freeit; /* * Loop through all interface addresses. We are listening to an address * if any of the following are true: * a) It's a loopback address * b) It was specified with the -h command line option * c) There were no -h command line options. * * Among addresses on which we are listening, choose in order of * preference an address that is: * * a) Equal to the hint * b) A link local address with the same scope ID as the client's * address, if the client's address is also link local * c) An address on the same subnet as the client's address * d) A non-localhost, non-p2p address * e) Any usable address */ bestif = NULL; bestif_goodness = 0; for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) { ifsa = ifap->ifa_addr; ifmasksa = ifap->ifa_netmask; /* Skip addresses where we don't listen */ if (ifsa == NULL || ifsa->sa_family != hint_sa->sa_family || !(ifap->ifa_flags & IFF_UP)) continue; if (!(ifap->ifa_flags & IFF_LOOPBACK) && !listen_addr(ifsa)) continue; if ((hint_sa->sa_family == AF_INET) && ((((struct sockaddr_in*)hint_sa)->sin_addr.s_addr == ((struct sockaddr_in*)ifsa)->sin_addr.s_addr))) { const int goodness = 4; bestif_goodness = goodness; bestif = ifap; goto found; } #ifdef INET6 if ((hint_sa->sa_family == AF_INET6) && (0 == memcmp(&((struct sockaddr_in6*)hint_sa)->sin6_addr, &((struct sockaddr_in6*)ifsa)->sin6_addr, sizeof(struct in6_addr))) && (((struct sockaddr_in6*)hint_sa)->sin6_scope_id == (((struct sockaddr_in6*)ifsa)->sin6_scope_id))) { const int goodness = 4; bestif_goodness = goodness; bestif = ifap; goto found; } if (hint_sa->sa_family == AF_INET6) { /* * For v6 link local addresses, if the caller is on * a link-local address then use the scope id to see * which one. */ if (IN6_IS_ADDR_LINKLOCAL(&SA2SIN6ADDR(ifsa)) && IN6_IS_ADDR_LINKLOCAL(&SA2SIN6ADDR(caller_sa)) && IN6_IS_ADDR_LINKLOCAL(&SA2SIN6ADDR(hint_sa))) { if (SA2SIN6(ifsa)->sin6_scope_id == SA2SIN6(caller_sa)->sin6_scope_id) { const int goodness = 3; if (bestif_goodness < goodness) { bestif = ifap; bestif_goodness = goodness; } } } } #endif /* INET6 */ if (0 == bitmaskcmp(hint_sa, ifsa, ifmasksa)) { const int goodness = 2; if (bestif_goodness < goodness) { bestif = ifap; bestif_goodness = goodness; } } if (!(ifap->ifa_flags & (IFF_LOOPBACK | IFF_POINTOPOINT))) { const int goodness = 1; if (bestif_goodness < goodness) { bestif = ifap; bestif_goodness = goodness; } } if (bestif == NULL) bestif = ifap; } if (bestif == NULL) goto freeit; found: /* * Construct the new address using the address from * `bestif', and the port number from `serv_uaddr'. */ serv_nbp = uaddr2taddr(nconf, serv_uaddr); if (serv_nbp == NULL) goto freeit; serv_sa = serv_nbp->buf; memcpy(&ss, bestif->ifa_addr, bestif->ifa_addr->sa_len); switch (ss.ss_family) { case AF_INET: SA2SIN(&ss)->sin_port = SA2SIN(serv_sa)->sin_port; break; #ifdef INET6 case AF_INET6: SA2SIN6(&ss)->sin6_port = SA2SIN6(serv_sa)->sin6_port; break; #endif } tbuf.len = ss.ss_len; tbuf.maxlen = sizeof(ss); tbuf.buf = &ss; ret = taddr2uaddr(nconf, &tbuf); freeit: if (caller_uaddr != NULL) free(caller_uaddr); if (hint_nbp != NULL) { free(hint_nbp->buf); free(hint_nbp); } if (serv_nbp != NULL) { free(serv_nbp->buf); free(serv_nbp); } if (ifp != NULL) freeifaddrs(ifp); #ifdef ND_DEBUG if (debugging) fprintf(stderr, "addrmerge: returning %s\n", ret); #endif return ret; }
RPCB *regp = (RPCB *)arg; static rpcb_entry_list_ptr rlist; register rpcblist_ptr rbl; rpcb_entry_list_ptr rp, tail; rpcprog_t prog; rpcvers_t vers; rpcb_entry *a; struct netconfig *nconf; struct netconfig *reg_nconf; char *saddr, *maddr = NULL; free_rpcb_entry_list(&rlist); tail = NULL; prog = regp->r_prog; vers = regp->r_vers; reg_nconf = rpcbind_get_conf(transp->xp_netid); if (reg_nconf == NULL) return (NULL); if (*(regp->r_addr) != '\0') { saddr = regp->r_addr; } else { saddr = NULL; } #ifdef RPCBIND_DEBUG if (debugging) { fprintf(stderr, "r_addr: %s r_netid: %s nc_protofmly: %s\n", regp->r_addr, regp->r_netid, reg_nconf->nc_protofmly); } #endif for (rbl = list_rbl; rbl != NULL; rbl = rbl->rpcb_next) { if ((rbl->rpcb_map.r_prog == prog) &&
bool_t rpcbproc_callit_com(rpcb_rmtcallargs *a, void *result, ar_svc_req_t *rqstp, arpcvers_t versnum) { rpcblist_ptr rbl; ar_netid_t *nconf; ar_svc_xprt_t *transp; char *uaddr, *m_uaddr = NULL, *local_uaddr = NULL; arpcproc_t reply_type; arpc_createerr_t cerr; struct sockaddr_storage ss; struct sockaddr *localsa; const char *netidstr; struct timespec tlimit; arpc_addr_t caller; arpc_addr_t tbuf; arpc_addr_t *na; char *errstr; struct finfo *info; int err; caller.maxlen = sizeof(ss); caller.len = sizeof(ss); caller.buf = (char *)&ss; reply_type = rqstp->rq_proc; transp = rqstp->rq_xprt; if (!ar_svc_control(rqstp->rq_xprt, AR_SVCGET_NETID, &netidstr)) { ar_svcflgerr_systemerr(rqstp); return FALSE; } if (!check_callit(rqstp, a, versnum)) { ar_svcflgerr_weakauth(rqstp); return FALSE; } if (!ar_svc_control(rqstp->rq_xprt, AR_SVCGET_REMOTE_ADDR, &caller)) { ar_svcflgerr_systemerr(rqstp); return FALSE; } #ifdef RPCBIND_DEBUG if (debugging) { uaddr = rqst2uaddr(rqstp); fprintf(stderr, "%s %s req for (%lu, %lu, %lu, %s) from %s : ", versnum == PMAPVERS ? "pmap_rmtcall" : versnum == RPCBVERS ? "rpcb_rmtcall" : versnum == RPCBVERS4 ? "rpcb_indirect" : "unknown", reply_type == RPCBPROC_INDIRECT ? "indirect" : "callit", (unsigned long)a->rmt_prog, (unsigned long)a->rmt_vers, (unsigned long)a->rmt_proc, netidstr, uaddr ? uaddr : "unknown"); if (uaddr) { free(uaddr); } uaddr = NULL; } #endif rbl = find_service(a->prog, a->vers, netidstr); rpcbs_rmtcall(versnum - 2, reply_type, a->prog, a->vers, a->proc, netidstr, rbl); if (rbl == (rpcblist_ptr)NULL) { #ifdef RPCBIND_DEBUG if (debugging) { fprintf(stderr, "not found\n"); } #endif if (reply_type == RPCBPROC_INDIRECT) { ar_svcflgerr_noprog(rqstp); } return FALSE; } if (rbl->rpcb_map.r_vers != a->vers) { if (reply_type == RPCBPROC_INDIRECT) { arpcvers_t vers_low, vers_high; find_versions(a->prog, netidstr, &vers_low, &vers_high); ar_svcflgerr_progvers(rqstp, vers_low, vers_high); } return FALSE; } #ifdef RPCBIND_DEBUG if (debugging) { fprintf(stderr, "found at uaddr %s\n", rbl->rpcb_map.r_addr); } #endif /* * Check whether this entry is valid and a server is present * Mergeaddr() returns NULL if no such entry is present, and * returns "" if the entry was present but the server is not * present (i.e., it crashed). */ if (reply_type == RPCBPROC_INDIRECT) { uaddr = mergeaddr(transp, netidstr, rbl->rpcb_map.r_addr, NULL); if (uaddr == NULL || uaddr[0] == '\0') { ar_svcflgerr_noprog(rqstp); if (uaddr != NULL) { free(uaddr); } uaddr = NULL; return FALSE; } free(uaddr); uaddr = NULL; } nconf = rpcbind_get_conf(netidstr); if (nconf == (ar_netid_t *)NULL) { if (reply_type == RPCBPROC_INDIRECT) { ar_svcflgerr_systemerr(rqstp); return FALSE; } if (debugging) { fprintf(stderr, "rpcbproc_callit_com: " "rpcbind_get_conf failed\n"); } return FALSE; } /* compute local_uaddr with merge (picks port from rbl, local address * that most matches the request's src addr. * * convert that back to uaddr */ localsa = local_sa(nconf->an_family, &tbuf.len); if (localsa == NULL) { if (debugging) { fprintf(stderr, "rpcbproc_callit_com: no local address\n"); } goto error; } tbuf.maxlen = tbuf.len; tbuf.buf = (char *)localsa; local_uaddr = addrmerge(&tbuf, rbl->rpcb_map.r_addr, NULL, netidstr); if (!local_uaddr) { err = ENOMEM; goto error; } m_uaddr = addrmerge(&caller, rbl->rpcb_map.r_addr, NULL, netidstr); if (!m_uaddr) { err = ENOMEM; goto error; } err = ar_uaddr2taddr(rpcbind_ioctx, netidstr, local_uaddr, &na); if (err != 0) { if (reply_type == RPCBPROC_INDIRECT) { ar_svcflgerr_systemerr(rqstp); } goto error; } /* allocate forward table entry */ err = forward_register(&info); if (err != 0) { goto error; } info->clnt = NULL; info->cco = NULL; info->sco = NULL; info->uaddr = m_uaddr; m_uaddr = NULL; /* setup/connect clnt. ar_clnt_tli_create() */ err = ar_clnt_tli_create(rpcbind_ioctx, netidstr, na, a->prog, a->vers, NULL, &cerr, &info->clnt); if (err != 0) { if (debugging) { errstr = ar_astrcreateerror(&cerr); if (errstr) { fprintf(stderr, "create clnt failed: %s\n", errstr); free(errstr); } } goto error; } tlimit.tv_sec = MAXTIME_OFF; tlimit.tv_nsec = 0; /* issue async rpc through clnt ar_clnt_call_async_copy */ err = ar_clnt_call_async_copy(info->clnt, a->proc, (axdrproc_t)&axdr_callit_rmtcallargs, a, (axdrproc_t)&axdr_callit_rmtresult, sizeof(rpcb_remote_result_t), &rpcb_callit_done, info, &tlimit, &info->cco); if (err != 0) { goto error; } /* call is running. Mark original server call as async so we * can finish it when we get the result. */ err = ar_svc_async(rqstp, &info->sco); if (err != 0) { goto error; } /* done */ info = NULL; err = 0; error: if (err != 0 && reply_type == RPCBPROC_INDIRECT) { ar_svcflgerr_systemerr(rqstp); } if (local_uaddr) { free(local_uaddr); local_uaddr = NULL; } if (m_uaddr) { free(m_uaddr); m_uaddr = NULL; } if (na) { if (na->buf) { free(na->buf); } na->buf = NULL; free(na); na = NULL; } if (info) { if (info->cco) { ar_clnt_call_cancel(info->cco); } info->cco = NULL; if (info->sco) { ar_svc_async_done(info->sco, err); } info->sco = NULL; if (info->clnt) { ar_clnt_destroy(info->clnt); } info->clnt = NULL; if (info->uaddr) { free(info->uaddr); } info->uaddr = NULL; info->flag &= ~FINFO_ACTIVE; info = NULL; } return FALSE; }