示例#1
0
/** see if at least one RR is known (flags, algo) */
static int
nsec3_rrset_has_known(struct ub_packed_rrset_key* s)
{
	int r;
	for(r=0; r < (int)rrset_get_count(s); r++) {
		if(!nsec3_unknown_flags(s, r) && nsec3_known_algo(s, r))
			return 1;
	}
	return 0;
}
示例#2
0
int 
val_dsset_isusable(struct ub_packed_rrset_key* ds_rrset)
{
	size_t i;
	for(i=0; i<rrset_get_count(ds_rrset); i++) {
		if(ds_digest_algo_is_supported(ds_rrset, i) &&
			ds_key_algo_is_supported(ds_rrset, i))
			return 1;
	}
	return 0;
}
示例#3
0
/** verify that a DS RR hashes to a key and that key signs the set */
static enum sec_status
verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve, 
	struct ub_packed_rrset_key* dnskey_rrset, 
        struct ub_packed_rrset_key* ds_rrset, size_t ds_idx, char** reason)
{
	enum sec_status sec = sec_status_bogus;
	size_t i, num, numchecked = 0, numhashok = 0;
	num = rrset_get_count(dnskey_rrset);
	for(i=0; i<num; i++) {
		/* Skip DNSKEYs that don't match the basic criteria. */
		if(ds_get_key_algo(ds_rrset, ds_idx) 
		   != dnskey_get_algo(dnskey_rrset, i)
		   || dnskey_calc_keytag(dnskey_rrset, i)
		   != ds_get_keytag(ds_rrset, ds_idx)) {
			continue;
		}
		numchecked++;
		verbose(VERB_ALGO, "attempt DS match algo %d keytag %d",
			ds_get_key_algo(ds_rrset, ds_idx),
			ds_get_keytag(ds_rrset, ds_idx));

		/* Convert the candidate DNSKEY into a hash using the 
		 * same DS hash algorithm. */
		if(!ds_digest_match_dnskey(env, dnskey_rrset, i, ds_rrset, 
			ds_idx)) {
			verbose(VERB_ALGO, "DS match attempt failed");
			continue;
		}
		numhashok++;
		verbose(VERB_ALGO, "DS match digest ok, trying signature");

		/* Otherwise, we have a match! Make sure that the DNSKEY 
		 * verifies *with this key*  */
		sec = dnskey_verify_rrset(env, ve, dnskey_rrset, 
			dnskey_rrset, i, reason);
		if(sec == sec_status_secure) {
			return sec;
		}
		/* If it didn't validate with the DNSKEY, try the next one! */
	}
	if(numchecked == 0)
		algo_needs_reason(env, ds_get_key_algo(ds_rrset, ds_idx),
			reason, "no keys have a DS");
	else if(numhashok == 0)
		*reason = "DS hash mismatches key";
	else if(!*reason)
		*reason = "keyset not secured by DNSKEY that matches DS";
	return sec_status_bogus;
}
/** setup sig alg list from dnskey */
static void
setup_sigalg(struct ub_packed_rrset_key* dnskey, uint8_t* sigalg)
{
	uint8_t a[ALGO_NEEDS_MAX];
	size_t i, n = 0;
	memset(a, 0, sizeof(a));
	for(i=0; i<rrset_get_count(dnskey); i++) {
		uint8_t algo = (uint8_t)dnskey_get_algo(dnskey, i);
		if(a[algo] == 0) {
			a[algo] = 1;
			sigalg[n++] = algo;
		}
	}
	sigalg[n] = 0;
}
示例#5
0
int val_favorite_ds_algo(struct ub_packed_rrset_key* ds_rrset)
{
	size_t i, num = rrset_get_count(ds_rrset);
	int d, digest_algo = 0; /* DS digest algo 0 is not used. */
	/* find favorite algo, for now, highest number supported */
	for(i=0; i<num; i++) {
		if(!ds_digest_algo_is_supported(ds_rrset, i) ||
			!ds_key_algo_is_supported(ds_rrset, i)) {
			continue;
		}
		d = ds_get_digest_algo(ds_rrset, i);
		if(d > digest_algo)
			digest_algo = d;
	}
	return digest_algo;
}
示例#6
0
void algo_needs_init_dnskey_add(struct algo_needs* n,
        struct ub_packed_rrset_key* dnskey, uint8_t* sigalg)
{
	uint8_t algo;
	size_t i, total = n->num;
	size_t num = rrset_get_count(dnskey);

