tests/krb5: Add assertion to make failures clearer
[Samba.git] / source4 / dns_server / dnsserver_common.c
blob8635d075be8060c21a789c60a5020e4ae740b69b
1 /*
2 Unix SMB/CIFS implementation.
4 DNS server utils
6 Copyright (C) 2010 Kai Blin
7 Copyright (C) 2014 Stefan Metzmacher
8 Copyright (C) 2015 Andrew Bartlett
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "includes.h"
25 #include "libcli/util/ntstatus.h"
26 #include "libcli/util/werror.h"
27 #include "librpc/ndr/libndr.h"
28 #include "librpc/gen_ndr/ndr_dns.h"
29 #include "librpc/gen_ndr/ndr_dnsp.h"
30 #include <ldb.h>
31 #include "dsdb/samdb/samdb.h"
32 #include "dsdb/common/util.h"
33 #include "dns_server/dnsserver_common.h"
34 #include "rpc_server/dnsserver/dnsserver.h"
35 #include "lib/util/dlinklist.h"
37 #undef DBGC_CLASS
38 #define DBGC_CLASS DBGC_DNS
40 uint8_t werr_to_dns_err(WERROR werr)
42 if (W_ERROR_EQUAL(WERR_OK, werr)) {
43 return DNS_RCODE_OK;
44 } else if (W_ERROR_EQUAL(DNS_ERR(FORMAT_ERROR), werr)) {
45 return DNS_RCODE_FORMERR;
46 } else if (W_ERROR_EQUAL(DNS_ERR(SERVER_FAILURE), werr)) {
47 return DNS_RCODE_SERVFAIL;
48 } else if (W_ERROR_EQUAL(DNS_ERR(NAME_ERROR), werr)) {
49 return DNS_RCODE_NXDOMAIN;
50 } else if (W_ERROR_EQUAL(WERR_DNS_ERROR_NAME_DOES_NOT_EXIST, werr)) {
51 return DNS_RCODE_NXDOMAIN;
52 } else if (W_ERROR_EQUAL(DNS_ERR(NOT_IMPLEMENTED), werr)) {
53 return DNS_RCODE_NOTIMP;
54 } else if (W_ERROR_EQUAL(DNS_ERR(REFUSED), werr)) {
55 return DNS_RCODE_REFUSED;
56 } else if (W_ERROR_EQUAL(DNS_ERR(YXDOMAIN), werr)) {
57 return DNS_RCODE_YXDOMAIN;
58 } else if (W_ERROR_EQUAL(DNS_ERR(YXRRSET), werr)) {
59 return DNS_RCODE_YXRRSET;
60 } else if (W_ERROR_EQUAL(DNS_ERR(NXRRSET), werr)) {
61 return DNS_RCODE_NXRRSET;
62 } else if (W_ERROR_EQUAL(DNS_ERR(NOTAUTH), werr)) {
63 return DNS_RCODE_NOTAUTH;
64 } else if (W_ERROR_EQUAL(DNS_ERR(NOTZONE), werr)) {
65 return DNS_RCODE_NOTZONE;
66 } else if (W_ERROR_EQUAL(DNS_ERR(BADKEY), werr)) {
67 return DNS_RCODE_BADKEY;
69 DEBUG(5, ("No mapping exists for %s\n", win_errstr(werr)));
70 return DNS_RCODE_SERVFAIL;
73 WERROR dns_common_extract(struct ldb_context *samdb,
74 const struct ldb_message_element *el,
75 TALLOC_CTX *mem_ctx,
76 struct dnsp_DnssrvRpcRecord **records,
77 uint16_t *num_records)
79 uint16_t ri;
80 struct dnsp_DnssrvRpcRecord *recs;
82 *records = NULL;
83 *num_records = 0;
85 recs = talloc_zero_array(mem_ctx, struct dnsp_DnssrvRpcRecord,
86 el->num_values);
87 if (recs == NULL) {
88 return WERR_NOT_ENOUGH_MEMORY;
90 for (ri = 0; ri < el->num_values; ri++) {
91 bool am_rodc;
92 int ret;
93 const char *dnsHostName = NULL;
94 struct ldb_val *v = &el->values[ri];
95 enum ndr_err_code ndr_err;
96 ndr_err = ndr_pull_struct_blob(v, recs, &recs[ri],
97 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
98 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
99 TALLOC_FREE(recs);
100 DEBUG(0, ("Failed to grab dnsp_DnssrvRpcRecord\n"));
101 return DNS_ERR(SERVER_FAILURE);
105 * In AD, except on an RODC (where we should list a random RWDC,
106 * we should over-stamp the MNAME with our own hostname
108 if (recs[ri].wType != DNS_TYPE_SOA) {
109 continue;
112 ret = samdb_rodc(samdb, &am_rodc);
113 if (ret != LDB_SUCCESS) {
114 DEBUG(0, ("Failed to confirm we are not an RODC: %s\n",
115 ldb_errstring(samdb)));
116 return DNS_ERR(SERVER_FAILURE);
119 if (am_rodc) {
120 continue;
123 ret = samdb_dns_host_name(samdb, &dnsHostName);
124 if (ret != LDB_SUCCESS || dnsHostName == NULL) {
125 DEBUG(0, ("Failed to get dnsHostName from rootDSE"));
126 return DNS_ERR(SERVER_FAILURE);
129 recs[ri].data.soa.mname = talloc_strdup(recs, dnsHostName);
132 *records = recs;
133 *num_records = el->num_values;
134 return WERR_OK;
138 * Lookup a DNS record, performing an exact match.
139 * i.e. DNS wild card records are not considered.
141 WERROR dns_common_lookup(struct ldb_context *samdb,
142 TALLOC_CTX *mem_ctx,
143 struct ldb_dn *dn,
144 struct dnsp_DnssrvRpcRecord **records,
145 uint16_t *num_records,
146 bool *tombstoned)
148 const struct timeval start = timeval_current();
149 static const char * const attrs[] = {
150 "dnsRecord",
151 "dNSTombstoned",
152 NULL
154 int ret;
155 WERROR werr = WERR_OK;
156 struct ldb_message *msg = NULL;
157 struct ldb_message_element *el;
159 *records = NULL;
160 *num_records = 0;
162 if (tombstoned != NULL) {
163 *tombstoned = false;
164 ret = dsdb_search_one(samdb, mem_ctx, &msg, dn,
165 LDB_SCOPE_BASE, attrs, 0,
166 "(objectClass=dnsNode)");
167 } else {
168 ret = dsdb_search_one(samdb, mem_ctx, &msg, dn,
169 LDB_SCOPE_BASE, attrs, 0,
170 "(&(objectClass=dnsNode)(!(dNSTombstoned=TRUE)))");
172 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
173 werr = WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
174 goto exit;
176 if (ret != LDB_SUCCESS) {
177 /* TODO: we need to check if there's a glue record we need to
178 * create a referral to */
179 werr = DNS_ERR(NAME_ERROR);
180 goto exit;
183 if (tombstoned != NULL) {
184 *tombstoned = ldb_msg_find_attr_as_bool(msg,
185 "dNSTombstoned", false);
188 el = ldb_msg_find_element(msg, "dnsRecord");
189 if (el == NULL) {
190 TALLOC_FREE(msg);
192 * records produced by older Samba releases
193 * keep dnsNode objects without dnsRecord and
194 * without setting dNSTombstoned=TRUE.
196 * We just pretend they're tombstones.
198 if (tombstoned != NULL) {
199 struct dnsp_DnssrvRpcRecord *recs;
200 recs = talloc_array(mem_ctx,
201 struct dnsp_DnssrvRpcRecord,
203 if (recs == NULL) {
204 werr = WERR_NOT_ENOUGH_MEMORY;
205 goto exit;
207 recs[0] = (struct dnsp_DnssrvRpcRecord) {
208 .wType = DNS_TYPE_TOMBSTONE,
210 * A value of timestamp != 0
211 * indicated that the object was already
212 * a tombstone, this will be used
213 * in dns_common_replace()
215 .data.timestamp = 1,
218 *tombstoned = true;
219 *records = recs;
220 *num_records = 1;
221 werr = WERR_OK;
222 goto exit;
223 } else {
225 * Because we are not looking for a tombstone
226 * in this codepath, we just pretend it does
227 * not exist at all.
229 werr = WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
230 goto exit;
234 werr = dns_common_extract(samdb, el, mem_ctx, records, num_records);
235 TALLOC_FREE(msg);
236 if (!W_ERROR_IS_OK(werr)) {
237 goto exit;
240 werr = WERR_OK;
241 exit:
242 DNS_COMMON_LOG_OPERATION(
243 win_errstr(werr),
244 &start,
245 NULL,
246 dn == NULL ? NULL : ldb_dn_get_linearized(dn),
247 NULL);
248 return werr;
252 * Build an ldb_parse_tree node for an equality check
254 * Note: name is assumed to have been validated by dns_name_check
255 * so will be zero terminated and of a reasonable size.
257 static struct ldb_parse_tree *build_equality_operation(
258 TALLOC_CTX *mem_ctx,
259 bool add_asterix, /* prepend an '*' to the name */
260 const uint8_t *name, /* the value being matched */
261 const char *attr, /* the attribute to check name against */
262 size_t size) /* length of name */
265 struct ldb_parse_tree *el = NULL; /* Equality node being built */
266 struct ldb_val *value = NULL; /* Value the attr will be compared
267 with */
268 size_t length = 0; /* calculated length of the value
269 including option '*' prefix and
270 '\0' string terminator */
272 el = talloc(mem_ctx, struct ldb_parse_tree);
273 if (el == NULL) {
274 DBG_ERR("Unable to allocate ldb_parse_tree\n");
275 return NULL;
278 el->operation = LDB_OP_EQUALITY;
279 el->u.equality.attr = talloc_strdup(mem_ctx, attr);
280 value = &el->u.equality.value;
281 length = (add_asterix) ? size + 2 : size + 1;
282 value->data = talloc_zero_array(el, uint8_t, length);
283 if (el == NULL) {
284 DBG_ERR("Unable to allocate value->data\n");
285 TALLOC_FREE(el);
286 return NULL;
289 value->length = length;
290 if (add_asterix) {
291 value->data[0] = '*';
292 memcpy(&value->data[1], name, size);
293 } else {
294 memcpy(value->data, name, size);
296 return el;
300 * Determine the number of levels in name
301 * essentially the number of '.'s in the name + 1
303 * name is assumed to have been validated by dns_name_check
305 static unsigned int number_of_labels(const struct ldb_val *name) {
306 int x = 0;
307 unsigned int labels = 1;
308 for (x = 0; x < name->length; x++) {
309 if (name->data[x] == '.') {
310 labels++;
313 return labels;
316 * Build a query that matches the target name, and any possible
317 * DNS wild card entries
319 * Builds a parse tree equivalent to the example query.
321 * x.y.z -> (|(name=x.y.z)(name=\2a.y.z)(name=\2a.z)(name=\2a))
323 * The attribute 'name' is used as this is what the LDB index is on
324 * (the RDN, being 'dc' in this use case, does not have an index in
325 * the AD schema).
327 * Returns NULL if unable to build the query.
329 * The first component of the DN is assumed to be the name being looked up
330 * and also that it has been validated by dns_name_check
333 #define BASE "(&(objectClass=dnsNode)(!(dNSTombstoned=TRUE))(|(a=b)(c=d)))"
334 static struct ldb_parse_tree *build_wildcard_query(
335 TALLOC_CTX *mem_ctx,
336 struct ldb_dn *dn)
338 const struct ldb_val *name = NULL; /* The DNS name being
339 queried */
340 const char *attr = "name"; /* The attribute name */
341 struct ldb_parse_tree *query = NULL; /* The constructed query
342 parse tree*/
343 struct ldb_parse_tree *wildcard_query = NULL; /* The parse tree for the
344 name and wild card
345 entries */
346 int labels = 0; /* The number of labels in the name */
348 name = ldb_dn_get_rdn_val(dn);
349 if (name == NULL) {
350 DBG_ERR("Unable to get domain name value\n");
351 return NULL;
353 labels = number_of_labels(name);
355 query = ldb_parse_tree(mem_ctx, BASE);
356 if (query == NULL) {
357 DBG_ERR("Unable to parse query %s\n", BASE);
358 return NULL;
362 * The 3rd element of BASE is a place holder which is replaced with
363 * the actual wild card query
365 wildcard_query = query->u.list.elements[2];
366 TALLOC_FREE(wildcard_query->u.list.elements);
368 wildcard_query->u.list.num_elements = labels + 1;
369 wildcard_query->u.list.elements = talloc_array(
370 wildcard_query,
371 struct ldb_parse_tree *,
372 labels + 1);
374 * Build the wild card query
377 int x = 0; /* current character in the name */
378 int l = 0; /* current equality operator index in elements */
379 struct ldb_parse_tree *el = NULL; /* Equality operator being
380 built */
381 bool add_asterix = true; /* prepend an '*' to the value */
382 for (l = 0, x = 0; l < labels && x < name->length; l++) {
383 unsigned int size = name->length - x;
384 add_asterix = (name->data[x] == '.');
385 el = build_equality_operation(
386 mem_ctx,
387 add_asterix,
388 &name->data[x],
389 attr,
390 size);
391 if (el == NULL) {
392 return NULL; /* Reason will have been logged */
394 wildcard_query->u.list.elements[l] = el;
396 /* skip to the start of the next label */
397 x++;
398 for (;x < name->length && name->data[x] != '.'; x++);
401 /* Add the base level "*" only query */
402 el = build_equality_operation(mem_ctx, true, NULL, attr, 0);
403 if (el == NULL) {
404 TALLOC_FREE(query);
405 return NULL; /* Reason will have been logged */
407 wildcard_query->u.list.elements[l] = el;
409 return query;
413 * Scan the list of records matching a dns wildcard query and return the
414 * best match.
416 * The best match is either an exact name match, or the longest wild card
417 * entry returned
419 * i.e. name = a.b.c candidates *.b.c, *.c, - *.b.c would be selected
420 * name = a.b.c candidates a.b.c, *.b.c, *.c - a.b.c would be selected
422 static struct ldb_message *get_best_match(struct ldb_dn *dn,
423 struct ldb_result *result)
425 int matched = 0; /* Index of the current best match in result */
426 size_t length = 0; /* The length of the current candidate */
427 const struct ldb_val *target = NULL; /* value we're looking for */
428 const struct ldb_val *candidate = NULL; /* current candidate value */
429 int x = 0;
431 target = ldb_dn_get_rdn_val(dn);
432 for(x = 0; x < result->count; x++) {
433 candidate = ldb_dn_get_rdn_val(result->msgs[x]->dn);
434 if (strncasecmp((char *) target->data,
435 (char *) candidate->data,
436 target->length) == 0) {
437 /* Exact match stop searching and return */
438 return result->msgs[x];
440 if (candidate->length > length) {
441 matched = x;
442 length = candidate->length;
445 return result->msgs[matched];
449 * Look up a DNS entry, if an exact match does not exist, return the
450 * closest matching DNS wildcard entry if available
452 * Returns: LDB_ERR_NO_SUCH_OBJECT If no matching record exists
453 * LDB_ERR_OPERATIONS_ERROR If the query fails
454 * LDB_SUCCESS If a matching record was retrieved
457 static int dns_wildcard_lookup(struct ldb_context *samdb,
458 TALLOC_CTX *mem_ctx,
459 struct ldb_dn *dn,
460 struct ldb_message **msg)
462 static const char * const attrs[] = {
463 "dnsRecord",
464 "dNSTombstoned",
465 NULL
467 struct ldb_dn *parent = NULL; /* The parent dn */
468 struct ldb_result *result = NULL; /* Results of the search */
469 int ret; /* Return code */
470 struct ldb_parse_tree *query = NULL; /* The query to run */
471 struct ldb_request *request = NULL; /* LDB request for the query op */
472 struct ldb_message *match = NULL; /* the best matching DNS record */
473 TALLOC_CTX *frame = talloc_stackframe();
475 parent = ldb_dn_get_parent(frame, dn);
476 if (parent == NULL) {
477 DBG_ERR("Unable to extract parent from dn\n");
478 TALLOC_FREE(frame);
479 return LDB_ERR_OPERATIONS_ERROR;
482 query = build_wildcard_query(frame, dn);
483 if (query == NULL) {
484 TALLOC_FREE(frame);
485 return LDB_ERR_OPERATIONS_ERROR;
488 result = talloc_zero(mem_ctx, struct ldb_result);
489 if (result == NULL) {
490 TALLOC_FREE(frame);
491 DBG_ERR("Unable to allocate ldb_result\n");
492 return LDB_ERR_OPERATIONS_ERROR;
495 ret = ldb_build_search_req_ex(&request,
496 samdb,
497 frame,
498 parent,
499 LDB_SCOPE_SUBTREE,
500 query,
501 attrs,
502 NULL,
503 result,
504 ldb_search_default_callback,
505 NULL);
506 if (ret != LDB_SUCCESS) {
507 TALLOC_FREE(frame);
508 DBG_ERR("ldb_build_search_req_ex returned %d\n", ret);
509 return ret;
512 ret = ldb_request(samdb, request);
513 if (ret != LDB_SUCCESS) {
514 TALLOC_FREE(frame);
515 return ret;
518 ret = ldb_wait(request->handle, LDB_WAIT_ALL);
519 if (ret != LDB_SUCCESS) {
520 TALLOC_FREE(frame);
521 return ret;
524 if (result->count == 0) {
525 TALLOC_FREE(frame);
526 return LDB_ERR_NO_SUCH_OBJECT;
529 match = get_best_match(dn, result);
530 if (match == NULL) {
531 TALLOC_FREE(frame);
532 return LDB_ERR_OPERATIONS_ERROR;
535 *msg = talloc_move(mem_ctx, &match);
536 TALLOC_FREE(frame);
537 return LDB_SUCCESS;
541 * Lookup a DNS record, will match DNS wild card records if an exact match
542 * is not found.
544 WERROR dns_common_wildcard_lookup(struct ldb_context *samdb,
545 TALLOC_CTX *mem_ctx,
546 struct ldb_dn *dn,
547 struct dnsp_DnssrvRpcRecord **records,
548 uint16_t *num_records)
550 const struct timeval start = timeval_current();
551 int ret;
552 WERROR werr = WERR_OK;
553 struct ldb_message *msg = NULL;
554 struct ldb_message_element *el = NULL;
555 const struct ldb_val *name = NULL;
557 *records = NULL;
558 *num_records = 0;
560 name = ldb_dn_get_rdn_val(dn);
561 if (name == NULL) {
562 werr = DNS_ERR(NAME_ERROR);
563 goto exit;
566 /* Don't look for a wildcard for @ */
567 if (name->length == 1 && name->data[0] == '@') {
568 werr = dns_common_lookup(samdb,
569 mem_ctx,
571 records,
572 num_records,
573 NULL);
574 goto exit;
577 werr = dns_name_check(
578 mem_ctx,
579 strlen((const char*)name->data),
580 (const char*) name->data);
581 if (!W_ERROR_IS_OK(werr)) {
582 goto exit;
586 * Do a point search first, then fall back to a wildcard
587 * lookup if it does not exist
589 werr = dns_common_lookup(samdb,
590 mem_ctx,
592 records,
593 num_records,
594 NULL);
595 if (!W_ERROR_EQUAL(werr, WERR_DNS_ERROR_NAME_DOES_NOT_EXIST)) {
596 goto exit;
599 ret = dns_wildcard_lookup(samdb, mem_ctx, dn, &msg);
600 if (ret == LDB_ERR_OPERATIONS_ERROR) {
601 werr = DNS_ERR(SERVER_FAILURE);
602 goto exit;
604 if (ret != LDB_SUCCESS) {
605 werr = DNS_ERR(NAME_ERROR);
606 goto exit;
609 el = ldb_msg_find_element(msg, "dnsRecord");
610 if (el == NULL) {
611 werr = WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
612 goto exit;
615 werr = dns_common_extract(samdb, el, mem_ctx, records, num_records);
616 TALLOC_FREE(msg);
617 if (!W_ERROR_IS_OK(werr)) {
618 goto exit;
621 werr = WERR_OK;
622 exit:
623 DNS_COMMON_LOG_OPERATION(
624 win_errstr(werr),
625 &start,
626 NULL,
627 dn == NULL ? NULL : ldb_dn_get_linearized(dn),
628 NULL);
629 return werr;
632 static int rec_cmp(const struct dnsp_DnssrvRpcRecord *r1,
633 const struct dnsp_DnssrvRpcRecord *r2)
635 if (r1->wType != r2->wType) {
637 * The records are sorted with higher types first
639 return r2->wType - r1->wType;
643 * Then we need to sort from the oldest to newest timestamp
645 return r1->dwTimeStamp - r2->dwTimeStamp;
649 * Check for valid DNS names. These are names which:
650 * - are non-empty
651 * - do not start with a dot
652 * - do not have any empty labels
653 * - have no more than 127 labels
654 * - are no longer than 253 characters
655 * - none of the labels exceed 63 characters
657 WERROR dns_name_check(TALLOC_CTX *mem_ctx, size_t len, const char *name)
659 size_t i;
660 unsigned int labels = 0;
661 unsigned int label_len = 0;
663 if (len == 0) {
664 return WERR_DS_INVALID_DN_SYNTAX;
667 if (len > 1 && name[0] == '.') {
668 return WERR_DS_INVALID_DN_SYNTAX;
671 if ((len - 1) > DNS_MAX_DOMAIN_LENGTH) {
672 return WERR_DS_INVALID_DN_SYNTAX;
675 for (i = 0; i < len - 1; i++) {
676 if (name[i] == '.' && name[i+1] == '.') {
677 return WERR_DS_INVALID_DN_SYNTAX;
679 if (name[i] == '.') {
680 labels++;
681 if (labels > DNS_MAX_LABELS) {
682 return WERR_DS_INVALID_DN_SYNTAX;
684 label_len = 0;
685 } else {
686 label_len++;
687 if (label_len > DNS_MAX_LABEL_LENGTH) {
688 return WERR_DS_INVALID_DN_SYNTAX;
693 return WERR_OK;
696 static WERROR check_name_list(TALLOC_CTX *mem_ctx, uint16_t rec_count,
697 struct dnsp_DnssrvRpcRecord *records)
699 WERROR werr;
700 uint16_t i;
701 size_t len;
702 struct dnsp_DnssrvRpcRecord record;
704 werr = WERR_OK;
705 for (i = 0; i < rec_count; i++) {
706 record = records[i];
708 switch (record.wType) {
710 case DNS_TYPE_NS:
711 len = strlen(record.data.ns);
712 werr = dns_name_check(mem_ctx, len, record.data.ns);
713 break;
714 case DNS_TYPE_CNAME:
715 len = strlen(record.data.cname);
716 werr = dns_name_check(mem_ctx, len, record.data.cname);
717 break;
718 case DNS_TYPE_SOA:
719 len = strlen(record.data.soa.mname);
720 werr = dns_name_check(mem_ctx, len, record.data.soa.mname);
721 if (!W_ERROR_IS_OK(werr)) {
722 break;
724 len = strlen(record.data.soa.rname);
725 werr = dns_name_check(mem_ctx, len, record.data.soa.rname);
726 break;
727 case DNS_TYPE_PTR:
728 len = strlen(record.data.ptr);
729 werr = dns_name_check(mem_ctx, len, record.data.ptr);
730 break;
731 case DNS_TYPE_MX:
732 len = strlen(record.data.mx.nameTarget);
733 werr = dns_name_check(mem_ctx, len, record.data.mx.nameTarget);
734 break;
735 case DNS_TYPE_SRV:
736 len = strlen(record.data.srv.nameTarget);
737 werr = dns_name_check(mem_ctx, len,
738 record.data.srv.nameTarget);
739 break;
741 * In the default case, the record doesn't have a DN, so it
742 * must be ok.
744 default:
745 break;
748 if (!W_ERROR_IS_OK(werr)) {
749 return werr;
753 return WERR_OK;
756 bool dns_name_is_static(struct dnsp_DnssrvRpcRecord *records,
757 uint16_t rec_count)
759 int i = 0;
760 for (i = 0; i < rec_count; i++) {
761 if (records[i].wType == DNS_TYPE_TOMBSTONE) {
762 continue;
765 if (records[i].wType == DNS_TYPE_SOA ||
766 records[i].dwTimeStamp == 0) {
767 return true;
770 return false;
774 * Helper function to copy a dnsp_ip4_array struct to an IP4_ARRAY struct.
775 * The new structure and it's data are allocated on the supplied talloc context
777 static struct IP4_ARRAY *copy_ip4_array(TALLOC_CTX *ctx,
778 const char *name,
779 struct dnsp_ip4_array array)
782 struct IP4_ARRAY *ip4_array = NULL;
783 unsigned int i;
785 ip4_array = talloc_zero(ctx, struct IP4_ARRAY);
786 if (ip4_array == NULL) {
787 DBG_ERR("Out of memory copying property [%s]\n", name);
788 return NULL;
791 ip4_array->AddrCount = array.addrCount;
792 if (ip4_array->AddrCount == 0) {
793 return ip4_array;
796 ip4_array->AddrArray =
797 talloc_array(ip4_array, uint32_t, ip4_array->AddrCount);
798 if (ip4_array->AddrArray == NULL) {
799 TALLOC_FREE(ip4_array);
800 DBG_ERR("Out of memory copying property [%s] values\n", name);
801 return NULL;
804 for (i = 0; i < ip4_array->AddrCount; i++) {
805 ip4_array->AddrArray[i] = array.addrArray[i];
808 return ip4_array;
811 bool dns_zoneinfo_load_zone_property(struct dnsserver_zoneinfo *zoneinfo,
812 struct dnsp_DnsProperty *prop)
814 switch (prop->id) {
815 case DSPROPERTY_ZONE_TYPE:
816 zoneinfo->dwZoneType = prop->data.zone_type;
817 break;
818 case DSPROPERTY_ZONE_ALLOW_UPDATE:
819 zoneinfo->fAllowUpdate = prop->data.allow_update_flag;
820 break;
821 case DSPROPERTY_ZONE_NOREFRESH_INTERVAL:
822 zoneinfo->dwNoRefreshInterval = prop->data.norefresh_hours;
823 break;
824 case DSPROPERTY_ZONE_REFRESH_INTERVAL:
825 zoneinfo->dwRefreshInterval = prop->data.refresh_hours;
826 break;
827 case DSPROPERTY_ZONE_AGING_STATE:
828 zoneinfo->fAging = prop->data.aging_enabled;
829 break;
830 case DSPROPERTY_ZONE_SCAVENGING_SERVERS:
831 zoneinfo->aipScavengeServers = copy_ip4_array(
832 zoneinfo, "ZONE_SCAVENGING_SERVERS", prop->data.servers);
833 if (zoneinfo->aipScavengeServers == NULL) {
834 return false;
836 break;
837 case DSPROPERTY_ZONE_AGING_ENABLED_TIME:
838 zoneinfo->dwAvailForScavengeTime =
839 prop->data.next_scavenging_cycle_hours;
840 break;
841 case DSPROPERTY_ZONE_MASTER_SERVERS:
842 zoneinfo->aipLocalMasters = copy_ip4_array(
843 zoneinfo, "ZONE_MASTER_SERVERS", prop->data.master_servers);
844 if (zoneinfo->aipLocalMasters == NULL) {
845 return false;
847 break;
848 case DSPROPERTY_ZONE_EMPTY:
849 case DSPROPERTY_ZONE_SECURE_TIME:
850 case DSPROPERTY_ZONE_DELETED_FROM_HOSTNAME:
851 case DSPROPERTY_ZONE_AUTO_NS_SERVERS:
852 case DSPROPERTY_ZONE_DCPROMO_CONVERT:
853 case DSPROPERTY_ZONE_SCAVENGING_SERVERS_DA:
854 case DSPROPERTY_ZONE_MASTER_SERVERS_DA:
855 case DSPROPERTY_ZONE_NS_SERVERS_DA:
856 case DSPROPERTY_ZONE_NODE_DBFLAGS:
857 break;
859 return true;
861 WERROR dns_get_zone_properties(struct ldb_context *samdb,
862 TALLOC_CTX *mem_ctx,
863 struct ldb_dn *zone_dn,
864 struct dnsserver_zoneinfo *zoneinfo)
867 int ret, i;
868 struct dnsp_DnsProperty *prop = NULL;
869 struct ldb_message_element *element = NULL;
870 const char *const attrs[] = {"dNSProperty", NULL};
871 struct ldb_result *res = NULL;
872 enum ndr_err_code err;
874 ret = ldb_search(samdb,
875 mem_ctx,
876 &res,
877 zone_dn,
878 LDB_SCOPE_BASE,
879 attrs,
880 "(objectClass=dnsZone)");
881 if (ret != LDB_SUCCESS) {
882 DBG_ERR("dnsserver: Failed to find DNS zone: %s\n",
883 ldb_dn_get_linearized(zone_dn));
884 return DNS_ERR(SERVER_FAILURE);
887 element = ldb_msg_find_element(res->msgs[0], "dNSProperty");
888 if (element == NULL) {
889 return DNS_ERR(NOTZONE);
892 for (i = 0; i < element->num_values; i++) {
893 bool valid_property;
894 prop = talloc_zero(mem_ctx, struct dnsp_DnsProperty);
895 if (prop == NULL) {
896 return WERR_NOT_ENOUGH_MEMORY;
898 err = ndr_pull_struct_blob(
899 &(element->values[i]),
900 mem_ctx,
901 prop,
902 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnsProperty);
903 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
905 * If we can't pull it, then there is no valid
906 * data to load into the zone, so ignore this
907 * as Micosoft does. Windows can load an
908 * invalid property with a zero length into
909 * the dnsProperty attribute.
911 continue;
914 valid_property =
915 dns_zoneinfo_load_zone_property(zoneinfo, prop);
916 if (!valid_property) {
917 return DNS_ERR(SERVER_FAILURE);
921 return WERR_OK;
924 WERROR dns_common_replace(struct ldb_context *samdb,
925 TALLOC_CTX *mem_ctx,
926 struct ldb_dn *dn,
927 bool needs_add,
928 uint32_t serial,
929 struct dnsp_DnssrvRpcRecord *records,
930 uint16_t rec_count)
932 const struct timeval start = timeval_current();
933 struct ldb_message_element *el;
934 uint16_t i;
935 int ret;
936 WERROR werr;
937 struct ldb_message *msg = NULL;
938 bool was_tombstoned = false;
939 bool become_tombstoned = false;
940 struct ldb_dn *zone_dn = NULL;
941 struct dnsserver_zoneinfo *zoneinfo = NULL;
942 NTTIME t;
944 msg = ldb_msg_new(mem_ctx);
945 W_ERROR_HAVE_NO_MEMORY(msg);
947 msg->dn = dn;
949 zone_dn = ldb_dn_copy(mem_ctx, dn);
950 if (zone_dn == NULL) {
951 werr = WERR_NOT_ENOUGH_MEMORY;
952 goto exit;
954 if (!ldb_dn_remove_child_components(zone_dn, 1)) {
955 werr = DNS_ERR(SERVER_FAILURE);
956 goto exit;
958 zoneinfo = talloc(mem_ctx, struct dnsserver_zoneinfo);
959 if (zoneinfo == NULL) {
960 werr = WERR_NOT_ENOUGH_MEMORY;
961 goto exit;
963 werr = dns_get_zone_properties(samdb, mem_ctx, zone_dn, zoneinfo);
964 if (W_ERROR_EQUAL(DNS_ERR(NOTZONE), werr)) {
966 * We only got zoneinfo for aging so if we didn't find any
967 * properties then just disable aging and keep going.
969 zoneinfo->fAging = 0;
970 } else if (!W_ERROR_IS_OK(werr)) {
971 goto exit;
974 werr = check_name_list(mem_ctx, rec_count, records);
975 if (!W_ERROR_IS_OK(werr)) {
976 goto exit;
979 ret = ldb_msg_add_empty(msg, "dnsRecord", LDB_FLAG_MOD_REPLACE, &el);
980 if (ret != LDB_SUCCESS) {
981 werr = DNS_ERR(SERVER_FAILURE);
982 goto exit;
986 * we have at least one value,
987 * which might be used for the tombstone marker
989 el->values = talloc_zero_array(el, struct ldb_val, MAX(1, rec_count));
990 if (rec_count > 0) {
991 if (el->values == NULL) {
992 werr = WERR_NOT_ENOUGH_MEMORY;
993 goto exit;
997 * We store a sorted list with the high wType values first
998 * that's what windows does. It also simplifies the
999 * filtering of DNS_TYPE_TOMBSTONE records
1001 TYPESAFE_QSORT(records, rec_count, rec_cmp);
1004 for (i = 0; i < rec_count; i++) {
1005 struct ldb_val *v = &el->values[el->num_values];
1006 enum ndr_err_code ndr_err;
1008 if (records[i].wType == DNS_TYPE_TOMBSTONE) {
1009 if (records[i].data.timestamp != 0) {
1010 was_tombstoned = true;
1012 continue;
1015 if (zoneinfo->fAging == 1 && records[i].dwTimeStamp != 0) {
1016 unix_to_nt_time(&t, time(NULL));
1017 t /= 10 * 1000 * 1000;
1018 t /= 3600;
1019 if (t - records[i].dwTimeStamp >
1020 zoneinfo->dwNoRefreshInterval) {
1021 records[i].dwTimeStamp = t;
1025 records[i].dwSerial = serial;
1026 ndr_err = ndr_push_struct_blob(v, el->values, &records[i],
1027 (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
1028 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1029 DEBUG(0, ("Failed to push dnsp_DnssrvRpcRecord\n"));
1030 werr = DNS_ERR(SERVER_FAILURE);
1031 goto exit;
1033 el->num_values++;
1036 if (needs_add) {
1037 if (el->num_values == 0) {
1038 werr = WERR_OK;
1039 goto exit;
1042 ret = ldb_msg_add_string(msg, "objectClass", "dnsNode");
1043 if (ret != LDB_SUCCESS) {
1044 werr = DNS_ERR(SERVER_FAILURE);
1045 goto exit;
1048 ret = ldb_add(samdb, msg);
1049 if (ret != LDB_SUCCESS) {
1050 werr = DNS_ERR(SERVER_FAILURE);
1051 goto exit;
1054 return WERR_OK;
1055 goto exit;
1058 if (el->num_values == 0) {
1059 struct dnsp_DnssrvRpcRecord tbs;
1060 struct ldb_val *v = &el->values[el->num_values];
1061 enum ndr_err_code ndr_err;
1062 struct timeval tv;
1064 if (was_tombstoned) {
1066 * This is already a tombstoned object.
1067 * Just leave it instead of updating the time stamp.
1069 werr = WERR_OK;
1070 goto exit;
1073 tv = timeval_current();
1074 tbs = (struct dnsp_DnssrvRpcRecord) {
1075 .wType = DNS_TYPE_TOMBSTONE,
1076 .dwSerial = serial,
1077 .data.timestamp = timeval_to_nttime(&tv),
1080 ndr_err = ndr_push_struct_blob(v, el->values, &tbs,
1081 (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
1082 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1083 DEBUG(0, ("Failed to push dnsp_DnssrvRpcRecord\n"));
1084 werr = DNS_ERR(SERVER_FAILURE);
1085 goto exit;
1087 el->num_values++;
1089 become_tombstoned = true;
1092 if (was_tombstoned || become_tombstoned) {
1093 ret = ldb_msg_add_empty(msg, "dNSTombstoned",
1094 LDB_FLAG_MOD_REPLACE, NULL);
1095 if (ret != LDB_SUCCESS) {
1096 werr = DNS_ERR(SERVER_FAILURE);
1097 goto exit;
1100 ret = ldb_msg_add_fmt(msg, "dNSTombstoned", "%s",
1101 become_tombstoned ? "TRUE" : "FALSE");
1102 if (ret != LDB_SUCCESS) {
1103 werr = DNS_ERR(SERVER_FAILURE);
1104 goto exit;
1108 ret = ldb_modify(samdb, msg);
1109 if (ret != LDB_SUCCESS) {
1110 NTSTATUS nt = dsdb_ldb_err_to_ntstatus(ret);
1111 werr = ntstatus_to_werror(nt);
1112 goto exit;
1115 werr = WERR_OK;
1116 exit:
1117 DNS_COMMON_LOG_OPERATION(
1118 win_errstr(werr),
1119 &start,
1120 NULL,
1121 dn == NULL ? NULL : ldb_dn_get_linearized(dn),
1122 NULL);
1123 return werr;
1126 bool dns_name_match(const char *zone, const char *name, size_t *host_part_len)
1128 size_t zl = strlen(zone);
1129 size_t nl = strlen(name);
1130 ssize_t zi, ni;
1131 static const size_t fixup = 'a' - 'A';
1133 if (zl > nl) {
1134 return false;
1137 for (zi = zl, ni = nl; zi >= 0; zi--, ni--) {
1138 char zc = zone[zi];
1139 char nc = name[ni];
1141 /* convert to lower case */
1142 if (zc >= 'A' && zc <= 'Z') {
1143 zc += fixup;
1145 if (nc >= 'A' && nc <= 'Z') {
1146 nc += fixup;
1149 if (zc != nc) {
1150 return false;
1154 if (ni >= 0) {
1155 if (name[ni] != '.') {
1156 return false;
1159 ni--;
1162 *host_part_len = ni+1;
1164 return true;
1167 WERROR dns_common_name2dn(struct ldb_context *samdb,
1168 struct dns_server_zone *zones,
1169 TALLOC_CTX *mem_ctx,
1170 const char *name,
1171 struct ldb_dn **_dn)
1173 struct ldb_dn *base;
1174 struct ldb_dn *dn;
1175 const struct dns_server_zone *z;
1176 size_t host_part_len = 0;
1177 struct ldb_val host_part;
1178 WERROR werr;
1179 bool ok;
1180 const char *casefold = NULL;
1182 if (name == NULL) {
1183 return DNS_ERR(FORMAT_ERROR);
1186 if (strcmp(name, "") == 0) {
1187 base = ldb_get_default_basedn(samdb);
1188 dn = ldb_dn_copy(mem_ctx, base);
1189 ok = ldb_dn_add_child_fmt(dn,
1190 "DC=@,DC=RootDNSServers,CN=MicrosoftDNS,CN=System");
1191 if (ok == false) {
1192 TALLOC_FREE(dn);
1193 return WERR_NOT_ENOUGH_MEMORY;
1196 *_dn = dn;
1197 return WERR_OK;
1200 /* Check non-empty names */
1201 werr = dns_name_check(mem_ctx, strlen(name), name);
1202 if (!W_ERROR_IS_OK(werr)) {
1203 return werr;
1206 for (z = zones; z != NULL; z = z->next) {
1207 bool match;
1209 match = dns_name_match(z->name, name, &host_part_len);
1210 if (match) {
1211 break;
1215 if (z == NULL) {
1216 return DNS_ERR(NAME_ERROR);
1219 if (host_part_len == 0) {
1220 dn = ldb_dn_copy(mem_ctx, z->dn);
1221 ok = ldb_dn_add_child_fmt(dn, "DC=@");
1222 if (! ok) {
1223 TALLOC_FREE(dn);
1224 return WERR_NOT_ENOUGH_MEMORY;
1226 *_dn = dn;
1227 return WERR_OK;
1230 dn = ldb_dn_copy(mem_ctx, z->dn);
1231 if (dn == NULL) {
1232 TALLOC_FREE(dn);
1233 return WERR_NOT_ENOUGH_MEMORY;
1236 host_part = data_blob_const(name, host_part_len);
1238 ok = ldb_dn_add_child_val(dn, "DC", host_part);
1240 if (ok == false) {
1241 TALLOC_FREE(dn);
1242 return WERR_NOT_ENOUGH_MEMORY;
1246 * Check the new DN here for validity, so as to catch errors
1247 * early
1249 ok = ldb_dn_validate(dn);
1250 if (ok == false) {
1251 TALLOC_FREE(dn);
1252 return DNS_ERR(NAME_ERROR);
1256 * The value from this check is saved in the DN, and doing
1257 * this here allows an easy return here.
1259 casefold = ldb_dn_get_casefold(dn);
1260 if (casefold == NULL) {
1261 TALLOC_FREE(dn);
1262 return DNS_ERR(NAME_ERROR);
1265 *_dn = dn;
1266 return WERR_OK;
1269 static int dns_common_sort_zones(struct ldb_message **m1, struct ldb_message **m2)
1271 const char *n1, *n2;
1272 size_t l1, l2;
1274 n1 = ldb_msg_find_attr_as_string(*m1, "name", NULL);
1275 n2 = ldb_msg_find_attr_as_string(*m2, "name", NULL);
1276 if (n1 == NULL || n2 == NULL) {
1277 if (n1 != NULL) {
1278 return -1;
1279 } else if (n2 != NULL) {
1280 return 1;
1281 } else {
1282 return 0;
1285 l1 = strlen(n1);
1286 l2 = strlen(n2);
1288 /* If the string lengths are not equal just sort by length */
1289 if (l1 != l2) {
1290 /* If m1 is the larger zone name, return it first */
1291 return l2 - l1;
1294 /*TODO: We need to compare DNs here, we want the DomainDNSZones first */
1295 return 0;
1298 NTSTATUS dns_common_zones(struct ldb_context *samdb,
1299 TALLOC_CTX *mem_ctx,
1300 struct ldb_dn *base_dn,
1301 struct dns_server_zone **zones_ret)
1303 const struct timeval start = timeval_current();
1304 int ret;
1305 static const char * const attrs[] = { "name", NULL};
1306 struct ldb_result *res;
1307 int i;
1308 struct dns_server_zone *new_list = NULL;
1309 TALLOC_CTX *frame = talloc_stackframe();
1310 NTSTATUS result = NT_STATUS_OK;
1312 if (base_dn) {
1313 /* This search will work against windows */
1314 ret = dsdb_search(samdb, frame, &res,
1315 base_dn, LDB_SCOPE_SUBTREE,
1316 attrs, 0, "(objectClass=dnsZone)");
1317 } else {
1318 /* TODO: this search does not work against windows */
1319 ret = dsdb_search(samdb, frame, &res, NULL,
1320 LDB_SCOPE_SUBTREE,
1321 attrs,
1322 DSDB_SEARCH_SEARCH_ALL_PARTITIONS,
1323 "(objectClass=dnsZone)");
1325 if (ret != LDB_SUCCESS) {
1326 TALLOC_FREE(frame);
1327 result = NT_STATUS_INTERNAL_DB_CORRUPTION;
1328 goto exit;
1331 TYPESAFE_QSORT(res->msgs, res->count, dns_common_sort_zones);
1333 for (i=0; i < res->count; i++) {
1334 struct dns_server_zone *z;
1336 z = talloc_zero(mem_ctx, struct dns_server_zone);
1337 if (z == NULL) {
1338 TALLOC_FREE(frame);
1339 result = NT_STATUS_NO_MEMORY;
1340 goto exit;
1343 z->name = ldb_msg_find_attr_as_string(res->msgs[i], "name", NULL);
1344 talloc_steal(z, z->name);
1345 z->dn = talloc_move(z, &res->msgs[i]->dn);
1347 * Ignore the RootDNSServers zone and zones that we don't support yet
1348 * RootDNSServers should never be returned (Windows DNS server don't)
1349 * ..TrustAnchors should never be returned as is, (Windows returns
1350 * TrustAnchors) and for the moment we don't support DNSSEC so we'd better
1351 * not return this zone.
1353 if ((strcmp(z->name, "RootDNSServers") == 0) ||
1354 (strcmp(z->name, "..TrustAnchors") == 0))
1356 DEBUG(10, ("Ignoring zone %s\n", z->name));
1357 talloc_free(z);
1358 continue;
1360 DLIST_ADD_END(new_list, z);
1363 *zones_ret = new_list;
1364 TALLOC_FREE(frame);
1365 result = NT_STATUS_OK;
1366 exit:
1367 DNS_COMMON_LOG_OPERATION(
1368 nt_errstr(result),
1369 &start,
1370 NULL,
1371 base_dn == NULL ? NULL : ldb_dn_get_linearized(base_dn),
1372 NULL);
1373 return result;
1377 see if two DNS names are the same
1379 bool dns_name_equal(const char *name1, const char *name2)
1381 size_t len1 = strlen(name1);
1382 size_t len2 = strlen(name2);
1384 if (len1 > 0 && name1[len1 - 1] == '.') {
1385 len1--;
1387 if (len2 > 0 && name2[len2 - 1] == '.') {
1388 len2--;
1390 if (len1 != len2) {
1391 return false;
1393 return strncasecmp(name1, name2, len1) == 0;