]> git.ipfire.org Git - thirdparty/ldns.git/commitdiff
More generalized zonemd functions + verification
authorWillem Toorop <willem@nlnetlabs.nl>
Mon, 16 Nov 2020 22:14:59 +0000 (23:14 +0100)
committerWillem Toorop <willem@nlnetlabs.nl>
Mon, 16 Nov 2020 22:14:59 +0000 (23:14 +0100)
buffer.c
dnssec_sign.c
dnssec_zone.c
error.c
examples/ldns-signzone.c
examples/ldns-verify-zone.c
ldns/dnssec_sign.h
ldns/dnssec_zone.h
ldns/error.h

index e008831fcc3717d10e1ab77d14114450cb3bbaf2..8f6aab59eaac826cc96000f0b3fc138037032ec5 100644 (file)
--- a/buffer.c
+++ b/buffer.c
@@ -63,6 +63,7 @@ ldns_buffer_set_capacity(ldns_buffer *buffer, size_t capacity)
        
        ldns_buffer_invariant(buffer);
        assert(buffer->_position <= capacity);
+       assert(!buffer->_fixed);
 
        data = (uint8_t *) LDNS_XREALLOC(buffer->_data, uint8_t, capacity);
        if (!data) {
@@ -79,7 +80,6 @@ bool
 ldns_buffer_reserve(ldns_buffer *buffer, size_t amount)
 {
        ldns_buffer_invariant(buffer);
-       assert(!buffer->_fixed);
        if (buffer->_capacity < buffer->_position + amount) {
                size_t new_capacity = buffer->_capacity * 3 / 2;
 
index c7623f36cda5fb9db536b4dc85ce81cb87a56d07..07754b19f8d17ed0e0e833f8541e01d205acaae3 100644 (file)
@@ -24,6 +24,9 @@
 #endif
 #endif /* HAVE_SSL */
 
+#define LDNS_SIGN_WITH_ZONEMD ( LDNS_SIGN_WITH_ZONEMD_SIMPLE_SHA384 \
+                              | LDNS_SIGN_WITH_ZONEMD_SIMPLE_SHA512 )
+
 ldns_rr *
 ldns_create_empty_rrsig(const ldns_rr_list *rrset,
                         const ldns_key *current_key)
@@ -1365,6 +1368,8 @@ ldns_dnssec_zone_sign(ldns_dnssec_zone *zone,
        return ldns_dnssec_zone_sign_flg(zone, new_rrs, key_list, func, arg, 0);
 }
 
+ldns_status dnssec_zone_equip_zonemd(
+               ldns_dnssec_zone *zone, int flags, ldns_key_list *key_list);
 ldns_status
 ldns_dnssec_zone_sign_flg(ldns_dnssec_zone *zone,
                                  ldns_rr_list *new_rrs,
@@ -1374,17 +1379,33 @@ ldns_dnssec_zone_sign_flg(ldns_dnssec_zone *zone,
                                  int flags)
 {
        ldns_status result = LDNS_STATUS_OK;
+       ldns_dnssec_rrsets zonemd_rrset;
+       bool zonemd_added = false;
 
        if (!zone || !new_rrs || !key_list) {
                return LDNS_STATUS_ERR;
        }
-
+       if (flags & LDNS_SIGN_WITH_ZONEMD) {
+               ldns_dnssec_rrsets **rrsets_ref = &zone->soa->rrsets;
+
+               while (*rrsets_ref
+                  && (*rrsets_ref)->type < LDNS_RR_TYPE_ZONEMD)
+                       rrsets_ref = &(*rrsets_ref)->next;
+               if (!*rrsets_ref
+               ||  (*rrsets_ref)->type > LDNS_RR_TYPE_ZONEMD) {
+                       zonemd_rrset.rrs = NULL;
+                       zonemd_rrset.type = LDNS_RR_TYPE_ZONEMD;
+                       zonemd_rrset.signatures = NULL;
+                       zonemd_rrset.next = *rrsets_ref;
+                       *rrsets_ref = &zonemd_rrset;
+                       zonemd_added = true;
+               }
+       }
        /* zone is already sorted */
        result = ldns_dnssec_zone_mark_glue(zone);
        if (result != LDNS_STATUS_OK) {
                return result;
        }
-
        /* check whether we need to add nsecs */
        if (zone->names && !((ldns_dnssec_name *)zone->names->root->data)->nsec) {
                result = ldns_dnssec_zone_create_nsecs(zone, new_rrs);
@@ -1392,7 +1413,6 @@ ldns_dnssec_zone_sign_flg(ldns_dnssec_zone *zone,
                        return result;
                }
        }
-
        result = ldns_dnssec_zone_create_rrsigs_flg(zone,
                                        new_rrs,
                                        key_list,
@@ -1400,7 +1420,18 @@ ldns_dnssec_zone_sign_flg(ldns_dnssec_zone *zone,
                                        arg,
                                        flags);
 
-       return result;
+       if (zonemd_added) {
+               ldns_dnssec_rrsets **rrsets_ref
+                   = &zone->soa->rrsets;
+
+               while (*rrsets_ref
+                  && (*rrsets_ref)->type < LDNS_RR_TYPE_ZONEMD)
+                       rrsets_ref = &(*rrsets_ref)->next;
+               *rrsets_ref = zonemd_rrset.next;
+       }
+       return flags & LDNS_SIGN_WITH_ZONEMD
+            ? dnssec_zone_equip_zonemd(zone, flags, key_list)
+            : result;
 }
 
 ldns_status
@@ -1436,6 +1467,8 @@ ldns_dnssec_zone_sign_nsec3_flg_mkmap(ldns_dnssec_zone *zone,
 {
        ldns_rr *nsec3, *nsec3param;
        ldns_status result = LDNS_STATUS_OK;
+       bool zonemd_added = false;
+       ldns_dnssec_rrsets zonemd_rrset;
 
        /* zone is already sorted */
        result = ldns_dnssec_zone_mark_glue(zone);
@@ -1481,6 +1514,23 @@ ldns_dnssec_zone_sign_nsec3_flg_mkmap(ldns_dnssec_zone *zone,
                                }
                                ldns_rr_list_push_rr(new_rrs, nsec3param);
                        }
+                       if (signflags & LDNS_SIGN_WITH_ZONEMD) {
+                               ldns_dnssec_rrsets **rrsets_ref
+                                   = &zone->soa->rrsets;
+
+                               while (*rrsets_ref
+                                  && (*rrsets_ref)->type < LDNS_RR_TYPE_ZONEMD)
+                                       rrsets_ref = &(*rrsets_ref)->next;
+                               if (!*rrsets_ref
+                               ||  (*rrsets_ref)->type > LDNS_RR_TYPE_ZONEMD) {
+                                       zonemd_rrset.rrs = NULL;
+                                       zonemd_rrset.type = LDNS_RR_TYPE_ZONEMD;
+                                       zonemd_rrset.signatures = NULL;
+                                       zonemd_rrset.next = *rrsets_ref;
+                                       *rrsets_ref = &zonemd_rrset;
+                                       zonemd_added = true;
+                               }
+                       }
                        result = ldns_dnssec_zone_create_nsec3s_mkmap(zone,
                                                                                        new_rrs,
                                                                                        algorithm,
@@ -1489,6 +1539,15 @@ ldns_dnssec_zone_sign_nsec3_flg_mkmap(ldns_dnssec_zone *zone,
                                                                                        salt_length,
                                                                                        salt,
                                                                                        map);
+                       if (zonemd_added) {
+                               ldns_dnssec_rrsets **rrsets_ref
+                                   = &zone->soa->rrsets;
+
+                               while (*rrsets_ref
+                                  && (*rrsets_ref)->type < LDNS_RR_TYPE_ZONEMD)
+                                       rrsets_ref = &(*rrsets_ref)->next;
+                               *rrsets_ref = zonemd_rrset.next;
+                       }
                        if (result != LDNS_STATUS_OK) {
                                return result;
                        }
@@ -1501,8 +1560,12 @@ ldns_dnssec_zone_sign_nsec3_flg_mkmap(ldns_dnssec_zone *zone,
                                                arg,
                                                signflags);
        }
+       if (result || !zone->names)
+               return result;
 
-       return result;
+       return signflags & LDNS_SIGN_WITH_ZONEMD
+            ? dnssec_zone_equip_zonemd(zone, signflags, key_list)
+            : result;
 }
 
 ldns_status
index e089754ddbc0e08753d0ac1c6094ba372b37584e..c307510dbcbf05465e27ad64dda16d793564cc75 100644 (file)
@@ -1201,3 +1201,653 @@ ldns_dnssec_zone_is_nsec3_optout(const ldns_dnssec_zone* zone)
        }
        return false;
 }
+
+/*
+ * Stuff for calculating and verifying zone digests
+ */
+static ldns_status
+rr_list2dnssec_rrs(ldns_rr_list *rr_list, ldns_dnssec_rrs **rrs)
+{
+       ldns_rr *rr = NULL;
+
+       if (!rr_list || !rrs)
+               return LDNS_STATUS_NULL;
+
+       if (ldns_rr_list_rr_count(rr_list) == 0)
+               return LDNS_STATUS_OK;
+
+       if (!*rrs) {
+               if (!(*rrs = ldns_dnssec_rrs_new()))
+                       return LDNS_STATUS_MEM_ERR;
+               (*rrs)->rr = ldns_rr_list_pop_rr(rr_list);
+
+       }
+       while ((rr = ldns_rr_list_pop_rr(rr_list))) {
+               ldns_status st;
+              
+               if ((st = ldns_dnssec_rrs_add_rr(*rrs, rr))) {
+                       ldns_rr_list_push_rr(rr_list, rr);
+                       return st;
+               }
+       }
+       return LDNS_STATUS_OK;
+}
+
+typedef enum dnssec_zone_rr_iter_state {
+         DNSSEC_ZONE_RR_ITER_LT_RRSIG
+       , DNSSEC_ZONE_RR_ITER_RRSIGs_NO_NSEC
+       , DNSSEC_ZONE_RR_ITER_REST
+       , DNSSEC_ZONE_RR_ITER_RRSIGs_NSEC
+       , DNSSEC_ZONE_RR_ITER_RRSIGs_NSEC_REST
+       , DNSSEC_ZONE_RR_ITER_NSEC3
+       , DNSSEC_ZONE_RR_ITER_FINI
+} dnssec_zone_rr_iter_state;
+
+typedef struct dnssec_zone_rr_iter {
+       ldns_dnssec_zone         *zone;
+       ldns_rbnode_t            *node;
+       ldns_dnssec_name         *name;
+       ldns_dnssec_rrsets       *rrsets;
+       ldns_dnssec_rrs          *rrs;
+       ldns_dnssec_rrsets       *rrsets4rrsigs;
+       ldns_rbnode_t            *nsec3_node;
+       ldns_dnssec_name         *nsec3_name;
+       dnssec_zone_rr_iter_state state;
+} dnssec_zone_rr_iter;
+
+INLINE void
+dnssec_zone_rr_iter_set_state_for_next_name(dnssec_zone_rr_iter *i)
+{
+       if(!i->name) {
+               if (!i->nsec3_name)
+                       i->state = DNSSEC_ZONE_RR_ITER_FINI;
+               else {
+                       i->rrs = i->nsec3_name->nsec_signatures;
+                       i->state = DNSSEC_ZONE_RR_ITER_NSEC3;
+               }
+       } else if (!i->nsec3_name) {
+               i->rrsets = i->name->rrsets;
+               i->state = DNSSEC_ZONE_RR_ITER_LT_RRSIG;
+
+       } else if (ldns_dname_compare( ldns_rr_owner(i->nsec3_name->nsec)
+                                    , (ldns_rdf *)i->node->key) < 0) {
+               i->rrs = i->nsec3_name->nsec_signatures;
+               i->state = DNSSEC_ZONE_RR_ITER_NSEC3;
+       } else {
+               i->rrsets = i->name->rrsets;
+               i->state = DNSSEC_ZONE_RR_ITER_LT_RRSIG;
+       }
+}
+
+/**
+ * Iterate over the RR's in the ldns_dnssec_zone in canonical order.
+ * There are three possible paths through the RR's in a ldns_dnssec_name.
+ * 
+ * 1. There is no NSEC:
+ *
+ *    1.1. All the RRs in the name->rrsets with type < RRSIG,
+ *         state: DNSSEC_ZONE_RR_ITER_LT_RRSIG
+ *
+ *    1.2. Then all the RRSIGs from name->rrsets (likely none)
+ *         state: DNSSEC_ZONE_RR_ITER_RRSIGs_NO_NSEC
+ *
+ *    1.3. Finally the remaining RRs in name->rrsets (type > RRSIG)
+ *         state: DNSSEC_ZONE_RR_ITER_REST
+ *
+ *
+ * 2. There is a NSEC of type NSEC with this name:
+ *
+ *    2.1. All the RRs in the name->rrsets with type < RRSIG,
+ *         state: DNSSEC_ZONE_RR_ITER_LT_RRSIG
+ *
+ *    2.2. Then all the RRSIGs from name->rrsets with type < NSEC
+ *         state: DNSSEC_ZONE_RR_ITER_RRSIGs_NO_NSEC
+ *
+ *    2.3. Then the signatures of the NSEC RR, followed by
+ *         the signatures of the remaining name->rrsets (type > NSEC),
+ *         followed by the NSEC rr.
+ *         state: DNSSEC_ZONE_RR_ITER_RRSIGs_NO_NSEC
+ *
+ *    2.4. Finally the remaining RRs in name->rrsets (type > RRSIG)
+ *         state: DNSSEC_ZONE_RR_ITER_REST
+ *
+ *
+ * 3. There is a NSEC of type NSEC3 for this name:
+ *
+ *    3.1. If the NSEC3 name is before the name for other RRsets in the zone,
+ *         Then all signatures of the NSEC3 RR, followed by the NSEC3
+ *         state: DNSSEC_ZONE_RR_ITER_NSEC3
+ *
+ *         otherwise follow path for "no NSEC" for the name for other RRsets
+ */
+static ldns_rr *
+dnssec_zone_rr_iter_next(dnssec_zone_rr_iter *i)
+{
+       ldns_rr *nsec3;
+
+       for (;;) {
+               if (i->rrs) {
+                       ldns_rr *rr = i->rrs->rr;
+                       i->rrs = i->rrs->next;
+                       return rr;
+               }
+               switch (i->state) {
+               case DNSSEC_ZONE_RR_ITER_LT_RRSIG:
+                       if (i->rrsets
+                       &&  i->rrsets->type < LDNS_RR_TYPE_RRSIG) {
+
+                               i->rrs = i->rrsets->rrs;
+                               i->rrsets = i->rrsets->next;
+                               break;
+                       }
+                       i->rrsets4rrsigs = i->name->rrsets;
+                       if (i->name->nsec && ldns_rr_get_type(i->name->nsec)
+                                                      == LDNS_RR_TYPE_NSEC) {
+
+                               i->state = DNSSEC_ZONE_RR_ITER_RRSIGs_NSEC;
+                               break;
+                       }
+                       i->state = DNSSEC_ZONE_RR_ITER_RRSIGs_NO_NSEC;
+                       /* fallthrough */
+
+               case DNSSEC_ZONE_RR_ITER_RRSIGs_NO_NSEC:
+                       if (i->rrsets4rrsigs) {
+                               i->rrs = i->rrsets4rrsigs->signatures;
+                               i->rrsets4rrsigs = i->rrsets4rrsigs->next;
+                               break;
+                       }
+                       i->state = DNSSEC_ZONE_RR_ITER_REST;
+                       /* fallthrough */
+
+               case DNSSEC_ZONE_RR_ITER_REST:
+                       if (i->rrsets) {
+                               i->rrs = i->rrsets->rrs;
+                               i->rrsets = i->rrsets->next;
+                               break;
+                       }
+                       /* next name */
+                       i->node = ldns_rbtree_next(i->node);
+                       i->name = i->node == LDNS_RBTREE_NULL ? NULL
+                               : (ldns_dnssec_name *)i->node->data;
+
+                       dnssec_zone_rr_iter_set_state_for_next_name(i);
+                       break;
+
+               case DNSSEC_ZONE_RR_ITER_RRSIGs_NSEC:
+                       if (i->rrsets4rrsigs
+                       &&  i->rrsets4rrsigs->type < LDNS_RR_TYPE_NSEC) {
+
+                               i->rrs = i->rrsets4rrsigs->signatures;
+                               i->rrsets4rrsigs = i->rrsets4rrsigs->next;
+                               break;
+                       }
+                       i->state = DNSSEC_ZONE_RR_ITER_RRSIGs_NSEC_REST;
+                       i->rrs = i->name->nsec_signatures;
+                       break;
+
+               case DNSSEC_ZONE_RR_ITER_RRSIGs_NSEC_REST:
+                       if (i->rrsets4rrsigs) {
+                               i->rrs = i->rrsets4rrsigs->signatures;
+                               i->rrsets4rrsigs = i->rrsets4rrsigs->next;
+                               break;
+                       }
+                       i->state = DNSSEC_ZONE_RR_ITER_REST;
+                       return i->name->nsec;
+
+               case DNSSEC_ZONE_RR_ITER_NSEC3:
+                       nsec3 = i->nsec3_name->nsec;
+
+                       /* next nsec3 */
+                       do {
+                               i->nsec3_node
+                                   = ldns_rbtree_next(i->nsec3_node);
+                               i->nsec3_name
+                                   = i->nsec3_node == LDNS_RBTREE_NULL ? NULL
+                                   : (ldns_dnssec_name*)i->nsec3_node->data;
+
+                               /* names for glue can be in the hashed_names 
+                                * tree, but will not have a NSEC3 
+                                */
+                       } while (i->nsec3_name && !i->nsec3_name->nsec);
+
+                       dnssec_zone_rr_iter_set_state_for_next_name(i);
+                       return nsec3;
+
+               case DNSSEC_ZONE_RR_ITER_FINI:
+                       return NULL;
+               }
+       }
+}
+
+static ldns_rr *
+dnssec_zone_rr_iter_first(dnssec_zone_rr_iter *i, ldns_dnssec_zone *zone)
+{
+       if (!i || !zone)
+               return NULL;
+
+       memset(i, 0, sizeof(*i));
+       i->zone = zone;
+
+       i->node = ldns_rbtree_first(zone->names);
+       i->name = i->node == LDNS_RBTREE_NULL ? NULL
+               : (ldns_dnssec_name *)i->node->data;
+
+       if (zone->hashed_names) {
+               do {
+                       i->nsec3_node = ldns_rbtree_first(zone->hashed_names);
+                       i->nsec3_name = i->nsec3_node == LDNS_RBTREE_NULL ?NULL
+                                     : (ldns_dnssec_name*)i->nsec3_node->data;
+               } while (i->nsec3_name && !i->nsec3_name->nsec);
+       }
+       dnssec_zone_rr_iter_set_state_for_next_name(i);
+       return dnssec_zone_rr_iter_next(i);
+}
+
+enum enum_zonemd_scheme {
+        ZONEMD_SCHEME_FIRST  = 1,
+        ZONEMD_SCHEME_SIMPLE = 1,
+        ZONEMD_SCHEME_LAST   = 1
+};
+typedef enum enum_zonemd_scheme zonemd_scheme;
+
+enum enum_zonemd_hash {
+        ZONEMD_HASH_FIRST  = 1,
+        ZONEMD_HASH_SHA384 = 1,
+        ZONEMD_HASH_SHA512 = 2,
+        ZONEMD_HASH_LAST   = 2
+};
+typedef enum enum_zonemd_hash zonemd_hash;
+
+struct struct_zone_digester {
+        ldns_sha384_CTX sha384_CTX;
+        ldns_sha512_CTX sha512_CTX;
+        unsigned simple_sha384 : 1;
+        unsigned simple_sha512 : 1;
+};
+typedef struct struct_zone_digester zone_digester;
+
+INLINE bool zone_digester_set(zone_digester *zd)
+{ return zd && (zd->simple_sha384 || zd->simple_sha512); }
+
+INLINE void zone_digester_init(zone_digester *zd)
+{ memset(zd, 0, sizeof(*zd)); }
+
+static ldns_status
+zone_digester_add(zone_digester *zd, zonemd_scheme scheme, zonemd_hash hash)
+{
+       if (!zd)
+               return LDNS_STATUS_NULL;
+
+       switch (scheme) {
+       case ZONEMD_SCHEME_SIMPLE:
+               switch (hash) {
+               case ZONEMD_HASH_SHA384:
+                       ldns_sha384_init(&zd->sha384_CTX);
+                       zd->simple_sha384 = 1;
+                       break;
+
+               case ZONEMD_HASH_SHA512:
+                       ldns_sha512_init(&zd->sha512_CTX);
+                       zd->simple_sha512 = 1;
+                       break;
+               default:
+                       return LDNS_STATUS_ZONEMD_UNKNOWN_HASH;
+               }
+               break;
+       default:
+               return LDNS_STATUS_ZONEMD_UNKNOWN_SCHEME;
+       }
+       return LDNS_STATUS_OK;
+}
+
+static ldns_status
+zone_digester_update(zone_digester *zd, ldns_rr *rr)
+{
+       uint8_t data[65536];
+       ldns_buffer buf;
+       ldns_status st;
+       
+       buf._data = data;
+       buf._position = 0;
+       buf._limit = sizeof(data);
+       buf._capacity = sizeof(data);
+       buf._fixed = 1;
+       buf._status = LDNS_STATUS_OK;
+
+       if ((st = ldns_rr2buffer_wire(&buf, rr, LDNS_SECTION_ANSWER)))
+               return st;
+
+       if (zd->simple_sha384)
+               ldns_sha384_update(&zd->sha384_CTX, data, buf._position);
+
+       if (zd->simple_sha512)
+               ldns_sha512_update(&zd->sha512_CTX, data, buf._position);
+
+       return LDNS_STATUS_OK;
+}
+
+INLINE ldns_rr *
+new_zonemd(ldns_rr *soa, zonemd_hash hash)
+{
+       ldns_rr  *rr     = NULL;
+       uint8_t  *data   = NULL;
+       ldns_rdf *rdf;
+       size_t    md_len = hash == ZONEMD_HASH_SHA384
+                        ? LDNS_SHA384_DIGEST_LENGTH
+                        : LDNS_SHA512_DIGEST_LENGTH;
+
+       if (!(rr = ldns_rr_new_frm_type(LDNS_RR_TYPE_ZONEMD)))
+               return NULL;
+
+       if (!(rdf = ldns_rdf_clone(ldns_rr_owner(soa))))
+               goto error;
+
+       ldns_rr_set_owner(rr, rdf);
+       ldns_rr_set_class(rr, ldns_rr_get_class(soa));
+       ldns_rr_set_ttl(rr, ldns_rr_ttl(soa));
+
+       if (!(rdf = ldns_rdf_clone(ldns_rr_rdf(soa, 2))))
+               goto error;
+       ldns_rr_set_rdf(rr, rdf, 0);
+
+       if (!(rdf = ldns_native2rdf_int8(LDNS_RDF_TYPE_INT8, 1)))
+               goto error;
+       ldns_rr_set_rdf(rr, rdf, 1);
+
+       if (!(rdf = ldns_native2rdf_int8(LDNS_RDF_TYPE_INT8, hash)))
+               goto error;
+       ldns_rr_set_rdf(rr, rdf, 2);
+
+       if (!(data = LDNS_XMALLOC(uint8_t, md_len)))
+               goto error;
+
+       if (!(rdf = ldns_rdf_new(LDNS_RDF_TYPE_HEX, md_len, data)))
+               goto error;
+       ldns_rr_set_rdf(rr, rdf, 3);
+
+       return rr;
+error:
+       if (data)
+               LDNS_FREE(data);
+       ldns_rr_free(rr);
+       return NULL;
+}
+
+static ldns_rr_list *
+zone_digester_export(
+               zone_digester *zd, ldns_rr *soa, ldns_status *ret_st)
+{
+       ldns_status st = LDNS_STATUS_OK;
+       ldns_rr_list *rr_list = NULL;
+       ldns_rr *sha384 = NULL;
+       ldns_rr *sha512 = NULL;
+
+       if (!zd || !soa)
+               st = LDNS_STATUS_NULL;
+
+       else if (ldns_rr_get_type(soa) != LDNS_RR_TYPE_SOA
+            ||  ldns_rr_rd_count(soa) < 3)
+               st = LDNS_STATUS_ZONEMD_INVALID_SOA;
+
+       else if (!(rr_list = ldns_rr_list_new()))
+               st = LDNS_STATUS_MEM_ERR;
+
+       else if (zd->simple_sha384
+            && !(sha384 = new_zonemd(soa, ZONEMD_HASH_SHA384)))
+               st = LDNS_STATUS_MEM_ERR;
+
+       else if (zd->simple_sha512
+            && !(sha512 = new_zonemd(soa, ZONEMD_HASH_SHA512)))
+               st = LDNS_STATUS_MEM_ERR;
+
+       else if (zd->simple_sha384
+            && !ldns_rr_list_push_rr(rr_list, sha384))
+               st = LDNS_STATUS_MEM_ERR;
+
+       else if (zd->simple_sha512
+            && !ldns_rr_list_push_rr(rr_list, sha512)) {
+               if (zd->simple_sha384)
+                       sha384 = NULL; /* deleted by ldns_rr_list_deep_free */
+               st = LDNS_STATUS_MEM_ERR;
+
+       } else {
+               if (sha384)
+                       ldns_sha384_final( ldns_rdf_data(ldns_rr_rdf(sha384,3))
+                                        , &zd->sha384_CTX);
+               if (sha512)
+                       ldns_sha512_final( ldns_rdf_data(ldns_rr_rdf(sha512,3))
+                                        , &zd->sha512_CTX);
+               return rr_list;
+       }
+       if (ret_st)
+               *ret_st = st;
+       if (sha384)
+               ldns_rr_free(sha384);
+       if (sha512)
+               ldns_rr_free(sha512);
+       if (rr_list)
+               ldns_rr_list_deep_free(rr_list);
+       return NULL;
+}
+
+static ldns_status
+ldns_digest_zone(ldns_dnssec_zone *zone, zone_digester *zd)
+{
+       ldns_status st = LDNS_STATUS_OK;
+       dnssec_zone_rr_iter rr_iter;
+       ldns_rr *rr;
+       ldns_rdf *apex_name; /* name of zone apex */
+
+       if (!zone || !zd || !zone->soa || !zone->soa->name)
+               return LDNS_STATUS_NULL;
+
+       apex_name = zone->soa->name;
+       for ( rr = dnssec_zone_rr_iter_first(&rr_iter, zone)
+           ; rr && !st
+           ; rr = dnssec_zone_rr_iter_next(&rr_iter)) {
+               /* Skip apex ZONEMD RRs */
+               if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_ZONEMD
+               && !ldns_dname_compare(ldns_rr_owner(rr), apex_name))
+                       continue;
+               /* Skip RRSIGs for apex ZONEMD RRs */
+               if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_RRSIG
+               &&  LDNS_RR_TYPE_ZONEMD == ldns_rdf2rr_type(
+                               ldns_rr_rrsig_typecovered(rr))
+               && !ldns_dname_compare(ldns_rr_owner(rr), apex_name))
+                       continue;
+               st = zone_digester_update(zd, rr);
+       }
+       return st;
+}
+
+ldns_status
+ldns_dnssec_zone_verify_zonemd(ldns_dnssec_zone *zone)
+{
+       ldns_dnssec_rrsets *zonemd, *soa;
+       zone_digester zd;
+       ldns_dnssec_rrs *rrs;
+       ldns_rr *soa_rr;
+       ldns_status st;
+       uint8_t simple_sha384[LDNS_SHA384_DIGEST_LENGTH];
+       uint8_t simple_sha512[LDNS_SHA512_DIGEST_LENGTH];
+       size_t valid_zonemds;
+
+       if (!zone)
+               return LDNS_STATUS_NULL;
+
+       zonemd = ldns_dnssec_zone_find_rrset(
+                       zone, zone->soa->name, LDNS_RR_TYPE_ZONEMD);
+       if (!zonemd) {
+               ldns_rbnode_t *nsec3_node;
+
+               /* we need proof of non-existance for ZONEMD at the apex */
+               if (zone->soa->nsec) {
+                       if (ldns_nsec_bitmap_covers_type(ldns_nsec_get_bitmap(
+                                                       zone->soa->nsec),
+                                               LDNS_RR_TYPE_ZONEMD))
+                               return LDNS_STATUS_NO_ZONEMD;
+
+               } else if (!zone->soa->hashed_name || !zone->hashed_names)
+                       return LDNS_STATUS_NO_ZONEMD;
+
+               else if (LDNS_RBTREE_NULL == 
+                   (nsec3_node = ldns_rbtree_search( zone->hashed_names
+                                                   , zone->soa->hashed_name)))
+                       return LDNS_STATUS_NO_ZONEMD;
+               else {
+                       ldns_dnssec_name *nsec3
+                               = (ldns_dnssec_name *)nsec3_node->data;
+                       if (ldns_nsec_bitmap_covers_type(ldns_nsec_get_bitmap(
+                                                       nsec3->nsec),
+                                               LDNS_RR_TYPE_ZONEMD))
+                               return LDNS_STATUS_NO_ZONEMD;
+               }
+               /* ZONEMD at apex does really not exist */
+               return LDNS_STATUS_OK;
+       }
+       soa = ldns_dnssec_zone_find_rrset(
+                       zone, zone->soa->name, LDNS_RR_TYPE_SOA);
+       if (!soa || !soa->rrs || !soa->rrs->rr)
+               return LDNS_STATUS_ZONEMD_INVALID_SOA;
+
+       soa_rr = soa->rrs->rr;
+       if (ldns_rr_get_type(soa_rr) != LDNS_RR_TYPE_SOA
+       ||  ldns_rr_rd_count(soa_rr) < 3)
+               return LDNS_STATUS_ZONEMD_INVALID_SOA;
+
+       zone_digester_init(&zd);
+       for (rrs = zonemd->rrs; rrs; rrs = rrs->next) {
+               if (!rrs->rr
+               ||  ldns_rr_get_type(rrs->rr) != LDNS_RR_TYPE_ZONEMD
+               ||  ldns_rr_rd_count(rrs->rr) < 4)
+                       continue;
+
+               /* serial should match SOA's serial */
+               if (ldns_rdf2native_int32(ldns_rr_rdf(soa_rr, 2))
+                   != ldns_rdf2native_int32(ldns_rr_rdf(rrs->rr, 0)))
+                       continue;
+               
+               /* Add (scheme, hash) to digester */
+               zone_digester_add(&zd,
+                               ldns_rdf2native_int8(ldns_rr_rdf(rrs->rr, 1)),
+                               ldns_rdf2native_int8(ldns_rr_rdf(rrs->rr, 2)));
+       }
+       if (!zone_digester_set(&zd))
+               return LDNS_STATUS_NO_VALID_ZONEMD;
+
+       if ((st = ldns_digest_zone(zone, &zd)))
+               return st;
+
+       if (zd.simple_sha384)
+               ldns_sha384_final(simple_sha384, &zd.sha384_CTX);
+       if (zd.simple_sha512)
+               ldns_sha512_final(simple_sha512, &zd.sha512_CTX);
+
+       valid_zonemds = 0;
+       for (rrs = zonemd->rrs; rrs; rrs = rrs->next) {
+               if (!rrs->rr
+               ||  ldns_rr_get_type(rrs->rr) != LDNS_RR_TYPE_ZONEMD
+               ||  ldns_rr_rd_count(rrs->rr) < 4)
+                       continue;
+
+               /* serial should match SOA's serial */
+               if (ldns_rdf2native_int32(ldns_rr_rdf(soa_rr, 2))
+                   != ldns_rdf2native_int32(ldns_rr_rdf(rrs->rr, 0)))
+                       continue;
+               
+               if (ZONEMD_SCHEME_SIMPLE !=
+                               ldns_rdf2native_int8(ldns_rr_rdf(rrs->rr, 1)))
+                       continue;
+
+               if (ZONEMD_HASH_SHA384
+                               == ldns_rdf2native_int8(ldns_rr_rdf(rrs->rr,2))
+               &&  LDNS_SHA384_DIGEST_LENGTH
+                               == ldns_rdf_size(ldns_rr_rdf(rrs->rr, 3))
+               &&  memcmp( simple_sha384
+                         , ldns_rdf_data(ldns_rr_rdf(rrs->rr, 3))
+                         , LDNS_SHA384_DIGEST_LENGTH) == 0)
+
+                       valid_zonemds += 1;
+
+               if (ZONEMD_HASH_SHA512
+                               == ldns_rdf2native_int8(ldns_rr_rdf(rrs->rr,2))
+               &&  LDNS_SHA512_DIGEST_LENGTH
+                               == ldns_rdf_size(ldns_rr_rdf(rrs->rr, 3))
+               &&  memcmp( simple_sha512
+                         , ldns_rdf_data(ldns_rr_rdf(rrs->rr, 3))
+                         , LDNS_SHA512_DIGEST_LENGTH) == 0)
+
+                       valid_zonemds += 1;
+       }
+       return valid_zonemds ? LDNS_STATUS_OK : LDNS_STATUS_NO_VALID_ZONEMD;
+}
+
+#ifdef HAVE_SSL
+ldns_status
+dnssec_zone_equip_zonemd(
+               ldns_dnssec_zone *zone, int signflags, ldns_key_list *key_list)
+{
+       ldns_status st = LDNS_STATUS_OK;
+       zone_digester zd;
+       ldns_rr_list *zonemd_rr_list = NULL;
+       ldns_rr_list *zonemd_rrsigs = NULL;
+       ldns_dnssec_rrsets *soa_rrset;
+       ldns_rr *soa_rr = NULL;
+       ldns_dnssec_rrsets **rrset_ref;
+       ldns_dnssec_rrsets *zonemd_rrset;
+
+       zone_digester_init(&zd);
+       if (signflags & LDNS_SIGN_WITH_ZONEMD_SIMPLE_SHA384)
+               zone_digester_add(&zd, ZONEMD_SCHEME_SIMPLE
+                                    , ZONEMD_HASH_SHA384);
+
+       if (signflags & LDNS_SIGN_WITH_ZONEMD_SIMPLE_SHA512)
+               zone_digester_add(&zd, ZONEMD_SCHEME_SIMPLE
+                                    , ZONEMD_HASH_SHA512);
+
+       if ((st = ldns_digest_zone(zone, &zd)))
+               return st;
+
+       soa_rrset = ldns_dnssec_zone_find_rrset(
+                       zone, zone->soa->name, LDNS_RR_TYPE_SOA);
+       if (!soa_rrset || !soa_rrset->rrs || !soa_rrset->rrs->rr)
+               return LDNS_STATUS_ZONEMD_INVALID_SOA;
+       soa_rr = soa_rrset->rrs->rr;
+
+       if (!(zonemd_rr_list = zone_digester_export(&zd, soa_rr, &st)))
+               return st;
+       
+       /* - replace or add ZONEMD rrset */
+       rrset_ref = &zone->soa->rrsets; /* scan rrsets at apex */
+       while (*rrset_ref && (*rrset_ref)->type < LDNS_RR_TYPE_ZONEMD)
+               rrset_ref = &(*rrset_ref)->next;
+       if (*rrset_ref && (*rrset_ref)->type == LDNS_RR_TYPE_ZONEMD) {
+               /* reuse zonemd rrset */
+               zonemd_rrset = *rrset_ref;
+               ldns_dnssec_rrs_free(zonemd_rrset->rrs);
+               zonemd_rrset->rrs = NULL;
+               ldns_dnssec_rrs_free(zonemd_rrset->signatures);
+               zonemd_rrset->signatures = NULL;
+       } else {
+               /* insert zonemd rrset */
+               zonemd_rrset = ldns_dnssec_rrsets_new();
+               if (!zonemd_rrset) {
+                       ldns_rr_list_deep_free(zonemd_rr_list);
+                       return LDNS_STATUS_MEM_ERR;
+               }
+               zonemd_rrset->type = LDNS_RR_TYPE_ZONEMD;
+               zonemd_rrset->next = *rrset_ref;
+               *rrset_ref = zonemd_rrset;
+       }
+       if ((zonemd_rrsigs = ldns_sign_public(zonemd_rr_list, key_list)))
+               st = rr_list2dnssec_rrs(  zonemd_rrsigs
+                                      , &zonemd_rrset->signatures);
+       if (!st)
+               st = rr_list2dnssec_rrs(zonemd_rr_list, &zonemd_rrset->rrs);
+       ldns_rr_list_deep_free(zonemd_rr_list);
+       ldns_rr_list_deep_free(zonemd_rrsigs);
+       return st;
+}
+
+#endif /* HAVE_SSL */
+
diff --git a/error.c b/error.c
index 7943017a9047632832a30b3d8b9b0d2a5d084889..29e167e33fdd6b4466b3eb857262672d0cf643b8 100644 (file)
--- a/error.c
+++ b/error.c
@@ -157,6 +157,15 @@ ldns_lookup_table ldns_error_str[] = {
                "X509_STORE_CTX_set0_dane() functions within OpenSSL >= 1.1.0 "
                "to be able to verify the DANE-TA usage type." },
 #endif
