- Added RPZ action overrides

- Added RPZ policy apply logging
This commit is contained in:
Ralph Dolmans 2019-05-16 22:30:42 +02:00
parent a7f68865e4
commit b0b69321f9
10 changed files with 3648 additions and 3328 deletions

View File

@ -636,8 +636,9 @@ depend:
# Dependencies
dns.lo dns.o: $(srcdir)/services/cache/dns.c config.h $(srcdir)/iterator/iter_delegpt.h $(srcdir)/util/log.h \
$(srcdir)/validator/val_nsec.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h \
$(srcdir)/util/locks.h $(srcdir)/validator/val_utils.h $(srcdir)/sldns/pkthdr.h $(srcdir)/services/cache/dns.h \
$(srcdir)/iterator/iter_utils.h $(srcdir)/iterator/iter_resptype.h $(srcdir)/validator/val_nsec.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h \
$(srcdir)/validator/val_utils.h $(srcdir)/sldns/pkthdr.h $(srcdir)/services/cache/dns.h \
$(srcdir)/util/data/msgreply.h $(srcdir)/services/cache/rrset.h $(srcdir)/util/storage/slabhash.h \
$(srcdir)/util/data/dname.h $(srcdir)/util/module.h $(srcdir)/util/data/msgparse.h $(srcdir)/sldns/rrdef.h \
$(srcdir)/util/net_help.h $(srcdir)/util/regional.h $(srcdir)/util/config_file.h $(srcdir)/sldns/sbuffer.h
@ -790,7 +791,7 @@ rpz.lo rpz.o: $(srcdir)/services/rpz.c config.h $(srcdir)/services/rpz.h $(srcdi
$(srcdir)/sldns/rrdef.h $(srcdir)/services/view.h $(srcdir)/sldns/sbuffer.h $(srcdir)/util/config_file.h \
$(srcdir)/services/authzone.h $(srcdir)/services/mesh.h $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/services/modstack.h $(srcdir)/sldns/wire2str.h \
$(srcdir)/util/data/dname.h $(srcdir)/util/net_help.h
$(srcdir)/sldns/str2wire.h $(srcdir)/util/data/dname.h $(srcdir)/util/net_help.h $(srcdir)/util/regional.h
outbound_list.lo outbound_list.o: $(srcdir)/services/outbound_list.c config.h \
$(srcdir)/services/outbound_list.h $(srcdir)/services/outside_network.h $(srcdir)/util/rbtree.h \
$(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \

View File

@ -1686,6 +1686,7 @@ const char* local_zone_type2str(enum localzone_type t)
case local_zone_always_nxdomain: return "always_nxdomain";
case local_zone_always_nodata: return "always_nodata";
case local_zone_noview: return "noview";
case local_zone_invalid: return "invalid";
}
return "badtyped";
}

View File

