/** test dname_subdomain_c */ static void dname_test_subdomain(void) { unit_show_func("util/data/dname.c", "dname_subdomain"); unit_assert(dname_subdomain_c( (uint8_t*)"", (uint8_t*)"")); unit_assert(dname_subdomain_c( (uint8_t*)"\003com", (uint8_t*)"")); unit_assert(!dname_subdomain_c( (uint8_t*)"", (uint8_t*)"\003com")); unit_assert(dname_subdomain_c( (uint8_t*)"\007example\003com", (uint8_t*)"\003com")); unit_assert(!dname_subdomain_c( (uint8_t*)"\003com", (uint8_t*)"\007example\003com")); unit_assert(dname_subdomain_c( (uint8_t*)"\007example\003com", (uint8_t*)"")); unit_assert(!dname_subdomain_c( (uint8_t*)"\003net", (uint8_t*)"\003com")); unit_assert(!dname_subdomain_c( (uint8_t*)"\003net", (uint8_t*)"\003org")); unit_assert(!dname_subdomain_c( (uint8_t*)"\007example\003net", (uint8_t*)"\003org")); unit_assert(!dname_subdomain_c( (uint8_t*)"\003net", (uint8_t*)"\007example\003org")); }
void val_neg_addreferral(struct val_neg_cache* neg, struct reply_info* rep, uint8_t* zone_name) { size_t i, need; uint8_t* signer; size_t signer_len; uint16_t dclass; struct val_neg_zone* zone; /* no SOA in this message, find RRSIG over NSEC's signer name. * note the NSEC records are maybe not validated yet */ signer = reply_nsec_signer(rep, &signer_len, &dclass); if(!signer) return; if(!dname_subdomain_c(signer, zone_name)) { /* the signer is not in the bailiwick, throw it out */ return; } log_nametypeclass(VERB_ALGO, "negcache insert referral ", signer, LDNS_RR_TYPE_NS, dclass); /* ask for enough space to store all of it */ need = calc_data_need(rep) + calc_zone_need(signer, signer_len); lock_basic_lock(&neg->lock); neg_make_space(neg, need); /* find or create the zone entry */ zone = neg_find_zone(neg, signer, signer_len, dclass); if(!zone) { if(!(zone = neg_create_zone(neg, signer, signer_len, dclass))) { lock_basic_unlock(&neg->lock); log_err("out of memory adding negative zone"); return; } } val_neg_zone_take_inuse(zone); /* insert the NSECs */ for(i=rep->an_numrrsets; i< rep->an_numrrsets+rep->ns_numrrsets; i++){ if(ntohs(rep->rrsets[i]->rk.type) != LDNS_RR_TYPE_NSEC && ntohs(rep->rrsets[i]->rk.type) != LDNS_RR_TYPE_NSEC3) continue; if(!dname_subdomain_c(rep->rrsets[i]->rk.dname, zone->name)) continue; /* insert NSEC into this zone's tree */ neg_insert_data(neg, zone, rep->rrsets[i]); } if(zone->tree.count == 0) { /* remove empty zone if inserts failed */ neg_delete_zone(neg, zone); } lock_basic_unlock(&neg->lock); }
/** * findClosestEncloser * Given a name and a list of NSEC3s, find the candidate closest encloser. * This will be the first ancestor of 'name' (including itself) to have a * matching NSEC3 RR. * @param env: module environment with temporary region and buffer. * @param flt: the NSEC3 RR filter, contains zone name and RRs. * @param ct: cached hashes table. * @param qinfo: query that is verified for. * @param ce: closest encloser information is returned in here. * @return true if a closest encloser candidate is found, false if not. */ static int nsec3_find_closest_encloser(struct module_env* env, struct nsec3_filter* flt, rbtree_t* ct, struct query_info* qinfo, struct ce_response* ce) { uint8_t* nm = qinfo->qname; size_t nmlen = qinfo->qname_len; /* This scans from longest name to shortest, so the first match * we find is the only viable candidate. */ /* (David:) FIXME: modify so that the NSEC3 matching the zone apex need * not be present. (Mark Andrews idea). * (Wouter:) But make sure you check for DNAME bit in zone apex, * if the NSEC3 you find is the only NSEC3 in the zone, then this * may be the case. */ while(dname_subdomain_c(nm, flt->zone)) { if(find_matching_nsec3(env, flt, ct, nm, nmlen, &ce->ce_rrset, &ce->ce_rr)) { ce->ce = nm; ce->ce_len = nmlen; return 1; } dname_remove_label(&nm, &nmlen); } return 0; }
void iter_scrub_ds(struct dns_msg* msg, struct ub_packed_rrset_key* ns, uint8_t* z) { /* Only the DS record for the delegation itself is expected. * We allow DS for everything between the bailiwick and the * zonecut, thus DS records must be at or above the zonecut. * And the DS records must be below the server authority zone. * The answer section is already scrubbed. */ size_t i = msg->rep->an_numrrsets; while(i < (msg->rep->an_numrrsets + msg->rep->ns_numrrsets)) { struct ub_packed_rrset_key* s = msg->rep->rrsets[i]; if(ntohs(s->rk.type) == LDNS_RR_TYPE_DS && (!ns || !dname_subdomain_c(ns->rk.dname, s->rk.dname) || query_dname_compare(z, s->rk.dname) == 0)) { log_nametypeclass(VERB_ALGO, "removing irrelevant DS", s->rk.dname, ntohs(s->rk.type), ntohs(s->rk.rrset_class)); memmove(msg->rep->rrsets+i, msg->rep->rrsets+i+1, sizeof(struct ub_packed_rrset_key*) * (msg->rep->rrset_count-i-1)); msg->rep->ns_numrrsets--; msg->rep->rrset_count--; /* stay at same i, but new record */ continue; } i++; } }
/** * Find best signer name in this set of rrsigs. * @param rrset: which rrsigs to look through. * @param qinf: the query name that needs validation. * @param signer_name: the best signer_name. Updated if a better one is found. * @param signer_len: length of signer name. * @param matchcount: count of current best name (starts at 0 for no match). * Updated if match is improved. */ static void val_find_best_signer(struct ub_packed_rrset_key* rrset, struct query_info* qinf, uint8_t** signer_name, size_t* signer_len, int* matchcount) { struct packed_rrset_data* d = (struct packed_rrset_data*) rrset->entry.data; uint8_t* sign; size_t i; int m; for(i=d->count; i<d->count+d->rrsig_count; i++) { sign = d->rr_data[i]+2+18; /* look at signatures that are valid (long enough), * and have a signer name that is a superdomain of qname, * and then check the number of labels in the shared topdomain * improve the match if possible */ if(d->rr_len[i] > 2+19 && /* rdata, sig + root label*/ dname_subdomain_c(qinf->qname, sign)) { (void)dname_lab_cmp(qinf->qname, dname_count_labels(qinf->qname), sign, dname_count_labels(sign), &m); if(m > *matchcount) { *matchcount = m; *signer_name = sign; (void)dname_count_size_labels(*signer_name, signer_len); } } } }
/** check subdomain with decompression, compressed is parent */ static int sub_of_pkt(ldns_buffer* pkt, uint8_t* zone, uint8_t* comprname) { uint8_t buf[LDNS_MAX_DOMAINLEN+1]; dname_pkt_copy(pkt, buf, comprname); return dname_subdomain_c(zone, buf); }
/** check subdomain with decompression */ static int pkt_sub(sldns_buffer* pkt, uint8_t* comprname, uint8_t* zone) { uint8_t buf[LDNS_MAX_DOMAINLEN+1]; dname_pkt_copy(pkt, buf, comprname); return dname_subdomain_c(buf, zone); }
/** * Initialize the filter structure. * Finds the zone by looking at available NSEC3 records and best match. * (skips the unknown flag and unknown algo NSEC3s). * * @param filter: nsec3 filter structure. * @param list: list of rrsets, an array of them. * @param num: number of rrsets in list. * @param qinfo: * query name to match a zone for. * query type (if DS a higher zone must be chosen) * qclass, to filter NSEC3s with. */ static void filter_init(struct nsec3_filter* filter, struct ub_packed_rrset_key** list, size_t num, struct query_info* qinfo) { size_t i; uint8_t* nm; size_t nmlen; filter->zone = NULL; filter->zone_len = 0; filter->list = list; filter->num = num; filter->fclass = qinfo->qclass; for(i=0; i<num; i++) { /* ignore other stuff in the list */ if(ntohs(list[i]->rk.type) != LDNS_RR_TYPE_NSEC3 || ntohs(list[i]->rk.rrset_class) != qinfo->qclass) continue; /* skip unknown flags, algo */ if(!nsec3_rrset_has_known(list[i])) continue; /* since NSEC3s are base32.zonename, we can find the zone * name by stripping off the first label of the record */ nm = list[i]->rk.dname; nmlen = list[i]->rk.dname_len; dname_remove_label(&nm, &nmlen); /* if we find a domain that can prove about the qname, * and if this domain is closer to the qname */ if(dname_subdomain_c(qinfo->qname, nm) && (!filter->zone || dname_subdomain_c(nm, filter->zone))) { /* for a type DS do not accept a zone equal to qname*/ if(qinfo->qtype == LDNS_RR_TYPE_DS && query_dname_compare(qinfo->qname, nm) == 0 && !dname_is_root(qinfo->qname)) continue; filter->zone = nm; filter->zone_len = nmlen; } } }
/** sum up number of items inuse in subtree */ static int sum_subtree_inuse(struct val_neg_zone* zone, struct val_neg_data* data) { struct val_neg_data* d; int num = 0; RBTREE_FOR(d, struct val_neg_data*, &zone->tree) { if(dname_subdomain_c(d->name, data->name)) { if(d->in_use) num++; } } return num; }
/** sum up number of items inuse in subtree */ static int sum_zone_subtree_inuse(struct val_neg_cache* neg, struct val_neg_zone* zone) { struct val_neg_zone* z; int num = 0; RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) { if(dname_subdomain_c(z->name, zone->name)) { if(z->in_use) num++; } } return num; }
/** callback to delete keys in zone */ static void zone_del_kcache(struct lruhash_entry* e, void* arg) { /* entry is locked */ struct del_info* inf = (struct del_info*)arg; struct key_entry_key* k = (struct key_entry_key*)e->key; if(dname_subdomain_c(k->name, inf->name)) { struct key_entry_data* d = (struct key_entry_data*)e->data; if(d->ttl >= inf->now) { d->ttl = inf->expired; inf->num_keys++; } } }
/** callback to delete messages in a zone */ static void zone_del_msg(struct lruhash_entry* e, void* arg) { /* entry is locked */ struct del_info* inf = (struct del_info*)arg; struct msgreply_entry* k = (struct msgreply_entry*)e->key; if(dname_subdomain_c(k->key.qname, inf->name)) { struct reply_info* d = (struct reply_info*)e->data; if(d->ttl >= inf->now) { d->ttl = inf->expired; inf->num_msgs++; } } }
void val_neg_addreply(struct val_neg_cache* neg, struct reply_info* rep) { size_t i, need; struct ub_packed_rrset_key* soa; struct val_neg_zone* zone; /* see if secure nsecs inside */ if(!reply_has_nsec(rep)) return; /* find the zone name in message */ soa = reply_find_soa(rep); if(!soa) return; log_nametypeclass(VERB_ALGO, "negcache insert for zone", soa->rk.dname, LDNS_RR_TYPE_SOA, ntohs(soa->rk.rrset_class)); /* ask for enough space to store all of it */ need = calc_data_need(rep) + calc_zone_need(soa->rk.dname, soa->rk.dname_len); lock_basic_lock(&neg->lock); neg_make_space(neg, need); /* find or create the zone entry */ zone = neg_find_zone(neg, soa->rk.dname, soa->rk.dname_len, ntohs(soa->rk.rrset_class)); if(!zone) { if(!(zone = neg_create_zone(neg, soa->rk.dname, soa->rk.dname_len, ntohs(soa->rk.rrset_class)))) { lock_basic_unlock(&neg->lock); log_err("out of memory adding negative zone"); return; } } val_neg_zone_take_inuse(zone); /* insert the NSECs */ for(i=rep->an_numrrsets; i< rep->an_numrrsets+rep->ns_numrrsets; i++){ if(ntohs(rep->rrsets[i]->rk.type) != LDNS_RR_TYPE_NSEC) continue; if(!dname_subdomain_c(rep->rrsets[i]->rk.dname, zone->name)) continue; /* insert NSEC into this zone's tree */ neg_insert_data(neg, zone, rep->rrsets[i]); } if(zone->tree.count == 0) { /* remove empty zone if inserts failed */ neg_delete_zone(neg, zone); } lock_basic_unlock(&neg->lock); }
/** callback to delete rrsets in a zone */ static void zone_del_rrset(struct lruhash_entry* e, void* arg) { /* entry is locked */ struct del_info* inf = (struct del_info*)arg; struct ub_packed_rrset_key* k = (struct ub_packed_rrset_key*)e->key; if(dname_subdomain_c(k->rk.dname, inf->name)) { struct packed_rrset_data* d = (struct packed_rrset_data*)e->data; if(d->ttl >= inf->now) { d->ttl = inf->expired; inf->num_rrsets++; } } }
int iter_dp_is_useless(struct query_info* qinfo, uint16_t qflags, struct delegpt* dp) { struct delegpt_ns* ns; /* check: * o RD qflag is on. * o no addresses are provided. * o all NS items are required glue. * OR * o RD qflag is on. * o no addresses are provided. * o the query is for one of the nameservers in dp, * and that nameserver is a glue-name for this dp. */ if(!(qflags&BIT_RD)) return 0; /* either available or unused targets */ if(dp->usable_list || dp->result_list) return 0; /* see if query is for one of the nameservers, which is glue */ if( (qinfo->qtype == LDNS_RR_TYPE_A || qinfo->qtype == LDNS_RR_TYPE_AAAA) && dname_subdomain_c(qinfo->qname, dp->name) && delegpt_find_ns(dp, qinfo->qname, qinfo->qname_len)) return 1; for(ns = dp->nslist; ns; ns = ns->next) { if(ns->resolved) /* skip failed targets */ continue; if(!dname_subdomain_c(ns->name, dp->name)) return 0; /* one address is not required glue */ } return 1; }
void val_mark_insecure(struct reply_info* rep, uint8_t* kname, struct rrset_cache* r, struct module_env* env) { size_t i; struct packed_rrset_data* d; for(i=0; i<rep->rrset_count; i++) { d = (struct packed_rrset_data*)rep->rrsets[i]->entry.data; if(d->security == sec_status_unchecked && dname_subdomain_c(rep->rrsets[i]->rk.dname, kname)) { /* mark as insecure */ d->security = sec_status_insecure; rrset_update_sec_status(r, rep->rrsets[i], *env->now); } } }
int iter_ds_toolow(struct dns_msg* msg, struct delegpt* dp) { /* if for query example.com, there is example.com SOA or a subdomain * of example.com, then we are too low and need to fetch NS. */ size_t i; /* if we have a DNAME or CNAME we are probably wrong */ /* if we have a qtype DS in the answer section, its fine */ for(i=0; i < msg->rep->an_numrrsets; i++) { struct ub_packed_rrset_key* s = msg->rep->rrsets[i]; if(ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME || ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME) { /* not the right answer, maybe too low, check the * RRSIG signer name (if there is any) for a hint * that it is from the dp zone anyway */ uint8_t* sname; size_t slen; val_find_rrset_signer(s, &sname, &slen); if(sname && query_dname_compare(dp->name, sname)==0) return 0; /* it is fine, from the right dp */ return 1; } if(ntohs(s->rk.type) == LDNS_RR_TYPE_DS) return 0; /* fine, we have a DS record */ } for(i=msg->rep->an_numrrsets; i < msg->rep->an_numrrsets + msg->rep->ns_numrrsets; i++) { struct ub_packed_rrset_key* s = msg->rep->rrsets[i]; if(ntohs(s->rk.type) == LDNS_RR_TYPE_SOA) { if(dname_subdomain_c(s->rk.dname, msg->qinfo.qname)) return 1; /* point is too low */ if(query_dname_compare(s->rk.dname, dp->name)==0) return 0; /* right dp */ } if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC || ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) { uint8_t* sname; size_t slen; val_find_rrset_signer(s, &sname, &slen); if(sname && query_dname_compare(dp->name, sname)==0) return 0; /* it is fine, from the right dp */ return 1; } } /* we do not know */ return 1; }
/** * Check if right hand name in NSEC is within zone * @param rrset: the NSEC rrset * @param zonename: the zone name. * @return true if BAD. */ static int sanitize_nsec_is_overreach(struct rrset_parse* rrset, uint8_t* zonename) { struct rr_parse* rr; uint8_t* rhs; size_t len; log_assert(rrset->type == LDNS_RR_TYPE_NSEC); for(rr = rrset->rr_first; rr; rr = rr->next) { rhs = rr->ttl_data+4+2; len = ldns_read_uint16(rr->ttl_data+4); if(!dname_valid(rhs, len)) { /* malformed domain name in rdata */ return 1; } if(!dname_subdomain_c(rhs, zonename)) { /* overreaching */ return 1; } } /* all NSEC RRs OK */ return 0; }
/** * Remove NSEC records between start and end points. * By walking the tree, the tree is sorted canonically. * @param neg: negative cache. * @param zone: the zone * @param el: element to start walking at. * @param nsec: the nsec record with the end point */ static void wipeout(struct val_neg_cache* neg, struct val_neg_zone* zone, struct val_neg_data* el, struct ub_packed_rrset_key* nsec) { struct packed_rrset_data* d = (struct packed_rrset_data*)nsec-> entry.data; uint8_t* end; size_t end_len; int end_labs, m; rbnode_t* walk, *next; struct val_neg_data* cur; uint8_t buf[257]; /* get endpoint */ if(!d || d->count == 0 || d->rr_len[0] < 2+1) return; if(ntohs(nsec->rk.type) == LDNS_RR_TYPE_NSEC) { end = d->rr_data[0]+2; end_len = dname_valid(end, d->rr_len[0]-2); end_labs = dname_count_labels(end); } else { /* NSEC3 */ if(!nsec3_get_nextowner_b32(nsec, 0, buf, sizeof(buf))) return; end = buf; end_labs = dname_count_size_labels(end, &end_len); } /* sanity check, both owner and end must be below the zone apex */ if(!dname_subdomain_c(el->name, zone->name) || !dname_subdomain_c(end, zone->name)) return; /* detect end of zone NSEC ; wipe until the end of zone */ if(query_dname_compare(end, zone->name) == 0) { end = NULL; } walk = rbtree_next(&el->node); while(walk && walk != RBTREE_NULL) { cur = (struct val_neg_data*)walk; /* sanity check: must be larger than start */ if(dname_canon_lab_cmp(cur->name, cur->labs, el->name, el->labs, &m) <= 0) { /* r == 0 skip original record. */ /* r < 0 too small! */ walk = rbtree_next(walk); continue; } /* stop at endpoint, also data at empty nonterminals must be * removed (no NSECs there) so everything between * start and end */ if(end && dname_canon_lab_cmp(cur->name, cur->labs, end, end_labs, &m) >= 0) { break; } /* this element has to be deleted, but we cannot do it * now, because we are walking the tree still ... */ /* get the next element: */ next = rbtree_next(walk); /* now delete the original element, this may trigger * rbtree rebalances, but really, the next element is * the one we need. * But it may trigger delete of other data and the * entire zone. However, if that happens, this is done * by deleting the *parents* of the element for deletion, * and maybe also the entire zone if it is empty. * But parents are smaller in canonical compare, thus, * if a larger element exists, then it is not a parent, * it cannot get deleted, the zone cannot get empty. * If the next==NULL, then zone can be empty. */ if(cur->in_use) neg_delete_data(neg, cur); walk = next; } }
struct dns_msg* val_neg_getmsg(struct val_neg_cache* neg, struct query_info* qinfo, struct regional* region, struct rrset_cache* rrset_cache, ldns_buffer* buf, uint32_t now, int addsoa, uint8_t* topname) { struct dns_msg* msg; struct ub_packed_rrset_key* rrset; uint8_t* zname; size_t zname_len; int zname_labs; struct val_neg_zone* zone; /* only for DS queries */ if(qinfo->qtype != LDNS_RR_TYPE_DS) return NULL; log_assert(!topname || dname_subdomain_c(qinfo->qname, topname)); /* see if info from neg cache is available * For NSECs, because there is no optout; a DS next to a delegation * always has exactly an NSEC for it itself; check its DS bit. * flags=0 (not the zone apex). */ rrset = grab_nsec(rrset_cache, qinfo->qname, qinfo->qname_len, LDNS_RR_TYPE_NSEC, qinfo->qclass, 0, region, 1, qinfo->qtype, now); if(rrset) { /* return msg with that rrset */ if(!(msg = dns_msg_create(qinfo->qname, qinfo->qname_len, qinfo->qtype, qinfo->qclass, region, 2))) return NULL; /* TTL already subtracted in grab_nsec */ if(!dns_msg_authadd(msg, region, rrset, 0)) return NULL; if(addsoa && !add_soa(rrset_cache, now, region, msg, NULL)) return NULL; return msg; } /* check NSEC3 neg cache for type DS */ /* need to look one zone higher for DS type */ zname = qinfo->qname; zname_len = qinfo->qname_len; dname_remove_label(&zname, &zname_len); zname_labs = dname_count_labels(zname); /* lookup closest zone */ lock_basic_lock(&neg->lock); zone = neg_closest_zone_parent(neg, zname, zname_len, zname_labs, qinfo->qclass); while(zone && !zone->in_use) zone = zone->parent; /* check that the zone is not too high up so that we do not pick data * out of a zone that is above the last-seen key (or trust-anchor). */ if(zone && topname) { if(!dname_subdomain_c(zone->name, topname)) zone = NULL; } if(!zone) { lock_basic_unlock(&neg->lock); return NULL; } msg = neg_nsec3_proof_ds(zone, qinfo->qname, qinfo->qname_len, zname_labs+1, buf, rrset_cache, region, now, topname); if(msg && addsoa && !add_soa(rrset_cache, now, region, msg, zone)) { lock_basic_unlock(&neg->lock); return NULL; } lock_basic_unlock(&neg->lock); return msg; }
enum sec_status dnskey_verify_rrset_sig(struct regional* region, sldns_buffer* buf, struct val_env* ve, time_t now, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, size_t dnskey_idx, size_t sig_idx, struct rbtree_t** sortree, int* buf_canon, char** reason) { enum sec_status sec; uint8_t* sig; /* RRSIG rdata */ size_t siglen; size_t rrnum = rrset_get_count(rrset); uint8_t* signer; /* rrsig signer name */ size_t signer_len; unsigned char* sigblock; /* signature rdata field */ unsigned int sigblock_len; uint16_t ktag; /* DNSKEY key tag */ unsigned char* key; /* public key rdata field */ unsigned int keylen; rrset_get_rdata(rrset, rrnum + sig_idx, &sig, &siglen); /* min length of rdatalen, fixed rrsig, root signer, 1 byte sig */ if(siglen < 2+20) { verbose(VERB_QUERY, "verify: signature too short"); *reason = "signature too short"; return sec_status_bogus; } if(!(dnskey_get_flags(dnskey, dnskey_idx) & DNSKEY_BIT_ZSK)) { verbose(VERB_QUERY, "verify: dnskey without ZSK flag"); *reason = "dnskey without ZSK flag"; return sec_status_bogus; } if(dnskey_get_protocol(dnskey, dnskey_idx) != LDNS_DNSSEC_KEYPROTO) { /* RFC 4034 says DNSKEY PROTOCOL MUST be 3 */ verbose(VERB_QUERY, "verify: dnskey has wrong key protocol"); *reason = "dnskey has wrong protocolnumber"; return sec_status_bogus; } /* verify as many fields in rrsig as possible */ signer = sig+2+18; signer_len = dname_valid(signer, siglen-2-18); if(!signer_len) { verbose(VERB_QUERY, "verify: malformed signer name"); *reason = "signer name malformed"; return sec_status_bogus; /* signer name invalid */ } if(!dname_subdomain_c(rrset->rk.dname, signer)) { verbose(VERB_QUERY, "verify: signer name is off-tree"); *reason = "signer name off-tree"; return sec_status_bogus; /* signer name offtree */ } sigblock = (unsigned char*)signer+signer_len; if(siglen < 2+18+signer_len+1) { verbose(VERB_QUERY, "verify: too short, no signature data"); *reason = "signature too short, no signature data"; return sec_status_bogus; /* sig rdf is < 1 byte */ } sigblock_len = (unsigned int)(siglen - 2 - 18 - signer_len); /* verify key dname == sig signer name */ if(query_dname_compare(signer, dnskey->rk.dname) != 0) { verbose(VERB_QUERY, "verify: wrong key for rrsig"); log_nametypeclass(VERB_QUERY, "RRSIG signername is", signer, 0, 0); log_nametypeclass(VERB_QUERY, "the key name is", dnskey->rk.dname, 0, 0); *reason = "signer name mismatches key name"; return sec_status_bogus; } /* verify covered type */ /* memcmp works because type is in network format for rrset */ if(memcmp(sig+2, &rrset->rk.type, 2) != 0) { verbose(VERB_QUERY, "verify: wrong type covered"); *reason = "signature covers wrong type"; return sec_status_bogus; } /* verify keytag and sig algo (possibly again) */ if((int)sig[2+2] != dnskey_get_algo(dnskey, dnskey_idx)) { verbose(VERB_QUERY, "verify: wrong algorithm"); *reason = "signature has wrong algorithm"; return sec_status_bogus; } ktag = htons(dnskey_calc_keytag(dnskey, dnskey_idx)); if(memcmp(sig+2+16, &ktag, 2) != 0) { verbose(VERB_QUERY, "verify: wrong keytag"); *reason = "signature has wrong keytag"; return sec_status_bogus; } /* verify labels is in a valid range */ if((int)sig[2+3] > dname_signame_label_count(rrset->rk.dname)) { verbose(VERB_QUERY, "verify: labelcount out of range"); *reason = "signature labelcount out of range"; return sec_status_bogus; } /* original ttl, always ok */ if(!*buf_canon) { /* create rrset canonical format in buffer, ready for * signature */ if(!rrset_canonical(region, buf, rrset, sig+2, 18 + signer_len, sortree)) { log_err("verify: failed due to alloc error"); return sec_status_unchecked; } *buf_canon = 1; } /* check that dnskey is available */ dnskey_get_pubkey(dnskey, dnskey_idx, &key, &keylen); if(!key) { verbose(VERB_QUERY, "verify: short DNSKEY RR"); return sec_status_unchecked; } /* verify */ sec = verify_canonrrset(buf, (int)sig[2+2], sigblock, sigblock_len, key, keylen, reason); if(sec == sec_status_secure) { /* check if TTL is too high - reduce if so */ adjust_ttl(ve, now, rrset, sig+2+4, sig+2+8, sig+2+12); /* verify inception, expiration dates * Do this last so that if you ignore expired-sigs the * rest is sure to be OK. */ if(!check_dates(ve, now, sig+2+8, sig+2+12, reason)) { return sec_status_bogus; } } return sec; }