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
This commit is contained in:
parent
8a25c0c741
commit
923dd9a7a7
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=203710
@ -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, rpcvers_t, struct netconfig *,
|
||||
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 @@ init_transport(struct netconfig *nconf)
|
||||
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)
|
||||
|
@ -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 */
|
||||
|
@ -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 *serv_uaddr, char *clnt_uaddr,
|
||||
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 *serv_uaddr, char *clnt_uaddr,
|
||||
!(ifap->ifa_flags & IFF_UP))
|
||||
continue;
|
||||
|
||||
if (!(ifap->ifa_flags & IFF_LOOPBACK) && !listen_addr(ifsa))
|
||||
continue;
|
||||
|
||||
switch (hint_sa->sa_family) {
|
||||
case AF_INET:
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user