	for(i=0; i<num; i++) {
		algo = (uint8_t)dnskey_get_algo(dnskey, i);
		if(!dnskey_algo_id_is_supported((int)algo))
			continue;
		if(n->needs[algo] == 0) {
			n->needs[algo] = 1;
			sigalg[total] = algo;
			total++;
		}
	}
	sigalg[total] = 0;
	n->num = total;
}
示例#7
0
enum sec_status 
dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve, 
	time_t now, struct ub_packed_rrset_key* rrset, 
	struct ub_packed_rrset_key* dnskey, size_t sig_idx, 
	struct rbtree_t** sortree, char** reason)
{
	/* find matching keys and check them */
	enum sec_status sec = sec_status_bogus;
	uint16_t tag = rrset_get_sig_keytag(rrset, sig_idx);
	int algo = rrset_get_sig_algo(rrset, sig_idx);
	size_t i, num = rrset_get_count(dnskey);
	size_t numchecked = 0;
	int buf_canon = 0;
	verbose(VERB_ALGO, "verify sig %d %d", (int)tag, algo);
	if(!dnskey_algo_id_is_supported(algo)) {
		verbose(VERB_QUERY, "verify sig: unknown algorithm");
		return sec_status_insecure;
	}
	
	for(i=0; i<num; i++) {
		/* see if key matches keytag and algo */
		if(algo != dnskey_get_algo(dnskey, i) ||
			tag != dnskey_calc_keytag(dnskey, i))
			continue;
		numchecked ++;

		/* see if key verifies */
		sec = dnskey_verify_rrset_sig(env->scratch, 
			env->scratch_buffer, ve, now, rrset, dnskey, i, 
			sig_idx, sortree, &buf_canon, reason);
		if(sec == sec_status_secure)
			return sec;
	}
	if(numchecked == 0) {
		*reason = "signatures from unknown keys";
		verbose(VERB_QUERY, "verify: could not find appropriate key");
		return sec_status_bogus;
	}
	return sec_status_bogus;
}
示例#8
0
/** 
 * Iterate through NSEC3 list, per RR 
 * This routine gives the next RR in the list (or sets rrset null). 
 * Usage:
 *
 * size_t rrsetnum;
 * int rrnum;
 * struct ub_packed_rrset_key* rrset;
 * for(rrset=filter_first(filter, &rrsetnum, &rrnum); rrset; 
 *	rrset=filter_next(filter, &rrsetnum, &rrnum))
 *		do_stuff;
 * 
 * Also filters out 
 * 	o unknown flag NSEC3s
 * 	o unknown algorithm NSEC3s.
 * @param filter: nsec3 filter structure.
 * @param rrsetnum: in/out rrset number to look at.
 * @param rrnum: in/out rr number in rrset to look at.
 * @returns ptr to the next rrset (or NULL at end).
 */
