@@ -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. */
12241233static 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