From 7f632c0640f174bbbc1deb532e3a3977d595d28a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henryk=20Pl=C3=B6tz?= Date: Fri, 3 Oct 2014 20:22:01 +0200 Subject: Apply djbdns-1.05-dnssec.patch SHA1 62e2ce1d31f1fe908fac84fc8bd049a12621810f, contained in tinydnssec-1.05-1.3.tar.bz2 Source was http://www.tinydnssec.org/download/tinydnssec-1.05-1.3.tar.bz2, SHA1 b33d5c3e0de67f6427aad8c00a99580b59804075 --- tdlookup.c | 308 +++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 278 insertions(+), 30 deletions(-) (limited to 'tdlookup.c') diff --git a/tdlookup.c b/tdlookup.c index b760340..ade5e49 100644 --- a/tdlookup.c +++ b/tdlookup.c @@ -10,6 +10,9 @@ #include "response.h" #include "ip6.h" #include "clientloc.h" +#include "alloc.h" +#include "sha1.h" +#include "base32hex.h" static int want(const char *owner,const char type[2]) { @@ -34,7 +37,7 @@ static int want(const char *owner,const char type[2]) } static char *d1; - +static char *wantAddr; static char clientloc[2]; static struct tai now; static struct cdb c; @@ -44,10 +47,18 @@ static uint32 dlen; static unsigned int dpos; static char type[2]; static uint32 ttl; +static char *nsec3; +static char *cname = 0; +/* returns -1 on failure, + * returns 0 on not found + * returns 1 when found + * returns 2 when flagwild is true and no wildcard match has been found but + * a direct match exists. This is for RFC-1034 section 4.3.3 compatibility. + */ static int find(char *d,int flagwild) { - int r; + int r, direct=0; char ch; struct tai cutoff; char ttd[8]; @@ -57,7 +68,9 @@ static int find(char *d,int flagwild) for (;;) { r = cdb_findnext(&c,d,dns_domain_length(d)); - if (r <= 0) return r; + if (r < 0) return r; /* -1 */ + if (r == 0) { return flagwild ? direct ? 2 : 0 + : 0; } dlen = cdb_datalen(&c); if (dlen > sizeof data) return -1; if (cdb_read(&c,data,dlen,cdb_datapos(&c)) == -1) return -1; @@ -68,6 +81,7 @@ static int find(char *d,int flagwild) dpos = dns_packet_copy(data,dlen,dpos,recordloc,2); if (!dpos) return -1; if (byte_diff(recordloc,2,clientloc)) continue; } + direct = direct || (ch != '*'); if (flagwild != (ch == '*')) continue; dpos = dns_packet_copy(data,dlen,dpos,ttlstr,4); if (!dpos) return -1; uint32_unpack_big(ttlstr,&ttl); @@ -105,6 +119,123 @@ static int doname(void) return response_addname(d1); } +static int addNSEC3(char *hashName) +{ +int r; + + cdb_findstart(&c); + while (r = find(hashName,0)) { + if (r == -1) return 0; + if (byte_equal(type,2,DNS_T_NSEC3)) { + if (!response_rstart(hashName,DNS_T_NSEC3,ttl)) return 0; + if (!response_addbytes(data + dpos,dlen - dpos)) return 0; + response_rfinish(RESPONSE_AUTHORITY); + } + else if (do_dnssec && byte_equal(type,2,DNS_T_RRSIG) && dlen > dpos+18 + && byte_equal(data+dpos,2,DNS_T_NSEC3)) { + if (!response_rstart(hashName,DNS_T_RRSIG,ttl)) return 0; + if (!dobytes(18)) return 0; + if (!doname()) return 0; + if (!response_addbytes(data + dpos,dlen - dpos)) return 0; + response_rfinish(RESPONSE_AUTHORITY); + } + } + return 1; +} + +static int addNSEC3Cover(char *name, char *control, int wild) +{ +SHA1_CTX ctx; +int algo = 0, flags = 0, saltlen = 0, r; +uint16 iterations = 0; +char salt[255]; +uint8_t digest[SHA1_DIGEST_SIZE]; + + /* Search NSEC3PARAM to find hash parameters */ + cdb_findstart(&c); + while (r = find(control,0)) { + if (r == -1) return 0; + if (byte_equal(type,2,DNS_T_NSEC3PARAM) && dlen - dpos > 5) { + algo = data[dpos]; + flags = data[dpos+1]; + uint16_unpack_big(data + dpos + 2, &iterations); + saltlen = data[dpos+4]; + if (algo != 1 || flags || dlen - dpos - 5 < saltlen) { + algo = 0; + } else { + byte_copy(salt,saltlen, data + dpos + 5); + break; + } + } + } + if (algo != 1) return 0; /* not found or unsupported algorithm / flags */ + + /* Compute hash value */ + case_lowerb(name,dns_domain_length(name)); + SHA1_Init(&ctx); + if (wild) SHA1_Update(&ctx, "\1*", 2); + SHA1_Update(&ctx, name, dns_domain_length(name)); + SHA1_Update(&ctx, salt, saltlen); + SHA1_Final(&ctx, digest); + while (iterations-- > 0) { + SHA1_Init(&ctx); + SHA1_Update(&ctx, digest, SHA1_DIGEST_SIZE); + SHA1_Update(&ctx, salt, saltlen); + SHA1_Final(&ctx, digest); + } + + /* Find covering hash */ + char nibble = ((digest[0] >> 4) & 0xf) + '0'; + if (nibble > '9') { nibble += 'a' - '9' - 1; } + salt[0] = 1; + salt[1] = nibble; + byte_copy(salt+2, dns_domain_length(control), control); + cdb_findstart(&c); + while (r = find(salt,0)) { + if (r == -1) return 0; + if (byte_equal(type,2,DNS_T_HASHLIST) && dlen - dpos >= SHA1_DIGEST_SIZE) { + int hpos = dpos + SHA1_DIGEST_SIZE; + while (byte_diff(digest,SHA1_DIGEST_SIZE,data+hpos) > 0 && hpos < dlen) hpos += SHA1_DIGEST_SIZE; + hpos -= SHA1_DIGEST_SIZE; + *salt = base32hex(salt+1,data+hpos,SHA1_DIGEST_SIZE); + byte_copy(salt + *salt + 1, dns_domain_length(control), control); + break; + } + } + if (*salt == 1) return 0; /* not found */ + return addNSEC3(salt); +} + +static int addClosestEncloserProof(char *name, char *control, int includeWild) +{ +char *q = name; +char *hashName = 0; +int r; + + while (*q) { + cdb_findstart(&c); + while (r = find(q,0)) { + if (r == -1) return 0; + if (byte_equal(type,2,DNS_T_HASHREF) && dlen > dpos) { + if (!dns_packet_getname(data,dlen,dpos,&hashName)) return 0; + break; + } + } + if (hashName) { + int rc = addNSEC3(hashName); + alloc_free(hashName); + if (!rc) return 0; + hashName = 0; + break; + } + name = q; + q += *q + 1; + } + if (!*q) return 0; + if (includeWild && !addNSEC3Cover(q, control, 1)) return 0; + return addNSEC3Cover(name, control, 0); +} + static int doit(char *q,char qtype[2]) { unsigned int bpos; @@ -118,6 +249,8 @@ static int doit(char *q,char qtype[2]) int r; int flagns; int flagauthoritative; + int flagsigned; + char *flagcname; char x[20]; uint16 u16; char addr[8][4]; @@ -132,18 +265,28 @@ static int doit(char *q,char qtype[2]) for (;;) { flagns = 0; flagauthoritative = 0; + flagsigned = 0; cdb_findstart(&c); while (r = find(control,0)) { if (r == -1) return 0; if (byte_equal(type,2,DNS_T_SOA)) flagauthoritative = 1; - if (byte_equal(type,2,DNS_T_NS)) flagns = 1; + else if (byte_equal(type,2,DNS_T_NS)) flagns = 1; + else if (byte_equal(type,2,DNS_T_DNSKEY)) flagsigned |= 1; + else if (byte_equal(type,2,DNS_T_RRSIG)) flagsigned |= 2; + else if (byte_equal(type,2,DNS_T_NSEC3PARAM)) flagsigned |= 4; } + flagsigned = (flagsigned == 7); if (flagns) break; - if (!*control) return 0; /* q is not within our bailiwick */ + if (!*control) { + if (!cname) return 0; /* q is not within our bailiwick */ + response[2] &= ~4; /* CNAME chain ends in external reference */ + return 1; + } control += *control; control += 1; } + wild = q; if (!flagauthoritative) { response[2] &= ~4; goto AUTHORITY; /* q is in a child zone */ @@ -152,7 +295,11 @@ static int doit(char *q,char qtype[2]) flaggavesoa = 0; flagfound = 0; - wild = q; + flagcname = 0; + if (nsec3) { + alloc_free(nsec3); + nsec3 = 0; + } for (;;) { addrnum = addr6num = 0; @@ -160,10 +307,29 @@ static int doit(char *q,char qtype[2]) cdb_findstart(&c); while (r = find(wild,wild != q)) { if (r == -1) return 0; + if (r == 2) break; flagfound = 1; if (flaggavesoa && byte_equal(type,2,DNS_T_SOA)) continue; - if (byte_diff(type,2,qtype) && byte_diff(qtype,2,DNS_T_ANY) && byte_diff(type,2,DNS_T_CNAME)) continue; - if (byte_equal(type,2,DNS_T_A) && (dlen - dpos == 4)) { + if (do_dnssec && byte_equal(type,2,DNS_T_HASHREF) && dlen > dpos) { + if (!dns_packet_getname(data,dlen,dpos,&nsec3)) return 0; + } + if (byte_diff(type,2,qtype) && byte_diff(qtype,2,DNS_T_ANY) && byte_diff(type,2,DNS_T_CNAME) + && (!do_dnssec || byte_diff(type,2,DNS_T_RRSIG))) continue; + if (byte_equal(type,2,DNS_T_HASHREF) || byte_equal(type,2,DNS_T_HASHLIST)) continue; + if (do_dnssec && byte_equal(type,2,DNS_T_RRSIG) && dlen - dpos > 18) { + char sigtype[2]; + struct tai valid; + uint32 validFrom, validUntil; + byte_copy(sigtype,2,data + dpos); + if (byte_diff(sigtype,2,qtype) && byte_diff(qtype,2,DNS_T_ANY) && byte_diff(sigtype,2,DNS_T_CNAME)) continue; + uint32_unpack_big(data + dpos + 12, &validFrom); + tai_unix(&valid, validFrom); + if (tai_less(&now, &valid)) continue; + uint32_unpack_big(data + dpos + 8, &validUntil); + tai_unix(&valid, validUntil); + if (tai_less(&valid, &now)) continue; + } + if (byte_equal(type,2,DNS_T_A) && (dlen - dpos == 4) && (!do_dnssec || addrnum < 8)) { addrttl = ttl; i = dns_random(addrnum + 1); if (i < 8) { @@ -174,7 +340,7 @@ static int doit(char *q,char qtype[2]) if (addrnum < 1000000) ++addrnum; continue; } - if (byte_equal(type,2,DNS_T_AAAA) && (dlen - dpos == 16)) { + if (byte_equal(type,2,DNS_T_AAAA) && (dlen - dpos == 16) && (!do_dnssec || addr6num < 8)) { addr6ttl = ttl; i = dns_random(addr6num + 1); if (i < 8) { @@ -188,6 +354,9 @@ static int doit(char *q,char qtype[2]) if (!response_rstart(q,type,ttl)) return 0; if (byte_equal(type,2,DNS_T_NS) || byte_equal(type,2,DNS_T_CNAME) || byte_equal(type,2,DNS_T_PTR)) { if (!doname()) return 0; + if (byte_equal(type,2,DNS_T_CNAME) && byte_diff(qtype,2,DNS_T_CNAME)) { + if (!dns_domain_copy(&flagcname,d1)) return 0; + } } else if (byte_equal(type,2,DNS_T_MX)) { if (!dobytes(2)) return 0; @@ -199,10 +368,18 @@ static int doit(char *q,char qtype[2]) if (!dobytes(20)) return 0; flaggavesoa = 1; } + else if (byte_equal(type,2,DNS_T_RRSIG) && dlen - dpos > 18) { + char sigtype[2]; + byte_copy(sigtype,2,data + dpos); + if (!dobytes(18)) return 0; + if (!doname()) return 0; + if (!response_addbytes(data + dpos,dlen - dpos)) return 0; + } else if (!response_addbytes(data + dpos,dlen - dpos)) return 0; response_rfinish(RESPONSE_ANSWER); } + if (r == 2) break; for (i = 0;i < addrnum;++i) if (i < 8) { if (!response_rstart(q,DNS_T_A,addrttl)) return 0; @@ -223,6 +400,16 @@ static int doit(char *q,char qtype[2]) wild += 1; } + if (flagcname) { + if (response[RESPONSE_ANSWER+1] >= 100) { + dns_domain_free(&flagcname); /* most likely a loop */ + return 0; + } + if (cname) dns_domain_free(&cname); + cname = flagcname; + return doit(cname, qtype); + } + if (!flagfound) response_nxdomain(); @@ -230,22 +417,49 @@ static int doit(char *q,char qtype[2]) AUTHORITY: aupos = response_len; - if (flagauthoritative && (aupos == anpos)) { - cdb_findstart(&c); - while (r = find(control,0)) { - if (r == -1) return 0; - if (byte_equal(type,2,DNS_T_SOA)) { - if (!response_rstart(control,DNS_T_SOA,ttl)) return 0; - if (!doname()) return 0; - if (!doname()) return 0; - if (!dobytes(20)) return 0; - response_rfinish(RESPONSE_AUTHORITY); - break; + if (flagauthoritative && (aupos == anpos)) { /* NODATA or NXDOMAIN */ + if (!flaggavesoa) { + cdb_findstart(&c); + while (r = find(control,0)) { + if (r == -1) return 0; + if (!flaggavesoa && byte_equal(type,2,DNS_T_SOA)) { + if (!response_rstart(control,DNS_T_SOA,ttl)) return 0; + if (!doname()) return 0; + if (!doname()) return 0; + if (!dobytes(20)) return 0; + response_rfinish(RESPONSE_AUTHORITY); + flaggavesoa = 1; + } + else if (do_dnssec && byte_equal(type,2,DNS_T_RRSIG) && dlen > dpos+18 + && byte_equal(data+dpos,2,DNS_T_SOA)) { + if (!response_rstart(control,DNS_T_RRSIG,ttl)) return 0; + if (!dobytes(18)) return 0; + if (!doname()) return 0; + if (!response_addbytes(data + dpos,dlen - dpos)) return 0; + response_rfinish(RESPONSE_AUTHORITY); + } + } + } + if (do_dnssec && flagsigned) { + if (flagfound && nsec3) { /* NODATA */ + if (!addNSEC3(nsec3)) return 0; + if (wild != q) { /* Wildcard NODATA */ + if (!addClosestEncloserProof(q, control, 0)) return 0; + } + } + else { /* NXDOMAIN, or query for NSEC3 owner name */ + if (!addClosestEncloserProof(q, control, 1)) return 0; } } } - else + else { + if (do_dnssec && wild != q && flagsigned) { /* Wildcard answer */ + char *nextCloser = q; + while (nextCloser + *nextCloser + 1 < wild) { nextCloser += *nextCloser + 1; } + if (!addNSEC3Cover(nextCloser, control, 0)) return 0; + } if (want(control,DNS_T_NS)) { + int have_ds = 0; cdb_findstart(&c); while (r = find(control,0)) { if (r == -1) return 0; @@ -254,10 +468,33 @@ static int doit(char *q,char qtype[2]) if (!doname()) return 0; response_rfinish(RESPONSE_AUTHORITY); } + else if (do_dnssec && byte_equal(type,2,DNS_T_DS)) { + if (!response_rstart(control,DNS_T_DS,ttl)) return 0; + if (!response_addbytes(data + dpos,dlen - dpos)) return 0; + response_rfinish(RESPONSE_AUTHORITY); + have_ds = 1; + } + else if (do_dnssec && byte_equal(type,2,DNS_T_RRSIG) && dlen > dpos+18 + && (byte_equal(data+dpos,2,DNS_T_NS) + || byte_equal(data+dpos,2,DNS_T_DS))) { + if (!response_rstart(control,DNS_T_RRSIG,ttl)) return 0; + if (!dobytes(18)) return 0; + if (!doname()) return 0; + if (!response_addbytes(data + dpos,dlen - dpos)) return 0; + response_rfinish(RESPONSE_AUTHORITY); + } } + if (do_dnssec && !flagauthoritative && !have_ds) { addNSEC3(control); } } + } arpos = response_len; + if (do_dnssec) { + /* Add EDNS0 OPT RR */ + if (!response_rstart("",DNS_T_OPT,1 << 15)) return 0; + uint16_pack_big(response+arpos+3, 512); + response_rfinish(RESPONSE_ADDITIONAL); + } bpos = anpos; while (bpos < arpos) { @@ -265,25 +502,33 @@ static int doit(char *q,char qtype[2]) bpos = dns_packet_copy(response,arpos,bpos,x,10); if (!bpos) return 0; if (byte_equal(x,2,DNS_T_NS) || byte_equal(x,2,DNS_T_MX)) { if (byte_equal(x,2,DNS_T_NS)) { - if (!dns_packet_getname(response,arpos,bpos,&d1)) return 0; + if (!dns_packet_getname(response,arpos,bpos,&wantAddr)) return 0; } else - if (!dns_packet_getname(response,arpos,bpos + 2,&d1)) return 0; - case_lowerb(d1,dns_domain_length(d1)); - if (want(d1,DNS_T_A)) { + if (!dns_packet_getname(response,arpos,bpos + 2,&wantAddr)) return 0; + case_lowerb(wantAddr,dns_domain_length(wantAddr)); + if (want(wantAddr,DNS_T_A)) { cdb_findstart(&c); - while (r = find(d1,0)) { + while (r = find(wantAddr,0)) { if (r == -1) return 0; if (byte_equal(type,2,DNS_T_A)) { - if (!response_rstart(d1,DNS_T_A,ttl)) return 0; + if (!response_rstart(wantAddr,DNS_T_A,ttl)) return 0; if (!dobytes(4)) return 0; response_rfinish(RESPONSE_ADDITIONAL); } else if (byte_equal(type,2,DNS_T_AAAA)) { - if (!response_rstart(d1,DNS_T_AAAA,ttl)) return 0; + if (!response_rstart(wantAddr,DNS_T_AAAA,ttl)) return 0; if (!dobytes(16)) return 0; response_rfinish(RESPONSE_ADDITIONAL); } + else if (do_dnssec && byte_equal(type,2,DNS_T_RRSIG) && dlen > dpos+18 + && (byte_equal(data+dpos,2,DNS_T_A) || byte_equal(data+dpos,2,DNS_T_AAAA))) { + if (!response_rstart(wantAddr,DNS_T_RRSIG,ttl)) return 0; + if (!dobytes(18)) return 0; + if (!doname()) return 0; + if (!response_addbytes(data + dpos,dlen - dpos)) return 0; + response_rfinish(RESPONSE_ADDITIONAL); + } } } } @@ -291,10 +536,10 @@ static int doit(char *q,char qtype[2]) bpos += u16; } - if (flagauthoritative && (response_len > 512)) { + if (flagauthoritative && (response_len > max_response_len)) { byte_zero(response + RESPONSE_ADDITIONAL,2); response_len = arpos; - if (response_len > 512) { + if (!do_dnssec && response_len > max_response_len) { byte_zero(response + RESPONSE_AUTHORITY,2); response_len = aupos; } @@ -316,6 +561,9 @@ int respond(char *q,char qtype[2],char ip[16]) cdb_init(&c,fd); r = doit(q,qtype); + if (cname) { + dns_domain_free(&cname); + } cdb_free(&c); close(fd); -- cgit v1.2.3