]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
layer/validate: added DS to DNSKEY authentication code
authorKarel Slany <karel.slany@nic.cz>
Wed, 15 Jul 2015 09:21:00 +0000 (11:21 +0200)
committerKarel Slany <karel.slany@nic.cz>
Wed, 15 Jul 2015 09:21:00 +0000 (11:21 +0200)
lib/dnssec.c
lib/dnssec.h
lib/layer/validate.c

index e7ac4a5d4ee81072c16f266fde43e41f2c0175e1..3c6f51ccbe35f79cd8923802edcb5328099e7876 100644 (file)
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <assert.h>
+#include <dnssec/binary.h>
 #include <dnssec/crypto.h>
+#include <dnssec/error.h>
+#include <dnssec/key.h>
+#include <libknot/descriptor.h>
+#include <libknot/rdataset.h>
+#include <libknot/rrset.h>
+#include <libknot/rrtype/dnskey.h>
 
+
+#include "lib/defines.h"
 #include "lib/dnssec.h"
 
 #define DEBUG_MSG(fmt...) fprintf(stderr, fmt)
@@ -34,3 +44,129 @@ void kr_crypto_reinit(void)
 {
        dnssec_crypto_reinit();
 }
+
+static inline
+uint16_t _knot_ds_ktag(const knot_rdataset_t *rrs, size_t pos)
+{
+       KNOT_RDATASET_CHECK(rrs, pos, return 0);
+       return wire_read_u16(knot_rdata_offset(rrs, pos, 0));
+}
+
+static inline
+uint8_t _knot_ds_alg(const knot_rdataset_t *rrs, size_t pos)
+{
+       KNOT_RDATASET_CHECK(rrs, pos, return 0);
+       return *knot_rdata_offset(rrs, pos, 2);
+}
+
+static inline
+uint8_t _knot_ds_dtype(const knot_rdataset_t *rrs, size_t pos)
+{
+       KNOT_RDATASET_CHECK(rrs, pos, return 0);
+       return *knot_rdata_offset(rrs, pos, 3);
+}
+
+static inline
+void _knot_ds_digest(const knot_rdataset_t *rrs, size_t pos,
+                    uint8_t **digest, uint16_t *digest_size)
+{
+       KNOT_RDATASET_CHECK(rrs, pos, return);
+       *digest = knot_rdata_offset(rrs, pos, 4);
+       const knot_rdata_t *rr = knot_rdataset_at(rrs, pos);
+       *digest_size = knot_rdata_rdlen(rr) - 4;
+}
+
+/* RFC4035 5.2, second bullet */
+static int authenticate_referral(const knot_rdata_t *krdata, const knot_dname_t *kown,
+                                 const knot_rrset_t *ds)
+{
+       assert(krdata && kown && ds);
+       assert(ds->type == KNOT_RRTYPE_DS);
+
+       int ret = 0;
+       dnssec_binary_t orig_ds_rdata;
+       dnssec_binary_t generated_ds_rdata = {0, };
+       dnssec_key_t *key = NULL;
+
+       {
+               /* Obtain RDATA of the supplied DS. */
+               const knot_rdata_t *rr = knot_rdataset_at(&ds->rrs, 0);
+               orig_ds_rdata.size = knot_rdata_rdlen(rr);
+               orig_ds_rdata.data = knot_rdata_data(rr);
+       }
+
+       /* Set-up DNSKEY. */
+       ret = dnssec_key_new(&key);
+       if (ret != DNSSEC_EOK) {
+               ret = kr_error(ENOMEM);
+               goto fail;
+       }
+       {
+               dnssec_binary_t binary_key;
+               binary_key.size = knot_rdata_rdlen(krdata);
+               binary_key.data = knot_rdata_data(krdata);
+               if (!binary_key.size || !binary_key.data) {
+                       ret = kr_error(KNOT_DNSSEC_ENOKEY);
+                       goto fail;
+               }
+               ret = dnssec_key_set_rdata(key, &binary_key);
+               if (ret != DNSSEC_EOK) {
+                       ret = kr_error(ENOMEM);
+                       goto fail;
+               }
+       }
+       ret = dnssec_key_set_dname(key, kown);
+       if (ret != DNSSEC_EOK) {
+               ret = kr_error(ENOMEM);
+               goto fail;
+       }
+
+       /* Compute DS RDATA from the DNSKEY. */
+       ret = dnssec_key_create_ds(key, _knot_ds_dtype(&ds->rrs, 0), &generated_ds_rdata);
+       if (ret != DNSSEC_EOK) {
+               ret = kr_error(ENOMEM);
+               goto fail;
+       }
+
+       /* DS records contain algorithm, key tag and the digest.
+        * Therefore the comparison of the two DS is sufficient.
+        */
+       ret = ((orig_ds_rdata.size == generated_ds_rdata.size) &&
+           (memcmp(orig_ds_rdata.data, generated_ds_rdata.data, orig_ds_rdata.size) == 0)) ? kr_ok() : kr_error(KNOT_DNSSEC_ENOKEY);
+
+fail:
+       dnssec_binary_free(&generated_ds_rdata);
+       dnssec_key_free(key);
+       return ret;
+}
+
+int kr_dnskey_trusted(const knot_pktsection_t *sec, const knot_rrset_t *keys, const knot_rrset_t *ta)
+{
+       if (!sec || !keys || !ta) {
+               return kr_error(EINVAL);
+       }
+
+#warning TODO: there should be an error saying that there is no matching key
+       int ret = kr_error(KNOT_DNSSEC_ENOKEY);
+
+       /* The supplied DS record has been authenticated.
+        * It has been validated or is part of a configured trust anchor.
+        *
+        * This implementation actually ignores the SEP flag.
+        */
+
+       for (uint16_t i = 0; i < keys->rrs.rr_count; ++i) {
+               const knot_rdata_t *krr = knot_rdataset_at(&keys->rrs, i);
+               if (authenticate_referral(krr, keys->owner, ta) != 0) {
+                       continue;
+               }
+#warning TODO: Check the signature of the rrset.
+               ret = kr_ok();
+       }
+
+       if (ret != 0) {
+               return ret;
+       }
+
+       return kr_error(ENOSYS);
+}
index a29249cc0a64252872ecbac3b4d86eb717f2470f..2e89c3f3f90a198c1656394c9dfc630e9d6a9023 100644 (file)
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <libknot/packet/pkt.h>
+
 /**
  * Initialise cryptographic back-end.
  */
