From: W.C.A. Wijngaards Date: Wed, 25 Feb 2026 09:59:33 +0000 (+0100) Subject: - Fix validator to set unchecked when validation recursion X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bff3d762ff74ddb4b2fd899109c42f60f5100136;p=thirdparty%2Funbound.git - Fix validator to set unchecked when validation recursion requests are passed. The edns subnet module checks if validation is needed for a cache response, and set the validator to protect the cache with validation for non-subnet lookups. --- diff --git a/doc/Changelog b/doc/Changelog index 2219c3d50..cb7222c44 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,9 @@ +25 February 2026: Wouter + - Fix validator to set unchecked when validation recursion + requests are passed. The edns subnet module checks if validation + is needed for a cache response, and set the validator to protect + the cache with validation for non-subnet lookups. + 23 February 2026: Wouter - Fix to have cachedb not return expired bogus data as non-bogus. - Fix to make the cachedb_val_expired.crpl succeed. diff --git a/edns-subnet/subnetmod.c b/edns-subnet/subnetmod.c index 5144070a5..8745d472a 100644 --- a/edns-subnet/subnetmod.c +++ b/edns-subnet/subnetmod.c @@ -483,6 +483,8 @@ lookup_and_reply(struct module_qstate *qstate, int id, struct subnet_qstate *sq, struct addrtree *tree; struct addrnode *node; uint8_t scope; + int must_validate = (!(qstate->query_flags&BIT_CD) + || qstate->env->cfg->ignore_cd) && qstate->env->need_to_validate; memset(&sq->ecs_client_out, 0, sizeof(sq->ecs_client_out)); @@ -515,7 +517,14 @@ lookup_and_reply(struct module_qstate *qstate, int id, struct subnet_qstate *sq, if (!qstate->return_msg) { /* Failed allocation or expired TTL */ return 0; } - + if(qstate->return_msg->rep->security == sec_status_unchecked + && must_validate) { + /* The message has to be validated first. */ + verbose(VERB_ALGO, "subnet: unchecked cache entry needs " + "validation"); + return 0; + } + if (sq->subnet_downstream) { /* relay to interested client */ sq->ecs_client_out.subnet_scope_mask = scope; sq->ecs_client_out.subnet_addr_fam = ecs->subnet_addr_fam; @@ -570,7 +579,10 @@ generate_sub_request(struct module_qstate *qstate, int id, struct subnet_qstate* qflags |= BIT_RD; if((qstate->query_flags & BIT_CD)!=0) { qflags |= BIT_CD; - valrec = 1; + /* The valrec is left off. Leave out: valrec = 1; + * So that the cache is protected with DNSSEC validation. + * Just like the global cache. DNSSEC validation is performed + * regardless of the setting of the querier's CD flag. */ } fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub)); diff --git a/testdata/subnet_scopezero_bogus.crpl b/testdata/subnet_scopezero_bogus.crpl new file mode 100644 index 000000000..704b69732 --- /dev/null +++ b/testdata/subnet_scopezero_bogus.crpl @@ -0,0 +1,274 @@ +; Test subnet option + +server: + trust-anchor: "example.com. 3600 IN DS 2854 3 1 46e4ffc6e9a4793b488954bd3f0cc6af0dfb201b" + val-override-date: "20070916134226" + trust-anchor-signaling: no + target-fetch-policy: "0 0 0 0 0" + send-client-subnet: 1.2.3.4 + max-client-subnet-ipv4: 17 + module-config: "subnetcache validator iterator" + verbosity: 3 + fake-sha1: yes + fake-dsa: yes + qname-minimisation: no + minimal-responses: yes + iter-scrub-promiscuous: yes + +stub-zone: + name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test subnet with scopezero bogus response + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 + ENTRY_BEGIN + MATCH opcode qtype qname ednsdata + ADJUST copy_id + REPLY QR NOERROR + SECTION QUESTION + . IN NS + SECTION ANSWER + . IN NS K.ROOT-SERVERS.NET. + SECTION ADDITIONAL + HEX_EDNSDATA_BEGIN + ;; we expect to receive empty + HEX_EDNSDATA_END + K.ROOT-SERVERS.NET. IN A 193.0.14.129 + ENTRY_END + + ENTRY_BEGIN + MATCH opcode qtype qname ednsdata + ADJUST copy_id + REPLY QR NOERROR + SECTION QUESTION + www.example.com. IN A + SECTION AUTHORITY + com. IN NS a.gtld-servers.net. + SECTION ADDITIONAL + HEX_EDNSDATA_BEGIN + ;; we expect to receive empty + HEX_EDNSDATA_END + a.gtld-servers.net. IN A 192.5.6.30 + ENTRY_END +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 100 + ADDRESS 192.5.6.30 + ENTRY_BEGIN + MATCH opcode qtype qname ednsdata + ADJUST copy_id + REPLY QR NOERROR + SECTION QUESTION + com. IN NS + SECTION ANSWER + com. IN NS a.gtld-servers.net. + SECTION ADDITIONAL + HEX_EDNSDATA_BEGIN + ;; we expect to receive empty + HEX_EDNSDATA_END + a.gtld-servers.net. IN A 192.5.6.30 + ENTRY_END + + ENTRY_BEGIN + MATCH opcode qtype qname ednsdata + ADJUST copy_id + REPLY QR NOERROR + SECTION QUESTION + www.example.com. IN A + SECTION AUTHORITY + example.com. IN NS ns.example.com. + SECTION ADDITIONAL + HEX_EDNSDATA_BEGIN + ;; we expect to receive empty + HEX_EDNSDATA_END + ns.example.com. IN A 1.2.3.4 + ENTRY_END +RANGE_END + +; ns.example.com. +RANGE_BEGIN 0 100 + ADDRESS 1.2.3.4 + ENTRY_BEGIN + MATCH opcode qtype qname ednsdata + ADJUST copy_id + REPLY QR NOERROR + SECTION QUESTION + example.com. IN NS + SECTION ANSWER + example.com. IN NS ns.example.com. + example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854} + SECTION ADDITIONAL + HEX_EDNSDATA_BEGIN + ;; we expect to receive empty + HEX_EDNSDATA_END + ns.example.com. IN A 1.2.3.4 + ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854} + ENTRY_END + +; no data for ns.example.com. AAAA + ENTRY_BEGIN + MATCH opcode qtype qname + ADJUST copy_id + REPLY QR AA NOERROR + SECTION QUESTION + ns.example.com. IN AAAA + SECTION AUTHORITY + example.com. IN SOA a. b. 3 28800 7200 604800 3600 + example.com. 3600 IN RRSIG SOA 3 2 3600 20070926134150 20070829134150 2854 example.com. ACYHmWSLfBwPXwjI23+PW0db/DuqFwgpJYCbHOPeftbLR9nGy3nyEAE= + ns.example.com. 3600 IN NSEC op.example.com. A RRSIG NSEC + ns.example.com. 3600 IN RRSIG NSEC 3 3 3600 20070926134150 20070829134150 2854 example.com. AHMBw+lDAm9o2xG7v/8oWkYUc3WefUOuHFMHN9qZEp5/kooJqmlj974= + ENTRY_END + + ; response to DNSKEY priming query + ENTRY_BEGIN + MATCH opcode qtype qname ednsdata + ADJUST copy_id + REPLY QR NOERROR + SECTION QUESTION + example.com. IN DNSKEY + SECTION ANSWER + example.com. 3600 IN DNSKEY 256 3 3 ALXLUsWqUrY3JYER3T4TBJII s70j+sDS/UT2QRp61SE7S3E EXopNXoFE73JLRmvpi/UrOO/Vz4Se 6wXv/CYCKjGw06U4WRgR YXcpEhJROyNapmdIKSx hOzfLVE1gqA0PweZR8d tY3aNQSRn3sPpwJr6Mi /PqQKAMMrZ9ckJpf1+b QMOOvxgzz2U1GS18b3y ZKcgTMEaJzd/GZYzi/B N2DzQ0MsrSwYXfsNLFO Bbs8PJMW4LYIxeeOe6rUgkWOF 7CC9Dh/dduQ1QrsJhmZAEFfd6ByYV+ ;{id = 2854 (zsk), size = 1688b} + example.com. 3600 IN RRSIG DNSKEY 3 2 3600 20070926134802 20070829134802 2854 example.com. MCwCFG1yhRNtTEa3Eno2zhVVuy2EJX3wAhQeLyUp6+UXcpC5qGNu9tkrTEgPUg== ;{id = 2854} + SECTION AUTHORITY + example.com. IN NS ns.example.com. + example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854} + SECTION ADDITIONAL + HEX_EDNSDATA_BEGIN + ;; we expect to receive empty + HEX_EDNSDATA_END + ns.example.com. IN A 1.2.3.4 + ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854} + ENTRY_END + + ; response to query of interest + ENTRY_BEGIN + MATCH opcode qtype qname ednsdata + ADJUST copy_id + REPLY QR NOERROR + SECTION QUESTION + www.example.com. IN A + SECTION ANSWER + ; to make it bogus, the address is changed. + ; and also the RRSIG is expired (dated in 2005). + ;www.example.com. IN A 10.20.30.40 + ;www.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFC99iE9K5y2WNgI0gFvBWaTi9wm6AhUAoUqOpDtG5Zct+Qr9F3mSdnbc6V4= ;{id = 2854} + www.example.com. IN A 10.20.30.41 + www.example.com. 3600 IN RRSIG A 3 3 3600 20050926134150 20050829134150 2854 example.com. MC0CFC99iE9K5y2WNgI0gFvBWaTi9wm6AhUAoUqOpDtG5Zct+Qr9F3mSdnbc6V4= ;{id = 2854} + SECTION AUTHORITY + example.com. IN NS ns.example.com. + example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854} + SECTION ADDITIONAL + HEX_EDNSDATA_BEGIN + ; client is 127.0.0.1 + 00 08 ; OPC + 00 07 ; option length + 00 01 ; Family + 11 00 ; source mask, scopemask + 7f 00 00 ; address + HEX_EDNSDATA_END + ns.example.com. IN A 1.2.3.4 + ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCQMyTjn7WWwpwAR1LlVeLpRgZGuQIUCcJDEkwAuzytTDRlYK7nIMwH1CM= ;{id = 2854} + ENTRY_END + + ; response to query of interest without subnet + ENTRY_BEGIN + MATCH opcode qtype qname ednsdata + ADJUST copy_id + REPLY QR NOERROR + SECTION QUESTION + www.example.com. IN A + SECTION ANSWER + ; to make it bogus, the address is changed. + ; and also the RRSIG is expired (dated in 2005). + ;www.example.com. IN A 10.20.30.40 + ;www.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFC99iE9K5y2WNgI0gFvBWaTi9wm6AhUAoUqOpDtG5Zct+Qr9F3mSdnbc6V4= ;{id = 2854} + www.example.com. IN A 10.20.30.41 + www.example.com. 3600 IN RRSIG A 3 3 3600 20050926134150 20050829134150 2854 example.com. MC0CFC99iE9K5y2WNgI0gFvBWaTi9wm6AhUAoUqOpDtG5Zct+Qr9F3mSdnbc6V4= ;{id = 2854} + SECTION AUTHORITY + example.com. IN NS ns.example.com. + example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854} + SECTION ADDITIONAL + HEX_EDNSDATA_BEGIN + ;; we expect to receive empty + HEX_EDNSDATA_END + ns.example.com. IN A 1.2.3.4 + ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCQMyTjn7WWwpwAR1LlVeLpRgZGuQIUCcJDEkwAuzytTDRlYK7nIMwH1CM= ;{id = 2854} + ENTRY_END +RANGE_END + +; query for www.example.com 0.0.0.0/0 with CD flag +STEP 10 QUERY +ENTRY_BEGIN +REPLY RD CD DO NOERROR +SECTION QUESTION +www.example.com. IN A +HEX_EDNSDATA_BEGIN + ; 0.0.0.0/0 scope /0 + 00 08 ; OPC + 00 04 ; option length + 00 01 ; Family + 00 00 ; source mask, scopemask + ; address +HEX_EDNSDATA_END +ENTRY_END + +; recursion happens here. +STEP 20 CHECK_ANSWER +ENTRY_BEGIN + MATCH all ednsdata + REPLY QR RD CD RA DO NOERROR + SECTION QUESTION + www.example.com. IN A + SECTION ANSWER + www.example.com. IN A 10.20.30.41 + www.example.com. 3600 IN RRSIG A 3 3 3600 20050926134150 20050829134150 2854 example.com. MC0CFC99iE9K5y2WNgI0gFvBWaTi9wm6AhUAoUqOpDtG5Zct+Qr9F3mSdnbc6V4= ;{id = 2854} + SECTION ADDITIONAL + HEX_EDNSDATA_BEGIN + ; 0.0.0.0/0 scope /0 + 00 08 ; OPC + 00 04 ; option length + 00 01 ; Family + 00 00 ; source mask, scopemask + ; address + HEX_EDNSDATA_END +ENTRY_END + +; query for www.example.com 0.0.0.0/0 without CD flag +STEP 30 QUERY +ENTRY_BEGIN +REPLY RD DO NOERROR +SECTION QUESTION +www.example.com. IN A +HEX_EDNSDATA_BEGIN + ; 0.0.0.0/0 scope /0 + 00 08 ; OPC + 00 04 ; option length + 00 01 ; Family + 00 00 ; source mask, scopemask + ; address +HEX_EDNSDATA_END +ENTRY_END + +STEP 40 CHECK_ANSWER +ENTRY_BEGIN + MATCH all ednsdata + REPLY QR RD RA DO SERVFAIL + SECTION QUESTION + www.example.com. IN A + SECTION ADDITIONAL + HEX_EDNSDATA_BEGIN +; ; 0.0.0.0/0 scope /0 +; 00 08 ; OPC +; 00 04 ; option length +; 00 01 ; Family +; 00 00 ; source mask, scopemask +; ; address + HEX_EDNSDATA_END +ENTRY_END + +SCENARIO_END diff --git a/validator/validator.c b/validator/validator.c index c129df920..d6f57bcc9 100644 --- a/validator/validator.c +++ b/validator/validator.c @@ -2699,7 +2699,9 @@ val_operate(struct module_qstate* qstate, enum module_ev event, int id, if(!needs_validation(qstate, qstate->return_rcode, qstate->return_msg)) { /* no need to validate this */ - if(qstate->return_msg) + /* For valrec responses, leave at sec_status_unchecked, + * no security status has been requested for it. */ + if(qstate->return_msg && !qstate->is_valrec) qstate->return_msg->rep->security = sec_status_indeterminate; qstate->ext_state[id] = module_finished;