static struct ub_packed_rrset_key*
filter_next(struct nsec3_filter* filter, size_t* rrsetnum, int* rrnum)
{
	size_t i;
	int r;
	uint8_t* nm;
	size_t nmlen;
	if(!filter->zone) /* empty list */
		return NULL;
	for(i=*rrsetnum; i<filter->num; i++) {
		/* see if RRset qualifies */
		if(ntohs(filter->list[i]->rk.type) != LDNS_RR_TYPE_NSEC3 ||
			ntohs(filter->list[i]->rk.rrset_class) != 
			filter->fclass) 
			continue;
		/* check RRset zone */
		nm = filter->list[i]->rk.dname;
		nmlen = filter->list[i]->rk.dname_len;
		dname_remove_label(&nm, &nmlen);
		if(query_dname_compare(nm, filter->zone) != 0)
			continue;
		if(i == *rrsetnum)
			r = (*rrnum) + 1; /* continue at next RR */
		else	r = 0;		/* new RRset start at first RR */
		for(; r < (int)rrset_get_count(filter->list[i]); r++) {
			/* skip unknown flags, algo */
			if(nsec3_unknown_flags(filter->list[i], r) ||
				!nsec3_known_algo(filter->list[i], r))
				continue;
			/* this one is a good target */
			*rrsetnum = i;
			*rrnum = r;
			return filter->list[i];
		}
	}
	return NULL;
}
示例#9
0
void algo_needs_init_ds(struct algo_needs* n, struct ub_packed_rrset_key* ds,
	int fav_ds_algo, uint8_t* sigalg)
{
	uint8_t algo;
	size_t i, total = 0;
	size_t num = rrset_get_count(ds);

	memset(n->needs, 0, sizeof(uint8_t)*ALGO_NEEDS_MAX);
	for(i=0; i<num; i++) {
		if(ds_get_digest_algo(ds, i) != fav_ds_algo)
			continue;
		algo = (uint8_t)ds_get_key_algo(ds, i);
		if(!dnskey_algo_id_is_supported((int)algo))
			continue;
		log_assert(algo != 0); /* we do not support 0 and is EOS */
		if(n->needs[algo] == 0) {
			n->needs[algo] = 1;
			sigalg[total] = algo;		
			total++;
		}
	}
	sigalg[total] = 0;
	n->num = total;
}
示例#10
0
enum sec_status 
val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve,
	struct ub_packed_rrset_key* dnskey_rrset,
	struct ub_packed_rrset_key* ds_rrset, char** reason)
{
	/* as long as this is false, we can consider this DS rrset to be
	 * equivalent to no DS rrset. */
	int has_useful_ds = 0, digest_algo, alg;
	struct algo_needs needs;
	size_t i, num;
	enum sec_status sec;

	if(dnskey_rrset->rk.dname_len != ds_rrset->rk.dname_len ||
		query_dname_compare(dnskey_rrset->rk.dname, ds_rrset->rk.dname)
		!= 0) {
		verbose(VERB_QUERY, "DNSKEY RRset did not match DS RRset "
			"by name");
		*reason = "DNSKEY RRset did not match DS RRset by name";
		return sec_status_bogus;
	}

	digest_algo = val_favorite_ds_algo(ds_rrset);
	algo_needs_init_ds(&needs, ds_rrset, digest_algo);
	num = rrset_get_count(ds_rrset);
	for(i=0; i<num; i++) {
		/* Check to see if we can understand this DS. 
		 * And check it is the strongest digest */
		if(!ds_digest_algo_is_supported(ds_rrset, i) ||
			!ds_key_algo_is_supported(ds_rrset, i) ||
			ds_get_digest_algo(ds_rrset, i) != digest_algo) {
			continue;
		}

		/* Once we see a single DS with a known digestID and 
		 * algorithm, we cannot return INSECURE (with a 
		 * "null" KeyEntry). */
		has_useful_ds = true;

		sec = verify_dnskeys_with_ds_rr(env, ve, dnskey_rrset, 
			ds_rrset, i, reason);
		if(sec == sec_status_secure) {
			if(algo_needs_set_secure(&needs,
				(uint8_t)ds_get_key_algo(ds_rrset, i))) {
				verbose(VERB_ALGO, "DS matched DNSKEY.");
				return sec_status_secure;
			}
		} else if(sec == sec_status_bogus) {
			algo_needs_set_bogus(&needs,
				(uint8_t)ds_get_key_algo(ds_rrset, i));
		}
	}

	/* None of the DS's worked out. */

	/* If no DSs were understandable, then this is OK. */
	if(!has_useful_ds) {
		verbose(VERB_ALGO, "No usable DS records were found -- "
			"treating as insecure.");
		return sec_status_insecure;
	}
	/* If any were understandable, then it is bad. */
	verbose(VERB_QUERY, "Failed to match any usable DS to a DNSKEY.");
	if((alg=algo_needs_missing(&needs)) != 0) {
		algo_needs_reason(env, alg, reason, "missing verification of "
			"DNSKEY signature");
	}
	return sec_status_bogus;
}
示例#11
0
enum sec_status 
val_verify_DNSKEY_with_TA(struct module_env* env, struct val_env* ve,
	struct ub_packed_rrset_key* dnskey_rrset,
	struct ub_packed_rrset_key* ta_ds,
	struct ub_packed_rrset_key* ta_dnskey, uint8_t* sigalg, char** reason)
{
	/* as long as this is false, we can consider this anchor to be
	 * equivalent to no anchor. */
	int has_useful_ta = 0, digest_algo = 0, alg;
	struct algo_needs needs;
	size_t i, num;
	enum sec_status sec;

