aboutsummaryrefslogtreecommitdiffstats
path: root/lib/dns/dnssec.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dns/dnssec.c')
-rw-r--r--lib/dns/dnssec.c100
1 files changed, 80 insertions, 20 deletions
diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c
index 6e9ab9f0d0d9..393a9c794d9d 100644
--- a/lib/dns/dnssec.c
+++ b/lib/dns/dnssec.c
@@ -35,16 +35,20 @@
#include <dns/dnssec.h>
#include <dns/fixedname.h>
#include <dns/keyvalues.h>
+#include <dns/log.h>
#include <dns/message.h>
#include <dns/rdata.h>
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
#include <dns/rdatastruct.h>
#include <dns/result.h>
+#include <dns/stats.h>
#include <dns/tsig.h> /* for DNS_TSIG_FUDGE */
#include <dst/result.h>
+LIBDNS_EXTERNAL_DATA isc_stats_t *dns_dnssec_stats;
+
#define is_response(msg) (msg->flags & DNS_MESSAGEFLAG_QR)
#define RETERR(x) do { \
@@ -74,6 +78,12 @@ digest_callback(void *arg, isc_region_t *data) {
return (dst_context_adddata(ctx, data));
}
+static inline void
+inc_stat(isc_statscounter_t counter) {
+ if (dns_dnssec_stats != NULL)
+ isc_stats_increment(dns_dnssec_stats, counter);
+}
+
/*
* Make qsort happy.
*/
@@ -150,7 +160,9 @@ dns_dnssec_keyfromrdata(dns_name_t *name, dns_rdata_t *rdata, isc_mem_t *mctx,
}
static isc_result_t
-digest_sig(dst_context_t *ctx, dns_rdata_t *sigrdata, dns_rdata_rrsig_t *sig) {
+digest_sig(dst_context_t *ctx, isc_boolean_t downcase, dns_rdata_t *sigrdata,
+ dns_rdata_rrsig_t *rrsig)
+{
isc_region_t r;
isc_result_t ret;
dns_fixedname_t fname;
@@ -162,11 +174,16 @@ digest_sig(dst_context_t *ctx, dns_rdata_t *sigrdata, dns_rdata_rrsig_t *sig) {
ret = dst_context_adddata(ctx, &r);
if (ret != ISC_R_SUCCESS)
return (ret);
- dns_fixedname_init(&fname);
- RUNTIME_CHECK(dns_name_downcase(&sig->signer,
- dns_fixedname_name(&fname), NULL)
- == ISC_R_SUCCESS);
- dns_name_toregion(dns_fixedname_name(&fname), &r);
+ if (downcase) {
+ dns_fixedname_init(&fname);
+
+ RUNTIME_CHECK(dns_name_downcase(&rrsig->signer,
+ dns_fixedname_name(&fname),
+ NULL) == ISC_R_SUCCESS);
+ dns_name_toregion(dns_fixedname_name(&fname), &r);
+ } else
+ dns_name_toregion(&rrsig->signer, &r);
+
return (dst_context_adddata(ctx, &r));
}
@@ -188,6 +205,7 @@ dns_dnssec_sign(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
isc_uint32_t flags;
unsigned int sigsize;
dns_fixedname_t fnewname;
+ dns_fixedname_t fsigner;
REQUIRE(name != NULL);
REQUIRE(dns_name_countlabels(name) <= 255);
@@ -215,8 +233,14 @@ dns_dnssec_sign(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
sig.common.rdtype = dns_rdatatype_rrsig;
ISC_LINK_INIT(&sig.common, link);
+ /*
+ * Downcase signer.
+ */
dns_name_init(&sig.signer, NULL);
- dns_name_clone(dst_key_name(key), &sig.signer);
+ dns_fixedname_init(&fsigner);
+ RUNTIME_CHECK(dns_name_downcase(dst_key_name(key),
+ dns_fixedname_name(&fsigner), NULL) == ISC_R_SUCCESS);
+ dns_name_clone(dns_fixedname_name(&fsigner), &sig.signer);
sig.covered = set->type;
sig.algorithm = dst_key_alg(key);
@@ -256,7 +280,7 @@ dns_dnssec_sign(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
/*
* Digest the SIG rdata.
*/
- ret = digest_sig(ctx, &tmpsigrdata, &sig);
+ ret = digest_sig(ctx, ISC_FALSE, &tmpsigrdata, &sig);
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
@@ -329,7 +353,7 @@ dns_dnssec_sign(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
memcpy(sig.signature, r.base, sig.siglen);
ret = dns_rdata_fromstruct(sigrdata, sig.common.rdclass,
- sig.common.rdtype, &sig, buffer);
+ sig.common.rdtype, &sig, buffer);
cleanup_array:
isc_mem_put(mctx, rdatas, nrdatas * sizeof(dns_rdata_t));
@@ -360,6 +384,7 @@ dns_dnssec_verify2(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
dst_context_t *ctx = NULL;
int labels = 0;
isc_uint32_t flags;
+ isc_boolean_t downcase = ISC_FALSE;
REQUIRE(name != NULL);
REQUIRE(set != NULL);
@@ -374,8 +399,10 @@ dns_dnssec_verify2(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
if (set->type != sig.covered)
return (DNS_R_SIGINVALID);
- if (isc_serial_lt(sig.timeexpire, sig.timesigned))
+ if (isc_serial_lt(sig.timeexpire, sig.timesigned)) {
+ inc_stat(dns_dnssecstats_fail);
return (DNS_R_SIGINVALID);
+ }
if (!ignoretime) {
isc_stdtime_get(&now);
@@ -383,10 +410,13 @@ dns_dnssec_verify2(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
/*
* Is SIG temporally valid?
*/
- if (isc_serial_lt((isc_uint32_t)now, sig.timesigned))
+ if (isc_serial_lt((isc_uint32_t)now, sig.timesigned)) {
+ inc_stat(dns_dnssecstats_fail);
return (DNS_R_SIGFUTURE);
- else if (isc_serial_lt(sig.timeexpire, (isc_uint32_t)now))
+ } else if (isc_serial_lt(sig.timeexpire, (isc_uint32_t)now)) {
+ inc_stat(dns_dnssecstats_fail);
return (DNS_R_SIGEXPIRED);
+ }
}
/*
@@ -397,16 +427,22 @@ dns_dnssec_verify2(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
case dns_rdatatype_ns:
case dns_rdatatype_soa:
case dns_rdatatype_dnskey:
- if (!dns_name_equal(name, &sig.signer))
+ if (!dns_name_equal(name, &sig.signer)) {
+ inc_stat(dns_dnssecstats_fail);
return (DNS_R_SIGINVALID);
+ }
break;
case dns_rdatatype_ds:
- if (dns_name_equal(name, &sig.signer))
+ if (dns_name_equal(name, &sig.signer)) {
+ inc_stat(dns_dnssecstats_fail);
return (DNS_R_SIGINVALID);
+ }
/* FALLTHROUGH */
default:
- if (!dns_name_issubdomain(name, &sig.signer))
+ if (!dns_name_issubdomain(name, &sig.signer)) {
+ inc_stat(dns_dnssecstats_fail);
return (DNS_R_SIGINVALID);
+ }
break;
}
@@ -414,11 +450,16 @@ dns_dnssec_verify2(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
* Is the key allowed to sign data?
*/
flags = dst_key_flags(key);
- if (flags & DNS_KEYTYPE_NOAUTH)
+ if (flags & DNS_KEYTYPE_NOAUTH) {
+ inc_stat(dns_dnssecstats_fail);
return (DNS_R_KEYUNAUTHORIZED);
- if ((flags & DNS_KEYFLAG_OWNERMASK) != DNS_KEYOWNER_ZONE)
+ }
+ if ((flags & DNS_KEYFLAG_OWNERMASK) != DNS_KEYOWNER_ZONE) {
+ inc_stat(dns_dnssecstats_fail);
return (DNS_R_KEYUNAUTHORIZED);
+ }
+ again:
ret = dst_context_create(key, mctx, &ctx);
if (ret != ISC_R_SUCCESS)
goto cleanup_struct;
@@ -426,7 +467,7 @@ dns_dnssec_verify2(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
/*
* Digest the SIG rdata (not including the signature).
*/
- ret = digest_sig(ctx, sigrdata, &sig);
+ ret = digest_sig(ctx, downcase, sigrdata, &sig);
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
@@ -505,21 +546,40 @@ dns_dnssec_verify2(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
r.base = sig.signature;
r.length = sig.siglen;
ret = dst_context_verify(ctx, &r);
- if (ret == DST_R_VERIFYFAILURE)
- ret = DNS_R_SIGINVALID;
+ if (ret == ISC_R_SUCCESS && downcase) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ dns_name_format(&sig.signer, namebuf, sizeof(namebuf));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO,
+ "sucessfully validated after lower casing "
+ "signer '%s'", namebuf);
+ inc_stat(dns_dnssecstats_downcase);
+ } else if (ret == ISC_R_SUCCESS)
+ inc_stat(dns_dnssecstats_asis);
cleanup_array:
isc_mem_put(mctx, rdatas, nrdatas * sizeof(dns_rdata_t));
cleanup_context:
dst_context_destroy(&ctx);
+ if (ret == DST_R_VERIFYFAILURE && !downcase) {
+ downcase = ISC_TRUE;
+ goto again;
+ }
cleanup_struct:
dns_rdata_freestruct(&sig);
+ if (ret == DST_R_VERIFYFAILURE)
+ ret = DNS_R_SIGINVALID;
+
+ if (ret != ISC_R_SUCCESS)
+ inc_stat(dns_dnssecstats_fail);
+
if (ret == ISC_R_SUCCESS && labels - sig.labels > 0) {
if (wild != NULL)
RUNTIME_CHECK(dns_name_concatenate(dns_wildcardname,
dns_fixedname_name(&fnewname),
wild, NULL) == ISC_R_SUCCESS);
+ inc_stat(dns_dnssecstats_wildcard);
ret = DNS_R_FROMWILDCARD;
}
return (ret);