2 Unix SMB/CIFS implementation.
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/>.
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"
31 #include "dsdb/samdb/samdb.h"
32 #include "dsdb/common/util.h"
33 #include "dns_server/dnsserver_common.h"
34 #include "lib/util/dlinklist.h"
37 #define DBGC_CLASS DBGC_DNS
39 uint8_t werr_to_dns_err(WERROR werr)
41 if (W_ERROR_EQUAL(WERR_OK, werr)) {
43 } else if (W_ERROR_EQUAL(DNS_ERR(FORMAT_ERROR), werr)) {
44 return DNS_RCODE_FORMERR;
45 } else if (W_ERROR_EQUAL(DNS_ERR(SERVER_FAILURE), werr)) {
46 return DNS_RCODE_SERVFAIL;
47 } else if (W_ERROR_EQUAL(DNS_ERR(NAME_ERROR), werr)) {
48 return DNS_RCODE_NXDOMAIN;
49 } else if (W_ERROR_EQUAL(WERR_DNS_ERROR_NAME_DOES_NOT_EXIST, werr)) {
50 return DNS_RCODE_NXDOMAIN;
51 } else if (W_ERROR_EQUAL(DNS_ERR(NOT_IMPLEMENTED), werr)) {
52 return DNS_RCODE_NOTIMP;
53 } else if (W_ERROR_EQUAL(DNS_ERR(REFUSED), werr)) {
54 return DNS_RCODE_REFUSED;
55 } else if (W_ERROR_EQUAL(DNS_ERR(YXDOMAIN), werr)) {
56 return DNS_RCODE_YXDOMAIN;
57 } else if (W_ERROR_EQUAL(DNS_ERR(YXRRSET), werr)) {
58 return DNS_RCODE_YXRRSET;
59 } else if (W_ERROR_EQUAL(DNS_ERR(NXRRSET), werr)) {
60 return DNS_RCODE_NXRRSET;
61 } else if (W_ERROR_EQUAL(DNS_ERR(NOTAUTH), werr)) {
62 return DNS_RCODE_NOTAUTH;
63 } else if (W_ERROR_EQUAL(DNS_ERR(NOTZONE), werr)) {
64 return DNS_RCODE_NOTZONE;
65 } else if (W_ERROR_EQUAL(DNS_ERR(BADKEY), werr)) {
66 return DNS_RCODE_BADKEY;
68 DEBUG(5, ("No mapping exists for %s\n", win_errstr(werr)));
69 return DNS_RCODE_SERVFAIL;
72 WERROR dns_common_extract(const struct ldb_message_element *el,
74 struct dnsp_DnssrvRpcRecord **records,
75 uint16_t *num_records)
78 struct dnsp_DnssrvRpcRecord *recs;
83 recs = talloc_zero_array(mem_ctx, struct dnsp_DnssrvRpcRecord,
88 for (ri = 0; ri < el->num_values; ri++) {
89 struct ldb_val *v = &el->values[ri];
90 enum ndr_err_code ndr_err;
92 ndr_err = ndr_pull_struct_blob(v, recs, &recs[ri],
93 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
94 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
96 DEBUG(0, ("Failed to grab dnsp_DnssrvRpcRecord\n"));
97 return DNS_ERR(SERVER_FAILURE);
101 *num_records = el->num_values;
105 WERROR dns_common_lookup(struct ldb_context *samdb,
108 struct dnsp_DnssrvRpcRecord **records,
109 uint16_t *num_records,
112 static const char * const attrs[] = {
119 struct ldb_message *msg = NULL;
120 struct ldb_message_element *el;
125 if (tombstoned != NULL) {
127 ret = dsdb_search_one(samdb, mem_ctx, &msg, dn,
128 LDB_SCOPE_BASE, attrs, 0,
129 "(objectClass=dnsNode)");
131 ret = dsdb_search_one(samdb, mem_ctx, &msg, dn,
132 LDB_SCOPE_BASE, attrs, 0,
133 "(&(objectClass=dnsNode)(!(dNSTombstoned=TRUE)))");
135 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
136 return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
138 if (ret != LDB_SUCCESS) {
139 /* TODO: we need to check if there's a glue record we need to
140 * create a referral to */
141 return DNS_ERR(NAME_ERROR);
144 if (tombstoned != NULL) {
145 *tombstoned = ldb_msg_find_attr_as_bool(msg,
146 "dNSTombstoned", false);
149 el = ldb_msg_find_element(msg, "dnsRecord");
153 * records produced by older Samba releases
154 * keep dnsNode objects without dnsRecord and
155 * without setting dNSTombstoned=TRUE.
157 * We just pretend they're tombstones.
159 if (tombstoned != NULL) {
160 struct dnsp_DnssrvRpcRecord *recs;
161 recs = talloc_array(mem_ctx,
162 struct dnsp_DnssrvRpcRecord,
167 recs[0] = (struct dnsp_DnssrvRpcRecord) {
168 .wType = DNS_TYPE_TOMBSTONE,
170 * A value of timestamp != 0
171 * indicated that the object was already
172 * a tombstone, this will be used
173 * in dns_common_replace()
184 * Because we are not looking for a tombstone
185 * in this codepath, we just pretend it does
188 return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
192 werr = dns_common_extract(el, mem_ctx, records, num_records);
194 if (!W_ERROR_IS_OK(werr)) {
201 static int rec_cmp(const struct dnsp_DnssrvRpcRecord *r1,
202 const struct dnsp_DnssrvRpcRecord *r2)
204 if (r1->wType != r2->wType) {
206 * The records are sorted with higher types first
208 return r2->wType - r1->wType;
212 * Then we need to sort from the oldest to newest timestamp
214 return r1->dwTimeStamp - r2->dwTimeStamp;
217 WERROR dns_common_replace(struct ldb_context *samdb,
222 struct dnsp_DnssrvRpcRecord *records,
225 struct ldb_message_element *el;
228 struct ldb_message *msg = NULL;
229 bool was_tombstoned = false;
230 bool become_tombstoned = false;
232 msg = ldb_msg_new(mem_ctx);
233 W_ERROR_HAVE_NO_MEMORY(msg);
237 ret = ldb_msg_add_empty(msg, "dnsRecord", LDB_FLAG_MOD_REPLACE, &el);
238 if (ret != LDB_SUCCESS) {
239 return DNS_ERR(SERVER_FAILURE);
243 * we have at least one value,
244 * which might be used for the tombstone marker
246 el->values = talloc_zero_array(el, struct ldb_val, MAX(1, rec_count));
248 W_ERROR_HAVE_NO_MEMORY(el->values);
251 * We store a sorted list with the high wType values first
252 * that's what windows does. It also simplifies the
253 * filtering of DNS_TYPE_TOMBSTONE records
255 TYPESAFE_QSORT(records, rec_count, rec_cmp);
258 for (i = 0; i < rec_count; i++) {
259 struct ldb_val *v = &el->values[el->num_values];
260 enum ndr_err_code ndr_err;
262 if (records[i].wType == DNS_TYPE_TOMBSTONE) {
263 if (records[i].data.timestamp != 0) {
264 was_tombstoned = true;
269 records[i].dwSerial = serial;
270 ndr_err = ndr_push_struct_blob(v, el->values, &records[i],
271 (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
272 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
273 DEBUG(0, ("Failed to push dnsp_DnssrvRpcRecord\n"));
274 return DNS_ERR(SERVER_FAILURE);
280 if (el->num_values == 0) {
284 ret = ldb_msg_add_string(msg, "objectClass", "dnsNode");
285 if (ret != LDB_SUCCESS) {
286 return DNS_ERR(SERVER_FAILURE);
289 ret = ldb_add(samdb, msg);
290 if (ret != LDB_SUCCESS) {
291 return DNS_ERR(SERVER_FAILURE);
297 if (el->num_values == 0) {
298 struct dnsp_DnssrvRpcRecord tbs;
299 struct ldb_val *v = &el->values[el->num_values];
300 enum ndr_err_code ndr_err;
303 if (was_tombstoned) {
305 * This is already a tombstoned object.
306 * Just leave it instead of updating the time stamp.
311 tv = timeval_current();
312 tbs = (struct dnsp_DnssrvRpcRecord) {
313 .wType = DNS_TYPE_TOMBSTONE,
315 .data.timestamp = timeval_to_nttime(&tv),
318 ndr_err = ndr_push_struct_blob(v, el->values, &tbs,
319 (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
320 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
321 DEBUG(0, ("Failed to push dnsp_DnssrvRpcRecord\n"));
322 return DNS_ERR(SERVER_FAILURE);
326 become_tombstoned = true;
329 if (was_tombstoned || become_tombstoned) {
330 ret = ldb_msg_add_empty(msg, "dNSTombstoned",
331 LDB_FLAG_MOD_REPLACE, NULL);
332 if (ret != LDB_SUCCESS) {
333 return DNS_ERR(SERVER_FAILURE);
336 ret = ldb_msg_add_fmt(msg, "dNSTombstoned", "%s",
337 become_tombstoned ? "TRUE" : "FALSE");
338 if (ret != LDB_SUCCESS) {
339 return DNS_ERR(SERVER_FAILURE);
343 ret = ldb_modify(samdb, msg);
344 if (ret != LDB_SUCCESS) {
345 NTSTATUS nt = dsdb_ldb_err_to_ntstatus(ret);
346 return ntstatus_to_werror(nt);
352 bool dns_name_match(const char *zone, const char *name, size_t *host_part_len)
354 size_t zl = strlen(zone);
355 size_t nl = strlen(name);
357 static const size_t fixup = 'a' - 'A';
363 for (zi = zl, ni = nl; zi >= 0; zi--, ni--) {
367 /* convert to lower case */
368 if (zc >= 'A' && zc <= 'Z') {
371 if (nc >= 'A' && nc <= 'Z') {
381 if (name[ni] != '.') {
388 *host_part_len = ni+1;
393 WERROR dns_common_name2dn(struct ldb_context *samdb,
394 struct dns_server_zone *zones,
401 const struct dns_server_zone *z;
402 size_t host_part_len = 0;
405 return DNS_ERR(FORMAT_ERROR);
408 /*TODO: Check if 'name' is a valid DNS name */
410 if (strcmp(name, "") == 0) {
411 base = ldb_get_default_basedn(samdb);
412 dn = ldb_dn_copy(mem_ctx, base);
413 ldb_dn_add_child_fmt(dn, "DC=@,DC=RootDNSServers,CN=MicrosoftDNS,CN=System");
418 for (z = zones; z != NULL; z = z->next) {
421 match = dns_name_match(z->name, name, &host_part_len);
428 return DNS_ERR(NAME_ERROR);
431 if (host_part_len == 0) {
432 dn = ldb_dn_copy(mem_ctx, z->dn);
433 ldb_dn_add_child_fmt(dn, "DC=@");
438 dn = ldb_dn_copy(mem_ctx, z->dn);
439 ldb_dn_add_child_fmt(dn, "DC=%*.*s", (int)host_part_len, (int)host_part_len, name);
444 static int dns_common_sort_zones(struct ldb_message **m1, struct ldb_message **m2)
449 n1 = ldb_msg_find_attr_as_string(*m1, "name", NULL);
450 n2 = ldb_msg_find_attr_as_string(*m2, "name", NULL);
455 /* If the string lengths are not equal just sort by length */
457 /* If m1 is the larger zone name, return it first */
461 /*TODO: We need to compare DNs here, we want the DomainDNSZones first */
465 NTSTATUS dns_common_zones(struct ldb_context *samdb,
467 struct dns_server_zone **zones_ret)
470 static const char * const attrs[] = { "name", NULL};
471 struct ldb_result *res;
473 struct dns_server_zone *new_list = NULL;
474 TALLOC_CTX *frame = talloc_stackframe();
476 // TODO: this search does not work against windows
477 ret = dsdb_search(samdb, frame, &res, NULL, LDB_SCOPE_SUBTREE,
478 attrs, DSDB_SEARCH_SEARCH_ALL_PARTITIONS, "(objectClass=dnsZone)");
479 if (ret != LDB_SUCCESS) {
481 return NT_STATUS_INTERNAL_DB_CORRUPTION;
484 TYPESAFE_QSORT(res->msgs, res->count, dns_common_sort_zones);
486 for (i=0; i < res->count; i++) {
487 struct dns_server_zone *z;
489 z = talloc_zero(mem_ctx, struct dns_server_zone);
492 return NT_STATUS_NO_MEMORY;
495 z->name = ldb_msg_find_attr_as_string(res->msgs[i], "name", NULL);
496 talloc_steal(z, z->name);
497 z->dn = talloc_move(z, &res->msgs[i]->dn);
499 * Ignore the RootDNSServers zone and zones that we don't support yet
500 * RootDNSServers should never be returned (Windows DNS server don't)
501 * ..TrustAnchors should never be returned as is, (Windows returns
502 * TrustAnchors) and for the moment we don't support DNSSEC so we'd better
503 * not return this zone.
505 if ((strcmp(z->name, "RootDNSServers") == 0) ||
506 (strcmp(z->name, "..TrustAnchors") == 0))
508 DEBUG(10, ("Ignoring zone %s\n", z->name));
512 DLIST_ADD_END(new_list, z, NULL);
515 *zones_ret = new_list;