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);
1021 if (!b9_has_soa(state, dn, zone)) {
1024 result = state->writeable_zone(view, zone);
1025 if (result != ISC_R_SUCCESS) {
1026 state->log(ISC_LOG_ERROR, "samba_dlz: Failed to configure zone '%s'",
1028 talloc_free(tmp_ctx);
1031 state->log(ISC_LOG_INFO, "samba_dlz: configured writeable zone '%s'", zone);
1035 talloc_free(tmp_ctx);
1036 return ISC_R_SUCCESS;
1040 authorize a zone update
1042 _PUBLIC_ isc_boolean_t dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr,
1043 const char *type, const char *key, uint32_t keydatalen, uint8_t *keydata,
1046 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1048 state->log(ISC_LOG_INFO, "samba_dlz: allowing update of signer=%s name=%s tcpaddr=%s type=%s key=%s keydatalen=%u",
1049 signer, name, tcpaddr, type, key, keydatalen);
1057 static isc_result_t b9_add_record(struct dlz_bind9_data *state, const char *name,
1059 struct dnsp_DnssrvRpcRecord *rec)
1061 struct ldb_message *msg;
1062 enum ndr_err_code ndr_err;
1066 msg = ldb_msg_new(rec);
1068 return ISC_R_NOMEMORY;
1071 ret = ldb_msg_add_string(msg, "objectClass", "dnsNode");
1072 if (ret != LDB_SUCCESS) {
1073 return ISC_R_FAILURE;
1076 ndr_err = ndr_push_struct_blob(&v, rec, rec, (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
1077 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1078 return ISC_R_FAILURE;
1080 ret = ldb_msg_add_value(msg, "dnsRecord", &v, NULL);
1081 if (ret != LDB_SUCCESS) {
1082 return ISC_R_FAILURE;
1085 ret = ldb_add(state->samdb, msg);
1086 if (ret != LDB_SUCCESS) {
1087 return ISC_R_FAILURE;
1090 return ISC_R_SUCCESS;
1094 see if two DNS names are the same
1096 static bool dns_name_equal(const char *name1, const char *name2)
1098 size_t len1 = strlen(name1);
1099 size_t len2 = strlen(name2);
1100 if (name1[len1-1] == '.') len1--;
1101 if (name2[len2-1] == '.') len2--;
1105 return strncasecmp_m(name1, name2, len1) == 0;
1110 see if two dns records match
1112 static bool b9_record_match(struct dlz_bind9_data *state,
1113 struct dnsp_DnssrvRpcRecord *rec1, struct dnsp_DnssrvRpcRecord *rec2)
1115 if (rec1->wType != rec2->wType) {
1118 /* see if this type is single valued */
1119 if (b9_single_valued(rec1->wType)) {
1123 /* see if the data matches */
1124 switch (rec1->wType) {
1126 return strcmp(rec1->data.ipv4, rec2->data.ipv4) == 0;
1128 return strcmp(rec1->data.ipv6, rec2->data.ipv6) == 0;
1129 case DNS_TYPE_CNAME:
1130 return dns_name_equal(rec1->data.cname, rec2->data.cname);
1132 return strcmp(rec1->data.txt, rec2->data.txt) == 0;
1134 return strcmp(rec1->data.ptr, rec2->data.ptr) == 0;
1136 return dns_name_equal(rec1->data.ns, rec2->data.ns);
1139 return rec1->data.srv.wPriority == rec2->data.srv.wPriority &&
1140 rec1->data.srv.wWeight == rec2->data.srv.wWeight &&
1141 rec1->data.srv.wPort == rec2->data.srv.wPort &&
1142 dns_name_equal(rec1->data.srv.nameTarget, rec2->data.srv.nameTarget);
1145 return rec1->data.mx.wPriority == rec2->data.mx.wPriority &&
1146 dns_name_equal(rec1->data.mx.nameTarget, rec2->data.mx.nameTarget);
1148 case DNS_TYPE_HINFO:
1149 return strcmp(rec1->data.hinfo.cpu, rec2->data.hinfo.cpu) == 0 &&
1150 strcmp(rec1->data.hinfo.os, rec2->data.hinfo.os) == 0;
1153 return dns_name_equal(rec1->data.soa.mname, rec2->data.soa.mname) &&
1154 dns_name_equal(rec1->data.soa.rname, rec2->data.soa.rname) &&
1155 rec1->data.soa.serial == rec2->data.soa.serial &&
1156 rec1->data.soa.refresh == rec2->data.soa.refresh &&
1157 rec1->data.soa.retry == rec2->data.soa.retry &&
1158 rec1->data.soa.expire == rec2->data.soa.expire &&
1159 rec1->data.soa.minimum == rec2->data.soa.minimum;
1161 state->log(ISC_LOG_ERROR, "samba b9_putrr: unhandled record type %u",
1171 add or modify a rdataset
1173 _PUBLIC_ isc_result_t dlz_addrdataset(const char *name, const char *rdatastr, void *dbdata, void *version)
1175 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1176 struct dnsp_DnssrvRpcRecord *rec;
1178 isc_result_t result;
1179 struct ldb_result *res;
1180 const char *attrs[] = { "dnsRecord", NULL };
1182 struct ldb_message_element *el;
1183 enum ndr_err_code ndr_err;
1186 if (state->transaction_token != (void*)version) {
1187 state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version");
1188 return ISC_R_FAILURE;
1191 rec = talloc_zero(state, struct dnsp_DnssrvRpcRecord);
1193 return ISC_R_NOMEMORY;
1196 unix_to_nt_time(&t, time(NULL));
1197 t /= 10*1000*1000; /* convert to seconds (NT time is in 100ns units) */
1198 t /= 3600; /* convert to hours */
1200 rec->rank = DNS_RANK_ZONE;
1201 rec->dwSerial = state->soa_serial;
1202 rec->dwTimeStamp = (uint32_t)t;
1204 if (!b9_parse(state, rdatastr, rec)) {
1205 state->log(ISC_LOG_INFO, "samba_dlz: failed to parse rdataset '%s'", rdatastr);
1207 return ISC_R_FAILURE;
1210 /* find the DN of the record */
1211 result = b9_find_name_dn(state, name, rec, &dn);
1212 if (result != ISC_R_SUCCESS) {
1217 /* get any existing records */
1218 ret = ldb_search(state->samdb, rec, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode");
1219 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1220 result = b9_add_record(state, name, dn, rec);
1222 if (result == ISC_R_SUCCESS) {
1223 state->log(ISC_LOG_ERROR, "samba_dlz: added %s %s", name, rdatastr);
1228 /* there are existing records. We need to see if this will
1229 * replace a record or add to it
1231 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
1233 state->log(ISC_LOG_ERROR, "samba_dlz: no dnsRecord attribute for %s",
1234 ldb_dn_get_linearized(dn));
1236 return ISC_R_FAILURE;
1239 for (i=0; i<el->num_values; i++) {
1240 struct dnsp_DnssrvRpcRecord rec2;
1242 ndr_err = ndr_pull_struct_blob(&el->values[i], rec, &rec2,
1243 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
1244 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1245 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
1246 ldb_dn_get_linearized(dn));
1248 return ISC_R_FAILURE;
1251 if (b9_record_match(state, rec, &rec2)) {
1255 if (i == el->num_values) {
1256 /* adding a new value */
1257 el->values = talloc_realloc(el, el->values, struct ldb_val, el->num_values+1);
1258 if (el->values == NULL) {
1260 return ISC_R_NOMEMORY;
1265 ndr_err = ndr_push_struct_blob(&el->values[i], rec, rec,
1266 (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
1267 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1268 state->log(ISC_LOG_ERROR, "samba_dlz: failed to push dnsRecord for %s",
1269 ldb_dn_get_linearized(dn));
1271 return ISC_R_FAILURE;
1274 /* modify the record */
1275 el->flags = LDB_FLAG_MOD_REPLACE;
1276 ret = ldb_modify(state->samdb, res->msgs[0]);
1277 if (ret != LDB_SUCCESS) {
1278 state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s",
1279 ldb_dn_get_linearized(dn), ldb_errstring(state->samdb));
1281 return ISC_R_FAILURE;
1284 state->log(ISC_LOG_INFO, "samba_dlz: added rdataset %s '%s'", name, rdatastr);
1287 return ISC_R_SUCCESS;
1293 _PUBLIC_ isc_result_t dlz_subrdataset(const char *name, const char *rdatastr, void *dbdata, void *version)
1295 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1296 struct dnsp_DnssrvRpcRecord *rec;
1298 isc_result_t result;
1299 struct ldb_result *res;
1300 const char *attrs[] = { "dnsRecord", NULL };
1302 struct ldb_message_element *el;
1303 enum ndr_err_code ndr_err;
1305 if (state->transaction_token != (void*)version) {
1306 state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version");
1307 return ISC_R_FAILURE;
1310 rec = talloc_zero(state, struct dnsp_DnssrvRpcRecord);
1312 return ISC_R_NOMEMORY;
1315 if (!b9_parse(state, rdatastr, rec)) {
1316 state->log(ISC_LOG_INFO, "samba_dlz: failed to parse rdataset '%s'", rdatastr);
1318 return ISC_R_FAILURE;
1321 /* find the DN of the record */
1322 result = b9_find_name_dn(state, name, rec, &dn);
1323 if (result != ISC_R_SUCCESS) {
1328 /* get the existing records */
1329 ret = ldb_search(state->samdb, rec, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode");
1330 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1332 return ISC_R_NOTFOUND;
1335 /* there are existing records. We need to see if any match
1337 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
1338 if (el == NULL || el->num_values == 0) {
1339 state->log(ISC_LOG_ERROR, "samba_dlz: no dnsRecord attribute for %s",
1340 ldb_dn_get_linearized(dn));
1342 return ISC_R_FAILURE;
1345 for (i=0; i<el->num_values; i++) {
1346 struct dnsp_DnssrvRpcRecord rec2;
1348 ndr_err = ndr_pull_struct_blob(&el->values[i], rec, &rec2,
1349 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
1350 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1351 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
1352 ldb_dn_get_linearized(dn));
1354 return ISC_R_FAILURE;
1357 if (b9_record_match(state, rec, &rec2)) {
1361 if (i == el->num_values) {
1363 return ISC_R_NOTFOUND;
1366 if (i < el->num_values-1) {
1367 memmove(&el->values[i], &el->values[i+1], sizeof(el->values[0])*((el->num_values-1)-i));
1371 if (el->num_values == 0) {
1372 /* delete the record */
1373 ret = ldb_delete(state->samdb, dn);
1375 /* modify the record */
1376 el->flags = LDB_FLAG_MOD_REPLACE;
1377 ret = ldb_modify(state->samdb, res->msgs[0]);
1379 if (ret != LDB_SUCCESS) {
1380 state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s",
1381 ldb_dn_get_linearized(dn), ldb_errstring(state->samdb));
1383 return ISC_R_FAILURE;
1386 state->log(ISC_LOG_INFO, "samba_dlz: subtracted rdataset %s '%s'", name, rdatastr);
1389 return ISC_R_SUCCESS;
1394 delete all records of the given type
1396 _PUBLIC_ isc_result_t dlz_delrdataset(const char *name, const char *type, void *dbdata, void *version)
1398 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1399 TALLOC_CTX *tmp_ctx;
1401 isc_result_t result;
1402 struct ldb_result *res;
1403 const char *attrs[] = { "dnsRecord", NULL };
1405 struct ldb_message_element *el;
1406 enum ndr_err_code ndr_err;
1407 enum dns_record_type dns_type;
1410 if (state->transaction_token != (void*)version) {
1411 state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version");
1412 return ISC_R_FAILURE;
1415 if (!b9_dns_type(type, &dns_type)) {
1416 state->log(ISC_LOG_INFO, "samba_dlz: bad dns type %s in delete", type);
1417 return ISC_R_FAILURE;
1420 tmp_ctx = talloc_new(state);
1422 /* find the DN of the record */
1423 result = b9_find_name_dn(state, name, tmp_ctx, &dn);
1424 if (result != ISC_R_SUCCESS) {
1425 talloc_free(tmp_ctx);
1429 /* get the existing records */
1430 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode");
1431 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1432 talloc_free(tmp_ctx);
1433 return ISC_R_NOTFOUND;
1436 /* there are existing records. We need to see if any match the type
1438 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
1439 if (el == NULL || el->num_values == 0) {
1440 talloc_free(tmp_ctx);
1441 return ISC_R_NOTFOUND;
1444 for (i=0; i<el->num_values; i++) {
1445 struct dnsp_DnssrvRpcRecord rec2;
1447 ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec2,
1448 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
1449 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1450 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
1451 ldb_dn_get_linearized(dn));
1452 talloc_free(tmp_ctx);
1453 return ISC_R_FAILURE;
1456 if (dns_type == rec2.wType) {
1457 if (i < el->num_values-1) {
1458 memmove(&el->values[i], &el->values[i+1],
1459 sizeof(el->values[0])*((el->num_values-1)-i));
1468 talloc_free(tmp_ctx);
1469 return ISC_R_FAILURE;
1472 if (el->num_values == 0) {
1473 /* delete the record */
1474 ret = ldb_delete(state->samdb, dn);
1476 /* modify the record */
1477 el->flags = LDB_FLAG_MOD_REPLACE;
1478 ret = ldb_modify(state->samdb, res->msgs[0]);
1480 if (ret != LDB_SUCCESS) {
1481 state->log(ISC_LOG_ERROR, "samba_dlz: failed to delete type %s in %s - %s",
1482 type, ldb_dn_get_linearized(dn), ldb_errstring(state->samdb));
1483 talloc_free(tmp_ctx);
1484 return ISC_R_FAILURE;
1487 state->log(ISC_LOG_INFO, "samba_dlz: deleted rdataset %s of type %s", name, type);
1489 talloc_free(tmp_ctx);
1490 return ISC_R_SUCCESS;