	if(ta_ds && (dnskey_rrset->rk.dname_len != ta_ds->rk.dname_len ||
		query_dname_compare(dnskey_rrset->rk.dname, ta_ds->rk.dname)
		!= 0)) {
		verbose(VERB_QUERY, "DNSKEY RRset did not match DS RRset "
			"by name");
		*reason = "DNSKEY RRset did not match DS RRset by name";
		return sec_status_bogus;
	}
	if(ta_dnskey && (dnskey_rrset->rk.dname_len != ta_dnskey->rk.dname_len
	     || query_dname_compare(dnskey_rrset->rk.dname, ta_dnskey->rk.dname)
		!= 0)) {
		verbose(VERB_QUERY, "DNSKEY RRset did not match anchor RRset "
			"by name");
		*reason = "DNSKEY RRset did not match anchor RRset by name";
		return sec_status_bogus;
	}

	if(ta_ds)
		digest_algo = val_favorite_ds_algo(ta_ds);
	if(sigalg) {
		if(ta_ds)
			algo_needs_init_ds(&needs, ta_ds, digest_algo, sigalg);
		else	memset(&needs, 0, sizeof(needs));
		if(ta_dnskey)
			algo_needs_init_dnskey_add(&needs, ta_dnskey, sigalg);
	}
	if(ta_ds) {
	    num = rrset_get_count(ta_ds);
	    for(i=0; i<num; i++) {
		/* Check to see if we can understand this DS. 
		 * And check it is the strongest digest */
		if(!ds_digest_algo_is_supported(ta_ds, i) ||
			!ds_key_algo_is_supported(ta_ds, i) ||
			ds_get_digest_algo(ta_ds, i) != digest_algo)
			continue;

		/* Once we see a single DS with a known digestID and 
		 * algorithm, we cannot return INSECURE (with a 
		 * "null" KeyEntry). */
		has_useful_ta = 1;

		sec = verify_dnskeys_with_ds_rr(env, ve, dnskey_rrset, 
			ta_ds, i, reason);
		if(sec == sec_status_secure) {
			if(!sigalg || algo_needs_set_secure(&needs,
				(uint8_t)ds_get_key_algo(ta_ds, i))) {
				verbose(VERB_ALGO, "DS matched DNSKEY.");
				return sec_status_secure;
			}
		} else if(sigalg && sec == sec_status_bogus) {
			algo_needs_set_bogus(&needs,
				(uint8_t)ds_get_key_algo(ta_ds, i));
		}
	    }
	}

	/* None of the DS's worked out: check the DNSKEYs. */
	if(ta_dnskey) {
	    num = rrset_get_count(ta_dnskey);
	    for(i=0; i<num; i++) {
		/* Check to see if we can understand this DNSKEY */
		if(!dnskey_algo_is_supported(ta_dnskey, i))
			continue;

		/* we saw a useful TA */
		has_useful_ta = 1;

		sec = dnskey_verify_rrset(env, ve, dnskey_rrset,
			ta_dnskey, i, reason);
		if(sec == sec_status_secure) {
			if(!sigalg || algo_needs_set_secure(&needs,
				(uint8_t)dnskey_get_algo(ta_dnskey, i))) {
				verbose(VERB_ALGO, "anchor matched DNSKEY.");
				return sec_status_secure;
			}
		} else if(sigalg && sec == sec_status_bogus) {
			algo_needs_set_bogus(&needs,
				(uint8_t)dnskey_get_algo(ta_dnskey, i));
		}
	    }
	}

	/* If no DSs were understandable, then this is OK. */
	if(!has_useful_ta) {
		verbose(VERB_ALGO, "No usable trust anchors were found -- "
			"treating as insecure.");
		return sec_status_insecure;
	}
	/* If any were understandable, then it is bad. */
	verbose(VERB_QUERY, "Failed to match any usable anchor to a DNSKEY.");
	if(sigalg && (alg=algo_needs_missing(&needs)) != 0) {
		algo_needs_reason(env, alg, reason, "missing verification of "
			"DNSKEY signature");
	}
	return sec_status_bogus;
}
示例#12
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;
}