Skip to content

Commit 35ad41d

Browse files
committed
resolved: dnssec - properly take wildcards into account in NESC3 proof
For NXDOMAIN, it is not sufficient to prove that the next-closest enclosure does not exist, we must also prove that there is no wildcard domain directly below the closest enclosure which would synthesise the name that has been requested. For positive responses, in addition to exact matches, we should accept wildcard ones. In that case we must first prove that there is no precise match (i.e., that the closest encounter is not the record itself) and secondly that the source of synthesis exists.
1 parent 6f76ec5 commit 35ad41d

File tree

1 file changed

+97
-10
lines changed

1 file changed

+97
-10
lines changed

src/resolve/resolved-dns-dnssec.c

Lines changed: 97 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,14 +1220,23 @@ static int nsec3_hashed_domain(const DnsResourceRecord *nsec3, const char *domai
12201220
return hashed_size;
12211221
}
12221222

1223-
/* See RFC 5155, Section 8 */
1223+
/* See RFC 5155, Section 8
1224+
* First try to find a NSEC3 record that matches our query precisely, if that fails, find the closest
1225+
* enclosure. Secondly, find a proof that there is no closer enclosure and either a proof that there
1226+
* is no wildcard domain as a direct descendant of the closest enclosure, or find an NSEC3 record that
1227+
* matches the wildcard domain.
1228+
*
1229+
* Based on this we can prove either the existence of the record in @key, or NXDOMAIN or NODATA, or
1230+
* that there is no proof either way. The latter is the case if a the proof of non-existence of a given
1231+
* name uses an NSEC3 record with the opt-out bit set. Lastly, if we are given insufficient NSEC3 records
1232+
* to conclude anything we indicate this by returning NO_RR. */
12241233
static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated) {
1225-
_cleanup_free_ char *next_closer_domain = NULL;
1234+
_cleanup_free_ char *next_closer_domain = NULL, *wildcard = NULL, *wildcard_domain = NULL;
12261235
const char *zone, *p, *pp = NULL;
1227-
DnsResourceRecord *rr, *enclosure_rr, *suffix_rr;
1236+
DnsResourceRecord *rr, *enclosure_rr, *suffix_rr, *wildcard_rr = NULL;
12281237
DnsAnswerFlags flags;
12291238
int hashed_size, r;
1230-
bool a;
1239+
bool a, no_closer = false, no_wildcard = false, optout = false;
12311240

12321241
assert(key);
12331242
assert(result);
@@ -1345,6 +1354,18 @@ static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecR
13451354
return 0;
13461355
}
13471356

1357+
/* Prove that there is no next closer and whether or not there is a wildcard domain. */
1358+
1359+
wildcard = strappend("*.", p);
1360+
if (!wildcard)
1361+
return -ENOMEM;
1362+
1363+
r = nsec3_hashed_domain(enclosure_rr, wildcard, zone, &wildcard_domain);
1364+
if (r < 0)
1365+
return r;
1366+
if (r != hashed_size)
1367+
return -EBADMSG;
1368+
13481369
r = nsec3_hashed_domain(enclosure_rr, pp, zone, &next_closer_domain);
13491370
if (r < 0)
13501371
return r;
@@ -1373,16 +1394,82 @@ static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecR
13731394
return r;
13741395
if (r > 0) {
13751396
if (rr->nsec3.flags & 1)
1376-
*result = DNSSEC_NSEC_OPTOUT;
1377-
else
1378-
*result = DNSSEC_NSEC_NXDOMAIN;
1397+
optout = true;
13791398

1380-
*authenticated = a && (flags & DNS_ANSWER_AUTHENTICATED);
1381-
return 1;
1399+
a = a && (flags & DNS_ANSWER_AUTHENTICATED);
1400+
1401+
no_closer = true;
1402+
}
1403+
1404+
r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), wildcard_domain);
1405+
if (r < 0)
1406+
return r;
1407+
if (r > 0) {
1408+
a = a && (flags & DNS_ANSWER_AUTHENTICATED);
1409+
1410+
wildcard_rr = rr;
1411+
}
1412+
1413+
r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), wildcard_domain, next_hashed_domain);
1414+
if (r < 0)
1415+
return r;
1416+
if (r > 0) {
1417+
if (rr->nsec3.flags & 1)
1418+
/* This only makes sense if we have a wildcard delegation, which is
1419+
* very unlikely, see RFC 4592, Section 4.2, but we cannot rely on
1420+
* this not happening, so hence cannot simply conclude NXDOMAIN as
1421+
* we would wish */
1422+
optout = true;
1423+
1424+
a = a && (flags & DNS_ANSWER_AUTHENTICATED);
1425+
1426+
no_wildcard = true;
13821427
}
13831428
}
13841429

1385-
*result = DNSSEC_NSEC_NO_RR;
1430+
if (wildcard_rr && no_wildcard)
1431+
return -EBADMSG;
1432+
1433+
if (!no_closer) {
1434+
*result = DNSSEC_NSEC_NO_RR;
1435+
1436+
return 0;
1437+
}
1438+
1439+
if (wildcard_rr) {
1440+
/* A wildcard exists that matches our query. */
1441+
if (optout)
1442+
/* This is not specified in any RFC to the best of my knowledge, but
1443+
* if the next closer enclosure is covered by an opt-out NSEC3 RR
1444+
* it means that we cannot prove that the source of synthesis is
1445+
* correct, as there may be a closer match. */
1446+
*result = DNSSEC_NSEC_OPTOUT;
1447+
else if (bitmap_isset(wildcard_rr->nsec3.types, key->type))
1448+
*result = DNSSEC_NSEC_FOUND;
1449+
else if (bitmap_isset(wildcard_rr->nsec3.types, DNS_TYPE_CNAME))
1450+
*result = DNSSEC_NSEC_CNAME;
1451+
else
1452+
*result = DNSSEC_NSEC_NODATA;
1453+
} else {
1454+
if (optout)
1455+
/* The RFC only specifies that we have to care for optout for NODATA for
1456+
* DS records. However, children of an insecure opt-out delegation should
1457+
* also be considered opt-out, rather than verified NXDOMAIN.
1458+
* Note that we do not require a proof of wildcard non-existence if the
1459+
* next closer domain is covered by an opt-out, as that would not provide
1460+
* any additional information. */
1461+
*result = DNSSEC_NSEC_OPTOUT;
1462+
else if (no_wildcard)
1463+
*result = DNSSEC_NSEC_NXDOMAIN;
1464+
else {
1465+
*result = DNSSEC_NSEC_NO_RR;
1466+
1467+
return 0;
1468+
}
1469+
}
1470+
1471+
*authenticated = a;
1472+
13861473
return 0;
13871474
}
13881475

0 commit comments

Comments
 (0)