@ -43,11 +43,13 @@
#include "services/rpz.h"
#include "util/config_file.h"
#include "sldns/wire2str.h"
#include "sldns/str2wire.h"
#include "util/data/dname.h"
#include "util/net_help.h"
#include "util/log.h"
#include "util/data/dname.h"
#include "util/locks.h"
#include "util/regional.h"
/** string for RPZ action enum */
static const char*
@ -61,10 +63,33 @@ rpz_action_to_string(enum rpz_action a)
case RPZ_TCP_ONLY_ACTION: return "TCP ONLY ACTION";
case RPZ_INVALID_ACTION: return "INVALID ACTION";
case RPZ_LOCAL_DATA_ACTION: return "LOCAL DATA ACTION";
case RPZ_DISABLED_ACTION: return "DISABLED ACTION";
case RPZ_CNAME_OVERRIDE_ACTION: return "CNAME OVERRIDE ACTION";
case RPZ_NO_OVERRIDE_ACTION: return "NO OVERRIDE ACTION";
}
return "UNKNOWN RPZ ACTION";
}
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;
}
/** string for RPZ trigger enum */
static const char*
rpz_trigger_to_string(enum rpz_trigger r)
@ -175,7 +200,9 @@ rpz_action_to_localzone_type(enum rpz_action a)
case RPZ_NODATA_ACTION: return local_zone_always_nodata;
case RPZ_DROP_ACTION: return local_zone_deny;
case RPZ_PASSTHRU_ACTION: return local_zone_always_transparent;
case RPZ_LOCAL_DATA_ACTION: return local_zone_redirect;
case RPZ_LOCAL_DATA_ACTION:
case RPZ_CNAME_OVERRIDE_ACTION:
return local_zone_redirect;
case RPZ_INVALID_ACTION:
case RPZ_TCP_ONLY_ACTION:
default:
@ -230,6 +257,7 @@ void rpz_delete(struct rpz* r)
if(!r)
return;
local_zones_delete(r->local_zones);
regional_destroy(r->region);
free(r->taglist);
free(r);
}
@ -245,6 +273,51 @@ rpz_clear_lz(struct rpz* r)
return 1;
}
/** new rrset containing CNAME override, does not yet contain a dname */
static struct ub_packed_rrset_key*
new_cname_override(struct regional* region, uint8_t* ct, size_t ctlen)
{
struct ub_packed_rrset_key* rrset;
struct packed_rrset_data* pd;
uint16_t rdlength = htons(ctlen);
rrset = (struct ub_packed_rrset_key*)regional_alloc_zero(region,
sizeof(*rrset));
if(!rrset) {
log_err("out of memory");
return NULL;
}
rrset->entry.key = rrset;
pd = (struct packed_rrset_data*)regional_alloc_zero(region, sizeof(*pd));
if(!pd) {
log_err("out of memory");
return NULL;
}
pd->trust = rrset_trust_prim_noglue;
pd->security = sec_status_insecure;
pd->count = 1;
pd->rr_len = regional_alloc_zero(region, sizeof(*pd->rr_len));
pd->rr_ttl = regional_alloc_zero(region, sizeof(*pd->rr_ttl));
pd->rr_data = regional_alloc_zero(region, sizeof(*pd->rr_data));
if(!pd->rr_len || !pd->rr_ttl || !pd->rr_data) {
log_err("out of memory");
return NULL;
}
pd->rr_len[0] = ctlen+2;
pd->rr_ttl[0] = 3600; /* TODO, what should this be? */
pd->rr_data[0] = regional_alloc_zero(region, 2 /* rdlength */ + ctlen);
if(!pd->rr_data[0]) {
log_err("out of memory");
return NULL;
}
memcpy(pd->rr_data[0], &rdlength, 2);
memcpy(pd->rr_data[0]+2, ct, ctlen);
rrset->entry.data = pd;
rrset->rk.type = htons(LDNS_RR_TYPE_CNAME);
rrset->rk.rrset_class = htons(LDNS_RR_CLASS_IN);
return rrset;
}
struct rpz*
rpz_create(struct config_auth* p)
{
@ -252,12 +325,53 @@ rpz_create(struct config_auth* p)
if(!r)
return 0;
r->region = regional_create_custom(sizeof(struct regional));
if(!r->region) {
free(r);
return 0;
}
if(!(r->local_zones = local_zones_create())){
free(r);
return 0;
}
r->taglist = memdup(p->rpz_taglist, p->rpz_taglistlen);
r->taglistlen = p->rpz_taglistlen;
if(p->rpz_action_override) {
r->action_override = rpz_config_to_action(p->rpz_action_override);
free(p->rpz_action_override);
p->rpz_action_override = NULL;
}
else
r->action_override = RPZ_NO_OVERRIDE_ACTION;
if(r->action_override == RPZ_CNAME_OVERRIDE_ACTION) {
uint8_t nm[LDNS_MAX_DOMAINLEN+1];
size_t nmlen = sizeof(nm);
if(!p->rpz_cname) {
log_err("RPZ override with cname action found, but not "
"rpz-cname-override configured");
free(r);
return 0;
}
if(sldns_str2wire_dname_buf(p->rpz_cname, nm, &nmlen) != 0) {
log_err("cannot parse RPZ cname override: %s",
p->rpz_cname);
free(p->rpz_cname);
free(r);
return 0;
}
free(p->rpz_cname);
p->rpz_cname = NULL;
r->cname_override = new_cname_override(r->region, nm, nmlen);
if(!r->cname_override) {
free(r);
return 0;
}
}
r->log = p->rpz_log;
return r;
}
@ -286,7 +400,7 @@ rpz_insert_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
char* rrstr;
if(a == RPZ_TCP_ONLY_ACTION || a == RPZ_INVALID_ACTION) {
verbose(VERB_ALGO, "RPZ: skipping unusupported action: %s",
verbose(VERB_ALGO, "RPZ: skipping unsupported action: %s",
rpz_action_to_string(a));
return 0;
}
@ -527,16 +641,16 @@ rpz_remove_rr(struct rpz* r, size_t aznamelen, uint8_t* dname,
* lz_inform_print().
*/
static void
rpz_inform_print(struct local_zone* z, struct query_info* qinfo,
log_rpz_apply(uint8_t* dname, enum rpz_action a, struct query_info* qinfo,
struct comm_reply* repinfo)
{
char ip[128], txt[512];
char zname[LDNS_MAX_DOMAINLEN+1];
char dnamestr[LDNS_MAX_DOMAINLEN+1];
uint16_t port = ntohs(((struct sockaddr_in*)&repinfo->addr)->sin_port);
dname_str(z->name, zname);
dname_str(dname, dnamestr);
addr_to_str(&repinfo->addr, repinfo->addrlen, ip, sizeof(ip));
snprintf(txt, sizeof(txt), "RPZ applied %s %s %s@%u", zname,
local_zone_type2str(z->type), ip, (unsigned)port);
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);
}
@ -548,30 +662,76 @@ rpz_apply_qname_trigger(struct auth_zones* az, struct module_env* env,
{
struct rpz* r;
int ret;
enum localzone_type lzt;
struct local_zone* z = NULL;
struct local_data* ld = NULL;
lock_rw_rdlock(&az->rpz_lock);
for(r = az->rpz_first; r && !z; r = r->next) {
for(r = az->rpz_first; r; r = r->next) {
if(!r->taglist || taglist_intersect(r->taglist,
r->taglistlen, taglist, taglen))
r->taglistlen, taglist, taglen)) {
z = rpz_find_zone(r, qinfo->qname, qinfo->qname_len,
qinfo->qclass, 0, 0);
if(z && r->action_override == RPZ_DISABLED_ACTION) {
if(r->log)
log_rpz_apply(z->name,
r->action_override,
qinfo,repinfo);
lock_rw_unlock(&z->lock);
z = NULL;
}
if(z)
break;
}
}
lock_rw_unlock(&az->rpz_lock);
if(!z)
return 0;
if(z->type == local_zone_redirect && local_data_answer(z, env, qinfo,
if(r->action_override == RPZ_NO_OVERRIDE_ACTION)
lzt = z->type;
else
lzt = rpz_action_to_localzone_type(r->action_override);
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);
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);
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);
lock_rw_unlock(&z->lock);
return 0;
}
if(lzt == local_zone_redirect && local_data_answer(z, env, qinfo,
edns, repinfo, buf, temp, dname_count_labels(qinfo->qname),
&ld, z->type, -1, NULL, 0, NULL, 0)) {
rpz_inform_print(z, qinfo, repinfo);
&ld, lzt, -1, NULL, 0, NULL, 0)) {
if(r->log)
log_rpz_apply(z->name,
localzone_type_to_rpz_action(lzt), qinfo,
repinfo);
lock_rw_unlock(&z->lock);
return !qinfo->local_alias;
}
ret = local_zones_zone_answer(z, env, qinfo, edns, repinfo, buf, temp,
0 /* no local data used */, z->type);
rpz_inform_print(z, qinfo, repinfo);
0 /* no local data used */, lzt);
if(r->log)
log_rpz_apply(z->name, localzone_type_to_rpz_action(lzt),
qinfo, repinfo);
lock_rw_unlock(&z->lock);
return ret;

