From: W.C.A. Wijngaards Date: Fri, 19 May 2023 12:38:41 +0000 (+0200) Subject: - Fix RPZ removal of client-ip, nsip, nsdname triggers from IXFR. X-Git-Tag: release-1.18.0rc1~24^2~37 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=da78c42f889a9ae5b5aa89ae015fec9f2376ea60;p=thirdparty%2Funbound.git - Fix RPZ removal of client-ip, nsip, nsdname triggers from IXFR. --- diff --git a/doc/Changelog b/doc/Changelog index 8923aa857..8d07f417d 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,6 @@ +19 May 2023: Wouter + - Fix RPZ removal of client-ip, nsip, nsdname triggers from IXFR. + 16 May 2023: Wouter - Fix #888: [FR] Use kernel timestamps for dnstap. - Fix to print debug log for ancillary data with correct IP address. diff --git a/services/authzone.c b/services/authzone.c index 3898767c7..31ab510e2 100644 --- a/services/authzone.c +++ b/services/authzone.c @@ -1306,8 +1306,8 @@ az_remove_rr(struct auth_zone* z, uint8_t* rr, size_t rr_len, auth_data_delete(node); } if(z->rpz) { - rpz_remove_rr(z->rpz, z->namelen, dname, dname_len, rr_type, - rr_class, rdata, rdatalen); + rpz_remove_rr(z->rpz, z->name, z->namelen, dname, dname_len, + rr_type, rr_class, rdata, rdatalen); } return 1; } diff --git a/services/rpz.c b/services/rpz.c index e876f3f94..d0931f44b 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -1188,6 +1188,22 @@ rpz_find_zone(struct local_zones* zones, uint8_t* qname, size_t qname_len, uint1 return z; } +/** Find entry for RR type in the list of rrsets for the clientip. */ +static struct local_rrset* +rpz_find_synthesized_rrset(uint16_t qtype, + struct clientip_synthesized_rr* data) +{ + struct local_rrset* cursor = data->data; + while( cursor != NULL) { + struct packed_rrset_key* packed_rrset = &cursor->rrset->rk; + if(htons(qtype) == packed_rrset->type) { + return cursor; + } + cursor = cursor->next; + } + return NULL; +} + /** * Remove RR from RPZ's local-data * @param z: local-zone for RPZ, holding write lock @@ -1270,15 +1286,15 @@ rpz_rrset_delete_rr(struct resp_addr* raddr, uint16_t rr_type, uint8_t* rdata, } -/** Remove RR from RPZ's local-zone */ +/** Remove RR from rpz localzones structure */ static void -rpz_remove_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, - enum rpz_action a, uint16_t rr_type, uint16_t rr_class, - uint8_t* rdatawl, size_t rdatalen) +rpz_remove_local_zones_trigger(struct local_zones* zones, uint8_t* dname, + size_t dnamelen, enum rpz_action a, uint16_t rr_type, + uint16_t rr_class, uint8_t* rdatawl, size_t rdatalen) { struct local_zone* z; int delete_zone = 1; - z = rpz_find_zone(r->local_zones, dname, dnamelen, rr_class, + z = rpz_find_zone(zones, dname, dnamelen, rr_class, 1 /* only exact */, 1 /* wr lock */, 1 /* keep lock*/); if(!z) { verbose(VERB_ALGO, "rpz: cannot remove RR from IXFR, " @@ -1290,15 +1306,24 @@ rpz_remove_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, dnamelen, rr_type, rdatawl, rdatalen); else if(a != localzone_type_to_rpz_action(z->type)) { lock_rw_unlock(&z->lock); - lock_rw_unlock(&r->local_zones->lock); + lock_rw_unlock(&zones->lock); return; } lock_rw_unlock(&z->lock); if(delete_zone) { - local_zones_del_zone(r->local_zones, z); + local_zones_del_zone(zones, z); } - lock_rw_unlock(&r->local_zones->lock); - return; + lock_rw_unlock(&zones->lock); +} + +/** Remove RR from RPZ's local-zone */ +static void +rpz_remove_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, + enum rpz_action a, uint16_t rr_type, uint16_t rr_class, + uint8_t* rdatawl, size_t rdatalen) +{ + rpz_remove_local_zones_trigger(r->local_zones, dname, dnamelen, + a, rr_type, rr_class, rdatawl, rdatalen); } static void @@ -1335,15 +1360,159 @@ rpz_remove_response_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, lock_rw_unlock(&r->respip_set->lock); } +/** find and remove type from list of local_rrset entries*/ +static void +del_local_rrset_from_list(struct local_rrset** list_head, uint16_t dtype) +{ + struct local_rrset* prev=NULL, *p=*list_head; + while(p && ntohs(p->rrset->rk.type) != dtype) { + prev = p; + p = p->next; + } + if(!p) + return; /* rrset type not found */ + /* unlink it */ + if(prev) prev->next = p->next; + else *list_head = p->next; + /* no memory recycling for zone deletions ... */ +} + +/** Delete client-ip trigger RR from its RRset and perhaps also the rrset + * from the linked list. Returns if the local data is empty and the node can + * be deleted too, or not. */ +static int rpz_remove_clientip_rr(struct clientip_synthesized_rr* node, + uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen) +{ + struct local_rrset* rrset; + struct packed_rrset_data* d; + size_t index; + rrset = rpz_find_synthesized_rrset(rr_type, node); + if(rrset == NULL) + return 0; /* type not found, ignore */ + d = (struct packed_rrset_data*)rrset->rrset->entry.data; + if(!packed_rrset_find_rr(d, rdatawl, rdatalen, &index)) + return 0; /* RR not found, ignore */ + if(d->count == 1) { + /* regional alloc'd */ + /* delete the type entry from the list */ + del_local_rrset_from_list(&node->data, rr_type); + /* if the list is empty, the node can be removed too */ + if(node->data == NULL) + return 1; + } else if (d->count > 1) { + if(!local_rrset_remove_rr(d, index)) + return 0; + } + return 0; +} + +/** remove trigger RR from clientip_syntheized set tree. */ +static void +rpz_clientip_remove_trigger_rr(struct clientip_synthesized_rrset* set, + struct sockaddr_storage* addr, socklen_t addrlen, int net, + enum rpz_action a, uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen) +{ + struct clientip_synthesized_rr* node; + int delete_node = 1; + + lock_rw_wrlock(&set->lock); + node = (struct clientip_synthesized_rr*)addr_tree_find(&set->entries, + addr, addrlen, net); + if(node == NULL) { + /* netblock not found */ + verbose(VERB_ALGO, "rpz: cannot remove RR from IXFR, " + "RPZ address, netblock not found"); + lock_rw_unlock(&set->lock); + return; + } + lock_rw_wrlock(&node->lock); + if(a == RPZ_LOCAL_DATA_ACTION) { + /* remove RR, signal whether entry can be removed */ + delete_node = rpz_remove_clientip_rr(node, rr_type, rdatawl, + rdatalen); + } else if(a != node->action) { + /* ignore the RR with different action specification */ + delete_node = 0; + } + if(delete_node) { + rbtree_delete(&set->entries, node->node.node.key); + } + lock_rw_unlock(&set->lock); + lock_rw_unlock(&node->lock); + if(delete_node) { + lock_rw_destroy(&node->lock); + } +} + +/** Remove clientip trigger RR from RPZ. */ +static void +rpz_remove_clientip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, + enum rpz_action a, uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen) +{ + struct sockaddr_storage addr; + socklen_t addrlen; + int net, af; + if(a == RPZ_INVALID_ACTION) + return; + if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af)) + return; + rpz_clientip_remove_trigger_rr(r->client_set, &addr, addrlen, net, + a, rr_type, rdatawl, rdatalen); +} + +/** Remove nsip trigger RR from RPZ. */ +static void +rpz_remove_nsip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, + enum rpz_action a, uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen) +{ + struct sockaddr_storage addr; + socklen_t addrlen; + int net, af; + if(a == RPZ_INVALID_ACTION) + return; + if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af)) + return; + rpz_clientip_remove_trigger_rr(r->ns_set, &addr, addrlen, net, + a, rr_type, rdatawl, rdatalen); +} + +/** Remove nsdname trigger RR from RPZ. */ +static void +rpz_remove_nsdname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, + enum rpz_action a, uint16_t rr_type, uint16_t rr_class, + uint8_t* rdatawl, size_t rdatalen) +{ + uint8_t* dname_stripped = NULL; + size_t dnamelen_stripped = 0; + if(a == RPZ_INVALID_ACTION) + return; + if(!rpz_strip_nsdname_suffix(dname, dnamelen, &dname_stripped, + &dnamelen_stripped)) + return; + rpz_remove_local_zones_trigger(r->nsdname_zones, dname_stripped, + dnamelen_stripped, a, rr_type, rr_class, rdatawl, rdatalen); + free(dname_stripped); +} + void -rpz_remove_rr(struct rpz* r, size_t aznamelen, uint8_t* dname, size_t dnamelen, - uint16_t rr_type, uint16_t rr_class, uint8_t* rdatawl, size_t rdatalen) +rpz_remove_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, uint8_t* dname, + size_t dnamelen, uint16_t rr_type, uint16_t rr_class, uint8_t* rdatawl, + size_t rdatalen) { size_t policydnamelen; enum rpz_trigger t; enum rpz_action a; uint8_t* policydname; + if(rpz_type_ignored(rr_type)) { + /* this rpz action is not valid, eg. this is the SOA or NS RR */ + return; + } + if(!dname_subdomain_c(dname, azname)) { + /* not subdomain of the RPZ zone. */ + return; + } + if(!(policydname = calloc(1, LDNS_MAX_DOMAINLEN + 1))) return; @@ -1358,13 +1527,28 @@ rpz_remove_rr(struct rpz* r, size_t aznamelen, uint8_t* dname, size_t dnamelen, return; } t = rpz_dname_to_trigger(policydname, policydnamelen); + if(t == RPZ_INVALID_TRIGGER) { + /* skipping invalid trigger */ + free(policydname); + return; + } if(t == RPZ_QNAME_TRIGGER) { rpz_remove_qname_trigger(r, policydname, policydnamelen, a, rr_type, rr_class, rdatawl, rdatalen); } else if(t == RPZ_RESPONSE_IP_TRIGGER) { rpz_remove_response_ip_trigger(r, policydname, policydnamelen, a, rr_type, rdatawl, rdatalen); + } else if(t == RPZ_CLIENT_IP_TRIGGER) { + rpz_remove_clientip_trigger(r, policydname, policydnamelen, a, + rr_type, rdatawl, rdatalen); + } else if(t == RPZ_NSIP_TRIGGER) { + rpz_remove_nsip_trigger(r, policydname, policydnamelen, a, + rr_type, rdatawl, rdatalen); + } else if(t == RPZ_NSDNAME_TRIGGER) { + rpz_remove_nsdname_trigger(r, policydname, policydnamelen, a, + rr_type, rr_class, rdatawl, rdatalen); } + /* else it was an unsupported trigger, also skipped. */ free(policydname); } @@ -1563,21 +1747,6 @@ rpz_local_encode(struct module_env* env, struct query_info* qinfo, return 1; } -static struct local_rrset* -rpz_find_synthesized_rrset(uint16_t qtype, - struct clientip_synthesized_rr* data) -{ - struct local_rrset* cursor = data->data; - while( cursor != NULL) { - struct packed_rrset_key* packed_rrset = &cursor->rrset->rk; - if(htons(qtype) == packed_rrset->type) { - return cursor; - } - cursor = cursor->next; - } - return NULL; -} - /** allocate SOA record ubrrsetkey in region */ static struct ub_packed_rrset_key* make_soa_ubrrset(struct auth_zone* auth_zone, struct auth_rrset* soa, diff --git a/services/rpz.h b/services/rpz.h index 53781197a..ae93af9a8 100644 --- a/services/rpz.h +++ b/services/rpz.h @@ -152,6 +152,7 @@ int rpz_insert_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, uint8_t* dna /** * Delete policy matching RR, used for IXFR. * @param r: the rpz to add the policy to. + * @param azname: dname of the auth-zone * @param aznamelen: the length of the auth-zone name * @param dname: dname of the RR * @param dnamelen: length of the dname @@ -160,9 +161,9 @@ int rpz_insert_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, uint8_t* dna * @param rdatawl: rdata of the RR, prepended with the rdata size * @param rdatalen: length if the RR, including the prepended rdata size */ -void rpz_remove_rr(struct rpz* r, size_t aznamelen, uint8_t* dname, - size_t dnamelen, uint16_t rr_type, uint16_t rr_class, uint8_t* rdatawl, - size_t rdatalen); +void rpz_remove_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, + uint8_t* dname, size_t dnamelen, uint16_t rr_type, uint16_t rr_class, + uint8_t* rdatawl, size_t rdatalen); /** * Walk over the RPZ zones to find and apply a QNAME trigger policy. diff --git a/testdata/rpz_ixfr.rpl b/testdata/rpz_ixfr.rpl index ca2b62335..356663157 100644 --- a/testdata/rpz_ixfr.rpl +++ b/testdata/rpz_ixfr.rpl @@ -4,6 +4,7 @@ server: target-fetch-policy: "0 0 0 0 0" qname-minimisation: no rrset-roundrobin: no + access-control: 192.0.0.0/8 allow rpz: name: "rpz.example.com." @@ -22,6 +23,11 @@ d.rpz.example.com. IN CNAME . 32.3.123.0.10.rpz-ip.rpz.example.com. A 10.66.0.3 32.3.123.0.10.rpz-ip.rpz.example.com. A 10.66.0.4 32.4.123.0.10.rpz-ip.rpz.example.com. CNAME . +; also test client-ip, and remove it later with an IXFR. +24.0.5.0.192.rpz-client-ip A 127.0.0.5 +24.0.6.0.192.rpz-client-ip CNAME *. +32.41.30.20.10.rpz-nsip A 127.0.0.1 +ns.gotham.com.rpz-nsdname A 127.0.0.1 TEMPFILE_END stub-zone: @@ -97,6 +103,42 @@ SECTION ANSWER d.rpz-ip. IN A 10.0.123.4 ENTRY_END +ENTRY_BEGIN +MATCH opcode qname qtype +ADJUST copy_id +REPLY QR NOERROR AA +SECTION QUESTION +a.a. IN A +SECTION ANSWER +a.a. IN A 10.0.123.5 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +foo.com. IN NS +SECTION ANSWER +SECTION AUTHORITY +foo.com. 10 IN NS ns.foo.com. +SECTION ADDITIONAL +ns.foo.com. 10 IN A 10.20.30.41 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +gotham.com. IN NS +SECTION ANSWER +SECTION AUTHORITY +gotham.com. 10 IN NS ns.gotham.com. +SECTION ADDITIONAL +ns.gotham.com. 10 IN A 10.20.30.42 +ENTRY_END + ENTRY_BEGIN MATCH opcode qname qtype ADJUST copy_id @@ -124,6 +166,10 @@ d.rpz.example.com. IN CNAME . 32.3.123.0.10.rpz-ip.rpz.example.com. A 10.66.0.3 32.3.123.0.10.rpz-ip.rpz.example.com. A 10.66.0.4 32.4.123.0.10.rpz-ip.rpz.example.com. CNAME . +24.0.5.0.192.rpz-client-ip.rpz.example.com. A 127.0.0.5 +24.0.6.0.192.rpz-client-ip.rpz.example.com. CNAME *. +32.41.30.20.10.rpz-nsip.rpz.example.com. A 127.0.0.1 +ns.gotham.com.rpz-nsdname.rpz.example.com. A 127.0.0.1 rpz.example.com. IN SOA ns.rpz.example.com. hostmaster.rpz.example.com. 2 3600 900 86400 3600 b.rpz.example.com. TXT "hello from RPZ" c.rpz.example.com. TXT "hello from RPZ" @@ -136,6 +182,78 @@ ENTRY_END RANGE_END +; ns.foo.com +RANGE_BEGIN 0 100 + ADDRESS 10.20.30.41 +ENTRY_BEGIN +MATCH opcode qname qtype +ADJUST copy_id +REPLY QR NOERROR AA +SECTION QUESTION +ns.foo.com. IN A +SECTION ANSWER +ns.foo.com. 10 IN A 10.20.30.41 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname qtype +ADJUST copy_id +REPLY QR NOERROR AA +SECTION QUESTION +ns.foo.com. IN AAAA +SECTION ANSWER +SECTION AUTHORITY +foo.com. 10 IN SOA ns.foo.com. root.foo.com. 1 2 3 4 10 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname qtype +ADJUST copy_id +REPLY QR NOERROR AA +SECTION QUESTION +www.foo.com. IN A +SECTION ANSWER +www.foo.com. 10 IN A 10.20.30.42 +ENTRY_END + +RANGE_END + +; ns.gotham.com +RANGE_BEGIN 0 100 + ADDRESS 10.20.30.42 +ENTRY_BEGIN +MATCH opcode qname qtype +ADJUST copy_id +REPLY QR NOERROR AA +SECTION QUESTION +ns.gotham.com. IN A +SECTION ANSWER +ns.gotham.com. 10 IN A 10.20.30.42 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname qtype +ADJUST copy_id +REPLY QR NOERROR AA +SECTION QUESTION +ns.gotham.com. IN AAAA +SECTION ANSWER +SECTION AUTHORITY +gotham.com. 10 IN SOA ns.gotham.com. root.gotham.com. 1 2 3 4 10 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname qtype +ADJUST copy_id +REPLY QR NOERROR AA +SECTION QUESTION +www.gotham.com. IN A +SECTION ANSWER +www.gotham.com. 10 IN A 10.20.30.43 +ENTRY_END + +RANGE_END + STEP 1 QUERY ENTRY_BEGIN REPLY RD @@ -244,7 +362,6 @@ SECTION QUESTION d.rpz-ip. IN A ENTRY_END - STEP 15 CHECK_ANSWER ENTRY_BEGIN MATCH all @@ -253,7 +370,74 @@ SECTION QUESTION d.rpz-ip. IN A ENTRY_END -STEP 16 TIME_PASSES ELAPSE 1 +STEP 16 QUERY ADDRESS 192.0.5.1 +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +a.a. IN A +ENTRY_END + +STEP 17 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA AA NOERROR +SECTION QUESTION +a.a. IN A +SECTION ANSWER +a.a. IN A 127.0.0.5 +ENTRY_END + +STEP 18 QUERY ADDRESS 192.0.6.1 +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +a.a. IN A +ENTRY_END + +STEP 19 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA AA NOERROR +SECTION QUESTION +a.a. IN A +SECTION ANSWER +ENTRY_END + +STEP 20 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +www.foo.com. IN A +ENTRY_END + +STEP 21 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA AA NOERROR +SECTION QUESTION +www.foo.com. IN A +SECTION ANSWER +www.foo.com. IN A 127.0.0.1 +ENTRY_END + +STEP 22 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +www.gotham.com. IN A +ENTRY_END + +STEP 23 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA AA NOERROR +SECTION QUESTION +www.gotham.com. IN A +SECTION ANSWER +www.gotham.com. IN A 127.0.0.1 +ENTRY_END + +STEP 24 TIME_PASSES ELAPSE 1 STEP 30 TIME_PASSES ELAPSE 3600 STEP 40 TRAFFIC @@ -376,4 +560,72 @@ SECTION ANSWER d.rpz-ip. IN A 10.0.123.4 ENTRY_END +STEP 64 QUERY ADDRESS 192.0.5.1 +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +a.a. IN A +ENTRY_END + +STEP 65 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +a.a. IN A +SECTION ANSWER +a.a. IN A 10.0.123.5 +ENTRY_END + +STEP 66 QUERY ADDRESS 192.0.6.1 +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +a.a. IN A +ENTRY_END + +STEP 67 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +a.a. IN A +SECTION ANSWER +a.a. IN A 10.0.123.5 +ENTRY_END + +STEP 68 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +www.foo.com. IN A +ENTRY_END + +STEP 69 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +www.foo.com. IN A +SECTION ANSWER +www.foo.com. 10 IN A 10.20.30.42 +ENTRY_END + +STEP 70 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +www.gotham.com. IN A +ENTRY_END + +STEP 71 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +www.gotham.com. IN A +SECTION ANSWER +www.gotham.com. 10 IN A 10.20.30.43 +ENTRY_END + SCENARIO_END