From dd70c2ef9afc32fae0ce48c7419bed23e32d0370 Mon Sep 17 00:00:00 2001 From: mb Date: Thu, 29 Oct 2020 15:58:13 +0100 Subject: [PATCH 01/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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/26] 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