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=a9bd8b13988dc31d6e5641921097be38fc3d6e08;hp=7793a014df930310497092e2aa22ad96565b963c;hb=56f64d95763a799ba4475daf44d8e9f72a1bd474;hpb=85818582574bcd1c3707c2a4609c9e283e47493f diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index 7793a014d..a9bd8b139 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,8 +858,9 @@ fail: return r; } -int dns_packet_read_name(DnsPacket *p, char **_ret, size_t *start) { - size_t saved_rindex, after_rindex = 0; +int dns_packet_read_name(DnsPacket *p, char **_ret, + bool allow_compression, size_t *start) { + size_t saved_rindex, after_rindex = 0, jump_barrier; _cleanup_free_ char *ret = NULL; size_t n = 0, allocated = 0; bool first = true; @@ -806,6 +870,7 @@ int dns_packet_read_name(DnsPacket *p, char **_ret, size_t *start) { assert(_ret); saved_rindex = p->rindex; + jump_barrier = p->rindex; for (;;) { uint8_t c, d; @@ -843,7 +908,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 */ @@ -852,7 +917,7 @@ int dns_packet_read_name(DnsPacket *p, char **_ret, size_t *start) { goto fail; ptr = (uint16_t) (c & ~0xc0) << 8 | (uint16_t) d; - if (ptr < DNS_PACKET_HEADER_SIZE || ptr >= saved_rindex) { + if (ptr < DNS_PACKET_HEADER_SIZE || ptr >= jump_barrier) { r = -EBADMSG; goto fail; } @@ -860,9 +925,13 @@ int dns_packet_read_name(DnsPacket *p, char **_ret, size_t *start) { if (after_rindex == 0) after_rindex = p->rindex; + /* Jumps are limited to a "prior occurence" (RFC-1035 4.1.4) */ + jump_barrier = ptr; p->rindex = ptr; - } else + } else { + r = -EBADMSG; goto fail; + } } if (!GREEDY_REALLOC(ret, allocated, n + 1)) { @@ -900,7 +969,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 +999,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 +1092,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: @@ -1008,6 +1114,13 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) { case DNS_TYPE_TXT: { char *s; + /* RFC 1035 says that TXT must be at least one + string. Reject empty records. */ + if (!rdlength) { + r = -EBADMSG; + goto fail; + } + while (p->rindex < offset + rdlength) { r = dns_packet_read_string(p, &s, NULL); if (r < 0) @@ -1031,11 +1144,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 +1176,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 +1194,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 +1233,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 +1246,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 +1362,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 +1416,8 @@ int dns_packet_extract(DnsPacket *p) { p->answer = answer; answer = NULL; + p->extracted = true; + r = 0; finish: @@ -1258,3 +1453,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);