From: W.C.A. Wijngaards Date: Mon, 15 Jun 2026 14:50:42 +0000 (+0200) Subject: - Fix that dns64 bypasses rpz-passthru rule during X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=HEAD;p=thirdparty%2Funbound.git - Fix that dns64 bypasses rpz-passthru rule during synthesis. This restricted more than necessary. Thanks to Qifan Zhang, Palo Alto Networks, for the report. --- diff --git a/daemon/worker.c b/daemon/worker.c index f05aebdbf..6d922fd92 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -714,7 +714,8 @@ apply_respip_action(struct worker* worker, const struct query_info* qinfo, struct respip_client_info* cinfo, struct reply_info* rep, struct sockaddr_storage* addr, socklen_t addrlen, struct ub_packed_rrset_key** alias_rrset, - struct reply_info** encode_repp, struct auth_zones* az) + struct reply_info** encode_repp, struct auth_zones* az, + int* rpz_passthru) { struct respip_action_info actinfo = {0, 0, 0, 0, NULL, 0, NULL}; actinfo.action = respip_none; @@ -725,7 +726,7 @@ apply_respip_action(struct worker* worker, const struct query_info* qinfo, return 1; if(!respip_rewrite_reply(qinfo, cinfo, rep, encode_repp, &actinfo, - alias_rrset, 0, worker->scratchpad, az, NULL, + alias_rrset, 0, worker->scratchpad, az, rpz_passthru, worker->env.views, worker->env.respip_set)) return 0; @@ -772,7 +773,7 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo, int* is_secure_answer, struct ub_packed_rrset_key** alias_rrset, struct reply_info** partial_repp, struct reply_info* rep, uint16_t id, uint16_t flags, - struct comm_reply* repinfo, struct edns_data* edns) + struct comm_reply* repinfo, struct edns_data* edns, int* rpz_passthru) { time_t timenow = *worker->env.now; uint16_t udpsize = edns->udp_size; @@ -882,7 +883,7 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo, if((worker->daemon->use_response_ip || worker->daemon->use_rpz) && !partial_rep && !apply_respip_action(worker, qinfo, cinfo, rep, &repinfo->client_addr, repinfo->client_addrlen, alias_rrset, - &encode_rep, worker->env.auth_zones)) { + &encode_rep, worker->env.auth_zones, rpz_passthru)) { goto bail_out; } else if(partial_rep && !respip_merge_cname(partial_rep, qinfo, rep, cinfo, @@ -1982,7 +1983,7 @@ lookup_cache: &alias_rrset, &partial_rep, rep, *(uint16_t*)(void *)sldns_buffer_begin(c->buffer), sldns_buffer_read_u16_at(c->buffer, 2), repinfo, - &edns)) { + &edns, &rpz_passthru)) { /* prefetch it if the prefetch TTL expired. * Note that if there is more than one pass * its qname must be that used for cache diff --git a/dns64/dns64.c b/dns64/dns64.c index 8431f556a..314dc4639 100644 --- a/dns64/dns64.c +++ b/dns64/dns64.c @@ -660,6 +660,7 @@ handle_event_moddone(struct module_qstate* qstate, int id) /* Store the response in cache. */ if( (!iq || !iq->started_no_cache_store) && + !qstate->rpz_applied && !qstate->rpz_passthru && !qstate->is_subnet_answer && qstate->return_msg && qstate->return_msg->rep && @@ -1016,6 +1017,18 @@ dns64_inform_super(struct module_qstate* qstate, int id, /* Use return code from A query in response to client. */ if (super->return_rcode != LDNS_RCODE_NOERROR) super->return_rcode = qstate->return_rcode; + /* RPZ applied to the subquery need to then change (not cache) + * the super query. With the super query not cached, it is + * going to run the state machine modules on incoming queries, + * that fetch the subquery (cache) response, and modify it + * according to the rpz policy. That makes the synthesized + * super query also adjusted by rpz policies. But loses cache + * hits. Even though the subquery likely is answered from cache, + * internally in its state machine process. */ + if(qstate->rpz_applied) + super->rpz_applied = 1; + if(qstate->rpz_passthru) + super->rpz_passthru = 1; /* Since the super qstate has a new response, its errinf is removed. */ super->errinf = NULL; @@ -1031,6 +1044,7 @@ dns64_inform_super(struct module_qstate* qstate, int id, /* Store the generated response in cache. */ if ( super->return_msg && super->return_msg->rep && (!super_dq || !super_dq->started_no_cache_store) && + !super->rpz_applied && !super->rpz_passthru && !super->is_subnet_answer && !dns_cache_store(super->env, &super->qinfo, super->return_msg->rep, 0, super->prefetch_leeway, 0, NULL, super->query_flags, diff --git a/doc/Changelog b/doc/Changelog index aae20bf3e..e7d9dba89 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -49,6 +49,9 @@ exec of the hook. The ipsecmod hook, if a script, has to start now with a line like `#!/bin/sh`. Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix that dns64 bypasses rpz-passthru rule during + synthesis. This restricted more than necessary. Thanks to + Qifan Zhang, Palo Alto Networks, for the report. 12 June 2026: Wouter - Fix that for auth-zone and rpz zones the allow-notify diff --git a/services/mesh.c b/services/mesh.c index a2618b477..edfac6cf4 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -1275,6 +1275,9 @@ int mesh_add_sub(struct module_qstate* qstate, struct query_info* qinfo, log_err("mesh_attach_sub: out of memory"); return 0; } + /* inherit RPZ passthru from the parent so respip on the sub + * sees the same client-IP/qname PASSTHRU decision */ + (*sub)->s.rpz_passthru = qstate->rpz_passthru; #ifdef UNBOUND_DEBUG n = #else diff --git a/testdata/rpz_clientip_passthru_cache.rpl b/testdata/rpz_clientip_passthru_cache.rpl new file mode 100644 index 000000000..4abada518 --- /dev/null +++ b/testdata/rpz_clientip_passthru_cache.rpl @@ -0,0 +1,205 @@ +; config options +server: + target-fetch-policy: "0 0 0 0 0" + qname-minimisation: no + minimal-responses: yes + log-servfail: yes + access-control: 0.0.0.0/0 allow + module-config: "respip iterator" + +rpz: + name: "rpz.example.com." + rpz-log: yes + rpz-log-name: "rpz.example.com" + zonefile: +TEMPFILE_NAME rpz.example.com +TEMPFILE_CONTENTS rpz.example.com +$ORIGIN example.com. +rpz 3600 IN SOA ns1.rpz.gotham.com. hostmaster.rpz.example.com. ( + 1379078166 28800 7200 604800 7200 ) + 3600 IN NS ns1.rpz.example.com. + 3600 IN NS ns2.rpz.example.com. +$ORIGIN rpz.example.com. +; whitelist client 192.0.3.33 +32.33.3.0.192.rpz-client-ip CNAME rpz-passthru. +; block A 10.20.30.42 +32.42.30.20.10.rpz-ip CNAME . +TEMPFILE_END + +stub-zone: + name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test RPZ passthru query, with answer from cache. + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +tld. IN NS +SECTION AUTHORITY +tld. IN NS ns.tld. +SECTION ADDITIONAL +ns.tld. IN A 1.2.3.5 +ENTRY_END +RANGE_END + +; ns.tld +RANGE_BEGIN 0 100 + ADDRESS 1.2.3.5 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +tld. IN NS +SECTION ANSWER +tld. IN NS ns.tld +SECTION ADDITIONAL +ns.tld. IN A 1.2.3.5 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +ns.tld. IN A +SECTION ANSWER +ns.tld. IN A 1.2.3.5 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +ns.tld. IN AAAA +SECTION AUTHORITY +tld. 3600 IN SOA ns.tld. host.tld. 20201 3600 1800 604800 3600 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +example.tld. IN NS +SECTION AUTHORITY +example.tld. 5 IN NS ns.example.tld. +SECTION ADDITIONAL +ns.example.tld. 5 IN A 1.2.3.4 +ENTRY_END +RANGE_END + +; ns.example.tld. +RANGE_BEGIN 0 100 + ADDRESS 1.2.3.4 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +example.tld. IN NS +SECTION ANSWER +example.tld. 86400 IN NS ns.example.tld. +SECTION ADDITIONAL +ns.example.tld. 86400 IN A 1.2.3.4 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +ns.example.tld. IN A +SECTION ANSWER +ns.example.tld. IN A 1.2.3.4 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +ns.example.tld. IN AAAA +SECTION AUTHORITY +example.tld. 3600 IN SOA ns.example.tld. host.example.tld. 20301 3600 1800 604800 3600 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +bad.example.tld. IN A +SECTION ANSWER +bad.example.tld. IN A 10.20.30.42 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +bad.example.tld. IN AAAA +SECTION AUTHORITY +example.tld. 3600 IN SOA ns.example.tld. host.example.tld. 20301 3600 1800 604800 3600 +ENTRY_END + +RANGE_END + +; query the record directly, from a blocked client +STEP 1 QUERY ADDRESS 192.0.2.1 +ENTRY_BEGIN +REPLY RD NOERROR +SECTION QUESTION +bad.example.tld. IN A +ENTRY_END + +; RPZ works +STEP 2 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NXDOMAIN +SECTION QUESTION +bad.example.tld. IN A +SECTION ANSWER +ENTRY_END + +; query the record directly, from the unblocked client +STEP 5 QUERY ADDRESS 192.0.3.33 +ENTRY_BEGIN +REPLY RD NOERROR +SECTION QUESTION +bad.example.tld. IN A +ENTRY_END + +STEP 6 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +bad.example.tld. IN A +SECTION ANSWER +bad.example.tld. IN A 10.20.30.42 +ENTRY_END + +SCENARIO_END diff --git a/testdata/rpz_clientip_passthru_dns64.rpl b/testdata/rpz_clientip_passthru_dns64.rpl new file mode 100644 index 000000000..91ca4ef18 --- /dev/null +++ b/testdata/rpz_clientip_passthru_dns64.rpl @@ -0,0 +1,247 @@ +; config options +server: + target-fetch-policy: "0 0 0 0 0" + qname-minimisation: no + minimal-responses: yes + log-servfail: yes + access-control: 0.0.0.0/0 allow + ; module-config: "dns64 respip iterator" + ; or + module-config: "respip dns64 iterator" + dns64-prefix: 64:ff9b::/96 + ; possibly as well: + ; response-ip: 192.0.2.66/32 always_nxdomain + +rpz: + name: "rpz.example.com." + rpz-log: yes + rpz-log-name: "rpz.example.com" + zonefile: +TEMPFILE_NAME rpz.example.com +TEMPFILE_CONTENTS rpz.example.com +$ORIGIN example.com. +rpz 3600 IN SOA ns1.rpz.gotham.com. hostmaster.rpz.example.com. ( + 1379078166 28800 7200 604800 7200 ) + 3600 IN NS ns1.rpz.example.com. + 3600 IN NS ns2.rpz.example.com. +$ORIGIN rpz.example.com. +; whitelist client 192.0.3.33 +32.33.3.0.192.rpz-client-ip CNAME rpz-passthru. +; block A 10.20.30.42 +32.42.30.20.10.rpz-ip CNAME . +TEMPFILE_END + +stub-zone: + name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test RPZ passthru query with DNS64 enabled. + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +tld. IN NS +SECTION AUTHORITY +tld. IN NS ns.tld. +SECTION ADDITIONAL +ns.tld. IN A 1.2.3.5 +ENTRY_END +RANGE_END + +; ns.tld +RANGE_BEGIN 0 100 + ADDRESS 1.2.3.5 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +tld. IN NS +SECTION ANSWER +tld. IN NS ns.tld +SECTION ADDITIONAL +ns.tld. IN A 1.2.3.5 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +ns.tld. IN A +SECTION ANSWER +ns.tld. IN A 1.2.3.5 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +ns.tld. IN AAAA +SECTION AUTHORITY +tld. 3600 IN SOA ns.tld. host.tld. 20201 3600 1800 604800 3600 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +example.tld. IN NS +SECTION AUTHORITY +example.tld. 5 IN NS ns.example.tld. +SECTION ADDITIONAL +ns.example.tld. 5 IN A 1.2.3.4 +ENTRY_END +RANGE_END + +; ns.example.tld. +RANGE_BEGIN 0 100 + ADDRESS 1.2.3.4 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +example.tld. IN NS +SECTION ANSWER +example.tld. 86400 IN NS ns.example.tld. +SECTION ADDITIONAL +ns.example.tld. 86400 IN A 1.2.3.4 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +ns.example.tld. IN A +SECTION ANSWER +ns.example.tld. IN A 1.2.3.4 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +ns.example.tld. IN AAAA +SECTION AUTHORITY +example.tld. 3600 IN SOA ns.example.tld. host.example.tld. 20301 3600 1800 604800 3600 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +bad.example.tld. IN A +SECTION ANSWER +bad.example.tld. IN A 10.20.30.42 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +bad.example.tld. IN AAAA +SECTION AUTHORITY +example.tld. 3600 IN SOA ns.example.tld. host.example.tld. 20301 3600 1800 604800 3600 +ENTRY_END + +RANGE_END + +; query the record directly, from the unblocked client +STEP 5 QUERY ADDRESS 192.0.3.33 +ENTRY_BEGIN +REPLY RD NOERROR +SECTION QUESTION +bad.example.tld. IN A +ENTRY_END + +STEP 6 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +bad.example.tld. IN A +SECTION ANSWER +bad.example.tld. IN A 10.20.30.42 +ENTRY_END + +; query the record with dns64, from the unblocked client. +STEP 10 QUERY ADDRESS 192.0.3.33 +ENTRY_BEGIN +REPLY RD NOERROR +SECTION QUESTION +bad.example.tld. IN AAAA +ENTRY_END + +STEP 11 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +bad.example.tld. IN AAAA +SECTION ANSWER +bad.example.tld. 3600 IN AAAA 64:ff9b::a14:1e2a +ENTRY_END + +; query the record with dns64, from a blocked client. +STEP 15 QUERY ADDRESS 192.0.2.1 +ENTRY_BEGIN +REPLY RD NOERROR +SECTION QUESTION +bad.example.tld. IN AAAA +ENTRY_END + +STEP 16 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +bad.example.tld. IN AAAA +SECTION ANSWER +SECTION AUTHORITY +example.tld. 3600 IN SOA ns.example.tld. host.example.tld. 20301 3600 1800 604800 3600 +ENTRY_END + +; query the record directly, from a blocked client +STEP 21 QUERY ADDRESS 192.0.2.1 +ENTRY_BEGIN +REPLY RD NOERROR +SECTION QUESTION +bad.example.tld. IN A +ENTRY_END + +; RPZ works +STEP 22 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NXDOMAIN +SECTION QUESTION +bad.example.tld. IN A +SECTION ANSWER +ENTRY_END + +SCENARIO_END