Esempio n. 1
0
/** 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"));
}
Esempio n. 2
0
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);
}
Esempio n. 3
0
/**
 * 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;
}
Esempio n. 4
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++;
	}
}
Esempio n. 5
0
/**
 * 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);
}
Esempio n. 7
0
/** 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);
}
Esempio n. 8
0
/** 
 * 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;
		}
	}
}
Esempio n. 9
0
/** 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;
}
Esempio n. 10
0
/** 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;
}
Esempio n. 11
0
/** 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++;
		}
	}
}
Esempio n. 12
0
/** 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++;
		}
	}
}
Esempio n. 13
0
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);
}
Esempio n. 14
0
/** 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++;
		}
	}
}
Esempio n. 15
0
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;
}
Esempio n. 16
0
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);
		}
	}
}
Esempio n. 17
0
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;
}
Esempio n. 19
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;
	}
}
Esempio n. 20
0
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;
}
Esempio n. 21
0
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;
}