+       { LDNS_STATUS_ZONEMD_UNKNOWN_SCHEME, "Unknown ZONEMD <scheme>" },
+       { LDNS_STATUS_ZONEMD_UNKNOWN_HASH, "Unknown ZONEMD hash algorithm" },
+       { LDNS_STATUS_ZONEMD_INVALID_SOA,
+               "Missing or invalid SOA to associate with ZONEMD RR" },
+       { LDNS_STATUS_NO_ZONEMD,
+               "NSEC and NSEC3 RRs say that ZONEMD exists, "
+               "but it is not present in the zone" },
+       { LDNS_STATUS_NO_VALID_ZONEMD,
+               "No ZONEMD matching the zone data was found" },
        { 0, NULL }
 };
 
index 9387876c2840f741ed2fef16616dbe1d61a6f38b..0b17ea7ad7da671cf7d840b7a9f8c2fcc4702d80 100644 (file)
@@ -564,45 +564,39 @@ shutdown_openssl ( ENGINE * const e )
 }
 #endif
 
-static bool ldns_rr_list_push_rrs(ldns_rr_list *rr_list, ldns_dnssec_rrs *rrs)
+int str2zonemd_signflag(const char *str, const char **reason)
 {
-       while (rrs && ldns_rr_list_push_rr(rr_list, rrs->rr))
-               rrs = rrs->next;
-       return !rrs;
-}
-
-static ldns_rr_list *ldns_dnssec_zone2rr_list(ldns_dnssec_zone *zone)
-{
-       ldns_rr_list *rr_list = zone ? ldns_rr_list_new() : NULL;
-       ldns_rbnode_t *node;
-       ldns_dnssec_name *name;
-       ldns_dnssec_rrsets *rrsets;
-
-       if (!rr_list)
-               return NULL;
-
-       /* Walking the zone->names rbtree starts at the apex */
-       node = ldns_rbtree_first(zone->names);
-       while (node != LDNS_RBTREE_NULL) {
-               name = (ldns_dnssec_name *) node->data;
-               for (rrsets = name->rrsets; rrsets; rrsets = rrsets->next) {
-                       if (!ldns_rr_list_push_rrs(rr_list, rrsets->rrs))
-                               goto error;
-                       if (!ldns_rr_list_push_rrs(rr_list, rrsets->signatures))
-                               goto error;
+       char *colon;
+
+       static const char *reasons[] = {
+             "Unknown <scheme>, should be \"simple\""
+           , "Syntax error in <hash>, should be \"sha384\" or \"sha512\""
+           , "Unknown <hash>, should be \"sha384\" or \"sha512\""
+       };
+
+       if (!str)
+               return LDNS_STATUS_NULL;
+
+       if ((colon = strchr(str, ':'))) {
+               if ((colon - str != 1 || str[0] != '1')
+               &&  (colon - str != 6 || strncasecmp(str, "simple", 6))) {
+                       if (reason) *reason = reasons[0];
+                       return 0;
                }
-               if (name->nsec) {
-                       if (!ldns_rr_list_push_rr(rr_list, name->nsec))
-                               goto error;
-                       if (!ldns_rr_list_push_rrs(rr_list, name->nsec_signatures))
-                               goto error;
+
+               if (strchr(colon + 1, ':')) {
+                       if (reason) *reason = reasons[1];
+                       return 0;
                }
-               node = ldns_rbtree_next(node);
+               return str2zonemd_signflag(colon + 1, reason);
        }
-       return rr_list;
-error:
-       ldns_rr_list_free(rr_list);
-       return NULL;
+       if (!strcasecmp(str, "1") || !strcasecmp(str, "sha384"))
+               return LDNS_SIGN_WITH_ZONEMD_SIMPLE_SHA384;
+       if (!strcasecmp(str, "2") || !strcasecmp(str, "sha512"))
+               return LDNS_SIGN_WITH_ZONEMD_SIMPLE_SHA512;
+
+       if (reason) *reason = reason[2];
+       return 0;
 }
 
 int
@@ -665,11 +659,9 @@ main(int argc, char *argv[])
        ldns_output_format_storage fmt_st;
        ldns_output_format* fmt = ldns_output_format_init(&fmt_st);
 
-       const EVP_MD *add_zonemd[2] = { NULL, NULL };
-       const char *zonemd_algs[2] = { "SHA384", "SHA512" };
-       ldns_rr *zonemd_rrs[2] = { NULL, NULL };
-       size_t n_zonemd_algs = sizeof(add_zonemd) / sizeof(const EVP_MD *);
-       bool zonemds = false;
+       /* For parson zone digest parameters */
+       int flag;
+       const char *reason = NULL;
 
        prog = strdup(argv[0]);
        inception = 0;
@@ -770,32 +762,15 @@ main(int argc, char *argv[])
                        exit(EXIT_SUCCESS);
                        break;
                case 'z':
-                       switch (atoi(optarg)) {
-                       case 1:
-                               if (add_zonemd[0])
-                                       break;
-                               add_zonemd[0] = EVP_get_digestbyname("sha384");
-                               if (!add_zonemd[0])
-                                       fprintf(stderr, "SHA384 not supported "
-                                           "for ZONEMD. Continuing without it.\n");
-                               else
-                                       zonemds = true;
-                               break;
-                       case 2: if (add_zonemd[1])
-                                       break;
-                               add_zonemd[1] = EVP_get_digestbyname("sha512");
-                               if (!add_zonemd[1])
-                                       fprintf(stderr, "SHA512 not supported "
-                                           "for ZONEMD. Continuing without it.\n");
-                               else
-                                       zonemds = true;
-                               break;
-                       default:
-                               fprintf(stderr, "Unknown ZONEMD <algorithm>: %s.\n"
-                                   "Should be 1 for SHA384 or 2 for SHA512\n",
-                                   optarg);
+                       flag = str2zonemd_signflag(optarg, &reason);
+                       if (flag)
+                               signflags |= flag;
+                       else {
+                               fprintf( stderr
+                                      , "%s\nwith zone digest parameters:"
+                                        " \"%s\"\n"
+                                      , reason, optarg);
                                exit(EXIT_FAILURE);
-                               break;
                        }
                        break;
                case 'A':
@@ -1044,120 +1019,6 @@ main(int argc, char *argv[])
                          ldns_rr_list_rr(ldns_zone_rrs(orig_zone), i));
                }
        }
-       if (zonemds) {
-               /* Remove apex ZONEMD resource records from zone
-                */
-               ldns_rbnode_t *node;
-
-               /* - find the apex */
-               node = ldns_rbtree_search(
-                   signed_zone->names, ldns_rr_owner(orig_soa));
-               if (node != NULL && node != LDNS_RBTREE_NULL) {
-                       ldns_dnssec_name *apex = (ldns_dnssec_name *)node->data;
-                       ldns_dnssec_rrsets **rrsets_ref = &apex->rrsets;
-
-                       /* - find ZONEMD at apex */
-                       for (  rrsets_ref = &apex->rrsets
-                           ; *rrsets_ref
-                           ;  rrsets_ref = &(*rrsets_ref)->next) {
-                               ldns_dnssec_rrsets *zonemd_rrset;
-
-                               if ((*rrsets_ref)->type != LDNS_RR_TYPE_ZONEMD)
-                                       continue;
-                               zonemd_rrset = (*rrsets_ref);
-
-                               /* - unlink the found ZONEMD rrset */
-                               *rrsets_ref = zonemd_rrset->next;
-
-                               /* - free the ZONEMD rrset */
-                               zonemd_rrset->next = NULL;
-                               ldns_dnssec_rrsets_free(zonemd_rrset);
-                               break;
-                       }
-               }
-       }
-       /* Add fresh ZONEMD records at apex */
-       for (i = 0; i < n_zonemd_algs; i++) {
-               ldns_rr *zonemd_rr;
-               ldns_rdf *rdf = NULL;
-
-               if (!add_zonemd[i])
-                       continue;
-               zonemd_rr = ldns_rr_new_frm_type(LDNS_RR_TYPE_ZONEMD);
-               if (!zonemd_rr) {
-                       fprintf(stderr, "Error creating "
-                                       "ZONEMD rr with algorithm %s. "
-                                       "Continuing without it.\n"
-                                     , zonemd_algs[i]);
-                       continue;
-               }
-               ldns_rr_set_class(zonemd_rr, ldns_rr_get_class(orig_soa));
-               ldns_rr_set_ttl(zonemd_rr, ldns_rr_ttl(orig_soa));
-               do {
-                       ldns_status st;
-                       void *md_buf;
-
-                       /* - set owner */
-                       rdf = ldns_rdf_clone(ldns_rr_owner(orig_soa));
-                       if (!rdf)
-                               break;
-                       ldns_rr_set_owner(zonemd_rr, rdf);
-
-                       /* - set serial rdata field */
-                       rdf = ldns_rdf_clone(ldns_rr_rdf(orig_soa, 2));
-                       if (!rdf)
-                               break;
-                       (void) ldns_rr_set_rdf(zonemd_rr, rdf, 0);
-
-                       /* - set scheme rdata field */
-                       /*   currently we only support SIMPLE scheme */
-                       rdf = ldns_native2rdf_int8(
-                           LDNS_RDF_TYPE_INT8, 1);
-                       if (!rdf)
-                               break;
-                       (void) ldns_rr_set_rdf(zonemd_rr, rdf, 1);
-
-                       /* - set algorithm rdata field */
-                       rdf = ldns_native2rdf_int8(
-                           LDNS_RDF_TYPE_INT8, (uint8_t)(i + 1));
-                       if (!rdf)
-                               break;
-                       (void) ldns_rr_set_rdf(zonemd_rr, rdf, 2);
-
-                       /* - set digest rdata field */
-                       md_buf = calloc(EVP_MD_size(add_zonemd[i]), 1);
-                       if (!md_buf) {
-                               rdf = NULL;
-                               break;
-                       }
-                       rdf = ldns_rdf_new( LDNS_RDF_TYPE_HEX
-                                         , EVP_MD_size(add_zonemd[i])
-                                         , md_buf);
-                       if (!rdf)
-                               break;
-                       (void) ldns_rr_set_rdf(zonemd_rr, rdf, 3);
-
-                       st = ldns_dnssec_zone_add_rr(
-                           signed_zone, zonemd_rr);
-                       if (st) {
-                               fprintf( stderr
-                                      , "Error adding ZONEMD to zone:"
-                                        " %s\nContinuing without it.\n"
-                                      , ldns_get_errorstr_by_id(st));
-                               ldns_rr_free(zonemd_rr);
-                               break;
-                       }
-                       zonemd_rrs[i] = zonemd_rr;
-               } while (0);
-               if (!rdf) {
-                       fprintf(stderr, "Memory error allocating for "
-                                       "ZONEMD RR with algorithm %s. "
-                                       "Continuing without it.\n"
-                                     , zonemd_algs[i]);
-                       ldns_rr_free(zonemd_rr);
-                       continue;
-               }
-       }
        /* list to store newly created rrs, so we can free them later */
        added_rrs = ldns_rr_list_new();
 
@@ -1193,150 +1054,6 @@ main(int argc, char *argv[])
        }
 
        if (signed_zone) {
-               ldns_status zonemd_result = LDNS_STATUS_OK;
-               EVP_MD_CTX *ctxs[2] = { NULL, NULL };
-
-               zonemds = false;
-               for (i = 0; i < n_zonemd_algs; i++) {
-                       if (zonemd_rrs[i]) {
-                               ctxs[i] = EVP_MD_CTX_create();
-                               if (!EVP_DigestInit(ctxs[i], add_zonemd[i])) {
-                                       fprintf( stderr
-                                              , "Error initializing hashing "
-                                                "algorithm %s for ZONEMD. "
-                                                "Continuing without it.\n"
-                                              , zonemd_algs[i]);
-                                       EVP_MD_CTX_destroy(ctxs[i]);
-                                       ctxs[i] = NULL;
-                               } else
-                                       zonemds = true;
-                       }
-               }
-               if (zonemds) do {
-                       ldns_buffer *buf = ldns_buffer_new(LDNS_MAX_PACKETLEN);
-                       ldns_rr_list *rr_list;
-                       size_t j;
-                       ldns_dnssec_rrsets *zonemd;
-                       ldns_rr_list *rrsigs;
-
-                       if (!buf) {
-                               fprintf( stderr
-                                      , "Error allocating buffer for "
-                                        "ZONEMD digest(s)\n");
-                               break;
-                       }
-                       rr_list = ldns_dnssec_zone2rr_list(signed_zone);
-                       if (!rr_list) {
-                               fprintf( stderr
-                                      , "Error making ZONEMD digest(s)\n");
-                               ldns_buffer_free(buf);
-                               break;
-                       }
-                       ldns_rr_list_sort(rr_list);
-                       for (i = 0; i < ldns_rr_list_rr_count(rr_list); i++) {
-                               ldns_rr *rr = ldns_rr_list_rr(rr_list, i);
-                               
-                               /* Skip apex ZONEMD RRs */
-                               if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_ZONEMD
-                               && !ldns_dname_compare( ldns_rr_owner(rr)
-                                                     , ldns_rr_owner(orig_soa))) {
-                                       continue;
-                               }
-                               /* Skip RRSIGs for apex ZONEMD RRs */
-                               if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_RRSIG
-                               &&  LDNS_RR_TYPE_ZONEMD == ldns_rdf2rr_type(
-                                               ldns_rr_rrsig_typecovered(rr))
-                               && !ldns_dname_compare( ldns_rr_owner(rr)
-                                                     , ldns_rr_owner(orig_soa))) {
-                                       continue;
-                               }
-                               zonemd_result = ldns_rr2buffer_wire(
-                                   buf, rr, LDNS_SECTION_ANSWER);
-                               if (zonemd_result)
-                                       break;
-
-                               for (j = 0; j < n_zonemd_algs; j++) {
-                                       if (!ctxs[j])
-                                               continue;
-                                       EVP_DigestUpdate(ctxs[j],
-                                           ldns_buffer_begin(buf),
-                                           ldns_buffer_position(buf));
-                               }
-                               ldns_buffer_clear(buf);
-                       }
-                       if (zonemd_result) {
-                               ldns_rr_list_free(rr_list);
-                               ldns_buffer_free(buf);
-                               break;
-                       }
-                       for (j = 0; j < n_zonemd_algs; j++) {
-                               if (!ctxs[j])
-                                       continue;
-                               EVP_DigestFinal_ex(ctxs[j],
-                                 ldns_rdf_data(ldns_rr_rdf(zonemd_rrs[j], 3)),
-                                 0);
-                       }
-                       ldns_buffer_free(buf);
-
-                       /* Resign ZONEMD rrset */
-                       /* - sign (create new signatures)  */
-                       ldns_rr_list_set_rr_count(rr_list, 0);
-                       for (j = 0; j < n_zonemd_algs; j++) {
-                               if (!zonemd_rrs[j])
-                                       continue;
-                               ldns_rr_list_push_rr(rr_list, zonemd_rrs[j]);
-                       }
-                       rrsigs = ldns_sign_public(rr_list, keys);
-                       if (!rrsigs) {
-                               zonemd_result = LDNS_STATUS_MEM_ERR;
-                               ldns_rr_list_free(rr_list);
-                               break;
-                       }
-
-                       /* - find ZONEMD rrset */
-                       zonemd = ldns_dnssec_zone_find_rrset(signed_zone,
-                           ldns_rr_owner(orig_soa), LDNS_RR_TYPE_ZONEMD);
-                       assert(zonemd);
-
-                       /* - remove old signatures */
-                       ldns_dnssec_rrs_free(zonemd->signatures);
-                       zonemd->signatures = NULL;
-
-                       /* - provision the new signatures */
-                       if (ldns_rr_list_rr_count(rrsigs)) {
-                               ldns_rr *rrsig = ldns_rr_list_rr(rrsigs, 0);
-
-                               zonemd->signatures = ldns_dnssec_rrs_new();
-                               if (!zonemd->signatures) {
-                                       zonemd_result = LDNS_STATUS_MEM_ERR;
-                                       ldns_rr_list_free(rrsigs);
-                                       ldns_rr_list_free(rr_list);
-                                       break;
-                               }
-                               zonemd->signatures->rr = rrsig;
-                               if (added_rrs)
-                                       ldns_rr_list_push_rr(added_rrs, rrsig);
-                       }
-                       for (i = 1; i < ldns_rr_list_rr_count(rrsigs); i++) {
-                               ldns_rr *rrsig = ldns_rr_list_rr(rrsigs, i);
-
-                               zonemd_result = ldns_dnssec_rrs_add_rr(
-                                   zonemd->signatures, rrsig);
-                               if (zonemd_result)
-                                       break;
-                               if (added_rrs)
-                                       ldns_rr_list_push_rr(added_rrs, rrsig);
-                       }
-
-                       ldns_rr_list_free(rrsigs);
-                       ldns_rr_list_free(rr_list);
-               } while(0);
-               if (zonemd_result != LDNS_STATUS_OK) {
-                       fprintf( stderr
-                              , "Error calculating ZONEMD digest(s): %s\n"
-                              , ldns_get_errorstr_by_id(result));
-               }
-
                if (strncmp(outputfile_name, "-", 2) == 0) {
                        ldns_dnssec_zone_print(stdout, signed_zone);
                } else {
index a5a1d003a87bb2d1e170db3f7b48089bfde4b188..68534d00c7a6c16e97bffab6be0693fd4d8b57bf 100644 (file)
@@ -922,6 +922,13 @@ main(int argc, char **argv)
                                dnssec_zone->soa->name, keys, apexonly,
                                percentage);
 
