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",
56 "CN=MicrosoftDNS,CN=System",
61 return the version of the API
63 _PUBLIC_ int dlz_version(unsigned int *flags)
65 return DLZ_DLOPEN_VERSION;
69 remember a helper function from the bind9 dlz_dlopen driver
71 static void b9_add_helper(struct dlz_bind9_data *state, const char *helper_name, void *ptr)
73 if (strcmp(helper_name, "log") == 0) {
76 if (strcmp(helper_name, "putrr") == 0) {
79 if (strcmp(helper_name, "putnamedrr") == 0) {
80 state->putnamedrr = ptr;
82 if (strcmp(helper_name, "writeable_zone") == 0) {
83 state->writeable_zone = ptr;
88 format a record for bind9
90 static bool b9_format(struct dlz_bind9_data *state,
92 struct dnsp_DnssrvRpcRecord *rec,
93 const char **type, const char **data)
98 *data = rec->data.ipv4;
103 *data = rec->data.ipv6;
108 *data = rec->data.cname;
113 *data = rec->data.txt;
118 *data = rec->data.ptr;
123 *data = talloc_asprintf(mem_ctx, "%u %u %u %s",
124 rec->data.srv.wPriority,
125 rec->data.srv.wWeight,
127 rec->data.srv.nameTarget);
132 *data = talloc_asprintf(mem_ctx, "%u %s",
133 rec->data.mx.wPriority,
134 rec->data.mx.nameTarget);
139 *data = talloc_asprintf(mem_ctx, "%s %s",
146 *data = rec->data.ns;
153 /* we need to fake the authoritative nameserver to
154 * point at ourselves. This is how AD DNS servers
155 * force clients to send updates to the right local DC
157 mname = talloc_asprintf(mem_ctx, "%s.%s",
158 lpcfg_netbios_name(state->lp), lpcfg_dnsdomain(state->lp));
162 mname = strlower_talloc(mem_ctx, mname);
167 state->soa_serial = rec->data.soa.serial;
169 *data = talloc_asprintf(mem_ctx, "%s %s %u %u %u %u %u",
172 rec->data.soa.serial,
173 rec->data.soa.refresh,
175 rec->data.soa.expire,
176 rec->data.soa.minimum);
181 state->log(ISC_LOG_ERROR, "samba b9_putrr: unhandled record type %u",
189 static const struct {
190 enum dns_record_type dns_type;
194 { DNS_TYPE_A, "A" , false},
195 { DNS_TYPE_AAAA, "AAAA" , false},
196 { DNS_TYPE_CNAME, "CNAME" , true},
197 { DNS_TYPE_TXT, "TXT" , false},
198 { DNS_TYPE_PTR, "PTR" , false},
199 { DNS_TYPE_SRV, "SRV" , false},
200 { DNS_TYPE_MX, "MX" , false},
201 { DNS_TYPE_HINFO, "HINFO" , false},
202 { DNS_TYPE_NS, "NS" , false},
203 { DNS_TYPE_SOA, "SOA" , true},
208 see if a DNS type is single valued
210 static bool b9_single_valued(enum dns_record_type dns_type)
213 for (i=0; i<ARRAY_SIZE(dns_typemap); i++) {
214 if (dns_typemap[i].dns_type == dns_type) {
215 return dns_typemap[i].single_valued;
222 see if a DNS type is single valued
224 static bool b9_dns_type(const char *type, enum dns_record_type *dtype)
227 for (i=0; i<ARRAY_SIZE(dns_typemap); i++) {
228 if (strcasecmp(dns_typemap[i].typestr, type) == 0) {
229 *dtype = dns_typemap[i].dns_type;
237 #define DNS_PARSE_STR(ret, str, sep, saveptr) do { \
238 (ret) = strtok_r(str, sep, &saveptr); \
239 if ((ret) == NULL) return false; \
242 #define DNS_PARSE_UINT(ret, str, sep, saveptr) do { \
243 char *istr = strtok_r(str, sep, &saveptr); \
244 if ((istr) == NULL) return false; \
245 (ret) = strtoul(istr, NULL, 10); \
249 parse a record from bind9
251 static bool b9_parse(struct dlz_bind9_data *state,
252 const char *rdatastr,
253 struct dnsp_DnssrvRpcRecord *rec)
255 char *full_name, *dclass, *type;
256 char *str, *saveptr=NULL;
259 str = talloc_strdup(rec, rdatastr);
264 /* parse the SDLZ string form */
265 DNS_PARSE_STR(full_name, str, "\t", saveptr);
266 DNS_PARSE_UINT(rec->dwTtlSeconds, NULL, "\t", saveptr);
267 DNS_PARSE_STR(dclass, NULL, "\t", saveptr);
268 DNS_PARSE_STR(type, NULL, "\t", saveptr);
270 /* construct the record */
271 for (i=0; i<ARRAY_SIZE(dns_typemap); i++) {
272 if (strcasecmp(type, dns_typemap[i].typestr) == 0) {
273 rec->wType = dns_typemap[i].dns_type;
277 if (i == ARRAY_SIZE(dns_typemap)) {
278 state->log(ISC_LOG_ERROR, "samba_dlz: unsupported record type '%s' for '%s'",
283 switch (rec->wType) {
285 DNS_PARSE_STR(rec->data.ipv4, NULL, " ", saveptr);
289 DNS_PARSE_STR(rec->data.ipv6, NULL, " ", saveptr);
293 DNS_PARSE_STR(rec->data.cname, NULL, " ", saveptr);
297 DNS_PARSE_STR(rec->data.txt, NULL, "\t", saveptr);
301 DNS_PARSE_STR(rec->data.ptr, NULL, " ", saveptr);
305 DNS_PARSE_UINT(rec->data.srv.wPriority, NULL, " ", saveptr);
306 DNS_PARSE_UINT(rec->data.srv.wWeight, NULL, " ", saveptr);
307 DNS_PARSE_UINT(rec->data.srv.wPort, NULL, " ", saveptr);
308 DNS_PARSE_STR(rec->data.srv.nameTarget, NULL, " ", saveptr);
312 DNS_PARSE_UINT(rec->data.mx.wPriority, NULL, " ", saveptr);
313 DNS_PARSE_STR(rec->data.mx.nameTarget, NULL, " ", saveptr);
317 DNS_PARSE_STR(rec->data.hinfo.cpu, NULL, " ", saveptr);
318 DNS_PARSE_STR(rec->data.hinfo.os, NULL, " ", saveptr);
322 DNS_PARSE_STR(rec->data.ns, NULL, " ", saveptr);
326 DNS_PARSE_STR(rec->data.soa.mname, NULL, " ", saveptr);
327 DNS_PARSE_STR(rec->data.soa.rname, NULL, " ", saveptr);
328 DNS_PARSE_UINT(rec->data.soa.serial, NULL, " ", saveptr);
329 DNS_PARSE_UINT(rec->data.soa.refresh, NULL, " ", saveptr);
330 DNS_PARSE_UINT(rec->data.soa.retry, NULL, " ", saveptr);
331 DNS_PARSE_UINT(rec->data.soa.expire, NULL, " ", saveptr);
332 DNS_PARSE_UINT(rec->data.soa.minimum, NULL, " ", saveptr);
336 state->log(ISC_LOG_ERROR, "samba b9_parse: unhandled record type %u",
341 /* we should be at the end of the buffer now */
342 if (strtok_r(NULL, "\t ", &saveptr) != NULL) {
343 state->log(ISC_LOG_ERROR, "samba b9_parse: expected data at end of string for '%s'");
351 send a resource recond to bind9
353 static isc_result_t b9_putrr(struct dlz_bind9_data *state,
354 void *handle, struct dnsp_DnssrvRpcRecord *rec,
358 const char *type, *data;
359 TALLOC_CTX *tmp_ctx = talloc_new(state);
361 if (!b9_format(state, tmp_ctx, rec, &type, &data)) {
362 return ISC_R_FAILURE;
366 talloc_free(tmp_ctx);
367 return ISC_R_NOMEMORY;
372 for (i=0; types[i]; i++) {
373 if (strcmp(types[i], type) == 0) break;
375 if (types[i] == NULL) {
377 return ISC_R_SUCCESS;
381 result = state->putrr(handle, type, rec->dwTtlSeconds, data);
382 if (result != ISC_R_SUCCESS) {
383 state->log(ISC_LOG_ERROR, "Failed to put rr");
385 talloc_free(tmp_ctx);
391 send a named resource recond to bind9
393 static isc_result_t b9_putnamedrr(struct dlz_bind9_data *state,
394 void *handle, const char *name,
395 struct dnsp_DnssrvRpcRecord *rec)
398 const char *type, *data;
399 TALLOC_CTX *tmp_ctx = talloc_new(state);
401 if (!b9_format(state, tmp_ctx, rec, &type, &data)) {
402 return ISC_R_FAILURE;
406 talloc_free(tmp_ctx);
407 return ISC_R_NOMEMORY;
410 result = state->putnamedrr(handle, name, type, rec->dwTtlSeconds, data);
411 if (result != ISC_R_SUCCESS) {
412 state->log(ISC_LOG_ERROR, "Failed to put named rr '%s'", name);
414 talloc_free(tmp_ctx);
425 static isc_result_t parse_options(struct dlz_bind9_data *state,
426 unsigned int argc, char *argv[],
427 struct b9_options *options)
431 struct poptOption long_options[] = {
432 { "url", 'H', POPT_ARG_STRING, &options->url, 0, "database URL", "URL" },
435 struct poptOption **popt_options;
438 fault_setup_disable();
440 popt_options = ldb_module_popt_options(state->samdb);
441 (*popt_options) = long_options;
443 ret = ldb_modules_hook(state->samdb, LDB_MODULE_HOOK_CMDLINE_OPTIONS);
444 if (ret != LDB_SUCCESS) {
445 state->log(ISC_LOG_ERROR, "dlz samba: failed cmdline hook");
446 return ISC_R_FAILURE;
449 pc = poptGetContext("dlz_bind9", argc, (const char **)argv, *popt_options,
450 POPT_CONTEXT_KEEP_FIRST);
452 while ((opt = poptGetNextOpt(pc)) != -1) {
455 state->log(ISC_LOG_ERROR, "dlz samba: Invalid option %s: %s",
456 poptBadOption(pc, 0), poptStrerror(opt));
457 return ISC_R_FAILURE;
461 ret = ldb_modules_hook(state->samdb, LDB_MODULE_HOOK_CMDLINE_PRECONNECT);
462 if (ret != LDB_SUCCESS) {
463 state->log(ISC_LOG_ERROR, "dlz samba: failed cmdline preconnect");
464 return ISC_R_FAILURE;
467 return ISC_R_SUCCESS;
472 called to initialise the driver
474 _PUBLIC_ isc_result_t dlz_create(const char *dlzname,
475 unsigned int argc, char *argv[],
478 struct dlz_bind9_data *state;
479 const char *helper_name;
485 struct b9_options options;
487 ZERO_STRUCT(options);
489 state = talloc_zero(NULL, struct dlz_bind9_data);
491 return ISC_R_NOMEMORY;
494 tmp_ctx = talloc_new(state);
496 /* fill in the helper functions */
497 va_start(ap, dbdata);
498 while ((helper_name = va_arg(ap, const char *)) != NULL) {
499 b9_add_helper(state, helper_name, va_arg(ap, void*));
503 state->ev_ctx = s4_event_context_init(state);
504 if (state->ev_ctx == NULL) {
505 result = ISC_R_NOMEMORY;
509 state->samdb = ldb_init(state, state->ev_ctx);
510 if (state->samdb == NULL) {
511 state->log(ISC_LOG_ERROR, "samba_dlz: Failed to create ldb");
512 result = ISC_R_FAILURE;
516 result = parse_options(state, argc, argv, &options);
517 if (result != ISC_R_SUCCESS) {
521 state->lp = loadparm_init_global(true);
522 if (state->lp == NULL) {
523 result = ISC_R_NOMEMORY;
527 if (options.url == NULL) {
528 options.url = talloc_asprintf(tmp_ctx, "ldapi://%s",
529 lpcfg_private_path(tmp_ctx, state->lp, "ldap_priv/ldapi"));
530 if (options.url == NULL) {
531 result = ISC_R_NOMEMORY;
536 ret = ldb_connect(state->samdb, options.url, 0, NULL);
537 if (ret != LDB_SUCCESS) {
538 state->log(ISC_LOG_ERROR, "samba_dlz: Failed to connect to %s - %s",
539 options.url, ldb_errstring(state->samdb));
540 result = ISC_R_FAILURE;
544 ret = ldb_modules_hook(state->samdb, LDB_MODULE_HOOK_CMDLINE_POSTCONNECT);
545 if (ret != LDB_SUCCESS) {
546 state->log(ISC_LOG_ERROR, "samba_dlz: Failed postconnect for %s - %s",
547 options.url, ldb_errstring(state->samdb));
548 result = ISC_R_FAILURE;
552 dn = ldb_get_default_basedn(state->samdb);
554 state->log(ISC_LOG_ERROR, "samba_dlz: Unable to get basedn for %s - %s",
555 options.url, ldb_errstring(state->samdb));
556 result = ISC_R_FAILURE;
560 state->log(ISC_LOG_INFO, "samba_dlz: started for DN %s",
561 ldb_dn_get_linearized(dn));
565 talloc_free(tmp_ctx);
566 return ISC_R_SUCCESS;
576 _PUBLIC_ void dlz_destroy(void *dbdata)
578 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
579 state->log(ISC_LOG_INFO, "samba_dlz: shutting down");
585 return the base DN for a zone
587 static isc_result_t b9_find_zone_dn(struct dlz_bind9_data *state, const char *zone_name,
588 TALLOC_CTX *mem_ctx, struct ldb_dn **zone_dn)
591 TALLOC_CTX *tmp_ctx = talloc_new(state);
592 const char *attrs[] = { NULL };
595 for (i=0; zone_prefixes[i]; i++) {
597 struct ldb_result *res;
599 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
601 talloc_free(tmp_ctx);
602 return ISC_R_NOMEMORY;
605 if (!ldb_dn_add_child_fmt(dn, "DC=%s,%s", zone_name, zone_prefixes[i])) {
606 talloc_free(tmp_ctx);
607 return ISC_R_NOMEMORY;
610 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsZone");
611 if (ret == LDB_SUCCESS) {
612 if (zone_dn != NULL) {
613 *zone_dn = talloc_steal(mem_ctx, dn);
615 talloc_free(tmp_ctx);
616 return ISC_R_SUCCESS;
621 talloc_free(tmp_ctx);
622 return ISC_R_NOTFOUND;
627 return the DN for a name. The record does not need to exist, but the
630 static isc_result_t b9_find_name_dn(struct dlz_bind9_data *state, const char *name,
631 TALLOC_CTX *mem_ctx, struct ldb_dn **dn)
635 /* work through the name piece by piece, until we find a zone */
638 result = b9_find_zone_dn(state, p, mem_ctx, dn);
639 if (result == ISC_R_SUCCESS) {
640 /* we found a zone, now extend the DN to get
645 ret = ldb_dn_add_child_fmt(*dn, "DC=@");
647 ret = ldb_dn_add_child_fmt(*dn, "DC=%.*s", (int)(p-name)-1, name);
651 return ISC_R_NOMEMORY;
653 return ISC_R_SUCCESS;
661 return ISC_R_NOTFOUND;
666 see if we handle a given zone
668 _PUBLIC_ isc_result_t dlz_findzonedb(void *dbdata, const char *name)
670 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
671 return b9_find_zone_dn(state, name, NULL, NULL);
678 static isc_result_t dlz_lookup_types(struct dlz_bind9_data *state,
679 const char *zone, const char *name,
680 dns_sdlzlookup_t *lookup,
683 TALLOC_CTX *tmp_ctx = talloc_new(state);
684 const char *attrs[] = { "dnsRecord", NULL };
685 int ret = LDB_SUCCESS, i;
686 struct ldb_result *res;
687 struct ldb_message_element *el;
690 for (i=0; zone_prefixes[i]; i++) {
691 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
693 talloc_free(tmp_ctx);
694 return ISC_R_NOMEMORY;
697 if (!ldb_dn_add_child_fmt(dn, "DC=%s,DC=%s,%s", name, zone, zone_prefixes[i])) {
698 talloc_free(tmp_ctx);
699 return ISC_R_NOMEMORY;
702 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
703 attrs, "objectClass=dnsNode");
704 if (ret == LDB_SUCCESS) {
708 if (ret != LDB_SUCCESS) {
709 talloc_free(tmp_ctx);
710 return ISC_R_NOTFOUND;
713 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
714 if (el == NULL || el->num_values == 0) {
715 talloc_free(tmp_ctx);
716 return ISC_R_NOTFOUND;
719 for (i=0; i<el->num_values; i++) {
720 struct dnsp_DnssrvRpcRecord rec;
721 enum ndr_err_code ndr_err;
724 ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec,
725 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
726 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
727 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
728 ldb_dn_get_linearized(dn));
729 talloc_free(tmp_ctx);
730 return ISC_R_FAILURE;
733 result = b9_putrr(state, lookup, &rec, types);
734 if (result != ISC_R_SUCCESS) {
735 talloc_free(tmp_ctx);
740 talloc_free(tmp_ctx);
741 return ISC_R_SUCCESS;
747 _PUBLIC_ isc_result_t dlz_lookup(const char *zone, const char *name,
748 void *dbdata, dns_sdlzlookup_t *lookup)
750 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
751 return dlz_lookup_types(state, zone, name, lookup, NULL);
756 see if a zone transfer is allowed
758 _PUBLIC_ isc_result_t dlz_allowzonexfr(void *dbdata, const char *name, const char *client)
760 /* just say yes for all our zones for now */
761 return dlz_findzonedb(dbdata, name);
765 perform a zone transfer
767 _PUBLIC_ isc_result_t dlz_allnodes(const char *zone, void *dbdata,
768 dns_sdlzallnodes_t *allnodes)
770 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
771 const char *attrs[] = { "dnsRecord", NULL };
772 int ret = LDB_SUCCESS, i, j;
774 struct ldb_result *res;
775 TALLOC_CTX *tmp_ctx = talloc_new(state);
777 for (i=0; zone_prefixes[i]; i++) {
778 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
780 talloc_free(tmp_ctx);
781 return ISC_R_NOMEMORY;
784 if (!ldb_dn_add_child_fmt(dn, "DC=%s,%s", zone, zone_prefixes[i])) {
785 talloc_free(tmp_ctx);
786 return ISC_R_NOMEMORY;
789 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
790 attrs, "objectClass=dnsNode");
791 if (ret == LDB_SUCCESS) {
795 if (ret != LDB_SUCCESS) {
796 talloc_free(tmp_ctx);
797 return ISC_R_NOTFOUND;
800 for (i=0; i<res->count; i++) {
801 struct ldb_message_element *el;
802 TALLOC_CTX *el_ctx = talloc_new(tmp_ctx);
803 const char *rdn, *name;
804 const struct ldb_val *v;
806 el = ldb_msg_find_element(res->msgs[i], "dnsRecord");
807 if (el == NULL || el->num_values == 0) {
808 state->log(ISC_LOG_INFO, "failed to find dnsRecord for %s",
809 ldb_dn_get_linearized(dn));
814 v = ldb_dn_get_rdn_val(res->msgs[i]->dn);
816 state->log(ISC_LOG_INFO, "failed to find RDN for %s",
817 ldb_dn_get_linearized(dn));
822 rdn = talloc_strndup(el_ctx, (char *)v->data, v->length);
824 talloc_free(tmp_ctx);
825 return ISC_R_NOMEMORY;
828 if (strcmp(rdn, "@") == 0) {
831 name = talloc_asprintf(el_ctx, "%s.%s", rdn, zone);
834 talloc_free(tmp_ctx);
835 return ISC_R_NOMEMORY;
838 for (j=0; j<el->num_values; j++) {
839 struct dnsp_DnssrvRpcRecord rec;
840 enum ndr_err_code ndr_err;
843 ndr_err = ndr_pull_struct_blob(&el->values[j], el_ctx, &rec,
844 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
845 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
846 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
847 ldb_dn_get_linearized(dn));
851 result = b9_putnamedrr(state, allnodes, name, &rec);
852 if (result != ISC_R_SUCCESS) {
858 talloc_free(tmp_ctx);
860 return ISC_R_SUCCESS;
867 _PUBLIC_ isc_result_t dlz_newversion(const char *zone, void *dbdata, void **versionp)
869 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
871 state->log(ISC_LOG_INFO, "samba_dlz: starting transaction on zone %s", zone);
873 if (state->transaction_token != NULL) {
874 state->log(ISC_LOG_INFO, "samba_dlz: transaction already started for zone %s", zone);
875 return ISC_R_FAILURE;
878 state->transaction_token = talloc_zero(state, int);
879 if (state->transaction_token == NULL) {
880 return ISC_R_NOMEMORY;
883 if (ldb_transaction_start(state->samdb) != LDB_SUCCESS) {
884 state->log(ISC_LOG_INFO, "samba_dlz: failed to start a transaction for zone %s", zone);
885 talloc_free(state->transaction_token);
886 state->transaction_token = NULL;
887 return ISC_R_FAILURE;
890 *versionp = (void *)state->transaction_token;
892 return ISC_R_SUCCESS;
898 _PUBLIC_ void dlz_closeversion(const char *zone, isc_boolean_t commit,
899 void *dbdata, void **versionp)
901 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
903 if (state->transaction_token != (int *)*versionp) {
904 state->log(ISC_LOG_INFO, "samba_dlz: transaction not started for zone %s", zone);
909 if (ldb_transaction_commit(state->samdb) != LDB_SUCCESS) {
910 state->log(ISC_LOG_INFO, "samba_dlz: failed to commit a transaction for zone %s", zone);
913 state->log(ISC_LOG_INFO, "samba_dlz: committed transaction on zone %s", zone);
915 if (ldb_transaction_cancel(state->samdb) != LDB_SUCCESS) {
916 state->log(ISC_LOG_INFO, "samba_dlz: failed to cancel a transaction for zone %s", zone);
919 state->log(ISC_LOG_INFO, "samba_dlz: cancelling transaction on zone %s", zone);
922 talloc_free(state->transaction_token);
923 state->transaction_token = NULL;
929 see if there is a SOA record for a zone
931 static bool b9_has_soa(struct dlz_bind9_data *state, struct ldb_dn *dn, const char *zone)
933 const char *attrs[] = { "dnsRecord", NULL };
934 struct ldb_result *res;
935 struct ldb_message_element *el;
936 TALLOC_CTX *tmp_ctx = talloc_new(state);
939 if (!ldb_dn_add_child_fmt(dn, "DC=@,DC=%s", zone)) {
940 talloc_free(tmp_ctx);
944 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
945 attrs, "objectClass=dnsNode");
946 if (ret != LDB_SUCCESS) {
947 talloc_free(tmp_ctx);
951 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
953 talloc_free(tmp_ctx);
956 for (i=0; i<el->num_values; i++) {
957 struct dnsp_DnssrvRpcRecord rec;
958 enum ndr_err_code ndr_err;
960 ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec,
961 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
962 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
965 if (rec.wType == DNS_TYPE_SOA) {
966 talloc_free(tmp_ctx);
971 talloc_free(tmp_ctx);
976 configure a writeable zone
978 _PUBLIC_ isc_result_t dlz_configure(dns_view_t *view, void *dbdata)
980 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
985 state->log(ISC_LOG_INFO, "samba_dlz: starting configure");
986 if (state->writeable_zone == NULL) {
987 state->log(ISC_LOG_INFO, "samba_dlz: no writeable_zone method available");
988 return ISC_R_FAILURE;
991 tmp_ctx = talloc_new(state);
993 for (i=0; zone_prefixes[i]; i++) {
994 const char *attrs[] = { "name", NULL };
996 struct ldb_result *res;
998 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
1000 talloc_free(tmp_ctx);
1001 return ISC_R_NOMEMORY;
1004 if (!ldb_dn_add_child_fmt(dn, "%s", zone_prefixes[i])) {
1005 talloc_free(tmp_ctx);
1006 return ISC_R_NOMEMORY;
1009 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
1010 attrs, "objectClass=dnsZone");
1011 if (ret != LDB_SUCCESS) {
1015 for (j=0; j<res->count; j++) {
1016 isc_result_t result;
1017 const char *zone = ldb_msg_find_attr_as_string(res->msgs[j], "name", NULL);
1018 struct ldb_dn *zone_dn;
1023 zone_dn = ldb_dn_copy(tmp_ctx, dn);
1025 if (!b9_has_soa(state, zone_dn, zone)) {
1028 result = state->writeable_zone(view, zone);
1029 if (result != ISC_R_SUCCESS) {
1030 state->log(ISC_LOG_ERROR, "samba_dlz: Failed to configure zone '%s'",
1032 talloc_free(tmp_ctx);
1035 state->log(ISC_LOG_INFO, "samba_dlz: configured writeable zone '%s'", zone);
1039 talloc_free(tmp_ctx);
1040 return ISC_R_SUCCESS;
1044 authorize a zone update
1046 _PUBLIC_ isc_boolean_t dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr,
1047 const char *type, const char *key, uint32_t keydatalen, uint8_t *keydata,
1050 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1052 state->log(ISC_LOG_INFO, "samba_dlz: allowing update of signer=%s name=%s tcpaddr=%s type=%s key=%s keydatalen=%u",
1053 signer, name, tcpaddr, type, key, keydatalen);
1061 static isc_result_t b9_add_record(struct dlz_bind9_data *state, const char *name,
1063 struct dnsp_DnssrvRpcRecord *rec)
1065 struct ldb_message *msg;
1066 enum ndr_err_code ndr_err;
1070 msg = ldb_msg_new(rec);
1072 return ISC_R_NOMEMORY;
1075 ret = ldb_msg_add_string(msg, "objectClass", "dnsNode");
1076 if (ret != LDB_SUCCESS) {
1077 return ISC_R_FAILURE;
1080 ndr_err = ndr_push_struct_blob(&v, rec, rec, (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
1081 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1082 return ISC_R_FAILURE;
1084 ret = ldb_msg_add_value(msg, "dnsRecord", &v, NULL);
1085 if (ret != LDB_SUCCESS) {
1086 return ISC_R_FAILURE;
1089 ret = ldb_add(state->samdb, msg);
1090 if (ret != LDB_SUCCESS) {
1091 return ISC_R_FAILURE;
1094 return ISC_R_SUCCESS;
1098 see if two DNS names are the same
1100 static bool dns_name_equal(const char *name1, const char *name2)
1102 size_t len1 = strlen(name1);
1103 size_t len2 = strlen(name2);
1104 if (name1[len1-1] == '.') len1--;
1105 if (name2[len2-1] == '.') len2--;
1109 return strncasecmp_m(name1, name2, len1) == 0;
1114 see if two dns records match
1116 static bool b9_record_match(struct dlz_bind9_data *state,
1117 struct dnsp_DnssrvRpcRecord *rec1, struct dnsp_DnssrvRpcRecord *rec2)
1119 if (rec1->wType != rec2->wType) {
1122 /* see if this type is single valued */
1123 if (b9_single_valued(rec1->wType)) {
1127 /* see if the data matches */
1128 switch (rec1->wType) {
1130 return strcmp(rec1->data.ipv4, rec2->data.ipv4) == 0;
1132 return strcmp(rec1->data.ipv6, rec2->data.ipv6) == 0;
1133 case DNS_TYPE_CNAME:
1134 return dns_name_equal(rec1->data.cname, rec2->data.cname);
1136 return strcmp(rec1->data.txt, rec2->data.txt) == 0;
1138 return strcmp(rec1->data.ptr, rec2->data.ptr) == 0;
1140 return dns_name_equal(rec1->data.ns, rec2->data.ns);
1143 return rec1->data.srv.wPriority == rec2->data.srv.wPriority &&
1144 rec1->data.srv.wWeight == rec2->data.srv.wWeight &&
1145 rec1->data.srv.wPort == rec2->data.srv.wPort &&
1146 dns_name_equal(rec1->data.srv.nameTarget, rec2->data.srv.nameTarget);
1149 return rec1->data.mx.wPriority == rec2->data.mx.wPriority &&
1150 dns_name_equal(rec1->data.mx.nameTarget, rec2->data.mx.nameTarget);
1152 case DNS_TYPE_HINFO:
1153 return strcmp(rec1->data.hinfo.cpu, rec2->data.hinfo.cpu) == 0 &&
1154 strcmp(rec1->data.hinfo.os, rec2->data.hinfo.os) == 0;
1157 return dns_name_equal(rec1->data.soa.mname, rec2->data.soa.mname) &&
1158 dns_name_equal(rec1->data.soa.rname, rec2->data.soa.rname) &&
1159 rec1->data.soa.serial == rec2->data.soa.serial &&
1160 rec1->data.soa.refresh == rec2->data.soa.refresh &&
1161 rec1->data.soa.retry == rec2->data.soa.retry &&
1162 rec1->data.soa.expire == rec2->data.soa.expire &&
1163 rec1->data.soa.minimum == rec2->data.soa.minimum;
1165 state->log(ISC_LOG_ERROR, "samba b9_putrr: unhandled record type %u",
1175 add or modify a rdataset
1177 _PUBLIC_ isc_result_t dlz_addrdataset(const char *name, const char *rdatastr, void *dbdata, void *version)
1179 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1180 struct dnsp_DnssrvRpcRecord *rec;
1182 isc_result_t result;
1183 struct ldb_result *res;
1184 const char *attrs[] = { "dnsRecord", NULL };
1186 struct ldb_message_element *el;
1187 enum ndr_err_code ndr_err;
1190 if (state->transaction_token != (void*)version) {
1191 state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version");
1192 return ISC_R_FAILURE;
1195 rec = talloc_zero(state, struct dnsp_DnssrvRpcRecord);
1197 return ISC_R_NOMEMORY;
1200 unix_to_nt_time(&t, time(NULL));
1201 t /= 10*1000*1000; /* convert to seconds (NT time is in 100ns units) */
1202 t /= 3600; /* convert to hours */
1204 rec->rank = DNS_RANK_ZONE;
1205 rec->dwSerial = state->soa_serial;
1206 rec->dwTimeStamp = (uint32_t)t;
1208 if (!b9_parse(state, rdatastr, rec)) {
1209 state->log(ISC_LOG_INFO, "samba_dlz: failed to parse rdataset '%s'", rdatastr);
1211 return ISC_R_FAILURE;
1214 /* find the DN of the record */
1215 result = b9_find_name_dn(state, name, rec, &dn);
1216 if (result != ISC_R_SUCCESS) {
1221 /* get any existing records */
1222 ret = ldb_search(state->samdb, rec, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode");
1223 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1224 result = b9_add_record(state, name, dn, rec);
1226 if (result == ISC_R_SUCCESS) {
1227 state->log(ISC_LOG_ERROR, "samba_dlz: added %s %s", name, rdatastr);
1232 /* there are existing records. We need to see if this will
1233 * replace a record or add to it
1235 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
1237 state->log(ISC_LOG_ERROR, "samba_dlz: no dnsRecord attribute for %s",
1238 ldb_dn_get_linearized(dn));
1240 return ISC_R_FAILURE;
1243 for (i=0; i<el->num_values; i++) {
1244 struct dnsp_DnssrvRpcRecord rec2;
1246 ndr_err = ndr_pull_struct_blob(&el->values[i], rec, &rec2,
1247 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
1248 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1249 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
1250 ldb_dn_get_linearized(dn));
1252 return ISC_R_FAILURE;
1255 if (b9_record_match(state, rec, &rec2)) {
1259 if (i == el->num_values) {
1260 /* adding a new value */
1261 el->values = talloc_realloc(el, el->values, struct ldb_val, el->num_values+1);
1262 if (el->values == NULL) {
1264 return ISC_R_NOMEMORY;
1269 ndr_err = ndr_push_struct_blob(&el->values[i], rec, rec,
1270 (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
1271 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1272 state->log(ISC_LOG_ERROR, "samba_dlz: failed to push dnsRecord for %s",
1273 ldb_dn_get_linearized(dn));
1275 return ISC_R_FAILURE;
1278 /* modify the record */
1279 el->flags = LDB_FLAG_MOD_REPLACE;
1280 ret = ldb_modify(state->samdb, res->msgs[0]);
1281 if (ret != LDB_SUCCESS) {
1282 state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s",
1283 ldb_dn_get_linearized(dn), ldb_errstring(state->samdb));
1285 return ISC_R_FAILURE;
1288 state->log(ISC_LOG_INFO, "samba_dlz: added rdataset %s '%s'", name, rdatastr);
1291 return ISC_R_SUCCESS;
1297 _PUBLIC_ isc_result_t dlz_subrdataset(const char *name, const char *rdatastr, void *dbdata, void *version)
1299 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1300 struct dnsp_DnssrvRpcRecord *rec;
1302 isc_result_t result;
1303 struct ldb_result *res;
1304 const char *attrs[] = { "dnsRecord", NULL };
1306 struct ldb_message_element *el;
1307 enum ndr_err_code ndr_err;
1309 if (state->transaction_token != (void*)version) {
1310 state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version");
1311 return ISC_R_FAILURE;
1314 rec = talloc_zero(state, struct dnsp_DnssrvRpcRecord);
1316 return ISC_R_NOMEMORY;
1319 if (!b9_parse(state, rdatastr, rec)) {
1320 state->log(ISC_LOG_INFO, "samba_dlz: failed to parse rdataset '%s'", rdatastr);
1322 return ISC_R_FAILURE;
1325 /* find the DN of the record */
1326 result = b9_find_name_dn(state, name, rec, &dn);
1327 if (result != ISC_R_SUCCESS) {
1332 /* get the existing records */
1333 ret = ldb_search(state->samdb, rec, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode");
1334 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1336 return ISC_R_NOTFOUND;
1339 /* there are existing records. We need to see if any match
1341 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
1342 if (el == NULL || el->num_values == 0) {
1343 state->log(ISC_LOG_ERROR, "samba_dlz: no dnsRecord attribute for %s",
1344 ldb_dn_get_linearized(dn));
1346 return ISC_R_FAILURE;
1349 for (i=0; i<el->num_values; i++) {
1350 struct dnsp_DnssrvRpcRecord rec2;
1352 ndr_err = ndr_pull_struct_blob(&el->values[i], rec, &rec2,
1353 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
1354 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1355 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
1356 ldb_dn_get_linearized(dn));
1358 return ISC_R_FAILURE;
1361 if (b9_record_match(state, rec, &rec2)) {
1365 if (i == el->num_values) {
1367 return ISC_R_NOTFOUND;
1370 if (i < el->num_values-1) {
1371 memmove(&el->values[i], &el->values[i+1], sizeof(el->values[0])*((el->num_values-1)-i));
1375 if (el->num_values == 0) {
1376 /* delete the record */
1377 ret = ldb_delete(state->samdb, dn);
1379 /* modify the record */
1380 el->flags = LDB_FLAG_MOD_REPLACE;
1381 ret = ldb_modify(state->samdb, res->msgs[0]);
1383 if (ret != LDB_SUCCESS) {
1384 state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s",
1385 ldb_dn_get_linearized(dn), ldb_errstring(state->samdb));
1387 return ISC_R_FAILURE;
1390 state->log(ISC_LOG_INFO, "samba_dlz: subtracted rdataset %s '%s'", name, rdatastr);
1393 return ISC_R_SUCCESS;
1398 delete all records of the given type
1400 _PUBLIC_ isc_result_t dlz_delrdataset(const char *name, const char *type, void *dbdata, void *version)
1402 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1403 TALLOC_CTX *tmp_ctx;
1405 isc_result_t result;
1406 struct ldb_result *res;
1407 const char *attrs[] = { "dnsRecord", NULL };
1409 struct ldb_message_element *el;
1410 enum ndr_err_code ndr_err;
1411 enum dns_record_type dns_type;
1414 if (state->transaction_token != (void*)version) {
1415 state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version");
1416 return ISC_R_FAILURE;
1419 if (!b9_dns_type(type, &dns_type)) {
1420 state->log(ISC_LOG_INFO, "samba_dlz: bad dns type %s in delete", type);
1421 return ISC_R_FAILURE;
1424 tmp_ctx = talloc_new(state);
1426 /* find the DN of the record */
1427 result = b9_find_name_dn(state, name, tmp_ctx, &dn);
1428 if (result != ISC_R_SUCCESS) {
1429 talloc_free(tmp_ctx);
1433 /* get the existing records */
1434 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode");
1435 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1436 talloc_free(tmp_ctx);
1437 return ISC_R_NOTFOUND;
1440 /* there are existing records. We need to see if any match the type
1442 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
1443 if (el == NULL || el->num_values == 0) {
1444 talloc_free(tmp_ctx);
1445 return ISC_R_NOTFOUND;
1448 for (i=0; i<el->num_values; i++) {
1449 struct dnsp_DnssrvRpcRecord rec2;
1451 ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec2,
1452 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
1453 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1454 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
1455 ldb_dn_get_linearized(dn));
1456 talloc_free(tmp_ctx);
1457 return ISC_R_FAILURE;
1460 if (dns_type == rec2.wType) {
1461 if (i < el->num_values-1) {
1462 memmove(&el->values[i], &el->values[i+1],
1463 sizeof(el->values[0])*((el->num_values-1)-i));
1472 talloc_free(tmp_ctx);
1473 return ISC_R_FAILURE;
1476 if (el->num_values == 0) {
1477 /* delete the record */
1478 ret = ldb_delete(state->samdb, dn);
1480 /* modify the record */
1481 el->flags = LDB_FLAG_MOD_REPLACE;
1482 ret = ldb_modify(state->samdb, res->msgs[0]);
1484 if (ret != LDB_SUCCESS) {
1485 state->log(ISC_LOG_ERROR, "samba_dlz: failed to delete type %s in %s - %s",
1486 type, ldb_dn_get_linearized(dn), ldb_errstring(state->samdb));
1487 talloc_free(tmp_ctx);
1488 return ISC_R_FAILURE;
1491 state->log(ISC_LOG_INFO, "samba_dlz: deleted rdataset %s of type %s", name, type);
1493 talloc_free(tmp_ctx);
1494 return ISC_R_SUCCESS;