1 /*
2 * services/rpz.c - rpz service
3 *
4 * Copyright (c) 2019, NLnet Labs. All rights reserved.
5 *
6 * This software is open source.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 *
15 * Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 *
19 * Neither the name of the NLNET LABS nor the names of its contributors may
20 * be used to endorse or promote products derived from this software without
21 * specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 /**
37 * \file
38 *
39 * This file contains functions to enable RPZ service.
40 */
41
42 #include "config.h"
43 #include "services/rpz.h"
44 #include "util/config_file.h"
45 #include "sldns/wire2str.h"
46 #include "sldns/str2wire.h"
47 #include "util/data/dname.h"
48 #include "util/net_help.h"
49 #include "util/log.h"
50 #include "util/data/dname.h"
51 #include "util/locks.h"
52 #include "util/regional.h"
53
54 /** string for RPZ action enum */
55 const char*
rpz_action_to_string(enum rpz_action a)56 rpz_action_to_string(enum rpz_action a)
57 {
58 switch(a) {
59 case RPZ_NXDOMAIN_ACTION: return "nxdomain";
60 case RPZ_NODATA_ACTION: return "nodata";
61 case RPZ_PASSTHRU_ACTION: return "passthru";
62 case RPZ_DROP_ACTION: return "drop";
63 case RPZ_TCP_ONLY_ACTION: return "tcp_only";
64 case RPZ_INVALID_ACTION: return "invalid";
65 case RPZ_LOCAL_DATA_ACTION: return "local_data";
66 case RPZ_DISABLED_ACTION: return "disabled";
67 case RPZ_CNAME_OVERRIDE_ACTION: return "cname_override";
68 case RPZ_NO_OVERRIDE_ACTION: return "no_override";
69 }
70 return "unknown";
71 }
72
73 /** RPZ action enum for config string */
74 static enum rpz_action
rpz_config_to_action(char * a)75 rpz_config_to_action(char* a)
76 {
77 if(strcmp(a, "nxdomain") == 0)
78 return RPZ_NXDOMAIN_ACTION;
79 else if(strcmp(a, "nodata") == 0)
80 return RPZ_NODATA_ACTION;
81 else if(strcmp(a, "passthru") == 0)
82 return RPZ_PASSTHRU_ACTION;
83 else if(strcmp(a, "drop") == 0)
84 return RPZ_DROP_ACTION;
85 else if(strcmp(a, "tcp_only") == 0)
86 return RPZ_TCP_ONLY_ACTION;
87 else if(strcmp(a, "cname") == 0)
88 return RPZ_CNAME_OVERRIDE_ACTION;
89 else if(strcmp(a, "disabled") == 0)
90 return RPZ_DISABLED_ACTION;
91 return RPZ_INVALID_ACTION;
92 }
93
94 /** string for RPZ trigger enum */
95 static const char*
rpz_trigger_to_string(enum rpz_trigger r)96 rpz_trigger_to_string(enum rpz_trigger r)
97 {
98 switch(r) {
99 case RPZ_QNAME_TRIGGER: return "qname";
100 case RPZ_CLIENT_IP_TRIGGER: return "client_ip";
101 case RPZ_RESPONSE_IP_TRIGGER: return "response_ip";
102 case RPZ_NSDNAME_TRIGGER: return "nsdname";
103 case RPZ_NSIP_TRIGGER: return "nsip";
104 case RPZ_INVALID_TRIGGER: return "invalid";
105 }
106 return "unknown";
107 }
108
109 /**
110 * Get the label that is just before the root label.
111 * @param dname: dname to work on
112 * @param maxdnamelen: maximum length of the dname
113 * @return: pointer to TLD label, NULL if not found or invalid dname
114 */
115 static uint8_t*
get_tld_label(uint8_t * dname,size_t maxdnamelen)116 get_tld_label(uint8_t* dname, size_t maxdnamelen)
117 {
118 uint8_t* prevlab = dname;
119 size_t dnamelen = 0;
120
121 /* one byte needed for label length */
122 if(dnamelen+1 > maxdnamelen)
123 return NULL;
124
125 /* only root label */
126 if(*dname == 0)
127 return NULL;
128
129 while(*dname) {
130 dnamelen += ((size_t)*dname)+1;
131 if(dnamelen+1 > maxdnamelen)
132 return NULL;
133 dname = dname+((size_t)*dname)+1;
134 if(*dname != 0)
135 prevlab = dname;
136 }
137 return prevlab;
138 }
139
140 /**
141 * Classify RPZ action for RR type/rdata
142 * @param rr_type: the RR type
143 * @param rdatawl: RDATA with 2 bytes length
144 * @param rdatalen: the length of rdatawl (including its 2 bytes length)
145 * @return: the RPZ action
146 */
147 static enum rpz_action
rpz_rr_to_action(uint16_t rr_type,uint8_t * rdatawl,size_t rdatalen)148 rpz_rr_to_action(uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen)
149 {
150 char* endptr;
151 uint8_t* rdata;
152 int rdatalabs;
153 uint8_t* tldlab = NULL;
154
155 switch(rr_type) {
156 case LDNS_RR_TYPE_SOA:
157 case LDNS_RR_TYPE_NS:
158 case LDNS_RR_TYPE_DNAME:
159 /* all DNSSEC-related RRs must be ignored */
160 case LDNS_RR_TYPE_DNSKEY:
161 case LDNS_RR_TYPE_DS:
162 case LDNS_RR_TYPE_RRSIG:
163 case LDNS_RR_TYPE_NSEC:
164 case LDNS_RR_TYPE_NSEC3:
165 return RPZ_INVALID_ACTION;
166 case LDNS_RR_TYPE_CNAME:
167 break;
168 default:
169 return RPZ_LOCAL_DATA_ACTION;
170 }
171
172 /* use CNAME target to determine RPZ action */
173 log_assert(rr_type == LDNS_RR_TYPE_CNAME);
174 if(rdatalen < 3)
175 return RPZ_INVALID_ACTION;
176
177 rdata = rdatawl + 2; /* 2 bytes of rdata length */
178 if(dname_valid(rdata, rdatalen-2) != rdatalen-2)
179 return RPZ_INVALID_ACTION;
180
181 rdatalabs = dname_count_labels(rdata);
182 if(rdatalabs == 1)
183 return RPZ_NXDOMAIN_ACTION;
184 else if(rdatalabs == 2) {
185 if(dname_subdomain_c(rdata, (uint8_t*)&"\001*\000"))
186 return RPZ_NODATA_ACTION;
187 else if(dname_subdomain_c(rdata,
188 (uint8_t*)&"\014rpz-passthru\000"))
189 return RPZ_PASSTHRU_ACTION;
190 else if(dname_subdomain_c(rdata, (uint8_t*)&"\010rpz-drop\000"))
191 return RPZ_DROP_ACTION;
192 else if(dname_subdomain_c(rdata,
193 (uint8_t*)&"\014rpz-tcp-only\000"))
194 return RPZ_TCP_ONLY_ACTION;
195 }
196
197 /* all other TLDs starting with "rpz-" are invalid */
198 tldlab = get_tld_label(rdata, rdatalen-2);
199 if(tldlab && dname_lab_startswith(tldlab, "rpz-", &endptr))
200 return RPZ_INVALID_ACTION;
201
202 /* no special label found */
203 return RPZ_LOCAL_DATA_ACTION;
204 }
205
206 static enum localzone_type
rpz_action_to_localzone_type(enum rpz_action a)207 rpz_action_to_localzone_type(enum rpz_action a)
208 {
209 switch(a) {
210 case RPZ_NXDOMAIN_ACTION: return local_zone_always_nxdomain;
211 case RPZ_NODATA_ACTION: return local_zone_always_nodata;
212 case RPZ_DROP_ACTION: return local_zone_always_deny;
213 case RPZ_PASSTHRU_ACTION: return local_zone_always_transparent;
214 case RPZ_LOCAL_DATA_ACTION: /* fallthrough */
215 case RPZ_CNAME_OVERRIDE_ACTION: return local_zone_redirect;
216 case RPZ_INVALID_ACTION: /* fallthrough */
217 case RPZ_TCP_ONLY_ACTION: /* fallthrough */
218 default: return local_zone_invalid;
219 }
220 }
221
222 enum respip_action
rpz_action_to_respip_action(enum rpz_action a)223 rpz_action_to_respip_action(enum rpz_action a)
224 {
225 switch(a) {
226 case RPZ_NXDOMAIN_ACTION: return respip_always_nxdomain;
227 case RPZ_NODATA_ACTION: return respip_always_nodata;
228 case RPZ_DROP_ACTION: return respip_always_deny;
229 case RPZ_PASSTHRU_ACTION: return respip_always_transparent;
230 case RPZ_LOCAL_DATA_ACTION: /* fallthrough */
231 case RPZ_CNAME_OVERRIDE_ACTION: return respip_redirect;
232 case RPZ_INVALID_ACTION: /* fallthrough */
233 case RPZ_TCP_ONLY_ACTION: /* fallthrough */
234 default: return respip_invalid;
235 }
236 }
237
238 static enum rpz_action
localzone_type_to_rpz_action(enum localzone_type lzt)239 localzone_type_to_rpz_action(enum localzone_type lzt)
240 {
241 switch(lzt) {
242 case local_zone_always_nxdomain: return RPZ_NXDOMAIN_ACTION;
243 case local_zone_always_nodata: return RPZ_NODATA_ACTION;
244 case local_zone_always_deny: return RPZ_DROP_ACTION;
245 case local_zone_always_transparent: return RPZ_PASSTHRU_ACTION;
246 case local_zone_redirect: return RPZ_LOCAL_DATA_ACTION;
247 case local_zone_invalid:
248 default:
249 return RPZ_INVALID_ACTION;
250 }
251 }
252
253 enum rpz_action
respip_action_to_rpz_action(enum respip_action a)254 respip_action_to_rpz_action(enum respip_action a)
255 {
256 switch(a) {
257 case respip_always_nxdomain: return RPZ_NXDOMAIN_ACTION;
258 case respip_always_nodata: return RPZ_NODATA_ACTION;
259 case respip_always_deny: return RPZ_DROP_ACTION;
260 case respip_always_transparent: return RPZ_PASSTHRU_ACTION;
261 case respip_redirect: return RPZ_LOCAL_DATA_ACTION;
262 case respip_invalid:
263 default:
264 return RPZ_INVALID_ACTION;
265 }
266 }
267
268 /**
269 * Get RPZ trigger for dname
270 * @param dname: dname containing RPZ trigger
271 * @param dname_len: length of the dname
272 * @return: RPZ trigger enum
273 */
274 static enum rpz_trigger
rpz_dname_to_trigger(uint8_t * dname,size_t dname_len)275 rpz_dname_to_trigger(uint8_t* dname, size_t dname_len)
276 {
277 uint8_t* tldlab;
278 char* endptr;
279
280 if(dname_valid(dname, dname_len) != dname_len)
281 return RPZ_INVALID_TRIGGER;
282
283 tldlab = get_tld_label(dname, dname_len);
284 if(!tldlab || !dname_lab_startswith(tldlab, "rpz-", &endptr))
285 return RPZ_QNAME_TRIGGER;
286
287 if(dname_subdomain_c(tldlab,
288 (uint8_t*)&"\015rpz-client-ip\000"))
289 return RPZ_CLIENT_IP_TRIGGER;
290 else if(dname_subdomain_c(tldlab, (uint8_t*)&"\006rpz-ip\000"))
291 return RPZ_RESPONSE_IP_TRIGGER;
292 else if(dname_subdomain_c(tldlab, (uint8_t*)&"\013rpz-nsdname\000"))
293 return RPZ_NSDNAME_TRIGGER;
294 else if(dname_subdomain_c(tldlab, (uint8_t*)&"\010rpz-nsip\000"))
295 return RPZ_NSIP_TRIGGER;
296
297 return RPZ_QNAME_TRIGGER;
298 }
299
rpz_delete(struct rpz * r)300 void rpz_delete(struct rpz* r)
301 {
302 if(!r)
303 return;
304 local_zones_delete(r->local_zones);
305 respip_set_delete(r->respip_set);
306 regional_destroy(r->region);
307 free(r->taglist);
308 free(r->log_name);
309 free(r);
310 }
311
312 int
rpz_clear(struct rpz * r)313 rpz_clear(struct rpz* r)
314 {
315 /* must hold write lock on auth_zone */
316 local_zones_delete(r->local_zones);
317 respip_set_delete(r->respip_set);
318 if(!(r->local_zones = local_zones_create())){
319 return 0;
320 }
321 if(!(r->respip_set = respip_set_create())) {
322 return 0;
323 }
324 return 1;
325 }
326
327 void
rpz_finish_config(struct rpz * r)328 rpz_finish_config(struct rpz* r)
329 {
330 lock_rw_wrlock(&r->respip_set->lock);
331 addr_tree_init_parents(&r->respip_set->ip_tree);
332 lock_rw_unlock(&r->respip_set->lock);
333 }
334
335 /** new rrset containing CNAME override, does not yet contain a dname */
336 static struct ub_packed_rrset_key*
new_cname_override(struct regional * region,uint8_t * ct,size_t ctlen)337 new_cname_override(struct regional* region, uint8_t* ct, size_t ctlen)
338 {
339 struct ub_packed_rrset_key* rrset;
340 struct packed_rrset_data* pd;
341 uint16_t rdlength = htons(ctlen);
342 rrset = (struct ub_packed_rrset_key*)regional_alloc_zero(region,
343 sizeof(*rrset));
344 if(!rrset) {
345 log_err("out of memory");
346 return NULL;
347 }
348 rrset->entry.key = rrset;
349 pd = (struct packed_rrset_data*)regional_alloc_zero(region, sizeof(*pd));
350 if(!pd) {
351 log_err("out of memory");
352 return NULL;
353 }
354 pd->trust = rrset_trust_prim_noglue;
355 pd->security = sec_status_insecure;
356
357 pd->count = 1;
358 pd->rr_len = regional_alloc_zero(region, sizeof(*pd->rr_len));
359 pd->rr_ttl = regional_alloc_zero(region, sizeof(*pd->rr_ttl));
360 pd->rr_data = regional_alloc_zero(region, sizeof(*pd->rr_data));
361 if(!pd->rr_len || !pd->rr_ttl || !pd->rr_data) {
362 log_err("out of memory");
363 return NULL;
364 }
365 pd->rr_len[0] = ctlen+2;
366 pd->rr_ttl[0] = 3600;
367 pd->rr_data[0] = regional_alloc_zero(region, 2 /* rdlength */ + ctlen);
368 if(!pd->rr_data[0]) {
369 log_err("out of memory");
370 return NULL;
371 }
372 memmove(pd->rr_data[0], &rdlength, 2);
373 memmove(pd->rr_data[0]+2, ct, ctlen);
374
375 rrset->entry.data = pd;
376 rrset->rk.type = htons(LDNS_RR_TYPE_CNAME);
377 rrset->rk.rrset_class = htons(LDNS_RR_CLASS_IN);
378 return rrset;
379 }
380
381 struct rpz*
rpz_create(struct config_auth * p)382 rpz_create(struct config_auth* p)
383 {
384 struct rpz* r = calloc(1, sizeof(*r));
385 if(!r)
386 goto err;
387
388 r->region = regional_create_custom(sizeof(struct regional));
389 if(!r->region) {
390 goto err;
391 }
392
393 if(!(r->local_zones = local_zones_create())){
394 goto err;
395 }
396 if(!(r->respip_set = respip_set_create())) {
397 goto err;
398 }
399 r->taglistlen = p->rpz_taglistlen;
400 r->taglist = memdup(p->rpz_taglist, r->taglistlen);
401 if(p->rpz_action_override) {
402 r->action_override = rpz_config_to_action(p->rpz_action_override);
403 }
404 else
405 r->action_override = RPZ_NO_OVERRIDE_ACTION;
406
407 if(r->action_override == RPZ_CNAME_OVERRIDE_ACTION) {
408 uint8_t nm[LDNS_MAX_DOMAINLEN+1];
409 size_t nmlen = sizeof(nm);
410
411 if(!p->rpz_cname) {
412 log_err("RPZ override with cname action found, but no "
413 "rpz-cname-override configured");
414 goto err;
415 }
416
417 if(sldns_str2wire_dname_buf(p->rpz_cname, nm, &nmlen) != 0) {
418 log_err("cannot parse RPZ cname override: %s",
419 p->rpz_cname);
420 goto err;
421 }
422 r->cname_override = new_cname_override(r->region, nm, nmlen);
423 if(!r->cname_override) {
424 goto err;
425 }
426 }
427 r->log = p->rpz_log;
428 if(p->rpz_log_name) {
429 if(!(r->log_name = strdup(p->rpz_log_name))) {
430 log_err("malloc failure on RPZ log_name strdup");
431 goto err;
432 }
433 }
434 return r;
435 err:
436 if(r) {
437 if(r->local_zones)
438 local_zones_delete(r->local_zones);
439 if(r->respip_set)
440 respip_set_delete(r->respip_set);
441 if(r->taglist)
442 free(r->taglist);
443 if(r->region)
444 regional_destroy(r->region);
445 free(r);
446 }
447 return NULL;
448 }
449
450 /**
451 * Remove RPZ zone name from dname
452 * Copy dname to newdname, without the originlen number of trailing bytes
453 */
454 static size_t
strip_dname_origin(uint8_t * dname,size_t dnamelen,size_t originlen,uint8_t * newdname,size_t maxnewdnamelen)455 strip_dname_origin(uint8_t* dname, size_t dnamelen, size_t originlen,
456 uint8_t* newdname, size_t maxnewdnamelen)
457 {
458 size_t newdnamelen;
459 if(dnamelen < originlen)
460 return 0;
461 newdnamelen = dnamelen - originlen;
462 if(newdnamelen+1 > maxnewdnamelen)
463 return 0;
464 memmove(newdname, dname, newdnamelen);
465 newdname[newdnamelen] = 0;
466 return newdnamelen + 1; /* + 1 for root label */
467 }
468
469 /** Insert RR into RPZ's local-zone */
470 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)471 rpz_insert_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
472 enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl,
473 uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len)
474 {
475 struct local_zone* z;
476 enum localzone_type tp = local_zone_always_transparent;
477 int dnamelabs = dname_count_labels(dname);
478 char* rrstr;
479 int newzone = 0;
480
481 if(a == RPZ_TCP_ONLY_ACTION || a == RPZ_INVALID_ACTION) {
482 verbose(VERB_ALGO, "RPZ: skipping unsupported action: %s",
483 rpz_action_to_string(a));
484 free(dname);
485 return;
486 }
487
488 lock_rw_wrlock(&r->local_zones->lock);
489 /* exact match */
490 z = local_zones_find(r->local_zones, dname, dnamelen, dnamelabs,
491 LDNS_RR_CLASS_IN);
492 if(z && a != RPZ_LOCAL_DATA_ACTION) {
493 rrstr = sldns_wire2str_rr(rr, rr_len);
494 if(!rrstr) {
495 log_err("malloc error while inserting RPZ qname "
496 "trigger");
497 free(dname);
498 lock_rw_unlock(&r->local_zones->lock);
499 return;
500 }
501 verbose(VERB_ALGO, "RPZ: skipping duplicate record: '%s'",
502 rrstr);
503 free(rrstr);
504 free(dname);
505 lock_rw_unlock(&r->local_zones->lock);
506 return;
507 }
508 if(!z) {
509 tp = rpz_action_to_localzone_type(a);
510 if(!(z = local_zones_add_zone(r->local_zones, dname, dnamelen,
511 dnamelabs, rrclass, tp))) {
512 log_warn("RPZ create failed");
513 lock_rw_unlock(&r->local_zones->lock);
514 /* dname will be free'd in failed local_zone_create() */
515 return;
516 }
517 newzone = 1;
518 }
519 if(a == RPZ_LOCAL_DATA_ACTION) {
520 rrstr = sldns_wire2str_rr(rr, rr_len);
521 if(!rrstr) {
522 log_err("malloc error while inserting RPZ qname "
523 "trigger");
524 free(dname);
525 lock_rw_unlock(&r->local_zones->lock);
526 return;
527 }
528 lock_rw_wrlock(&z->lock);
529 local_zone_enter_rr(z, dname, dnamelen, dnamelabs,
530 rrtype, rrclass, ttl, rdata, rdata_len, rrstr);
531 lock_rw_unlock(&z->lock);
532 free(rrstr);
533 }
534 if(!newzone)
535 free(dname);
536 lock_rw_unlock(&r->local_zones->lock);
537 return;
538 }
539
540 /** Insert RR into RPZ's respip_set */
541 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)542 rpz_insert_response_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
543 enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl,
544 uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len)
545 {
546 struct resp_addr* node;
547 struct sockaddr_storage addr;
548 socklen_t addrlen;
549 int net, af;
550 char* rrstr;
551 enum respip_action respa = rpz_action_to_respip_action(a);
552
553 if(a == RPZ_TCP_ONLY_ACTION || a == RPZ_INVALID_ACTION ||
554 respa == respip_invalid) {
555 verbose(VERB_ALGO, "RPZ: skipping unsupported action: %s",
556 rpz_action_to_string(a));
557 return 0;
558 }
559
560 if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af))
561 return 0;
562
563 lock_rw_wrlock(&r->respip_set->lock);
564 rrstr = sldns_wire2str_rr(rr, rr_len);
565 if(!rrstr) {
566 log_err("malloc error while inserting RPZ respip trigger");
567 lock_rw_unlock(&r->respip_set->lock);
568 return 0;
569 }
570 if(!(node=respip_sockaddr_find_or_create(r->respip_set, &addr, addrlen,
571 net, 1, rrstr))) {
572 lock_rw_unlock(&r->respip_set->lock);
573 free(rrstr);
574 return 0;
575 }
576
577 lock_rw_wrlock(&node->lock);
578 lock_rw_unlock(&r->respip_set->lock);
579 node->action = respa;
580
581 if(a == RPZ_LOCAL_DATA_ACTION) {
582 respip_enter_rr(r->respip_set->region, node, rrtype,
583 rrclass, ttl, rdata, rdata_len, rrstr, "");
584 }
585 lock_rw_unlock(&node->lock);
586 free(rrstr);
587 return 1;
588 }
589
590 int
rpz_insert_rr(struct rpz * r,uint8_t * azname,size_t aznamelen,uint8_t * dname,size_t dnamelen,uint16_t rr_type,uint16_t rr_class,uint32_t rr_ttl,uint8_t * rdatawl,size_t rdatalen,uint8_t * rr,size_t rr_len)591 rpz_insert_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, uint8_t* dname,
592 size_t dnamelen, uint16_t rr_type, uint16_t rr_class, uint32_t rr_ttl,
593 uint8_t* rdatawl, size_t rdatalen, uint8_t* rr, size_t rr_len)
594 {
595 size_t policydnamelen;
596 /* name is free'd in local_zone delete */
597 enum rpz_trigger t;
598 enum rpz_action a;
599 uint8_t* policydname;
600
601 if(!dname_subdomain_c(dname, azname)) {
602 char* dname_str = sldns_wire2str_dname(dname, dnamelen);
603 char* azname_str = sldns_wire2str_dname(azname, aznamelen);
604 if(dname_str && azname_str) {
605 log_err("RPZ: name of record (%s) to insert into RPZ is not a "
606 "subdomain of the configured name of the RPZ zone (%s)",
607 dname_str, azname_str);
608 } else {
609 log_err("RPZ: name of record to insert into RPZ is not a "
610 "subdomain of the configured name of the RPZ zone");
611 }
612 free(dname_str);
613 free(azname_str);
614 return 0;
615 }
616
617 log_assert(dnamelen >= aznamelen);
618 if(!(policydname = calloc(1, (dnamelen-aznamelen)+1))) {
619 log_err("malloc error while inserting RPZ RR");
620 return 0;
621 }
622
623 a = rpz_rr_to_action(rr_type, rdatawl, rdatalen);
624 if(!(policydnamelen = strip_dname_origin(dname, dnamelen, aznamelen,
625 policydname, (dnamelen-aznamelen)+1))) {
626 free(policydname);
627 return 0;
628 }
629 t = rpz_dname_to_trigger(policydname, policydnamelen);
630 if(t == RPZ_INVALID_TRIGGER) {
631 free(policydname);
632 verbose(VERB_ALGO, "RPZ: skipping invalid trigger");
633 return 1;
634 }
635 if(t == RPZ_QNAME_TRIGGER) {
636 rpz_insert_qname_trigger(r, policydname, policydnamelen,
637 a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr,
638 rr_len);
639 }
640 else if(t == RPZ_RESPONSE_IP_TRIGGER) {
641 rpz_insert_response_ip_trigger(r, policydname, policydnamelen,
642 a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr,
643 rr_len);
644 free(policydname);
645 }
646 else {
647 free(policydname);
648 verbose(VERB_ALGO, "RPZ: skipping unsupported trigger: %s",
649 rpz_trigger_to_string(t));
650 }
651 return 1;
652 }
653
654 /**
655 * Find RPZ local-zone by qname.
656 * @param r: rpz containing local-zone tree
657 * @param qname: qname
658 * @param qname_len: length of qname
659 * @param qclass: qclass
660 * @param only_exact: if 1 only excact (non wildcard) matches are returned
661 * @param wr: get write lock for local-zone if 1, read lock if 0
662 * @param zones_keep_lock: if set do not release the r->local_zones lock, this
663 * makes the caller of this function responsible for releasing the lock.
664 * @return: NULL or local-zone holding rd or wr lock
665 */
666 static struct local_zone*
rpz_find_zone(struct rpz * r,uint8_t * qname,size_t qname_len,uint16_t qclass,int only_exact,int wr,int zones_keep_lock)667 rpz_find_zone(struct rpz* r, uint8_t* qname, size_t qname_len, uint16_t qclass,
668 int only_exact, int wr, int zones_keep_lock)
669 {
670 uint8_t* ce;
671 size_t ce_len;
672 int ce_labs;
673 uint8_t wc[LDNS_MAX_DOMAINLEN+1];
674 int exact;
675 struct local_zone* z = NULL;
676 if(wr) {
677 lock_rw_wrlock(&r->local_zones->lock);
678 } else {
679 lock_rw_rdlock(&r->local_zones->lock);
680 }
681 z = local_zones_find_le(r->local_zones, qname, qname_len,
682 dname_count_labels(qname),
683 LDNS_RR_CLASS_IN, &exact);
684 if(!z || (only_exact && !exact)) {
685 lock_rw_unlock(&r->local_zones->lock);
686 return NULL;
687 }
688 if(wr) {
689 lock_rw_wrlock(&z->lock);
690 } else {
691 lock_rw_rdlock(&z->lock);
692 }
693 if(!zones_keep_lock) {
694 lock_rw_unlock(&r->local_zones->lock);
695 }
696
697 if(exact)
698 return z;
699
700 /* No exact match found, lookup wildcard. closest encloser must
701 * be the shared parent between the qname and the best local
702 * zone match, append '*' to that and do another lookup. */
703
704 ce = dname_get_shared_topdomain(z->name, qname);
705 if(!ce /* should not happen */ || !*ce /* root */) {
706 lock_rw_unlock(&z->lock);
707 if(zones_keep_lock) {
708 lock_rw_unlock(&r->local_zones->lock);
709 }
710 return NULL;
711 }
712 ce_labs = dname_count_size_labels(ce, &ce_len);
713 if(ce_len+2 > sizeof(wc)) {
714 lock_rw_unlock(&z->lock);
715 if(zones_keep_lock) {
716 lock_rw_unlock(&r->local_zones->lock);
717 }
718 return NULL;
719 }
720 wc[0] = 1; /* length of wildcard label */
721 wc[1] = (uint8_t)'*'; /* wildcard label */
722 memmove(wc+2, ce, ce_len);
723 lock_rw_unlock(&z->lock);
724
725 if(!zones_keep_lock) {
726 if(wr) {
727 lock_rw_wrlock(&r->local_zones->lock);
728 } else {
729 lock_rw_rdlock(&r->local_zones->lock);
730 }
731 }
732 z = local_zones_find_le(r->local_zones, wc,
733 ce_len+2, ce_labs+1, qclass, &exact);
734 if(!z || !exact) {
735 lock_rw_unlock(&r->local_zones->lock);
736 return NULL;
737 }
738 if(wr) {
739 lock_rw_wrlock(&z->lock);
740 } else {
741 lock_rw_rdlock(&z->lock);
742 }
743 if(!zones_keep_lock) {
744 lock_rw_unlock(&r->local_zones->lock);
745 }
746 return z;
747 }
748
749 /**
750 * Remove RR from RPZ's local-data
751 * @param z: local-zone for RPZ, holding write lock
752 * @param policydname: dname of RR to remove
753 * @param policydnamelen: lenth of policydname
754 * @param rr_type: RR type of RR to remove
755 * @param rdata: rdata of RR to remove
756 * @param rdatalen: length of rdata
757 * @return: 1 if zone must be removed after RR deletion
758 */
759 static int
rpz_data_delete_rr(struct local_zone * z,uint8_t * policydname,size_t policydnamelen,uint16_t rr_type,uint8_t * rdata,size_t rdatalen)760 rpz_data_delete_rr(struct local_zone* z, uint8_t* policydname,
761 size_t policydnamelen, uint16_t rr_type, uint8_t* rdata,
762 size_t rdatalen)
763 {
764 struct local_data* ld;
765 struct packed_rrset_data* d;
766 size_t index;
767 ld = local_zone_find_data(z, policydname, policydnamelen,
768 dname_count_labels(policydname));
769 if(ld) {
770 struct local_rrset* prev=NULL, *p=ld->rrsets;
771 while(p && ntohs(p->rrset->rk.type) != rr_type) {
772 prev = p;
773 p = p->next;
774 }
775 if(!p)
776 return 0;
777 d = (struct packed_rrset_data*)p->rrset->entry.data;
778 if(packed_rrset_find_rr(d, rdata, rdatalen, &index)) {
779 if(d->count == 1) {
780 /* no memory recycling for zone deletions ... */
781 if(prev) prev->next = p->next;
782 else ld->rrsets = p->next;
783 }
784 if(d->count > 1) {
785 if(!local_rrset_remove_rr(d, index))
786 return 0;
787 }
788 }
789 }
790 if(ld && ld->rrsets)
791 return 0;
792 return 1;
793 }
794
795 /**
796 * Remove RR from RPZ's respip set
797 * @param raddr: respip node
798 * @param rr_type: RR type of RR to remove
799 * @param rdata: rdata of RR to remove
800 * @param rdatalen: length of rdata
801 * @return: 1 if zone must be removed after RR deletion
802 */
803 static int
rpz_rrset_delete_rr(struct resp_addr * raddr,uint16_t rr_type,uint8_t * rdata,size_t rdatalen)804 rpz_rrset_delete_rr(struct resp_addr* raddr, uint16_t rr_type, uint8_t* rdata,
805 size_t rdatalen)
806 {
807 size_t index;
808 struct packed_rrset_data* d;
809 if(!raddr->data)
810 return 1;
811 d = raddr->data->entry.data;
812 if(ntohs(raddr->data->rk.type) != rr_type) {
813 return 0;
814 }
815 if(packed_rrset_find_rr(d, rdata, rdatalen, &index)) {
816 if(d->count == 1) {
817 /* regional alloc'd */
818 raddr->data->entry.data = NULL;
819 raddr->data = NULL;
820 return 1;
821 }
822 if(d->count > 1) {
823 if(!local_rrset_remove_rr(d, index))
824 return 0;
825 }
826 }
827 return 0;
828
829 }
830
831 /** Remove RR from RPZ's local-zone */
832 static void
rpz_remove_qname_trigger(struct rpz * r,uint8_t * dname,size_t dnamelen,enum rpz_action a,uint16_t rr_type,uint16_t rr_class,uint8_t * rdatawl,size_t rdatalen)833 rpz_remove_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
834 enum rpz_action a, uint16_t rr_type, uint16_t rr_class,
835 uint8_t* rdatawl, size_t rdatalen)
836 {
837 struct local_zone* z;
838 int delete_zone = 1;
839 z = rpz_find_zone(r, dname, dnamelen, rr_class,
840 1 /* only exact */, 1 /* wr lock */, 1 /* keep lock*/);
841 if(!z) {
842 verbose(VERB_ALGO, "RPZ: cannot remove RR from IXFR, "
843 "RPZ domain not found");
844 return;
845 }
846 if(a == RPZ_LOCAL_DATA_ACTION)
847 delete_zone = rpz_data_delete_rr(z, dname,
848 dnamelen, rr_type, rdatawl, rdatalen);
849 else if(a != localzone_type_to_rpz_action(z->type)) {
850 lock_rw_unlock(&z->lock);
851 lock_rw_unlock(&r->local_zones->lock);
852 return;
853 }
854 lock_rw_unlock(&z->lock);
855 if(delete_zone) {
856 local_zones_del_zone(r->local_zones, z);
857 }
858 lock_rw_unlock(&r->local_zones->lock);
859 return;
860 }
861
862 static void
rpz_remove_response_ip_trigger(struct rpz * r,uint8_t * dname,size_t dnamelen,enum rpz_action a,uint16_t rr_type,uint8_t * rdatawl,size_t rdatalen)863 rpz_remove_response_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
864 enum rpz_action a, uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen)
865 {
866 struct resp_addr* node;
867 struct sockaddr_storage addr;
868 socklen_t addrlen;
869 int net, af;
870 int delete_respip = 1;
871
872 if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af))
873 return;
874
875 lock_rw_wrlock(&r->respip_set->lock);
876 if(!(node = (struct resp_addr*)addr_tree_find(
877 &r->respip_set->ip_tree, &addr, addrlen, net))) {
878 verbose(VERB_ALGO, "RPZ: cannot remove RR from IXFR, "
879 "RPZ domain not found");
880 lock_rw_unlock(&r->respip_set->lock);
881 return;
882 }
883
884 lock_rw_wrlock(&node->lock);
885 if(a == RPZ_LOCAL_DATA_ACTION) {
886 /* remove RR, signal whether RR can be removed */
887 delete_respip = rpz_rrset_delete_rr(node, rr_type, rdatawl,
888 rdatalen);
889 }
890 lock_rw_unlock(&node->lock);
891 if(delete_respip)
892 respip_sockaddr_delete(r->respip_set, node);
893 lock_rw_unlock(&r->respip_set->lock);
894 }
895
896 void
rpz_remove_rr(struct rpz * r,size_t aznamelen,uint8_t * dname,size_t dnamelen,uint16_t rr_type,uint16_t rr_class,uint8_t * rdatawl,size_t rdatalen)897 rpz_remove_rr(struct rpz* r, size_t aznamelen, uint8_t* dname, size_t dnamelen,
898 uint16_t rr_type, uint16_t rr_class, uint8_t* rdatawl, size_t rdatalen)
899 {
900 size_t policydnamelen;
901 enum rpz_trigger t;
902 enum rpz_action a;
903 uint8_t* policydname;
904
905 if(!(policydname = calloc(1, LDNS_MAX_DOMAINLEN + 1)))
906 return;
907
908 a = rpz_rr_to_action(rr_type, rdatawl, rdatalen);
909 if(a == RPZ_INVALID_ACTION) {
910 free(policydname);
911 return;
912 }
913 if(!(policydnamelen = strip_dname_origin(dname, dnamelen, aznamelen,
914 policydname, LDNS_MAX_DOMAINLEN + 1))) {
915 free(policydname);
916 return;
917 }
918 t = rpz_dname_to_trigger(policydname, policydnamelen);
919 if(t == RPZ_QNAME_TRIGGER) {
920 rpz_remove_qname_trigger(r, policydname, policydnamelen, a,
921 rr_type, rr_class, rdatawl, rdatalen);
922 } else if(t == RPZ_RESPONSE_IP_TRIGGER) {
923 rpz_remove_response_ip_trigger(r, policydname, policydnamelen,
924 a, rr_type, rdatawl, rdatalen);
925 }
926 free(policydname);
927 }
928
929 /** print log information for an applied RPZ policy. Based on local-zone's
930 * lz_inform_print().
931 */
932 static void
log_rpz_apply(uint8_t * dname,enum rpz_action a,struct query_info * qinfo,struct comm_reply * repinfo,char * log_name)933 log_rpz_apply(uint8_t* dname, enum rpz_action a, struct query_info* qinfo,
934 struct comm_reply* repinfo, char* log_name)
935 {
936 char ip[128], txt[512];
937 char dnamestr[LDNS_MAX_DOMAINLEN+1];
938 uint16_t port = ntohs(((struct sockaddr_in*)&repinfo->addr)->sin_port);
939 dname_str(dname, dnamestr);
940 addr_to_str(&repinfo->addr, repinfo->addrlen, ip, sizeof(ip));
941 if(log_name)
942 snprintf(txt, sizeof(txt), "RPZ applied [%s] %s %s %s@%u",
943 log_name, dnamestr, rpz_action_to_string(a), ip,
944 (unsigned)port);
945 else
946 snprintf(txt, sizeof(txt), "RPZ applied %s %s %s@%u",
947 dnamestr, rpz_action_to_string(a), ip, (unsigned)port);
948 log_nametypeclass(0, txt, qinfo->qname, qinfo->qtype, qinfo->qclass);
949 }
950
951 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)952 rpz_apply_qname_trigger(struct auth_zones* az, struct module_env* env,
953 struct query_info* qinfo, struct edns_data* edns, sldns_buffer* buf,
954 struct regional* temp, struct comm_reply* repinfo,
955 uint8_t* taglist, size_t taglen, struct ub_server_stats* stats)
956 {
957 struct rpz* r = NULL;
958 struct auth_zone* a;
959 int ret;
960 enum localzone_type lzt;
961 struct local_zone* z = NULL;
962 struct local_data* ld = NULL;
963 lock_rw_rdlock(&az->rpz_lock);
964 for(a = az->rpz_first; a; a = a->rpz_az_next) {
965 lock_rw_rdlock(&a->lock);
966 r = a->rpz;
967 if(!r->disabled && (!r->taglist || taglist_intersect(r->taglist,
968 r->taglistlen, taglist, taglen))) {
969 z = rpz_find_zone(r, qinfo->qname, qinfo->qname_len,
970 qinfo->qclass, 0, 0, 0);
971 if(z && r->action_override == RPZ_DISABLED_ACTION) {
972 if(r->log)
973 log_rpz_apply(z->name,
974 r->action_override,
975 qinfo, repinfo, r->log_name);
976 /* TODO only register stats when stats_extended?
977 * */
978 stats->rpz_action[r->action_override]++;
979 lock_rw_unlock(&z->lock);
980 z = NULL;
981 }
982 if(z)
983 break;
984 }
985 lock_rw_unlock(&a->lock); /* not found in this auth_zone */
986 }
987 lock_rw_unlock(&az->rpz_lock);
988 if(!z)
989 return 0; /* not holding auth_zone.lock anymore */
990
991 log_assert(r);
992 if(r->action_override == RPZ_NO_OVERRIDE_ACTION)
993 lzt = z->type;
994 else
995 lzt = rpz_action_to_localzone_type(r->action_override);
996
997 if(r->action_override == RPZ_CNAME_OVERRIDE_ACTION) {
998 qinfo->local_alias =
999 regional_alloc_zero(temp, sizeof(struct local_rrset));
1000 if(!qinfo->local_alias) {
1001 lock_rw_unlock(&z->lock);
1002 lock_rw_unlock(&a->lock);
1003 return 0; /* out of memory */
1004 }
1005 qinfo->local_alias->rrset =
1006 regional_alloc_init(temp, r->cname_override,
1007 sizeof(*r->cname_override));
1008 if(!qinfo->local_alias->rrset) {
1009 lock_rw_unlock(&z->lock);
1010 lock_rw_unlock(&a->lock);
1011 return 0; /* out of memory */
1012 }
1013 qinfo->local_alias->rrset->rk.dname = qinfo->qname;
1014 qinfo->local_alias->rrset->rk.dname_len = qinfo->qname_len;
1015 if(r->log)
1016 log_rpz_apply(z->name, RPZ_CNAME_OVERRIDE_ACTION,
1017 qinfo, repinfo, r->log_name);
1018 stats->rpz_action[RPZ_CNAME_OVERRIDE_ACTION]++;
1019 lock_rw_unlock(&z->lock);
1020 lock_rw_unlock(&a->lock);
1021 return 0;
1022 }
1023
1024 if(lzt == local_zone_redirect && local_data_answer(z, env, qinfo,
1025 edns, repinfo, buf, temp, dname_count_labels(qinfo->qname),
1026 &ld, lzt, -1, NULL, 0, NULL, 0)) {
1027 if(r->log)
1028 log_rpz_apply(z->name,
1029 localzone_type_to_rpz_action(lzt), qinfo,
1030 repinfo, r->log_name);
1031 stats->rpz_action[localzone_type_to_rpz_action(lzt)]++;
1032 lock_rw_unlock(&z->lock);
1033 lock_rw_unlock(&a->lock);
1034 return !qinfo->local_alias;
1035 }
1036
1037 ret = local_zones_zone_answer(z, env, qinfo, edns, repinfo, buf, temp,
1038 0 /* no local data used */, lzt);
1039 if(r->log)
1040 log_rpz_apply(z->name, localzone_type_to_rpz_action(lzt),
1041 qinfo, repinfo, r->log_name);
1042 stats->rpz_action[localzone_type_to_rpz_action(lzt)]++;
1043 lock_rw_unlock(&z->lock);
1044 lock_rw_unlock(&a->lock);
1045
1046 return ret;
1047 }
1048
rpz_enable(struct rpz * r)1049 void rpz_enable(struct rpz* r)
1050 {
1051 if(!r)
1052 return;
1053 r->disabled = 0;
1054 }
1055
rpz_disable(struct rpz * r)1056 void rpz_disable(struct rpz* r)
1057 {
1058 if(!r)
1059 return;
1060 r->disabled = 1;
1061 }
1062