1 #!/usr/bin/python3 2 3 # Copyright (C) Internet Systems Consortium, Inc. ("ISC") 4 # 5 # SPDX-License-Identifier: MPL-2.0 6 # 7 # This Source Code Form is subject to the terms of the Mozilla Public 8 # License, v. 2.0. If a copy of the MPL was not distributed with this 9 # file, you can obtain one at https://mozilla.org/MPL/2.0/. 10 # 11 # See the COPYRIGHT file distributed with this work for additional 12 # information regarding copyright ownership. 13 14 import dns.rdataclass 15 import dns.rdatatype 16 import dns.rdtypes.ANY.RRSIG 17 import dns.zone 18 19 from isctest.run import EnvCmd 20 21 import isctest 22 23 24 def duplicate_rrsig(rdata, i): 25 return dns.rdtypes.ANY.RRSIG.RRSIG( 26 rdclass=rdata.rdclass, 27 rdtype=rdata.rdtype, 28 type_covered=rdata.type_covered, 29 algorithm=rdata.algorithm, 30 labels=rdata.labels, 31 # increment orig TTL so the rdataset isn't treated as identical record by dnspython 32 original_ttl=rdata.original_ttl + i, 33 expiration=rdata.expiration, 34 inception=rdata.inception, 35 key_tag=rdata.key_tag, 36 signer=rdata.signer, 37 signature=rdata.signature, 38 ) 39 40 41 def bootstrap(): 42 keygen = EnvCmd("KEYGEN", "-a ECDSA256 -Kns2 -q") 43 signer = EnvCmd("SIGNER", "-S -g") 44 45 zone = "excessive-nsec-rrsigs" 46 infile = f"{zone}.db.in" 47 signedfile = f"{zone}.db.signed" 48 49 isctest.log.info(f"{zone}: generate ksk and zsk") 50 ksk_name = keygen(f"-f KSK {zone}").out.strip() 51 keygen(f"{zone}").out.strip() 52 ksk = isctest.kasp.Key(ksk_name, keydir="ns2") 53 54 isctest.log.info(f"{zone}: sign zone") 55 signer(f"-P -x -O full -o {zone} -f {signedfile} {infile}", cwd="ns2") 56 57 isctest.log.info( 58 f"{zone}: duplicate the RRSIG(NSEC) to exceed max-records-per-type" 59 ) 60 zone = dns.zone.from_file(f"ns2/{signedfile}", origin=zone) 61 for node in zone.values(): 62 rrsig_rdataset = node.find_rdataset( 63 dns.rdataclass.IN, dns.rdatatype.RRSIG, dns.rdatatype.NSEC 64 ) 65 orig = rrsig_rdataset[0] 66 rrsig_rdataset.add(duplicate_rrsig(orig, 1)) 67 rrsig_rdataset.add(duplicate_rrsig(orig, 2)) 68 zone.to_file(f"ns2/{signedfile}", sorted=True) 69 70 return { 71 "trust_anchors": [ 72 ksk.into_ta("static-key"), 73 ], 74 } 75 76 77 # reproducer for CVE-2026-3104 [GL#5742] 78 def test_excessive_rrsigs(ns3): 79 # the real test is that there is no crash on shutdown - checked by the test 80 # framework when the test finishes 81 82 # multiple queries seem more reliable to reproduce the memory leak, using a 83 # single query sometimes didn't cause a crash on shutdown 84 for i in range(10): 85 msg = isctest.query.create(f"x{i}.excessive-nsec-rrsigs", "A") 86 res = isctest.query.udp(msg, ns3.ip, attempts=1) 87 isctest.check.servfail(res) 88