From 754e6f13497a3613ae5f682c7beeade230cfc1e8 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 9 Oct 2020 16:46:20 +0200 Subject: [PATCH] zonemd, routine to check zonemd hash if present --- services/authzone.c | 109 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/services/authzone.c b/services/authzone.c index c3c472f06..d58e2a481 100644 --- a/services/authzone.c +++ b/services/authzone.c @@ -1761,6 +1761,115 @@ auth_zones_read_zones(struct auth_zones* az, struct config_file* cfg) return 1; } +/** fetch the content of a ZONEMD RR from the rdata */ +static int zonemd_fetch_parameters(struct auth_rrset* zonemd_rrset, size_t i, + uint32_t* serial, int* scheme, int* hashalgo, uint8_t** hash, + size_t* hashlen) +{ + size_t rr_len; + uint8_t* rdata; + if(i >= zonemd_rrset->data->count) + return 0; + rr_len = zonemd_rrset->data->rr_len[i]; + if(rr_len < 2+4+1+1) + return 0; /* too short, for rdlen+serial+scheme+algo */ + rdata = zonemd_rrset->data->rr_data[i]; + *serial = sldns_read_uint32(rdata+2); + *scheme = rdata[6]; + *hashalgo = rdata[7]; + *hashlen = rr_len - 8; + if(*hashlen == 0) + *hash = NULL; + else *hash = rdata+8; + return 1; +} + +/** + * Check ZONEMDs if present for the auth zone. Depending on config + * it can warn or fail on that. Checks the hash of the ZONEMD. + * @param z: auth zone to check for. + * caller must hold lock on zone. + * @param cfg: config file options + */ +void auth_zone_zonemd_check_hash(struct auth_zone* z /*, struct config_file* cfg*/) +{ + /* loop over ZONEMDs and see which one is valid. if not print + * failure (depending on config) */ + struct auth_data* apex; + struct auth_rrset* zonemd_rrset; + size_t i; + char* reason = NULL; + struct regional* region = NULL; + struct sldns_buffer* buf = NULL; + char zstr[255+1]; + uint32_t soa_serial = 0; + region = regional_create(); + if(!region) { + return; + } + buf = sldns_buffer_new(65535); + if(!buf) { + regional_destroy(region); + return; + } + if(!auth_zone_get_serial(z, &soa_serial)) { + regional_destroy(region); + sldns_buffer_free(buf); + return; + } + + apex = az_find_name(z, z->name, z->namelen); + if(!apex) { + regional_destroy(region); + sldns_buffer_free(buf); + return; + } + zonemd_rrset = az_domain_rrset(apex, LDNS_RR_TYPE_ZONEMD); + if(!zonemd_rrset || zonemd_rrset->data->count==0) { + regional_destroy(region); + sldns_buffer_free(buf); + return; /* no RRset or no RRs in rrset */ + } + + /* we have a ZONEMD, check if it is correct */ + for(i=0; idata->count; i++) { + uint32_t serial = 0; + int scheme = 0, hashalgo = 0; + uint8_t* hash = NULL; + size_t hashlen = 0; + if(!zonemd_fetch_parameters(zonemd_rrset, i, &serial, &scheme, + &hashalgo, &hash, &hashlen)) { + /* malformed RR */ + reason = "ZONEMD rdata malformed"; + continue; + } + if(serial != soa_serial) { + reason = "ZONEMD serial is wrong"; + continue; + } + if(auth_zone_generate_zonemd_check(z, scheme, hashalgo, + hash, hashlen, region, buf, &reason)) { + /* success */ + if(verbosity >= VERB_ALGO) { + dname_str(z->name, zstr); + verbose(VERB_ALGO, "auth-zone %s ZONEMD hash is correct", zstr); + } + regional_destroy(region); + sldns_buffer_free(buf); + return; + } + /* try next one */ + } + /* fail, we may have reason */ + if(!reason) + reason = "no ZONEMD records found"; + dname_str(z->name, zstr); + log_warn("auth-zone %s ZONEMD failed: %s", zstr, reason); + + regional_destroy(region); + sldns_buffer_free(buf); +} + /** find serial number of zone or false if none */ int auth_zone_get_serial(struct auth_zone* z, uint32_t* serial) -- 2.47.2