Merge pull request #2092 from poettering/dnssec2
Second DNSSEC patch set
This commit is contained in:
commit
8eb3655cdb
|
@ -5184,6 +5184,8 @@ systemd_resolved_SOURCES = \
|
|||
src/resolve/resolved-dns-stream.c \
|
||||
src/resolve/resolved-dns-dnssec.h \
|
||||
src/resolve/resolved-dns-dnssec.c \
|
||||
src/resolve/resolved-dns-trust-anchor.h \
|
||||
src/resolve/resolved-dns-trust-anchor.c \
|
||||
src/resolve/dns-type.c \
|
||||
src/resolve/dns-type.h
|
||||
|
||||
|
|
|
@ -67,6 +67,8 @@ static void print_source(uint64_t flags, usec_t rtt) {
|
|||
|
||||
fputc('.', stdout);
|
||||
fputc('\n', stdout);
|
||||
|
||||
printf("-- Data is authenticated: %s\n", yes_no(flags & SD_RESOLVED_AUTHENTICATED));
|
||||
}
|
||||
|
||||
static int resolve_host(sd_bus *bus, const char *name) {
|
||||
|
|
|
@ -197,7 +197,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
|
|||
r = sd_bus_message_append(
|
||||
reply, "st",
|
||||
DNS_RESOURCE_KEY_NAME(canonical->key),
|
||||
SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family));
|
||||
SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, q->answer_authenticated));
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
|
@ -344,7 +344,7 @@ static void bus_method_resolve_address_complete(DnsQuery *q) {
|
|||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
r = sd_bus_message_append(reply, "t", SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family));
|
||||
r = sd_bus_message_append(reply, "t", SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, q->answer_authenticated));
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
|
@ -510,7 +510,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) {
|
|||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
r = sd_bus_message_append(reply, "t", SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family));
|
||||
r = sd_bus_message_append(reply, "t", SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, q->answer_authenticated));
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
|
@ -859,7 +859,7 @@ static void resolve_service_all_complete(DnsQuery *q) {
|
|||
reply,
|
||||
"ssst",
|
||||
name, type, domain,
|
||||
SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family));
|
||||
SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, q->answer_authenticated));
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
|
|
|
@ -234,6 +234,41 @@ int config_parse_support(
|
|||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_dnssec(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
Manager *m = data;
|
||||
DnssecMode mode;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
|
||||
mode = dnssec_mode_from_string(rvalue);
|
||||
if (mode < 0) {
|
||||
r = parse_boolean(rvalue);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse DNSSEC mode '%s'. Ignoring.", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
mode = r ? DNSSEC_YES : DNSSEC_NO;
|
||||
}
|
||||
|
||||
m->unicast_scope->dnssec_mode = mode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int manager_parse_config_file(Manager *m) {
|
||||
int r;
|
||||
|
||||
|
|
|
@ -36,3 +36,4 @@ const struct ConfigPerfItem* resolved_gperf_lookup(const char *key, unsigned len
|
|||
int config_parse_dns_servers(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_search_domains(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_support(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_dnssec(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#define SD_RESOLVED_NO_TXT (UINT64_C(1) << 6)
|
||||
#define SD_RESOLVED_NO_ADDRESS (UINT64_C(1) << 7)
|
||||
#define SD_RESOLVED_NO_SEARCH (UINT64_C(1) << 8)
|
||||
#define SD_RESOLVED_AUTHENTICATED (UINT64_C(1) << 9)
|
||||
|
||||
#define SD_RESOLVED_LLMNR (SD_RESOLVED_LLMNR_IPV4|SD_RESOLVED_LLMNR_IPV6)
|
||||
#define SD_RESOLVED_PROTOCOLS_ALL (SD_RESOLVED_LLMNR|SD_RESOLVED_DNS)
|
||||
|
|
|
@ -46,6 +46,7 @@ struct DnsCacheItem {
|
|||
usec_t until;
|
||||
DnsCacheItemType type;
|
||||
unsigned prioq_idx;
|
||||
bool authenticated;
|
||||
int owner_family;
|
||||
union in_addr_union owner_address;
|
||||
LIST_FIELDS(DnsCacheItem, by_key);
|
||||
|
@ -237,7 +238,7 @@ static DnsCacheItem* dns_cache_get(DnsCache *c, DnsResourceRecord *rr) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static void dns_cache_item_update_positive(DnsCache *c, DnsCacheItem *i, DnsResourceRecord *rr, usec_t timestamp) {
|
||||
static void dns_cache_item_update_positive(DnsCache *c, DnsCacheItem *i, DnsResourceRecord *rr, bool authenticated, usec_t timestamp) {
|
||||
assert(c);
|
||||
assert(i);
|
||||
assert(rr);
|
||||
|
@ -257,6 +258,7 @@ static void dns_cache_item_update_positive(DnsCache *c, DnsCacheItem *i, DnsReso
|
|||
dns_resource_key_unref(i->key);
|
||||
i->key = dns_resource_key_ref(rr->key);
|
||||
|
||||
i->authenticated = authenticated;
|
||||
i->until = timestamp + MIN(rr->ttl * USEC_PER_SEC, CACHE_TTL_MAX_USEC);
|
||||
|
||||
prioq_reshuffle(c->by_expiry, i, &i->prioq_idx);
|
||||
|
@ -265,6 +267,7 @@ static void dns_cache_item_update_positive(DnsCache *c, DnsCacheItem *i, DnsReso
|
|||
static int dns_cache_put_positive(
|
||||
DnsCache *c,
|
||||
DnsResourceRecord *rr,
|
||||
bool authenticated,
|
||||
usec_t timestamp,
|
||||
int owner_family,
|
||||
const union in_addr_union *owner_address) {
|
||||
|
@ -300,7 +303,7 @@ static int dns_cache_put_positive(
|
|||
/* Entry exists already? Update TTL and timestamp */
|
||||
existing = dns_cache_get(c, rr);
|
||||
if (existing) {
|
||||
dns_cache_item_update_positive(c, existing, rr, timestamp);
|
||||
dns_cache_item_update_positive(c, existing, rr, authenticated, timestamp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -322,6 +325,7 @@ static int dns_cache_put_positive(
|
|||
i->prioq_idx = PRIOQ_IDX_NULL;
|
||||
i->owner_family = owner_family;
|
||||
i->owner_address = *owner_address;
|
||||
i->authenticated = authenticated;
|
||||
|
||||
r = dns_cache_link_item(c, i);
|
||||
if (r < 0)
|
||||
|
@ -341,6 +345,7 @@ static int dns_cache_put_negative(
|
|||
DnsCache *c,
|
||||
DnsResourceKey *key,
|
||||
int rcode,
|
||||
bool authenticated,
|
||||
usec_t timestamp,
|
||||
uint32_t soa_ttl,
|
||||
int owner_family,
|
||||
|
@ -389,6 +394,7 @@ static int dns_cache_put_negative(
|
|||
i->prioq_idx = PRIOQ_IDX_NULL;
|
||||
i->owner_family = owner_family;
|
||||
i->owner_address = *owner_address;
|
||||
i->authenticated = authenticated;
|
||||
|
||||
r = dns_cache_link_item(c, i);
|
||||
if (r < 0)
|
||||
|
@ -410,6 +416,7 @@ int dns_cache_put(
|
|||
int rcode,
|
||||
DnsAnswer *answer,
|
||||
unsigned max_rrs,
|
||||
bool authenticated,
|
||||
usec_t timestamp,
|
||||
int owner_family,
|
||||
const union in_addr_union *owner_address) {
|
||||
|
@ -452,7 +459,7 @@ int dns_cache_put(
|
|||
|
||||
/* Second, add in positive entries for all contained RRs */
|
||||
for (i = 0; i < MIN(max_rrs, answer->n_rrs); i++) {
|
||||
r = dns_cache_put_positive(c, answer->items[i].rr, timestamp, owner_family, owner_address);
|
||||
r = dns_cache_put_positive(c, answer->items[i].rr, authenticated, timestamp, owner_family, owner_address);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
@ -496,13 +503,13 @@ int dns_cache_put(
|
|||
if (!dns_answer_match_soa(canonical_key, soa->key))
|
||||
continue;
|
||||
|
||||
r = dns_cache_put_negative(c, canonical_key, rcode, timestamp, MIN(soa->soa.minimum, soa->ttl), owner_family, owner_address);
|
||||
r = dns_cache_put_negative(c, canonical_key, rcode, authenticated, timestamp, MIN(soa->soa.minimum, soa->ttl), owner_family, owner_address);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
r = dns_cache_put_negative(c, key, rcode, timestamp, MIN(soa->soa.minimum, soa->ttl), owner_family, owner_address);
|
||||
r = dns_cache_put_negative(c, key, rcode, authenticated, timestamp, MIN(soa->soa.minimum, soa->ttl), owner_family, owner_address);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
|
@ -522,7 +529,6 @@ fail:
|
|||
}
|
||||
|
||||
static DnsCacheItem *dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache *c, DnsResourceKey *k) {
|
||||
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *nsec_key = NULL, *cname_key = NULL;
|
||||
DnsCacheItem *i;
|
||||
const char *n;
|
||||
int r;
|
||||
|
@ -540,35 +546,23 @@ static DnsCacheItem *dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache *c, D
|
|||
n = DNS_RESOURCE_KEY_NAME(k);
|
||||
|
||||
/* Check if we have an NSEC record instead for the name. */
|
||||
nsec_key = dns_resource_key_new(k->class, DNS_TYPE_NSEC, n);
|
||||
if (!nsec_key)
|
||||
return NULL;
|
||||
|
||||
i = hashmap_get(c->by_key, nsec_key);
|
||||
i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_NSEC, n));
|
||||
if (i)
|
||||
return i;
|
||||
|
||||
/* Check if we have a CNAME record instead */
|
||||
cname_key = dns_resource_key_new_cname(k);
|
||||
if (!cname_key)
|
||||
return NULL;
|
||||
i = hashmap_get(c->by_key, cname_key);
|
||||
i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_CNAME, n));
|
||||
if (i)
|
||||
return i;
|
||||
|
||||
/* OK, let's look for cached DNAME records. */
|
||||
for (;;) {
|
||||
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *dname_key = NULL;
|
||||
char label[DNS_LABEL_MAX];
|
||||
|
||||
if (isempty(n))
|
||||
return NULL;
|
||||
|
||||
dname_key = dns_resource_key_new(k->class, DNS_TYPE_DNAME, n);
|
||||
if (!dname_key)
|
||||
return NULL;
|
||||
|
||||
i = hashmap_get(c->by_key, dname_key);
|
||||
i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_DNAME, n));
|
||||
if (i)
|
||||
return i;
|
||||
|
||||
|
@ -581,24 +575,26 @@ static DnsCacheItem *dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache *c, D
|
|||
return NULL;
|
||||
}
|
||||
|
||||
int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **ret) {
|
||||
int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **ret, bool *authenticated) {
|
||||
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
|
||||
unsigned n = 0;
|
||||
int r;
|
||||
bool nxdomain = false;
|
||||
_cleanup_free_ char *key_str = NULL;
|
||||
DnsResourceRecord *nsec = NULL;
|
||||
DnsCacheItem *j, *first;
|
||||
DnsCacheItem *j, *first, *nsec = NULL;
|
||||
bool have_authenticated = false, have_non_authenticated = false;
|
||||
|
||||
assert(c);
|
||||
assert(key);
|
||||
assert(rcode);
|
||||
assert(ret);
|
||||
assert(authenticated);
|
||||
|
||||
if (key->type == DNS_TYPE_ANY ||
|
||||
key->class == DNS_CLASS_ANY) {
|
||||
|
||||
/* If we have ANY lookups we simply refresh */
|
||||
/* If we have ANY lookups we don't use the cache, so
|
||||
* that the caller refreshes via the network. */
|
||||
|
||||
r = dns_resource_key_to_string(key, &key_str);
|
||||
if (r < 0)
|
||||
|
@ -629,10 +625,16 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r
|
|||
LIST_FOREACH(by_key, j, first) {
|
||||
if (j->rr) {
|
||||
if (j->rr->key->type == DNS_TYPE_NSEC)
|
||||
nsec = j->rr;
|
||||
nsec = j;
|
||||
|
||||
n++;
|
||||
} else if (j->type == DNS_CACHE_NXDOMAIN)
|
||||
nxdomain = true;
|
||||
|
||||
if (j->authenticated)
|
||||
have_authenticated = true;
|
||||
else
|
||||
have_non_authenticated = true;
|
||||
}
|
||||
|
||||
r = dns_resource_key_to_string(key, &key_str);
|
||||
|
@ -648,8 +650,11 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r
|
|||
|
||||
*ret = NULL;
|
||||
*rcode = DNS_RCODE_SUCCESS;
|
||||
*authenticated = nsec->authenticated;
|
||||
|
||||
return !bitmap_isset(nsec->nsec.types, key->type);
|
||||
return !bitmap_isset(nsec->rr->nsec.types, key->type) &&
|
||||
!bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_CNAME) &&
|
||||
!bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_DNAME);
|
||||
}
|
||||
|
||||
log_debug("%s cache hit for %s",
|
||||
|
@ -660,6 +665,7 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r
|
|||
if (n <= 0) {
|
||||
*ret = NULL;
|
||||
*rcode = nxdomain ? DNS_RCODE_NXDOMAIN : DNS_RCODE_SUCCESS;
|
||||
*authenticated = have_authenticated && !have_non_authenticated;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -678,6 +684,7 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r
|
|||
|
||||
*ret = answer;
|
||||
*rcode = DNS_RCODE_SUCCESS;
|
||||
*authenticated = have_authenticated && !have_non_authenticated;
|
||||
answer = NULL;
|
||||
|
||||
return n;
|
||||
|
|
|
@ -38,8 +38,8 @@ typedef struct DnsCache {
|
|||
void dns_cache_flush(DnsCache *c);
|
||||
void dns_cache_prune(DnsCache *c);
|
||||
|
||||
int dns_cache_put(DnsCache *c, DnsResourceKey *key, int rcode, DnsAnswer *answer, unsigned max_rrs, usec_t timestamp, int owner_family, const union in_addr_union *owner_address);
|
||||
int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **answer);
|
||||
int dns_cache_put(DnsCache *c, DnsResourceKey *key, int rcode, DnsAnswer *answer, unsigned max_rrs, bool authenticated, usec_t timestamp, int owner_family, const union in_addr_union *owner_address);
|
||||
int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **answer, bool *authenticated);
|
||||
|
||||
int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address);
|
||||
|
||||
|
|
|
@ -25,17 +25,34 @@
|
|||
#include "dns-domain.h"
|
||||
#include "resolved-dns-dnssec.h"
|
||||
#include "resolved-dns-packet.h"
|
||||
#include "string-table.h"
|
||||
|
||||
/* Open question:
|
||||
*
|
||||
* How does the DNSSEC canonical form of a hostname with a label
|
||||
* containing a dot look like, the way DNS-SD does it?
|
||||
*
|
||||
* TODO:
|
||||
*
|
||||
* - Iterative validation
|
||||
* - NSEC proof of non-existance
|
||||
* - NSEC3 proof of non-existance
|
||||
* - Make trust anchor store read additional DS+DNSKEY data from disk
|
||||
* - wildcard zones compatibility
|
||||
* - multi-label zone compatibility
|
||||
* - DMSSEC cname/dname compatibility
|
||||
* - per-interface DNSSEC setting
|
||||
* - DSA support
|
||||
* - EC support?
|
||||
*
|
||||
* */
|
||||
|
||||
#define VERIFY_RRS_MAX 256
|
||||
#define MAX_KEY_SIZE (32*1024)
|
||||
|
||||
/* Permit a maximum clock skew of 1h 10min. This should be enough to deal with DST confusion */
|
||||
#define SKEW_MAX (1*USEC_PER_HOUR + 10*USEC_PER_MINUTE)
|
||||
|
||||
/*
|
||||
* The DNSSEC Chain of trust:
|
||||
*
|
||||
|
@ -228,10 +245,14 @@ static int dnssec_rrsig_expired(DnsResourceRecord *rrsig, usec_t realtime) {
|
|||
inception = rrsig->rrsig.inception * USEC_PER_SEC;
|
||||
|
||||
if (inception > expiration)
|
||||
return -EINVAL;
|
||||
return -EKEYREJECTED;
|
||||
|
||||
/* Permit a certain amount of clock skew of 10% of the valid time range */
|
||||
/* Permit a certain amount of clock skew of 10% of the valid
|
||||
* time range. This takes inspiration from unbound's
|
||||
* resolver. */
|
||||
skew = (expiration - inception) / 10;
|
||||
if (skew > SKEW_MAX)
|
||||
skew = SKEW_MAX;
|
||||
|
||||
if (inception < skew)
|
||||
inception = 0;
|
||||
|
@ -690,3 +711,10 @@ finish:
|
|||
gcry_md_close(md);
|
||||
return r;
|
||||
}
|
||||
|
||||
static const char* const dnssec_mode_table[_DNSSEC_MODE_MAX] = {
|
||||
[DNSSEC_NO] = "no",
|
||||
[DNSSEC_TRUST] = "trust",
|
||||
[DNSSEC_YES] = "yes",
|
||||
};
|
||||
DEFINE_STRING_TABLE_LOOKUP(dnssec_mode, DnssecMode);
|
||||
|
|
|
@ -21,10 +21,26 @@
|
|||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
typedef enum DnssecMode DnssecMode;
|
||||
|
||||
#include "dns-domain.h"
|
||||
#include "resolved-dns-answer.h"
|
||||
#include "resolved-dns-rr.h"
|
||||
|
||||
enum DnssecMode {
|
||||
/* No DNSSEC validation is done */
|
||||
DNSSEC_NO,
|
||||
|
||||
/* Trust the AD bit sent by the server. UNSAFE! */
|
||||
DNSSEC_TRUST,
|
||||
|
||||
/* Validate locally, if the server knows DO, but if not, don't. Don't trust the AD bit */
|
||||
DNSSEC_YES,
|
||||
|
||||
_DNSSEC_MODE_MAX,
|
||||
_DNSSEC_MODE_INVALID = -1
|
||||
};
|
||||
|
||||
enum {
|
||||
DNSSEC_VERIFIED,
|
||||
DNSSEC_INVALID,
|
||||
|
@ -33,7 +49,6 @@ enum {
|
|||
DNSSEC_SIGNATURE_EXPIRED,
|
||||
};
|
||||
|
||||
|
||||
#define DNSSEC_CANONICAL_HOSTNAME_MAX (DNS_HOSTNAME_MAX + 2)
|
||||
|
||||
int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey);
|
||||
|
@ -47,3 +62,6 @@ int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds);
|
|||
uint16_t dnssec_keytag(DnsResourceRecord *dnskey);
|
||||
|
||||
int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max);
|
||||
|
||||
const char* dnssec_mode_to_string(DnssecMode m) _const_;
|
||||
DnssecMode dnssec_mode_from_string(const char *s) _pure_;
|
||||
|
|
|
@ -65,7 +65,7 @@ int dns_packet_new(DnsPacket **ret, DnsProtocol protocol, size_t mtu) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int dns_packet_new_query(DnsPacket **ret, DnsProtocol protocol, size_t mtu) {
|
||||
int dns_packet_new_query(DnsPacket **ret, DnsProtocol protocol, size_t mtu, bool dnssec_checking_disabled) {
|
||||
DnsPacket *p;
|
||||
DnsPacketHeader *h;
|
||||
int r;
|
||||
|
@ -96,7 +96,7 @@ int dns_packet_new_query(DnsPacket **ret, DnsProtocol protocol, size_t mtu) {
|
|||
1 /* rd (ask for recursion) */,
|
||||
0 /* ra */,
|
||||
0 /* ad */,
|
||||
0 /* cd */,
|
||||
dnssec_checking_disabled /* cd */,
|
||||
0 /* rcode */));
|
||||
|
||||
*ret = p;
|
||||
|
|
|
@ -144,7 +144,7 @@ static inline unsigned DNS_PACKET_RRCOUNT(DnsPacket *p) {
|
|||
}
|
||||
|
||||
int dns_packet_new(DnsPacket **p, DnsProtocol protocol, size_t mtu);
|
||||
int dns_packet_new_query(DnsPacket **p, DnsProtocol protocol, size_t mtu);
|
||||
int dns_packet_new_query(DnsPacket **p, DnsProtocol protocol, size_t mtu, bool dnssec_checking_disabled);
|
||||
|
||||
DnsPacket *dns_packet_ref(DnsPacket *p);
|
||||
DnsPacket *dns_packet_unref(DnsPacket *p);
|
||||
|
@ -225,16 +225,19 @@ DnsProtocol dns_protocol_from_string(const char *s) _pure_;
|
|||
#define LLMNR_MULTICAST_IPV4_ADDRESS ((struct in_addr) { .s_addr = htobe32(224U << 24 | 252U) })
|
||||
#define LLMNR_MULTICAST_IPV6_ADDRESS ((struct in6_addr) { .s6_addr = { 0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03 } })
|
||||
|
||||
static inline uint64_t SD_RESOLVED_FLAGS_MAKE(DnsProtocol protocol, int family) {
|
||||
static inline uint64_t SD_RESOLVED_FLAGS_MAKE(DnsProtocol protocol, int family, bool authenticated) {
|
||||
uint64_t f;
|
||||
|
||||
/* Converts a protocol + family into a flags field as used in queries */
|
||||
/* Converts a protocol + family into a flags field as used in queries and responses */
|
||||
|
||||
f = authenticated ? SD_RESOLVED_AUTHENTICATED : 0;
|
||||
|
||||
switch (protocol) {
|
||||
case DNS_PROTOCOL_DNS:
|
||||
return SD_RESOLVED_DNS;
|
||||
return f|SD_RESOLVED_DNS;
|
||||
|
||||
case DNS_PROTOCOL_LLMNR:
|
||||
return family == AF_INET6 ? SD_RESOLVED_LLMNR_IPV6 : SD_RESOLVED_LLMNR_IPV4;
|
||||
return f|(family == AF_INET6 ? SD_RESOLVED_LLMNR_IPV6 : SD_RESOLVED_LLMNR_IPV4);
|
||||
|
||||
default:
|
||||
break;
|
||||
|
|
|
@ -430,15 +430,17 @@ static int dns_query_add_candidate(DnsQuery *q, DnsScope *s) {
|
|||
return r;
|
||||
|
||||
/* If this a single-label domain on DNS, we might append a suitable search domain first. */
|
||||
r = dns_scope_name_needs_search_domain(s, dns_question_first_name(q->question));
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
if (r > 0) {
|
||||
/* OK, we need a search domain now. Let's find one for this scope */
|
||||
|
||||
r = dns_query_candidate_next_search_domain(c);
|
||||
if (r <= 0) /* if there's no search domain, then we won't add any transaction. */
|
||||
if ((q->flags & SD_RESOLVED_NO_SEARCH) == 0) {
|
||||
r = dns_scope_name_needs_search_domain(s, dns_question_first_name(q->question));
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
if (r > 0) {
|
||||
/* OK, we need a search domain now. Let's find one for this scope */
|
||||
|
||||
r = dns_query_candidate_next_search_domain(c);
|
||||
if (r <= 0) /* if there's no search domain, then we won't add any transaction. */
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
r = dns_query_candidate_setup_transactions(c);
|
||||
|
@ -970,6 +972,7 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
|
|||
DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
|
||||
DnsTransaction *t;
|
||||
Iterator i;
|
||||
bool has_authenticated = false, has_non_authenticated = false;
|
||||
|
||||
assert(q);
|
||||
|
||||
|
@ -997,6 +1000,11 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
|
|||
q->answer = merged;
|
||||
q->answer_rcode = t->answer_rcode;
|
||||
|
||||
if (t->answer_authenticated)
|
||||
has_authenticated = true;
|
||||
else
|
||||
has_non_authenticated = true;
|
||||
|
||||
state = DNS_TRANSACTION_SUCCESS;
|
||||
break;
|
||||
}
|
||||
|
@ -1026,6 +1034,7 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
|
|||
|
||||
q->answer_protocol = c->scope->protocol;
|
||||
q->answer_family = c->scope->family;
|
||||
q->answer_authenticated = has_authenticated && !has_non_authenticated;
|
||||
|
||||
dns_search_domain_unref(q->answer_search_domain);
|
||||
q->answer_search_domain = dns_search_domain_ref(c->search_domain);
|
||||
|
|
|
@ -75,6 +75,7 @@ struct DnsQuery {
|
|||
DnsProtocol answer_protocol;
|
||||
int answer_family;
|
||||
DnsSearchDomain *answer_search_domain;
|
||||
bool answer_authenticated;
|
||||
|
||||
/* Bus client information */
|
||||
sd_bus_message *request;
|
||||
|
|
|
@ -51,12 +51,6 @@ DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *
|
|||
return k;
|
||||
}
|
||||
|
||||
DnsResourceKey* dns_resource_key_new_cname(const DnsResourceKey *key) {
|
||||
assert(key);
|
||||
|
||||
return dns_resource_key_new(key->class, DNS_TYPE_CNAME, DNS_RESOURCE_KEY_NAME(key));
|
||||
}
|
||||
|
||||
DnsResourceKey* dns_resource_key_new_redirect(const DnsResourceKey *key, const DnsResourceRecord *cname) {
|
||||
int r;
|
||||
|
||||
|
@ -137,6 +131,10 @@ DnsResourceKey* dns_resource_key_ref(DnsResourceKey *k) {
|
|||
if (!k)
|
||||
return NULL;
|
||||
|
||||
/* Static/const keys created with DNS_RESOURCE_KEY_CONST will
|
||||
* set this to -1, they should not be reffed/unreffed */
|
||||
assert(k->n_ref != (unsigned) -1);
|
||||
|
||||
assert(k->n_ref > 0);
|
||||
k->n_ref++;
|
||||
|
||||
|
@ -147,6 +145,7 @@ DnsResourceKey* dns_resource_key_unref(DnsResourceKey *k) {
|
|||
if (!k)
|
||||
return NULL;
|
||||
|
||||
assert(k->n_ref != (unsigned) -1);
|
||||
assert(k->n_ref > 0);
|
||||
|
||||
if (k->n_ref == 1) {
|
||||
|
@ -158,6 +157,14 @@ DnsResourceKey* dns_resource_key_unref(DnsResourceKey *k) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
bool dns_resource_key_is_address(const DnsResourceKey *key) {
|
||||
assert(key);
|
||||
|
||||
/* Check if this is an A or AAAA resource key */
|
||||
|
||||
return key->class == DNS_CLASS_IN && IN_SET(key->type, DNS_TYPE_A, DNS_TYPE_AAAA);
|
||||
}
|
||||
|
||||
int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b) {
|
||||
int r;
|
||||
|
||||
|
|
|
@ -78,6 +78,19 @@ struct DnsResourceKey {
|
|||
char *_name; /* don't access directy, use DNS_RESOURCE_KEY_NAME()! */
|
||||
};
|
||||
|
||||
/* Creates a temporary resource key. This is only useful to quickly
|
||||
* look up something, without allocating a full DnsResourceKey object
|
||||
* for it. Note that it is not OK to take references to this kind of
|
||||
* resource key object. */
|
||||
#define DNS_RESOURCE_KEY_CONST(c, t, n) \
|
||||
((DnsResourceKey) { \
|
||||
.n_ref = (unsigned) -1, \
|
||||
.class = c, \
|
||||
.type = t, \
|
||||
._name = (char*) n, \
|
||||
})
|
||||
|
||||
|
||||
struct DnsTxtItem {
|
||||
size_t length;
|
||||
LIST_FIELDS(DnsTxtItem, items);
|
||||
|
@ -221,13 +234,12 @@ static inline const char* DNS_RESOURCE_KEY_NAME(const DnsResourceKey *key) {
|
|||
}
|
||||
|
||||
DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name);
|
||||
DnsResourceKey* dns_resource_key_new_cname(const DnsResourceKey *key);
|
||||
DnsResourceKey* dns_resource_key_new_dname(const DnsResourceKey *key);
|
||||
DnsResourceKey* dns_resource_key_new_redirect(const DnsResourceKey *key, const DnsResourceRecord *cname);
|
||||
int dns_resource_key_new_append_suffix(DnsResourceKey **ret, DnsResourceKey *key, char *name);
|
||||
DnsResourceKey* dns_resource_key_new_consume(uint16_t class, uint16_t type, char *name);
|
||||
DnsResourceKey* dns_resource_key_ref(DnsResourceKey *key);
|
||||
DnsResourceKey* dns_resource_key_unref(DnsResourceKey *key);
|
||||
bool dns_resource_key_is_address(const DnsResourceKey *key);
|
||||
int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b);
|
||||
int dns_resource_key_match_rr(const DnsResourceKey *key, const DnsResourceRecord *rr, const char *search_domain);
|
||||
int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRecord *rr, const char *search_domain);
|
||||
|
|
|
@ -368,13 +368,14 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co
|
|||
assert(s);
|
||||
assert(domain);
|
||||
|
||||
/* Checks if the specified domain is something to look up on
|
||||
* this scope. Note that this accepts non-qualified hostnames,
|
||||
* i.e. those without any search path prefixed yet. */
|
||||
|
||||
if (ifindex != 0 && (!s->link || s->link->ifindex != ifindex))
|
||||
return DNS_SCOPE_NO;
|
||||
|
||||
if ((SD_RESOLVED_FLAGS_MAKE(s->protocol, s->family) & flags) == 0)
|
||||
return DNS_SCOPE_NO;
|
||||
|
||||
if (dns_name_is_root(domain))
|
||||
if ((SD_RESOLVED_FLAGS_MAKE(s->protocol, s->family, 0) & flags) == 0)
|
||||
return DNS_SCOPE_NO;
|
||||
|
||||
/* Never resolve any loopback hostname or IP address via DNS,
|
||||
|
@ -385,6 +386,12 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co
|
|||
dns_name_equal(domain, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0)
|
||||
return DNS_SCOPE_NO;
|
||||
|
||||
/* Never respond to some of the domains listed in RFC6303 */
|
||||
if (dns_name_endswith(domain, "0.in-addr.arpa") > 0 ||
|
||||
dns_name_equal(domain, "255.255.255.255.in-addr.arpa") > 0 ||
|
||||
dns_name_equal(domain, "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0)
|
||||
return DNS_SCOPE_NO;
|
||||
|
||||
/* Always honour search domains for routing queries. Note that
|
||||
* we return DNS_SCOPE_YES here, rather than just
|
||||
* DNS_SCOPE_MAYBE, which means wildcard scopes won't be
|
||||
|
@ -397,10 +404,12 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co
|
|||
|
||||
case DNS_PROTOCOL_DNS:
|
||||
|
||||
if ((!dns_name_is_single_label(domain) ||
|
||||
(!(flags & SD_RESOLVED_NO_SEARCH) && dns_scope_has_search_domains(s))) &&
|
||||
dns_name_endswith(domain, "254.169.in-addr.arpa") == 0 &&
|
||||
dns_name_endswith(domain, "0.8.e.f.ip6.arpa") == 0)
|
||||
/* Exclude link-local IP ranges */
|
||||
if (dns_name_endswith(domain, "254.169.in-addr.arpa") == 0 &&
|
||||
dns_name_endswith(domain, "8.e.f.ip6.arpa") == 0 &&
|
||||
dns_name_endswith(domain, "9.e.f.ip6.arpa") == 0 &&
|
||||
dns_name_endswith(domain, "a.e.f.ip6.arpa") == 0 &&
|
||||
dns_name_endswith(domain, "b.e.f.ip6.arpa") == 0)
|
||||
return DNS_SCOPE_MAYBE;
|
||||
|
||||
return DNS_SCOPE_NO;
|
||||
|
@ -434,8 +443,27 @@ int dns_scope_good_key(DnsScope *s, DnsResourceKey *key) {
|
|||
assert(s);
|
||||
assert(key);
|
||||
|
||||
if (s->protocol == DNS_PROTOCOL_DNS)
|
||||
return true;
|
||||
/* Check if it makes sense to resolve the specified key on
|
||||
* this scope. Note that this call assumes as fully qualified
|
||||
* name, i.e. the search suffixes already appended. */
|
||||
|
||||
if (s->protocol == DNS_PROTOCOL_DNS) {
|
||||
|
||||
/* On classic DNS, lookin up non-address RRs is always
|
||||
* fine. (Specifically, we want to permit looking up
|
||||
* DNSKEY and DS records on the root and top-level
|
||||
* domains.) */
|
||||
if (!dns_resource_key_is_address(key))
|
||||
return true;
|
||||
|
||||
/* However, we refuse to look up A and AAAA RRs on the
|
||||
* root and single-label domains, under the assumption
|
||||
* that those should be resolved via LLMNR or search
|
||||
* path only, and should not be leaked onto the
|
||||
* internet. */
|
||||
return !(dns_name_is_single_label(DNS_RESOURCE_KEY_NAME(key)) ||
|
||||
dns_name_is_root(DNS_RESOURCE_KEY_NAME(key)));
|
||||
}
|
||||
|
||||
/* On mDNS and LLMNR, send A and AAAA queries only on the
|
||||
* respective scopes */
|
||||
|
@ -901,34 +929,13 @@ void dns_scope_dump(DnsScope *s, FILE *f) {
|
|||
DnsSearchDomain *dns_scope_get_search_domains(DnsScope *s) {
|
||||
assert(s);
|
||||
|
||||
/* Returns the list of *local* search domains -- not the
|
||||
* global ones. */
|
||||
|
||||
if (s->protocol != DNS_PROTOCOL_DNS)
|
||||
return NULL;
|
||||
|
||||
if (s->link)
|
||||
return s->link->search_domains;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool dns_scope_has_search_domains(DnsScope *s) {
|
||||
assert(s);
|
||||
|
||||
/* Tests if there are *any* search domains suitable for this
|
||||
* scope. This means either local or global ones */
|
||||
|
||||
if (s->protocol != DNS_PROTOCOL_DNS)
|
||||
return false;
|
||||
|
||||
if (s->manager->search_domains)
|
||||
return true;
|
||||
|
||||
if (s->link && s->link->search_domains)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
return s->manager->search_domains;
|
||||
}
|
||||
|
||||
bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name) {
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
typedef struct DnsScope DnsScope;
|
||||
|
||||
#include "resolved-dns-cache.h"
|
||||
#include "resolved-dns-dnssec.h"
|
||||
#include "resolved-dns-packet.h"
|
||||
#include "resolved-dns-server.h"
|
||||
#include "resolved-dns-zone.h"
|
||||
|
@ -44,6 +45,7 @@ struct DnsScope {
|
|||
|
||||
DnsProtocol protocol;
|
||||
int family;
|
||||
DnssecMode dnssec_mode;
|
||||
|
||||
Link *link;
|
||||
|
||||
|
@ -102,6 +104,5 @@ void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p);
|
|||
void dns_scope_dump(DnsScope *s, FILE *f);
|
||||
|
||||
DnsSearchDomain *dns_scope_get_search_domains(DnsScope *s);
|
||||
bool dns_scope_has_search_domains(DnsScope *s);
|
||||
|
||||
bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name);
|
||||
|
|
|
@ -61,10 +61,11 @@ struct DnsServer {
|
|||
int family;
|
||||
union in_addr_union address;
|
||||
|
||||
bool marked:1;
|
||||
|
||||
usec_t resend_timeout;
|
||||
usec_t max_rtt;
|
||||
|
||||
bool marked:1;
|
||||
DnsServerFeatureLevel verified_features;
|
||||
DnsServerFeatureLevel possible_features;
|
||||
size_t received_udp_packet_max;
|
||||
|
|
|
@ -481,20 +481,29 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
|
|||
return;
|
||||
}
|
||||
|
||||
/* Install the answer as answer to the transaction */
|
||||
dns_answer_unref(t->answer);
|
||||
t->answer = dns_answer_ref(p->answer);
|
||||
t->answer_rcode = DNS_PACKET_RCODE(p);
|
||||
|
||||
/* Only consider responses with equivalent query section to the request */
|
||||
if (p->question->n_keys != 1 || dns_resource_key_equal(p->question->keys[0], t->key) <= 0) {
|
||||
dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Install the answer as answer to the transaction */
|
||||
dns_answer_unref(t->answer);
|
||||
t->answer = dns_answer_ref(p->answer);
|
||||
t->answer_rcode = DNS_PACKET_RCODE(p);
|
||||
t->answer_authenticated = t->scope->dnssec_mode == DNSSEC_TRUST && DNS_PACKET_AD(p);
|
||||
|
||||
/* According to RFC 4795, section 2.9. only the RRs from the answer section shall be cached */
|
||||
if (DNS_PACKET_SHALL_CACHE(p))
|
||||
dns_cache_put(&t->scope->cache, t->key, DNS_PACKET_RCODE(p), p->answer, DNS_PACKET_ANCOUNT(p), 0, p->family, &p->sender);
|
||||
dns_cache_put(&t->scope->cache,
|
||||
t->key,
|
||||
DNS_PACKET_RCODE(p),
|
||||
p->answer,
|
||||
DNS_PACKET_ANCOUNT(p),
|
||||
t->answer_authenticated,
|
||||
0,
|
||||
p->family,
|
||||
&p->sender);
|
||||
|
||||
if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS)
|
||||
dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
|
||||
|
@ -598,7 +607,7 @@ static int dns_transaction_make_packet(DnsTransaction *t) {
|
|||
if (t->sent)
|
||||
return 0;
|
||||
|
||||
r = dns_packet_new_query(&p, t->scope->protocol, 0);
|
||||
r = dns_packet_new_query(&p, t->scope->protocol, 0, t->scope->dnssec_mode == DNSSEC_YES);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -675,7 +684,21 @@ int dns_transaction_go(DnsTransaction *t) {
|
|||
t->answer_rcode = 0;
|
||||
t->answer_source = _DNS_TRANSACTION_SOURCE_INVALID;
|
||||
|
||||
/* Check the zone, but obly if this transaction is not used
|
||||
/* Check the trust anchor. Do so only on classic DNS, since DNSSEC does not apply otherwise. */
|
||||
if (t->scope->protocol == DNS_PROTOCOL_DNS) {
|
||||
r = dns_trust_anchor_lookup(&t->scope->manager->trust_anchor, t->key, &t->answer);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0) {
|
||||
t->answer_rcode = DNS_RCODE_SUCCESS;
|
||||
t->answer_source = DNS_TRANSACTION_TRUST_ANCHOR;
|
||||
t->answer_authenticated = true;
|
||||
dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check the zone, but only if this transaction is not used
|
||||
* for probing or verifying a zone item. */
|
||||
if (set_isempty(t->zone_items)) {
|
||||
|
||||
|
@ -685,6 +708,7 @@ int dns_transaction_go(DnsTransaction *t) {
|
|||
if (r > 0) {
|
||||
t->answer_rcode = DNS_RCODE_SUCCESS;
|
||||
t->answer_source = DNS_TRANSACTION_ZONE;
|
||||
t->answer_authenticated = true;
|
||||
dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
|
||||
return 0;
|
||||
}
|
||||
|
@ -702,7 +726,7 @@ int dns_transaction_go(DnsTransaction *t) {
|
|||
/* Let's then prune all outdated entries */
|
||||
dns_cache_prune(&t->scope->cache);
|
||||
|
||||
r = dns_cache_lookup(&t->scope->cache, t->key, &t->answer_rcode, &t->answer);
|
||||
r = dns_cache_lookup(&t->scope->cache, t->key, &t->answer_rcode, &t->answer, &t->answer_authenticated);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0) {
|
||||
|
@ -817,5 +841,6 @@ static const char* const dns_transaction_source_table[_DNS_TRANSACTION_SOURCE_MA
|
|||
[DNS_TRANSACTION_NETWORK] = "network",
|
||||
[DNS_TRANSACTION_CACHE] = "cache",
|
||||
[DNS_TRANSACTION_ZONE] = "zone",
|
||||
[DNS_TRANSACTION_TRUST_ANCHOR] = "trust-anchor",
|
||||
};
|
||||
DEFINE_STRING_TABLE_LOOKUP(dns_transaction_source, DnsTransactionSource);
|
||||
|
|
|
@ -44,6 +44,7 @@ enum DnsTransactionSource {
|
|||
DNS_TRANSACTION_NETWORK,
|
||||
DNS_TRANSACTION_CACHE,
|
||||
DNS_TRANSACTION_ZONE,
|
||||
DNS_TRANSACTION_TRUST_ANCHOR,
|
||||
_DNS_TRANSACTION_SOURCE_MAX,
|
||||
_DNS_TRANSACTION_SOURCE_INVALID = -1
|
||||
};
|
||||
|
@ -68,6 +69,7 @@ struct DnsTransaction {
|
|||
DnsAnswer *answer;
|
||||
int answer_rcode;
|
||||
DnsTransactionSource answer_source;
|
||||
bool answer_authenticated;
|
||||
|
||||
usec_t start_usec;
|
||||
sd_event_source *timeout_event_source;
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2015 Lennart Poettering
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "resolved-dns-trust-anchor.h"
|
||||
|
||||
/* The DS RR from https://data.iana.org/root-anchors/root-anchors.xml */
|
||||
static const uint8_t root_digest[] =
|
||||
{ 0x49, 0xAA, 0xC1, 0x1D, 0x7B, 0x6F, 0x64, 0x46, 0x70, 0x2E, 0x54, 0xA1, 0x60, 0x73, 0x71, 0x60,
|
||||
0x7A, 0x1A, 0x41, 0x85, 0x52, 0x00, 0xFD, 0x2C, 0xE1, 0xCD, 0xDE, 0x32, 0xF2, 0x4E, 0x8F, 0xB5 };
|
||||
|
||||
int dns_trust_anchor_load(DnsTrustAnchor *d) {
|
||||
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
|
||||
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
|
||||
int r;
|
||||
|
||||
assert(d);
|
||||
|
||||
r = hashmap_ensure_allocated(&d->by_key, &dns_resource_key_hash_ops);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (hashmap_get(d->by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DS, ".")))
|
||||
return 0;
|
||||
|
||||
/* Add the RR from https://data.iana.org/root-anchors/root-anchors.xml */
|
||||
rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, "");
|
||||
if (!rr)
|
||||
return -ENOMEM;
|
||||
|
||||
rr->ds.key_tag = 19036;
|
||||
rr->ds.algorithm = DNSSEC_ALGORITHM_RSASHA256;
|
||||
rr->ds.digest_type = DNSSEC_DIGEST_SHA256;
|
||||
rr->ds.digest_size = sizeof(root_digest);
|
||||
rr->ds.digest = memdup(root_digest, rr->ds.digest_size);
|
||||
if (!rr->ds.digest)
|
||||
return -ENOMEM;
|
||||
|
||||
answer = dns_answer_new(1);
|
||||
if (!answer)
|
||||
return -ENOMEM;
|
||||
|
||||
r = dns_answer_add(answer, rr, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = hashmap_put(d->by_key, rr->key, answer);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
answer = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dns_trust_anchor_flush(DnsTrustAnchor *d) {
|
||||
DnsAnswer *a;
|
||||
|
||||
assert(d);
|
||||
|
||||
while ((a = hashmap_steal_first(d->by_key)))
|
||||
dns_answer_unref(a);
|
||||
|
||||
d->by_key = hashmap_free(d->by_key);
|
||||
}
|
||||
|
||||
int dns_trust_anchor_lookup(DnsTrustAnchor *d, DnsResourceKey *key, DnsAnswer **ret) {
|
||||
DnsAnswer *a;
|
||||
|
||||
assert(d);
|
||||
assert(key);
|
||||
assert(ret);
|
||||
|
||||
/* We only serve DS and DNSKEY RRs. */
|
||||
if (!IN_SET(key->type, DNS_TYPE_DS, DNS_TYPE_DNSKEY))
|
||||
return 0;
|
||||
|
||||
a = hashmap_get(d->by_key, key);
|
||||
if (!a)
|
||||
return 0;
|
||||
|
||||
*ret = dns_answer_ref(a);
|
||||
return 1;
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2015 Lennart Poettering
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
typedef struct DnsTrustAnchor DnsTrustAnchor;
|
||||
|
||||
#include "hashmap.h"
|
||||
#include "resolved-dns-answer.h"
|
||||
#include "resolved-dns-rr.h"
|
||||
|
||||
/* This contains a fixed database mapping domain names to DS or DNSKEY records. */
|
||||
|
||||
struct DnsTrustAnchor {
|
||||
Hashmap *by_key;
|
||||
};
|
||||
|
||||
int dns_trust_anchor_load(DnsTrustAnchor *d);
|
||||
void dns_trust_anchor_flush(DnsTrustAnchor *d);
|
||||
|
||||
int dns_trust_anchor_lookup(DnsTrustAnchor *d, DnsResourceKey* key, DnsAnswer **answer);
|
|
@ -163,7 +163,6 @@ static int dns_zone_link_item(DnsZone *z, DnsZoneItem *i) {
|
|||
}
|
||||
|
||||
static int dns_zone_item_probe_start(DnsZoneItem *i) {
|
||||
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
|
||||
DnsTransaction *t;
|
||||
int r;
|
||||
|
||||
|
@ -172,12 +171,14 @@ static int dns_zone_item_probe_start(DnsZoneItem *i) {
|
|||
if (i->probe_transaction)
|
||||
return 0;
|
||||
|
||||
key = dns_resource_key_new(i->rr->key->class, DNS_TYPE_ANY, DNS_RESOURCE_KEY_NAME(i->rr->key));
|
||||
if (!key)
|
||||
return -ENOMEM;
|
||||
|
||||
t = dns_scope_find_transaction(i->scope, key, false);
|
||||
t = dns_scope_find_transaction(i->scope, &DNS_RESOURCE_KEY_CONST(i->rr->key->class, DNS_TYPE_ANY, DNS_RESOURCE_KEY_NAME(i->rr->key)), false);
|
||||
if (!t) {
|
||||
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
|
||||
|
||||
key = dns_resource_key_new(i->rr->key->class, DNS_TYPE_ANY, DNS_RESOURCE_KEY_NAME(i->rr->key));
|
||||
if (!key)
|
||||
return -ENOMEM;
|
||||
|
||||
r = dns_transaction_new(&t, i->scope, key);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
|
|
@ -18,3 +18,4 @@ Resolve.DNS, config_parse_dns_servers, DNS_SERVER_SYSTEM, 0
|
|||
Resolve.FallbackDNS, config_parse_dns_servers, DNS_SERVER_FALLBACK, 0
|
||||
Resolve.Domains, config_parse_search_domains, 0, 0
|
||||
Resolve.LLMNR, config_parse_support, 0, offsetof(Manager, llmnr_support)
|
||||
Resolve.DNSSEC, config_parse_dnssec, 0, 0
|
||||
|
|
|
@ -478,6 +478,10 @@ int manager_new(Manager **ret) {
|
|||
m->read_resolv_conf = true;
|
||||
m->need_builtin_fallbacks = true;
|
||||
|
||||
r = dns_trust_anchor_load(&m->trust_anchor);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_event_default(&m->event);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
@ -572,6 +576,8 @@ Manager *manager_free(Manager *m) {
|
|||
free(m->llmnr_hostname);
|
||||
free(m->mdns_hostname);
|
||||
|
||||
dns_trust_anchor_flush(&m->trust_anchor);
|
||||
|
||||
free(m);
|
||||
|
||||
return NULL;
|
||||
|
|
|
@ -44,6 +44,7 @@ enum Support {
|
|||
#include "resolved-dns-search-domain.h"
|
||||
#include "resolved-dns-server.h"
|
||||
#include "resolved-dns-stream.h"
|
||||
#include "resolved-dns-trust-anchor.h"
|
||||
#include "resolved-link.h"
|
||||
|
||||
#define MANAGER_SEARCH_DOMAINS_MAX 32
|
||||
|
@ -85,6 +86,8 @@ struct Manager {
|
|||
bool read_resolv_conf:1;
|
||||
usec_t resolv_conf_mtime;
|
||||
|
||||
DnsTrustAnchor trust_anchor;
|
||||
|
||||
LIST_HEAD(DnsScope, dns_scopes);
|
||||
DnsScope *unicast_scope;
|
||||
|
||||
|
|
|
@ -16,3 +16,4 @@
|
|||
#FallbackDNS=@DNS_SERVERS@
|
||||
#Domains=
|
||||
#LLMNR=yes
|
||||
#DNSSEC=no
|
||||
|
|
Loading…
Reference in New Issue