static void test_dns_name_equal_one(const char *a, const char *b, int ret) { int r; r = dns_name_equal(a, b); assert_se(r == ret); r = dns_name_equal(b, a); assert_se(r == ret); }
static inline isc_boolean_t match(dns_name_t *name1, dns_name_t *name2) { if (dns_name_iswildcard(name2)) return(dns_name_matcheswildcard(name1, name2)); return (dns_name_equal(name1, name2)); }
static void test_dns_name_apply_idna_one(const char *s, const char *result) { #ifdef HAVE_LIBIDN _cleanup_free_ char *buf = NULL; assert_se(dns_name_apply_idna(s, &buf) >= 0); assert_se(dns_name_equal(buf, result) > 0); #endif }
isc_boolean_t dns_aclelement_equal(dns_aclelement_t *ea, dns_aclelement_t *eb) { if (ea->type != eb->type) return (ISC_FALSE); switch (ea->type) { case dns_aclelementtype_ipprefix: if (ea->u.ip_prefix.prefixlen != eb->u.ip_prefix.prefixlen) return (ISC_FALSE); return (isc_netaddr_eqprefix(&ea->u.ip_prefix.address, &eb->u.ip_prefix.address, ea->u.ip_prefix.prefixlen)); case dns_aclelementtype_keyname: return (dns_name_equal(&ea->u.keyname, &eb->u.keyname)); case dns_aclelementtype_nestedacl: return (dns_acl_equal(ea->u.nestedacl, eb->u.nestedacl)); case dns_aclelementtype_localhost: case dns_aclelementtype_localnets: case dns_aclelementtype_any: return (ISC_TRUE); default: INSIST(0); return (ISC_FALSE); } }
static inline isc_boolean_t checkowner_in_a(ARGS_CHECKOWNER) { dns_name_t prefix, suffix; REQUIRE(type == dns_rdatatype_a); REQUIRE(rdclass == dns_rdataclass_in); UNUSED(type); UNUSED(rdclass); /* * Handle Active Diretory gc._msdcs.<forest> name. */ if (dns_name_countlabels(name) > 2U) { dns_name_init(&prefix, NULL); dns_name_init(&suffix, NULL); dns_name_split(name, dns_name_countlabels(name) - 2, &prefix, &suffix); if (dns_name_equal(&gc_msdcs, &prefix) && dns_name_ishostname(&suffix, ISC_FALSE)) return (ISC_TRUE); } return (dns_name_ishostname(name, wildcard)); }
static void test_dns_service_split_one(const char *joined, const char *a, const char *b, const char *c, int r) { _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL, *t = NULL; assert_se(dns_service_split(joined, &x, &y, &z) == r); assert_se(streq_ptr(x, a)); assert_se(streq_ptr(y, b)); assert_se(streq_ptr(z, c)); if (r < 0) return; if (y) { assert_se(dns_service_join(x, y, z, &t) == 0); assert_se(dns_name_equal(joined, t) > 0); } else assert_se(!x && dns_name_equal(z, joined) > 0); }
isc_result_t dns_tsigkey_find(dns_tsigkey_t **tsigkey, dns_name_t *name, dns_name_t *algorithm, dns_tsig_keyring_t *ring) { dns_tsigkey_t *key; isc_stdtime_t now; isc_result_t result; REQUIRE(tsigkey != NULL); REQUIRE(*tsigkey == NULL); REQUIRE(name != NULL); REQUIRE(ring != NULL); RWLOCK(&ring->lock, isc_rwlocktype_write); cleanup_ring(ring); RWUNLOCK(&ring->lock, isc_rwlocktype_write); isc_stdtime_get(&now); RWLOCK(&ring->lock, isc_rwlocktype_read); key = NULL; result = dns_rbt_findname(ring->keys, name, 0, NULL, (void *)&key); if (result == DNS_R_PARTIALMATCH || result == ISC_R_NOTFOUND) { RWUNLOCK(&ring->lock, isc_rwlocktype_read); return (ISC_R_NOTFOUND); } if (algorithm != NULL && !dns_name_equal(key->algorithm, algorithm)) { RWUNLOCK(&ring->lock, isc_rwlocktype_read); return (ISC_R_NOTFOUND); } if (key->inception != key->expire && isc_serial_lt(key->expire, now)) { /* * The key has expired. */ RWUNLOCK(&ring->lock, isc_rwlocktype_read); RWLOCK(&ring->lock, isc_rwlocktype_write); remove_fromring(key); RWUNLOCK(&ring->lock, isc_rwlocktype_write); return (ISC_R_NOTFOUND); } #if 0 /* * MPAXXX We really should look at the inception time. */ if (key->inception != key->expire && isc_serial_lt(key->inception, now)) { RWUNLOCK(&ring->lock, isc_rwlocktype_read); adjust_lru(key); return (ISC_R_NOTFOUND); } #endif isc_refcount_increment(&key->refs, NULL); RWUNLOCK(&ring->lock, isc_rwlocktype_read); adjust_lru(key); *tsigkey = key; return (ISC_R_SUCCESS); }
/* * Find the longest match of name in the table. * If match is found return ISC_TRUE. prefix, suffix and offset are updated. * If no match is found return ISC_FALSE. */ isc_boolean_t dns_compress_findglobal(dns_compress_t *cctx, const dns_name_t *name, dns_name_t *prefix, isc_uint16_t *offset) { dns_name_t tname, nname; dns_compressnode_t *node = NULL; unsigned int labels, hash, n; REQUIRE(VALID_CCTX(cctx)); REQUIRE(dns_name_isabsolute(name) == ISC_TRUE); REQUIRE(offset != NULL); if (cctx->count == 0) return (ISC_FALSE); labels = dns_name_countlabels(name); INSIST(labels > 0); dns_name_init(&tname, NULL); dns_name_init(&nname, NULL); for (n = 0; n < labels - 1; n++) { dns_name_getlabelsequence(name, n, labels - n, &tname); hash = dns_name_hash(&tname, ISC_FALSE) % DNS_COMPRESS_TABLESIZE; for (node = cctx->table[hash]; node != NULL; node = node->next) { NODENAME(node, &nname); if ((cctx->allowed & DNS_COMPRESS_CASESENSITIVE) != 0) { if (dns_name_caseequal(&nname, &tname)) break; } else { if (dns_name_equal(&nname, &tname)) break; } } if (node != NULL) break; } /* * If node == NULL, we found no match at all. */ if (node == NULL) return (ISC_FALSE); if (n == 0) dns_name_reset(prefix); else dns_name_getlabelsequence(name, 0, n, prefix); *offset = node->offset; return (ISC_TRUE); }
static inline isc_boolean_t checkowner_opt(ARGS_CHECKOWNER) { REQUIRE(type == 41); UNUSED(type); UNUSED(rdclass); UNUSED(wildcard); return (dns_name_equal(name, dns_rootname)); }
static void test_dns_name_between_one(const char *a, const char *b, const char *c, int ret) { int r; r = dns_name_between(a, b, c); assert_se(r == ret); r = dns_name_between(c, b, a); if (ret >= 0) assert_se(r == 0 || dns_name_equal(a, c) > 0); else assert_se(r == ret); }
static void test_dns_service_join_one(const char *a, const char *b, const char *c, int r, const char *d) { _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL, *t = NULL; assert_se(dns_service_join(a, b, c, &t) == r); assert_se(streq_ptr(t, d)); if (r < 0) return; assert_se(dns_service_split(t, &x, &y, &z) >= 0); assert_se(streq_ptr(a, x)); assert_se(streq_ptr(b, y)); assert_se(dns_name_equal(c, z) > 0); }
/* see if two dns records match */ static bool b9_record_match(struct dlz_bind9_data *state, struct dnsp_DnssrvRpcRecord *rec1, struct dnsp_DnssrvRpcRecord *rec2) { if (rec1->wType != rec2->wType) { return false; } /* see if this type is single valued */ if (b9_single_valued(rec1->wType)) { return true; } /* see if the data matches */ switch (rec1->wType) { case DNS_TYPE_A: return strcmp(rec1->data.ipv4, rec2->data.ipv4) == 0; case DNS_TYPE_AAAA: return strcmp(rec1->data.ipv6, rec2->data.ipv6) == 0; case DNS_TYPE_CNAME: return dns_name_equal(rec1->data.cname, rec2->data.cname); case DNS_TYPE_TXT: return strcmp(rec1->data.txt, rec2->data.txt) == 0; case DNS_TYPE_PTR: return strcmp(rec1->data.ptr, rec2->data.ptr) == 0; case DNS_TYPE_NS: return dns_name_equal(rec1->data.ns, rec2->data.ns); case DNS_TYPE_SRV: return rec1->data.srv.wPriority == rec2->data.srv.wPriority && rec1->data.srv.wWeight == rec2->data.srv.wWeight && rec1->data.srv.wPort == rec2->data.srv.wPort && dns_name_equal(rec1->data.srv.nameTarget, rec2->data.srv.nameTarget); case DNS_TYPE_MX: return rec1->data.mx.wPriority == rec2->data.mx.wPriority && dns_name_equal(rec1->data.mx.nameTarget, rec2->data.mx.nameTarget); case DNS_TYPE_HINFO: return strcmp(rec1->data.hinfo.cpu, rec2->data.hinfo.cpu) == 0 && strcmp(rec1->data.hinfo.os, rec2->data.hinfo.os) == 0; case DNS_TYPE_SOA: return dns_name_equal(rec1->data.soa.mname, rec2->data.soa.mname) && dns_name_equal(rec1->data.soa.rname, rec2->data.soa.rname) && rec1->data.soa.serial == rec2->data.soa.serial && rec1->data.soa.refresh == rec2->data.soa.refresh && rec1->data.soa.retry == rec2->data.soa.retry && rec1->data.soa.expire == rec2->data.soa.expire && rec1->data.soa.minimum == rec2->data.soa.minimum; default: state->log(ISC_LOG_ERROR, "samba b9_putrr: unhandled record type %u", rec1->wType); break; } return false; }
static void test_dns_name_apply_idna_one(const char *s, int expected, const char *result) { _cleanup_free_ char *buf = NULL; int r; r = dns_name_apply_idna(s, &buf); log_debug("dns_name_apply_idna: \"%s\" → %d/\"%s\" (expected %d/\"%s\")", s, r, strnull(buf), expected, strnull(result)); /* Different libidn2 versions are more and less accepting * of underscore-prefixed names. So let's list the lowest * expected return value. */ assert_se(r >= expected); if (expected == 1) assert_se(dns_name_equal(buf, result) == 1); }
isc_result_t dst_key_fromfile(dns_name_t *name, dns_keytag_t id, unsigned int alg, int type, const char *directory, isc_mem_t *mctx, dst_key_t **keyp) { isc_result_t result; char filename[ISC_DIR_NAMEMAX]; isc_buffer_t buf; dst_key_t *key; REQUIRE(dst_initialized == ISC_TRUE); REQUIRE(dns_name_isabsolute(name)); REQUIRE((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) != 0); REQUIRE(mctx != NULL); REQUIRE(keyp != NULL && *keyp == NULL); CHECKALG(alg); key = NULL; isc_buffer_init(&buf, filename, ISC_DIR_NAMEMAX); result = dst_key_getfilename(name, id, alg, type, NULL, mctx, &buf); if (result != ISC_R_SUCCESS) goto out; result = dst_key_fromnamedfile(filename, directory, type, mctx, &key); if (result != ISC_R_SUCCESS) goto out; result = computeid(key); if (result != ISC_R_SUCCESS) goto out; if (!dns_name_equal(name, key->key_name) || id != key->key_id || alg != key->key_alg) { result = DST_R_INVALIDPRIVATEKEY; goto out; } *keyp = key; result = ISC_R_SUCCESS; out: if ((key != NULL) && (result != ISC_R_SUCCESS)) dst_key_free(&key); return (result); }
void dns_diff_appendminimal(dns_diff_t *diff, dns_difftuple_t **tuplep) { dns_difftuple_t *ot, *next_ot; REQUIRE(DNS_DIFF_VALID(diff)); REQUIRE(DNS_DIFFTUPLE_VALID(*tuplep)); /* * Look for an existing tuple with the same owner name, * rdata, and TTL. If we are doing an addition and find a * deletion or vice versa, remove both the old and the * new tuple since they cancel each other out (assuming * that we never delete nonexistent data or add existing * data). * * If we find an old update of the same kind as * the one we are doing, there must be a programming * error. We report it but try to continue anyway. */ for (ot = ISC_LIST_HEAD(diff->tuples); ot != NULL; ot = next_ot) { next_ot = ISC_LIST_NEXT(ot, link); if (dns_name_equal(&ot->name, &(*tuplep)->name) && dns_rdata_compare(&ot->rdata, &(*tuplep)->rdata) == 0 && ot->ttl == (*tuplep)->ttl) { ISC_LIST_UNLINK(diff->tuples, ot, link); if ((*tuplep)->op == ot->op) { UNEXPECTED_ERROR(__FILE__, __LINE__, "unexpected non-minimal diff"); } else { dns_difftuple_free(tuplep); } dns_difftuple_free(&ot); break; } } if (*tuplep != NULL) { ISC_LIST_APPEND(diff->tuples, *tuplep, link); *tuplep = NULL; } ENSURE(*tuplep == NULL); }
isc_result_t dst_key_fromfile(dns_name_t *name, dns_keytag_t id, unsigned int alg, int type, const char *directory, isc_mem_t *mctx, dst_key_t **keyp) { char filename[ISC_DIR_NAMEMAX]; isc_buffer_t b; dst_key_t *key; isc_result_t result; REQUIRE(dst_initialized == ISC_TRUE); REQUIRE(dns_name_isabsolute(name)); REQUIRE((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) != 0); REQUIRE(mctx != NULL); REQUIRE(keyp != NULL && *keyp == NULL); CHECKALG(alg); isc_buffer_init(&b, filename, sizeof(filename)); result = buildfilename(name, id, alg, type, directory, &b); if (result != ISC_R_SUCCESS) return (result); key = NULL; result = dst_key_fromnamedfile(filename, type, mctx, &key); if (result != ISC_R_SUCCESS) return (result); result = computeid(key); if (result != ISC_R_SUCCESS) { dst_key_free(&key); return (result); } if (!dns_name_equal(name, key->key_name) || id != key->key_id || alg != key->key_alg) { dst_key_free(&key); return (DST_R_INVALIDPRIVATEKEY); } key->key_id = id; *keyp = key; return (ISC_R_SUCCESS); }
static unsigned int dst_alg_fromname(dns_name_t *algorithm) { if (dns_name_equal(algorithm, DNS_TSIG_HMACMD5_NAME)) { return (DST_ALG_HMACMD5); } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA1_NAME)) { return (DST_ALG_HMACSHA1); } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA224_NAME)) { return (DST_ALG_HMACSHA224); } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA256_NAME)) { return (DST_ALG_HMACSHA256); } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA384_NAME)) { return (DST_ALG_HMACSHA384); } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA512_NAME)) { return (DST_ALG_HMACSHA512); } else if (dns_name_equal(algorithm, DNS_TSIG_GSSAPI_NAME)) { return (DST_ALG_GSSAPI); } else if (dns_name_equal(algorithm, DNS_TSIG_GSSAPIMS_NAME)) { return (DST_ALG_GSSAPI); } else return (0); }
/* see if two dns records match */ bool dns_records_match(struct dnsp_DnssrvRpcRecord *rec1, struct dnsp_DnssrvRpcRecord *rec2) { if (rec1->wType != rec2->wType) { return false; } /* see if the data matches */ switch (rec1->wType) { case DNS_TYPE_A: return strcmp(rec1->data.ipv4, rec2->data.ipv4) == 0; case DNS_TYPE_AAAA: return strcmp(rec1->data.ipv6, rec2->data.ipv6) == 0; case DNS_TYPE_CNAME: return dns_name_equal(rec1->data.cname, rec2->data.cname); case DNS_TYPE_TXT: return strcmp(rec1->data.txt, rec2->data.txt) == 0; case DNS_TYPE_PTR: return strcmp(rec1->data.ptr, rec2->data.ptr) == 0; case DNS_TYPE_NS: return dns_name_equal(rec1->data.ns, rec2->data.ns); case DNS_TYPE_SRV: return rec1->data.srv.wPriority == rec2->data.srv.wPriority && rec1->data.srv.wWeight == rec2->data.srv.wWeight && rec1->data.srv.wPort == rec2->data.srv.wPort && dns_name_equal(rec1->data.srv.nameTarget, rec2->data.srv.nameTarget); case DNS_TYPE_MX: return rec1->data.mx.wPriority == rec2->data.mx.wPriority && dns_name_equal(rec1->data.mx.nameTarget, rec2->data.mx.nameTarget); case DNS_TYPE_HINFO: return strcmp(rec1->data.hinfo.cpu, rec2->data.hinfo.cpu) == 0 && strcmp(rec1->data.hinfo.os, rec2->data.hinfo.os) == 0; case DNS_TYPE_SOA: return dns_name_equal(rec1->data.soa.mname, rec2->data.soa.mname) && dns_name_equal(rec1->data.soa.rname, rec2->data.soa.rname) && rec1->data.soa.serial == rec2->data.soa.serial && rec1->data.soa.refresh == rec2->data.soa.refresh && rec1->data.soa.retry == rec2->data.soa.retry && rec1->data.soa.expire == rec2->data.soa.expire && rec1->data.soa.minimum == rec2->data.soa.minimum; default: break; } return false; }
static isc_result_t process_deletetkey(dns_message_t *msg, dns_name_t *signer, dns_name_t *name, dns_rdata_tkey_t *tkeyin, dns_rdata_tkey_t *tkeyout, dns_tsig_keyring_t *ring, dns_namelist_t *namelist) { isc_result_t result; dns_tsigkey_t *tsigkey = NULL; dns_name_t *identity; UNUSED(msg); UNUSED(namelist); result = dns_tsigkey_find(&tsigkey, name, &tkeyin->algorithm, ring); if (result != ISC_R_SUCCESS) { tkeyout->error = dns_tsigerror_badname; return (ISC_R_SUCCESS); } /* * Only allow a delete if the identity that created the key is the * same as the identity that signed the message. */ identity = dns_tsigkey_identity(tsigkey); if (identity == NULL || !dns_name_equal(identity, signer)) { dns_tsigkey_detach(&tsigkey); return (DNS_R_REFUSED); } /* * Set the key to be deleted when no references are left. If the key * was not generated with TKEY and is in the config file, it may be * reloaded later. */ dns_tsigkey_setdeleted(tsigkey); /* Release the reference */ dns_tsigkey_detach(&tsigkey); return (ISC_R_SUCCESS); }
isc_result_t dns_tsigkey_find(dns_tsigkey_t **tsigkey, dns_name_t *name, dns_name_t *algorithm, dns_tsig_keyring_t *ring) { dns_tsigkey_t *key; isc_stdtime_t now; isc_result_t result; REQUIRE(tsigkey != NULL); REQUIRE(*tsigkey == NULL); REQUIRE(name != NULL); REQUIRE(ring != NULL); isc_stdtime_get(&now); RWLOCK(&ring->lock, isc_rwlocktype_read); key = NULL; result = dns_rbt_findname(ring->keys, name, 0, NULL, (void *)&key); if (result == DNS_R_PARTIALMATCH || result == ISC_R_NOTFOUND) { RWUNLOCK(&ring->lock, isc_rwlocktype_read); return (ISC_R_NOTFOUND); } if (algorithm != NULL && !dns_name_equal(key->algorithm, algorithm)) { RWUNLOCK(&ring->lock, isc_rwlocktype_read); return (ISC_R_NOTFOUND); } if (key->inception != key->expire && key->expire < now) { /* * The key has expired. */ RWUNLOCK(&ring->lock, isc_rwlocktype_read); RWLOCK(&ring->lock, isc_rwlocktype_write); (void) dns_rbt_deletename(ring->keys, name, ISC_FALSE); RWUNLOCK(&ring->lock, isc_rwlocktype_write); return (ISC_R_NOTFOUND); } isc_refcount_increment(&key->refs, NULL); RWUNLOCK(&ring->lock, isc_rwlocktype_read); *tsigkey = key; return (ISC_R_SUCCESS); }
struct dns_server_tkey *dns_find_tkey(struct dns_server_tkey_store *store, const char *name) { struct dns_server_tkey *tkey = NULL; uint16_t i = 0; do { struct dns_server_tkey *tmp_key = store->tkeys[i]; i++; i %= TKEY_BUFFER_SIZE; if (tmp_key == NULL) { continue; } if (dns_name_equal(name, tmp_key->name)) { tkey = tmp_key; break; } } while (i != 0); return tkey; }
isc_result_t dns_tsigkey_create(dns_name_t *name, dns_name_t *algorithm, unsigned char *secret, int length, isc_boolean_t generated, dns_name_t *creator, isc_stdtime_t inception, isc_stdtime_t expire, isc_mem_t *mctx, dns_tsig_keyring_t *ring, dns_tsigkey_t **key) { dst_key_t *dstkey = NULL; isc_result_t result; REQUIRE(length >= 0); if (length > 0) REQUIRE(secret != NULL); if (!dns_name_equal(algorithm, DNS_TSIG_HMACMD5_NAME) && length > 0) return (DNS_R_BADALG); if (secret != NULL) { isc_buffer_t b; isc_buffer_init(&b, secret, length); isc_buffer_add(&b, length); result = dst_key_frombuffer(name, DST_ALG_HMACMD5, DNS_KEYOWNER_ENTITY, DNS_KEYPROTO_DNSSEC, dns_rdataclass_in, &b, mctx, &dstkey); if (result != ISC_R_SUCCESS) return (result); } result = dns_tsigkey_createfromkey(name, algorithm, dstkey, generated, creator, inception, expire, mctx, ring, key); if (result != ISC_R_SUCCESS && dstkey != NULL) dst_key_free(&dstkey); return (result); }
void dns_badcache_flushname(dns_badcache_t *bc, dns_name_t *name) { dns_bcentry_t *bad, *prev, *next; isc_result_t result; isc_time_t now; unsigned int i; REQUIRE(VALID_BADCACHE(bc)); REQUIRE(name != NULL); LOCK(&bc->lock); result = isc_time_now(&now); if (result != ISC_R_SUCCESS) isc_time_settoepoch(&now); i = dns_name_hash(name, ISC_FALSE) % bc->size; prev = NULL; for (bad = bc->table[i]; bad != NULL; bad = next) { int n; next = bad->next; n = isc_time_compare(&bad->expire, &now); if (n < 0 || dns_name_equal(name, &bad->name)) { if (prev == NULL) bc->table[i] = bad->next; else prev->next = bad->next; isc_mem_put(bc->mctx, bad, sizeof(*bad) + bad->name.length); bc->count--; } else prev = bad; } UNLOCK(&bc->lock); }
isc_boolean_t dns_ssutable_checkrules(dns_ssutable_t *table, dns_name_t *signer, dns_name_t *name, isc_netaddr_t *tcpaddr, dns_rdatatype_t type, const dst_key_t *key) { dns_ssurule_t *rule; unsigned int i; dns_fixedname_t fixed; dns_name_t *wildcard; dns_name_t *tcpself; dns_name_t *stfself; isc_result_t result; REQUIRE(VALID_SSUTABLE(table)); REQUIRE(signer == NULL || dns_name_isabsolute(signer)); REQUIRE(dns_name_isabsolute(name)); if (signer == NULL && tcpaddr == NULL) return (ISC_FALSE); for (rule = ISC_LIST_HEAD(table->rules); rule != NULL; rule = ISC_LIST_NEXT(rule, link)) { switch (rule->matchtype) { case DNS_SSUMATCHTYPE_NAME: case DNS_SSUMATCHTYPE_SUBDOMAIN: case DNS_SSUMATCHTYPE_WILDCARD: case DNS_SSUMATCHTYPE_SELF: case DNS_SSUMATCHTYPE_SELFSUB: case DNS_SSUMATCHTYPE_SELFWILD: if (signer == NULL) continue; if (dns_name_iswildcard(rule->identity)) { if (!dns_name_matcheswildcard(signer, rule->identity)) continue; } else { if (!dns_name_equal(signer, rule->identity)) continue; } break; case DNS_SSUMATCHTYPE_SELFKRB5: case DNS_SSUMATCHTYPE_SELFMS: case DNS_SSUMATCHTYPE_SUBDOMAINKRB5: case DNS_SSUMATCHTYPE_SUBDOMAINMS: if (signer == NULL) continue; break; case DNS_SSUMATCHTYPE_TCPSELF: case DNS_SSUMATCHTYPE_6TO4SELF: if (tcpaddr == NULL) continue; break; } switch (rule->matchtype) { case DNS_SSUMATCHTYPE_NAME: if (!dns_name_equal(name, rule->name)) continue; break; case DNS_SSUMATCHTYPE_SUBDOMAIN: if (!dns_name_issubdomain(name, rule->name)) continue; break; case DNS_SSUMATCHTYPE_WILDCARD: if (!dns_name_matcheswildcard(name, rule->name)) continue; break; case DNS_SSUMATCHTYPE_SELF: if (!dns_name_equal(signer, name)) continue; break; case DNS_SSUMATCHTYPE_SELFSUB: if (!dns_name_issubdomain(name, signer)) continue; break; case DNS_SSUMATCHTYPE_SELFWILD: dns_fixedname_init(&fixed); wildcard = dns_fixedname_name(&fixed); result = dns_name_concatenate(dns_wildcardname, signer, wildcard, NULL); if (result != ISC_R_SUCCESS) continue; if (!dns_name_matcheswildcard(name, wildcard)) continue; break; case DNS_SSUMATCHTYPE_SELFKRB5: if (!dst_gssapi_identitymatchesrealmkrb5(signer, name, rule->identity)) continue; break; case DNS_SSUMATCHTYPE_SELFMS: if (!dst_gssapi_identitymatchesrealmms(signer, name, rule->identity)) continue; break; case DNS_SSUMATCHTYPE_SUBDOMAINKRB5: if (!dns_name_issubdomain(name, rule->name)) continue; if (!dst_gssapi_identitymatchesrealmkrb5(signer, NULL, rule->identity)) continue; break; case DNS_SSUMATCHTYPE_SUBDOMAINMS: if (!dns_name_issubdomain(name, rule->name)) continue; if (!dst_gssapi_identitymatchesrealmms(signer, NULL, rule->identity)) continue; break; case DNS_SSUMATCHTYPE_TCPSELF: dns_fixedname_init(&fixed); tcpself = dns_fixedname_name(&fixed); reverse_from_address(tcpself, tcpaddr); if (dns_name_iswildcard(rule->identity)) { if (!dns_name_matcheswildcard(tcpself, rule->identity)) continue; } else { if (!dns_name_equal(tcpself, rule->identity)) continue; } if (!dns_name_equal(tcpself, name)) continue; break; case DNS_SSUMATCHTYPE_6TO4SELF: dns_fixedname_init(&fixed); stfself = dns_fixedname_name(&fixed); stf_from_address(stfself, tcpaddr); if (dns_name_iswildcard(rule->identity)) { if (!dns_name_matcheswildcard(stfself, rule->identity)) continue; } else { if (!dns_name_equal(stfself, rule->identity)) continue; } if (!dns_name_equal(stfself, name)) continue; break; case DNS_SSUMATCHTYPE_EXTERNAL: if (!dns_ssu_external_match(rule->identity, signer, name, tcpaddr, type, key, table->mctx)) continue; break; case DNS_SSUMATCHTYPE_DLZ: if (!dns_dlz_ssumatch(table->dlzdatabase, signer, name, tcpaddr, type, key)) continue; break; } if (rule->ntypes == 0) { /* * If this is a DLZ rule, then the DLZ ssu * checks will have already checked * the type. */ if (rule->matchtype != DNS_SSUMATCHTYPE_DLZ && !isusertype(type)) continue; } else { for (i = 0; i < rule->ntypes; i++) { if (rule->types[i] == dns_rdatatype_any || rule->types[i] == type) break; } if (i == rule->ntypes) continue; } return (rule->grant); } return (ISC_FALSE); }
void ns_xfr_start(ns_client_t *client, dns_rdatatype_t reqtype) { isc_result_t result; dns_name_t *question_name; dns_rdataset_t *question_rdataset; dns_zone_t *zone = NULL, *raw = NULL, *mayberaw; dns_db_t *db = NULL; dns_dbversion_t *ver = NULL; dns_rdataclass_t question_class; rrstream_t *soa_stream = NULL; rrstream_t *data_stream = NULL; rrstream_t *stream = NULL; dns_difftuple_t *current_soa_tuple = NULL; dns_name_t *soa_name; dns_rdataset_t *soa_rdataset; dns_rdata_t soa_rdata = DNS_RDATA_INIT; isc_boolean_t have_soa = ISC_FALSE; const char *mnemonic = NULL; isc_mem_t *mctx = client->mctx; dns_message_t *request = client->message; xfrout_ctx_t *xfr = NULL; isc_quota_t *quota = NULL; dns_transfer_format_t format = client->view->transfer_format; isc_netaddr_t na; dns_peer_t *peer = NULL; isc_buffer_t *tsigbuf = NULL; char *journalfile; char msg[NS_CLIENT_ACLMSGSIZE("zone transfer")]; char keyname[DNS_NAME_FORMATSIZE]; isc_boolean_t is_poll = ISC_FALSE; isc_boolean_t is_dlz = ISC_FALSE; isc_boolean_t is_ixfr = ISC_FALSE; isc_uint32_t begin_serial = 0, current_serial; switch (reqtype) { case dns_rdatatype_axfr: mnemonic = "AXFR"; break; case dns_rdatatype_ixfr: mnemonic = "IXFR"; break; default: INSIST(0); break; } ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT, NS_LOGMODULE_XFER_OUT, ISC_LOG_DEBUG(6), "%s request", mnemonic); /* * Apply quota. */ result = isc_quota_attach(&ns_g_server->xfroutquota, "a); if (result != ISC_R_SUCCESS) { isc_log_write(XFROUT_COMMON_LOGARGS, ISC_LOG_WARNING, "%s request denied: %s", mnemonic, isc_result_totext(result)); goto failure; } /* * Interpret the question section. */ result = dns_message_firstname(request, DNS_SECTION_QUESTION); INSIST(result == ISC_R_SUCCESS); /* * The question section must contain exactly one question, and * it must be for AXFR/IXFR as appropriate. */ question_name = NULL; dns_message_currentname(request, DNS_SECTION_QUESTION, &question_name); question_rdataset = ISC_LIST_HEAD(question_name->list); question_class = question_rdataset->rdclass; INSIST(question_rdataset->type == reqtype); if (ISC_LIST_NEXT(question_rdataset, link) != NULL) FAILC(DNS_R_FORMERR, "multiple questions"); result = dns_message_nextname(request, DNS_SECTION_QUESTION); if (result != ISC_R_NOMORE) FAILC(DNS_R_FORMERR, "multiple questions"); result = dns_zt_find(client->view->zonetable, question_name, 0, NULL, &zone); if (result != ISC_R_SUCCESS) { /* * Normal zone table does not have a match. * Try the DLZ database */ // Temporary: only searching the first DLZ database if (! ISC_LIST_EMPTY(client->view->dlz_searched)) { result = dns_dlzallowzonexfr(client->view, question_name, &client->peeraddr, &db); pfilter_notify(result, client, "zonexfr"); if (result == ISC_R_NOPERM) { char _buf1[DNS_NAME_FORMATSIZE]; char _buf2[DNS_RDATACLASS_FORMATSIZE]; result = DNS_R_REFUSED; dns_name_format(question_name, _buf1, sizeof(_buf1)); dns_rdataclass_format(question_class, _buf2, sizeof(_buf2)); ns_client_log(client, DNS_LOGCATEGORY_SECURITY, NS_LOGMODULE_XFER_OUT, ISC_LOG_ERROR, "zone transfer '%s/%s' denied", _buf1, _buf2); goto failure; } if (result != ISC_R_SUCCESS) FAILQ(DNS_R_NOTAUTH, "non-authoritative zone", question_name, question_class); is_dlz = ISC_TRUE; } else { /* * not DLZ and not in normal zone table, we are * not authoritative */ FAILQ(DNS_R_NOTAUTH, "non-authoritative zone", question_name, question_class); } } else { /* zone table has a match */ switch(dns_zone_gettype(zone)) { /* Master and slave zones are OK for transfer. */ case dns_zone_master: case dns_zone_slave: case dns_zone_dlz: break; default: FAILQ(DNS_R_NOTAUTH, "non-authoritative zone", question_name, question_class); } CHECK(dns_zone_getdb(zone, &db)); dns_db_currentversion(db, &ver); } xfrout_log1(client, question_name, question_class, ISC_LOG_DEBUG(6), "%s question section OK", mnemonic); /* * Check the authority section. Look for a SOA record with * the same name and class as the question. */ for (result = dns_message_firstname(request, DNS_SECTION_AUTHORITY); result == ISC_R_SUCCESS; result = dns_message_nextname(request, DNS_SECTION_AUTHORITY)) { soa_name = NULL; dns_message_currentname(request, DNS_SECTION_AUTHORITY, &soa_name); /* * Ignore data whose owner name is not the zone apex. */ if (! dns_name_equal(soa_name, question_name)) continue; for (soa_rdataset = ISC_LIST_HEAD(soa_name->list); soa_rdataset != NULL; soa_rdataset = ISC_LIST_NEXT(soa_rdataset, link)) { /* * Ignore non-SOA data. */ if (soa_rdataset->type != dns_rdatatype_soa) continue; if (soa_rdataset->rdclass != question_class) continue; CHECK(dns_rdataset_first(soa_rdataset)); dns_rdataset_current(soa_rdataset, &soa_rdata); result = dns_rdataset_next(soa_rdataset); if (result == ISC_R_SUCCESS) FAILC(DNS_R_FORMERR, "IXFR authority section " "has multiple SOAs"); have_soa = ISC_TRUE; goto got_soa; } } got_soa: if (result != ISC_R_NOMORE) CHECK(result); xfrout_log1(client, question_name, question_class, ISC_LOG_DEBUG(6), "%s authority section OK", mnemonic); /* * If not a DLZ zone, decide whether to allow this transfer. */ if (!is_dlz) { ns_client_aclmsg("zone transfer", question_name, reqtype, client->view->rdclass, msg, sizeof(msg)); CHECK(ns_client_checkacl(client, NULL, msg, dns_zone_getxfracl(zone), ISC_TRUE, ISC_LOG_ERROR)); } /* * AXFR over UDP is not possible. */ if (reqtype == dns_rdatatype_axfr && (client->attributes & NS_CLIENTATTR_TCP) == 0) FAILC(DNS_R_FORMERR, "attempted AXFR over UDP"); /* * Look up the requesting server in the peer table. */ isc_netaddr_fromsockaddr(&na, &client->peeraddr); (void)dns_peerlist_peerbyaddr(client->view->peers, &na, &peer); /* * Decide on the transfer format (one-answer or many-answers). */ if (peer != NULL) (void)dns_peer_gettransferformat(peer, &format); /* * Get a dynamically allocated copy of the current SOA. */ if (is_dlz) dns_db_currentversion(db, &ver); CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_EXISTS, ¤t_soa_tuple)); current_serial = dns_soa_getserial(¤t_soa_tuple->rdata); if (reqtype == dns_rdatatype_ixfr) { isc_boolean_t provide_ixfr; /* * Outgoing IXFR may have been disabled for this peer * or globally. */ provide_ixfr = client->view->provideixfr; if (peer != NULL) (void) dns_peer_getprovideixfr(peer, &provide_ixfr); if (provide_ixfr == ISC_FALSE) goto axfr_fallback; if (! have_soa) FAILC(DNS_R_FORMERR, "IXFR request missing SOA"); begin_serial = dns_soa_getserial(&soa_rdata); /* * RFC1995 says "If an IXFR query with the same or * newer version number than that of the server * is received, it is replied to with a single SOA * record of the server's current version, just as * in AXFR". The claim about AXFR is incorrect, * but other than that, we do as the RFC says. * * Sending a single SOA record is also how we refuse * IXFR over UDP (currently, we always do). */ if (DNS_SERIAL_GE(begin_serial, current_serial) || (client->attributes & NS_CLIENTATTR_TCP) == 0) { CHECK(soa_rrstream_create(mctx, db, ver, &stream)); is_poll = ISC_TRUE; goto have_stream; } journalfile = is_dlz ? NULL : dns_zone_getjournal(zone); if (journalfile != NULL) result = ixfr_rrstream_create(mctx, journalfile, begin_serial, current_serial, &data_stream); else result = ISC_R_NOTFOUND; if (result == ISC_R_NOTFOUND || result == ISC_R_RANGE) { xfrout_log1(client, question_name, question_class, ISC_LOG_DEBUG(4), "IXFR version not in journal, " "falling back to AXFR"); mnemonic = "AXFR-style IXFR"; goto axfr_fallback; } CHECK(result); is_ixfr = ISC_TRUE; } else { axfr_fallback: CHECK(axfr_rrstream_create(mctx, db, ver, &data_stream)); } /* * Bracket the data stream with SOAs. */ CHECK(soa_rrstream_create(mctx, db, ver, &soa_stream)); CHECK(compound_rrstream_create(mctx, &soa_stream, &data_stream, &stream)); soa_stream = NULL; data_stream = NULL; have_stream: CHECK(dns_message_getquerytsig(request, mctx, &tsigbuf)); /* * Create the xfrout context object. This transfers the ownership * of "stream", "db", "ver", and "quota" to the xfrout context object. */ if (is_dlz) CHECK(xfrout_ctx_create(mctx, client, request->id, question_name, reqtype, question_class, zone, db, ver, quota, stream, dns_message_gettsigkey(request), tsigbuf, 3600, 3600, (format == dns_many_answers) ? ISC_TRUE : ISC_FALSE, &xfr)); else CHECK(xfrout_ctx_create(mctx, client, request->id, question_name, reqtype, question_class, zone, db, ver, quota, stream, dns_message_gettsigkey(request), tsigbuf, dns_zone_getmaxxfrout(zone), dns_zone_getidleout(zone), (format == dns_many_answers) ? ISC_TRUE : ISC_FALSE, &xfr)); xfr->mnemonic = mnemonic; stream = NULL; quota = NULL; CHECK(xfr->stream->methods->first(xfr->stream)); if (xfr->tsigkey != NULL) dns_name_format(&xfr->tsigkey->name, keyname, sizeof(keyname)); else keyname[0] = '\0'; if (is_poll) xfrout_log1(client, question_name, question_class, ISC_LOG_DEBUG(1), "IXFR poll up to date%s%s", (xfr->tsigkey != NULL) ? ": TSIG " : "", keyname); else if (is_ixfr) xfrout_log1(client, question_name, question_class, ISC_LOG_INFO, "%s started%s%s (serial %u -> %u)", mnemonic, (xfr->tsigkey != NULL) ? ": TSIG " : "", keyname, begin_serial, current_serial); else xfrout_log1(client, question_name, question_class, ISC_LOG_INFO, "%s started%s%s (serial %u)", mnemonic, (xfr->tsigkey != NULL) ? ": TSIG " : "", keyname, current_serial); if (zone != NULL) { dns_zone_getraw(zone, &raw); mayberaw = (raw != NULL) ? raw : zone; if ((client->attributes & NS_CLIENTATTR_WANTEXPIRE) != 0 && dns_zone_gettype(mayberaw) == dns_zone_slave) { isc_time_t expiretime; isc_uint32_t secs; dns_zone_getexpiretime(zone, &expiretime); secs = isc_time_seconds(&expiretime); if (secs >= client->now && result == ISC_R_SUCCESS) { client->attributes |= NS_CLIENTATTR_HAVEEXPIRE; client->expire = secs - client->now; } } if (raw != NULL) dns_zone_detach(&raw); } /* * Hand the context over to sendstream(). Set xfr to NULL; * sendstream() is responsible for either passing the * context on to a later event handler or destroying it. */ sendstream(xfr); xfr = NULL; result = ISC_R_SUCCESS; failure: if (result == DNS_R_REFUSED) inc_stats(zone, dns_nsstatscounter_xfrrej); if (quota != NULL) isc_quota_detach("a); if (current_soa_tuple != NULL) dns_difftuple_free(¤t_soa_tuple); if (stream != NULL) stream->methods->destroy(&stream); if (soa_stream != NULL) soa_stream->methods->destroy(&soa_stream); if (data_stream != NULL) data_stream->methods->destroy(&data_stream); if (ver != NULL) dns_db_closeversion(db, &ver, ISC_FALSE); if (db != NULL) dns_db_detach(&db); if (zone != NULL) dns_zone_detach(&zone); /* XXX kludge */ if (xfr != NULL) { xfrout_fail(xfr, result, "setting up zone transfer"); } else if (result != ISC_R_SUCCESS) { ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT, NS_LOGMODULE_XFER_OUT, ISC_LOG_DEBUG(3), "zone transfer setup failed"); ns_client_error(client, result); } }
isc_result_t dns_ncache_getsigrdataset(dns_rdataset_t *ncacherdataset, dns_name_t *name, dns_rdatatype_t covers, dns_rdataset_t *rdataset) { dns_name_t tname; dns_rdata_rrsig_t rrsig; dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdataset_t clone; dns_rdatatype_t type; dns_trust_t trust = dns_trust_none; isc_buffer_t source; isc_region_t remaining, sigregion; isc_result_t result; unsigned char *raw; unsigned int count; REQUIRE(ncacherdataset != NULL); REQUIRE(ncacherdataset->type == 0); REQUIRE((ncacherdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0); REQUIRE(name != NULL); REQUIRE(!dns_rdataset_isassociated(rdataset)); dns_rdataset_init(&clone); dns_rdataset_clone(ncacherdataset, &clone); result = dns_rdataset_first(&clone); while (result == ISC_R_SUCCESS) { dns_rdataset_current(&clone, &rdata); isc_buffer_init(&source, rdata.data, rdata.length); isc_buffer_add(&source, rdata.length); dns_name_init(&tname, NULL); isc_buffer_remainingregion(&source, &remaining); dns_name_fromregion(&tname, &remaining); INSIST(remaining.length >= tname.length); isc_buffer_forward(&source, tname.length); isc_region_consume(&remaining, tname.length); INSIST(remaining.length >= 2); type = isc_buffer_getuint16(&source); isc_region_consume(&remaining, 2); if (type != dns_rdatatype_rrsig || !dns_name_equal(&tname, name)) { result = dns_rdataset_next(&clone); dns_rdata_reset(&rdata); continue; } INSIST(remaining.length >= 1); trust = isc_buffer_getuint8(&source); INSIST(trust <= dns_trust_ultimate); isc_region_consume(&remaining, 1); raw = remaining.base; count = raw[0] * 256 + raw[1]; INSIST(count > 0); raw += 2; sigregion.length = raw[0] * 256 + raw[1]; raw += 2; sigregion.base = raw; dns_rdata_reset(&rdata); dns_rdata_fromregion(&rdata, rdataset->rdclass, dns_rdatatype_rrsig, &sigregion); (void)dns_rdata_tostruct(&rdata, &rrsig, NULL); if (rrsig.covered == covers) { isc_buffer_remainingregion(&source, &remaining); break; } result = dns_rdataset_next(&clone); dns_rdata_reset(&rdata); } dns_rdataset_disassociate(&clone); if (result == ISC_R_NOMORE) return (ISC_R_NOTFOUND); if (result != ISC_R_SUCCESS) return (result); INSIST(remaining.length != 0); rdataset->methods = &rdataset_methods; rdataset->rdclass = ncacherdataset->rdclass; rdataset->type = dns_rdatatype_rrsig; rdataset->covers = covers; rdataset->ttl = ncacherdataset->ttl; rdataset->trust = trust; rdataset->private1 = NULL; rdataset->private2 = NULL; rdataset->private3 = remaining.base; /* * Reset iterator state. */ rdataset->privateuint4 = 0; rdataset->private5 = NULL; rdataset->private6 = NULL; return (ISC_R_SUCCESS); }
isc_result_t dns_ncache_getrdataset(dns_rdataset_t *ncacherdataset, dns_name_t *name, dns_rdatatype_t type, dns_rdataset_t *rdataset) { isc_result_t result; dns_rdata_t rdata = DNS_RDATA_INIT; isc_region_t remaining; isc_buffer_t source; dns_name_t tname; dns_rdatatype_t ttype; dns_trust_t trust = dns_trust_none; dns_rdataset_t clone; REQUIRE(ncacherdataset != NULL); REQUIRE(ncacherdataset->type == 0); REQUIRE((ncacherdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0); REQUIRE(name != NULL); REQUIRE(!dns_rdataset_isassociated(rdataset)); REQUIRE(type != dns_rdatatype_rrsig); dns_rdataset_init(&clone); dns_rdataset_clone(ncacherdataset, &clone); result = dns_rdataset_first(&clone); while (result == ISC_R_SUCCESS) { dns_rdataset_current(&clone, &rdata); isc_buffer_init(&source, rdata.data, rdata.length); isc_buffer_add(&source, rdata.length); dns_name_init(&tname, NULL); isc_buffer_remainingregion(&source, &remaining); dns_name_fromregion(&tname, &remaining); INSIST(remaining.length >= tname.length); isc_buffer_forward(&source, tname.length); remaining.length -= tname.length; INSIST(remaining.length >= 3); ttype = isc_buffer_getuint16(&source); if (ttype == type && dns_name_equal(&tname, name)) { trust = isc_buffer_getuint8(&source); INSIST(trust <= dns_trust_ultimate); isc_buffer_remainingregion(&source, &remaining); break; } result = dns_rdataset_next(&clone); dns_rdata_reset(&rdata); } dns_rdataset_disassociate(&clone); if (result == ISC_R_NOMORE) return (ISC_R_NOTFOUND); if (result != ISC_R_SUCCESS) return (result); INSIST(remaining.length != 0); rdataset->methods = &rdataset_methods; rdataset->rdclass = ncacherdataset->rdclass; rdataset->type = type; rdataset->covers = 0; rdataset->ttl = ncacherdataset->ttl; rdataset->trust = trust; rdataset->private1 = NULL; rdataset->private2 = NULL; rdataset->private3 = remaining.base; /* * Reset iterator state. */ rdataset->privateuint4 = 0; rdataset->private5 = NULL; rdataset->private6 = NULL; return (ISC_R_SUCCESS); }
isc_result_t dns_tsigkey_createfromkey(dns_name_t *name, dns_name_t *algorithm, dst_key_t *dstkey, isc_boolean_t generated, dns_name_t *creator, isc_stdtime_t inception, isc_stdtime_t expire, isc_mem_t *mctx, dns_tsig_keyring_t *ring, dns_tsigkey_t **key) { dns_tsigkey_t *tkey; isc_result_t ret; unsigned int refs = 0; REQUIRE(key == NULL || *key == NULL); REQUIRE(name != NULL); REQUIRE(algorithm != NULL); REQUIRE(mctx != NULL); REQUIRE(key != NULL || ring != NULL); tkey = (dns_tsigkey_t *) isc_mem_get(mctx, sizeof(dns_tsigkey_t)); if (tkey == NULL) return (ISC_R_NOMEMORY); dns_name_init(&tkey->name, NULL); ret = dns_name_dup(name, mctx, &tkey->name); if (ret != ISC_R_SUCCESS) goto cleanup_key; (void)dns_name_downcase(&tkey->name, &tkey->name, NULL); if (dns_name_equal(algorithm, DNS_TSIG_HMACMD5_NAME)) { tkey->algorithm = DNS_TSIG_HMACMD5_NAME; if (dstkey != NULL && dst_key_alg(dstkey) != DST_ALG_HMACMD5) { ret = DNS_R_BADALG; goto cleanup_name; } } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA1_NAME)) { tkey->algorithm = DNS_TSIG_HMACSHA1_NAME; if (dstkey != NULL && dst_key_alg(dstkey) != DST_ALG_HMACSHA1) { ret = DNS_R_BADALG; goto cleanup_name; } } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA224_NAME)) { tkey->algorithm = DNS_TSIG_HMACSHA224_NAME; if (dstkey != NULL && dst_key_alg(dstkey) != DST_ALG_HMACSHA224) { ret = DNS_R_BADALG; goto cleanup_name; } } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA256_NAME)) { tkey->algorithm = DNS_TSIG_HMACSHA256_NAME; if (dstkey != NULL && dst_key_alg(dstkey) != DST_ALG_HMACSHA256) { ret = DNS_R_BADALG; goto cleanup_name; } } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA384_NAME)) { tkey->algorithm = DNS_TSIG_HMACSHA384_NAME; if (dstkey != NULL && dst_key_alg(dstkey) != DST_ALG_HMACSHA384) { ret = DNS_R_BADALG; goto cleanup_name; } } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA512_NAME)) { tkey->algorithm = DNS_TSIG_HMACSHA512_NAME; if (dstkey != NULL && dst_key_alg(dstkey) != DST_ALG_HMACSHA512) { ret = DNS_R_BADALG; goto cleanup_name; } } else if (dns_name_equal(algorithm, DNS_TSIG_GSSAPI_NAME)) { tkey->algorithm = DNS_TSIG_GSSAPI_NAME; if (dstkey != NULL && dst_key_alg(dstkey) != DST_ALG_GSSAPI) { ret = DNS_R_BADALG; goto cleanup_name; } } else if (dns_name_equal(algorithm, DNS_TSIG_GSSAPIMS_NAME)) { tkey->algorithm = DNS_TSIG_GSSAPIMS_NAME; if (dstkey != NULL && dst_key_alg(dstkey) != DST_ALG_GSSAPI) { ret = DNS_R_BADALG; goto cleanup_name; } } else { if (dstkey != NULL) { ret = DNS_R_BADALG; goto cleanup_name; } tkey->algorithm = isc_mem_get(mctx, sizeof(dns_name_t)); if (tkey->algorithm == NULL) { ret = ISC_R_NOMEMORY; goto cleanup_name; } dns_name_init(tkey->algorithm, NULL); ret = dns_name_dup(algorithm, mctx, tkey->algorithm); if (ret != ISC_R_SUCCESS) goto cleanup_algorithm; (void)dns_name_downcase(tkey->algorithm, tkey->algorithm, NULL); } if (creator != NULL) { tkey->creator = isc_mem_get(mctx, sizeof(dns_name_t)); if (tkey->creator == NULL) { ret = ISC_R_NOMEMORY; goto cleanup_algorithm; } dns_name_init(tkey->creator, NULL); ret = dns_name_dup(creator, mctx, tkey->creator); if (ret != ISC_R_SUCCESS) { isc_mem_put(mctx, tkey->creator, sizeof(dns_name_t)); goto cleanup_algorithm; } } else tkey->creator = NULL; tkey->key = NULL; if (dstkey != NULL) dst_key_attach(dstkey, &tkey->key); tkey->ring = ring; if (key != NULL) refs = 1; if (ring != NULL) refs++; ret = isc_refcount_init(&tkey->refs, refs); if (ret != ISC_R_SUCCESS) goto cleanup_creator; tkey->generated = generated; tkey->inception = inception; tkey->expire = expire; tkey->mctx = NULL; isc_mem_attach(mctx, &tkey->mctx); ISC_LINK_INIT(tkey, link); tkey->magic = TSIG_MAGIC; if (ring != NULL) { ret = keyring_add(ring, name, tkey); if (ret != ISC_R_SUCCESS) goto cleanup_refs; } /* * Ignore this if it's a GSS key, since the key size is meaningless. */ if (dstkey != NULL && dst_key_size(dstkey) < 64 && !dns_name_equal(algorithm, DNS_TSIG_GSSAPI_NAME) && !dns_name_equal(algorithm, DNS_TSIG_GSSAPIMS_NAME)) { char namestr[DNS_NAME_FORMATSIZE]; dns_name_format(name, namestr, sizeof(namestr)); isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_TSIG, ISC_LOG_INFO, "the key '%s' is too short to be secure", namestr); } if (key != NULL) *key = tkey; return (ISC_R_SUCCESS); cleanup_refs: tkey->magic = 0; while (refs-- > 0) isc_refcount_decrement(&tkey->refs, NULL); isc_refcount_destroy(&tkey->refs); cleanup_creator: if (tkey->key != NULL) dst_key_free(&tkey->key); if (tkey->creator != NULL) { dns_name_free(tkey->creator, mctx); isc_mem_put(mctx, tkey->creator, sizeof(dns_name_t)); } cleanup_algorithm: if (algname_is_allocated(tkey->algorithm)) { if (dns_name_dynamic(tkey->algorithm)) dns_name_free(tkey->algorithm, mctx); isc_mem_put(mctx, tkey->algorithm, sizeof(dns_name_t)); } cleanup_name: dns_name_free(&tkey->name, mctx); cleanup_key: isc_mem_put(mctx, tkey, sizeof(dns_tsigkey_t)); return (ret); }
static isc_result_t tsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg) { dns_rdata_any_tsig_t tsig, querytsig; isc_region_t r, source_r, header_r, sig_r; isc_buffer_t databuf; unsigned char data[32]; dns_name_t *keyname; dns_rdata_t rdata = DNS_RDATA_INIT; isc_stdtime_t now; isc_result_t ret; dns_tsigkey_t *tsigkey; dst_key_t *key = NULL; unsigned char header[DNS_MESSAGE_HEADERLEN]; isc_uint16_t addcount, id; isc_boolean_t has_tsig = ISC_FALSE; isc_mem_t *mctx; REQUIRE(source != NULL); REQUIRE(msg != NULL); REQUIRE(dns_message_gettsigkey(msg) != NULL); REQUIRE(msg->tcp_continuation == 1); REQUIRE(msg->querytsig != NULL); if (!is_response(msg)) return (DNS_R_EXPECTEDRESPONSE); mctx = msg->mctx; tsigkey = dns_message_gettsigkey(msg); /* * Extract and parse the previous TSIG */ ret = dns_rdataset_first(msg->querytsig); if (ret != ISC_R_SUCCESS) return (ret); dns_rdataset_current(msg->querytsig, &rdata); ret = dns_rdata_tostruct(&rdata, &querytsig, NULL); if (ret != ISC_R_SUCCESS) return (ret); dns_rdata_reset(&rdata); /* * If there is a TSIG in this message, do some checks. */ if (msg->tsig != NULL) { has_tsig = ISC_TRUE; keyname = msg->tsigname; ret = dns_rdataset_first(msg->tsig); if (ret != ISC_R_SUCCESS) goto cleanup_querystruct; dns_rdataset_current(msg->tsig, &rdata); ret = dns_rdata_tostruct(&rdata, &tsig, NULL); if (ret != ISC_R_SUCCESS) goto cleanup_querystruct; /* * Do the key name and algorithm match that of the query? */ if (!dns_name_equal(keyname, &tsigkey->name) || !dns_name_equal(&tsig.algorithm, &querytsig.algorithm)) { msg->tsigstatus = dns_tsigerror_badkey; ret = DNS_R_TSIGVERIFYFAILURE; tsig_log(msg->tsigkey, 2, "key name and algorithm do not match"); goto cleanup_querystruct; } /* * Is the time ok? */ isc_stdtime_get(&now); if (now + msg->timeadjust > tsig.timesigned + tsig.fudge) { msg->tsigstatus = dns_tsigerror_badtime; tsig_log(msg->tsigkey, 2, "signature has expired"); ret = DNS_R_CLOCKSKEW; goto cleanup_querystruct; } else if (now + msg->timeadjust < tsig.timesigned - tsig.fudge) { msg->tsigstatus = dns_tsigerror_badtime; tsig_log(msg->tsigkey, 2, "signature is in the future"); ret = DNS_R_CLOCKSKEW; goto cleanup_querystruct; } } key = tsigkey->key; if (msg->tsigctx == NULL) { ret = dst_context_create3(key, mctx, DNS_LOGCATEGORY_DNSSEC, ISC_FALSE, &msg->tsigctx); if (ret != ISC_R_SUCCESS) goto cleanup_querystruct; /* * Digest the length of the query signature */ isc_buffer_init(&databuf, data, sizeof(data)); isc_buffer_putuint16(&databuf, querytsig.siglen); isc_buffer_usedregion(&databuf, &r); ret = dst_context_adddata(msg->tsigctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; /* * Digest the data of the query signature */ if (querytsig.siglen > 0) { r.length = querytsig.siglen; r.base = querytsig.signature; ret = dst_context_adddata(msg->tsigctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; } } /* * Extract the header. */ isc_buffer_usedregion(source, &r); memmove(header, r.base, DNS_MESSAGE_HEADERLEN); isc_region_consume(&r, DNS_MESSAGE_HEADERLEN); /* * Decrement the additional field counter if necessary. */ if (has_tsig) { memmove(&addcount, &header[DNS_MESSAGE_HEADERLEN - 2], 2); addcount = htons((isc_uint16_t)(ntohs(addcount) - 1)); memmove(&header[DNS_MESSAGE_HEADERLEN - 2], &addcount, 2); } /* * Put in the original id. */ /* XXX Can TCP transfers be forwarded? How would that work? */ if (has_tsig) { id = htons(tsig.originalid); memmove(&header[0], &id, 2); } /* * Digest the modified header. */ header_r.base = (unsigned char *) header; header_r.length = DNS_MESSAGE_HEADERLEN; ret = dst_context_adddata(msg->tsigctx, &header_r); if (ret != ISC_R_SUCCESS) goto cleanup_context; /* * Digest all non-TSIG records. */ isc_buffer_usedregion(source, &source_r); r.base = source_r.base + DNS_MESSAGE_HEADERLEN; if (has_tsig) r.length = msg->sigstart - DNS_MESSAGE_HEADERLEN; else r.length = source_r.length - DNS_MESSAGE_HEADERLEN; ret = dst_context_adddata(msg->tsigctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; /* * Digest the time signed and fudge. */ if (has_tsig) { isc_buffer_init(&databuf, data, sizeof(data)); isc_buffer_putuint48(&databuf, tsig.timesigned); isc_buffer_putuint16(&databuf, tsig.fudge); isc_buffer_usedregion(&databuf, &r); ret = dst_context_adddata(msg->tsigctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; sig_r.base = tsig.signature; sig_r.length = tsig.siglen; if (tsig.siglen == 0) { if (tsig.error != dns_rcode_noerror) { if (tsig.error == dns_tsigerror_badtime) ret = DNS_R_CLOCKSKEW; else ret = DNS_R_TSIGERRORSET; } else { tsig_log(msg->tsigkey, 2, "signature is empty"); ret = DNS_R_TSIGVERIFYFAILURE; } goto cleanup_context; } ret = dst_context_verify(msg->tsigctx, &sig_r); if (ret == DST_R_VERIFYFAILURE) { msg->tsigstatus = dns_tsigerror_badsig; tsig_log(msg->tsigkey, 2, "signature failed to verify(2)"); ret = DNS_R_TSIGVERIFYFAILURE; goto cleanup_context; } else if (ret != ISC_R_SUCCESS) goto cleanup_context; dst_context_destroy(&msg->tsigctx); } msg->tsigstatus = dns_rcode_noerror; return (ISC_R_SUCCESS); cleanup_context: dst_context_destroy(&msg->tsigctx); cleanup_querystruct: dns_rdata_freestruct(&querytsig); return (ret); }
isc_result_t dns_tsig_verify(isc_buffer_t *source, dns_message_t *msg, dns_tsig_keyring_t *ring1, dns_tsig_keyring_t *ring2) { dns_rdata_any_tsig_t tsig, querytsig; isc_region_t r, source_r, header_r, sig_r; isc_buffer_t databuf; unsigned char data[32]; dns_name_t *keyname; dns_rdata_t rdata = DNS_RDATA_INIT; isc_stdtime_t now; isc_result_t ret; dns_tsigkey_t *tsigkey; dst_key_t *key = NULL; unsigned char header[DNS_MESSAGE_HEADERLEN]; dst_context_t *ctx = NULL; isc_mem_t *mctx; isc_uint16_t addcount, id; unsigned int siglen; unsigned int alg; isc_boolean_t response; REQUIRE(source != NULL); REQUIRE(DNS_MESSAGE_VALID(msg)); tsigkey = dns_message_gettsigkey(msg); response = is_response(msg); REQUIRE(tsigkey == NULL || VALID_TSIG_KEY(tsigkey)); msg->verify_attempted = 1; if (msg->tcp_continuation) { if (tsigkey == NULL || msg->querytsig == NULL) return (DNS_R_UNEXPECTEDTSIG); return (tsig_verify_tcp(source, msg)); } /* * There should be a TSIG record... */ if (msg->tsig == NULL) return (DNS_R_EXPECTEDTSIG); /* * If this is a response and there's no key or query TSIG, there * shouldn't be one on the response. */ if (response && (tsigkey == NULL || msg->querytsig == NULL)) return (DNS_R_UNEXPECTEDTSIG); mctx = msg->mctx; /* * If we're here, we know the message is well formed and contains a * TSIG record. */ keyname = msg->tsigname; ret = dns_rdataset_first(msg->tsig); if (ret != ISC_R_SUCCESS) return (ret); dns_rdataset_current(msg->tsig, &rdata); ret = dns_rdata_tostruct(&rdata, &tsig, NULL); if (ret != ISC_R_SUCCESS) return (ret); dns_rdata_reset(&rdata); if (response) { ret = dns_rdataset_first(msg->querytsig); if (ret != ISC_R_SUCCESS) return (ret); dns_rdataset_current(msg->querytsig, &rdata); ret = dns_rdata_tostruct(&rdata, &querytsig, NULL); if (ret != ISC_R_SUCCESS) return (ret); } #if defined(__clang__) && \ ( __clang_major__ < 3 || \ (__clang_major__ == 3 && __clang_minor__ < 2) || \ (__clang_major__ == 4 && __clang_minor__ < 2)) /* false positive: http://llvm.org/bugs/show_bug.cgi?id=14461 */ else memset(&querytsig, 0, sizeof(querytsig)); #endif /* * Do the key name and algorithm match that of the query? */ if (response && (!dns_name_equal(keyname, &tsigkey->name) || !dns_name_equal(&tsig.algorithm, &querytsig.algorithm))) { msg->tsigstatus = dns_tsigerror_badkey; tsig_log(msg->tsigkey, 2, "key name and algorithm do not match"); return (DNS_R_TSIGVERIFYFAILURE); } /* * Get the current time. */ isc_stdtime_get(&now); /* * Find dns_tsigkey_t based on keyname. */ if (tsigkey == NULL) { ret = ISC_R_NOTFOUND; if (ring1 != NULL) ret = dns_tsigkey_find(&tsigkey, keyname, &tsig.algorithm, ring1); if (ret == ISC_R_NOTFOUND && ring2 != NULL) ret = dns_tsigkey_find(&tsigkey, keyname, &tsig.algorithm, ring2); if (ret != ISC_R_SUCCESS) { msg->tsigstatus = dns_tsigerror_badkey; ret = dns_tsigkey_create(keyname, &tsig.algorithm, NULL, 0, ISC_FALSE, NULL, now, now, mctx, NULL, &msg->tsigkey); if (ret != ISC_R_SUCCESS) return (ret); tsig_log(msg->tsigkey, 2, "unknown key"); return (DNS_R_TSIGVERIFYFAILURE); } msg->tsigkey = tsigkey; } key = tsigkey->key; /* * Is the time ok? */ if (now + msg->timeadjust > tsig.timesigned + tsig.fudge) { msg->tsigstatus = dns_tsigerror_badtime; tsig_log(msg->tsigkey, 2, "signature has expired"); return (DNS_R_CLOCKSKEW); } else if (now + msg->timeadjust < tsig.timesigned - tsig.fudge) { msg->tsigstatus = dns_tsigerror_badtime; tsig_log(msg->tsigkey, 2, "signature is in the future"); return (DNS_R_CLOCKSKEW); } /* * Check digest length. */ alg = dst_key_alg(key); ret = dst_key_sigsize(key, &siglen); if (ret != ISC_R_SUCCESS) return (ret); if (alg == DST_ALG_HMACMD5 || alg == DST_ALG_HMACSHA1 || alg == DST_ALG_HMACSHA224 || alg == DST_ALG_HMACSHA256 || alg == DST_ALG_HMACSHA384 || alg == DST_ALG_HMACSHA512) { isc_uint16_t digestbits = dst_key_getbits(key); if (tsig.siglen > siglen) { tsig_log(msg->tsigkey, 2, "signature length too big"); return (DNS_R_FORMERR); } if (tsig.siglen > 0 && (tsig.siglen < 10 || tsig.siglen < ((siglen + 1) / 2))) { tsig_log(msg->tsigkey, 2, "signature length below minimum"); return (DNS_R_FORMERR); } if (tsig.siglen > 0 && digestbits != 0 && tsig.siglen < ((digestbits + 1) / 8)) { msg->tsigstatus = dns_tsigerror_badtrunc; tsig_log(msg->tsigkey, 2, "truncated signature length too small"); return (DNS_R_TSIGVERIFYFAILURE); } if (tsig.siglen > 0 && digestbits == 0 && tsig.siglen < siglen) { msg->tsigstatus = dns_tsigerror_badtrunc; tsig_log(msg->tsigkey, 2, "signature length too small"); return (DNS_R_TSIGVERIFYFAILURE); } } if (tsig.siglen > 0) { sig_r.base = tsig.signature; sig_r.length = tsig.siglen; ret = dst_context_create3(key, mctx, DNS_LOGCATEGORY_DNSSEC, ISC_FALSE, &ctx); if (ret != ISC_R_SUCCESS) return (ret); if (response) { isc_buffer_init(&databuf, data, sizeof(data)); isc_buffer_putuint16(&databuf, querytsig.siglen); isc_buffer_usedregion(&databuf, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; if (querytsig.siglen > 0) { r.length = querytsig.siglen; r.base = querytsig.signature; ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; } } /* * Extract the header. */ isc_buffer_usedregion(source, &r); memmove(header, r.base, DNS_MESSAGE_HEADERLEN); isc_region_consume(&r, DNS_MESSAGE_HEADERLEN); /* * Decrement the additional field counter. */ memmove(&addcount, &header[DNS_MESSAGE_HEADERLEN - 2], 2); addcount = htons((isc_uint16_t)(ntohs(addcount) - 1)); memmove(&header[DNS_MESSAGE_HEADERLEN - 2], &addcount, 2); /* * Put in the original id. */ id = htons(tsig.originalid); memmove(&header[0], &id, 2); /* * Digest the modified header. */ header_r.base = (unsigned char *) header; header_r.length = DNS_MESSAGE_HEADERLEN; ret = dst_context_adddata(ctx, &header_r); if (ret != ISC_R_SUCCESS) goto cleanup_context; /* * Digest all non-TSIG records. */ isc_buffer_usedregion(source, &source_r); r.base = source_r.base + DNS_MESSAGE_HEADERLEN; r.length = msg->sigstart - DNS_MESSAGE_HEADERLEN; ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; /* * Digest the key name. */ dns_name_toregion(&tsigkey->name, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; isc_buffer_init(&databuf, data, sizeof(data)); isc_buffer_putuint16(&databuf, tsig.common.rdclass); isc_buffer_putuint32(&databuf, msg->tsig->ttl); isc_buffer_usedregion(&databuf, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; /* * Digest the key algorithm. */ dns_name_toregion(tsigkey->algorithm, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; isc_buffer_clear(&databuf); isc_buffer_putuint48(&databuf, tsig.timesigned); isc_buffer_putuint16(&databuf, tsig.fudge); isc_buffer_putuint16(&databuf, tsig.error); isc_buffer_putuint16(&databuf, tsig.otherlen); isc_buffer_usedregion(&databuf, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; if (tsig.otherlen > 0) { r.base = tsig.other; r.length = tsig.otherlen; ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; } ret = dst_context_verify(ctx, &sig_r); if (ret == DST_R_VERIFYFAILURE) { msg->tsigstatus = dns_tsigerror_badsig; ret = DNS_R_TSIGVERIFYFAILURE; tsig_log(msg->tsigkey, 2, "signature failed to verify(1)"); goto cleanup_context; } else if (ret != ISC_R_SUCCESS) goto cleanup_context; dst_context_destroy(&ctx); } else if (tsig.error != dns_tsigerror_badsig && tsig.error != dns_tsigerror_badkey) { msg->tsigstatus = dns_tsigerror_badsig; tsig_log(msg->tsigkey, 2, "signature was empty"); return (DNS_R_TSIGVERIFYFAILURE); } msg->tsigstatus = dns_rcode_noerror; if (tsig.error != dns_rcode_noerror) { if (tsig.error == dns_tsigerror_badtime) return (DNS_R_CLOCKSKEW); else return (DNS_R_TSIGERRORSET); } msg->verified_sig = 1; return (ISC_R_SUCCESS); cleanup_context: if (ctx != NULL) dst_context_destroy(&ctx); return (ret); }