diff options
Diffstat (limited to 'usr.sbin/rpcbind')
| -rw-r--r-- | usr.sbin/rpcbind/Makefile | 32 | ||||
| -rw-r--r-- | usr.sbin/rpcbind/Makefile.depend | 19 | ||||
| -rw-r--r-- | usr.sbin/rpcbind/Makefile.depend.options | 5 | ||||
| -rw-r--r-- | usr.sbin/rpcbind/check_bound.c | 235 | ||||
| -rw-r--r-- | usr.sbin/rpcbind/pmap_svc.c | 360 | ||||
| -rw-r--r-- | usr.sbin/rpcbind/rpcb_stat.c | 202 | ||||
| -rw-r--r-- | usr.sbin/rpcbind/rpcb_svc.c | 233 | ||||
| -rw-r--r-- | usr.sbin/rpcbind/rpcb_svc_4.c | 452 | ||||
| -rw-r--r-- | usr.sbin/rpcbind/rpcb_svc_com.c | 1489 | ||||
| -rw-r--r-- | usr.sbin/rpcbind/rpcbind.8 | 189 | ||||
| -rw-r--r-- | usr.sbin/rpcbind/rpcbind.c | 941 | ||||
| -rw-r--r-- | usr.sbin/rpcbind/rpcbind.h | 161 | ||||
| -rw-r--r-- | usr.sbin/rpcbind/security.c | 287 | ||||
| -rw-r--r-- | usr.sbin/rpcbind/tests/Makefile | 23 | ||||
| -rw-r--r-- | usr.sbin/rpcbind/tests/Makefile.depend | 19 | ||||
| -rw-r--r-- | usr.sbin/rpcbind/tests/addrmerge_test.c | 902 | ||||
| -rw-r--r-- | usr.sbin/rpcbind/util.c | 418 | ||||
| -rw-r--r-- | usr.sbin/rpcbind/warmstart.c | 178 |
18 files changed, 6145 insertions, 0 deletions
diff --git a/usr.sbin/rpcbind/Makefile b/usr.sbin/rpcbind/Makefile new file mode 100644 index 000000000000..eadadc71310e --- /dev/null +++ b/usr.sbin/rpcbind/Makefile @@ -0,0 +1,32 @@ +# $NetBSD: Makefile,v 1.3 2000/06/20 13:56:43 fvdl Exp $ + +.include <src.opts.mk> + +PROG= rpcbind +MAN= rpcbind.8 +SRCS= check_bound.c rpcb_stat.c rpcb_svc_4.c rpcbind.c pmap_svc.c \ + rpcb_svc.c rpcb_svc_com.c security.c warmstart.c util.c + +CFLAGS+= -DPORTMAP + +LIBADD= util + +.if ${MK_INET6_SUPPORT} != "no" +CFLAGS+= -DINET6 +.endif + +.if ${MK_RPCBIND_WARMSTART_SUPPORT} != "no" +CFLAGS+= -DWARMSTART +.endif + +.if ${MK_TCP_WRAPPERS} != "no" +CFLAGS+= -DLIBWRAP +LIBADD+= wrap +.endif + +HAS_TESTS= +SUBDIR.${MK_TESTS}+= tests + +WARNS?= 1 + +.include <bsd.prog.mk> diff --git a/usr.sbin/rpcbind/Makefile.depend b/usr.sbin/rpcbind/Makefile.depend new file mode 100644 index 000000000000..7e5c47e39608 --- /dev/null +++ b/usr.sbin/rpcbind/Makefile.depend @@ -0,0 +1,19 @@ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + include/arpa \ + include/rpc \ + include/rpcsvc \ + include/xlocale \ + lib/${CSU_DIR} \ + lib/libc \ + lib/libcompiler_rt \ + lib/libutil \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/usr.sbin/rpcbind/Makefile.depend.options b/usr.sbin/rpcbind/Makefile.depend.options new file mode 100644 index 000000000000..fbc21670804e --- /dev/null +++ b/usr.sbin/rpcbind/Makefile.depend.options @@ -0,0 +1,5 @@ +# This file is not autogenerated - take care! + +DIRDEPS_OPTIONS= TCP_WRAPPERS + +.include <dirdeps-options.mk> diff --git a/usr.sbin/rpcbind/check_bound.c b/usr.sbin/rpcbind/check_bound.c new file mode 100644 index 000000000000..820f76d37346 --- /dev/null +++ b/usr.sbin/rpcbind/check_bound.c @@ -0,0 +1,235 @@ +/* $NetBSD: check_bound.c,v 1.2 2000/06/22 08:09:26 fvdl Exp $ */ + +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 2009, Sun Microsystems, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of Sun Microsystems, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc. + */ + +/* + * check_bound.c + * Checks to see whether the program is still bound to the + * claimed address and returns the universal merged address + * + */ + +#include <sys/types.h> +#include <sys/socket.h> + +#include <rpc/rpc.h> +#include <rpc/svc_dg.h> + +#include <netconfig.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +#include "rpcbind.h" + +struct fdlist { + int fd; + struct netconfig *nconf; + struct fdlist *next; + int check_binding; +}; + +static struct fdlist *fdhead; /* Link list of the check fd's */ +static struct fdlist *fdtail; +static char nullstring[] = ""; + +static bool_t check_bound(struct fdlist *, const char *uaddr); + +/* + * Returns 1 if the given address is bound for the given addr & transport + * For all error cases, we assume that the address is bound + * Returns 0 for success. + */ +static bool_t +check_bound(struct fdlist *fdl, const char *uaddr) +{ + int fd; + struct netbuf *na; + int ans; + + if (fdl->check_binding == FALSE) + return (TRUE); + + na = uaddr2taddr(fdl->nconf, uaddr); + if (!na) + return (TRUE); /* punt, should never happen */ + + fd = __rpc_nconf2fd(fdl->nconf); + if (fd < 0) { + free(na->buf); + free(na); + return (TRUE); + } + + ans = bind(fd, (struct sockaddr *)na->buf, na->len); + + close(fd); + free(na->buf); + free(na); + + return (ans == 0 ? FALSE : TRUE); +} + +int +add_bndlist(const struct netconfig *nconf, struct netbuf *baddr __unused) +{ + struct fdlist *fdl; + struct netconfig *newnconf; + + newnconf = getnetconfigent(nconf->nc_netid); + if (newnconf == NULL) + return (-1); + fdl = malloc(sizeof(*fdl)); + if (fdl == NULL) { + freenetconfigent(newnconf); + syslog(LOG_ERR, "no memory!"); + return (-1); + } + fdl->nconf = newnconf; + fdl->next = NULL; + if (fdhead == NULL) { + fdhead = fdl; + fdtail = fdl; + } else { + fdtail->next = fdl; + fdtail = fdl; + } + /* XXX no bound checking for now */ + fdl->check_binding = FALSE; + + return 0; +} + +bool_t +is_bound(const char *netid, const char *uaddr) +{ + struct fdlist *fdl; + + for (fdl = fdhead; fdl; fdl = fdl->next) + if (strcmp(fdl->nconf->nc_netid, netid) == 0) + break; + if (fdl == NULL) + return (TRUE); + return (check_bound(fdl, uaddr)); +} + +/* + * 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; + struct netbuf *callee; + 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 (nullstring); + /* + * Try to determine the local address on which the client contacted us, + * so we can send a reply from the same address. If it's unknown, then + * try to determine which address the client used, and pick a nearby + * local address. + * + * 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. + */ + callee = svc_getrpccallee(xprt); + if (callee != NULL && callee->buf != NULL) { + c_uaddr = taddr2uaddr(fdl->nconf, callee); + allocated_uaddr = c_uaddr; + } else if (saddr != NULL) { + c_uaddr = saddr; + } else { + c_uaddr = taddr2uaddr(fdl->nconf, svc_getrpccaller(xprt)); + allocated_uaddr = c_uaddr; + } + if (c_uaddr == NULL) { + syslog(LOG_ERR, "taddr2uaddr failed for %s", + fdl->nconf->nc_netid); + return (NULL); + } + +#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 + free(allocated_uaddr); + return (m_uaddr); +} + +/* + * Returns a netconf structure from its internal list. This + * structure should not be freed. + */ +struct netconfig * +rpcbind_get_conf(const char *netid) +{ + struct fdlist *fdl; + + for (fdl = fdhead; fdl; fdl = fdl->next) + if (strcmp(fdl->nconf->nc_netid, netid) == 0) + break; + if (fdl == NULL) + return (NULL); + return (fdl->nconf); +} diff --git a/usr.sbin/rpcbind/pmap_svc.c b/usr.sbin/rpcbind/pmap_svc.c new file mode 100644 index 000000000000..4dcbb0f6ddd2 --- /dev/null +++ b/usr.sbin/rpcbind/pmap_svc.c @@ -0,0 +1,360 @@ +/* $NetBSD: pmap_svc.c,v 1.2 2000/10/20 11:49:40 fvdl Exp $ */ + +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 2009, Sun Microsystems, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of Sun Microsystems, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Copyright (c) 1984 - 1991 by Sun Microsystems, Inc. + */ + +/* + * pmap_svc.c + * The server procedure for the version 2 portmapper. + * All the portmapper related interface from the portmap side. + */ + +#ifdef PORTMAP +#include <sys/types.h> +#include <sys/socket.h> +#include <stdio.h> +#include <rpc/rpc.h> +#include <rpc/pmap_prot.h> +#include <rpc/rpcb_prot.h> +#ifdef RPCBIND_DEBUG +#include <stdio.h> +#include <stdlib.h> +#endif +#include "rpcbind.h" + +static struct pmaplist *find_service_pmap(rpcprog_t, rpcvers_t, rpcprot_t); +static bool_t pmapproc_change(struct svc_req *, SVCXPRT *, u_long); +static bool_t pmapproc_getport(struct svc_req *, SVCXPRT *); +static bool_t pmapproc_dump(struct svc_req *, SVCXPRT *); + +/* + * Called for all the version 2 inquiries. + */ +void +pmap_service(struct svc_req *rqstp, SVCXPRT *xprt) +{ + rpcbs_procinfo(RPCBVERS_2_STAT, rqstp->rq_proc); + switch (rqstp->rq_proc) { + case PMAPPROC_NULL: + /* + * Null proc call + */ +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "PMAPPROC_NULL\n"); +#endif + check_access(xprt, rqstp->rq_proc, NULL, PMAPVERS); + if ((!svc_sendreply(xprt, (xdrproc_t) xdr_void, NULL)) && + debugging) { + if (doabort) { + rpcbind_abort(); + } + } + break; + + case PMAPPROC_SET: + /* + * Set a program, version to port mapping + */ + pmapproc_change(rqstp, xprt, rqstp->rq_proc); + break; + + case PMAPPROC_UNSET: + /* + * Remove a program, version to port mapping. + */ + pmapproc_change(rqstp, xprt, rqstp->rq_proc); + break; + + case PMAPPROC_GETPORT: + /* + * Lookup the mapping for a program, version and return its + * port number. + */ + pmapproc_getport(rqstp, xprt); + break; + + case PMAPPROC_DUMP: + /* + * Return the current set of mapped program, version + */ +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "PMAPPROC_DUMP\n"); +#endif + pmapproc_dump(rqstp, xprt); + break; + + case PMAPPROC_CALLIT: + /* + * Calls a procedure on the local machine. If the requested + * procedure is not registered this procedure does not return + * error information!! + * This procedure is only supported on rpc/udp and calls via + * rpc/udp. It passes null authentication parameters. + */ + rpcbproc_callit_com(rqstp, xprt, PMAPPROC_CALLIT, PMAPVERS); + break; + + default: + svcerr_noproc(xprt); + break; + } +} + +/* + * returns the item with the given program, version number. If that version + * number is not found, it returns the item with that program number, so that + * the port number is now returned to the caller. The caller when makes a + * call to this program, version number, the call will fail and it will + * return with PROGVERS_MISMATCH. The user can then determine the highest + * and the lowest version number for this program using clnt_geterr() and + * use those program version numbers. + */ +static struct pmaplist * +find_service_pmap(rpcprog_t prog, rpcvers_t vers, rpcprot_t prot) +{ + register struct pmaplist *hit = NULL; + register struct pmaplist *pml; + + for (pml = list_pml; pml != NULL; pml = pml->pml_next) { + if ((pml->pml_map.pm_prog != prog) || + (pml->pml_map.pm_prot != prot)) + continue; + hit = pml; + if (pml->pml_map.pm_vers == vers) + break; + } + return (hit); +} + +static bool_t +pmapproc_change(struct svc_req *rqstp __unused, SVCXPRT *xprt, unsigned long op) +{ + struct pmap reg; + RPCB rpcbreg; + long ans; + struct sockaddr_in *who; + uid_t uid; + char uidbuf[32]; + + if (!svc_getargs(xprt, (xdrproc_t) xdr_pmap, (char *)®)) { + svcerr_decode(xprt); + return (FALSE); + } + +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "%s request for (%lu, %lu) : ", + op == PMAPPROC_SET ? "PMAP_SET" : "PMAP_UNSET", + reg.pm_prog, reg.pm_vers); +#endif + + if (!check_access(xprt, op, ®, PMAPVERS)) { + svcerr_weakauth(xprt); + return FALSE; + } + + who = svc_getcaller(xprt); + + /* + * Can't use getpwnam here. We might end up calling ourselves + * and looping. + */ + if (__rpc_get_local_uid(xprt, &uid) < 0) + rpcbreg.r_owner = __UNCONST(rpcbind_unknown); + else if (uid == 0) + rpcbreg.r_owner = __UNCONST(rpcbind_superuser); + else { + /* r_owner will be strdup-ed later */ + snprintf(uidbuf, sizeof(uidbuf), "%d", uid); + rpcbreg.r_owner = uidbuf; + } + + rpcbreg.r_prog = reg.pm_prog; + rpcbreg.r_vers = reg.pm_vers; + + if (op == PMAPPROC_SET) { + char buf[32]; + + snprintf(buf, sizeof(buf), "0.0.0.0.%d.%d", + (int)((reg.pm_port >> 8) & 0xff), + (int)(reg.pm_port & 0xff)); + rpcbreg.r_addr = buf; + if (reg.pm_prot == IPPROTO_UDP) { + rpcbreg.r_netid = __UNCONST(udptrans); + } else if (reg.pm_prot == IPPROTO_TCP) { + rpcbreg.r_netid = __UNCONST(tcptrans); + } else { + ans = FALSE; + goto done_change; + } + ans = map_set(&rpcbreg, rpcbreg.r_owner); + } else if (op == PMAPPROC_UNSET) { + bool_t ans1, ans2; + + rpcbreg.r_addr = NULL; + rpcbreg.r_netid = __UNCONST(tcptrans); + ans1 = map_unset(&rpcbreg, rpcbreg.r_owner); + rpcbreg.r_netid = __UNCONST(udptrans); + ans2 = map_unset(&rpcbreg, rpcbreg.r_owner); + ans = ans1 || ans2; + } else { + ans = FALSE; + } +done_change: + if ((!svc_sendreply(xprt, (xdrproc_t) xdr_long, (caddr_t) &ans)) && + debugging) { + fprintf(stderr, "portmap: svc_sendreply\n"); + if (doabort) { + rpcbind_abort(); + } + } +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "%s\n", ans == TRUE ? "succeeded" : "failed"); +#endif + if (op == PMAPPROC_SET) + rpcbs_set(RPCBVERS_2_STAT, ans); + else + rpcbs_unset(RPCBVERS_2_STAT, ans); + return (TRUE); +} + +/* ARGSUSED */ +static bool_t +pmapproc_getport(struct svc_req *rqstp __unused, SVCXPRT *xprt) +{ + struct pmap reg; + long lport; + int port = 0; + struct pmaplist *fnd; +#ifdef RPCBIND_DEBUG + char *uaddr; +#endif + + if (!svc_getargs(xprt, (xdrproc_t) xdr_pmap, (char *)®)) { + svcerr_decode(xprt); + return (FALSE); + } + + if (!check_access(xprt, PMAPPROC_GETPORT, ®, PMAPVERS)) { + svcerr_weakauth(xprt); + return FALSE; + } + +#ifdef RPCBIND_DEBUG + if (debugging) { + uaddr = taddr2uaddr(rpcbind_get_conf(xprt->xp_netid), + svc_getrpccaller(xprt)); + fprintf(stderr, "PMAP_GETPORT req for (%lu, %lu, %s) from %s :", + reg.pm_prog, reg.pm_vers, + reg.pm_prot == IPPROTO_UDP ? "udp" : "tcp", uaddr); + free(uaddr); + } +#endif + fnd = find_service_pmap(reg.pm_prog, reg.pm_vers, reg.pm_prot); + if (fnd) { + char serveuaddr[32]; + int h1, h2, h3, h4, p1, p2; + const char *netid, *ua; + + if (reg.pm_prot == IPPROTO_UDP) { + ua = udp_uaddr; + netid = udptrans; + } else { + ua = tcp_uaddr; /* To get the len */ + netid = tcptrans; + } + if (ua == NULL) { + goto sendreply; + } + if (sscanf(ua, "%d.%d.%d.%d.%d.%d", &h1, &h2, &h3, + &h4, &p1, &p2) == 6) { + p1 = (fnd->pml_map.pm_port >> 8) & 0xff; + p2 = (fnd->pml_map.pm_port) & 0xff; + snprintf(serveuaddr, sizeof(serveuaddr), + "%d.%d.%d.%d.%d.%d", h1, h2, h3, h4, p1, p2); + if (is_bound(netid, serveuaddr)) { + port = fnd->pml_map.pm_port; + } else { /* this service is dead; delete it */ + delete_prog(reg.pm_prog); + } + } + } +sendreply: + lport = port; + if ((!svc_sendreply(xprt, (xdrproc_t) xdr_long, (caddr_t)&lport)) && + debugging) { + (void) fprintf(stderr, "portmap: svc_sendreply\n"); + if (doabort) { + rpcbind_abort(); + } + } +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "port = %d\n", port); +#endif + rpcbs_getaddr(RPCBVERS_2_STAT, reg.pm_prog, reg.pm_vers, + reg.pm_prot == IPPROTO_UDP ? udptrans : tcptrans, + port ? udptrans : ""); + + return (TRUE); +} + +/* ARGSUSED */ +static bool_t +pmapproc_dump(struct svc_req *rqstp __unused, SVCXPRT *xprt) +{ + if (!svc_getargs(xprt, (xdrproc_t)xdr_void, NULL)) { + svcerr_decode(xprt); + return (FALSE); + } + + if (!check_access(xprt, PMAPPROC_DUMP, NULL, PMAPVERS)) { + svcerr_weakauth(xprt); + return FALSE; + } + + if ((!svc_sendreply(xprt, (xdrproc_t) xdr_pmaplist_ptr, + (caddr_t)&list_pml)) && debugging) { + if (debugging) + (void) fprintf(stderr, "portmap: svc_sendreply\n"); + if (doabort) { + rpcbind_abort(); + } + } + return (TRUE); +} + +#endif /* PORTMAP */ diff --git a/usr.sbin/rpcbind/rpcb_stat.c b/usr.sbin/rpcbind/rpcb_stat.c new file mode 100644 index 000000000000..9500bb7b7cbe --- /dev/null +++ b/usr.sbin/rpcbind/rpcb_stat.c @@ -0,0 +1,202 @@ +/* + * $NetBSD: rpcb_stat.c,v 1.2 2000/07/04 20:27:40 matt Exp $ + */ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 2009, Sun Microsystems, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of Sun Microsystems, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * rpcb_stat.c + * Allows for gathering of statistics + * + * Copyright (c) 1990 by Sun Microsystems, Inc. + */ + +#include <sys/stat.h> + +#include <rpc/rpc.h> +#include <rpc/rpcb_prot.h> +#ifdef PORTMAP +#include <rpc/pmap_prot.h> +#endif + +#include <netconfig.h> +#include <stdlib.h> +#include <string.h> + +#include "rpcbind.h" + +static rpcb_stat_byvers inf; + +void +rpcbs_init(void) +{ + +} + +void +rpcbs_procinfo(rpcvers_t rtype, rpcproc_t proc) +{ + switch (rtype + 2) { +#ifdef PORTMAP + case PMAPVERS: /* version 2 */ + if (proc > rpcb_highproc_2) + return; + break; +#endif + case RPCBVERS: /* version 3 */ + if (proc > rpcb_highproc_3) + return; + break; + case RPCBVERS4: /* version 4 */ + if (proc > rpcb_highproc_4) + return; + break; + default: return; + } + inf[rtype].info[proc]++; +} + +void +rpcbs_set(rpcvers_t rtype, bool_t success) +{ + if ((rtype >= RPCBVERS_STAT) || (success == FALSE)) + return; + inf[rtype].setinfo++; +} + +void +rpcbs_unset(rpcvers_t rtype, bool_t success) +{ + if ((rtype >= RPCBVERS_STAT) || (success == FALSE)) + return; + inf[rtype].unsetinfo++; +} + +void +rpcbs_getaddr(rpcvers_t rtype, rpcprog_t prog, rpcvers_t vers, + const char *netid, const 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 = malloc(sizeof(*al)); + 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; +} + +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 = malloc(sizeof(*rl)); + 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; +} + +void * +rpcbproc_getstat(void *arg __unused, struct svc_req *req __unused, + SVCXPRT *xprt __unused, rpcvers_t versnum __unused) +{ + return (void *)&inf; +} diff --git a/usr.sbin/rpcbind/rpcb_svc.c b/usr.sbin/rpcbind/rpcb_svc.c new file mode 100644 index 000000000000..5a23abe3dbc6 --- /dev/null +++ b/usr.sbin/rpcbind/rpcb_svc.c @@ -0,0 +1,233 @@ +/* $NetBSD: rpcb_svc.c,v 1.1 2000/06/02 23:15:41 fvdl Exp $ */ + +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 2009, Sun Microsystems, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of Sun Microsystems, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc. + */ + +/* + * rpcb_svc.c + * The server procedure for the version 3 rpcbind (TLI). + * + * It maintains a separate list of all the registered services with the + * version 3 of rpcbind. + */ +#include <sys/types.h> +#include <rpc/rpc.h> +#include <rpc/rpcb_prot.h> +#include <netconfig.h> +#include <stdio.h> +#ifdef RPCBIND_DEBUG +#include <stdio.h> +#include <stdlib.h> +#endif +#include <string.h> + +#include "rpcbind.h" + +static void *rpcbproc_getaddr_3_local(void *, struct svc_req *, SVCXPRT *, + rpcvers_t); +static void *rpcbproc_dump_3_local(void *, struct svc_req *, SVCXPRT *, + rpcvers_t); + +/* + * Called by svc_getreqset. There is a separate server handle for + * every transport that it waits on. + */ +void +rpcb_service_3(struct svc_req *rqstp, SVCXPRT *transp) +{ + union { + RPCB rpcbproc_set_3_arg; + RPCB rpcbproc_unset_3_arg; + RPCB rpcbproc_getaddr_3_local_arg; + struct rpcb_rmtcallargs rpcbproc_callit_3_arg; + char *rpcbproc_uaddr2taddr_3_arg; + struct netbuf rpcbproc_taddr2uaddr_3_arg; + } argument; + char *result; + xdrproc_t xdr_argument, xdr_result; + void *(*local)(void *, struct svc_req *, SVCXPRT *, rpcvers_t); + + rpcbs_procinfo(RPCBVERS_3_STAT, rqstp->rq_proc); + + switch (rqstp->rq_proc) { + case NULLPROC: + /* + * Null proc call + */ +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "RPCBPROC_NULL\n"); +#endif + /* This call just logs, no actual checks */ + check_access(transp, rqstp->rq_proc, NULL, RPCBVERS); + (void) svc_sendreply(transp, (xdrproc_t)xdr_void, NULL); + return; + + case RPCBPROC_SET: + xdr_argument = (xdrproc_t )xdr_rpcb; + xdr_result = (xdrproc_t )xdr_bool; + local = rpcbproc_set_com; + break; + + case RPCBPROC_UNSET: + xdr_argument = (xdrproc_t)xdr_rpcb; + xdr_result = (xdrproc_t)xdr_bool; + local = rpcbproc_unset_com; + break; + + case RPCBPROC_GETADDR: + xdr_argument = (xdrproc_t)xdr_rpcb; + xdr_result = (xdrproc_t)xdr_wrapstring; + local = rpcbproc_getaddr_3_local; + break; + + case RPCBPROC_DUMP: +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "RPCBPROC_DUMP\n"); +#endif + xdr_argument = (xdrproc_t)xdr_void; + xdr_result = (xdrproc_t)xdr_rpcblist_ptr; + local = rpcbproc_dump_3_local; + break; + + case RPCBPROC_CALLIT: + rpcbproc_callit_com(rqstp, transp, rqstp->rq_proc, RPCBVERS); + return; + + case RPCBPROC_GETTIME: +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "RPCBPROC_GETTIME\n"); +#endif + xdr_argument = (xdrproc_t)xdr_void; + xdr_result = (xdrproc_t)xdr_u_long; + local = rpcbproc_gettime_com; + break; + + case RPCBPROC_UADDR2TADDR: +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "RPCBPROC_UADDR2TADDR\n"); +#endif + xdr_argument = (xdrproc_t)xdr_wrapstring; + xdr_result = (xdrproc_t)xdr_netbuf; + local = rpcbproc_uaddr2taddr_com; + break; + + case RPCBPROC_TADDR2UADDR: +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "RPCBPROC_TADDR2UADDR\n"); +#endif + xdr_argument = (xdrproc_t)xdr_netbuf; + xdr_result = (xdrproc_t)xdr_wrapstring; + local = rpcbproc_taddr2uaddr_com; + break; + + default: + svcerr_noproc(transp); + return; + } + (void) memset((char *)&argument, 0, sizeof (argument)); + if (!svc_getargs(transp, (xdrproc_t) xdr_argument, + (char *) &argument)) { + svcerr_decode(transp); + if (debugging) + (void) fprintf(stderr, "rpcbind: could not decode\n"); + return; + } + if (!check_access(transp, rqstp->rq_proc, &argument, RPCBVERS)) { + svcerr_weakauth(transp); + goto done; + } + result = (*local)(&argument, rqstp, transp, RPCBVERS); + if (result != NULL && !svc_sendreply(transp, (xdrproc_t)xdr_result, + result)) { + svcerr_systemerr(transp); + if (debugging) { + (void) fprintf(stderr, "rpcbind: svc_sendreply\n"); + if (doabort) { + rpcbind_abort(); + } + } + } +done: + if (!svc_freeargs(transp, (xdrproc_t)xdr_argument, (char *) + &argument)) { + if (debugging) { + (void) fprintf(stderr, "unable to free arguments\n"); + if (doabort) { + rpcbind_abort(); + } + } + } +} + +/* + * Lookup the mapping for a program, version and return its + * address. Assuming that the caller wants the address of the + * server running on the transport on which the request came. + * + * We also try to resolve the universal address in terms of + * address of the caller. + */ +/* ARGSUSED */ +static void * +rpcbproc_getaddr_3_local(void *arg, struct svc_req *rqstp __unused, + SVCXPRT *transp __unused, rpcvers_t versnum __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, RPCBVERS, + RPCB_ALLVERS)); +} + +/* ARGSUSED */ +static void * +rpcbproc_dump_3_local(void *arg __unused, struct svc_req *rqstp __unused, + SVCXPRT *transp __unused, rpcvers_t versnum __unused) +{ + return ((void *)&list_rbl); +} diff --git a/usr.sbin/rpcbind/rpcb_svc_4.c b/usr.sbin/rpcbind/rpcb_svc_4.c new file mode 100644 index 000000000000..817312709cf0 --- /dev/null +++ b/usr.sbin/rpcbind/rpcb_svc_4.c @@ -0,0 +1,452 @@ +/* + * $NetBSD: rpcb_svc_4.c,v 1.1 2000/06/02 23:15:41 fvdl Exp $ + */ + +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 2009, Sun Microsystems, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of Sun Microsystems, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc. + */ + +/* + * rpcb_svc_4.c + * The server procedure for the version 4 rpcbind. + * + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <rpc/rpc.h> +#include <stdio.h> +#include <netconfig.h> +#include <string.h> +#include <stdlib.h> +#include "rpcbind.h" + +static void *rpcbproc_getaddr_4_local(void *, struct svc_req *, SVCXPRT *, + rpcvers_t); +static void *rpcbproc_getversaddr_4_local(void *, struct svc_req *, SVCXPRT *, + rpcvers_t); +static void *rpcbproc_getaddrlist_4_local(void *, struct svc_req *, SVCXPRT *, + rpcvers_t); +static void free_rpcb_entry_list(rpcb_entry_list_ptr *); +static void *rpcbproc_dump_4_local(void *, struct svc_req *, SVCXPRT *, + rpcvers_t); + +/* + * Called by svc_getreqset. There is a separate server handle for + * every transport that it waits on. + */ +void +rpcb_service_4(struct svc_req *rqstp, SVCXPRT *transp) +{ + union { + rpcb rpcbproc_set_4_arg; + rpcb rpcbproc_unset_4_arg; + rpcb rpcbproc_getaddr_4_local_arg; + char *rpcbproc_uaddr2taddr_4_arg; + struct netbuf rpcbproc_taddr2uaddr_4_arg; + } argument; + char *result; + xdrproc_t xdr_argument, xdr_result; + void *(*local)(void *, struct svc_req *, SVCXPRT *, rpcvers_t); + + rpcbs_procinfo(RPCBVERS_4_STAT, rqstp->rq_proc); + + switch (rqstp->rq_proc) { + case NULLPROC: + /* + * Null proc call + */ +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "RPCBPROC_NULL\n"); +#endif + check_access(transp, rqstp->rq_proc, NULL, RPCBVERS4); + (void) svc_sendreply(transp, (xdrproc_t) xdr_void, NULL); + return; + + case RPCBPROC_SET: + /* + * Check to see whether the message came from + * loopback transports (for security reasons) + */ + xdr_argument = (xdrproc_t)xdr_rpcb; + xdr_result = (xdrproc_t)xdr_bool; + local = rpcbproc_set_com; + break; + + case RPCBPROC_UNSET: + /* + * Check to see whether the message came from + * loopback transports (for security reasons) + */ + xdr_argument = (xdrproc_t)xdr_rpcb; + xdr_result = (xdrproc_t)xdr_bool; + local = rpcbproc_unset_com; + break; + + case RPCBPROC_GETADDR: + xdr_argument = (xdrproc_t)xdr_rpcb; + xdr_result = (xdrproc_t)xdr_wrapstring; + local = rpcbproc_getaddr_4_local; + break; + + case RPCBPROC_GETVERSADDR: +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "RPCBPROC_GETVERSADDR\n"); +#endif + xdr_argument = (xdrproc_t)xdr_rpcb; + xdr_result = (xdrproc_t)xdr_wrapstring; + local = rpcbproc_getversaddr_4_local; + break; + + case RPCBPROC_DUMP: +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "RPCBPROC_DUMP\n"); +#endif + xdr_argument = (xdrproc_t)xdr_void; + xdr_result = (xdrproc_t)xdr_rpcblist_ptr; + local = rpcbproc_dump_4_local; + break; + + case RPCBPROC_INDIRECT: +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "RPCBPROC_INDIRECT\n"); +#endif + rpcbproc_callit_com(rqstp, transp, rqstp->rq_proc, RPCBVERS4); + return; + +/* case RPCBPROC_CALLIT: */ + case RPCBPROC_BCAST: +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "RPCBPROC_BCAST\n"); +#endif + rpcbproc_callit_com(rqstp, transp, rqstp->rq_proc, RPCBVERS4); + return; + + case RPCBPROC_GETTIME: +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "RPCBPROC_GETTIME\n"); +#endif + xdr_argument = (xdrproc_t)xdr_void; + xdr_result = (xdrproc_t)xdr_u_long; + local = rpcbproc_gettime_com; + break; + + case RPCBPROC_UADDR2TADDR: +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "RPCBPROC_UADDR2TADDR\n"); +#endif + xdr_argument = (xdrproc_t)xdr_wrapstring; + xdr_result = (xdrproc_t)xdr_netbuf; + local = rpcbproc_uaddr2taddr_com; + break; + + case RPCBPROC_TADDR2UADDR: +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "RPCBPROC_TADDR2UADDR\n"); +#endif + xdr_argument = (xdrproc_t)xdr_netbuf; + xdr_result = (xdrproc_t)xdr_wrapstring; + local = rpcbproc_taddr2uaddr_com; + break; + + case RPCBPROC_GETADDRLIST: +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "RPCBPROC_GETADDRLIST\n"); +#endif + xdr_argument = (xdrproc_t)xdr_rpcb; + xdr_result = (xdrproc_t)xdr_rpcb_entry_list_ptr; + local = rpcbproc_getaddrlist_4_local; + break; + + case RPCBPROC_GETSTAT: +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "RPCBPROC_GETSTAT\n"); +#endif + xdr_argument = (xdrproc_t)xdr_void; + xdr_result = (xdrproc_t)xdr_rpcb_stat_byvers; + local = rpcbproc_getstat; + break; + + default: + svcerr_noproc(transp); + return; + } + memset((char *)&argument, 0, sizeof (argument)); + if (!svc_getargs(transp, (xdrproc_t) xdr_argument, + (char *)&argument)) { + svcerr_decode(transp); + if (debugging) + (void) fprintf(stderr, "rpcbind: could not decode\n"); + return; + } + if (!check_access(transp, rqstp->rq_proc, &argument, RPCBVERS4)) { + svcerr_weakauth(transp); + goto done; + } + result = (*local)(&argument, rqstp, transp, RPCBVERS4); + if (result != NULL && !svc_sendreply(transp, (xdrproc_t) xdr_result, + result)) { + svcerr_systemerr(transp); + if (debugging) { + (void) fprintf(stderr, "rpcbind: svc_sendreply\n"); + if (doabort) { + rpcbind_abort(); + } + } + } +done: + if (!svc_freeargs(transp, (xdrproc_t) xdr_argument, + (char *)&argument)) { + if (debugging) { + (void) fprintf(stderr, "unable to free arguments\n"); + if (doabort) { + rpcbind_abort(); + } + } + } +} + +/* + * Lookup the mapping for a program, version and return its + * address. Assuming that the caller wants the address of the + * server running on the transport on which the request came. + * Even if a service with a different version number is available, + * it will return that address. The client should check with an + * clnt_call to verify whether the service is the one that is desired. + * We also try to resolve the universal address in terms of + * address of the caller. + */ +/* 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)); +} + +/* + * Lookup the mapping for a program, version and return its + * address. Assuming that the caller wants the address of the + * server running on the transport on which the request came. + * + * We also try to resolve the universal address in terms of + * address of the caller. + */ +/* ARGSUSED */ +static void * +rpcbproc_getversaddr_4_local(void *arg, struct svc_req *rqstp, SVCXPRT *transp, + rpcvers_t versnum __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_GETVERSADDR rqst 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_ONEVERS)); +} + +/* + * Lookup the mapping for a program, version and return the + * addresses for all transports in the current transport family. + * We return a merged address. + */ +/* 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(*rp)); + 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 if (tail != NULL) { + 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 (&rlist); + +fail: + free_rpcb_entry_list(&rlist); + return (NULL); +} + +/* + * Free only the allocated structure, rest is all a pointer to some + * other data somewhere else. + */ +static void +free_rpcb_entry_list(rpcb_entry_list_ptr *rlistp) +{ + register rpcb_entry_list_ptr rbl, tmp; + + for (rbl = *rlistp; rbl != NULL; ) { + tmp = rbl; + rbl = rbl->rpcb_entry_next; + free((char *)tmp->rpcb_entry_map.r_maddr); + free((char *)tmp); + } + *rlistp = NULL; +} + +/* ARGSUSED */ +static void * +rpcbproc_dump_4_local(void *arg __unused, struct svc_req *req __unused, + SVCXPRT *xprt __unused, rpcvers_t versnum __unused) +{ + return ((void *)&list_rbl); +} diff --git a/usr.sbin/rpcbind/rpcb_svc_com.c b/usr.sbin/rpcbind/rpcb_svc_com.c new file mode 100644 index 000000000000..a82cf44bfa3c --- /dev/null +++ b/usr.sbin/rpcbind/rpcb_svc_com.c @@ -0,0 +1,1489 @@ +/* $NetBSD: rpcb_svc_com.c,v 1.9 2002/11/08 00:16:39 fvdl Exp $ */ + +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 2009, Sun Microsystems, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of Sun Microsystems, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc. + */ + +/* + * rpcb_svc_com.c + * The commom server procedure for the rpcbind. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <sys/poll.h> +#include <sys/socket.h> +#include <rpc/rpc.h> +#include <rpc/rpcb_prot.h> +#include <rpc/svc_dg.h> +#include <assert.h> +#include <netconfig.h> +#include <errno.h> +#include <syslog.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#ifdef PORTMAP +#include <netinet/in.h> +#include <rpc/rpc_com.h> +#include <rpc/pmap_prot.h> +#endif /* PORTMAP */ + +#include "rpcbind.h" + +#define RPC_BUF_MAX 65536 /* can be raised if required */ + +static char *nullstring = ""; +static int rpcb_rmtcalls; + +struct rmtcallfd_list { + int fd; + SVCXPRT *xprt; + char *netid; + struct rmtcallfd_list *next; +}; + +#define NFORWARD 64 +#define MAXTIME_OFF 300 /* 5 minutes */ + +struct finfo { + int flag; +#define FINFO_ACTIVE 0x1 + u_int32_t caller_xid; + struct netbuf *caller_addr; + u_int32_t forward_xid; + int forward_fd; + char *uaddr; + rpcproc_t reply_type; + rpcvers_t versnum; + time_t time; +}; +static struct finfo FINFO[NFORWARD]; + + +static bool_t xdr_encap_parms(XDR *, struct encap_parms *); +static bool_t xdr_rmtcall_args(XDR *, struct r_rmtcall_args *); +static bool_t xdr_rmtcall_result(XDR *, struct r_rmtcall_args *); +static bool_t xdr_opaque_parms(XDR *, struct r_rmtcall_args *); +static int find_rmtcallfd_by_netid(char *); +static SVCXPRT *find_rmtcallxprt_by_fd(int); +static int forward_register(u_int32_t, struct netbuf *, int, char *, + rpcproc_t, rpcvers_t, u_int32_t *); +static struct finfo *forward_find(u_int32_t); +static int free_slot_by_xid(u_int32_t); +static int free_slot_by_index(int); +static int netbufcmp(struct netbuf *, struct netbuf *); +static struct netbuf *netbufdup(struct netbuf *); +static void netbuffree(struct netbuf *); +static int check_rmtcalls(struct pollfd *, int); +static void xprt_set_caller(SVCXPRT *, struct finfo *); +static void send_svcsyserr(SVCXPRT *, struct finfo *); +static void handle_reply(int, SVCXPRT *); +static void find_versions(rpcprog_t, const char *, rpcvers_t *, rpcvers_t *); +static rpcblist_ptr find_service(rpcprog_t, rpcvers_t, char *); +static char *getowner(SVCXPRT *, char *, size_t); +static int add_pmaplist(RPCB *); +static int del_pmaplist(RPCB *); + +/* + * Set a mapping of program, version, netid + */ +/* ARGSUSED */ +void * +rpcbproc_set_com(void *arg, struct svc_req *rqstp __unused, SVCXPRT *transp, + rpcvers_t rpcbversnum) +{ + RPCB *regp = arg; + static bool_t ans; + char owner[64]; + +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "%s: RPCB_SET request for (%lu, %lu, %s, %s): ", + __func__, (unsigned long)regp->r_prog, + (unsigned long)regp->r_vers, regp->r_netid, regp->r_addr); +#endif + ans = map_set(regp, getowner(transp, owner, sizeof owner)); +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "%s\n", ans == TRUE ? "succeeded" : "failed"); +#endif + /* XXX: should have used some defined constant here */ + rpcbs_set(rpcbversnum - 2, ans); + return (void *)&ans; +} + +bool_t +map_set(RPCB *regp, const char *owner) +{ + RPCB reg, *a; + rpcblist_ptr rbl, fnd; + + reg = *regp; + /* + * check to see if already used + * find_service returns a hit even if + * the versions don't match, so check for it + */ + fnd = find_service(reg.r_prog, reg.r_vers, reg.r_netid); + if (fnd && (fnd->rpcb_map.r_vers == reg.r_vers)) { + if (!strcmp(fnd->rpcb_map.r_addr, reg.r_addr)) + /* + * if these match then it is already + * registered so just say "OK". + */ + return (TRUE); + else + return (FALSE); + } + /* + * add to the end of the list + */ + rbl = malloc(sizeof(*rbl)); + if (rbl == NULL) + return (FALSE); + a = &(rbl->rpcb_map); + a->r_prog = reg.r_prog; + a->r_vers = reg.r_vers; + a->r_netid = strdup(reg.r_netid); + a->r_addr = strdup(reg.r_addr); + a->r_owner = strdup(owner); + if (!a->r_addr || !a->r_netid || !a->r_owner) { + free(a->r_netid); + free(a->r_addr); + free(a->r_owner); + free(rbl); + return (FALSE); + } + rbl->rpcb_next = NULL; + if (list_rbl == NULL) { + list_rbl = rbl; + } else { + for (fnd = list_rbl; fnd->rpcb_next; + fnd = fnd->rpcb_next) + ; + fnd->rpcb_next = rbl; + } +#ifdef PORTMAP + (void)add_pmaplist(regp); +#endif + return (TRUE); +} + +/* + * Unset a mapping of program, version, netid + */ +/* ARGSUSED */ +void * +rpcbproc_unset_com(void *arg, struct svc_req *rqstp __unused, SVCXPRT *transp, + rpcvers_t rpcbversnum) +{ + RPCB *regp = arg; + static bool_t ans; + char owner[64]; + +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "%s: RPCB_UNSET request for (%lu, %lu, %s): ", + __func__, (unsigned long)regp->r_prog, + (unsigned long)regp->r_vers, regp->r_netid); +#endif + ans = map_unset(regp, getowner(transp, owner, sizeof owner)); +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "%s\n", ans == TRUE ? "succeeded" : "failed"); +#endif + /* XXX: should have used some defined constant here */ + rpcbs_unset(rpcbversnum - 2, ans); + return (void *)&ans; +} + +bool_t +map_unset(RPCB *regp, const char *owner) +{ + int ans = 0; + rpcblist_ptr rbl, prev, tmp; + + if (owner == NULL) + return (0); + + for (prev = NULL, rbl = list_rbl; rbl; /* cstyle */) { + if ((rbl->rpcb_map.r_prog != regp->r_prog) || + (rbl->rpcb_map.r_vers != regp->r_vers) || + (regp->r_netid[0] && strcasecmp(regp->r_netid, + rbl->rpcb_map.r_netid))) { + /* both rbl & prev move forwards */ + prev = rbl; + rbl = rbl->rpcb_next; + continue; + } + /* + * Check whether appropriate uid. Unset only + * if superuser or the owner itself. + */ + if (strcmp(owner, rpcbind_superuser) && + strcmp(rbl->rpcb_map.r_owner, owner)) + return (0); + /* found it; rbl moves forward, prev stays */ + ans = 1; + tmp = rbl; + rbl = rbl->rpcb_next; + if (prev == NULL) + list_rbl = rbl; + else + prev->rpcb_next = rbl; + free(tmp->rpcb_map.r_addr); + free(tmp->rpcb_map.r_netid); + free(tmp->rpcb_map.r_owner); + free(tmp); + } +#ifdef PORTMAP + if (ans) + (void)del_pmaplist(regp); +#endif + /* + * We return 1 either when the entry was not there or it + * was able to unset it. It can come to this point only if + * at least one of the conditions is true. + */ + return (1); +} + +void +delete_prog(rpcprog_t prog) +{ + RPCB reg; + rpcblist_ptr rbl; + + for (rbl = list_rbl; rbl != NULL; rbl = rbl->rpcb_next) { + if ((rbl->rpcb_map.r_prog != prog)) + continue; + if (is_bound(rbl->rpcb_map.r_netid, rbl->rpcb_map.r_addr)) + continue; + reg.r_prog = rbl->rpcb_map.r_prog; + reg.r_vers = rbl->rpcb_map.r_vers; + reg.r_netid = strdup(rbl->rpcb_map.r_netid); + if (reg.r_netid == NULL) + syslog(LOG_ERR, "%s: %m", __func__); + else { + (void)map_unset(®, rpcbind_superuser); + free(reg.r_netid); + } + } +} + +void * +rpcbproc_getaddr_com(RPCB *regp, struct svc_req *rqstp __unused, + SVCXPRT *transp, rpcvers_t rpcbversnum, rpcvers_t verstype) +{ + static char *uaddr; + char *saddr = NULL; + rpcblist_ptr fnd; + + if (uaddr != NULL && uaddr != nullstring) { + free(uaddr); + uaddr = NULL; + } + fnd = find_service(regp->r_prog, regp->r_vers, transp->xp_netid); + if (fnd && ((verstype == RPCB_ALLVERS) || + (regp->r_vers == fnd->rpcb_map.r_vers))) { + if (*(regp->r_addr) != '\0') { /* may contain a hint about */ + saddr = regp->r_addr; /* the interface that we */ + } /* should use */ + if (!(uaddr = mergeaddr(transp, transp->xp_netid, + fnd->rpcb_map.r_addr, saddr))) { + /* Try whatever we have */ + uaddr = strdup(fnd->rpcb_map.r_addr); + } else if (!uaddr[0]) { + /* + * The server died. Unset all versions of this prog. + */ + delete_prog(regp->r_prog); + uaddr = nullstring; + } + } else { + uaddr = nullstring; + } +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "%s: %s\n", __func__, uaddr); +#endif + /* XXX: should have used some defined constant here */ + rpcbs_getaddr(rpcbversnum - 2, regp->r_prog, regp->r_vers, + transp->xp_netid, uaddr); + return (&uaddr); +} + +/* ARGSUSED */ +void * +rpcbproc_gettime_com(void *arg __unused, struct svc_req *rqstp __unused, + SVCXPRT *transp __unused, rpcvers_t rpcbversnum __unused) +{ + static time_t curtime; + + (void)time(&curtime); + return (&curtime); +} + +/* + * Convert uaddr to taddr. Should be used only by + * local servers/clients. (kernel level stuff only) + */ +/* ARGSUSED */ +void * +rpcbproc_uaddr2taddr_com(void *arg, struct svc_req *rqstp __unused, + SVCXPRT *transp, rpcvers_t rpcbversnum __unused) +{ + char **uaddrp = arg; + struct netconfig *nconf; + static struct netbuf nbuf; + static struct netbuf *taddr; + + netbuffree(taddr); + taddr = NULL; + if (((nconf = rpcbind_get_conf(transp->xp_netid)) == NULL) || + ((taddr = uaddr2taddr(nconf, *uaddrp)) == NULL)) { + memset(&nbuf, 0, sizeof(nbuf)); + return (&nbuf); + } + return (taddr); +} + +/* + * Convert taddr to uaddr. Should be used only by + * local servers/clients. (kernel level stuff only) + */ +/* ARGSUSED */ +void * +rpcbproc_taddr2uaddr_com(void *arg, struct svc_req *rqstp __unused, + SVCXPRT *transp, rpcvers_t rpcbversnum __unused) +{ + struct netbuf *taddr = arg; + static char *uaddr; + struct netconfig *nconf; + +#ifdef CHEW_FDS + int fd; + + if ((fd = open("/dev/null", O_RDONLY)) == -1) { + uaddr = strerror(errno); + return (&uaddr); + } +#endif /* CHEW_FDS */ + if (uaddr != NULL && uaddr != nullstring) { + free(uaddr); + uaddr = NULL; + } + if (((nconf = rpcbind_get_conf(transp->xp_netid)) == NULL) || + ((uaddr = taddr2uaddr(nconf, taddr)) == NULL)) { + uaddr = nullstring; + } + return (void *)&uaddr; +} + + +static bool_t +xdr_encap_parms(XDR *xdrs, struct encap_parms *epp) +{ + return (xdr_bytes(xdrs, &(epp->args), (u_int *) &(epp->arglen), + RPC_MAXDATASIZE)); +} + +/* + * XDR remote call arguments. It ignores the address part. + * written for XDR_DECODE direction only + */ +static bool_t +xdr_rmtcall_args(XDR *xdrs, struct r_rmtcall_args *cap) +{ + /* does not get the address or the arguments */ + if (xdr_rpcprog(xdrs, &(cap->rmt_prog)) && + xdr_rpcvers(xdrs, &(cap->rmt_vers)) && + xdr_rpcproc(xdrs, &(cap->rmt_proc))) { + return (xdr_encap_parms(xdrs, &(cap->rmt_args))); + } + return (FALSE); +} + +/* + * XDR remote call results along with the address. Ignore + * program number, version number and proc number. + * Written for XDR_ENCODE direction only. + */ +static bool_t +xdr_rmtcall_result(XDR *xdrs, struct r_rmtcall_args *cap) +{ + bool_t result; + +#ifdef PORTMAP + if (cap->rmt_localvers == PMAPVERS) { + int h1, h2, h3, h4, p1, p2; + u_long port; + + /* interpret the universal address for TCP/IP */ + if (sscanf(cap->rmt_uaddr, "%d.%d.%d.%d.%d.%d", + &h1, &h2, &h3, &h4, &p1, &p2) != 6) + return (FALSE); + port = ((p1 & 0xff) << 8) + (p2 & 0xff); + result = xdr_u_long(xdrs, &port); + } else +#endif + if ((cap->rmt_localvers == RPCBVERS) || + (cap->rmt_localvers == RPCBVERS4)) { + result = xdr_wrapstring(xdrs, &(cap->rmt_uaddr)); + } else { + return (FALSE); + } + if (result == TRUE) + return (xdr_encap_parms(xdrs, &(cap->rmt_args))); + return (FALSE); +} + +/* + * only worries about the struct encap_parms part of struct r_rmtcall_args. + * The arglen must already be set!! + */ +static bool_t +xdr_opaque_parms(XDR *xdrs, struct r_rmtcall_args *cap) +{ + return (xdr_opaque(xdrs, cap->rmt_args.args, cap->rmt_args.arglen)); +} + +static struct rmtcallfd_list *rmthead; +static struct rmtcallfd_list *rmttail; + +int +create_rmtcall_fd(const struct netconfig *nconf) +{ + int fd; + struct rmtcallfd_list *rmt; + SVCXPRT *xprt; + + if ((fd = __rpc_nconf2fd(nconf)) == -1) { + if (debugging) + fprintf(stderr, "%s: couldn't open \"%s\": %m\n", + __func__, nconf->nc_device); + return (-1); + } + xprt = svc_tli_create(fd, 0, NULL, 0, 0); + if (xprt == NULL) { + if (debugging) + fprintf(stderr, "%s: svc_tli_create failed\n", + __func__); + return (-1); + } + rmt = malloc(sizeof(*rmt)); + if (rmt == NULL) { + syslog(LOG_ERR, "%s: %m", __func__); + return (-1); + } + rmt->xprt = xprt; + rmt->netid = strdup(nconf->nc_netid); + xprt->xp_netid = rmt->netid; + rmt->fd = fd; + rmt->next = NULL; + if (rmthead == NULL) { + rmthead = rmt; + rmttail = rmt; + } else { + rmttail->next = rmt; + rmttail = rmt; + } + /* XXX not threadsafe */ + if (fd > svc_maxfd) + svc_maxfd = fd; + FD_SET(fd, &svc_fdset); + return (fd); +} + +static int +find_rmtcallfd_by_netid(char *netid) +{ + struct rmtcallfd_list *rmt; + + for (rmt = rmthead; rmt != NULL; rmt = rmt->next) { + if (strcmp(netid, rmt->netid) == 0) { + return (rmt->fd); + } + } + return (-1); +} + +static SVCXPRT * +find_rmtcallxprt_by_fd(int fd) +{ + struct rmtcallfd_list *rmt; + + for (rmt = rmthead; rmt != NULL; rmt = rmt->next) { + if (fd == rmt->fd) { + return (rmt->xprt); + } + } + return (NULL); +} + + +/* + * Call a remote procedure service. This procedure is very quiet when things + * go wrong. The proc is written to support broadcast rpc. In the broadcast + * case, a machine should shut-up instead of complain, lest the requestor be + * overrun with complaints at the expense of not hearing a valid reply. + * When receiving a request and verifying that the service exists, we + * + * receive the request + * + * open a new TLI endpoint on the same transport on which we received + * the original request + * + * remember the original request's XID (which requires knowing the format + * of the svc_dg_data structure) + * + * forward the request, with a new XID, to the requested service, + * remembering the XID used to send this request (for later use in + * reassociating the answer with the original request), the requestor's + * address, the file descriptor on which the forwarded request is + * made and the service's address. + * + * mark the file descriptor on which we anticipate receiving a reply from + * the service and one to select for in our private svc_run procedure + * + * At some time in the future, a reply will be received from the service to + * which we forwarded the request. At that time, we detect that the socket + * used was for forwarding (by looking through the finfo structures to see + * whether the fd corresponds to one of those) and call handle_reply() to + * + * receive the reply + * + * bundle the reply, along with the service's universal address + * + * create a SVCXPRT structure and use a version of svc_sendreply + * that allows us to specify the reply XID and destination, send the reply + * to the original requestor. + */ + +void +rpcbproc_callit_com(struct svc_req *rqstp, SVCXPRT *transp, + rpcproc_t reply_type, rpcvers_t versnum) +{ + rpcblist_ptr rbl; + struct netconfig *nconf; + struct netbuf *caller; + struct r_rmtcall_args a; + char *buf_alloc = NULL, *outbufp; + char *outbuf_alloc = NULL; + char buf[RPC_BUF_MAX], outbuf[RPC_BUF_MAX]; + struct netbuf *na = NULL; + struct rpc_msg call_msg; + int outlen; + u_int sendsz; + XDR outxdr; + AUTH *auth; + int fd = -1; + char *uaddr, *m_uaddr = NULL, *local_uaddr = NULL; + u_int32_t *xidp; + struct __rpc_sockinfo si; + struct sockaddr *localsa; + struct netbuf tbuf; + + if (!__rpc_fd2sockinfo(transp->xp_fd, &si)) { + if (reply_type == RPCBPROC_INDIRECT) + svcerr_systemerr(transp); + return; + } + if (si.si_socktype != SOCK_DGRAM) + return; /* Only datagram type accepted */ + sendsz = __rpc_get_t_size(si.si_af, si.si_proto, UDPMSGSIZE); + if (sendsz == 0) { /* data transfer not supported */ + if (reply_type == RPCBPROC_INDIRECT) + svcerr_systemerr(transp); + return; + } + /* + * Should be multiple of 4 for XDR. + */ + sendsz = roundup(sendsz, 4); + if (sendsz > RPC_BUF_MAX) { +#ifdef notyet + buf_alloc = alloca(sendsz); /* not in IDR2? */ +#else + buf_alloc = malloc(sendsz); +#endif /* notyet */ + if (buf_alloc == NULL) { + if (debugging) + fprintf(stderr, "%s: %m\n", __func__); + if (reply_type == RPCBPROC_INDIRECT) + svcerr_systemerr(transp); + return; + } + a.rmt_args.args = buf_alloc; + } else { + a.rmt_args.args = buf; + } + + call_msg.rm_xid = 0; /* For error checking purposes */ + if (!svc_getargs(transp, (xdrproc_t)xdr_rmtcall_args, (char *) &a)) { + if (reply_type == RPCBPROC_INDIRECT) + svcerr_decode(transp); + if (debugging) + fprintf(stderr, "%s: svc_getargs failed\n", __func__); + goto error; + } + + if (!check_callit(transp, &a, versnum)) { + svcerr_weakauth(transp); + goto error; + } + + caller = svc_getrpccaller(transp); +#ifdef RPCBIND_DEBUG + if (debugging) { + uaddr = taddr2uaddr(rpcbind_get_conf(transp->xp_netid), caller); + fprintf(stderr, + "%s: %s %s req for (%lu, %lu, %lu, %s) from %s: ", + __func__, versnum == PMAPVERS ? "pmap_rmtcall" : + versnum == RPCBVERS ? "rpcb_rmtcall" : + versnum == RPCBVERS4 ? "rpcb_indirect" : rpcbind_unknown, + reply_type == RPCBPROC_INDIRECT ? "indirect" : "callit", + (unsigned long)a.rmt_prog, (unsigned long)a.rmt_vers, + (unsigned long)a.rmt_proc, transp->xp_netid, + uaddr ? uaddr : rpcbind_unknown); + free(uaddr); + } +#endif + + rbl = find_service(a.rmt_prog, a.rmt_vers, transp->xp_netid); + + rpcbs_rmtcall(versnum - 2, reply_type, a.rmt_prog, a.rmt_vers, + a.rmt_proc, transp->xp_netid, rbl); + + if (rbl == NULL) { +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "not found\n"); +#endif + if (reply_type == RPCBPROC_INDIRECT) + svcerr_noprog(transp); + goto error; + } + if (rbl->rpcb_map.r_vers != a.rmt_vers) { + if (reply_type == RPCBPROC_INDIRECT) { + rpcvers_t vers_low, vers_high; + + find_versions(a.rmt_prog, transp->xp_netid, + &vers_low, &vers_high); + svcerr_progvers(transp, vers_low, vers_high); + } + goto error; + } + +#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, transp->xp_netid, + rbl->rpcb_map.r_addr, NULL); + if (uaddr == NULL || uaddr[0] == '\0') { + svcerr_noprog(transp); + free(uaddr); + goto error; + } + free(uaddr); + } + nconf = rpcbind_get_conf(transp->xp_netid); + if (nconf == NULL) { + if (reply_type == RPCBPROC_INDIRECT) + svcerr_systemerr(transp); + if (debugging) + fprintf(stderr, "%s: rpcbind_get_conf failed\n", + __func__); + goto error; + } + localsa = local_sa(((struct sockaddr *)caller->buf)->sa_family); + if (localsa == NULL) { + if (debugging) + fprintf(stderr, "%s: no local address\n", __func__); + goto error; + } + tbuf.len = tbuf.maxlen = localsa->sa_len; + tbuf.buf = localsa; + local_uaddr = + addrmerge(&tbuf, rbl->rpcb_map.r_addr, NULL, nconf->nc_netid); + m_uaddr = addrmerge(caller, rbl->rpcb_map.r_addr, NULL, + nconf->nc_netid); +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "%s: merged uaddr %s\n", __func__, m_uaddr); +#endif + if ((fd = find_rmtcallfd_by_netid(nconf->nc_netid)) == -1) { + if (reply_type == RPCBPROC_INDIRECT) + svcerr_systemerr(transp); + goto error; + } + xidp = __rpcb_get_dg_xidp(transp); + switch (forward_register(*xidp, caller, fd, m_uaddr, reply_type, + versnum, &call_msg.rm_xid)) { + case 1: + /* Success; forward_register() will free m_uaddr for us. */ + m_uaddr = NULL; + break; + case 0: + /* + * A duplicate request for the slow server. Let's not + * beat on it any more. + */ + if (debugging) + fprintf(stderr, "%s: duplicate request\n", __func__); + goto error; + case -1: + /* forward_register failed. Perhaps no memory. */ + if (debugging) + fprintf(stderr, "%s: forward_register failed\n", + __func__); + goto error; + } + +#ifdef DEBUG_RMTCALL + if (debugging) + fprintf(stderr, "%s: original XID %x, new XID %x\n", __func__, + *xidp, call_msg.rm_xid); +#endif + call_msg.rm_direction = CALL; + call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; + call_msg.rm_call.cb_prog = a.rmt_prog; + call_msg.rm_call.cb_vers = a.rmt_vers; + if (sendsz > RPC_BUF_MAX) { +#ifdef notyet + outbuf_alloc = alloca(sendsz); /* not in IDR2? */ +#else + outbuf_alloc = malloc(sendsz); +#endif /* notyet */ + if (outbuf_alloc == NULL) { + if (debugging) + fprintf(stderr, "%s: %m\n", __func__); + if (reply_type == RPCBPROC_INDIRECT) + svcerr_systemerr(transp); + goto error; + } + xdrmem_create(&outxdr, outbuf_alloc, sendsz, XDR_ENCODE); + } else { + xdrmem_create(&outxdr, outbuf, sendsz, XDR_ENCODE); + } + if (!xdr_callhdr(&outxdr, &call_msg)) { + if (reply_type == RPCBPROC_INDIRECT) + svcerr_systemerr(transp); + if (debugging) + fprintf(stderr, "%s: xdr_callhdr failed\n", __func__); + goto error; + } + if (!xdr_u_int32_t(&outxdr, &(a.rmt_proc))) { + if (reply_type == RPCBPROC_INDIRECT) + svcerr_systemerr(transp); + if (debugging) + fprintf(stderr, "%s: xdr_u_long failed\n", __func__); + goto error; + } + + if (rqstp->rq_cred.oa_flavor == AUTH_NULL) { + auth = authnone_create(); + } else if (rqstp->rq_cred.oa_flavor == AUTH_SYS) { + struct authunix_parms *au; + + au = (struct authunix_parms *)rqstp->rq_clntcred; + auth = authunix_create(au->aup_machname, + au->aup_uid, au->aup_gid, + au->aup_len, au->aup_gids); + if (auth == NULL) /* fall back */ + auth = authnone_create(); + } else { + /* we do not support any other authentication scheme */ + if (debugging) + fprintf(stderr, "%s: oa_flavor != AUTH_NONE and " + "oa_flavor != AUTH_SYS\n", __func__); + if (reply_type == RPCBPROC_INDIRECT) + svcerr_weakauth(transp); /* XXX too strong.. */ + goto error; + } + if (auth == NULL) { + if (reply_type == RPCBPROC_INDIRECT) + svcerr_systemerr(transp); + if (debugging) + fprintf(stderr, + "%s: authwhatever_create returned NULL\n", + __func__); + goto error; + } + if (!AUTH_MARSHALL(auth, &outxdr)) { + if (reply_type == RPCBPROC_INDIRECT) + svcerr_systemerr(transp); + AUTH_DESTROY(auth); + if (debugging) + fprintf(stderr, "%s: AUTH_MARSHALL failed\n", + __func__); + goto error; + } + AUTH_DESTROY(auth); + if (!xdr_opaque_parms(&outxdr, &a)) { + if (reply_type == RPCBPROC_INDIRECT) + svcerr_systemerr(transp); + if (debugging) + fprintf(stderr, "%s: xdr_opaque_parms failed\n", + __func__); + goto error; + } + outlen = (int) XDR_GETPOS(&outxdr); + if (outbuf_alloc) + outbufp = outbuf_alloc; + else + outbufp = outbuf; + + na = uaddr2taddr(nconf, local_uaddr); + if (!na) { + if (reply_type == RPCBPROC_INDIRECT) + svcerr_systemerr(transp); + goto error; + } + + if (sendto(fd, outbufp, outlen, 0, (struct sockaddr *)na->buf, na->len) + != outlen) { + if (debugging) + fprintf(stderr, "%s: sendto failed: %m\n", __func__); + if (reply_type == RPCBPROC_INDIRECT) + svcerr_systemerr(transp); + goto error; + } + goto out; + +error: + if (call_msg.rm_xid != 0) + (void)free_slot_by_xid(call_msg.rm_xid); +out: + free(local_uaddr); + free(buf_alloc); + free(outbuf_alloc); + netbuffree(na); + free(m_uaddr); +} + +/* + * Makes an entry into the FIFO for the given request. + * Returns 1 on success, 0 if this is a duplicate request, or -1 on error. + * *callxidp is set to the xid of the call. + */ +static int +forward_register(u_int32_t caller_xid, struct netbuf *caller_addr, + int forward_fd, char *uaddr, rpcproc_t reply_type, + rpcvers_t versnum, u_int32_t *callxidp) +{ + static u_int32_t lastxid; + time_t min_time, time_now; + int i, j = 0, entry = -1; + + min_time = FINFO[0].time; + time_now = time((time_t *)0); + /* initialization */ + if (lastxid == 0) + lastxid = time_now * NFORWARD; + + /* + * Check if it is a duplicate entry. Then, + * try to find an empty slot. If not available, then + * use the slot with the earliest time. + */ + for (i = 0; i < NFORWARD; i++) { + if (FINFO[i].flag & FINFO_ACTIVE) { + if ((FINFO[i].caller_xid == caller_xid) && + (FINFO[i].reply_type == reply_type) && + (FINFO[i].versnum == versnum) && + (!netbufcmp(FINFO[i].caller_addr, + caller_addr))) { + FINFO[i].time = time((time_t *)0); + return (0); /* Duplicate entry */ + } else { + /* Should we wait any longer */ + if ((time_now - FINFO[i].time) > MAXTIME_OFF) + (void)free_slot_by_index(i); + } + } + if (entry == -1) { + if ((FINFO[i].flag & FINFO_ACTIVE) == 0) { + entry = i; + } else if (FINFO[i].time < min_time) { + j = i; + min_time = FINFO[i].time; + } + } + } + if (entry != -1) { + /* use this empty slot */ + j = entry; + } else { + (void)free_slot_by_index(j); + } + if ((FINFO[j].caller_addr = netbufdup(caller_addr)) == NULL) { + return (-1); + } + rpcb_rmtcalls++; /* no of pending calls */ + FINFO[j].flag = FINFO_ACTIVE; + FINFO[j].reply_type = reply_type; + FINFO[j].versnum = versnum; + FINFO[j].time = time_now; + FINFO[j].caller_xid = caller_xid; + FINFO[j].forward_fd = forward_fd; + /* + * Though uaddr is not allocated here, it will still be freed + * from free_slot_*(). + */ + FINFO[j].uaddr = uaddr; + lastxid = lastxid + NFORWARD; + /* Don't allow a zero xid below. */ + if ((u_int32_t)(lastxid + NFORWARD) <= NFORWARD) + lastxid = NFORWARD; + FINFO[j].forward_xid = lastxid + j; /* encode slot */ + *callxidp = FINFO[j].forward_xid; /* forward on this xid */ + return (1); +} + +static struct finfo * +forward_find(u_int32_t reply_xid) +{ + int i; + + i = reply_xid % (u_int32_t)NFORWARD; + if ((FINFO[i].flag & FINFO_ACTIVE) && + (FINFO[i].forward_xid == reply_xid)) { + return (&FINFO[i]); + } + return (NULL); +} + +static int +free_slot_by_xid(u_int32_t xid) +{ + int entry; + + entry = xid % (u_int32_t)NFORWARD; + return (free_slot_by_index(entry)); +} + +static int +free_slot_by_index(int index) +{ + struct finfo *fi; + + fi = &FINFO[index]; + if (fi->flag & FINFO_ACTIVE) { + netbuffree(fi->caller_addr); + /* XXX may be too big, but can't access xprt array here */ + if (fi->forward_fd >= svc_maxfd) + svc_maxfd--; + free(fi->uaddr); + fi->flag &= ~FINFO_ACTIVE; + rpcb_rmtcalls--; + return (1); + } + return (0); +} + +static int +netbufcmp(struct netbuf *n1, struct netbuf *n2) +{ + return ((n1->len != n2->len) || memcmp(n1->buf, n2->buf, n1->len)); +} + +static bool_t +netbuf_copybuf(struct netbuf *dst, const struct netbuf *src) +{ + assert(src->len <= src->maxlen); + + if (dst->maxlen < src->len || dst->buf == NULL) { + free(dst->buf); + if ((dst->buf = calloc(1, src->maxlen)) == NULL) + return (FALSE); + dst->maxlen = src->maxlen; + } + + dst->len = src->len; + memcpy(dst->buf, src->buf, src->len); + + return (TRUE); +} + +static struct netbuf * +netbufdup(struct netbuf *ap) +{ + struct netbuf *np; + + if ((np = calloc(1, sizeof(struct netbuf))) == NULL) + return (NULL); + if (netbuf_copybuf(np, ap) == FALSE) { + free(np); + return (NULL); + } + return (np); +} + +static void +netbuffree(struct netbuf *ap) +{ + + if (ap == NULL) + return; + free(ap->buf); + ap->buf = NULL; + free(ap); +} + + +#define MASKVAL (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND) +extern bool_t __svc_clean_idle(fd_set *, int, bool_t); + +void +my_svc_run(void) +{ + struct pollfd pollfds[FD_SETSIZE + 1]; + fd_set cleanfds; + struct pollfd *p; + size_t nfds; +#ifdef SVC_RUN_DEBUG + size_t i; +#endif + int n, poll_ret, check_ret; + + for (;;) { + p = pollfds; + p->fd = terminate_rfd; + p->events = MASKVAL; + p++; + for (n = 0; n <= svc_maxfd; n++) { + if (FD_ISSET(n, &svc_fdset)) { + p->fd = n; + p->events = MASKVAL; + p++; + } + } + nfds = p - pollfds; + poll_ret = 0; +#ifdef SVC_RUN_DEBUG + if (debugging) { + fprintf(stderr, "%s: polling for read on fd < ", + __func__); + for (i = 0, p = pollfds; i < nfds; i++, p++) + if (p->events) + fprintf(stderr, "%d ", p->fd); + fprintf(stderr, ">\n"); + } +#endif + poll_ret = poll(pollfds, nfds, 30 * 1000); + + if (doterminate != 0) { + close(rpcbindlockfd); +#ifdef WARMSTART + syslog(LOG_ERR, + "rpcbind terminating on signal %d. Restart with \"rpcbind -w\"", + (int)doterminate); + write_warmstart(); /* Dump yourself */ +#endif + exit(2); + } + + switch (poll_ret) { + case -1: + /* + * We ignore all errors, continuing with the assumption + * that it was set by the signal handlers (or any + * other outside event) and not caused by poll(). + */ +#ifdef SVC_RUN_DEBUG + if (debugging) { + fprintf(stderr, "%s: poll returned %d: %m\n", + __func__, poll_ret); + } +#endif + /* FALLTHROUGH */ + case 0: + cleanfds = svc_fdset; + __svc_clean_idle(&cleanfds, 30, FALSE); + continue; + default: +#ifdef SVC_RUN_DEBUG + if (debugging) { + fprintf(stderr, "%s: poll returned read fds < ", + __func__); + for (i = 0, p = pollfds; i < nfds; i++, p++) + if (p->revents) + fprintf(stderr, "%d (%#x)", + p->fd, p->revents); + fprintf(stderr, ">\n"); + } +#endif + /* + * If we found as many replies on callback fds + * as the number of descriptors selectable which + * poll() returned, there can be no more so we + * don't call svc_getreq_poll. Otherwise, there + * must be another so we must call svc_getreq_poll. + */ + if ((check_ret = check_rmtcalls(pollfds, nfds)) == + poll_ret) + continue; + svc_getreq_poll(pollfds, poll_ret-check_ret); + } +#ifdef SVC_RUN_DEBUG + if (debugging) { + fprintf(stderr, "%s: svc_maxfd now %u\n", __func__, + svc_maxfd); + } +#endif + } +} + +static int +check_rmtcalls(struct pollfd *pfds, int nfds) +{ + int j, ncallbacks_found = 0, rmtcalls_pending; + SVCXPRT *xprt; + + if (rpcb_rmtcalls == 0) + return (0); + + rmtcalls_pending = rpcb_rmtcalls; + for (j = 0; j < nfds; j++) { + if ((xprt = find_rmtcallxprt_by_fd(pfds[j].fd)) != NULL) { + if (pfds[j].revents) { + ncallbacks_found++; +#ifdef DEBUG_RMTCALL + if (debugging) + fprintf(stderr, "%s: polled on forwarding " + "fd %d, netid %s - calling handle_reply\n", + __func__, pfds[j].fd, xprt->xp_netid); +#endif + handle_reply(pfds[j].fd, xprt); + pfds[j].revents = 0; + if (ncallbacks_found >= rmtcalls_pending) { + break; + } + } + } + } + return (ncallbacks_found); +} + +static void +xprt_set_caller(SVCXPRT *xprt, struct finfo *fi) +{ + u_int32_t *xidp; + + netbuf_copybuf(svc_getrpccaller(xprt), fi->caller_addr); + xidp = __rpcb_get_dg_xidp(xprt); + *xidp = fi->caller_xid; +} + +/* + * Call svcerr_systemerr() only if RPCBVERS4 + */ +static void +send_svcsyserr(SVCXPRT *xprt, struct finfo *fi) +{ + if (fi->reply_type == RPCBPROC_INDIRECT) { + xprt_set_caller(xprt, fi); + svcerr_systemerr(xprt); + } +} + +static void +handle_reply(int fd, SVCXPRT *xprt) +{ + XDR reply_xdrs; + struct rpc_msg reply_msg; + struct rpc_err reply_error; + char *buffer; + struct finfo *fi; + int inlen, pos, len; + struct r_rmtcall_args a; + struct sockaddr_storage ss; + socklen_t fromlen; +#ifdef SVC_RUN_DEBUG + char *uaddr; +#endif + + buffer = malloc(RPC_BUF_MAX); + if (buffer == NULL) + goto done; + + do { + fromlen = sizeof(ss); + inlen = recvfrom(fd, buffer, RPC_BUF_MAX, 0, + (struct sockaddr *)&ss, &fromlen); + } while (inlen < 0 && errno == EINTR); + if (inlen < 0) { + if (debugging) + fprintf(stderr, "%s: recvfrom returned %d: %m\n", + __func__, inlen); + goto done; + } + + reply_msg.acpted_rply.ar_verf = _null_auth; + reply_msg.acpted_rply.ar_results.where = 0; + reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void; + + xdrmem_create(&reply_xdrs, buffer, (u_int)inlen, XDR_DECODE); + if (!xdr_replymsg(&reply_xdrs, &reply_msg)) { + if (debugging) + fprintf(stderr, "%s: xdr_replymsg failed\n", __func__); + goto done; + } + fi = forward_find(reply_msg.rm_xid); +#ifdef SVC_RUN_DEBUG + if (debugging) { + fprintf(stderr, "%s: reply xid: %d fi addr: %p\n", + __func__, reply_msg.rm_xid, fi); + } +#endif + if (fi == NULL) { + goto done; + } + _seterr_reply(&reply_msg, &reply_error); + if (reply_error.re_status != RPC_SUCCESS) { + if (debugging) + fprintf(stderr, "%s: %s\n", __func__, + clnt_sperrno(reply_error.re_status)); + send_svcsyserr(xprt, fi); + goto done; + } + pos = XDR_GETPOS(&reply_xdrs); + len = inlen - pos; + a.rmt_args.args = &buffer[pos]; + a.rmt_args.arglen = len; + a.rmt_uaddr = fi->uaddr; + a.rmt_localvers = fi->versnum; + + xprt_set_caller(xprt, fi); +#ifdef SVC_RUN_DEBUG + uaddr = taddr2uaddr(rpcbind_get_conf("udp"), + svc_getrpccaller(xprt)); + if (debugging) { + fprintf(stderr, "%s: forwarding address %s to %s\n", + __func__, a.rmt_uaddr, uaddr ? uaddr : rpcbind_unknown); + } + free(uaddr); +#endif + svc_sendreply(xprt, (xdrproc_t)xdr_rmtcall_result, &a); +done: + free(buffer); + + if (reply_msg.rm_xid == 0) { +#ifdef SVC_RUN_DEBUG + if (debugging) { + fprintf(stderr, "%s: NULL xid on exit!\n", __func__); + } +#endif + } else + (void)free_slot_by_xid(reply_msg.rm_xid); +} + +static void +find_versions(rpcprog_t prog, const char *netid, rpcvers_t *lowvp, + rpcvers_t *highvp) +{ + rpcblist_ptr rbl; + rpcvers_t lowv = 0; + rpcvers_t highv = 0; + + for (rbl = list_rbl; rbl != NULL; rbl = rbl->rpcb_next) { + if ((rbl->rpcb_map.r_prog != prog) || + ((rbl->rpcb_map.r_netid != NULL) && + (strcasecmp(rbl->rpcb_map.r_netid, netid) != 0))) + continue; + if (lowv == 0) { + highv = rbl->rpcb_map.r_vers; + lowv = highv; + } else if (rbl->rpcb_map.r_vers < lowv) { + lowv = rbl->rpcb_map.r_vers; + } else if (rbl->rpcb_map.r_vers > highv) { + highv = rbl->rpcb_map.r_vers; + } + } + *lowvp = lowv; + *highvp = highv; +} + +/* + * returns the item with the given program, version number and netid. + * If that version number is not found, it returns the item with that + * program number, so that address is now returned to the caller. The + * caller when makes a call to this program, version number, the call + * will fail and it will return with PROGVERS_MISMATCH. The user can + * then determine the highest and the lowest version number for this + * program using clnt_geterr() and use those program version numbers. + * + * Returns the RPCBLIST for the given prog, vers and netid + */ +static rpcblist_ptr +find_service(rpcprog_t prog, rpcvers_t vers, char *netid) +{ + rpcblist_ptr hit = NULL; + rpcblist_ptr rbl; + + for (rbl = list_rbl; rbl != NULL; rbl = rbl->rpcb_next) { + if ((rbl->rpcb_map.r_prog != prog) || + ((rbl->rpcb_map.r_netid != NULL) && + (strcasecmp(rbl->rpcb_map.r_netid, netid) != 0))) + continue; + hit = rbl; + if (rbl->rpcb_map.r_vers == vers) + break; + } + return (hit); +} + +/* + * Copies the name associated with the uid of the caller and returns + * a pointer to it. Similar to getwd(). + */ +static char * +getowner(SVCXPRT *transp, char *owner, size_t ownersize) +{ + uid_t uid; + + if (__rpc_get_local_uid(transp, &uid) < 0) + strlcpy(owner, rpcbind_unknown, ownersize); + else if (uid == 0) + strlcpy(owner, rpcbind_superuser, ownersize); + else + snprintf(owner, ownersize, "%d", uid); + + return owner; +} + +#ifdef PORTMAP +/* + * Add this to the pmap list only if it is UDP or TCP. + */ +static int +add_pmaplist(RPCB *arg) +{ + struct pmap pmap; + struct pmaplist *pml; + int h1, h2, h3, h4, p1, p2; + + if (strcmp(arg->r_netid, udptrans) == 0) { + /* It is UDP! */ + pmap.pm_prot = IPPROTO_UDP; + } else if (strcmp(arg->r_netid, tcptrans) == 0) { + /* It is TCP */ + pmap.pm_prot = IPPROTO_TCP; + } else + /* Not an IP protocol */ + return (0); + + /* interpret the universal address for TCP/IP */ + if (sscanf(arg->r_addr, "%d.%d.%d.%d.%d.%d", + &h1, &h2, &h3, &h4, &p1, &p2) != 6) + return (0); + pmap.pm_port = ((p1 & 0xff) << 8) + (p2 & 0xff); + pmap.pm_prog = arg->r_prog; + pmap.pm_vers = arg->r_vers; + /* + * add to END of list + */ + pml = malloc(sizeof(*pml)); + if (pml == NULL) { + syslog(LOG_ERR, "%s: %m", __func__); + return (1); + } + pml->pml_map = pmap; + pml->pml_next = NULL; + if (list_pml == NULL) { + list_pml = pml; + } else { + struct pmaplist *fnd; + + /* Attach to the end of the list */ + for (fnd = list_pml; fnd->pml_next; fnd = fnd->pml_next) + continue; + fnd->pml_next = pml; + } + return (0); +} + +/* + * Delete this from the pmap list only if it is UDP or TCP. + */ +static int +del_pmaplist(RPCB *arg) +{ + struct pmaplist *pml; + struct pmaplist *prevpml, *fnd; + unsigned long prot; + + if (strcmp(arg->r_netid, udptrans) == 0) { + /* It is UDP! */ + prot = IPPROTO_UDP; + } else if (strcmp(arg->r_netid, tcptrans) == 0) { + /* It is TCP */ + prot = IPPROTO_TCP; + } else if (arg->r_netid[0] == 0) { + prot = 0; /* Remove all occurrences */ + } else { + /* Not an IP protocol */ + return (0); + } + for (prevpml = NULL, pml = list_pml; pml; /* cstyle */) { + if ((pml->pml_map.pm_prog != arg->r_prog) || + (pml->pml_map.pm_vers != arg->r_vers) || + (prot && (pml->pml_map.pm_prot != prot))) { + /* both pml & prevpml move forwards */ + prevpml = pml; + pml = pml->pml_next; + continue; + } + /* found it; pml moves forward, prevpml stays */ + fnd = pml; + pml = pml->pml_next; + if (prevpml == NULL) + list_pml = pml; + else + prevpml->pml_next = pml; + free(fnd); + } + return (0); +} +#endif /* PORTMAP */ diff --git a/usr.sbin/rpcbind/rpcbind.8 b/usr.sbin/rpcbind/rpcbind.8 new file mode 100644 index 000000000000..0132c8f6a5d0 --- /dev/null +++ b/usr.sbin/rpcbind/rpcbind.8 @@ -0,0 +1,189 @@ +.\" Copyright 1989 AT&T +.\" Copyright 1991 Sun Microsystems, Inc. +.Dd May 30, 2025 +.Dt RPCBIND 8 +.Os +.Sh NAME +.Nm rpcbind +.Nd universal addresses to RPC program number mapper +.Sh SYNOPSIS +.Nm +.Op Fl 6adIiLlNPswW +.Op Fl h Ar bindip +.Sh DESCRIPTION +The +.Nm +utility is a server that converts +.Tn RPC +program numbers into +universal addresses. +It must be running on the host to be able to make +.Tn RPC +calls +on a server on that machine. +.Pp +When an +.Tn RPC +service is started, +it tells +.Nm +the address at which it is listening, +and the +.Tn RPC +program numbers it is prepared to serve. +When a client wishes to make an +.Tn RPC +call to a given program number, +it first contacts +.Nm +on the server machine to determine +the address where +.Tn RPC +requests should be sent. +.Pp +The +.Nm +utility should be started before any other RPC service. +Normally, standard +.Tn RPC +servers are started by port monitors, so +.Nm +must be started before port monitors are invoked. +.Pp +When +.Nm +is started, it checks that certain name-to-address +translation-calls function correctly. +If they fail, the network configuration databases may be corrupt. +Since +.Tn RPC +services cannot function correctly in this situation, +.Nm +reports the condition and terminates. +.Pp +The +.Nm +utility can only be started by the super-user. +.Sh OPTIONS +.Bl -tag -width indent +.It Fl 6 +Bind to AF_INET6 (IPv6) addresses only. +.It Fl a +When debugging +.Pq Fl d , +do an abort on errors. +.It Fl d +Run in debug mode. +In this mode, +.Nm +will not fork when it starts, will print additional information +during operation, and will abort on certain errors if +.Fl a +is also specified. +With this option, the name-to-address translation consistency +checks are shown in detail. +.It Fl h Ar bindip +IP addresses to bind to when servicing TCP and UDP requests. +This option +may be specified multiple times and is typically necessary when running +on a multi-homed host. +If no +.Fl h +option is specified, +.Nm +will bind to +.Dv INADDR_ANY , +which could lead to problems on a multi-homed host due to +.Nm +returning a UDP packet from a different IP address than it was +sent to. +Note that when specifying IP addresses with +.Fl h , +and no +.Fl I +option is specified, +.Nm +will automatically add +.Li 127.0.0.1 +and if IPv6 is enabled, +.Li ::1 +to the list. +.It Fl I +Cause +.Nm +not to bind to the localhost when +.Fl h +has been specified. +.It Fl i +.Dq Insecure +mode. +Allow calls to SET and UNSET from any host. +Normally +.Nm +accepts these requests only from the loopback interface for security reasons. +This change is necessary for programs that were compiled with earlier +versions of the rpc library and do not make those requests using the +loopback interface. +.It Fl L +Allow old-style local connections over the loopback interface. +Without this flag, local connections are only allowed over a local socket, +.Pa /var/run/rpcbind.sock . +.It Fl l +Turn on libwrap connection logging. +.It Fl N +Run in foreground mode. +In this mode, +.Nm +will not fork when it starts. +.It Fl P +Specify alternative location of a file where main process PID will be stored. +The default location is +.Pa /var/run/rpcbind.pid . +.It Fl s +Cause +.Nm +to change to the user daemon as soon as possible. +This causes +.Nm +to use non-privileged ports for outgoing connections, preventing non-privileged +clients from using +.Nm +to connect to services from a privileged port. +.It Fl W +Enable libwrap (TCP wrappers) support. +.It Fl w +Enable the warmstart feature. +.Pp +The warmstart feature saves RPC registrations on termination. +Any saved RPC registrations are restored on restart if +.Fl w +is specified. +This feature helps avoid RPC service interruption when restarting +.Nm . +warmstart support must be compiled in to +.Nm . +Portmap registrations are stored in +.Pa /tmp/portmap.file . +.Nm +registrations are stored in +.Pa /tmp/rpcbind.file . +.El +.Sh NOTES +All RPC servers must be restarted if +.Nm +is restarted. +.Sh FILES +.Bl -tag -width /var/run/rpcbind.sock -compact +.It Pa /tmp/portmap.file +saved portmap registrations file. +.It Pa /tmp/rpcbind.file +saved +.Nm +registrations file. +.It Pa /var/run/rpcbind.sock +socket used for local connections. +.El +.Sh SEE ALSO +.Xr rpcbind 3 , +.Xr netconfig 5 , +.Xr rpcinfo 8 diff --git a/usr.sbin/rpcbind/rpcbind.c b/usr.sbin/rpcbind/rpcbind.c new file mode 100644 index 000000000000..faa24a4415e2 --- /dev/null +++ b/usr.sbin/rpcbind/rpcbind.c @@ -0,0 +1,941 @@ +/* $NetBSD: rpcbind.c,v 1.3 2002/11/08 00:16:40 fvdl Exp $ */ + +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 2009, Sun Microsystems, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of Sun Microsystems, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Copyright (c) 1984 - 1991 by Sun Microsystems, Inc. + */ + +/* + * rpcbind.c + * Implements the program, version to address mapping for rpc. + * + */ + +#include <sys/param.h> +#include <sys/linker.h> +#include <sys/module.h> +#include <sys/resource.h> +#include <sys/signal.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/un.h> +#include <sys/wait.h> + +#include <rpc/rpc.h> +#include <rpc/rpc_com.h> +#ifdef PORTMAP +#include <netinet/in.h> +#endif +#include <arpa/inet.h> + +#include <assert.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <libutil.h> +#include <netconfig.h> +#include <netdb.h> +#include <pwd.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +#include "rpcbind.h" + +/* Global variables */ +int debugging = 0; /* Tell me what's going on */ +int doabort = 0; /* When debugging, do an abort on errors */ +int terminate_rfd; /* Pipefd to wake on signal */ +volatile sig_atomic_t doterminate = 0; /* Terminal signal received */ +rpcblist_ptr list_rbl; /* A list of version 3/4 rpcbind services */ +int rpcbindlockfd; + +/* who to suid to if -s is given */ +#define RUN_AS "daemon" + +#define RPCBINDDLOCK "/var/run/rpcbind.lock" + +#define DEFAULT_PIDFILE "/var/run/rpcbind.pid" + +char *pidfile_path = DEFAULT_PIDFILE; +struct pidfh *pidfh = NULL; + +static int runasdaemon = 0; +int insecure = 0; +int oldstyle_local = 0; +#ifdef LIBWRAP +int libwrap = 0; +#endif +int nofork = 0; +int verboselog = 0; +int nobind_localhost = 0; + +static char **hosts = NULL; +static struct sockaddr **bound_sa; +static int ipv6_only = 0; +static int nhosts = 0; +static int on = 1; +static int terminate_wfd; + +#ifdef WARMSTART +/* Local Variable */ +static int warmstart = 0; /* Grab an old copy of registrations */ +#endif + +#ifdef PORTMAP +struct pmaplist *list_pml; /* A list of version 2 rpcbind services */ +const char *udptrans; /* Name of UDP transport */ +const char *tcptrans; /* Name of TCP transport */ +const char *udp_uaddr; /* Universal UDP address */ +const char *tcp_uaddr; /* Universal TCP address */ +#endif +static const char servname[] = "rpcbind"; +const char rpcbind_superuser[] = "superuser"; +const char rpcbind_unknown[] = "unknown"; +static const char nlname[] = "netlink"; + +static const struct netconfig netlink_nconf = { + .nc_netid = __UNCONST(nlname), + .nc_semantics = NC_TPI_CLTS, +}; + +static const struct t_bind netlink_taddr = { + .addr = { + .maxlen = sizeof(nlname), + .len = sizeof(nlname), + .buf = __UNCONST(nlname), + }, +}; + +static int init_transport(const struct netconfig *); +static void rbllist_add(rpcprog_t, rpcvers_t, const struct netconfig *, + struct netbuf *); +static void cleanup_pidfile(void); +static void terminate(int); +static void parseargs(int, char *[]); +static void update_bound_sa(void); + +int +main(int argc, char *argv[]) +{ + struct netconfig *nconf; + void *nc_handle; /* Net config handle */ + struct rlimit rl; + int maxrec = RPC_MAXDATASIZE; + int error, fds[2]; + + parseargs(argc, argv); + + update_bound_sa(); + + /* Ensure krpc is loaded */ + if (modfind("krpc") < 0 && kldload("krpc") < 0) { + warn("failed to load krpc module, " + "rpcbind services for kernel disabled"); + } + + /* Check that another rpcbind isn't already running. */ + if ((rpcbindlockfd = open(RPCBINDDLOCK, O_RDONLY|O_CREAT, 0444)) < 0) + err(1, "%s", RPCBINDDLOCK); + + if (flock(rpcbindlockfd, LOCK_EX|LOCK_NB) != 0 && errno == EWOULDBLOCK) + errx(1, "another rpcbind is already running. Aborting"); + + if (pidfile_path != NULL) { + pidfh = pidfile_open(pidfile_path, 0600, NULL); + if (pidfh == NULL) + warn("cannot open pid file"); + atexit(cleanup_pidfile); + } + + getrlimit(RLIMIT_NOFILE, &rl); + if (rl.rlim_cur < 128) { + if (rl.rlim_max <= 128) + rl.rlim_cur = rl.rlim_max; + else + rl.rlim_cur = 128; + setrlimit(RLIMIT_NOFILE, &rl); + } + openlog("rpcbind", LOG_CONS, LOG_DAEMON); + if (geteuid()) { /* This command allowed only to root */ + fprintf(stderr, "Sorry. You are not superuser\n"); + exit(1); + } + nc_handle = setnetconfig(); /* open netconfig file */ + if (nc_handle == NULL) { + syslog(LOG_ERR, "could not read /etc/netconfig"); + exit(1); + } +#ifdef PORTMAP + udptrans = ""; + tcptrans = ""; +#endif + + nconf = getnetconfigent("local"); + if (nconf == NULL) + nconf = getnetconfigent("unix"); + if (nconf == NULL) { + syslog(LOG_ERR, "%s: can't find local transport\n", argv[0]); + exit(1); + } + + rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec); + + init_transport(nconf); + + while ((nconf = getnetconfig(nc_handle))) { + if (nconf->nc_flag & NC_VISIBLE) { + if (ipv6_only == 1 && strcmp(nconf->nc_protofmly, + "inet") == 0) { + /* DO NOTHING */ + } else + init_transport(nconf); + } + } + endnetconfig(nc_handle); + + init_transport(&netlink_nconf); + + /* + * Allocate pipe fd to wake main thread from signal handler in non-racy + * way. + */ + error = pipe(fds); + if (error != 0) + err(1, "pipe failed"); + terminate_rfd = fds[0]; + terminate_wfd = fds[1]; + + /* catch the usual termination signals for graceful exit */ + (void) signal(SIGCHLD, reap); + (void) signal(SIGINT, terminate); + (void) signal(SIGTERM, terminate); + (void) signal(SIGQUIT, terminate); + /* ignore others that could get sent */ + (void) signal(SIGPIPE, SIG_IGN); + (void) signal(SIGHUP, SIG_IGN); + (void) signal(SIGUSR1, SIG_IGN); + (void) signal(SIGUSR2, SIG_IGN); +#ifdef WARMSTART + if (warmstart) { + read_warmstart(); + } +#endif + if (debugging) { + printf("rpcbind debugging enabled."); + if (doabort) { + printf(" Will abort on errors!\n"); + } else { + printf("\n"); + } + } else if (!nofork) { + if (daemon(0, 0)) + err(1, "fork failed"); + } + + if (pidfh != NULL && pidfile_write(pidfh) != 0) + syslog(LOG_ERR, "pidfile_write(): %m"); + + if (runasdaemon) { + struct passwd *p; + + if((p = getpwnam(RUN_AS)) == NULL) { + syslog(LOG_ERR, "cannot get uid of daemon: %m"); + exit(1); + } + if (setuid(p->pw_uid) == -1) { + syslog(LOG_ERR, "setuid to daemon failed: %m"); + exit(1); + } + } + + network_init(); + + my_svc_run(); + syslog(LOG_ERR, "svc_run returned unexpectedly"); + rpcbind_abort(); + /* NOTREACHED */ + + return 0; +} + +/* + * Adds the entry into the rpcbind database. + * If PORTMAP, then for UDP and TCP, it adds the entries for version 2 also + * Returns 0 if succeeds, else fails + */ +static int +init_transport(const struct netconfig *nconf) +{ + int fd = -1; + struct t_bind taddr; + struct addrinfo hints, *res = NULL; + struct __rpc_sockinfo si; + SVCXPRT *my_xprt = NULL; + int status; /* bound checking ? */ + int aicode; + int addrlen; + int nhostsbak; + int bound; + u_int32_t host_addr[4]; /* IPv4 or IPv6 */ + struct sockaddr *sa; + struct sockaddr_un sun; + mode_t oldmask; + bool local, netlink; + + local = strcmp(nconf->nc_netid, "local") == 0 || + strcmp(nconf->nc_netid, "unix") == 0; + netlink = strcmp(nconf->nc_netid, "netlink") == 0; + + if ((nconf->nc_semantics != NC_TPI_CLTS) && + (nconf->nc_semantics != NC_TPI_COTS) && + (nconf->nc_semantics != NC_TPI_COTS_ORD)) + return (1); /* not my type */ +#ifdef RPCBIND_DEBUG + if (debugging) { + unsigned int i; + char **s; + + (void)fprintf(stderr, "%s: %ld lookup routines :\n", + nconf->nc_netid, nconf->nc_nlookups); + for (i = 0, s = nconf->nc_lookups; i < nconf->nc_nlookups; + i++, s++) + (void)fprintf(stderr, "[%d] - %s\n", i, *s); + } +#endif + + /* + * XXX - using RPC library internal functions. + */ + if (local) { + /* + * For other transports we call this later, for each socket we + * like to bind. + */ + if ((fd = __rpc_nconf2fd(nconf)) < 0) { + syslog(errno == EAFNOSUPPORT ? LOG_DEBUG : LOG_ERR, + "cannot create socket for %s", + nconf->nc_netid); + return (1); + } + } + + if (!__rpc_nconf2sockinfo(nconf, &si)) { + syslog(LOG_ERR, "cannot get information for %s", + nconf->nc_netid); + return (1); + } + + if (local) { + memset(&sun, 0, sizeof sun); + sun.sun_family = AF_LOCAL; + unlink(_PATH_RPCBINDSOCK); + strcpy(sun.sun_path, _PATH_RPCBINDSOCK); + sun.sun_len = SUN_LEN(&sun); + addrlen = sizeof (struct sockaddr_un); + sa = (struct sockaddr *)&sun; + } else if (!netlink) { + /* Get rpcbind's address on this transport */ + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = si.si_af; + hints.ai_socktype = si.si_socktype; + hints.ai_protocol = si.si_proto; + } + + if (!local && !netlink) { + /* + * If no hosts were specified, just bind to INADDR_ANY. + * Otherwise make sure 127.0.0.1 is added to the list. + */ + nhostsbak = nhosts + 1; + hosts = realloc(hosts, nhostsbak * sizeof(char *)); + if (nhostsbak == 1) + hosts[0] = "*"; + else { + if (hints.ai_family == AF_INET && + !nobind_localhost) { + hosts[nhostsbak - 1] = "127.0.0.1"; + } else if (hints.ai_family == AF_INET6 && + !nobind_localhost) { + hosts[nhostsbak - 1] = "::1"; + } else + return 1; + } + + /* + * Bind to specific IPs if asked to + */ + bound = 0; + while (nhostsbak > 0) { + --nhostsbak; + /* + * XXX - using RPC library internal functions. + */ + if ((fd = __rpc_nconf2fd(nconf)) < 0) { + int non_fatal = 0; + if (errno == EAFNOSUPPORT && + nconf->nc_semantics != NC_TPI_CLTS) + non_fatal = 1; + syslog(non_fatal ? LOG_DEBUG : LOG_ERR, + "cannot create socket for %s", nconf->nc_netid); + return (1); + } + switch (hints.ai_family) { + case AF_INET: + if (inet_pton(AF_INET, hosts[nhostsbak], + host_addr) == 1) { + hints.ai_flags &= AI_NUMERICHOST; + } else { + /* + * Skip if we have an AF_INET6 address. + */ + if (inet_pton(AF_INET6, + hosts[nhostsbak], host_addr) == 1) { + close(fd); + continue; + } + } + break; + case AF_INET6: + if (inet_pton(AF_INET6, hosts[nhostsbak], + host_addr) == 1) { + hints.ai_flags &= AI_NUMERICHOST; + } else { + /* + * Skip if we have an AF_INET address. + */ + if (inet_pton(AF_INET, hosts[nhostsbak], + host_addr) == 1) { + close(fd); + continue; + } + } + if (setsockopt(fd, IPPROTO_IPV6, + IPV6_V6ONLY, &on, sizeof on) < 0) { + syslog(LOG_ERR, + "can't set v6-only binding for " + "ipv6 socket: %m"); + continue; + } + break; + default: + break; + } + + /* + * If no hosts were specified, just bind to INADDR_ANY + */ + if (strcmp("*", hosts[nhostsbak]) == 0) + hosts[nhostsbak] = NULL; + if ((aicode = getaddrinfo(hosts[nhostsbak], servname, &hints, + &res)) != 0) { + syslog(LOG_ERR, "cannot get local address for %s: %s", + nconf->nc_netid, gai_strerror(aicode)); + continue; + } + addrlen = res->ai_addrlen; + sa = (struct sockaddr *)res->ai_addr; + oldmask = umask(S_IXUSR|S_IXGRP|S_IXOTH); + if (bind(fd, sa, addrlen) != 0) { + syslog(LOG_ERR, "cannot bind %s on %s: %m", + (hosts[nhostsbak] == NULL) ? "*" : + hosts[nhostsbak], nconf->nc_netid); + if (res != NULL) + freeaddrinfo(res); + continue; + } else + bound = 1; + (void)umask(oldmask); + + /* Copy the address */ + taddr.addr.len = taddr.addr.maxlen = addrlen; + taddr.addr.buf = malloc(addrlen); + if (taddr.addr.buf == NULL) { + syslog(LOG_ERR, + "cannot allocate memory for %s address", + nconf->nc_netid); + if (res != NULL) + freeaddrinfo(res); + return 1; + } + memcpy(taddr.addr.buf, sa, addrlen); +#ifdef RPCBIND_DEBUG + if (debugging) { + /* + * for debugging print out our universal + * address + */ + char *uaddr; + struct netbuf nb; + + nb.buf = sa; + nb.len = nb.maxlen = sa->sa_len; + uaddr = taddr2uaddr(nconf, &nb); + (void)fprintf(stderr, + "rpcbind : my address is %s\n", uaddr); + (void)free(uaddr); + } +#endif + + if (nconf->nc_semantics != NC_TPI_CLTS) + listen(fd, SOMAXCONN); + + my_xprt = (SVCXPRT *)svc_tli_create(fd, nconf, &taddr, + RPC_MAXDATASIZE, RPC_MAXDATASIZE); + } + } else if (local) { + oldmask = umask(S_IXUSR|S_IXGRP|S_IXOTH); + if (bind(fd, sa, addrlen) < 0) { + syslog(LOG_ERR, "cannot bind %s: %m", nconf->nc_netid); + if (res != NULL) + freeaddrinfo(res); + return 1; + } + (void) umask(oldmask); + + /* Copy the address */ + taddr.addr.len = taddr.addr.maxlen = addrlen; + taddr.addr.buf = malloc(addrlen); + if (taddr.addr.buf == NULL) { + syslog(LOG_ERR, "cannot allocate memory for %s address", + nconf->nc_netid); + if (res != NULL) + freeaddrinfo(res); + return 1; + } + memcpy(taddr.addr.buf, sa, addrlen); +#ifdef RPCBIND_DEBUG + if (debugging) { + /* for debugging print out our universal address */ + char *uaddr; + struct netbuf nb; + + nb.buf = sa; + nb.len = nb.maxlen = sa->sa_len; + uaddr = taddr2uaddr(nconf, &nb); + (void)fprintf(stderr, "rpcbind : my address is %s\n", + uaddr); + (void)free(uaddr); + } +#endif + + if (nconf->nc_semantics != NC_TPI_CLTS) + listen(fd, SOMAXCONN); + + my_xprt = (SVCXPRT *)svc_tli_create(fd, nconf, &taddr, + RPC_MAXDATASIZE, RPC_MAXDATASIZE); + } else { + assert(netlink); + taddr = netlink_taddr; + my_xprt = svc_nl_create("rpcbind"); + } + + if (my_xprt == (SVCXPRT *)NULL) { + syslog(LOG_ERR, "%s: could not create service", + nconf->nc_netid); + goto error; + } + +#ifdef PORTMAP + /* + * Register both the versions for tcp/ip, udp/ip and local. + */ + if (!netlink && (local || (strcmp(nconf->nc_protofmly, NC_INET) == 0 && + (strcmp(nconf->nc_proto, NC_TCP) == 0 || + strcmp(nconf->nc_proto, NC_UDP) == 0)))) { + struct pmaplist *pml; + + if (!svc_register(my_xprt, PMAPPROG, PMAPVERS, + pmap_service, 0)) { + syslog(LOG_ERR, "could not register on %s", + nconf->nc_netid); + goto error; + } + pml = malloc(sizeof (struct pmaplist)); + if (pml == NULL) { + syslog(LOG_ERR, "no memory!"); + exit(1); + } + pml->pml_map.pm_prog = PMAPPROG; + pml->pml_map.pm_vers = PMAPVERS; + pml->pml_map.pm_port = PMAPPORT; + if (strcmp(nconf->nc_proto, NC_TCP) == 0) { + if (tcptrans[0]) { + free(pml); + pml = NULL; + syslog(LOG_ERR, + "cannot have more than one TCP transport"); + goto error; + } + tcptrans = strdup(nconf->nc_netid); + pml->pml_map.pm_prot = IPPROTO_TCP; + + /* Let's snarf the universal address */ + /* "h1.h2.h3.h4.p1.p2" */ + tcp_uaddr = taddr2uaddr(nconf, &taddr.addr); + } else if (strcmp(nconf->nc_proto, NC_UDP) == 0) { + if (udptrans[0]) { + syslog(LOG_ERR, + "cannot have more than one UDP transport"); + goto error; + } + udptrans = strdup(nconf->nc_netid); + pml->pml_map.pm_prot = IPPROTO_UDP; + + /* Let's snarf the universal address */ + /* "h1.h2.h3.h4.p1.p2" */ + udp_uaddr = taddr2uaddr(nconf, &taddr.addr); + } else if (strcmp(nconf->nc_netid, "local") == 0) + pml->pml_map.pm_prot = IPPROTO_ST; + else if (strcmp(nconf->nc_netid, "unix") == 0) + pml->pml_map.pm_prot = IPPROTO_ST; + pml->pml_next = list_pml; + list_pml = pml; + + /* Add version 3 information */ + pml = malloc(sizeof(*pml)); + if (pml == NULL) { + syslog(LOG_ERR, "%m"); + exit(1); + } + pml->pml_map = list_pml->pml_map; + pml->pml_map.pm_vers = RPCBVERS; + pml->pml_next = list_pml; + list_pml = pml; + + /* Add version 4 information */ + pml = malloc(sizeof(*pml)); + if (pml == NULL) { + syslog(LOG_ERR, "%m"); + exit(1); + } + pml->pml_map = list_pml->pml_map; + pml->pml_map.pm_vers = RPCBVERS4; + pml->pml_next = list_pml; + list_pml = pml; + + /* Also add version 2 stuff to rpcbind list */ + rbllist_add(PMAPPROG, PMAPVERS, nconf, &taddr.addr); + } +#endif + + /* version 3 registration */ + if (!svc_reg(my_xprt, RPCBPROG, RPCBVERS, rpcb_service_3, NULL)) { + syslog(LOG_ERR, "could not register %s version 3", + nconf->nc_netid); + goto error; + } + rbllist_add(RPCBPROG, RPCBVERS, nconf, &taddr.addr); + + /* version 4 registration */ + if (!svc_reg(my_xprt, RPCBPROG, RPCBVERS4, rpcb_service_4, NULL)) { + syslog(LOG_ERR, "could not register %s version 4", + nconf->nc_netid); + goto error; + } + rbllist_add(RPCBPROG, RPCBVERS4, nconf, &taddr.addr); + + /* decide if bound checking works for this transport */ + status = add_bndlist(nconf, &taddr.addr); +#ifdef RPCBIND_DEBUG + if (debugging) { + if (status < 0) { + fprintf(stderr, "Error in finding bind status for %s\n", + nconf->nc_netid); + } else if (status == 0) { + fprintf(stderr, "check binding for %s\n", + nconf->nc_netid); + } else if (status > 0) { + fprintf(stderr, "No check binding for %s\n", + nconf->nc_netid); + } + } +#endif + /* + * rmtcall only supported on CLTS transports for now. + */ + if (!netlink && nconf->nc_semantics == NC_TPI_CLTS) { + status = create_rmtcall_fd(nconf); + +#ifdef RPCBIND_DEBUG + if (debugging) { + if (status < 0) { + fprintf(stderr, + "Could not create rmtcall fd for %s\n", + nconf->nc_netid); + } else { + fprintf(stderr, "rmtcall fd for %s is %d\n", + nconf->nc_netid, status); + } + } +#endif + } + return (0); +error: + if (fd != -1) + close(fd); + return (1); +} + +/* + * Create the list of addresses that we're bound to. Normally, this + * list is empty because we're listening on the wildcard address + * (nhost == 0). If -h is specified on the command line, then + * bound_sa will have a list of the addresses that the program binds + * to specifically. This function takes that list and converts them to + * struct sockaddr * and stores them in bound_sa. + */ +static void +update_bound_sa(void) +{ + struct addrinfo hints, *res = NULL; + int i; + + if (nhosts == 0) + return; + bound_sa = malloc(sizeof(*bound_sa) * nhosts); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + for (i = 0; i < nhosts; i++) { + if (getaddrinfo(hosts[i], NULL, &hints, &res) != 0) + continue; + bound_sa[i] = malloc(res->ai_addrlen); + memcpy(bound_sa[i], res->ai_addr, res->ai_addrlen); + } +} + +/* + * Match the sa against the list of addresses we've bound to. If + * we've not specifically bound to anything, we match everything. + * Otherwise, if the IPv4 or IPv6 address matches one of the addresses + * in bound_sa, we return true. If not, we return false. + */ +int +listen_addr(const struct sockaddr *sa) +{ + int i; + + /* + * If nhosts == 0, then there were no -h options on the + * command line, so all addresses are addresses we're + * listening to. + */ + if (nhosts == 0) + return 1; + for (i = 0; i < nhosts; i++) { + if (bound_sa[i] == NULL || + sa->sa_family != bound_sa[i]->sa_family) + continue; + switch (sa->sa_family) { + case AF_INET: + if (memcmp(&SA2SINADDR(sa), &SA2SINADDR(bound_sa[i]), + sizeof(struct in_addr)) == 0) + return (1); + break; +#ifdef INET6 + case AF_INET6: + if (memcmp(&SA2SIN6ADDR(sa), &SA2SIN6ADDR(bound_sa[i]), + sizeof(struct in6_addr)) == 0) + return (1); + break; +#endif + default: + break; + } + } + return (0); +} + +static void +rbllist_add(rpcprog_t prog, rpcvers_t vers, const struct netconfig *nconf, + struct netbuf *addr) +{ + rpcblist_ptr rbl; + + rbl = malloc(sizeof (rpcblist)); + if (rbl == NULL) { + syslog(LOG_ERR, "no memory!"); + exit(1); + } + + rbl->rpcb_map.r_prog = prog; + rbl->rpcb_map.r_vers = vers; + rbl->rpcb_map.r_netid = strdup(nconf->nc_netid); + rbl->rpcb_map.r_addr = taddr2uaddr(nconf, addr); + rbl->rpcb_map.r_owner = strdup(rpcbind_superuser); + rbl->rpcb_next = list_rbl; /* Attach to global list */ + list_rbl = rbl; +} + +/* + * atexit callback for pidfh cleanup + */ +static void +cleanup_pidfile(void) +{ + if (pidfh != NULL) + pidfile_remove(pidfh); +} + +/* + * Catch the signal and die + */ +static void +terminate(int signum) +{ + char c = '\0'; + ssize_t wr; + + doterminate = signum; + wr = write(terminate_wfd, &c, 1); + if (wr < 1) { + /* + * The call to cleanup_pidfile should be async-signal safe. + * pidfile_remove calls fstat and funlinkat system calls, and + * we are exiting immediately. + */ + cleanup_pidfile(); + _exit(2); + } +} + +void +rpcbind_abort(void) +{ +#ifdef WARMSTART + write_warmstart(); /* Dump yourself */ +#endif + abort(); +} + +/* get command line options */ +static void +parseargs(int argc, char *argv[]) +{ + int c; + +#ifdef WARMSTART +#define WSOP "w" +#else +#define WSOP "" +#endif +#ifdef LIBWRAP +#define WRAPOP "W" +#else +#define WRAPOP "" +#endif + while ((c = getopt(argc, argv, "6adh:IiLlNP:s" WRAPOP WSOP)) != -1) { + switch (c) { + case '6': + ipv6_only = 1; + break; + case 'a': + doabort = 1; /* when debugging, do an abort on */ + break; /* errors; for rpcbind developers */ + /* only! */ + case 'd': + debugging = 1; + break; + case 'h': + ++nhosts; + hosts = realloc(hosts, nhosts * sizeof(char *)); + if (hosts == NULL) + errx(1, "Out of memory"); + hosts[nhosts - 1] = strdup(optarg); + if (hosts[nhosts - 1] == NULL) + errx(1, "Out of memory"); + break; + case 'I': + nobind_localhost = 1; + break; + case 'i': + insecure = 1; + break; + case 'L': + oldstyle_local = 1; + break; + case 'l': + verboselog = 1; + break; + case 'N': + nofork = 1; + break; + case 's': + runasdaemon = 1; + break; + case 'P': + pidfile_path = strdup(optarg); + break; +#ifdef LIBWRAP + case 'W': + libwrap = 1; + break; +#endif +#ifdef WARMSTART + case 'w': + warmstart = 1; + break; +#endif + default: /* error */ + fprintf(stderr, + "usage: rpcbind [-6adIiLlNPs%s%s] [-h bindip]\n", + WRAPOP, WSOP); + exit (1); + } + } + if (doabort && !debugging) { + fprintf(stderr, + "-a (abort) specified without -d (debugging) -- ignored.\n"); + doabort = 0; + } +#undef WSOP +} + +void +reap(int dummy __unused) +{ + int save_errno = errno; + + while (wait3(NULL, WNOHANG, NULL) > 0) + ; + errno = save_errno; +} + +void +toggle_verboselog(int dummy __unused) +{ + verboselog = !verboselog; +} diff --git a/usr.sbin/rpcbind/rpcbind.h b/usr.sbin/rpcbind/rpcbind.h new file mode 100644 index 000000000000..dd49b27efe0d --- /dev/null +++ b/usr.sbin/rpcbind/rpcbind.h @@ -0,0 +1,161 @@ +/* $NetBSD: rpcbind.h,v 1.1 2000/06/03 00:47:21 fvdl Exp $ */ + +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 2009, Sun Microsystems, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of Sun Microsystems, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc. + */ + +/* + * rpcbind.h + * The common header declarations + */ + +#ifndef rpcbind_h +#define rpcbind_h + +#include <signal.h> + +#ifdef PORTMAP +#include <rpc/pmap_prot.h> +#endif +#include <rpc/rpcb_prot.h> + +/* + * Stuff for the rmtcall service + */ +struct encap_parms { + u_int32_t arglen; + char *args; +}; + +struct r_rmtcall_args { + u_int32_t rmt_prog; + u_int32_t rmt_vers; + u_int32_t rmt_proc; + int rmt_localvers; /* whether to send port # or uaddr */ + char *rmt_uaddr; + struct encap_parms rmt_args; +}; + +extern int debugging; +extern int doabort; +extern int terminate_rfd; +extern volatile sig_atomic_t doterminate; +#ifdef LIBWRAP +extern int libwrap; +#endif +extern int verboselog; +extern int insecure; +extern int oldstyle_local; +extern rpcblist_ptr list_rbl; /* A list of version 3 & 4 rpcbind services */ +extern int rpcbindlockfd; + +#ifdef PORTMAP +extern struct pmaplist *list_pml; /* A list of version 2 rpcbind services */ +extern const char *udptrans; /* Name of UDP transport */ +extern const char *tcptrans; /* Name of TCP transport */ +extern const char *udp_uaddr; /* Universal UDP address */ +extern const char *tcp_uaddr; /* Universal TCP address */ +#endif + +extern const char rpcbind_superuser[]; +extern const char rpcbind_unknown[]; + +int add_bndlist(const struct netconfig *, struct netbuf *); +bool_t is_bound(const char *, const char *); +char *mergeaddr(SVCXPRT *, char *, char *, char *); +struct netconfig *rpcbind_get_conf(const char *); + +void rpcbs_init(void); +void rpcbs_procinfo(rpcvers_t, rpcproc_t); +void rpcbs_set(rpcvers_t, bool_t); +void rpcbs_unset(rpcvers_t, bool_t); +void rpcbs_getaddr(rpcvers_t, rpcprog_t, rpcvers_t, const char *, + const char *); +void rpcbs_rmtcall(rpcvers_t, rpcproc_t, rpcprog_t, rpcvers_t, rpcproc_t, + char *, rpcblist_ptr); +void *rpcbproc_getstat(void *, struct svc_req *, SVCXPRT *, rpcvers_t); + +void rpcb_service_3(struct svc_req *, SVCXPRT *); +void rpcb_service_4(struct svc_req *, SVCXPRT *); + +/* Common functions shared between versions */ +void *rpcbproc_set_com(void *, struct svc_req *, SVCXPRT *, rpcvers_t); +void *rpcbproc_unset_com(void *, struct svc_req *, SVCXPRT *, rpcvers_t); +bool_t map_set(RPCB *, const char *); +bool_t map_unset(RPCB *, const char *); +void delete_prog(unsigned int); +void *rpcbproc_getaddr_com(RPCB *, struct svc_req *, SVCXPRT *, rpcvers_t, + rpcvers_t); +void *rpcbproc_gettime_com(void *, struct svc_req *, SVCXPRT *, rpcvers_t); +void *rpcbproc_uaddr2taddr_com(void *, struct svc_req *, SVCXPRT *, rpcvers_t); +void *rpcbproc_taddr2uaddr_com(void *, struct svc_req *, SVCXPRT *, rpcvers_t); +int create_rmtcall_fd(const struct netconfig *); +void rpcbproc_callit_com(struct svc_req *, SVCXPRT *, rpcvers_t, rpcvers_t); +void my_svc_run(void); + +void rpcbind_abort(void); +void reap(int); +void toggle_verboselog(int); + +int check_access(SVCXPRT *, rpcproc_t, void *, unsigned int); +int check_callit(SVCXPRT *, struct r_rmtcall_args *, int); +void logit(int, struct sockaddr *, rpcproc_t, rpcprog_t, const char *); +int is_loopback(struct netbuf *); + +#ifdef PORTMAP +extern void pmap_service(struct svc_req *, SVCXPRT *); +#endif + +void write_warmstart(void); +void read_warmstart(void); + +char *addrmerge(struct netbuf *caller, const char *serv_uaddr, + const char *clnt_uaddr, char const *netid); +int listen_addr(const struct sockaddr *sa); +void network_init(void); +struct sockaddr *local_sa(int); + +/* For different getaddr semantics */ +#define RPCB_ALLVERS 0 +#define RPCB_ONEVERS 1 + +/* To convert a struct sockaddr to IPv4 or IPv6 address */ +#define SA2SIN(sa) ((struct sockaddr_in *)(sa)) +#define SA2SINADDR(sa) (SA2SIN(sa)->sin_addr) +#ifdef INET6 +#define SA2SIN6(sa) ((struct sockaddr_in6 *)(sa)) +#define SA2SIN6ADDR(sa) (SA2SIN6(sa)->sin6_addr) +#endif + +#define __UNCONST(a) __DECONST(void *, (a)) + +#endif /* rpcbind_h */ diff --git a/usr.sbin/rpcbind/security.c b/usr.sbin/rpcbind/security.c new file mode 100644 index 000000000000..d345ffb510d4 --- /dev/null +++ b/usr.sbin/rpcbind/security.c @@ -0,0 +1,287 @@ +/* $NetBSD: security.c,v 1.5 2000/06/08 09:01:05 fvdl Exp $ */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <rpc/rpc.h> +#include <rpc/rpcb_prot.h> +#include <rpc/pmap_prot.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <syslog.h> +#include <netdb.h> + +/* + * XXX for special case checks in check_callit. + */ +#include <rpcsvc/mount.h> +#include <rpcsvc/rquota.h> +#include <rpcsvc/nfs_prot.h> +#include <rpcsvc/yp.h> +#include <rpcsvc/ypclnt.h> +#include <rpcsvc/yppasswd.h> + +#include "rpcbind.h" + +#ifdef LIBWRAP +# include <tcpd.h> +#ifndef LIBWRAP_ALLOW_FACILITY +# define LIBWRAP_ALLOW_FACILITY LOG_AUTH +#endif +#ifndef LIBWRAP_ALLOW_SEVERITY +# define LIBWRAP_ALLOW_SEVERITY LOG_INFO +#endif +#ifndef LIBWRAP_DENY_FACILITY +# define LIBWRAP_DENY_FACILITY LOG_AUTH +#endif +#ifndef LIBWRAP_DENY_SEVERITY +# define LIBWRAP_DENY_SEVERITY LOG_WARNING +#endif +int allow_severity = LIBWRAP_ALLOW_FACILITY|LIBWRAP_ALLOW_SEVERITY; +int deny_severity = LIBWRAP_DENY_FACILITY|LIBWRAP_DENY_SEVERITY; +#endif + +#ifndef PORTMAP_LOG_FACILITY +# define PORTMAP_LOG_FACILITY LOG_AUTH +#endif +#ifndef PORTMAP_LOG_SEVERITY +# define PORTMAP_LOG_SEVERITY LOG_INFO +#endif +int log_severity = PORTMAP_LOG_FACILITY|PORTMAP_LOG_SEVERITY; + +extern int verboselog; + +int +check_access(SVCXPRT *xprt, rpcproc_t proc, void *args, unsigned int rpcbvers) +{ + struct netbuf *caller = svc_getrpccaller(xprt); + struct sockaddr *addr = (struct sockaddr *)caller->buf; +#ifdef LIBWRAP + struct request_info req; +#endif + rpcprog_t prog = 0; + rpcb *rpcbp; + struct pmap *pmap; + + /* + * The older PMAP_* equivalents have the same numbers, so + * they are accounted for here as well. + */ + switch (proc) { + case RPCBPROC_GETADDR: + case RPCBPROC_SET: + case RPCBPROC_UNSET: + if (rpcbvers > PMAPVERS) { + rpcbp = (rpcb *)args; + prog = rpcbp->r_prog; + } else { + pmap = (struct pmap *)args; + prog = pmap->pm_prog; + } + if (proc == RPCBPROC_GETADDR) + break; + if (!insecure && !is_loopback(caller)) { + if (verboselog) + logit(log_severity, addr, proc, prog, + " declined (non-loopback sender)"); + return 0; + } + break; + 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 (libwrap && addr->sa_family != AF_LOCAL) { + 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; +} + +int +is_loopback(struct netbuf *nbuf) +{ + struct sockaddr *addr = (struct sockaddr *)nbuf->buf; + struct sockaddr_in *sin; +#ifdef INET6 + struct sockaddr_in6 *sin6; +#endif + + switch (addr->sa_family) { + case AF_INET: + if (!oldstyle_local) + return 0; + sin = (struct sockaddr_in *)addr; + return ((sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) && + (ntohs(sin->sin_port) < IPPORT_RESERVED)); +#ifdef INET6 + case AF_INET6: + if (!oldstyle_local) + return 0; + sin6 = (struct sockaddr_in6 *)addr; + return (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr) && + (ntohs(sin6->sin6_port) < IPV6PORT_RESERVED)); +#endif + case AF_LOCAL: + case AF_NETLINK: + return 1; + default: + break; + } + + return 0; +} + + +/* logit - report events of interest via the syslog daemon */ +void +logit(int severity, struct sockaddr *addr, rpcproc_t procnum, rpcprog_t prognum, + const char *text) +{ + const char *procname; + char procbuf[32]; + char *progname; + char progbuf[32]; + char fromname[NI_MAXHOST]; + struct rpcent *rpc; + static const char *procmap[] = { + /* RPCBPROC_NULL */ "null", + /* RPCBPROC_SET */ "set", + /* RPCBPROC_UNSET */ "unset", + /* RPCBPROC_GETADDR */ "getport/addr", + /* RPCBPROC_DUMP */ "dump", + /* RPCBPROC_CALLIT */ "callit", + /* RPCBPROC_GETTIME */ "gettime", + /* RPCBPROC_UADDR2TADDR */ "uaddr2taddr", + /* RPCBPROC_TADDR2UADDR */ "taddr2uaddr", + /* RPCBPROC_GETVERSADDR */ "getversaddr", + /* RPCBPROC_INDIRECT */ "indirect", + /* RPCBPROC_GETADDRLIST */ "getaddrlist", + /* RPCBPROC_GETSTAT */ "getstat" + }; + + /* + * Fork off a process or the portmap daemon might hang while + * getrpcbynumber() or syslog() does its thing. + */ + + if (fork() == 0) { + setproctitle("logit"); + + /* Try to map program number to name. */ + + if (prognum == 0) { + progname = ""; + } else if ((rpc = getrpcbynumber((int) prognum))) { + progname = rpc->r_name; + } else { + snprintf(progname = progbuf, sizeof(progbuf), "%u", + (unsigned)prognum); + } + + /* Try to map procedure number to name. */ + + if (procnum >= (sizeof procmap / sizeof (char *))) { + snprintf(procbuf, sizeof procbuf, "%u", + (unsigned)procnum); + procname = procbuf; + } else + procname = procmap[procnum]; + + /* Write syslog record. */ + + if (addr->sa_family == AF_LOCAL) + strcpy(fromname, "local"); + else + getnameinfo(addr, addr->sa_len, fromname, + sizeof fromname, NULL, 0, NI_NUMERICHOST); + + syslog(severity, "connect from %s to %s(%s)%s", + fromname, procname, progname, text); + _exit(0); + } +} + +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; +} diff --git a/usr.sbin/rpcbind/tests/Makefile b/usr.sbin/rpcbind/tests/Makefile new file mode 100644 index 000000000000..f37d0433abc2 --- /dev/null +++ b/usr.sbin/rpcbind/tests/Makefile @@ -0,0 +1,23 @@ +.include <src.opts.mk> + +.PATH: ${.CURDIR}/.. + +ATF_TESTS_C= addrmerge_test +CFLAGS+= -I${.CURDIR:H} -Wno-cast-qual +SRCS.addrmerge_test= addrmerge_test.c util.c + +.if ${MK_INET6_SUPPORT} != "no" +CFLAGS+= -DINET6 +.endif + +WARNS?= 3 + +.if ${MK_ASAN} != "no" +# Work around "error: duplicate symbol: getifaddrs" when building with ASAN. +# The ASAN interceptors also define getifaddrs, but we want to prefer the local +# stub symbol here, so using a shared sanitizer runtime moves the local +# definition first in the symbol resolution order. +LDFLAGS+=-shared-libasan +.endif + +.include <bsd.test.mk> diff --git a/usr.sbin/rpcbind/tests/Makefile.depend b/usr.sbin/rpcbind/tests/Makefile.depend new file mode 100644 index 000000000000..f17dae18048f --- /dev/null +++ b/usr.sbin/rpcbind/tests/Makefile.depend @@ -0,0 +1,19 @@ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + gnu/lib/csu \ + include \ + include/arpa \ + include/rpc \ + include/xlocale \ + lib/${CSU_DIR} \ + lib/atf/libatf-c \ + lib/libc \ + lib/libcompiler_rt \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/usr.sbin/rpcbind/tests/addrmerge_test.c b/usr.sbin/rpcbind/tests/addrmerge_test.c new file mode 100644 index 000000000000..e0830bc2401b --- /dev/null +++ b/usr.sbin/rpcbind/tests/addrmerge_test.c @@ -0,0 +1,902 @@ +/*- + * Copyright (c) 2014 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include <rpc/rpc.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <ifaddrs.h> +#include <stdlib.h> + +#include <atf-c.h> + +#include "rpcbind.h" + +#define MAX_IFADDRS 16 + +int debugging = false; + +/* Data for mocking getifaddrs */ +struct ifaddr_storage { + struct ifaddrs ifaddr; + struct sockaddr_storage addr; + struct sockaddr_storage mask; + struct sockaddr_storage bcast; +} mock_ifaddr_storage[MAX_IFADDRS]; +struct ifaddrs *mock_ifaddrs = NULL; +int ifaddr_count = 0; + +/* Data for mocking listen_addr */ +int bind_address_count = 0; +struct sockaddr* bind_addresses[MAX_IFADDRS]; + +/* Stub library functions */ +void +freeifaddrs(struct ifaddrs *ifp __unused) +{ + return ; +} + +int +getifaddrs(struct ifaddrs **ifap) +{ + *ifap = mock_ifaddrs; + return (0); +} + +static void +mock_ifaddr4(const char* name, const char* addr, const char* mask, + const char* bcast, unsigned int flags, bool bind) +{ + struct ifaddrs *ifaddr = &mock_ifaddr_storage[ifaddr_count].ifaddr; + struct sockaddr_in *in = (struct sockaddr_in*) + &mock_ifaddr_storage[ifaddr_count].addr; + struct sockaddr_in *mask_in = (struct sockaddr_in*) + &mock_ifaddr_storage[ifaddr_count].mask; + struct sockaddr_in *bcast_in = (struct sockaddr_in*) + &mock_ifaddr_storage[ifaddr_count].bcast; + + in->sin_family = AF_INET; + in->sin_port = 0; + in->sin_len = sizeof(*in); + in->sin_addr.s_addr = inet_addr(addr); + mask_in->sin_family = AF_INET; + mask_in->sin_port = 0; + mask_in->sin_len = sizeof(*mask_in); + mask_in->sin_addr.s_addr = inet_addr(mask); + bcast_in->sin_family = AF_INET; + bcast_in->sin_port = 0; + bcast_in->sin_len = sizeof(*bcast_in); + bcast_in->sin_addr.s_addr = inet_addr(bcast); + *ifaddr = (struct ifaddrs) { + .ifa_next = NULL, + .ifa_name = (char*) name, + .ifa_flags = flags, + .ifa_addr = (struct sockaddr*) in, + .ifa_netmask = (struct sockaddr*) mask_in, + .ifa_broadaddr = (struct sockaddr*) bcast_in, + .ifa_data = NULL, /* addrmerge doesn't care*/ + }; + + if (ifaddr_count > 0) + mock_ifaddr_storage[ifaddr_count - 1].ifaddr.ifa_next = ifaddr; + ifaddr_count++; + mock_ifaddrs = &mock_ifaddr_storage[0].ifaddr; + + /* Optionally simulate binding an ip ala "rpcbind -h foo" */ + if (bind) { + bind_addresses[bind_address_count] = (struct sockaddr*)in; + bind_address_count++; + } +} + +#ifdef INET6 +static void +mock_ifaddr6(const char* name, const char* addr, const char* mask, + const char* bcast, unsigned int flags, uint32_t scope_id, bool bind) +{ + struct ifaddrs *ifaddr = &mock_ifaddr_storage[ifaddr_count].ifaddr; + struct sockaddr_in6 *in6 = (struct sockaddr_in6*) + &mock_ifaddr_storage[ifaddr_count].addr; + struct sockaddr_in6 *mask_in6 = (struct sockaddr_in6*) + &mock_ifaddr_storage[ifaddr_count].mask; + struct sockaddr_in6 *bcast_in6 = (struct sockaddr_in6*) + &mock_ifaddr_storage[ifaddr_count].bcast; + + in6->sin6_family = AF_INET6; + in6->sin6_port = 0; + in6->sin6_len = sizeof(*in6); + in6->sin6_scope_id = scope_id; + ATF_REQUIRE_EQ(1, inet_pton(AF_INET6, addr, (void*)&in6->sin6_addr)); + mask_in6->sin6_family = AF_INET6; + mask_in6->sin6_port = 0; + mask_in6->sin6_len = sizeof(*mask_in6); + mask_in6->sin6_scope_id = scope_id; + ATF_REQUIRE_EQ(1, inet_pton(AF_INET6, mask, + (void*)&mask_in6->sin6_addr)); + bcast_in6->sin6_family = AF_INET6; + bcast_in6->sin6_port = 0; + bcast_in6->sin6_len = sizeof(*bcast_in6); + bcast_in6->sin6_scope_id = scope_id; + ATF_REQUIRE_EQ(1, inet_pton(AF_INET6, bcast, + (void*)&bcast_in6->sin6_addr)); + *ifaddr = (struct ifaddrs) { + .ifa_next = NULL, + .ifa_name = (char*) name, + .ifa_flags = flags, + .ifa_addr = (struct sockaddr*) in6, + .ifa_netmask = (struct sockaddr*) mask_in6, + .ifa_broadaddr = (struct sockaddr*) bcast_in6, + .ifa_data = NULL, /* addrmerge doesn't care*/ + }; + + if (ifaddr_count > 0) + mock_ifaddr_storage[ifaddr_count - 1].ifaddr.ifa_next = ifaddr; + ifaddr_count++; + mock_ifaddrs = &mock_ifaddr_storage[0].ifaddr; + + /* Optionally simulate binding an ip ala "rpcbind -h foo" */ + if (bind) { + bind_addresses[bind_address_count] = (struct sockaddr*)in6; + bind_address_count++; + } +} +#else +static void +mock_ifaddr6(const char* name __unused, const char* addr __unused, + const char* mask __unused, const char* bcast __unused, + unsigned int flags __unused, uint32_t scope_id __unused, bool bind __unused) +{ +} +#endif /*INET6 */ + +static void +mock_lo0(void) +{ + /* + * This broadcast address looks wrong, but it's what getifaddrs(2) + * actually returns. It's invalid because IFF_BROADCAST is not set + */ + mock_ifaddr4("lo0", "127.0.0.1", "255.0.0.0", "127.0.0.1", + IFF_LOOPBACK | IFF_UP | IFF_RUNNING | IFF_MULTICAST, false); + mock_ifaddr6("lo0", "::1", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", + "::1", + IFF_LOOPBACK | IFF_UP | IFF_RUNNING | IFF_MULTICAST, 0, false); +} + +static void +mock_igb0(void) +{ + mock_ifaddr4("igb0", "192.0.2.2", "255.255.255.128", "192.0.2.127", + IFF_UP | IFF_BROADCAST | IFF_RUNNING | IFF_SIMPLEX | IFF_MULTICAST, + false); + mock_ifaddr6("igb0", "2001:db8::2", "ffff:ffff:ffff:ffff::", + "2001:db8::ffff:ffff:ffff:ffff", + IFF_UP | IFF_BROADCAST | IFF_RUNNING | IFF_SIMPLEX | IFF_MULTICAST, + 0, false); + /* Link local address */ + mock_ifaddr6("igb0", "fe80::2", "ffff:ffff:ffff:ffff::", + "fe80::ffff:ffff:ffff:ffff", + IFF_UP | IFF_BROADCAST | IFF_RUNNING | IFF_SIMPLEX | IFF_MULTICAST, + 2, false); +} + +/* On the same subnet as igb0 */ +static void +mock_igb1(bool bind) +{ + mock_ifaddr4("igb1", "192.0.2.3", "255.255.255.128", "192.0.2.127", + IFF_UP | IFF_BROADCAST | IFF_RUNNING | IFF_SIMPLEX | IFF_MULTICAST, + bind); + mock_ifaddr6("igb1", "2001:db8::3", "ffff:ffff:ffff:ffff::", + "2001:db8::ffff:ffff:ffff:ffff", + IFF_UP | IFF_BROADCAST | IFF_RUNNING | IFF_SIMPLEX | IFF_MULTICAST, + 0, bind); + /* Link local address */ + mock_ifaddr6("igb1", "fe80::3", "ffff:ffff:ffff:ffff::", + "fe80::ffff:ffff:ffff:ffff", + IFF_UP | IFF_BROADCAST | IFF_RUNNING | IFF_SIMPLEX | IFF_MULTICAST, + 3, bind); +} + +/* igb2 is on a different subnet than igb0 */ +static void +mock_igb2(void) +{ + mock_ifaddr4("igb2", "192.0.2.130", "255.255.255.128", "192.0.2.255", + IFF_UP | IFF_BROADCAST | IFF_RUNNING | IFF_SIMPLEX | IFF_MULTICAST, + false); + mock_ifaddr6("igb2", "2001:db8:1::2", "ffff:ffff:ffff:ffff::", + "2001:db8:1:0:ffff:ffff:ffff:ffff", + IFF_UP | IFF_BROADCAST | IFF_RUNNING | IFF_SIMPLEX | IFF_MULTICAST, + 0, false); +} + +/* tun0 is a P2P interface */ +static void +mock_tun0(void) +{ + mock_ifaddr4("tun0", "192.0.2.5", "255.255.255.255", "192.0.2.6", + IFF_UP | IFF_RUNNING | IFF_POINTOPOINT | IFF_MULTICAST, false); + mock_ifaddr6("tun0", "2001:db8::5", + "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", + "2001:db8::6", + IFF_UP | IFF_RUNNING | IFF_POINTOPOINT | IFF_MULTICAST, 0, false); +} + +static void +mock_mlxen0(void) +{ + mock_ifaddr4("mlxen0", "192.0.3.1", "255.255.255.128", "192.0.3.127", + IFF_UP | IFF_BROADCAST | IFF_RUNNING | IFF_SIMPLEX | IFF_MULTICAST, + false); + /* Setting link local address before ipv6 address*/ + mock_ifaddr6("mlxen0", "fe80::4", "ffff:ffff:ffff:ffff::", + "fe80::ffff:ffff:ffff:ffff", + IFF_UP | IFF_BROADCAST | IFF_RUNNING | IFF_SIMPLEX | IFF_MULTICAST, + 3, false); + mock_ifaddr6("mlxen0", "2001:db8::7", "ffff:ffff:ffff:ffff::", + "2001:db8::ffff:ffff:ffff:ffff", + IFF_UP | IFF_BROADCAST | IFF_RUNNING | IFF_SIMPLEX | IFF_MULTICAST, + 0, false); +} + +/* Stub rpcbind functions */ +int +listen_addr(const struct sockaddr *sa) +{ + int i; + + if (bind_address_count == 0) + return (1); + + for (i = 0; i < bind_address_count; i++) { + if (bind_addresses[i]->sa_family != sa->sa_family) + continue; + + if (0 == memcmp(bind_addresses[i]->sa_data, sa->sa_data, + sa->sa_len)) + return (1); + } + return (0); +} + +struct netconfig* +rpcbind_get_conf(const char* netid __unused) +{ + /* Use static variables so we can return pointers to them */ + static char* lookups = NULL; + static struct netconfig nconf_udp; +#ifdef INET6 + static struct netconfig nconf_udp6; +#endif /* INET6 */ + + nconf_udp.nc_netid = "udp"; //netid_storage; + nconf_udp.nc_semantics = NC_TPI_CLTS; + nconf_udp.nc_flag = NC_VISIBLE; + nconf_udp.nc_protofmly = (char*)"inet"; + nconf_udp.nc_proto = (char*)"udp"; + nconf_udp.nc_device = (char*)"-"; + nconf_udp.nc_nlookups = 0; + nconf_udp.nc_lookups = &lookups; + +#ifdef INET6 + nconf_udp6.nc_netid = "udp6"; //netid_storage; + nconf_udp6.nc_semantics = NC_TPI_CLTS; + nconf_udp6.nc_flag = NC_VISIBLE; + nconf_udp6.nc_protofmly = (char*)"inet6"; + nconf_udp6.nc_proto = (char*)"udp6"; + nconf_udp6.nc_device = (char*)"-"; + nconf_udp6.nc_nlookups = 0; + nconf_udp6.nc_lookups = &lookups; +#endif /* INET6 */ + + if (0 == strncmp("udp", netid, sizeof("udp"))) + return (&nconf_udp); +#ifdef INET6 + else if (0 == strncmp("udp6", netid, sizeof("udp6"))) + return (&nconf_udp6); +#endif /* INET6 */ + else + return (NULL); +} + +/* + * Helper function used by most test cases + * param recvdstaddr If non-null, the uaddr on which the request was received + */ +static char* +do_addrmerge4(const char* recvdstaddr) +{ + struct netbuf caller; + struct sockaddr_in caller_in; + const char *serv_uaddr, *clnt_uaddr, *netid; + + /* caller contains the client's IP address */ + caller.maxlen = sizeof(struct sockaddr_storage); + caller.len = sizeof(caller_in); + caller_in.sin_family = AF_INET; + caller_in.sin_len = sizeof(caller_in); + caller_in.sin_port = 1234; + caller_in.sin_addr.s_addr = inet_addr("192.0.2.1"); + caller.buf = (void*)&caller_in; + if (recvdstaddr != NULL) + clnt_uaddr = recvdstaddr; + else + clnt_uaddr = "192.0.2.1.3.46"; + + /* assume server is bound in INADDR_ANY port 814 */ + serv_uaddr = "0.0.0.0.3.46"; + + netid = "udp"; + return (addrmerge(&caller, serv_uaddr, clnt_uaddr, netid)); +} + +#ifdef INET6 +/* + * Variant of do_addrmerge4 where the caller has an IPv6 address + * param recvdstaddr If non-null, the uaddr on which the request was received + */ +static char* +do_addrmerge6(const char* recvdstaddr) +{ + struct netbuf caller; + struct sockaddr_in6 caller_in6; + const char *serv_uaddr, *clnt_uaddr, *netid; + + /* caller contains the client's IP address */ + caller.maxlen = sizeof(struct sockaddr_storage); + caller.len = sizeof(caller_in6); + caller_in6.sin6_family = AF_INET6; + caller_in6.sin6_len = sizeof(caller_in6); + caller_in6.sin6_port = 1234; + ATF_REQUIRE_EQ(1, inet_pton(AF_INET6, "2001:db8::1", + (void*)&caller_in6.sin6_addr)); + caller.buf = (void*)&caller_in6; + if (recvdstaddr != NULL) + clnt_uaddr = recvdstaddr; + else + clnt_uaddr = "2001:db8::1.3.46"; + + /* assume server is bound in INADDR_ANY port 814 */ + serv_uaddr = "::1.3.46"; + + netid = "udp6"; + return (addrmerge(&caller, serv_uaddr, clnt_uaddr, netid)); +} + +/* Variant of do_addrmerge6 where the caller uses a link local address */ +static char* +do_addrmerge6_ll(void) +{ + struct netbuf caller; + struct sockaddr_in6 caller_in6; + const char *serv_uaddr, *clnt_uaddr, *netid; + + /* caller contains the client's IP address */ + caller.maxlen = sizeof(struct sockaddr_storage); + caller.len = sizeof(caller_in6); + caller_in6.sin6_family = AF_INET6; + caller_in6.sin6_len = sizeof(caller_in6); + caller_in6.sin6_port = 1234; + caller_in6.sin6_scope_id = 2; /* same as igb0 */ + ATF_REQUIRE_EQ(1, inet_pton(AF_INET6, "fe80::beef", + (void*)&caller_in6.sin6_addr)); + caller.buf = (void*)&caller_in6; + clnt_uaddr = "fe80::beef.3.46"; + + /* assume server is bound in INADDR_ANY port 814 */ + serv_uaddr = "::1.3.46"; + + netid = "udp6"; + return (addrmerge(&caller, serv_uaddr, clnt_uaddr, netid)); +} +#endif /* INET6 */ + +ATF_TC_WITHOUT_HEAD(addrmerge_noifaddrs); +ATF_TC_BODY(addrmerge_noifaddrs, tc) +{ + char* maddr; + + maddr = do_addrmerge4(NULL); + + /* Since getifaddrs returns null, addrmerge must too */ + ATF_CHECK_EQ(NULL, maddr); +} + +ATF_TC_WITHOUT_HEAD(addrmerge_localhost_only); +ATF_TC_BODY(addrmerge_localhost_only, tc) +{ + char *maddr; + + /* getifaddrs will return localhost only */ + mock_lo0(); + + maddr = do_addrmerge4(NULL); + + /* We must return localhost if there is nothing better */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("127.0.0.1.3.46", maddr); + free(maddr); +} + +ATF_TC_WITHOUT_HEAD(addrmerge_singlehomed); +ATF_TC_BODY(addrmerge_singlehomed, tc) +{ + char *maddr; + + /* getifaddrs will return one public address */ + mock_lo0(); + mock_igb0(); + + maddr = do_addrmerge4(NULL); + + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("192.0.2.2.3.46", maddr); + free(maddr); +} + +ATF_TC_WITHOUT_HEAD(addrmerge_one_addr_on_each_subnet); +ATF_TC_BODY(addrmerge_one_addr_on_each_subnet, tc) +{ + char *maddr; + + mock_lo0(); + mock_igb0(); + mock_igb2(); + + maddr = do_addrmerge4(NULL); + + /* We must return the address on the caller's subnet */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("192.0.2.2.3.46", maddr); + free(maddr); +} + + +/* + * Like addrmerge_one_addr_on_each_subnet, but getifaddrs returns a different + * order + */ +ATF_TC_WITHOUT_HEAD(addrmerge_one_addr_on_each_subnet_rev); +ATF_TC_BODY(addrmerge_one_addr_on_each_subnet_rev, tc) +{ + char *maddr; + + /* getifaddrs will return one public address on each of two subnets */ + mock_igb2(); + mock_igb0(); + mock_lo0(); + + maddr = do_addrmerge4(NULL); + + /* We must return the address on the caller's subnet */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("192.0.2.2.3.46", maddr); + free(maddr); +} + +ATF_TC_WITHOUT_HEAD(addrmerge_point2point); +ATF_TC_BODY(addrmerge_point2point, tc) +{ + char *maddr; + + /* getifaddrs will return one normal and one p2p address */ + mock_lo0(); + mock_igb2(); + mock_tun0(); + + maddr = do_addrmerge4(NULL); + + /* addrmerge should disprefer P2P interfaces */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("192.0.2.130.3.46", maddr); + free(maddr); +} + +/* Like addrerge_point2point, but getifaddrs returns a different order */ +ATF_TC_WITHOUT_HEAD(addrmerge_point2point_rev); +ATF_TC_BODY(addrmerge_point2point_rev, tc) +{ + char *maddr; + + /* getifaddrs will return one normal and one p2p address */ + mock_tun0(); + mock_igb2(); + mock_lo0(); + + maddr = do_addrmerge4(NULL); + + /* addrmerge should disprefer P2P interfaces */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("192.0.2.130.3.46", maddr); + free(maddr); +} + +/* + * Simulate using rpcbind -h to select just one ip when the subnet has + * multiple + */ +ATF_TC_WITHOUT_HEAD(addrmerge_bindip); +ATF_TC_BODY(addrmerge_bindip, tc) +{ + char *maddr; + + /* getifaddrs will return one public address on each of two subnets */ + mock_lo0(); + mock_igb0(); + mock_igb1(true); + + maddr = do_addrmerge4(NULL); + + /* We must return the address to which we are bound */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("192.0.2.3.3.46", maddr); + free(maddr); +} + +/* Like addrmerge_bindip, but getifaddrs returns a different order */ +ATF_TC_WITHOUT_HEAD(addrmerge_bindip_rev); +ATF_TC_BODY(addrmerge_bindip_rev, tc) +{ + char *maddr; + + /* getifaddrs will return one public address on each of two subnets */ + mock_igb1(true); + mock_igb0(); + mock_lo0(); + + maddr = do_addrmerge4(NULL); + + /* We must return the address to which we are bound */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("192.0.2.3.3.46", maddr); + free(maddr); +} + +/* + * The address on which the request was received is known, and is provided as + * the hint. + */ +ATF_TC_WITHOUT_HEAD(addrmerge_recvdstaddr); +ATF_TC_BODY(addrmerge_recvdstaddr, tc) +{ + char *maddr; + + mock_lo0(); + mock_igb0(); + mock_igb1(false); + + maddr = do_addrmerge4("192.0.2.2.3.46"); + + /* We must return the address on which the request was received */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("192.0.2.2.3.46", maddr); + free(maddr); +} + +ATF_TC_WITHOUT_HEAD(addrmerge_recvdstaddr_rev); +ATF_TC_BODY(addrmerge_recvdstaddr_rev, tc) +{ + char *maddr; + + mock_igb1(false); + mock_igb0(); + mock_lo0(); + + maddr = do_addrmerge4("192.0.2.2.3.46"); + + /* We must return the address on which the request was received */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("192.0.2.2.3.46", maddr); + free(maddr); +} + +#ifdef INET6 +ATF_TC_WITHOUT_HEAD(addrmerge_localhost_only6); +ATF_TC_BODY(addrmerge_localhost_only6, tc) +{ + char *maddr; + + /* getifaddrs will return localhost only */ + mock_lo0(); + + maddr = do_addrmerge6(NULL); + + /* We must return localhost if there is nothing better */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("::1.3.46", maddr); + free(maddr); +} + +ATF_TC_WITHOUT_HEAD(addrmerge_singlehomed6); +ATF_TC_BODY(addrmerge_singlehomed6, tc) +{ + char *maddr; + + /* getifaddrs will return one public address */ + mock_lo0(); + mock_igb0(); + + maddr = do_addrmerge6(NULL); + + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("2001:db8::2.3.46", maddr); + free(maddr); +} + +ATF_TC_WITHOUT_HEAD(addrmerge_one_addr_on_each_subnet6); +ATF_TC_BODY(addrmerge_one_addr_on_each_subnet6, tc) +{ + char *maddr; + + mock_lo0(); + mock_igb0(); + mock_igb2(); + + maddr = do_addrmerge6(NULL); + + /* We must return the address on the caller's subnet */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("2001:db8::2.3.46", maddr); + free(maddr); +} + + +/* + * Like addrmerge_one_addr_on_each_subnet6, but getifaddrs returns a different + * order + */ +ATF_TC_WITHOUT_HEAD(addrmerge_one_addr_on_each_subnet6_rev); +ATF_TC_BODY(addrmerge_one_addr_on_each_subnet6_rev, tc) +{ + char *maddr; + + /* getifaddrs will return one public address on each of two subnets */ + mock_igb2(); + mock_igb0(); + mock_lo0(); + + maddr = do_addrmerge6(NULL); + + /* We must return the address on the caller's subnet */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("2001:db8::2.3.46", maddr); + free(maddr); +} + +ATF_TC_WITHOUT_HEAD(addrmerge_point2point6); +ATF_TC_BODY(addrmerge_point2point6, tc) +{ + char *maddr; + + /* getifaddrs will return one normal and one p2p address */ + mock_lo0(); + mock_igb2(); + mock_tun0(); + + maddr = do_addrmerge6(NULL); + + /* addrmerge should disprefer P2P interfaces */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("2001:db8:1::2.3.46", maddr); + free(maddr); +} + +/* Like addrerge_point2point, but getifaddrs returns a different order */ +ATF_TC_WITHOUT_HEAD(addrmerge_point2point6_rev); +ATF_TC_BODY(addrmerge_point2point6_rev, tc) +{ + char *maddr; + + /* getifaddrs will return one normal and one p2p address */ + mock_tun0(); + mock_igb2(); + mock_lo0(); + + maddr = do_addrmerge6(NULL); + + /* addrmerge should disprefer P2P interfaces */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("2001:db8:1::2.3.46", maddr); + free(maddr); +} + +ATF_TC_WITHOUT_HEAD(addrmerge_bindip6); +ATF_TC_BODY(addrmerge_bindip6, tc) +{ + char *maddr; + + /* getifaddrs will return one public address on each of two subnets */ + mock_lo0(); + mock_igb0(); + mock_igb1(true); + + maddr = do_addrmerge6(NULL); + + /* We must return the address to which we are bound */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("2001:db8::3.3.46", maddr); + free(maddr); +} + +/* Like addrerge_bindip, but getifaddrs returns a different order */ +ATF_TC_WITHOUT_HEAD(addrmerge_bindip6_rev); +ATF_TC_BODY(addrmerge_bindip6_rev, tc) +{ + char *maddr; + + /* getifaddrs will return one public address on each of two subnets */ + mock_igb1(true); + mock_igb0(); + mock_lo0(); + + maddr = do_addrmerge6(NULL); + + /* We must return the address to which we are bound */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("2001:db8::3.3.46", maddr); + free(maddr); +} + +/* + * IPv6 Link Local addresses with the same scope id as the caller, if the caller + * is also a link local address, should be preferred + */ +ATF_TC_WITHOUT_HEAD(addrmerge_ipv6_linklocal); +ATF_TC_BODY(addrmerge_ipv6_linklocal, tc) +{ + char *maddr; + + /* + * getifaddrs will return two link local addresses with the same netmask + * and prefix but different scope IDs + */ + mock_igb1(false); + mock_igb0(); + mock_lo0(); + + maddr = do_addrmerge6_ll(); + + /* We must return the address to which we are bound */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("fe80::2.3.46", maddr); + free(maddr); +} + +ATF_TC_WITHOUT_HEAD(addrmerge_ipv6_linklocal_rev); +ATF_TC_BODY(addrmerge_ipv6_linklocal_rev, tc) +{ + char *maddr; + + /* + * getifaddrs will return two link local addresses with the same netmask + * and prefix but different scope IDs + */ + mock_lo0(); + mock_igb0(); + mock_igb1(false); + + maddr = do_addrmerge6_ll(); + + /* We must return the address to which we are bound */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("fe80::2.3.46", maddr); + free(maddr); +} + +ATF_TC_WITHOUT_HEAD(addrmerge_recvdstaddr6); +ATF_TC_BODY(addrmerge_recvdstaddr6, tc) +{ + char *maddr; + + mock_lo0(); + mock_igb0(); + mock_igb1(false); + + maddr = do_addrmerge6("2001:db8::2.3.46"); + + /* We must return the address on which the request was received */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("2001:db8::2.3.46", maddr); + free(maddr); +} + +ATF_TC_WITHOUT_HEAD(addrmerge_recvdstaddr6_rev); +ATF_TC_BODY(addrmerge_recvdstaddr6_rev, tc) +{ + char *maddr; + + mock_igb1(false); + mock_igb0(); + mock_lo0(); + + maddr = do_addrmerge6("2001:db8::2.3.46"); + + /* We must return the address on which the request was received */ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("2001:db8::2.3.46", maddr); + free(maddr); +} + +ATF_TC_WITHOUT_HEAD(addrmerge_ipv6_other_subnet); +ATF_TC_BODY(addrmerge_ipv6_other_subnet, tc) +{ + char *maddr; + + /* getifaddrs will return link local before normal ipv6 */ + mock_lo0(); + mock_mlxen0(); + + maddr = do_addrmerge6("2001:db8:1::1.3.46"); + + /* We must return the closest ipv6 address*/ + ATF_REQUIRE(maddr != NULL); + ATF_CHECK_STREQ("2001:db8::7.3.46", maddr); + free(maddr); +} +#endif /* INET6 */ + + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, addrmerge_noifaddrs); + ATF_TP_ADD_TC(tp, addrmerge_localhost_only); + ATF_TP_ADD_TC(tp, addrmerge_singlehomed); + ATF_TP_ADD_TC(tp, addrmerge_one_addr_on_each_subnet); + ATF_TP_ADD_TC(tp, addrmerge_one_addr_on_each_subnet_rev); + ATF_TP_ADD_TC(tp, addrmerge_point2point); + ATF_TP_ADD_TC(tp, addrmerge_point2point_rev); + ATF_TP_ADD_TC(tp, addrmerge_bindip); + ATF_TP_ADD_TC(tp, addrmerge_bindip_rev); + ATF_TP_ADD_TC(tp, addrmerge_recvdstaddr); + ATF_TP_ADD_TC(tp, addrmerge_recvdstaddr_rev); +#ifdef INET6 + ATF_TP_ADD_TC(tp, addrmerge_localhost_only6); + ATF_TP_ADD_TC(tp, addrmerge_singlehomed6); + ATF_TP_ADD_TC(tp, addrmerge_one_addr_on_each_subnet6); + ATF_TP_ADD_TC(tp, addrmerge_one_addr_on_each_subnet6_rev); + ATF_TP_ADD_TC(tp, addrmerge_point2point6); + ATF_TP_ADD_TC(tp, addrmerge_point2point6_rev); + ATF_TP_ADD_TC(tp, addrmerge_bindip6); + ATF_TP_ADD_TC(tp, addrmerge_bindip6_rev); + ATF_TP_ADD_TC(tp, addrmerge_ipv6_linklocal); + ATF_TP_ADD_TC(tp, addrmerge_ipv6_linklocal_rev); + ATF_TP_ADD_TC(tp, addrmerge_recvdstaddr6); + ATF_TP_ADD_TC(tp, addrmerge_recvdstaddr6_rev); + ATF_TP_ADD_TC(tp, addrmerge_ipv6_other_subnet); +#endif + + return (atf_no_error()); +} diff --git a/usr.sbin/rpcbind/util.c b/usr.sbin/rpcbind/util.c new file mode 100644 index 000000000000..491c3e53b9e5 --- /dev/null +++ b/usr.sbin/rpcbind/util.c @@ -0,0 +1,418 @@ +/* + * $NetBSD: util.c,v 1.4 2000/08/03 00:04:30 fvdl Exp $ + */ + +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Frank van der Linden. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/queue.h> +#include <net/if.h> +#include <netinet/in.h> +#include <ifaddrs.h> +#include <sys/poll.h> +#include <rpc/rpc.h> +#include <stdlib.h> +#include <string.h> +#include <netdb.h> +#include <netconfig.h> +#include <stdio.h> +#include <arpa/inet.h> + +#include "rpcbind.h" + +static struct sockaddr_in *local_in4; +#ifdef INET6 +static struct sockaddr_in6 *local_in6; +#endif + +static int bitmaskcmp(struct sockaddr *, struct sockaddr *, struct sockaddr *); + +/* + * For all bits set in "mask", compare the corresponding bits in + * "dst" and "src", and see if they match. Returns 0 if the addresses + * match. + */ +static int +bitmaskcmp(struct sockaddr *dst, struct sockaddr *src, struct sockaddr *mask) +{ + int i; + u_int8_t *p1, *p2, *netmask; + int bytelen; + + if (dst->sa_family != src->sa_family || + dst->sa_family != mask->sa_family) + return (1); + + switch (dst->sa_family) { + case AF_INET: + p1 = (uint8_t*) &SA2SINADDR(dst); + p2 = (uint8_t*) &SA2SINADDR(src); + netmask = (uint8_t*) &SA2SINADDR(mask); + bytelen = sizeof(struct in_addr); + break; +#ifdef INET6 + case AF_INET6: + p1 = (uint8_t*) &SA2SIN6ADDR(dst); + p2 = (uint8_t*) &SA2SIN6ADDR(src); + netmask = (uint8_t*) &SA2SIN6ADDR(mask); + bytelen = sizeof(struct in6_addr); + break; +#endif + default: + return (1); + } + + for (i = 0; i < bytelen; i++) + if ((p1[i] & netmask[i]) != (p2[i] & netmask[i])) + return (1); + return (0); +} + +/* + * Find a server address that can be used by `caller' to contact + * the local service specified by `serv_uaddr'. If `contct_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 *contct_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; +#ifdef ND_DEBUG + const char *hint_uaddr = NULL; +#endif + char *ret = NULL; + int bestif_goodness; + +#ifdef ND_DEBUG + if (debugging) + fprintf(stderr, "addrmerge(caller, %s, %s, %s\n", serv_uaddr, + contct_uaddr == NULL ? "NULL" : contct_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 `contct_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 (contct_uaddr != NULL) { +#ifdef ND_DEBUG + hint_uaddr = contct_uaddr; +#endif + if ((hint_nbp = uaddr2taddr(nconf, contct_uaddr)) == NULL) + goto freeit; + hint_sa = hint_nbp->buf; + } + if (hint_sa == NULL || hint_sa->sa_family != caller_sa->sa_family) { +#ifdef ND_DEBUG + hint_uaddr = caller_uaddr; +#endif + 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))) { + if (IN6_IS_ADDR_LINKLOCAL(&SA2SIN6ADDR(caller_sa)) && + IN6_IS_ADDR_LINKLOCAL(&SA2SIN6ADDR(hint_sa)) && + (SA2SIN6(ifsa)->sin6_scope_id == + SA2SIN6(caller_sa)->sin6_scope_id)) { + const int goodness = 3; + + if (bestif_goodness < goodness) { + bestif = ifap; + bestif_goodness = goodness; + } + } else { + continue; + } + } + } +#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: + 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; +} + +void +network_init(void) +{ +#ifdef INET6 + struct ifaddrs *ifap, *ifp; + struct ipv6_mreq mreq6; + unsigned int ifindex; + int s; +#endif + int ecode; + struct addrinfo hints, *res; + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_INET; + if ((ecode = getaddrinfo(NULL, "sunrpc", &hints, &res))) { + if (debugging) + fprintf(stderr, "can't get local ip4 address: %s\n", + gai_strerror(ecode)); + } else { + local_in4 = (struct sockaddr_in *)malloc(sizeof *local_in4); + if (local_in4 == NULL) { + if (debugging) + fprintf(stderr, "can't alloc local ip4 addr\n"); + exit(1); + } + memcpy(local_in4, res->ai_addr, sizeof *local_in4); + freeaddrinfo(res); + } + +#ifdef INET6 + hints.ai_family = AF_INET6; + if ((ecode = getaddrinfo(NULL, "sunrpc", &hints, &res))) { + if (debugging) + fprintf(stderr, "can't get local ip6 address: %s\n", + gai_strerror(ecode)); + } else { + local_in6 = (struct sockaddr_in6 *)malloc(sizeof *local_in6); + if (local_in6 == NULL) { + if (debugging) + fprintf(stderr, "can't alloc local ip6 addr\n"); + exit(1); + } + memcpy(local_in6, res->ai_addr, sizeof *local_in6); + freeaddrinfo(res); + } + + /* + * Now join the RPC ipv6 multicast group on all interfaces. + */ + if (getifaddrs(&ifp) < 0) + return; + + mreq6.ipv6mr_interface = 0; + inet_pton(AF_INET6, RPCB_MULTICAST_ADDR, &mreq6.ipv6mr_multiaddr); + + s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (s == -1) { + if (debugging) + fprintf(stderr, "couldn't create ip6 socket"); + goto done_inet6; + } + + /* + * Loop through all interfaces. For each IPv6 multicast-capable + * interface, join the RPC multicast group on that interface. + */ + for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) { + if (ifap->ifa_addr->sa_family != AF_INET6 || + !(ifap->ifa_flags & IFF_MULTICAST)) + continue; + ifindex = if_nametoindex(ifap->ifa_name); + if (ifindex == mreq6.ipv6mr_interface) + /* + * Already did this one. + */ + continue; + mreq6.ipv6mr_interface = ifindex; + if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6, + sizeof mreq6) < 0) + if (debugging) + perror("setsockopt v6 multicast"); + } +done_inet6: + freeifaddrs(ifp); +#endif + + /* close(s); */ +} + +struct sockaddr * +local_sa(int af) +{ + switch (af) { + case AF_INET: + return (struct sockaddr *)local_in4; +#ifdef INET6 + case AF_INET6: + return (struct sockaddr *)local_in6; +#endif + default: + return NULL; + } +} diff --git a/usr.sbin/rpcbind/warmstart.c b/usr.sbin/rpcbind/warmstart.c new file mode 100644 index 000000000000..e3c075f64a08 --- /dev/null +++ b/usr.sbin/rpcbind/warmstart.c @@ -0,0 +1,178 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 2009, Sun Microsystems, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of Sun Microsystems, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * warmstart.c + * Allows for gathering of registrations from an earlier dumped file. + * + * Copyright (c) 1990 by Sun Microsystems, Inc. + */ + +/* + */ +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <rpc/rpc.h> +#include <rpc/rpcb_prot.h> +#include <rpc/xdr.h> +#ifdef PORTMAP +#include <netinet/in.h> +#include <rpc/pmap_prot.h> +#endif +#include <syslog.h> +#include <unistd.h> + +#include "rpcbind.h" + +/* + * XXX this code is unsafe and is not used. It should be made safe. + */ + + +/* These files keep the pmap_list and rpcb_list in XDR format */ +#define RPCBFILE "/tmp/rpcbind.file" +#ifdef PORTMAP +#define PMAPFILE "/tmp/portmap.file" +#endif + +static bool_t write_struct(char *, xdrproc_t, void *); +static bool_t read_struct(char *, xdrproc_t, void *); + +static bool_t +write_struct(char *filename, xdrproc_t structproc, void *list) +{ + FILE *fp; + XDR xdrs; + mode_t omask; + + omask = umask(077); + fp = fopen(filename, "w"); + if (fp == NULL) { + int i; + + for (i = 0; i < 10; i++) + close(i); + fp = fopen(filename, "w"); + if (fp == NULL) { + syslog(LOG_ERR, + "cannot open file = %s for writing", filename); + syslog(LOG_ERR, "cannot save any registration"); + return (FALSE); + } + } + (void)umask(omask); + xdrstdio_create(&xdrs, fp, XDR_ENCODE); + + if (structproc(&xdrs, list) == FALSE) { + syslog(LOG_ERR, "rpcbind: xdr_%s: failed", filename); + fclose(fp); + return (FALSE); + } + XDR_DESTROY(&xdrs); + fclose(fp); + return (TRUE); +} + +static bool_t +read_struct(char *filename, xdrproc_t structproc, void *list) +{ + FILE *fp; + XDR xdrs; + struct stat sbuf; + + if (stat(filename, &sbuf) != 0) { + fprintf(stderr, + "rpcbind: cannot stat file = %s for reading\n", filename); + goto error; + } + if ((sbuf.st_uid != 0) || (sbuf.st_mode & S_IRWXG) || + (sbuf.st_mode & S_IRWXO)) { + fprintf(stderr, + "rpcbind: invalid permissions on file = %s for reading\n", + filename); + goto error; + } + fp = fopen(filename, "r"); + if (fp == NULL) { + fprintf(stderr, + "rpcbind: cannot open file = %s for reading\n", filename); + goto error; + } + xdrstdio_create(&xdrs, fp, XDR_DECODE); + + if (structproc(&xdrs, list) == FALSE) { + fprintf(stderr, "rpcbind: xdr_%s: failed\n", filename); + fclose(fp); + goto error; + } + XDR_DESTROY(&xdrs); + fclose(fp); + return (TRUE); + +error: fprintf(stderr, "rpcbind: will start from scratch\n"); + return (FALSE); +} + +void +write_warmstart(void) +{ + (void)write_struct(RPCBFILE, (xdrproc_t)xdr_rpcblist_ptr, &list_rbl); +#ifdef PORTMAP + (void)write_struct(PMAPFILE, (xdrproc_t)xdr_pmaplist_ptr, &list_pml); +#endif + +} + +void +read_warmstart(void) +{ + rpcblist_ptr tmp_rpcbl = NULL; +#ifdef PORTMAP + struct pmaplist *tmp_pmapl = NULL; +#endif + int ok1, ok2 = TRUE; + + ok1 = read_struct(RPCBFILE, (xdrproc_t)xdr_rpcblist_ptr, &tmp_rpcbl); + if (ok1 == FALSE) + return; +#ifdef PORTMAP + ok2 = read_struct(PMAPFILE, (xdrproc_t)xdr_pmaplist_ptr, &tmp_pmapl); +#endif + if (ok2 == FALSE) { + xdr_free((xdrproc_t)xdr_rpcblist_ptr, &tmp_rpcbl); + return; + } + xdr_free((xdrproc_t)xdr_rpcblist_ptr, &list_rbl); + list_rbl = tmp_rpcbl; +#ifdef PORTMAP + xdr_free((xdrproc_t)xdr_pmaplist_ptr, &list_pml); + list_pml = tmp_pmapl; +#endif +} |
