From owner-svn-src-all@FreeBSD.ORG Tue Feb 9 18:10:56 2010 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 9D702106568D; Tue, 9 Feb 2010 18:10:56 +0000 (UTC) (envelope-from imp@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 8C5AE8FC1B; Tue, 9 Feb 2010 18:10:56 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id o19IAuiR042483; Tue, 9 Feb 2010 18:10:56 GMT (envelope-from imp@svn.freebsd.org) Received: (from imp@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id o19IAuiw042479; Tue, 9 Feb 2010 18:10:56 GMT (envelope-from imp@svn.freebsd.org) Message-Id: <201002091810.o19IAuiw042479@svn.freebsd.org> From: Warner Losh Date: Tue, 9 Feb 2010 18:10:56 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r203710 - head/usr.sbin/rpcbind X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 09 Feb 2010 18:10:56 -0000 Author: imp Date: Tue Feb 9 18:10:56 2010 New Revision: 203710 URL: http://svn.freebsd.org/changeset/base/203710 Log: When you have multiple addresses on the same network on different interfaces (such as when you are part of a carp pool), and you run rpcbind -h to restrict which interfaces have rpc services, rpcbind can none-the-less return addresses that aren't in the -h list. This patch enforces the rule that when you specify -h on the command line, then services returned from rpcbind must be to one of the addresses listed in -h, or be a loopback address (since localhost is implicit when running -h). The root cause of this is the assumption in addrmerge that there can be only one interface that matches a given network IP address. This turns out not to be the case. To retain historical behavior, I didn't try to fix the routine to prefer the address that the request came into, since I didn't know the side effects that might cause in the normal case. My quick analysis suggests that it wouldn't be a problem, but since this code is tricky I opted for the more conservative patch of only restricting the reply when -h is in effect. Hence, this change will have no effect when you are running rpcbind without -h. Reviewed by: alfred@ Sponsored by: iX Systems MFC after: 2 weeks Modified: head/usr.sbin/rpcbind/rpcbind.c head/usr.sbin/rpcbind/rpcbind.h head/usr.sbin/rpcbind/util.c Modified: head/usr.sbin/rpcbind/rpcbind.c ============================================================================== --- head/usr.sbin/rpcbind/rpcbind.c Tue Feb 9 17:29:04 2010 (r203709) +++ head/usr.sbin/rpcbind/rpcbind.c Tue Feb 9 18:10:56 2010 (r203710) @@ -92,6 +92,7 @@ int oldstyle_local = 0; int verboselog = 0; char **hosts = NULL; +struct sockaddr **bound_sa; int ipv6_only = 0; int nhosts = 0; int on = 1; @@ -119,6 +120,7 @@ static void rbllist_add(rpcprog_t, rpcve struct netbuf *); static void terminate(int); static void parseargs(int, char *[]); +static void update_bound_sa(void); int main(int argc, char *argv[]) @@ -130,6 +132,8 @@ main(int argc, char *argv[]) parseargs(argc, argv); + update_bound_sa(); + /* Check that another rpcbind isn't already running. */ if ((rpcbindlockfd = (open(RPCBINDDLOCK, O_RDONLY|O_CREAT, 0444))) == -1) @@ -323,8 +327,7 @@ init_transport(struct netconfig *nconf) * If no hosts were specified, just bind to INADDR_ANY. * Otherwise make sure 127.0.0.1 is added to the list. */ - nhostsbak = nhosts; - nhostsbak++; + nhostsbak = nhosts + 1; hosts = realloc(hosts, nhostsbak * sizeof(char *)); if (nhostsbak == 1) hosts[0] = "*"; @@ -657,6 +660,75 @@ error: 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, struct netconfig *nconf, struct netbuf *addr) Modified: head/usr.sbin/rpcbind/rpcbind.h ============================================================================== --- head/usr.sbin/rpcbind/rpcbind.h Tue Feb 9 17:29:04 2010 (r203709) +++ head/usr.sbin/rpcbind/rpcbind.h Tue Feb 9 18:10:56 2010 (r203710) @@ -134,6 +134,7 @@ void read_warmstart(void); char *addrmerge(struct netbuf *caller, char *serv_uaddr, char *clnt_uaddr, char *netid); +int listen_addr(const struct sockaddr *sa); void network_init(void); struct sockaddr *local_sa(int); @@ -141,4 +142,12 @@ struct sockaddr *local_sa(int); #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 + #endif /* rpcbind_h */ Modified: head/usr.sbin/rpcbind/util.c ============================================================================== --- head/usr.sbin/rpcbind/util.c Tue Feb 9 17:29:04 2010 (r203709) +++ head/usr.sbin/rpcbind/util.c Tue Feb 9 18:10:56 2010 (r203710) @@ -58,13 +58,6 @@ #include "rpcbind.h" -#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 - static struct sockaddr_in *local_in4; #ifdef INET6 static struct sockaddr_in6 *local_in6; @@ -176,9 +169,13 @@ addrmerge(struct netbuf *caller, char *s goto freeit; /* - * Loop through all interfaces. For each interface, see if the - * network portion of its address is equal to that of the client. - * If so, we have found the interface that we want to use. + * Loop through all interfaces. For each interface, see if it + * is either the loopback interface (which we always listen + * on) or is one of the addresses the program bound to (the + * wildcard by default, or a subset if -h is specified) and + * the network portion of its address is equal to that of the + * client. If so, we have found the interface that we want to + * use. */ bestif = NULL; for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) { @@ -189,6 +186,9 @@ addrmerge(struct netbuf *caller, char *s !(ifap->ifa_flags & IFF_UP)) continue; + if (!(ifap->ifa_flags & IFF_LOOPBACK) && !listen_addr(ifsa)) + continue; + switch (hint_sa->sa_family) { case AF_INET: /*