diff options
-rw-r--r-- | src/dnsmasq.c | 3 | ||||
-rw-r--r-- | src/dnsmasq.h | 4 | ||||
-rw-r--r-- | src/network.c | 59 | ||||
-rw-r--r-- | src/rfc1035.c | 2 |
4 files changed, 65 insertions, 3 deletions
diff --git a/src/dnsmasq.c b/src/dnsmasq.c index 83d77d8..a2b37dc 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c @@ -632,6 +632,8 @@ int main (int argc, char **argv) if (bind_fallback) my_syslog(LOG_WARNING, _("setting --bind-interfaces option because of OS limitations")); + + warn_bound_listeners(); if (!option_bool(OPT_NOWILD)) for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next) @@ -856,6 +858,7 @@ int main (int argc, char **argv) enumerate_interfaces(0); /* NB, is_dad_listeners() == 1 --> we're binding interfaces */ create_bound_listeners(0); + warn_bound_listeners(); } #ifdef HAVE_LINUX_NETWORK diff --git a/src/dnsmasq.h b/src/dnsmasq.h index adf0828..17a351f 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -454,7 +454,7 @@ struct ipsets { struct irec { union mysockaddr addr; struct in_addr netmask; /* only valid for IPv4 */ - int tftp_ok, dhcp_ok, mtu, done, dad, dns_auth, index, multicast_done; + int tftp_ok, dhcp_ok, mtu, done, warned, dad, dns_auth, index, multicast_done; char *name; struct irec *next; }; @@ -988,6 +988,7 @@ unsigned char *skip_questions(struct dns_header *header, size_t plen); int extract_name(struct dns_header *header, size_t plen, unsigned char **pp, char *name, int isExtract, int extrabytes); int in_arpa_name_2_addr(char *namein, struct all_addr *addrp); +int private_net(struct in_addr addr, int ban_localhost); /* auth.c */ #ifdef HAVE_AUTH @@ -1068,6 +1069,7 @@ void check_servers(void); int enumerate_interfaces(int reset); void create_wildcard_listeners(void); void create_bound_listeners(int die); +void warn_bound_listeners(void); int is_dad_listeners(void); int iface_check(int family, struct all_addr *addr, char *name, int *auth_dns); int loopback_exception(int fd, int family, struct all_addr *addr, char *name); diff --git a/src/network.c b/src/network.c index fc0346e..de5d9f2 100644 --- a/src/network.c +++ b/src/network.c @@ -16,6 +16,10 @@ #include "dnsmasq.h" +#ifndef IN6_IS_ADDR_ULA +#define IN6_IS_ADDR_ULA(a) ((((__const uint32_t *) (a))[0] & htonl (0xfe00000)) == htonl (0xfc000000)) +#endif + #ifdef HAVE_LINUX_NETWORK int indextoname(int fd, int index, char *name) @@ -383,7 +387,7 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label, iface->dns_auth = auth_dns; iface->mtu = mtu; iface->dad = dad; - iface->done = iface->multicast_done = 0; + iface->done = iface->multicast_done = iface->warned = 0; iface->index = if_index; if ((iface->name = whine_malloc(strlen(ifr.ifr_name)+1))) { @@ -824,6 +828,59 @@ void create_bound_listeners(int dienow) } } +/* In --bind-interfaces, the only access control is the addresses we're listening on. + There's nothing to avoid a query to the address of an internal interface arriving via + an external interface where we don't want to accept queries, except that in the usual + case the addresses of internal interfaces are RFC1918. When bind-interfaces in use, + and we listen on an address that looks like it's probably globally routeable, shout. + + The fix is to use --bind-dynamic, which actually checks the arrival interface too. + Tough if your platform doesn't support this. +*/ + +void warn_bound_listeners(void) +{ + struct irec *iface; + int advice = 0; + + for (iface = daemon->interfaces; iface; iface = iface->next) + if (option_bool(OPT_NOWILD) && !iface->dns_auth) + { + int warn = 0; + if (iface->addr.sa.sa_family == AF_INET) + { + if (!private_net(iface->addr.in.sin_addr, 1)) + { + inet_ntop(AF_INET, &iface->addr.in.sin_addr, daemon->addrbuff, ADDRSTRLEN); + warn = 1; + } + } +#ifdef HAVE_IPV6 + else + { + if (!IN6_IS_ADDR_LINKLOCAL(&iface->addr.in6.sin6_addr) && + !IN6_IS_ADDR_SITELOCAL(&iface->addr.in6.sin6_addr) && + !IN6_IS_ADDR_ULA(&iface->addr.in6.sin6_addr) && + !IN6_IS_ADDR_LOOPBACK(&iface->addr.in6.sin6_addr)) + { + inet_ntop(AF_INET6, &iface->addr.in6.sin6_addr, daemon->addrbuff, ADDRSTRLEN); + warn = 1; + } + } +#endif + if (warn) + { + iface->warned = advice = 1; + my_syslog(LOG_WARNING, + _("LOUD WARNING: listening on %s may accept requests via interfaces other than %s. "), + daemon->addrbuff, iface->name); + } + } + + if (advice) + my_syslog(LOG_WARNING, _("LOUD WARNING: use --bind-dynamic rather than --bind-interfaces to avoid DNS amplification attacks via these interface(s).")); +} + int is_dad_listeners(void) { struct irec *iface; diff --git a/src/rfc1035.c b/src/rfc1035.c index 655216e..573ec31 100644 --- a/src/rfc1035.c +++ b/src/rfc1035.c @@ -724,7 +724,7 @@ int check_source(struct dns_header *header, size_t plen, unsigned char *pseudohe } /* is addr in the non-globally-routed IP space? */ -static int private_net(struct in_addr addr, int ban_localhost) +int private_net(struct in_addr addr, int ban_localhost) { in_addr_t ip_addr = ntohl(addr.s_addr); |