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/auth.h"
29 #include "auth/session.h"
30 #include "auth/gensec/gensec.h"
31 #include "librpc/gen_ndr/security.h"
32 #include "auth/credentials/credentials.h"
33 #include "system/kerberos.h"
34 #include "auth/kerberos/kerberos.h"
35 #include "gen_ndr/ndr_dnsp.h"
36 #include "gen_ndr/server_id.h"
37 #include "messaging/messaging.h"
38 #include "lib/cmdline/popt_common.h"
39 #include "dlz_minimal.h"
47 struct dlz_bind9_data {
48 struct b9_options options;
49 struct ldb_context *samdb;
50 struct tevent_context *ev_ctx;
51 struct loadparm_context *lp;
52 int *transaction_token;
55 /* Used for dynamic update */
56 struct smb_krb5_context *smb_krb5_ctx;
57 struct auth4_context *auth_context;
58 struct auth_session_info *session_info;
61 /* helper functions from the dlz_dlopen driver */
62 void (*log)(int level, const char *fmt, ...);
63 isc_result_t (*putrr)(dns_sdlzlookup_t *handle, const char *type,
64 dns_ttl_t ttl, const char *data);
65 isc_result_t (*putnamedrr)(dns_sdlzlookup_t *handle, const char *name,
66 const char *type, dns_ttl_t ttl, const char *data);
67 isc_result_t (*writeable_zone)(dns_view_t *view, const char *zone_name);
71 static const char *zone_prefixes[] = {
72 "CN=MicrosoftDNS,DC=DomainDnsZones",
73 "CN=MicrosoftDNS,DC=ForestDnsZones",
74 "CN=MicrosoftDNS,CN=System",
79 return the version of the API
81 _PUBLIC_ int dlz_version(unsigned int *flags)
83 return DLZ_DLOPEN_VERSION;
87 remember a helper function from the bind9 dlz_dlopen driver
89 static void b9_add_helper(struct dlz_bind9_data *state, const char *helper_name, void *ptr)
91 if (strcmp(helper_name, "log") == 0) {
94 if (strcmp(helper_name, "putrr") == 0) {
97 if (strcmp(helper_name, "putnamedrr") == 0) {
98 state->putnamedrr = ptr;
100 if (strcmp(helper_name, "writeable_zone") == 0) {
101 state->writeable_zone = ptr;
106 format a record for bind9
108 static bool b9_format(struct dlz_bind9_data *state,
110 struct dnsp_DnssrvRpcRecord *rec,
111 const char **type, const char **data)
116 switch (rec->wType) {
119 *data = rec->data.ipv4;
124 *data = rec->data.ipv6;
129 *data = rec->data.cname;
134 tmp = talloc_asprintf(mem_ctx, "\"%s\"", rec->data.txt.str[0]);
135 for (i=1; i<rec->data.txt.count; i++) {
136 tmp = talloc_asprintf_append(tmp, " \"%s\"", rec->data.txt.str[i]);
143 *data = rec->data.ptr;
148 *data = talloc_asprintf(mem_ctx, "%u %u %u %s",
149 rec->data.srv.wPriority,
150 rec->data.srv.wWeight,
152 rec->data.srv.nameTarget);
157 *data = talloc_asprintf(mem_ctx, "%u %s",
158 rec->data.mx.wPriority,
159 rec->data.mx.nameTarget);
164 *data = talloc_asprintf(mem_ctx, "%s %s",
171 *data = rec->data.ns;
178 /* we need to fake the authoritative nameserver to
179 * point at ourselves. This is how AD DNS servers
180 * force clients to send updates to the right local DC
182 mname = talloc_asprintf(mem_ctx, "%s.%s",
183 lpcfg_netbios_name(state->lp), lpcfg_dnsdomain(state->lp));
187 mname = strlower_talloc(mem_ctx, mname);
192 state->soa_serial = rec->data.soa.serial;
194 *data = talloc_asprintf(mem_ctx, "%s %s %u %u %u %u %u",
197 rec->data.soa.serial,
198 rec->data.soa.refresh,
200 rec->data.soa.expire,
201 rec->data.soa.minimum);
206 state->log(ISC_LOG_ERROR, "samba b9_putrr: unhandled record type %u",
214 static const struct {
215 enum dns_record_type dns_type;
219 { DNS_TYPE_A, "A" , false},
220 { DNS_TYPE_AAAA, "AAAA" , false},
221 { DNS_TYPE_CNAME, "CNAME" , true},
222 { DNS_TYPE_TXT, "TXT" , false},
223 { DNS_TYPE_PTR, "PTR" , false},
224 { DNS_TYPE_SRV, "SRV" , false},
225 { DNS_TYPE_MX, "MX" , false},
226 { DNS_TYPE_HINFO, "HINFO" , false},
227 { DNS_TYPE_NS, "NS" , false},
228 { DNS_TYPE_SOA, "SOA" , true},
233 see if a DNS type is single valued
235 static bool b9_single_valued(enum dns_record_type dns_type)
238 for (i=0; i<ARRAY_SIZE(dns_typemap); i++) {
239 if (dns_typemap[i].dns_type == dns_type) {
240 return dns_typemap[i].single_valued;
247 see if a DNS type is single valued
249 static bool b9_dns_type(const char *type, enum dns_record_type *dtype)
252 for (i=0; i<ARRAY_SIZE(dns_typemap); i++) {
253 if (strcasecmp(dns_typemap[i].typestr, type) == 0) {
254 *dtype = dns_typemap[i].dns_type;
262 #define DNS_PARSE_STR(ret, str, sep, saveptr) do { \
263 (ret) = strtok_r(str, sep, &saveptr); \
264 if ((ret) == NULL) return false; \
267 #define DNS_PARSE_UINT(ret, str, sep, saveptr) do { \
268 char *istr = strtok_r(str, sep, &saveptr); \
269 if ((istr) == NULL) return false; \
270 (ret) = strtoul(istr, NULL, 10); \
274 parse a record from bind9
276 static bool b9_parse(struct dlz_bind9_data *state,
277 const char *rdatastr,
278 struct dnsp_DnssrvRpcRecord *rec)
280 char *full_name, *dclass, *type;
281 char *str, *tmp, *saveptr=NULL;
284 str = talloc_strdup(rec, rdatastr);
289 /* parse the SDLZ string form */
290 DNS_PARSE_STR(full_name, str, "\t", saveptr);
291 DNS_PARSE_UINT(rec->dwTtlSeconds, NULL, "\t", saveptr);
292 DNS_PARSE_STR(dclass, NULL, "\t", saveptr);
293 DNS_PARSE_STR(type, NULL, "\t", saveptr);
295 /* construct the record */
296 for (i=0; i<ARRAY_SIZE(dns_typemap); i++) {
297 if (strcasecmp(type, dns_typemap[i].typestr) == 0) {
298 rec->wType = dns_typemap[i].dns_type;
302 if (i == ARRAY_SIZE(dns_typemap)) {
303 state->log(ISC_LOG_ERROR, "samba_dlz: unsupported record type '%s' for '%s'",
308 switch (rec->wType) {
310 DNS_PARSE_STR(rec->data.ipv4, NULL, " ", saveptr);
314 DNS_PARSE_STR(rec->data.ipv6, NULL, " ", saveptr);
318 DNS_PARSE_STR(rec->data.cname, NULL, " ", saveptr);
322 rec->data.txt.count = 0;
323 rec->data.txt.str = talloc_array(rec, const char *, rec->data.txt.count);
324 tmp = strtok_r(NULL, "\t", &saveptr);
326 rec->data.txt.str = talloc_realloc(rec, rec->data.txt.str, const char *,
327 rec->data.txt.count+1);
330 rec->data.txt.str[rec->data.txt.count] = talloc_strndup(rec, &tmp[1], strlen(tmp)-2);
332 rec->data.txt.str[rec->data.txt.count] = talloc_strdup(rec, tmp);
334 rec->data.txt.count++;
335 tmp = strtok_r(NULL, " ", &saveptr);
340 DNS_PARSE_STR(rec->data.ptr, NULL, " ", saveptr);
344 DNS_PARSE_UINT(rec->data.srv.wPriority, NULL, " ", saveptr);
345 DNS_PARSE_UINT(rec->data.srv.wWeight, NULL, " ", saveptr);
346 DNS_PARSE_UINT(rec->data.srv.wPort, NULL, " ", saveptr);
347 DNS_PARSE_STR(rec->data.srv.nameTarget, NULL, " ", saveptr);
351 DNS_PARSE_UINT(rec->data.mx.wPriority, NULL, " ", saveptr);
352 DNS_PARSE_STR(rec->data.mx.nameTarget, NULL, " ", saveptr);
356 DNS_PARSE_STR(rec->data.hinfo.cpu, NULL, " ", saveptr);
357 DNS_PARSE_STR(rec->data.hinfo.os, NULL, " ", saveptr);
361 DNS_PARSE_STR(rec->data.ns, NULL, " ", saveptr);
365 DNS_PARSE_STR(rec->data.soa.mname, NULL, " ", saveptr);
366 DNS_PARSE_STR(rec->data.soa.rname, NULL, " ", saveptr);
367 DNS_PARSE_UINT(rec->data.soa.serial, NULL, " ", saveptr);
368 DNS_PARSE_UINT(rec->data.soa.refresh, NULL, " ", saveptr);
369 DNS_PARSE_UINT(rec->data.soa.retry, NULL, " ", saveptr);
370 DNS_PARSE_UINT(rec->data.soa.expire, NULL, " ", saveptr);
371 DNS_PARSE_UINT(rec->data.soa.minimum, NULL, " ", saveptr);
375 state->log(ISC_LOG_ERROR, "samba b9_parse: unhandled record type %u",
380 /* we should be at the end of the buffer now */
381 if (strtok_r(NULL, "\t ", &saveptr) != NULL) {
382 state->log(ISC_LOG_ERROR, "samba b9_parse: expected data at end of string for '%s'");
390 send a resource recond to bind9
392 static isc_result_t b9_putrr(struct dlz_bind9_data *state,
393 void *handle, struct dnsp_DnssrvRpcRecord *rec,
397 const char *type, *data;
398 TALLOC_CTX *tmp_ctx = talloc_new(state);
400 if (!b9_format(state, tmp_ctx, rec, &type, &data)) {
401 return ISC_R_FAILURE;
405 talloc_free(tmp_ctx);
406 return ISC_R_NOMEMORY;
411 for (i=0; types[i]; i++) {
412 if (strcmp(types[i], type) == 0) break;
414 if (types[i] == NULL) {
416 return ISC_R_SUCCESS;
420 result = state->putrr(handle, type, rec->dwTtlSeconds, data);
421 if (result != ISC_R_SUCCESS) {
422 state->log(ISC_LOG_ERROR, "Failed to put rr");
424 talloc_free(tmp_ctx);
430 send a named resource recond to bind9
432 static isc_result_t b9_putnamedrr(struct dlz_bind9_data *state,
433 void *handle, const char *name,
434 struct dnsp_DnssrvRpcRecord *rec)
437 const char *type, *data;
438 TALLOC_CTX *tmp_ctx = talloc_new(state);
440 if (!b9_format(state, tmp_ctx, rec, &type, &data)) {
441 return ISC_R_FAILURE;
445 talloc_free(tmp_ctx);
446 return ISC_R_NOMEMORY;
449 result = state->putnamedrr(handle, name, type, rec->dwTtlSeconds, data);
450 if (result != ISC_R_SUCCESS) {
451 state->log(ISC_LOG_ERROR, "Failed to put named rr '%s'", name);
453 talloc_free(tmp_ctx);
460 static isc_result_t parse_options(struct dlz_bind9_data *state,
461 unsigned int argc, char *argv[],
462 struct b9_options *options)
466 struct poptOption long_options[] = {
467 { "url", 'H', POPT_ARG_STRING, &options->url, 0, "database URL", "URL" },
468 { "debug", 'd', POPT_ARG_STRING, &options->debug, 0, "debug level", "DEBUG" },
472 pc = poptGetContext("dlz_bind9", argc, (const char **)argv, long_options,
473 POPT_CONTEXT_KEEP_FIRST);
474 while ((opt = poptGetNextOpt(pc)) != -1) {
477 state->log(ISC_LOG_ERROR, "dlz_bind9: Invalid option %s: %s",
478 poptBadOption(pc, 0), poptStrerror(opt));
479 return ISC_R_FAILURE;
483 return ISC_R_SUCCESS;
488 * Create session info from PAC
489 * This is called as auth_context->generate_session_info_pac()
491 static NTSTATUS b9_generate_session_info_pac(struct auth4_context *auth_context,
493 struct smb_krb5_context *smb_krb5_context,
495 const char *principal_name,
496 const struct tsocket_address *remote_addr,
497 uint32_t session_info_flags,
498 struct auth_session_info **session_info)
501 struct auth_user_info_dc *user_info_dc;
504 tmp_ctx = talloc_new(mem_ctx);
505 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
507 status = kerberos_pac_blob_to_user_info_dc(tmp_ctx,
509 smb_krb5_context->krb5_context,
513 if (!NT_STATUS_IS_OK(status)) {
514 talloc_free(tmp_ctx);
518 if (user_info_dc->info->authenticated) {
519 session_info_flags |= AUTH_SESSION_INFO_AUTHENTICATED;
522 session_info_flags |= AUTH_SESSION_INFO_SIMPLE_PRIVILEGES;
524 status = auth_generate_session_info(mem_ctx, NULL, NULL, user_info_dc,
525 session_info_flags, session_info);
526 if (!NT_STATUS_IS_OK(status)) {
527 talloc_free(tmp_ctx);
531 talloc_free(tmp_ctx);
537 called to initialise the driver
539 _PUBLIC_ isc_result_t dlz_create(const char *dlzname,
540 unsigned int argc, char *argv[],
543 struct dlz_bind9_data *state;
544 const char *helper_name;
550 state = talloc_zero(NULL, struct dlz_bind9_data);
552 return ISC_R_NOMEMORY;
555 /* fill in the helper functions */
556 va_start(ap, dbdata);
557 while ((helper_name = va_arg(ap, const char *)) != NULL) {
558 b9_add_helper(state, helper_name, va_arg(ap, void*));
562 /* Do not install samba signal handlers */
563 fault_setup_disable();
566 setup_logging("samba_dlz", DEBUG_DEFAULT_STDERR);
568 state->ev_ctx = s4_event_context_init(state);
569 if (state->ev_ctx == NULL) {
570 result = ISC_R_NOMEMORY;
574 result = parse_options(state, argc, argv, &state->options);
575 if (result != ISC_R_SUCCESS) {
579 state->lp = loadparm_init_global(true);
580 if (state->lp == NULL) {
581 result = ISC_R_NOMEMORY;
585 if (state->options.debug) {
586 lpcfg_do_global_parameter(state->lp, "log level", state->options.debug);
588 lpcfg_do_global_parameter(state->lp, "log level", "0");
591 if (smb_krb5_init_context(state, state->ev_ctx, state->lp, &state->smb_krb5_ctx) != 0) {
592 result = ISC_R_NOMEMORY;
596 nt_status = gensec_init();
597 if (!NT_STATUS_IS_OK(nt_status)) {
598 result = ISC_R_NOMEMORY;
602 state->auth_context = talloc_zero(state, struct auth4_context);
603 if (state->auth_context == NULL) {
604 result = ISC_R_NOMEMORY;
608 if (state->options.url == NULL) {
609 state->options.url = lpcfg_private_path(state, state->lp, "dns/sam.ldb");
610 if (state->options.url == NULL) {
611 result = ISC_R_NOMEMORY;
616 state->samdb = samdb_connect_url(state, state->ev_ctx, state->lp,
617 system_session(state->lp), 0, state->options.url);
618 if (state->samdb == NULL) {
619 state->log(ISC_LOG_ERROR, "samba_dlz: Failed to connect to %s",
621 result = ISC_R_FAILURE;
625 dn = ldb_get_default_basedn(state->samdb);
627 state->log(ISC_LOG_ERROR, "samba_dlz: Unable to get basedn for %s - %s",
628 state->options.url, ldb_errstring(state->samdb));
629 result = ISC_R_FAILURE;
633 state->log(ISC_LOG_INFO, "samba_dlz: started for DN %s",
634 ldb_dn_get_linearized(dn));
636 state->auth_context->event_ctx = state->ev_ctx;
637 state->auth_context->lp_ctx = state->lp;
638 state->auth_context->sam_ctx = state->samdb;
639 state->auth_context->generate_session_info_pac = b9_generate_session_info_pac;
643 return ISC_R_SUCCESS;
653 _PUBLIC_ void dlz_destroy(void *dbdata)
655 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
656 state->log(ISC_LOG_INFO, "samba_dlz: shutting down");
662 return the base DN for a zone
664 static isc_result_t b9_find_zone_dn(struct dlz_bind9_data *state, const char *zone_name,
665 TALLOC_CTX *mem_ctx, struct ldb_dn **zone_dn)
668 TALLOC_CTX *tmp_ctx = talloc_new(state);
669 const char *attrs[] = { NULL };
672 for (i=0; zone_prefixes[i]; i++) {
674 struct ldb_result *res;
676 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
678 talloc_free(tmp_ctx);
679 return ISC_R_NOMEMORY;
682 if (!ldb_dn_add_child_fmt(dn, "DC=%s,%s", zone_name, zone_prefixes[i])) {
683 talloc_free(tmp_ctx);
684 return ISC_R_NOMEMORY;
687 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsZone");
688 if (ret == LDB_SUCCESS) {
689 if (zone_dn != NULL) {
690 *zone_dn = talloc_steal(mem_ctx, dn);
692 talloc_free(tmp_ctx);
693 return ISC_R_SUCCESS;
698 talloc_free(tmp_ctx);
699 return ISC_R_NOTFOUND;
704 return the DN for a name. The record does not need to exist, but the
707 static isc_result_t b9_find_name_dn(struct dlz_bind9_data *state, const char *name,
708 TALLOC_CTX *mem_ctx, struct ldb_dn **dn)
712 /* work through the name piece by piece, until we find a zone */
715 result = b9_find_zone_dn(state, p, mem_ctx, dn);
716 if (result == ISC_R_SUCCESS) {
717 /* we found a zone, now extend the DN to get
722 ret = ldb_dn_add_child_fmt(*dn, "DC=@");
724 ret = ldb_dn_add_child_fmt(*dn, "DC=%.*s", (int)(p-name)-1, name);
728 return ISC_R_NOMEMORY;
730 return ISC_R_SUCCESS;
738 return ISC_R_NOTFOUND;
743 see if we handle a given zone
745 _PUBLIC_ isc_result_t dlz_findzonedb(void *dbdata, const char *name)
747 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
748 return b9_find_zone_dn(state, name, NULL, NULL);
755 static isc_result_t dlz_lookup_types(struct dlz_bind9_data *state,
756 const char *zone, const char *name,
757 dns_sdlzlookup_t *lookup,
760 TALLOC_CTX *tmp_ctx = talloc_new(state);
761 const char *attrs[] = { "dnsRecord", NULL };
762 int ret = LDB_SUCCESS, i;
763 struct ldb_result *res;
764 struct ldb_message_element *el;
767 for (i=0; zone_prefixes[i]; i++) {
768 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
770 talloc_free(tmp_ctx);
771 return ISC_R_NOMEMORY;
774 if (!ldb_dn_add_child_fmt(dn, "DC=%s,DC=%s,%s", name, zone, zone_prefixes[i])) {
775 talloc_free(tmp_ctx);
776 return ISC_R_NOMEMORY;
779 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
780 attrs, "objectClass=dnsNode");
781 if (ret == LDB_SUCCESS) {
785 if (ret != LDB_SUCCESS) {
786 talloc_free(tmp_ctx);
787 return ISC_R_NOTFOUND;
790 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
791 if (el == NULL || el->num_values == 0) {
792 talloc_free(tmp_ctx);
793 return ISC_R_NOTFOUND;
796 for (i=0; i<el->num_values; i++) {
797 struct dnsp_DnssrvRpcRecord rec;
798 enum ndr_err_code ndr_err;
801 ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec,
802 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
803 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
804 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
805 ldb_dn_get_linearized(dn));
806 talloc_free(tmp_ctx);
807 return ISC_R_FAILURE;
810 result = b9_putrr(state, lookup, &rec, types);
811 if (result != ISC_R_SUCCESS) {
812 talloc_free(tmp_ctx);
817 talloc_free(tmp_ctx);
818 return ISC_R_SUCCESS;
824 _PUBLIC_ isc_result_t dlz_lookup(const char *zone, const char *name,
825 void *dbdata, dns_sdlzlookup_t *lookup)
827 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
828 return dlz_lookup_types(state, zone, name, lookup, NULL);
833 see if a zone transfer is allowed
835 _PUBLIC_ isc_result_t dlz_allowzonexfr(void *dbdata, const char *name, const char *client)
837 /* just say yes for all our zones for now */
838 return dlz_findzonedb(dbdata, name);
842 perform a zone transfer
844 _PUBLIC_ isc_result_t dlz_allnodes(const char *zone, void *dbdata,
845 dns_sdlzallnodes_t *allnodes)
847 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
848 const char *attrs[] = { "dnsRecord", NULL };
849 int ret = LDB_SUCCESS, i, j;
851 struct ldb_result *res;
852 TALLOC_CTX *tmp_ctx = talloc_new(state);
854 for (i=0; zone_prefixes[i]; i++) {
855 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
857 talloc_free(tmp_ctx);
858 return ISC_R_NOMEMORY;
861 if (!ldb_dn_add_child_fmt(dn, "DC=%s,%s", zone, zone_prefixes[i])) {
862 talloc_free(tmp_ctx);
863 return ISC_R_NOMEMORY;
866 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
867 attrs, "objectClass=dnsNode");
868 if (ret == LDB_SUCCESS) {
872 if (ret != LDB_SUCCESS) {
873 talloc_free(tmp_ctx);
874 return ISC_R_NOTFOUND;
877 for (i=0; i<res->count; i++) {
878 struct ldb_message_element *el;
879 TALLOC_CTX *el_ctx = talloc_new(tmp_ctx);
880 const char *rdn, *name;
881 const struct ldb_val *v;
883 el = ldb_msg_find_element(res->msgs[i], "dnsRecord");
884 if (el == NULL || el->num_values == 0) {
885 state->log(ISC_LOG_INFO, "failed to find dnsRecord for %s",
886 ldb_dn_get_linearized(dn));
891 v = ldb_dn_get_rdn_val(res->msgs[i]->dn);
893 state->log(ISC_LOG_INFO, "failed to find RDN for %s",
894 ldb_dn_get_linearized(dn));
899 rdn = talloc_strndup(el_ctx, (char *)v->data, v->length);
901 talloc_free(tmp_ctx);
902 return ISC_R_NOMEMORY;
905 if (strcmp(rdn, "@") == 0) {
908 name = talloc_asprintf(el_ctx, "%s.%s", rdn, zone);
911 talloc_free(tmp_ctx);
912 return ISC_R_NOMEMORY;
915 for (j=0; j<el->num_values; j++) {
916 struct dnsp_DnssrvRpcRecord rec;
917 enum ndr_err_code ndr_err;
920 ndr_err = ndr_pull_struct_blob(&el->values[j], el_ctx, &rec,
921 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
922 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
923 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
924 ldb_dn_get_linearized(dn));
928 result = b9_putnamedrr(state, allnodes, name, &rec);
929 if (result != ISC_R_SUCCESS) {
935 talloc_free(tmp_ctx);
937 return ISC_R_SUCCESS;
944 _PUBLIC_ isc_result_t dlz_newversion(const char *zone, void *dbdata, void **versionp)
946 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
948 state->log(ISC_LOG_INFO, "samba_dlz: starting transaction on zone %s", zone);
950 if (state->transaction_token != NULL) {
951 state->log(ISC_LOG_INFO, "samba_dlz: transaction already started for zone %s", zone);
952 return ISC_R_FAILURE;
955 state->transaction_token = talloc_zero(state, int);
956 if (state->transaction_token == NULL) {
957 return ISC_R_NOMEMORY;
960 if (ldb_transaction_start(state->samdb) != LDB_SUCCESS) {
961 state->log(ISC_LOG_INFO, "samba_dlz: failed to start a transaction for zone %s", zone);
962 talloc_free(state->transaction_token);
963 state->transaction_token = NULL;
964 return ISC_R_FAILURE;
967 *versionp = (void *)state->transaction_token;
969 return ISC_R_SUCCESS;
975 _PUBLIC_ void dlz_closeversion(const char *zone, isc_boolean_t commit,
976 void *dbdata, void **versionp)
978 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
980 if (state->transaction_token != (int *)*versionp) {
981 state->log(ISC_LOG_INFO, "samba_dlz: transaction not started for zone %s", zone);
986 if (ldb_transaction_commit(state->samdb) != LDB_SUCCESS) {
987 state->log(ISC_LOG_INFO, "samba_dlz: failed to commit a transaction for zone %s", zone);
990 state->log(ISC_LOG_INFO, "samba_dlz: committed transaction on zone %s", zone);
992 if (ldb_transaction_cancel(state->samdb) != LDB_SUCCESS) {
993 state->log(ISC_LOG_INFO, "samba_dlz: failed to cancel a transaction for zone %s", zone);
996 state->log(ISC_LOG_INFO, "samba_dlz: cancelling transaction on zone %s", zone);
999 talloc_free(state->transaction_token);
1000 state->transaction_token = NULL;
1006 see if there is a SOA record for a zone
1008 static bool b9_has_soa(struct dlz_bind9_data *state, struct ldb_dn *dn, const char *zone)
1010 const char *attrs[] = { "dnsRecord", NULL };
1011 struct ldb_result *res;
1012 struct ldb_message_element *el;
1013 TALLOC_CTX *tmp_ctx = talloc_new(state);
1016 if (!ldb_dn_add_child_fmt(dn, "DC=@,DC=%s", zone)) {
1017 talloc_free(tmp_ctx);
1021 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
1022 attrs, "objectClass=dnsNode");
1023 if (ret != LDB_SUCCESS) {
1024 talloc_free(tmp_ctx);
1028 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
1030 talloc_free(tmp_ctx);
1033 for (i=0; i<el->num_values; i++) {
1034 struct dnsp_DnssrvRpcRecord rec;
1035 enum ndr_err_code ndr_err;
1037 ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec,
1038 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
1039 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1042 if (rec.wType == DNS_TYPE_SOA) {
1043 talloc_free(tmp_ctx);
1048 talloc_free(tmp_ctx);
1053 configure a writeable zone
1055 _PUBLIC_ isc_result_t dlz_configure(dns_view_t *view, void *dbdata)
1057 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1058 TALLOC_CTX *tmp_ctx;
1062 state->log(ISC_LOG_INFO, "samba_dlz: starting configure");
1063 if (state->writeable_zone == NULL) {
1064 state->log(ISC_LOG_INFO, "samba_dlz: no writeable_zone method available");
1065 return ISC_R_FAILURE;
1068 tmp_ctx = talloc_new(state);
1070 for (i=0; zone_prefixes[i]; i++) {
1071 const char *attrs[] = { "name", NULL };
1073 struct ldb_result *res;
1075 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
1077 talloc_free(tmp_ctx);
1078 return ISC_R_NOMEMORY;
1081 if (!ldb_dn_add_child_fmt(dn, "%s", zone_prefixes[i])) {
1082 talloc_free(tmp_ctx);
1083 return ISC_R_NOMEMORY;
1086 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
1087 attrs, "objectClass=dnsZone");
1088 if (ret != LDB_SUCCESS) {
1092 for (j=0; j<res->count; j++) {
1093 isc_result_t result;
1094 const char *zone = ldb_msg_find_attr_as_string(res->msgs[j], "name", NULL);
1095 struct ldb_dn *zone_dn;
1100 zone_dn = ldb_dn_copy(tmp_ctx, dn);
1101 if (zone_dn == NULL) {
1102 talloc_free(tmp_ctx);
1103 return ISC_R_NOMEMORY;
1106 if (!b9_has_soa(state, zone_dn, zone)) {
1109 result = state->writeable_zone(view, zone);
1110 if (result != ISC_R_SUCCESS) {
1111 state->log(ISC_LOG_ERROR, "samba_dlz: Failed to configure zone '%s'",
1113 talloc_free(tmp_ctx);
1116 state->log(ISC_LOG_INFO, "samba_dlz: configured writeable zone '%s'", zone);
1120 talloc_free(tmp_ctx);
1121 return ISC_R_SUCCESS;
1125 authorize a zone update
1127 _PUBLIC_ isc_boolean_t dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr,
1128 const char *type, const char *key, uint32_t keydatalen, uint8_t *keydata,
1131 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1132 TALLOC_CTX *tmp_ctx;
1134 struct cli_credentials *server_credentials;
1139 struct gensec_security *gensec_ctx;
1140 struct auth_session_info *session_info;
1142 isc_result_t result;
1143 struct ldb_result *res;
1144 const char * attrs[] = { NULL };
1145 uint32_t access_mask;
1147 /* Remove cached credentials, if any */
1148 if (state->session_info) {
1149 talloc_free(state->session_info);
1150 state->session_info = NULL;
1152 if (state->update_name) {
1153 talloc_free(state->update_name);
1154 state->update_name = NULL;
1157 tmp_ctx = talloc_new(NULL);
1158 if (tmp_ctx == NULL) {
1159 state->log(ISC_LOG_ERROR, "samba_dlz: no memory");
1163 ap_req = data_blob_const(keydata, keydatalen);
1164 server_credentials = cli_credentials_init(tmp_ctx);
1165 if (!server_credentials) {
1166 state->log(ISC_LOG_ERROR, "samba_dlz: failed to init server credentials");
1167 talloc_free(tmp_ctx);
1171 cli_credentials_set_krb5_context(server_credentials, state->smb_krb5_ctx);
1172 cli_credentials_set_conf(server_credentials, state->lp);
1174 keytab_name = talloc_asprintf(tmp_ctx, "file:%s/dns.keytab",
1175 lpcfg_private_dir(state->lp));
1176 ret = cli_credentials_set_keytab_name(server_credentials, state->lp, keytab_name,
1179 state->log(ISC_LOG_ERROR, "samba_dlz: failed to obtain server credentials from %s",
1181 talloc_free(tmp_ctx);
1184 talloc_free(keytab_name);
1186 nt_status = gensec_server_start(tmp_ctx,
1187 lpcfg_gensec_settings(tmp_ctx, state->lp),
1188 state->auth_context, &gensec_ctx);
1189 if (!NT_STATUS_IS_OK(nt_status)) {
1190 state->log(ISC_LOG_ERROR, "samba_dlz: failed to start gensec server");
1191 talloc_free(tmp_ctx);
1195 gensec_set_credentials(gensec_ctx, server_credentials);
1197 nt_status = gensec_start_mech_by_name(gensec_ctx, "spnego");
1198 if (!NT_STATUS_IS_OK(nt_status)) {
1199 state->log(ISC_LOG_ERROR, "samba_dlz: failed to start spnego");
1200 talloc_free(tmp_ctx);
1204 nt_status = gensec_update(gensec_ctx, tmp_ctx, state->ev_ctx, ap_req, &ap_req);
1205 if (!NT_STATUS_IS_OK(nt_status)) {
1206 state->log(ISC_LOG_ERROR, "samba_dlz: spnego update failed");
1207 talloc_free(tmp_ctx);
1211 nt_status = gensec_session_info(gensec_ctx, tmp_ctx, &session_info);
1212 if (!NT_STATUS_IS_OK(nt_status)) {
1213 state->log(ISC_LOG_ERROR, "samba_dlz: failed to create session info");
1214 talloc_free(tmp_ctx);
1218 /* Get the DN from name */
1219 result = b9_find_name_dn(state, name, tmp_ctx, &dn);
1220 if (result != ISC_R_SUCCESS) {
1221 state->log(ISC_LOG_ERROR, "samba_dlz: failed to find name %s", name);
1222 talloc_free(tmp_ctx);
1226 /* make sure the dn exists, or find parent dn in case new object is being added */
1227 ldb_ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
1228 attrs, "objectClass=dnsNode");
1229 if (ldb_ret == LDB_ERR_NO_SUCH_OBJECT) {
1230 ldb_dn_remove_child_components(dn, 1);
1231 access_mask = SEC_ADS_CREATE_CHILD;
1233 } else if (ldb_ret == LDB_SUCCESS) {
1234 access_mask = SEC_STD_REQUIRED | SEC_ADS_SELF_WRITE;
1237 talloc_free(tmp_ctx);
1242 ldb_ret = dsdb_check_access_on_dn(state->samdb, tmp_ctx, dn,
1243 session_info->security_token,
1245 if (ldb_ret != LDB_SUCCESS) {
1246 state->log(ISC_LOG_INFO,
1247 "samba_dlz: disallowing update of signer=%s name=%s type=%s error=%s",
1248 signer, name, type, ldb_strerror(ldb_ret));
1249 talloc_free(tmp_ctx);
1253 /* Cache session_info, so it can be used in the actual add/delete operation */
1254 state->update_name = talloc_strdup(state, name);
1255 if (state->update_name == NULL) {
1256 state->log(ISC_LOG_ERROR, "samba_dlz: memory allocation error");
1257 talloc_free(tmp_ctx);
1260 state->session_info = talloc_steal(state, session_info);
1262 state->log(ISC_LOG_INFO, "samba_dlz: allowing update of signer=%s name=%s tcpaddr=%s type=%s key=%s",
1263 signer, name, tcpaddr, type, key);
1265 talloc_free(tmp_ctx);
1273 static isc_result_t b9_add_record(struct dlz_bind9_data *state, const char *name,
1275 struct dnsp_DnssrvRpcRecord *rec)
1277 struct ldb_message *msg;
1278 enum ndr_err_code ndr_err;
1282 msg = ldb_msg_new(rec);
1284 return ISC_R_NOMEMORY;
1287 ret = ldb_msg_add_string(msg, "objectClass", "dnsNode");
1288 if (ret != LDB_SUCCESS) {
1289 return ISC_R_FAILURE;
1292 ndr_err = ndr_push_struct_blob(&v, rec, rec, (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
1293 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1294 return ISC_R_FAILURE;
1296 ret = ldb_msg_add_value(msg, "dnsRecord", &v, NULL);
1297 if (ret != LDB_SUCCESS) {
1298 return ISC_R_FAILURE;
1301 ret = ldb_add(state->samdb, msg);
1302 if (ret != LDB_SUCCESS) {
1303 return ISC_R_FAILURE;
1306 return ISC_R_SUCCESS;
1310 see if two DNS names are the same
1312 static bool dns_name_equal(const char *name1, const char *name2)
1314 size_t len1 = strlen(name1);
1315 size_t len2 = strlen(name2);
1316 if (name1[len1-1] == '.') len1--;
1317 if (name2[len2-1] == '.') len2--;
1321 return strncasecmp_m(name1, name2, len1) == 0;
1326 see if two dns records match
1328 static bool b9_record_match(struct dlz_bind9_data *state,
1329 struct dnsp_DnssrvRpcRecord *rec1, struct dnsp_DnssrvRpcRecord *rec2)
1334 if (rec1->wType != rec2->wType) {
1337 /* see if this type is single valued */
1338 if (b9_single_valued(rec1->wType)) {
1342 /* see if the data matches */
1343 switch (rec1->wType) {
1345 return strcmp(rec1->data.ipv4, rec2->data.ipv4) == 0;
1347 return strcmp(rec1->data.ipv6, rec2->data.ipv6) == 0;
1348 case DNS_TYPE_CNAME:
1349 return dns_name_equal(rec1->data.cname, rec2->data.cname);
1351 status = (rec1->data.txt.count == rec2->data.txt.count);
1352 if (!status) return status;
1353 for (i=0; i<rec1->data.txt.count; i++) {
1354 status &= (strcmp(rec1->data.txt.str[i], rec2->data.txt.str[i]) == 0);
1358 return dns_name_equal(rec1->data.ptr, rec2->data.ptr);
1360 return dns_name_equal(rec1->data.ns, rec2->data.ns);
1363 return rec1->data.srv.wPriority == rec2->data.srv.wPriority &&
1364 rec1->data.srv.wWeight == rec2->data.srv.wWeight &&
1365 rec1->data.srv.wPort == rec2->data.srv.wPort &&
1366 dns_name_equal(rec1->data.srv.nameTarget, rec2->data.srv.nameTarget);
1369 return rec1->data.mx.wPriority == rec2->data.mx.wPriority &&
1370 dns_name_equal(rec1->data.mx.nameTarget, rec2->data.mx.nameTarget);
1372 case DNS_TYPE_HINFO:
1373 return strcmp(rec1->data.hinfo.cpu, rec2->data.hinfo.cpu) == 0 &&
1374 strcmp(rec1->data.hinfo.os, rec2->data.hinfo.os) == 0;
1377 return dns_name_equal(rec1->data.soa.mname, rec2->data.soa.mname) &&
1378 dns_name_equal(rec1->data.soa.rname, rec2->data.soa.rname) &&
1379 rec1->data.soa.serial == rec2->data.soa.serial &&
1380 rec1->data.soa.refresh == rec2->data.soa.refresh &&
1381 rec1->data.soa.retry == rec2->data.soa.retry &&
1382 rec1->data.soa.expire == rec2->data.soa.expire &&
1383 rec1->data.soa.minimum == rec2->data.soa.minimum;
1385 state->log(ISC_LOG_ERROR, "samba b9_putrr: unhandled record type %u",
1394 * Update session_info on samdb using the cached credentials
1396 static bool b9_set_session_info(struct dlz_bind9_data *state, const char *name)
1400 if (state->update_name == NULL || state->session_info == NULL) {
1401 state->log(ISC_LOG_ERROR, "samba_dlz: invalid credentials");
1405 /* Do not use client credentials, if we not updating the client specified name */
1406 if (strcmp(state->update_name, name) != 0) {
1410 ret = ldb_set_opaque(state->samdb, "sessionInfo", state->session_info);
1411 if (ret != LDB_SUCCESS) {
1412 state->log(ISC_LOG_ERROR, "samba_dlz: unable to set session info");
1420 * Reset session_info on samdb as system session
1422 static void b9_reset_session_info(struct dlz_bind9_data *state)
1424 ldb_set_opaque(state->samdb, "sessionInfo", system_session(state->lp));
1428 add or modify a rdataset
1430 _PUBLIC_ isc_result_t dlz_addrdataset(const char *name, const char *rdatastr, void *dbdata, void *version)
1432 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1433 struct dnsp_DnssrvRpcRecord *rec;
1435 isc_result_t result;
1436 struct ldb_result *res;
1437 const char *attrs[] = { "dnsRecord", NULL };
1439 struct ldb_message_element *el;
1440 enum ndr_err_code ndr_err;
1443 if (state->transaction_token != (void*)version) {
1444 state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version");
1445 return ISC_R_FAILURE;
1448 rec = talloc_zero(state, struct dnsp_DnssrvRpcRecord);
1450 return ISC_R_NOMEMORY;
1453 unix_to_nt_time(&t, time(NULL));
1454 t /= 10*1000*1000; /* convert to seconds (NT time is in 100ns units) */
1455 t /= 3600; /* convert to hours */
1457 rec->rank = DNS_RANK_ZONE;
1458 rec->dwSerial = state->soa_serial;
1459 rec->dwTimeStamp = (uint32_t)t;
1461 if (!b9_parse(state, rdatastr, rec)) {
1462 state->log(ISC_LOG_INFO, "samba_dlz: failed to parse rdataset '%s'", rdatastr);
1464 return ISC_R_FAILURE;
1467 /* find the DN of the record */
1468 result = b9_find_name_dn(state, name, rec, &dn);
1469 if (result != ISC_R_SUCCESS) {
1474 /* get any existing records */
1475 ret = ldb_search(state->samdb, rec, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode");
1476 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1477 if (!b9_set_session_info(state, name)) {
1479 return ISC_R_FAILURE;
1481 result = b9_add_record(state, name, dn, rec);
1482 b9_reset_session_info(state);
1484 if (result == ISC_R_SUCCESS) {
1485 state->log(ISC_LOG_INFO, "samba_dlz: added %s %s", name, rdatastr);
1490 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
1492 ret = ldb_msg_add_empty(res->msgs[0], "dnsRecord", LDB_FLAG_MOD_ADD, &el);
1493 if (ret != LDB_SUCCESS) {
1494 state->log(ISC_LOG_ERROR, "samba_dlz: failed to add dnsRecord for %s",
1495 ldb_dn_get_linearized(dn));
1497 return ISC_R_FAILURE;
1501 /* there are existing records. We need to see if this will
1502 * replace a record or add to it
1504 for (i=0; i<el->num_values; i++) {
1505 struct dnsp_DnssrvRpcRecord rec2;
1507 ndr_err = ndr_pull_struct_blob(&el->values[i], rec, &rec2,
1508 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
1509 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1510 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
1511 ldb_dn_get_linearized(dn));
1513 return ISC_R_FAILURE;
1516 if (b9_record_match(state, rec, &rec2)) {
1520 if (i == el->num_values) {
1521 /* adding a new value */
1522 el->values = talloc_realloc(el, el->values, struct ldb_val, el->num_values+1);
1523 if (el->values == NULL) {
1525 return ISC_R_NOMEMORY;
1530 ndr_err = ndr_push_struct_blob(&el->values[i], rec, rec,
1531 (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
1532 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1533 state->log(ISC_LOG_ERROR, "samba_dlz: failed to push dnsRecord for %s",
1534 ldb_dn_get_linearized(dn));
1536 return ISC_R_FAILURE;
1540 if (!b9_set_session_info(state, name)) {
1542 return ISC_R_FAILURE;
1545 /* modify the record */
1546 el->flags = LDB_FLAG_MOD_REPLACE;
1547 ret = ldb_modify(state->samdb, res->msgs[0]);
1548 b9_reset_session_info(state);
1549 if (ret != LDB_SUCCESS) {
1550 state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s",
1551 ldb_dn_get_linearized(dn), ldb_errstring(state->samdb));
1553 return ISC_R_FAILURE;
1556 state->log(ISC_LOG_INFO, "samba_dlz: added rdataset %s '%s'", name, rdatastr);
1559 return ISC_R_SUCCESS;
1565 _PUBLIC_ isc_result_t dlz_subrdataset(const char *name, const char *rdatastr, void *dbdata, void *version)
1567 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1568 struct dnsp_DnssrvRpcRecord *rec;
1570 isc_result_t result;
1571 struct ldb_result *res;
1572 const char *attrs[] = { "dnsRecord", NULL };
1574 struct ldb_message_element *el;
1575 enum ndr_err_code ndr_err;
1577 if (state->transaction_token != (void*)version) {
1578 state->log(ISC_LOG_ERROR, "samba_dlz: bad transaction version");
1579 return ISC_R_FAILURE;
1582 rec = talloc_zero(state, struct dnsp_DnssrvRpcRecord);
1584 return ISC_R_NOMEMORY;
1587 if (!b9_parse(state, rdatastr, rec)) {
1588 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse rdataset '%s'", rdatastr);
1590 return ISC_R_FAILURE;
1593 /* find the DN of the record */
1594 result = b9_find_name_dn(state, name, rec, &dn);
1595 if (result != ISC_R_SUCCESS) {
1600 /* get the existing records */
1601 ret = ldb_search(state->samdb, rec, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode");
1602 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1604 return ISC_R_NOTFOUND;
1607 /* there are existing records. We need to see if any match
1609 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
1610 if (el == NULL || el->num_values == 0) {
1611 state->log(ISC_LOG_ERROR, "samba_dlz: no dnsRecord attribute for %s",
1612 ldb_dn_get_linearized(dn));
1614 return ISC_R_FAILURE;
1617 for (i=0; i<el->num_values; i++) {
1618 struct dnsp_DnssrvRpcRecord rec2;
1620 ndr_err = ndr_pull_struct_blob(&el->values[i], rec, &rec2,
1621 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
1622 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1623 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
1624 ldb_dn_get_linearized(dn));
1626 return ISC_R_FAILURE;
1629 if (b9_record_match(state, rec, &rec2)) {
1633 if (i == el->num_values) {
1635 return ISC_R_NOTFOUND;
1638 if (i < el->num_values-1) {
1639 memmove(&el->values[i], &el->values[i+1], sizeof(el->values[0])*((el->num_values-1)-i));
1643 if (!b9_set_session_info(state, name)) {
1645 return ISC_R_FAILURE;
1648 if (el->num_values == 0) {
1649 el->flags = LDB_FLAG_MOD_DELETE;
1651 el->flags = LDB_FLAG_MOD_REPLACE;
1653 ret = ldb_modify(state->samdb, res->msgs[0]);
1655 b9_reset_session_info(state);
1656 if (ret != LDB_SUCCESS) {
1657 state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s",
1658 ldb_dn_get_linearized(dn), ldb_errstring(state->samdb));
1660 return ISC_R_FAILURE;
1663 state->log(ISC_LOG_INFO, "samba_dlz: subtracted rdataset %s '%s'", name, rdatastr);
1666 return ISC_R_SUCCESS;
1671 delete all records of the given type
1673 _PUBLIC_ isc_result_t dlz_delrdataset(const char *name, const char *type, void *dbdata, void *version)
1675 struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1676 TALLOC_CTX *tmp_ctx;
1678 isc_result_t result;
1679 struct ldb_result *res;
1680 const char *attrs[] = { "dnsRecord", NULL };
1682 struct ldb_message_element *el;
1683 enum ndr_err_code ndr_err;
1684 enum dns_record_type dns_type;
1687 if (state->transaction_token != (void*)version) {
1688 state->log(ISC_LOG_ERROR, "samba_dlz: bad transaction version");
1689 return ISC_R_FAILURE;
1692 if (!b9_dns_type(type, &dns_type)) {
1693 state->log(ISC_LOG_ERROR, "samba_dlz: bad dns type %s in delete", type);
1694 return ISC_R_FAILURE;
1697 tmp_ctx = talloc_new(state);
1699 /* find the DN of the record */
1700 result = b9_find_name_dn(state, name, tmp_ctx, &dn);
1701 if (result != ISC_R_SUCCESS) {
1702 talloc_free(tmp_ctx);
1706 /* get the existing records */
1707 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode");
1708 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1709 talloc_free(tmp_ctx);
1710 return ISC_R_NOTFOUND;
1713 /* there are existing records. We need to see if any match the type
1715 el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
1716 if (el == NULL || el->num_values == 0) {
1717 talloc_free(tmp_ctx);
1718 return ISC_R_NOTFOUND;
1721 for (i=0; i<el->num_values; i++) {
1722 struct dnsp_DnssrvRpcRecord rec2;
1724 ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec2,
1725 (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
1726 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1727 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
1728 ldb_dn_get_linearized(dn));
1729 talloc_free(tmp_ctx);
1730 return ISC_R_FAILURE;
1733 if (dns_type == rec2.wType) {
1734 if (i < el->num_values-1) {
1735 memmove(&el->values[i], &el->values[i+1],
1736 sizeof(el->values[0])*((el->num_values-1)-i));
1745 talloc_free(tmp_ctx);
1746 return ISC_R_FAILURE;
1749 if (!b9_set_session_info(state, name)) {
1750 talloc_free(tmp_ctx);
1751 return ISC_R_FAILURE;
1754 if (el->num_values == 0) {
1755 el->flags = LDB_FLAG_MOD_DELETE;
1757 el->flags = LDB_FLAG_MOD_REPLACE;
1759 ret = ldb_modify(state->samdb, res->msgs[0]);
1761 b9_reset_session_info(state);
1762 if (ret != LDB_SUCCESS) {
1763 state->log(ISC_LOG_ERROR, "samba_dlz: failed to delete type %s in %s - %s",
1764 type, ldb_dn_get_linearized(dn), ldb_errstring(state->samdb));
1765 talloc_free(tmp_ctx);
1766 return ISC_R_FAILURE;
1769 state->log(ISC_LOG_INFO, "samba_dlz: deleted rdataset %s of type %s", name, type);
1771 talloc_free(tmp_ctx);
1772 return ISC_R_SUCCESS;