Compare commits

..
8 Commits
Author SHA1 Message Date
Simon Kelley 7f227a83f2 Fix struct hostinfo free code and BSD compile.
The code added in6 c596f1cc1d92b2b90ef5ce043ace314eefa868b
fails to free the returned datastructures from gethostinfo()
because sdetails.hostinfo is used to loop through the addresses
and ends up NULL. In some libc implementations this results
in a SEGV when freeaddrinfo() is called.

Also fix FTBFS under BSD. Thanks to Johnny S. Lee for the bug report.
2022-11-17 13:16:55 +00:00
Simon Kelley 9ed3ee67ec Handle DS records for unsupported crypto algorithms correctly.
Such a DS, as long as it is validated, should allow answers
in the domain is attests to be returned as unvalidated, and not
as a validation error.
2022-11-16 16:49:30 +00:00
Simon Kelley 1f9215f5f9 Fix GOST signature algorithms for DNSSEC validation.
Use CryptoPro version of the hash function.
Handle the little-endian wire format of key data.
Get the wire order of S and R correct.

Note that Nettle version 3.6 or later is required for GOST support.
2022-11-16 15:57:31 +00:00
Simon Kelley f52cfdd8c3 Handle known DNSSEC signature algorithms which are not supported.
This fixes a confusion if certain algorithms are not supported
because the version is the crypto library is too old.  The validation
should be treated the same as for a completely unknown algorithm,
(ie return unverified answer) and not as a validation failure
(ie return SERVFAIL).

The algorithems affected are GOST and ED448.
2022-11-13 15:55:09 +00:00
Simon Kelley 2fc904111d Fix --server=/domain/# 2022-11-07 23:24:31 +00:00
Simon Kelley 262dadf50e Fix --server with multiple domains. 2022-11-07 23:14:30 +00:00
Simon Kelley 6c596f1cc1 Make specifying nameservers by name work for DBus API. 2022-11-07 23:00:34 +00:00
Simon Kelley dafa16c400 Call freeaddrinfo() in domain_rev[46]() 2022-11-07 22:01:28 +00:00
6 changed files with 174 additions and 85 deletions

No files matched your search