View File

@ -48,20 +48,6 @@
#include "services/authzone.h"
#include "sldns/sbuffer.h"
/**
* RPZ containing policies. Pointed to from corresponding authz-one. Part of a
* linked list to keep configuration order. Iterating or changing the linked
* list requires the rpz_lock from struct auth_zones.
*/
struct rpz {
struct local_zones* local_zones;
uint8_t* taglist;
size_t taglistlen;
struct rpz* next;
struct rpz* prev;
/* tags */
};
/**
* RPZ triggers, only the QNAME trigger is currently supported in Unbound.
*/
@ -87,6 +73,27 @@ enum rpz_action {
"rpz-" in target, SOA, NS, DNAME and
DNSSEC-related records. */
RPZ_LOCAL_DATA_ACTION, /* anything else */
/* RPZ override actions */
RPZ_DISABLED_ACTION, /* RPZ action disabled using override */
RPZ_NO_OVERRIDE_ACTION, /* RPZ action no override*/
RPZ_CNAME_OVERRIDE_ACTION, /* RPZ CNAME action override*/
};
/**
* RPZ containing policies. Pointed to from corresponding authz-one. Part of a
* linked list to keep configuration order. Iterating or changing the linked
* list requires the rpz_lock from struct auth_zones.
*/
struct rpz {
struct local_zones* local_zones;
uint8_t* taglist;
size_t taglistlen;
enum rpz_action action_override;
struct ub_packed_rrset_key* cname_override;
int log;
struct rpz* next;
struct rpz* prev;
struct regional* region;
};
/**

View File

@ -637,7 +637,13 @@ struct config_auth {
uint8_t* rpz_taglist;
/** length of the taglist (in bytes) */
size_t rpz_taglistlen;
/** Override RPZ action for this zone, regardless of zone content */
char* rpz_action_override;
/** Log when this RPZ policy is applied */
int rpz_log;
/** Always reply with this CNAME target if the cname override action is
* used */
char* rpz_cname;
};
/**

File diff suppressed because it is too large Load Diff

View File

@ -319,6 +319,9 @@ forward-tls-upstream{COLON} { YDVAR(1, VAR_FORWARD_SSL_UPSTREAM) }
auth-zone{COLON} { YDVAR(0, VAR_AUTH_ZONE) }
rpz{COLON} { YDVAR(0, VAR_RPZ) }
tags{COLON} { YDVAR(1, VAR_TAGS) }
rpz-action-override{COLON} { YDVAR(1, VAR_RPZ_ACTION_OVERRIDE) }
rpz-cname-override{COLON} { YDVAR(1, VAR_RPZ_CNAME_OVERRIDE) }
rpz-log{COLON} { YDVAR(1, VAR_RPZ_LOG) }
zonefile{COLON} { YDVAR(1, VAR_ZONEFILE) }
master{COLON} { YDVAR(1, VAR_MASTER) }
url{COLON} { YDVAR(1, VAR_URL) }

File diff suppressed because it is too large Load Diff

View File

@ -310,7 +310,10 @@ extern int yydebug;
VAR_TLS_CIPHERSUITES = 520,
VAR_TLS_SESSION_TICKET_KEYS = 521,
VAR_RPZ = 522,
VAR_TAGS = 523
VAR_TAGS = 523,
VAR_RPZ_ACTION_OVERRIDE = 524,
VAR_RPZ_CNAME_OVERRIDE = 525,
VAR_RPZ_LOG = 526
};
#endif
/* Tokens. */
@ -580,6 +583,9 @@ extern int yydebug;
#define VAR_TLS_SESSION_TICKET_KEYS 521
#define VAR_RPZ 522
#define VAR_TAGS 523
#define VAR_RPZ_ACTION_OVERRIDE 524
#define VAR_RPZ_CNAME_OVERRIDE 525
#define VAR_RPZ_LOG 526
/* Value type. */
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
@ -590,7 +596,7 @@ union YYSTYPE
char* str;
#line 594 "util/configparser.h" /* yacc.c:1909 */
#line 600 "util/configparser.h" /* yacc.c:1909 */
};
typedef union YYSTYPE YYSTYPE;

