diff options
Diffstat (limited to 'bin/named/query.c')
| -rw-r--r-- | bin/named/query.c | 180 |
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; |
