Compare commits

...
6 Commits
Author SHA1 Message Date
Simon Kelley e1791f36ea Fix logging of DNSSEC queries in TCP mode. Destination server address was misleading. 2018-10-06 23:23:23 +01:00
Simon Kelley 0fdf3c1f61 Fix dhcp-match-name to match hostname, not complete FQDN.
Also do name matching for DHCPv6.
2018-10-05 23:35:54 +01:00
Simon Kelley ee1df06aab Tweak strategy for confirming SLAAC addresses.
The code which conirms possible SLAAC addresses associated with
hosts known from DHCPv4 addresses keeps trying at longer and longer
intervals essentially forever, EXCEPT if sending an ICMP ping results
in a HOSTUNREACH error, which terminates the process immediately.

It turns out that this is too drastic. Routing changes associated
with addressing changes can cause temporary HOSTUNREACH problems,
even when an address has not gone forever. Therefore continue
trying in the face of HOSTUNREACH for the first part of the
process. HOSTUNREACH errors will still terminate the process
after it reaches the slow tail of retries.

Thanks to Andrey Vakhitov for help diagnosing this.
2018-10-05 22:22:41 +01:00
Simon Kelley 1e87eba424 Clarify manpage for --auth-sec-servers 2018-10-05 16:49:31 +01:00
Simon Kelley 08933475ab Make interface spec optional in --auth-server.
But make auth-server required when any auth-zones are defined.

The "glue record" field in auth-server is needed to synthesise
SOA and NS records in auth zones, so the --auth-server has to
be specified. If makes sense, however to define one or more
auth-zones that appear within the normal recursive DNS service
without actually acting as an authoritative DNS server on
any interface. Hence making the interface field optional.
2018-10-05 16:44:05 +01:00
Simon Kelley 7cbf497da4 Example config file fix for CERT Vulnerability VU#598349. 2018-09-26 18:04:38 +01:00
11 changed files with 126 additions and 70 deletions

No files matched your search

