From 87070192375fa54b94244837ddba09c6e60425c9 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Sat, 1 Mar 2014 20:48:24 +0000 Subject: Mass edit of INSECURE->BOGUS returns for server failure/bad input. --- src/dnssec.c | 157 +++++++++++++++++++++++++++-------------------------------- 1 file changed, 73 insertions(+), 84 deletions(-) diff --git a/src/dnssec.c b/src/dnssec.c index c68640a..ec34f2f 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -592,8 +592,8 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int STAT_SECURE if it validates. STAT_SECURE_WILDCARD if it validates and is the result of wildcard expansion. STAT_NO_SIG no RRsigs found. - STAT_INSECURE can't validate (no RRSIG, bad packet). - STAT_BOGUS signature is wrong. + STAT_INSECURE RRset empty. + STAT_BOGUS signature is wrong, bad packet. STAT_NEED_KEY need DNSKEY to complete validation (name is returned in keyname) if key is non-NULL, use that key, which has the algo and tag given in the params of those names, @@ -614,7 +614,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in u16 *rr_desc = get_desc(type); if (!(p = skip_questions(header, plen))) - return STAT_INSECURE; + return STAT_BOGUS; name_labels = count_labels(name); /* For 4035 5.3.2 check */ @@ -628,7 +628,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in pstart = p; if (!(res = extract_name(header, plen, &p, name, 0, 10))) - return STAT_INSECURE; /* bad packet */ + return STAT_BOGUS; /* bad packet */ GETSHORT(stype, p); GETSHORT(sclass, p); @@ -639,14 +639,14 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in GETSHORT(rdlen, p); if (!CHECK_LEN(header, p, plen, rdlen)) - return STAT_INSECURE; + return STAT_BOGUS; if (res == 1 && sclass == class) { if (stype == type) { if (!expand_workspace(&rrset, &rrset_sz, rrsetidx)) - return STAT_INSECURE; + return STAT_BOGUS; rrset[rrsetidx++] = pstart; } @@ -654,14 +654,14 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in if (stype == T_RRSIG) { if (rdlen < 18) - return STAT_INSECURE; /* bad packet */ + return STAT_BOGUS; /* bad packet */ GETSHORT(type_covered, p); if (type_covered == type) { if (!expand_workspace(&sigs, &sig_sz, sigidx)) - return STAT_INSECURE; + return STAT_BOGUS; sigs[sigidx++] = pdata; } @@ -671,7 +671,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in } if (!ADD_RDLEN(header, p, plen, rdlen)) - return STAT_INSECURE; + return STAT_BOGUS; } /* RRset empty */ @@ -710,7 +710,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in GETSHORT(key_tag, p); if (!extract_name(header, plen, &p, keyname, 1, 0)) - return STAT_INSECURE; + return STAT_BOGUS; /* RFC 4035 5.3.1 says that the Signer's Name field MUST equal the name of the zone containing the RRset. We can't tell that @@ -765,7 +765,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in p = rrset[i]; if (!extract_name(header, plen, &p, name, 1, 10)) - return STAT_INSECURE; + return STAT_BOGUS; name_start = name; @@ -788,7 +788,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in p += 8; /* skip class, type, ttl */ GETSHORT(rdlen, p); if (!CHECK_LEN(header, p, plen, rdlen)) - return STAT_INSECURE; + return STAT_BOGUS; end = p + rdlen; @@ -839,10 +839,10 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in /* The DNS packet is expected to contain the answer to a DNSKEY query. Put all DNSKEYs in the answer which are valid into the cache. return codes: - STAT_INSECURE bad packet, no DNSKEYs in reply. + STAT_INSECURE No DNSKEYs in reply. STAT_SECURE At least one valid DNSKEY found and in cache. STAT_BOGUS No DNSKEYs found, which can be validated with DS, - or self-sign for DNSKEY RRset is not valid. + or self-sign for DNSKEY RRset is not valid, bad packet. STAT_NEED_DS DS records to validate a key not found, name in keyname */ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class) @@ -855,13 +855,16 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch if (ntohs(header->qdcount) != 1 || !extract_name(header, plen, &p, name, 1, 4)) - return STAT_INSECURE; + return STAT_BOGUS; GETSHORT(qtype, p); GETSHORT(qclass, p); - if (qtype != T_DNSKEY || qclass != class || ntohs(header->ancount) == 0) + if (qtype != T_DNSKEY || qclass != class) return STAT_BOGUS; + + if (ntohs(header->ancount) == 0) + return STAT_INSECURE; /* See if we have cached a DS record which validates this key */ if (!(crecp = cache_find_by_name(NULL, name, now, F_DS))) @@ -879,7 +882,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch { /* Ensure we have type, class TTL and length */ if (!(rc = extract_name(header, plen, &p, name, 0, 10))) - return STAT_INSECURE; /* bad packet */ + return STAT_BOGUS; /* bad packet */ GETSHORT(qtype, p); GETSHORT(qclass, p); @@ -887,7 +890,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch GETSHORT(rdlen, p); if (!CHECK_LEN(header, p, plen, rdlen) || rdlen < 4) - return STAT_INSECURE; /* bad packet */ + return STAT_BOGUS; /* bad packet */ if (qclass != class || qtype != T_DNSKEY || rc == 2) { @@ -914,7 +917,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch { if (key) blockdata_free(key); - return STAT_INSECURE; /* bad packet */ + return STAT_BOGUS; /* bad packet */ } /* No zone key flag or malloc failure */ @@ -976,7 +979,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch GETSHORT(rdlen, p); if (!CHECK_LEN(header, p, plen, rdlen)) - return STAT_INSECURE; /* bad packet */ + return STAT_BOGUS; /* bad packet */ if (qclass == class && rc == 1) { @@ -985,7 +988,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch if (qtype == T_DNSKEY) { if (rdlen < 4) - return STAT_INSECURE; /* bad packet */ + return STAT_BOGUS; /* bad packet */ GETSHORT(flags, p); if (*p++ != 3) @@ -1017,7 +1020,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch { /* RRSIG, cache if covers DNSKEY RRset */ if (rdlen < 18) - return STAT_INSECURE; /* bad packet */ + return STAT_BOGUS; /* bad packet */ GETSHORT(type_covered, p); @@ -1049,7 +1052,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch } if (!ADD_RDLEN(header, p, plen, rdlen)) - return STAT_INSECURE; /* bad packet */ + return STAT_BOGUS; /* bad packet */ } /* commit cache insert. */ @@ -1064,10 +1067,10 @@ 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. return codes: - STAT_INSECURE bad packet, no DS in reply, proven no DS in reply. + STAT_INSECURE no DS in reply or not signed. STAT_SECURE At least one valid DS found and in cache. STAT_NO_DS It's proved there's no DS here. - STAT_BOGUS At least one DS found, which fails validation. + STAT_BOGUS At least one DS found, which fails validation, bad packet. STAT_NEED_DNSKEY DNSKEY records to validate a DS not found, name in keyname */ @@ -1078,7 +1081,7 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char if (ntohs(header->qdcount) != 1 || !(p = skip_name(p, header, plen, 4))) - return STAT_INSECURE; + return STAT_BOGUS; GETSHORT(qtype, p); GETSHORT(qclass, p); @@ -1096,7 +1099,7 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char p += 4; /* qtype, qclass */ if (!(p = skip_section(p, ntohs(header->ancount), header, plen))) - return STAT_INSECURE; + return STAT_BOGUS; if (val == STAT_BOGUS) log_query(F_UPSTREAM, name, NULL, "BOGUS DS"); @@ -1116,7 +1119,7 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char for (i = ntohs(header->nscount); i != 0; i--) { if (!(p = skip_name(p, header, plen, 0))) - return STAT_INSECURE; + return STAT_BOGUS; GETSHORT(qtype, p); GETSHORT(qclass, p); @@ -1124,7 +1127,7 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char GETSHORT(rdlen, p); if (!CHECK_LEN(header, p, plen, rdlen)) - return STAT_INSECURE; /* bad packet */ + return STAT_BOGUS; /* bad packet */ if (qclass != class || qtype != T_SOA) { @@ -1137,10 +1140,10 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char /* MNAME */ if (!(p = skip_name(p, header, plen, 0))) - return STAT_INSECURE; + return STAT_BOGUS; /* RNAME */ if (!(p = skip_name(p, header, plen, 20))) - return STAT_INSECURE; + return STAT_BOGUS; p += 16; /* SERIAL REFRESH RETRY EXPIRE */ GETLONG(ttl, p); /* minTTL */ @@ -1237,7 +1240,7 @@ static int find_nsec_records(struct dns_header *header, size_t plen, unsigned ch static unsigned char **nsecset = NULL; static int nsecset_sz = 0; - int type_found = -1; + int type_found = 0; unsigned char *p = skip_questions(header, plen); int type, class, rdlen, i, nsecs_found; @@ -1296,12 +1299,12 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi { p = nsecs[i]; if (!extract_name(header, plen, &p, workspace1, 1, 10)) - return STAT_INSECURE; + return STAT_BOGUS; p += 8; /* class, type, TTL */ GETSHORT(rdlen, p); psave = p; if (!extract_name(header, plen, &p, workspace2, 1, 10)) - return STAT_INSECURE; + return STAT_BOGUS; rc = hostname_cmp(workspace1, name); @@ -1319,7 +1322,7 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi while (rdlen >= 2) { if (!CHECK_LEN(header, p, plen, rdlen)) - return STAT_INSECURE; + return STAT_BOGUS; if (p[0] == type >> 8) { @@ -1434,7 +1437,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns for (i = 0; i < nsec_count; i++) { if (!(p = skip_name(nsecs[i], header, plen, 15))) - return STAT_INSECURE; /* bad packet */ + return STAT_BOGUS; /* bad packet */ p += 10; /* type, class, TTL, rdlen */ algo = *p++; @@ -1452,7 +1455,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns salt_len = *p++; salt = p; if (!CHECK_LEN(header, salt, plen, salt_len)) - return STAT_INSECURE; /* bad packet */ + return STAT_BOGUS; /* bad packet */ /* Now prune so we only have NSEC3 records with same iterations, salt and algo */ for (i = 0; i < nsec_count; i++) @@ -1463,7 +1466,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns nsecs[i] = NULL; /* Speculative, will be restored if OK. */ if (!(p = skip_name(nsec3p, header, plen, 15))) - return STAT_INSECURE; /* bad packet */ + return STAT_BOGUS; /* bad packet */ p += 10; /* type, class, TTL, rdlen */ @@ -1480,7 +1483,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns continue; if (!CHECK_LEN(header, p, plen, salt_len)) - return STAT_INSECURE; /* bad packet */ + return STAT_BOGUS; /* bad packet */ if (memcmp(p, salt, salt_len) != 0) continue; @@ -1491,7 +1494,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns /* Algo is checked as 1 above */ if (!(hash = hash_find("sha1"))) - return STAT_INSECURE; + return STAT_BOGUS; /* Now, we need the "closest encloser NSEC3" */ closest_encloser = name; @@ -1503,14 +1506,14 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns closest_encloser++; if ((digest_len = hash_name(closest_encloser, &digest, hash, salt, salt_len, iterations)) == 0) - return STAT_INSECURE; + return STAT_BOGUS; for (i = 0; i < nsec_count; i++) if ((p = nsecs[i])) { if (!extract_name(header, plen, &p, workspace1, 1, 0) || !(base32_len = base32_decode(workspace1, (unsigned char *)workspace2))) - return STAT_INSECURE; + return STAT_BOGUS; if (digest_len == base32_len && memcmp(digest, workspace2, digest_len) == 0) @@ -1543,14 +1546,14 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns p += 5 + salt_len; /* algo, flags, iterations, salt_len, salt */ hash_len = *p++; if (!CHECK_LEN(header, p, plen, hash_len)) - return STAT_INSECURE; /* bad packet */ + return STAT_BOGUS; /* bad packet */ p += hash_len; rdlen -= p - psave; while (rdlen >= 2) { if (!CHECK_LEN(header, p, plen, rdlen)) - return STAT_INSECURE; + return STAT_BOGUS; if (p[0] == type >> 8) { @@ -1570,20 +1573,20 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns /* Look for NSEC3 that proves the non-existence of the next-closest encloser */ if ((digest_len = hash_name(next_closest, &digest, hash, salt, salt_len, iterations)) == 0) - return STAT_INSECURE; + return STAT_BOGUS; for (i = 0; i < nsec_count; i++) if ((p = nsecs[i])) { if (!extract_name(header, plen, &p, workspace1, 1, 0) || !(base32_len = base32_decode(workspace1, (unsigned char *)workspace2))) - return STAT_INSECURE; + return STAT_BOGUS; p += 15 + salt_len; /* class, type, TTL, rdlen, algo, flags, iterations, salt_len, salt */ hash_len = *p++; /* p now points to next hashed name */ if (!CHECK_LEN(header, p, plen, hash_len)) - return STAT_INSECURE; + return STAT_BOGUS; if (digest_len == base32_len && hash_len == base32_len) { @@ -1611,20 +1614,20 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns *wildcard = '*'; if ((digest_len = hash_name(wildcard, &digest, hash, salt, salt_len, iterations)) == 0) - return STAT_INSECURE; + return STAT_BOGUS; for (i = 0; i < nsec_count; i++) if ((p = nsecs[i])) { if (!extract_name(header, plen, &p, workspace1, 1, 0) || !(base32_len = base32_decode(workspace1, (unsigned char *)workspace2))) - return STAT_INSECURE; + return STAT_BOGUS; p += 15 + salt_len; /* class, type, TTL, rdlen, algo, flags, iterations, salt_len, salt */ hash_len = *p++; /* p now points to next hashed name */ if (!CHECK_LEN(header, p, plen, hash_len)) - return STAT_INSECURE; + return STAT_BOGUS; if (digest_len == base32_len && hash_len == base32_len) { @@ -1659,16 +1662,16 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch if (neganswer) *neganswer = 0; - if (RCODE(header) == SERVFAIL) + if (RCODE(header) == SERVFAIL || ntohs(header->qdcount) != 1) return STAT_BOGUS; - if ((RCODE(header) != NXDOMAIN && RCODE(header) != NOERROR) || ntohs(header->qdcount) != 1) + if (RCODE(header) != NXDOMAIN && RCODE(header) != NOERROR) return STAT_INSECURE; qname = p1 = (unsigned char *)(header+1); if (!extract_name(header, plen, &p1, name, 1, 4)) - return STAT_INSECURE; + return STAT_BOGUS; GETSHORT(qtype, p1); GETSHORT(qclass, p1); @@ -1684,7 +1687,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch /* leave pointer to missing name in qname */ if (!(rc = extract_name(header, plen, &p1, name, 0, 10))) - return STAT_INSECURE; /* bad packet */ + return STAT_BOGUS; /* bad packet */ GETSHORT(type2, p1); GETSHORT(class2, p1); @@ -1705,7 +1708,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch /* looped CNAMES */ if (!cname_count-- || !extract_name(header, plen, &p1, name, 1, 0)) - return STAT_INSECURE; + return STAT_BOGUS; p1 = ans_start; goto cname_loop; @@ -1713,7 +1716,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch } if (!ADD_RDLEN(header, p1, plen, rdlen2)) - return STAT_INSECURE; + return STAT_BOGUS; } if (neganswer && !have_answer) @@ -1726,7 +1729,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch for (p1 = ans_start, i = 0; i < ntohs(header->ancount) + ntohs(header->nscount); i++) { if (!extract_name(header, plen, &p1, name, 1, 10)) - return STAT_INSECURE; /* bad packet */ + return STAT_BOGUS; /* bad packet */ GETSHORT(type1, p1); GETSHORT(class1, p1); @@ -1740,7 +1743,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch for (p2 = ans_start, j = 0; j < i; j++) { if (!(rc = extract_name(header, plen, &p2, name, 0, 10))) - return STAT_INSECURE; /* bad packet */ + return STAT_BOGUS; /* bad packet */ GETSHORT(type2, p2); GETSHORT(class2, p2); @@ -1751,7 +1754,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch break; /* Done it before: name, type, class all match. */ if (!ADD_RDLEN(header, p2, plen, rdlen2)) - return STAT_INSECURE; + return STAT_BOGUS; } /* Not done, validate now */ @@ -1771,15 +1774,8 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch answer and overlay a genuine RR. To prove this hasn't happened, the answer must prove that the gennuine record doesn't exist. Check that here. */ - if (!nsec_type) - { - nsec_type = find_nsec_records(header, plen, &nsecs, &nsec_count, class1); - - if (nsec_type == 0) - return STAT_INSECURE; /* Bad packet */ - if (nsec_type == -1) - return STAT_BOGUS; /* No NSECs */ - } + if (!nsec_type && !(nsec_type = find_nsec_records(header, plen, &nsecs, &nsec_count, class1))) + return STAT_BOGUS; /* No NSECs or bad packet */ if (nsec_type == T_NSEC) rc = prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, type1); @@ -1802,7 +1798,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch for (p2 = ans_start, j = 0; j < ntohs(header->ancount); j++) { if (!(rc = extract_name(header, plen, &p2, name, 0, 10))) - return STAT_INSECURE; /* bad packet */ + return STAT_BOGUS; /* bad packet */ GETSHORT(type2, p2); GETSHORT(class2, p2); @@ -1810,7 +1806,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch GETSHORT(rdlen2, p2); if (!CHECK_LEN(header, p2, plen, rdlen2)) - return STAT_INSECURE; /* bad packet */ + return STAT_BOGUS; /* bad packet */ if (class2 == class1 && rc == 1) { @@ -1819,7 +1815,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch if (type1 == T_DS && type2 == T_DS) { if (rdlen2 < 4) - return STAT_INSECURE; /* bad packet */ + return STAT_BOGUS; /* bad packet */ GETSHORT(keytag, p2); algo = *p2++; @@ -1847,7 +1843,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch else if (type2 == T_RRSIG) { if (rdlen2 < 18) - return STAT_INSECURE; /* bad packet */ + return STAT_BOGUS; /* bad packet */ GETSHORT(type_covered, p2); @@ -1883,7 +1879,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch } if (!ADD_RDLEN(header, p2, plen, rdlen2)) - return STAT_INSECURE; /* bad packet */ + return STAT_BOGUS; /* bad packet */ } cache_end_insert(); @@ -1891,7 +1887,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch } if (!ADD_RDLEN(header, p1, plen, rdlen1)) - return STAT_INSECURE; + return STAT_BOGUS; } /* OK, all the RRsets validate, now see if we have a NODATA or NXDOMAIN reply */ @@ -1900,19 +1896,12 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch /* NXDOMAIN or NODATA reply, prove that (name, class1, type1) can't exist */ /* First marshall the NSEC records, if we've not done it previously */ - if (!nsec_type) - { - nsec_type = find_nsec_records(header, plen, &nsecs, &nsec_count, qclass); - - if (nsec_type == 0) - return STAT_INSECURE; /* Bad packet */ - if (nsec_type == -1) - return STAT_BOGUS; /* No NSECs */ - } + if (!nsec_type && !(nsec_type = find_nsec_records(header, plen, &nsecs, &nsec_count, qclass))) + return STAT_BOGUS; /* No NSECs */ /* Get name of missing answer */ if (!extract_name(header, plen, &qname, name, 1, 0)) - return STAT_INSECURE; + return STAT_BOGUS; if (nsec_type == T_NSEC) return prove_non_existence_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, qtype); -- cgit v1.2.1