X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fresolve%2Fresolved-dns-packet.c;h=e5d07b3b1ff06666e4be428fd4f78ac31ec62b48;hp=7793a014df930310497092e2aa22ad96565b963c;hb=b37d45c9ab5f645502695e47d268af1a54216e0e;hpb=85818582574bcd1c3707c2a4609c9e283e47493f diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index 7793a014d..e5d07b3b1 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -22,6 +22,7 @@ #include "utf8.h" #include "util.h" #include "strv.h" +#include "unaligned.h" #include "resolved-dns-domain.h" #include "resolved-dns-packet.h" @@ -311,8 +312,7 @@ int dns_packet_append_uint16(DnsPacket *p, uint16_t v, size_t *start) { if (r < 0) return r; - ((uint8_t*) d)[0] = (uint8_t) (v >> 8); - ((uint8_t*) d)[1] = (uint8_t) v; + unaligned_write_be16(d, v); return 0; } @@ -327,10 +327,7 @@ int dns_packet_append_uint32(DnsPacket *p, uint32_t v, size_t *start) { if (r < 0) return r; - ((uint8_t*) d)[0] = (uint8_t) (v >> 24); - ((uint8_t*) d)[1] = (uint8_t) (v >> 16); - ((uint8_t*) d)[2] = (uint8_t) (v >> 8); - ((uint8_t*) d)[3] = (uint8_t) v; + unaligned_write_be32(d, v); return 0; } @@ -377,7 +374,8 @@ int dns_packet_append_label(DnsPacket *p, const char *d, size_t l, size_t *start return 0; } -int dns_packet_append_name(DnsPacket *p, const char *name, size_t *start) { +int dns_packet_append_name(DnsPacket *p, const char *name, + bool allow_compression, size_t *start) { size_t saved_size; int r; @@ -389,9 +387,11 @@ int dns_packet_append_name(DnsPacket *p, const char *name, size_t *start) { while (*name) { _cleanup_free_ char *s = NULL; char label[DNS_LABEL_MAX]; - size_t n; + size_t n = 0; + int k; - n = PTR_TO_SIZE(hashmap_get(p->names, name)); + if (allow_compression) + n = PTR_TO_SIZE(hashmap_get(p->names, name)); if (n > 0) { assert(n < p->size); @@ -414,19 +414,32 @@ int dns_packet_append_name(DnsPacket *p, const char *name, size_t *start) { if (r < 0) goto fail; - r = dns_packet_append_label(p, label, r, &n); - if (r < 0) + if (p->protocol == DNS_PROTOCOL_DNS) + k = dns_label_apply_idna(label, r, label, sizeof(label)); + else + k = dns_label_undo_idna(label, r, label, sizeof(label)); + if (k < 0) { + r = k; goto fail; + } + if (k > 0) + r = k; - r = hashmap_ensure_allocated(&p->names, dns_name_hash_func, dns_name_compare_func); + r = dns_packet_append_label(p, label, r, &n); if (r < 0) goto fail; - r = hashmap_put(p->names, s, SIZE_TO_PTR(n)); - if (r < 0) - goto fail; + if (allow_compression) { + r = hashmap_ensure_allocated(&p->names, &dns_name_hash_ops); + if (r < 0) + goto fail; - s = NULL; + r = hashmap_put(p->names, s, SIZE_TO_PTR(n)); + if (r < 0) + goto fail; + + s = NULL; + } } r = dns_packet_append_uint8(p, 0, NULL); @@ -453,7 +466,7 @@ int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *k, size_t *start) saved_size = p->size; - r = dns_packet_append_name(p, DNS_RESOURCE_KEY_NAME(k), NULL); + r = dns_packet_append_name(p, DNS_RESOURCE_KEY_NAME(k), true, NULL); if (r < 0) goto fail; @@ -512,14 +525,14 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star if (r < 0) goto fail; - r = dns_packet_append_name(p, rr->srv.name, NULL); + r = dns_packet_append_name(p, rr->srv.name, true, NULL); break; case DNS_TYPE_PTR: case DNS_TYPE_NS: case DNS_TYPE_CNAME: case DNS_TYPE_DNAME: - r = dns_packet_append_name(p, rr->ptr.name, NULL); + r = dns_packet_append_name(p, rr->ptr.name, true, NULL); break; case DNS_TYPE_HINFO: @@ -553,11 +566,11 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star break; case DNS_TYPE_SOA: - r = dns_packet_append_name(p, rr->soa.mname, NULL); + r = dns_packet_append_name(p, rr->soa.mname, true, NULL); if (r < 0) goto fail; - r = dns_packet_append_name(p, rr->soa.rname, NULL); + r = dns_packet_append_name(p, rr->soa.rname, true, NULL); if (r < 0) goto fail; @@ -585,7 +598,7 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star if (r < 0) goto fail; - r = dns_packet_append_name(p, rr->mx.exchange, NULL); + r = dns_packet_append_name(p, rr->mx.exchange, true, NULL); break; case DNS_TYPE_LOC: @@ -605,21 +618,22 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star if (r < 0) goto fail; - r = dns_packet_append_uint16(p, rr->loc.latitude, NULL); + r = dns_packet_append_uint32(p, rr->loc.latitude, NULL); if (r < 0) goto fail; - r = dns_packet_append_uint16(p, rr->loc.longitude, NULL); + r = dns_packet_append_uint32(p, rr->loc.longitude, NULL); if (r < 0) goto fail; - r = dns_packet_append_uint16(p, rr->loc.altitude, NULL); + r = dns_packet_append_uint32(p, rr->loc.altitude, NULL); break; case DNS_TYPE_SSHFP: r = dns_packet_append_uint8(p, rr->sshfp.algorithm, NULL); if (r < 0) goto fail; + r = dns_packet_append_uint8(p, rr->sshfp.fptype, NULL); if (r < 0) goto fail; @@ -627,6 +641,58 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star r = dns_packet_append_blob(p, rr->sshfp.key, rr->sshfp.key_size, NULL); break; + case DNS_TYPE_DNSKEY: + r = dns_packet_append_uint16(p, dnskey_to_flags(rr), NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint8(p, 3u, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint8(p, rr->dnskey.algorithm, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_blob(p, rr->dnskey.key, rr->dnskey.key_size, NULL); + break; + + case DNS_TYPE_RRSIG: + r = dns_packet_append_uint16(p, rr->rrsig.type_covered, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint8(p, rr->rrsig.algorithm, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint8(p, rr->rrsig.labels, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint32(p, rr->rrsig.original_ttl, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint32(p, rr->rrsig.expiration, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint32(p, rr->rrsig.inception, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint8(p, rr->rrsig.key_tag, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_name(p, rr->rrsig.signer, false, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_blob(p, rr->rrsig.signature, rr->rrsig.signature_size, NULL); + break; + case _DNS_TYPE_INVALID: /* unparseable */ default: @@ -724,8 +790,8 @@ int dns_packet_read_uint16(DnsPacket *p, uint16_t *ret, size_t *start) { if (r < 0) return r; - *ret = (((uint16_t) ((uint8_t*) d)[0]) << 8) | - ((uint16_t) ((uint8_t*) d)[1]); + *ret = unaligned_read_be16(d); + return 0; } @@ -739,10 +805,7 @@ int dns_packet_read_uint32(DnsPacket *p, uint32_t *ret, size_t *start) { if (r < 0) return r; - *ret = (((uint32_t) ((uint8_t*) d)[0]) << 24) | - (((uint32_t) ((uint8_t*) d)[1]) << 16) | - (((uint32_t) ((uint8_t*) d)[2]) << 8) | - ((uint32_t) ((uint8_t*) d)[3]); + *ret = unaligned_read_be32(d); return 0; } @@ -795,7 +858,8 @@ fail: return r; } -int dns_packet_read_name(DnsPacket *p, char **_ret, size_t *start) { +int dns_packet_read_name(DnsPacket *p, char **_ret, + bool allow_compression, size_t *start) { size_t saved_rindex, after_rindex = 0; _cleanup_free_ char *ret = NULL; size_t n = 0, allocated = 0; @@ -843,7 +907,7 @@ int dns_packet_read_name(DnsPacket *p, char **_ret, size_t *start) { memcpy(ret + n, t, r); n += r; continue; - } else if ((c & 0xc0) == 0xc0) { + } else if (allow_compression && (c & 0xc0) == 0xc0) { uint16_t ptr; /* Pointer */ @@ -900,7 +964,7 @@ int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start) { saved_rindex = p->rindex; - r = dns_packet_read_name(p, &name, NULL); + r = dns_packet_read_name(p, &name, true, NULL); if (r < 0) goto fail; @@ -930,6 +994,43 @@ fail: return r; } +static int dns_packet_read_public_key(DnsPacket *p, size_t length, + void **dp, size_t *lengthp, + size_t *start) { + int r; + const void *d; + void *d2; + + r = dns_packet_read(p, length, &d, NULL); + if (r < 0) + return r; + + d2 = memdup(d, length); + if (!d2) + return -ENOMEM; + + *dp = d2; + *lengthp = length; + return 0; +} + +static bool loc_size_ok(uint8_t size) { + uint8_t m = size >> 4, e = size & 0xF; + + return m <= 9 && e <= 9 && (m > 0 || e == 0); +} + +static int dnskey_parse_flags(DnsResourceRecord *rr, uint16_t flags) { + assert(rr); + + if (flags & ~(DNSKEY_FLAG_SEP | DNSKEY_FLAG_ZONE_KEY)) + return -EBADMSG; + + rr->dnskey.zone_key_flag = flags & DNSKEY_FLAG_ZONE_KEY; + rr->dnskey.sep_flag = flags & DNSKEY_FLAG_SEP; + return 0; +} + int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) { _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; @@ -986,14 +1087,14 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) { r = dns_packet_read_uint16(p, &rr->srv.port, NULL); if (r < 0) goto fail; - r = dns_packet_read_name(p, &rr->srv.name, NULL); + r = dns_packet_read_name(p, &rr->srv.name, true, NULL); break; case DNS_TYPE_PTR: case DNS_TYPE_NS: case DNS_TYPE_CNAME: case DNS_TYPE_DNAME: - r = dns_packet_read_name(p, &rr->ptr.name, NULL); + r = dns_packet_read_name(p, &rr->ptr.name, true, NULL); break; case DNS_TYPE_HINFO: @@ -1031,11 +1132,11 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) { break; case DNS_TYPE_SOA: - r = dns_packet_read_name(p, &rr->soa.mname, NULL); + r = dns_packet_read_name(p, &rr->soa.mname, true, NULL); if (r < 0) goto fail; - r = dns_packet_read_name(p, &rr->soa.rname, NULL); + r = dns_packet_read_name(p, &rr->soa.rname, true, NULL); if (r < 0) goto fail; @@ -1063,7 +1164,7 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) { if (r < 0) goto fail; - r = dns_packet_read_name(p, &rr->mx.exchange, NULL); + r = dns_packet_read_name(p, &rr->mx.exchange, true, NULL); break; case DNS_TYPE_LOC: { @@ -1081,14 +1182,29 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) { if (r < 0) goto fail; + if (!loc_size_ok(rr->loc.size)) { + r = -EBADMSG; + goto fail; + } + r = dns_packet_read_uint8(p, &rr->loc.horiz_pre, NULL); if (r < 0) goto fail; + if (!loc_size_ok(rr->loc.horiz_pre)) { + r = -EBADMSG; + goto fail; + } + r = dns_packet_read_uint8(p, &rr->loc.vert_pre, NULL); if (r < 0) goto fail; + if (!loc_size_ok(rr->loc.vert_pre)) { + r = -EBADMSG; + goto fail; + } + r = dns_packet_read_uint32(p, &rr->loc.latitude, NULL); if (r < 0) goto fail; @@ -1105,12 +1221,11 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) { } else { dns_packet_rewind(p, pos); rr->unparseable = true; - /* fall through */ + goto unparseable; } } case DNS_TYPE_SSHFP: - r = dns_packet_read_uint8(p, &rr->sshfp.algorithm, NULL); if (r < 0) goto fail; @@ -1119,20 +1234,83 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) { if (r < 0) goto fail; - r = dns_packet_read(p, rdlength - 2, &d, NULL); + r = dns_packet_read_public_key(p, rdlength - 2, + &rr->sshfp.key, &rr->sshfp.key_size, + NULL); + break; + + case DNS_TYPE_DNSKEY: { + uint16_t flags; + uint8_t proto; + + r = dns_packet_read_uint16(p, &flags, NULL); if (r < 0) goto fail; - rr->sshfp.key = memdup(d, rdlength - 2); - if (!rr->sshfp.key) { - r = -ENOMEM; + r = dnskey_parse_flags(rr, flags); + if (r < 0) + goto fail; + + r = dns_packet_read_uint8(p, &proto, NULL); + if (r < 0) + goto fail; + + /* protocol is required to be always 3 */ + if (proto != 3) { + r = -EBADMSG; goto fail; } - rr->sshfp.key_size = rdlength - 2; + r = dns_packet_read_uint8(p, &rr->dnskey.algorithm, NULL); + if (r < 0) + goto fail; + + r = dns_packet_read_public_key(p, rdlength - 4, + &rr->dnskey.key, &rr->dnskey.key_size, + NULL); + break; + } + + case DNS_TYPE_RRSIG: + r = dns_packet_read_uint16(p, &rr->rrsig.type_covered, NULL); + if (r < 0) + goto fail; + + r = dns_packet_read_uint8(p, &rr->rrsig.algorithm, NULL); + if (r < 0) + goto fail; + + r = dns_packet_read_uint8(p, &rr->rrsig.labels, NULL); + if (r < 0) + goto fail; + + r = dns_packet_read_uint32(p, &rr->rrsig.original_ttl, NULL); + if (r < 0) + goto fail; + + r = dns_packet_read_uint32(p, &rr->rrsig.expiration, NULL); + if (r < 0) + goto fail; + + r = dns_packet_read_uint32(p, &rr->rrsig.inception, NULL); + if (r < 0) + goto fail; + + r = dns_packet_read_uint16(p, &rr->rrsig.key_tag, NULL); + if (r < 0) + goto fail; + + r = dns_packet_read_name(p, &rr->rrsig.signer, false, NULL); + if (r < 0) + goto fail; + + r = dns_packet_read_public_key(p, offset + rdlength - p->rindex, + &rr->rrsig.signature, &rr->rrsig.signature_size, + NULL); break; default: + unparseable: r = dns_packet_read(p, rdlength, &d, NULL); if (r < 0) goto fail; @@ -1172,6 +1350,9 @@ int dns_packet_extract(DnsPacket *p) { unsigned n, i; int r; + if (p->extracted) + return 0; + saved_rindex = p->rindex; dns_packet_rewind(p, DNS_PACKET_HEADER_SIZE); @@ -1223,6 +1404,8 @@ int dns_packet_extract(DnsPacket *p) { p->answer = answer; answer = NULL; + p->extracted = true; + r = 0; finish: @@ -1258,3 +1441,15 @@ static const char* const dns_protocol_table[_DNS_PROTOCOL_MAX] = { [DNS_PROTOCOL_LLMNR] = "llmnr", }; DEFINE_STRING_TABLE_LOOKUP(dns_protocol, DnsProtocol); + +static const char* const dnssec_algorithm_table[_DNSSEC_ALGORITHM_MAX_DEFINED] = { + [DNSSEC_ALGORITHM_RSAMD5] = "RSAMD5", + [DNSSEC_ALGORITHM_DH] = "DH", + [DNSSEC_ALGORITHM_DSA] = "DSA", + [DNSSEC_ALGORITHM_ECC] = "ECC", + [DNSSEC_ALGORITHM_RSASHA1] = "RSASHA1", + [DNSSEC_ALGORITHM_INDIRECT] = "INDIRECT", + [DNSSEC_ALGORITHM_PRIVATEDNS] = "PRIVATEDNS", + [DNSSEC_ALGORITHM_PRIVATEOID] = "PRIVATEOID", +}; +DEFINE_STRING_TABLE_LOOKUP(dnssec_algorithm, int);