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;
43 /* helper functions from the dlz_dlopen driver */
44 void (*log)(int level, const char *fmt, ...);
45 isc_result_t (*putrr)(dns_sdlzlookup_t *handle, const char *type,
46 dns_ttl_t ttl, const char *data);
47 isc_result_t (*putnamedrr)(dns_sdlzlookup_t *handle, const char *name,
48 const char *type, dns_ttl_t ttl, const char *data);
49 isc_result_t (*writeable_zone)(dns_view_t *view, const char *zone_name);
53 static const char *zone_prefixes[] = {
54 "CN=MicrosoftDNS,DC=DomainDnsZones",
55 "CN=MicrosoftDNS,DC=ForestDnsZones",
60 return the version of the API
62 _PUBLIC_ int dlz_version(unsigned int *flags)
64 return DLZ_DLOPEN_VERSION;
68 remember a helper function from the bind9 dlz_dlopen driver
70 static void b9_add_helper(struct dlz_bind9_data *state, const char *helper_name, void *ptr)
72 if (strcmp(helper_name, "log") == 0) {
75 if (strcmp(helper_name, "putrr") == 0) {
78 if (strcmp(helper_name, "putnamedrr") == 0) {
79 state->putnamedrr = ptr;
81 if (strcmp(helper_name, "writeable_zone") == 0) {
82 state->writeable_zone = ptr;
87 format a record for bind9
89 static bool b9_format(struct dlz_bind9_data *state,
91 struct dnsp_DnssrvRpcRecord *rec,
92 const char **type, const char **data)
97 *data = rec->data.ipv4;
102 *data = rec->data.ipv6;
107 *data = rec->data.cname;
112 *data = rec->data.txt;
117 *data = rec->data.ptr;
122 *data = talloc_asprintf(mem_ctx, "%u %u %u %s",
123 rec->data.srv.wPriority,
124 rec->data.srv.wWeight,
126 rec->data.srv.nameTarget);
131 *data = talloc_asprintf(mem_ctx, "%u %s",
132 rec->data.mx.wPriority,
133 rec->data.mx.nameTarget);
138 *data = talloc_asprintf(mem_ctx, "%s %s",
145 *data = rec->data.ns;
152 /* we need to fake the authoritative nameserver to
153 * point at ourselves. This is how AD DNS servers
154 * force clients to send updates to the right local DC
156 mname = talloc_asprintf(mem_ctx, "%s.%s",
157 lpcfg_netbios_name(state->lp), lpcfg_dnsdomain(state->lp));
161 mname = strlower_talloc(mem_ctx, mname);
166 state->soa_serial = rec->data.soa.serial;
168 *data = talloc_asprintf(mem_ctx, "%s %s %u %u %u %u %u",
171 rec->data.soa.serial,
172 rec->data.soa.refresh,
174 rec->data.soa.expire,
175 rec->data.soa.minimum);
180 state->log(ISC_LOG_ERROR, "samba b9_putrr: unhandled record type %u",
188 static const struct {
189 enum dns_record_type dns_type;
193 { DNS_TYPE_A, "A" , false},
194 { DNS_TYPE_AAAA, "AAAA" , false},
195 { DNS_TYPE_CNAME, "CNAME" , true},
196 { DNS_TYPE_TXT, "TXT" , false},
197 { DNS_TYPE_PTR, "PTR" , false},
198 { DNS_TYPE_SRV, "SRV" , false},
199 { DNS_TYPE_MX, "MX" , false},
200 { DNS_TYPE_HINFO, "HINFO" , false},
201 { DNS_TYPE_NS, "NS" , false},
202 { DNS_TYPE_SOA, "SOA" , true},
207 see if a DNS type is single valued
209 static bool b9_single_valued(enum dns_record_type dns_type)
212 for (i=0; i<ARRAY_SIZE(dns_typemap); i++) {
213 if (dns_typemap[i].dns_type == dns_type) {
214 return dns_typemap[i].single_valued;
221 see if a DNS type is single valued
223 static enum dns_record_type b9_dns_type(const char *type)
226 for (i=0; i<ARRAY_SIZE(dns_typemap); i++) {
227 if (strcasecmp(dns_typemap[i].typestr, type) == 0) {
228 return dns_typemap[i].dns_type;
231 return DNS_TYPE_ZERO;
235 #define DNS_PARSE_STR(ret, str, sep, saveptr) do { \
236 (ret) = strtok_r(str, sep, &saveptr); \
237 if ((ret) == NULL) return false; \
240 #define DNS_PARSE_UINT(ret, str, sep, saveptr) do { \
241 char *istr = strtok_r(str, sep, &saveptr); \
242 if ((istr) == NULL) return false; \
243 (ret) = strtoul(istr, NULL, 10); \
247 parse a record from bind9
249 static bool b9_parse(struct dlz_bind9_data *state,
250 const char *rdatastr,
251 struct dnsp_DnssrvRpcRecord *rec)
253 char *full_name, *dclass, *type;
254 char *str, *saveptr=NULL;
257 str = talloc_strdup(rec, rdatastr);
262 /* parse the SDLZ string form */
263 DNS_PARSE_STR(full_name, str, "\t", saveptr);
264 DNS_PARSE_UINT(rec->dwTtlSeconds, NULL, "\t", saveptr);
265 DNS_PARSE_STR(dclass, NULL, "\t", saveptr);
266 DNS_PARSE_STR(type, NULL, "\t", saveptr);
268 /* construct the record */
269 for (i=0; i<ARRAY_SIZE(dns_typemap); i++) {
270 if (strcasecmp(type, dns_typemap[i].typestr) == 0) {
271 rec->wType = dns_typemap[i].dns_type;
275 if (i == ARRAY_SIZE(dns_typemap)) {
276 state->log(ISC_LOG_ERROR, "samba_dlz: unsupported record type '%s' for '%s'",
281 switch (rec->wType) {
283 DNS_PARSE_STR(rec->data.ipv4, NULL, " ", saveptr);
287 DNS_PARSE_STR(rec->data.ipv6, NULL, " ", saveptr);
291 DNS_PARSE_STR(rec->data.cname, NULL, " ", saveptr);
295 DNS_PARSE_STR(rec->data.txt, NULL, "\t", saveptr);
299 DNS_PARSE_STR(rec->data.ptr, NULL, " ", saveptr);
303 DNS_PARSE_UINT(rec->data.srv.wPriority, NULL, " ", saveptr);
304 DNS_PARSE_UINT(rec->data.srv.wWeight, NULL, " ", saveptr);
305 DNS_PARSE_UINT(rec->data.srv.wPort, NULL, " ", saveptr);
306 DNS_PARSE_STR(rec->data.srv.nameTarget, NULL, " ", saveptr);
310 DNS_PARSE_UINT(rec->data.mx.wPriority, NULL, " ", saveptr);
311 DNS_PARSE_STR(rec->data.mx.nameTarget, NULL, " ", saveptr);
315 DNS_PARSE_STR(rec->data.hinfo.cpu, NULL, " ", saveptr);
316 DNS_PARSE_STR(rec->data.hinfo.os, NULL, " ", saveptr);
320 DNS_PARSE_STR(rec->data.ns, NULL, " ", saveptr);
324 DNS_PARSE_STR(rec->data.soa.mname, NULL, " ", saveptr);
325 DNS_PARSE_STR(rec->data.soa.rname, NULL, " ", saveptr);
326 DNS_PARSE_UINT(rec->data.soa.serial, NULL, " ", saveptr);
327 DNS_PARSE_UINT(rec->data.soa.refresh, NULL, " ", saveptr);
328 DNS_PARSE_UINT(rec->data.soa.retry, NULL, " ", saveptr);
329 DNS_PARSE_UINT(rec->data.soa.expire, NULL, " ", saveptr);
330 DNS_PARSE_UINT(rec->data.soa.minimum, NULL, " ", saveptr);
334 state->log(ISC_LOG_ERROR, "samba b9_parse: unhandled record type %u",
339 /* we should be at the end of the buffer now */
340 if (strtok_r(NULL, "\t ", &saveptr) != NULL) {
341 state->log(ISC_LOG_ERROR, "samba b9_parse: expected data at end of string for '%s'");
349 send a resource recond to bind9
351 static isc_result_t b9_putrr(struct dlz_bind9_data *state,
352 void *handle, struct dnsp_DnssrvRpcRecord *rec,
356 const char *type, *data;
357 TALLOC_CTX *tmp_ctx = talloc_new(state);
359 if (!b9_format(state, tmp_ctx, rec, &type, &data)) {
360 return ISC_R_FAILURE;
364 talloc_free(tmp_ctx);
365 return ISC_R_NOMEMORY;
370 for (i=0; types[i]; i++) {
371 if (strcmp(types[i], type) == 0) break;
373 if (types[i] == NULL) {
375 return ISC_R_SUCCESS;
379 result = state->putrr(handle, type, rec->dwTtlSeconds, data);
380 if (result != ISC_R_SUCCESS) {
381 state->log(ISC_LOG_ERROR, "Failed to put rr");
383 talloc_free(tmp_ctx);
389 send a named resource recond to bind9
391 static isc_result_t b9_putnamedrr(struct dlz_bind9_data *state,
392 void *handle, const char *name,
393 struct dnsp_DnssrvRpcRecord *rec)
396 const char *type, *data;
397 TALLOC_CTX *tmp_ctx = talloc_new(state);
399 if (!b9_format(state, tmp_ctx, rec, &type, &data)) {
400 return ISC_R_FAILURE;
404 talloc_free(tmp_ctx);
405 return ISC_R_NOMEMORY;
408 result = state->putnamedrr(handle, name, type, rec->dwTtlSeconds, data);
409 if (result != ISC_R_SUCCESS) {
410 state->log(ISC_LOG_ERROR, "Failed to put named rr '%s'", name);
412 talloc_free(tmp_ctx);
423 static isc_result_t parse_options(struct dlz_bind9_data *state,
424 unsigned int argc, char *argv[],
425 struct b9_options *options)
429 struct poptOption long_options[] = {
430 { "url", 'H', POPT_ARG_STRING, &options->url, 0, "database URL", "URL" },
433 struct poptOption **popt_options;
436 fault_setup_disable();
438 popt_options = ldb_module_popt_options(state->samdb);
439 (*popt_options) = long_options;
441 ret = ldb_modules_hook(state->samdb, LDB_MODULE_HOOK_CMDLINE_OPTIONS);
442 if (ret != LDB_SUCCESS) {
443 state->log(ISC_LOG_ERROR, "dlz samba: failed cmdline hook");
444 return ISC_R_FAILURE;
447 pc = poptGetContext("dlz_bind9", argc, (const char **)argv, *popt_options,
448 POPT_CONTEXT_KEEP_FIRST);
450 while ((opt = poptGetNextOpt(pc)) != -1) {
453 state->log(ISC_LOG_ERROR, "dlz samba: Invalid option %s: %s",
454 poptBadOption(pc, 0), poptStrerror(opt));
455 return ISC_R_FAILURE;
459 ret = ldb_modules_hook(state->samdb, LDB_MODULE_HOOK_CMDLINE_PRECONNECT);
460 if (ret != LDB_SUCCESS) {
461 state->log(ISC_LOG_ERROR, "dlz samba: failed cmdline preconnect");
462 return ISC_R_FAILURE;
465 return ISC_R_SUCCESS;
470 called to initialise the driver
472 _PUBLIC_ isc_result_t dlz_create(const char *dlzname,
473 unsigned int argc, char *argv[],
476 struct dlz_bind9_data *state;
477 const char *helper_name;
483 struct b9_options options;
485 ZERO_STRUCT(options);
487 state = talloc_zero(NULL, struct dlz_bind9_data);
489 return ISC_R_NOMEMORY;
492 tmp_ctx = talloc_new(state);
494 /* fill in the helper functions */
495 va_start(ap, dbdata);
496 while ((helper_name = va_arg(ap, const char *)) != NULL) {
497 b9_add_helper(state, helper_name, va_arg(ap, void*));
501 state->ev_ctx = s4_event_context_init(state);
502 if (state->ev_ctx == NULL) {
503 result = ISC_R_NOMEMORY;
507 state->samdb = ldb_init(state, state->ev_ctx);
508 if (state->samdb == NULL) {
509 state->log(ISC_LOG_ERROR, "samba_dlz: Failed to create ldb");
510 result = ISC_R_FAILURE;
514 result = parse_options(state, argc, argv, &options);
515 if (result != ISC_R_SUCCESS) {
519 state->lp = loadparm_init_global(true);
520 if (state->lp == NULL) {
521 result = ISC_R_NOMEMORY;
525 if (options.url == NULL) {
526 options.url = talloc_asprintf(tmp_ctx, "ldapi://%s",
527 private_path(tmp_ctx, state->lp, "ldap_priv/ldapi"));
528 if (options.url == NULL) {
529 result = ISC_R_NOMEMORY;
534 ret = ldb_connect(state->samdb, options.url, 0, NULL);
536 state->log(ISC_LOG_ERROR, "samba_dlz: Failed to connect to %s - %s",
537 options.url, ldb_errstring(state->samdb));
538 result = ISC_R_FAILURE;
542 ret = ldb_modules_hook(state->samdb, LDB_MODULE_HOOK_CMDLINE_POSTCONNECT);
543 if (ret != LDB_SUCCESS) {
544 state->log(ISC_LOG_ERROR, "samba_dlz: Failed postconnect for %s - %s",
545 options.url, ldb_errstring(state->samdb));
546 result = ISC_R_FAILURE;
550 dn = ldb_get_default_basedn(state->samdb);
552 state->log(ISC_LOG_ERROR, "samba_dlz: Unable to get basedn for %s - %s",
553 options.url, ldb_errstring(state->samdb));
554 result = ISC_R_FAILURE;
558 state->log(ISC_LOG_INFO, "samba_dlz: started for DN %s",
559 ldb_dn_get_linearized(dn));
563 talloc_free(tmp_ctx);
564 return ISC_R_SUCCESS;
574 _PUBLIC_ void dlz_destroy(void *dbdata)
576 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
577 state->log(ISC_LOG_INFO, "samba_dlz: shutting down");
583 return the base DN for a zone
585 static isc_result_t b9_find_zone_dn(struct dlz_bind9_data *state, const char *zone_name,
586 TALLOC_CTX *mem_ctx, struct ldb_dn **zone_dn)
589 TALLOC_CTX *tmp_ctx = talloc_new(state);
590 const char *attrs[] = { NULL };
593 for (i=0; zone_prefixes[i]; i++) {
595 struct ldb_result *res;
597 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
599 talloc_free(tmp_ctx);
600 return ISC_R_NOMEMORY;
603 if (!ldb_dn_add_child_fmt(dn, "DC=%s,%s", zone_name, zone_prefixes[i])) {
604 talloc_free(tmp_ctx);
605 return ISC_R_NOMEMORY;
608 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsZone");
609 if (ret == LDB_SUCCESS) {
610 if (zone_dn != NULL) {
611 *zone_dn = talloc_steal(mem_ctx, dn);
613 talloc_free(tmp_ctx);
614 return ISC_R_SUCCESS;
619 talloc_free(tmp_ctx);
620 return ISC_R_NOTFOUND;
625 return the DN for a name. The record does not need to exist, but the
628 static isc_result_t b9_find_name_dn(struct dlz_bind9_data *state, const char *name,
629 TALLOC_CTX *mem_ctx, struct ldb_dn **dn)
633 /* work through the name piece by piece, until we find a zone */
636 result = b9_find_zone_dn(state, p, mem_ctx, dn);
637 if (result == ISC_R_SUCCESS) {
638 /* we found a zone, now extend the DN to get
643 ret = ldb_dn_add_child_fmt(*dn, "DC=@");
645 ret = ldb_dn_add_child_fmt(*dn, "DC=%.*s", (int)(p-name)-1, name);
649 return ISC_R_NOMEMORY;
651 return ISC_R_SUCCESS;
659 return ISC_R_NOTFOUND;
664 see if we handle a given zone
666 _PUBLIC_ isc_result_t dlz_findzonedb(void *dbdata, const char *name)
668 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
669 return b9_find_zone_dn(state, name, NULL, NULL);
676 static isc_result_t dlz_lookup_types(struct dlz_bind9_data *state,
677 const char *zone, const char *name,
678 dns_sdlzlookup_t *lookup,
681 TALLOC_CTX *tmp_ctx = talloc_new(state);
682 const char *attrs[] = { "dnsRecord", NULL };
683 int ret = LDB_SUCCESS, i;
684 struct ldb_result *res;
685 struct ldb_message_element *el;
688 for (i=0; zone_prefixes[i]; i++) {
689 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
691 talloc_free(tmp_ctx);
692 return ISC_R_NOMEMORY;
695 if (!ldb_dn_add_child_fmt(dn, "DC=%s,DC=%s,%s", name, zone, zone_prefixes[i])) {
696 talloc_free(tmp_ctx);
697 return ISC_R_NOMEMORY;
700 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
701 attrs, "objectClass=dnsNode");
702 if (ret == LDB_SUCCESS) {
706 if (ret != LDB_SUCCESS) {
707 talloc_free(tmp_ctx);
708 return ISC_R_NOTFOUND;
711 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
712 if (el == NULL || el->num_values == 0) {
713 talloc_free(tmp_ctx);
714 return ISC_R_NOTFOUND;
717 for (i=0; i<el->num_values; i++) {
718 struct dnsp_DnssrvRpcRecord rec;
719 enum ndr_err_code ndr_err;
722 ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec,
723 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
724 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
725 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
726 ldb_dn_get_linearized(dn));
727 talloc_free(tmp_ctx);
728 return ISC_R_FAILURE;
731 result = b9_putrr(state, lookup, &rec, types);
732 if (result != ISC_R_SUCCESS) {
733 talloc_free(tmp_ctx);
738 talloc_free(tmp_ctx);
739 return ISC_R_SUCCESS;
745 _PUBLIC_ isc_result_t dlz_lookup(const char *zone, const char *name,
746 void *dbdata, dns_sdlzlookup_t *lookup)
748 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
749 return dlz_lookup_types(state, zone, name, lookup, NULL);
754 see if a zone transfer is allowed
756 _PUBLIC_ isc_result_t dlz_allowzonexfr(void *dbdata, const char *name, const char *client)
758 /* just say yes for all our zones for now */
759 return dlz_findzonedb(dbdata, name);
763 perform a zone transfer
765 _PUBLIC_ isc_result_t dlz_allnodes(const char *zone, void *dbdata,
766 dns_sdlzallnodes_t *allnodes)
768 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
769 const char *attrs[] = { "dnsRecord", NULL };
770 int ret = LDB_SUCCESS, i, j;
772 struct ldb_result *res;
773 TALLOC_CTX *tmp_ctx = talloc_new(state);
775 for (i=0; zone_prefixes[i]; i++) {
776 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
778 talloc_free(tmp_ctx);
779 return ISC_R_NOMEMORY;
782 if (!ldb_dn_add_child_fmt(dn, "DC=%s,%s", zone, zone_prefixes[i])) {
783 talloc_free(tmp_ctx);
784 return ISC_R_NOMEMORY;
787 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
788 attrs, "objectClass=dnsNode");
789 if (ret == LDB_SUCCESS) {
793 if (ret != LDB_SUCCESS) {
794 talloc_free(tmp_ctx);
795 return ISC_R_NOTFOUND;
798 for (i=0; i<res->count; i++) {
799 struct ldb_message_element *el;
800 TALLOC_CTX *el_ctx = talloc_new(tmp_ctx);
801 const char *rdn, *name;
802 const struct ldb_val *v;
804 el = ldb_msg_find_element(res->msgs[i], "dnsRecord");
805 if (el == NULL || el->num_values == 0) {
806 state->log(ISC_LOG_INFO, "failed to find dnsRecord for %s",
807 ldb_dn_get_linearized(dn));
812 v = ldb_dn_get_rdn_val(res->msgs[i]->dn);
814 state->log(ISC_LOG_INFO, "failed to find RDN for %s",
815 ldb_dn_get_linearized(dn));
820 rdn = talloc_strndup(el_ctx, (char *)v->data, v->length);
822 talloc_free(tmp_ctx);
823 return ISC_R_NOMEMORY;
826 if (strcmp(rdn, "@") == 0) {
829 name = talloc_asprintf(el_ctx, "%s.%s", rdn, zone);
832 talloc_free(tmp_ctx);
833 return ISC_R_NOMEMORY;
836 for (j=0; j<el->num_values; j++) {
837 struct dnsp_DnssrvRpcRecord rec;
838 enum ndr_err_code ndr_err;
841 ndr_err = ndr_pull_struct_blob(&el->values[j], el_ctx, &rec,
842 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
843 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
844 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
845 ldb_dn_get_linearized(dn));
849 result = b9_putnamedrr(state, allnodes, name, &rec);
850 if (result != ISC_R_SUCCESS) {
856 talloc_free(tmp_ctx);
858 return ISC_R_SUCCESS;
865 _PUBLIC_ isc_result_t dlz_newversion(const char *zone, void *dbdata, void **versionp)
867 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
869 state->log(ISC_LOG_INFO, "samba_dlz: starting transaction on zone %s", zone);
871 if (state->transaction_token != NULL) {
872 state->log(ISC_LOG_INFO, "samba_dlz: transaction already started for zone %s", zone);
873 return ISC_R_FAILURE;
876 state->transaction_token = talloc_zero(state, int);
877 if (state->transaction_token == NULL) {
878 return ISC_R_NOMEMORY;
881 if (ldb_transaction_start(state->samdb) != LDB_SUCCESS) {
882 state->log(ISC_LOG_INFO, "samba_dlz: failed to start a transaction for zone %s", zone);
883 talloc_free(state->transaction_token);
884 state->transaction_token = NULL;
885 return ISC_R_FAILURE;
888 *versionp = (void *)state->transaction_token;
890 return ISC_R_SUCCESS;
896 _PUBLIC_ void dlz_closeversion(const char *zone, isc_boolean_t commit,
897 void *dbdata, void **versionp)
899 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
901 if (state->transaction_token != (int *)*versionp) {
902 state->log(ISC_LOG_INFO, "samba_dlz: transaction not started for zone %s", zone);
907 if (ldb_transaction_commit(state->samdb) != LDB_SUCCESS) {
908 state->log(ISC_LOG_INFO, "samba_dlz: failed to commit a transaction for zone %s", zone);
911 state->log(ISC_LOG_INFO, "samba_dlz: committed transaction on zone %s", zone);
913 if (ldb_transaction_cancel(state->samdb) != LDB_SUCCESS) {
914 state->log(ISC_LOG_INFO, "samba_dlz: failed to cancel a transaction for zone %s", zone);
917 state->log(ISC_LOG_INFO, "samba_dlz: cancelling transaction on zone %s", zone);
920 talloc_free(state->transaction_token);
921 state->transaction_token = NULL;
927 see if there is a SOA record for a zone
929 static bool b9_has_soa(struct dlz_bind9_data *state, struct ldb_dn *dn, const char *zone)
931 const char *attrs[] = { "dnsRecord", NULL };
932 struct ldb_result *res;
933 struct ldb_message_element *el;
934 TALLOC_CTX *tmp_ctx = talloc_new(state);
937 if (!ldb_dn_add_child_fmt(dn, "DC=@,DC=%s", zone)) {
938 talloc_free(tmp_ctx);
942 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
943 attrs, "objectClass=dnsNode");
944 if (ret != LDB_SUCCESS) {
945 talloc_free(tmp_ctx);
949 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
951 talloc_free(tmp_ctx);
954 for (i=0; i<el->num_values; i++) {
955 struct dnsp_DnssrvRpcRecord rec;
956 enum ndr_err_code ndr_err;
958 ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec,
959 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
960 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
963 if (rec.wType == DNS_TYPE_SOA) {
964 talloc_free(tmp_ctx);
969 talloc_free(tmp_ctx);
974 configure a writeable zone
976 _PUBLIC_ isc_result_t dlz_configure(dns_view_t *view, void *dbdata)
978 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
983 state->log(ISC_LOG_INFO, "samba_dlz: starting configure");
984 if (state->writeable_zone == NULL) {
985 state->log(ISC_LOG_INFO, "samba_dlz: no writeable_zone method available");
986 return ISC_R_FAILURE;
989 tmp_ctx = talloc_new(state);
991 for (i=0; zone_prefixes[i]; i++) {
992 const char *attrs[] = { "name", NULL };
994 struct ldb_result *res;
996 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
998 talloc_free(tmp_ctx);
999 return ISC_R_NOMEMORY;
1002 if (!ldb_dn_add_child_fmt(dn, "%s", zone_prefixes[i])) {
1003 talloc_free(tmp_ctx);
1004 return ISC_R_NOMEMORY;
1007 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
1008 attrs, "objectClass=dnsZone");
1009 if (ret != LDB_SUCCESS) {
1013 for (j=0; j<res->count; j++) {
1014 isc_result_t result;
1015 const char *zone = ldb_msg_find_attr_as_string(res->msgs[j], "name", NULL);
1019 if (!b9_has_soa(state, dn, zone)) {
1022 result = state->writeable_zone(view, zone);
1023 if (result != ISC_R_SUCCESS) {
1024 state->log(ISC_LOG_ERROR, "samba_dlz: Failed to configure zone '%s'",
1026 talloc_free(tmp_ctx);
1029 state->log(ISC_LOG_INFO, "samba_dlz: configured writeable zone '%s'", zone);
1033 talloc_free(tmp_ctx);
1034 return ISC_R_SUCCESS;
1038 authorize a zone update
1040 _PUBLIC_ isc_boolean_t dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr,
1041 const char *type, const char *key, uint32_t keydatalen, uint8_t *keydata,
1044 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1046 state->log(ISC_LOG_INFO, "samba_dlz: allowing update of signer=%s name=%s tcpaddr=%s type=%s key=%s keydatalen=%u",
1047 signer, name, tcpaddr, type, key, keydatalen);
1055 static isc_result_t b9_add_record(struct dlz_bind9_data *state, const char *name,
1057 struct dnsp_DnssrvRpcRecord *rec)
1059 struct ldb_message *msg;
1060 enum ndr_err_code ndr_err;
1064 msg = ldb_msg_new(rec);
1066 return ISC_R_NOMEMORY;
1069 ret = ldb_msg_add_string(msg, "objectClass", "dnsNode");
1070 if (ret != LDB_SUCCESS) {
1071 return ISC_R_FAILURE;
1074 ndr_err = ndr_push_struct_blob(&v, rec, rec, (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
1075 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1076 return ISC_R_FAILURE;
1078 ret = ldb_msg_add_value(msg, "dnsRecord", &v, NULL);
1079 if (ret != LDB_SUCCESS) {
1080 return ISC_R_FAILURE;
1083 ret = ldb_add(state->samdb, msg);
1084 if (ret != LDB_SUCCESS) {
1085 return ISC_R_FAILURE;
1088 return ISC_R_SUCCESS;
1092 see if two DNS names are the same
1094 static bool dns_name_equal(const char *name1, const char *name2)
1096 size_t len1 = strlen(name1);
1097 size_t len2 = strlen(name2);
1098 if (name1[len1-1] == '.') len1--;
1099 if (name2[len2-1] == '.') len2--;
1103 return strncasecmp_m(name1, name2, len1) == 0;
1108 see if two dns records match
1110 static bool b9_record_match(struct dlz_bind9_data *state,
1111 struct dnsp_DnssrvRpcRecord *rec1, struct dnsp_DnssrvRpcRecord *rec2)
1113 if (rec1->wType != rec2->wType) {
1116 /* see if this type is single valued */
1117 if (b9_single_valued(rec1->wType)) {
1121 /* see if the data matches */
1122 switch (rec1->wType) {
1124 return strcmp(rec1->data.ipv4, rec2->data.ipv4) == 0;
1126 return strcmp(rec1->data.ipv6, rec2->data.ipv6) == 0;
1127 case DNS_TYPE_CNAME:
1128 return dns_name_equal(rec1->data.cname, rec2->data.cname);
1130 return strcmp(rec1->data.txt, rec2->data.txt) == 0;
1132 return strcmp(rec1->data.ptr, rec2->data.ptr) == 0;
1134 return dns_name_equal(rec1->data.ns, rec2->data.ns);
1137 return rec1->data.srv.wPriority == rec2->data.srv.wPriority &&
1138 rec1->data.srv.wWeight == rec2->data.srv.wWeight &&
1139 rec1->data.srv.wPort == rec2->data.srv.wPort &&
1140 dns_name_equal(rec1->data.srv.nameTarget, rec2->data.srv.nameTarget);
1143 return rec1->data.mx.wPriority == rec2->data.mx.wPriority &&
1144 dns_name_equal(rec1->data.mx.nameTarget, rec2->data.mx.nameTarget);
1146 case DNS_TYPE_HINFO:
1147 return strcmp(rec1->data.hinfo.cpu, rec2->data.hinfo.cpu) == 0 &&
1148 strcmp(rec1->data.hinfo.os, rec2->data.hinfo.os) == 0;
1151 return dns_name_equal(rec1->data.soa.mname, rec2->data.soa.mname) &&
1152 dns_name_equal(rec1->data.soa.rname, rec2->data.soa.rname) &&
1153 rec1->data.soa.serial == rec2->data.soa.serial &&
1154 rec1->data.soa.refresh == rec2->data.soa.refresh &&
1155 rec1->data.soa.retry == rec2->data.soa.retry &&
1156 rec1->data.soa.expire == rec2->data.soa.expire &&
1157 rec1->data.soa.minimum == rec2->data.soa.minimum;
1159 state->log(ISC_LOG_ERROR, "samba b9_putrr: unhandled record type %u",
1169 add or modify a rdataset
1171 _PUBLIC_ isc_result_t dlz_addrdataset(const char *name, const char *rdatastr, void *dbdata, void *version)
1173 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1174 struct dnsp_DnssrvRpcRecord *rec;
1176 isc_result_t result;
1177 struct ldb_result *res;
1178 const char *attrs[] = { "dnsRecord", NULL };
1180 struct ldb_message_element *el;
1181 enum ndr_err_code ndr_err;
1184 if (state->transaction_token != (void*)version) {
1185 state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version");
1186 return ISC_R_FAILURE;
1189 rec = talloc_zero(state, struct dnsp_DnssrvRpcRecord);
1191 return ISC_R_NOMEMORY;
1194 unix_to_nt_time(&t, time(NULL));
1195 t /= 10*1000*1000; /* convert to seconds (NT time is in 100ns units) */
1196 t /= 3600; /* convert to hours */
1198 rec->rank = DNS_RANK_ZONE;
1199 rec->dwSerial = state->soa_serial;
1200 rec->dwTimeStamp = (uint32_t)t;
1202 if (!b9_parse(state, rdatastr, rec)) {
1203 state->log(ISC_LOG_INFO, "samba_dlz: failed to parse rdataset '%s'", rdatastr);
1205 return ISC_R_FAILURE;
1208 /* find the DN of the record */
1209 result = b9_find_name_dn(state, name, rec, &dn);
1210 if (result != ISC_R_SUCCESS) {
1215 /* get any existing records */
1216 ret = ldb_search(state->samdb, rec, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode");
1217 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1218 result = b9_add_record(state, name, dn, rec);
1220 if (result == ISC_R_SUCCESS) {
1221 state->log(ISC_LOG_ERROR, "samba_dlz: added %s %s", name, rdatastr);
1226 /* there are existing records. We need to see if this will
1227 * replace a record or add to it
1229 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
1231 state->log(ISC_LOG_ERROR, "samba_dlz: no dnsRecord attribute for %s",
1232 ldb_dn_get_linearized(dn));
1234 return ISC_R_FAILURE;
1237 for (i=0; i<el->num_values; i++) {
1238 struct dnsp_DnssrvRpcRecord rec2;
1240 ndr_err = ndr_pull_struct_blob(&el->values[i], rec, &rec2,
1241 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
1242 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1243 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
1244 ldb_dn_get_linearized(dn));
1246 return ISC_R_FAILURE;
1249 if (b9_record_match(state, rec, &rec2)) {
1253 if (i == el->num_values) {
1254 /* adding a new value */
1255 el->values = talloc_realloc(el, el->values, struct ldb_val, el->num_values+1);
1256 if (el->values == NULL) {
1258 return ISC_R_NOMEMORY;
1263 ndr_err = ndr_push_struct_blob(&el->values[i], rec, rec,
1264 (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
1265 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1266 state->log(ISC_LOG_ERROR, "samba_dlz: failed to push dnsRecord for %s",
1267 ldb_dn_get_linearized(dn));
1269 return ISC_R_FAILURE;
1272 /* modify the record */
1273 el->flags = LDB_FLAG_MOD_REPLACE;
1274 ret = ldb_modify(state->samdb, res->msgs[0]);
1275 if (ret != LDB_SUCCESS) {
1276 state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s",
1277 ldb_dn_get_linearized(dn), ldb_errstring(state->samdb));
1279 return ISC_R_FAILURE;
1282 state->log(ISC_LOG_INFO, "samba_dlz: added rdataset %s '%s'", name, rdatastr);
1285 return ISC_R_SUCCESS;
1291 _PUBLIC_ isc_result_t dlz_subrdataset(const char *name, const char *rdatastr, void *dbdata, void *version)
1293 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1294 struct dnsp_DnssrvRpcRecord *rec;
1296 isc_result_t result;
1297 struct ldb_result *res;
1298 const char *attrs[] = { "dnsRecord", NULL };
1300 struct ldb_message_element *el;
1301 enum ndr_err_code ndr_err;
1303 if (state->transaction_token != (void*)version) {
1304 state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version");
1305 return ISC_R_FAILURE;
1308 rec = talloc_zero(state, struct dnsp_DnssrvRpcRecord);
1310 return ISC_R_NOMEMORY;
1313 if (!b9_parse(state, rdatastr, rec)) {
1314 state->log(ISC_LOG_INFO, "samba_dlz: failed to parse rdataset '%s'", rdatastr);
1316 return ISC_R_FAILURE;
1319 /* find the DN of the record */
1320 result = b9_find_name_dn(state, name, rec, &dn);
1321 if (result != ISC_R_SUCCESS) {
1326 /* get the existing records */
1327 ret = ldb_search(state->samdb, rec, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode");
1328 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1330 return ISC_R_NOTFOUND;
1333 /* there are existing records. We need to see if any match
1335 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
1336 if (el == NULL || el->num_values == 0) {
1337 state->log(ISC_LOG_ERROR, "samba_dlz: no dnsRecord attribute for %s",
1338 ldb_dn_get_linearized(dn));
1340 return ISC_R_FAILURE;
1343 for (i=0; i<el->num_values; i++) {
1344 struct dnsp_DnssrvRpcRecord rec2;
1346 ndr_err = ndr_pull_struct_blob(&el->values[i], rec, &rec2,
1347 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
1348 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1349 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
1350 ldb_dn_get_linearized(dn));
1352 return ISC_R_FAILURE;
1355 if (b9_record_match(state, rec, &rec2)) {
1359 if (i == el->num_values) {
1361 return ISC_R_NOTFOUND;
1364 if (i < el->num_values-1) {
1365 memmove(&el->values[i], &el->values[i+1], sizeof(el->values[0])*((el->num_values-1)-i));
1369 if (el->num_values == 0) {
1370 /* delete the record */
1371 ret = ldb_delete(state->samdb, dn);
1373 /* modify the record */
1374 el->flags = LDB_FLAG_MOD_REPLACE;
1375 ret = ldb_modify(state->samdb, res->msgs[0]);
1377 if (ret != LDB_SUCCESS) {
1378 state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s",
1379 ldb_dn_get_linearized(dn), ldb_errstring(state->samdb));
1381 return ISC_R_FAILURE;
1384 state->log(ISC_LOG_INFO, "samba_dlz: subtracted rdataset %s '%s'", name, rdatastr);
1387 return ISC_R_SUCCESS;
1392 delete all records of the given type
1394 _PUBLIC_ isc_result_t dlz_delrdataset(const char *name, const char *type, void *dbdata, void *version)
1396 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1397 TALLOC_CTX *tmp_ctx;
1399 isc_result_t result;
1400 struct ldb_result *res;
1401 const char *attrs[] = { "dnsRecord", NULL };
1403 struct ldb_message_element *el;
1404 enum ndr_err_code ndr_err;
1405 enum dns_record_type dns_type;
1408 if (state->transaction_token != (void*)version) {
1409 state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version");
1410 return ISC_R_FAILURE;
1413 dns_type = b9_dns_type(type);
1414 if (dns_type == DNS_TYPE_ZERO) {
1415 state->log(ISC_LOG_INFO, "samba_dlz: bad dns type %s in delete", type);
1416 return ISC_R_FAILURE;
1419 tmp_ctx = talloc_new(state);
1421 /* find the DN of the record */
1422 result = b9_find_name_dn(state, name, tmp_ctx, &dn);
1423 if (result != ISC_R_SUCCESS) {
1424 talloc_free(tmp_ctx);
1428 /* get the existing records */
1429 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode");
1430 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1431 talloc_free(tmp_ctx);
1432 return ISC_R_NOTFOUND;
1435 /* there are existing records. We need to see if any match the type
1437 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
1438 if (el == NULL || el->num_values == 0) {
1439 talloc_free(tmp_ctx);
1440 return ISC_R_NOTFOUND;
1443 for (i=0; i<el->num_values; i++) {
1444 struct dnsp_DnssrvRpcRecord rec2;
1446 ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec2,
1447 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
1448 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1449 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
1450 ldb_dn_get_linearized(dn));
1451 talloc_free(tmp_ctx);
1452 return ISC_R_FAILURE;
1455 if (dns_type == rec2.wType) {
1456 if (i < el->num_values-1) {
1457 memmove(&el->values[i], &el->values[i+1],
1458 sizeof(el->values[0])*((el->num_values-1)-i));
1467 talloc_free(tmp_ctx);
1468 return ISC_R_FAILURE;
1471 if (el->num_values == 0) {
1472 /* delete the record */
1473 ret = ldb_delete(state->samdb, dn);
1475 /* modify the record */
1476 el->flags = LDB_FLAG_MOD_REPLACE;
1477 ret = ldb_modify(state->samdb, res->msgs[0]);
1479 if (ret != LDB_SUCCESS) {
1480 state->log(ISC_LOG_ERROR, "samba_dlz: failed to delete type %s in %s - %s",
1481 type, ldb_dn_get_linearized(dn), ldb_errstring(state->samdb));
1482 talloc_free(tmp_ctx);
1483 return ISC_R_FAILURE;
1486 state->log(ISC_LOG_INFO, "samba_dlz: deleted rdataset %s of type %s", name, type);
1488 talloc_free(tmp_ctx);
1489 return ISC_R_SUCCESS;