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/>.
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/session.h"
29 #include "auth/gensec/gensec.h"
30 #include "gen_ndr/ndr_dnsp.h"
31 #include "lib/cmdline/popt_common.h"
32 #include "lib/cmdline/popt_credentials.h"
33 #include "ldb_module.h"
34 #include "dlz_minimal.h"
36 struct dlz_bind9_data {
37 struct ldb_context *samdb;
38 struct tevent_context *ev_ctx;
39 struct loadparm_context *lp;
40 int *transaction_token;
42 /* helper functions from the dlz_dlopen driver */
43 void (*log)(int level, const char *fmt, ...);
44 isc_result_t (*putrr)(dns_sdlzlookup_t *handle, const char *type,
45 dns_ttl_t ttl, const char *data);
46 isc_result_t (*putnamedrr)(dns_sdlzlookup_t *handle, const char *name,
47 const char *type, dns_ttl_t ttl, const char *data);
48 isc_result_t (*writeable_zone)(dns_view_t *view, const char *zone_name);
52 static const char *zone_prefixes[] = {
53 "CN=MicrosoftDNS,DC=DomainDnsZones",
54 "CN=MicrosoftDNS,DC=ForestDnsZones",
59 return the version of the API
61 _PUBLIC_ int dlz_version(unsigned int *flags)
63 return DLZ_DLOPEN_VERSION;
67 remember a helper function from the bind9 dlz_dlopen driver
69 static void b9_add_helper(struct dlz_bind9_data *state, const char *helper_name, void *ptr)
71 if (strcmp(helper_name, "log") == 0) {
74 if (strcmp(helper_name, "putrr") == 0) {
77 if (strcmp(helper_name, "putnamedrr") == 0) {
78 state->putnamedrr = ptr;
80 if (strcmp(helper_name, "writeable_zone") == 0) {
81 state->writeable_zone = ptr;
86 format a record for bind9
88 static bool b9_format(struct dlz_bind9_data *state,
90 struct dnsp_DnssrvRpcRecord *rec,
91 const char **type, const char **data)
96 *data = rec->data.ipv4;
101 *data = rec->data.ipv6;
106 *data = rec->data.cname;
111 *data = rec->data.txt;
116 *data = rec->data.ptr;
121 *data = talloc_asprintf(mem_ctx, "%u %u %u %s",
122 rec->data.srv.wPriority,
123 rec->data.srv.wWeight,
125 rec->data.srv.nameTarget);
130 *data = talloc_asprintf(mem_ctx, "%u %s",
131 rec->data.mx.wPriority,
132 rec->data.mx.nameTarget);
137 *data = talloc_asprintf(mem_ctx, "%s %s",
144 *data = rec->data.ns;
149 *data = talloc_asprintf(mem_ctx, "%s %s %u %u %u %u %u",
152 rec->data.soa.serial,
153 rec->data.soa.refresh,
155 rec->data.soa.expire,
156 rec->data.soa.minimum);
160 state->log(ISC_LOG_ERROR, "samba b9_putrr: unhandled record type %u",
168 static const struct {
169 enum dns_record_type dns_type;
173 { DNS_TYPE_A, "A" , false},
174 { DNS_TYPE_AAAA, "AAAA" , false},
175 { DNS_TYPE_CNAME, "CNAME" , true},
176 { DNS_TYPE_TXT, "TXT" , false},
177 { DNS_TYPE_PTR, "PTR" , false},
178 { DNS_TYPE_SRV, "SRV" , false},
179 { DNS_TYPE_MX, "MX" , false},
180 { DNS_TYPE_HINFO, "HINFO" , false},
181 { DNS_TYPE_NS, "NS" , false},
182 { DNS_TYPE_SOA, "SOA" , true},
187 see if a DNS type is single valued
189 static bool b9_single_valued(enum dns_record_type dns_type)
192 for (i=0; i<ARRAY_SIZE(dns_typemap); i++) {
193 if (dns_typemap[i].dns_type == dns_type) {
194 return dns_typemap[i].single_valued;
201 see if a DNS type is single valued
203 static enum dns_record_type b9_dns_type(const char *type)
206 for (i=0; i<ARRAY_SIZE(dns_typemap); i++) {
207 if (strcasecmp(dns_typemap[i].typestr, type) == 0) {
208 return dns_typemap[i].dns_type;
211 return DNS_TYPE_ZERO;
215 #define DNS_PARSE_STR(ret, str, sep, saveptr) do { \
216 (ret) = strtok_r(str, sep, &saveptr); \
217 if ((ret) == NULL) return false; \
220 #define DNS_PARSE_UINT(ret, str, sep, saveptr) do { \
221 char *istr = strtok_r(str, sep, &saveptr); \
222 if ((istr) == NULL) return false; \
223 (ret) = strtoul(istr, NULL, 10); \
227 parse a record from bind9
229 static bool b9_parse(struct dlz_bind9_data *state,
230 const char *rdatastr,
231 struct dnsp_DnssrvRpcRecord *rec)
233 char *full_name, *dclass, *type;
234 char *str, *saveptr=NULL;
237 str = talloc_strdup(rec, rdatastr);
242 /* parse the SDLZ string form */
243 DNS_PARSE_STR(full_name, str, "\t", saveptr);
244 DNS_PARSE_UINT(rec->dwTtlSeconds, NULL, "\t", saveptr);
245 DNS_PARSE_STR(dclass, NULL, "\t", saveptr);
246 DNS_PARSE_STR(type, NULL, "\t", saveptr);
248 /* construct the record */
249 for (i=0; i<ARRAY_SIZE(dns_typemap); i++) {
250 if (strcasecmp(type, dns_typemap[i].typestr) == 0) {
251 rec->wType = dns_typemap[i].dns_type;
255 if (i == ARRAY_SIZE(dns_typemap)) {
256 state->log(ISC_LOG_ERROR, "samba_dlz: unsupported record type '%s' for '%s'",
261 switch (rec->wType) {
263 DNS_PARSE_STR(rec->data.ipv4, NULL, " ", saveptr);
267 DNS_PARSE_STR(rec->data.ipv6, NULL, " ", saveptr);
271 DNS_PARSE_STR(rec->data.cname, NULL, " ", saveptr);
275 DNS_PARSE_STR(rec->data.txt, NULL, "\t", saveptr);
279 DNS_PARSE_STR(rec->data.ptr, NULL, " ", saveptr);
283 DNS_PARSE_UINT(rec->data.srv.wPriority, NULL, " ", saveptr);
284 DNS_PARSE_UINT(rec->data.srv.wWeight, NULL, " ", saveptr);
285 DNS_PARSE_UINT(rec->data.srv.wPort, NULL, " ", saveptr);
286 DNS_PARSE_STR(rec->data.srv.nameTarget, NULL, " ", saveptr);
290 DNS_PARSE_UINT(rec->data.mx.wPriority, NULL, " ", saveptr);
291 DNS_PARSE_STR(rec->data.mx.nameTarget, NULL, " ", saveptr);
295 DNS_PARSE_STR(rec->data.hinfo.cpu, NULL, " ", saveptr);
296 DNS_PARSE_STR(rec->data.hinfo.os, NULL, " ", saveptr);
300 DNS_PARSE_STR(rec->data.ns, NULL, " ", saveptr);
304 DNS_PARSE_STR(rec->data.soa.mname, NULL, " ", saveptr);
305 DNS_PARSE_STR(rec->data.soa.rname, NULL, " ", saveptr);
306 DNS_PARSE_UINT(rec->data.soa.serial, NULL, " ", saveptr);
307 DNS_PARSE_UINT(rec->data.soa.refresh, NULL, " ", saveptr);
308 DNS_PARSE_UINT(rec->data.soa.retry, NULL, " ", saveptr);
309 DNS_PARSE_UINT(rec->data.soa.expire, NULL, " ", saveptr);
310 DNS_PARSE_UINT(rec->data.soa.minimum, NULL, " ", saveptr);
314 state->log(ISC_LOG_ERROR, "samba b9_parse: unhandled record type %u",
319 /* we should be at the end of the buffer now */
320 if (strtok_r(NULL, "\t ", &saveptr) != NULL) {
321 state->log(ISC_LOG_ERROR, "samba b9_parse: expected data at end of string for '%s'");
329 send a resource recond to bind9
331 static isc_result_t b9_putrr(struct dlz_bind9_data *state,
332 void *handle, struct dnsp_DnssrvRpcRecord *rec,
336 const char *type, *data;
337 TALLOC_CTX *tmp_ctx = talloc_new(state);
339 if (!b9_format(state, tmp_ctx, rec, &type, &data)) {
340 return ISC_R_FAILURE;
344 talloc_free(tmp_ctx);
345 return ISC_R_NOMEMORY;
350 for (i=0; types[i]; i++) {
351 if (strcmp(types[i], type) == 0) break;
353 if (types[i] == NULL) {
355 return ISC_R_SUCCESS;
359 result = state->putrr(handle, type, rec->dwTtlSeconds, data);
360 if (result != ISC_R_SUCCESS) {
361 state->log(ISC_LOG_ERROR, "Failed to put rr");
363 talloc_free(tmp_ctx);
369 send a named resource recond to bind9
371 static isc_result_t b9_putnamedrr(struct dlz_bind9_data *state,
372 void *handle, const char *name,
373 struct dnsp_DnssrvRpcRecord *rec)
376 const char *type, *data;
377 TALLOC_CTX *tmp_ctx = talloc_new(state);
379 if (!b9_format(state, tmp_ctx, rec, &type, &data)) {
380 return ISC_R_FAILURE;
384 talloc_free(tmp_ctx);
385 return ISC_R_NOMEMORY;
388 result = state->putnamedrr(handle, name, type, rec->dwTtlSeconds, data);
389 if (result != ISC_R_SUCCESS) {
390 state->log(ISC_LOG_ERROR, "Failed to put named rr '%s'", name);
392 talloc_free(tmp_ctx);
403 static isc_result_t parse_options(struct dlz_bind9_data *state,
404 unsigned int argc, char *argv[],
405 struct b9_options *options)
409 struct poptOption long_options[] = {
410 { "url", 'H', POPT_ARG_STRING, &options->url, 0, "database URL", "URL" },
413 struct poptOption **popt_options;
416 popt_options = ldb_module_popt_options(state->samdb);
417 (*popt_options) = long_options;
419 ret = ldb_modules_hook(state->samdb, LDB_MODULE_HOOK_CMDLINE_OPTIONS);
420 if (ret != LDB_SUCCESS) {
421 state->log(ISC_LOG_ERROR, "dlz samba: failed cmdline hook");
422 return ISC_R_FAILURE;
425 pc = poptGetContext("dlz_bind9", argc, (const char **)argv, *popt_options,
426 POPT_CONTEXT_KEEP_FIRST);
428 while ((opt = poptGetNextOpt(pc)) != -1) {
431 state->log(ISC_LOG_ERROR, "dlz samba: Invalid option %s: %s",
432 poptBadOption(pc, 0), poptStrerror(opt));
433 return ISC_R_FAILURE;
437 ret = ldb_modules_hook(state->samdb, LDB_MODULE_HOOK_CMDLINE_PRECONNECT);
438 if (ret != LDB_SUCCESS) {
439 state->log(ISC_LOG_ERROR, "dlz samba: failed cmdline preconnect");
440 return ISC_R_FAILURE;
443 return ISC_R_SUCCESS;
448 called to initialise the driver
450 _PUBLIC_ isc_result_t dlz_create(const char *dlzname,
451 unsigned int argc, char *argv[],
454 struct dlz_bind9_data *state;
455 const char *helper_name;
461 struct b9_options options;
463 ZERO_STRUCT(options);
465 state = talloc_zero(NULL, struct dlz_bind9_data);
467 return ISC_R_NOMEMORY;
470 tmp_ctx = talloc_new(state);
472 /* fill in the helper functions */
473 va_start(ap, dbdata);
474 while ((helper_name = va_arg(ap, const char *)) != NULL) {
475 b9_add_helper(state, helper_name, va_arg(ap, void*));
479 state->ev_ctx = s4_event_context_init(state);
480 if (state->ev_ctx == NULL) {
481 result = ISC_R_NOMEMORY;
485 state->samdb = ldb_init(state, state->ev_ctx);
486 if (state->samdb == NULL) {
487 state->log(ISC_LOG_ERROR, "samba_dlz: Failed to create ldb");
488 result = ISC_R_FAILURE;
492 result = parse_options(state, argc, argv, &options);
493 if (result != ISC_R_SUCCESS) {
497 state->lp = loadparm_init_global(true);
498 if (state->lp == NULL) {
499 result = ISC_R_NOMEMORY;
503 if (options.url == NULL) {
504 options.url = talloc_asprintf(tmp_ctx, "ldapi://%s",
505 private_path(tmp_ctx, state->lp, "ldap_priv/ldapi"));
506 if (options.url == NULL) {
507 result = ISC_R_NOMEMORY;
512 ret = ldb_connect(state->samdb, options.url, 0, NULL);
514 state->log(ISC_LOG_ERROR, "samba_dlz: Failed to connect to %s - %s",
515 options.url, ldb_errstring(state->samdb));
516 result = ISC_R_FAILURE;
520 ret = ldb_modules_hook(state->samdb, LDB_MODULE_HOOK_CMDLINE_POSTCONNECT);
521 if (ret != LDB_SUCCESS) {
522 state->log(ISC_LOG_ERROR, "samba_dlz: Failed postconnect for %s - %s",
523 options.url, ldb_errstring(state->samdb));
524 result = ISC_R_FAILURE;
528 dn = ldb_get_default_basedn(state->samdb);
530 state->log(ISC_LOG_ERROR, "samba_dlz: Unable to get basedn for %s - %s",
531 options.url, ldb_errstring(state->samdb));
532 result = ISC_R_FAILURE;
536 state->log(ISC_LOG_INFO, "samba_dlz: started for DN %s",
537 ldb_dn_get_linearized(dn));
541 talloc_free(tmp_ctx);
542 return ISC_R_SUCCESS;
552 _PUBLIC_ void dlz_destroy(void *dbdata)
554 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
555 state->log(ISC_LOG_INFO, "samba_dlz: shutting down");
561 return the base DN for a zone
563 static isc_result_t b9_find_zone_dn(struct dlz_bind9_data *state, const char *zone_name,
564 TALLOC_CTX *mem_ctx, struct ldb_dn **zone_dn)
567 TALLOC_CTX *tmp_ctx = talloc_new(state);
568 const char *attrs[] = { NULL };
571 for (i=0; zone_prefixes[i]; i++) {
573 struct ldb_result *res;
575 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
577 talloc_free(tmp_ctx);
578 return ISC_R_NOMEMORY;
581 if (!ldb_dn_add_child_fmt(dn, "DC=%s,%s", zone_name, zone_prefixes[i])) {
582 talloc_free(tmp_ctx);
583 return ISC_R_NOMEMORY;
586 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsZone");
587 if (ret == LDB_SUCCESS) {
588 if (zone_dn != NULL) {
589 *zone_dn = talloc_steal(mem_ctx, dn);
591 talloc_free(tmp_ctx);
592 return ISC_R_SUCCESS;
597 talloc_free(tmp_ctx);
598 return ISC_R_NOTFOUND;
603 return the DN for a name. The record does not need to exist, but the
606 static isc_result_t b9_find_name_dn(struct dlz_bind9_data *state, const char *name,
607 TALLOC_CTX *mem_ctx, struct ldb_dn **dn)
611 /* work through the name piece by piece, until we find a zone */
614 result = b9_find_zone_dn(state, p, mem_ctx, dn);
615 if (result == ISC_R_SUCCESS) {
616 /* we found a zone, now extend the DN to get
621 ret = ldb_dn_add_child_fmt(*dn, "DC=@");
623 ret = ldb_dn_add_child_fmt(*dn, "DC=%.*s", (int)(p-name)-1, name);
627 return ISC_R_NOMEMORY;
629 return ISC_R_SUCCESS;
637 return ISC_R_NOTFOUND;
642 see if we handle a given zone
644 _PUBLIC_ isc_result_t dlz_findzonedb(void *dbdata, const char *name)
646 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
647 return b9_find_zone_dn(state, name, NULL, NULL);
654 static isc_result_t dlz_lookup_types(struct dlz_bind9_data *state,
655 const char *zone, const char *name,
656 dns_sdlzlookup_t *lookup,
659 TALLOC_CTX *tmp_ctx = talloc_new(state);
660 const char *attrs[] = { "dnsRecord", NULL };
662 struct ldb_result *res;
663 struct ldb_message_element *el;
666 for (i=0; zone_prefixes[i]; i++) {
667 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
669 talloc_free(tmp_ctx);
670 return ISC_R_NOMEMORY;
673 if (!ldb_dn_add_child_fmt(dn, "DC=%s,DC=%s,%s", name, zone, zone_prefixes[i])) {
674 talloc_free(tmp_ctx);
675 return ISC_R_NOMEMORY;
678 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
679 attrs, "objectClass=dnsNode");
680 if (ret == LDB_SUCCESS) {
684 if (ret != LDB_SUCCESS) {
685 talloc_free(tmp_ctx);
686 return ISC_R_NOTFOUND;
689 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
690 if (el == NULL || el->num_values == 0) {
691 talloc_free(tmp_ctx);
692 return ISC_R_NOTFOUND;
695 for (i=0; i<el->num_values; i++) {
696 struct dnsp_DnssrvRpcRecord rec;
697 enum ndr_err_code ndr_err;
700 ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec,
701 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
702 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
703 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
704 ldb_dn_get_linearized(dn));
705 talloc_free(tmp_ctx);
706 return ISC_R_FAILURE;
709 result = b9_putrr(state, lookup, &rec, types);
710 if (result != ISC_R_SUCCESS) {
711 talloc_free(tmp_ctx);
716 talloc_free(tmp_ctx);
717 return ISC_R_SUCCESS;
723 _PUBLIC_ isc_result_t dlz_lookup(const char *zone, const char *name,
724 void *dbdata, dns_sdlzlookup_t *lookup)
726 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
727 return dlz_lookup_types(state, zone, name, lookup, NULL);
732 see if a zone transfer is allowed
734 _PUBLIC_ isc_result_t dlz_allowzonexfr(void *dbdata, const char *name, const char *client)
736 /* just say yes for all our zones for now */
737 return dlz_findzonedb(dbdata, name);
741 perform a zone transfer
743 _PUBLIC_ isc_result_t dlz_allnodes(const char *zone, void *dbdata,
744 dns_sdlzallnodes_t *allnodes)
746 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
747 const char *attrs[] = { "dnsRecord", NULL };
750 struct ldb_result *res;
751 TALLOC_CTX *tmp_ctx = talloc_new(state);
753 for (i=0; zone_prefixes[i]; i++) {
754 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
756 talloc_free(tmp_ctx);
757 return ISC_R_NOMEMORY;
760 if (!ldb_dn_add_child_fmt(dn, "DC=%s,%s", zone, zone_prefixes[i])) {
761 talloc_free(tmp_ctx);
762 return ISC_R_NOMEMORY;
765 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
766 attrs, "objectClass=dnsNode");
767 if (ret == LDB_SUCCESS) {
771 if (ret != LDB_SUCCESS) {
772 talloc_free(tmp_ctx);
773 return ISC_R_NOTFOUND;
776 for (i=0; i<res->count; i++) {
777 struct ldb_message_element *el;
778 TALLOC_CTX *el_ctx = talloc_new(tmp_ctx);
779 const char *rdn, *name;
780 const struct ldb_val *v;
782 el = ldb_msg_find_element(res->msgs[i], "dnsRecord");
783 if (el == NULL || el->num_values == 0) {
784 state->log(ISC_LOG_INFO, "failed to find dnsRecord for %s",
785 ldb_dn_get_linearized(dn));
790 v = ldb_dn_get_rdn_val(res->msgs[i]->dn);
792 state->log(ISC_LOG_INFO, "failed to find RDN for %s",
793 ldb_dn_get_linearized(dn));
798 rdn = talloc_strndup(el_ctx, (char *)v->data, v->length);
800 talloc_free(tmp_ctx);
801 return ISC_R_NOMEMORY;
804 if (strcmp(rdn, "@") == 0) {
807 name = talloc_asprintf(el_ctx, "%s.%s", rdn, zone);
810 talloc_free(tmp_ctx);
811 return ISC_R_NOMEMORY;
814 for (j=0; j<el->num_values; j++) {
815 struct dnsp_DnssrvRpcRecord rec;
816 enum ndr_err_code ndr_err;
819 ndr_err = ndr_pull_struct_blob(&el->values[j], el_ctx, &rec,
820 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
821 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
822 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
823 ldb_dn_get_linearized(dn));
827 result = b9_putnamedrr(state, allnodes, name, &rec);
828 if (result != ISC_R_SUCCESS) {
834 talloc_free(tmp_ctx);
836 return ISC_R_SUCCESS;
843 _PUBLIC_ isc_result_t dlz_newversion(const char *zone, void *dbdata, void **versionp)
845 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
847 state->log(ISC_LOG_INFO, "samba_dlz: starting transaction on zone %s", zone);
849 if (state->transaction_token != NULL) {
850 state->log(ISC_LOG_INFO, "samba_dlz: transaction already started for zone %s", zone);
851 return ISC_R_FAILURE;
854 state->transaction_token = talloc_zero(state, int);
855 if (state->transaction_token == NULL) {
856 return ISC_R_NOMEMORY;
859 if (ldb_transaction_start(state->samdb) != LDB_SUCCESS) {
860 state->log(ISC_LOG_INFO, "samba_dlz: failed to start a transaction for zone %s", zone);
861 talloc_free(state->transaction_token);
862 state->transaction_token = NULL;
863 return ISC_R_FAILURE;
866 *versionp = (void *)state->transaction_token;
868 return ISC_R_SUCCESS;
874 _PUBLIC_ void dlz_closeversion(const char *zone, isc_boolean_t commit,
875 void *dbdata, void **versionp)
877 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
879 if (state->transaction_token != (int *)*versionp) {
880 state->log(ISC_LOG_INFO, "samba_dlz: transaction not started for zone %s", zone);
885 if (ldb_transaction_commit(state->samdb) != LDB_SUCCESS) {
886 state->log(ISC_LOG_INFO, "samba_dlz: failed to commit a transaction for zone %s", zone);
889 state->log(ISC_LOG_INFO, "samba_dlz: committed transaction on zone %s", zone);
891 if (ldb_transaction_cancel(state->samdb) != LDB_SUCCESS) {
892 state->log(ISC_LOG_INFO, "samba_dlz: failed to cancel a transaction for zone %s", zone);
895 state->log(ISC_LOG_INFO, "samba_dlz: cancelling transaction on zone %s", zone);
898 talloc_free(state->transaction_token);
899 state->transaction_token = NULL;
905 see if there is a SOA record for a zone
907 static bool b9_has_soa(struct dlz_bind9_data *state, struct ldb_dn *dn, const char *zone)
909 const char *attrs[] = { "dnsRecord", NULL };
910 struct ldb_result *res;
911 struct ldb_message_element *el;
912 TALLOC_CTX *tmp_ctx = talloc_new(state);
915 if (!ldb_dn_add_child_fmt(dn, "DC=@,DC=%s", zone)) {
916 talloc_free(tmp_ctx);
920 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
921 attrs, "objectClass=dnsNode");
922 if (ret != LDB_SUCCESS) {
923 talloc_free(tmp_ctx);
927 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
929 talloc_free(tmp_ctx);
932 for (i=0; i<el->num_values; i++) {
933 struct dnsp_DnssrvRpcRecord rec;
934 enum ndr_err_code ndr_err;
936 ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec,
937 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
938 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
941 if (rec.wType == DNS_TYPE_SOA) {
942 talloc_free(tmp_ctx);
947 talloc_free(tmp_ctx);
952 configure a writeable zone
954 _PUBLIC_ isc_result_t dlz_configure(dns_view_t *view, void *dbdata)
956 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
961 state->log(ISC_LOG_INFO, "samba_dlz: starting configure");
962 if (state->writeable_zone == NULL) {
963 state->log(ISC_LOG_INFO, "samba_dlz: no writeable_zone method available");
964 return ISC_R_FAILURE;
967 tmp_ctx = talloc_new(state);
969 for (i=0; zone_prefixes[i]; i++) {
970 const char *attrs[] = { "name", NULL };
972 struct ldb_result *res;
974 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
976 talloc_free(tmp_ctx);
977 return ISC_R_NOMEMORY;
980 if (!ldb_dn_add_child_fmt(dn, "%s", zone_prefixes[i])) {
981 talloc_free(tmp_ctx);
982 return ISC_R_NOMEMORY;
985 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
986 attrs, "objectClass=dnsZone");
987 if (ret != LDB_SUCCESS) {
991 for (j=0; j<res->count; j++) {
993 const char *zone = ldb_msg_find_attr_as_string(res->msgs[j], "name", NULL);
997 if (!b9_has_soa(state, dn, zone)) {
1000 result = state->writeable_zone(view, zone);
1001 if (result != ISC_R_SUCCESS) {
1002 state->log(ISC_LOG_ERROR, "samba_dlz: Failed to configure zone '%s'",
1004 talloc_free(tmp_ctx);
1007 state->log(ISC_LOG_INFO, "samba_dlz: configured writeable zone '%s'", zone);
1011 talloc_free(tmp_ctx);
1012 return ISC_R_SUCCESS;
1016 authorize a zone update
1018 _PUBLIC_ isc_boolean_t dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr,
1019 const char *type, const char *key, uint32_t keydatalen, uint8_t *keydata,
1022 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1024 state->log(ISC_LOG_INFO, "samba_dlz: allowing update of signer=%s name=%s tcpaddr=%s type=%s key=%s keydatalen=%u",
1025 signer, name, tcpaddr, type, key, keydatalen);
1033 static isc_result_t b9_add_record(struct dlz_bind9_data *state, const char *name,
1035 struct dnsp_DnssrvRpcRecord *rec)
1037 struct ldb_message *msg;
1038 enum ndr_err_code ndr_err;
1042 msg = ldb_msg_new(rec);
1044 return ISC_R_NOMEMORY;
1047 ret = ldb_msg_add_string(msg, "objectClass", "dnsNode");
1048 if (ret != LDB_SUCCESS) {
1049 return ISC_R_FAILURE;
1052 ndr_err = ndr_push_struct_blob(&v, rec, rec, (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
1053 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1054 return ISC_R_FAILURE;
1056 ret = ldb_msg_add_value(msg, "dnsRecord", &v, NULL);
1057 if (ret != LDB_SUCCESS) {
1058 return ISC_R_FAILURE;
1061 ret = ldb_add(state->samdb, msg);
1062 if (ret != LDB_SUCCESS) {
1063 return ISC_R_FAILURE;
1066 return ISC_R_SUCCESS;
1071 see if two dns records match
1073 static bool b9_record_match(struct dlz_bind9_data *state,
1074 struct dnsp_DnssrvRpcRecord *rec1, struct dnsp_DnssrvRpcRecord *rec2)
1076 if (rec1->wType != rec2->wType) {
1079 /* see if this type is single valued */
1080 if (b9_single_valued(rec1->wType)) {
1084 /* see if the data matches */
1085 switch (rec1->wType) {
1087 return strcmp(rec1->data.ipv4, rec2->data.ipv4) == 0;
1089 return strcmp(rec1->data.ipv6, rec2->data.ipv6) == 0;
1090 case DNS_TYPE_CNAME:
1091 return strcmp(rec1->data.cname, rec2->data.cname) == 0;
1093 return strcmp(rec1->data.txt, rec2->data.txt) == 0;
1095 return strcmp(rec1->data.ptr, rec2->data.ptr) == 0;
1097 return strcmp(rec1->data.ns, rec2->data.ns) == 0;
1100 return rec1->data.srv.wPriority == rec2->data.srv.wPriority &&
1101 rec1->data.srv.wWeight == rec2->data.srv.wWeight &&
1102 rec1->data.srv.wPort == rec2->data.srv.wPort &&
1103 strcmp(rec1->data.srv.nameTarget, rec2->data.srv.nameTarget) == 0;
1106 return rec1->data.mx.wPriority == rec2->data.mx.wPriority &&
1107 strcmp(rec1->data.mx.nameTarget, rec2->data.mx.nameTarget) == 0;
1109 case DNS_TYPE_HINFO:
1110 return strcmp(rec1->data.hinfo.cpu, rec2->data.hinfo.cpu) == 0 &&
1111 strcmp(rec1->data.hinfo.os, rec2->data.hinfo.os) == 0;
1114 return strcmp(rec1->data.soa.mname, rec2->data.soa.mname) == 0 &&
1115 strcmp(rec1->data.soa.rname, rec2->data.soa.rname) == 0 &&
1116 rec1->data.soa.serial == rec2->data.soa.serial &&
1117 rec1->data.soa.refresh == rec2->data.soa.refresh &&
1118 rec1->data.soa.retry == rec2->data.soa.retry &&
1119 rec1->data.soa.expire == rec2->data.soa.expire &&
1120 rec1->data.soa.minimum == rec2->data.soa.minimum;
1122 state->log(ISC_LOG_ERROR, "samba b9_putrr: unhandled record type %u",
1132 add or modify a rdataset
1134 _PUBLIC_ isc_result_t dlz_addrdataset(const char *name, const char *rdatastr, void *dbdata, void *version)
1136 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1137 struct dnsp_DnssrvRpcRecord *rec;
1139 isc_result_t result;
1140 struct ldb_result *res;
1141 const char *attrs[] = { "dnsRecord", NULL };
1143 struct ldb_message_element *el;
1144 enum ndr_err_code ndr_err;
1146 if (state->transaction_token != (void*)version) {
1147 state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version");
1148 return ISC_R_FAILURE;
1151 rec = talloc_zero(state, struct dnsp_DnssrvRpcRecord);
1153 return ISC_R_NOMEMORY;
1156 if (!b9_parse(state, rdatastr, rec)) {
1157 state->log(ISC_LOG_INFO, "samba_dlz: failed to parse rdataset '%s'", rdatastr);
1159 return ISC_R_FAILURE;
1162 /* find the DN of the record */
1163 result = b9_find_name_dn(state, name, rec, &dn);
1164 if (result != ISC_R_SUCCESS) {
1169 /* get any existing records */
1170 ret = ldb_search(state->samdb, rec, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode");
1171 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1172 result = b9_add_record(state, name, dn, rec);
1174 if (result == ISC_R_SUCCESS) {
1175 state->log(ISC_LOG_ERROR, "samba_dlz: added %s %s", name, rdatastr);
1180 /* there are existing records. We need to see if this will
1181 * replace a record or add to it
1183 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
1185 state->log(ISC_LOG_ERROR, "samba_dlz: no dnsRecord attribute for %s",
1186 ldb_dn_get_linearized(dn));
1188 return ISC_R_FAILURE;
1191 for (i=0; i<el->num_values; i++) {
1192 struct dnsp_DnssrvRpcRecord rec2;
1194 ndr_err = ndr_pull_struct_blob(&el->values[i], rec, &rec2,
1195 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
1196 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1197 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
1198 ldb_dn_get_linearized(dn));
1200 return ISC_R_FAILURE;
1203 if (b9_record_match(state, rec, &rec2)) {
1207 if (i == el->num_values) {
1208 /* adding a new value */
1209 el->values = talloc_realloc(el, el->values, struct ldb_val, el->num_values+1);
1210 if (el->values == NULL) {
1212 return ISC_R_NOMEMORY;
1217 ndr_err = ndr_push_struct_blob(&el->values[i], rec, rec,
1218 (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
1219 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1220 state->log(ISC_LOG_ERROR, "samba_dlz: failed to push dnsRecord for %s",
1221 ldb_dn_get_linearized(dn));
1223 return ISC_R_FAILURE;
1226 /* modify the record */
1227 el->flags = LDB_FLAG_MOD_REPLACE;
1228 ret = ldb_modify(state->samdb, res->msgs[0]);
1229 if (ret != LDB_SUCCESS) {
1230 state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s",
1231 ldb_dn_get_linearized(dn), ldb_errstring(state->samdb));
1233 return ISC_R_FAILURE;
1236 state->log(ISC_LOG_INFO, "samba_dlz: added rdataset %s '%s'", name, rdatastr);
1239 return ISC_R_SUCCESS;
1245 _PUBLIC_ isc_result_t dlz_subrdataset(const char *name, const char *rdatastr, void *dbdata, void *version)
1247 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1248 struct dnsp_DnssrvRpcRecord *rec;
1250 isc_result_t result;
1251 struct ldb_result *res;
1252 const char *attrs[] = { "dnsRecord", NULL };
1254 struct ldb_message_element *el;
1255 enum ndr_err_code ndr_err;
1257 if (state->transaction_token != (void*)version) {
1258 state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version");
1259 return ISC_R_FAILURE;
1262 rec = talloc_zero(state, struct dnsp_DnssrvRpcRecord);
1264 return ISC_R_NOMEMORY;
1267 if (!b9_parse(state, rdatastr, rec)) {
1268 state->log(ISC_LOG_INFO, "samba_dlz: failed to parse rdataset '%s'", rdatastr);
1270 return ISC_R_FAILURE;
1273 /* find the DN of the record */
1274 result = b9_find_name_dn(state, name, rec, &dn);
1275 if (result != ISC_R_SUCCESS) {
1280 /* get the existing records */
1281 ret = ldb_search(state->samdb, rec, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode");
1282 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1284 return ISC_R_NOTFOUND;
1287 /* there are existing records. We need to see if any match
1289 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
1290 if (el == NULL || el->num_values == 0) {
1291 state->log(ISC_LOG_ERROR, "samba_dlz: no dnsRecord attribute for %s",
1292 ldb_dn_get_linearized(dn));
1294 return ISC_R_FAILURE;
1297 for (i=0; i<el->num_values; i++) {
1298 struct dnsp_DnssrvRpcRecord rec2;
1300 ndr_err = ndr_pull_struct_blob(&el->values[i], rec, &rec2,
1301 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
1302 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1303 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
1304 ldb_dn_get_linearized(dn));
1306 return ISC_R_FAILURE;
1309 if (b9_record_match(state, rec, &rec2)) {
1313 if (i == el->num_values) {
1315 return ISC_R_NOTFOUND;
1318 if (i < el->num_values-1) {
1319 memmove(&el->values[i], &el->values[i+1], sizeof(el->values[0])*((el->num_values-1)-i));
1323 if (el->num_values == 0) {
1324 /* delete the record */
1325 ret = ldb_delete(state->samdb, dn);
1327 /* modify the record */
1328 el->flags = LDB_FLAG_MOD_REPLACE;
1329 ret = ldb_modify(state->samdb, res->msgs[0]);
1331 if (ret != LDB_SUCCESS) {
1332 state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s",
1333 ldb_dn_get_linearized(dn), ldb_errstring(state->samdb));
1335 return ISC_R_FAILURE;
1338 state->log(ISC_LOG_INFO, "samba_dlz: subtracted rdataset %s '%s'", name, rdatastr);
1341 return ISC_R_SUCCESS;
1346 delete all records of the given type
1348 _PUBLIC_ isc_result_t dlz_delrdataset(const char *name, const char *type, void *dbdata, void *version)
1350 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1351 TALLOC_CTX *tmp_ctx;
1353 isc_result_t result;
1354 struct ldb_result *res;
1355 const char *attrs[] = { "dnsRecord", NULL };
1357 struct ldb_message_element *el;
1358 enum ndr_err_code ndr_err;
1359 enum dns_record_type dns_type;
1362 if (state->transaction_token != (void*)version) {
1363 state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version");
1364 return ISC_R_FAILURE;
1367 dns_type = b9_dns_type(type);
1368 if (dns_type == DNS_TYPE_ZERO) {
1369 state->log(ISC_LOG_INFO, "samba_dlz: bad dns type %s in delete", type);
1370 return ISC_R_FAILURE;
1373 tmp_ctx = talloc_new(state);
1375 /* find the DN of the record */
1376 result = b9_find_name_dn(state, name, tmp_ctx, &dn);
1377 if (result != ISC_R_SUCCESS) {
1378 talloc_free(tmp_ctx);
1382 /* get the existing records */
1383 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode");
1384 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1385 talloc_free(tmp_ctx);
1386 return ISC_R_NOTFOUND;
1389 /* there are existing records. We need to see if any match the type
1391 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
1392 if (el == NULL || el->num_values == 0) {
1393 talloc_free(tmp_ctx);
1394 return ISC_R_NOTFOUND;
1397 for (i=0; i<el->num_values; i++) {
1398 struct dnsp_DnssrvRpcRecord rec2;
1400 ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec2,
1401 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
1402 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1403 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
1404 ldb_dn_get_linearized(dn));
1405 talloc_free(tmp_ctx);
1406 return ISC_R_FAILURE;
1409 if (dns_type == rec2.wType) {
1410 if (i < el->num_values-1) {
1411 memmove(&el->values[i], &el->values[i+1],
1412 sizeof(el->values[0])*((el->num_values-1)-i));
1421 talloc_free(tmp_ctx);
1422 return ISC_R_FAILURE;
1425 if (el->num_values == 0) {
1426 /* delete the record */
1427 ret = ldb_delete(state->samdb, dn);
1429 /* modify the record */
1430 el->flags = LDB_FLAG_MOD_REPLACE;
1431 ret = ldb_modify(state->samdb, res->msgs[0]);
1433 if (ret != LDB_SUCCESS) {
1434 state->log(ISC_LOG_ERROR, "samba_dlz: failed to delete type %s in %s - %s",
1435 type, ldb_dn_get_linearized(dn), ldb_errstring(state->samdb));
1436 talloc_free(tmp_ctx);
1437 return ISC_R_FAILURE;
1440 state->log(ISC_LOG_INFO, "samba_dlz: deleted rdataset %s of type %s", name, type);
1442 talloc_free(tmp_ctx);
1443 return ISC_R_SUCCESS;