View File

@ -166,7 +166,8 @@ extern struct config_parser_state* cfg_parser;
%token VAR_FORWARD_NO_CACHE VAR_STUB_NO_CACHE VAR_LOG_SERVFAIL VAR_DENY_ANY
%token VAR_UNKNOWN_SERVER_TIME_LIMIT VAR_LOG_TAG_QUERYREPLY
%token VAR_STREAM_WAIT_SIZE VAR_TLS_CIPHERS VAR_TLS_CIPHERSUITES
%token VAR_TLS_SESSION_TICKET_KEYS VAR_RPZ VAR_TAGS
%token VAR_TLS_SESSION_TICKET_KEYS VAR_RPZ VAR_TAGS VAR_RPZ_ACTION_OVERRIDE
%token VAR_RPZ_CNAME_OVERRIDE VAR_RPZ_LOG
%%
toplevelvars: /* empty */ | toplevelvars toplevelvar ;
@ -365,6 +366,45 @@ rpz_tag: VAR_TAGS STRING_ARG
}
}
;
rpz_action_override: VAR_RPZ_ACTION_OVERRIDE STRING_ARG
{
OUTYY(("P(rpz_action_override:%s)\n", $2));
if(strcmp($2, "nxdomain")!=0 && strcmp($2, "nodata")!=0 &&
strcmp($2, "passthru")!=0 && strcmp($2, "drop")!=0 &&
strcmp($2, "cname")!=0 && strcmp($2, "disabled")!=0) {
yyerror("rpz-action-override action: expected nxdomain, "
"nodata, passthru, drop cname or disabled");
free($2);
cfg_parser->cfg->auths->rpz_action_override = NULL;
}
else {
cfg_parser->cfg->auths->rpz_action_override = $2;
}
}
;
rpz_cname_override: VAR_RPZ_CNAME_OVERRIDE STRING_ARG
{
OUTYY(("P(rpz_cname_override:%s)\n", $2));
if(cfg_parser->cfg->auths->rpz_cname)
yyerror("there can only be one CNAME override per "
"RPZ");
free(cfg_parser->cfg->auths->rpz_cname);
cfg_parser->cfg->auths->rpz_cname = $2;
}
;
rpz_log: VAR_RPZ_LOG STRING_ARG
{
OUTYY(("P(rpz_log:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->auths->rpz_log = (strcmp($2, "yes")==0);
free($2);
}
;
rpzstart: VAR_RPZ
{
struct config_auth* s;
@ -385,7 +425,8 @@ rpzstart: VAR_RPZ
contents_rpz: contents_rpz content_rpz
| ;
content_rpz: auth_name | auth_zonefile | rpz_tag | auth_master | auth_url |
auth_allow_notify
auth_allow_notify | rpz_action_override | rpz_cname_override |
rpz_log
;
server_num_threads: VAR_NUM_THREADS STRING_ARG
{