+4
View File
@@ -64,6 +64,10 @@ version 2.80
now always forward, and never answer from the cache. This now always forward, and never answer from the cache. This
allows "dig +trace" command to work. allows "dig +trace" command to work.
Include in the example config file a formulation which
stops DHCP clients from claiming the DNS name "wpad".
This is a fix for the CERT Vulnerability VU#598349.
version 2.79 version 2.79
Fix parsing of CNAME arguments, which are confused by extra spaces. Fix parsing of CNAME arguments, which are confused by extra spaces.
+5
View File
@@ -672,3 +672,8 @@
# Include all files in a directory which end in .conf # Include all files in a directory which end in .conf
#conf-dir=/etc/dnsmasq.d/,*.conf #conf-dir=/etc/dnsmasq.d/,*.conf
# If a DHCP client claims that its name is "wpad", ignore that.
# This fixes a security hole. see CERT Vulnerability VU#598349
#dhcp-name-match=set:wpad-ignore,wpad
#dhcp-ignore-names=tag:wpad-ignore
+4 -3
View File
@@ -231,7 +231,7 @@ options always override the others. The comments about interface labels for
.B --listen-address .B --listen-address
apply here. apply here.
.TP .TP
.B --auth-server=<domain>,<interface>|<ip-address> .B --auth-server=<domain>,[<interface>|<ip-address>...]
Enable DNS authoritative mode for queries arriving at an interface or address. Note that the interface or address Enable DNS authoritative mode for queries arriving at an interface or address. Note that the interface or address
need not be mentioned in need not be mentioned in
.B --interface .B --interface
@@ -244,7 +244,7 @@ specified interface. The <domain> is the "glue record". It should
resolve in the global DNS to an A and/or AAAA record which points to resolve in the global DNS to an A and/or AAAA record which points to
the address dnsmasq is listening on. When an interface is specified, the address dnsmasq is listening on. When an interface is specified,
it may be qualified with "/4" or "/6" to specify only the IPv4 or IPv6 it may be qualified with "/4" or "/6" to specify only the IPv4 or IPv6
addresses associated with the interface. addresses associated with the interface. Since any defined authoritative zones are also available as part of the normal recusive DNS service supplied by dnsmasq, it can make sense to have an --auth-server declaration with no interfaces or address, but simply specifying the glue record.
.TP .TP
.B --local-service .B --local-service
Accept DNS queries only from hosts whose address is on a local subnet, Accept DNS queries only from hosts whose address is on a local subnet,
@@ -827,7 +827,8 @@ authoritative zones as dnsmasq.
.B --auth-peer=<ip-address>[,<ip-address>[,<ip-address>...]] .B --auth-peer=<ip-address>[,<ip-address>[,<ip-address>...]]
Specify the addresses of secondary servers which are allowed to Specify the addresses of secondary servers which are allowed to
initiate zone transfer (AXFR) requests for zones for which dnsmasq is initiate zone transfer (AXFR) requests for zones for which dnsmasq is
authoritative. If this option is not given, then AXFR requests will be authoritative. If this option is not given but --auth-sec-servers is,
then AXFR requests will be
accepted from any secondary. Specifying accepted from any secondary. Specifying
.B --auth-peer .B --auth-peer
without without
+9 -2
View File
@@ -216,7 +216,7 @@ int main (int argc, char **argv)
#endif #endif
#ifndef HAVE_AUTH #ifndef HAVE_AUTH
if (daemon->authserver || daemon->auth_zones) if (daemon->auth_zones)
die(_("authoritative DNS not available: set HAVE_AUTH in src/config.h"), NULL, EC_BADCONF); die(_("authoritative DNS not available: set HAVE_AUTH in src/config.h"), NULL, EC_BADCONF);
#endif #endif
@@ -235,13 +235,20 @@ int main (int argc, char **argv)
now = dnsmasq_time(); now = dnsmasq_time();
if (daemon->auth_zones)
{
if (!daemon->authserver)
die(_("--auth-server required when an auth zone is defined."), NULL, EC_BADCONF);
/* Create a serial at startup if not configured. */ /* Create a serial at startup if not configured. */
if (daemon->auth_zones && daemon->soa_sn == 0)
#ifdef HAVE_BROKEN_RTC #ifdef HAVE_BROKEN_RTC
if (daemon_>soa_sn == 0)
die(_("zone serial must be configured in --auth-soa"), NULL, EC_BADCONF); die(_("zone serial must be configured in --auth-soa"), NULL, EC_BADCONF);
#else #else
if (daemon->soa_sn == 0)
daemon->soa_sn = now; daemon->soa_sn = now;
#endif #endif
}
#ifdef HAVE_DHCP6 #ifdef HAVE_DHCP6
if (daemon->dhcp6) if (daemon->dhcp6)
+1 -1
View File
@@ -1227,7 +1227,7 @@ int in_zone(struct auth_zone *zone, char *name, char **cut);
#endif #endif
/* dnssec.c */ /* dnssec.c */
size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class, int type, union mysockaddr *addr, int edns_pktsz); size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class, int type, int edns_pktsz);
int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class); int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class); int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class, int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class,
+1 -9
View File
@@ -2026,19 +2026,11 @@ int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen)
} }
size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class, size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class,
int type, union mysockaddr *addr, int edns_pktsz) int type, int edns_pktsz)
{ {
unsigned char *p; unsigned char *p;
char *types = querystr("dnssec-query", type);
size_t ret; size_t ret;
if (addr->sa.sa_family == AF_INET)
log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, name, (struct all_addr *)&addr->in.sin_addr, types);
#ifdef HAVE_IPV6
else
log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, name, (struct all_addr *)&addr->in6.sin6_addr, types);
#endif
header->qdcount = htons(1); header->qdcount = htons(1);
header->ancount = htons(0); header->ancount = htons(0);
header->nscount = htons(0); header->nscount = htons(0);
+27 -6
View File
@@ -1058,7 +1058,7 @@ void reply_query(int fd, int family, time_t now)
status = STAT_ABANDONED; status = STAT_ABANDONED;
else else
{ {
int fd, type = SERV_DO_DNSSEC; int querytype, fd, type = SERV_DO_DNSSEC;
struct frec *next = new->next; struct frec *next = new->next;
char *domain; char *domain;
@@ -1111,15 +1111,26 @@ void reply_query(int fd, int family, time_t now)
if (status == STAT_NEED_KEY) if (status == STAT_NEED_KEY)
{ {
new->flags |= FREC_DNSKEY_QUERY; new->flags |= FREC_DNSKEY_QUERY;
nn = dnssec_generate_query(header, ((unsigned char *) header) + server->edns_pktsz, querytype = T_DNSKEY;
daemon->keyname, forward->class, T_DNSKEY, &server->addr, server->edns_pktsz);
} }
else else
{ {
new->flags |= FREC_DS_QUERY; new->flags |= FREC_DS_QUERY;
nn = dnssec_generate_query(header,((unsigned char *) header) + server->edns_pktsz, querytype = T_DS;
daemon->keyname, forward->class, T_DS, &server->addr, server->edns_pktsz);
} }
nn = dnssec_generate_query(header,((unsigned char *) header) + server->edns_pktsz,
daemon->keyname, forward->class, querytype, server->edns_pktsz);
if (server->addr.sa.sa_family == AF_INET)
log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, daemon->keyname, (struct all_addr *)&(server->addr.in.sin_addr),
querystr("dnssec-query", querytype));
#ifdef HAVE_IPV6
else
log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, daemon->keyname, (struct all_addr *)&(server->addr.in6.sin6_addr),
querystr("dnssec-query", querytype));
#endif
if ((hash = hash_questions(header, nn, daemon->namebuff))) if ((hash = hash_questions(header, nn, daemon->namebuff)))
memcpy(new->hash, hash, HASH_SIZE); memcpy(new->hash, hash, HASH_SIZE);
new->new_id = get_id(); new->new_id = get_id();
@@ -1655,7 +1666,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
} }
m = dnssec_generate_query(new_header, ((unsigned char *) new_header) + 65536, keyname, class, m = dnssec_generate_query(new_header, ((unsigned char *) new_header) + 65536, keyname, class,
new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS, &server->addr, server->edns_pktsz); new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS, server->edns_pktsz);
*length = htons(m); *length = htons(m);
@@ -1729,6 +1740,16 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
continue; continue;
} }
if (server->addr.sa.sa_family == AF_INET)
log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, keyname, (struct all_addr *)&(server->addr.in.sin_addr),
querystr("dnssec-query", new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS));
#ifdef HAVE_IPV6
else
log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, keyname, (struct all_addr *)&(server->addr.in6.sin6_addr),
querystr("dnssec-query", new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS));
#endif
server->flags |= SERV_GOT_TCP; server->flags |= SERV_GOT_TCP;
m = (c1 << 8) | c2; m = (c1 << 8) | c2;
+5 -7
View File
@@ -1902,12 +1902,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
} }
case LOPT_AUTHSERV: /* --auth-server */ case LOPT_AUTHSERV: /* --auth-server */
if (!(comma = split(arg))) comma = split(arg);
ret_err(gen_err);
daemon->authserver = opt_string_alloc(arg); daemon->authserver = opt_string_alloc(arg);
arg = comma;
do { while ((arg = comma))
{
struct iname *new = opt_malloc(sizeof(struct iname)); struct iname *new = opt_malloc(sizeof(struct iname));
comma = split(arg); comma = split(arg);
new->name = NULL; new->name = NULL;
@@ -1937,9 +1937,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
} }
new->next = daemon->authinterface; new->next = daemon->authinterface;
daemon->authinterface = new; daemon->authinterface = new;
};
arg = comma;
} while (arg);
break; break;
+39 -41
View File
@@ -700,15 +700,48 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
client_hostname = daemon->dhcp_buff; client_hostname = daemon->dhcp_buff;
} }
if (client_hostname) if (client_hostname && option_bool(OPT_LOG_OPTS))
{
struct dhcp_match_name *m;
size_t nl = strlen(client_hostname);
if (option_bool(OPT_LOG_OPTS))
my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), ntohl(mess->xid), client_hostname); my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), ntohl(mess->xid), client_hostname);
if (have_config(config, CONFIG_NAME))
{
hostname = config->hostname;
domain = config->domain;
hostname_auth = 1;
/* be careful not to send an OFFER with a hostname not matching the DISCOVER. */
if (fqdn_flags != 0 || !client_hostname || hostname_isequal(hostname, client_hostname))
offer_hostname = hostname;
}
else if (client_hostname)
{
struct dhcp_match_name *m;
size_t nl;
domain = strip_hostname(client_hostname);
if ((nl = strlen(client_hostname)) != 0)
{
hostname = client_hostname;
if (!config)
{
/* Search again now we have a hostname.
Only accept configs without CLID and HWADDR here, (they won't match)
to avoid impersonation by name. */
struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0,
mess->chaddr, mess->hlen,
mess->htype, hostname);
if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr)
{
config = new;
/* set "known" tag for known hosts */
known_id.net = "known";
known_id.next = netid;
netid = &known_id;
}
}
for (m = daemon->dhcp_name_match; m; m = m->next) for (m = daemon->dhcp_name_match; m; m = m->next)
{ {
size_t ml = strlen(m->name); size_t ml = strlen(m->name);
@@ -733,41 +766,6 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
client_hostname[ml] = save; client_hostname[ml] = save;
} }
} }
if (have_config(config, CONFIG_NAME))
{
hostname = config->hostname;
domain = config->domain;
hostname_auth = 1;
/* be careful not to send an OFFER with a hostname not matching the DISCOVER. */
if (fqdn_flags != 0 || !client_hostname || hostname_isequal(hostname, client_hostname))
offer_hostname = hostname;
}
else if (client_hostname)
{
domain = strip_hostname(client_hostname);
if (strlen(client_hostname) != 0)
{
hostname = client_hostname;
if (!config)
{
/* Search again now we have a hostname.
Only accept configs without CLID and HWADDR here, (they won't match)
to avoid impersonation by name. */
struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0,
mess->chaddr, mess->hlen,
mess->htype, hostname);
if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr)
{
config = new;
/* set "known" tag for known hosts */
known_id.net = "known";
known_id.next = netid;
netid = &known_id;
}
}
}
} }
if (config) if (config)
+29
View File
@@ -496,11 +496,16 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
} }
else if (state->client_hostname) else if (state->client_hostname)
{ {
struct dhcp_match_name *m;
size_t nl;
state->domain = strip_hostname(state->client_hostname); state->domain = strip_hostname(state->client_hostname);
nl = strlen(state->client_hostname);
if (strlen(state->client_hostname) != 0) if (strlen(state->client_hostname) != 0)
{ {
state->hostname = state->client_hostname; state->hostname = state->client_hostname;
if (!config) if (!config)
{ {
/* Search again now we have a hostname. /* Search again now we have a hostname.
@@ -510,6 +515,30 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr) if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr)
config = new; config = new;
} }
for (m = daemon->dhcp_name_match; m; m = m->next)
{
size_t ml = strlen(m->name);
char save = 0;
if (nl < ml)
continue;
if (nl > ml)
{
save = state->client_hostname[ml];
state->client_hostname[ml] = 0;
}
if (hostname_isequal(state->client_hostname, m->name) &&
(save == 0 || m->wildcard))
{
m->netid->next = state->tags;
state->tags = m->netid;
}
if (save != 0)
state->client_hostname[ml] = save;
}
} }
} }
} }
+2 -1
View File
@@ -166,7 +166,8 @@ time_t periodic_slaac(time_t now, struct dhcp_lease *leases)
if (sendto(daemon->icmp6fd, daemon->outpacket.iov_base, save_counter(-1), 0, if (sendto(daemon->icmp6fd, daemon->outpacket.iov_base, save_counter(-1), 0,
(struct sockaddr *)&addr, sizeof(addr)) == -1 && (struct sockaddr *)&addr, sizeof(addr)) == -1 &&
errno == EHOSTUNREACH) errno == EHOSTUNREACH &&
slaac->backoff == 12)
slaac->ping_time = 0; /* Give up */ slaac->ping_time = 0; /* Give up */
else else
{ {