@@ -31,3 +33,12 @@ void kr_crypto_cleanup(void);
  * @note Must be called after fork() in the child.
  */
 void kr_crypto_reinit(void);
+
+/**
+ * Check whether the DNSKEY rrset matches the supplied trust anchor RRSet.
+ * @param sec Packet section containing the DNSKEY RRSet including its signatures.
+ * @param key DNSKEY RRSet to check.
+ * @param ta  Trust anchor RRSet agains which to validate the DNSKEY.
+ * @return     0 or error code.
+ */
+int kr_dnskey_trusted(const knot_pktsection_t *sec, const knot_rrset_t *keys, const knot_rrset_t *ta);
index e45f32b0778b01ded633d5f0fd734e7a23161a89..c4cc6dd62fdea3e156e3fdc871ad34bad374c8bf 100644 (file)
@@ -24,6 +24,7 @@
 #include <libknot/rrtype/rdname.h>
 #include <libknot/rrtype/dnskey.h>
 
+#include "lib/dnssec.h"
 #include "lib/layer/iterate.h"
 #include "lib/resolve.h"
 #include "lib/rplan.h"
@@ -541,6 +542,11 @@ static int validate_keyset(struct kr_query *qry, knot_pkt_t *answer)
 
        /* Check if there's a key for current TA. */
 #warning TODO: check if there is a DNSKEY we can trust (matching current TA)
+       int ret = kr_dnskey_trusted(an, qry->zone_cut.key, qry->zone_cut.trust_anchor);
+       if (ret != 0) {
+               knot_rrset_free(&qry->zone_cut.key, qry->zone_cut.pool);
+               return ret;
+       }
        return kr_ok();
 }