diff options
Diffstat (limited to 'services/rpz.c')
-rw-r--r-- | services/rpz.c | 342 |
1 files changed, 248 insertions, 94 deletions
diff --git a/services/rpz.c b/services/rpz.c index 18d76c07bff3..f036cc5fd649 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -478,43 +478,30 @@ new_cname_override(struct regional* region, uint8_t* ct, size_t ctlen) return rrset; } -struct rpz* -rpz_create(struct config_auth* p) +/** delete the cname override */ +static void +delete_cname_override(struct rpz* r) { - struct rpz* r = calloc(1, sizeof(*r)); - if(!r) - goto err; - - r->region = regional_create_custom(sizeof(struct regional)); - if(!r->region) { - goto err; - } - - 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; - } - - r->client_set = rpz_clientip_synthesized_set_create(); - if(r->client_set == NULL) { - goto err; + if(r->cname_override) { + /* The cname override is what is allocated in the region. */ + regional_free_all(r->region); + r->cname_override = NULL; } +} - r->ns_set = rpz_clientip_synthesized_set_create(); - if(r->ns_set == NULL) { - goto err; +/** Apply rpz config elements to the rpz structure, false on failure. */ +static int +rpz_apply_cfg_elements(struct rpz* r, struct config_auth* p) +{ + if(p->rpz_taglist && p->rpz_taglistlen) { + r->taglistlen = p->rpz_taglistlen; + r->taglist = memdup(p->rpz_taglist, r->taglistlen); + if(!r->taglist) { + log_err("malloc failure on RPZ taglist alloc"); + return 0; + } } - r->taglistlen = p->rpz_taglistlen; - r->taglist = memdup(p->rpz_taglist, r->taglistlen); if(p->rpz_action_override) { r->action_override = rpz_config_to_action(p->rpz_action_override); } @@ -528,17 +515,17 @@ rpz_create(struct config_auth* p) if(!p->rpz_cname) { log_err("rpz: override with cname action found, but no " "rpz-cname-override configured"); - goto err; + return 0; } if(sldns_str2wire_dname_buf(p->rpz_cname, nm, &nmlen) != 0) { log_err("rpz: cannot parse cname override: %s", p->rpz_cname); - goto err; + return 0; } r->cname_override = new_cname_override(r->region, nm, nmlen); if(!r->cname_override) { - goto err; + return 0; } } r->log = p->rpz_log; @@ -546,9 +533,49 @@ rpz_create(struct config_auth* p) if(p->rpz_log_name) { if(!(r->log_name = strdup(p->rpz_log_name))) { log_err("malloc failure on RPZ log_name strdup"); - goto err; + return 0; } } + return 1; +} + +struct rpz* +rpz_create(struct config_auth* p) +{ + struct rpz* r = calloc(1, sizeof(*r)); + if(!r) + goto err; + + r->region = regional_create_custom(sizeof(struct regional)); + if(!r->region) { + goto err; + } + + 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; + } + + r->client_set = rpz_clientip_synthesized_set_create(); + if(r->client_set == NULL) { + goto err; + } + + r->ns_set = rpz_clientip_synthesized_set_create(); + if(r->ns_set == NULL) { + goto err; + } + + if(!rpz_apply_cfg_elements(r, p)) + goto err; return r; err: if(r) { @@ -571,6 +598,32 @@ err: return NULL; } +int +rpz_config(struct rpz* r, struct config_auth* p) +{ + /* If the zonefile changes, it is read later, after which + * rpz_clear and rpz_finish_config is called. */ + + /* free taglist, if any */ + if(r->taglist) { + free(r->taglist); + r->taglist = NULL; + r->taglistlen = 0; + } + + /* free logname, if any */ + if(r->log_name) { + free(r->log_name); + r->log_name = NULL; + } + + delete_cname_override(r); + + if(!rpz_apply_cfg_elements(r, p)) + return 0; + return 1; +} + /** * Remove RPZ zone name from dname * Copy dname to newdname, without the originlen number of trailing bytes @@ -1191,16 +1244,20 @@ rpz_find_zone(struct local_zones* zones, uint8_t* qname, size_t qname_len, uint1 /** Find entry for RR type in the list of rrsets for the clientip. */ static struct local_rrset* rpz_find_synthesized_rrset(uint16_t qtype, - struct clientip_synthesized_rr* data) + struct clientip_synthesized_rr* data, int alias_ok) { - struct local_rrset* cursor = data->data; + struct local_rrset* cursor = data->data, *cname = NULL; while( cursor != NULL) { struct packed_rrset_key* packed_rrset = &cursor->rrset->rk; if(htons(qtype) == packed_rrset->type) { return cursor; } + if(ntohs(packed_rrset->type) == LDNS_RR_TYPE_CNAME && alias_ok) + cname = cursor; cursor = cursor->next; } + if(alias_ok) + return cname; return NULL; } @@ -1386,7 +1443,7 @@ static int rpz_remove_clientip_rr(struct clientip_synthesized_rr* node, struct local_rrset* rrset; struct packed_rrset_data* d; size_t index; - rrset = rpz_find_synthesized_rrset(rr_type, node); + rrset = rpz_find_synthesized_rrset(rr_type, node, 0); if(rrset == NULL) return 0; /* type not found, ignore */ d = (struct packed_rrset_data*)rrset->rrset->entry.data; @@ -1789,7 +1846,7 @@ rpz_apply_clientip_localdata_action(struct clientip_synthesized_rr* raddr, } /* check query type / rr type */ - rrset = rpz_find_synthesized_rrset(qinfo->qtype, raddr); + rrset = rpz_find_synthesized_rrset(qinfo->qtype, raddr, 1); if(rrset == NULL) { verbose(VERB_ALGO, "rpz: unable to find local-data for query"); rrset_count = 0; @@ -1823,6 +1880,28 @@ nodata: rrset_count, rcode, rsoa); } +/** Apply the cname override action, during worker request callback. + * false on failure. */ +static int +rpz_apply_cname_override_action(struct rpz* r, + struct query_info* qinfo, struct regional* temp) +{ + if(!r) + return 0; + 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 = respip_copy_rrset(r->cname_override, temp); + if(qinfo->local_alias->rrset == NULL) { + qinfo->local_alias = NULL; + return 0; /* out of memory */ + } + qinfo->local_alias->rrset->rk.dname = qinfo->qname; + qinfo->local_alias->rrset->rk.dname_len = qinfo->qname_len; + return 1; +} + /** add additional section SOA record to the reply. * Since this gets fed into the normal iterator answer creation, it * gets minimal-responses applied to it, that can remove the additional SOA @@ -1933,6 +2012,7 @@ rpz_synthesize_localdata_from_rrset(struct rpz* ATTR_UNUSED(r), struct module_qs msg = rpz_dns_msg_new(ms->region); if(msg == NULL) { return NULL; } + msg->qinfo = *qi; new_reply_info = construct_reply_info_base(ms->region, LDNS_RCODE_NOERROR | BIT_QR | BIT_AA | BIT_RA, 1, /* qd */ @@ -1975,40 +2055,42 @@ rpz_synthesize_localdata_from_rrset(struct rpz* ATTR_UNUSED(r), struct module_qs static inline struct dns_msg* rpz_synthesize_nsip_localdata(struct rpz* r, struct module_qstate* ms, - struct clientip_synthesized_rr* data, struct auth_zone* az) + struct query_info* qi, struct clientip_synthesized_rr* data, + struct auth_zone* az) { - struct query_info* qi = &ms->qinfo; struct local_rrset* rrset; - rrset = rpz_find_synthesized_rrset(qi->qtype, data); + rrset = rpz_find_synthesized_rrset(qi->qtype, data, 1); if(rrset == NULL) { verbose(VERB_ALGO, "rpz: nsip: no matching local data found"); return NULL; } - return rpz_synthesize_localdata_from_rrset(r, ms, &ms->qinfo, rrset, az); + return rpz_synthesize_localdata_from_rrset(r, ms, qi, rrset, az); } /* 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; + struct local_rrset* p, *cname = NULL; 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; + cname = p; } + if(alias_ok) + return cname; 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 matched_delegation_point const* match, - struct auth_zone* az) + struct query_info* qi, struct local_zone* z, + struct matched_delegation_point const* match, struct auth_zone* az) { struct local_data key; struct local_data* ld; @@ -2029,13 +2111,13 @@ rpz_synthesize_nsdname_localdata(struct rpz* r, struct module_qstate* ms, return NULL; } - rrset = local_data_find_type(ld, ms->qinfo.qtype, 1); + rrset = local_data_find_type(ld, qi->qtype, 1); if(rrset == NULL) { verbose(VERB_ALGO, "rpz: nsdname: no matching local data found"); return NULL; } - return rpz_synthesize_localdata_from_rrset(r, ms, &ms->qinfo, rrset, az); + return rpz_synthesize_localdata_from_rrset(r, ms, qi, rrset, az); } /* like local_data_answer for qname triggers after a cname */ @@ -2052,17 +2134,70 @@ rpz_synthesize_qname_localdata_msg(struct rpz* r, struct module_qstate* ms, key.namelabs = dname_count_labels(qinfo->qname); ld = (struct local_data*)rbtree_search(&z->data, &key.node); if(ld == NULL) { - verbose(VERB_ALGO, "rpz: qname after cname: name not found"); + verbose(VERB_ALGO, "rpz: qname: name not found"); return NULL; } rrset = local_data_find_type(ld, qinfo->qtype, 1); if(rrset == NULL) { - verbose(VERB_ALGO, "rpz: qname after cname: type not found"); + verbose(VERB_ALGO, "rpz: qname: type not found"); return NULL; } return rpz_synthesize_localdata_from_rrset(r, ms, qinfo, rrset, az); } +/** Synthesize a CNAME message for RPZ action override */ +static struct dns_msg* +rpz_synthesize_cname_override_msg(struct rpz* r, struct module_qstate* ms, + struct query_info* qinfo) +{ + struct dns_msg* msg = NULL; + struct reply_info* new_reply_info; + struct ub_packed_rrset_key* rp; + + msg = rpz_dns_msg_new(ms->region); + if(msg == NULL) { return NULL; } + + msg->qinfo = *qinfo; + new_reply_info = construct_reply_info_base(ms->region, + LDNS_RCODE_NOERROR | BIT_QR | BIT_AA | BIT_RA, + 1, /* qd */ + 0, /* ttl */ + 0, /* prettl */ + 0, /* expttl */ + 1, /* an */ + 0, /* ns */ + 0, /* ar */ + 1, /* total */ + sec_status_insecure, + LDNS_EDE_NONE); + if(new_reply_info == NULL) { + log_err("out of memory"); + return NULL; + } + new_reply_info->authoritative = 1; + + rp = respip_copy_rrset(r->cname_override, ms->region); + if(rp == NULL) { + log_err("out of memory"); + return NULL; + } + rp->rk.dname = qinfo->qname; + rp->rk.dname_len = qinfo->qname_len; + /* this rrset is from the rpz data, or synthesized. + * It is not actually from the network, so we flag it with this + * flags as a fake RRset. If later the cache is used to look up + * rrsets, then the fake ones are not returned (if you look without + * the flag). For like CNAME lookups from the iterator or A, AAAA + * lookups for nameserver targets, it would use the without flag + * actual data. So that the actual network data and fake data + * are kept track of separately. */ + rp->rk.flags |= PACKED_RRSET_RPZ; + new_reply_info->rrsets[0] = rp; + + msg->rep = new_reply_info; + return msg; +} + static int rpz_synthesize_qname_localdata(struct module_env* env, struct rpz* r, struct local_zone* z, enum localzone_type lzt, struct query_info* qinfo, @@ -2072,17 +2207,8 @@ rpz_synthesize_qname_localdata(struct module_env* env, struct rpz* r, 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(!rpz_apply_cname_override_action(r, qinfo, temp)) + return 0; if(r->log) { log_rpz_apply("qname", z->name, NULL, RPZ_CNAME_OVERRIDE_ACTION, qinfo, repinfo, NULL, r->log_name); @@ -2134,8 +2260,9 @@ rpz_delegation_point_ipbased_trigger_lookup(struct rpz* rpz, struct iter_qstate* } static struct dns_msg* -rpz_apply_nsip_trigger(struct module_qstate* ms, struct rpz* r, - struct clientip_synthesized_rr* raddr, struct auth_zone* az) +rpz_apply_nsip_trigger(struct module_qstate* ms, struct query_info* qchase, + struct rpz* r, struct clientip_synthesized_rr* raddr, + struct auth_zone* az) { enum rpz_action action = raddr->action; struct dns_msg* ret = NULL; @@ -2148,16 +2275,16 @@ rpz_apply_nsip_trigger(struct module_qstate* ms, struct rpz* r, if(action == RPZ_LOCAL_DATA_ACTION && raddr->data == NULL) { verbose(VERB_ALGO, "rpz: bug: nsip local data action but no local data"); - ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); + ret = rpz_synthesize_nodata(r, ms, qchase, az); goto done; } switch(action) { case RPZ_NXDOMAIN_ACTION: - ret = rpz_synthesize_nxdomain(r, ms, &ms->qinfo, az); + ret = rpz_synthesize_nxdomain(r, ms, qchase, az); break; case RPZ_NODATA_ACTION: - ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); + ret = rpz_synthesize_nodata(r, ms, qchase, az); break; case RPZ_TCP_ONLY_ACTION: /* basically a passthru here but the tcp-only will be @@ -2166,17 +2293,20 @@ rpz_apply_nsip_trigger(struct module_qstate* ms, struct rpz* r, ret = NULL; break; case RPZ_DROP_ACTION: - ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); + ret = rpz_synthesize_nodata(r, ms, qchase, az); ms->is_drop = 1; break; case RPZ_LOCAL_DATA_ACTION: - ret = rpz_synthesize_nsip_localdata(r, ms, raddr, az); - if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); } + ret = rpz_synthesize_nsip_localdata(r, ms, qchase, raddr, az); + if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, qchase, az); } break; case RPZ_PASSTHRU_ACTION: ret = NULL; ms->rpz_passthru = 1; break; + case RPZ_CNAME_OVERRIDE_ACTION: + ret = rpz_synthesize_cname_override_msg(r, ms, qchase); + break; default: verbose(VERB_ALGO, "rpz: nsip: bug: unhandled or invalid action: '%s'", rpz_action_to_string(action)); @@ -2194,9 +2324,9 @@ done: } static struct dns_msg* -rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r, - struct local_zone* z, struct matched_delegation_point const* match, - struct auth_zone* az) +rpz_apply_nsdname_trigger(struct module_qstate* ms, struct query_info* qchase, + struct rpz* r, struct local_zone* z, + struct matched_delegation_point const* match, struct auth_zone* az) { struct dns_msg* ret = NULL; enum rpz_action action = localzone_type_to_rpz_action(z->type); @@ -2209,10 +2339,10 @@ rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r, switch(action) { case RPZ_NXDOMAIN_ACTION: - ret = rpz_synthesize_nxdomain(r, ms, &ms->qinfo, az); + ret = rpz_synthesize_nxdomain(r, ms, qchase, az); break; case RPZ_NODATA_ACTION: - ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); + ret = rpz_synthesize_nodata(r, ms, qchase, az); break; case RPZ_TCP_ONLY_ACTION: /* basically a passthru here but the tcp-only will be @@ -2221,19 +2351,22 @@ rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r, ret = NULL; break; case RPZ_DROP_ACTION: - ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); + ret = rpz_synthesize_nodata(r, ms, qchase, az); ms->is_drop = 1; break; case RPZ_LOCAL_DATA_ACTION: - ret = rpz_synthesize_nsdname_localdata(r, ms, z, match, az); - if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); } + ret = rpz_synthesize_nsdname_localdata(r, ms, qchase, z, match, az); + if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, qchase, az); } break; case RPZ_PASSTHRU_ACTION: ret = NULL; ms->rpz_passthru = 1; break; + case RPZ_CNAME_OVERRIDE_ACTION: + ret = rpz_synthesize_cname_override_msg(r, ms, qchase); + break; default: - verbose(VERB_ALGO, "rpz: nsip: bug: unhandled or invalid action: '%s'", + verbose(VERB_ALGO, "rpz: nsdname: bug: unhandled or invalid action: '%s'", rpz_action_to_string(action)); ret = NULL; } @@ -2324,7 +2457,7 @@ rpz_callback_from_iterator_module(struct module_qstate* ms, struct iter_qstate* /* the nsdname has precedence over the nsip triggers */ z = rpz_delegation_point_zone_lookup(is->dp, r->nsdname_zones, - ms->qinfo.qclass, &match); + is->qchase.qclass, &match); if(z != NULL) { lock_rw_unlock(&a->lock); break; @@ -2347,9 +2480,9 @@ rpz_callback_from_iterator_module(struct module_qstate* ms, struct iter_qstate* if(z) { lock_rw_unlock(&z->lock); } - return rpz_apply_nsip_trigger(ms, r, raddr, a); + return rpz_apply_nsip_trigger(ms, &is->qchase, r, raddr, a); } - return rpz_apply_nsdname_trigger(ms, r, z, &match, a); + return rpz_apply_nsdname_trigger(ms, &is->qchase, r, z, &match, a); } struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms, @@ -2412,10 +2545,10 @@ struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms, dname_str(is->qchase.qname, nm); dname_str(z->name, zn); if(strcmp(zn, nm) != 0) - verbose(VERB_ALGO, "rpz: qname trigger after cname %s on %s, with action=%s", + verbose(VERB_ALGO, "rpz: qname trigger %s on %s, with action=%s", zn, nm, rpz_action_to_string(localzone_type_to_rpz_action(lzt))); else - verbose(VERB_ALGO, "rpz: qname trigger after cname %s, with action=%s", + verbose(VERB_ALGO, "rpz: qname trigger %s, with action=%s", nm, rpz_action_to_string(localzone_type_to_rpz_action(lzt))); } switch(localzone_type_to_rpz_action(lzt)) { @@ -2444,7 +2577,7 @@ struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms, ms->rpz_passthru = 1; break; default: - verbose(VERB_ALGO, "rpz: qname trigger after cname: bug: unhandled or invalid action: '%s'", + verbose(VERB_ALGO, "rpz: qname trigger: bug: unhandled or invalid action: '%s'", rpz_action_to_string(localzone_type_to_rpz_action(lzt))); ret = NULL; } @@ -2472,8 +2605,21 @@ rpz_apply_maybe_clientip_trigger(struct auth_zones* az, struct module_env* env, az, qinfo, repinfo, taglist, taglen, stats, z_out, a_out, r_out); client_action = ((node == NULL) ? RPZ_INVALID_ACTION : node->action); + if(node != NULL && *r_out && + (*r_out)->action_override != RPZ_NO_OVERRIDE_ACTION) { + client_action = (*r_out)->action_override; + } if(client_action == RPZ_PASSTHRU_ACTION) { + if(*r_out && (*r_out)->log) + log_rpz_apply( + (node?"clientip":"qname"), + ((*z_out)?(*z_out)->name:NULL), + (node?&node->node:NULL), + client_action, qinfo, repinfo, NULL, + (*r_out)->log_name); *passthru = 1; + ret = 0; + goto done; } if(*z_out == NULL || (client_action != RPZ_INVALID_ACTION && client_action != RPZ_PASSTHRU_ACTION)) { @@ -2488,14 +2634,15 @@ rpz_apply_maybe_clientip_trigger(struct auth_zones* az, struct module_env* env, if(client_action == RPZ_LOCAL_DATA_ACTION) { rpz_apply_clientip_localdata_action(node, env, qinfo, edns, repinfo, buf, temp, *a_out); + ret = 1; + } else if(client_action == RPZ_CNAME_OVERRIDE_ACTION) { + if(!rpz_apply_cname_override_action(*r_out, qinfo, + temp)) { + ret = 0; + goto done; + } + ret = 0; } else { - if(*r_out && (*r_out)->log) - log_rpz_apply( - (node?"clientip":"qname"), - ((*z_out)?(*z_out)->name:NULL), - (node?&node->node:NULL), - client_action, qinfo, repinfo, NULL, - (*r_out)->log_name); local_zones_zone_answer(*z_out /*likely NULL, no zone*/, env, qinfo, edns, repinfo, buf, temp, 0 /* no local data used */, rpz_action_to_localzone_type(client_action)); @@ -2503,8 +2650,15 @@ rpz_apply_maybe_clientip_trigger(struct auth_zones* az, struct module_env* env, LDNS_RCODE_WIRE(sldns_buffer_begin(buf)) == LDNS_RCODE_NXDOMAIN) LDNS_RA_CLR(sldns_buffer_begin(buf)); + ret = 1; } - ret = 1; + if(*r_out && (*r_out)->log) + log_rpz_apply( + (node?"clientip":"qname"), + ((*z_out)?(*z_out)->name:NULL), + (node?&node->node:NULL), + client_action, qinfo, repinfo, NULL, + (*r_out)->log_name); goto done; } ret = -1; |