/** * Inser canonical owner name into buffer. * @param buf: buffer to insert into at current position. * @param k: rrset with its owner name. * @param sig: signature with signer name and label count. * must be length checked, at least 18 bytes long. * @param can_owner: position in buffer returned for future use. * @param can_owner_len: length of canonical owner name. */ static void insert_can_owner(sldns_buffer* buf, struct ub_packed_rrset_key* k, uint8_t* sig, uint8_t** can_owner, size_t* can_owner_len) { int rrsig_labels = (int)sig[3]; int fqdn_labels = dname_signame_label_count(k->rk.dname); *can_owner = sldns_buffer_current(buf); if(rrsig_labels == fqdn_labels) { /* no change */ sldns_buffer_write(buf, k->rk.dname, k->rk.dname_len); query_dname_tolower(*can_owner); *can_owner_len = k->rk.dname_len; return; } log_assert(rrsig_labels < fqdn_labels); /* *. | fqdn(rightmost rrsig_labels) */ if(rrsig_labels < fqdn_labels) { int i; uint8_t* nm = k->rk.dname; size_t len = k->rk.dname_len; /* so skip fqdn_labels-rrsig_labels */ for(i=0; i<fqdn_labels-rrsig_labels; i++) { dname_remove_label(&nm, &len); } *can_owner_len = len+2; sldns_buffer_write(buf, (uint8_t*)"\001*", 2); sldns_buffer_write(buf, nm, len); query_dname_tolower(*can_owner); } }
/** test dname_signame_label_count */ static void dname_test_sigcount(void) { unit_show_func("util/data/dname.c", "dname_signame_label_count"); unit_assert(dname_signame_label_count((uint8_t*)"\000") == 0); unit_assert(dname_signame_label_count((uint8_t*)"\001*\000") == 0); unit_assert(dname_signame_label_count((uint8_t*)"\003xom\000") == 1); unit_assert(dname_signame_label_count( (uint8_t*)"\001*\003xom\000") == 1); unit_assert(dname_signame_label_count( (uint8_t*)"\007example\003xom\000") == 2); unit_assert(dname_signame_label_count( (uint8_t*)"\001*\007example\003xom\000") == 2); unit_assert(dname_signame_label_count( (uint8_t*)"\003www\007example\003xom\000") == 3); unit_assert(dname_signame_label_count( (uint8_t*)"\001*\003www\007example\003xom\000") == 3); }
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; }