}
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 */
+
}
#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
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;
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':
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();
}
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 {