s3:winbind: Remove no longer used domain's private_data pointer
[Samba.git] / source4 / dns_server / dlz_bind9.c
blob0bd55f1aac43f736080d658bf9eb92042955a2c1
1 /*
2 Unix SMB/CIFS implementation.
4 bind9 dlz driver for Samba
6 Copyright (C) 2010 Andrew Tridgell
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "includes.h"
23 #include "talloc.h"
24 #include "param/param.h"
25 #include "lib/events/events.h"
26 #include "dsdb/samdb/samdb.h"
27 #include "dsdb/common/util.h"
28 #include "auth/auth.h"
29 #include "auth/session.h"
30 #include "auth/gensec/gensec.h"
31 #include "librpc/gen_ndr/security.h"
32 #include "auth/credentials/credentials.h"
33 #include "system/kerberos.h"
34 #include "auth/kerberos/kerberos.h"
35 #include "gen_ndr/ndr_dnsp.h"
36 #include "gen_ndr/server_id.h"
37 #include "messaging/messaging.h"
38 #include <popt.h>
39 #include "lib/util/dlinklist.h"
40 #include "dlz_minimal.h"
41 #include "dnsserver_common.h"
42 #include "lib/util/smb_strtox.h"
43 #include "lib/util/access.h"
45 #undef strcasecmp
47 struct b9_options {
48 const char *url;
49 const char *debug;
52 struct b9_zone {
53 char *name;
54 struct b9_zone *prev, *next;
57 struct dlz_bind9_data {
58 struct b9_options options;
59 struct ldb_context *samdb;
60 struct tevent_context *ev_ctx;
61 struct loadparm_context *lp;
62 int *transaction_token;
63 uint32_t soa_serial;
64 struct b9_zone *zonelist;
66 /* Used for dynamic update */
67 struct smb_krb5_context *smb_krb5_ctx;
68 struct auth4_context *auth_context;
69 struct auth_session_info *session_info;
70 char *update_name;
72 /* helper functions from the dlz_dlopen driver */
73 log_t *log;
74 dns_sdlz_putrr_t *putrr;
75 dns_sdlz_putnamedrr_t *putnamedrr;
76 dns_dlz_writeablezone_t *writeable_zone;
79 static struct dlz_bind9_data *dlz_bind9_state = NULL;
80 static int dlz_bind9_state_ref_count = 0;
82 static const char *zone_prefixes[] = {
83 "CN=MicrosoftDNS,DC=DomainDnsZones",
84 "CN=MicrosoftDNS,DC=ForestDnsZones",
85 "CN=MicrosoftDNS,CN=System",
86 NULL
90 * Get a printable string representation of an isc_result_t
92 static const char *isc_result_str( const isc_result_t result) {
93 switch (result) {
94 case ISC_R_SUCCESS:
95 return "ISC_R_SUCCESS";
96 case ISC_R_NOMEMORY:
97 return "ISC_R_NOMEMORY";
98 case ISC_R_NOPERM:
99 return "ISC_R_NOPERM";
100 case ISC_R_NOSPACE:
101 return "ISC_R_NOSPACE";
102 case ISC_R_NOTFOUND:
103 return "ISC_R_NOTFOUND";
104 case ISC_R_FAILURE:
105 return "ISC_R_FAILURE";
106 case ISC_R_NOTIMPLEMENTED:
107 return "ISC_R_NOTIMPLEMENTED";
108 case ISC_R_NOMORE:
109 return "ISC_R_NOMORE";
110 case ISC_R_INVALIDFILE:
111 return "ISC_R_INVALIDFILE";
112 case ISC_R_UNEXPECTED:
113 return "ISC_R_UNEXPECTED";
114 case ISC_R_FILENOTFOUND:
115 return "ISC_R_FILENOTFOUND";
116 default:
117 return "UNKNOWN";
122 return the version of the API
124 _PUBLIC_ int dlz_version(unsigned int *flags)
126 return DLZ_DLOPEN_VERSION;
130 remember a helper function from the bind9 dlz_dlopen driver
132 static void b9_add_helper(struct dlz_bind9_data *state, const char *helper_name, void *ptr)
134 if (strcmp(helper_name, "log") == 0) {
135 state->log = ptr;
137 if (strcmp(helper_name, "putrr") == 0) {
138 state->putrr = ptr;
140 if (strcmp(helper_name, "putnamedrr") == 0) {
141 state->putnamedrr = ptr;
143 if (strcmp(helper_name, "writeable_zone") == 0) {
144 state->writeable_zone = ptr;
149 * Add a trailing '.' if it's missing
151 static const char *b9_format_fqdn(TALLOC_CTX *mem_ctx, const char *str)
153 size_t len;
154 const char *tmp;
156 if (str == NULL || str[0] == '\0') {
157 return str;
160 len = strlen(str);
161 if (str[len-1] != '.') {
162 tmp = talloc_asprintf(mem_ctx, "%s.", str);
163 } else {
164 tmp = str;
166 return tmp;
170 format a record for bind9
172 static bool b9_format(struct dlz_bind9_data *state,
173 TALLOC_CTX *mem_ctx,
174 struct dnsp_DnssrvRpcRecord *rec,
175 const char **type, const char **data)
177 uint32_t i;
178 char *tmp;
179 const char *fqdn;
181 switch (rec->wType) {
182 case DNS_TYPE_A:
183 *type = "a";
184 *data = rec->data.ipv4;
185 break;
187 case DNS_TYPE_AAAA:
188 *type = "aaaa";
189 *data = rec->data.ipv6;
190 break;
192 case DNS_TYPE_CNAME:
193 *type = "cname";
194 *data = b9_format_fqdn(mem_ctx, rec->data.cname);
195 break;
197 case DNS_TYPE_TXT:
198 *type = "txt";
199 tmp = talloc_asprintf(mem_ctx, "\"%s\"", rec->data.txt.str[0]);
200 for (i=1; i<rec->data.txt.count; i++) {
201 tmp = talloc_asprintf_append(tmp, " \"%s\"", rec->data.txt.str[i]);
203 *data = tmp;
204 break;
206 case DNS_TYPE_PTR:
207 *type = "ptr";
208 *data = b9_format_fqdn(mem_ctx, rec->data.ptr);
209 break;
211 case DNS_TYPE_SRV:
212 *type = "srv";
213 fqdn = b9_format_fqdn(mem_ctx, rec->data.srv.nameTarget);
214 if (fqdn == NULL) {
215 return false;
217 *data = talloc_asprintf(mem_ctx, "%u %u %u %s",
218 rec->data.srv.wPriority,
219 rec->data.srv.wWeight,
220 rec->data.srv.wPort,
221 fqdn);
222 break;
224 case DNS_TYPE_MX:
225 *type = "mx";
226 fqdn = b9_format_fqdn(mem_ctx, rec->data.mx.nameTarget);
227 if (fqdn == NULL) {
228 return false;
230 *data = talloc_asprintf(mem_ctx, "%u %s",
231 rec->data.mx.wPriority, fqdn);
232 break;
234 case DNS_TYPE_NS:
235 *type = "ns";
236 *data = b9_format_fqdn(mem_ctx, rec->data.ns);
237 break;
239 case DNS_TYPE_SOA: {
240 const char *mname;
241 *type = "soa";
243 /* we need to fake the authoritative nameserver to
244 * point at ourselves. This is how AD DNS servers
245 * force clients to send updates to the right local DC
247 mname = talloc_asprintf(mem_ctx, "%s.%s.",
248 lpcfg_netbios_name(state->lp),
249 lpcfg_dnsdomain(state->lp));
250 if (mname == NULL) {
251 return false;
253 mname = strlower_talloc(mem_ctx, mname);
254 if (mname == NULL) {
255 return false;
258 fqdn = b9_format_fqdn(mem_ctx, rec->data.soa.rname);
259 if (fqdn == NULL) {
260 return false;
263 state->soa_serial = rec->data.soa.serial;
265 *data = talloc_asprintf(mem_ctx, "%s %s %u %u %u %u %u",
266 mname, fqdn,
267 rec->data.soa.serial,
268 rec->data.soa.refresh,
269 rec->data.soa.retry,
270 rec->data.soa.expire,
271 rec->data.soa.minimum);
272 break;
275 default:
276 state->log(ISC_LOG_ERROR, "samba_dlz b9_format: unhandled record type %u",
277 rec->wType);
278 return false;
281 return true;
284 static const struct {
285 enum dns_record_type dns_type;
286 const char *typestr;
287 bool single_valued;
288 } dns_typemap[] = {
289 { DNS_TYPE_A, "A" , false},
290 { DNS_TYPE_AAAA, "AAAA" , false},
291 { DNS_TYPE_CNAME, "CNAME" , true},
292 { DNS_TYPE_TXT, "TXT" , false},
293 { DNS_TYPE_PTR, "PTR" , false},
294 { DNS_TYPE_SRV, "SRV" , false},
295 { DNS_TYPE_MX, "MX" , false},
296 { DNS_TYPE_NS, "NS" , false},
297 { DNS_TYPE_SOA, "SOA" , true},
302 see if a DNS type is single valued
304 static bool b9_single_valued(enum dns_record_type dns_type)
306 int i;
307 for (i=0; i<ARRAY_SIZE(dns_typemap); i++) {
308 if (dns_typemap[i].dns_type == dns_type) {
309 return dns_typemap[i].single_valued;
312 return false;
316 get a DNS_TYPE_* value from the corresponding string
318 static bool b9_dns_type(const char *type, enum dns_record_type *dtype)
320 int i;
321 for (i=0; i<ARRAY_SIZE(dns_typemap); i++) {
322 if (strcasecmp(dns_typemap[i].typestr, type) == 0) {
323 *dtype = dns_typemap[i].dns_type;
324 return true;
327 return false;
331 #define DNS_PARSE_STR(ret, str, sep, saveptr) do { \
332 (ret) = strtok_r(str, sep, &saveptr); \
333 if ((ret) == NULL) return false; \
334 } while (0)
336 #define DNS_PARSE_UINT(ret, str, sep, saveptr) do { \
337 char *istr = strtok_r(str, sep, &saveptr); \
338 int error = 0;\
339 if ((istr) == NULL) return false; \
340 (ret) = smb_strtoul(istr, NULL, 10, &error, SMB_STR_STANDARD); \
341 if (error != 0) {\
342 return false;\
344 } while (0)
347 parse a record from bind9
349 static bool b9_parse(struct dlz_bind9_data *state,
350 const char *rdatastr,
351 struct dnsp_DnssrvRpcRecord *rec)
353 char *full_name, *dclass, *type;
354 char *str, *tmp, *saveptr=NULL;
355 int i;
357 str = talloc_strdup(rec, rdatastr);
358 if (str == NULL) {
359 return false;
362 /* parse the SDLZ string form */
363 DNS_PARSE_STR(full_name, str, "\t", saveptr);
364 DNS_PARSE_UINT(rec->dwTtlSeconds, NULL, "\t", saveptr);
365 DNS_PARSE_STR(dclass, NULL, "\t", saveptr);
366 DNS_PARSE_STR(type, NULL, "\t", saveptr);
368 /* construct the record */
369 for (i=0; i<ARRAY_SIZE(dns_typemap); i++) {
370 if (strcasecmp(type, dns_typemap[i].typestr) == 0) {
371 rec->wType = dns_typemap[i].dns_type;
372 break;
375 if (i == ARRAY_SIZE(dns_typemap)) {
376 state->log(ISC_LOG_ERROR, "samba_dlz: unsupported record type '%s' for '%s'",
377 type, full_name);
378 return false;
381 switch (rec->wType) {
382 case DNS_TYPE_A:
383 DNS_PARSE_STR(rec->data.ipv4, NULL, " ", saveptr);
384 break;
386 case DNS_TYPE_AAAA:
387 DNS_PARSE_STR(rec->data.ipv6, NULL, " ", saveptr);
388 break;
390 case DNS_TYPE_CNAME:
391 DNS_PARSE_STR(rec->data.cname, NULL, " ", saveptr);
392 break;
394 case DNS_TYPE_TXT:
395 rec->data.txt.count = 0;
396 rec->data.txt.str = talloc_array(rec, const char *, rec->data.txt.count);
397 tmp = strtok_r(NULL, "\t", &saveptr);
398 while (tmp) {
399 rec->data.txt.str = talloc_realloc(rec, rec->data.txt.str, const char *,
400 rec->data.txt.count+1);
401 if (tmp[0] == '"') {
402 /* Strip quotes */
403 rec->data.txt.str[rec->data.txt.count] = talloc_strndup(rec, &tmp[1], strlen(tmp)-2);
404 } else {
405 rec->data.txt.str[rec->data.txt.count] = talloc_strdup(rec, tmp);
407 rec->data.txt.count++;
408 tmp = strtok_r(NULL, " ", &saveptr);
410 break;
412 case DNS_TYPE_PTR:
413 DNS_PARSE_STR(rec->data.ptr, NULL, " ", saveptr);
414 break;
416 case DNS_TYPE_SRV:
417 DNS_PARSE_UINT(rec->data.srv.wPriority, NULL, " ", saveptr);
418 DNS_PARSE_UINT(rec->data.srv.wWeight, NULL, " ", saveptr);
419 DNS_PARSE_UINT(rec->data.srv.wPort, NULL, " ", saveptr);
420 DNS_PARSE_STR(rec->data.srv.nameTarget, NULL, " ", saveptr);
421 break;
423 case DNS_TYPE_MX:
424 DNS_PARSE_UINT(rec->data.mx.wPriority, NULL, " ", saveptr);
425 DNS_PARSE_STR(rec->data.mx.nameTarget, NULL, " ", saveptr);
426 break;
428 case DNS_TYPE_NS:
429 DNS_PARSE_STR(rec->data.ns, NULL, " ", saveptr);
430 break;
432 case DNS_TYPE_SOA:
433 DNS_PARSE_STR(rec->data.soa.mname, NULL, " ", saveptr);
434 DNS_PARSE_STR(rec->data.soa.rname, NULL, " ", saveptr);
435 DNS_PARSE_UINT(rec->data.soa.serial, NULL, " ", saveptr);
436 DNS_PARSE_UINT(rec->data.soa.refresh, NULL, " ", saveptr);
437 DNS_PARSE_UINT(rec->data.soa.retry, NULL, " ", saveptr);
438 DNS_PARSE_UINT(rec->data.soa.expire, NULL, " ", saveptr);
439 DNS_PARSE_UINT(rec->data.soa.minimum, NULL, " ", saveptr);
440 break;
442 default:
443 state->log(ISC_LOG_ERROR, "samba_dlz b9_parse: unhandled record type %u",
444 rec->wType);
445 return false;
448 /* we should be at the end of the buffer now */
449 if (strtok_r(NULL, "\t ", &saveptr) != NULL) {
450 state->log(ISC_LOG_ERROR, "samba_dlz b9_parse: unexpected data at end of string for '%s'",
451 rdatastr);
452 return false;
455 return true;
459 send a resource record to bind9
461 static isc_result_t b9_putrr(struct dlz_bind9_data *state,
462 void *handle, struct dnsp_DnssrvRpcRecord *rec,
463 const char **types)
465 isc_result_t result;
466 const char *type, *data;
467 TALLOC_CTX *tmp_ctx = talloc_new(state);
469 if (!b9_format(state, tmp_ctx, rec, &type, &data)) {
470 return ISC_R_FAILURE;
473 if (data == NULL) {
474 talloc_free(tmp_ctx);
475 return ISC_R_NOMEMORY;
478 if (types) {
479 int i;
480 for (i=0; types[i]; i++) {
481 if (strcmp(types[i], type) == 0) break;
483 if (types[i] == NULL) {
484 /* skip it */
485 return ISC_R_SUCCESS;
489 result = state->putrr(handle, type, rec->dwTtlSeconds, data);
490 if (result != ISC_R_SUCCESS) {
491 state->log(ISC_LOG_ERROR, "Failed to put rr");
493 talloc_free(tmp_ctx);
494 return result;
499 send a named resource record to bind9
501 static isc_result_t b9_putnamedrr(struct dlz_bind9_data *state,
502 void *handle, const char *name,
503 struct dnsp_DnssrvRpcRecord *rec)
505 isc_result_t result;
506 const char *type, *data;
507 TALLOC_CTX *tmp_ctx = talloc_new(state);
509 if (!b9_format(state, tmp_ctx, rec, &type, &data)) {
510 return ISC_R_FAILURE;
513 if (data == NULL) {
514 talloc_free(tmp_ctx);
515 return ISC_R_NOMEMORY;
518 result = state->putnamedrr(handle, name, type, rec->dwTtlSeconds, data);
519 if (result != ISC_R_SUCCESS) {
520 state->log(ISC_LOG_ERROR, "Failed to put named rr '%s'", name);
522 talloc_free(tmp_ctx);
523 return result;
527 parse options
529 static isc_result_t parse_options(struct dlz_bind9_data *state,
530 unsigned int argc, const char **argv,
531 struct b9_options *options)
533 int opt;
534 poptContext pc;
535 struct poptOption long_options[] = {
536 { "url", 'H', POPT_ARG_STRING, &options->url, 0, "database URL", "URL" },
537 { "debug", 'd', POPT_ARG_STRING, &options->debug, 0, "debug level", "DEBUG" },
541 pc = poptGetContext("dlz_bind9", argc, argv, long_options,
542 POPT_CONTEXT_KEEP_FIRST);
543 while ((opt = poptGetNextOpt(pc)) != -1) {
544 switch (opt) {
545 default:
546 state->log(ISC_LOG_ERROR, "dlz_bind9: Invalid option %s: %s",
547 poptBadOption(pc, 0), poptStrerror(opt));
548 poptFreeContext(pc);
549 return ISC_R_FAILURE;
553 poptFreeContext(pc);
554 return ISC_R_SUCCESS;
559 * Create session info from PAC
560 * This is called as auth_context->generate_session_info_pac()
562 static NTSTATUS b9_generate_session_info_pac(struct auth4_context *auth_context,
563 TALLOC_CTX *mem_ctx,
564 struct smb_krb5_context *smb_krb5_context,
565 DATA_BLOB *pac_blob,
566 const char *principal_name,
567 const struct tsocket_address *remote_addr,
568 uint32_t session_info_flags,
569 struct auth_session_info **session_info)
571 NTSTATUS status;
572 struct auth_user_info_dc *user_info_dc;
573 TALLOC_CTX *tmp_ctx;
575 tmp_ctx = talloc_new(mem_ctx);
576 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
578 status = kerberos_pac_blob_to_user_info_dc(tmp_ctx,
579 *pac_blob,
580 smb_krb5_context->krb5_context,
581 &user_info_dc,
582 NULL,
583 NULL);
584 if (!NT_STATUS_IS_OK(status)) {
585 talloc_free(tmp_ctx);
586 return status;
589 if (user_info_dc->info->authenticated) {
590 session_info_flags |= AUTH_SESSION_INFO_AUTHENTICATED;
593 session_info_flags |= AUTH_SESSION_INFO_SIMPLE_PRIVILEGES;
595 status = auth_generate_session_info(mem_ctx, NULL, NULL, user_info_dc,
596 session_info_flags, session_info);
597 if (!NT_STATUS_IS_OK(status)) {
598 talloc_free(tmp_ctx);
599 return status;
602 talloc_free(tmp_ctx);
603 return status;
606 /* Callback for the DEBUG() system, to catch the remaining messages */
607 static void b9_debug(void *private_ptr, int msg_level, const char *msg)
609 static const int isc_log_map[] = {
610 ISC_LOG_CRITICAL, /* 0 */
611 ISC_LOG_ERROR, /* 1 */
612 ISC_LOG_WARNING, /* 2 */
613 ISC_LOG_NOTICE /* 3 */
615 struct dlz_bind9_data *state = private_ptr;
616 int isc_log_level;
618 if (msg_level >= ARRAY_SIZE(isc_log_map) || msg_level < 0) {
619 isc_log_level = ISC_LOG_INFO;
620 } else {
621 isc_log_level = isc_log_map[msg_level];
623 state->log(isc_log_level, "samba_dlz: %s", msg);
626 static int dlz_state_debug_unregister(struct dlz_bind9_data *state)
628 /* Stop logging (to the bind9 logs) */
629 debug_set_callback(NULL, NULL);
630 return 0;
634 called to initialise the driver
636 _PUBLIC_ isc_result_t dlz_create(const char *dlzname,
637 unsigned int argc, const char **argv,
638 void **dbdata, ...)
640 struct dlz_bind9_data *state;
641 const char *helper_name;
642 va_list ap;
643 isc_result_t result;
644 struct ldb_dn *dn;
645 NTSTATUS nt_status;
646 int ret;
647 char *errstring = NULL;
649 if (dlz_bind9_state != NULL) {
650 dlz_bind9_state->log(ISC_LOG_ERROR,
651 "samba_dlz: dlz_create ignored, #refs=%d",
652 dlz_bind9_state_ref_count);
653 *dbdata = dlz_bind9_state;
654 dlz_bind9_state_ref_count++;
655 return ISC_R_SUCCESS;
658 state = talloc_zero(NULL, struct dlz_bind9_data);
659 if (state == NULL) {
660 return ISC_R_NOMEMORY;
663 talloc_set_destructor(state, dlz_state_debug_unregister);
665 /* fill in the helper functions */
666 va_start(ap, dbdata);
667 while ((helper_name = va_arg(ap, const char *)) != NULL) {
668 b9_add_helper(state, helper_name, va_arg(ap, void*));
670 va_end(ap);
672 /* Do not install samba signal handlers */
673 fault_setup_disable();
675 /* Start logging (to the bind9 logs) */
676 debug_set_callback(state, b9_debug);
678 state->ev_ctx = s4_event_context_init(state);
679 if (state->ev_ctx == NULL) {
680 result = ISC_R_NOMEMORY;
681 goto failed;
684 result = parse_options(state, argc, argv, &state->options);
685 if (result != ISC_R_SUCCESS) {
686 goto failed;
689 state->lp = loadparm_init_global(true);
690 if (state->lp == NULL) {
691 result = ISC_R_NOMEMORY;
692 goto failed;
695 if (state->options.debug) {
696 lpcfg_do_global_parameter(state->lp, "log level", state->options.debug);
697 } else {
698 lpcfg_do_global_parameter(state->lp, "log level", "0");
701 if (smb_krb5_init_context(state, state->lp, &state->smb_krb5_ctx) != 0) {
702 result = ISC_R_NOMEMORY;
703 goto failed;
706 nt_status = gensec_init();
707 if (!NT_STATUS_IS_OK(nt_status)) {
708 result = ISC_R_NOMEMORY;
709 goto failed;
712 state->auth_context = talloc_zero(state, struct auth4_context);
713 if (state->auth_context == NULL) {
714 result = ISC_R_NOMEMORY;
715 goto failed;
718 if (state->options.url == NULL) {
719 state->options.url = talloc_asprintf(state,
720 "%s/dns/sam.ldb",
721 lpcfg_binddns_dir(state->lp));
722 if (state->options.url == NULL) {
723 result = ISC_R_NOMEMORY;
724 goto failed;
727 if (!file_exist(state->options.url)) {
728 state->options.url = talloc_asprintf(state,
729 "%s/dns/sam.ldb",
730 lpcfg_private_dir(state->lp));
731 if (state->options.url == NULL) {
732 result = ISC_R_NOMEMORY;
733 goto failed;
738 ret = samdb_connect_url(state,
739 state->ev_ctx,
740 state->lp,
741 system_session(state->lp),
743 state->options.url,
744 NULL,
745 &state->samdb,
746 &errstring);
747 if (ret != LDB_SUCCESS) {
748 state->log(ISC_LOG_ERROR,
749 "samba_dlz: Failed to connect to %s: %s",
750 errstring, ldb_strerror(ret));
751 result = ISC_R_FAILURE;
752 goto failed;
755 dn = ldb_get_default_basedn(state->samdb);
756 if (dn == NULL) {
757 state->log(ISC_LOG_ERROR, "samba_dlz: Unable to get basedn for %s - %s",
758 state->options.url, ldb_errstring(state->samdb));
759 result = ISC_R_FAILURE;
760 goto failed;
763 state->log(ISC_LOG_INFO, "samba_dlz: started for DN %s",
764 ldb_dn_get_linearized(dn));
766 state->auth_context->event_ctx = state->ev_ctx;
767 state->auth_context->lp_ctx = state->lp;
768 state->auth_context->sam_ctx = state->samdb;
769 state->auth_context->generate_session_info_pac = b9_generate_session_info_pac;
771 *dbdata = state;
772 dlz_bind9_state = state;
773 dlz_bind9_state_ref_count++;
775 return ISC_R_SUCCESS;
777 failed:
778 state->log(ISC_LOG_INFO,
779 "samba_dlz: FAILED dlz_create call result=%d #refs=%d",
780 result,
781 dlz_bind9_state_ref_count);
782 talloc_free(state);
783 return result;
787 shutdown the backend
789 _PUBLIC_ void dlz_destroy(void *dbdata)
791 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
793 dlz_bind9_state_ref_count--;
794 if (dlz_bind9_state_ref_count == 0) {
795 state->log(ISC_LOG_INFO, "samba_dlz: shutting down");
796 talloc_unlink(state, state->samdb);
797 talloc_free(state);
798 dlz_bind9_state = NULL;
799 } else {
800 state->log(ISC_LOG_INFO,
801 "samba_dlz: dlz_destroy called. %d refs remaining.",
802 dlz_bind9_state_ref_count);
808 return the base DN for a zone
810 static isc_result_t b9_find_zone_dn(struct dlz_bind9_data *state, const char *zone_name,
811 TALLOC_CTX *mem_ctx, struct ldb_dn **zone_dn)
813 int ret;
814 TALLOC_CTX *tmp_ctx = talloc_new(state);
815 const char *attrs[] = { NULL };
816 int i;
818 for (i=0; zone_prefixes[i]; i++) {
819 const char *casefold;
820 struct ldb_dn *dn;
821 struct ldb_result *res;
822 struct ldb_val zone_name_val
823 = data_blob_string_const(zone_name);
825 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
826 if (dn == NULL) {
827 talloc_free(tmp_ctx);
828 return ISC_R_NOMEMORY;
832 * This dance ensures that it is not possible to put
833 * (eg) an extra DC=x, into the DNS name being
834 * queried
837 if (!ldb_dn_add_child_fmt(dn,
838 "DC=X,%s",
839 zone_prefixes[i])) {
840 talloc_free(tmp_ctx);
841 return ISC_R_NOMEMORY;
844 ret = ldb_dn_set_component(dn,
846 "DC",
847 zone_name_val);
848 if (ret != LDB_SUCCESS) {
849 talloc_free(tmp_ctx);
850 return ISC_R_NOMEMORY;
854 * Check if this is a plausibly valid DN early
855 * (time spent here will be saved during the
856 * search due to an internal cache)
858 casefold = ldb_dn_get_casefold(dn);
860 if (casefold == NULL) {
861 talloc_free(tmp_ctx);
862 return ISC_R_NOTFOUND;
865 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsZone");
866 if (ret == LDB_SUCCESS) {
867 if (zone_dn != NULL) {
868 *zone_dn = talloc_steal(mem_ctx, dn);
870 talloc_free(tmp_ctx);
871 return ISC_R_SUCCESS;
873 talloc_free(dn);
876 talloc_free(tmp_ctx);
877 return ISC_R_NOTFOUND;
882 return the DN for a name. The record does not need to exist, but the
883 zone must exist
885 static isc_result_t b9_find_name_dn(struct dlz_bind9_data *state, const char *name,
886 TALLOC_CTX *mem_ctx, struct ldb_dn **dn)
888 const char *p;
890 /* work through the name piece by piece, until we find a zone */
891 for (p=name; p; ) {
892 isc_result_t result;
893 result = b9_find_zone_dn(state, p, mem_ctx, dn);
894 if (result == ISC_R_SUCCESS) {
895 const char *casefold;
897 /* we found a zone, now extend the DN to get
898 * the full DN
900 bool ret;
901 if (p == name) {
902 ret = ldb_dn_add_child_fmt(*dn, "DC=@");
903 if (ret == false) {
904 talloc_free(*dn);
905 return ISC_R_NOMEMORY;
907 } else {
908 struct ldb_val name_val
909 = data_blob_const(name,
910 (int)(p-name)-1);
912 if (!ldb_dn_add_child_val(*dn,
913 "DC",
914 name_val)) {
915 talloc_free(*dn);
916 return ISC_R_NOMEMORY;
921 * Check if this is a plausibly valid DN early
922 * (time spent here will be saved during the
923 * search due to an internal cache)
925 casefold = ldb_dn_get_casefold(*dn);
927 if (casefold == NULL) {
928 return ISC_R_NOTFOUND;
931 return ISC_R_SUCCESS;
933 p = strchr(p, '.');
934 if (p == NULL) {
935 break;
937 p++;
939 return ISC_R_NOTFOUND;
944 see if we handle a given zone
946 _PUBLIC_ isc_result_t dlz_findzonedb(void *dbdata, const char *name,
947 dns_clientinfomethods_t *methods,
948 dns_clientinfo_t *clientinfo)
950 struct timeval start = timeval_current();
951 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
952 isc_result_t result = ISC_R_SUCCESS;
954 result = b9_find_zone_dn(state, name, NULL, NULL);
955 DNS_COMMON_LOG_OPERATION(
956 isc_result_str(result),
957 &start,
958 NULL,
959 name,
960 NULL);
961 return result;
966 lookup one record
968 static isc_result_t dlz_lookup_types(struct dlz_bind9_data *state,
969 const char *zone, const char *name,
970 dns_sdlzlookup_t *lookup,
971 const char **types)
973 TALLOC_CTX *tmp_ctx = talloc_new(state);
974 struct ldb_dn *dn;
975 WERROR werr = WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
976 struct dnsp_DnssrvRpcRecord *records = NULL;
977 uint16_t num_records = 0, i;
978 struct ldb_val zone_name_val
979 = data_blob_string_const(zone);
980 struct ldb_val name_val
981 = data_blob_string_const(name);
983 for (i=0; zone_prefixes[i]; i++) {
984 int ret;
985 const char *casefold;
986 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
987 if (dn == NULL) {
988 talloc_free(tmp_ctx);
989 return ISC_R_NOMEMORY;
993 * This dance ensures that it is not possible to put
994 * (eg) an extra DC=x, into the DNS name being
995 * queried
998 if (!ldb_dn_add_child_fmt(dn,
999 "DC=X,DC=X,%s",
1000 zone_prefixes[i])) {
1001 talloc_free(tmp_ctx);
1002 return ISC_R_NOMEMORY;
1005 ret = ldb_dn_set_component(dn,
1007 "DC",
1008 zone_name_val);
1009 if (ret != LDB_SUCCESS) {
1010 talloc_free(tmp_ctx);
1011 return ISC_R_NOMEMORY;
1014 ret = ldb_dn_set_component(dn,
1016 "DC",
1017 name_val);
1018 if (ret != LDB_SUCCESS) {
1019 talloc_free(tmp_ctx);
1020 return ISC_R_NOMEMORY;
1024 * Check if this is a plausibly valid DN early
1025 * (time spent here will be saved during the
1026 * search due to an internal cache)
1028 casefold = ldb_dn_get_casefold(dn);
1030 if (casefold == NULL) {
1031 talloc_free(tmp_ctx);
1032 return ISC_R_NOTFOUND;
1035 werr = dns_common_wildcard_lookup(state->samdb, tmp_ctx, dn,
1036 &records, &num_records);
1037 if (W_ERROR_IS_OK(werr)) {
1038 break;
1041 if (!W_ERROR_IS_OK(werr)) {
1042 talloc_free(tmp_ctx);
1043 return ISC_R_NOTFOUND;
1046 for (i=0; i < num_records; i++) {
1047 isc_result_t result;
1049 result = b9_putrr(state, lookup, &records[i], types);
1050 if (result != ISC_R_SUCCESS) {
1051 talloc_free(tmp_ctx);
1052 return result;
1056 talloc_free(tmp_ctx);
1057 return ISC_R_SUCCESS;
1061 lookup one record
1063 _PUBLIC_ isc_result_t dlz_lookup(const char *zone, const char *name,
1064 void *dbdata, dns_sdlzlookup_t *lookup,
1065 dns_clientinfomethods_t *methods,
1066 dns_clientinfo_t *clientinfo)
1068 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1069 isc_result_t result = ISC_R_SUCCESS;
1070 struct timeval start = timeval_current();
1072 result = dlz_lookup_types(state, zone, name, lookup, NULL);
1073 DNS_COMMON_LOG_OPERATION(
1074 isc_result_str(result),
1075 &start,
1076 zone,
1077 name,
1078 NULL);
1080 return result;
1085 see if a zone transfer is allowed
1087 _PUBLIC_ isc_result_t dlz_allowzonexfr(void *dbdata, const char *name, const char *client)
1089 struct dlz_bind9_data *state = talloc_get_type(
1090 dbdata, struct dlz_bind9_data);
1091 isc_result_t ret;
1092 const char **authorized_clients, **denied_clients;
1093 const char *cname="";
1095 /* check that the zone is known */
1096 ret = b9_find_zone_dn(state, name, NULL, NULL);
1097 if (ret != ISC_R_SUCCESS) {
1098 return ret;
1101 /* default is to deny all transfers */
1103 authorized_clients = lpcfg_dns_zone_transfer_clients_allow(state->lp);
1104 denied_clients = lpcfg_dns_zone_transfer_clients_deny(state->lp);
1106 /* The logic of allow_access() when both allow and deny lists are given
1107 * does not match our expectation here: it would allow clients thar are
1108 * neither allowed nor denied.
1109 * Here, we want to deny clients by default.
1110 * Using the allow_access() function is still useful as it takes care of
1111 * parsing IP adresses and subnets in a consistent way with other options
1112 * from smb.conf.
1114 * We will then check the deny list first, then the allow list, so that
1115 * we accept only clients that are explicitely allowed AND not explicitely
1116 * denied.
1118 if ((authorized_clients == NULL) && (denied_clients == NULL)) {
1119 /* No "allow" or "deny" lists given. Deny by default. */
1120 return ISC_R_NOPERM;
1123 if (denied_clients != NULL) {
1124 bool ok = allow_access(denied_clients, NULL, cname, client);
1125 if (!ok) {
1126 /* client on deny list. Deny. */
1127 return ISC_R_NOPERM;
1131 if (authorized_clients != NULL) {
1132 bool ok = allow_access(NULL, authorized_clients, cname, client);
1133 if (ok) {
1135 * client is not on deny list and is on allow list.
1136 * This is the only place we should return "allow".
1138 return ISC_R_SUCCESS;
1141 /* We shouldn't get here, but deny by default. */
1142 return ISC_R_NOPERM;
1146 perform a zone transfer
1148 _PUBLIC_ isc_result_t dlz_allnodes(const char *zone, void *dbdata,
1149 dns_sdlzallnodes_t *allnodes)
1151 struct timeval start = timeval_current();
1152 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1153 const char *attrs[] = { "dnsRecord", NULL };
1154 int ret = LDB_ERR_NO_SUCH_OBJECT;
1155 size_t i, j;
1156 struct ldb_dn *dn = NULL;
1157 struct ldb_result *res;
1158 TALLOC_CTX *tmp_ctx = talloc_new(state);
1159 struct ldb_val zone_name_val = data_blob_string_const(zone);
1160 isc_result_t result = ISC_R_SUCCESS;
1162 for (i=0; zone_prefixes[i]; i++) {
1163 const char *casefold;
1165 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
1166 if (dn == NULL) {
1167 talloc_free(tmp_ctx);
1168 result = ISC_R_NOMEMORY;
1169 goto exit;
1173 * This dance ensures that it is not possible to put
1174 * (eg) an extra DC=x, into the DNS name being
1175 * queried
1178 if (!ldb_dn_add_child_fmt(dn,
1179 "DC=X,%s",
1180 zone_prefixes[i])) {
1181 talloc_free(tmp_ctx);
1182 result = ISC_R_NOMEMORY;
1183 goto exit;
1186 ret = ldb_dn_set_component(dn,
1188 "DC",
1189 zone_name_val);
1190 if (ret != LDB_SUCCESS) {
1191 talloc_free(tmp_ctx);
1192 result = ISC_R_NOMEMORY;
1193 goto exit;
1197 * Check if this is a plausibly valid DN early
1198 * (time spent here will be saved during the
1199 * search due to an internal cache)
1201 casefold = ldb_dn_get_casefold(dn);
1203 if (casefold == NULL) {
1204 result = ISC_R_NOTFOUND;
1205 goto exit;
1208 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
1209 attrs, "objectClass=dnsNode");
1210 if (ret == LDB_SUCCESS) {
1211 break;
1214 if (ret != LDB_SUCCESS || dn == NULL) {
1215 talloc_free(tmp_ctx);
1216 result = ISC_R_NOTFOUND;
1217 goto exit;
1220 for (i=0; i<res->count; i++) {
1221 struct ldb_message_element *el;
1222 TALLOC_CTX *el_ctx = talloc_new(tmp_ctx);
1223 const char *rdn, *name;
1224 const struct ldb_val *v;
1225 WERROR werr;
1226 struct dnsp_DnssrvRpcRecord *recs = NULL;
1227 uint16_t num_recs = 0;
1229 el = ldb_msg_find_element(res->msgs[i], "dnsRecord");
1230 if (el == NULL || el->num_values == 0) {
1231 state->log(ISC_LOG_INFO, "failed to find dnsRecord for %s",
1232 ldb_dn_get_linearized(dn));
1233 talloc_free(el_ctx);
1234 continue;
1237 v = ldb_dn_get_rdn_val(res->msgs[i]->dn);
1238 if (v == NULL) {
1239 state->log(ISC_LOG_INFO, "failed to find RDN for %s",
1240 ldb_dn_get_linearized(dn));
1241 talloc_free(el_ctx);
1242 continue;
1245 rdn = talloc_strndup(el_ctx, (char *)v->data, v->length);
1246 if (rdn == NULL) {
1247 talloc_free(tmp_ctx);
1248 result = ISC_R_NOMEMORY;
1249 goto exit;
1252 if (strcmp(rdn, "@") == 0) {
1253 name = zone;
1254 } else {
1255 name = talloc_asprintf(el_ctx, "%s.%s", rdn, zone);
1257 name = b9_format_fqdn(el_ctx, name);
1258 if (name == NULL) {
1259 talloc_free(tmp_ctx);
1260 result = ISC_R_NOMEMORY;
1261 goto exit;
1264 werr = dns_common_extract(state->samdb, el, el_ctx, &recs, &num_recs);
1265 if (!W_ERROR_IS_OK(werr)) {
1266 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s, %s",
1267 ldb_dn_get_linearized(dn), win_errstr(werr));
1268 talloc_free(el_ctx);
1269 continue;
1272 for (j=0; j < num_recs; j++) {
1273 isc_result_t rc;
1275 rc = b9_putnamedrr(state, allnodes, name, &recs[j]);
1276 if (rc != ISC_R_SUCCESS) {
1277 continue;
1281 talloc_free(el_ctx);
1284 talloc_free(tmp_ctx);
1285 exit:
1286 DNS_COMMON_LOG_OPERATION(
1287 isc_result_str(result),
1288 &start,
1289 zone,
1290 NULL,
1291 NULL);
1292 return result;
1297 start a transaction
1299 _PUBLIC_ isc_result_t dlz_newversion(const char *zone, void *dbdata, void **versionp)
1301 struct timeval start = timeval_current();
1302 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1303 isc_result_t result = ISC_R_SUCCESS;
1305 state->log(ISC_LOG_INFO, "samba_dlz: starting transaction on zone %s", zone);
1307 if (state->transaction_token != NULL) {
1308 state->log(ISC_LOG_INFO, "samba_dlz: transaction already started for zone %s", zone);
1309 result = ISC_R_FAILURE;
1310 goto exit;
1313 state->transaction_token = talloc_zero(state, int);
1314 if (state->transaction_token == NULL) {
1315 result = ISC_R_NOMEMORY;
1316 goto exit;
1319 if (ldb_transaction_start(state->samdb) != LDB_SUCCESS) {
1320 state->log(ISC_LOG_INFO, "samba_dlz: failed to start a transaction for zone %s", zone);
1321 talloc_free(state->transaction_token);
1322 state->transaction_token = NULL;
1323 result = ISC_R_FAILURE;
1324 goto exit;
1327 *versionp = (void *)state->transaction_token;
1328 exit:
1329 DNS_COMMON_LOG_OPERATION(
1330 isc_result_str(result),
1331 &start,
1332 zone,
1333 NULL,
1334 NULL);
1335 return result;
1339 end a transaction
1341 _PUBLIC_ void dlz_closeversion(const char *zone, isc_boolean_t commit,
1342 void *dbdata, void **versionp)
1344 struct timeval start = timeval_current();
1345 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1346 const char *data = NULL;
1348 data = commit ? "commit" : "cancel";
1350 if (state->transaction_token != (int *)*versionp) {
1351 state->log(ISC_LOG_INFO, "samba_dlz: transaction not started for zone %s", zone);
1352 goto exit;
1355 if (commit) {
1356 if (ldb_transaction_commit(state->samdb) != LDB_SUCCESS) {
1357 state->log(ISC_LOG_INFO, "samba_dlz: failed to commit a transaction for zone %s", zone);
1358 goto exit;
1360 state->log(ISC_LOG_INFO, "samba_dlz: committed transaction on zone %s", zone);
1361 } else {
1362 if (ldb_transaction_cancel(state->samdb) != LDB_SUCCESS) {
1363 state->log(ISC_LOG_INFO, "samba_dlz: failed to cancel a transaction for zone %s", zone);
1364 goto exit;
1366 state->log(ISC_LOG_INFO, "samba_dlz: cancelling transaction on zone %s", zone);
1369 talloc_free(state->transaction_token);
1370 state->transaction_token = NULL;
1371 *versionp = NULL;
1373 exit:
1374 DNS_COMMON_LOG_OPERATION(
1375 isc_result_str(ISC_R_SUCCESS),
1376 &start,
1377 zone,
1378 NULL,
1379 data);
1384 see if there is a SOA record for a zone
1386 static bool b9_has_soa(struct dlz_bind9_data *state, struct ldb_dn *dn, const char *zone)
1388 TALLOC_CTX *tmp_ctx = talloc_new(state);
1389 WERROR werr;
1390 struct dnsp_DnssrvRpcRecord *records = NULL;
1391 uint16_t num_records = 0, i;
1392 struct ldb_val zone_name_val
1393 = data_blob_string_const(zone);
1396 * This dance ensures that it is not possible to put
1397 * (eg) an extra DC=x, into the DNS name being
1398 * queried
1401 if (!ldb_dn_add_child_val(dn,
1402 "DC",
1403 zone_name_val)) {
1404 talloc_free(tmp_ctx);
1405 return false;
1409 * The SOA record is alwas stored under DC=@,DC=zonename
1410 * This can probably be removed when dns_common_lookup makes a fallback
1411 * lookup on @ pseudo record
1414 if (!ldb_dn_add_child_fmt(dn,"DC=@")) {
1415 talloc_free(tmp_ctx);
1416 return false;
1419 werr = dns_common_lookup(state->samdb, tmp_ctx, dn,
1420 &records, &num_records, NULL);
1421 if (!W_ERROR_IS_OK(werr)) {
1422 talloc_free(tmp_ctx);
1423 return false;
1426 for (i=0; i < num_records; i++) {
1427 if (records[i].wType == DNS_TYPE_SOA) {
1428 talloc_free(tmp_ctx);
1429 return true;
1433 talloc_free(tmp_ctx);
1434 return false;
1437 static bool b9_zone_add(struct dlz_bind9_data *state, const char *name)
1439 struct b9_zone *zone;
1441 zone = talloc_zero(state, struct b9_zone);
1442 if (zone == NULL) {
1443 return false;
1446 zone->name = talloc_strdup(zone, name);
1447 if (zone->name == NULL) {
1448 talloc_free(zone);
1449 return false;
1452 DLIST_ADD(state->zonelist, zone);
1453 return true;
1456 static bool b9_zone_exists(struct dlz_bind9_data *state, const char *name)
1458 struct b9_zone *zone = state->zonelist;
1459 bool found = false;
1461 while (zone != NULL) {
1462 if (strcasecmp(name, zone->name) == 0) {
1463 found = true;
1464 break;
1466 zone = zone->next;
1469 return found;
1474 configure a writeable zone
1476 _PUBLIC_ isc_result_t dlz_configure(dns_view_t *view, dns_dlzdb_t *dlzdb,
1477 void *dbdata)
1479 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1480 TALLOC_CTX *tmp_ctx;
1481 struct ldb_dn *dn;
1482 int i;
1484 state->log(ISC_LOG_INFO, "samba_dlz: starting configure");
1485 if (state->writeable_zone == NULL) {
1486 state->log(ISC_LOG_INFO, "samba_dlz: no writeable_zone method available");
1487 return ISC_R_FAILURE;
1490 tmp_ctx = talloc_new(state);
1492 for (i=0; zone_prefixes[i]; i++) {
1493 const char *attrs[] = { "name", NULL };
1494 int j, ret;
1495 struct ldb_result *res;
1497 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
1498 if (dn == NULL) {
1499 talloc_free(tmp_ctx);
1500 return ISC_R_NOMEMORY;
1503 if (!ldb_dn_add_child_fmt(dn, "%s", zone_prefixes[i])) {
1504 talloc_free(tmp_ctx);
1505 return ISC_R_NOMEMORY;
1508 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
1509 attrs, "objectClass=dnsZone");
1510 if (ret != LDB_SUCCESS) {
1511 continue;
1514 for (j=0; j<res->count; j++) {
1515 isc_result_t result;
1516 const char *zone = ldb_msg_find_attr_as_string(res->msgs[j], "name", NULL);
1517 struct ldb_dn *zone_dn;
1519 if (zone == NULL) {
1520 continue;
1522 /* Ignore zones that are not handled in BIND */
1523 if ((strcmp(zone, "RootDNSServers") == 0) ||
1524 (strcmp(zone, "..TrustAnchors") == 0)) {
1525 continue;
1527 zone_dn = ldb_dn_copy(tmp_ctx, dn);
1528 if (zone_dn == NULL) {
1529 talloc_free(tmp_ctx);
1530 return ISC_R_NOMEMORY;
1533 if (!b9_has_soa(state, zone_dn, zone)) {
1534 continue;
1537 if (b9_zone_exists(state, zone)) {
1538 state->log(ISC_LOG_WARNING, "samba_dlz: Ignoring duplicate zone '%s' from '%s'",
1539 zone, ldb_dn_get_linearized(zone_dn));
1540 continue;
1543 if (!b9_zone_add(state, zone)) {
1544 talloc_free(tmp_ctx);
1545 return ISC_R_NOMEMORY;
1548 result = state->writeable_zone(view, dlzdb, zone);
1549 if (result != ISC_R_SUCCESS) {
1550 state->log(ISC_LOG_ERROR, "samba_dlz: Failed to configure zone '%s'",
1551 zone);
1552 talloc_free(tmp_ctx);
1553 return result;
1555 state->log(ISC_LOG_INFO, "samba_dlz: configured writeable zone '%s'", zone);
1559 talloc_free(tmp_ctx);
1560 return ISC_R_SUCCESS;
1564 authorize a zone update
1566 _PUBLIC_ isc_boolean_t dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr,
1567 const char *type, const char *key, uint32_t keydatalen, uint8_t *keydata,
1568 void *dbdata)
1570 struct timeval start = timeval_current();
1571 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1572 TALLOC_CTX *tmp_ctx;
1573 DATA_BLOB ap_req;
1574 struct cli_credentials *server_credentials;
1575 char *keytab_name;
1576 char *keytab_file = NULL;
1577 int ret;
1578 int ldb_ret;
1579 NTSTATUS nt_status;
1580 struct gensec_security *gensec_ctx;
1581 struct auth_session_info *session_info;
1582 struct ldb_dn *dn;
1583 isc_result_t rc;
1584 struct ldb_result *res;
1585 const char * attrs[] = { NULL };
1586 uint32_t access_mask;
1587 struct gensec_settings *settings = NULL;
1588 const struct gensec_security_ops **backends = NULL;
1589 size_t idx = 0;
1590 isc_boolean_t result = ISC_FALSE;
1591 NTSTATUS status;
1592 bool ok;
1594 /* Remove cached credentials, if any */
1595 if (state->session_info) {
1596 talloc_free(state->session_info);
1597 state->session_info = NULL;
1599 if (state->update_name) {
1600 talloc_free(state->update_name);
1601 state->update_name = NULL;
1604 tmp_ctx = talloc_new(state);
1605 if (tmp_ctx == NULL) {
1606 state->log(ISC_LOG_ERROR, "samba_dlz: no memory");
1607 result = ISC_FALSE;
1608 goto exit;
1611 ap_req = data_blob_const(keydata, keydatalen);
1612 server_credentials = cli_credentials_init(tmp_ctx);
1613 if (!server_credentials) {
1614 state->log(ISC_LOG_ERROR, "samba_dlz: failed to init server credentials");
1615 talloc_free(tmp_ctx);
1616 result = ISC_FALSE;
1617 goto exit;
1620 status = cli_credentials_set_krb5_context(server_credentials,
1621 state->smb_krb5_ctx);
1622 if (!NT_STATUS_IS_OK(status)) {
1623 state->log(ISC_LOG_ERROR,
1624 "samba_dlz: failed to set krb5 context");
1625 talloc_free(tmp_ctx);
1626 result = ISC_FALSE;
1627 goto exit;
1630 ok = cli_credentials_set_conf(server_credentials, state->lp);
1631 if (!ok) {
1632 state->log(ISC_LOG_ERROR,
1633 "samba_dlz: failed to load smb.conf");
1634 talloc_free(tmp_ctx);
1635 result = ISC_FALSE;
1636 goto exit;
1639 keytab_file = talloc_asprintf(tmp_ctx,
1640 "%s/dns.keytab",
1641 lpcfg_binddns_dir(state->lp));
1642 if (keytab_file == NULL) {
1643 state->log(ISC_LOG_ERROR, "samba_dlz: Out of memory!");
1644 talloc_free(tmp_ctx);
1645 result = ISC_FALSE;
1646 goto exit;
1649 if (!file_exist(keytab_file)) {
1650 keytab_file = talloc_asprintf(tmp_ctx,
1651 "%s/dns.keytab",
1652 lpcfg_private_dir(state->lp));
1653 if (keytab_file == NULL) {
1654 state->log(ISC_LOG_ERROR, "samba_dlz: Out of memory!");
1655 talloc_free(tmp_ctx);
1656 result = ISC_FALSE;
1657 goto exit;
1661 keytab_name = talloc_asprintf(tmp_ctx, "FILE:%s", keytab_file);
1662 if (keytab_name == NULL) {
1663 state->log(ISC_LOG_ERROR, "samba_dlz: Out of memory!");
1664 talloc_free(tmp_ctx);
1665 result = ISC_FALSE;
1666 goto exit;
1669 ret = cli_credentials_set_keytab_name(server_credentials, state->lp, keytab_name,
1670 CRED_SPECIFIED);
1671 if (ret != 0) {
1672 state->log(ISC_LOG_ERROR, "samba_dlz: failed to obtain server credentials from %s",
1673 keytab_name);
1674 talloc_free(tmp_ctx);
1675 result = ISC_FALSE;
1676 goto exit;
1678 talloc_free(keytab_name);
1680 settings = lpcfg_gensec_settings(tmp_ctx, state->lp);
1681 if (settings == NULL) {
1682 state->log(ISC_LOG_ERROR, "samba_dlz: lpcfg_gensec_settings failed");
1683 talloc_free(tmp_ctx);
1684 result = ISC_FALSE;
1685 goto exit;
1687 backends = talloc_zero_array(settings,
1688 const struct gensec_security_ops *, 3);
1689 if (backends == NULL) {
1690 state->log(ISC_LOG_ERROR, "samba_dlz: talloc_zero_array gensec_security_ops failed");
1691 talloc_free(tmp_ctx);
1692 result = ISC_FALSE;
1693 goto exit;
1695 settings->backends = backends;
1697 gensec_init();
1699 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_KERBEROS5);
1700 backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_SPNEGO);
1702 nt_status = gensec_server_start(tmp_ctx, settings,
1703 state->auth_context, &gensec_ctx);
1704 if (!NT_STATUS_IS_OK(nt_status)) {
1705 state->log(ISC_LOG_ERROR, "samba_dlz: failed to start gensec server");
1706 talloc_free(tmp_ctx);
1707 result = ISC_FALSE;
1708 goto exit;
1711 gensec_set_credentials(gensec_ctx, server_credentials);
1713 nt_status = gensec_start_mech_by_oid(gensec_ctx, GENSEC_OID_SPNEGO);
1714 if (!NT_STATUS_IS_OK(nt_status)) {
1715 state->log(ISC_LOG_ERROR, "samba_dlz: failed to start spnego");
1716 talloc_free(tmp_ctx);
1717 result = ISC_FALSE;
1718 goto exit;
1722 * We only allow SPNEGO/KRB5 and make sure the backend
1723 * to is RPC/IPC free.
1725 * See gensec_gssapi_update_internal() as
1726 * GENSEC_SERVER.
1728 * It allows gensec_update() not to block.
1730 * If that changes in future we need to use
1731 * gensec_update_send/recv here!
1733 nt_status = gensec_update(gensec_ctx, tmp_ctx, ap_req, &ap_req);
1734 if (!NT_STATUS_IS_OK(nt_status)) {
1735 state->log(ISC_LOG_ERROR, "samba_dlz: spnego update failed");
1736 talloc_free(tmp_ctx);
1737 result = ISC_FALSE;
1738 goto exit;
1741 nt_status = gensec_session_info(gensec_ctx, tmp_ctx, &session_info);
1742 if (!NT_STATUS_IS_OK(nt_status)) {
1743 state->log(ISC_LOG_ERROR, "samba_dlz: failed to create session info");
1744 talloc_free(tmp_ctx);
1745 result = ISC_FALSE;
1746 goto exit;
1749 /* Get the DN from name */
1750 rc = b9_find_name_dn(state, name, tmp_ctx, &dn);
1751 if (rc != ISC_R_SUCCESS) {
1752 state->log(ISC_LOG_ERROR, "samba_dlz: failed to find name %s", name);
1753 talloc_free(tmp_ctx);
1754 result = ISC_FALSE;
1755 goto exit;
1758 /* make sure the dn exists, or find parent dn in case new object is being added */
1759 ldb_ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
1760 attrs, "objectClass=dnsNode");
1761 if (ldb_ret == LDB_ERR_NO_SUCH_OBJECT) {
1762 ldb_dn_remove_child_components(dn, 1);
1763 access_mask = SEC_ADS_CREATE_CHILD;
1764 talloc_free(res);
1765 } else if (ldb_ret == LDB_SUCCESS) {
1766 access_mask = SEC_STD_REQUIRED | SEC_ADS_SELF_WRITE;
1767 talloc_free(res);
1768 } else {
1769 talloc_free(tmp_ctx);
1770 result = ISC_FALSE;
1771 goto exit;
1774 /* Do ACL check */
1775 ldb_ret = dsdb_check_access_on_dn(state->samdb, tmp_ctx, dn,
1776 session_info->security_token,
1777 access_mask, NULL);
1778 if (ldb_ret != LDB_SUCCESS) {
1779 state->log(ISC_LOG_INFO,
1780 "samba_dlz: disallowing update of signer=%s name=%s type=%s error=%s",
1781 signer, name, type, ldb_strerror(ldb_ret));
1782 talloc_free(tmp_ctx);
1783 result = ISC_FALSE;
1784 goto exit;
1787 /* Cache session_info, so it can be used in the actual add/delete operation */
1788 state->update_name = talloc_strdup(state, name);
1789 if (state->update_name == NULL) {
1790 state->log(ISC_LOG_ERROR, "samba_dlz: memory allocation error");
1791 talloc_free(tmp_ctx);
1792 result = ISC_FALSE;
1793 goto exit;
1795 state->session_info = talloc_steal(state, session_info);
1797 state->log(ISC_LOG_INFO, "samba_dlz: allowing update of signer=%s name=%s tcpaddr=%s type=%s key=%s",
1798 signer, name, tcpaddr, type, key);
1800 talloc_free(tmp_ctx);
1801 result = ISC_TRUE;
1802 exit:
1803 DNS_COMMON_LOG_OPERATION(
1804 isc_result_str(result),
1805 &start,
1806 NULL,
1807 name,
1808 NULL);
1809 return result;
1814 see if two dns records match
1816 static bool b9_record_match(struct dnsp_DnssrvRpcRecord *rec1,
1817 struct dnsp_DnssrvRpcRecord *rec2)
1819 if (rec1->wType != rec2->wType) {
1820 return false;
1822 /* see if this type is single valued */
1823 if (b9_single_valued(rec1->wType)) {
1824 return true;
1827 return dns_record_match(rec1, rec2);
1831 * Update session_info on samdb using the cached credentials
1833 static bool b9_set_session_info(struct dlz_bind9_data *state, const char *name)
1835 int ret;
1837 if (state->update_name == NULL || state->session_info == NULL) {
1838 state->log(ISC_LOG_ERROR, "samba_dlz: invalid credentials");
1839 return false;
1842 /* Do not use client credentials, if we're not updating the client specified name */
1843 if (strcmp(state->update_name, name) != 0) {
1844 return true;
1847 ret = ldb_set_opaque(
1848 state->samdb,
1849 DSDB_SESSION_INFO,
1850 state->session_info);
1851 if (ret != LDB_SUCCESS) {
1852 state->log(ISC_LOG_ERROR, "samba_dlz: unable to set session info");
1853 return false;
1856 return true;
1860 * Reset session_info on samdb as system session
1862 static void b9_reset_session_info(struct dlz_bind9_data *state)
1864 ldb_set_opaque(
1865 state->samdb,
1866 DSDB_SESSION_INFO,
1867 system_session(state->lp));
1871 add or modify a rdataset
1873 _PUBLIC_ isc_result_t dlz_addrdataset(const char *name, const char *rdatastr, void *dbdata, void *version)
1875 struct timeval start = timeval_current();
1876 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1877 struct dnsp_DnssrvRpcRecord *rec;
1878 struct ldb_dn *dn;
1879 isc_result_t result = ISC_R_SUCCESS;
1880 bool tombstoned = false;
1881 bool needs_add = false;
1882 struct dnsp_DnssrvRpcRecord *recs = NULL;
1883 uint16_t num_recs = 0;
1884 uint16_t first = 0;
1885 uint16_t i;
1886 WERROR werr;
1888 if (state->transaction_token != (void*)version) {
1889 state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version");
1890 result = ISC_R_FAILURE;
1891 goto exit;
1894 rec = talloc_zero(state, struct dnsp_DnssrvRpcRecord);
1895 if (rec == NULL) {
1896 result = ISC_R_NOMEMORY;
1897 goto exit;
1900 rec->rank = DNS_RANK_ZONE;
1902 if (!b9_parse(state, rdatastr, rec)) {
1903 state->log(ISC_LOG_INFO, "samba_dlz: failed to parse rdataset '%s'", rdatastr);
1904 talloc_free(rec);
1905 result = ISC_R_FAILURE;
1906 goto exit;
1909 /* find the DN of the record */
1910 result = b9_find_name_dn(state, name, rec, &dn);
1911 if (result != ISC_R_SUCCESS) {
1912 talloc_free(rec);
1913 goto exit;
1916 /* get any existing records */
1917 werr = dns_common_lookup(state->samdb, rec, dn,
1918 &recs, &num_recs, &tombstoned);
1919 if (W_ERROR_EQUAL(werr, WERR_DNS_ERROR_NAME_DOES_NOT_EXIST)) {
1920 needs_add = true;
1921 werr = WERR_OK;
1923 if (!W_ERROR_IS_OK(werr)) {
1924 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s, %s",
1925 ldb_dn_get_linearized(dn), win_errstr(werr));
1926 talloc_free(rec);
1927 result = ISC_R_FAILURE;
1928 goto exit;
1931 if (tombstoned) {
1933 * we need to keep the existing tombstone record
1934 * and ignore it
1936 first = num_recs;
1939 /* there may be existing records. We need to see if this will
1940 * replace a record or add to it
1942 for (i=first; i < num_recs; i++) {
1943 if (b9_record_match(rec, &recs[i])) {
1944 break;
1947 if (i == UINT16_MAX) {
1948 state->log(ISC_LOG_ERROR,
1949 "samba_dlz: failed to find record to modify, and "
1950 "there are already %u dnsRecord values for %s",
1951 i, ldb_dn_get_linearized(dn));
1952 talloc_free(rec);
1953 result = ISC_R_FAILURE;
1954 goto exit;
1957 if (i == num_recs) {
1958 /* adding a new value */
1959 recs = talloc_realloc(rec, recs,
1960 struct dnsp_DnssrvRpcRecord,
1961 num_recs + 1);
1962 if (recs == NULL) {
1963 talloc_free(rec);
1964 result = ISC_R_NOMEMORY;
1965 goto exit;
1967 num_recs++;
1969 if (dns_name_is_static(recs, num_recs)) {
1970 rec->dwTimeStamp = 0;
1971 } else {
1972 rec->dwTimeStamp = unix_to_dns_timestamp(time(NULL));
1976 recs[i] = *rec;
1978 if (!b9_set_session_info(state, name)) {
1979 talloc_free(rec);
1980 result = ISC_R_FAILURE;
1981 goto exit;
1984 /* modify the record */
1985 werr = dns_common_replace(state->samdb, rec, dn,
1986 needs_add,
1987 state->soa_serial,
1988 recs, num_recs);
1989 b9_reset_session_info(state);
1990 if (!W_ERROR_IS_OK(werr)) {
1991 state->log(ISC_LOG_ERROR, "samba_dlz: failed to %s %s - %s",
1992 needs_add ? "add" : "modify",
1993 ldb_dn_get_linearized(dn), win_errstr(werr));
1994 talloc_free(rec);
1995 result = ISC_R_FAILURE;
1996 goto exit;
1999 state->log(ISC_LOG_INFO, "samba_dlz: added rdataset %s '%s'", name, rdatastr);
2001 talloc_free(rec);
2002 exit:
2003 DNS_COMMON_LOG_OPERATION(
2004 isc_result_str(result),
2005 &start,
2006 NULL,
2007 name,
2008 rdatastr);
2009 return result;
2013 remove a rdataset
2015 _PUBLIC_ isc_result_t dlz_subrdataset(const char *name, const char *rdatastr, void *dbdata, void *version)
2017 struct timeval start = timeval_current();
2018 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
2019 struct dnsp_DnssrvRpcRecord *rec;
2020 struct ldb_dn *dn;
2021 isc_result_t result = ISC_R_SUCCESS;
2022 struct dnsp_DnssrvRpcRecord *recs = NULL;
2023 uint16_t num_recs = 0;
2024 uint16_t i;
2025 WERROR werr;
2027 if (state->transaction_token != (void*)version) {
2028 state->log(ISC_LOG_ERROR, "samba_dlz: bad transaction version");
2029 result = ISC_R_FAILURE;
2030 goto exit;
2033 rec = talloc_zero(state, struct dnsp_DnssrvRpcRecord);
2034 if (rec == NULL) {
2035 result = ISC_R_NOMEMORY;
2036 goto exit;
2039 if (!b9_parse(state, rdatastr, rec)) {
2040 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse rdataset '%s'", rdatastr);
2041 talloc_free(rec);
2042 result = ISC_R_FAILURE;
2043 goto exit;
2046 /* find the DN of the record */
2047 result = b9_find_name_dn(state, name, rec, &dn);
2048 if (result != ISC_R_SUCCESS) {
2049 talloc_free(rec);
2050 goto exit;
2053 /* get the existing records */
2054 werr = dns_common_lookup(state->samdb, rec, dn,
2055 &recs, &num_recs, NULL);
2056 if (!W_ERROR_IS_OK(werr)) {
2057 talloc_free(rec);
2058 result = ISC_R_NOTFOUND;
2059 goto exit;
2062 for (i=0; i < num_recs; i++) {
2063 if (b9_record_match(rec, &recs[i])) {
2064 recs[i] = (struct dnsp_DnssrvRpcRecord) {
2065 .wType = DNS_TYPE_TOMBSTONE,
2067 break;
2070 if (i == num_recs) {
2071 talloc_free(rec);
2072 result = ISC_R_NOTFOUND;
2073 goto exit;
2076 if (!b9_set_session_info(state, name)) {
2077 talloc_free(rec);
2078 result = ISC_R_FAILURE;
2079 goto exit;
2082 /* modify the record */
2083 werr = dns_common_replace(state->samdb, rec, dn,
2084 false,/* needs_add */
2085 state->soa_serial,
2086 recs, num_recs);
2087 b9_reset_session_info(state);
2088 if (!W_ERROR_IS_OK(werr)) {
2089 state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s",
2090 ldb_dn_get_linearized(dn), win_errstr(werr));
2091 talloc_free(rec);
2092 result = ISC_R_FAILURE;
2093 goto exit;
2096 state->log(ISC_LOG_INFO, "samba_dlz: subtracted rdataset %s '%s'", name, rdatastr);
2098 talloc_free(rec);
2099 exit:
2100 DNS_COMMON_LOG_OPERATION(
2101 isc_result_str(result),
2102 &start,
2103 NULL,
2104 name,
2105 rdatastr);
2106 return result;
2111 delete all records of the given type
2113 _PUBLIC_ isc_result_t dlz_delrdataset(const char *name, const char *type, void *dbdata, void *version)
2115 struct timeval start = timeval_current();
2116 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
2117 TALLOC_CTX *tmp_ctx;
2118 struct ldb_dn *dn;
2119 isc_result_t result = ISC_R_SUCCESS;
2120 enum dns_record_type dns_type;
2121 bool found = false;
2122 struct dnsp_DnssrvRpcRecord *recs = NULL;
2123 uint16_t num_recs = 0;
2124 uint16_t ri = 0;
2125 WERROR werr;
2127 if (state->transaction_token != (void*)version) {
2128 state->log(ISC_LOG_ERROR, "samba_dlz: bad transaction version");
2129 result = ISC_R_FAILURE;
2130 goto exit;
2133 if (!b9_dns_type(type, &dns_type)) {
2134 state->log(ISC_LOG_ERROR, "samba_dlz: bad dns type %s in delete", type);
2135 result = ISC_R_FAILURE;
2136 goto exit;
2139 tmp_ctx = talloc_new(state);
2141 /* find the DN of the record */
2142 result = b9_find_name_dn(state, name, tmp_ctx, &dn);
2143 if (result != ISC_R_SUCCESS) {
2144 talloc_free(tmp_ctx);
2145 goto exit;
2148 /* get the existing records */
2149 werr = dns_common_lookup(state->samdb, tmp_ctx, dn,
2150 &recs, &num_recs, NULL);
2151 if (!W_ERROR_IS_OK(werr)) {
2152 talloc_free(tmp_ctx);
2153 result = ISC_R_NOTFOUND;
2154 goto exit;
2157 for (ri=0; ri < num_recs; ri++) {
2158 if (dns_type != recs[ri].wType) {
2159 continue;
2162 found = true;
2163 recs[ri] = (struct dnsp_DnssrvRpcRecord) {
2164 .wType = DNS_TYPE_TOMBSTONE,
2168 if (!found) {
2169 talloc_free(tmp_ctx);
2170 result = ISC_R_FAILURE;
2171 goto exit;
2174 if (!b9_set_session_info(state, name)) {
2175 talloc_free(tmp_ctx);
2176 result = ISC_R_FAILURE;
2177 goto exit;
2180 /* modify the record */
2181 werr = dns_common_replace(state->samdb, tmp_ctx, dn,
2182 false,/* needs_add */
2183 state->soa_serial,
2184 recs, num_recs);
2185 b9_reset_session_info(state);
2186 if (!W_ERROR_IS_OK(werr)) {
2187 state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s",
2188 ldb_dn_get_linearized(dn), win_errstr(werr));
2189 talloc_free(tmp_ctx);
2190 result = ISC_R_FAILURE;
2191 goto exit;
2194 state->log(ISC_LOG_INFO, "samba_dlz: deleted rdataset %s of type %s", name, type);
2196 talloc_free(tmp_ctx);
2197 exit:
2198 DNS_COMMON_LOG_OPERATION(
2199 isc_result_str(result),
2200 &start,
2201 NULL,
2202 name,
2203 type);
2204 return result;