summaryrefslogtreecommitdiff
path: root/bin/named/query.c
diff options
context:
space:
mode:
Diffstat (limited to 'bin/named/query.c')
-rw-r--r--bin/named/query.c180
1 files changed, 149 insertions, 31 deletions
diff --git a/bin/named/query.c b/bin/named/query.c
index 9e67f2d2187fd..8c589841396bf 100644
--- a/bin/named/query.c
+++ b/bin/named/query.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2004-2012 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2004-2013 Internet Systems Consortium, Inc. ("ISC")
* Copyright (C) 1999-2003 Internet Software Consortium.
*
* Permission to use, copy, modify, and/or distribute this software for any
@@ -94,6 +94,10 @@
/*% Want DNSSEC? */
#define WANTDNSSEC(c) (((c)->attributes & \
NS_CLIENTATTR_WANTDNSSEC) != 0)
+/*% Want WANTAD? */
+#define WANTAD(c) (((c)->attributes & \
+ NS_CLIENTATTR_WANTAD) != 0)
+
/*% No authority? */
#define NOAUTHORITY(c) (((c)->query.attributes & \
NS_QUERYATTR_NOAUTHORITY) != 0)
@@ -651,7 +655,7 @@ query_validatezonedb(ns_client_t *client, dns_name_t *name,
dns_dbversion_t **versionp)
{
isc_result_t result;
- dns_acl_t *queryacl;
+ dns_acl_t *queryacl, *queryonacl;
ns_dbversion_t *dbversion;
REQUIRE(zone != NULL);
@@ -763,6 +767,21 @@ query_validatezonedb(ns_client_t *client, dns_name_t *name,
client->query.attributes |= NS_QUERYATTR_QUERYOKVALID;
}
+ /* If and only if we've gotten this far, check allow-query-on too */
+ if (result == ISC_R_SUCCESS) {
+ queryonacl = dns_zone_getqueryonacl(zone);
+ if (queryonacl == NULL)
+ queryonacl = client->view->queryonacl;
+
+ result = ns_client_checkaclsilent(client, NULL,
+ queryonacl, ISC_TRUE);
+ if ((options & DNS_GETDB_NOLOG) == 0 &&
+ result != ISC_R_SUCCESS)
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_QUERY, ISC_LOG_INFO,
+ "query-on denied");
+ }
+
dbversion->acl_checked = ISC_TRUE;
if (result != ISC_R_SUCCESS) {
dbversion->queryok = ISC_FALSE;
@@ -831,12 +850,29 @@ query_getzonedb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype,
}
static void
-rpz_log_rewrite(ns_client_t *client, const char *disabled,
+rpz_log_rewrite(ns_client_t *client, isc_boolean_t disabled,
dns_rpz_policy_t policy, dns_rpz_type_t type,
- dns_name_t *rpz_qname) {
+ dns_zone_t *zone, dns_name_t *rpz_qname)
+{
+ isc_stats_t *zonestats;
char qname_buf[DNS_NAME_FORMATSIZE];
char rpz_qname_buf[DNS_NAME_FORMATSIZE];
+ /*
+ * Count enabled rewrites in the global counter.
+ * Count both enabled and disabled rewrites for each zone.
+ */
+ if (!disabled && policy != DNS_RPZ_POLICY_PASSTHRU) {
+ isc_stats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_rpz_rewrites);
+ }
+ if (zone != NULL) {
+ zonestats = dns_zone_getrequeststats(zone);
+ if (zonestats != NULL)
+ isc_stats_increment(zonestats,
+ dns_nsstatscounter_rpz_rewrites);
+ }
+
if (!isc_log_wouldlog(ns_g_lctx, DNS_RPZ_INFO_LEVEL))
return;
@@ -845,7 +881,7 @@ rpz_log_rewrite(ns_client_t *client, const char *disabled,
ns_client_log(client, DNS_LOGCATEGORY_RPZ, NS_LOGMODULE_QUERY,
DNS_RPZ_INFO_LEVEL, "%srpz %s %s rewrite %s via %s",
- disabled,
+ disabled ? "disabled " : "",
dns_rpz_type2str(type), dns_rpz_policy2str(policy),
qname_buf, rpz_qname_buf);
}
@@ -861,6 +897,9 @@ rpz_log_fail(ns_client_t *client, int level,
if (!isc_log_wouldlog(ns_g_lctx, level))
return;
+ /*
+ * bin/tests/system/rpz/tests.sh looks for "rpz.*failed".
+ */
dns_name_format(client->query.qname, namebuf1, sizeof(namebuf1));
dns_name_format(name, namebuf2, sizeof(namebuf2));
ns_client_log(client, NS_LOGCATEGORY_QUERY_EERRORS,
@@ -3075,6 +3114,14 @@ query_addbestns(ns_client_t *client) {
goto cleanup;
/*
+ * If the answer is secure only add NS records if they are secure * when the client may be looking for AD in the response.
+ */
+ if (SECURE(client) && (WANTDNSSEC(client) || WANTAD(client)) &&
+ ((rdataset->trust != dns_trust_secure) ||
+ (sigrdataset != NULL && sigrdataset->trust != dns_trust_secure)))
+ goto cleanup;
+
+ /*
* If the client doesn't want DNSSEC we can discard the sigrdataset
* now.
*/
@@ -4028,6 +4075,8 @@ rpz_rewrite_rrset(ns_client_t *client, dns_rpz_type_t rpz_type,
rdatasetp, resuming);
switch (result) {
case ISC_R_SUCCESS:
+ case DNS_R_GLUE:
+ case DNS_R_ZONECUT:
result = rpz_rewrite_ip(client, *rdatasetp, rpz_type);
break;
case DNS_R_EMPTYNAME:
@@ -4121,6 +4170,8 @@ rpz_find(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qnamef,
dns_name_t *found;
isc_result_t result;
+ REQUIRE(nodep != NULL);
+
result = rpz_ready(client, zonep, dbp, nodep, rdatasetp);
if (result != ISC_R_SUCCESS) {
*policyp = DNS_RPZ_POLICY_ERROR;
@@ -4204,26 +4255,32 @@ rpz_find(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qnamef,
result = DNS_R_CNAME;
}
break;
+ case DNS_R_NXRRSET:
+ policy = DNS_RPZ_POLICY_NODATA;
+ break;
case DNS_R_DNAME:
/*
* DNAME policy RRs have very few if any uses that are not
* better served with simple wildcards. Making the work would
* require complications to get the number of labels matched
* in the name or the found name to the main DNS_R_DNAME case
- * in query_find(). So fall through to treat them as NODATA.
+ * in query_find().
+ */
+ dns_rdataset_disassociate(*rdatasetp);
+ dns_db_detachnode(*dbp, nodep);
+ /*
+ * Fall through to treat it as a miss.
*/
- case DNS_R_NXRRSET:
- policy = DNS_RPZ_POLICY_NODATA;
- break;
case DNS_R_NXDOMAIN:
case DNS_R_EMPTYNAME:
/*
* If we don't get a qname hit,
* see if it is worth looking for other types.
*/
- dns_db_rpz_enabled(*dbp, client->query.rpz_st);
+ (void)dns_db_rpz_enabled(*dbp, client->query.rpz_st);
dns_db_detach(dbp);
dns_zone_detach(zonep);
+ result = DNS_R_NXDOMAIN;
policy = DNS_RPZ_POLICY_MISS;
break;
default:
@@ -4231,9 +4288,7 @@ rpz_find(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qnamef,
dns_zone_detach(zonep);
rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type, qnamef,
"", result);
- policy = DNS_RPZ_POLICY_ERROR;
- result = DNS_R_SERVFAIL;
- break;
+ return (DNS_R_SERVFAIL);
}
*policyp = policy;
@@ -4299,6 +4354,9 @@ rpz_rewrite_name(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname,
if (result == ISC_R_SUCCESS)
break;
INSIST(result == DNS_R_NAMETOOLONG);
+ /*
+ * Trim the name until it is not too long.
+ */
labels = dns_name_countlabels(prefix);
if (labels < 2) {
rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL,
@@ -4322,7 +4380,6 @@ rpz_rewrite_name(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname,
rdatasetp, &policy);
switch (result) {
case DNS_R_NXDOMAIN:
- case DNS_R_EMPTYNAME:
break;
case DNS_R_SERVFAIL:
rpz_clean(&zone, &db, &node, rdatasetp);
@@ -4345,13 +4402,45 @@ rpz_rewrite_name(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname,
(st->m.type == rpz_type &&
0 >= dns_name_compare(rpz_qname, st->qname))))
continue;
+#if 0
+ /*
+ * This code would block a customer reported information
+ * leak of rpz rules by rewriting requests in the
+ * rpz-ip, rpz-nsip, rpz-nsdname,and rpz-passthru TLDs.
+ * Without this code, a bad guy could request
+ * 24.0.3.2.10.rpz-ip. to find the policy rule for
+ * 10.2.3.0/14. It is an insignificant leak and this
+ * code is not worth its cost, because the bad guy
+ * could publish "evil.com A 10.2.3.4" and request
+ * evil.com to get the same information.
+ * Keep code with "#if 0" in case customer demand
+ * is irresistible.
+ *
+ * We have the less frequent case of a triggered
+ * policy. Check that we have not trigger on one
+ * of the pretend RPZ TLDs.
+ * This test would make it impossible to rewrite
+ * names in TLDs that start with "rpz-" should
+ * ICANN ever allow such TLDs.
+ */
+ labels = dns_name_countlabels(qname);
+ if (labels >= 2) {
+ dns_label_t label;
+ dns_name_getlabel(qname, labels-2, &label);
+ if (label.length >= sizeof(DNS_RPZ_PREFIX)-1 &&
+ strncasecmp((const char *)label.base+1,
+ DNS_RPZ_PREFIX,
+ sizeof(DNS_RPZ_PREFIX)-1) == 0)
+ continue;
+ }
+#endif
/*
* Merely log DNS_RPZ_POLICY_DISABLED hits.
*/
if (rpz->policy == DNS_RPZ_POLICY_DISABLED) {
- rpz_log_rewrite(client, "disabled ",
- policy, rpz_type, rpz_qname);
+ rpz_log_rewrite(client, ISC_TRUE, policy,
+ rpz_type, zone, rpz_qname);
continue;
}
@@ -4482,7 +4571,7 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, isc_result_t qresult,
rdataset = NULL;
if ((st->state & DNS_RPZ_DONE_QNAME) == 0) {
/*
- * Check rules for the query name if this it the first time
+ * Check rules for the query name if this is the first time
* for the current qname, i.e. we've not been recursing.
* There is a first time for each name in a CNAME chain.
*/
@@ -4524,7 +4613,7 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, isc_result_t qresult,
dns_fixedname_init(&nsnamef);
dns_name_clone(client->query.qname, dns_fixedname_name(&nsnamef));
- while (st->r.label > 1) {
+ while (st->r.label > client->view->rpz_min_ns_labels) {
/*
* Get NS rrset for each domain in the current qname.
*/
@@ -4655,8 +4744,8 @@ cleanup:
st->m.policy == DNS_RPZ_POLICY_ERROR) {
if (st->m.policy == DNS_RPZ_POLICY_PASSTHRU &&
result != DNS_R_DELEGATION)
- rpz_log_rewrite(client, "", st->m.policy, st->m.type,
- st->qname);
+ rpz_log_rewrite(client, ISC_FALSE, st->m.policy,
+ st->m.type, st->m.zone, st->qname);
rpz_match_clear(st);
}
if (st->m.policy == DNS_RPZ_POLICY_ERROR) {
@@ -4671,7 +4760,7 @@ cleanup:
}
/*
- * See if response policy zone rewriting is allowed a lack of interest
+ * See if response policy zone rewriting is allowed by a lack of interest
* by the client in DNSSEC or a lack of signatures.
*/
static isc_boolean_t
@@ -4766,7 +4855,8 @@ rpz_add_cname(ns_client_t *client, dns_rpz_st_t *st,
fname, dns_trust_authanswer, st->m.ttl);
if (result != ISC_R_SUCCESS)
return (result);
- rpz_log_rewrite(client, "", st->m.policy, st->m.type, st->qname);
+ rpz_log_rewrite(client, ISC_FALSE, st->m.policy,
+ st->m.type, st->m.zone, st->qname);
ns_client_qnamereplace(client, fname);
/*
* Turn off DNSSEC because the results of a
@@ -5703,9 +5793,10 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
client->attributes &= ~(NS_CLIENTATTR_WANTDNSSEC |
DNS_MESSAGEFLAG_AD);
query_putrdataset(client, &sigrdataset);
+ rpz_st->q.is_zone = is_zone;
is_zone = ISC_TRUE;
- rpz_log_rewrite(client, "", rpz_st->m.policy,
- rpz_st->m.type, rpz_st->qname);
+ rpz_log_rewrite(client, ISC_FALSE, rpz_st->m.policy,
+ rpz_st->m.type, zone, rpz_st->qname);
}
}
@@ -6080,6 +6171,15 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
rdataset = NULL;
sigrdataset = NULL;
type = qtype = dns_rdatatype_a;
+ rpz_st = client->query.rpz_st;
+ if (rpz_st != NULL) {
+ /*
+ * Arrange for RPZ rewriting of any A records.
+ */
+ if ((rpz_st->state & DNS_RPZ_REWRITTEN) != 0)
+ is_zone = rpz_st->q.is_zone;
+ rpz_st_clear(client);
+ }
dns64 = ISC_TRUE;
goto db_find;
}
@@ -6108,7 +6208,10 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
* closest provable encloser.
*/
if (dns_rdataset_isassociated(rdataset) &&
- !dns_name_equal(qname, found)) {
+ !dns_name_equal(qname, found) &&
+ !(ns_g_nonearest &&
+ qtype != dns_rdatatype_ds))
+ {
unsigned int count;
unsigned int skip;
@@ -6338,6 +6441,15 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
sigrdataset = NULL;
fname = NULL;
type = qtype = dns_rdatatype_a;
+ rpz_st = client->query.rpz_st;
+ if (rpz_st != NULL) {
+ /*
+ * Arrange for RPZ rewriting of any A records.
+ */
+ if ((rpz_st->state & DNS_RPZ_REWRITTEN) != 0)
+ is_zone = rpz_st->q.is_zone;
+ rpz_st_clear(client);
+ }
dns64 = ISC_TRUE;
goto db_find;
}
@@ -6838,6 +6950,15 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
rdataset = NULL;
sigrdataset = NULL;
type = qtype = dns_rdatatype_a;
+ rpz_st = client->query.rpz_st;
+ if (rpz_st != NULL) {
+ /*
+ * Arrange for RPZ rewriting of any A records.
+ */
+ if ((rpz_st->state & DNS_RPZ_REWRITTEN) != 0)
+ is_zone = rpz_st->q.is_zone;
+ rpz_st_clear(client);
+ }
dns64_exclude = dns64 = ISC_TRUE;
goto db_find;
}
@@ -7124,7 +7245,6 @@ ns_query_start(ns_client_t *client) {
dns_rdatatype_t qtype;
unsigned int saved_extflags = client->extflags;
unsigned int saved_flags = client->message->flags;
- isc_boolean_t want_ad;
CTRACE("ns_query_start");
@@ -7286,13 +7406,11 @@ ns_query_start(ns_client_t *client) {
client->query.attributes &= ~NS_QUERYATTR_SECURE;
/*
- * Set 'want_ad' if the client has set AD in the query.
+ * Set NS_CLIENTATTR_WANTDNSSEC if the client has set AD in the query.
* This allows AD to be returned on queries without DO set.
*/
if ((message->flags & DNS_MESSAGEFLAG_AD) != 0)
- want_ad = ISC_TRUE;
- else
- want_ad = ISC_FALSE;
+ client->attributes |= NS_CLIENTATTR_WANTAD;
/*
* This is an ordinary query.
@@ -7317,7 +7435,7 @@ ns_query_start(ns_client_t *client) {
* Set AD. We must clear it if we add non-validated data to a
* response.
*/
- if (WANTDNSSEC(client) || want_ad)
+ if (WANTDNSSEC(client) || WANTAD(client))
message->flags |= DNS_MESSAGEFLAG_AD;
qclient = NULL;