From 50dcadd495f326ba7a2ddf652466a6b83a371a78 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 14 May 2021 15:34:48 +0200 Subject: [PATCH] - rpz-triggers, for clientip modified answers the rpz SOA is added to the additional section with the serial number and name of the rpz zone that was applied. --- cachedb/cachedb.c | 2 +- daemon/worker.c | 4 +-- services/authzone.c | 13 +++++++++- services/authzone.h | 3 +++ services/localzone.c | 2 +- services/mesh.c | 4 +-- services/rpz.c | 51 +++++++++++++++++++++++++++++++++------ testdata/rpz_clientip.rpl | 6 +++++ util/data/msgencode.c | 4 +-- util/data/msgencode.h | 4 ++- 10 files changed, 76 insertions(+), 17 deletions(-) diff --git a/cachedb/cachedb.c b/cachedb/cachedb.c index af4ffe5f2..707319e85 100644 --- a/cachedb/cachedb.c +++ b/cachedb/cachedb.c @@ -402,7 +402,7 @@ prep_data(struct module_qstate* qstate, struct sldns_buffer* buf) qstate->return_msg->rep); if(!reply_info_answer_encode(&qstate->return_msg->qinfo, qstate->return_msg->rep, 0, qstate->query_flags, - buf, 0, 1, qstate->env->scratch, 65535, &edns, 1, 0)) + buf, 0, 1, qstate->env->scratch, 65535, &edns, 1, 0, 0)) return 0; /* TTLs in the return_msg are relative to time(0) so we have to diff --git a/daemon/worker.c b/daemon/worker.c index 2ee8b8d9b..421cd82c4 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -522,7 +522,7 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo, repinfo->c, worker->scratchpad) || !reply_info_answer_encode(&msg->qinfo, msg->rep, id, flags, repinfo->c->buffer, 0, 1, worker->scratchpad, - udpsize, edns, (int)(edns->bits & EDNS_DO), secure)) { + udpsize, edns, (int)(edns->bits & EDNS_DO), secure, 0)) { if(!inplace_cb_reply_servfail_call(&worker->env, qinfo, NULL, NULL, LDNS_RCODE_SERVFAIL, edns, repinfo, worker->scratchpad, worker->env.now_tv)) @@ -726,7 +726,7 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo, repinfo->c, worker->scratchpad) || !reply_info_answer_encode(qinfo, encode_rep, id, flags, repinfo->c->buffer, timenow, 1, worker->scratchpad, - udpsize, edns, (int)(edns->bits & EDNS_DO), *is_secure_answer)) { + udpsize, edns, (int)(edns->bits & EDNS_DO), *is_secure_answer, 0)) { if(!inplace_cb_reply_servfail_call(&worker->env, qinfo, NULL, NULL, LDNS_RCODE_SERVFAIL, edns, repinfo, worker->scratchpad, worker->env.now_tv)) diff --git a/services/authzone.c b/services/authzone.c index 196fe6693..19f462586 100644 --- a/services/authzone.c +++ b/services/authzone.c @@ -1948,6 +1948,17 @@ static int auth_zone_zonemd_check_hash(struct auth_zone* z, return 0; } +/** find the apex SOA RRset, if it exists */ +struct auth_rrset* auth_zone_get_soa_rrset(struct auth_zone* z) +{ + struct auth_data* apex; + struct auth_rrset* soa; + apex = az_find_name(z, z->name, z->namelen); + if(!apex) return NULL; + soa = az_domain_rrset(apex, LDNS_RR_TYPE_SOA); + return soa; +} + /** find serial number of zone or false if none */ int auth_zone_get_serial(struct auth_zone* z, uint32_t* serial) @@ -3484,7 +3495,7 @@ auth_answer_encode(struct query_info* qinfo, struct module_env* env, *(uint16_t*)sldns_buffer_begin(buf), sldns_buffer_read_u16_at(buf, 2), buf, 0, 0, temp, udpsize, edns, - (int)(edns->bits&EDNS_DO), 0)) { + (int)(edns->bits&EDNS_DO), 0, 0)) { error_encode(buf, (LDNS_RCODE_SERVFAIL|BIT_AA), qinfo, *(uint16_t*)sldns_buffer_begin(buf), sldns_buffer_read_u16_at(buf, 2), edns); diff --git a/services/authzone.h b/services/authzone.h index 4810017a3..7b9fc60fc 100644 --- a/services/authzone.h +++ b/services/authzone.h @@ -632,6 +632,9 @@ int auth_zones_startprobesequence(struct auth_zones* az, /** read auth zone from zonefile. caller must lock zone. false on failure */ int auth_zone_read_zonefile(struct auth_zone* z, struct config_file* cfg); +/** find the apex SOA RRset, if it exists. NULL if no SOA RRset. */ +struct auth_rrset* auth_zone_get_soa_rrset(struct auth_zone* z); + /** find serial number of zone or false if none (no SOA record) */ int auth_zone_get_serial(struct auth_zone* z, uint32_t* serial); diff --git a/services/localzone.c b/services/localzone.c index a24137eac..6eecab62a 100644 --- a/services/localzone.c +++ b/services/localzone.c @@ -1261,7 +1261,7 @@ local_encode(struct query_info* qinfo, struct module_env* env, if(!inplace_cb_reply_local_call(env, qinfo, NULL, &rep, rcode, edns, repinfo, temp, env->now_tv) || !reply_info_answer_encode(qinfo, &rep, *(uint16_t*)sldns_buffer_begin(buf), sldns_buffer_read_u16_at(buf, 2), - buf, 0, 0, temp, udpsize, edns, (int)(edns->bits&EDNS_DO), 0)) { + buf, 0, 0, temp, udpsize, edns, (int)(edns->bits&EDNS_DO), 0, 0)) { error_encode(buf, (LDNS_RCODE_SERVFAIL|BIT_AA), qinfo, *(uint16_t*)sldns_buffer_begin(buf), sldns_buffer_read_u16_at(buf, 2), edns); diff --git a/services/mesh.c b/services/mesh.c index f00ad3e86..3ff14a0dd 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -1167,7 +1167,7 @@ mesh_do_callback(struct mesh_state* m, int rcode, struct reply_info* rep, !reply_info_answer_encode(&m->s.qinfo, rep, r->qid, r->qflags, r->buf, 0, 1, m->s.env->scratch, udp_size, &r->edns, - (int)(r->edns.bits & EDNS_DO), secure)) + (int)(r->edns.bits & EDNS_DO), secure, 0)) { fptr_ok(fptr_whitelist_mesh_cb(r->cb)); (*r->cb)(r->cb_arg, LDNS_RCODE_SERVFAIL, r->buf, @@ -1313,7 +1313,7 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, !reply_info_answer_encode(&m->s.qinfo, rep, r->qid, r->qflags, r_buffer, 0, 1, m->s.env->scratch, udp_size, &r->edns, (int)(r->edns.bits & EDNS_DO), - secure)) + secure, 0)) { if(!inplace_cb_reply_servfail_call(m->s.env, &m->s.qinfo, &m->s, rep, LDNS_RCODE_SERVFAIL, &r->edns, &r->query_reply, m->s.region, &r->start_time)) diff --git a/services/rpz.c b/services/rpz.c index e53c5cf81..9e3510b7d 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -1494,20 +1494,30 @@ static int rpz_local_encode(struct module_env* env, struct query_info* qinfo, struct edns_data* edns, struct comm_reply* repinfo, sldns_buffer* buf, struct regional* temp, struct ub_packed_rrset_key* rrset, int ansec, - int rcode) + int rcode, struct ub_packed_rrset_key* soa_rrset) { struct reply_info rep; uint16_t udpsize; + struct ub_packed_rrset_key* rrsetlist[3]; memset(&rep, 0, sizeof(rep)); rep.flags = (uint16_t)((BIT_QR | BIT_AA | BIT_RA) | rcode); rep.qdcount = 1; rep.rrset_count = ansec; + rep.rrsets = rrsetlist; if(ansec > 0) { rep.an_numrrsets = 1; - rep.rrsets = &rrset; + rep.rrsets[0] = rrset; rep.ttl = ((struct packed_rrset_data*)rrset->entry.data)->rr_ttl[0]; } + if(soa_rrset != NULL) { + rep.ar_numrrsets = 1; + rep.rrsets[rep.rrset_count] = soa_rrset; + rep.rrset_count ++; + if(rep.ttl < ((struct packed_rrset_data*)soa_rrset->entry.data)->rr_ttl[0]) { + rep.ttl = ((struct packed_rrset_data*)soa_rrset->entry.data)->rr_ttl[0]; + } + } udpsize = edns->udp_size; edns->edns_version = EDNS_ADVERTISED_VERSION; @@ -1518,7 +1528,8 @@ rpz_local_encode(struct module_env* env, struct query_info* qinfo, repinfo, temp, env->now_tv) || !reply_info_answer_encode(qinfo, &rep, *(uint16_t*)sldns_buffer_begin(buf), sldns_buffer_read_u16_at(buf, 2), - buf, 0, 0, temp, udpsize, edns, (int)(edns->bits&EDNS_DO), 0)) { + buf, 0, 0, temp, udpsize, edns, (int)(edns->bits&EDNS_DO), 0, + 1 /* not minimal */ )) { error_encode(buf, (LDNS_RCODE_SERVFAIL|BIT_AA), qinfo, *(uint16_t*)sldns_buffer_begin(buf), sldns_buffer_read_u16_at(buf, 2), edns); @@ -1544,11 +1555,12 @@ static void rpz_apply_clientip_localdata_action(struct clientip_synthesized_rr* raddr, struct module_env* env, struct query_info* qinfo, struct edns_data* edns, struct comm_reply* repinfo, sldns_buffer* buf, - struct regional* temp) + struct regional* temp, struct auth_zone* auth_zone) { struct local_rrset* rrset; enum rpz_action action = RPZ_INVALID_ACTION; struct ub_packed_rrset_key* rp = NULL; + struct ub_packed_rrset_key* rsoa = NULL; int rcode = LDNS_RCODE_NOERROR|BIT_AA; int rrset_count = 1; @@ -1573,12 +1585,37 @@ rpz_apply_clientip_localdata_action(struct clientip_synthesized_rr* raddr, return; } - rp->rk.flags |= PACKED_RRSET_FIXEDTTL; + rp->rk.flags |= PACKED_RRSET_FIXEDTTL | PACKED_RRSET_RPZ; rp->rk.dname = qinfo->qname; rp->rk.dname_len = qinfo->qname_len; + rp->entry.hash = rrset_key_hash(&rp->rk); nodata: + if(auth_zone) { + struct auth_rrset* soa = NULL; + soa = auth_zone_get_soa_rrset(auth_zone); + if(soa) { + struct ub_packed_rrset_key csoa; + memset(&csoa, 0, sizeof(csoa)); + csoa.entry.key = &csoa; + csoa.rk.rrset_class = htons(LDNS_RR_CLASS_IN); + csoa.rk.type = htons(LDNS_RR_TYPE_SOA); + csoa.rk.flags |= PACKED_RRSET_FIXEDTTL + | PACKED_RRSET_RPZ; + csoa.rk.dname = auth_zone->name; + csoa.rk.dname_len = auth_zone->namelen; + csoa.entry.hash = rrset_key_hash(&csoa.rk); + csoa.entry.data = soa->data; + rsoa = respip_copy_rrset(&csoa, temp); + if(!rsoa) { + verbose(VERB_ALGO, "rpz: local data action soa: out of memory"); + return; + } + } + + } + rpz_local_encode(env, qinfo, edns, repinfo, buf, temp, rp, - rrset_count, rcode); + rrset_count, rcode, rsoa); } static inline struct dns_msg* @@ -2150,7 +2187,7 @@ rpz_apply_maybe_clientip_trigger(struct auth_zones* az, struct module_env* env, stats->rpz_action[client_action]++; if(client_action == RPZ_LOCAL_DATA_ACTION) { rpz_apply_clientip_localdata_action(node, env, qinfo, - edns, repinfo, buf, temp); + edns, repinfo, buf, temp, *a_out); } else { if(*r_out && (*r_out)->log) log_rpz_apply(((*z_out)?(*z_out)->name:NULL), diff --git a/testdata/rpz_clientip.rpl b/testdata/rpz_clientip.rpl index 64082210c..7d5b5330d 100644 --- a/testdata/rpz_clientip.rpl +++ b/testdata/rpz_clientip.rpl @@ -207,6 +207,8 @@ SECTION QUESTION a.a. IN A SECTION ANSWER a.a. IN A 127.0.0.1 +SECTION ADDITIONAL +rpz.example.com. 3600 IN SOA ns1.rpz.example.com. hostmaster.rpz.example.com. ( 1379078166 28800 7200 604800 7200 ) ENTRY_END ; should be synthesized @@ -226,6 +228,8 @@ SECTION QUESTION a.a. IN TXT SECTION ANSWER a.a. IN TXT "42" +SECTION ADDITIONAL +rpz.example.com. 3600 IN SOA ns1.rpz.example.com. hostmaster.rpz.example.com. ( 1379078166 28800 7200 604800 7200 ) ENTRY_END ; should be synthesized NODATA @@ -243,6 +247,8 @@ MATCH all REPLY QR AA RD RA NOERROR SECTION QUESTION a.a. IN AAAA +SECTION ADDITIONAL +rpz.example.com. 3600 IN SOA ns1.rpz.example.com. hostmaster.rpz.example.com. ( 1379078166 28800 7200 604800 7200 ) ENTRY_END ; should be DROPPED diff --git a/util/data/msgencode.c b/util/data/msgencode.c index 5f297b551..1cf30dd5d 100644 --- a/util/data/msgencode.c +++ b/util/data/msgencode.c @@ -878,7 +878,7 @@ int reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep, uint16_t id, uint16_t qflags, sldns_buffer* pkt, time_t timenow, int cached, struct regional* region, uint16_t udpsize, - struct edns_data* edns, int dnssec, int secure) + struct edns_data* edns, int dnssec, int secure, int notminimal) { uint16_t flags; unsigned int attach_edns = 0; @@ -916,7 +916,7 @@ reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep, } if(!reply_info_encode(qinf, rep, id, flags, pkt, timenow, region, - udpsize, dnssec, MINIMAL_RESPONSES)) { + udpsize, dnssec, (notminimal?0:MINIMAL_RESPONSES))) { log_err("reply encode: out of memory"); return 0; } diff --git a/util/data/msgencode.h b/util/data/msgencode.h index 30dc515cb..3baa0eaf9 100644 --- a/util/data/msgencode.h +++ b/util/data/msgencode.h @@ -64,12 +64,14 @@ struct edns_data; * or if edns_present = 0, it is not included. * @param dnssec: if 0 DNSSEC records are omitted from the answer. * @param secure: if 1, the AD bit is set in the reply. + * @param notminimal: if 1, ignore minimalresponses and include additional + * section anyway. * @return: 0 on error (server failure). */ int reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep, uint16_t id, uint16_t qflags, struct sldns_buffer* dest, time_t timenow, int cached, struct regional* region, uint16_t udpsize, - struct edns_data* edns, int dnssec, int secure); + struct edns_data* edns, int dnssec, int secure, int notminimal); /** * Regenerate the wireformat from the stored msg reply. -- 2.47.3