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));
828 result = b9_putnamedrr(state, allnodes, name, &rec);
829 if (result != ISC_R_SUCCESS) {
836 talloc_free(tmp_ctx);
838 return ISC_R_SUCCESS;
845 _PUBLIC_ isc_result_t dlz_newversion(const char *zone, void *dbdata, void **versionp)
847 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
849 state->log(ISC_LOG_INFO, "samba_dlz: starting transaction on zone %s", zone);
851 if (state->transaction_token != NULL) {
852 state->log(ISC_LOG_INFO, "samba_dlz: transaction already started for zone %s", zone);
853 return ISC_R_FAILURE;
856 state->transaction_token = talloc_zero(state, int);
857 if (state->transaction_token == NULL) {
858 return ISC_R_NOMEMORY;
861 if (ldb_transaction_start(state->samdb) != LDB_SUCCESS) {
862 state->log(ISC_LOG_INFO, "samba_dlz: failed to start a transaction for zone %s", zone);
863 talloc_free(state->transaction_token);
864 state->transaction_token = NULL;
865 return ISC_R_FAILURE;
868 *versionp = (void *)state->transaction_token;
870 return ISC_R_SUCCESS;
876 _PUBLIC_ void dlz_closeversion(const char *zone, isc_boolean_t commit,
877 void *dbdata, void **versionp)
879 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
881 if (state->transaction_token != (int *)*versionp) {
882 state->log(ISC_LOG_INFO, "samba_dlz: transaction not started for zone %s", zone);
887 if (ldb_transaction_commit(state->samdb) != LDB_SUCCESS) {
888 state->log(ISC_LOG_INFO, "samba_dlz: failed to commit a transaction for zone %s", zone);
891 state->log(ISC_LOG_INFO, "samba_dlz: committed transaction on zone %s", zone);
893 if (ldb_transaction_cancel(state->samdb) != LDB_SUCCESS) {
894 state->log(ISC_LOG_INFO, "samba_dlz: failed to cancel a transaction for zone %s", zone);
897 state->log(ISC_LOG_INFO, "samba_dlz: cancelling transaction on zone %s", zone);
900 talloc_free(state->transaction_token);
901 state->transaction_token = NULL;
907 see if there is a SOA record for a zone
909 static bool b9_has_soa(struct dlz_bind9_data *state, struct ldb_dn *dn, const char *zone)
911 const char *attrs[] = { "dnsRecord", NULL };
912 struct ldb_result *res;
913 struct ldb_message_element *el;
914 TALLOC_CTX *tmp_ctx = talloc_new(state);
917 if (!ldb_dn_add_child_fmt(dn, "DC=@,DC=%s", zone)) {
918 talloc_free(tmp_ctx);
922 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
923 attrs, "objectClass=dnsNode");
924 if (ret != LDB_SUCCESS) {
925 talloc_free(tmp_ctx);
929 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
931 talloc_free(tmp_ctx);
934 for (i=0; i<el->num_values; i++) {
935 struct dnsp_DnssrvRpcRecord rec;
936 enum ndr_err_code ndr_err;
938 ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec,
939 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
940 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
943 if (rec.wType == DNS_TYPE_SOA) {
944 talloc_free(tmp_ctx);
949 talloc_free(tmp_ctx);
954 configure a writeable zone
956 _PUBLIC_ isc_result_t dlz_configure(dns_view_t *view, void *dbdata)
958 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
963 state->log(ISC_LOG_INFO, "samba_dlz: starting configure");
964 if (state->writeable_zone == NULL) {
965 state->log(ISC_LOG_INFO, "samba_dlz: no writeable_zone method available");
966 return ISC_R_FAILURE;
969 tmp_ctx = talloc_new(state);
971 for (i=0; zone_prefixes[i]; i++) {
972 const char *attrs[] = { "name", NULL };
974 struct ldb_result *res;
976 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
978 talloc_free(tmp_ctx);
979 return ISC_R_NOMEMORY;
982 if (!ldb_dn_add_child_fmt(dn, "%s", zone_prefixes[i])) {
983 talloc_free(tmp_ctx);
984 return ISC_R_NOMEMORY;
987 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
988 attrs, "objectClass=dnsZone");
989 if (ret != LDB_SUCCESS) {
993 for (j=0; j<res->count; j++) {
995 const char *zone = ldb_msg_find_attr_as_string(res->msgs[j], "name", NULL);
999 if (!b9_has_soa(state, dn, zone)) {
1002 result = state->writeable_zone(view, zone);
1003 if (result != ISC_R_SUCCESS) {
1004 state->log(ISC_LOG_ERROR, "samba_dlz: Failed to configure zone '%s'",
1006 talloc_free(tmp_ctx);
1009 state->log(ISC_LOG_INFO, "samba_dlz: configured writeable zone '%s'", zone);
1013 talloc_free(tmp_ctx);
1014 return ISC_R_SUCCESS;
1018 authorize a zone update
1020 _PUBLIC_ isc_boolean_t dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr,
1021 const char *type, const char *key, uint32_t keydatalen, uint8_t *keydata,
1024 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1026 state->log(ISC_LOG_INFO, "samba_dlz: allowing update of signer=%s name=%s tcpaddr=%s type=%s key=%s keydatalen=%u",
1027 signer, name, tcpaddr, type, key, keydatalen);
1035 static isc_result_t b9_add_record(struct dlz_bind9_data *state, const char *name,
1037 struct dnsp_DnssrvRpcRecord *rec)
1039 struct ldb_message *msg;
1040 enum ndr_err_code ndr_err;
1044 msg = ldb_msg_new(rec);
1046 return ISC_R_NOMEMORY;
1049 ret = ldb_msg_add_string(msg, "objectClass", "dnsNode");
1050 if (ret != LDB_SUCCESS) {
1051 return ISC_R_FAILURE;
1054 ndr_err = ndr_push_struct_blob(&v, rec, rec, (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
1055 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1056 return ISC_R_FAILURE;
1058 ret = ldb_msg_add_value(msg, "dnsRecord", &v, NULL);
1059 if (ret != LDB_SUCCESS) {
1060 return ISC_R_FAILURE;
1063 ret = ldb_add(state->samdb, msg);
1064 if (ret != LDB_SUCCESS) {
1065 return ISC_R_FAILURE;
1068 return ISC_R_SUCCESS;
1073 see if two dns records match
1075 static bool b9_record_match(struct dlz_bind9_data *state,
1076 struct dnsp_DnssrvRpcRecord *rec1, struct dnsp_DnssrvRpcRecord *rec2)
1078 if (rec1->wType != rec2->wType) {
1081 /* see if this type is single valued */
1082 if (b9_single_valued(rec1->wType)) {
1086 /* see if the data matches */
1087 switch (rec1->wType) {
1089 return strcmp(rec1->data.ipv4, rec2->data.ipv4) == 0;
1091 return strcmp(rec1->data.ipv6, rec2->data.ipv6) == 0;
1092 case DNS_TYPE_CNAME:
1093 return strcmp(rec1->data.cname, rec2->data.cname) == 0;
1095 return strcmp(rec1->data.txt, rec2->data.txt) == 0;
1097 return strcmp(rec1->data.ptr, rec2->data.ptr) == 0;
1099 return strcmp(rec1->data.ns, rec2->data.ns) == 0;
1102 return rec1->data.srv.wPriority == rec2->data.srv.wPriority &&
1103 rec1->data.srv.wWeight == rec2->data.srv.wWeight &&
1104 rec1->data.srv.wPort == rec2->data.srv.wPort &&
1105 strcmp(rec1->data.srv.nameTarget, rec2->data.srv.nameTarget) == 0;
1108 return rec1->data.mx.wPriority == rec2->data.mx.wPriority &&
1109 strcmp(rec1->data.mx.nameTarget, rec2->data.mx.nameTarget) == 0;
1111 case DNS_TYPE_HINFO:
1112 return strcmp(rec1->data.hinfo.cpu, rec2->data.hinfo.cpu) == 0 &&
1113 strcmp(rec1->data.hinfo.os, rec2->data.hinfo.os) == 0;
1116 return strcmp(rec1->data.soa.mname, rec2->data.soa.mname) == 0 &&
1117 strcmp(rec1->data.soa.rname, rec2->data.soa.rname) == 0 &&
1118 rec1->data.soa.serial == rec2->data.soa.serial &&
1119 rec1->data.soa.refresh == rec2->data.soa.refresh &&
1120 rec1->data.soa.retry == rec2->data.soa.retry &&
1121 rec1->data.soa.expire == rec2->data.soa.expire &&
1122 rec1->data.soa.minimum == rec2->data.soa.minimum;
1124 state->log(ISC_LOG_ERROR, "samba b9_putrr: unhandled record type %u",
1134 add or modify a rdataset
1136 _PUBLIC_ isc_result_t dlz_addrdataset(const char *name, const char *rdatastr, void *dbdata, void *version)
1138 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1139 struct dnsp_DnssrvRpcRecord *rec;
1141 isc_result_t result;
1142 struct ldb_result *res;
1143 const char *attrs[] = { "dnsRecord", NULL };
1145 struct ldb_message_element *el;
1146 enum ndr_err_code ndr_err;
1148 if (state->transaction_token != (void*)version) {
1149 state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version");
1150 return ISC_R_FAILURE;
1153 rec = talloc_zero(state, struct dnsp_DnssrvRpcRecord);
1155 return ISC_R_NOMEMORY;
1158 if (!b9_parse(state, rdatastr, rec)) {
1159 state->log(ISC_LOG_INFO, "samba_dlz: failed to parse rdataset '%s'", rdatastr);
1161 return ISC_R_FAILURE;
1164 /* find the DN of the record */
1165 result = b9_find_name_dn(state, name, rec, &dn);
1166 if (result != ISC_R_SUCCESS) {
1171 /* get any existing records */
1172 ret = ldb_search(state->samdb, rec, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode");
1173 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1174 result = b9_add_record(state, name, dn, rec);
1176 if (result == ISC_R_SUCCESS) {
1177 state->log(ISC_LOG_ERROR, "samba_dlz: added %s %s", name, rdatastr);
1182 /* there are existing records. We need to see if this will
1183 * replace a record or add to it
1185 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
1187 state->log(ISC_LOG_ERROR, "samba_dlz: no dnsRecord attribute for %s",
1188 ldb_dn_get_linearized(dn));
1190 return ISC_R_FAILURE;
1193 for (i=0; i<el->num_values; i++) {
1194 struct dnsp_DnssrvRpcRecord rec2;
1196 ndr_err = ndr_pull_struct_blob(&el->values[i], rec, &rec2,
1197 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
1198 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1199 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
1200 ldb_dn_get_linearized(dn));
1202 return ISC_R_FAILURE;
1205 if (b9_record_match(state, rec, &rec2)) {
1209 if (i == el->num_values) {
1210 /* adding a new value */
1211 el->values = talloc_realloc(el, el->values, struct ldb_val, el->num_values+1);
1212 if (el->values == NULL) {
1214 return ISC_R_NOMEMORY;
1219 ndr_err = ndr_push_struct_blob(&el->values[i], rec, rec,
1220 (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
1221 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1222 state->log(ISC_LOG_ERROR, "samba_dlz: failed to push dnsRecord for %s",
1223 ldb_dn_get_linearized(dn));
1225 return ISC_R_FAILURE;
1228 /* modify the record */
1229 el->flags = LDB_FLAG_MOD_REPLACE;
1230 ret = ldb_modify(state->samdb, res->msgs[0]);
1231 if (ret != LDB_SUCCESS) {
1232 state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s",
1233 ldb_dn_get_linearized(dn), ldb_errstring(state->samdb));
1235 return ISC_R_FAILURE;
1238 state->log(ISC_LOG_INFO, "samba_dlz: added rdataset %s '%s'", name, rdatastr);
1241 return ISC_R_SUCCESS;
1247 _PUBLIC_ isc_result_t dlz_subrdataset(const char *name, const char *rdatastr, void *dbdata, void *version)
1249 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1250 struct dnsp_DnssrvRpcRecord *rec;
1252 isc_result_t result;
1253 struct ldb_result *res;
1254 const char *attrs[] = { "dnsRecord", NULL };
1256 struct ldb_message_element *el;
1257 enum ndr_err_code ndr_err;
1259 if (state->transaction_token != (void*)version) {
1260 state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version");
1261 return ISC_R_FAILURE;
1264 rec = talloc_zero(state, struct dnsp_DnssrvRpcRecord);
1266 return ISC_R_NOMEMORY;
1269 if (!b9_parse(state, rdatastr, rec)) {
1270 state->log(ISC_LOG_INFO, "samba_dlz: failed to parse rdataset '%s'", rdatastr);
1272 return ISC_R_FAILURE;
1275 /* find the DN of the record */
1276 result = b9_find_name_dn(state, name, rec, &dn);
1277 if (result != ISC_R_SUCCESS) {
1282 /* get the existing records */
1283 ret = ldb_search(state->samdb, rec, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode");
1284 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1286 return ISC_R_NOTFOUND;
1289 /* there are existing records. We need to see if any match
1291 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
1292 if (el == NULL || el->num_values == 0) {
1293 state->log(ISC_LOG_ERROR, "samba_dlz: no dnsRecord attribute for %s",
1294 ldb_dn_get_linearized(dn));
1296 return ISC_R_FAILURE;
1299 for (i=0; i<el->num_values; i++) {
1300 struct dnsp_DnssrvRpcRecord rec2;
1302 ndr_err = ndr_pull_struct_blob(&el->values[i], rec, &rec2,
1303 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
1304 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1305 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
1306 ldb_dn_get_linearized(dn));
1308 return ISC_R_FAILURE;
1311 if (b9_record_match(state, rec, &rec2)) {
1315 if (i == el->num_values) {
1317 return ISC_R_NOTFOUND;
1320 if (i < el->num_values-1) {
1321 memmove(&el->values[i], &el->values[i+1], sizeof(el->values[0])*((el->num_values-1)-i));
1325 if (el->num_values == 0) {
1326 /* delete the record */
1327 ret = ldb_delete(state->samdb, dn);
1329 /* modify the record */
1330 el->flags = LDB_FLAG_MOD_REPLACE;
1331 ret = ldb_modify(state->samdb, res->msgs[0]);
1333 if (ret != LDB_SUCCESS) {
1334 state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s",
1335 ldb_dn_get_linearized(dn), ldb_errstring(state->samdb));
1337 return ISC_R_FAILURE;
1340 state->log(ISC_LOG_INFO, "samba_dlz: subtracted rdataset %s '%s'", name, rdatastr);
1343 return ISC_R_SUCCESS;
1348 delete all records of the given type
1350 _PUBLIC_ isc_result_t dlz_delrdataset(const char *name, const char *type, void *dbdata, void *version)
1352 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1353 TALLOC_CTX *tmp_ctx;
1355 isc_result_t result;
1356 struct ldb_result *res;
1357 const char *attrs[] = { "dnsRecord", NULL };
1359 struct ldb_message_element *el;
1360 enum ndr_err_code ndr_err;
1361 enum dns_record_type dns_type;
1364 if (state->transaction_token != (void*)version) {
1365 state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version");
1366 return ISC_R_FAILURE;
1369 dns_type = b9_dns_type(type);
1370 if (dns_type == DNS_TYPE_ZERO) {
1371 state->log(ISC_LOG_INFO, "samba_dlz: bad dns type %s in delete", type);
1372 return ISC_R_FAILURE;
1375 tmp_ctx = talloc_new(state);
1377 /* find the DN of the record */
1378 result = b9_find_name_dn(state, name, tmp_ctx, &dn);
1379 if (result != ISC_R_SUCCESS) {
1380 talloc_free(tmp_ctx);
1384 /* get the existing records */
1385 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode");
1386 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1387 talloc_free(tmp_ctx);
1388 return ISC_R_NOTFOUND;
1391 /* there are existing records. We need to see if any match the type
1393 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
1394 if (el == NULL || el->num_values == 0) {
1395 talloc_free(tmp_ctx);
1396 return ISC_R_NOTFOUND;
1399 for (i=0; i<el->num_values; i++) {
1400 struct dnsp_DnssrvRpcRecord rec2;
1402 ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec2,
1403 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
1404 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1405 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
1406 ldb_dn_get_linearized(dn));
1407 talloc_free(tmp_ctx);
1408 return ISC_R_FAILURE;
1411 if (dns_type == rec2.wType) {
1412 if (i < el->num_values-1) {
1413 memmove(&el->values[i], &el->values[i+1],
1414 sizeof(el->values[0])*((el->num_values-1)-i));
1423 talloc_free(tmp_ctx);
1424 return ISC_R_FAILURE;
1427 if (el->num_values == 0) {
1428 /* delete the record */
1429 ret = ldb_delete(state->samdb, dn);
1431 /* modify the record */
1432 el->flags = LDB_FLAG_MOD_REPLACE;
1433 ret = ldb_modify(state->samdb, res->msgs[0]);
1435 if (ret != LDB_SUCCESS) {
1436 state->log(ISC_LOG_ERROR, "samba_dlz: failed to delete type %s in %s - %s",
1437 type, ldb_dn_get_linearized(dn), ldb_errstring(state->samdb));
1438 talloc_free(tmp_ctx);
1439 return ISC_R_FAILURE;
1442 state->log(ISC_LOG_INFO, "samba_dlz: deleted rdataset %s of type %s", name, type);
1444 talloc_free(tmp_ctx);
1445 return ISC_R_SUCCESS;