Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix root server proofs #76

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
012dd3d
rs: respond with servfail if bogus
buffrr Sep 12, 2020
10d8142
resource: add no ds proof
buffrr Mar 6, 2021
fdbb861
resource: move all NSEC type maps to resource.h
pinheadmz Nov 18, 2021
9f41ff0
dns: parse escaped byte codes in name serialize
pinheadmz Nov 20, 2021
608cfe3
resource/ns: replace empty proofs with minimally-covering NSEC
pinheadmz Nov 20, 2021
87921cf
resource: implement nsec previous name and test
pinheadmz Nov 22, 2021
71bce30
dns: send REFUSED is name contains special characters
pinheadmz Nov 22, 2021
84dd308
resource/ns: replace empty NX proofs with minimally-covering NSEC
pinheadmz Nov 22, 2021
8f590a8
test: better coverage for NSEC in resource decoding
pinheadmz Nov 22, 2021
3a10a90
ns: remove DS record from root zone apex
pinheadmz Nov 22, 2021
4426b2e
resource: nsec proofs for root zone
pinheadmz Nov 22, 2021
86ae853
resource: use default TTL for NSEC
pinheadmz Nov 22, 2021
8ae46f8
resource: fix aa flag when querying root
pinheadmz Nov 23, 2021
d5adee6
test: add vector with NS and DS
pinheadmz Nov 23, 2021
4f8e851
dns/resource: fix nsec for 63 char names
pinheadmz Nov 29, 2021
c837b2d
test: do not hard code test vector size
pinheadmz Nov 30, 2021
491088f
dns: make name_dirty test more like hsd rule.verifyName()
pinheadmz Dec 1, 2021
9bb9fd8
ns/resource: fix NSEC handling for _synth
pinheadmz Dec 13, 2021
34457f4
ns: use strcat()
pinheadmz Dec 15, 2021
58bb9b3
dns: fix name read and write, parse escape characters
pinheadmz Dec 21, 2021
d413814
test: integration test with hsd
pinheadmz Dec 22, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions src/cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -262,9 +262,6 @@ hsk_cache_key_set(hsk_cache_key_t *ck, const char *name, uint16_t type) {
if (!hsk_dns_name_verify(name))
return false;

if (hsk_dns_name_dirty(name))
return false;

int labels = hsk_dns_label_count(name);
bool ref = false;

Expand Down
158 changes: 122 additions & 36 deletions src/dns.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <strings.h>
#include <stdint.h>
#include <stdbool.h>
#include <ctype.h>

#include "bio.h"
#include "dns.h"
Expand Down Expand Up @@ -725,9 +726,6 @@ hsk_dns_rr_set_name(hsk_dns_rr_t *rr, const char *name) {
if (!hsk_dns_name_verify(name))
return false;

if (hsk_dns_name_dirty(name))
return false;

if (hsk_dns_name_is_fqdn(name))
strcpy(rr->name, name);
else
Expand Down Expand Up @@ -2133,15 +2131,30 @@ hsk_dns_name_parse(
for (j = off; j < off + c; j++) {
uint8_t b = data[j];

// Hack because we're
// using c-strings.
if (b == 0x00)
b = 0xff;

// This allows for double-dots
// too easily in our design.
if (b == 0x2e)
b = 0xfe;
switch (b) {
// Escape special characters
case 0x2e /*.*/:
case 0x28 /*(*/:
case 0x29 /*)*/:
case 0x3b /*;*/:
case 0x20 /* */:
case 0x40 /*@*/:
case 0x22 /*"*/:
case 0x5c /*\\*/:
if (name)
sprintf(&name[noff], "\\%c", (char)b);

noff += 2;
continue;
default:
if (b < 0x20 || b > 0x7e) {
if (name)
sprintf(&name[noff], "\\%03d", b);

noff += 4;
continue;
}
}

if (name)
name[noff] = b;
Expand Down Expand Up @@ -2224,15 +2237,59 @@ hsk_dns_name_serialize(
int size;
int i;
char *s;
int size_adjust = 0;
bool escaped = false;
size_t max = strlen(name) - 1;

for (s = (char *)name, i = 0; *s; s++, i++) {
// Check for escaped byte codes and adjust length measurement.
if (name[i] == '\\') {
// Check the next three bytes (if within string length)
// for three-digit code which must encode a one-byte value.
if (i + 3 <= max &&
isdigit(name[i + 1]) &&
isdigit(name[i + 2]) &&
isdigit(name[i + 3])) {
uint16_t value = (name[i + 1] - 0x30) * 100;
value += (name[i + 2] - 0x30) * 10;
value += (name[i + 3] - 0x30);

// Bad escape, byte code out of range.
if (value > 0xff) {
*len = off;
return false;
}

// The next three characters don't count towards final size.
size_adjust += 2;
s += 2;
i += 2;

// Escaped dot by byte code
if (value == 0x2e)
escaped = true;
}

// Literal escaped dot
if (i + 1 <= max && name[i + 1] == 0x2e)
escaped = true;

// Remove single slash and skip next character.
size_adjust += 1;
s += 1;
i += 1;

continue;
}

if (name[i] == '.') {
if (i > 0 && name[i - 1] == '.') {
if (i > 0 && name[i - 1] == '.' && !escaped) {
// Multiple dots (escaped dot is ok)
*len = off;
return false;
}

size = i - begin;
size = i - begin - size_adjust;

if (size > HSK_DNS_MAX_LABEL) {
*len = off;
Expand Down Expand Up @@ -2272,14 +2329,35 @@ hsk_dns_name_serialize(
int j;
for (j = begin; j < i; j++) {
char ch = name[j];

// 0xff -> NUL
if (ch == -1)
ch = '\0';

// 0xfe -> .
if (ch == -2)
ch = '.';
// Check for escaped byte codes and process into output result
if (ch == '\\') {
// Check the next three bytes (if within label length)
// for three-digit code which must encode a one-byte value.
if (j + 3 <= i &&
isdigit(name[j + 1]) &&
isdigit(name[j + 2]) &&
isdigit(name[j + 3])) {
// Compute byte from next three characters.
uint16_t value = (name[++j] - 0x30) * 100;
value += (name[++j] - 0x30) * 10;
value += (name[++j] - 0x30);

// Bad escape, byte code out of range.
if (value > 0xff) {
*len = off;
return false;
}

// Write
data[off++] = value;
continue;
} else {
// Remove single slash and write next character
// as long as there is a next character.
if (j + 1 < i)
ch = name[++j];
}
}

data[off++] = ch;
}
Expand All @@ -2288,7 +2366,10 @@ hsk_dns_name_serialize(
}

begin = i + 1;
size_adjust = 0;
}

escaped = false;
}

if (i > HSK_DNS_MAX_NAME) {
Expand Down Expand Up @@ -2404,26 +2485,31 @@ hsk_dns_name_alloc(

bool
hsk_dns_name_dirty(const char *name) {
char *s = (char *)name;
int len = strlen(name);
if (len > HSK_DNS_MAX_LABEL)
return true;

while (*s) {
uint8_t c = (uint8_t)*s;
for (int i = 0; i < len; i++) {
uint8_t c = name[i];

if (c >= 0x41 && c <= 0x5a)
c ^= 0x20;

switch (c) {
case 0x28 /*(*/:
case 0x29 /*)*/:
case 0x3b /*;*/:
case 0x20 /* */:
case 0x40 /*@*/:
case 0x22 /*"*/:
case 0x5c /*\\*/:
return true;
case 0x5f: /* _ */
case 0x2d: /* - */
if (i == 0 || i == len - 1) {
return true;
} else {
continue;
}
}

if (c < 0x20 || c > 0x7e)
if (c < 0x30 ||
nodech marked this conversation as resolved.
Show resolved Hide resolved
(c > 0x39 && c < 0x61) ||
c > 0x7a) {
return true;

s += 1;
}
}

return false;
Expand Down
78 changes: 51 additions & 27 deletions src/ns.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,6 @@
#include "uv.h"
#include "dnssec.h"

// A RRSIG NSEC
static const uint8_t hsk_type_map_a[] = {
0x00, 0x06, 0x40, 0x00, 0x00, 0x00, 0x00, 0x03
};

// AAAA RRSIG NSEC
static const uint8_t hsk_type_map_aaaa[] = {
0x00, 0x06, 0x00, 0x00, 0x00, 0x80, 0x00, 0x03
};

/*
* Types
*/
Expand Down Expand Up @@ -297,6 +287,10 @@ hsk_ns_onrecv(
const struct sockaddr *addr,
uint32_t flags
) {
uint8_t *wire = NULL;
size_t wire_len = 0;
hsk_dns_msg_t *msg = NULL;

hsk_dns_req_t *req = hsk_dns_req_create(data, data_len, addr);

if (!req) {
Expand All @@ -306,10 +300,6 @@ hsk_ns_onrecv(

hsk_dns_req_print(req, "ns: ");

uint8_t *wire = NULL;
size_t wire_len = 0;
hsk_dns_msg_t *msg = NULL;

// Hit cache first.
msg = hsk_cache_get(&ns->cache, req);

Expand Down Expand Up @@ -342,18 +332,26 @@ hsk_ns_onrecv(

hsk_dns_rrs_t *an = &msg->an;
hsk_dns_rrs_t *rrns = &msg->ns;
hsk_dns_rrs_t *ar = &msg->ar;

uint8_t ip[16];
uint16_t family;
char synth[HSK_DNS_MAX_LABEL + 1];
hsk_dns_label_from(req->name, -2, synth);

if (req->labels == 1) {
hsk_resource_to_empty(req->tld, NULL, 0, rrns);
hsk_dnssec_sign_zsk(rrns, HSK_DNS_NSEC);
// TLD '._synth' is being queried on its own, send SOA
// so recursive asks again with complete synth record.
hsk_resource_root_to_soa(rrns);
nodech marked this conversation as resolved.
Show resolved Hide resolved
hsk_dnssec_sign_zsk(rrns, HSK_DNS_SOA);
// Empty non-terminal proof:
hsk_resource_to_nsec(
"_synth.",
"\\000._synth.",
hsk_type_map_empty,
sizeof(hsk_type_map_empty),
rrns
);
hsk_dnssec_sign_zsk(rrns, HSK_DNS_NSEC);
}

if (pointer_to_ip(synth, ip, &family)) {
Expand All @@ -372,19 +370,20 @@ hsk_ns_onrecv(
}

if (!match) {
// Needs SOA.
// TODO: Make the reverse pointers TLDs.
// Empty proof:
char next[HSK_DNS_MAX_NAME] = "\\000.";
strcat(next, req->name);
if (family == HSK_DNS_A) {
hsk_resource_to_empty(
hsk_resource_to_nsec(
req->name,
next,
hsk_type_map_a,
sizeof(hsk_type_map_a),
rrns
);
} else {
hsk_resource_to_empty(
hsk_resource_to_nsec(
req->name,
next,
hsk_type_map_aaaa,
sizeof(hsk_type_map_aaaa),
rrns
Expand Down Expand Up @@ -418,7 +417,7 @@ hsk_ns_onrecv(

hsk_dns_rrs_push(an, rr);

hsk_dnssec_sign_zsk(ar, rrtype);
hsk_dnssec_sign_zsk(an, rrtype);
}
}

Expand All @@ -434,6 +433,28 @@ hsk_ns_onrecv(
goto done;
}

// Send REFUSED if name is dirty
// (contains escaped byte codes or special characters)
if (hsk_dns_name_dirty(req->tld)) {
msg = hsk_resource_to_refused();

if (!msg) {
hsk_ns_log(ns, "failed creating refused\n");
goto fail;
}

if (!hsk_dns_msg_finalize(&msg, req, ns->ec, ns->key, &wire, &wire_len)) {
hsk_ns_log(ns, "could not reply\n");
goto done;
}

hsk_ns_log(ns, "refusing query for msg (%u): %u\n", req->id, wire_len);

hsk_ns_send(ns, wire, wire_len, addr, true);

goto done;
}

// Requesting a lookup.
if (req->labels > 0) {
// Check blacklist.
Expand All @@ -445,7 +466,7 @@ hsk_ns_onrecv(
|| strcmp(req->tld, "onion") == 0 // Tor
|| strcmp(req->tld, "tor") == 0 // OnioNS
|| strcmp(req->tld, "zkey") == 0) { // GNS
msg = hsk_resource_to_nx();
msg = hsk_resource_to_nx(req->tld);
} else {
req->ns = (void *)ns;

Expand Down Expand Up @@ -538,9 +559,12 @@ hsk_ns_respond(
// not possible for SPV nodes since they
// can't arbitrarily iterate over the tree.
//
// Instead, we give a phony proof, which
// makes the root zone look empty.
msg = hsk_resource_to_nx();
// Instead, we give a minimally covering
// NSEC record based on rfc4470
// https://tools.ietf.org/html/rfc4470

// Proving the name doesn't exist
msg = hsk_resource_to_nx(req->tld);

if (!msg)
hsk_ns_log(ns, "could not create nx response (%u)\n", req->id);
Expand Down
Loading