+6 -1
View File
@@ -44,8 +44,13 @@ version 2.88
cases where is can be useful. Thanks to Dominik Derigs for
the patch.
Handle DS records for unsupported crypto algorithms correctly.
Such a DS, as long as it is validated, should allow answers
in the domain is attests to be returned as unvalidated, and not
as a validation error.
(version 2.87
version 2.87
Allow arbitrary prefix lengths in --rev-server and
--domain=....,local
+21 -8
View File
@@ -309,14 +309,14 @@ static int dnsmasq_gostdsa_verify(struct blockdata *key_data, unsigned int key_l
mpz_init(y);
}
mpz_import(x, 32 , 1, 1, 0, 0, p);
mpz_import(y, 32 , 1, 1, 0, 0, p + 32);
mpz_import(x, 32, -1, 1, 0, 0, p);
mpz_import(y, 32, -1, 1, 0, 0, p + 32);
if (!ecc_point_set(gost_key, x, y))
return 0;
return 0;
mpz_import(sig_struct->r, 32, 1, 1, 0, 0, sig);
mpz_import(sig_struct->s, 32, 1, 1, 0, 0, sig + 32);
mpz_import(sig_struct->s, 32, 1, 1, 0, 0, sig);
mpz_import(sig_struct->r, 32, 1, 1, 0, 0, sig + 32);
return nettle_gostdsa_verify(gost_key, digest_len, digest, sig_struct);
}
@@ -390,7 +390,12 @@ static int (*verify_func(int algo))(struct blockdata *key_data, unsigned int key
return dnsmasq_ecdsa_verify;
#if MIN_VERSION(3, 1)
case 15: case 16:
case 15:
return dnsmasq_eddsa_verify;
#endif
#if MIN_VERSION(3, 6)
case 16:
return dnsmasq_eddsa_verify;
#endif
}
@@ -425,7 +430,9 @@ char *ds_digest_name(int digest)
{
case 1: return "sha1";
case 2: return "sha256";
case 3: return "gosthash94";
#if MIN_VERSION(3, 6)
case 3: return "gosthash94cp";
#endif
case 4: return "sha384";
default: return NULL;
}
@@ -444,11 +451,17 @@ char *algo_digest_name(int algo)
case 7: return "sha1"; /* RSASHA1-NSEC3-SHA1 */
case 8: return "sha256"; /* RSA/SHA-256 */
case 10: return "sha512"; /* RSA/SHA-512 */
case 12: return "gosthash94"; /* ECC-GOST */
#if MIN_VERSION(3, 6)
case 12: return "gosthash94cp"; /* ECC-GOST */
#endif
case 13: return "sha256"; /* ECDSAP256SHA256 */
case 14: return "sha384"; /* ECDSAP384SHA384 */
#if MIN_VERSION(3, 1)
case 15: return "null_hash"; /* ED25519 */
# if MIN_VERSION(3, 6)
case 16: return "null_hash"; /* ED448 */
# endif
#endif
default: return NULL;
}
}
+56 -18
View File
@@ -379,21 +379,6 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
strcpy(str_addr, str);
}
/* parse the IP address */
if ((addr_err = parse_server(str_addr, &sdetails)) ||
(addr_err = parse_server_addr(&sdetails)))
{
error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
"Invalid IP address '%s': %s",
str, addr_err);
break;
}
/* 0.0.0.0 for server address == NULL, for Dbus */
if (addr.in.sin_family == AF_INET &&
addr.in.sin_addr.s_addr == 0)
flags |= SERV_LITERAL_ADDRESS;
if (strings)
{
char *p;
@@ -407,7 +392,31 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
else
p = NULL;
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain, NULL);
if (strings && strlen(str_addr) == 0)
add_update_server(SERV_LITERAL_ADDRESS | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain, NULL);
else
{
if ((addr_err = parse_server(str_addr, &sdetails)))
{
error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
"Invalid IP address '%s': %s",
str, addr_err);
break;
}
while (parse_server_next(&sdetails))
{
if ((addr_err = parse_server_addr(&sdetails)))
{
error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
"Invalid IP address '%s': %s",
str, addr_err);
break;
}
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain, NULL);
}
}
} while ((str_domain = p));
}
else
@@ -421,11 +430,40 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
if (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING)
dbus_message_iter_get_basic(&string_iter, &str);
dbus_message_iter_next (&string_iter);
if ((addr_err = parse_server(str_addr, &sdetails)))
{
error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
"Invalid IP address '%s': %s",
str, addr_err);
break;
}
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str, NULL);
while (parse_server_next(&sdetails))
{
if ((addr_err = parse_server_addr(&sdetails)))
{
error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
"Invalid IP address '%s': %s",
str, addr_err);
break;
}
/* 0.0.0.0 for server address == NULL, for Dbus */
if (addr.in.sin_family == AF_INET &&
addr.in.sin_addr.s_addr == 0)
flags |= SERV_LITERAL_ADDRESS;
else
flags &= ~SERV_LITERAL_ADDRESS;
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str, NULL);
}
} while (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING);
}
if (sdetails.orig_hostinfo)
freeaddrinfo(sdetails.orig_hostinfo);
/* jump to next element in outer array */
dbus_message_iter_next(&array_iter);
}
+3 -2
View File
@@ -1295,9 +1295,9 @@ extern struct daemon {
struct server_details {
union mysockaddr *addr, *source_addr;
struct addrinfo *hostinfo;
struct addrinfo *hostinfo, *orig_hostinfo;
char *interface, *source, *scope_id, *interface_opt;
int serv_port, source_port, addr_type, scope_index, valid, resolved;
int serv_port, source_port, addr_type, scope_index, valid;
u16 *flags;
};
@@ -1478,6 +1478,7 @@ void reset_option_bool(unsigned int opt);
struct hostsfile *expand_filelist(struct hostsfile *list);
char *parse_server(char *arg, struct server_details *sdetails);
char *parse_server_addr(struct server_details *sdetails);
int parse_server_next(struct server_details *sdetails);
int option_read_dynfile(char *file, int flags);
/* forward.c */
+37 -22
View File
@@ -979,10 +979,13 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
}
/* The DNS packet is expected to contain the answer to a DS query
Put all DSs in the answer which are valid into the cache.
Put all DSs in the answer which are valid and have hash and signature algos
we support into the cache.
Also handles replies which prove that there's no DS at this location,
either because the zone is unsigned or this isn't a zone cut. These are
cached too.
If none of the DS's are for supported algos, treat the answer as if
it's a proof of no DS at this location. RFC4035 para 5.2.
return codes:
STAT_OK At least one valid DS found and in cache.
STAT_BOGUS no DS in reply or not signed, fails validation, bad packet.
@@ -993,8 +996,8 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class)
{
unsigned char *p = (unsigned char *)(header+1);
int qtype, qclass, rc, i, neganswer, nons, neg_ttl = 0;
int aclass, atype, rdlen;
int qtype, qclass, rc, i, neganswer, nons, neg_ttl = 0, found_supported = 0;
int aclass, atype, rdlen, flags;
unsigned long ttl;
union all_addr a;
@@ -1065,14 +1068,22 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
algo = *p++;
digest = *p++;
if ((key = blockdata_alloc((char*)p, rdlen - 4)))
if (!ds_digest_name(digest) || !ds_digest_name(digest))
{
a.log.keytag = keytag;
a.log.algo = algo;
a.log.digest = digest;
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu (not supported)", 0);
neg_ttl = ttl;
}
else if ((key = blockdata_alloc((char*)p, rdlen - 4)))
{
a.ds.digest = digest;
a.ds.keydata = key;
a.ds.algo = algo;
a.ds.keytag = keytag;
a.ds.keylen = rdlen - 4;
if (!cache_insert(name, &a, class, now, ttl, F_FORWARD | F_DS | F_DNSSECOK))
{
blockdata_free(key);
@@ -1083,26 +1094,29 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
a.log.keytag = keytag;
a.log.algo = algo;
a.log.digest = digest;
if (ds_digest_name(digest) && algo_digest_name(algo))
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu", 0);
else
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu (not supported)", 0);
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu", 0);
found_supported = 1;
}
}
p = psave;
}
if (!ADD_RDLEN(header, p, plen, rdlen))
return STAT_BOGUS; /* bad packet */
}
cache_end_insert();
/* Fall through if no supported algo DS found. */
if (found_supported)
return STAT_OK;
}
else
flags = F_FORWARD | F_DS | F_NEG | F_DNSSECOK;
if (neganswer)
{
int flags = F_FORWARD | F_DS | F_NEG | F_DNSSECOK;
if (RCODE(header) == NXDOMAIN)
flags |= F_NXDOMAIN;
@@ -1110,17 +1124,18 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
to store presence/absence of NS. */
if (nons)
flags &= ~F_DNSSECOK;
cache_start_insert();
/* Use TTL from NSEC for negative cache entries */
if (!cache_insert(name, NULL, class, now, neg_ttl, flags))
return STAT_BOGUS;
cache_end_insert();
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, nons ? "no DS/cut" : "no DS", 0);
}
cache_start_insert();
/* Use TTL from NSEC for negative cache entries */
if (!cache_insert(name, NULL, class, now, neg_ttl, flags))
return STAT_BOGUS;
cache_end_insert();
if (neganswer)
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, nons ? "no DS/cut" : "no DS", 0);
return STAT_OK;
}
+51 -34
View File
@@ -860,14 +860,21 @@ char *parse_server(char *arg, struct server_details *sdetails)
sdetails->serv_port = NAMESERVER_PORT;
char *portno;
int ecode = 0;
struct addrinfo hints = { 0 };
struct addrinfo hints;
memset(&hints, 0, sizeof(struct addrinfo));
*sdetails->interface = 0;
sdetails->addr_type = AF_UNSPEC;
sdetails->valid = 0;
sdetails->hostinfo = sdetails->orig_hostinfo = NULL;
if (strcmp(arg, "#") == 0)
{
if (sdetails->flags)
*sdetails->flags |= SERV_USE_RESOLV;
sdetails->addr_type = AF_LOCAL;
sdetails->valid = 1;
return NULL;
}
@@ -941,7 +948,7 @@ char *parse_server(char *arg, struct server_details *sdetails)
above, and returns a pointer to the start of the list in <hostinfo>.
The items in the linked list are linked by the <ai_next> field. */
sdetails->valid = 1;
sdetails->resolved = 1;
sdetails->orig_hostinfo = sdetails->hostinfo;
return NULL;
}
else
@@ -965,7 +972,7 @@ char *parse_server_addr(struct server_details *sdetails)
sdetails->addr->in.sin_port = htons(sdetails->serv_port);
sdetails->addr->sa.sa_family = sdetails->source_addr->sa.sa_family = AF_INET;
#ifdef HAVE_SOCKADDR_SA_LEN
source_addr->in.sin_len = addr->in.sin_len = sizeof(struct sockaddr_in);
sdetails->source_addr->in.sin_len = sdetails->addr->in.sin_len = sizeof(struct sockaddr_in);
#endif
sdetails->source_addr->in.sin_addr.s_addr = INADDR_ANY;
sdetails->source_addr->in.sin_port = htons(daemon->query_port);
@@ -983,7 +990,7 @@ char *parse_server_addr(struct server_details *sdetails)
/* When resolving a server IP by hostname, we can simply skip mismatching
server / source IP pairs. Otherwise, when an IP address is given directly,
this is a fatal error. */
if (!sdetails->resolved)
if (!sdetails->orig_hostinfo)
return _("cannot use IPv4 server address with IPv6 source address");
}
else
@@ -1014,7 +1021,7 @@ char *parse_server_addr(struct server_details *sdetails)
sdetails->addr->sa.sa_family = sdetails->source_addr->sa.sa_family = AF_INET6;
sdetails->addr->in6.sin6_flowinfo = sdetails->source_addr->in6.sin6_flowinfo = 0;
#ifdef HAVE_SOCKADDR_SA_LEN
sdetails->addr->in6.sin6_len = sdetails->source_addr->in6.sin6_len = sizeof(addr->in6);
sdetails->addr->in6.sin6_len = sdetails->source_addr->in6.sin6_len = sizeof(sdetails->addr->in6);
#endif
if (sdetails->source)
{
@@ -1029,7 +1036,7 @@ char *parse_server_addr(struct server_details *sdetails)
/* When resolving a server IP by hostname, we can simply skip mismatching
server / source IP pairs. Otherwise, when an IP address is given directly,
this is a fatal error. */
if(!sdetails->resolved)
if(!sdetails->orig_hostinfo)
return _("cannot use IPv6 server address with IPv4 source address");
}
else
@@ -1047,12 +1054,13 @@ char *parse_server_addr(struct server_details *sdetails)
}
}
}
else
else if (sdetails->addr_type != AF_LOCAL)
return _("bad address");
return NULL;
}
static int parse_server_next(struct server_details *sdetails)
int parse_server_next(struct server_details *sdetails)
{
/* Looping over resolved addresses? */
if (sdetails->hostinfo)
@@ -1095,12 +1103,12 @@ static char *domain_rev4(int from_file, char *server, struct in_addr *addr4, int
union mysockaddr serv_addr, source_addr;
char interface[IF_NAMESIZE+1];
int count = 1, rem, addrbytes, addrbits;
struct server_details sdetails = { 0 };
struct server_details sdetails;
sdetails.addr = &serv_addr;
sdetails.source_addr = &source_addr;
sdetails.interface = interface;
sdetails.flags = &flags;
if (!server)
flags = SERV_LITERAL_ADDRESS;
else if ((string = parse_server(server, &sdetails)))
@@ -1157,6 +1165,9 @@ static char *domain_rev4(int from_file, char *server, struct in_addr *addr4, int
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
return _("error");
}
if (sdetails.orig_hostinfo)
freeaddrinfo(sdetails.orig_hostinfo);
}
}
@@ -1173,12 +1184,12 @@ static char *domain_rev6(int from_file, char *server, struct in6_addr *addr6, in
union mysockaddr serv_addr, source_addr;
char interface[IF_NAMESIZE+1];
int count = 1, rem, addrbytes, addrbits;
struct server_details sdetails = { 0 };
struct server_details sdetails;
sdetails.addr = &serv_addr;
sdetails.source_addr = &source_addr;
sdetails.interface = interface;
sdetails.flags = &flags;
if (!server)
flags = SERV_LITERAL_ADDRESS;
else if ((string = parse_server(server, &sdetails)))
@@ -1237,6 +1248,9 @@ static char *domain_rev6(int from_file, char *server, struct in6_addr *addr6, in
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
return _("error");
}
if (sdetails.orig_hostinfo)
freeaddrinfo(sdetails.orig_hostinfo);
}
}
@@ -2950,19 +2964,19 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
case LOPT_LOCAL: /* --local */
case 'A': /* --address */
{
char *lastdomain = NULL, *domain = "";
char *lastdomain = NULL, *domain = "", *cur_domain;
u16 flags = 0;
char *err;
union all_addr addr;
union mysockaddr serv_addr, source_addr;
char interface[IF_NAMESIZE+1];
struct server_details sdetails = { 0 };
struct server_details sdetails;
sdetails.addr = &serv_addr;
sdetails.source_addr = &source_addr;
sdetails.interface = interface;
sdetails.flags = &flags;
unhide_metas(arg);
/* split the domain args, if any and skip to the end of them. */
@@ -3001,9 +3015,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
if (servers_only && option == 'S')
flags |= SERV_FROM_FILE;
cur_domain = domain;
while ((flags & SERV_LITERAL_ADDRESS) || parse_server_next(&sdetails))
{
cur_domain = domain;
if (!(flags & SERV_LITERAL_ADDRESS) && (err = parse_server_addr(&sdetails)))
ret_err(err);
@@ -3015,32 +3032,32 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
{
/* server=//1.2.3.4 is special. */
if (lastdomain)
{
if (strlen(domain) == 0)
flags |= SERV_FOR_NODOTS;
else
flags &= ~SERV_FOR_NODOTS;
/* address=/#/ matches the same as without domain */
if (option == 'A' && domain[0] == '#' && domain[1] == 0)
domain[0] = 0;
}
if (!add_update_server(flags, sdetails.addr, sdetails.source_addr, sdetails.interface, domain, &addr))
{
if (strlen(cur_domain) == 0)
flags |= SERV_FOR_NODOTS;
else
flags &= ~SERV_FOR_NODOTS;
/* address=/#/ matches the same as without domain */
if (option == 'A' && cur_domain[0] == '#' && cur_domain[1] == 0)
cur_domain[0] = 0;
}
if (!add_update_server(flags, sdetails.addr, sdetails.source_addr, sdetails.interface, cur_domain, &addr))
ret_err(gen_err);
if (!lastdomain || domain == lastdomain)
if (!lastdomain || cur_domain == lastdomain)
break;
domain += strlen(domain) + 1;
cur_domain += strlen(cur_domain) + 1;
}
if (flags & SERV_LITERAL_ADDRESS)
break;
}
if (sdetails.resolved)
freeaddrinfo(sdetails.hostinfo);
if (sdetails.orig_hostinfo)
freeaddrinfo(sdetails.orig_hostinfo);
break;
}