From dd70c2ef9afc32fae0ce48c7419bed23e32d0370 Mon Sep 17 00:00:00 2001 From: mb Date: Thu, 29 Oct 2020 15:58:13 +0100 Subject: [PATCH 01/68] RPZ: implement stubs for rpz-tcp-only actions. --- respip/respip.c | 1 - services/localzone.c | 20 ++++++++++++++++++-- services/localzone.h | 4 ++++ services/rpz.c | 36 +++++++++++++++++++++++------------- testdata/rpz_qname.rpl | 40 ++++++++++++++++++++++++++++++++++++++-- testdata/rpz_respip.rpl | 28 ++++++++++++++++++++++++++++ 6 files changed, 111 insertions(+), 18 deletions(-) diff --git a/respip/respip.c b/respip/respip.c index 6fa4f1885..82ca37d50 100644 --- a/respip/respip.c +++ b/respip/respip.c @@ -807,7 +807,6 @@ respip_nodata_answer(uint16_t qtype, enum respip_action action, * is explicitly specified. */ int rcode = (action == respip_always_nxdomain)? LDNS_RCODE_NXDOMAIN:LDNS_RCODE_NOERROR; - /* We should empty the answer section except for any preceding * CNAMEs (in that case rrset_id > 0). Type-ANY case is * special as noted in respip_data_answer(). */ diff --git a/services/localzone.c b/services/localzone.c index cad460663..b5d8472bc 100644 --- a/services/localzone.c +++ b/services/localzone.c @@ -1514,6 +1514,15 @@ local_zone_does_not_cover(struct local_zone* z, struct query_info* qinfo, return (lr == NULL); } +static inline int +local_zone_is_udp_query(struct comm_reply* repinfo) { + return repinfo != NULL + ? (repinfo->c != NULL + ? repinfo->c->type == comm_udp + : 0) + : 0; +} + int local_zones_zone_answer(struct local_zone* z, struct module_env* env, struct query_info* qinfo, struct edns_data* edns, @@ -1536,7 +1545,9 @@ local_zones_zone_answer(struct local_zone* z, struct module_env* env, lz_type == local_zone_redirect || lz_type == local_zone_inform_redirect || lz_type == local_zone_always_nxdomain || - lz_type == local_zone_always_nodata) { + lz_type == local_zone_always_nodata || + (lz_type == local_zone_truncate + && local_zone_is_udp_query(repinfo))) { /* for static, reply nodata or nxdomain * for redirect, reply nodata */ /* no additional section processing, @@ -1546,8 +1557,10 @@ local_zones_zone_answer(struct local_zone* z, struct module_env* env, */ int rcode = (ld || lz_type == local_zone_redirect || lz_type == local_zone_inform_redirect || - lz_type == local_zone_always_nodata)? + lz_type == local_zone_always_nodata || + lz_type == local_zone_truncate)? LDNS_RCODE_NOERROR:LDNS_RCODE_NXDOMAIN; + rcode = lz_type == local_zone_truncate ? (rcode|BIT_TC) : rcode; if(z->soa) return local_encode(qinfo, env, edns, repinfo, buf, temp, z->soa, 0, rcode); @@ -1763,6 +1776,7 @@ const char* local_zone_type2str(enum localzone_type t) case local_zone_always_nodata: return "always_nodata"; case local_zone_always_deny: return "always_deny"; case local_zone_noview: return "noview"; + case local_zone_truncate: return "truncate"; case local_zone_invalid: return "invalid"; } return "badtyped"; @@ -1800,6 +1814,8 @@ int local_zone_str2type(const char* type, enum localzone_type* t) *t = local_zone_always_deny; else if(strcmp(type, "noview") == 0) *t = local_zone_noview; + else if(strcmp(type, "truncate") == 0) + *t = local_zone_truncate; else if(strcmp(type, "nodefault") == 0) *t = local_zone_nodefault; else return 0; diff --git a/services/localzone.h b/services/localzone.h index bb3593936..39517c36a 100644 --- a/services/localzone.h +++ b/services/localzone.h @@ -98,6 +98,8 @@ enum localzone_type { local_zone_always_deny, /** answer not from the view, but global or no-answer */ local_zone_noview, + /** truncate the response; client should retry via tcp */ + local_zone_truncate, /** Invalid type, cannot be used to generate answer */ local_zone_invalid }; @@ -556,6 +558,8 @@ enum respip_action { respip_always_nodata = local_zone_always_nodata, /** answer with nodata response */ respip_always_deny = local_zone_always_deny, + /** RPZ: truncate answer in order to force switch to tcp */ + respip_truncate = local_zone_truncate, /* The rest of the values are only possible as * access-control-tag-action */ diff --git a/services/rpz.c b/services/rpz.c index 13304652c..25f8c8892 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -213,8 +213,8 @@ rpz_action_to_localzone_type(enum rpz_action a) case RPZ_PASSTHRU_ACTION: return local_zone_always_transparent; case RPZ_LOCAL_DATA_ACTION: /* fallthrough */ case RPZ_CNAME_OVERRIDE_ACTION: return local_zone_redirect; + case RPZ_TCP_ONLY_ACTION: return local_zone_truncate; case RPZ_INVALID_ACTION: /* fallthrough */ - case RPZ_TCP_ONLY_ACTION: /* fallthrough */ default: return local_zone_invalid; } } @@ -223,15 +223,15 @@ enum respip_action rpz_action_to_respip_action(enum rpz_action a) { switch(a) { - case RPZ_NXDOMAIN_ACTION: return respip_always_nxdomain; - case RPZ_NODATA_ACTION: return respip_always_nodata; - case RPZ_DROP_ACTION: return respip_always_deny; - case RPZ_PASSTHRU_ACTION: return respip_always_transparent; - case RPZ_LOCAL_DATA_ACTION: /* fallthrough */ + case RPZ_NXDOMAIN_ACTION: return respip_always_nxdomain; + case RPZ_NODATA_ACTION: return respip_always_nodata; + case RPZ_DROP_ACTION: return respip_always_deny; + case RPZ_PASSTHRU_ACTION: return respip_always_transparent; + case RPZ_LOCAL_DATA_ACTION: /* fallthrough */ case RPZ_CNAME_OVERRIDE_ACTION: return respip_redirect; - case RPZ_INVALID_ACTION: /* fallthrough */ - case RPZ_TCP_ONLY_ACTION: /* fallthrough */ - default: return respip_invalid; + case RPZ_TCP_ONLY_ACTION: return respip_truncate; + case RPZ_INVALID_ACTION: /* fallthrough */ + default: return respip_invalid; } } @@ -244,6 +244,7 @@ localzone_type_to_rpz_action(enum localzone_type lzt) case local_zone_always_deny: return RPZ_DROP_ACTION; case local_zone_always_transparent: return RPZ_PASSTHRU_ACTION; case local_zone_redirect: return RPZ_LOCAL_DATA_ACTION; + case local_zone_truncate: return RPZ_TCP_ONLY_ACTION; case local_zone_invalid: default: return RPZ_INVALID_ACTION; @@ -259,6 +260,7 @@ respip_action_to_rpz_action(enum respip_action a) case respip_always_deny: return RPZ_DROP_ACTION; case respip_always_transparent: return RPZ_PASSTHRU_ACTION; case respip_redirect: return RPZ_LOCAL_DATA_ACTION; + case respip_truncate: return RPZ_TCP_ONLY_ACTION; case respip_invalid: default: return RPZ_INVALID_ACTION; @@ -478,13 +480,17 @@ rpz_insert_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, char* rrstr; int newzone = 0; - if(a == RPZ_TCP_ONLY_ACTION || a == RPZ_INVALID_ACTION) { + if(a == RPZ_INVALID_ACTION) { verbose(VERB_ALGO, "RPZ: skipping unsupported action: %s", rpz_action_to_string(a)); free(dname); return; } + if(a == RPZ_TCP_ONLY_ACTION) { + verbose(VERB_ALGO, "RPZ: insert qname trigger: tcp-only"); + } + lock_rw_wrlock(&r->local_zones->lock); /* exact match */ z = local_zones_find(r->local_zones, dname, dnamelen, dnamelabs, @@ -550,13 +556,16 @@ rpz_insert_response_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, char* rrstr; enum respip_action respa = rpz_action_to_respip_action(a); - if(a == RPZ_TCP_ONLY_ACTION || a == RPZ_INVALID_ACTION || - respa == respip_invalid) { + if(a == RPZ_INVALID_ACTION || respa == respip_invalid) { verbose(VERB_ALGO, "RPZ: skipping unsupported action: %s", rpz_action_to_string(a)); return 0; } + if(a == RPZ_TCP_ONLY_ACTION) { + verbose(VERB_ALGO, "RPZ: insert respip trigger: tcp-only"); + } + if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af)) return 0; @@ -984,6 +993,7 @@ rpz_apply_qname_trigger(struct auth_zones* az, struct module_env* env, lock_rw_unlock(&a->lock); /* not found in this auth_zone */ } lock_rw_unlock(&az->rpz_lock); + if(!z) return 0; /* not holding auth_zone.lock anymore */ @@ -1032,7 +1042,7 @@ rpz_apply_qname_trigger(struct auth_zones* az, struct module_env* env, lock_rw_unlock(&a->lock); return !qinfo->local_alias; } - +verbose(VERB_ALGO, "xxxxxx repinfo=%p is_udp=%d", repinfo, repinfo->c->type == comm_udp); ret = local_zones_zone_answer(z, env, qinfo, edns, repinfo, buf, temp, 0 /* no local data used */, lzt); if(r->log) diff --git a/testdata/rpz_qname.rpl b/testdata/rpz_qname.rpl index 7940e9392..4f17f8577 100644 --- a/testdata/rpz_qname.rpl +++ b/testdata/rpz_qname.rpl @@ -38,6 +38,7 @@ d TXT "local data 2nd zone" e CNAME *.a.example. *.e CNAME *.b.example. drop CNAME rpz-drop. +tcp CNAME rpz-tcp-only. TEMPFILE_END stub-zone: @@ -295,10 +296,45 @@ something.e.b.example. IN TXT "*.b.example. answer from upstream ns" ENTRY_END ; deny zone -STEP 90 QUERY +;STEP 90 QUERY +;ENTRY_BEGIN +;SECTION QUESTION +;drop. IN TXT +;ENTRY_END + +; tcp-only action + +STEP 95 QUERY ENTRY_BEGIN SECTION QUESTION -drop. IN TXT +tcp. IN TXT ENTRY_END + +STEP 96 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RA AA TC NOERROR +SECTION QUESTION +tcp. IN TXT +SECTION ANSWER +ENTRY_END + +STEP 97 QUERY +ENTRY_BEGIN +MATCH TCP +SECTION QUESTION +tcp. IN TXT +ENTRY_END + +STEP 98 CHECK_ANSWER +ENTRY_BEGIN +MATCH all TCP +REPLY QR RD RA AA NOERROR +SECTION QUESTION +tcp. IN TXT +SECTION ANSWER +tcp. IN TXT "local tcp data 2nd zone" +ENTRY_END + ; no answer is checked at exit of testbound. SCENARIO_END diff --git a/testdata/rpz_respip.rpl b/testdata/rpz_respip.rpl index 94f998be6..894a7cc5f 100644 --- a/testdata/rpz_respip.rpl +++ b/testdata/rpz_respip.rpl @@ -20,6 +20,7 @@ $ORIGIN rpz.example.com. 16.0.0.10.10.rpz-ip CNAME . 24.0.10.10.10.rpz-ip CNAME rpz-drop. 32.10.10.10.10.rpz-ip CNAME rpz-passthru. +32.1.1.1.10.rpz-ip CNAME rpz-tcp-only. 32.zz.db8.2001.rpz-ip CNAME *. 48.zz.aa.db8.2001.rpz-ip CNAME . 64.zz.bb.aa.db8.2001.rpz-ip CNAME rpz-drop. @@ -217,6 +218,16 @@ SECTION ANSWER h. IN AAAA 2001:db8:aa:bb:cc::124 ENTRY_END +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +y. IN A +SECTION ANSWER +y. IN A 10.1.1.1 +ENTRY_END + RANGE_END STEP 1 QUERY @@ -446,4 +457,21 @@ SECTION QUESTION e. IN AAAA ENTRY_END STEP 29 TIME_PASSES ELAPSE 12 + +STEP 30 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +y. IN A +ENTRY_END + +STEP 31 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR TC RD RA NOERROR +SECTION QUESTION +y. IN A +SECTION ANSWER +ENTRY_END + SCENARIO_END From fb8c5a764d52c19d9330a095b0b1f766f41cb080 Mon Sep 17 00:00:00 2001 From: mb Date: Tue, 3 Nov 2020 11:02:36 +0100 Subject: [PATCH 02/68] RPZ: provide tcp-only triggers for respip actions --- services/mesh.c | 20 ++++++++++++++++++++ services/rpz.c | 6 ++---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/services/mesh.c b/services/mesh.c index cd9050936..a255595e8 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -1175,6 +1175,22 @@ mesh_do_callback(struct mesh_state* m, int rcode, struct reply_info* rep, m->s.env->mesh->num_reply_addrs--; } +static inline int +mesh_is_rpz_respip_tcponly_action(struct mesh_state const* m) +{ + struct respip_action_info const* respip_info = m->s.respip_action_info; + return respip_info == NULL + ? 0 + : (respip_info->rpz_used + && !respip_info->rpz_disabled + && respip_info->action == respip_truncate); +} + +static inline int +mesh_is_udp(struct mesh_reply const* r) { + return r->query_reply.c->type == comm_udp; +} + /** * Send reply to mesh reply entry * @param m: mesh state to send it for. @@ -1193,6 +1209,10 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, struct timeval end_time; struct timeval duration; int secure; + + rcode = mesh_is_udp(r) && mesh_is_rpz_respip_tcponly_action(m) + ? (rcode|BIT_TC) : rcode; + /* Copy the client's EDNS for later restore, to make sure the edns * compare is with the correct edns options. */ struct edns_data edns_bak = r->edns; diff --git a/services/rpz.c b/services/rpz.c index 25f8c8892..fb047a7f2 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -480,6 +480,8 @@ rpz_insert_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, char* rrstr; int newzone = 0; + verbose(VERB_ALGO, "RPZ: insert qname trigger: %s", rpz_action_to_string(a)); + if(a == RPZ_INVALID_ACTION) { verbose(VERB_ALGO, "RPZ: skipping unsupported action: %s", rpz_action_to_string(a)); @@ -487,10 +489,6 @@ rpz_insert_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, return; } - if(a == RPZ_TCP_ONLY_ACTION) { - verbose(VERB_ALGO, "RPZ: insert qname trigger: tcp-only"); - } - lock_rw_wrlock(&r->local_zones->lock); /* exact match */ z = local_zones_find(r->local_zones, dname, dnamelen, dnamelabs, From 1ab84a1d27dd9178caefe0d09b7fa99e48029e06 Mon Sep 17 00:00:00 2001 From: mb Date: Tue, 3 Nov 2020 11:03:29 +0100 Subject: [PATCH 03/68] RPZ: testbound: add qname and respip tcp-only examples --- testdata/rpz_qname_tcponly.rpl | 117 ++++++++++++++++++ testdata/rpz_respip_tcponly.rpl | 207 ++++++++++++++++++++++++++++++++ 2 files changed, 324 insertions(+) create mode 100644 testdata/rpz_qname_tcponly.rpl create mode 100644 testdata/rpz_respip_tcponly.rpl diff --git a/testdata/rpz_qname_tcponly.rpl b/testdata/rpz_qname_tcponly.rpl new file mode 100644 index 000000000..d30b88616 --- /dev/null +++ b/testdata/rpz_qname_tcponly.rpl @@ -0,0 +1,117 @@ +; config options +server: + module-config: "respip validator iterator" + target-fetch-policy: "0 0 0 0 0" + qname-minimisation: no + +rpz: + name: "rpz.example.com." + zonefile: +TEMPFILE_NAME rpz.example.com +TEMPFILE_CONTENTS rpz.example.com +$ORIGIN example.com. +rpz 3600 IN SOA ns1.rpz.example.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. +a.a CNAME rpz-passthru. +b.a CNAME rpz-tcp-only. +TEMPFILE_END + +stub-zone: + name: "a." + stub-addr: 10.20.30.40 +CONFIG_END + +SCENARIO_BEGIN Test RPZ qname trigger and tcp-only action + +RANGE_BEGIN 0 100 + ADDRESS 10.20.30.40 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a. IN NS +SECTION ANSWER +a. IN NS ns.a. +SECTION ADDITIONAL +ns.a IN A 10.20.30.40 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.a. IN TXT +SECTION ANSWER +a.a. IN TXT "upstream txt rr a.a." +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +b.a. IN TXT +SECTION ANSWER +b.a. IN TXT "upstream txt rr b.a." +ENTRY_END + +RANGE_END + +STEP 10 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +a.a. IN TXT +ENTRY_END + +STEP 11 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +a.a. IN TXT +SECTION ANSWER +a.a. IN TXT "upstream txt rr a.a." +ENTRY_END + +STEP 20 QUERY +ENTRY_BEGIN +MATCH UDP +REPLY RD +SECTION QUESTION +b.a. IN TXT +ENTRY_END + +STEP 21 CHECK_ANSWER +ENTRY_BEGIN +MATCH all UDP +REPLY QR AA TC RD RA NOERROR +SECTION QUESTION +b.a. IN TXT +SECTION ANSWER +ENTRY_END + +STEP 30 QUERY +ENTRY_BEGIN +MATCH TCP +REPLY RD +SECTION QUESTION +b.a. IN TXT +ENTRY_END + +STEP 31 CHECK_ANSWER +ENTRY_BEGIN +MATCH all TCP +REPLY QR RD RA NOERROR +SECTION QUESTION +b.a. IN TXT +SECTION ANSWER +b.a. IN TXT "upstream txt rr b.a." +ENTRY_END + +SCENARIO_END diff --git a/testdata/rpz_respip_tcponly.rpl b/testdata/rpz_respip_tcponly.rpl new file mode 100644 index 000000000..c495de203 --- /dev/null +++ b/testdata/rpz_respip_tcponly.rpl @@ -0,0 +1,207 @@ +; config options +server: + module-config: "respip validator iterator" + target-fetch-policy: "0 0 0 0 0" + qname-minimisation: no + +rpz: + name: "rpz.example.com." + zonefile: +TEMPFILE_NAME rpz.example.com +TEMPFILE_CONTENTS rpz.example.com +$ORIGIN example.com. +rpz 3600 IN SOA ns1.rpz.example.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. +8.0.0.0.10.rpz-ip CNAME *. +16.0.0.10.10.rpz-ip CNAME . +24.0.10.10.10.rpz-ip CNAME rpz-drop. +32.10.10.10.10.rpz-ip CNAME rpz-passthru. +32.1.1.1.10.rpz-ip CNAME rpz-tcp-only. +TEMPFILE_END + +stub-zone: + name: "." + stub-addr: 10.20.30.40 +CONFIG_END + +SCENARIO_BEGIN Test RPZ response IP address trigger and tcp-only action + +RANGE_BEGIN 0 100 + ADDRESS 10.20.30.40 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS ns. +SECTION ADDITIONAL +ns. IN A 10.20.30.40 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a. IN A +SECTION ANSWER +a. IN A 10.0.0.123 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +b. IN A +SECTION ANSWER +b. IN A 10.1.0.123 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +c. IN A +SECTION ANSWER +c. IN A 10.11.0.123 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +d. IN A +SECTION ANSWER +d. IN A 10.10.0.123 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +f. IN A +SECTION ANSWER +f. IN A 10.10.10.10 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +y. IN A +SECTION ANSWER +y. IN A 10.1.1.1 +ENTRY_END + +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +a. IN A +ENTRY_END + +STEP 2 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +a. IN A +SECTION ANSWER +ENTRY_END + +STEP 10 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +b. IN A +ENTRY_END + +STEP 11 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +b. IN A +SECTION ANSWER +ENTRY_END + +STEP 13 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +d. IN A +ENTRY_END + +STEP 14 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NXDOMAIN +SECTION QUESTION +d. IN A +SECTION ANSWER +ENTRY_END + +STEP 17 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +f. IN A +ENTRY_END + +STEP 18 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +f. IN A +SECTION ANSWER +f. IN A 10.10.10.10 +ENTRY_END + +STEP 30 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +y. IN A +ENTRY_END + +STEP 31 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR TC RD RA NOERROR +SECTION QUESTION +y. IN A +SECTION ANSWER +ENTRY_END + +STEP 40 QUERY +ENTRY_BEGIN +MATCH TCP +REPLY RD +SECTION QUESTION +y. IN A +ENTRY_END + +STEP 41 CHECK_ANSWER +ENTRY_BEGIN +MATCH all TCP +REPLY QR RD RA NOERROR +SECTION QUESTION +y. IN A +SECTION ANSWER +y. IN A 10.1.1.1 +ENTRY_END + +SCENARIO_END From fc4de71fe07e2272d17fb1ebe753bd61b6476c97 Mon Sep 17 00:00:00 2001 From: mb Date: Tue, 3 Nov 2020 15:45:26 +0100 Subject: [PATCH 04/68] RPZ: fix for c89 --- services/mesh.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/services/mesh.c b/services/mesh.c index a255595e8..ec2e940a4 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -1209,10 +1209,6 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, struct timeval end_time; struct timeval duration; int secure; - - rcode = mesh_is_udp(r) && mesh_is_rpz_respip_tcponly_action(m) - ? (rcode|BIT_TC) : rcode; - /* Copy the client's EDNS for later restore, to make sure the edns * compare is with the correct edns options. */ struct edns_data edns_bak = r->edns; @@ -1222,6 +1218,11 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, * null stops the mesh state remove and thus * reply_list modification and accounting */ struct mesh_reply* rlist = m->reply_list; + + /* RPZ: apply actions */ + rcode = mesh_is_udp(r) && mesh_is_rpz_respip_tcponly_action(m) + ? (rcode|BIT_TC) : rcode; + /* examine security status */ if(m->s.env->need_to_validate && (!(r->qflags&BIT_CD) || m->s.env->cfg->ignore_cd) && rep && From bd0c910830808b6d039bc4ac2655a95efd3991fe Mon Sep 17 00:00:00 2001 From: mb Date: Wed, 4 Nov 2020 17:00:28 +0100 Subject: [PATCH 05/68] RPZ: provide rpz-client-ip trigger and actions --- services/localzone.c | 4 +- services/rpz.c | 314 +++++++++++++++++++++++++++++--------- services/rpz.h | 1 + testdata/rpz_clientip.rpl | 180 ++++++++++++++++++++++ 4 files changed, 427 insertions(+), 72 deletions(-) create mode 100644 testdata/rpz_clientip.rpl diff --git a/services/localzone.c b/services/localzone.c index b5d8472bc..5e2104a7c 100644 --- a/services/localzone.c +++ b/services/localzone.c @@ -1561,7 +1561,7 @@ local_zones_zone_answer(struct local_zone* z, struct module_env* env, lz_type == local_zone_truncate)? LDNS_RCODE_NOERROR:LDNS_RCODE_NXDOMAIN; rcode = lz_type == local_zone_truncate ? (rcode|BIT_TC) : rcode; - if(z->soa) + if(z != NULL && z->soa) return local_encode(qinfo, env, edns, repinfo, buf, temp, z->soa, 0, rcode); local_error_encode(qinfo, env, edns, repinfo, buf, temp, rcode, @@ -1578,7 +1578,7 @@ local_zones_zone_answer(struct local_zone* z, struct module_env* env, * does not, then we should make this noerror/nodata */ if(ld && ld->rrsets) { int rcode = LDNS_RCODE_NOERROR; - if(z->soa) + if(z != NULL && z->soa) return local_encode(qinfo, env, edns, repinfo, buf, temp, z->soa, 0, rcode); local_error_encode(qinfo, env, edns, repinfo, buf, temp, rcode, diff --git a/services/rpz.c b/services/rpz.c index fb047a7f2..f8fa803de 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -51,6 +51,8 @@ #include "util/locks.h" #include "util/regional.h" +typedef struct resp_addr rpz_aclnode_type; + /** string for RPZ action enum */ const char* rpz_action_to_string(enum rpz_action a) @@ -305,6 +307,7 @@ void rpz_delete(struct rpz* r) return; local_zones_delete(r->local_zones); respip_set_delete(r->respip_set); + respip_set_delete(r->client_set); regional_destroy(r->region); free(r->taglist); free(r->log_name); @@ -317,12 +320,16 @@ rpz_clear(struct rpz* r) /* must hold write lock on auth_zone */ local_zones_delete(r->local_zones); respip_set_delete(r->respip_set); + respip_set_delete(r->client_set); if(!(r->local_zones = local_zones_create())){ return 0; } if(!(r->respip_set = respip_set_create())) { return 0; } + if(!(r->client_set = respip_set_create())) { + return 0; + } return 1; } @@ -332,6 +339,10 @@ rpz_finish_config(struct rpz* r) lock_rw_wrlock(&r->respip_set->lock); addr_tree_init_parents(&r->respip_set->ip_tree); lock_rw_unlock(&r->respip_set->lock); + + lock_rw_wrlock(&r->client_set->lock); + addr_tree_init_parents(&r->client_set->ip_tree); + lock_rw_unlock(&r->client_set->lock); } /** new rrset containing CNAME override, does not yet contain a dname */ @@ -398,6 +409,12 @@ rpz_create(struct config_auth* p) if(!(r->respip_set = respip_set_create())) { goto err; } + + /* (ab)use respip for client acl */ + if(!(r->client_set = respip_set_create())) { + goto err; + } + r->taglistlen = p->rpz_taglistlen; r->taglist = memdup(p->rpz_taglist, r->taglistlen); if(p->rpz_action_override) { @@ -440,6 +457,8 @@ err: local_zones_delete(r->local_zones); if(r->respip_set) respip_set_delete(r->respip_set); + if(r->client_set) + respip_set_delete(r->client_set); if(r->taglist) free(r->taglist); if(r->region) @@ -541,57 +560,95 @@ rpz_insert_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, return; } +static int +rpz_insert_ipaddr_based_trigger(struct respip_set* set, struct sockaddr_storage* addr, + socklen_t addrlen, int net, enum rpz_action a, uint16_t rrtype, + uint16_t rrclass, uint32_t ttl, uint8_t* rdata, size_t rdata_len, + uint8_t* rr, size_t rr_len) +{ + struct resp_addr* node; + char* rrstr; + enum respip_action respa = rpz_action_to_respip_action(a); + + lock_rw_wrlock(&set->lock); + rrstr = sldns_wire2str_rr(rr, rr_len); + if(!rrstr) { + log_err("malloc error while inserting RPZ respip trigger"); + lock_rw_unlock(&set->lock); + return 0; + } + if(!(node=respip_sockaddr_find_or_create(set, addr, addrlen, + net, 1, rrstr))) { + lock_rw_unlock(&set->lock); + free(rrstr); + return 0; + } + + lock_rw_wrlock(&node->lock); + lock_rw_unlock(&set->lock); + node->action = respa; + + if(a == RPZ_LOCAL_DATA_ACTION) { + respip_enter_rr(set->region, node, rrtype, + rrclass, ttl, rdata, rdata_len, rrstr, ""); + } + lock_rw_unlock(&node->lock); + free(rrstr); + return 1; +} + +static int +rpz_insert_client_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, + enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl, + uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len) +{ + struct sockaddr_storage addr; + socklen_t addrlen; + int net, af; + enum respip_action respa = rpz_action_to_respip_action(a); + + verbose(VERB_ALGO, "RPZ: insert client ip trigger: %s", rpz_action_to_string(a)); + if(a == RPZ_INVALID_ACTION || respa == respip_invalid) { + verbose(VERB_ALGO, "RPZ: skipping unsupported action: %s", + rpz_action_to_string(a)); + return 0; + } + + if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af)) { + verbose(VERB_ALGO, "RPZ: unable to parse client ip"); + return 0; + } + + return rpz_insert_ipaddr_based_trigger(r->client_set, &addr, addrlen, net, + a, rrtype, rrclass, ttl, rdata, rdata_len, rr, rr_len); +} + /** Insert RR into RPZ's respip_set */ static int rpz_insert_response_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl, uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len) { - struct resp_addr* node; struct sockaddr_storage addr; socklen_t addrlen; int net, af; - char* rrstr; enum respip_action respa = rpz_action_to_respip_action(a); + verbose(VERB_ALGO, "RPZ: insert response ip trigger: %s", rpz_action_to_string(a)); + if(a == RPZ_INVALID_ACTION || respa == respip_invalid) { verbose(VERB_ALGO, "RPZ: skipping unsupported action: %s", rpz_action_to_string(a)); return 0; } - if(a == RPZ_TCP_ONLY_ACTION) { - verbose(VERB_ALGO, "RPZ: insert respip trigger: tcp-only"); - } - - if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af)) - return 0; - - lock_rw_wrlock(&r->respip_set->lock); - rrstr = sldns_wire2str_rr(rr, rr_len); - if(!rrstr) { - log_err("malloc error while inserting RPZ respip trigger"); - lock_rw_unlock(&r->respip_set->lock); - return 0; - } - if(!(node=respip_sockaddr_find_or_create(r->respip_set, &addr, addrlen, - net, 1, rrstr))) { - lock_rw_unlock(&r->respip_set->lock); - free(rrstr); + if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af)) { + verbose(VERB_ALGO, "RPZ: unable to parse response ip"); return 0; } - lock_rw_wrlock(&node->lock); - lock_rw_unlock(&r->respip_set->lock); - node->action = respa; - - if(a == RPZ_LOCAL_DATA_ACTION) { - respip_enter_rr(r->respip_set->region, node, rrtype, - rrclass, ttl, rdata, rdata_len, rrstr, ""); - } - lock_rw_unlock(&node->lock); - free(rrstr); - return 1; + return rpz_insert_ipaddr_based_trigger(r->respip_set, &addr, addrlen, net, + a, rrtype, rrclass, ttl, rdata, rdata_len, rr, rr_len); } int @@ -643,14 +700,17 @@ rpz_insert_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, uint8_t* dname, rpz_insert_qname_trigger(r, policydname, policydnamelen, a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr, rr_len); - } - else if(t == RPZ_RESPONSE_IP_TRIGGER) { + } else if(t == RPZ_RESPONSE_IP_TRIGGER) { rpz_insert_response_ip_trigger(r, policydname, policydnamelen, a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr, rr_len); free(policydname); - } - else { + } else if(t == RPZ_CLIENT_IP_TRIGGER) { + rpz_insert_client_ip_trigger(r, policydname, policydnamelen, + a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr, + rr_len); + free(policydname); + } else { free(policydname); verbose(VERB_ALGO, "RPZ: skipping unsupported trigger: %s", rpz_trigger_to_string(t)); @@ -954,6 +1014,124 @@ log_rpz_apply(uint8_t* dname, enum rpz_action a, struct query_info* qinfo, log_nametypeclass(0, txt, qinfo->qname, qinfo->qtype, qinfo->qclass); } +static enum rpz_action +rpz_apply_client_ip_trigger(struct rpz* r, struct comm_reply* repinfo) +{ + struct resp_addr* raddr = NULL; + enum rpz_action action = RPZ_INVALID_ACTION; + struct sockaddr_storage* addr = &repinfo->addr; + socklen_t addrlen = repinfo->addrlen; + + lock_rw_rdlock(&r->client_set->lock); + + raddr = (struct resp_addr*)addr_tree_lookup(&r->client_set->ip_tree, + addr, addrlen); + if(raddr != NULL) { + action = respip_action_to_rpz_action(raddr->action); + lock_rw_unlock(&raddr->lock); + } + + verbose(VERB_ALGO, "RPZ: apply client ip trigger: found=%d action=%s", + raddr != NULL, rpz_action_to_string(action)); + + lock_rw_unlock(&r->client_set->lock); + + return action; +} + +static inline +enum rpz_action +rpz_resolve_client_action_and_zone(struct auth_zones* az, struct query_info* qinfo, + struct comm_reply* repinfo, uint8_t* taglist, size_t taglen, + struct ub_server_stats* stats, + /* output parameters */ + struct local_zone** z_out, struct auth_zone** a_out, struct rpz** r_out ) +{ + struct auth_zone* a = NULL; + struct rpz* r = NULL; + struct local_zone* z = NULL; + enum rpz_action action = RPZ_PASSTHRU_ACTION; + + lock_rw_rdlock(&az->rpz_lock); + + for(a = az->rpz_first; a; a = a->rpz_az_next) { + lock_rw_rdlock(&a->lock); + r = a->rpz; + if(r->taglist && !taglist_intersect(r->taglist, + r->taglistlen, taglist, taglen)) { + lock_rw_unlock(&a->lock); + continue; + } + z = rpz_find_zone(r, qinfo->qname, qinfo->qname_len, + qinfo->qclass, 0, 0, 0); + action = rpz_apply_client_ip_trigger(r, repinfo); + if(z && r->action_override == RPZ_DISABLED_ACTION) { + if(r->log) + log_rpz_apply(z->name, + r->action_override, + qinfo, repinfo, r->log_name); + /* TODO only register stats when stats_extended? */ + stats->rpz_action[r->action_override]++; + lock_rw_unlock(&z->lock); + z = NULL; + } + if(z) { + break; + } + /* not found in this auth_zone */ + lock_rw_unlock(&a->lock); + } + + lock_rw_unlock(&az->rpz_lock); + + *r_out = r; + *a_out = a; + *z_out = z; + + return action; +} + +static inline int +rpz_resolve_final_localzone_action(struct rpz* r, struct local_zone* z, enum rpz_action client_action) +{ + enum localzone_type lzt; + if(r->action_override == RPZ_NO_OVERRIDE_ACTION) { + switch (client_action) { + case RPZ_NODATA_ACTION: + case RPZ_NXDOMAIN_ACTION: + case RPZ_DROP_ACTION: + case RPZ_TCP_ONLY_ACTION: + case RPZ_PASSTHRU_ACTION: + lzt = rpz_action_to_localzone_type(client_action); + break; + case RPZ_LOCAL_DATA_ACTION: + verbose(VERB_ALGO, + "RPZ: client ip trigger with local-data unimplemented:" + " defaulting to rpz-passthru"); + lzt = rpz_action_to_localzone_type(RPZ_PASSTHRU_ACTION); + break; + case RPZ_INVALID_ACTION: + lzt = z->type; + break; + default: + lzt = z->type; + break; + } + } else { + lzt = rpz_action_to_localzone_type(r->action_override); + } + return lzt; +} + +static inline int +rpz_is_udp_query(struct comm_reply* repinfo) { + return repinfo != NULL + ? (repinfo->c != NULL + ? repinfo->c->type == comm_udp + : 0) + : 0; +} + int rpz_apply_qname_trigger(struct auth_zones* az, struct module_env* env, struct query_info* qinfo, struct edns_data* edns, sldns_buffer* buf, @@ -961,45 +1139,41 @@ rpz_apply_qname_trigger(struct auth_zones* az, struct module_env* env, uint8_t* taglist, size_t taglen, struct ub_server_stats* stats) { struct rpz* r = NULL; - struct auth_zone* a; - int ret; - enum localzone_type lzt; + struct auth_zone* a = NULL; struct local_zone* z = NULL; struct local_data* ld = NULL; - lock_rw_rdlock(&az->rpz_lock); - for(a = az->rpz_first; a; a = a->rpz_az_next) { - lock_rw_rdlock(&a->lock); - r = a->rpz; - if(!r->taglist || taglist_intersect(r->taglist, - r->taglistlen, taglist, taglen)) { - z = rpz_find_zone(r, qinfo->qname, qinfo->qname_len, - qinfo->qclass, 0, 0, 0); - if(z && r->action_override == RPZ_DISABLED_ACTION) { - if(r->log) - log_rpz_apply(z->name, - r->action_override, - qinfo, repinfo, r->log_name); - /* TODO only register stats when stats_extended? - * */ - stats->rpz_action[r->action_override]++; - lock_rw_unlock(&z->lock); - z = NULL; - } - if(z) - break; - } - lock_rw_unlock(&a->lock); /* not found in this auth_zone */ - } - lock_rw_unlock(&az->rpz_lock); + int ret; + enum localzone_type lzt; + enum rpz_action client_action; - if(!z) - return 0; /* not holding auth_zone.lock anymore */ + client_action = rpz_resolve_client_action_and_zone( + az, qinfo, repinfo, taglist, taglen, stats, &z, &a, &r); + + verbose(VERB_ALGO, "RPZ: qname trigger: client action=%s", + rpz_action_to_string(client_action)); + + if(!z) { + verbose(VERB_ALGO, "RPZ: client action without zone"); + if(client_action == RPZ_PASSTHRU_ACTION + || client_action == RPZ_INVALID_ACTION + || (client_action == RPZ_TCP_ONLY_ACTION + && !rpz_is_udp_query(repinfo))) { + return 0; + } + // XXX: log_rpz_apply not possbile because no zone + stats->rpz_action[client_action]++; + local_zones_zone_answer(NULL /*no zone*/, env, qinfo, edns, + repinfo, buf, temp, 0 /* no local data used */, + rpz_action_to_localzone_type(client_action)); + return 1; + } log_assert(r); - if(r->action_override == RPZ_NO_OVERRIDE_ACTION) - lzt = z->type; - else - lzt = rpz_action_to_localzone_type(r->action_override); + + lzt = rpz_resolve_final_localzone_action(r, z, client_action); + + verbose(VERB_ALGO, "RPZ: final client action=%s", + rpz_action_to_string(localzone_type_to_rpz_action(lzt))); if(r->action_override == RPZ_CNAME_OVERRIDE_ACTION) { qinfo->local_alias = @@ -1040,7 +1214,7 @@ rpz_apply_qname_trigger(struct auth_zones* az, struct module_env* env, lock_rw_unlock(&a->lock); return !qinfo->local_alias; } -verbose(VERB_ALGO, "xxxxxx repinfo=%p is_udp=%d", repinfo, repinfo->c->type == comm_udp); + ret = local_zones_zone_answer(z, env, qinfo, edns, repinfo, buf, temp, 0 /* no local data used */, lzt); if(r->log) diff --git a/services/rpz.h b/services/rpz.h index 77a2db55c..1210f2039 100644 --- a/services/rpz.h +++ b/services/rpz.h @@ -92,6 +92,7 @@ enum rpz_action { struct rpz { struct local_zones* local_zones; struct respip_set* respip_set; + struct respip_set* client_set; uint8_t* taglist; size_t taglistlen; enum rpz_action action_override; diff --git a/testdata/rpz_clientip.rpl b/testdata/rpz_clientip.rpl new file mode 100644 index 000000000..bd559871b --- /dev/null +++ b/testdata/rpz_clientip.rpl @@ -0,0 +1,180 @@ +; config options +server: + module-config: "respip validator iterator" + target-fetch-policy: "0 0 0 0 0" + qname-minimisation: no + access-control: 192.0.0.0/8 allow + +rpz: + name: "rpz.example.com." + zonefile: +TEMPFILE_NAME rpz.example.com +TEMPFILE_CONTENTS rpz.example.com +$ORIGIN example.com. +rpz 3600 IN SOA ns1.rpz.example.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. +24.0.0.0.192.rpz-client-ip CNAME . +24.0.1.0.192.rpz-client-ip CNAME *. +24.0.2.0.192.rpz-client-ip CNAME rpz-drop. +24.0.3.0.192.rpz-client-ip CNAME rpz-passthru. +24.0.4.0.192.rpz-client-ip CNAME rpz-tcp-only. +TEMPFILE_END + +stub-zone: + name: "a." + stub-addr: 10.20.30.40 +CONFIG_END + +SCENARIO_BEGIN Test RPZ client ip triggers + +RANGE_BEGIN 0 100 + ADDRESS 10.20.30.40 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a. IN NS +SECTION ANSWER +a. IN NS ns.a. +SECTION ADDITIONAL +ns.a IN A 10.20.30.40 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.a. IN TXT +SECTION ANSWER +a.a. IN TXT "upstream txt rr a.a." +ENTRY_END + +RANGE_END + +; unrelated client ip address -- passthru + +STEP 10 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +a.a. IN TXT +ENTRY_END + +STEP 11 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +a.a. IN TXT +SECTION ANSWER +a.a. IN TXT "upstream txt rr a.a." +ENTRY_END + +; should be NXDOMAIN + +STEP 20 QUERY ADDRESS 192.0.0.1 +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +a.a. IN TXT +ENTRY_END + +STEP 21 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR AA RD RA NXDOMAIN +SECTION QUESTION +a.a. IN TXT +SECTION ANSWER +ENTRY_END + +; should be NODATA + +STEP 30 QUERY ADDRESS 192.0.1.1 +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +a.a. IN TXT +ENTRY_END + +STEP 31 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR AA RD RA NOERROR +SECTION QUESTION +a.a. IN TXT +SECTION ANSWER +ENTRY_END + +; should be PASSTHRU + +STEP 40 QUERY ADDRESS 192.0.3.1 +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +a.a. IN TXT +ENTRY_END + +STEP 41 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +a.a. IN TXT +SECTION ANSWER +a.a. IN TXT "upstream txt rr a.a." +ENTRY_END + +; should be TRUNCATED + +STEP 50 QUERY ADDRESS 192.0.4.1 +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +a.a. IN TXT +ENTRY_END + +STEP 51 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR AA TC RD RA NOERROR +SECTION QUESTION +a.a. IN TXT +SECTION ANSWER +ENTRY_END + +; should not be TRUNCATED via TCP + +STEP 52 QUERY ADDRESS 192.0.4.1 +ENTRY_BEGIN +MATCH TCP +REPLY RD +SECTION QUESTION +a.a. IN TXT +ENTRY_END + +STEP 53 CHECK_ANSWER +ENTRY_BEGIN +MATCH all TCP +REPLY QR RD RA NOERROR +SECTION QUESTION +a.a. IN TXT +SECTION ANSWER +a.a. IN TXT "upstream txt rr a.a." +ENTRY_END + +; should be DROPPED + +STEP 90 QUERY ADDRESS 192.0.2.1 +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +a.a. IN TXT +ENTRY_END + +SCENARIO_END From 80205ba133fa0dd8a81478baccddb41a9567db8b Mon Sep 17 00:00:00 2001 From: mb Date: Fri, 6 Nov 2020 12:54:32 +0100 Subject: [PATCH 06/68] RPZ: towards client ip trigger and local data action --- respip/respip.c | 10 ++-- respip/respip.h | 3 + services/rpz.c | 121 ++++++++++++++++++++++++++++++-------- testdata/rpz_clientip.rpl | 20 +++++++ 4 files changed, 123 insertions(+), 31 deletions(-) diff --git a/respip/respip.c b/respip/respip.c index 82ca37d50..75400bb4d 100644 --- a/respip/respip.c +++ b/respip/respip.c @@ -483,8 +483,8 @@ respip_views_apply_cfg(struct views* vs, struct config_file* cfg, * This function returns the copied rrset key on success, and NULL on memory * allocation failure. */ -static struct ub_packed_rrset_key* -copy_rrset(const struct ub_packed_rrset_key* key, struct regional* region) +struct ub_packed_rrset_key* +respip_copy_rrset(const struct ub_packed_rrset_key* key, struct regional* region) { struct ub_packed_rrset_key* ck = regional_alloc(region, sizeof(struct ub_packed_rrset_key)); @@ -730,7 +730,7 @@ respip_data_answer(enum respip_action action, "response-ip redirect with tag data [%d] %s", tag, (tagrk.dname = rep->rrsets[rrset_id]->rk.dname; @@ -1208,7 +1208,7 @@ respip_merge_cname(struct reply_info* base_rep, if(!new_rep) return 0; for(i=0,j=base_rep->an_numrrsets; ian_numrrsets; i++,j++) { - new_rep->rrsets[j] = copy_rrset(tgt_rep->rrsets[i], region); + new_rep->rrsets[j] = respip_copy_rrset(tgt_rep->rrsets[i], region); if(!new_rep->rrsets[j]) return 0; } diff --git a/respip/respip.h b/respip/respip.h index bbd471421..3dfb4e9f0 100644 --- a/respip/respip.h +++ b/respip/respip.h @@ -294,4 +294,7 @@ respip_enter_rr(struct regional* region, struct resp_addr* raddr, */ void respip_sockaddr_delete(struct respip_set* set, struct resp_addr* node); + +struct ub_packed_rrset_key* +respip_copy_rrset(const struct ub_packed_rrset_key* key, struct regional* region); #endif /* RESPIP_RESPIP_H */ diff --git a/services/rpz.c b/services/rpz.c index f8fa803de..4bf845547 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -50,6 +50,7 @@ #include "util/data/dname.h" #include "util/locks.h" #include "util/regional.h" +#include "util/data/msgencode.h" typedef struct resp_addr rpz_aclnode_type; @@ -1096,27 +1097,7 @@ rpz_resolve_final_localzone_action(struct rpz* r, struct local_zone* z, enum rpz { enum localzone_type lzt; if(r->action_override == RPZ_NO_OVERRIDE_ACTION) { - switch (client_action) { - case RPZ_NODATA_ACTION: - case RPZ_NXDOMAIN_ACTION: - case RPZ_DROP_ACTION: - case RPZ_TCP_ONLY_ACTION: - case RPZ_PASSTHRU_ACTION: - lzt = rpz_action_to_localzone_type(client_action); - break; - case RPZ_LOCAL_DATA_ACTION: - verbose(VERB_ALGO, - "RPZ: client ip trigger with local-data unimplemented:" - " defaulting to rpz-passthru"); - lzt = rpz_action_to_localzone_type(RPZ_PASSTHRU_ACTION); - break; - case RPZ_INVALID_ACTION: - lzt = z->type; - break; - default: - lzt = z->type; - break; - } + lzt = z->type; } else { lzt = rpz_action_to_localzone_type(r->action_override); } @@ -1132,6 +1113,88 @@ rpz_is_udp_query(struct comm_reply* repinfo) { : 0; } +/** encode answer consisting of 1 rrset */ +static int +rpz_local_encode(struct query_info* qinfo, struct module_env* env, + struct edns_data* edns, struct comm_reply* repinfo, sldns_buffer* buf, + struct regional* temp, struct ub_packed_rrset_key* rrset, int ansec, + int rcode) +{ + struct reply_info rep; + uint16_t udpsize; + /* make answer with time=0 for fixed TTL values */ + memset(&rep, 0, sizeof(rep)); + rep.flags = (uint16_t)((BIT_QR | BIT_AA | BIT_RA) | rcode); + rep.qdcount = 1; + if(ansec) + rep.an_numrrsets = 1; + else rep.ns_numrrsets = 1; + rep.rrset_count = 1; + rep.rrsets = &rrset; + + udpsize = edns->udp_size; + edns->edns_version = EDNS_ADVERTISED_VERSION; + edns->udp_size = EDNS_ADVERTISED_SIZE; + edns->ext_rcode = 0; + edns->bits &= EDNS_DO; + //!inplace_cb_reply_local_call(env, qinfo, NULL, &rep, rcode, edns,repinfo, temp) || + if(!inplace_cb_reply_local_call(env, qinfo, NULL, &rep, rcode, edns,repinfo, temp) ||!reply_info_answer_encode(qinfo, &rep, + *(uint16_t*)sldns_buffer_begin(buf), sldns_buffer_read_u16_at(buf, 2), + buf, 0, 0, temp, udpsize, edns, (int)(edns->bits&EDNS_DO), 0)) { + error_encode(buf, (LDNS_RCODE_SERVFAIL|BIT_AA), qinfo, + *(uint16_t*)sldns_buffer_begin(buf), + sldns_buffer_read_u16_at(buf, 2), edns); + } + return 1; +} + +static void +rpz_apply_clientip_localdata_action(struct rpz* r, struct module_env* env, + struct query_info* qinfo, struct edns_data* edns, struct comm_reply* repinfo, + sldns_buffer* buf, struct regional* temp) +{ + struct resp_addr* raddr = NULL; + enum rpz_action action = RPZ_INVALID_ACTION; + struct sockaddr_storage* addr = &repinfo->addr; + socklen_t addrlen = repinfo->addrlen; + struct ub_packed_rrset_key* rp = NULL; + int rcode = LDNS_RCODE_NOERROR|BIT_AA; + + verbose(VERB_ALGO, "RPZ: apply client ip trigger: found=%d action=%s", + raddr != NULL, rpz_action_to_string(action)); + + lock_rw_rdlock(&r->client_set->lock); + + raddr = (struct resp_addr*)addr_tree_lookup(&r->client_set->ip_tree, + addr, addrlen); + if(raddr == NULL) { + lock_rw_unlock(&r->client_set->lock); + return; + } + + /* prepare synthesized answer for the matching client */ + + action = respip_action_to_rpz_action(raddr->action); + if(action != RPZ_LOCAL_DATA_ACTION && raddr->data == NULL ) { + verbose(VERB_ALGO, "RPZ: bug: local-data action and no local data"); + goto done; + } + rp = respip_copy_rrset(raddr->data, temp); + if(!rp) { + verbose(VERB_ALGO, "RPZ: local-data action: out of memory"); + goto done; + } + rp->rk.flags |= PACKED_RRSET_FIXEDTTL; + rp->rk.dname = qinfo->qname; + rp->rk.dname_len = qinfo->qname_len; + rpz_local_encode(qinfo, env, edns, repinfo, buf, temp, rp, 1, rcode); + +done: + lock_rw_unlock(&raddr->lock); + lock_rw_unlock(&r->client_set->lock); + +} + int rpz_apply_qname_trigger(struct auth_zones* az, struct module_env* env, struct query_info* qinfo, struct edns_data* edns, sldns_buffer* buf, @@ -1152,7 +1215,8 @@ rpz_apply_qname_trigger(struct auth_zones* az, struct module_env* env, verbose(VERB_ALGO, "RPZ: qname trigger: client action=%s", rpz_action_to_string(client_action)); - if(!z) { + if(z == NULL || (client_action != RPZ_INVALID_ACTION && + client_action != RPZ_PASSTHRU_ACTION)) { verbose(VERB_ALGO, "RPZ: client action without zone"); if(client_action == RPZ_PASSTHRU_ACTION || client_action == RPZ_INVALID_ACTION @@ -1160,11 +1224,16 @@ rpz_apply_qname_trigger(struct auth_zones* az, struct module_env* env, && !rpz_is_udp_query(repinfo))) { return 0; } - // XXX: log_rpz_apply not possbile because no zone stats->rpz_action[client_action]++; - local_zones_zone_answer(NULL /*no zone*/, env, qinfo, edns, - repinfo, buf, temp, 0 /* no local data used */, - rpz_action_to_localzone_type(client_action)); + if(client_action == RPZ_LOCAL_DATA_ACTION) { + rpz_apply_clientip_localdata_action(r, env, qinfo, edns, + repinfo, buf, temp); + } else { + // XXX: log_rpz_apply not possbile because no zone + local_zones_zone_answer(NULL /*no zone*/, env, qinfo, edns, + repinfo, buf, temp, 0 /* no local data used */, + rpz_action_to_localzone_type(client_action)); + } return 1; } diff --git a/testdata/rpz_clientip.rpl b/testdata/rpz_clientip.rpl index bd559871b..24256cbf3 100644 --- a/testdata/rpz_clientip.rpl +++ b/testdata/rpz_clientip.rpl @@ -21,6 +21,7 @@ $ORIGIN rpz.example.com. 24.0.2.0.192.rpz-client-ip CNAME rpz-drop. 24.0.3.0.192.rpz-client-ip CNAME rpz-passthru. 24.0.4.0.192.rpz-client-ip CNAME rpz-tcp-only. +24.0.5.0.192.rpz-client-ip A 127.0.0.1 TEMPFILE_END stub-zone: @@ -168,6 +169,25 @@ SECTION ANSWER a.a. IN TXT "upstream txt rr a.a." ENTRY_END +; should be synthesized + +STEP 60 QUERY ADDRESS 192.0.5.1 +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +a.a. IN TXT +ENTRY_END + +STEP 61 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR AA RD RA NOERROR +SECTION QUESTION +a.a. IN A +SECTION ANSWER +a.a. IN A 127.0.0.1 +ENTRY_END + ; should be DROPPED STEP 90 QUERY ADDRESS 192.0.2.1 From 731afdc9248f60fdeca25cc2a7d6aad4cbe6b94d Mon Sep 17 00:00:00 2001 From: mb Date: Fri, 6 Nov 2020 13:42:48 +0100 Subject: [PATCH 07/68] RPZ: refactor clientip trigger --- services/rpz.c | 98 +++++++++++++++++++++++++++----------------------- 1 file changed, 54 insertions(+), 44 deletions(-) diff --git a/services/rpz.c b/services/rpz.c index 4bf845547..b823f6d76 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -1043,10 +1043,10 @@ rpz_apply_client_ip_trigger(struct rpz* r, struct comm_reply* repinfo) static inline enum rpz_action rpz_resolve_client_action_and_zone(struct auth_zones* az, struct query_info* qinfo, - struct comm_reply* repinfo, uint8_t* taglist, size_t taglen, - struct ub_server_stats* stats, - /* output parameters */ - struct local_zone** z_out, struct auth_zone** a_out, struct rpz** r_out ) + struct comm_reply* repinfo, uint8_t* taglist, size_t taglen, + struct ub_server_stats* stats, + /* output parameters */ + struct local_zone** z_out, struct auth_zone** a_out, struct rpz** r_out ) { struct auth_zone* a = NULL; struct rpz* r = NULL; @@ -1092,18 +1092,6 @@ rpz_resolve_client_action_and_zone(struct auth_zones* az, struct query_info* qin return action; } -static inline int -rpz_resolve_final_localzone_action(struct rpz* r, struct local_zone* z, enum rpz_action client_action) -{ - enum localzone_type lzt; - if(r->action_override == RPZ_NO_OVERRIDE_ACTION) { - lzt = z->type; - } else { - lzt = rpz_action_to_localzone_type(r->action_override); - } - return lzt; -} - static inline int rpz_is_udp_query(struct comm_reply* repinfo) { return repinfo != NULL @@ -1138,7 +1126,7 @@ rpz_local_encode(struct query_info* qinfo, struct module_env* env, edns->ext_rcode = 0; edns->bits &= EDNS_DO; //!inplace_cb_reply_local_call(env, qinfo, NULL, &rep, rcode, edns,repinfo, temp) || - if(!inplace_cb_reply_local_call(env, qinfo, NULL, &rep, rcode, edns,repinfo, temp) ||!reply_info_answer_encode(qinfo, &rep, + if(!reply_info_answer_encode(qinfo, &rep, *(uint16_t*)sldns_buffer_begin(buf), sldns_buffer_read_u16_at(buf, 2), buf, 0, 0, temp, udpsize, edns, (int)(edns->bits&EDNS_DO), 0)) { error_encode(buf, (LDNS_RCODE_SERVFAIL|BIT_AA), qinfo, @@ -1195,6 +1183,45 @@ done: } +static int +rpz_maybe_apply_clientip_trigger(struct auth_zones* az, struct module_env* env, + struct query_info* qinfo, struct edns_data* edns, + struct comm_reply* repinfo, uint8_t* taglist, size_t taglen, + struct ub_server_stats* stats,sldns_buffer* buf, struct regional* temp, + /* output parameters */ + struct local_zone** z_out, struct auth_zone** a_out, struct rpz** r_out) +{ + enum rpz_action client_action; + client_action = rpz_resolve_client_action_and_zone( + az, qinfo, repinfo, taglist, taglen, stats, z_out, a_out, r_out); + + verbose(VERB_ALGO, "RPZ: qname trigger: client action=%s", + rpz_action_to_string(client_action)); + + if(*z_out == NULL || (client_action != RPZ_INVALID_ACTION && + client_action != RPZ_PASSTHRU_ACTION)) { + verbose(VERB_ALGO, "RPZ: client action without zone"); + if(client_action == RPZ_PASSTHRU_ACTION + || client_action == RPZ_INVALID_ACTION + || (client_action == RPZ_TCP_ONLY_ACTION + && !rpz_is_udp_query(repinfo))) { + return 0; + } + stats->rpz_action[client_action]++; + if(client_action == RPZ_LOCAL_DATA_ACTION) { + rpz_apply_clientip_localdata_action(*r_out, env, qinfo, + edns, repinfo, buf, temp); + } else { + // XXX: log_rpz_apply not possbile because no zone + local_zones_zone_answer(NULL /*no zone*/, env, qinfo, edns, + repinfo, buf, temp, 0 /* no local data used */, + rpz_action_to_localzone_type(client_action)); + } + return 1; + } + return -1; +} + int rpz_apply_qname_trigger(struct auth_zones* az, struct module_env* env, struct query_info* qinfo, struct edns_data* edns, sldns_buffer* buf, @@ -1207,39 +1234,22 @@ rpz_apply_qname_trigger(struct auth_zones* az, struct module_env* env, struct local_data* ld = NULL; int ret; enum localzone_type lzt; - enum rpz_action client_action; - client_action = rpz_resolve_client_action_and_zone( - az, qinfo, repinfo, taglist, taglen, stats, &z, &a, &r); + int clientip_trigger = rpz_maybe_apply_clientip_trigger(az, env, qinfo, + edns, repinfo, taglist, taglen, stats, buf, temp, &z, &a, &r); + if(clientip_trigger >= 0) { return clientip_trigger; } - verbose(VERB_ALGO, "RPZ: qname trigger: client action=%s", - rpz_action_to_string(client_action)); - - if(z == NULL || (client_action != RPZ_INVALID_ACTION && - client_action != RPZ_PASSTHRU_ACTION)) { - verbose(VERB_ALGO, "RPZ: client action without zone"); - if(client_action == RPZ_PASSTHRU_ACTION - || client_action == RPZ_INVALID_ACTION - || (client_action == RPZ_TCP_ONLY_ACTION - && !rpz_is_udp_query(repinfo))) { - return 0; - } - stats->rpz_action[client_action]++; - if(client_action == RPZ_LOCAL_DATA_ACTION) { - rpz_apply_clientip_localdata_action(r, env, qinfo, edns, - repinfo, buf, temp); - } else { - // XXX: log_rpz_apply not possbile because no zone - local_zones_zone_answer(NULL /*no zone*/, env, qinfo, edns, - repinfo, buf, temp, 0 /* no local data used */, - rpz_action_to_localzone_type(client_action)); - } - return 1; + if(z == NULL) { + return 0; } log_assert(r); - lzt = rpz_resolve_final_localzone_action(r, z, client_action); + if(r->action_override == RPZ_NO_OVERRIDE_ACTION) { + lzt = z->type; + } else { + lzt = rpz_action_to_localzone_type(r->action_override); + } verbose(VERB_ALGO, "RPZ: final client action=%s", rpz_action_to_string(localzone_type_to_rpz_action(lzt))); From 9b4bbb49b431f40bd758d65f10e6a26272dec414 Mon Sep 17 00:00:00 2001 From: mb Date: Mon, 9 Nov 2020 12:08:06 +0100 Subject: [PATCH 08/68] RPZ: one more testcase --- services/rpz.c | 11 +++++++++-- testdata/rpz_clientip.rpl | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/services/rpz.c b/services/rpz.c index b823f6d76..6e9265c9f 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -574,7 +574,7 @@ rpz_insert_ipaddr_based_trigger(struct respip_set* set, struct sockaddr_storage* lock_rw_wrlock(&set->lock); rrstr = sldns_wire2str_rr(rr, rr_len); if(!rrstr) { - log_err("malloc error while inserting RPZ respip trigger"); + log_err("malloc error while inserting RPZ ipaddr based trigger"); lock_rw_unlock(&set->lock); return 0; } @@ -1110,7 +1110,7 @@ rpz_local_encode(struct query_info* qinfo, struct module_env* env, { struct reply_info rep; uint16_t udpsize; - /* make answer with time=0 for fixed TTL values */ + memset(&rep, 0, sizeof(rep)); rep.flags = (uint16_t)((BIT_QR | BIT_AA | BIT_RA) | rcode); rep.qdcount = 1; @@ -1119,6 +1119,7 @@ rpz_local_encode(struct query_info* qinfo, struct module_env* env, else rep.ns_numrrsets = 1; rep.rrset_count = 1; rep.rrsets = &rrset; + rep.ttl = ((struct packed_rrset_data*)rrset->entry.data)->rr_ttl[0]; udpsize = edns->udp_size; edns->edns_version = EDNS_ADVERTISED_VERSION; @@ -1167,11 +1168,17 @@ rpz_apply_clientip_localdata_action(struct rpz* r, struct module_env* env, verbose(VERB_ALGO, "RPZ: bug: local-data action and no local data"); goto done; } + rp = respip_copy_rrset(raddr->data, temp); if(!rp) { verbose(VERB_ALGO, "RPZ: local-data action: out of memory"); goto done; } + + //struct packed_rrset_data* pd = raddr->data->entry.data; + //struct packed_rrset_data* pd2 = rp->entry.data; + //verbose(VERB_ALGO, "ttl=%ld ttl=%ld", pd->rr_ttl[0], pd2->rr_ttl[0]); + rp->rk.flags |= PACKED_RRSET_FIXEDTTL; rp->rk.dname = qinfo->qname; rp->rk.dname_len = qinfo->qname_len; diff --git a/testdata/rpz_clientip.rpl b/testdata/rpz_clientip.rpl index 24256cbf3..f679d5575 100644 --- a/testdata/rpz_clientip.rpl +++ b/testdata/rpz_clientip.rpl @@ -188,6 +188,24 @@ SECTION ANSWER a.a. IN A 127.0.0.1 ENTRY_END +; should be synthesized NODATA + +STEP 62 QUERY ADDRESS 192.0.5.1 +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +a.a. IN TXT +ENTRY_END + +STEP 63 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR AA RD RA NOERROR +SECTION QUESTION +a.a. IN TXT +SECTION ANSWER +ENTRY_END + ; should be DROPPED STEP 90 QUERY ADDRESS 192.0.2.1 From 667863770fffcb29cd5128ccdcad21ab05b70897 Mon Sep 17 00:00:00 2001 From: mb Date: Mon, 9 Nov 2020 15:14:25 +0100 Subject: [PATCH 09/68] RPZ: refactor clientip to handle multiple rrsets --- services/rpz.c | 267 +++++++++++++++++++++++++++++++++----- services/rpz.h | 23 +++- testdata/rpz_clientip.rpl | 43 +++++- 3 files changed, 300 insertions(+), 33 deletions(-) diff --git a/services/rpz.c b/services/rpz.c index 6e9265c9f..37438da3a 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -302,13 +302,53 @@ rpz_dname_to_trigger(uint8_t* dname, size_t dname_len) return RPZ_QNAME_TRIGGER; } -void rpz_delete(struct rpz* r) +static inline struct clientip_synthesized_rrset* +rpz_clientip_synthesized_set_create(void) +{ + struct clientip_synthesized_rrset* set = calloc(1, sizeof(*set)); + if(set == NULL) { + return NULL; + } + set->region = regional_create(); + if(set->region == NULL) { + free(set); + return NULL; + } + addr_tree_init(&set->entries); + lock_rw_init(&set->lock); + return set; +} + +static void +rpz_clientip_synthesized_rr_delete(rbnode_type* n, void* ATTR_UNUSED(arg)) +{ + struct clientip_synthesized_rr* r = (struct clientip_synthesized_rr*)n->key; + lock_rw_destroy(&r->lock); +#ifdef THREADS_DISABLED + (void)r; +#endif +} + +static inline void +rpz_clientip_synthesized_set_delete(struct clientip_synthesized_rrset* set) +{ + if(set == NULL) { + return; + } + lock_rw_destroy(&set->lock); + traverse_postorder(&set->entries, rpz_clientip_synthesized_rr_delete, NULL); + regional_destroy(set->region); + free(set); +} + +void +rpz_delete(struct rpz* r) { if(!r) return; local_zones_delete(r->local_zones); respip_set_delete(r->respip_set); - respip_set_delete(r->client_set); + rpz_clientip_synthesized_set_delete(r->client_set); regional_destroy(r->region); free(r->taglist); free(r->log_name); @@ -321,14 +361,14 @@ rpz_clear(struct rpz* r) /* must hold write lock on auth_zone */ local_zones_delete(r->local_zones); respip_set_delete(r->respip_set); - respip_set_delete(r->client_set); + rpz_clientip_synthesized_set_delete(r->client_set); if(!(r->local_zones = local_zones_create())){ return 0; } if(!(r->respip_set = respip_set_create())) { return 0; } - if(!(r->client_set = respip_set_create())) { + if(!(r->client_set = rpz_clientip_synthesized_set_create())) { return 0; } return 1; @@ -342,7 +382,7 @@ rpz_finish_config(struct rpz* r) lock_rw_unlock(&r->respip_set->lock); lock_rw_wrlock(&r->client_set->lock); - addr_tree_init_parents(&r->client_set->ip_tree); + addr_tree_init_parents(&r->client_set->entries); lock_rw_unlock(&r->client_set->lock); } @@ -411,8 +451,8 @@ rpz_create(struct config_auth* p) goto err; } - /* (ab)use respip for client acl */ - if(!(r->client_set = respip_set_create())) { + r->client_set = rpz_clientip_synthesized_set_create(); + if(r->client_set == NULL) { goto err; } @@ -458,8 +498,8 @@ err: local_zones_delete(r->local_zones); if(r->respip_set) respip_set_delete(r->respip_set); - if(r->client_set) - respip_set_delete(r->client_set); + if(r->client_set != NULL) + rpz_clientip_synthesized_set_delete(r->client_set); if(r->taglist) free(r->taglist); if(r->region) @@ -598,8 +638,149 @@ rpz_insert_ipaddr_based_trigger(struct respip_set* set, struct sockaddr_storage* return 1; } +static inline struct clientip_synthesized_rr* +rpz_clientip_ensure_entry(struct clientip_synthesized_rrset* set, + struct sockaddr_storage* addr, socklen_t addrlen, int net) +{ + int insert_ok; + struct clientip_synthesized_rr* node = + (struct clientip_synthesized_rr*)addr_tree_find(&set->entries, + addr, addrlen, net); + + if(node != NULL) { return node; } + + /* node does not yet exist => allocate one */ + node = regional_alloc_zero(set->region, sizeof(*node)); + if(node == NULL) { + log_err("out of memory"); + return NULL; + } + + lock_rw_init(&node->lock); + node->action = RPZ_INVALID_ACTION; + insert_ok = addr_tree_insert(&set->entries, &node->node, + addr, addrlen, net); + if (!insert_ok) { + log_warn("RPZ: unexpected: unable to insert clientip address node"); + /* we can not free the just allocated node. + * theoretically a memleak */ + return NULL; + } + + return node; +} + +static void +rpz_report_rrset_error(const char* msg, uint8_t* rr, size_t rr_len) { + char* rrstr = sldns_wire2str_rr(rr, rr_len); + if(rrstr == NULL) { + log_err("malloc error while inserting RPZ clientip based record"); + return; + } + log_err("RPZ: unexpected: unable to insert %s: %s", msg, rrstr); + free(rrstr); +} + +/* from localzone.c; difference is we don't have a dname */ +struct local_rrset* +rpz_clientip_new_rrset(struct regional* region, + struct clientip_synthesized_rr* raddr, uint16_t rrtype, uint16_t rrclass) +{ + struct packed_rrset_data* pd; + struct local_rrset* rrset = (struct local_rrset*) + regional_alloc_zero(region, sizeof(*rrset)); + if(rrset == NULL) { + log_err("out of memory"); + return NULL; + } + rrset->next = raddr->data; + raddr->data = rrset; + rrset->rrset = (struct ub_packed_rrset_key*) + regional_alloc_zero(region, sizeof(*rrset->rrset)); + if(rrset->rrset == NULL) { + log_err("out of memory"); + return NULL; + } + rrset->rrset->entry.key = rrset->rrset; + pd = (struct packed_rrset_data*)regional_alloc_zero(region, sizeof(*pd)); + if(pd == NULL) { + log_err("out of memory"); + return NULL; + } + pd->trust = rrset_trust_prim_noglue; + pd->security = sec_status_insecure; + rrset->rrset->entry.data = pd; + rrset->rrset->rk.type = htons(rrtype); + rrset->rrset->rk.rrset_class = htons(rrclass); + rrset->rrset->rk.dname = regional_alloc_zero(region, 1); + if(rrset->rrset->rk.dname == NULL) { + log_err("out of memory"); + return NULL; + } + rrset->rrset->rk.dname_len = 1; + return rrset; +} + static int -rpz_insert_client_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, +rpz_clientip_enter_rr(struct regional* region, struct clientip_synthesized_rr* raddr, + uint16_t rrtype, uint16_t rrclass, time_t ttl, uint8_t* rdata, + size_t rdata_len) +{ + struct local_rrset* rrset; + struct sockaddr* sa; + sa = (struct sockaddr*)&raddr->node.addr; + if (rrtype == LDNS_RR_TYPE_CNAME && raddr->data != NULL) { + log_err("CNAME response-ip data can not co-exist with other " + "client-ip data"); + return 0; + } + + rrset = rpz_clientip_new_rrset(region, raddr, rrtype, rrclass); + if(raddr->data == NULL) { + return 0; + } + + return rrset_insert_rr(region, rrset->rrset->entry.data, rdata, rdata_len, ttl, "fixme"); +} + +static int +rpz_clientip_insert_trigger_rr(struct clientip_synthesized_rrset* set, struct sockaddr_storage* addr, + socklen_t addrlen, int net, enum rpz_action a, uint16_t rrtype, + uint16_t rrclass, uint32_t ttl, uint8_t* rdata, size_t rdata_len, + uint8_t* rr, size_t rr_len) +{ + struct clientip_synthesized_rr* node; + + lock_rw_wrlock(&set->lock); + + node = rpz_clientip_ensure_entry(set, addr, addrlen, net); + if(node == NULL) { + lock_rw_unlock(&set->lock); + rpz_report_rrset_error("client ip address", rr, rr_len); + return 0; + } + + lock_rw_wrlock(&node->lock); + lock_rw_unlock(&set->lock); + + node->action = a; + if(a == RPZ_LOCAL_DATA_ACTION) { + if(!rpz_clientip_enter_rr(set->region, node, rrtype, + rrclass, ttl, rdata, rdata_len)) { + verbose(VERB_ALGO, "RPZ: unable to insert clientip rr"); + lock_rw_unlock(&node->lock); + return 0; + } + + } + + lock_rw_unlock(&node->lock); + + return 1; +} + +static int +rpz_insert_clientip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl, uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len) { @@ -608,7 +789,8 @@ rpz_insert_client_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, int net, af; enum respip_action respa = rpz_action_to_respip_action(a); - verbose(VERB_ALGO, "RPZ: insert client ip trigger: %s", rpz_action_to_string(a)); + verbose(VERB_ALGO, "RPZ: insert clientip trigger: %s", rpz_action_to_string(a)); + if(a == RPZ_INVALID_ACTION || respa == respip_invalid) { verbose(VERB_ALGO, "RPZ: skipping unsupported action: %s", rpz_action_to_string(a)); @@ -620,10 +802,12 @@ rpz_insert_client_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, return 0; } - return rpz_insert_ipaddr_based_trigger(r->client_set, &addr, addrlen, net, + return rpz_clientip_insert_trigger_rr(r->client_set, &addr, addrlen, net, a, rrtype, rrclass, ttl, rdata, rdata_len, rr, rr_len); } + + /** Insert RR into RPZ's respip_set */ static int rpz_insert_response_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, @@ -707,7 +891,7 @@ rpz_insert_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, uint8_t* dname, rr_len); free(policydname); } else if(t == RPZ_CLIENT_IP_TRIGGER) { - rpz_insert_client_ip_trigger(r, policydname, policydnamelen, + rpz_insert_clientip_trigger(r, policydname, policydnamelen, a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr, rr_len); free(policydname); @@ -1018,17 +1202,17 @@ log_rpz_apply(uint8_t* dname, enum rpz_action a, struct query_info* qinfo, static enum rpz_action rpz_apply_client_ip_trigger(struct rpz* r, struct comm_reply* repinfo) { - struct resp_addr* raddr = NULL; + struct clientip_synthesized_rr* raddr = NULL; enum rpz_action action = RPZ_INVALID_ACTION; struct sockaddr_storage* addr = &repinfo->addr; socklen_t addrlen = repinfo->addrlen; lock_rw_rdlock(&r->client_set->lock); - raddr = (struct resp_addr*)addr_tree_lookup(&r->client_set->ip_tree, + raddr = (struct clientip_synthesized_rr*)addr_tree_lookup(&r->client_set->entries, addr, addrlen); if(raddr != NULL) { - action = respip_action_to_rpz_action(raddr->action); + action = raddr->action; lock_rw_unlock(&raddr->lock); } @@ -1114,12 +1298,12 @@ rpz_local_encode(struct query_info* qinfo, struct module_env* env, memset(&rep, 0, sizeof(rep)); rep.flags = (uint16_t)((BIT_QR | BIT_AA | BIT_RA) | rcode); rep.qdcount = 1; - if(ansec) + rep.rrset_count = ansec; + if(ansec > 0) { rep.an_numrrsets = 1; - else rep.ns_numrrsets = 1; - rep.rrset_count = 1; - rep.rrsets = &rrset; - rep.ttl = ((struct packed_rrset_data*)rrset->entry.data)->rr_ttl[0]; + rep.rrsets = &rrset; + rep.ttl = ((struct packed_rrset_data*)rrset->entry.data)->rr_ttl[0]; + } udpsize = edns->udp_size; edns->edns_version = EDNS_ADVERTISED_VERSION; @@ -1137,39 +1321,63 @@ rpz_local_encode(struct query_info* qinfo, struct module_env* env, return 1; } +static struct local_rrset* +rpz_clientip_find_rrset(struct query_info* qinfo, 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(qinfo->qtype) == packed_rrset->type) { + return cursor; + } + cursor = cursor->next; + } + return NULL; +} + static void rpz_apply_clientip_localdata_action(struct rpz* r, struct module_env* env, struct query_info* qinfo, struct edns_data* edns, struct comm_reply* repinfo, sldns_buffer* buf, struct regional* temp) { - struct resp_addr* raddr = NULL; + struct clientip_synthesized_rr* raddr = NULL; + struct local_rrset* rrset; enum rpz_action action = RPZ_INVALID_ACTION; struct sockaddr_storage* addr = &repinfo->addr; socklen_t addrlen = repinfo->addrlen; struct ub_packed_rrset_key* rp = NULL; int rcode = LDNS_RCODE_NOERROR|BIT_AA; + int rrset_count = 1; verbose(VERB_ALGO, "RPZ: apply client ip trigger: found=%d action=%s", raddr != NULL, rpz_action_to_string(action)); lock_rw_rdlock(&r->client_set->lock); - raddr = (struct resp_addr*)addr_tree_lookup(&r->client_set->ip_tree, - addr, addrlen); + raddr = (struct clientip_synthesized_rr*)addr_tree_lookup( + &r->client_set->entries, addr, addrlen); if(raddr == NULL) { lock_rw_unlock(&r->client_set->lock); return; } - /* prepare synthesized answer for the matching client */ + /* prepare synthesized answer for client */ - action = respip_action_to_rpz_action(raddr->action); + action = raddr->action; if(action != RPZ_LOCAL_DATA_ACTION && raddr->data == NULL ) { verbose(VERB_ALGO, "RPZ: bug: local-data action and no local data"); goto done; } - rp = respip_copy_rrset(raddr->data, temp); + /* check query type / rr type */ + + rrset = rpz_clientip_find_rrset(qinfo, raddr); + if(rrset == NULL) { + verbose(VERB_ALGO, "RPZ: unable to find local-data for query"); + rrset_count = 0; + goto nodata; + } + + rp = respip_copy_rrset(rrset->rrset, temp); if(!rp) { verbose(VERB_ALGO, "RPZ: local-data action: out of memory"); goto done; @@ -1178,16 +1386,15 @@ rpz_apply_clientip_localdata_action(struct rpz* r, struct module_env* env, //struct packed_rrset_data* pd = raddr->data->entry.data; //struct packed_rrset_data* pd2 = rp->entry.data; //verbose(VERB_ALGO, "ttl=%ld ttl=%ld", pd->rr_ttl[0], pd2->rr_ttl[0]); - rp->rk.flags |= PACKED_RRSET_FIXEDTTL; rp->rk.dname = qinfo->qname; rp->rk.dname_len = qinfo->qname_len; - rpz_local_encode(qinfo, env, edns, repinfo, buf, temp, rp, 1, rcode); +nodata: + rpz_local_encode(qinfo, env, edns, repinfo, buf, temp, rp, rrset_count, rcode); done: lock_rw_unlock(&raddr->lock); lock_rw_unlock(&r->client_set->lock); - } static int diff --git a/services/rpz.h b/services/rpz.h index 1210f2039..19dd867b6 100644 --- a/services/rpz.h +++ b/services/rpz.h @@ -83,6 +83,27 @@ enum rpz_action { RPZ_CNAME_OVERRIDE_ACTION, /* RPZ CNAME action override*/ }; +struct clientip_synthesized_rrset{ + struct regional* region; + struct rbtree_type entries; + lock_rw_type lock; /* lock on the respip tree */ +}; + +struct clientip_synthesized_rr { + /** node in address tree */ + struct addr_tree_node node; + /** lock on the node item */ + lock_rw_type lock; + /** tag bitlist */ + uint8_t* taglist; + /** length of the taglist (in bytes) */ + size_t taglen; + /** action for this address span */ + enum rpz_action action; + /** "local data" for this node */ + struct local_rrset* data; +}; + /** * RPZ containing policies. Pointed to from corresponding auth-zone. Part of a * linked list to keep configuration order. Iterating or changing the linked @@ -92,7 +113,7 @@ enum rpz_action { struct rpz { struct local_zones* local_zones; struct respip_set* respip_set; - struct respip_set* client_set; + struct clientip_synthesized_rrset* client_set; uint8_t* taglist; size_t taglistlen; enum rpz_action action_override; diff --git a/testdata/rpz_clientip.rpl b/testdata/rpz_clientip.rpl index f679d5575..64082210c 100644 --- a/testdata/rpz_clientip.rpl +++ b/testdata/rpz_clientip.rpl @@ -22,6 +22,7 @@ $ORIGIN rpz.example.com. 24.0.3.0.192.rpz-client-ip CNAME rpz-passthru. 24.0.4.0.192.rpz-client-ip CNAME rpz-tcp-only. 24.0.5.0.192.rpz-client-ip A 127.0.0.1 +24.0.5.0.192.rpz-client-ip TXT "42" TEMPFILE_END stub-zone: @@ -55,6 +56,26 @@ SECTION ANSWER a.a. IN TXT "upstream txt rr a.a." ENTRY_END +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.a. IN A +SECTION ANSWER +a.a. IN A 10.20.30.40 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.a. IN AAAA +SECTION ANSWER +a.a. IN AAAA 2001:db8::123 +ENTRY_END + RANGE_END ; unrelated client ip address -- passthru @@ -175,7 +196,7 @@ STEP 60 QUERY ADDRESS 192.0.5.1 ENTRY_BEGIN REPLY RD SECTION QUESTION -a.a. IN TXT +a.a. IN A ENTRY_END STEP 61 CHECK_ANSWER @@ -188,7 +209,7 @@ SECTION ANSWER a.a. IN A 127.0.0.1 ENTRY_END -; should be synthesized NODATA +; should be synthesized STEP 62 QUERY ADDRESS 192.0.5.1 ENTRY_BEGIN @@ -204,6 +225,24 @@ REPLY QR AA RD RA NOERROR SECTION QUESTION a.a. IN TXT SECTION ANSWER +a.a. IN TXT "42" +ENTRY_END + +; should be synthesized NODATA + +STEP 64 QUERY ADDRESS 192.0.5.1 +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +a.a. IN AAAA +ENTRY_END + +STEP 65 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR AA RD RA NOERROR +SECTION QUESTION +a.a. IN AAAA ENTRY_END ; should be DROPPED From 9149a6d1e42ef2c8dfb1fca1b1c5b7dae4db9032 Mon Sep 17 00:00:00 2001 From: mb Date: Wed, 11 Nov 2020 11:21:54 +0100 Subject: [PATCH 10/68] RPZ: stub nsip testbound scenario --- testdata/rpz_nsip.rpl | 187 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 testdata/rpz_nsip.rpl diff --git a/testdata/rpz_nsip.rpl b/testdata/rpz_nsip.rpl new file mode 100644 index 000000000..d07199c35 --- /dev/null +++ b/testdata/rpz_nsip.rpl @@ -0,0 +1,187 @@ +; config options +server: + module-config: "respip validator iterator" + target-fetch-policy: "0 0 0 0 0" + qname-minimisation: no + access-control: 192.0.0.0/8 allow + +rpz: + 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. +24.0.0.0.192.rpz-nsip CNAME . +24.0.1.0.192.rpz-nsip CNAME *. +24.0.2.0.192.rpz-nsip CNAME rpz-drop. +24.0.3.0.192.rpz-nsip CNAME rpz-passthru. +24.0.4.0.192.rpz-nsip CNAME rpz-tcp-only. +24.0.5.0.192.rpz-nsip A 127.0.0.1 +24.0.5.0.192.rpz-nsip TXT "42" +TEMPFILE_END + +stub-zone: + name: "." + stub-addr: 1.1.1.1 +CONFIG_END + +SCENARIO_BEGIN Test RPZ nsip triggers + +; . +RANGE_BEGIN 0 100 + ADDRESS 1.1.1.1 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS ns.root. +SECTION ADDITIONAL +ns.root IN A 1.1.1.1 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +com. IN A +SECTION AUTHORITY +com. IN NS ns1.com. +SECTION ADDITIONAL +ns1.com. IN A 8.8.8.8 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +aa. IN A +SECTION AUTHORITY +aa. IN NS ns1.aa. +SECTION ADDITIONAL +ns1.aa. IN A 8.8.0.8 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +bb. IN A +SECTION AUTHORITY +bb. IN NS ns1.bb. +SECTION ADDITIONAL +ns1.bb. IN A 8.8.1.8 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +cc. IN A +SECTION AUTHORITY +cc. IN NS ns1.cc. +SECTION ADDITIONAL +ns1.cc. IN A 8.8.2.8 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +dd. IN A +SECTION AUTHORITY +dd. IN NS ns1.dd. +SECTION ADDITIONAL +ns1.dd. IN A 8.8.3.8 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +ee. IN A +SECTION AUTHORITY +ee. IN NS ns1.ee. +SECTION ADDITIONAL +ns1.ee. IN A 8.8.5.8 +ENTRY_END + +RANGE_END + +; com. +RANGE_BEGIN 0 100 + ADDRESS 8.8.8.8 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +com. IN NS +SECTION ANSWER +com. IN NS ns1.com. +SECTION ADDITIONAL +ns1.com. IN A 8.8.8.8 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +gotham.com. IN A +SECTION AUTHORITY +gotham.com. IN NS ns1.gotham.com. +SECTION ADDITIONAL +ns1.gotham.com. IN A 192.0.6.1 +ENTRY_END + +RANGE_END + +; ns1.gotham.com. +RANGE_BEGIN 0 100 + ADDRESS 192.0.6.1 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +gotham.com. IN A +SECTION ANSWER +gotham.com. IN A 192.0.6.2 +ENTRY_END + +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +gotham.com. IN A +ENTRY_END + +STEP 2 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +gotham.com. IN A +SECTION ANSWER +gotham.com. IN A 192.0.6.2 +ENTRY_END + +SCENARIO_END From e27b160acdb611c4e800a61f03cec5c585698ee5 Mon Sep 17 00:00:00 2001 From: mb Date: Fri, 13 Nov 2020 14:33:11 +0100 Subject: [PATCH 11/68] rpz: stubs for nsip triggers --- iterator/iterator.c | 5 + services/rpz.c | 259 ++++++++++++++++++++++++++++++------------ services/rpz.h | 4 + testdata/rpz_nsip.rpl | 68 ++++++++++- 4 files changed, 261 insertions(+), 75 deletions(-) diff --git a/iterator/iterator.c b/iterator/iterator.c index 99d020117..fcda1bb3b 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -2660,11 +2660,16 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, { int dnsseclame = 0; enum response_type type; + int rpz_filter_result; + iq->num_current_queries--; if(!inplace_cb_query_response_call(qstate->env, qstate, iq->response)) log_err("unable to call query_response callback"); + rpz_filter_result = rpz_iterator_module_callback(qstate, iq); + if(rpz_filter_result > 0) { next_state(iq, FINISHED_STATE); } + if(iq->response == NULL) { /* Don't increment qname when QNAME minimisation is enabled */ if(qstate->env->cfg->qname_minimisation) { diff --git a/services/rpz.c b/services/rpz.c index 37438da3a..ff994a482 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -51,6 +51,8 @@ #include "util/locks.h" #include "util/regional.h" #include "util/data/msgencode.h" +#include "services/cache/dns.h" +#include "iterator/iterator.h" typedef struct resp_addr rpz_aclnode_type; @@ -349,6 +351,7 @@ rpz_delete(struct rpz* r) local_zones_delete(r->local_zones); respip_set_delete(r->respip_set); rpz_clientip_synthesized_set_delete(r->client_set); + rpz_clientip_synthesized_set_delete(r->ns_set); regional_destroy(r->region); free(r->taglist); free(r->log_name); @@ -362,6 +365,7 @@ rpz_clear(struct rpz* r) local_zones_delete(r->local_zones); respip_set_delete(r->respip_set); rpz_clientip_synthesized_set_delete(r->client_set); + rpz_clientip_synthesized_set_delete(r->ns_set); if(!(r->local_zones = local_zones_create())){ return 0; } @@ -371,6 +375,9 @@ rpz_clear(struct rpz* r) if(!(r->client_set = rpz_clientip_synthesized_set_create())) { return 0; } + if(!(r->ns_set = rpz_clientip_synthesized_set_create())) { + return 0; + } return 1; } @@ -384,6 +391,10 @@ rpz_finish_config(struct rpz* r) lock_rw_wrlock(&r->client_set->lock); addr_tree_init_parents(&r->client_set->entries); lock_rw_unlock(&r->client_set->lock); + + lock_rw_wrlock(&r->ns_set->lock); + addr_tree_init_parents(&r->ns_set->entries); + lock_rw_unlock(&r->ns_set->lock); } /** new rrset containing CNAME override, does not yet contain a dname */ @@ -456,6 +467,11 @@ rpz_create(struct config_auth* p) goto err; } + r->ns_set = rpz_clientip_synthesized_set_create(); + if(r->ns_set == NULL) { + goto err; + } + r->taglistlen = p->rpz_taglistlen; r->taglist = memdup(p->rpz_taglist, r->taglistlen); if(p->rpz_action_override) { @@ -500,6 +516,8 @@ err: respip_set_delete(r->respip_set); if(r->client_set != NULL) rpz_clientip_synthesized_set_delete(r->client_set); + if(r->ns_set != NULL) + rpz_clientip_synthesized_set_delete(r->ns_set); if(r->taglist) free(r->taglist); if(r->region) @@ -540,10 +558,10 @@ rpz_insert_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, char* rrstr; int newzone = 0; - verbose(VERB_ALGO, "RPZ: insert qname trigger: %s", rpz_action_to_string(a)); + verbose(VERB_ALGO, "rpz: insert qname trigger: %s", rpz_action_to_string(a)); if(a == RPZ_INVALID_ACTION) { - verbose(VERB_ALGO, "RPZ: skipping unsupported action: %s", + verbose(VERB_ALGO, "rpz: skipping unsupported action: %s", rpz_action_to_string(a)); free(dname); return; @@ -562,7 +580,7 @@ rpz_insert_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, lock_rw_unlock(&r->local_zones->lock); return; } - verbose(VERB_ALGO, "RPZ: skipping duplicate record: '%s'", + verbose(VERB_ALGO, "rpz: skipping duplicate record: '%s'", rrstr); free(rrstr); free(dname); @@ -573,7 +591,7 @@ rpz_insert_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, tp = rpz_action_to_localzone_type(a); if(!(z = local_zones_add_zone(r->local_zones, dname, dnamelen, dnamelabs, rrclass, tp))) { - log_warn("RPZ create failed"); + log_warn("rpz: create failed"); lock_rw_unlock(&r->local_zones->lock); /* dname will be free'd in failed local_zone_create() */ return; @@ -661,7 +679,7 @@ rpz_clientip_ensure_entry(struct clientip_synthesized_rrset* set, insert_ok = addr_tree_insert(&set->entries, &node->node, addr, addrlen, net); if (!insert_ok) { - log_warn("RPZ: unexpected: unable to insert clientip address node"); + log_warn("rpz: unexpected: unable to insert clientip address node"); /* we can not free the just allocated node. * theoretically a memleak */ return NULL; @@ -677,7 +695,7 @@ rpz_report_rrset_error(const char* msg, uint8_t* rr, size_t rr_len) { log_err("malloc error while inserting RPZ clientip based record"); return; } - log_err("RPZ: unexpected: unable to insert %s: %s", msg, rrstr); + log_err("rpz: unexpected: unable to insert %s: %s", msg, rrstr); free(rrstr); } @@ -767,7 +785,7 @@ rpz_clientip_insert_trigger_rr(struct clientip_synthesized_rrset* set, struct so if(a == RPZ_LOCAL_DATA_ACTION) { if(!rpz_clientip_enter_rr(set->region, node, rrtype, rrclass, ttl, rdata, rdata_len)) { - verbose(VERB_ALGO, "RPZ: unable to insert clientip rr"); + verbose(VERB_ALGO, "rpz: unable to insert clientip rr"); lock_rw_unlock(&node->lock); return 0; } @@ -787,18 +805,14 @@ rpz_insert_clientip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, struct sockaddr_storage addr; socklen_t addrlen; int net, af; - enum respip_action respa = rpz_action_to_respip_action(a); - verbose(VERB_ALGO, "RPZ: insert clientip trigger: %s", rpz_action_to_string(a)); - - if(a == RPZ_INVALID_ACTION || respa == respip_invalid) { - verbose(VERB_ALGO, "RPZ: skipping unsupported action: %s", - rpz_action_to_string(a)); + verbose(VERB_ALGO, "rpz: insert clientip trigger: %s", rpz_action_to_string(a)); + if(a == RPZ_INVALID_ACTION) { return 0; } if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af)) { - verbose(VERB_ALGO, "RPZ: unable to parse client ip"); + verbose(VERB_ALGO, "rpz: unable to parse client ip"); return 0; } @@ -806,7 +820,28 @@ rpz_insert_clientip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, a, rrtype, rrclass, ttl, rdata, rdata_len, rr, rr_len); } +static int +rpz_insert_nsip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, + enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl, + uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len) +{ + struct sockaddr_storage addr; + socklen_t addrlen; + int net, af; + verbose(VERB_ALGO, "rpz: insert nsip trigger: %s", rpz_action_to_string(a)); + if(a == RPZ_INVALID_ACTION) { + return 0; + } + + if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af)) { + verbose(VERB_ALGO, "rpz: unable to parse ns ip"); + return 0; + } + + return rpz_clientip_insert_trigger_rr(r->ns_set, &addr, addrlen, net, + a, rrtype, rrclass, ttl, rdata, rdata_len, rr, rr_len); +} /** Insert RR into RPZ's respip_set */ static int @@ -817,18 +852,14 @@ rpz_insert_response_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, struct sockaddr_storage addr; socklen_t addrlen; int net, af; - enum respip_action respa = rpz_action_to_respip_action(a); - verbose(VERB_ALGO, "RPZ: insert response ip trigger: %s", rpz_action_to_string(a)); - - if(a == RPZ_INVALID_ACTION || respa == respip_invalid) { - verbose(VERB_ALGO, "RPZ: skipping unsupported action: %s", - rpz_action_to_string(a)); + verbose(VERB_ALGO, "rpz: insert response ip trigger: %s", rpz_action_to_string(a)); + if(a == RPZ_INVALID_ACTION) { return 0; } if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af)) { - verbose(VERB_ALGO, "RPZ: unable to parse response ip"); + verbose(VERB_ALGO, "rpz: unable to parse response ip"); return 0; } @@ -851,11 +882,11 @@ rpz_insert_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, uint8_t* dname, char* dname_str = sldns_wire2str_dname(dname, dnamelen); char* azname_str = sldns_wire2str_dname(azname, aznamelen); if(dname_str && azname_str) { - log_err("RPZ: name of record (%s) to insert into RPZ is not a " + log_err("rpz: name of record (%s) to insert into RPZ is not a " "subdomain of the configured name of the RPZ zone (%s)", dname_str, azname_str); } else { - log_err("RPZ: name of record to insert into RPZ is not a " + log_err("rpz: name of record to insert into RPZ is not a " "subdomain of the configured name of the RPZ zone"); } free(dname_str); @@ -878,7 +909,7 @@ rpz_insert_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, uint8_t* dname, t = rpz_dname_to_trigger(policydname, policydnamelen); if(t == RPZ_INVALID_TRIGGER) { free(policydname); - verbose(VERB_ALGO, "RPZ: skipping invalid trigger"); + verbose(VERB_ALGO, "rpz: skipping invalid trigger"); return 1; } if(t == RPZ_QNAME_TRIGGER) { @@ -895,9 +926,14 @@ rpz_insert_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, uint8_t* dname, a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr, rr_len); free(policydname); + } else if(t == RPZ_NSIP_TRIGGER) { + rpz_insert_nsip_trigger(r, policydname, policydnamelen, + a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr, + rr_len); + free(policydname); } else { free(policydname); - verbose(VERB_ALGO, "RPZ: skipping unsupported trigger: %s", + verbose(VERB_ALGO, "rpz: skipping unsupported trigger: %s", rpz_trigger_to_string(t)); } return 1; @@ -1090,7 +1126,7 @@ rpz_remove_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, z = rpz_find_zone(r, dname, dnamelen, rr_class, 1 /* only exact */, 1 /* wr lock */, 1 /* keep lock*/); if(!z) { - verbose(VERB_ALGO, "RPZ: cannot remove RR from IXFR, " + verbose(VERB_ALGO, "rpz: cannot remove RR from IXFR, " "RPZ domain not found"); return; } @@ -1126,7 +1162,7 @@ rpz_remove_response_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, lock_rw_wrlock(&r->respip_set->lock); if(!(node = (struct resp_addr*)addr_tree_find( &r->respip_set->ip_tree, &addr, addrlen, net))) { - verbose(VERB_ALGO, "RPZ: cannot remove RR from IXFR, " + verbose(VERB_ALGO, "rpz: cannot remove RR from IXFR, " "RPZ domain not found"); lock_rw_unlock(&r->respip_set->lock); return; @@ -1199,43 +1235,41 @@ log_rpz_apply(uint8_t* dname, enum rpz_action a, struct query_info* qinfo, log_nametypeclass(0, txt, qinfo->qname, qinfo->qtype, qinfo->qclass); } -static enum rpz_action -rpz_apply_client_ip_trigger(struct rpz* r, struct comm_reply* repinfo) +static struct clientip_synthesized_rr* +rpz_ipbased_trigger_lookup(struct clientip_synthesized_rrset* set, struct sockaddr_storage* addr, socklen_t addrlen) { struct clientip_synthesized_rr* raddr = NULL; enum rpz_action action = RPZ_INVALID_ACTION; - struct sockaddr_storage* addr = &repinfo->addr; - socklen_t addrlen = repinfo->addrlen; - lock_rw_rdlock(&r->client_set->lock); + lock_rw_rdlock(&set->lock); - raddr = (struct clientip_synthesized_rr*)addr_tree_lookup(&r->client_set->entries, + raddr = (struct clientip_synthesized_rr*)addr_tree_lookup(&set->entries, addr, addrlen); if(raddr != NULL) { action = raddr->action; lock_rw_unlock(&raddr->lock); } - verbose(VERB_ALGO, "RPZ: apply client ip trigger: found=%d action=%s", + verbose(VERB_ALGO, "rpz: ipbased trigger lookup: found=%d action=%s", raddr != NULL, rpz_action_to_string(action)); - lock_rw_unlock(&r->client_set->lock); + lock_rw_unlock(&set->lock); - return action; + return raddr; } static inline -enum rpz_action +struct clientip_synthesized_rr* rpz_resolve_client_action_and_zone(struct auth_zones* az, struct query_info* qinfo, struct comm_reply* repinfo, uint8_t* taglist, size_t taglen, struct ub_server_stats* stats, /* output parameters */ struct local_zone** z_out, struct auth_zone** a_out, struct rpz** r_out ) { + struct clientip_synthesized_rr* node = NULL; struct auth_zone* a = NULL; struct rpz* r = NULL; struct local_zone* z = NULL; - enum rpz_action action = RPZ_PASSTHRU_ACTION; lock_rw_rdlock(&az->rpz_lock); @@ -1249,7 +1283,7 @@ rpz_resolve_client_action_and_zone(struct auth_zones* az, struct query_info* qin } z = rpz_find_zone(r, qinfo->qname, qinfo->qname_len, qinfo->qclass, 0, 0, 0); - action = rpz_apply_client_ip_trigger(r, repinfo); + node = rpz_ipbased_trigger_lookup(r->client_set, &repinfo->addr, repinfo->addrlen); if(z && r->action_override == RPZ_DISABLED_ACTION) { if(r->log) log_rpz_apply(z->name, @@ -1262,6 +1296,11 @@ rpz_resolve_client_action_and_zone(struct auth_zones* az, struct query_info* qin } if(z) { break; + } else { + if(node != NULL) { + lock_rw_unlock(&node->lock); + node = NULL; + } } /* not found in this auth_zone */ lock_rw_unlock(&a->lock); @@ -1273,7 +1312,7 @@ rpz_resolve_client_action_and_zone(struct auth_zones* az, struct query_info* qin *a_out = a; *z_out = z; - return action; + return node; } static inline int @@ -1335,52 +1374,40 @@ rpz_clientip_find_rrset(struct query_info* qinfo, struct clientip_synthesized_rr } static void -rpz_apply_clientip_localdata_action(struct rpz* r, struct module_env* env, - struct query_info* qinfo, struct edns_data* edns, struct comm_reply* repinfo, - sldns_buffer* buf, struct regional* temp) +rpz_apply_clientip_localdata_action(struct rpz* r, struct clientip_synthesized_rr* raddr, + struct module_env* env, struct query_info* qinfo, struct edns_data* edns, + struct comm_reply* repinfo, sldns_buffer* buf, struct regional* temp) { - struct clientip_synthesized_rr* raddr = NULL; struct local_rrset* rrset; enum rpz_action action = RPZ_INVALID_ACTION; - struct sockaddr_storage* addr = &repinfo->addr; - socklen_t addrlen = repinfo->addrlen; struct ub_packed_rrset_key* rp = NULL; int rcode = LDNS_RCODE_NOERROR|BIT_AA; int rrset_count = 1; - verbose(VERB_ALGO, "RPZ: apply client ip trigger: found=%d action=%s", + verbose(VERB_ALGO, "rpz: apply client ip trigger: found=%d action=%s", raddr != NULL, rpz_action_to_string(action)); - lock_rw_rdlock(&r->client_set->lock); - - raddr = (struct clientip_synthesized_rr*)addr_tree_lookup( - &r->client_set->entries, addr, addrlen); - if(raddr == NULL) { - lock_rw_unlock(&r->client_set->lock); - return; - } - /* prepare synthesized answer for client */ action = raddr->action; - if(action != RPZ_LOCAL_DATA_ACTION && raddr->data == NULL ) { - verbose(VERB_ALGO, "RPZ: bug: local-data action and no local data"); - goto done; + if(action == RPZ_LOCAL_DATA_ACTION && raddr->data == NULL ) { + verbose(VERB_ALGO, "rpz: bug: local-data action but no local data"); + return; } /* check query type / rr type */ rrset = rpz_clientip_find_rrset(qinfo, raddr); if(rrset == NULL) { - verbose(VERB_ALGO, "RPZ: unable to find local-data for query"); + verbose(VERB_ALGO, "rpz: unable to find local-data for query"); rrset_count = 0; goto nodata; } rp = respip_copy_rrset(rrset->rrset, temp); if(!rp) { - verbose(VERB_ALGO, "RPZ: local-data action: out of memory"); - goto done; + verbose(VERB_ALGO, "rpz: local-data action: out of memory"); + return; } //struct packed_rrset_data* pd = raddr->data->entry.data; @@ -1391,10 +1418,88 @@ rpz_apply_clientip_localdata_action(struct rpz* r, struct module_env* env, rp->rk.dname_len = qinfo->qname_len; nodata: rpz_local_encode(qinfo, env, edns, repinfo, buf, temp, rp, rrset_count, rcode); +} + +int +rpz_iterator_module_callback(struct module_qstate* ms, struct iter_qstate* is) +{ + struct auth_zones* az = ms->env->auth_zones; + struct auth_zone* a; + struct clientip_synthesized_rr* raddr; + struct local_rrset* rrset; + enum rpz_action action = RPZ_INVALID_ACTION; + struct sockaddr_storage* addr = &ms->reply->addr; + socklen_t addrlen = ms->reply->addrlen; + struct ub_packed_rrset_key* rp = NULL; + int rcode = LDNS_RCODE_NOERROR|BIT_AA; + int rrset_count = 1; + struct rpz* r; + int ret = 0; + + verbose(VERB_ALGO, "rpz: iterator module callback: have_rpz=%d", az->rpz_first != NULL); + + lock_rw_rdlock(&az->rpz_lock); + + raddr = NULL; + for(a = az->rpz_first; a != NULL; a = a->rpz_az_next) { + lock_rw_rdlock(&a->lock); + r = a->rpz; + raddr = rpz_ipbased_trigger_lookup(r->ns_set, &ms->reply->addr, ms->reply->addrlen); + if(raddr != NULL) { + lock_rw_unlock(&a->lock); + break; + } + lock_rw_unlock(&a->lock); + } + + if(raddr == NULL) { return 0; } + + verbose(VERB_ALGO, "rpz: iterator callback: nsip: apply action=%s", + rpz_action_to_string(raddr->action)); + + action = raddr->action; + if(action == RPZ_LOCAL_DATA_ACTION && raddr->data == NULL ) { + verbose(VERB_ALGO, "rpz: bug: local-data action but no local data"); + ret = -1; + goto done; + } + + switch(action) { + case RPZ_NXDOMAIN_ACTION: + FLAGS_SET_RCODE(is->response->rep->flags, LDNS_RCODE_NXDOMAIN); + is->response->rep->an_numrrsets = 0; + is->response->rep->ns_numrrsets = 0; + is->response->rep->ar_numrrsets = 0; + is->response->rep->authoritative = 1; + is->response->rep->qdcount = 1; + ret = 1; + break; + case RPZ_NODATA_ACTION: + FLAGS_SET_RCODE(is->response->rep->flags, LDNS_RCODE_NOERROR); + is->response->rep->an_numrrsets = 0; + is->response->rep->ns_numrrsets = 0; + is->response->rep->ar_numrrsets = 0; + is->response->rep->authoritative = 1; + is->response->rep->qdcount = 1; + ret = 1; + break; + case RPZ_PASSTHRU_ACTION: + ret = 0; + break; + default: + ret = 0; + } + + //rrset = rpz_clientip_find_rrset(qinfo, raddr); + //if(rrset == NULL) { + // verbose(VERB_ALGO, "rpz: unable to find local-data for query"); + // rrset_count = 0; + // goto nodata; + //} done: lock_rw_unlock(&raddr->lock); - lock_rw_unlock(&r->client_set->lock); + return ret; } static int @@ -1405,35 +1510,45 @@ rpz_maybe_apply_clientip_trigger(struct auth_zones* az, struct module_env* env, /* output parameters */ struct local_zone** z_out, struct auth_zone** a_out, struct rpz** r_out) { + int ret = 0; enum rpz_action client_action; - client_action = rpz_resolve_client_action_and_zone( + struct clientip_synthesized_rr* node = rpz_resolve_client_action_and_zone( az, qinfo, repinfo, taglist, taglen, stats, z_out, a_out, r_out); - verbose(VERB_ALGO, "RPZ: qname trigger: client action=%s", + client_action = node == NULL ? RPZ_INVALID_ACTION : node->action; + + verbose(VERB_ALGO, "rpz: qname trigger: client action=%s", rpz_action_to_string(client_action)); if(*z_out == NULL || (client_action != RPZ_INVALID_ACTION && client_action != RPZ_PASSTHRU_ACTION)) { - verbose(VERB_ALGO, "RPZ: client action without zone"); + verbose(VERB_ALGO, "rpz: client action without zone"); if(client_action == RPZ_PASSTHRU_ACTION || client_action == RPZ_INVALID_ACTION || (client_action == RPZ_TCP_ONLY_ACTION && !rpz_is_udp_query(repinfo))) { - return 0; + ret = 0; + goto done; } stats->rpz_action[client_action]++; if(client_action == RPZ_LOCAL_DATA_ACTION) { - rpz_apply_clientip_localdata_action(*r_out, env, qinfo, - edns, repinfo, buf, temp); + rpz_apply_clientip_localdata_action(*r_out, node, env, + qinfo, edns, repinfo, buf, temp); } else { // XXX: log_rpz_apply not possbile because no zone local_zones_zone_answer(NULL /*no zone*/, env, qinfo, edns, repinfo, buf, temp, 0 /* no local data used */, rpz_action_to_localzone_type(client_action)); } - return 1; + ret = 1; + goto done; } - return -1; + ret = -1; +done: + if(node != NULL) { + lock_rw_unlock(&node->lock); + } + return ret; } int @@ -1465,7 +1580,7 @@ rpz_apply_qname_trigger(struct auth_zones* az, struct module_env* env, lzt = rpz_action_to_localzone_type(r->action_override); } - verbose(VERB_ALGO, "RPZ: final client action=%s", + verbose(VERB_ALGO, "rpz: final client action=%s", rpz_action_to_string(localzone_type_to_rpz_action(lzt))); if(r->action_override == RPZ_CNAME_OVERRIDE_ACTION) { diff --git a/services/rpz.h b/services/rpz.h index 19dd867b6..71badbb74 100644 --- a/services/rpz.h +++ b/services/rpz.h @@ -114,6 +114,7 @@ struct rpz { struct local_zones* local_zones; struct respip_set* respip_set; struct clientip_synthesized_rrset* client_set; + struct clientip_synthesized_rrset* ns_set; uint8_t* taglist; size_t taglistlen; enum rpz_action action_override; @@ -177,6 +178,9 @@ int rpz_apply_qname_trigger(struct auth_zones* az, struct module_env* env, struct regional* temp, struct comm_reply* repinfo, uint8_t* taglist, size_t taglen, struct ub_server_stats* stats); +struct iter_qstate; +int rpz_iterator_module_callback(struct module_qstate*, struct iter_qstate*); + /** * Delete RPZ * @param r: RPZ struct to delete diff --git a/testdata/rpz_nsip.rpl b/testdata/rpz_nsip.rpl index d07199c35..29cd8e189 100644 --- a/testdata/rpz_nsip.rpl +++ b/testdata/rpz_nsip.rpl @@ -32,7 +32,7 @@ CONFIG_END SCENARIO_BEGIN Test RPZ nsip triggers -; . +; . -------------------------------------------------------------------------- RANGE_BEGIN 0 100 ADDRESS 1.1.1.1 ENTRY_BEGIN @@ -121,7 +121,7 @@ ENTRY_END RANGE_END -; com. +; com. ----------------------------------------------------------------------- RANGE_BEGIN 0 100 ADDRESS 8.8.8.8 @@ -151,7 +151,37 @@ ENTRY_END RANGE_END -; ns1.gotham.com. +; aa. ------------------------------------------------------------------------ +RANGE_BEGIN 0 100 + ADDRESS 8.8.0.8 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +aa. IN NS +SECTION ANSWER +aa. IN NS ns1.aa. +SECTION ADDITIONAL +ns1.aa. IN A 8.8.0.8 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +gotham.aa. IN A +SECTION AUTHORITY +gotham.aa. IN NS ns1.gotham.aa. +SECTION ADDITIONAL +ns1.gotham.aa. IN A 192.0.0.1 +ENTRY_END + +RANGE_END + +; ns1.gotham.com. ------------------------------------------------------------ RANGE_BEGIN 0 100 ADDRESS 192.0.6.1 @@ -167,6 +197,22 @@ ENTRY_END RANGE_END +; ns1.gotham.aa. ------------------------------------------------------------- +RANGE_BEGIN 0 100 + ADDRESS 192.0.0.1 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +gotham.aa. IN A +SECTION ANSWER +gotham.aa. IN A 192.0.0.2 +ENTRY_END + +RANGE_END + STEP 1 QUERY ENTRY_BEGIN REPLY RD @@ -184,4 +230,20 @@ SECTION ANSWER gotham.com. IN A 192.0.6.2 ENTRY_END +STEP 10 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +gotham.aa. IN A +ENTRY_END + +STEP 11 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR AA RD RA NXDOMAIN +SECTION QUESTION +gotham.aa. IN A +SECTION ANSWER +ENTRY_END + SCENARIO_END From 07d937f508006c9d9df82f941a46390d40e90148 Mon Sep 17 00:00:00 2001 From: mb Date: Mon, 16 Nov 2020 09:49:06 +0100 Subject: [PATCH 12/68] rpz: more nsip --- services/rpz.c | 4 ++- testdata/rpz_nsip.rpl | 64 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/services/rpz.c b/services/rpz.c index ff994a482..9b1d23884 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -1458,7 +1458,7 @@ rpz_iterator_module_callback(struct module_qstate* ms, struct iter_qstate* is) rpz_action_to_string(raddr->action)); action = raddr->action; - if(action == RPZ_LOCAL_DATA_ACTION && raddr->data == NULL ) { + if(action == RPZ_LOCAL_DATA_ACTION && raddr->data == NULL) { verbose(VERB_ALGO, "rpz: bug: local-data action but no local data"); ret = -1; goto done; @@ -1467,6 +1467,7 @@ rpz_iterator_module_callback(struct module_qstate* ms, struct iter_qstate* is) switch(action) { case RPZ_NXDOMAIN_ACTION: FLAGS_SET_RCODE(is->response->rep->flags, LDNS_RCODE_NXDOMAIN); + is->response->rep->flags |= BIT_QR | BIT_AA | BIT_RA; is->response->rep->an_numrrsets = 0; is->response->rep->ns_numrrsets = 0; is->response->rep->ar_numrrsets = 0; @@ -1476,6 +1477,7 @@ rpz_iterator_module_callback(struct module_qstate* ms, struct iter_qstate* is) break; case RPZ_NODATA_ACTION: FLAGS_SET_RCODE(is->response->rep->flags, LDNS_RCODE_NOERROR); + is->response->rep->flags |= BIT_QR | BIT_AA | BIT_RA; is->response->rep->an_numrrsets = 0; is->response->rep->ns_numrrsets = 0; is->response->rep->ar_numrrsets = 0; diff --git a/testdata/rpz_nsip.rpl b/testdata/rpz_nsip.rpl index 29cd8e189..1d4462df0 100644 --- a/testdata/rpz_nsip.rpl +++ b/testdata/rpz_nsip.rpl @@ -181,6 +181,36 @@ ENTRY_END RANGE_END +; bb. ------------------------------------------------------------------------ +RANGE_BEGIN 0 100 + ADDRESS 8.8.1.8 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +bb. IN NS +SECTION ANSWER +bb. IN NS ns1.aa. +SECTION ADDITIONAL +ns1.bb. IN A 8.8.1.8 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +gotham.bb. IN A +SECTION AUTHORITY +gotham.bb. IN NS ns1.gotham.bb. +SECTION ADDITIONAL +ns1.gotham.bb. IN A 192.0.1.1 +ENTRY_END + +RANGE_END + ; ns1.gotham.com. ------------------------------------------------------------ RANGE_BEGIN 0 100 ADDRESS 192.0.6.1 @@ -213,6 +243,22 @@ ENTRY_END RANGE_END +; ns1.gotham.bb. ------------------------------------------------------------- +RANGE_BEGIN 0 100 + ADDRESS 192.0.1.1 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +gotham.bb. IN A +SECTION ANSWER +gotham.bb. IN A 192.0.1.2 +ENTRY_END + +RANGE_END + STEP 1 QUERY ENTRY_BEGIN REPLY RD @@ -240,10 +286,26 @@ ENTRY_END STEP 11 CHECK_ANSWER ENTRY_BEGIN MATCH all -REPLY QR AA RD RA NXDOMAIN +REPLY QR RD RA NXDOMAIN SECTION QUESTION gotham.aa. IN A SECTION ANSWER ENTRY_END +STEP 20 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +gotham.bb. IN A +ENTRY_END + +STEP 21 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +gotham.bb. IN A +SECTION ANSWER +ENTRY_END + SCENARIO_END From f7fb338c958450c08e78d22e631906c1084e64e5 Mon Sep 17 00:00:00 2001 From: mb Date: Mon, 16 Nov 2020 12:42:23 +0100 Subject: [PATCH 13/68] rpz: continue work on the nsip trigger --- respip/respip.c | 37 -------------- services/rpz.c | 113 +++++++++++++++++++++++++++++++----------- testdata/rpz_nsip.rpl | 79 ++++++++++++++++++++++++++++- util/data/msgreply.c | 26 ++++++++++ util/data/msgreply.h | 15 ++++++ 5 files changed, 202 insertions(+), 68 deletions(-) diff --git a/respip/respip.c b/respip/respip.c index 75400bb4d..40f839645 100644 --- a/respip/respip.c +++ b/respip/respip.c @@ -635,43 +635,6 @@ respip_addr_lookup(const struct reply_info *rep, struct respip_set* rs, return NULL; } -/* - * Create a new reply_info based on 'rep'. The new info is based on - * the passed 'rep', but ignores any rrsets except for the first 'an_numrrsets' - * RRsets in the answer section. These answer rrsets are copied to the - * new info, up to 'copy_rrsets' rrsets (which must not be larger than - * 'an_numrrsets'). If an_numrrsets > copy_rrsets, the remaining rrsets array - * entries will be kept empty so the caller can fill them later. When rrsets - * are copied, they are shallow copied. The caller must ensure that the - * copied rrsets are valid throughout its lifetime and must provide appropriate - * mutex if it can be shared by multiple threads. - */ -static struct reply_info * -make_new_reply_info(const struct reply_info* rep, struct regional* region, - size_t an_numrrsets, size_t copy_rrsets) -{ - struct reply_info* new_rep; - size_t i; - - /* create a base struct. we specify 'insecure' security status as - * the modified response won't be DNSSEC-valid. In our faked response - * the authority and additional sections will be empty (except possible - * EDNS0 OPT RR in the additional section appended on sending it out), - * so the total number of RRsets is an_numrrsets. */ - new_rep = construct_reply_info_base(region, rep->flags, - rep->qdcount, rep->ttl, rep->prefetch_ttl, - rep->serve_expired_ttl, an_numrrsets, 0, 0, an_numrrsets, - sec_status_insecure); - if(!new_rep) - return NULL; - if(!reply_info_alloc_rrset_keys(new_rep, NULL, region)) - return NULL; - for(i=0; irrsets[i] = rep->rrsets[i]; - - return new_rep; -} - /** * See if response-ip or tag data should override the original answer rrset * (which is rep->rrsets[rrset_id]) and if so override it. diff --git a/services/rpz.c b/services/rpz.c index 9b1d23884..eae3feeeb 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -1361,11 +1361,11 @@ rpz_local_encode(struct query_info* qinfo, struct module_env* env, } static struct local_rrset* -rpz_clientip_find_rrset(struct query_info* qinfo, struct clientip_synthesized_rr* data) { +rpz_find_synthesized_rrset(int 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(qinfo->qtype) == packed_rrset->type) { + if(htons(qtype) == packed_rrset->type) { return cursor; } cursor = cursor->next; @@ -1397,7 +1397,7 @@ rpz_apply_clientip_localdata_action(struct rpz* r, struct clientip_synthesized_r /* check query type / rr type */ - rrset = rpz_clientip_find_rrset(qinfo, raddr); + rrset = rpz_find_synthesized_rrset(qinfo->qtype, raddr); if(rrset == NULL) { verbose(VERB_ALGO, "rpz: unable to find local-data for query"); rrset_count = 0; @@ -1420,19 +1420,82 @@ nodata: rpz_local_encode(qinfo, env, edns, repinfo, buf, temp, rp, rrset_count, rcode); } +static inline void +rpz_patch_nodata(struct reply_info* ri) +{ + FLAGS_SET_RCODE(ri->flags, LDNS_RCODE_NOERROR); + ri->flags |= BIT_QR | BIT_AA | BIT_RA; + ri->an_numrrsets = 0; + ri->ns_numrrsets = 0; + ri->ar_numrrsets = 0; + ri->authoritative = 0; + ri->rrset_count = 1; + ri->qdcount = 1; +} + +static inline void +rpz_patch_nxdomain(struct reply_info* ri) +{ + FLAGS_SET_RCODE(ri->flags, LDNS_RCODE_NXDOMAIN); + ri->flags |= BIT_QR | BIT_AA | BIT_RA; + ri->an_numrrsets = 0; + ri->ns_numrrsets = 0; + ri->ar_numrrsets = 0; + ri->authoritative = 0; + ri->rrset_count = 1; + ri->qdcount = 1; +} + +static inline int +rpz_patch_localdata(struct dns_msg* response, struct clientip_synthesized_rr* data, + struct regional* region) +{ + struct query_info* qi = &response->qinfo; + struct ub_packed_rrset_key* rp; + struct local_rrset* rrset; + struct reply_info* new_reply_info; + struct reply_info* ri = response->rep; + + rrset = rpz_find_synthesized_rrset(qi->qtype, data); + if(rrset == NULL) { + verbose(VERB_ALGO, "rpz: nsip: no matching synthesized data found; resorting to nodata"); + rpz_patch_nodata(ri); + return 1; + } + new_reply_info = make_new_reply_info(ri, region, 0, 0); + if(new_reply_info == NULL) { + log_err("out of memory"); + rpz_patch_nodata(ri); + return 1; + } + rp = respip_copy_rrset(rrset->rrset, region); + if(rp == NULL) { + log_err("out of memory"); + rpz_patch_nodata(ri); + return 1; + } + new_reply_info->rrsets = regional_alloc(region, sizeof(*new_reply_info->rrsets)); + if(new_reply_info->rrsets == NULL) { + log_err("out of memory"); + rpz_patch_nodata(ri); + return 1; + } + rp->rk.dname = qi->qname; + rp->rk.dname_len = qi->qname_len; + new_reply_info->rrset_count = 1; + new_reply_info->an_numrrsets = 1; + new_reply_info->rrsets[0] = rp; + response->rep = new_reply_info; + return 1; +} + int rpz_iterator_module_callback(struct module_qstate* ms, struct iter_qstate* is) { struct auth_zones* az = ms->env->auth_zones; struct auth_zone* a; struct clientip_synthesized_rr* raddr; - struct local_rrset* rrset; enum rpz_action action = RPZ_INVALID_ACTION; - struct sockaddr_storage* addr = &ms->reply->addr; - socklen_t addrlen = ms->reply->addrlen; - struct ub_packed_rrset_key* rp = NULL; - int rcode = LDNS_RCODE_NOERROR|BIT_AA; - int rrset_count = 1; struct rpz* r; int ret = 0; @@ -1466,39 +1529,29 @@ rpz_iterator_module_callback(struct module_qstate* ms, struct iter_qstate* is) switch(action) { case RPZ_NXDOMAIN_ACTION: - FLAGS_SET_RCODE(is->response->rep->flags, LDNS_RCODE_NXDOMAIN); - is->response->rep->flags |= BIT_QR | BIT_AA | BIT_RA; - is->response->rep->an_numrrsets = 0; - is->response->rep->ns_numrrsets = 0; - is->response->rep->ar_numrrsets = 0; - is->response->rep->authoritative = 1; - is->response->rep->qdcount = 1; + rpz_patch_nxdomain(is->response->rep); ret = 1; break; case RPZ_NODATA_ACTION: - FLAGS_SET_RCODE(is->response->rep->flags, LDNS_RCODE_NOERROR); - is->response->rep->flags |= BIT_QR | BIT_AA | BIT_RA; - is->response->rep->an_numrrsets = 0; - is->response->rep->ns_numrrsets = 0; - is->response->rep->ar_numrrsets = 0; - is->response->rep->authoritative = 1; - is->response->rep->qdcount = 1; + rpz_patch_nodata(is->response->rep); ret = 1; break; + case RPZ_TCP_ONLY_ACTION: + log_err("rpz: nsip: tcp-only trigger unimplemented; resorting to passthru"); + ret = 0; + break; case RPZ_PASSTHRU_ACTION: ret = 0; break; + case RPZ_LOCAL_DATA_ACTION: + ret = rpz_patch_localdata(is->response, raddr, ms->region); + break; default: + verbose(VERB_ALGO, "rpz: nsip: bug: unhandled or invalid action: '%s'", + rpz_action_to_string(action)); ret = 0; } - //rrset = rpz_clientip_find_rrset(qinfo, raddr); - //if(rrset == NULL) { - // verbose(VERB_ALGO, "rpz: unable to find local-data for query"); - // rrset_count = 0; - // goto nodata; - //} - done: lock_rw_unlock(&raddr->lock); return ret; diff --git a/testdata/rpz_nsip.rpl b/testdata/rpz_nsip.rpl index 1d4462df0..ac9e80b80 100644 --- a/testdata/rpz_nsip.rpl +++ b/testdata/rpz_nsip.rpl @@ -119,6 +119,18 @@ SECTION ADDITIONAL ns1.ee. IN A 8.8.5.8 ENTRY_END +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +ff. IN A +SECTION AUTHORITY +ff. IN NS ns1.ff. +SECTION ADDITIONAL +ns1.ff. IN A 8.8.6.8 +ENTRY_END + RANGE_END ; com. ----------------------------------------------------------------------- @@ -192,7 +204,7 @@ REPLY QR NOERROR SECTION QUESTION bb. IN NS SECTION ANSWER -bb. IN NS ns1.aa. +bb. IN NS ns1.bb. SECTION ADDITIONAL ns1.bb. IN A 8.8.1.8 ENTRY_END @@ -211,6 +223,36 @@ ENTRY_END RANGE_END +; ff. ------------------------------------------------------------------------ +RANGE_BEGIN 0 100 + ADDRESS 8.8.6.8 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +ff. IN NS +SECTION ANSWER +ff. IN NS ns1.ff. +SECTION ADDITIONAL +ns1.ff. IN A 8.8.6.8 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +gotham.ff. IN A +SECTION AUTHORITY +gotham.ff. IN NS ns1.gotham.ff. +SECTION ADDITIONAL +ns1.gotham.ff. IN A 192.0.5.1 +ENTRY_END + +RANGE_END + ; ns1.gotham.com. ------------------------------------------------------------ RANGE_BEGIN 0 100 ADDRESS 192.0.6.1 @@ -259,6 +301,24 @@ ENTRY_END RANGE_END +; ns1.gotham.ff. ------------------------------------------------------------- +RANGE_BEGIN 0 100 + ADDRESS 192.0.5.1 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +gotham.ff. IN A +SECTION ANSWER +gotham.ff. IN A 192.0.5.2 +ENTRY_END + +RANGE_END + +; ---------------------------------------------------------------------------- + STEP 1 QUERY ENTRY_BEGIN REPLY RD @@ -308,4 +368,21 @@ gotham.bb. IN A SECTION ANSWER ENTRY_END +STEP 30 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +gotham.ff. IN A +ENTRY_END + +STEP 31 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +gotham.ff. IN A +SECTION ANSWER +gotham.ff. IN A 127.0.0.1 +ENTRY_END + SCENARIO_END diff --git a/util/data/msgreply.c b/util/data/msgreply.c index 927bf09a2..fed6e5345 100644 --- a/util/data/msgreply.c +++ b/util/data/msgreply.c @@ -164,6 +164,32 @@ reply_info_alloc_rrset_keys(struct reply_info* rep, struct alloc_cache* alloc, return 1; } +struct reply_info * +make_new_reply_info(const struct reply_info* rep, struct regional* region, + size_t an_numrrsets, size_t copy_rrsets) +{ + struct reply_info* new_rep; + size_t i; + + /* create a base struct. we specify 'insecure' security status as + * the modified response won't be DNSSEC-valid. In our faked response + * the authority and additional sections will be empty (except possible + * EDNS0 OPT RR in the additional section appended on sending it out), + * so the total number of RRsets is an_numrrsets. */ + new_rep = construct_reply_info_base(region, rep->flags, + rep->qdcount, rep->ttl, rep->prefetch_ttl, + rep->serve_expired_ttl, an_numrrsets, 0, 0, an_numrrsets, + sec_status_insecure); + if(!new_rep) + return NULL; + if(!reply_info_alloc_rrset_keys(new_rep, NULL, region)) + return NULL; + for(i=0; irrsets[i] = rep->rrsets[i]; + + return new_rep; +} + /** find the minimumttl in the rdata of SOA record */ static time_t soa_find_minttl(struct rr_parse* rr) diff --git a/util/data/msgreply.h b/util/data/msgreply.h index 385780268..64ff4dfbe 100644 --- a/util/data/msgreply.h +++ b/util/data/msgreply.h @@ -382,6 +382,21 @@ struct reply_info* reply_info_copy(struct reply_info* rep, int reply_info_alloc_rrset_keys(struct reply_info* rep, struct alloc_cache* alloc, struct regional* region); +/* + * Create a new reply_info based on 'rep'. The new info is based on + * the passed 'rep', but ignores any rrsets except for the first 'an_numrrsets' + * RRsets in the answer section. These answer rrsets are copied to the + * new info, up to 'copy_rrsets' rrsets (which must not be larger than + * 'an_numrrsets'). If an_numrrsets > copy_rrsets, the remaining rrsets array + * entries will be kept empty so the caller can fill them later. When rrsets + * are copied, they are shallow copied. The caller must ensure that the + * copied rrsets are valid throughout its lifetime and must provide appropriate + * mutex if it can be shared by multiple threads. + */ +struct reply_info * +make_new_reply_info(const struct reply_info* rep, struct regional* region, + size_t an_numrrsets, size_t copy_rrsets); + /** * Copy a parsed rrset into given key, decompressing and allocating rdata. * @param pkt: packet for decompression From 354c19f6ace2e23b30678a6d8d62db24efefd5aa Mon Sep 17 00:00:00 2001 From: mb Date: Tue, 24 Nov 2020 09:33:08 +0100 Subject: [PATCH 14/68] rpz: apply trigger at query time not response time --- iterator/iterator.c | 8 +++++--- services/rpz.c | 26 ++++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/iterator/iterator.c b/iterator/iterator.c index fcda1bb3b..f2585be31 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -2471,6 +2471,8 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, /* Add the current set of unused targets to our queue. */ delegpt_add_unused_targets(iq->dp); + rpz_iterator_module_callback(qstate, iq); + /* Select the next usable target, filtering out unsuitable targets. */ target = iter_server_selection(ie, qstate->env, iq->dp, iq->dp->name, iq->dp->namelen, iq->qchase.qtype, @@ -2660,15 +2662,15 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, { int dnsseclame = 0; enum response_type type; - int rpz_filter_result; + //int rpz_filter_result; iq->num_current_queries--; if(!inplace_cb_query_response_call(qstate->env, qstate, iq->response)) log_err("unable to call query_response callback"); - rpz_filter_result = rpz_iterator_module_callback(qstate, iq); - if(rpz_filter_result > 0) { next_state(iq, FINISHED_STATE); } + //rpz_filter_result = rpz_iterator_module_callback(qstate, iq); + //if(rpz_filter_result > 0) { next_state(iq, FINISHED_STATE); } if(iq->response == NULL) { /* Don't increment qname when QNAME minimisation is enabled */ diff --git a/services/rpz.c b/services/rpz.c index eae3feeeb..5b5c10eb8 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -53,6 +53,7 @@ #include "util/data/msgencode.h" #include "services/cache/dns.h" #include "iterator/iterator.h" +#include "iterator/iter_delegpt.h" typedef struct resp_addr rpz_aclnode_type; @@ -1489,16 +1490,35 @@ rpz_patch_localdata(struct dns_msg* response, struct clientip_synthesized_rr* da return 1; } +struct clientip_synthesized_rr* +rpz_delegation_point_ipbased_trigger_lookup(struct rpz* rpz, + struct iter_qstate* is) +{ + struct delegpt_addr* cursor; + struct clientip_synthesized_rr* action = NULL; + if(is->dp == NULL) { return NULL; } + for(cursor = is->dp->target_list; cursor != NULL; cursor = cursor->next_target) { + if(cursor->bogus) { continue; } + action = rpz_ipbased_trigger_lookup(rpz->ns_set, &cursor->addr, cursor->addrlen); + if(action != NULL) { return action; } + } + return NULL; +} + int rpz_iterator_module_callback(struct module_qstate* ms, struct iter_qstate* is) { - struct auth_zones* az = ms->env->auth_zones; + struct auth_zones* az; struct auth_zone* a; struct clientip_synthesized_rr* raddr; enum rpz_action action = RPZ_INVALID_ACTION; struct rpz* r; int ret = 0; + if(ms->env == NULL || ms->env->auth_zones == NULL) { return 0; } + + az = ms->env->auth_zones; + verbose(VERB_ALGO, "rpz: iterator module callback: have_rpz=%d", az->rpz_first != NULL); lock_rw_rdlock(&az->rpz_lock); @@ -1507,7 +1527,7 @@ rpz_iterator_module_callback(struct module_qstate* ms, struct iter_qstate* is) for(a = az->rpz_first; a != NULL; a = a->rpz_az_next) { lock_rw_rdlock(&a->lock); r = a->rpz; - raddr = rpz_ipbased_trigger_lookup(r->ns_set, &ms->reply->addr, ms->reply->addrlen); + raddr = rpz_delegation_point_ipbased_trigger_lookup(r, is); if(raddr != NULL) { lock_rw_unlock(&a->lock); break; @@ -1515,6 +1535,8 @@ rpz_iterator_module_callback(struct module_qstate* ms, struct iter_qstate* is) lock_rw_unlock(&a->lock); } + lock_rw_unlock(&az->rpz_lock); + if(raddr == NULL) { return 0; } verbose(VERB_ALGO, "rpz: iterator callback: nsip: apply action=%s", From 126e114d6f5f3ce1668f1c70845b8169dc1c2db3 Mon Sep 17 00:00:00 2001 From: mb Date: Tue, 24 Nov 2020 11:25:01 +0100 Subject: [PATCH 15/68] rpz: forge responses --- iterator/iterator.c | 13 +++-- services/rpz.c | 128 ++++++++++++++++++++++++++------------------ services/rpz.h | 2 +- 3 files changed, 86 insertions(+), 57 deletions(-) diff --git a/iterator/iterator.c b/iterator/iterator.c index f2585be31..609786a16 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -2471,7 +2471,14 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, /* Add the current set of unused targets to our queue. */ delegpt_add_unused_targets(iq->dp); - rpz_iterator_module_callback(qstate, iq); + { /* apply rpz triggers at query time */ + struct dns_msg* forged_response = rpz_iterator_module_callback(qstate, iq); + if(forged_response != NULL) { + iq->response = forged_response; + next_state(iq, FINISHED_STATE); + return 0; + } + } /* Select the next usable target, filtering out unsuitable targets. */ target = iter_server_selection(ie, qstate->env, iq->dp, @@ -2662,16 +2669,12 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, { int dnsseclame = 0; enum response_type type; - //int rpz_filter_result; iq->num_current_queries--; if(!inplace_cb_query_response_call(qstate->env, qstate, iq->response)) log_err("unable to call query_response callback"); - //rpz_filter_result = rpz_iterator_module_callback(qstate, iq); - //if(rpz_filter_result > 0) { next_state(iq, FINISHED_STATE); } - if(iq->response == NULL) { /* Don't increment qname when QNAME minimisation is enabled */ if(qstate->env->cfg->qname_minimisation) { diff --git a/services/rpz.c b/services/rpz.c index 5b5c10eb8..acf2b8848 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -1421,73 +1421,101 @@ nodata: rpz_local_encode(qinfo, env, edns, repinfo, buf, temp, rp, rrset_count, rcode); } -static inline void -rpz_patch_nodata(struct reply_info* ri) +static inline struct dns_msg* +rpz_dns_msg_new(struct regional* region) { - FLAGS_SET_RCODE(ri->flags, LDNS_RCODE_NOERROR); - ri->flags |= BIT_QR | BIT_AA | BIT_RA; - ri->an_numrrsets = 0; - ri->ns_numrrsets = 0; - ri->ar_numrrsets = 0; - ri->authoritative = 0; - ri->rrset_count = 1; - ri->qdcount = 1; + struct dns_msg* msg = + (struct dns_msg*)regional_alloc(region, + sizeof(struct dns_msg)); + if(msg == NULL) { return NULL; } + memset(msg, 0, sizeof(struct dns_msg)); + + return msg; } -static inline void -rpz_patch_nxdomain(struct reply_info* ri) +static inline struct dns_msg* +rpz_patch_nodata(struct rpz* r, struct module_qstate* ms) { - FLAGS_SET_RCODE(ri->flags, LDNS_RCODE_NXDOMAIN); - ri->flags |= BIT_QR | BIT_AA | BIT_RA; - ri->an_numrrsets = 0; - ri->ns_numrrsets = 0; - ri->ar_numrrsets = 0; - ri->authoritative = 0; - ri->rrset_count = 1; - ri->qdcount = 1; + struct dns_msg* msg = rpz_dns_msg_new(ms->region); + if(msg == NULL) { return msg; } + msg->qinfo = ms->qinfo; + msg->rep = construct_reply_info_base(ms->region, + LDNS_RCODE_NOERROR|BIT_QR|BIT_AA|BIT_RA, + 1, //qd + 0, //ttl + 0, //prettl + 0, //expttl + 0, //an + 0, //ns + 0, //ar + 1, //total + sec_status_secure); + return msg; } -static inline int -rpz_patch_localdata(struct dns_msg* response, struct clientip_synthesized_rr* data, - struct regional* region) +static inline struct dns_msg* +rpz_patch_nxdomain(struct rpz* r, struct module_qstate* ms) { - struct query_info* qi = &response->qinfo; + struct dns_msg* msg = rpz_dns_msg_new(ms->region); + if(msg == NULL) { return msg; } + msg->qinfo = ms->qinfo; + msg->rep = construct_reply_info_base(ms->region, + LDNS_RCODE_NXDOMAIN|BIT_QR|BIT_AA|BIT_RA, + 1, //qd + 0, //ttl + 0, //prettl + 0, //expttl + 0, //an + 0, //ns + 0, //ar + 1, //total + sec_status_secure); + return msg; +} + +static inline struct dns_msg* +rpz_patch_localdata(struct rpz* r, + struct module_qstate* ms, + struct clientip_synthesized_rr* data) +{ + struct dns_msg* msg = NULL; + struct query_info* qi = &msg->qinfo; struct ub_packed_rrset_key* rp; struct local_rrset* rrset; struct reply_info* new_reply_info; - struct reply_info* ri = response->rep; + struct reply_info* ri = msg->rep; rrset = rpz_find_synthesized_rrset(qi->qtype, data); if(rrset == NULL) { verbose(VERB_ALGO, "rpz: nsip: no matching synthesized data found; resorting to nodata"); - rpz_patch_nodata(ri); - return 1; + return rpz_patch_nodata(r, ms); } - new_reply_info = make_new_reply_info(ri, region, 0, 0); + msg = rpz_dns_msg_new(ms->region); + if(msg == NULL) { return NULL; } + + // XXX: use ttl etc from rpz zone? + new_reply_info = make_new_reply_info(ri, ms->region, 0, 0); if(new_reply_info == NULL) { log_err("out of memory"); - rpz_patch_nodata(ri); - return 1; + return NULL; } - rp = respip_copy_rrset(rrset->rrset, region); + rp = respip_copy_rrset(rrset->rrset, ms->region); if(rp == NULL) { log_err("out of memory"); - rpz_patch_nodata(ri); - return 1; + return NULL; } - new_reply_info->rrsets = regional_alloc(region, sizeof(*new_reply_info->rrsets)); + new_reply_info->rrsets = regional_alloc(ms->region, sizeof(*new_reply_info->rrsets)); if(new_reply_info->rrsets == NULL) { log_err("out of memory"); - rpz_patch_nodata(ri); - return 1; + return NULL; } rp->rk.dname = qi->qname; rp->rk.dname_len = qi->qname_len; new_reply_info->rrset_count = 1; new_reply_info->an_numrrsets = 1; new_reply_info->rrsets[0] = rp; - response->rep = new_reply_info; - return 1; + msg->rep = new_reply_info; + return msg; } struct clientip_synthesized_rr* @@ -1505,7 +1533,7 @@ rpz_delegation_point_ipbased_trigger_lookup(struct rpz* rpz, return NULL; } -int +struct dns_msg* rpz_iterator_module_callback(struct module_qstate* ms, struct iter_qstate* is) { struct auth_zones* az; @@ -1513,7 +1541,7 @@ rpz_iterator_module_callback(struct module_qstate* ms, struct iter_qstate* is) struct clientip_synthesized_rr* raddr; enum rpz_action action = RPZ_INVALID_ACTION; struct rpz* r; - int ret = 0; + struct dns_msg* ret = NULL; if(ms->env == NULL || ms->env->auth_zones == NULL) { return 0; } @@ -1537,7 +1565,7 @@ rpz_iterator_module_callback(struct module_qstate* ms, struct iter_qstate* is) lock_rw_unlock(&az->rpz_lock); - if(raddr == NULL) { return 0; } + if(raddr == NULL) { return NULL; } verbose(VERB_ALGO, "rpz: iterator callback: nsip: apply action=%s", rpz_action_to_string(raddr->action)); @@ -1545,33 +1573,31 @@ rpz_iterator_module_callback(struct module_qstate* ms, struct iter_qstate* is) action = raddr->action; if(action == RPZ_LOCAL_DATA_ACTION && raddr->data == NULL) { verbose(VERB_ALGO, "rpz: bug: local-data action but no local data"); - ret = -1; + ret = rpz_patch_nodata(r, ms); goto done; } switch(action) { case RPZ_NXDOMAIN_ACTION: - rpz_patch_nxdomain(is->response->rep); - ret = 1; + ret = rpz_patch_nxdomain(r, ms); break; case RPZ_NODATA_ACTION: - rpz_patch_nodata(is->response->rep); - ret = 1; + ret = rpz_patch_nodata(r, ms); break; case RPZ_TCP_ONLY_ACTION: - log_err("rpz: nsip: tcp-only trigger unimplemented; resorting to passthru"); - ret = 0; + verbose(VERB_ALGO, "rpz: nsip: tcp-only trigger ignored"); + ret = NULL; break; case RPZ_PASSTHRU_ACTION: - ret = 0; + ret = NULL; break; case RPZ_LOCAL_DATA_ACTION: - ret = rpz_patch_localdata(is->response, raddr, ms->region); + ret = rpz_patch_localdata(r, ms, raddr); break; default: verbose(VERB_ALGO, "rpz: nsip: bug: unhandled or invalid action: '%s'", rpz_action_to_string(action)); - ret = 0; + ret = NULL; } done: diff --git a/services/rpz.h b/services/rpz.h index 71badbb74..7a2e9500e 100644 --- a/services/rpz.h +++ b/services/rpz.h @@ -179,7 +179,7 @@ int rpz_apply_qname_trigger(struct auth_zones* az, struct module_env* env, uint8_t* taglist, size_t taglen, struct ub_server_stats* stats); struct iter_qstate; -int rpz_iterator_module_callback(struct module_qstate*, struct iter_qstate*); +struct dns_msg* rpz_iterator_module_callback(struct module_qstate*, struct iter_qstate*); /** * Delete RPZ From b178cf34b65be7dbb5d81547910d87d94af87db2 Mon Sep 17 00:00:00 2001 From: mb Date: Tue, 24 Nov 2020 11:33:16 +0100 Subject: [PATCH 16/68] rpz: update ext_state in the iterator --- iterator/iterator.c | 1 + services/rpz.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/iterator/iterator.c b/iterator/iterator.c index 609786a16..6655ec875 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -2476,6 +2476,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, if(forged_response != NULL) { iq->response = forged_response; next_state(iq, FINISHED_STATE); + qstate->ext_state[id] = module_finished; return 0; } } diff --git a/services/rpz.c b/services/rpz.c index acf2b8848..fb219ab09 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -1448,7 +1448,7 @@ rpz_patch_nodata(struct rpz* r, struct module_qstate* ms) 0, //an 0, //ns 0, //ar - 1, //total + 0, //total sec_status_secure); return msg; } @@ -1468,7 +1468,7 @@ rpz_patch_nxdomain(struct rpz* r, struct module_qstate* ms) 0, //an 0, //ns 0, //ar - 1, //total + 0, //total sec_status_secure); return msg; } From afc73e28d8d601354c516a4cddaf66debe7a1e7d Mon Sep 17 00:00:00 2001 From: mb Date: Tue, 24 Nov 2020 12:02:59 +0100 Subject: [PATCH 17/68] rpz: fix forged messages --- iterator/iterator.c | 5 +++-- services/rpz.c | 6 ++++-- testdata/rpz_nsip.rpl | 4 ++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/iterator/iterator.c b/iterator/iterator.c index 6655ec875..85c0b29de 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -2474,9 +2474,10 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, { /* apply rpz triggers at query time */ struct dns_msg* forged_response = rpz_iterator_module_callback(qstate, iq); if(forged_response != NULL) { - iq->response = forged_response; - next_state(iq, FINISHED_STATE); qstate->ext_state[id] = module_finished; + qstate->return_rcode = forged_response->rep->flags; + qstate->return_msg = forged_response; + next_state(iq, FINISHED_STATE); return 0; } } diff --git a/services/rpz.c b/services/rpz.c index fb219ab09..f39c5297c 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -1440,7 +1440,7 @@ rpz_patch_nodata(struct rpz* r, struct module_qstate* ms) if(msg == NULL) { return msg; } msg->qinfo = ms->qinfo; msg->rep = construct_reply_info_base(ms->region, - LDNS_RCODE_NOERROR|BIT_QR|BIT_AA|BIT_RA, + BIT_RD|BIT_QR|BIT_AA|BIT_RA, 1, //qd 0, //ttl 0, //prettl @@ -1450,6 +1450,7 @@ rpz_patch_nodata(struct rpz* r, struct module_qstate* ms) 0, //ar 0, //total sec_status_secure); + FLAGS_SET_RCODE(msg->rep->flags, LDNS_RCODE_NOERROR); return msg; } @@ -1460,7 +1461,7 @@ rpz_patch_nxdomain(struct rpz* r, struct module_qstate* ms) if(msg == NULL) { return msg; } msg->qinfo = ms->qinfo; msg->rep = construct_reply_info_base(ms->region, - LDNS_RCODE_NXDOMAIN|BIT_QR|BIT_AA|BIT_RA, + BIT_RD|BIT_QR|BIT_AA|BIT_RA, 1, //qd 0, //ttl 0, //prettl @@ -1470,6 +1471,7 @@ rpz_patch_nxdomain(struct rpz* r, struct module_qstate* ms) 0, //ar 0, //total sec_status_secure); + FLAGS_SET_RCODE(msg->rep->flags, LDNS_RCODE_NXDOMAIN); return msg; } diff --git a/testdata/rpz_nsip.rpl b/testdata/rpz_nsip.rpl index ac9e80b80..215ee3a28 100644 --- a/testdata/rpz_nsip.rpl +++ b/testdata/rpz_nsip.rpl @@ -346,7 +346,7 @@ ENTRY_END STEP 11 CHECK_ANSWER ENTRY_BEGIN MATCH all -REPLY QR RD RA NXDOMAIN +REPLY QR AA RD RA NXDOMAIN SECTION QUESTION gotham.aa. IN A SECTION ANSWER @@ -362,7 +362,7 @@ ENTRY_END STEP 21 CHECK_ANSWER ENTRY_BEGIN MATCH all -REPLY QR RD RA NOERROR +REPLY QR AA RD RA NOERROR SECTION QUESTION gotham.bb. IN A SECTION ANSWER From 7acf1a5088cb8ebc9d02a815918352b4961ed04f Mon Sep 17 00:00:00 2001 From: mb Date: Tue, 24 Nov 2020 16:29:15 +0100 Subject: [PATCH 18/68] rpz: fix forged response --- iterator/iterator.c | 2 +- services/rpz.c | 40 ++++++++++++++++++++++++++-------------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/iterator/iterator.c b/iterator/iterator.c index 85c0b29de..831d90761 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -2475,7 +2475,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, struct dns_msg* forged_response = rpz_iterator_module_callback(qstate, iq); if(forged_response != NULL) { qstate->ext_state[id] = module_finished; - qstate->return_rcode = forged_response->rep->flags; + qstate->return_rcode = FLAGS_GET_RCODE(forged_response->rep->flags); qstate->return_msg = forged_response; next_state(iq, FINISHED_STATE); return 0; diff --git a/services/rpz.c b/services/rpz.c index f39c5297c..a089f4fa4 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -1440,7 +1440,7 @@ rpz_patch_nodata(struct rpz* r, struct module_qstate* ms) if(msg == NULL) { return msg; } msg->qinfo = ms->qinfo; msg->rep = construct_reply_info_base(ms->region, - BIT_RD|BIT_QR|BIT_AA|BIT_RA, + BIT_RD | BIT_QR | BIT_AA | BIT_RA, 1, //qd 0, //ttl 0, //prettl @@ -1461,7 +1461,7 @@ rpz_patch_nxdomain(struct rpz* r, struct module_qstate* ms) if(msg == NULL) { return msg; } msg->qinfo = ms->qinfo; msg->rep = construct_reply_info_base(ms->region, - BIT_RD|BIT_QR|BIT_AA|BIT_RA, + BIT_RD | BIT_QR | BIT_AA | BIT_RA, 1, //qd 0, //ttl 0, //prettl @@ -1481,22 +1481,32 @@ rpz_patch_localdata(struct rpz* r, struct clientip_synthesized_rr* data) { struct dns_msg* msg = NULL; - struct query_info* qi = &msg->qinfo; + struct query_info* qi = &ms->qinfo; struct ub_packed_rrset_key* rp; struct local_rrset* rrset; struct reply_info* new_reply_info; - struct reply_info* ri = msg->rep; rrset = rpz_find_synthesized_rrset(qi->qtype, data); if(rrset == NULL) { verbose(VERB_ALGO, "rpz: nsip: no matching synthesized data found; resorting to nodata"); return rpz_patch_nodata(r, ms); } + msg = rpz_dns_msg_new(ms->region); if(msg == NULL) { return NULL; } // XXX: use ttl etc from rpz zone? - new_reply_info = make_new_reply_info(ri, ms->region, 0, 0); + new_reply_info = construct_reply_info_base(ms->region, + LDNS_RCODE_NOERROR | BIT_RD | BIT_QR | BIT_AA | BIT_RA, + 1, //qd + 0, //ttl + 0, //prettl + 0, //expttl + 1, //an + 0, //ns + 0, //ar + 1, //total + sec_status_secure); if(new_reply_info == NULL) { log_err("out of memory"); return NULL; @@ -1506,15 +1516,13 @@ rpz_patch_localdata(struct rpz* r, log_err("out of memory"); return NULL; } - new_reply_info->rrsets = regional_alloc(ms->region, sizeof(*new_reply_info->rrsets)); - if(new_reply_info->rrsets == NULL) { - log_err("out of memory"); - return NULL; - } + //new_reply_info->rrsets = regional_alloc(ms->region, sizeof(*new_reply_info->rrsets)); + //if(new_reply_info->rrsets == NULL) { + // log_err("out of memory"); + // return NULL; + //} rp->rk.dname = qi->qname; rp->rk.dname_len = qi->qname_len; - new_reply_info->rrset_count = 1; - new_reply_info->an_numrrsets = 1; new_reply_info->rrsets[0] = rp; msg->rep = new_reply_info; return msg; @@ -1590,12 +1598,16 @@ rpz_iterator_module_callback(struct module_qstate* ms, struct iter_qstate* is) verbose(VERB_ALGO, "rpz: nsip: tcp-only trigger ignored"); ret = NULL; break; - case RPZ_PASSTHRU_ACTION: - ret = NULL; + case RPZ_DROP_ACTION: + ret = rpz_patch_nodata(r, ms); + ms->is_drop = 1; break; case RPZ_LOCAL_DATA_ACTION: ret = rpz_patch_localdata(r, ms, raddr); break; + case RPZ_PASSTHRU_ACTION: + ret = NULL; + break; default: verbose(VERB_ALGO, "rpz: nsip: bug: unhandled or invalid action: '%s'", rpz_action_to_string(action)); From d6a69d77e219e64ad4ff8508ab6f0d809ce70f00 Mon Sep 17 00:00:00 2001 From: mb Date: Wed, 25 Nov 2020 11:45:33 +0100 Subject: [PATCH 19/68] rpz: minor cleanups --- services/rpz.c | 106 ++++++++++++++++++++++++------------------ testdata/rpz_nsip.rpl | 4 +- 2 files changed, 64 insertions(+), 46 deletions(-) diff --git a/services/rpz.c b/services/rpz.c index a089f4fa4..06327ea03 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -1237,7 +1237,8 @@ log_rpz_apply(uint8_t* dname, enum rpz_action a, struct query_info* qinfo, } static struct clientip_synthesized_rr* -rpz_ipbased_trigger_lookup(struct clientip_synthesized_rrset* set, struct sockaddr_storage* addr, socklen_t addrlen) +rpz_ipbased_trigger_lookup(struct clientip_synthesized_rrset* set, + struct sockaddr_storage* addr, socklen_t addrlen) { struct clientip_synthesized_rr* raddr = NULL; enum rpz_action action = RPZ_INVALID_ACTION; @@ -1261,11 +1262,15 @@ rpz_ipbased_trigger_lookup(struct clientip_synthesized_rrset* set, struct sockad static inline struct clientip_synthesized_rr* -rpz_resolve_client_action_and_zone(struct auth_zones* az, struct query_info* qinfo, - struct comm_reply* repinfo, uint8_t* taglist, size_t taglen, - struct ub_server_stats* stats, - /* output parameters */ - struct local_zone** z_out, struct auth_zone** a_out, struct rpz** r_out ) +rpz_resolve_client_action_and_zone(struct auth_zones* az, + struct query_info* qinfo, + struct comm_reply* repinfo, + uint8_t* taglist, size_t taglen, + struct ub_server_stats* stats, + /* output parameters */ + struct local_zone** z_out, + struct auth_zone** a_out, + struct rpz** r_out ) { struct clientip_synthesized_rr* node = NULL; struct auth_zone* a = NULL; @@ -1327,10 +1332,15 @@ rpz_is_udp_query(struct comm_reply* repinfo) { /** encode answer consisting of 1 rrset */ static int -rpz_local_encode(struct query_info* qinfo, struct module_env* env, - struct edns_data* edns, struct comm_reply* repinfo, sldns_buffer* buf, - struct regional* temp, struct ub_packed_rrset_key* rrset, int ansec, - int rcode) +rpz_local_encode(struct query_info* qinfo, + struct module_env* env, + struct edns_data* edns, + struct comm_reply* repinfo, + sldns_buffer* buf, + struct regional* temp, + struct ub_packed_rrset_key* rrset, + int ansec, + int rcode) { struct reply_info rep; uint16_t udpsize; @@ -1375,9 +1385,14 @@ rpz_find_synthesized_rrset(int qtype, struct clientip_synthesized_rr* data) { } static void -rpz_apply_clientip_localdata_action(struct rpz* r, struct clientip_synthesized_rr* raddr, - struct module_env* env, struct query_info* qinfo, struct edns_data* edns, - struct comm_reply* repinfo, sldns_buffer* buf, struct regional* temp) +rpz_apply_clientip_localdata_action(struct rpz* r, + struct clientip_synthesized_rr* raddr, + struct module_env* env, + struct query_info* qinfo, + struct edns_data* edns, + struct comm_reply* repinfo, + sldns_buffer* buf, + struct regional* temp) { struct local_rrset* rrset; enum rpz_action action = RPZ_INVALID_ACTION; @@ -1411,9 +1426,6 @@ rpz_apply_clientip_localdata_action(struct rpz* r, struct clientip_synthesized_r return; } - //struct packed_rrset_data* pd = raddr->data->entry.data; - //struct packed_rrset_data* pd2 = rp->entry.data; - //verbose(VERB_ALGO, "ttl=%ld ttl=%ld", pd->rr_ttl[0], pd2->rr_ttl[0]); rp->rk.flags |= PACKED_RRSET_FIXEDTTL; rp->rk.dname = qinfo->qname; rp->rk.dname_len = qinfo->qname_len; @@ -1434,13 +1446,13 @@ rpz_dns_msg_new(struct regional* region) } static inline struct dns_msg* -rpz_patch_nodata(struct rpz* r, struct module_qstate* ms) +rpz_forge_nodata(struct rpz* r, struct module_qstate* ms) { struct dns_msg* msg = rpz_dns_msg_new(ms->region); if(msg == NULL) { return msg; } msg->qinfo = ms->qinfo; msg->rep = construct_reply_info_base(ms->region, - BIT_RD | BIT_QR | BIT_AA | BIT_RA, + LDNS_RCODE_NOERROR | BIT_RD | BIT_QR | BIT_AA | BIT_RA, 1, //qd 0, //ttl 0, //prettl @@ -1450,18 +1462,17 @@ rpz_patch_nodata(struct rpz* r, struct module_qstate* ms) 0, //ar 0, //total sec_status_secure); - FLAGS_SET_RCODE(msg->rep->flags, LDNS_RCODE_NOERROR); return msg; } static inline struct dns_msg* -rpz_patch_nxdomain(struct rpz* r, struct module_qstate* ms) +rpz_forge_nxdomain(struct rpz* r, struct module_qstate* ms) { struct dns_msg* msg = rpz_dns_msg_new(ms->region); if(msg == NULL) { return msg; } msg->qinfo = ms->qinfo; msg->rep = construct_reply_info_base(ms->region, - BIT_RD | BIT_QR | BIT_AA | BIT_RA, + LDNS_RCODE_NXDOMAIN | BIT_RD | BIT_QR | BIT_AA | BIT_RA, 1, //qd 0, //ttl 0, //prettl @@ -1471,12 +1482,11 @@ rpz_patch_nxdomain(struct rpz* r, struct module_qstate* ms) 0, //ar 0, //total sec_status_secure); - FLAGS_SET_RCODE(msg->rep->flags, LDNS_RCODE_NXDOMAIN); return msg; } static inline struct dns_msg* -rpz_patch_localdata(struct rpz* r, +rpz_forge_localdata(struct rpz* r, struct module_qstate* ms, struct clientip_synthesized_rr* data) { @@ -1489,7 +1499,7 @@ rpz_patch_localdata(struct rpz* r, rrset = rpz_find_synthesized_rrset(qi->qtype, data); if(rrset == NULL) { verbose(VERB_ALGO, "rpz: nsip: no matching synthesized data found; resorting to nodata"); - return rpz_patch_nodata(r, ms); + return rpz_forge_nodata(r, ms); } msg = rpz_dns_msg_new(ms->region); @@ -1516,11 +1526,6 @@ rpz_patch_localdata(struct rpz* r, log_err("out of memory"); return NULL; } - //new_reply_info->rrsets = regional_alloc(ms->region, sizeof(*new_reply_info->rrsets)); - //if(new_reply_info->rrsets == NULL) { - // log_err("out of memory"); - // return NULL; - //} rp->rk.dname = qi->qname; rp->rk.dname_len = qi->qname_len; new_reply_info->rrsets[0] = rp; @@ -1583,27 +1588,27 @@ rpz_iterator_module_callback(struct module_qstate* ms, struct iter_qstate* is) action = raddr->action; if(action == RPZ_LOCAL_DATA_ACTION && raddr->data == NULL) { verbose(VERB_ALGO, "rpz: bug: local-data action but no local data"); - ret = rpz_patch_nodata(r, ms); + ret = rpz_forge_nodata(r, ms); goto done; } switch(action) { case RPZ_NXDOMAIN_ACTION: - ret = rpz_patch_nxdomain(r, ms); + ret = rpz_forge_nxdomain(r, ms); break; case RPZ_NODATA_ACTION: - ret = rpz_patch_nodata(r, ms); + ret = rpz_forge_nodata(r, ms); break; case RPZ_TCP_ONLY_ACTION: verbose(VERB_ALGO, "rpz: nsip: tcp-only trigger ignored"); ret = NULL; break; case RPZ_DROP_ACTION: - ret = rpz_patch_nodata(r, ms); + ret = rpz_forge_nodata(r, ms); ms->is_drop = 1; break; case RPZ_LOCAL_DATA_ACTION: - ret = rpz_patch_localdata(r, ms, raddr); + ret = rpz_forge_localdata(r, ms, raddr); break; case RPZ_PASSTHRU_ACTION: ret = NULL; @@ -1620,12 +1625,19 @@ done: } static int -rpz_maybe_apply_clientip_trigger(struct auth_zones* az, struct module_env* env, - struct query_info* qinfo, struct edns_data* edns, - struct comm_reply* repinfo, uint8_t* taglist, size_t taglen, - struct ub_server_stats* stats,sldns_buffer* buf, struct regional* temp, - /* output parameters */ - struct local_zone** z_out, struct auth_zone** a_out, struct rpz** r_out) +rpz_maybe_apply_clientip_trigger(struct auth_zones* az, + struct module_env* env, + struct query_info* qinfo, + struct edns_data* edns, + struct comm_reply* repinfo, + uint8_t* taglist, size_t taglen, + struct ub_server_stats* stats, + sldns_buffer* buf, + struct regional* temp, + /* output parameters */ + struct local_zone** z_out, + struct auth_zone** a_out, + struct rpz** r_out) { int ret = 0; enum rpz_action client_action; @@ -1669,10 +1681,16 @@ done: } int -rpz_apply_qname_trigger(struct auth_zones* az, struct module_env* env, - struct query_info* qinfo, struct edns_data* edns, sldns_buffer* buf, - struct regional* temp, struct comm_reply* repinfo, - uint8_t* taglist, size_t taglen, struct ub_server_stats* stats) +rpz_apply_qname_trigger(struct auth_zones* az, + struct module_env* env, + struct query_info* qinfo, + struct edns_data* edns, + sldns_buffer* buf, + struct regional* temp, + struct comm_reply* repinfo, + uint8_t* taglist, + size_t taglen, + struct ub_server_stats* stats) { struct rpz* r = NULL; struct auth_zone* a = NULL; diff --git a/testdata/rpz_nsip.rpl b/testdata/rpz_nsip.rpl index 215ee3a28..ac9e80b80 100644 --- a/testdata/rpz_nsip.rpl +++ b/testdata/rpz_nsip.rpl @@ -346,7 +346,7 @@ ENTRY_END STEP 11 CHECK_ANSWER ENTRY_BEGIN MATCH all -REPLY QR AA RD RA NXDOMAIN +REPLY QR RD RA NXDOMAIN SECTION QUESTION gotham.aa. IN A SECTION ANSWER @@ -362,7 +362,7 @@ ENTRY_END STEP 21 CHECK_ANSWER ENTRY_BEGIN MATCH all -REPLY QR AA RD RA NOERROR +REPLY QR RD RA NOERROR SECTION QUESTION gotham.bb. IN A SECTION ANSWER From f78aa90ff1a296e1a09581b440d34407cfe5ff89 Mon Sep 17 00:00:00 2001 From: mb Date: Thu, 26 Nov 2020 11:33:49 +0100 Subject: [PATCH 20/68] rpz: nsdname stubs --- daemon/worker.c | 2 +- iterator/iterator.c | 2 +- services/rpz.c | 356 +++++++++++++++++++++++++++----------------- services/rpz.h | 5 +- 4 files changed, 224 insertions(+), 141 deletions(-) diff --git a/daemon/worker.c b/daemon/worker.c index 458afa04e..7d8a086e4 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -1373,7 +1373,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error, goto send_reply; } if(worker->env.auth_zones && - rpz_apply_qname_trigger(worker->env.auth_zones, + rpz_callback_from_worker_request(worker->env.auth_zones, &worker->env, &qinfo, &edns, c->buffer, worker->scratchpad, repinfo, acladdr->taglist, acladdr->taglen, &worker->stats)) { regional_free_all(worker->scratchpad); diff --git a/iterator/iterator.c b/iterator/iterator.c index 831d90761..1ebbfe277 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -2472,7 +2472,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, delegpt_add_unused_targets(iq->dp); { /* apply rpz triggers at query time */ - struct dns_msg* forged_response = rpz_iterator_module_callback(qstate, iq); + struct dns_msg* forged_response = rpz_callback_from_iterator_module(qstate, iq); if(forged_response != NULL) { qstate->ext_state[id] = module_finished; qstate->return_rcode = FLAGS_GET_RCODE(forged_response->rep->flags); diff --git a/services/rpz.c b/services/rpz.c index 06327ea03..45064b388 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -350,6 +350,7 @@ rpz_delete(struct rpz* r) if(!r) return; local_zones_delete(r->local_zones); + local_zones_delete(r->nsdname_zones); respip_set_delete(r->respip_set); rpz_clientip_synthesized_set_delete(r->client_set); rpz_clientip_synthesized_set_delete(r->ns_set); @@ -364,12 +365,18 @@ rpz_clear(struct rpz* r) { /* must hold write lock on auth_zone */ local_zones_delete(r->local_zones); + local_zones_delete(r->nsdname_zones); respip_set_delete(r->respip_set); rpz_clientip_synthesized_set_delete(r->client_set); rpz_clientip_synthesized_set_delete(r->ns_set); + // XXX: this approach seems to leak memory if(!(r->local_zones = local_zones_create())){ return 0; } + r->nsdname_zones = local_zones_create(); + if(r->nsdname_zones == NULL) { + return 0; + } if(!(r->respip_set = respip_set_create())) { return 0; } @@ -459,6 +466,12 @@ rpz_create(struct config_auth* p) if(!(r->local_zones = local_zones_create())){ goto err; } + + r->nsdname_zones = local_zones_create(); + if(r->local_zones == NULL){ + goto err; + } + if(!(r->respip_set = respip_set_create())) { goto err; } @@ -513,6 +526,8 @@ err: if(r) { if(r->local_zones) local_zones_delete(r->local_zones); + if(r->nsdname_zones) + local_zones_delete(r->nsdname_zones); if(r->respip_set) respip_set_delete(r->respip_set); if(r->client_set != NULL) @@ -547,77 +562,99 @@ strip_dname_origin(uint8_t* dname, size_t dnamelen, size_t originlen, return newdnamelen + 1; /* + 1 for root label */ } -/** Insert RR into RPZ's local-zone */ static void -rpz_insert_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, - enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl, - uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len) +rpz_insert_local_zones_trigger(struct local_zones* lz, uint8_t* dname, + size_t dnamelen, enum rpz_action a, uint16_t rrtype, uint16_t rrclass, + uint32_t ttl, uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len) { struct local_zone* z; enum localzone_type tp = local_zone_always_transparent; int dnamelabs = dname_count_labels(dname); - char* rrstr; + int newzone = 0; - verbose(VERB_ALGO, "rpz: insert qname trigger: %s", rpz_action_to_string(a)); - - if(a == RPZ_INVALID_ACTION) { - verbose(VERB_ALGO, "rpz: skipping unsupported action: %s", - rpz_action_to_string(a)); - free(dname); - return; - } - - lock_rw_wrlock(&r->local_zones->lock); + lock_rw_wrlock(&lz->lock); /* exact match */ - z = local_zones_find(r->local_zones, dname, dnamelen, dnamelabs, - LDNS_RR_CLASS_IN); - if(z && a != RPZ_LOCAL_DATA_ACTION) { - rrstr = sldns_wire2str_rr(rr, rr_len); - if(!rrstr) { - log_err("malloc error while inserting RPZ qname " - "trigger"); + z = local_zones_find(lz, dname, dnamelen, dnamelabs, LDNS_RR_CLASS_IN); + if(z != NULL && a != RPZ_LOCAL_DATA_ACTION) { + char* rrstr = sldns_wire2str_rr(rr, rr_len); + if(rrstr == NULL) { + log_err("malloc error while inserting rpz nsdname trigger"); free(dname); - lock_rw_unlock(&r->local_zones->lock); + lock_rw_unlock(&lz->lock); return; } - verbose(VERB_ALGO, "rpz: skipping duplicate record: '%s'", - rrstr); + verbose(VERB_ALGO, "rpz: skipping duplicate record: '%s'", rrstr); free(rrstr); free(dname); - lock_rw_unlock(&r->local_zones->lock); + lock_rw_unlock(&lz->lock); return; } - if(!z) { + if(z == NULL) { tp = rpz_action_to_localzone_type(a); - if(!(z = local_zones_add_zone(r->local_zones, dname, dnamelen, - dnamelabs, rrclass, tp))) { + z = local_zones_add_zone(lz, dname, dnamelen, + dnamelabs, rrclass, tp); + if(z == NULL) { log_warn("rpz: create failed"); - lock_rw_unlock(&r->local_zones->lock); + lock_rw_unlock(&lz->lock); /* dname will be free'd in failed local_zone_create() */ return; } newzone = 1; } if(a == RPZ_LOCAL_DATA_ACTION) { - rrstr = sldns_wire2str_rr(rr, rr_len); - if(!rrstr) { - log_err("malloc error while inserting RPZ qname " - "trigger"); + char* rrstr = sldns_wire2str_rr(rr, rr_len); + if(rrstr == NULL) { + log_err("malloc error while inserting rpz nsdname trigger"); free(dname); - lock_rw_unlock(&r->local_zones->lock); + lock_rw_unlock(&lz->lock); return; } lock_rw_wrlock(&z->lock); - local_zone_enter_rr(z, dname, dnamelen, dnamelabs, - rrtype, rrclass, ttl, rdata, rdata_len, rrstr); + local_zone_enter_rr(z, dname, dnamelen, dnamelabs, rrtype, + rrclass, ttl, rdata, rdata_len, rrstr); lock_rw_unlock(&z->lock); free(rrstr); } - if(!newzone) + if(!newzone) { free(dname); - lock_rw_unlock(&r->local_zones->lock); - return; + } + lock_rw_unlock(&lz->lock); +} + +/** Insert RR into RPZ's local-zone */ +static void +rpz_insert_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, + enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl, + uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len) +{ + verbose(VERB_ALGO, "rpz: insert qname trigger: %s", rpz_action_to_string(a)); + + if(a == RPZ_INVALID_ACTION) { + verbose(VERB_ALGO, "rpz: skipping invalid action"); + free(dname); + return; + } + + rpz_insert_local_zones_trigger(r->local_zones, dname, dnamelen, a, rrtype, + rrclass, ttl, rdata, rdata_len, rr, rr_len); +} + +static void +rpz_insert_nsdname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, + enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl, + uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len) +{ + verbose(VERB_ALGO, "rpz: insert nsdname trigger: %s", rpz_action_to_string(a)); + + if(a == RPZ_INVALID_ACTION) { + verbose(VERB_ALGO, "rpz: skipping invalid action"); + free(dname); + return; + } + + rpz_insert_local_zones_trigger(r->nsdname_zones, dname, dnamelen, a, rrtype, + rrclass, ttl, rdata, rdata_len, rr, rr_len); } static int @@ -631,14 +668,16 @@ rpz_insert_ipaddr_based_trigger(struct respip_set* set, struct sockaddr_storage* enum respip_action respa = rpz_action_to_respip_action(a); lock_rw_wrlock(&set->lock); + rrstr = sldns_wire2str_rr(rr, rr_len); - if(!rrstr) { - log_err("malloc error while inserting RPZ ipaddr based trigger"); + if(rrstr == NULL) { + log_err("malloc error while inserting rpz ipaddr based trigger"); lock_rw_unlock(&set->lock); return 0; } - if(!(node=respip_sockaddr_find_or_create(set, addr, addrlen, - net, 1, rrstr))) { + + node = respip_sockaddr_find_or_create(set, addr, addrlen, net, 1, rrstr); + if(node == NULL) { lock_rw_unlock(&set->lock); free(rrstr); return 0; @@ -646,12 +685,14 @@ rpz_insert_ipaddr_based_trigger(struct respip_set* set, struct sockaddr_storage* lock_rw_wrlock(&node->lock); lock_rw_unlock(&set->lock); + node->action = respa; if(a == RPZ_LOCAL_DATA_ACTION) { respip_enter_rr(set->region, node, rrtype, - rrclass, ttl, rdata, rdata_len, rrstr, ""); + rrclass, ttl, rdata, rdata_len, rrstr, ""); } + lock_rw_unlock(&node->lock); free(rrstr); return 1; @@ -693,7 +734,7 @@ static void rpz_report_rrset_error(const char* msg, uint8_t* rr, size_t rr_len) { char* rrstr = sldns_wire2str_rr(rr, rr_len); if(rrstr == NULL) { - log_err("malloc error while inserting RPZ clientip based record"); + log_err("malloc error while inserting rpz clientip based record"); return; } log_err("rpz: unexpected: unable to insert %s: %s", msg, rrstr); @@ -759,7 +800,7 @@ rpz_clientip_enter_rr(struct regional* region, struct clientip_synthesized_rr* r return 0; } - return rrset_insert_rr(region, rrset->rrset->entry.data, rdata, rdata_len, ttl, "fixme"); + return rrset_insert_rr(region, rrset->rrset->entry.data, rdata, rdata_len, ttl, ""); } static int @@ -914,6 +955,7 @@ rpz_insert_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, uint8_t* dname, return 1; } if(t == RPZ_QNAME_TRIGGER) { + // policydname will be consumed, no free rpz_insert_qname_trigger(r, policydname, policydnamelen, a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr, rr_len); @@ -932,6 +974,11 @@ rpz_insert_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, uint8_t* dname, a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr, rr_len); free(policydname); + } else if(t == RPZ_NSDNAME_TRIGGER) { + // policydname will be consumed, no free + rpz_insert_nsdname_trigger(r, policydname, policydnamelen, + a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr, + rr_len); } else { free(policydname); verbose(VERB_ALGO, "rpz: skipping unsupported trigger: %s", @@ -953,7 +1000,7 @@ rpz_insert_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, uint8_t* dname, * @return: NULL or local-zone holding rd or wr lock */ static struct local_zone* -rpz_find_zone(struct rpz* r, uint8_t* qname, size_t qname_len, uint16_t qclass, +rpz_find_zone(struct local_zones* zones, uint8_t* qname, size_t qname_len, uint16_t qclass, int only_exact, int wr, int zones_keep_lock) { uint8_t* ce; @@ -961,16 +1008,17 @@ rpz_find_zone(struct rpz* r, uint8_t* qname, size_t qname_len, uint16_t qclass, uint8_t wc[LDNS_MAX_DOMAINLEN+1]; int exact; struct local_zone* z = NULL; + if(wr) { - lock_rw_wrlock(&r->local_zones->lock); + lock_rw_wrlock(&zones->lock); } else { - lock_rw_rdlock(&r->local_zones->lock); + lock_rw_rdlock(&zones->lock); } - z = local_zones_find_le(r->local_zones, qname, qname_len, + z = local_zones_find_le(zones, qname, qname_len, dname_count_labels(qname), LDNS_RR_CLASS_IN, &exact); if(!z || (only_exact && !exact)) { - lock_rw_unlock(&r->local_zones->lock); + lock_rw_unlock(&zones->lock); return NULL; } if(wr) { @@ -979,7 +1027,7 @@ rpz_find_zone(struct rpz* r, uint8_t* qname, size_t qname_len, uint16_t qclass, lock_rw_rdlock(&z->lock); } if(!zones_keep_lock) { - lock_rw_unlock(&r->local_zones->lock); + lock_rw_unlock(&zones->lock); } if(exact) @@ -993,7 +1041,7 @@ rpz_find_zone(struct rpz* r, uint8_t* qname, size_t qname_len, uint16_t qclass, if(!ce /* should not happen */ || !*ce /* root */) { lock_rw_unlock(&z->lock); if(zones_keep_lock) { - lock_rw_unlock(&r->local_zones->lock); + lock_rw_unlock(&zones->lock); } return NULL; } @@ -1001,7 +1049,7 @@ rpz_find_zone(struct rpz* r, uint8_t* qname, size_t qname_len, uint16_t qclass, if(ce_len+2 > sizeof(wc)) { lock_rw_unlock(&z->lock); if(zones_keep_lock) { - lock_rw_unlock(&r->local_zones->lock); + lock_rw_unlock(&zones->lock); } return NULL; } @@ -1012,15 +1060,15 @@ rpz_find_zone(struct rpz* r, uint8_t* qname, size_t qname_len, uint16_t qclass, if(!zones_keep_lock) { if(wr) { - lock_rw_wrlock(&r->local_zones->lock); + lock_rw_wrlock(&zones->lock); } else { - lock_rw_rdlock(&r->local_zones->lock); + lock_rw_rdlock(&zones->lock); } } - z = local_zones_find_le(r->local_zones, wc, + z = local_zones_find_le(zones, wc, ce_len+2, ce_labs+1, qclass, &exact); if(!z || !exact) { - lock_rw_unlock(&r->local_zones->lock); + lock_rw_unlock(&zones->lock); return NULL; } if(wr) { @@ -1029,7 +1077,7 @@ rpz_find_zone(struct rpz* r, uint8_t* qname, size_t qname_len, uint16_t qclass, lock_rw_rdlock(&z->lock); } if(!zones_keep_lock) { - lock_rw_unlock(&r->local_zones->lock); + lock_rw_unlock(&zones->lock); } return z; } @@ -1124,7 +1172,7 @@ rpz_remove_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, { struct local_zone* z; int delete_zone = 1; - z = rpz_find_zone(r, dname, dnamelen, rr_class, + z = rpz_find_zone(r->local_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, " @@ -1262,15 +1310,11 @@ rpz_ipbased_trigger_lookup(struct clientip_synthesized_rrset* set, static inline struct clientip_synthesized_rr* -rpz_resolve_client_action_and_zone(struct auth_zones* az, - struct query_info* qinfo, - struct comm_reply* repinfo, - uint8_t* taglist, size_t taglen, - struct ub_server_stats* stats, - /* output parameters */ - struct local_zone** z_out, - struct auth_zone** a_out, - struct rpz** r_out ) +rpz_resolve_client_action_and_zone(struct auth_zones* az, struct query_info* qinfo, + struct comm_reply* repinfo, uint8_t* taglist, size_t taglen, + struct ub_server_stats* stats, + /* output parameters */ + struct local_zone** z_out, struct auth_zone** a_out, struct rpz** r_out) { struct clientip_synthesized_rr* node = NULL; struct auth_zone* a = NULL; @@ -1287,7 +1331,7 @@ rpz_resolve_client_action_and_zone(struct auth_zones* az, lock_rw_unlock(&a->lock); continue; } - z = rpz_find_zone(r, qinfo->qname, qinfo->qname_len, + z = rpz_find_zone(r->local_zones, qinfo->qname, qinfo->qname_len, qinfo->qclass, 0, 0, 0); node = rpz_ipbased_trigger_lookup(r->client_set, &repinfo->addr, repinfo->addrlen); if(z && r->action_override == RPZ_DISABLED_ACTION) { @@ -1486,9 +1530,8 @@ rpz_forge_nxdomain(struct rpz* r, struct module_qstate* ms) } static inline struct dns_msg* -rpz_forge_localdata(struct rpz* r, - struct module_qstate* ms, - struct clientip_synthesized_rr* data) +rpz_forge_localdata(struct rpz* r, struct module_qstate* ms, + struct clientip_synthesized_rr* data) { struct dns_msg* msg = NULL; struct query_info* qi = &ms->qinfo; @@ -1534,8 +1577,7 @@ rpz_forge_localdata(struct rpz* r, } struct clientip_synthesized_rr* -rpz_delegation_point_ipbased_trigger_lookup(struct rpz* rpz, - struct iter_qstate* is) +rpz_delegation_point_ipbased_trigger_lookup(struct rpz* rpz, struct iter_qstate* is) { struct delegpt_addr* cursor; struct clientip_synthesized_rr* action = NULL; @@ -1549,43 +1591,14 @@ rpz_delegation_point_ipbased_trigger_lookup(struct rpz* rpz, } struct dns_msg* -rpz_iterator_module_callback(struct module_qstate* ms, struct iter_qstate* is) +rpz_apply_nsip_tigger(struct module_qstate* ms, struct rpz* r,struct clientip_synthesized_rr* raddr) { - struct auth_zones* az; - struct auth_zone* a; - struct clientip_synthesized_rr* raddr; - enum rpz_action action = RPZ_INVALID_ACTION; - struct rpz* r; + enum rpz_action action = raddr->action; struct dns_msg* ret = NULL; - if(ms->env == NULL || ms->env->auth_zones == NULL) { return 0; } - - az = ms->env->auth_zones; - - verbose(VERB_ALGO, "rpz: iterator module callback: have_rpz=%d", az->rpz_first != NULL); - - lock_rw_rdlock(&az->rpz_lock); - - raddr = NULL; - for(a = az->rpz_first; a != NULL; a = a->rpz_az_next) { - lock_rw_rdlock(&a->lock); - r = a->rpz; - raddr = rpz_delegation_point_ipbased_trigger_lookup(r, is); - if(raddr != NULL) { - lock_rw_unlock(&a->lock); - break; - } - lock_rw_unlock(&a->lock); - } - - lock_rw_unlock(&az->rpz_lock); - - if(raddr == NULL) { return NULL; } - verbose(VERB_ALGO, "rpz: iterator callback: nsip: apply action=%s", rpz_action_to_string(raddr->action)); - action = raddr->action; if(action == RPZ_LOCAL_DATA_ACTION && raddr->data == NULL) { verbose(VERB_ALGO, "rpz: bug: local-data action but no local data"); ret = rpz_forge_nodata(r, ms); @@ -1624,20 +1637,92 @@ done: return ret; } +struct dns_msg* +rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r, struct local_zone* z) +{ + struct dns_msg* ret = NULL; + + verbose(VERB_ALGO, "rpz: iterator callback: nsdname: apply action"); + + return ret; +} + +static struct local_zone* +rpz_delegation_point_zone_lookup(struct delegpt* dp, struct local_zones* zones, uint16_t qclass) +{ + struct delegpt_ns* nameserver; + struct local_zone* z = NULL; + + z = rpz_find_zone(zones, dp->name, dp->namelen, qclass, 0, 0, 0); + + if(z == NULL) { + for(nameserver = dp->nslist; + nameserver != NULL; + nameserver = nameserver->next) { + z = rpz_find_zone(zones, nameserver->name, nameserver->namelen, + qclass, 0, 0, 0); + if(z != NULL) { break; } + } + } + + verbose(VERB_ALGO, "rpz: delegation point zone found=%d", z != NULL); + + return z; +} + +struct dns_msg* +rpz_callback_from_iterator_module(struct module_qstate* ms, struct iter_qstate* is) +{ + struct auth_zones* az; + struct auth_zone* a; + struct clientip_synthesized_rr* raddr = NULL; + struct rpz* r; + struct local_zone* z = NULL; + + if(ms->env == NULL || ms->env->auth_zones == NULL) { return 0; } + + az = ms->env->auth_zones; + + verbose(VERB_ALGO, "rpz: iterator module callback: have_rpz=%d", az->rpz_first != NULL); + + lock_rw_rdlock(&az->rpz_lock); + + for(a = az->rpz_first; a != NULL; a = a->rpz_az_next) { + lock_rw_rdlock(&a->lock); + r = a->rpz; + + // XXX: check rfc which action has preference + + raddr = rpz_delegation_point_ipbased_trigger_lookup(r, is); + if(raddr != NULL) { + lock_rw_unlock(&a->lock); + break; + } + + z = rpz_delegation_point_zone_lookup(is->dp, r->nsdname_zones, + ms->qinfo.qclass); + if(z != NULL) { + lock_rw_unlock(&a->lock); + break; + } + lock_rw_unlock(&a->lock); + } + + lock_rw_unlock(&az->rpz_lock); + + if(raddr == NULL && z == NULL) { return NULL; } + else if(raddr != NULL) { return rpz_apply_nsip_tigger(ms, r, raddr); } + else if(z != NULL) { return rpz_apply_nsdname_trigger(ms, r, z); } + else { return NULL; } +} + static int -rpz_maybe_apply_clientip_trigger(struct auth_zones* az, - struct module_env* env, - struct query_info* qinfo, - struct edns_data* edns, - struct comm_reply* repinfo, - uint8_t* taglist, size_t taglen, - struct ub_server_stats* stats, - sldns_buffer* buf, - struct regional* temp, - /* output parameters */ - struct local_zone** z_out, - struct auth_zone** a_out, - struct rpz** r_out) +rpz_apply_maybe_clientip_trigger(struct auth_zones* az, struct module_env* env, + struct query_info* qinfo, struct edns_data* edns, struct comm_reply* repinfo, + uint8_t* taglist, size_t taglen, struct ub_server_stats* stats, + sldns_buffer* buf, struct regional* temp, + /* output parameters */ + struct local_zone** z_out, struct auth_zone** a_out, struct rpz** r_out) { int ret = 0; enum rpz_action client_action; @@ -1681,16 +1766,10 @@ done: } int -rpz_apply_qname_trigger(struct auth_zones* az, - struct module_env* env, - struct query_info* qinfo, - struct edns_data* edns, - sldns_buffer* buf, - struct regional* temp, - struct comm_reply* repinfo, - uint8_t* taglist, - size_t taglen, - struct ub_server_stats* stats) +rpz_callback_from_worker_request(struct auth_zones* az, struct module_env* env, + struct query_info* qinfo, struct edns_data* edns, sldns_buffer* buf, + struct regional* temp, struct comm_reply* repinfo, uint8_t* taglist, + size_t taglen, struct ub_server_stats* stats) { struct rpz* r = NULL; struct auth_zone* a = NULL; @@ -1699,7 +1778,7 @@ rpz_apply_qname_trigger(struct auth_zones* az, int ret; enum localzone_type lzt; - int clientip_trigger = rpz_maybe_apply_clientip_trigger(az, env, qinfo, + int clientip_trigger = rpz_apply_maybe_clientip_trigger(az, env, qinfo, edns, repinfo, taglist, taglen, stats, buf, temp, &z, &a, &r); if(clientip_trigger >= 0) { return clientip_trigger; } @@ -1736,9 +1815,10 @@ rpz_apply_qname_trigger(struct auth_zones* az, } qinfo->local_alias->rrset->rk.dname = qinfo->qname; qinfo->local_alias->rrset->rk.dname_len = qinfo->qname_len; - if(r->log) - log_rpz_apply(z->name, RPZ_CNAME_OVERRIDE_ACTION, - qinfo, repinfo, r->log_name); + if(r->log) { + log_rpz_apply(z->name, RPZ_CNAME_OVERRIDE_ACTION, + qinfo, repinfo, r->log_name); + } stats->rpz_action[RPZ_CNAME_OVERRIDE_ACTION]++; lock_rw_unlock(&z->lock); lock_rw_unlock(&a->lock); @@ -1748,10 +1828,11 @@ rpz_apply_qname_trigger(struct auth_zones* az, if(lzt == local_zone_redirect && local_data_answer(z, env, qinfo, edns, repinfo, buf, temp, dname_count_labels(qinfo->qname), &ld, lzt, -1, NULL, 0, NULL, 0)) { - if(r->log) + if(r->log) { log_rpz_apply(z->name, localzone_type_to_rpz_action(lzt), qinfo, repinfo, r->log_name); + } stats->rpz_action[localzone_type_to_rpz_action(lzt)]++; lock_rw_unlock(&z->lock); lock_rw_unlock(&a->lock); @@ -1760,9 +1841,10 @@ rpz_apply_qname_trigger(struct auth_zones* az, ret = local_zones_zone_answer(z, env, qinfo, edns, repinfo, buf, temp, 0 /* no local data used */, lzt); - if(r->log) + if(r->log) { log_rpz_apply(z->name, localzone_type_to_rpz_action(lzt), - qinfo, repinfo, r->log_name); + qinfo, repinfo, r->log_name); + } stats->rpz_action[localzone_type_to_rpz_action(lzt)]++; lock_rw_unlock(&z->lock); lock_rw_unlock(&a->lock); diff --git a/services/rpz.h b/services/rpz.h index 7a2e9500e..7924c2c3a 100644 --- a/services/rpz.h +++ b/services/rpz.h @@ -115,6 +115,7 @@ struct rpz { struct respip_set* respip_set; struct clientip_synthesized_rrset* client_set; struct clientip_synthesized_rrset* ns_set; + struct local_zones* nsdname_zones; uint8_t* taglist; size_t taglistlen; enum rpz_action action_override; @@ -173,13 +174,13 @@ void rpz_remove_rr(struct rpz* r, size_t aznamelen, uint8_t* dname, * @param stats: worker stats struct * @return: 1 if client answer is ready, 0 to continue resolving */ -int rpz_apply_qname_trigger(struct auth_zones* az, struct module_env* env, +int rpz_callback_from_worker_request(struct auth_zones* az, struct module_env* env, struct query_info* qinfo, struct edns_data* edns, sldns_buffer* buf, struct regional* temp, struct comm_reply* repinfo, uint8_t* taglist, size_t taglen, struct ub_server_stats* stats); struct iter_qstate; -struct dns_msg* rpz_iterator_module_callback(struct module_qstate*, struct iter_qstate*); +struct dns_msg* rpz_callback_from_iterator_module(struct module_qstate*, struct iter_qstate*); /** * Delete RPZ From caaa38f7c53972050649396289099d44e6a49235 Mon Sep 17 00:00:00 2001 From: mb Date: Thu, 26 Nov 2020 12:16:33 +0100 Subject: [PATCH 21/68] rpz: strip .rpz-nsdname suffix when inserting trigger --- services/rpz.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/services/rpz.c b/services/rpz.c index 45064b388..51018b8bd 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -622,7 +622,15 @@ rpz_insert_local_zones_trigger(struct local_zones* lz, uint8_t* dname, lock_rw_unlock(&lz->lock); } -/** Insert RR into RPZ's local-zone */ +static void +rpz_log_dname(char const* msg, uint8_t* dname, size_t dname_len) +{ + char buf[LDNS_MAX_DOMAINLEN+1]; + (void)dname_len; + dname_str(dname, buf); + verbose(VERB_ALGO, "rpz: %s: <%s>", msg, buf); +} + static void rpz_insert_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl, @@ -630,6 +638,8 @@ rpz_insert_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, { verbose(VERB_ALGO, "rpz: insert qname trigger: %s", rpz_action_to_string(a)); + rpz_log_dname("insert qname trigger", dname, dnamelen); + if(a == RPZ_INVALID_ACTION) { verbose(VERB_ALGO, "rpz: skipping invalid action"); free(dname); @@ -640,6 +650,14 @@ rpz_insert_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, rrclass, ttl, rdata, rdata_len, rr, rr_len); } +static void +rpz_strip_nsdname_suffix(uint8_t* dname, size_t maxdnamelen) +{ + uint8_t* stripped = get_tld_label(dname, maxdnamelen); + if(stripped == NULL) { return; } + *stripped = 0; +} + static void rpz_insert_nsdname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl, @@ -647,6 +665,10 @@ rpz_insert_nsdname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, { verbose(VERB_ALGO, "rpz: insert nsdname trigger: %s", rpz_action_to_string(a)); + rpz_log_dname("insert nsdname trigger", dname, dnamelen); + rpz_strip_nsdname_suffix(dname, dnamelen); + rpz_log_dname("insert nsdname trigger (stripped)", dname, dnamelen); + if(a == RPZ_INVALID_ACTION) { verbose(VERB_ALGO, "rpz: skipping invalid action"); free(dname); @@ -1653,12 +1675,14 @@ rpz_delegation_point_zone_lookup(struct delegpt* dp, struct local_zones* zones, struct delegpt_ns* nameserver; struct local_zone* z = NULL; + rpz_log_dname("delegation point", dp->name, dp->namelen); z = rpz_find_zone(zones, dp->name, dp->namelen, qclass, 0, 0, 0); if(z == NULL) { for(nameserver = dp->nslist; nameserver != NULL; nameserver = nameserver->next) { + rpz_log_dname("delegation point", nameserver->name, nameserver->namelen); z = rpz_find_zone(zones, nameserver->name, nameserver->namelen, qclass, 0, 0, 0); if(z != NULL) { break; } From 714d546d1d12e35ab013fe17380e944673055cdb Mon Sep 17 00:00:00 2001 From: mb Date: Thu, 26 Nov 2020 12:26:23 +0100 Subject: [PATCH 22/68] rpz: add testbound nsdname script (stub) --- services/rpz.c | 1 + testdata/rpz_nsdname.rpl | 388 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 389 insertions(+) create mode 100644 testdata/rpz_nsdname.rpl diff --git a/services/rpz.c b/services/rpz.c index 51018b8bd..646c9093e 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -1676,6 +1676,7 @@ rpz_delegation_point_zone_lookup(struct delegpt* dp, struct local_zones* zones, struct local_zone* z = NULL; rpz_log_dname("delegation point", dp->name, dp->namelen); + // XXX: do we want this? z = rpz_find_zone(zones, dp->name, dp->namelen, qclass, 0, 0, 0); if(z == NULL) { diff --git a/testdata/rpz_nsdname.rpl b/testdata/rpz_nsdname.rpl new file mode 100644 index 000000000..7587ccfe5 --- /dev/null +++ b/testdata/rpz_nsdname.rpl @@ -0,0 +1,388 @@ +; config options +server: + module-config: "respip validator iterator" + target-fetch-policy: "0 0 0 0 0" + qname-minimisation: no + access-control: 192.0.0.0/8 allow + +rpz: + name: "rpz.example.com." + zonefile: +TEMPFILE_NAME rpz.example.com +TEMPFILE_CONTENTS rpz.example.com +$ORIGIN example.com. +rpz 3600 IN SOA ns1.rpz.example.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. +gotham.aa.rpz-nsdname CNAME . +gotham.bb.rpz-nsdname CNAME *. +gotham.cc.rpz-nsdname CNAME rpz-drop. +gotham.com.rpz-nsdname CNAME rpz-passthru. +gotham.dd.rpz-nsdname CNAME rpz-tcp-only. +gotham.ee.rpz-nsdname A 127.0.0.1 +gotham.ff.rpz-nsdname TXT "42" +TEMPFILE_END + +stub-zone: + name: "." + stub-addr: 1.1.1.1 +CONFIG_END + +SCENARIO_BEGIN Test RPZ nsip triggers + +; . -------------------------------------------------------------------------- +RANGE_BEGIN 0 100 + ADDRESS 1.1.1.1 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS ns.root. +SECTION ADDITIONAL +ns.root IN A 1.1.1.1 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +com. IN A +SECTION AUTHORITY +com. IN NS ns1.com. +SECTION ADDITIONAL +ns1.com. IN A 8.8.8.8 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +aa. IN A +SECTION AUTHORITY +aa. IN NS ns1.aa. +SECTION ADDITIONAL +ns1.aa. IN A 8.8.0.8 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +bb. IN A +SECTION AUTHORITY +bb. IN NS ns1.bb. +SECTION ADDITIONAL +ns1.bb. IN A 8.8.1.8 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +cc. IN A +SECTION AUTHORITY +cc. IN NS ns1.cc. +SECTION ADDITIONAL +ns1.cc. IN A 8.8.2.8 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +dd. IN A +SECTION AUTHORITY +dd. IN NS ns1.dd. +SECTION ADDITIONAL +ns1.dd. IN A 8.8.3.8 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +ee. IN A +SECTION AUTHORITY +ee. IN NS ns1.ee. +SECTION ADDITIONAL +ns1.ee. IN A 8.8.5.8 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +ff. IN A +SECTION AUTHORITY +ff. IN NS ns1.ff. +SECTION ADDITIONAL +ns1.ff. IN A 8.8.6.8 +ENTRY_END + +RANGE_END + +; com. ----------------------------------------------------------------------- +RANGE_BEGIN 0 100 + ADDRESS 8.8.8.8 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +com. IN NS +SECTION ANSWER +com. IN NS ns1.com. +SECTION ADDITIONAL +ns1.com. IN A 8.8.8.8 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +gotham.com. IN A +SECTION AUTHORITY +gotham.com. IN NS ns1.gotham.com. +SECTION ADDITIONAL +ns1.gotham.com. IN A 192.0.6.1 +ENTRY_END + +RANGE_END + +; aa. ------------------------------------------------------------------------ +RANGE_BEGIN 0 100 + ADDRESS 8.8.0.8 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +aa. IN NS +SECTION ANSWER +aa. IN NS ns1.aa. +SECTION ADDITIONAL +ns1.aa. IN A 8.8.0.8 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +gotham.aa. IN A +SECTION AUTHORITY +gotham.aa. IN NS ns1.gotham.aa. +SECTION ADDITIONAL +ns1.gotham.aa. IN A 192.0.0.1 +ENTRY_END + +RANGE_END + +; bb. ------------------------------------------------------------------------ +RANGE_BEGIN 0 100 + ADDRESS 8.8.1.8 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +bb. IN NS +SECTION ANSWER +bb. IN NS ns1.bb. +SECTION ADDITIONAL +ns1.bb. IN A 8.8.1.8 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +gotham.bb. IN A +SECTION AUTHORITY +gotham.bb. IN NS ns1.gotham.bb. +SECTION ADDITIONAL +ns1.gotham.bb. IN A 192.0.1.1 +ENTRY_END + +RANGE_END + +; ff. ------------------------------------------------------------------------ +RANGE_BEGIN 0 100 + ADDRESS 8.8.6.8 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +ff. IN NS +SECTION ANSWER +ff. IN NS ns1.ff. +SECTION ADDITIONAL +ns1.ff. IN A 8.8.6.8 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +gotham.ff. IN A +SECTION AUTHORITY +gotham.ff. IN NS ns1.gotham.ff. +SECTION ADDITIONAL +ns1.gotham.ff. IN A 192.0.5.1 +ENTRY_END + +RANGE_END + +; ns1.gotham.com. ------------------------------------------------------------ +RANGE_BEGIN 0 100 + ADDRESS 192.0.6.1 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +gotham.com. IN A +SECTION ANSWER +gotham.com. IN A 192.0.6.2 +ENTRY_END + +RANGE_END + +; ns1.gotham.aa. ------------------------------------------------------------- +RANGE_BEGIN 0 100 + ADDRESS 192.0.0.1 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +gotham.aa. IN A +SECTION ANSWER +gotham.aa. IN A 192.0.0.2 +ENTRY_END + +RANGE_END + +; ns1.gotham.bb. ------------------------------------------------------------- +RANGE_BEGIN 0 100 + ADDRESS 192.0.1.1 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +gotham.bb. IN A +SECTION ANSWER +gotham.bb. IN A 192.0.1.2 +ENTRY_END + +RANGE_END + +; ns1.gotham.ff. ------------------------------------------------------------- +RANGE_BEGIN 0 100 + ADDRESS 192.0.5.1 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +gotham.ff. IN A +SECTION ANSWER +gotham.ff. IN A 192.0.5.2 +ENTRY_END + +RANGE_END + +; ---------------------------------------------------------------------------- + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +gotham.com. IN A +ENTRY_END + +STEP 2 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +gotham.com. IN A +SECTION ANSWER +gotham.com. IN A 192.0.6.2 +ENTRY_END + +STEP 10 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +gotham.aa. IN A +ENTRY_END + +STEP 11 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NXDOMAIN +SECTION QUESTION +gotham.aa. IN A +SECTION ANSWER +ENTRY_END + +STEP 20 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +gotham.bb. IN A +ENTRY_END + +STEP 21 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +gotham.bb. IN A +SECTION ANSWER +ENTRY_END + +STEP 30 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +gotham.ff. IN A +ENTRY_END + +STEP 31 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +gotham.ff. IN A +SECTION ANSWER +gotham.ff. IN A 127.0.0.1 +ENTRY_END + +SCENARIO_END From eba7e1a7c70fc20e0995de58815fcc0bffe07db6 Mon Sep 17 00:00:00 2001 From: mb Date: Fri, 27 Nov 2020 10:09:54 +0100 Subject: [PATCH 23/68] rpz: nsdname actions --- services/mesh.c | 2 +- services/rpz.c | 327 ++++++++++++++++++++++++++---------------------- 2 files changed, 179 insertions(+), 150 deletions(-) diff --git a/services/mesh.c b/services/mesh.c index ec2e940a4..b639695fc 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -1219,7 +1219,7 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, * reply_list modification and accounting */ struct mesh_reply* rlist = m->reply_list; - /* RPZ: apply actions */ + /* rpz: apply actions */ rcode = mesh_is_udp(r) && mesh_is_rpz_respip_tcponly_action(m) ? (rcode|BIT_TC) : rcode; diff --git a/services/rpz.c b/services/rpz.c index 646c9093e..2f035e1d6 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -62,39 +62,32 @@ const char* rpz_action_to_string(enum rpz_action a) { switch(a) { - case RPZ_NXDOMAIN_ACTION: return "nxdomain"; - case RPZ_NODATA_ACTION: return "nodata"; - case RPZ_PASSTHRU_ACTION: return "passthru"; - case RPZ_DROP_ACTION: return "drop"; - case RPZ_TCP_ONLY_ACTION: return "tcp_only"; - case RPZ_INVALID_ACTION: return "invalid"; - case RPZ_LOCAL_DATA_ACTION: return "local_data"; - case RPZ_DISABLED_ACTION: return "disabled"; - case RPZ_CNAME_OVERRIDE_ACTION: return "cname_override"; - case RPZ_NO_OVERRIDE_ACTION: return "no_override"; + case RPZ_NXDOMAIN_ACTION: return "rpz-nxdomain"; + case RPZ_NODATA_ACTION: return "rpz-nodata"; + case RPZ_PASSTHRU_ACTION: return "rpz-passthru"; + case RPZ_DROP_ACTION: return "rpz-drop"; + case RPZ_TCP_ONLY_ACTION: return "rpz-tcp-only"; + case RPZ_INVALID_ACTION: return "rpz-invalid"; + case RPZ_LOCAL_DATA_ACTION: return "rpz-local-data"; + case RPZ_DISABLED_ACTION: return "rpz-disabled"; + case RPZ_CNAME_OVERRIDE_ACTION: return "rpz-cname-override"; + case RPZ_NO_OVERRIDE_ACTION: return "rpz-no-override"; + default: return "rpz-unkown-action"; } - return "unknown"; } /** RPZ action enum for config string */ static enum rpz_action rpz_config_to_action(char* a) { - if(strcmp(a, "nxdomain") == 0) - return RPZ_NXDOMAIN_ACTION; - else if(strcmp(a, "nodata") == 0) - return RPZ_NODATA_ACTION; - else if(strcmp(a, "passthru") == 0) - return RPZ_PASSTHRU_ACTION; - else if(strcmp(a, "drop") == 0) - return RPZ_DROP_ACTION; - else if(strcmp(a, "tcp_only") == 0) - return RPZ_TCP_ONLY_ACTION; - else if(strcmp(a, "cname") == 0) - return RPZ_CNAME_OVERRIDE_ACTION; - else if(strcmp(a, "disabled") == 0) - return RPZ_DISABLED_ACTION; - return RPZ_INVALID_ACTION; + if(strcmp(a, "nxdomain") == 0) return RPZ_NXDOMAIN_ACTION; + else if(strcmp(a, "nodata") == 0) return RPZ_NODATA_ACTION; + else if(strcmp(a, "passthru") == 0) return RPZ_PASSTHRU_ACTION; + else if(strcmp(a, "drop") == 0) return RPZ_DROP_ACTION; + else if(strcmp(a, "tcp_only") == 0) return RPZ_TCP_ONLY_ACTION; + else if(strcmp(a, "cname") == 0) return RPZ_CNAME_OVERRIDE_ACTION; + else if(strcmp(a, "disabled") == 0) return RPZ_DISABLED_ACTION; + else return RPZ_INVALID_ACTION; } /** string for RPZ trigger enum */ @@ -102,14 +95,14 @@ static const char* rpz_trigger_to_string(enum rpz_trigger r) { switch(r) { - case RPZ_QNAME_TRIGGER: return "qname"; - case RPZ_CLIENT_IP_TRIGGER: return "client_ip"; - case RPZ_RESPONSE_IP_TRIGGER: return "response_ip"; - case RPZ_NSDNAME_TRIGGER: return "nsdname"; - case RPZ_NSIP_TRIGGER: return "nsip"; - case RPZ_INVALID_TRIGGER: return "invalid"; + case RPZ_QNAME_TRIGGER: return "rpz-qname"; + case RPZ_CLIENT_IP_TRIGGER: return "rpz-client-ip"; + case RPZ_RESPONSE_IP_TRIGGER: return "rpz-response-ip"; + case RPZ_NSDNAME_TRIGGER: return "rpz-nsdname"; + case RPZ_NSIP_TRIGGER: return "rpz-nsip"; + case RPZ_INVALID_TRIGGER: return "rpz-invalid"; + default: return "rpz-unknown-trigger"; } - return "unknown"; } /** @@ -213,15 +206,15 @@ static enum localzone_type rpz_action_to_localzone_type(enum rpz_action a) { switch(a) { - case RPZ_NXDOMAIN_ACTION: return local_zone_always_nxdomain; - case RPZ_NODATA_ACTION: return local_zone_always_nodata; - case RPZ_DROP_ACTION: return local_zone_always_deny; - case RPZ_PASSTHRU_ACTION: return local_zone_always_transparent; + case RPZ_NXDOMAIN_ACTION: return local_zone_always_nxdomain; + case RPZ_NODATA_ACTION: return local_zone_always_nodata; + case RPZ_DROP_ACTION: return local_zone_always_deny; + case RPZ_PASSTHRU_ACTION: return local_zone_always_transparent; case RPZ_LOCAL_DATA_ACTION: /* fallthrough */ case RPZ_CNAME_OVERRIDE_ACTION: return local_zone_redirect; - case RPZ_TCP_ONLY_ACTION: return local_zone_truncate; - case RPZ_INVALID_ACTION: /* fallthrough */ - default: return local_zone_invalid; + case RPZ_TCP_ONLY_ACTION: return local_zone_truncate; + case RPZ_INVALID_ACTION: /* fallthrough */ + default: return local_zone_invalid; } } @@ -229,15 +222,15 @@ enum respip_action rpz_action_to_respip_action(enum rpz_action a) { switch(a) { - case RPZ_NXDOMAIN_ACTION: return respip_always_nxdomain; - case RPZ_NODATA_ACTION: return respip_always_nodata; - case RPZ_DROP_ACTION: return respip_always_deny; - case RPZ_PASSTHRU_ACTION: return respip_always_transparent; - case RPZ_LOCAL_DATA_ACTION: /* fallthrough */ + case RPZ_NXDOMAIN_ACTION: return respip_always_nxdomain; + case RPZ_NODATA_ACTION: return respip_always_nodata; + case RPZ_DROP_ACTION: return respip_always_deny; + case RPZ_PASSTHRU_ACTION: return respip_always_transparent; + case RPZ_LOCAL_DATA_ACTION: /* fallthrough */ case RPZ_CNAME_OVERRIDE_ACTION: return respip_redirect; - case RPZ_TCP_ONLY_ACTION: return respip_truncate; - case RPZ_INVALID_ACTION: /* fallthrough */ - default: return respip_invalid; + case RPZ_TCP_ONLY_ACTION: return respip_truncate; + case RPZ_INVALID_ACTION: /* fallthrough */ + default: return respip_invalid; } } @@ -245,15 +238,14 @@ static enum rpz_action localzone_type_to_rpz_action(enum localzone_type lzt) { switch(lzt) { - case local_zone_always_nxdomain: return RPZ_NXDOMAIN_ACTION; - case local_zone_always_nodata: return RPZ_NODATA_ACTION; - case local_zone_always_deny: return RPZ_DROP_ACTION; - case local_zone_always_transparent: return RPZ_PASSTHRU_ACTION; - case local_zone_redirect: return RPZ_LOCAL_DATA_ACTION; - case local_zone_truncate: return RPZ_TCP_ONLY_ACTION; - case local_zone_invalid: - default: - return RPZ_INVALID_ACTION; + case local_zone_always_nxdomain: return RPZ_NXDOMAIN_ACTION; + case local_zone_always_nodata: return RPZ_NODATA_ACTION; + case local_zone_always_deny: return RPZ_DROP_ACTION; + case local_zone_always_transparent: return RPZ_PASSTHRU_ACTION; + case local_zone_redirect: return RPZ_LOCAL_DATA_ACTION; + case local_zone_truncate: return RPZ_TCP_ONLY_ACTION; + case local_zone_invalid: /* fallthrough */ + default: return RPZ_INVALID_ACTION; } } @@ -261,15 +253,14 @@ enum rpz_action respip_action_to_rpz_action(enum respip_action a) { switch(a) { - case respip_always_nxdomain: return RPZ_NXDOMAIN_ACTION; - case respip_always_nodata: return RPZ_NODATA_ACTION; - case respip_always_deny: return RPZ_DROP_ACTION; - case respip_always_transparent: return RPZ_PASSTHRU_ACTION; - case respip_redirect: return RPZ_LOCAL_DATA_ACTION; - case respip_truncate: return RPZ_TCP_ONLY_ACTION; - case respip_invalid: - default: - return RPZ_INVALID_ACTION; + case respip_always_nxdomain: return RPZ_NXDOMAIN_ACTION; + case respip_always_nodata: return RPZ_NODATA_ACTION; + case respip_always_deny: return RPZ_DROP_ACTION; + case respip_always_transparent: return RPZ_PASSTHRU_ACTION; + case respip_redirect: return RPZ_LOCAL_DATA_ACTION; + case respip_truncate: return RPZ_TCP_ONLY_ACTION; + case respip_invalid: /* fallthrough */ + default: return RPZ_INVALID_ACTION; } } @@ -1297,11 +1288,11 @@ log_rpz_apply(uint8_t* dname, enum rpz_action a, struct query_info* qinfo, dname_str(dname, dnamestr); addr_to_str(&repinfo->addr, repinfo->addrlen, ip, sizeof(ip)); if(log_name) - snprintf(txt, sizeof(txt), "RPZ applied [%s] %s %s %s@%u", + snprintf(txt, sizeof(txt), "rpz: applied [%s] %s %s %s@%u", log_name, dnamestr, rpz_action_to_string(a), ip, (unsigned)port); else - snprintf(txt, sizeof(txt), "RPZ applied %s %s %s@%u", + snprintf(txt, sizeof(txt), "rpz: applied %s %s %s@%u", dnamestr, rpz_action_to_string(a), ip, (unsigned)port); log_nametypeclass(0, txt, qinfo->qname, qinfo->qtype, qinfo->qclass); } @@ -1398,15 +1389,8 @@ rpz_is_udp_query(struct comm_reply* repinfo) { /** encode answer consisting of 1 rrset */ static int -rpz_local_encode(struct query_info* qinfo, - struct module_env* env, - struct edns_data* edns, - struct comm_reply* repinfo, - sldns_buffer* buf, - struct regional* temp, - struct ub_packed_rrset_key* rrset, - int ansec, - int rcode) +rpz_local_encode(struct query_info* qinfo,struct edns_data* edns, sldns_buffer* buf, + struct regional* temp, struct ub_packed_rrset_key* rrset, int ansec, int rcode) { struct reply_info rep; uint16_t udpsize; @@ -1434,6 +1418,7 @@ rpz_local_encode(struct query_info* qinfo, *(uint16_t*)sldns_buffer_begin(buf), sldns_buffer_read_u16_at(buf, 2), edns); } + return 1; } @@ -1451,14 +1436,9 @@ rpz_find_synthesized_rrset(int qtype, struct clientip_synthesized_rr* data) { } static void -rpz_apply_clientip_localdata_action(struct rpz* r, - struct clientip_synthesized_rr* raddr, - struct module_env* env, - struct query_info* qinfo, - struct edns_data* edns, - struct comm_reply* repinfo, - sldns_buffer* buf, - struct regional* temp) +rpz_apply_clientip_localdata_action(struct clientip_synthesized_rr* raddr, + struct query_info* qinfo, struct edns_data* edns, sldns_buffer* buf, + struct regional* temp) { struct local_rrset* rrset; enum rpz_action action = RPZ_INVALID_ACTION; @@ -1496,7 +1476,7 @@ rpz_apply_clientip_localdata_action(struct rpz* r, rp->rk.dname = qinfo->qname; rp->rk.dname_len = qinfo->qname_len; nodata: - rpz_local_encode(qinfo, env, edns, repinfo, buf, temp, rp, rrset_count, rcode); + rpz_local_encode(qinfo, edns, buf, temp, rp, rrset_count, rcode); } static inline struct dns_msg* @@ -1512,7 +1492,7 @@ rpz_dns_msg_new(struct regional* region) } static inline struct dns_msg* -rpz_forge_nodata(struct rpz* r, struct module_qstate* ms) +rpz_synthesize_nodata(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms) { struct dns_msg* msg = rpz_dns_msg_new(ms->region); if(msg == NULL) { return msg; } @@ -1532,7 +1512,7 @@ rpz_forge_nodata(struct rpz* r, struct module_qstate* ms) } static inline struct dns_msg* -rpz_forge_nxdomain(struct rpz* r, struct module_qstate* ms) +rpz_synthesize_nxdomain(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms) { struct dns_msg* msg = rpz_dns_msg_new(ms->region); if(msg == NULL) { return msg; } @@ -1552,7 +1532,7 @@ rpz_forge_nxdomain(struct rpz* r, struct module_qstate* ms) } static inline struct dns_msg* -rpz_forge_localdata(struct rpz* r, struct module_qstate* ms, +rpz_synthesize_localdata(struct rpz* r, struct module_qstate* ms, struct clientip_synthesized_rr* data) { struct dns_msg* msg = NULL; @@ -1564,7 +1544,7 @@ rpz_forge_localdata(struct rpz* r, struct module_qstate* ms, rrset = rpz_find_synthesized_rrset(qi->qtype, data); if(rrset == NULL) { verbose(VERB_ALGO, "rpz: nsip: no matching synthesized data found; resorting to nodata"); - return rpz_forge_nodata(r, ms); + return rpz_synthesize_nodata(r, ms); } msg = rpz_dns_msg_new(ms->region); @@ -1598,6 +1578,57 @@ rpz_forge_localdata(struct rpz* r, struct module_qstate* ms, return msg; } +static int +rpz_synthesize_local_zone(struct module_env* env, struct rpz* r, struct local_zone* z, + enum localzone_type lzt, struct query_info* qinfo, + struct edns_data* edns, sldns_buffer* buf, + struct regional* temp, struct comm_reply* repinfo, + struct ub_server_stats* stats) +{ + struct local_data* ld = NULL; + int ret = 0; + if(r->action_override == RPZ_CNAME_OVERRIDE_ACTION) { + qinfo->local_alias = regional_alloc_zero(temp, sizeof(struct local_rrset)); + if(qinfo->local_alias == NULL) { + return 0; /* out of memory */ + } + qinfo->local_alias->rrset = regional_alloc_init(temp, r->cname_override, + sizeof(*r->cname_override)); + if(qinfo->local_alias->rrset == NULL) { + return 0; /* out of memory */ + } + qinfo->local_alias->rrset->rk.dname = qinfo->qname; + qinfo->local_alias->rrset->rk.dname_len = qinfo->qname_len; + if(r->log) { + log_rpz_apply(z->name, RPZ_CNAME_OVERRIDE_ACTION, + qinfo, repinfo, r->log_name); + } + stats->rpz_action[RPZ_CNAME_OVERRIDE_ACTION]++; + return 0; + } + + if(lzt == local_zone_redirect && local_data_answer(z, env, qinfo, + edns, repinfo, buf, temp, dname_count_labels(qinfo->qname), + &ld, lzt, -1, NULL, 0, NULL, 0)) { + if(r->log) { + log_rpz_apply(z->name, + localzone_type_to_rpz_action(lzt), qinfo, + repinfo, r->log_name); + } + stats->rpz_action[localzone_type_to_rpz_action(lzt)]++; + return !qinfo->local_alias; + } + + ret = local_zones_zone_answer(z, env, qinfo, edns, repinfo, buf, temp, + 0 /* no local data used */, lzt); + if(r->log) { + log_rpz_apply(z->name, localzone_type_to_rpz_action(lzt), + qinfo, repinfo, r->log_name); + } + stats->rpz_action[localzone_type_to_rpz_action(lzt)]++; + return ret; +} + struct clientip_synthesized_rr* rpz_delegation_point_ipbased_trigger_lookup(struct rpz* rpz, struct iter_qstate* is) { @@ -1618,32 +1649,40 @@ rpz_apply_nsip_tigger(struct module_qstate* ms, struct rpz* r,struct clientip_sy enum rpz_action action = raddr->action; struct dns_msg* ret = NULL; + if(r->action_override != RPZ_NO_OVERRIDE_ACTION) { + verbose(VERB_ALGO, "rpz: using override action=%s (replaces=%s)", + rpz_action_to_string(r->action_override), rpz_action_to_string(action)); + action = r->action_override; + } + verbose(VERB_ALGO, "rpz: iterator callback: nsip: apply action=%s", rpz_action_to_string(raddr->action)); if(action == RPZ_LOCAL_DATA_ACTION && raddr->data == NULL) { verbose(VERB_ALGO, "rpz: bug: local-data action but no local data"); - ret = rpz_forge_nodata(r, ms); + ret = rpz_synthesize_nodata(r, ms); goto done; } switch(action) { case RPZ_NXDOMAIN_ACTION: - ret = rpz_forge_nxdomain(r, ms); + ret = rpz_synthesize_nxdomain(r, ms); break; case RPZ_NODATA_ACTION: - ret = rpz_forge_nodata(r, ms); + ret = rpz_synthesize_nodata(r, ms); break; case RPZ_TCP_ONLY_ACTION: - verbose(VERB_ALGO, "rpz: nsip: tcp-only trigger ignored"); + // basically a passthru here but the tcp-only will be + // honored before the query gets send + ms->respip_action_info->action = respip_truncate; ret = NULL; break; case RPZ_DROP_ACTION: - ret = rpz_forge_nodata(r, ms); + ret = rpz_synthesize_nodata(r, ms); ms->is_drop = 1; break; case RPZ_LOCAL_DATA_ACTION: - ret = rpz_forge_localdata(r, ms, raddr); + ret = rpz_synthesize_localdata(r, ms, raddr); break; case RPZ_PASSTHRU_ACTION: ret = NULL; @@ -1663,8 +1702,45 @@ struct dns_msg* rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r, struct local_zone* z) { struct dns_msg* ret = NULL; + enum rpz_action action = localzone_type_to_rpz_action(z->type); - verbose(VERB_ALGO, "rpz: iterator callback: nsdname: apply action"); + if(r->action_override != RPZ_NO_OVERRIDE_ACTION) { + verbose(VERB_ALGO, "rpz: using override action=%s (replaces=%s)", + rpz_action_to_string(r->action_override), rpz_action_to_string(action)); + action = r->action_override; + } + + verbose(VERB_ALGO, "rpz: nsdame trigger with action=%s", rpz_action_to_string(action)); + + switch(action) { + case RPZ_NXDOMAIN_ACTION: + ret = rpz_synthesize_nxdomain(r, ms); + break; + case RPZ_NODATA_ACTION: + ret = rpz_synthesize_nodata(r, ms); + break; + case RPZ_TCP_ONLY_ACTION: + // basically a passthru here but the tcp-only will be + // honored before the query gets send + ms->respip_action_info->action = respip_truncate; + ret = NULL; + break; + case RPZ_DROP_ACTION: + ret = rpz_synthesize_nodata(r, ms); + ms->is_drop = 1; + break; + case RPZ_LOCAL_DATA_ACTION: + //ret = rpz_synthesize_localdata(r, ms, raddr); + ret = NULL; + break; + case RPZ_PASSTHRU_ACTION: + ret = NULL; + break; + default: + verbose(VERB_ALGO, "rpz: nsip: bug: unhandled or invalid action: '%s'", + rpz_action_to_string(action)); + ret = NULL; + } return ret; } @@ -1771,8 +1847,8 @@ rpz_apply_maybe_clientip_trigger(struct auth_zones* az, struct module_env* env, } stats->rpz_action[client_action]++; if(client_action == RPZ_LOCAL_DATA_ACTION) { - rpz_apply_clientip_localdata_action(*r_out, node, env, - qinfo, edns, repinfo, buf, temp); + rpz_apply_clientip_localdata_action(node, qinfo, edns, + buf, temp); } else { // XXX: log_rpz_apply not possbile because no zone local_zones_zone_answer(NULL /*no zone*/, env, qinfo, edns, @@ -1799,7 +1875,6 @@ rpz_callback_from_worker_request(struct auth_zones* az, struct module_env* env, struct rpz* r = NULL; struct auth_zone* a = NULL; struct local_zone* z = NULL; - struct local_data* ld = NULL; int ret; enum localzone_type lzt; @@ -1819,58 +1894,12 @@ rpz_callback_from_worker_request(struct auth_zones* az, struct module_env* env, lzt = rpz_action_to_localzone_type(r->action_override); } - verbose(VERB_ALGO, "rpz: final client action=%s", + verbose(VERB_ALGO, "rpz: qname trigger with action=%s", rpz_action_to_string(localzone_type_to_rpz_action(lzt))); - if(r->action_override == RPZ_CNAME_OVERRIDE_ACTION) { - qinfo->local_alias = - regional_alloc_zero(temp, sizeof(struct local_rrset)); - if(!qinfo->local_alias) { - lock_rw_unlock(&z->lock); - lock_rw_unlock(&a->lock); - return 0; /* out of memory */ - } - qinfo->local_alias->rrset = - regional_alloc_init(temp, r->cname_override, - sizeof(*r->cname_override)); - if(!qinfo->local_alias->rrset) { - lock_rw_unlock(&z->lock); - lock_rw_unlock(&a->lock); - return 0; /* out of memory */ - } - qinfo->local_alias->rrset->rk.dname = qinfo->qname; - qinfo->local_alias->rrset->rk.dname_len = qinfo->qname_len; - if(r->log) { - log_rpz_apply(z->name, RPZ_CNAME_OVERRIDE_ACTION, - qinfo, repinfo, r->log_name); - } - stats->rpz_action[RPZ_CNAME_OVERRIDE_ACTION]++; - lock_rw_unlock(&z->lock); - lock_rw_unlock(&a->lock); - return 0; - } + ret = rpz_synthesize_local_zone(env, r, z, lzt, qinfo, edns, buf, temp, + repinfo, stats); - if(lzt == local_zone_redirect && local_data_answer(z, env, qinfo, - edns, repinfo, buf, temp, dname_count_labels(qinfo->qname), - &ld, lzt, -1, NULL, 0, NULL, 0)) { - if(r->log) { - log_rpz_apply(z->name, - localzone_type_to_rpz_action(lzt), qinfo, - repinfo, r->log_name); - } - stats->rpz_action[localzone_type_to_rpz_action(lzt)]++; - lock_rw_unlock(&z->lock); - lock_rw_unlock(&a->lock); - return !qinfo->local_alias; - } - - ret = local_zones_zone_answer(z, env, qinfo, edns, repinfo, buf, temp, - 0 /* no local data used */, lzt); - if(r->log) { - log_rpz_apply(z->name, localzone_type_to_rpz_action(lzt), - qinfo, repinfo, r->log_name); - } - stats->rpz_action[localzone_type_to_rpz_action(lzt)]++; lock_rw_unlock(&z->lock); lock_rw_unlock(&a->lock); From 8fce4ff27a07cd28f8651e23a365ac1481c0feda Mon Sep 17 00:00:00 2001 From: mb Date: Fri, 27 Nov 2020 10:46:54 +0100 Subject: [PATCH 24/68] rpz: continue nsdname and minor cleanups --- services/rpz.c | 97 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 74 insertions(+), 23 deletions(-) diff --git a/services/rpz.c b/services/rpz.c index 2f035e1d6..56677cb99 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -1532,20 +1532,14 @@ rpz_synthesize_nxdomain(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms) } static inline struct dns_msg* -rpz_synthesize_localdata(struct rpz* r, struct module_qstate* ms, - struct clientip_synthesized_rr* data) +rpz_synthesize_localdata_from_rrset(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms, + struct local_rrset* rrset) { struct dns_msg* msg = NULL; struct query_info* qi = &ms->qinfo; - struct ub_packed_rrset_key* rp; - struct local_rrset* rrset; struct reply_info* new_reply_info; + struct ub_packed_rrset_key* rp; - rrset = rpz_find_synthesized_rrset(qi->qtype, data); - if(rrset == NULL) { - verbose(VERB_ALGO, "rpz: nsip: no matching synthesized data found; resorting to nodata"); - return rpz_synthesize_nodata(r, ms); - } msg = rpz_dns_msg_new(ms->region); if(msg == NULL) { return NULL; } @@ -1578,12 +1572,64 @@ rpz_synthesize_localdata(struct rpz* r, struct module_qstate* ms, return msg; } +static inline struct dns_msg* +rpz_synthesize_localdata(struct rpz* r, struct module_qstate* ms, + struct clientip_synthesized_rr* data) +{ + struct query_info* qi = &ms->qinfo; + struct local_rrset* rrset; + + rrset = rpz_find_synthesized_rrset(qi->qtype, data); + if(rrset == NULL) { + verbose(VERB_ALGO, "rpz: nsip: no matching synthesized data found; resorting to nodata"); + return rpz_synthesize_nodata(r, ms); + } + + return rpz_synthesize_localdata_from_rrset(r, ms, rrset); +} + +// copy'n'pase from localzone.c +static struct local_rrset* +local_data_find_type(struct local_data* data, uint16_t type, int alias_ok) +{ + struct local_rrset* p; + type = htons(type); + for(p = data->rrsets; p; p = p->next) { + if(p->rrset->rk.type == type) + return p; + if(alias_ok && p->rrset->rk.type == htons(LDNS_RR_TYPE_CNAME)) + return p; + } + return NULL; +} + +static inline struct dns_msg* +rpz_synthesize_nsdname_localdata(struct rpz* r, struct module_qstate* ms, struct local_zone* z) { + struct query_info* qi = &ms->qinfo; + struct local_data key; + struct local_data* ld; + struct local_rrset* rrset; + + ld = (struct local_data*)rbtree_search(&z->data, &key.node); + if(ld == NULL) { + verbose(VERB_ALGO, "rpz: impossible: dname not found"); + return NULL; + } + + rrset = local_data_find_type(ld, qi->qtype, 1); + if(rrset == NULL) { + verbose(VERB_ALGO, "rpz: no matching localdata found"); + return NULL; + } + + return rpz_synthesize_localdata_from_rrset(r, ms, rrset); +} + static int -rpz_synthesize_local_zone(struct module_env* env, struct rpz* r, struct local_zone* z, - enum localzone_type lzt, struct query_info* qinfo, - struct edns_data* edns, sldns_buffer* buf, - struct regional* temp, struct comm_reply* repinfo, - struct ub_server_stats* stats) +rpz_synthesize_qname_localdata(struct module_env* env, struct rpz* r, + struct local_zone* z, enum localzone_type lzt, struct query_info* qinfo, + struct edns_data* edns, sldns_buffer* buf, struct regional* temp, + struct comm_reply* repinfo, struct ub_server_stats* stats) { struct local_data* ld = NULL; int ret = 0; @@ -1635,16 +1681,20 @@ rpz_delegation_point_ipbased_trigger_lookup(struct rpz* rpz, struct iter_qstate* struct delegpt_addr* cursor; struct clientip_synthesized_rr* action = NULL; if(is->dp == NULL) { return NULL; } - for(cursor = is->dp->target_list; cursor != NULL; cursor = cursor->next_target) { + for(cursor = is->dp->target_list; + cursor != NULL; + cursor = cursor->next_target) { if(cursor->bogus) { continue; } - action = rpz_ipbased_trigger_lookup(rpz->ns_set, &cursor->addr, cursor->addrlen); + action = rpz_ipbased_trigger_lookup(rpz->ns_set, &cursor->addr, + cursor->addrlen); if(action != NULL) { return action; } } return NULL; } struct dns_msg* -rpz_apply_nsip_tigger(struct module_qstate* ms, struct rpz* r,struct clientip_synthesized_rr* raddr) +rpz_apply_nsip_trigger(struct module_qstate* ms, struct rpz* r, + struct clientip_synthesized_rr* raddr) { enum rpz_action action = raddr->action; struct dns_msg* ret = NULL; @@ -1659,7 +1709,7 @@ rpz_apply_nsip_tigger(struct module_qstate* ms, struct rpz* r,struct clientip_sy rpz_action_to_string(raddr->action)); if(action == RPZ_LOCAL_DATA_ACTION && raddr->data == NULL) { - verbose(VERB_ALGO, "rpz: bug: local-data action but no local data"); + verbose(VERB_ALGO, "rpz: bug: nsip local-data action but no local data"); ret = rpz_synthesize_nodata(r, ms); goto done; } @@ -1683,6 +1733,7 @@ rpz_apply_nsip_tigger(struct module_qstate* ms, struct rpz* r,struct clientip_sy break; case RPZ_LOCAL_DATA_ACTION: ret = rpz_synthesize_localdata(r, ms, raddr); + if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms); } break; case RPZ_PASSTHRU_ACTION: ret = NULL; @@ -1730,8 +1781,8 @@ rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r, struct local_ ms->is_drop = 1; break; case RPZ_LOCAL_DATA_ACTION: - //ret = rpz_synthesize_localdata(r, ms, raddr); - ret = NULL; + ret = rpz_synthesize_nsdname_localdata(r, ms, z); + if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms); } break; case RPZ_PASSTHRU_ACTION: ret = NULL; @@ -1812,7 +1863,7 @@ rpz_callback_from_iterator_module(struct module_qstate* ms, struct iter_qstate* lock_rw_unlock(&az->rpz_lock); if(raddr == NULL && z == NULL) { return NULL; } - else if(raddr != NULL) { return rpz_apply_nsip_tigger(ms, r, raddr); } + else if(raddr != NULL) { return rpz_apply_nsip_trigger(ms, r, raddr); } else if(z != NULL) { return rpz_apply_nsdname_trigger(ms, r, z); } else { return NULL; } } @@ -1897,8 +1948,8 @@ rpz_callback_from_worker_request(struct auth_zones* az, struct module_env* env, verbose(VERB_ALGO, "rpz: qname trigger with action=%s", rpz_action_to_string(localzone_type_to_rpz_action(lzt))); - ret = rpz_synthesize_local_zone(env, r, z, lzt, qinfo, edns, buf, temp, - repinfo, stats); + ret = rpz_synthesize_qname_localdata(env, r, z, lzt, qinfo, edns, buf, temp, + repinfo, stats); lock_rw_unlock(&z->lock); lock_rw_unlock(&a->lock); From 842c9bfc4116b7247d5b2f0bf1726b0ad7700b7f Mon Sep 17 00:00:00 2001 From: mb Date: Fri, 27 Nov 2020 10:52:01 +0100 Subject: [PATCH 25/68] rpz: cleanups --- services/rpz.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/services/rpz.c b/services/rpz.c index 56677cb99..f8f71f1d4 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -1573,7 +1573,7 @@ rpz_synthesize_localdata_from_rrset(struct rpz* ATTR_UNUSED(r), struct module_qs } static inline struct dns_msg* -rpz_synthesize_localdata(struct rpz* r, struct module_qstate* ms, +rpz_synthesize_nsip_localdata(struct rpz* r, struct module_qstate* ms, struct clientip_synthesized_rr* data) { struct query_info* qi = &ms->qinfo; @@ -1581,8 +1581,8 @@ rpz_synthesize_localdata(struct rpz* r, struct module_qstate* ms, rrset = rpz_find_synthesized_rrset(qi->qtype, data); if(rrset == NULL) { - verbose(VERB_ALGO, "rpz: nsip: no matching synthesized data found; resorting to nodata"); - return rpz_synthesize_nodata(r, ms); + verbose(VERB_ALGO, "rpz: nsip: no matching local data found"); + return NULL; } return rpz_synthesize_localdata_from_rrset(r, ms, rrset); @@ -1618,7 +1618,7 @@ rpz_synthesize_nsdname_localdata(struct rpz* r, struct module_qstate* ms, struct rrset = local_data_find_type(ld, qi->qtype, 1); if(rrset == NULL) { - verbose(VERB_ALGO, "rpz: no matching localdata found"); + verbose(VERB_ALGO, "rpz: nsdname: no matching local data found"); return NULL; } @@ -1709,7 +1709,7 @@ rpz_apply_nsip_trigger(struct module_qstate* ms, struct rpz* r, rpz_action_to_string(raddr->action)); if(action == RPZ_LOCAL_DATA_ACTION && raddr->data == NULL) { - verbose(VERB_ALGO, "rpz: bug: nsip local-data action but no local data"); + verbose(VERB_ALGO, "rpz: bug: nsip local data action but no local data"); ret = rpz_synthesize_nodata(r, ms); goto done; } @@ -1732,7 +1732,7 @@ rpz_apply_nsip_trigger(struct module_qstate* ms, struct rpz* r, ms->is_drop = 1; break; case RPZ_LOCAL_DATA_ACTION: - ret = rpz_synthesize_localdata(r, ms, raddr); + ret = rpz_synthesize_nsip_localdata(r, ms, raddr); if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms); } break; case RPZ_PASSTHRU_ACTION: From 78bcfdee64b20f6bc67e121433034f058b264f42 Mon Sep 17 00:00:00 2001 From: mb Date: Fri, 27 Nov 2020 12:22:56 +0100 Subject: [PATCH 26/68] rpz: fix local data lookup for nsdname --- services/rpz.c | 56 ++++++++++++++++++++++++++++++---------- testdata/rpz_nsdname.rpl | 2 +- 2 files changed, 43 insertions(+), 15 deletions(-) diff --git a/services/rpz.c b/services/rpz.c index f8f71f1d4..777007b42 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -57,6 +57,11 @@ typedef struct resp_addr rpz_aclnode_type; +struct matched_delegation_point { + uint8_t* dname; + size_t dname_len; +}; + /** string for RPZ action enum */ const char* rpz_action_to_string(enum rpz_action a) @@ -1468,7 +1473,7 @@ rpz_apply_clientip_localdata_action(struct clientip_synthesized_rr* raddr, rp = respip_copy_rrset(rrset->rrset, temp); if(!rp) { - verbose(VERB_ALGO, "rpz: local-data action: out of memory"); + verbose(VERB_ALGO, "rpz: local data action: out of memory"); return; } @@ -1588,13 +1593,14 @@ rpz_synthesize_nsip_localdata(struct rpz* r, struct module_qstate* ms, return rpz_synthesize_localdata_from_rrset(r, ms, rrset); } -// copy'n'pase from localzone.c +// copy'n'paste from localzone.c static struct local_rrset* local_data_find_type(struct local_data* data, uint16_t type, int alias_ok) { struct local_rrset* p; type = htons(type); for(p = data->rrsets; p; p = p->next) { + verbose(VERB_ALGO, "type=%d (%d)", type, p->rrset->rk.type); if(p->rrset->rk.type == type) return p; if(alias_ok && p->rrset->rk.type == htons(LDNS_RR_TYPE_CNAME)) @@ -1603,20 +1609,31 @@ local_data_find_type(struct local_data* data, uint16_t type, int alias_ok) return NULL; } +// based on localzone.c:local_data_answer() static inline struct dns_msg* -rpz_synthesize_nsdname_localdata(struct rpz* r, struct module_qstate* ms, struct local_zone* z) { - struct query_info* qi = &ms->qinfo; +rpz_synthesize_nsdname_localdata(struct rpz* r, struct module_qstate* ms, + struct local_zone* z, struct matched_delegation_point const* match) +{ struct local_data key; struct local_data* ld; struct local_rrset* rrset; + if(match->dname == NULL) { return NULL; } + + key.node.key = &key; + key.name = match->dname; + key.namelen = match->dname_len; + key.namelabs = dname_count_labels(match->dname); + + rpz_log_dname("nsdname local data", key.name, key.namelen); + ld = (struct local_data*)rbtree_search(&z->data, &key.node); if(ld == NULL) { - verbose(VERB_ALGO, "rpz: impossible: dname not found"); + verbose(VERB_ALGO, "rpz: nsdname: impossible: qname not found"); return NULL; } - rrset = local_data_find_type(ld, qi->qtype, 1); + rrset = local_data_find_type(ld, ms->qinfo.qtype, 1); if(rrset == NULL) { verbose(VERB_ALGO, "rpz: nsdname: no matching local data found"); return NULL; @@ -1750,7 +1767,8 @@ done: } struct dns_msg* -rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r, struct local_zone* z) +rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r, + struct local_zone* z, struct matched_delegation_point const* match) { struct dns_msg* ret = NULL; enum rpz_action action = localzone_type_to_rpz_action(z->type); @@ -1781,7 +1799,7 @@ rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r, struct local_ ms->is_drop = 1; break; case RPZ_LOCAL_DATA_ACTION: - ret = rpz_synthesize_nsdname_localdata(r, ms, z); + ret = rpz_synthesize_nsdname_localdata(r, ms, z, match); if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms); } break; case RPZ_PASSTHRU_ACTION: @@ -1797,7 +1815,10 @@ rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r, struct local_ } static struct local_zone* -rpz_delegation_point_zone_lookup(struct delegpt* dp, struct local_zones* zones, uint16_t qclass) +rpz_delegation_point_zone_lookup(struct delegpt* dp, struct local_zones* zones, + uint16_t qclass, + /* output parameter */ + struct matched_delegation_point* match) { struct delegpt_ns* nameserver; struct local_zone* z = NULL; @@ -1805,15 +1826,21 @@ rpz_delegation_point_zone_lookup(struct delegpt* dp, struct local_zones* zones, rpz_log_dname("delegation point", dp->name, dp->namelen); // XXX: do we want this? z = rpz_find_zone(zones, dp->name, dp->namelen, qclass, 0, 0, 0); - - if(z == NULL) { + if(z != NULL) { + match->dname = dp->name; + match->dname_len = dp->namelen; + } else if(z == NULL) { for(nameserver = dp->nslist; nameserver != NULL; nameserver = nameserver->next) { rpz_log_dname("delegation point", nameserver->name, nameserver->namelen); z = rpz_find_zone(zones, nameserver->name, nameserver->namelen, qclass, 0, 0, 0); - if(z != NULL) { break; } + if(z != NULL) { + match->dname = nameserver->name; + match->dname_len = nameserver->namelen; + break; + } } } @@ -1830,6 +1857,7 @@ rpz_callback_from_iterator_module(struct module_qstate* ms, struct iter_qstate* struct clientip_synthesized_rr* raddr = NULL; struct rpz* r; struct local_zone* z = NULL; + struct matched_delegation_point match = {0}; if(ms->env == NULL || ms->env->auth_zones == NULL) { return 0; } @@ -1852,7 +1880,7 @@ rpz_callback_from_iterator_module(struct module_qstate* ms, struct iter_qstate* } z = rpz_delegation_point_zone_lookup(is->dp, r->nsdname_zones, - ms->qinfo.qclass); + ms->qinfo.qclass, &match); if(z != NULL) { lock_rw_unlock(&a->lock); break; @@ -1864,7 +1892,7 @@ rpz_callback_from_iterator_module(struct module_qstate* ms, struct iter_qstate* if(raddr == NULL && z == NULL) { return NULL; } else if(raddr != NULL) { return rpz_apply_nsip_trigger(ms, r, raddr); } - else if(z != NULL) { return rpz_apply_nsdname_trigger(ms, r, z); } + else if(z != NULL) { return rpz_apply_nsdname_trigger(ms, r, z, &match); } else { return NULL; } } diff --git a/testdata/rpz_nsdname.rpl b/testdata/rpz_nsdname.rpl index 7587ccfe5..64fb98880 100644 --- a/testdata/rpz_nsdname.rpl +++ b/testdata/rpz_nsdname.rpl @@ -21,7 +21,7 @@ gotham.bb.rpz-nsdname CNAME *. gotham.cc.rpz-nsdname CNAME rpz-drop. gotham.com.rpz-nsdname CNAME rpz-passthru. gotham.dd.rpz-nsdname CNAME rpz-tcp-only. -gotham.ee.rpz-nsdname A 127.0.0.1 +gotham.ff.rpz-nsdname A 127.0.0.1 gotham.ff.rpz-nsdname TXT "42" TEMPFILE_END From 15b46257e1aee908d83de1b0342c620d0c6da32c Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 14 Jan 2021 13:49:42 +0100 Subject: [PATCH 27/68] - fix that testdata/rpz_clientip.rpl trigger tests succeed. --- services/rpz.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/rpz.c b/services/rpz.c index 0d825238f..b89583e8e 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -1356,7 +1356,7 @@ rpz_resolve_client_action_and_zone(struct auth_zones* az, struct query_info* qin z = rpz_find_zone(r->local_zones, qinfo->qname, qinfo->qname_len, qinfo->qclass, 0, 0, 0); node = rpz_ipbased_trigger_lookup(r->client_set, &repinfo->addr, repinfo->addrlen); - if(z && r->action_override == RPZ_DISABLED_ACTION) { + if((z || node) && r->action_override == RPZ_DISABLED_ACTION) { if(r->log) log_rpz_apply(z->name, r->action_override, @@ -1366,7 +1366,7 @@ rpz_resolve_client_action_and_zone(struct auth_zones* az, struct query_info* qin lock_rw_unlock(&z->lock); z = NULL; } - if(z) { + if(z || node) { break; } else { if(node != NULL) { @@ -1913,7 +1913,7 @@ rpz_apply_maybe_clientip_trigger(struct auth_zones* az, struct module_env* env, struct clientip_synthesized_rr* node = rpz_resolve_client_action_and_zone( az, qinfo, repinfo, taglist, taglen, stats, z_out, a_out, r_out); - client_action = node == NULL ? RPZ_INVALID_ACTION : node->action; + client_action = ((node == NULL) ? RPZ_INVALID_ACTION : node->action); verbose(VERB_ALGO, "rpz: qname trigger: client action=%s", rpz_action_to_string(client_action)); From 5e00d516fe9d0f5da94866657232d5497bebfa82 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 14 Jan 2021 15:31:17 +0100 Subject: [PATCH 28/68] - fix that testdata/rpz_qname.rpl trigger tests succeed. --- testdata/rpz_qname.rpl | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/testdata/rpz_qname.rpl b/testdata/rpz_qname.rpl index 4f17f8577..dd47f188a 100644 --- a/testdata/rpz_qname.rpl +++ b/testdata/rpz_qname.rpl @@ -47,12 +47,15 @@ stub-zone: stub-zone: name: "example." stub-addr: 10.20.30.50 +stub-zone: + name: "tcp." + stub-addr: 10.20.30.60 CONFIG_END SCENARIO_BEGIN Test all support RPZ action for QNAME trigger ; a. -RANGE_BEGIN 0 100 +RANGE_BEGIN 0 1000 ADDRESS 10.20.30.40 ENTRY_BEGIN MATCH opcode qtype qname @@ -89,7 +92,7 @@ ENTRY_END RANGE_END ; example. -RANGE_BEGIN 0 100 +RANGE_BEGIN 0 1000 ADDRESS 10.20.30.50 ENTRY_BEGIN MATCH opcode qtype qname @@ -125,6 +128,32 @@ ENTRY_END RANGE_END +; tcp. +RANGE_BEGIN 0 1000 + ADDRESS 10.20.30.60 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +tcp. IN NS +SECTION ANSWER +tcp. IN NS ns.example. +SECTION ADDITIONAL +ns.tcp IN A 10.20.30.60 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +tcp. IN TXT +SECTION ANSWER +tcp. IN TXT "tcp. answer from upstream ns" +ENTRY_END +RANGE_END + STEP 10 QUERY ENTRY_BEGIN REPLY RD @@ -306,6 +335,7 @@ ENTRY_END STEP 95 QUERY ENTRY_BEGIN +REPLY RD SECTION QUESTION tcp. IN TXT ENTRY_END @@ -313,7 +343,7 @@ ENTRY_END STEP 96 CHECK_ANSWER ENTRY_BEGIN MATCH all -REPLY QR RA AA TC NOERROR +REPLY QR RD RA AA TC NOERROR SECTION QUESTION tcp. IN TXT SECTION ANSWER @@ -322,6 +352,7 @@ ENTRY_END STEP 97 QUERY ENTRY_BEGIN MATCH TCP +REPLY RD SECTION QUESTION tcp. IN TXT ENTRY_END @@ -329,11 +360,11 @@ ENTRY_END STEP 98 CHECK_ANSWER ENTRY_BEGIN MATCH all TCP -REPLY QR RD RA AA NOERROR +REPLY QR RD RA NOERROR SECTION QUESTION tcp. IN TXT SECTION ANSWER -tcp. IN TXT "local tcp data 2nd zone" +tcp. IN TXT "tcp. answer from upstream ns" ENTRY_END ; no answer is checked at exit of testbound. From 5c760a13a4a5d50af30e22f227087b23445f0541 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 14 Jan 2021 15:31:33 +0100 Subject: [PATCH 29/68] - rpz trigger, unlock and remove node when rpz is disabled with action override. --- services/rpz.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/rpz.c b/services/rpz.c index b89583e8e..00f3b5a3d 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -1365,6 +1365,10 @@ rpz_resolve_client_action_and_zone(struct auth_zones* az, struct query_info* qin stats->rpz_action[r->action_override]++; lock_rw_unlock(&z->lock); z = NULL; + if(node != NULL) { + lock_rw_unlock(&node->lock); + node = NULL; + } } if(z || node) { break; From eb82c368e08e2fd7ec4044a3a6e3902819135560 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 12 Mar 2021 08:54:00 +0100 Subject: [PATCH 30/68] - man page contents, list new triggers and actions. --- doc/example.conf.in | 9 +++++---- doc/unbound.conf.5.in | 8 ++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/doc/example.conf.in b/doc/example.conf.in index b51bcfca5..b4870a344 100644 --- a/doc/example.conf.in +++ b/doc/example.conf.in @@ -1104,10 +1104,11 @@ remote-control: # dnstap-log-forwarder-response-messages: no # Response Policy Zones -# RPZ policies. Applied in order of configuration. QNAME and Response IP -# Address trigger are the only supported triggers. Supported actions are: -# NXDOMAIN, NODATA, PASSTHRU, DROP and Local Data. Policies can be loaded from -# file, using zone transfer, or using HTTP. The respip module needs to be added +# RPZ policies. Applied in order of configuration. QNAME, Response IP +# Address, nsdname, nsip and clientip triggers are supported. Supported +# actions are: NXDOMAIN, NODATA, PASSTHRU, DROP, Local Data, tcp-only +# and drop. Policies can be loaded from a file, or using zone +# transfer, or using HTTP. The respip module needs to be added # to the module-config, e.g.: module-config: "respip validator iterator". # rpz: # name: "rpz.example.com" diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in index e7964d969..06e9e9ad3 100644 --- a/doc/unbound.conf.5.in +++ b/doc/unbound.conf.5.in @@ -2329,10 +2329,10 @@ with a different name. RPZ clauses are applied in order of configuration. The \fBrespip\fR module needs to be added to the \fBmodule-config\fR, e.g.: \fBmodule-config: "respip validator iterator"\fR. .P -Only the QNAME and Response IP Address triggers are supported. The supported RPZ -actions are: NXDOMAIN, NODATA, PASSTHRU, DROP and Local Data. RPZ QNAME triggers -are applied after -\fBlocal-zones\fR and before \fBauth-zones\fR. +QNAME, Response IP Address, nsdname, nsip and clientip triggers are supported. +Supported actions are: NXDOMAIN, NODATA, PASSTHRU, DROP, Local Data, tcp\-only +and drop. RPZ QNAME triggers are applied after \fBlocal\-zones\fR and +before \fBauth\-zones\fR. .TP .B name: \fI Name of the authority zone. From 7a5d82dd48704b1a854c2802afcbebba980e1792 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 12 Mar 2021 09:10:11 +0100 Subject: [PATCH 31/68] - rpz trigger, fix merge for unsupported ipaddr based trigger action. --- services/rpz.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/services/rpz.c b/services/rpz.c index cf2c10191..843036f34 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -704,15 +704,6 @@ rpz_insert_ipaddr_based_trigger(struct respip_set* set, struct sockaddr_storage* enum respip_action respa = rpz_action_to_respip_action(a); lock_rw_wrlock(&set->lock); - if(a == RPZ_TCP_ONLY_ACTION || a == RPZ_INVALID_ACTION || - respa == respip_invalid) { - char str[255+1]; - dname_str(dname, str); - verbose(VERB_ALGO, "RPZ: respip trigger, %s skipping unsupported action: %s", - str, rpz_action_to_string(a)); - return 0; - } - rrstr = sldns_wire2str_rr(rr, rr_len); if(rrstr == NULL) { log_err("malloc error while inserting rpz ipaddr based trigger"); @@ -949,6 +940,15 @@ rpz_insert_response_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, return 0; } + if(a == RPZ_TCP_ONLY_ACTION || a == RPZ_INVALID_ACTION || + rpz_action_to_respip_action(a) == respip_invalid) { + char str[255+1]; + dname_str(dname, str); + verbose(VERB_ALGO, "RPZ: respip trigger, %s skipping unsupported action: %s", + str, rpz_action_to_string(a)); + return 0; + } + return rpz_insert_ipaddr_based_trigger(r->respip_set, &addr, addrlen, net, a, rrtype, rrclass, ttl, rdata, rdata_len, rr, rr_len); } From c7bb118a883de708a268fd0d841170563c8ddc94 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 12 Mar 2021 09:18:38 +0100 Subject: [PATCH 32/68] - rpz trigger, fix tcp only action after merge --- services/rpz.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/rpz.c b/services/rpz.c index 843036f34..e9d1226a0 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -569,7 +569,7 @@ rpz_insert_local_zones_trigger(struct local_zones* lz, uint8_t* dname, int dnamelabs = dname_count_labels(dname); int newzone = 0; - if(a == RPZ_TCP_ONLY_ACTION || a == RPZ_INVALID_ACTION) { + if(a == RPZ_INVALID_ACTION) { char str[255+1]; if(rrtype == LDNS_RR_TYPE_SOA || rrtype == LDNS_RR_TYPE_NS || rrtype == LDNS_RR_TYPE_DNAME || @@ -940,7 +940,7 @@ rpz_insert_response_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, return 0; } - if(a == RPZ_TCP_ONLY_ACTION || a == RPZ_INVALID_ACTION || + if(a == RPZ_INVALID_ACTION || rpz_action_to_respip_action(a) == respip_invalid) { char str[255+1]; dname_str(dname, str); From 4f892a37bd15be83519515a9a3b7fdf1da669e6a Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 12 Mar 2021 10:21:29 +0100 Subject: [PATCH 33/68] - rpz triggers, spelling fix. --- services/rpz.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/rpz.c b/services/rpz.c index e9d1226a0..1e6cc0bd2 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -77,7 +77,7 @@ rpz_action_to_string(enum rpz_action a) case RPZ_DISABLED_ACTION: return "rpz-disabled"; case RPZ_CNAME_OVERRIDE_ACTION: return "rpz-cname-override"; case RPZ_NO_OVERRIDE_ACTION: return "rpz-no-override"; - default: return "rpz-unkown-action"; + default: return "rpz-unknown-action"; } } From da0bbcec483a86e8f96533d0872a48fbd884a90c Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 12 Mar 2021 10:21:48 +0100 Subject: [PATCH 34/68] - rpz triggers, man page explanation of rpz zone contents. --- doc/unbound.conf.5.in | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in index a8c323e2f..c8d41d7d9 100644 --- a/doc/unbound.conf.5.in +++ b/doc/unbound.conf.5.in @@ -2389,6 +2389,44 @@ QNAME, Response IP Address, nsdname, nsip and clientip triggers are supported. Supported actions are: NXDOMAIN, NODATA, PASSTHRU, DROP, Local Data, tcp\-only and drop. RPZ QNAME triggers are applied after \fBlocal\-zones\fR and before \fBauth\-zones\fR. +.P +The rpz zone is formatted with a SOA start record as usual. The items in +the zone are entries, that specify what to act on (the trigger) and what to +do (the action). The trigger to act on is recorded in the name, the action +to do is recorded as the resource record. The names all end in the zone +name, so you could type them without a trailing dot in the zonefile. +.P +An example RPZ record, that answers example.com with NXDOMAIN +.nf + example.com CNAME . +.fi +.P +The triggers are encoded in the name on the left +.nf + name query name + netblock.rpz-client-ip. client IP address + netblock.rpz-ip. response IP address in the answer + name.rpz-nsdname. nameserver name + netblock.rpz-nsip. nameserver IP address +.fi +The netblock is written as .. +For IPv6 use 'zz' for '::'. Specify indiviual addresses with scope length +of 32 or 128. For example, 24.10.100.51.198.rpz-ip is 198.51.100.10/24 and +32.10.zz.db8.2001.rpz-ip is 2001:db8:0:0:0:0:0:10/32. +.P +The actions are specified with the record on the right +.nf + CNAME . nxdomain reply + CNAME *. nodata reply + CNAME rpz-passthru. do nothing, allow to continue + CNAME rpz-drop. the query is dropped + CNAME rpz-tcp-only. answer over TCP + A 192.0.2.1 answer with this IP address +.fi +Other records like AAAA, TXT and other CNAMEs (not rpz-..) can also be used to +answer queries with that content. +.P +The RPZ zones can be configured in the config file with these settings in the \fBrpz:\fR block. .TP .B name: \fI Name of the authority zone. From 263f9741203af6ef675fb8bc9f0a93f448ee25e8 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 12 Mar 2021 10:23:20 +0100 Subject: [PATCH 35/68] rpz triggers, man page fix end label --- doc/unbound.conf.5.in | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in index c8d41d7d9..95f0c0038 100644 --- a/doc/unbound.conf.5.in +++ b/doc/unbound.conf.5.in @@ -2394,7 +2394,8 @@ The rpz zone is formatted with a SOA start record as usual. The items in the zone are entries, that specify what to act on (the trigger) and what to do (the action). The trigger to act on is recorded in the name, the action to do is recorded as the resource record. The names all end in the zone -name, so you could type them without a trailing dot in the zonefile. +name, so you could type the trigger names without a trailing dot in the +zonefile. .P An example RPZ record, that answers example.com with NXDOMAIN .nf @@ -2404,10 +2405,10 @@ An example RPZ record, that answers example.com with NXDOMAIN The triggers are encoded in the name on the left .nf name query name - netblock.rpz-client-ip. client IP address - netblock.rpz-ip. response IP address in the answer - name.rpz-nsdname. nameserver name - netblock.rpz-nsip. nameserver IP address + netblock.rpz-client-ip client IP address + netblock.rpz-ip response IP address in the answer + name.rpz-nsdname nameserver name + netblock.rpz-nsip nameserver IP address .fi The netblock is written as .. For IPv6 use 'zz' for '::'. Specify indiviual addresses with scope length From 62674aaff8e7972bf050c56c77f518d27f606aee Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 12 Mar 2021 13:13:17 +0100 Subject: [PATCH 36/68] - rpz triggers, fix to put braces around operator. --- doc/unbound.conf.5.in | 2 +- services/localzone.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in index 95f0c0038..d98514af5 100644 --- a/doc/unbound.conf.5.in +++ b/doc/unbound.conf.5.in @@ -2411,7 +2411,7 @@ The triggers are encoded in the name on the left netblock.rpz-nsip nameserver IP address .fi The netblock is written as .. -For IPv6 use 'zz' for '::'. Specify indiviual addresses with scope length +For IPv6 use 'zz' for '::'. Specify individual addresses with scope length of 32 or 128. For example, 24.10.100.51.198.rpz-ip is 198.51.100.10/24 and 32.10.zz.db8.2001.rpz-ip is 2001:db8:0:0:0:0:0:10/32. .P diff --git a/services/localzone.c b/services/localzone.c index fa586ef38..a24137eac 100644 --- a/services/localzone.c +++ b/services/localzone.c @@ -1604,7 +1604,7 @@ local_zones_zone_answer(struct local_zone* z, struct module_env* env, lz_type == local_zone_always_nodata || lz_type == local_zone_truncate)? LDNS_RCODE_NOERROR:LDNS_RCODE_NXDOMAIN; - rcode = lz_type == local_zone_truncate ? (rcode|BIT_TC) : rcode; + rcode = (lz_type == local_zone_truncate ? (rcode|BIT_TC) : rcode); if(z != NULL && z->soa && z->soa_negative) return local_encode(qinfo, env, edns, repinfo, buf, temp, z->soa_negative, 0, rcode); From 5ea042c863e0c5d9d44a2ac5cd3263a37e7e7b49 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 12 Mar 2021 13:24:17 +0100 Subject: [PATCH 37/68] - rpz triggers, document statistics and fix comment about statistics. --- doc/unbound-control.8.in | 4 ++-- services/rpz.c | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/doc/unbound-control.8.in b/doc/unbound-control.8.in index 20325abf2..bdb14cff0 100644 --- a/doc/unbound-control.8.in +++ b/doc/unbound-control.8.in @@ -685,8 +685,8 @@ specific cache, after getting processed by the edns client subnet module. .TP .I num.rpz.action. Number of queries answered using configured RPZ policy, per RPZ action type. -Possible actions are: nxdomain, nodata, passthru, drop, local_data, disabled, -and cname_override. +Possible actions are: nxdomain, nodata, passthru, drop, tcp\-only, local\-data, +disabled, and cname\-override. .SH "FILES" .TP .I @ub_conf_file@ diff --git a/services/rpz.c b/services/rpz.c index 1e6cc0bd2..568ae9840 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -1388,7 +1388,6 @@ rpz_resolve_client_action_and_zone(struct auth_zones* az, struct query_info* qin log_rpz_apply(z->name, r->action_override, qinfo, repinfo, r->log_name); - /* TODO only register stats when stats_extended? */ stats->rpz_action[r->action_override]++; lock_rw_unlock(&z->lock); z = NULL; From 2d4523d94615e84dfa987a4817c6dcb67ca522bf Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 12 Mar 2021 14:32:10 +0100 Subject: [PATCH 38/68] - rpz triggers, fix that nsdname suffix removal returns allocated memory of the correct length and the correct dnamelen. --- services/rpz.c | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/services/rpz.c b/services/rpz.c index 568ae9840..c4b15445c 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -664,12 +664,34 @@ rpz_insert_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, rrclass, ttl, rdata, rdata_len, rr, rr_len); } -static void -rpz_strip_nsdname_suffix(uint8_t* dname, size_t maxdnamelen) +static int +rpz_strip_nsdname_suffix(uint8_t* dname, size_t maxdnamelen, + uint8_t** stripdname, size_t* stripdnamelen) { uint8_t* stripped = get_tld_label(dname, maxdnamelen); - if(stripped == NULL) { return; } + uint8_t swap; + if(stripped == NULL) { + *stripdname = memdup(dname, maxdnamelen); + if(!*stripdname) { + log_err("malloc failure for rpz strip suffix"); + return 0; + } + *stripdnamelen = maxdnamelen; + return 1; + } + /* shorten the domain name briefly, + * then we allocate a new name with the correct length */ + swap = *stripped; *stripped = 0; + (void)dname_count_size_labels(dname, stripdnamelen); + *stripdname = memdup(dname, *stripdnamelen); + *stripped = swap; + if(!*stripdname) { + *stripdnamelen = 0; + log_err("malloc failure for rpz strip suffix"); + return 0; + } + return 1; } static void @@ -677,20 +699,26 @@ rpz_insert_nsdname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl, uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len) { + uint8_t* dname_stripped = NULL; + size_t dnamelen_stripped = 0; verbose(VERB_ALGO, "rpz: insert nsdname trigger: %s", rpz_action_to_string(a)); rpz_log_dname("insert nsdname trigger", dname, dnamelen); - rpz_strip_nsdname_suffix(dname, dnamelen); - rpz_log_dname("insert nsdname trigger (stripped)", dname, dnamelen); + rpz_strip_nsdname_suffix(dname, dnamelen, &dname_stripped, + &dnamelen_stripped); + rpz_log_dname("insert nsdname trigger (stripped)", dname_stripped, dnamelen_stripped); if(a == RPZ_INVALID_ACTION) { verbose(VERB_ALGO, "rpz: skipping invalid action"); + free(dname_stripped); free(dname); return; } - rpz_insert_local_zones_trigger(r->nsdname_zones, dname, dnamelen, a, rrtype, - rrclass, ttl, rdata, rdata_len, rr, rr_len); + rpz_insert_local_zones_trigger(r->nsdname_zones, dname_stripped, + dnamelen_stripped, a, rrtype, rrclass, ttl, rdata, rdata_len, + rr, rr_len); + free(dname); } static int From e7afe0b153cb70e9918a96bffdecf14a13ac44ef Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 12 Mar 2021 14:35:34 +0100 Subject: [PATCH 39/68] - rpz triggers, rpz_insert_rr nicer. --- services/rpz.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/services/rpz.c b/services/rpz.c index c4b15445c..c8ce5ca0f 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -711,14 +711,13 @@ rpz_insert_nsdname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, if(a == RPZ_INVALID_ACTION) { verbose(VERB_ALGO, "rpz: skipping invalid action"); free(dname_stripped); - free(dname); return; } + /* dname_stripped is consumed or freed by the insert routine */ rpz_insert_local_zones_trigger(r->nsdname_zones, dname_stripped, dnamelen_stripped, a, rrtype, rrclass, ttl, rdata, rdata_len, rr, rr_len); - free(dname); } static int @@ -1047,10 +1046,10 @@ rpz_insert_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, uint8_t* dname, rr_len); free(policydname); } else if(t == RPZ_NSDNAME_TRIGGER) { - // policydname will be consumed, no free rpz_insert_nsdname_trigger(r, policydname, policydnamelen, a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr, rr_len); + free(policydname); } else { free(policydname); verbose(VERB_ALGO, "rpz: skipping unsupported trigger: %s", From 1a528238e2b855352ad514356230ad988f59d966 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 12 Mar 2021 16:07:37 +0100 Subject: [PATCH 40/68] - rpz triggers, use tld name for tld pointer in suffix removal. --- services/rpz.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/services/rpz.c b/services/rpz.c index c8ce5ca0f..3988bba46 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -668,11 +668,17 @@ static int rpz_strip_nsdname_suffix(uint8_t* dname, size_t maxdnamelen, uint8_t** stripdname, size_t* stripdnamelen) { - uint8_t* stripped = get_tld_label(dname, maxdnamelen); + uint8_t* tldstart = get_tld_label(dname, maxdnamelen); uint8_t swap; - if(stripped == NULL) { + if(tldstart == NULL) { + if(dname == NULL) { + *stripdname = NULL; + *stripdnamelen = 0; + return 0; + } *stripdname = memdup(dname, maxdnamelen); if(!*stripdname) { + *stripdnamelen = 0; log_err("malloc failure for rpz strip suffix"); return 0; } @@ -681,11 +687,11 @@ rpz_strip_nsdname_suffix(uint8_t* dname, size_t maxdnamelen, } /* shorten the domain name briefly, * then we allocate a new name with the correct length */ - swap = *stripped; - *stripped = 0; + swap = *tldstart; + *tldstart = 0; (void)dname_count_size_labels(dname, stripdnamelen); *stripdname = memdup(dname, *stripdnamelen); - *stripped = swap; + *tldstart = swap; if(!*stripdname) { *stripdnamelen = 0; log_err("malloc failure for rpz strip suffix"); @@ -701,12 +707,14 @@ rpz_insert_nsdname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, { uint8_t* dname_stripped = NULL; size_t dnamelen_stripped = 0; - verbose(VERB_ALGO, "rpz: insert nsdname trigger: %s", rpz_action_to_string(a)); + verbose(VERB_ALGO, "rpz: insert nsdname trigger: %s", + rpz_action_to_string(a)); rpz_log_dname("insert nsdname trigger", dname, dnamelen); rpz_strip_nsdname_suffix(dname, dnamelen, &dname_stripped, &dnamelen_stripped); - rpz_log_dname("insert nsdname trigger (stripped)", dname_stripped, dnamelen_stripped); + rpz_log_dname("insert nsdname trigger (stripped)", dname_stripped, + dnamelen_stripped); if(a == RPZ_INVALID_ACTION) { verbose(VERB_ALGO, "rpz: skipping invalid action"); From 7f39003c04c2f663f5e1697b7be22724076b5e5b Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 19 Mar 2021 17:31:44 +0100 Subject: [PATCH 41/68] - rpz triggers, implement qname trigger after cname. --- iterator/iterator.c | 32 ++++++++++++ services/rpz.c | 112 +++++++++++++++++++++++++++++++++++++++-- services/rpz.h | 2 + testdata/rpz_qname.rpl | 29 +++++++++++ 4 files changed, 171 insertions(+), 4 deletions(-) diff --git a/iterator/iterator.c b/iterator/iterator.c index 1ebbfe277..63f8cbd56 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -3013,6 +3013,38 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, /* set the current request's qname to the new value. */ iq->qchase.qname = sname; iq->qchase.qname_len = snamelen; + if(qstate->env->auth_zones) { + /* apply rpz qname triggers after cname */ + struct dns_msg* forged_response = + rpz_callback_from_iterator_cname(qstate, iq); + while(forged_response && reply_find_rrset_section_an( + forged_response->rep, iq->qchase.qname, + iq->qchase.qname_len, LDNS_RR_TYPE_CNAME, + iq->qchase.qclass)) { + /* another cname to follow */ + if(!handle_cname_response(qstate, iq, forged_response, + &sname, &snamelen)) { + errinf(qstate, "malloc failure, CNAME info"); + return error_response(qstate, id, LDNS_RCODE_SERVFAIL); + } + iq->qchase.qname = sname; + iq->qchase.qname_len = snamelen; + forged_response = + rpz_callback_from_iterator_cname(qstate, iq); + } + if(forged_response != NULL) { + qstate->ext_state[id] = module_finished; + qstate->return_rcode = FLAGS_GET_RCODE(forged_response->rep->flags); + qstate->return_msg = forged_response; + next_state(iq, FINISHED_STATE); + if(!iter_prepend(iq, qstate->return_msg, qstate->region)) { + log_err("rpz, prepend rrsets: out of memory"); + return error_response(qstate, id, LDNS_RCODE_SERVFAIL); + } + qstate->return_msg->qinfo = qstate->qinfo; + return 0; + } + } /* Clear the query state, since this is a query restart. */ iq->deleg_msg = NULL; iq->dp = NULL; diff --git a/services/rpz.c b/services/rpz.c index 3988bba46..538429e51 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -1607,10 +1607,9 @@ rpz_synthesize_nxdomain(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms) static inline struct dns_msg* rpz_synthesize_localdata_from_rrset(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms, - struct local_rrset* rrset) + struct query_info* qi, struct local_rrset* rrset) { struct dns_msg* msg = NULL; - struct query_info* qi = &ms->qinfo; struct reply_info* new_reply_info; struct ub_packed_rrset_key* rp; @@ -1659,7 +1658,7 @@ rpz_synthesize_nsip_localdata(struct rpz* r, struct module_qstate* ms, return NULL; } - return rpz_synthesize_localdata_from_rrset(r, ms, rrset); + return rpz_synthesize_localdata_from_rrset(r, ms, &ms->qinfo, rrset); } // copy'n'paste from localzone.c @@ -1708,7 +1707,32 @@ rpz_synthesize_nsdname_localdata(struct rpz* r, struct module_qstate* ms, return NULL; } - return rpz_synthesize_localdata_from_rrset(r, ms, rrset); + return rpz_synthesize_localdata_from_rrset(r, ms, &ms->qinfo, rrset); +} + +/* like local_data_answer for qname triggers after a cname */ +static struct dns_msg* +rpz_synthesize_qname_localdata_msg(struct rpz* r, struct module_qstate* ms, + struct query_info* qinfo, struct local_zone* z) +{ + struct local_data key; + struct local_data* ld; + struct local_rrset* rrset; + key.node.key = &key; + key.name = qinfo->qname; + key.namelen = qinfo->qname_len; + key.namelabs = dname_count_labels(qinfo->qname); + ld = (struct local_data*)rbtree_search(&z->data, &key.node); + if(ld == NULL) { + verbose(VERB_ALGO, "rpz: qname after cname: name not found"); + return NULL; + } + rrset = local_data_find_type(ld, qinfo->qtype, 1); + if(rrset == NULL) { + verbose(VERB_ALGO, "rpz: qname after cname: type not found"); + return NULL; + } + return rpz_synthesize_localdata_from_rrset(r, ms, qinfo, rrset); } static int @@ -1965,6 +1989,86 @@ rpz_callback_from_iterator_module(struct module_qstate* ms, struct iter_qstate* else { return NULL; } } +struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms, + struct iter_qstate* is) +{ + struct auth_zones* az; + struct auth_zone* a = NULL; + struct rpz* r = NULL; + struct local_zone* z = NULL; + enum localzone_type lzt; + struct dns_msg* ret = NULL; + + if(ms->env == NULL || ms->env->auth_zones == NULL) { return 0; } + az = ms->env->auth_zones; + + lock_rw_rdlock(&az->rpz_lock); + + for(a = az->rpz_first; a; a = a->rpz_az_next) { + lock_rw_rdlock(&a->lock); + r = a->rpz; + if(r->disabled) { + lock_rw_unlock(&a->lock); + continue; + } + z = rpz_find_zone(r->local_zones, is->qchase.qname, + is->qchase.qname_len, is->qchase.qclass, 0, 0, 0); + if(z && r->action_override == RPZ_DISABLED_ACTION) { + lock_rw_unlock(&z->lock); + z = NULL; + } + if(z) { + break; + } + /* not found in this auth_zone */ + lock_rw_unlock(&a->lock); + } + lock_rw_unlock(&az->rpz_lock); + + if(z == NULL) + return NULL; + if(r->action_override == RPZ_NO_OVERRIDE_ACTION) { + lzt = z->type; + } else { + lzt = rpz_action_to_localzone_type(r->action_override); + } + + verbose(VERB_ALGO, "rpz: qname trigger after cname, with action=%s", + rpz_action_to_string(localzone_type_to_rpz_action(lzt))); + switch(localzone_type_to_rpz_action(lzt)) { + case RPZ_NXDOMAIN_ACTION: + ret = rpz_synthesize_nxdomain(r, ms); + break; + case RPZ_NODATA_ACTION: + ret = rpz_synthesize_nodata(r, ms); + break; + case RPZ_TCP_ONLY_ACTION: + /* basically a passthru here but the tcp-only will be + * honored before the query gets sent. */ + ms->respip_action_info->action = respip_truncate; + ret = NULL; + break; + case RPZ_DROP_ACTION: + ret = rpz_synthesize_nodata(r, ms); + ms->is_drop = 1; + break; + case RPZ_LOCAL_DATA_ACTION: + ret = rpz_synthesize_qname_localdata_msg(r, ms, &is->qchase, z); + if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms); } + break; + case RPZ_PASSTHRU_ACTION: + ret = NULL; + break; + default: + verbose(VERB_ALGO, "rpz: qname trigger after cname: bug: unhandled or invalid action: '%s'", + rpz_action_to_string(localzone_type_to_rpz_action(lzt))); + ret = NULL; + } + lock_rw_unlock(&z->lock); + lock_rw_unlock(&a->lock); + return ret; +} + static int rpz_apply_maybe_clientip_trigger(struct auth_zones* az, struct module_env* env, struct query_info* qinfo, struct edns_data* edns, struct comm_reply* repinfo, diff --git a/services/rpz.h b/services/rpz.h index 62c345f29..74a8d09e2 100644 --- a/services/rpz.h +++ b/services/rpz.h @@ -183,6 +183,8 @@ int rpz_callback_from_worker_request(struct auth_zones* az, struct module_env* e struct iter_qstate; struct dns_msg* rpz_callback_from_iterator_module(struct module_qstate*, struct iter_qstate*); +struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate*, struct iter_qstate*); + /** * Delete RPZ * @param r: RPZ struct to delete diff --git a/testdata/rpz_qname.rpl b/testdata/rpz_qname.rpl index dd47f188a..2bc038c37 100644 --- a/testdata/rpz_qname.rpl +++ b/testdata/rpz_qname.rpl @@ -126,6 +126,16 @@ SECTION ANSWER something.e.b.example. IN TXT "*.b.example. answer from upstream ns" ENTRY_END +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +f.example. IN TXT +SECTION ANSWER +f.example. IN CNAME d. +ENTRY_END + RANGE_END ; tcp. @@ -367,5 +377,24 @@ SECTION ANSWER tcp. IN TXT "tcp. answer from upstream ns" ENTRY_END +; check if the name after the CNAME has the qname trigger applied to it. +STEP 100 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +f.example. IN TXT +ENTRY_END + +STEP 101 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +f.example. IN TXT +SECTION ANSWER +f.example. IN CNAME d. +d. IN TXT "local data 2nd zone" +ENTRY_END + ; no answer is checked at exit of testbound. SCENARIO_END From 5a23c849519da1046021d404c284df1aa471af5a Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 19 Mar 2021 17:36:09 +0100 Subject: [PATCH 42/68] - rpz triggers, fix query name on trigger after cname. --- services/rpz.c | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/services/rpz.c b/services/rpz.c index 538429e51..1daaf9bf2 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -1566,11 +1566,12 @@ rpz_dns_msg_new(struct regional* region) } static inline struct dns_msg* -rpz_synthesize_nodata(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms) +rpz_synthesize_nodata(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms, + struct query_info* qinfo) { struct dns_msg* msg = rpz_dns_msg_new(ms->region); if(msg == NULL) { return msg; } - msg->qinfo = ms->qinfo; + msg->qinfo = *qinfo; msg->rep = construct_reply_info_base(ms->region, LDNS_RCODE_NOERROR | BIT_RD | BIT_QR | BIT_AA | BIT_RA, 1, //qd @@ -1586,11 +1587,12 @@ rpz_synthesize_nodata(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms) } static inline struct dns_msg* -rpz_synthesize_nxdomain(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms) +rpz_synthesize_nxdomain(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms, + struct query_info* qinfo) { struct dns_msg* msg = rpz_dns_msg_new(ms->region); if(msg == NULL) { return msg; } - msg->qinfo = ms->qinfo; + msg->qinfo = *qinfo; msg->rep = construct_reply_info_base(ms->region, LDNS_RCODE_NXDOMAIN | BIT_RD | BIT_QR | BIT_AA | BIT_RA, 1, //qd @@ -1820,16 +1822,16 @@ rpz_apply_nsip_trigger(struct module_qstate* ms, struct rpz* r, if(action == RPZ_LOCAL_DATA_ACTION && raddr->data == NULL) { verbose(VERB_ALGO, "rpz: bug: nsip local data action but no local data"); - ret = rpz_synthesize_nodata(r, ms); + ret = rpz_synthesize_nodata(r, ms, &ms->qinfo); goto done; } switch(action) { case RPZ_NXDOMAIN_ACTION: - ret = rpz_synthesize_nxdomain(r, ms); + ret = rpz_synthesize_nxdomain(r, ms, &ms->qinfo); break; case RPZ_NODATA_ACTION: - ret = rpz_synthesize_nodata(r, ms); + ret = rpz_synthesize_nodata(r, ms, &ms->qinfo); break; case RPZ_TCP_ONLY_ACTION: // basically a passthru here but the tcp-only will be @@ -1838,12 +1840,12 @@ rpz_apply_nsip_trigger(struct module_qstate* ms, struct rpz* r, ret = NULL; break; case RPZ_DROP_ACTION: - ret = rpz_synthesize_nodata(r, ms); + ret = rpz_synthesize_nodata(r, ms, &ms->qinfo); ms->is_drop = 1; break; case RPZ_LOCAL_DATA_ACTION: ret = rpz_synthesize_nsip_localdata(r, ms, raddr); - if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms); } + if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, &ms->qinfo); } break; case RPZ_PASSTHRU_ACTION: ret = NULL; @@ -1876,10 +1878,10 @@ rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r, switch(action) { case RPZ_NXDOMAIN_ACTION: - ret = rpz_synthesize_nxdomain(r, ms); + ret = rpz_synthesize_nxdomain(r, ms, &ms->qinfo); break; case RPZ_NODATA_ACTION: - ret = rpz_synthesize_nodata(r, ms); + ret = rpz_synthesize_nodata(r, ms, &ms->qinfo); break; case RPZ_TCP_ONLY_ACTION: // basically a passthru here but the tcp-only will be @@ -1888,12 +1890,12 @@ rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r, ret = NULL; break; case RPZ_DROP_ACTION: - ret = rpz_synthesize_nodata(r, ms); + ret = rpz_synthesize_nodata(r, ms, &ms->qinfo); ms->is_drop = 1; break; case RPZ_LOCAL_DATA_ACTION: ret = rpz_synthesize_nsdname_localdata(r, ms, z, match); - if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms); } + if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, &ms->qinfo); } break; case RPZ_PASSTHRU_ACTION: ret = NULL; @@ -2037,10 +2039,10 @@ struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms, rpz_action_to_string(localzone_type_to_rpz_action(lzt))); switch(localzone_type_to_rpz_action(lzt)) { case RPZ_NXDOMAIN_ACTION: - ret = rpz_synthesize_nxdomain(r, ms); + ret = rpz_synthesize_nxdomain(r, ms, &is->qchase); break; case RPZ_NODATA_ACTION: - ret = rpz_synthesize_nodata(r, ms); + ret = rpz_synthesize_nodata(r, ms, &is->qchase); break; case RPZ_TCP_ONLY_ACTION: /* basically a passthru here but the tcp-only will be @@ -2049,12 +2051,12 @@ struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms, ret = NULL; break; case RPZ_DROP_ACTION: - ret = rpz_synthesize_nodata(r, ms); + ret = rpz_synthesize_nodata(r, ms, &is->qchase); ms->is_drop = 1; break; case RPZ_LOCAL_DATA_ACTION: ret = rpz_synthesize_qname_localdata_msg(r, ms, &is->qchase, z); - if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms); } + if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, &is->qchase); } break; case RPZ_PASSTHRU_ACTION: ret = NULL; From 81cd0d76c8952f49fc6007e18f496c3eb273ff65 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 22 Mar 2021 09:39:12 +0100 Subject: [PATCH 43/68] - rpz-triggers, call rpz callback only if there are auth zones configured. --- iterator/iterator.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/iterator/iterator.c b/iterator/iterator.c index 63f8cbd56..f83170c16 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -2471,7 +2471,8 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, /* Add the current set of unused targets to our queue. */ delegpt_add_unused_targets(iq->dp); - { /* apply rpz triggers at query time */ + if(qstate->env->auth_zones) { + /* apply rpz triggers at query time */ struct dns_msg* forged_response = rpz_callback_from_iterator_module(qstate, iq); if(forged_response != NULL) { qstate->ext_state[id] = module_finished; From 8e7ced72e5c68671c8586152ea518df61b7b2e05 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 22 Mar 2021 09:42:04 +0100 Subject: [PATCH 44/68] - rpz-triggers, fix that after cname an nsdname or nsip trigger has cname rrsets prepended by the iterator. --- iterator/iterator.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/iterator/iterator.c b/iterator/iterator.c index f83170c16..0883b91b0 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -2479,6 +2479,10 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, qstate->return_rcode = FLAGS_GET_RCODE(forged_response->rep->flags); qstate->return_msg = forged_response; next_state(iq, FINISHED_STATE); + if(!iter_prepend(iq, qstate->return_msg, qstate->region)) { + log_err("rpz, prepend rrsets: out of memory"); + return error_response(qstate, id, LDNS_RCODE_SERVFAIL); + } return 0; } } @@ -3039,7 +3043,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, qstate->return_msg = forged_response; next_state(iq, FINISHED_STATE); if(!iter_prepend(iq, qstate->return_msg, qstate->region)) { - log_err("rpz, prepend rrsets: out of memory"); + log_err("rpz after cname, prepend rrsets: out of memory"); return error_response(qstate, id, LDNS_RCODE_SERVFAIL); } qstate->return_msg->qinfo = qstate->qinfo; From 3dd804755bec46dc9bee035c38ee2b081c3d41d2 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 22 Mar 2021 09:49:22 +0100 Subject: [PATCH 45/68] - rpz-triggers, function documentation comments for iterator callback functions. --- services/rpz.c | 2 +- services/rpz.h | 25 ++++++++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/services/rpz.c b/services/rpz.c index 1daaf9bf2..24029537b 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -1068,7 +1068,7 @@ rpz_insert_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, uint8_t* dname, /** * Find RPZ local-zone by qname. - * @param r: rpz containing local-zone tree + * @param zones: local-zone tree * @param qname: qname * @param qname_len: length of qname * @param qclass: qclass diff --git a/services/rpz.h b/services/rpz.h index 74a8d09e2..642548bdd 100644 --- a/services/rpz.h +++ b/services/rpz.h @@ -50,6 +50,7 @@ #include "sldns/sbuffer.h" #include "daemon/stats.h" #include "respip/respip.h" +struct iter_qstate; /** * RPZ triggers, only the QNAME trigger is currently supported in Unbound. @@ -180,10 +181,28 @@ int rpz_callback_from_worker_request(struct auth_zones* az, struct module_env* e struct regional* temp, struct comm_reply* repinfo, uint8_t* taglist, size_t taglen, struct ub_server_stats* stats); -struct iter_qstate; -struct dns_msg* rpz_callback_from_iterator_module(struct module_qstate*, struct iter_qstate*); +/** + * Callback to process when the iterator module is about to send queries. + * Checks for nsip and nsdname triggers. + * @param qstate: the query state. + * @param iq: iterator module query state. + * @return NULL if nothing is done. Or a new message with the contents from + * the rpz, based on the delegation point. It is allocated in the + * qstate region. + */ +struct dns_msg* rpz_callback_from_iterator_module(struct module_qstate* qstate, + struct iter_qstate* iq); -struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate*, struct iter_qstate*); +/** + * Callback to process when the iterator module has followed a cname. + * There can be a qname trigger for the new query name. + * @param qstate: the query state. + * @param iq: iterator module query state. + * @return NULL if nothing is done. Or a new message with the contents from + * the rpz, based on the iq.qchase. It is allocated in the qstate region. + */ +struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* qstate, + struct iter_qstate* iq); /** * Delete RPZ From a8e0de95b8f47817340ab5d661c16bce71325610 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 22 Mar 2021 09:52:00 +0100 Subject: [PATCH 46/68] - rpz-triggers, remove unused variable warning. --- services/rpz.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/services/rpz.c b/services/rpz.c index 24029537b..f61225e03 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -857,8 +857,6 @@ rpz_clientip_enter_rr(struct regional* region, struct clientip_synthesized_rr* r size_t rdata_len) { struct local_rrset* rrset; - struct sockaddr* sa; - sa = (struct sockaddr*)&raddr->node.addr; if (rrtype == LDNS_RR_TYPE_CNAME && raddr->data != NULL) { log_err("CNAME response-ip data can not co-exist with other " "client-ip data"); From 1c75e628041ed9c0e680002dbc82380cf1b2b3c3 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 1 Apr 2021 12:06:14 +0200 Subject: [PATCH 47/68] - rpz-triggers, separate cache storage of RPZ records from network records. --- iterator/iterator.c | 2 ++ services/rpz.c | 9 ++++++++ testdata/rpz_nsip.rpl | 49 ++++++++++++++++++++++++++++++++++++++++ util/data/packed_rrset.h | 8 +++++++ 4 files changed, 68 insertions(+) diff --git a/iterator/iterator.c b/iterator/iterator.c index 0883b91b0..7099f05da 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -2478,6 +2478,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, qstate->ext_state[id] = module_finished; qstate->return_rcode = FLAGS_GET_RCODE(forged_response->rep->flags); qstate->return_msg = forged_response; + iq->response = forged_response; next_state(iq, FINISHED_STATE); if(!iter_prepend(iq, qstate->return_msg, qstate->region)) { log_err("rpz, prepend rrsets: out of memory"); @@ -3041,6 +3042,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, qstate->ext_state[id] = module_finished; qstate->return_rcode = FLAGS_GET_RCODE(forged_response->rep->flags); qstate->return_msg = forged_response; + iq->response = forged_response; next_state(iq, FINISHED_STATE); if(!iter_prepend(iq, qstate->return_msg, qstate->region)) { log_err("rpz after cname, prepend rrsets: out of memory"); diff --git a/services/rpz.c b/services/rpz.c index 2ab62581a..6a37cb9b3 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -1642,6 +1642,15 @@ rpz_synthesize_localdata_from_rrset(struct rpz* ATTR_UNUSED(r), struct module_qs } rp->rk.dname = qi->qname; rp->rk.dname_len = qi->qname_len; + /* this rrset is from the rpz data, or synthesized. + * It is not actually from the network, so we flag it with this + * flags as a fake RRset. If later the cache is used to look up + * rrsets, then the fake ones are not returned (if you look without + * the flag). For like CNAME lookups from the iterator or A, AAAA + * lookups for nameserver targets, it would use the without flag + * actual data. So that the actual network data and fake data + * are kept track of separately. */ + rp->rk.flags |= PACKED_RRSET_RPZ; new_reply_info->rrsets[0] = rp; msg->rep = new_reply_info; return msg; diff --git a/testdata/rpz_nsip.rpl b/testdata/rpz_nsip.rpl index ac9e80b80..11d1999b6 100644 --- a/testdata/rpz_nsip.rpl +++ b/testdata/rpz_nsip.rpl @@ -131,6 +131,18 @@ SECTION ADDITIONAL ns1.ff. IN A 8.8.6.8 ENTRY_END +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +bar. IN A +SECTION AUTHORITY +bar. IN NS gotham.ff. +SECTION ADDITIONAL +gotham.ff. IN A 192.0.5.2 +ENTRY_END + RANGE_END ; com. ----------------------------------------------------------------------- @@ -385,4 +397,41 @@ SECTION ANSWER gotham.ff. IN A 127.0.0.1 ENTRY_END +; again with more cache items +STEP 40 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +gotham.ff. IN A +ENTRY_END + +STEP 41 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +gotham.ff. IN A +SECTION ANSWER +gotham.ff. IN A 127.0.0.1 +ENTRY_END + +; query with a referral that tries to get the +; just faked A record as nameserver glue (from cache). +STEP 50 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +gotham.bar. IN A +ENTRY_END + +STEP 51 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +gotham.bar. IN A +SECTION ANSWER +gotham.bar. IN A 127.0.0.1 +ENTRY_END + SCENARIO_END diff --git a/util/data/packed_rrset.h b/util/data/packed_rrset.h index ff95c0af0..e1feb22bb 100644 --- a/util/data/packed_rrset.h +++ b/util/data/packed_rrset.h @@ -61,6 +61,13 @@ typedef uint64_t rrset_id_type; * updated on encoding in a reply. This flag is not expected to be set in * cached data. */ #define PACKED_RRSET_FIXEDTTL 0x80000000 +/** This rrset is from RPZ. It is not real, it is synthesized data to block + * access. The flag makes lookups, from cache in iterator, ignore the fake + * items and only use actual data. Eg. when the iterator looksup NS, CNAME, + * A and AAAA types, it then gets items without this flag that are the + * actual network. But messages with these records in it can be stored in + * the cache and retrieved for a reply. */ +#define PACKED_RRSET_RPZ 0x8 /** number of rrs and rrsets for integer overflow protection. More than * this is not really possible (64K packet has much less RRs and RRsets) in @@ -88,6 +95,7 @@ struct packed_rrset_key { * o PACKED_RRSET_PARENT_SIDE * o PACKED_RRSET_SOA_NEG * o PACKED_RRSET_FIXEDTTL (not supposed to be cached) + * o PACKED_RRSET_RPZ */ uint32_t flags; /** the rrset type in network format */ From 14582d2445a15b7efc4f226d72655b1574a73437 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 1 Apr 2021 12:56:47 +0200 Subject: [PATCH 48/68] - rpz-triggers, remove unused test. --- testdata/rpz_nsip.rpl | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/testdata/rpz_nsip.rpl b/testdata/rpz_nsip.rpl index 11d1999b6..a3cda6050 100644 --- a/testdata/rpz_nsip.rpl +++ b/testdata/rpz_nsip.rpl @@ -131,18 +131,6 @@ SECTION ADDITIONAL ns1.ff. IN A 8.8.6.8 ENTRY_END -ENTRY_BEGIN -MATCH opcode subdomain -ADJUST copy_id copy_query -REPLY QR NOERROR -SECTION QUESTION -bar. IN A -SECTION AUTHORITY -bar. IN NS gotham.ff. -SECTION ADDITIONAL -gotham.ff. IN A 192.0.5.2 -ENTRY_END - RANGE_END ; com. ----------------------------------------------------------------------- @@ -415,23 +403,4 @@ SECTION ANSWER gotham.ff. IN A 127.0.0.1 ENTRY_END -; query with a referral that tries to get the -; just faked A record as nameserver glue (from cache). -STEP 50 QUERY -ENTRY_BEGIN -REPLY RD -SECTION QUESTION -gotham.bar. IN A -ENTRY_END - -STEP 51 CHECK_ANSWER -ENTRY_BEGIN -MATCH all -REPLY QR RD RA NOERROR -SECTION QUESTION -gotham.bar. IN A -SECTION ANSWER -gotham.bar. IN A 127.0.0.1 -ENTRY_END - SCENARIO_END From a2ea701f31be11fd0e07cbb64cb994f359bd3411 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 1 Apr 2021 13:11:51 +0200 Subject: [PATCH 49/68] - rpz-triggers, fix what domain name to match for nsdname. --- services/rpz.c | 29 ++++++++++++----------------- testdata/rpz_nsdname.rpl | 14 +++++++------- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/services/rpz.c b/services/rpz.c index 6a37cb9b3..9af652984 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -1928,23 +1928,18 @@ rpz_delegation_point_zone_lookup(struct delegpt* dp, struct local_zones* zones, struct local_zone* z = NULL; rpz_log_dname("delegation point", dp->name, dp->namelen); - // XXX: do we want this? - z = rpz_find_zone(zones, dp->name, dp->namelen, qclass, 0, 0, 0); - if(z != NULL) { - match->dname = dp->name; - match->dname_len = dp->namelen; - } else if(z == NULL) { - for(nameserver = dp->nslist; - nameserver != NULL; - nameserver = nameserver->next) { - rpz_log_dname("delegation point", nameserver->name, nameserver->namelen); - z = rpz_find_zone(zones, nameserver->name, nameserver->namelen, - qclass, 0, 0, 0); - if(z != NULL) { - match->dname = nameserver->name; - match->dname_len = nameserver->namelen; - break; - } + /* the rpz specs match the nameserver names (NS records), not the + * name of the delegation point itself, to the nsdname triggers */ + for(nameserver = dp->nslist; + nameserver != NULL; + nameserver = nameserver->next) { + rpz_log_dname("delegation point ns", nameserver->name, nameserver->namelen); + z = rpz_find_zone(zones, nameserver->name, nameserver->namelen, + qclass, 0, 0, 0); + if(z != NULL) { + match->dname = nameserver->name; + match->dname_len = nameserver->namelen; + break; } } diff --git a/testdata/rpz_nsdname.rpl b/testdata/rpz_nsdname.rpl index 64fb98880..7b55ebeb7 100644 --- a/testdata/rpz_nsdname.rpl +++ b/testdata/rpz_nsdname.rpl @@ -16,13 +16,13 @@ rpz 3600 IN SOA ns1.rpz.example.com. hostmaster.rpz.example.com. ( 3600 IN NS ns1.rpz.example.com. 3600 IN NS ns2.rpz.example.com. $ORIGIN rpz.example.com. -gotham.aa.rpz-nsdname CNAME . -gotham.bb.rpz-nsdname CNAME *. -gotham.cc.rpz-nsdname CNAME rpz-drop. -gotham.com.rpz-nsdname CNAME rpz-passthru. -gotham.dd.rpz-nsdname CNAME rpz-tcp-only. -gotham.ff.rpz-nsdname A 127.0.0.1 -gotham.ff.rpz-nsdname TXT "42" +ns1.gotham.aa.rpz-nsdname CNAME . +ns1.gotham.bb.rpz-nsdname CNAME *. +ns1.gotham.cc.rpz-nsdname CNAME rpz-drop. +ns1.gotham.com.rpz-nsdname CNAME rpz-passthru. +ns1.gotham.dd.rpz-nsdname CNAME rpz-tcp-only. +ns1.gotham.ff.rpz-nsdname A 127.0.0.1 +ns1.gotham.ff.rpz-nsdname TXT "42" TEMPFILE_END stub-zone: From 473f0cc44be9be16068b754ae48bb9a623aa7d58 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 8 Apr 2021 14:39:48 +0200 Subject: [PATCH 50/68] - rpz-triggers, precedence fix for nsdname and nsip triggers. --- services/rpz.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/services/rpz.c b/services/rpz.c index 9af652984..7c928fb21 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -1966,21 +1966,25 @@ rpz_callback_from_iterator_module(struct module_qstate* ms, struct iter_qstate* lock_rw_rdlock(&az->rpz_lock); + /* precedencey of RPZ works, loosely, like this: + * CNAMEs in order of the CNAME chain. rpzs in the order they are + * configured. In an RPZ: first client-IP addr, then QNAME, then + * response IP, then NSDNAME, then NSIP. Longest match first. Smallest + * one from a set. */ for(a = az->rpz_first; a != NULL; a = a->rpz_az_next) { lock_rw_rdlock(&a->lock); r = a->rpz; - // XXX: check rfc which action has preference - - raddr = rpz_delegation_point_ipbased_trigger_lookup(r, is); - if(raddr != NULL) { + /* the nsdname has precedence over the nsip triggers */ + z = rpz_delegation_point_zone_lookup(is->dp, r->nsdname_zones, + ms->qinfo.qclass, &match); + if(z != NULL) { lock_rw_unlock(&a->lock); break; } - z = rpz_delegation_point_zone_lookup(is->dp, r->nsdname_zones, - ms->qinfo.qclass, &match); - if(z != NULL) { + raddr = rpz_delegation_point_ipbased_trigger_lookup(r, is); + if(raddr != NULL) { lock_rw_unlock(&a->lock); break; } From 0491176315aef21538ab0eadd9a1cdb38a4109ac Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 9 Apr 2021 08:38:18 +0200 Subject: [PATCH 51/68] - rpz-triggers, spelling --- services/rpz.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/rpz.c b/services/rpz.c index 7c928fb21..f8dba3d5b 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -1966,7 +1966,7 @@ rpz_callback_from_iterator_module(struct module_qstate* ms, struct iter_qstate* lock_rw_rdlock(&az->rpz_lock); - /* precedencey of RPZ works, loosely, like this: + /* precedence of RPZ works, loosely, like this: * CNAMEs in order of the CNAME chain. rpzs in the order they are * configured. In an RPZ: first client-IP addr, then QNAME, then * response IP, then NSDNAME, then NSIP. Longest match first. Smallest From ab94ca9684d31fbeaa6bb14bebc685ea8c9d99d6 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 14 Apr 2021 12:11:11 +0200 Subject: [PATCH 52/68] - rpz-triggers, nicer logging for nsdname and nsip triggers, one line and only when it applies to the delegation point. --- services/rpz.c | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/services/rpz.c b/services/rpz.c index f8dba3d5b..07189c198 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -709,15 +709,9 @@ rpz_insert_nsdname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, { uint8_t* dname_stripped = NULL; size_t dnamelen_stripped = 0; - verbose(VERB_ALGO, "rpz: insert nsdname trigger: %s", - rpz_action_to_string(a)); - rpz_log_dname("insert nsdname trigger", dname, dnamelen); rpz_strip_nsdname_suffix(dname, dnamelen, &dname_stripped, &dnamelen_stripped); - rpz_log_dname("insert nsdname trigger (stripped)", dname_stripped, - dnamelen_stripped); - if(a == RPZ_INVALID_ACTION) { verbose(VERB_ALGO, "rpz: skipping invalid action"); free(dname_stripped); @@ -941,7 +935,6 @@ rpz_insert_nsip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, socklen_t addrlen; int net, af; - verbose(VERB_ALGO, "rpz: insert nsip trigger: %s", rpz_action_to_string(a)); if(a == RPZ_INVALID_ACTION) { return 0; } @@ -1377,12 +1370,16 @@ rpz_ipbased_trigger_lookup(struct clientip_synthesized_rrset* set, addr, addrlen); if(raddr != NULL) { action = raddr->action; + if(verbosity >= VERB_ALGO) { + char ip[256], net[256]; + addr_to_str(addr, addrlen, ip, sizeof(ip)); + addr_to_str(&raddr->node.addr, raddr->node.addrlen, + net, sizeof(net)); + verbose(VERB_ALGO, "rpz: trigger nsip %s/%d on %s action=%s", + net, raddr->node.net, ip, rpz_action_to_string(action)); + } lock_rw_unlock(&raddr->lock); } - - verbose(VERB_ALGO, "rpz: ipbased trigger lookup: found=%d action=%s", - raddr != NULL, rpz_action_to_string(action)); - lock_rw_unlock(&set->lock); return raddr; @@ -1679,7 +1676,6 @@ local_data_find_type(struct local_data* data, uint16_t type, int alias_ok) struct local_rrset* p; type = htons(type); for(p = data->rrsets; p; p = p->next) { - verbose(VERB_ALGO, "type=%d (%d)", type, p->rrset->rk.type); if(p->rrset->rk.type == type) return p; if(alias_ok && p->rrset->rk.type == htons(LDNS_RR_TYPE_CNAME)) @@ -1826,9 +1822,6 @@ rpz_apply_nsip_trigger(struct module_qstate* ms, struct rpz* r, action = r->action_override; } - verbose(VERB_ALGO, "rpz: iterator callback: nsip: apply action=%s", - rpz_action_to_string(raddr->action)); - if(action == RPZ_LOCAL_DATA_ACTION && raddr->data == NULL) { verbose(VERB_ALGO, "rpz: bug: nsip local data action but no local data"); ret = rpz_synthesize_nodata(r, ms, &ms->qinfo); @@ -1883,8 +1876,6 @@ rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r, action = r->action_override; } - verbose(VERB_ALGO, "rpz: nsdame trigger with action=%s", rpz_action_to_string(action)); - switch(action) { case RPZ_NXDOMAIN_ACTION: ret = rpz_synthesize_nxdomain(r, ms, &ms->qinfo); @@ -1927,24 +1918,31 @@ rpz_delegation_point_zone_lookup(struct delegpt* dp, struct local_zones* zones, struct delegpt_ns* nameserver; struct local_zone* z = NULL; - rpz_log_dname("delegation point", dp->name, dp->namelen); /* the rpz specs match the nameserver names (NS records), not the * name of the delegation point itself, to the nsdname triggers */ for(nameserver = dp->nslist; nameserver != NULL; nameserver = nameserver->next) { - rpz_log_dname("delegation point ns", nameserver->name, nameserver->namelen); z = rpz_find_zone(zones, nameserver->name, nameserver->namelen, qclass, 0, 0, 0); if(z != NULL) { match->dname = nameserver->name; match->dname_len = nameserver->namelen; + if(verbosity >= VERB_ALGO) { + char nm[255+1], zn[255+1]; + dname_str(match->dname, nm); + dname_str(z->name, zn); + if(strcmp(nm, zn) != 0) + verbose(VERB_ALGO, "rpz: trigger nsdname %s on %s action=%s", + zn, nm, rpz_action_to_string(localzone_type_to_rpz_action(z->type))); + else + verbose(VERB_ALGO, "rpz: trigger nsdname %s action=%s", + nm, rpz_action_to_string(localzone_type_to_rpz_action(z->type))); + } break; } } - verbose(VERB_ALGO, "rpz: delegation point zone found=%d", z != NULL); - return z; } @@ -1971,6 +1969,8 @@ rpz_callback_from_iterator_module(struct module_qstate* ms, struct iter_qstate* * configured. In an RPZ: first client-IP addr, then QNAME, then * response IP, then NSDNAME, then NSIP. Longest match first. Smallest * one from a set. */ + /* we use the precedence rules for the topics and triggers that + * are pertinent at this stage of the resolve processing */ for(a = az->rpz_first; a != NULL; a = a->rpz_az_next) { lock_rw_rdlock(&a->lock); r = a->rpz; From a23efc70eeb257fc699cb5f13b23e148b141777d Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 14 Apr 2021 13:54:27 +0200 Subject: [PATCH 53/68] - rpz-triggers, nicer logging for qname and clientip triggers. --- services/rpz.c | 81 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 23 deletions(-) diff --git a/services/rpz.c b/services/rpz.c index 07189c198..3a998afaa 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -141,6 +141,31 @@ get_tld_label(uint8_t* dname, size_t maxdnamelen) return prevlab; } +/** + * The RR types that are to be ignored. + * DNSSEC RRs at the apex, and SOA and NS are ignored. + */ +static int +rpz_type_ignored(uint16_t rr_type) +{ + switch(rr_type) { + case LDNS_RR_TYPE_SOA: + case LDNS_RR_TYPE_NS: + case LDNS_RR_TYPE_DNAME: + /* all DNSSEC-related RRs must be ignored */ + case LDNS_RR_TYPE_DNSKEY: + case LDNS_RR_TYPE_DS: + case LDNS_RR_TYPE_RRSIG: + case LDNS_RR_TYPE_NSEC: + case LDNS_RR_TYPE_NSEC3: + case LDNS_RR_TYPE_NSEC3PARAM: + return 1; + default: + break; + } + return 0; +} + /** * Classify RPZ action for RR type/rdata * @param rr_type: the RR type @@ -600,6 +625,8 @@ rpz_insert_local_zones_trigger(struct local_zones* lz, uint8_t* dname, lock_rw_unlock(&lz->lock); return; } + if(rrstr[0]) + rrstr[strlen(rrstr)-1]=0; /* remove newline */ verbose(VERB_ALGO, "rpz: skipping duplicate record: '%s'", rrstr); free(rrstr); free(dname); @@ -652,10 +679,6 @@ rpz_insert_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl, uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len) { - verbose(VERB_ALGO, "rpz: insert qname trigger: %s", rpz_action_to_string(a)); - - rpz_log_dname("insert qname trigger", dname, dnamelen); - if(a == RPZ_INVALID_ACTION) { verbose(VERB_ALGO, "rpz: skipping invalid action"); free(dname); @@ -912,7 +935,6 @@ rpz_insert_clientip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, socklen_t addrlen; int net, af; - verbose(VERB_ALGO, "rpz: insert clientip trigger: %s", rpz_action_to_string(a)); if(a == RPZ_INVALID_ACTION) { return 0; } @@ -992,6 +1014,10 @@ rpz_insert_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, uint8_t* dname, 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 1; + } if(!dname_subdomain_c(dname, azname)) { char* dname_str = sldns_wire2str_dname(dname, dnamelen); char* azname_str = sldns_wire2str_dname(azname, aznamelen); @@ -1359,7 +1385,7 @@ log_rpz_apply(uint8_t* dname, enum rpz_action a, struct query_info* qinfo, static struct clientip_synthesized_rr* rpz_ipbased_trigger_lookup(struct clientip_synthesized_rrset* set, - struct sockaddr_storage* addr, socklen_t addrlen) + struct sockaddr_storage* addr, socklen_t addrlen, char* triggername) { struct clientip_synthesized_rr* raddr = NULL; enum rpz_action action = RPZ_INVALID_ACTION; @@ -1375,8 +1401,8 @@ rpz_ipbased_trigger_lookup(struct clientip_synthesized_rrset* set, addr_to_str(addr, addrlen, ip, sizeof(ip)); addr_to_str(&raddr->node.addr, raddr->node.addrlen, net, sizeof(net)); - verbose(VERB_ALGO, "rpz: trigger nsip %s/%d on %s action=%s", - net, raddr->node.net, ip, rpz_action_to_string(action)); + verbose(VERB_ALGO, "rpz: trigger %s %s/%d on %s action=%s", + triggername, net, raddr->node.net, ip, rpz_action_to_string(action)); } lock_rw_unlock(&raddr->lock); } @@ -1414,7 +1440,7 @@ rpz_resolve_client_action_and_zone(struct auth_zones* az, struct query_info* qin } z = rpz_find_zone(r->local_zones, qinfo->qname, qinfo->qname_len, qinfo->qclass, 0, 0, 0); - node = rpz_ipbased_trigger_lookup(r->client_set, &repinfo->addr, repinfo->addrlen); + node = rpz_ipbased_trigger_lookup(r->client_set, &repinfo->addr, repinfo->addrlen, "clientip"); if((z || node) && r->action_override == RPZ_DISABLED_ACTION) { if(r->log) log_rpz_apply(z->name, @@ -1517,11 +1543,7 @@ rpz_apply_clientip_localdata_action(struct clientip_synthesized_rr* raddr, int rcode = LDNS_RCODE_NOERROR|BIT_AA; int rrset_count = 1; - verbose(VERB_ALGO, "rpz: apply client ip trigger: found=%d action=%s", - raddr != NULL, rpz_action_to_string(action)); - /* prepare synthesized answer for client */ - action = raddr->action; if(action == RPZ_LOCAL_DATA_ACTION && raddr->data == NULL ) { verbose(VERB_ALGO, "rpz: bug: local-data action but no local data"); @@ -1529,7 +1551,6 @@ rpz_apply_clientip_localdata_action(struct clientip_synthesized_rr* raddr, } /* check query type / rr type */ - rrset = rpz_find_synthesized_rrset(qinfo->qtype, raddr); if(rrset == NULL) { verbose(VERB_ALGO, "rpz: unable to find local-data for query"); @@ -1803,7 +1824,7 @@ rpz_delegation_point_ipbased_trigger_lookup(struct rpz* rpz, struct iter_qstate* cursor = cursor->next_target) { if(cursor->bogus) { continue; } action = rpz_ipbased_trigger_lookup(rpz->ns_set, &cursor->addr, - cursor->addrlen); + cursor->addrlen, "nsip"); if(action != NULL) { return action; } } return NULL; @@ -2043,8 +2064,17 @@ struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms, lzt = rpz_action_to_localzone_type(r->action_override); } - verbose(VERB_ALGO, "rpz: qname trigger after cname, with action=%s", - rpz_action_to_string(localzone_type_to_rpz_action(lzt))); + if(verbosity >= VERB_ALGO) { + char nm[255+1], zn[255+1]; + dname_str(is->qchase.qname, nm); + dname_str(z->name, zn); + if(strcmp(zn, nm) != 0) + verbose(VERB_ALGO, "rpz: qname trigger after cname %s on %s, with action=%s", + zn, nm, rpz_action_to_string(localzone_type_to_rpz_action(lzt))); + else + verbose(VERB_ALGO, "rpz: qname trigger after cname %s, with action=%s", + nm, rpz_action_to_string(localzone_type_to_rpz_action(lzt))); + } switch(localzone_type_to_rpz_action(lzt)) { case RPZ_NXDOMAIN_ACTION: ret = rpz_synthesize_nxdomain(r, ms, &is->qchase); @@ -2094,12 +2124,8 @@ rpz_apply_maybe_clientip_trigger(struct auth_zones* az, struct module_env* env, client_action = ((node == NULL) ? RPZ_INVALID_ACTION : node->action); - verbose(VERB_ALGO, "rpz: qname trigger: client action=%s", - rpz_action_to_string(client_action)); - if(*z_out == NULL || (client_action != RPZ_INVALID_ACTION && client_action != RPZ_PASSTHRU_ACTION)) { - verbose(VERB_ALGO, "rpz: client action without zone"); if(client_action == RPZ_PASSTHRU_ACTION || client_action == RPZ_INVALID_ACTION || (client_action == RPZ_TCP_ONLY_ACTION @@ -2156,8 +2182,17 @@ rpz_callback_from_worker_request(struct auth_zones* az, struct module_env* env, lzt = rpz_action_to_localzone_type(r->action_override); } - verbose(VERB_ALGO, "rpz: qname trigger with action=%s", - rpz_action_to_string(localzone_type_to_rpz_action(lzt))); + if(verbosity >= VERB_ALGO) { + char nm[255+1], zn[255+1]; + dname_str(qinfo->qname, nm); + dname_str(z->name, zn); + if(strcmp(zn, nm) != 0) + verbose(VERB_ALGO, "rpz: qname trigger %s on %s with action=%s", + zn, nm, rpz_action_to_string(localzone_type_to_rpz_action(lzt))); + else + verbose(VERB_ALGO, "rpz: qname trigger %s with action=%s", + nm, rpz_action_to_string(localzone_type_to_rpz_action(lzt))); + } ret = rpz_synthesize_qname_localdata(env, r, z, lzt, qinfo, edns, buf, temp, repinfo, stats); From 69349c7c249767de5e359d9a93652e0005d31f62 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 14 Apr 2021 16:35:39 +0200 Subject: [PATCH 54/68] - rpz-triggers, nicer logging for response IP triggers. --- respip/respip.c | 26 +++++++++++++++++++++----- services/rpz.c | 1 - 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/respip/respip.c b/respip/respip.c index 633cde6e0..cf1de07a5 100644 --- a/respip/respip.c +++ b/respip/respip.c @@ -602,7 +602,7 @@ rdata2sockaddr(const struct packed_rrset_data* rd, uint16_t rtype, size_t i, */ static struct resp_addr* respip_addr_lookup(const struct reply_info *rep, struct respip_set* rs, - size_t* rrset_id) + size_t* rrset_id, size_t* rr_id) { size_t i; struct resp_addr* ra; @@ -625,6 +625,7 @@ respip_addr_lookup(const struct reply_info *rep, struct respip_set* rs, &ss, addrlen); if(ra) { *rrset_id = i; + *rr_id = j; lock_rw_rdlock(&ra->lock); lock_rw_unlock(&rs->lock); return ra; @@ -869,7 +870,7 @@ respip_rewrite_reply(const struct query_info* qinfo, size_t tag_datas_size; struct view* view = NULL; struct respip_set* ipset = NULL; - size_t rrset_id = 0; + size_t rrset_id = 0, rr_id = 0; enum respip_action action = respip_none; int tag = -1; struct resp_addr* raddr = NULL; @@ -910,7 +911,7 @@ respip_rewrite_reply(const struct query_info* qinfo, lock_rw_rdlock(&view->lock); if(view->respip_set) { if((raddr = respip_addr_lookup(rep, - view->respip_set, &rrset_id))) { + view->respip_set, &rrset_id, &rr_id))) { /** for per-view respip directives the action * can only be direct (i.e. not tag-based) */ action = raddr->action; @@ -924,7 +925,7 @@ respip_rewrite_reply(const struct query_info* qinfo, } } if(!raddr && (raddr = respip_addr_lookup(rep, ipset, - &rrset_id))) { + &rrset_id, &rr_id))) { action = (enum respip_action)local_data_find_tag_action( raddr->taglist, raddr->taglen, ctaglist, ctaglen, tag_actions, tag_actions_size, @@ -938,7 +939,7 @@ respip_rewrite_reply(const struct query_info* qinfo, if(!r->taglist || taglist_intersect(r->taglist, r->taglistlen, ctaglist, ctaglen)) { if((raddr = respip_addr_lookup(rep, - r->respip_set, &rrset_id))) { + r->respip_set, &rrset_id, &rr_id))) { if(!respip_use_rpz(raddr, r, &action, &data, &rpz_log, &log_name, &rpz_cname_override, region, &rpz_used)) { @@ -949,6 +950,21 @@ respip_rewrite_reply(const struct query_info* qinfo, return 0; } if(rpz_used) { + if(verbosity >= VERB_ALGO) { + struct sockaddr_storage ss; + socklen_t ss_len = 0; + char nm[256], ip[256]; + char qn[255+1]; + if(!rdata2sockaddr(rep->rrsets[rrset_id]->entry.data, ntohs(rep->rrsets[rrset_id]->rk.type), rr_id, &ss, &ss_len)) + snprintf(ip, sizeof(ip), "invalidRRdata"); + else + addr_to_str(&ss, ss_len, ip, sizeof(ip)); + dname_str(qinfo->qname, qn); + addr_to_str(&raddr->node.addr, + raddr->node.addrlen, + nm, sizeof(nm)); + verbose(VERB_ALGO, "respip: rpz response-ip trigger %s/%d on %s %s with action %s", nm, raddr->node.net, qn, ip, rpz_action_to_string(respip_action_to_rpz_action(action))); + } /* break to make sure 'a' stays pointed * to used auth_zone, and keeps lock */ break; diff --git a/services/rpz.c b/services/rpz.c index 3a998afaa..966f7833a 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -980,7 +980,6 @@ rpz_insert_response_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen, socklen_t addrlen; int net, af; - verbose(VERB_ALGO, "rpz: insert response ip trigger: %s", rpz_action_to_string(a)); if(a == RPZ_INVALID_ACTION) { return 0; } From f9f3e9d4d0f18b31f60295e11febff22d7436d7a Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 14 May 2021 13:25:21 +0200 Subject: [PATCH 55/68] - rpz-triggers, fix compile warning --- respip/respip.c | 1 + 1 file changed, 1 insertion(+) diff --git a/respip/respip.c b/respip/respip.c index cf1de07a5..a4ef7dfce 100644 --- a/respip/respip.c +++ b/respip/respip.c @@ -25,6 +25,7 @@ #include "respip/respip.h" #include "services/view.h" #include "sldns/rrdef.h" +#include "util/data/dname.h" /** Subset of resp_addr.node, used for inform-variant logging */ From 9f40e93fe6f1473cd2bec93416b397599ce03fc2 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 14 May 2021 13:28:10 +0200 Subject: [PATCH 56/68] - rpz-triggers, use sec_status_insecure like respip, AA flag on RPZ responses. --- services/rpz.c | 11 ++++++++--- testdata/rpz_nsdname.rpl | 4 ++-- testdata/rpz_nsip.rpl | 6 +++--- testdata/rpz_qname.rpl | 2 +- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/services/rpz.c b/services/rpz.c index 966f7833a..c06e096ae 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -1599,7 +1599,9 @@ rpz_synthesize_nodata(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms, 0, //ns 0, //ar 0, //total - sec_status_secure); + sec_status_insecure); + if(msg->rep) + msg->rep->authoritative = 1; return msg; } @@ -1620,7 +1622,9 @@ rpz_synthesize_nxdomain(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms, 0, //ns 0, //ar 0, //total - sec_status_secure); + sec_status_insecure); + if(msg->rep) + msg->rep->authoritative = 1; return msg; } @@ -1647,11 +1651,12 @@ rpz_synthesize_localdata_from_rrset(struct rpz* ATTR_UNUSED(r), struct module_qs 0, //ns 0, //ar 1, //total - sec_status_secure); + sec_status_insecure); if(new_reply_info == NULL) { log_err("out of memory"); return NULL; } + new_reply_info->authoritative = 1; rp = respip_copy_rrset(rrset->rrset, ms->region); if(rp == NULL) { log_err("out of memory"); diff --git a/testdata/rpz_nsdname.rpl b/testdata/rpz_nsdname.rpl index 7b55ebeb7..79b5bec40 100644 --- a/testdata/rpz_nsdname.rpl +++ b/testdata/rpz_nsdname.rpl @@ -362,7 +362,7 @@ ENTRY_END STEP 21 CHECK_ANSWER ENTRY_BEGIN MATCH all -REPLY QR RD RA NOERROR +REPLY QR RD RA AA NOERROR SECTION QUESTION gotham.bb. IN A SECTION ANSWER @@ -378,7 +378,7 @@ ENTRY_END STEP 31 CHECK_ANSWER ENTRY_BEGIN MATCH all -REPLY QR RD RA NOERROR +REPLY QR RD RA AA NOERROR SECTION QUESTION gotham.ff. IN A SECTION ANSWER diff --git a/testdata/rpz_nsip.rpl b/testdata/rpz_nsip.rpl index a3cda6050..58b377e8f 100644 --- a/testdata/rpz_nsip.rpl +++ b/testdata/rpz_nsip.rpl @@ -362,7 +362,7 @@ ENTRY_END STEP 21 CHECK_ANSWER ENTRY_BEGIN MATCH all -REPLY QR RD RA NOERROR +REPLY QR RD RA AA NOERROR SECTION QUESTION gotham.bb. IN A SECTION ANSWER @@ -378,7 +378,7 @@ ENTRY_END STEP 31 CHECK_ANSWER ENTRY_BEGIN MATCH all -REPLY QR RD RA NOERROR +REPLY QR RD RA AA NOERROR SECTION QUESTION gotham.ff. IN A SECTION ANSWER @@ -396,7 +396,7 @@ ENTRY_END STEP 41 CHECK_ANSWER ENTRY_BEGIN MATCH all -REPLY QR RD RA NOERROR +REPLY QR RD RA AA NOERROR SECTION QUESTION gotham.ff. IN A SECTION ANSWER diff --git a/testdata/rpz_qname.rpl b/testdata/rpz_qname.rpl index 2bc038c37..ede697233 100644 --- a/testdata/rpz_qname.rpl +++ b/testdata/rpz_qname.rpl @@ -388,7 +388,7 @@ ENTRY_END STEP 101 CHECK_ANSWER ENTRY_BEGIN MATCH all -REPLY QR RD RA NOERROR +REPLY QR RD RA AA NOERROR SECTION QUESTION f.example. IN TXT SECTION ANSWER From a9df16cf9b7c7bbfe127aa83f72a63054c3f1632 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 14 May 2021 13:35:24 +0200 Subject: [PATCH 57/68] - rpz-triggers, fix memory allocation trouble on allocation error path. --- services/rpz.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/services/rpz.c b/services/rpz.c index c06e096ae..9c05e69a5 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -387,11 +387,15 @@ rpz_clear(struct rpz* r) { /* must hold write lock on auth_zone */ local_zones_delete(r->local_zones); + r->local_zones = NULL; local_zones_delete(r->nsdname_zones); + r->nsdname_zones = NULL; respip_set_delete(r->respip_set); + r->respip_set = NULL; rpz_clientip_synthesized_set_delete(r->client_set); + r->client_set = NULL; rpz_clientip_synthesized_set_delete(r->ns_set); - // XXX: this approach seems to leak memory + r->ns_set = NULL; if(!(r->local_zones = local_zones_create())){ return 0; } From 193e37d6e892c86c114bba149bbadfd0cda5dd02 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 14 May 2021 13:42:54 +0200 Subject: [PATCH 58/68] - rpz-triggers, call module local callback for rpz local encode replies. --- services/rpz.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/services/rpz.c b/services/rpz.c index 9c05e69a5..dceab4a8a 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -1056,7 +1056,7 @@ rpz_insert_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, uint8_t* dname, return 1; } if(t == RPZ_QNAME_TRIGGER) { - // policydname will be consumed, no free + /* policydname will be consumed, no free */ rpz_insert_qname_trigger(r, policydname, policydnamelen, a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr, rr_len); @@ -1489,8 +1489,10 @@ rpz_is_udp_query(struct comm_reply* repinfo) { /** encode answer consisting of 1 rrset */ static int -rpz_local_encode(struct query_info* qinfo,struct edns_data* edns, sldns_buffer* buf, - struct regional* temp, struct ub_packed_rrset_key* rrset, int ansec, int rcode) +rpz_local_encode(struct module_env* env, struct query_info* qinfo, + struct edns_data* edns, struct comm_reply* repinfo, sldns_buffer* buf, + struct regional* temp, struct ub_packed_rrset_key* rrset, int ansec, + int rcode) { struct reply_info rep; uint16_t udpsize; @@ -1510,8 +1512,9 @@ rpz_local_encode(struct query_info* qinfo,struct edns_data* edns, sldns_buffer* edns->udp_size = EDNS_ADVERTISED_SIZE; edns->ext_rcode = 0; edns->bits &= EDNS_DO; - //!inplace_cb_reply_local_call(env, qinfo, NULL, &rep, rcode, edns,repinfo, temp) || - if(!reply_info_answer_encode(qinfo, &rep, + if(!inplace_cb_reply_local_call(env, qinfo, NULL, &rep, rcode, edns, + repinfo, temp, env->now_tv) || + !reply_info_answer_encode(qinfo, &rep, *(uint16_t*)sldns_buffer_begin(buf), sldns_buffer_read_u16_at(buf, 2), buf, 0, 0, temp, udpsize, edns, (int)(edns->bits&EDNS_DO), 0)) { error_encode(buf, (LDNS_RCODE_SERVFAIL|BIT_AA), qinfo, @@ -1537,7 +1540,8 @@ rpz_find_synthesized_rrset(int qtype, struct clientip_synthesized_rr* data) { static void rpz_apply_clientip_localdata_action(struct clientip_synthesized_rr* raddr, - struct query_info* qinfo, struct edns_data* edns, sldns_buffer* buf, + struct module_env* env, struct query_info* qinfo, + struct edns_data* edns, struct comm_reply* repinfo, sldns_buffer* buf, struct regional* temp) { struct local_rrset* rrset; @@ -1571,7 +1575,8 @@ rpz_apply_clientip_localdata_action(struct clientip_synthesized_rr* raddr, rp->rk.dname = qinfo->qname; rp->rk.dname_len = qinfo->qname_len; nodata: - rpz_local_encode(qinfo, edns, buf, temp, rp, rrset_count, rcode); + rpz_local_encode(env, qinfo, edns, repinfo, buf, temp, rp, + rrset_count, rcode); } static inline struct dns_msg* @@ -2143,8 +2148,8 @@ rpz_apply_maybe_clientip_trigger(struct auth_zones* az, struct module_env* env, } stats->rpz_action[client_action]++; if(client_action == RPZ_LOCAL_DATA_ACTION) { - rpz_apply_clientip_localdata_action(node, qinfo, edns, - buf, temp); + rpz_apply_clientip_localdata_action(node, env, qinfo, + edns, repinfo, buf, temp); } else { // XXX: log_rpz_apply not possbile because no zone local_zones_zone_answer(NULL /*no zone*/, env, qinfo, edns, From e7f476d5c3e863e7d7fa93789942d2b59b65be19 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 14 May 2021 13:57:04 +0200 Subject: [PATCH 59/68] - rpz-triggers, fix comments, log no zone answers. --- services/rpz.c | 70 ++++++++++++++++++++++++++------------------------ 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/services/rpz.c b/services/rpz.c index dceab4a8a..307c67a56 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -1374,7 +1374,9 @@ log_rpz_apply(uint8_t* dname, enum rpz_action a, struct query_info* qinfo, char ip[128], txt[512]; char dnamestr[LDNS_MAX_DOMAINLEN+1]; uint16_t port = ntohs(((struct sockaddr_in*)&repinfo->addr)->sin_port); - dname_str(dname, dnamestr); + if(dname) + dname_str(dname, dnamestr); + else dnamestr[0]=0; addr_to_str(&repinfo->addr, repinfo->addrlen, ip, sizeof(ip)); if(log_name) snprintf(txt, sizeof(txt), "rpz: applied [%s] %s %s %s@%u", @@ -1600,14 +1602,14 @@ rpz_synthesize_nodata(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms, msg->qinfo = *qinfo; msg->rep = construct_reply_info_base(ms->region, LDNS_RCODE_NOERROR | BIT_RD | BIT_QR | BIT_AA | BIT_RA, - 1, //qd - 0, //ttl - 0, //prettl - 0, //expttl - 0, //an - 0, //ns - 0, //ar - 0, //total + 1, /* qd */ + 0, /* ttl */ + 0, /* prettl */ + 0, /* expttl */ + 0, /* an */ + 0, /* ns */ + 0, /* ar */ + 0, /* total */ sec_status_insecure); if(msg->rep) msg->rep->authoritative = 1; @@ -1623,14 +1625,14 @@ rpz_synthesize_nxdomain(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms, msg->qinfo = *qinfo; msg->rep = construct_reply_info_base(ms->region, LDNS_RCODE_NXDOMAIN | BIT_RD | BIT_QR | BIT_AA | BIT_RA, - 1, //qd - 0, //ttl - 0, //prettl - 0, //expttl - 0, //an - 0, //ns - 0, //ar - 0, //total + 1, /* qd */ + 0, /* ttl */ + 0, /* prettl */ + 0, /* expttl */ + 0, /* an */ + 0, /* ns */ + 0, /* ar */ + 0, /* total */ sec_status_insecure); if(msg->rep) msg->rep->authoritative = 1; @@ -1649,17 +1651,16 @@ rpz_synthesize_localdata_from_rrset(struct rpz* ATTR_UNUSED(r), struct module_qs msg = rpz_dns_msg_new(ms->region); if(msg == NULL) { return NULL; } - // XXX: use ttl etc from rpz zone? new_reply_info = construct_reply_info_base(ms->region, LDNS_RCODE_NOERROR | BIT_RD | BIT_QR | BIT_AA | BIT_RA, - 1, //qd - 0, //ttl - 0, //prettl - 0, //expttl - 1, //an - 0, //ns - 0, //ar - 1, //total + 1, /* qd */ + 0, /* ttl */ + 0, /* prettl */ + 0, /* expttl */ + 1, /* an */ + 0, /* ns */ + 0, /* ar */ + 1, /* total */ sec_status_insecure); if(new_reply_info == NULL) { log_err("out of memory"); @@ -1703,7 +1704,7 @@ rpz_synthesize_nsip_localdata(struct rpz* r, struct module_qstate* ms, return rpz_synthesize_localdata_from_rrset(r, ms, &ms->qinfo, rrset); } -// copy'n'paste from localzone.c +/* copy'n'paste from localzone.c */ static struct local_rrset* local_data_find_type(struct local_data* data, uint16_t type, int alias_ok) { @@ -1718,7 +1719,7 @@ local_data_find_type(struct local_data* data, uint16_t type, int alias_ok) return NULL; } -// based on localzone.c:local_data_answer() +/* based on localzone.c:local_data_answer() */ static inline struct dns_msg* rpz_synthesize_nsdname_localdata(struct rpz* r, struct module_qstate* ms, struct local_zone* z, struct matched_delegation_point const* match) @@ -1870,8 +1871,8 @@ rpz_apply_nsip_trigger(struct module_qstate* ms, struct rpz* r, ret = rpz_synthesize_nodata(r, ms, &ms->qinfo); break; case RPZ_TCP_ONLY_ACTION: - // basically a passthru here but the tcp-only will be - // honored before the query gets send + /* basically a passthru here but the tcp-only will be + * honored before the query gets send */ ms->respip_action_info->action = respip_truncate; ret = NULL; break; @@ -1918,8 +1919,8 @@ rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r, ret = rpz_synthesize_nodata(r, ms, &ms->qinfo); break; case RPZ_TCP_ONLY_ACTION: - // basically a passthru here but the tcp-only will be - // honored before the query gets send + /* basically a passthru here but the tcp-only will be + * honored before the query gets send */ ms->respip_action_info->action = respip_truncate; ret = NULL; break; @@ -2151,7 +2152,10 @@ rpz_apply_maybe_clientip_trigger(struct auth_zones* az, struct module_env* env, rpz_apply_clientip_localdata_action(node, env, qinfo, edns, repinfo, buf, temp); } else { - // XXX: log_rpz_apply not possbile because no zone + if(*r_out && (*r_out)->log) + log_rpz_apply(((*z_out)?(*z_out)->name:NULL), + client_action, qinfo, repinfo, + (*r_out)->log_name); local_zones_zone_answer(NULL /*no zone*/, env, qinfo, edns, repinfo, buf, temp, 0 /* no local data used */, rpz_action_to_localzone_type(client_action)); From ad08971b7a75a83b1af48508b0df6cb8967157f6 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 14 May 2021 14:04:42 +0200 Subject: [PATCH 60/68] - rpz-triggers, use zone for local data zone based answer if available. --- services/rpz.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/rpz.c b/services/rpz.c index 307c67a56..e53c5cf81 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -2156,7 +2156,7 @@ rpz_apply_maybe_clientip_trigger(struct auth_zones* az, struct module_env* env, log_rpz_apply(((*z_out)?(*z_out)->name:NULL), client_action, qinfo, repinfo, (*r_out)->log_name); - local_zones_zone_answer(NULL /*no zone*/, env, qinfo, edns, + local_zones_zone_answer(*z_out /*likely NULL, no zone*/, env, qinfo, edns, repinfo, buf, temp, 0 /* no local data used */, rpz_action_to_localzone_type(client_action)); } From 50dcadd495f326ba7a2ddf652466a6b83a371a78 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 14 May 2021 15:34:48 +0200 Subject: [PATCH 61/68] - rpz-triggers, for clientip modified answers the rpz SOA is added to the additional section with the serial number and name of the rpz zone that was applied. --- cachedb/cachedb.c | 2 +- daemon/worker.c | 4 +-- services/authzone.c | 13 +++++++++- services/authzone.h | 3 +++ services/localzone.c | 2 +- services/mesh.c | 4 +-- services/rpz.c | 51 +++++++++++++++++++++++++++++++++------ testdata/rpz_clientip.rpl | 6 +++++ util/data/msgencode.c | 4 +-- util/data/msgencode.h | 4 ++- 10 files changed, 76 insertions(+), 17 deletions(-) diff --git a/cachedb/cachedb.c b/cachedb/cachedb.c index af4ffe5f2..707319e85 100644 --- a/cachedb/cachedb.c +++ b/cachedb/cachedb.c @@ -402,7 +402,7 @@ prep_data(struct module_qstate* qstate, struct sldns_buffer* buf) qstate->return_msg->rep); if(!reply_info_answer_encode(&qstate->return_msg->qinfo, qstate->return_msg->rep, 0, qstate->query_flags, - buf, 0, 1, qstate->env->scratch, 65535, &edns, 1, 0)) + buf, 0, 1, qstate->env->scratch, 65535, &edns, 1, 0, 0)) return 0; /* TTLs in the return_msg are relative to time(0) so we have to diff --git a/daemon/worker.c b/daemon/worker.c index 2ee8b8d9b..421cd82c4 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -522,7 +522,7 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo, repinfo->c, worker->scratchpad) || !reply_info_answer_encode(&msg->qinfo, msg->rep, id, flags, repinfo->c->buffer, 0, 1, worker->scratchpad, - udpsize, edns, (int)(edns->bits & EDNS_DO), secure)) { + udpsize, edns, (int)(edns->bits & EDNS_DO), secure, 0)) { if(!inplace_cb_reply_servfail_call(&worker->env, qinfo, NULL, NULL, LDNS_RCODE_SERVFAIL, edns, repinfo, worker->scratchpad, worker->env.now_tv)) @@ -726,7 +726,7 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo, repinfo->c, worker->scratchpad) || !reply_info_answer_encode(qinfo, encode_rep, id, flags, repinfo->c->buffer, timenow, 1, worker->scratchpad, - udpsize, edns, (int)(edns->bits & EDNS_DO), *is_secure_answer)) { + udpsize, edns, (int)(edns->bits & EDNS_DO), *is_secure_answer, 0)) { if(!inplace_cb_reply_servfail_call(&worker->env, qinfo, NULL, NULL, LDNS_RCODE_SERVFAIL, edns, repinfo, worker->scratchpad, worker->env.now_tv)) diff --git a/services/authzone.c b/services/authzone.c index 196fe6693..19f462586 100644 --- a/services/authzone.c +++ b/services/authzone.c @@ -1948,6 +1948,17 @@ static int auth_zone_zonemd_check_hash(struct auth_zone* z, return 0; } +/** find the apex SOA RRset, if it exists */ +struct auth_rrset* auth_zone_get_soa_rrset(struct auth_zone* z) +{ + struct auth_data* apex; + struct auth_rrset* soa; + apex = az_find_name(z, z->name, z->namelen); + if(!apex) return NULL; + soa = az_domain_rrset(apex, LDNS_RR_TYPE_SOA); + return soa; +} + /** find serial number of zone or false if none */ int auth_zone_get_serial(struct auth_zone* z, uint32_t* serial) @@ -3484,7 +3495,7 @@ auth_answer_encode(struct query_info* qinfo, struct module_env* env, *(uint16_t*)sldns_buffer_begin(buf), sldns_buffer_read_u16_at(buf, 2), buf, 0, 0, temp, udpsize, edns, - (int)(edns->bits&EDNS_DO), 0)) { + (int)(edns->bits&EDNS_DO), 0, 0)) { error_encode(buf, (LDNS_RCODE_SERVFAIL|BIT_AA), qinfo, *(uint16_t*)sldns_buffer_begin(buf), sldns_buffer_read_u16_at(buf, 2), edns); diff --git a/services/authzone.h b/services/authzone.h index 4810017a3..7b9fc60fc 100644 --- a/services/authzone.h +++ b/services/authzone.h @@ -632,6 +632,9 @@ int auth_zones_startprobesequence(struct auth_zones* az, /** read auth zone from zonefile. caller must lock zone. false on failure */ int auth_zone_read_zonefile(struct auth_zone* z, struct config_file* cfg); +/** find the apex SOA RRset, if it exists. NULL if no SOA RRset. */ +struct auth_rrset* auth_zone_get_soa_rrset(struct auth_zone* z); + /** find serial number of zone or false if none (no SOA record) */ int auth_zone_get_serial(struct auth_zone* z, uint32_t* serial); diff --git a/services/localzone.c b/services/localzone.c index a24137eac..6eecab62a 100644 --- a/services/localzone.c +++ b/services/localzone.c @@ -1261,7 +1261,7 @@ local_encode(struct query_info* qinfo, struct module_env* env, if(!inplace_cb_reply_local_call(env, qinfo, NULL, &rep, rcode, edns, repinfo, temp, env->now_tv) || !reply_info_answer_encode(qinfo, &rep, *(uint16_t*)sldns_buffer_begin(buf), sldns_buffer_read_u16_at(buf, 2), - buf, 0, 0, temp, udpsize, edns, (int)(edns->bits&EDNS_DO), 0)) { + buf, 0, 0, temp, udpsize, edns, (int)(edns->bits&EDNS_DO), 0, 0)) { error_encode(buf, (LDNS_RCODE_SERVFAIL|BIT_AA), qinfo, *(uint16_t*)sldns_buffer_begin(buf), sldns_buffer_read_u16_at(buf, 2), edns); diff --git a/services/mesh.c b/services/mesh.c index f00ad3e86..3ff14a0dd 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -1167,7 +1167,7 @@ mesh_do_callback(struct mesh_state* m, int rcode, struct reply_info* rep, !reply_info_answer_encode(&m->s.qinfo, rep, r->qid, r->qflags, r->buf, 0, 1, m->s.env->scratch, udp_size, &r->edns, - (int)(r->edns.bits & EDNS_DO), secure)) + (int)(r->edns.bits & EDNS_DO), secure, 0)) { fptr_ok(fptr_whitelist_mesh_cb(r->cb)); (*r->cb)(r->cb_arg, LDNS_RCODE_SERVFAIL, r->buf, @@ -1313,7 +1313,7 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, !reply_info_answer_encode(&m->s.qinfo, rep, r->qid, r->qflags, r_buffer, 0, 1, m->s.env->scratch, udp_size, &r->edns, (int)(r->edns.bits & EDNS_DO), - secure)) + secure, 0)) { if(!inplace_cb_reply_servfail_call(m->s.env, &m->s.qinfo, &m->s, rep, LDNS_RCODE_SERVFAIL, &r->edns, &r->query_reply, m->s.region, &r->start_time)) diff --git a/services/rpz.c b/services/rpz.c index e53c5cf81..9e3510b7d 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -1494,20 +1494,30 @@ static int rpz_local_encode(struct module_env* env, struct query_info* qinfo, struct edns_data* edns, struct comm_reply* repinfo, sldns_buffer* buf, struct regional* temp, struct ub_packed_rrset_key* rrset, int ansec, - int rcode) + int rcode, struct ub_packed_rrset_key* soa_rrset) { struct reply_info rep; uint16_t udpsize; + struct ub_packed_rrset_key* rrsetlist[3]; memset(&rep, 0, sizeof(rep)); rep.flags = (uint16_t)((BIT_QR | BIT_AA | BIT_RA) | rcode); rep.qdcount = 1; rep.rrset_count = ansec; + rep.rrsets = rrsetlist; if(ansec > 0) { rep.an_numrrsets = 1; - rep.rrsets = &rrset; + rep.rrsets[0] = rrset; rep.ttl = ((struct packed_rrset_data*)rrset->entry.data)->rr_ttl[0]; } + if(soa_rrset != NULL) { + rep.ar_numrrsets = 1; + rep.rrsets[rep.rrset_count] = soa_rrset; + rep.rrset_count ++; + if(rep.ttl < ((struct packed_rrset_data*)soa_rrset->entry.data)->rr_ttl[0]) { + rep.ttl = ((struct packed_rrset_data*)soa_rrset->entry.data)->rr_ttl[0]; + } + } udpsize = edns->udp_size; edns->edns_version = EDNS_ADVERTISED_VERSION; @@ -1518,7 +1528,8 @@ rpz_local_encode(struct module_env* env, struct query_info* qinfo, repinfo, temp, env->now_tv) || !reply_info_answer_encode(qinfo, &rep, *(uint16_t*)sldns_buffer_begin(buf), sldns_buffer_read_u16_at(buf, 2), - buf, 0, 0, temp, udpsize, edns, (int)(edns->bits&EDNS_DO), 0)) { + buf, 0, 0, temp, udpsize, edns, (int)(edns->bits&EDNS_DO), 0, + 1 /* not minimal */ )) { error_encode(buf, (LDNS_RCODE_SERVFAIL|BIT_AA), qinfo, *(uint16_t*)sldns_buffer_begin(buf), sldns_buffer_read_u16_at(buf, 2), edns); @@ -1544,11 +1555,12 @@ static void rpz_apply_clientip_localdata_action(struct clientip_synthesized_rr* raddr, struct module_env* env, struct query_info* qinfo, struct edns_data* edns, struct comm_reply* repinfo, sldns_buffer* buf, - struct regional* temp) + struct regional* temp, struct auth_zone* auth_zone) { struct local_rrset* rrset; enum rpz_action action = RPZ_INVALID_ACTION; struct ub_packed_rrset_key* rp = NULL; + struct ub_packed_rrset_key* rsoa = NULL; int rcode = LDNS_RCODE_NOERROR|BIT_AA; int rrset_count = 1; @@ -1573,12 +1585,37 @@ rpz_apply_clientip_localdata_action(struct clientip_synthesized_rr* raddr, return; } - rp->rk.flags |= PACKED_RRSET_FIXEDTTL; + rp->rk.flags |= PACKED_RRSET_FIXEDTTL | PACKED_RRSET_RPZ; rp->rk.dname = qinfo->qname; rp->rk.dname_len = qinfo->qname_len; + rp->entry.hash = rrset_key_hash(&rp->rk); nodata: + if(auth_zone) { + struct auth_rrset* soa = NULL; + soa = auth_zone_get_soa_rrset(auth_zone); + if(soa) { + struct ub_packed_rrset_key csoa; + memset(&csoa, 0, sizeof(csoa)); + csoa.entry.key = &csoa; + csoa.rk.rrset_class = htons(LDNS_RR_CLASS_IN); + csoa.rk.type = htons(LDNS_RR_TYPE_SOA); + csoa.rk.flags |= PACKED_RRSET_FIXEDTTL + | PACKED_RRSET_RPZ; + csoa.rk.dname = auth_zone->name; + csoa.rk.dname_len = auth_zone->namelen; + csoa.entry.hash = rrset_key_hash(&csoa.rk); + csoa.entry.data = soa->data; + rsoa = respip_copy_rrset(&csoa, temp); + if(!rsoa) { + verbose(VERB_ALGO, "rpz: local data action soa: out of memory"); + return; + } + } + + } + rpz_local_encode(env, qinfo, edns, repinfo, buf, temp, rp, - rrset_count, rcode); + rrset_count, rcode, rsoa); } static inline struct dns_msg* @@ -2150,7 +2187,7 @@ rpz_apply_maybe_clientip_trigger(struct auth_zones* az, struct module_env* env, stats->rpz_action[client_action]++; if(client_action == RPZ_LOCAL_DATA_ACTION) { rpz_apply_clientip_localdata_action(node, env, qinfo, - edns, repinfo, buf, temp); + edns, repinfo, buf, temp, *a_out); } else { if(*r_out && (*r_out)->log) log_rpz_apply(((*z_out)?(*z_out)->name:NULL), diff --git a/testdata/rpz_clientip.rpl b/testdata/rpz_clientip.rpl index 64082210c..7d5b5330d 100644 --- a/testdata/rpz_clientip.rpl +++ b/testdata/rpz_clientip.rpl @@ -207,6 +207,8 @@ SECTION QUESTION a.a. IN A SECTION ANSWER a.a. IN A 127.0.0.1 +SECTION ADDITIONAL +rpz.example.com. 3600 IN SOA ns1.rpz.example.com. hostmaster.rpz.example.com. ( 1379078166 28800 7200 604800 7200 ) ENTRY_END ; should be synthesized @@ -226,6 +228,8 @@ SECTION QUESTION a.a. IN TXT SECTION ANSWER a.a. IN TXT "42" +SECTION ADDITIONAL +rpz.example.com. 3600 IN SOA ns1.rpz.example.com. hostmaster.rpz.example.com. ( 1379078166 28800 7200 604800 7200 ) ENTRY_END ; should be synthesized NODATA @@ -243,6 +247,8 @@ MATCH all REPLY QR AA RD RA NOERROR SECTION QUESTION a.a. IN AAAA +SECTION ADDITIONAL +rpz.example.com. 3600 IN SOA ns1.rpz.example.com. hostmaster.rpz.example.com. ( 1379078166 28800 7200 604800 7200 ) ENTRY_END ; should be DROPPED diff --git a/util/data/msgencode.c b/util/data/msgencode.c index 5f297b551..1cf30dd5d 100644 --- a/util/data/msgencode.c +++ b/util/data/msgencode.c @@ -878,7 +878,7 @@ int reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep, uint16_t id, uint16_t qflags, sldns_buffer* pkt, time_t timenow, int cached, struct regional* region, uint16_t udpsize, - struct edns_data* edns, int dnssec, int secure) + struct edns_data* edns, int dnssec, int secure, int notminimal) { uint16_t flags; unsigned int attach_edns = 0; @@ -916,7 +916,7 @@ reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep, } if(!reply_info_encode(qinf, rep, id, flags, pkt, timenow, region, - udpsize, dnssec, MINIMAL_RESPONSES)) { + udpsize, dnssec, (notminimal?0:MINIMAL_RESPONSES))) { log_err("reply encode: out of memory"); return 0; } diff --git a/util/data/msgencode.h b/util/data/msgencode.h index 30dc515cb..3baa0eaf9 100644 --- a/util/data/msgencode.h +++ b/util/data/msgencode.h @@ -64,12 +64,14 @@ struct edns_data; * or if edns_present = 0, it is not included. * @param dnssec: if 0 DNSSEC records are omitted from the answer. * @param secure: if 1, the AD bit is set in the reply. + * @param notminimal: if 1, ignore minimalresponses and include additional + * section anyway. * @return: 0 on error (server failure). */ int reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep, uint16_t id, uint16_t qflags, struct sldns_buffer* dest, time_t timenow, int cached, struct regional* region, uint16_t udpsize, - struct edns_data* edns, int dnssec, int secure); + struct edns_data* edns, int dnssec, int secure, int notminimal); /** * Regenerate the wireformat from the stored msg reply. From 2094763bebc374c50b61ea1e40e6500b010b0a08 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 14 May 2021 16:28:55 +0200 Subject: [PATCH 62/68] - rpz-triggers, add rpz SOA when minimal responses is not set. --- services/rpz.c | 128 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 86 insertions(+), 42 deletions(-) diff --git a/services/rpz.c b/services/rpz.c index 9e3510b7d..a612cc094 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -1551,6 +1551,27 @@ rpz_find_synthesized_rrset(int qtype, struct clientip_synthesized_rr* data) { 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, + struct regional* temp) +{ + struct ub_packed_rrset_key csoa; + if(!soa) + return NULL; + memset(&csoa, 0, sizeof(csoa)); + csoa.entry.key = &csoa; + csoa.rk.rrset_class = htons(LDNS_RR_CLASS_IN); + csoa.rk.type = htons(LDNS_RR_TYPE_SOA); + csoa.rk.flags |= PACKED_RRSET_FIXEDTTL + | PACKED_RRSET_RPZ; + csoa.rk.dname = auth_zone->name; + csoa.rk.dname_len = auth_zone->namelen; + csoa.entry.hash = rrset_key_hash(&csoa.rk); + csoa.entry.data = soa->data; + return respip_copy_rrset(&csoa, temp); +} + static void rpz_apply_clientip_localdata_action(struct clientip_synthesized_rr* raddr, struct module_env* env, struct query_info* qinfo, @@ -1594,30 +1615,45 @@ nodata: struct auth_rrset* soa = NULL; soa = auth_zone_get_soa_rrset(auth_zone); if(soa) { - struct ub_packed_rrset_key csoa; - memset(&csoa, 0, sizeof(csoa)); - csoa.entry.key = &csoa; - csoa.rk.rrset_class = htons(LDNS_RR_CLASS_IN); - csoa.rk.type = htons(LDNS_RR_TYPE_SOA); - csoa.rk.flags |= PACKED_RRSET_FIXEDTTL - | PACKED_RRSET_RPZ; - csoa.rk.dname = auth_zone->name; - csoa.rk.dname_len = auth_zone->namelen; - csoa.entry.hash = rrset_key_hash(&csoa.rk); - csoa.entry.data = soa->data; - rsoa = respip_copy_rrset(&csoa, temp); + rsoa = make_soa_ubrrset(auth_zone, soa, temp); if(!rsoa) { verbose(VERB_ALGO, "rpz: local data action soa: out of memory"); return; } } - } rpz_local_encode(env, qinfo, edns, repinfo, buf, temp, rp, rrset_count, rcode, rsoa); } +/** add additional section SOA record to the reply. + * Since this gets fed into the normal iterator answer creation, it + * gets minimal-responses applied to it, that can remove the additional SOA + * again. */ +static int +rpz_add_soa(struct reply_info* rep, struct module_qstate* ms, + struct auth_zone* az) +{ + struct auth_rrset* soa = NULL; + struct ub_packed_rrset_key* rsoa = NULL; + struct ub_packed_rrset_key** prevrrsets; + soa = auth_zone_get_soa_rrset(az); + if(!soa) return 1; + if(!rep) return 0; + rsoa = make_soa_ubrrset(az, soa, ms->region); + if(!rsoa) return 0; + prevrrsets = rep->rrsets; + rep->rrsets = regional_alloc_zero(ms->region, + sizeof(*rep->rrsets)*(rep->rrset_count+1)); + if(prevrrsets && rep->rrset_count > 0) + memcpy(rep->rrsets, prevrrsets, rep->rrset_count*sizeof(*rep->rrsets)); + rep->rrset_count++; + rep->ar_numrrsets++; + rep->rrsets[rep->rrset_count-1] = rsoa; + return 1; +} + static inline struct dns_msg* rpz_dns_msg_new(struct regional* region) { @@ -1632,7 +1668,7 @@ rpz_dns_msg_new(struct regional* region) static inline struct dns_msg* rpz_synthesize_nodata(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms, - struct query_info* qinfo) + struct query_info* qinfo, struct auth_zone* az) { struct dns_msg* msg = rpz_dns_msg_new(ms->region); if(msg == NULL) { return msg; } @@ -1650,12 +1686,14 @@ rpz_synthesize_nodata(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms, sec_status_insecure); if(msg->rep) msg->rep->authoritative = 1; + if(!rpz_add_soa(msg->rep, ms, az)) + return NULL; return msg; } static inline struct dns_msg* rpz_synthesize_nxdomain(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms, - struct query_info* qinfo) + struct query_info* qinfo, struct auth_zone* az) { struct dns_msg* msg = rpz_dns_msg_new(ms->region); if(msg == NULL) { return msg; } @@ -1673,12 +1711,14 @@ rpz_synthesize_nxdomain(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms, sec_status_insecure); if(msg->rep) msg->rep->authoritative = 1; + if(!rpz_add_soa(msg->rep, ms, az)) + return NULL; return msg; } static inline struct dns_msg* rpz_synthesize_localdata_from_rrset(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms, - struct query_info* qi, struct local_rrset* rrset) + struct query_info* qi, struct local_rrset* rrset, struct auth_zone* az) { struct dns_msg* msg = NULL; struct reply_info* new_reply_info; @@ -1722,12 +1762,14 @@ rpz_synthesize_localdata_from_rrset(struct rpz* ATTR_UNUSED(r), struct module_qs rp->rk.flags |= PACKED_RRSET_RPZ; new_reply_info->rrsets[0] = rp; msg->rep = new_reply_info; + if(!rpz_add_soa(msg->rep, ms, az)) + return NULL; return msg; } static inline struct dns_msg* rpz_synthesize_nsip_localdata(struct rpz* r, struct module_qstate* ms, - struct clientip_synthesized_rr* data) + struct clientip_synthesized_rr* data, struct auth_zone* az) { struct query_info* qi = &ms->qinfo; struct local_rrset* rrset; @@ -1738,7 +1780,7 @@ rpz_synthesize_nsip_localdata(struct rpz* r, struct module_qstate* ms, return NULL; } - return rpz_synthesize_localdata_from_rrset(r, ms, &ms->qinfo, rrset); + return rpz_synthesize_localdata_from_rrset(r, ms, &ms->qinfo, rrset, az); } /* copy'n'paste from localzone.c */ @@ -1759,7 +1801,8 @@ local_data_find_type(struct local_data* data, uint16_t type, int alias_ok) /* based on localzone.c:local_data_answer() */ static inline struct dns_msg* rpz_synthesize_nsdname_localdata(struct rpz* r, struct module_qstate* ms, - struct local_zone* z, struct matched_delegation_point const* match) + struct local_zone* z, struct matched_delegation_point const* match, + struct auth_zone* az) { struct local_data key; struct local_data* ld; @@ -1786,13 +1829,13 @@ rpz_synthesize_nsdname_localdata(struct rpz* r, struct module_qstate* ms, return NULL; } - return rpz_synthesize_localdata_from_rrset(r, ms, &ms->qinfo, rrset); + return rpz_synthesize_localdata_from_rrset(r, ms, &ms->qinfo, rrset, az); } /* like local_data_answer for qname triggers after a cname */ static struct dns_msg* rpz_synthesize_qname_localdata_msg(struct rpz* r, struct module_qstate* ms, - struct query_info* qinfo, struct local_zone* z) + struct query_info* qinfo, struct local_zone* z, struct auth_zone* az) { struct local_data key; struct local_data* ld; @@ -1811,7 +1854,7 @@ rpz_synthesize_qname_localdata_msg(struct rpz* r, struct module_qstate* ms, verbose(VERB_ALGO, "rpz: qname after cname: type not found"); return NULL; } - return rpz_synthesize_localdata_from_rrset(r, ms, qinfo, rrset); + return rpz_synthesize_localdata_from_rrset(r, ms, qinfo, rrset, az); } static int @@ -1883,7 +1926,7 @@ rpz_delegation_point_ipbased_trigger_lookup(struct rpz* rpz, struct iter_qstate* struct dns_msg* rpz_apply_nsip_trigger(struct module_qstate* ms, struct rpz* r, - struct clientip_synthesized_rr* raddr) + struct clientip_synthesized_rr* raddr, struct auth_zone* az) { enum rpz_action action = raddr->action; struct dns_msg* ret = NULL; @@ -1896,16 +1939,16 @@ rpz_apply_nsip_trigger(struct module_qstate* ms, struct rpz* r, if(action == RPZ_LOCAL_DATA_ACTION && raddr->data == NULL) { verbose(VERB_ALGO, "rpz: bug: nsip local data action but no local data"); - ret = rpz_synthesize_nodata(r, ms, &ms->qinfo); + ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); goto done; } switch(action) { case RPZ_NXDOMAIN_ACTION: - ret = rpz_synthesize_nxdomain(r, ms, &ms->qinfo); + ret = rpz_synthesize_nxdomain(r, ms, &ms->qinfo, az); break; case RPZ_NODATA_ACTION: - ret = rpz_synthesize_nodata(r, ms, &ms->qinfo); + ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); break; case RPZ_TCP_ONLY_ACTION: /* basically a passthru here but the tcp-only will be @@ -1914,12 +1957,12 @@ rpz_apply_nsip_trigger(struct module_qstate* ms, struct rpz* r, ret = NULL; break; case RPZ_DROP_ACTION: - ret = rpz_synthesize_nodata(r, ms, &ms->qinfo); + ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); ms->is_drop = 1; break; case RPZ_LOCAL_DATA_ACTION: - ret = rpz_synthesize_nsip_localdata(r, ms, raddr); - if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, &ms->qinfo); } + ret = rpz_synthesize_nsip_localdata(r, ms, raddr, az); + if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); } break; case RPZ_PASSTHRU_ACTION: ret = NULL; @@ -1937,7 +1980,8 @@ done: struct dns_msg* rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r, - struct local_zone* z, struct matched_delegation_point const* match) + struct local_zone* z, struct matched_delegation_point const* match, + struct auth_zone* az) { struct dns_msg* ret = NULL; enum rpz_action action = localzone_type_to_rpz_action(z->type); @@ -1950,10 +1994,10 @@ rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r, switch(action) { case RPZ_NXDOMAIN_ACTION: - ret = rpz_synthesize_nxdomain(r, ms, &ms->qinfo); + ret = rpz_synthesize_nxdomain(r, ms, &ms->qinfo, az); break; case RPZ_NODATA_ACTION: - ret = rpz_synthesize_nodata(r, ms, &ms->qinfo); + ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); break; case RPZ_TCP_ONLY_ACTION: /* basically a passthru here but the tcp-only will be @@ -1962,12 +2006,12 @@ rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r, ret = NULL; break; case RPZ_DROP_ACTION: - ret = rpz_synthesize_nodata(r, ms, &ms->qinfo); + ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); ms->is_drop = 1; break; case RPZ_LOCAL_DATA_ACTION: - ret = rpz_synthesize_nsdname_localdata(r, ms, z, match); - if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, &ms->qinfo); } + ret = rpz_synthesize_nsdname_localdata(r, ms, z, match, az); + if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); } break; case RPZ_PASSTHRU_ACTION: ret = NULL; @@ -2066,8 +2110,8 @@ rpz_callback_from_iterator_module(struct module_qstate* ms, struct iter_qstate* lock_rw_unlock(&az->rpz_lock); if(raddr == NULL && z == NULL) { return NULL; } - else if(raddr != NULL) { return rpz_apply_nsip_trigger(ms, r, raddr); } - else if(z != NULL) { return rpz_apply_nsdname_trigger(ms, r, z, &match); } + else if(raddr != NULL) { return rpz_apply_nsip_trigger(ms, r, raddr, a); } + else if(z != NULL) { return rpz_apply_nsdname_trigger(ms, r, z, &match, a); } else { return NULL; } } @@ -2128,10 +2172,10 @@ struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms, } switch(localzone_type_to_rpz_action(lzt)) { case RPZ_NXDOMAIN_ACTION: - ret = rpz_synthesize_nxdomain(r, ms, &is->qchase); + ret = rpz_synthesize_nxdomain(r, ms, &is->qchase, a); break; case RPZ_NODATA_ACTION: - ret = rpz_synthesize_nodata(r, ms, &is->qchase); + ret = rpz_synthesize_nodata(r, ms, &is->qchase, a); break; case RPZ_TCP_ONLY_ACTION: /* basically a passthru here but the tcp-only will be @@ -2140,12 +2184,12 @@ struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms, ret = NULL; break; case RPZ_DROP_ACTION: - ret = rpz_synthesize_nodata(r, ms, &is->qchase); + ret = rpz_synthesize_nodata(r, ms, &is->qchase, a); ms->is_drop = 1; break; case RPZ_LOCAL_DATA_ACTION: - ret = rpz_synthesize_qname_localdata_msg(r, ms, &is->qchase, z); - if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, &is->qchase); } + ret = rpz_synthesize_qname_localdata_msg(r, ms, &is->qchase, z, a); + if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, &is->qchase, a); } break; case RPZ_PASSTHRU_ACTION: ret = NULL; From 355526da7def69e614d2c49b65953195d1489af8 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 14 May 2021 16:34:38 +0200 Subject: [PATCH 63/68] - rpz-triggers, the added soa for client ip modified answers is affected by the minimal-responses config option. --- cachedb/cachedb.c | 2 +- daemon/worker.c | 4 ++-- services/authzone.c | 2 +- services/localzone.c | 2 +- services/mesh.c | 4 ++-- services/rpz.c | 3 +-- testdata/rpz_clientip.rpl | 1 + util/data/msgencode.c | 4 ++-- util/data/msgencode.h | 4 +--- 9 files changed, 12 insertions(+), 14 deletions(-) diff --git a/cachedb/cachedb.c b/cachedb/cachedb.c index 707319e85..af4ffe5f2 100644 --- a/cachedb/cachedb.c +++ b/cachedb/cachedb.c @@ -402,7 +402,7 @@ prep_data(struct module_qstate* qstate, struct sldns_buffer* buf) qstate->return_msg->rep); if(!reply_info_answer_encode(&qstate->return_msg->qinfo, qstate->return_msg->rep, 0, qstate->query_flags, - buf, 0, 1, qstate->env->scratch, 65535, &edns, 1, 0, 0)) + buf, 0, 1, qstate->env->scratch, 65535, &edns, 1, 0)) return 0; /* TTLs in the return_msg are relative to time(0) so we have to diff --git a/daemon/worker.c b/daemon/worker.c index 421cd82c4..2ee8b8d9b 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -522,7 +522,7 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo, repinfo->c, worker->scratchpad) || !reply_info_answer_encode(&msg->qinfo, msg->rep, id, flags, repinfo->c->buffer, 0, 1, worker->scratchpad, - udpsize, edns, (int)(edns->bits & EDNS_DO), secure, 0)) { + udpsize, edns, (int)(edns->bits & EDNS_DO), secure)) { if(!inplace_cb_reply_servfail_call(&worker->env, qinfo, NULL, NULL, LDNS_RCODE_SERVFAIL, edns, repinfo, worker->scratchpad, worker->env.now_tv)) @@ -726,7 +726,7 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo, repinfo->c, worker->scratchpad) || !reply_info_answer_encode(qinfo, encode_rep, id, flags, repinfo->c->buffer, timenow, 1, worker->scratchpad, - udpsize, edns, (int)(edns->bits & EDNS_DO), *is_secure_answer, 0)) { + udpsize, edns, (int)(edns->bits & EDNS_DO), *is_secure_answer)) { if(!inplace_cb_reply_servfail_call(&worker->env, qinfo, NULL, NULL, LDNS_RCODE_SERVFAIL, edns, repinfo, worker->scratchpad, worker->env.now_tv)) diff --git a/services/authzone.c b/services/authzone.c index 19f462586..b0cfc4e26 100644 --- a/services/authzone.c +++ b/services/authzone.c @@ -3495,7 +3495,7 @@ auth_answer_encode(struct query_info* qinfo, struct module_env* env, *(uint16_t*)sldns_buffer_begin(buf), sldns_buffer_read_u16_at(buf, 2), buf, 0, 0, temp, udpsize, edns, - (int)(edns->bits&EDNS_DO), 0, 0)) { + (int)(edns->bits&EDNS_DO), 0)) { error_encode(buf, (LDNS_RCODE_SERVFAIL|BIT_AA), qinfo, *(uint16_t*)sldns_buffer_begin(buf), sldns_buffer_read_u16_at(buf, 2), edns); diff --git a/services/localzone.c b/services/localzone.c index 6eecab62a..a24137eac 100644 --- a/services/localzone.c +++ b/services/localzone.c @@ -1261,7 +1261,7 @@ local_encode(struct query_info* qinfo, struct module_env* env, if(!inplace_cb_reply_local_call(env, qinfo, NULL, &rep, rcode, edns, repinfo, temp, env->now_tv) || !reply_info_answer_encode(qinfo, &rep, *(uint16_t*)sldns_buffer_begin(buf), sldns_buffer_read_u16_at(buf, 2), - buf, 0, 0, temp, udpsize, edns, (int)(edns->bits&EDNS_DO), 0, 0)) { + buf, 0, 0, temp, udpsize, edns, (int)(edns->bits&EDNS_DO), 0)) { error_encode(buf, (LDNS_RCODE_SERVFAIL|BIT_AA), qinfo, *(uint16_t*)sldns_buffer_begin(buf), sldns_buffer_read_u16_at(buf, 2), edns); diff --git a/services/mesh.c b/services/mesh.c index 3ff14a0dd..f00ad3e86 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -1167,7 +1167,7 @@ mesh_do_callback(struct mesh_state* m, int rcode, struct reply_info* rep, !reply_info_answer_encode(&m->s.qinfo, rep, r->qid, r->qflags, r->buf, 0, 1, m->s.env->scratch, udp_size, &r->edns, - (int)(r->edns.bits & EDNS_DO), secure, 0)) + (int)(r->edns.bits & EDNS_DO), secure)) { fptr_ok(fptr_whitelist_mesh_cb(r->cb)); (*r->cb)(r->cb_arg, LDNS_RCODE_SERVFAIL, r->buf, @@ -1313,7 +1313,7 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, !reply_info_answer_encode(&m->s.qinfo, rep, r->qid, r->qflags, r_buffer, 0, 1, m->s.env->scratch, udp_size, &r->edns, (int)(r->edns.bits & EDNS_DO), - secure, 0)) + secure)) { if(!inplace_cb_reply_servfail_call(m->s.env, &m->s.qinfo, &m->s, rep, LDNS_RCODE_SERVFAIL, &r->edns, &r->query_reply, m->s.region, &r->start_time)) diff --git a/services/rpz.c b/services/rpz.c index a612cc094..d7bada4a9 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -1528,8 +1528,7 @@ rpz_local_encode(struct module_env* env, struct query_info* qinfo, repinfo, temp, env->now_tv) || !reply_info_answer_encode(qinfo, &rep, *(uint16_t*)sldns_buffer_begin(buf), sldns_buffer_read_u16_at(buf, 2), - buf, 0, 0, temp, udpsize, edns, (int)(edns->bits&EDNS_DO), 0, - 1 /* not minimal */ )) { + buf, 0, 0, temp, udpsize, edns, (int)(edns->bits&EDNS_DO), 0)) { error_encode(buf, (LDNS_RCODE_SERVFAIL|BIT_AA), qinfo, *(uint16_t*)sldns_buffer_begin(buf), sldns_buffer_read_u16_at(buf, 2), edns); diff --git a/testdata/rpz_clientip.rpl b/testdata/rpz_clientip.rpl index 7d5b5330d..78e05ad91 100644 --- a/testdata/rpz_clientip.rpl +++ b/testdata/rpz_clientip.rpl @@ -3,6 +3,7 @@ server: module-config: "respip validator iterator" target-fetch-policy: "0 0 0 0 0" qname-minimisation: no + minimal-responses: no access-control: 192.0.0.0/8 allow rpz: diff --git a/util/data/msgencode.c b/util/data/msgencode.c index 1cf30dd5d..5f297b551 100644 --- a/util/data/msgencode.c +++ b/util/data/msgencode.c @@ -878,7 +878,7 @@ int reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep, uint16_t id, uint16_t qflags, sldns_buffer* pkt, time_t timenow, int cached, struct regional* region, uint16_t udpsize, - struct edns_data* edns, int dnssec, int secure, int notminimal) + struct edns_data* edns, int dnssec, int secure) { uint16_t flags; unsigned int attach_edns = 0; @@ -916,7 +916,7 @@ reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep, } if(!reply_info_encode(qinf, rep, id, flags, pkt, timenow, region, - udpsize, dnssec, (notminimal?0:MINIMAL_RESPONSES))) { + udpsize, dnssec, MINIMAL_RESPONSES)) { log_err("reply encode: out of memory"); return 0; } diff --git a/util/data/msgencode.h b/util/data/msgencode.h index 3baa0eaf9..30dc515cb 100644 --- a/util/data/msgencode.h +++ b/util/data/msgencode.h @@ -64,14 +64,12 @@ struct edns_data; * or if edns_present = 0, it is not included. * @param dnssec: if 0 DNSSEC records are omitted from the answer. * @param secure: if 1, the AD bit is set in the reply. - * @param notminimal: if 1, ignore minimalresponses and include additional - * section anyway. * @return: 0 on error (server failure). */ int reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep, uint16_t id, uint16_t qflags, struct sldns_buffer* dest, time_t timenow, int cached, struct regional* region, uint16_t udpsize, - struct edns_data* edns, int dnssec, int secure, int notminimal); + struct edns_data* edns, int dnssec, int secure); /** * Regenerate the wireformat from the stored msg reply. From 568aab1b67eb78e4307f517e29c891328d7d2c9b Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 14 May 2021 16:41:54 +0200 Subject: [PATCH 64/68] - rpz-triggers, in callback from iterator, if rpz is disabled, skip. --- services/rpz.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/rpz.c b/services/rpz.c index d7bada4a9..a389cf101 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -2089,6 +2089,10 @@ rpz_callback_from_iterator_module(struct module_qstate* ms, struct iter_qstate* for(a = az->rpz_first; a != NULL; a = a->rpz_az_next) { lock_rw_rdlock(&a->lock); r = a->rpz; + if(r->disabled) { + lock_rw_unlock(&a->lock); + continue; + } /* the nsdname has precedence over the nsip triggers */ z = rpz_delegation_point_zone_lookup(is->dp, r->nsdname_zones, From 44f5d27683c4970ae66c7f10a7a2f991eba151f1 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 14 May 2021 17:27:15 +0200 Subject: [PATCH 65/68] - rpz-triggers, check for alloc failure and zone for add rpz soa. --- services/rpz.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/rpz.c b/services/rpz.c index a389cf101..eb49f1cb3 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -1637,6 +1637,7 @@ rpz_add_soa(struct reply_info* rep, struct module_qstate* ms, struct auth_rrset* soa = NULL; struct ub_packed_rrset_key* rsoa = NULL; struct ub_packed_rrset_key** prevrrsets; + if(!az) return 1; soa = auth_zone_get_soa_rrset(az); if(!soa) return 1; if(!rep) return 0; @@ -1645,6 +1646,8 @@ rpz_add_soa(struct reply_info* rep, struct module_qstate* ms, prevrrsets = rep->rrsets; rep->rrsets = regional_alloc_zero(ms->region, sizeof(*rep->rrsets)*(rep->rrset_count+1)); + if(!rep->rrsets) + return 0; if(prevrrsets && rep->rrset_count > 0) memcpy(rep->rrsets, prevrrsets, rep->rrset_count*sizeof(*rep->rrsets)); rep->rrset_count++; From ddec5aa559540e462372ab503da8944976940c45 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 19 May 2021 11:40:31 +0200 Subject: [PATCH 66/68] - rpz-triggers, spelling fix in comment. --- services/rpz.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/rpz.c b/services/rpz.c index eb49f1cb3..7fb3591b0 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -1954,7 +1954,7 @@ rpz_apply_nsip_trigger(struct module_qstate* ms, struct rpz* r, break; case RPZ_TCP_ONLY_ACTION: /* basically a passthru here but the tcp-only will be - * honored before the query gets send */ + * honored before the query gets sent. */ ms->respip_action_info->action = respip_truncate; ret = NULL; break; @@ -2003,7 +2003,7 @@ rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r, break; case RPZ_TCP_ONLY_ACTION: /* basically a passthru here but the tcp-only will be - * honored before the query gets send */ + * honored before the query gets sent. */ ms->respip_action_info->action = respip_truncate; ret = NULL; break; From 0a6b22dc55faa7a49f0f3f1aef0ac44fad155f20 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 21 May 2021 14:05:19 +0200 Subject: [PATCH 67/68] - rpz-triggers, rpz log and stats for nsip and clientip and nsdname. --- services/rpz.c | 87 +++++++++++++++++++++++++++++----------- testdata/rpz_nsdname.rpl | 2 + testdata/rpz_nsip.rpl | 2 + 3 files changed, 67 insertions(+), 24 deletions(-) diff --git a/services/rpz.c b/services/rpz.c index 7fb3591b0..3a4404360 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -54,6 +54,7 @@ #include "services/cache/dns.h" #include "iterator/iterator.h" #include "iterator/iter_delegpt.h" +#include "daemon/worker.h" typedef struct resp_addr rpz_aclnode_type; @@ -1366,25 +1367,42 @@ rpz_remove_rr(struct rpz* r, size_t aznamelen, uint8_t* dname, size_t dnamelen, /** print log information for an applied RPZ policy. Based on local-zone's * lz_inform_print(). + * The repinfo contains the reply address. If it is NULL, the module + * state is used to report the first IP address (if any). + * The dname is used, for the applied rpz, if NULL, addrnode is used. */ static void -log_rpz_apply(uint8_t* dname, enum rpz_action a, struct query_info* qinfo, - struct comm_reply* repinfo, char* log_name) +log_rpz_apply(char* trigger, uint8_t* dname, struct addr_tree_node* addrnode, + enum rpz_action a, struct query_info* qinfo, + struct comm_reply* repinfo, struct module_qstate* ms, char* log_name) { - char ip[128], txt[512]; + char ip[128], txt[512], portstr[32]; char dnamestr[LDNS_MAX_DOMAINLEN+1]; - uint16_t port = ntohs(((struct sockaddr_in*)&repinfo->addr)->sin_port); - if(dname) + uint16_t port = 0; + if(dname) { dname_str(dname, dnamestr); - else dnamestr[0]=0; - addr_to_str(&repinfo->addr, repinfo->addrlen, ip, sizeof(ip)); - if(log_name) - snprintf(txt, sizeof(txt), "rpz: applied [%s] %s %s %s@%u", - log_name, dnamestr, rpz_action_to_string(a), ip, - (unsigned)port); - else - snprintf(txt, sizeof(txt), "rpz: applied %s %s %s@%u", - dnamestr, rpz_action_to_string(a), ip, (unsigned)port); + } else if(addrnode) { + char a[128]; + addr_to_str(&addrnode->addr, addrnode->addrlen, a, sizeof(a)); + snprintf(dnamestr, sizeof(dnamestr), "%s/%d", a, addrnode->net); + } else { + dnamestr[0]=0; + } + if(repinfo) { + addr_to_str(&repinfo->addr, repinfo->addrlen, ip, sizeof(ip)); + port = ntohs(((struct sockaddr_in*)&repinfo->addr)->sin_port); + } else if(ms && ms->mesh_info && ms->mesh_info->reply_list) { + addr_to_str(&ms->mesh_info->reply_list->query_reply.addr, ms->mesh_info->reply_list->query_reply.addrlen, ip, sizeof(ip)); + port = ntohs(((struct sockaddr_in*)&ms->mesh_info->reply_list->query_reply.addr)->sin_port); + } else { + ip[0]=0; + port = 0; + } + snprintf(portstr, sizeof(portstr), "@%u", (unsigned)port); + snprintf(txt, sizeof(txt), "rpz: applied %s%s%s%s %s %s %s%s", + (log_name?"[":""), (log_name?log_name:""), (log_name?"] ":""), + trigger, dnamestr, rpz_action_to_string(a), + (ip[0]?ip:""), (ip[0]?portstr:"")); log_nametypeclass(0, txt, qinfo->qname, qinfo->qtype, qinfo->qclass); } @@ -1448,9 +1466,11 @@ rpz_resolve_client_action_and_zone(struct auth_zones* az, struct query_info* qin node = rpz_ipbased_trigger_lookup(r->client_set, &repinfo->addr, repinfo->addrlen, "clientip"); if((z || node) && r->action_override == RPZ_DISABLED_ACTION) { if(r->log) - log_rpz_apply(z->name, + log_rpz_apply((node?"clientip":"qname"), + (z?z->name:NULL), + (node?&node->node:NULL), r->action_override, - qinfo, repinfo, r->log_name); + qinfo, repinfo, NULL, r->log_name); stats->rpz_action[r->action_override]++; lock_rw_unlock(&z->lock); z = NULL; @@ -1880,8 +1900,8 @@ rpz_synthesize_qname_localdata(struct module_env* env, struct rpz* r, qinfo->local_alias->rrset->rk.dname = qinfo->qname; qinfo->local_alias->rrset->rk.dname_len = qinfo->qname_len; if(r->log) { - log_rpz_apply(z->name, RPZ_CNAME_OVERRIDE_ACTION, - qinfo, repinfo, r->log_name); + log_rpz_apply("qname", z->name, NULL, RPZ_CNAME_OVERRIDE_ACTION, + qinfo, repinfo, NULL, r->log_name); } stats->rpz_action[RPZ_CNAME_OVERRIDE_ACTION]++; return 0; @@ -1891,9 +1911,9 @@ rpz_synthesize_qname_localdata(struct module_env* env, struct rpz* r, edns, repinfo, buf, temp, dname_count_labels(qinfo->qname), &ld, lzt, -1, NULL, 0, NULL, 0)) { if(r->log) { - log_rpz_apply(z->name, + log_rpz_apply("qname", z->name, NULL, localzone_type_to_rpz_action(lzt), qinfo, - repinfo, r->log_name); + repinfo, NULL, r->log_name); } stats->rpz_action[localzone_type_to_rpz_action(lzt)]++; return !qinfo->local_alias; @@ -1902,8 +1922,8 @@ rpz_synthesize_qname_localdata(struct module_env* env, struct rpz* r, ret = local_zones_zone_answer(z, env, qinfo, edns, repinfo, buf, temp, 0 /* no local data used */, lzt); if(r->log) { - log_rpz_apply(z->name, localzone_type_to_rpz_action(lzt), - qinfo, repinfo, r->log_name); + log_rpz_apply("qname", z->name, NULL, localzone_type_to_rpz_action(lzt), + qinfo, repinfo, NULL, r->log_name); } stats->rpz_action[localzone_type_to_rpz_action(lzt)]++; return ret; @@ -1976,6 +1996,11 @@ rpz_apply_nsip_trigger(struct module_qstate* ms, struct rpz* r, } done: + if(r->log) + log_rpz_apply("nsip", NULL, &raddr->node, + action, &ms->qinfo, NULL, ms, r->log_name); + if(ms->env->worker) + ms->env->worker->stats.rpz_action[action]++; lock_rw_unlock(&raddr->lock); return ret; } @@ -2024,6 +2049,11 @@ rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r, ret = NULL; } + if(r->log) + log_rpz_apply("nsdname", match->dname, NULL, + action, &ms->qinfo, NULL, ms, r->log_name); + if(ms->env->worker) + ms->env->worker->stats.rpz_action[action]++; return ret; } @@ -2146,6 +2176,12 @@ struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms, z = rpz_find_zone(r->local_zones, is->qchase.qname, is->qchase.qname_len, is->qchase.qclass, 0, 0, 0); if(z && r->action_override == RPZ_DISABLED_ACTION) { + if(r->log) + log_rpz_apply("qname", z->name, NULL, + r->action_override, + &ms->qinfo, NULL, ms, r->log_name); + if(ms->env->worker) + ms->env->worker->stats.rpz_action[r->action_override]++; lock_rw_unlock(&z->lock); z = NULL; } @@ -2240,8 +2276,11 @@ rpz_apply_maybe_clientip_trigger(struct auth_zones* az, struct module_env* env, edns, repinfo, buf, temp, *a_out); } else { if(*r_out && (*r_out)->log) - log_rpz_apply(((*z_out)?(*z_out)->name:NULL), - client_action, qinfo, repinfo, + log_rpz_apply( + (node?"clientip":"qname"), + ((*z_out)?(*z_out)->name:NULL), + (node?&node->node:NULL), + client_action, qinfo, repinfo, NULL, (*r_out)->log_name); local_zones_zone_answer(*z_out /*likely NULL, no zone*/, env, qinfo, edns, repinfo, buf, temp, 0 /* no local data used */, diff --git a/testdata/rpz_nsdname.rpl b/testdata/rpz_nsdname.rpl index 79b5bec40..08ff3c2e2 100644 --- a/testdata/rpz_nsdname.rpl +++ b/testdata/rpz_nsdname.rpl @@ -7,6 +7,8 @@ server: 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 diff --git a/testdata/rpz_nsip.rpl b/testdata/rpz_nsip.rpl index 58b377e8f..ac132cae0 100644 --- a/testdata/rpz_nsip.rpl +++ b/testdata/rpz_nsip.rpl @@ -7,6 +7,8 @@ server: 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 From 76532f7447a610949f0d2e0d4e74f81cda5ea4af Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 21 May 2021 14:56:38 +0200 Subject: [PATCH 68/68] - rpz-triggers, silence qname trigger explanation in rpz-log, this is backwards compatible. --- services/rpz.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/services/rpz.c b/services/rpz.c index 3a4404360..085353b3f 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -1399,9 +1399,11 @@ log_rpz_apply(char* trigger, uint8_t* dname, struct addr_tree_node* addrnode, port = 0; } snprintf(portstr, sizeof(portstr), "@%u", (unsigned)port); - snprintf(txt, sizeof(txt), "rpz: applied %s%s%s%s %s %s %s%s", + snprintf(txt, sizeof(txt), "rpz: applied %s%s%s%s%s%s %s %s%s", (log_name?"[":""), (log_name?log_name:""), (log_name?"] ":""), - trigger, dnamestr, rpz_action_to_string(a), + (strcmp(trigger,"qname")==0?"":trigger), + (strcmp(trigger,"qname")==0?"":" "), + dnamestr, rpz_action_to_string(a), (ip[0]?ip:""), (ip[0]?portstr:"")); log_nametypeclass(0, txt, qinfo->qname, qinfo->qtype, qinfo->qclass); }