diff options
Diffstat (limited to 'source4/dns_server')
-rw-r--r-- | source4/dns_server/dns_crypto.c | 49 | ||||
-rw-r--r-- | source4/dns_server/dns_query.c | 27 | ||||
-rw-r--r-- | source4/dns_server/dns_update.c | 11 | ||||
-rw-r--r-- | source4/dns_server/dnsserver_common.c | 8 |
4 files changed, 81 insertions, 14 deletions
diff --git a/source4/dns_server/dns_crypto.c b/source4/dns_server/dns_crypto.c index be79a4e..d30e971 100644 --- a/source4/dns_server/dns_crypto.c +++ b/source4/dns_server/dns_crypto.c @@ -27,6 +27,7 @@ #include "libcli/util/ntstatus.h" #include "auth/auth.h" #include "auth/gensec/gensec.h" +#include "lib/util/bytearray.h" #undef DBGC_CLASS #define DBGC_CLASS DBGC_DNS @@ -106,7 +107,7 @@ WERROR dns_verify_tsig(struct dns_server *dns, struct dns_server_tkey *tkey = NULL; struct dns_fake_tsig_rec *check_rec = talloc_zero(mem_ctx, struct dns_fake_tsig_rec); - + const char *algorithm = NULL; /* Find the first TSIG record in the additional records */ for (i=0; i < packet->arcount; i++) { @@ -145,7 +146,7 @@ WERROR dns_verify_tsig(struct dns_server *dns, tkey = dns_find_tkey(dns->tkeys, state->tsig->name); if (tkey == NULL) { - DBG_DEBUG("dns_find_tkey() => NOTAUTH / DNS_RCODE_BADKEY\n"); + DBG_DEBUG("dns_find_tkey() => REFUSED / DNS_RCODE_BADKEY\n"); /* * We must save the name for use in the TSIG error * response and have no choice here but to save the @@ -157,10 +158,20 @@ WERROR dns_verify_tsig(struct dns_server *dns, return WERR_NOT_ENOUGH_MEMORY; } state->tsig_error = DNS_RCODE_BADKEY; - return DNS_ERR(NOTAUTH); + return DNS_ERR(REFUSED); } DBG_DEBUG("dns_find_tkey() => found\n"); + algorithm = state->tsig->rdata.tsig_record.algorithm_name; + if (strcmp(algorithm, "gss-tsig") == 0) { + /* ok */ + } else if (strcmp(algorithm, "gss.microsoft.com") == 0) { + /* ok */ + } else { + state->tsig_error = DNS_RCODE_BADKEY; + return DNS_ERR(REFUSED); + } + /* * Remember the keyname that found an existing tkey, used * later to fetch the key with dns_find_tkey() when signing @@ -183,7 +194,7 @@ WERROR dns_verify_tsig(struct dns_server *dns, } check_rec->rr_class = DNS_QCLASS_ANY; check_rec->ttl = 0; - check_rec->algorithm_name = talloc_strdup(check_rec, tkey->algorithm); + check_rec->algorithm_name = talloc_strdup(check_rec, algorithm); if (check_rec->algorithm_name == NULL) { return WERR_NOT_ENOUGH_MEMORY; } @@ -239,7 +250,7 @@ WERROR dns_verify_tsig(struct dns_server *dns, dump_data_dbgc(DBGC_DNS, 8, buffer, buffer_len); DBG_NOTICE("Verifying tsig failed: %s\n", nt_errstr(status)); state->tsig_error = DNS_RCODE_BADSIG; - return DNS_ERR(NOTAUTH); + return DNS_ERR(REFUSED); } if (!NT_STATUS_IS_OK(status)) { @@ -271,11 +282,19 @@ static WERROR dns_tsig_compute_mac(TALLOC_CTX *mem_ctx, struct dns_fake_tsig_rec *check_rec = talloc_zero(mem_ctx, struct dns_fake_tsig_rec); size_t mac_size = 0; + bool gss_tsig; if (check_rec == NULL) { return WERR_NOT_ENOUGH_MEMORY; } + if (strcmp(tkey->algorithm, "gss-tsig") == 0) { + gss_tsig = true; + } else { + /* gss.microsoft.com */ + gss_tsig = false; + } + /* first build and verify check packet */ check_rec->name = talloc_strdup(check_rec, tkey->name); if (check_rec->name == NULL) { @@ -315,6 +334,9 @@ static WERROR dns_tsig_compute_mac(TALLOC_CTX *mem_ctx, } buffer_len = mac_size; + if (gss_tsig && mac_size > 0) { + buffer_len += 2; + } buffer_len += packet_blob.length; if (buffer_len < packet_blob.length) { @@ -335,11 +357,21 @@ static WERROR dns_tsig_compute_mac(TALLOC_CTX *mem_ctx, /* * RFC 2845 "4.2 TSIG on Answers", how to lay out the buffer * that we're going to sign: - * 1. MAC of request (if present) + * 1. if MAC of request is present + * - 16bit big endian length of MAC of request + * - MAC of request * 2. Outgoing packet * 3. TSIG record */ if (mac_size > 0) { + if (gss_tsig) { + /* + * only gss-tsig not with + * gss.microsoft.com + */ + PUSH_BE_U16(p, 0, mac_size); + p += 2; + } memcpy(p, state->tsig->rdata.tsig_record.mac, mac_size); p += mac_size; } @@ -372,6 +404,7 @@ WERROR dns_sign_tsig(struct dns_server *dns, .data = NULL, .length = 0 }; + const char *algorithm = "gss-tsig"; tsig = talloc_zero(mem_ctx, struct dns_res_rec); if (tsig == NULL) { @@ -392,6 +425,8 @@ WERROR dns_sign_tsig(struct dns_server *dns, if (!W_ERROR_IS_OK(werror)) { return werror; } + + algorithm = tkey->algorithm; } tsig->name = talloc_strdup(tsig, state->key_name); @@ -402,7 +437,7 @@ WERROR dns_sign_tsig(struct dns_server *dns, tsig->rr_type = DNS_QTYPE_TSIG; tsig->ttl = 0; tsig->length = UINT16_MAX; - tsig->rdata.tsig_record.algorithm_name = talloc_strdup(tsig, "gss-tsig"); + tsig->rdata.tsig_record.algorithm_name = talloc_strdup(tsig, algorithm); if (tsig->rdata.tsig_record.algorithm_name == NULL) { return WERR_NOT_ENOUGH_MEMORY; } diff --git a/source4/dns_server/dns_query.c b/source4/dns_server/dns_query.c index 181beda..1f46ee0 100644 --- a/source4/dns_server/dns_query.c +++ b/source4/dns_server/dns_query.c @@ -663,8 +663,17 @@ static NTSTATUS create_tkey(struct dns_server *dns, { NTSTATUS status; struct dns_server_tkey_store *store = dns->tkeys; - struct dns_server_tkey *k = talloc_zero(store, struct dns_server_tkey); + struct dns_server_tkey *k = NULL; + + if (strcmp(algorithm, "gss-tsig") == 0) { + /* ok */ + } else if (strcmp(algorithm, "gss.microsoft.com") == 0) { + /* ok */ + } else { + return NT_STATUS_ACCESS_DENIED; + } + k = talloc_zero(store, struct dns_server_tkey); if (k == NULL) { return NT_STATUS_NO_MEMORY; } @@ -790,12 +799,22 @@ static WERROR handle_tkey(struct dns_server *dns, { struct dns_res_rec *in_tkey = NULL; struct dns_res_rec *ret_tkey; - uint16_t i; - for (i = 0; i < in->arcount; i++) { + /* + * TKEY needs to we the last one in + * additional or answers + */ + if (in->arcount >= 1) { + uint16_t i = in->arcount - 1; if (in->additional[i].rr_type == DNS_QTYPE_TKEY) { in_tkey = &in->additional[i]; - break; + } + } else if (in->nscount >= 1) { + /* no lookup */ + } else if (in->ancount >= 1) { + uint16_t i = in->ancount - 1; + if (in->answers[i].rr_type == DNS_QTYPE_TKEY) { + in_tkey = &in->answers[i]; } } diff --git a/source4/dns_server/dns_update.c b/source4/dns_server/dns_update.c index 4d2ee0b..1285111 100644 --- a/source4/dns_server/dns_update.c +++ b/source4/dns_server/dns_update.c @@ -570,6 +570,8 @@ static WERROR handle_one_update(struct dns_server *dns, W_ERROR_NOT_OK_RETURN(werror); for (i = first; i < rcount; i++) { + struct dnsp_DnssrvRpcRecord orig_rec = recs[i]; + if (!dns_record_match(&recs[i], &recs[rcount])) { continue; } @@ -583,6 +585,15 @@ static WERROR handle_one_update(struct dns_server *dns, werror = dns_replace_records(dns, mem_ctx, dn, needs_add, recs, rcount); DBG_DEBUG("dns_replace_records(REPLACE): %s\n", win_errstr(werror)); + if (W_ERROR_EQUAL(werror, WERR_ACCESS_DENIED) && + !needs_add && + orig_rec.dwTtlSeconds == recs[i].dwTtlSeconds) + { + DBG_NOTICE("dns_replace_records(REPLACE): %s " + "=> skip no-op\n", + win_errstr(werror)); + werror = WERR_OK; + } W_ERROR_NOT_OK_RETURN(werror); return WERR_OK; diff --git a/source4/dns_server/dnsserver_common.c b/source4/dns_server/dnsserver_common.c index aba7f41..88aed2e 100644 --- a/source4/dns_server/dnsserver_common.c +++ b/source4/dns_server/dnsserver_common.c @@ -68,6 +68,8 @@ uint8_t werr_to_dns_err(WERROR werr) return DNS_RCODE_NOTZONE; } else if (W_ERROR_EQUAL(DNS_ERR(BADKEY), werr)) { return DNS_RCODE_BADKEY; + } else if (W_ERROR_EQUAL(WERR_ACCESS_DENIED, werr)) { + return DNS_RCODE_REFUSED; } DEBUG(5, ("No mapping exists for %s\n", win_errstr(werr))); return DNS_RCODE_SERVFAIL; @@ -642,7 +644,7 @@ static int rec_cmp(const struct dnsp_DnssrvRpcRecord *r1, * The records are sorted with higher types first, * which puts tombstones (type 0) last. */ - return r2->wType - r1->wType; + return NUMERIC_CMP(r2->wType, r1->wType); } /* * Then we need to sort from the oldest to newest timestamp. @@ -650,7 +652,7 @@ static int rec_cmp(const struct dnsp_DnssrvRpcRecord *r1, * Note that dwTimeStamp == 0 (never expiring) records come first, * then the ones whose expiry is soonest. */ - return r1->dwTimeStamp - r2->dwTimeStamp; + return NUMERIC_CMP(r1->dwTimeStamp, r2->dwTimeStamp); } /* @@ -1408,7 +1410,7 @@ static int dns_common_sort_zones(struct ldb_message **m1, struct ldb_message **m /* If the string lengths are not equal just sort by length */ if (l1 != l2) { /* If m1 is the larger zone name, return it first */ - return l2 - l1; + return NUMERIC_CMP(l2, l1); } /*TODO: We need to compare DNs here, we want the DomainDNSZones first */ |