+               if (!result) {
+                       result = ldns_dnssec_zone_verify_zonemd(dnssec_zone);
+                       if (result && verbosity > 3) {
+                               fprintf(myerr,
+                                       "Could not validate zone digest\n");
+                       }
+               }
                if (result == LDNS_STATUS_OK) {
                        if (verbosity >= 3) {
                                fprintf(myout,
index a88a46affab1396a43ba02755a148af031c21862..96bc8cdd9be22d00dddd555eb580914f538f727a 100644 (file)
@@ -14,6 +14,8 @@ extern "C" {
 /** Sign flag that makes DNSKEY type signed by all keys, not only by SEP keys*/
 #define LDNS_SIGN_DNSKEY_WITH_ZSK 1
 #define LDNS_SIGN_WITH_ALL_ALGORITHMS 2 
+#define LDNS_SIGN_WITH_ZONEMD_SIMPLE_SHA384 4
+#define LDNS_SIGN_WITH_ZONEMD_SIMPLE_SHA512 8
 
 /**
  * Create an empty RRSIG RR (i.e. without the actual signature data)
index e97ea30c6008eac6e052263e2b0a21d9c9a10483..bc8e404257c584260865542e99251247a80e9950 100644 (file)
@@ -476,6 +476,8 @@ ldns_status ldns_dnssec_zone_add_empty_nonterminals(ldns_dnssec_zone *zone);
  */
 bool ldns_dnssec_zone_is_nsec3_optout(const ldns_dnssec_zone* zone);
 
+ldns_status ldns_dnssec_zone_verify_zonemd(ldns_dnssec_zone *zone);
+
 #ifdef __cplusplus
 }
 #endif
index 15f49a28e153e80652cf9255ab763f6da69234cf..e5580fcf90ab433520a26a83a766146b02523cf1 100644 (file)
@@ -129,7 +129,12 @@ enum ldns_enum_status {
        LDNS_STATUS_RDATA_OVERFLOW,
        LDNS_STATUS_SYNTAX_SUPERFLUOUS_TEXT_ERR,
        LDNS_STATUS_NSEC3_DOMAINNAME_OVERFLOW,
-       LDNS_STATUS_DANE_NEED_OPENSSL_GE_1_1_FOR_DANE_TA
+       LDNS_STATUS_DANE_NEED_OPENSSL_GE_1_1_FOR_DANE_TA,
+       LDNS_STATUS_ZONEMD_UNKNOWN_SCHEME,
+       LDNS_STATUS_ZONEMD_UNKNOWN_HASH,
+       LDNS_STATUS_ZONEMD_INVALID_SOA,
+       LDNS_STATUS_NO_ZONEMD,
+       LDNS_STATUS_NO_VALID_ZONEMD
 };
 typedef enum ldns_enum_status ldns_status;