s4 dns_server Bind9: Log opertion durations
[samba.git] / source4 / dns_server / dlz_bind9.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    bind9 dlz driver for Samba
5
6    Copyright (C) 2010 Andrew Tridgell
7
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.
12
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.
17
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/>.
20 */
21
22 #include "includes.h"
23 #include "talloc.h"
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 <popt.h>
39 #include "lib/util/dlinklist.h"
40 #include "dlz_minimal.h"
41 #include "dnsserver_common.h"
42
43 struct b9_options {
44         const char *url;
45         const char *debug;
46 };
47
48 struct b9_zone {
49         char *name;
50         struct b9_zone *prev, *next;
51 };
52
53 struct dlz_bind9_data {
54         struct b9_options options;
55         struct ldb_context *samdb;
56         struct tevent_context *ev_ctx;
57         struct loadparm_context *lp;
58         int *transaction_token;
59         uint32_t soa_serial;
60         struct b9_zone *zonelist;
61
62         /* Used for dynamic update */
63         struct smb_krb5_context *smb_krb5_ctx;
64         struct auth4_context *auth_context;
65         struct auth_session_info *session_info;
66         char *update_name;
67
68         /* helper functions from the dlz_dlopen driver */
69         log_t *log;
70         dns_sdlz_putrr_t *putrr;
71         dns_sdlz_putnamedrr_t *putnamedrr;
72         dns_dlz_writeablezone_t *writeable_zone;
73 };
74
75 static struct dlz_bind9_data *dlz_bind9_state = NULL;
76 static int dlz_bind9_state_ref_count = 0;
77
78 static const char *zone_prefixes[] = {
79         "CN=MicrosoftDNS,DC=DomainDnsZones",
80         "CN=MicrosoftDNS,DC=ForestDnsZones",
81         "CN=MicrosoftDNS,CN=System",
82         NULL
83 };
84
85 /*
86  * Get a printable string representation of an isc_result_t
87  */
88 static const char *isc_result_str( const isc_result_t result) {
89         switch (result) {
90         case ISC_R_SUCCESS:
91                 return "ISC_R_SUCCESS";
92         case ISC_R_NOMEMORY:
93                 return "ISC_R_NOMEMORY";
94         case ISC_R_NOPERM:
95                 return "ISC_R_NOPERM";
96         case ISC_R_NOSPACE:
97                 return "ISC_R_NOSPACE";
98         case ISC_R_NOTFOUND:
99                 return "ISC_R_NOTFOUND";
100         case ISC_R_FAILURE:
101                 return "ISC_R_FAILURE";
102         case ISC_R_NOTIMPLEMENTED:
103                 return "ISC_R_NOTIMPLEMENTED";
104         case ISC_R_NOMORE:
105                 return "ISC_R_NOMORE";
106         case ISC_R_INVALIDFILE:
107                 return "ISC_R_INVALIDFILE";
108         case ISC_R_UNEXPECTED:
109                 return "ISC_R_UNEXPECTED";
110         case ISC_R_FILENOTFOUND:
111                 return "ISC_R_FILENOTFOUND";
112         default:
113                 return "UNKNOWN";
114         }
115 }
116
117 /*
118   return the version of the API
119  */
120 _PUBLIC_ int dlz_version(unsigned int *flags)
121 {
122         return DLZ_DLOPEN_VERSION;
123 }
124
125 /*
126    remember a helper function from the bind9 dlz_dlopen driver
127  */
128 static void b9_add_helper(struct dlz_bind9_data *state, const char *helper_name, void *ptr)
129 {
130         if (strcmp(helper_name, "log") == 0) {
131                 state->log = ptr;
132         }
133         if (strcmp(helper_name, "putrr") == 0) {
134                 state->putrr = ptr;
135         }
136         if (strcmp(helper_name, "putnamedrr") == 0) {
137                 state->putnamedrr = ptr;
138         }
139         if (strcmp(helper_name, "writeable_zone") == 0) {
140                 state->writeable_zone = ptr;
141         }
142 }
143
144 /*
145  * Add a trailing '.' if it's missing
146  */
147 static const char *b9_format_fqdn(TALLOC_CTX *mem_ctx, const char *str)
148 {
149         size_t len;
150         const char *tmp;
151
152         if (str == NULL || str[0] == '\0') {
153                 return str;
154         }
155
156         len = strlen(str);
157         if (str[len-1] != '.') {
158                 tmp = talloc_asprintf(mem_ctx, "%s.", str);
159         } else {
160                 tmp = str;
161         }
162         return tmp;
163 }
164
165 /*
166   format a record for bind9
167  */
168 static bool b9_format(struct dlz_bind9_data *state,
169                       TALLOC_CTX *mem_ctx,
170                       struct dnsp_DnssrvRpcRecord *rec,
171                       const char **type, const char **data)
172 {
173         uint32_t i;
174         char *tmp;
175         const char *fqdn;
176
177         switch (rec->wType) {
178         case DNS_TYPE_A:
179                 *type = "a";
180                 *data = rec->data.ipv4;
181                 break;
182
183         case DNS_TYPE_AAAA:
184                 *type = "aaaa";
185                 *data = rec->data.ipv6;
186                 break;
187
188         case DNS_TYPE_CNAME:
189                 *type = "cname";
190                 *data = b9_format_fqdn(mem_ctx, rec->data.cname);
191                 break;
192
193         case DNS_TYPE_TXT:
194                 *type = "txt";
195                 tmp = talloc_asprintf(mem_ctx, "\"%s\"", rec->data.txt.str[0]);
196                 for (i=1; i<rec->data.txt.count; i++) {
197                         tmp = talloc_asprintf_append(tmp, " \"%s\"", rec->data.txt.str[i]);
198                 }
199                 *data = tmp;
200                 break;
201
202         case DNS_TYPE_PTR:
203                 *type = "ptr";
204                 *data = b9_format_fqdn(mem_ctx, rec->data.ptr);
205                 break;
206
207         case DNS_TYPE_SRV:
208                 *type = "srv";
209                 fqdn = b9_format_fqdn(mem_ctx, rec->data.srv.nameTarget);
210                 if (fqdn == NULL) {
211                         return false;
212                 }
213                 *data = talloc_asprintf(mem_ctx, "%u %u %u %s",
214                                         rec->data.srv.wPriority,
215                                         rec->data.srv.wWeight,
216                                         rec->data.srv.wPort,
217                                         fqdn);
218                 break;
219
220         case DNS_TYPE_MX:
221                 *type = "mx";
222                 fqdn = b9_format_fqdn(mem_ctx, rec->data.mx.nameTarget);
223                 if (fqdn == NULL) {
224                         return false;
225                 }
226                 *data = talloc_asprintf(mem_ctx, "%u %s",
227                                         rec->data.mx.wPriority, fqdn);
228                 break;
229
230         case DNS_TYPE_HINFO:
231                 *type = "hinfo";
232                 *data = talloc_asprintf(mem_ctx, "%s %s",
233                                         rec->data.hinfo.cpu,
234                                         rec->data.hinfo.os);
235                 break;
236
237         case DNS_TYPE_NS:
238                 *type = "ns";
239                 *data = b9_format_fqdn(mem_ctx, rec->data.ns);
240                 break;
241
242         case DNS_TYPE_SOA: {
243                 const char *mname;
244                 *type = "soa";
245
246                 /* we need to fake the authoritative nameserver to
247                  * point at ourselves. This is how AD DNS servers
248                  * force clients to send updates to the right local DC
249                  */
250                 mname = talloc_asprintf(mem_ctx, "%s.%s.",
251                                         lpcfg_netbios_name(state->lp),
252                                         lpcfg_dnsdomain(state->lp));
253                 if (mname == NULL) {
254                         return false;
255                 }
256                 mname = strlower_talloc(mem_ctx, mname);
257                 if (mname == NULL) {
258                         return false;
259                 }
260
261                 fqdn = b9_format_fqdn(mem_ctx, rec->data.soa.rname);
262                 if (fqdn == NULL) {
263                         return false;
264                 }
265
266                 state->soa_serial = rec->data.soa.serial;
267
268                 *data = talloc_asprintf(mem_ctx, "%s %s %u %u %u %u %u",
269                                         mname, fqdn,
270                                         rec->data.soa.serial,
271                                         rec->data.soa.refresh,
272                                         rec->data.soa.retry,
273                                         rec->data.soa.expire,
274                                         rec->data.soa.minimum);
275                 break;
276         }
277
278         default:
279                 state->log(ISC_LOG_ERROR, "samba_dlz b9_format: unhandled record type %u",
280                            rec->wType);
281                 return false;
282         }
283
284         return true;
285 }
286
287 static const struct {
288         enum dns_record_type dns_type;
289         const char *typestr;
290         bool single_valued;
291 } dns_typemap[] = {
292         { DNS_TYPE_A,     "A"     , false},
293         { DNS_TYPE_AAAA,  "AAAA"  , false},
294         { DNS_TYPE_CNAME, "CNAME" , true},
295         { DNS_TYPE_TXT,   "TXT"   , false},
296         { DNS_TYPE_PTR,   "PTR"   , false},
297         { DNS_TYPE_SRV,   "SRV"   , false},
298         { DNS_TYPE_MX,    "MX"    , false},
299         { DNS_TYPE_HINFO, "HINFO" , false},
300         { DNS_TYPE_NS,    "NS"    , false},
301         { DNS_TYPE_SOA,   "SOA"   , true},
302 };
303
304
305 /*
306   see if a DNS type is single valued
307  */
308 static bool b9_single_valued(enum dns_record_type dns_type)
309 {
310         int i;
311         for (i=0; i<ARRAY_SIZE(dns_typemap); i++) {
312                 if (dns_typemap[i].dns_type == dns_type) {
313                         return dns_typemap[i].single_valued;
314                 }
315         }
316         return false;
317 }
318
319 /*
320   see if a DNS type is single valued
321  */
322 static bool b9_dns_type(const char *type, enum dns_record_type *dtype)
323 {
324         int i;
325         for (i=0; i<ARRAY_SIZE(dns_typemap); i++) {
326                 if (strcasecmp(dns_typemap[i].typestr, type) == 0) {
327                         *dtype = dns_typemap[i].dns_type;
328                         return true;
329                 }
330         }
331         return false;
332 }
333
334
335 #define DNS_PARSE_STR(ret, str, sep, saveptr) do {      \
336         (ret) = strtok_r(str, sep, &saveptr); \
337         if ((ret) == NULL) return false; \
338         } while (0)
339
340 #define DNS_PARSE_UINT(ret, str, sep, saveptr) do {  \
341         char *istr = strtok_r(str, sep, &saveptr); \
342         int error = 0;\
343         if ((istr) == NULL) return false; \
344         (ret) = strtoul_err(istr, NULL, 10, &error); \
345         if (error != 0) {\
346                 return false;\
347         }\
348         } while (0)
349
350 /*
351   parse a record from bind9
352  */
353 static bool b9_parse(struct dlz_bind9_data *state,
354                      const char *rdatastr,
355                      struct dnsp_DnssrvRpcRecord *rec)
356 {
357         char *full_name, *dclass, *type;
358         char *str, *tmp, *saveptr=NULL;
359         int i;
360
361         str = talloc_strdup(rec, rdatastr);
362         if (str == NULL) {
363                 return false;
364         }
365
366         /* parse the SDLZ string form */
367         DNS_PARSE_STR(full_name, str, "\t", saveptr);
368         DNS_PARSE_UINT(rec->dwTtlSeconds, NULL, "\t", saveptr);
369         DNS_PARSE_STR(dclass, NULL, "\t", saveptr);
370         DNS_PARSE_STR(type, NULL, "\t", saveptr);
371
372         /* construct the record */
373         for (i=0; i<ARRAY_SIZE(dns_typemap); i++) {
374                 if (strcasecmp(type, dns_typemap[i].typestr) == 0) {
375                         rec->wType = dns_typemap[i].dns_type;
376                         break;
377                 }
378         }
379         if (i == ARRAY_SIZE(dns_typemap)) {
380                 state->log(ISC_LOG_ERROR, "samba_dlz: unsupported record type '%s' for '%s'",
381                            type, full_name);
382                 return false;
383         }
384
385         switch (rec->wType) {
386         case DNS_TYPE_A:
387                 DNS_PARSE_STR(rec->data.ipv4, NULL, " ", saveptr);
388                 break;
389
390         case DNS_TYPE_AAAA:
391                 DNS_PARSE_STR(rec->data.ipv6, NULL, " ", saveptr);
392                 break;
393
394         case DNS_TYPE_CNAME:
395                 DNS_PARSE_STR(rec->data.cname, NULL, " ", saveptr);
396                 break;
397
398         case DNS_TYPE_TXT:
399                 rec->data.txt.count = 0;
400                 rec->data.txt.str = talloc_array(rec, const char *, rec->data.txt.count);
401                 tmp = strtok_r(NULL, "\t", &saveptr);
402                 while (tmp) {
403                         rec->data.txt.str = talloc_realloc(rec, rec->data.txt.str, const char *,
404                                                         rec->data.txt.count+1);
405                         if (tmp[0] == '"') {
406                                 /* Strip quotes */
407                                 rec->data.txt.str[rec->data.txt.count] = talloc_strndup(rec, &tmp[1], strlen(tmp)-2);
408                         } else {
409                                 rec->data.txt.str[rec->data.txt.count] = talloc_strdup(rec, tmp);
410                         }
411                         rec->data.txt.count++;
412                         tmp = strtok_r(NULL, " ", &saveptr);
413                 }
414                 break;
415
416         case DNS_TYPE_PTR:
417                 DNS_PARSE_STR(rec->data.ptr, NULL, " ", saveptr);
418                 break;
419
420         case DNS_TYPE_SRV:
421                 DNS_PARSE_UINT(rec->data.srv.wPriority, NULL, " ", saveptr);
422                 DNS_PARSE_UINT(rec->data.srv.wWeight, NULL, " ", saveptr);
423                 DNS_PARSE_UINT(rec->data.srv.wPort, NULL, " ", saveptr);
424                 DNS_PARSE_STR(rec->data.srv.nameTarget, NULL, " ", saveptr);
425                 break;
426
427         case DNS_TYPE_MX:
428                 DNS_PARSE_UINT(rec->data.mx.wPriority, NULL, " ", saveptr);
429                 DNS_PARSE_STR(rec->data.mx.nameTarget, NULL, " ", saveptr);
430                 break;
431
432         case DNS_TYPE_HINFO:
433                 DNS_PARSE_STR(rec->data.hinfo.cpu, NULL, " ", saveptr);
434                 DNS_PARSE_STR(rec->data.hinfo.os, NULL, " ", saveptr);
435                 break;
436
437         case DNS_TYPE_NS:
438                 DNS_PARSE_STR(rec->data.ns, NULL, " ", saveptr);
439                 break;
440
441         case DNS_TYPE_SOA:
442                 DNS_PARSE_STR(rec->data.soa.mname, NULL, " ", saveptr);
443                 DNS_PARSE_STR(rec->data.soa.rname, NULL, " ", saveptr);
444                 DNS_PARSE_UINT(rec->data.soa.serial, NULL, " ", saveptr);
445                 DNS_PARSE_UINT(rec->data.soa.refresh, NULL, " ", saveptr);
446                 DNS_PARSE_UINT(rec->data.soa.retry, NULL, " ", saveptr);
447                 DNS_PARSE_UINT(rec->data.soa.expire, NULL, " ", saveptr);
448                 DNS_PARSE_UINT(rec->data.soa.minimum, NULL, " ", saveptr);
449                 break;
450
451         default:
452                 state->log(ISC_LOG_ERROR, "samba_dlz b9_parse: unhandled record type %u",
453                            rec->wType);
454                 return false;
455         }
456
457         /* we should be at the end of the buffer now */
458         if (strtok_r(NULL, "\t ", &saveptr) != NULL) {
459                 state->log(ISC_LOG_ERROR, "samba_dlz b9_parse: unexpected data at end of string for '%s'",
460                            rdatastr);
461                 return false;
462         }
463
464         return true;
465 }
466
467 /*
468   send a resource record to bind9
469  */
470 static isc_result_t b9_putrr(struct dlz_bind9_data *state,
471                              void *handle, struct dnsp_DnssrvRpcRecord *rec,
472                              const char **types)
473 {
474         isc_result_t result;
475         const char *type, *data;
476         TALLOC_CTX *tmp_ctx = talloc_new(state);
477
478         if (!b9_format(state, tmp_ctx, rec, &type, &data)) {
479                 return ISC_R_FAILURE;
480         }
481
482         if (data == NULL) {
483                 talloc_free(tmp_ctx);
484                 return ISC_R_NOMEMORY;
485         }
486
487         if (types) {
488                 int i;
489                 for (i=0; types[i]; i++) {
490                         if (strcmp(types[i], type) == 0) break;
491                 }
492                 if (types[i] == NULL) {
493                         /* skip it */
494                         return ISC_R_SUCCESS;
495                 }
496         }
497
498         result = state->putrr(handle, type, rec->dwTtlSeconds, data);
499         if (result != ISC_R_SUCCESS) {
500                 state->log(ISC_LOG_ERROR, "Failed to put rr");
501         }
502         talloc_free(tmp_ctx);
503         return result;
504 }
505
506
507 /*
508   send a named resource record to bind9
509  */
510 static isc_result_t b9_putnamedrr(struct dlz_bind9_data *state,
511                                   void *handle, const char *name,
512                                   struct dnsp_DnssrvRpcRecord *rec)
513 {
514         isc_result_t result;
515         const char *type, *data;
516         TALLOC_CTX *tmp_ctx = talloc_new(state);
517
518         if (!b9_format(state, tmp_ctx, rec, &type, &data)) {
519                 return ISC_R_FAILURE;
520         }
521
522         if (data == NULL) {
523                 talloc_free(tmp_ctx);
524                 return ISC_R_NOMEMORY;
525         }
526
527         result = state->putnamedrr(handle, name, type, rec->dwTtlSeconds, data);
528         if (result != ISC_R_SUCCESS) {
529                 state->log(ISC_LOG_ERROR, "Failed to put named rr '%s'", name);
530         }
531         talloc_free(tmp_ctx);
532         return result;
533 }
534
535 /*
536    parse options
537  */
538 static isc_result_t parse_options(struct dlz_bind9_data *state,
539                                   unsigned int argc, const char **argv,
540                                   struct b9_options *options)
541 {
542         int opt;
543         poptContext pc;
544         struct poptOption long_options[] = {
545                 { "url", 'H', POPT_ARG_STRING, &options->url, 0, "database URL", "URL" },
546                 { "debug", 'd', POPT_ARG_STRING, &options->debug, 0, "debug level", "DEBUG" },
547                 { NULL }
548         };
549
550         pc = poptGetContext("dlz_bind9", argc, argv, long_options,
551                         POPT_CONTEXT_KEEP_FIRST);
552         while ((opt = poptGetNextOpt(pc)) != -1) {
553                 switch (opt) {
554                 default:
555                         state->log(ISC_LOG_ERROR, "dlz_bind9: Invalid option %s: %s",
556                                    poptBadOption(pc, 0), poptStrerror(opt));
557                         return ISC_R_FAILURE;
558                 }
559         }
560
561         return ISC_R_SUCCESS;
562 }
563
564
565 /*
566  * Create session info from PAC
567  * This is called as auth_context->generate_session_info_pac()
568  */
569 static NTSTATUS b9_generate_session_info_pac(struct auth4_context *auth_context,
570                                              TALLOC_CTX *mem_ctx,
571                                              struct smb_krb5_context *smb_krb5_context,
572                                              DATA_BLOB *pac_blob,
573                                              const char *principal_name,
574                                              const struct tsocket_address *remote_addr,
575                                              uint32_t session_info_flags,
576                                              struct auth_session_info **session_info)
577 {
578         NTSTATUS status;
579         struct auth_user_info_dc *user_info_dc;
580         TALLOC_CTX *tmp_ctx;
581
582         tmp_ctx = talloc_new(mem_ctx);
583         NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
584
585         status = kerberos_pac_blob_to_user_info_dc(tmp_ctx,
586                                                    *pac_blob,
587                                                    smb_krb5_context->krb5_context,
588                                                    &user_info_dc,
589                                                    NULL,
590                                                    NULL);
591         if (!NT_STATUS_IS_OK(status)) {
592                 talloc_free(tmp_ctx);
593                 return status;
594         }
595
596         if (user_info_dc->info->authenticated) {
597                 session_info_flags |= AUTH_SESSION_INFO_AUTHENTICATED;
598         }
599
600         session_info_flags |= AUTH_SESSION_INFO_SIMPLE_PRIVILEGES;
601
602         status = auth_generate_session_info(mem_ctx, NULL, NULL, user_info_dc,
603                                             session_info_flags, session_info);
604         if (!NT_STATUS_IS_OK(status)) {
605                 talloc_free(tmp_ctx);
606                 return status;
607         }
608
609         talloc_free(tmp_ctx);
610         return status;
611 }
612
613 /* Callback for the DEBUG() system, to catch the remaining messages */
614 static void b9_debug(void *private_ptr, int msg_level, const char *msg)
615 {
616         static const int isc_log_map[] = {
617                 ISC_LOG_CRITICAL, /* 0 */
618                 ISC_LOG_ERROR,    /* 1 */
619                 ISC_LOG_WARNING,   /* 2 */
620                 ISC_LOG_NOTICE    /* 3 */
621         };
622         struct dlz_bind9_data *state = private_ptr;
623         int     isc_log_level;
624         
625         if (msg_level >= ARRAY_SIZE(isc_log_map) || msg_level < 0) {
626                 isc_log_level = ISC_LOG_INFO;
627         } else {
628                 isc_log_level = isc_log_map[msg_level];
629         }
630         state->log(isc_log_level, "samba_dlz: %s", msg);
631 }
632
633 static int dlz_state_debug_unregister(struct dlz_bind9_data *state)
634 {
635         /* Stop logging (to the bind9 logs) */
636         debug_set_callback(NULL, NULL);
637         return 0;
638 }
639
640 /*
641   called to initialise the driver
642  */
643 _PUBLIC_ isc_result_t dlz_create(const char *dlzname,
644                                  unsigned int argc, const char **argv,
645                                  void **dbdata, ...)
646 {
647         struct dlz_bind9_data *state;
648         const char *helper_name;
649         va_list ap;
650         isc_result_t result;
651         struct ldb_dn *dn;
652         NTSTATUS nt_status;
653         int ret;
654         char *errstring = NULL;
655
656         if (dlz_bind9_state != NULL) {
657                 dlz_bind9_state->log(ISC_LOG_ERROR,
658                                      "samba_dlz: dlz_create ignored, #refs=%d",
659                                      dlz_bind9_state_ref_count);
660                 *dbdata = dlz_bind9_state;
661                 dlz_bind9_state_ref_count++;
662                 return ISC_R_SUCCESS;
663         }
664
665         state = talloc_zero(NULL, struct dlz_bind9_data);
666         if (state == NULL) {
667                 return ISC_R_NOMEMORY;
668         }
669
670         talloc_set_destructor(state, dlz_state_debug_unregister);
671
672         /* fill in the helper functions */
673         va_start(ap, dbdata);
674         while ((helper_name = va_arg(ap, const char *)) != NULL) {
675                 b9_add_helper(state, helper_name, va_arg(ap, void*));
676         }
677         va_end(ap);
678
679         /* Do not install samba signal handlers */
680         fault_setup_disable();
681
682         /* Start logging (to the bind9 logs) */
683         debug_set_callback(state, b9_debug);
684
685         state->ev_ctx = s4_event_context_init(state);
686         if (state->ev_ctx == NULL) {
687                 result = ISC_R_NOMEMORY;
688                 goto failed;
689         }
690
691         result = parse_options(state, argc, argv, &state->options);
692         if (result != ISC_R_SUCCESS) {
693                 goto failed;
694         }
695
696         state->lp = loadparm_init_global(true);
697         if (state->lp == NULL) {
698                 result = ISC_R_NOMEMORY;
699                 goto failed;
700         }
701
702         if (state->options.debug) {
703                 lpcfg_do_global_parameter(state->lp, "log level", state->options.debug);
704         } else {
705                 lpcfg_do_global_parameter(state->lp, "log level", "0");
706         }
707
708         if (smb_krb5_init_context(state, state->lp, &state->smb_krb5_ctx) != 0) {
709                 result = ISC_R_NOMEMORY;
710                 goto failed;
711         }
712
713         nt_status = gensec_init();
714         if (!NT_STATUS_IS_OK(nt_status)) {
715                 result = ISC_R_NOMEMORY;
716                 goto failed;
717         }
718
719         state->auth_context = talloc_zero(state, struct auth4_context);
720         if (state->auth_context == NULL) {
721                 result = ISC_R_NOMEMORY;
722                 goto failed;
723         }
724
725         if (state->options.url == NULL) {
726                 state->options.url = talloc_asprintf(state,
727                                                      "%s/dns/sam.ldb",
728                                                      lpcfg_binddns_dir(state->lp));
729                 if (state->options.url == NULL) {
730                         result = ISC_R_NOMEMORY;
731                         goto failed;
732                 }
733
734                 if (!file_exist(state->options.url)) {
735                         state->options.url = talloc_asprintf(state,
736                                                              "%s/dns/sam.ldb",
737                                                              lpcfg_private_dir(state->lp));
738                         if (state->options.url == NULL) {
739                                 result = ISC_R_NOMEMORY;
740                                 goto failed;
741                         }
742                 }
743         }
744
745         ret = samdb_connect_url(state,
746                                 state->ev_ctx,
747                                 state->lp,
748                                 system_session(state->lp),
749                                 0,
750                                 state->options.url,
751                                 NULL,
752                                 &state->samdb,
753                                 &errstring);
754         if (ret != LDB_SUCCESS) {
755                 state->log(ISC_LOG_ERROR,
756                            "samba_dlz: Failed to connect to %s: %s",
757                            errstring, ldb_strerror(ret));
758                 result = ISC_R_FAILURE;
759                 goto failed;
760         }
761
762         dn = ldb_get_default_basedn(state->samdb);
763         if (dn == NULL) {
764                 state->log(ISC_LOG_ERROR, "samba_dlz: Unable to get basedn for %s - %s",
765                            state->options.url, ldb_errstring(state->samdb));
766                 result = ISC_R_FAILURE;
767                 goto failed;
768         }
769
770         state->log(ISC_LOG_INFO, "samba_dlz: started for DN %s",
771                    ldb_dn_get_linearized(dn));
772
773         state->auth_context->event_ctx = state->ev_ctx;
774         state->auth_context->lp_ctx = state->lp;
775         state->auth_context->sam_ctx = state->samdb;
776         state->auth_context->generate_session_info_pac = b9_generate_session_info_pac;
777
778         *dbdata = state;
779         dlz_bind9_state = state;
780         dlz_bind9_state_ref_count++;
781
782         return ISC_R_SUCCESS;
783
784 failed:
785         state->log(ISC_LOG_INFO,
786                    "samba_dlz: FAILED dlz_create call result=%d #refs=%d",
787                    result,
788                    dlz_bind9_state_ref_count);
789         talloc_free(state);
790         return result;
791 }
792
793 /*
794   shutdown the backend
795  */
796 _PUBLIC_ void dlz_destroy(void *dbdata)
797 {
798         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
799
800         dlz_bind9_state_ref_count--;
801         if (dlz_bind9_state_ref_count == 0) {
802                 state->log(ISC_LOG_INFO, "samba_dlz: shutting down");
803                 talloc_unlink(state, state->samdb);
804                 talloc_free(state);
805                 dlz_bind9_state = NULL;
806         } else {
807                 state->log(ISC_LOG_INFO,
808                            "samba_dlz: dlz_destroy called. %d refs remaining.",
809                            dlz_bind9_state_ref_count);
810         }
811 }
812
813
814 /*
815   return the base DN for a zone
816  */
817 static isc_result_t b9_find_zone_dn(struct dlz_bind9_data *state, const char *zone_name,
818                                     TALLOC_CTX *mem_ctx, struct ldb_dn **zone_dn)
819 {
820         int ret;
821         TALLOC_CTX *tmp_ctx = talloc_new(state);
822         const char *attrs[] = { NULL };
823         int i;
824
825         for (i=0; zone_prefixes[i]; i++) {
826                 const char *casefold;
827                 struct ldb_dn *dn;
828                 struct ldb_result *res;
829                 struct ldb_val zone_name_val
830                         = data_blob_string_const(zone_name);
831
832                 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
833                 if (dn == NULL) {
834                         talloc_free(tmp_ctx);
835                         return ISC_R_NOMEMORY;
836                 }
837
838                 /*
839                  * This dance ensures that it is not possible to put
840                  * (eg) an extra DC=x, into the DNS name being
841                  * queried
842                  */
843
844                 if (!ldb_dn_add_child_fmt(dn,
845                                           "DC=X,%s",
846                                           zone_prefixes[i])) {
847                         talloc_free(tmp_ctx);
848                         return ISC_R_NOMEMORY;
849                 }
850
851                 ret = ldb_dn_set_component(dn,
852                                            0,
853                                            "DC",
854                                            zone_name_val);
855                 if (ret != LDB_SUCCESS) {
856                         talloc_free(tmp_ctx);
857                         return ISC_R_NOMEMORY;
858                 }
859
860                 /*
861                  * Check if this is a plausibly valid DN early
862                  * (time spent here will be saved during the
863                  * search due to an internal cache)
864                  */
865                 casefold = ldb_dn_get_casefold(dn);
866
867                 if (casefold == NULL) {
868                         talloc_free(tmp_ctx);
869                         return ISC_R_NOTFOUND;
870                 }
871
872                 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsZone");
873                 if (ret == LDB_SUCCESS) {
874                         if (zone_dn != NULL) {
875                                 *zone_dn = talloc_steal(mem_ctx, dn);
876                         }
877                         talloc_free(tmp_ctx);
878                         return ISC_R_SUCCESS;
879                 }
880                 talloc_free(dn);
881         }
882
883         talloc_free(tmp_ctx);
884         return ISC_R_NOTFOUND;
885 }
886
887
888 /*
889   return the DN for a name. The record does not need to exist, but the
890   zone must exist
891  */
892 static isc_result_t b9_find_name_dn(struct dlz_bind9_data *state, const char *name,
893                                     TALLOC_CTX *mem_ctx, struct ldb_dn **dn)
894 {
895         const char *p;
896
897         /* work through the name piece by piece, until we find a zone */
898         for (p=name; p; ) {
899                 isc_result_t result;
900                 result = b9_find_zone_dn(state, p, mem_ctx, dn);
901                 if (result == ISC_R_SUCCESS) {
902                         const char *casefold;
903
904                         /* we found a zone, now extend the DN to get
905                          * the full DN
906                          */
907                         bool ret;
908                         if (p == name) {
909                                 ret = ldb_dn_add_child_fmt(*dn, "DC=@");
910                                 if (ret == false) {
911                                         talloc_free(*dn);
912                                         return ISC_R_NOMEMORY;
913                                 }
914                         } else {
915                                 struct ldb_val name_val
916                                         = data_blob_const(name,
917                                                           (int)(p-name)-1);
918
919                                 if (!ldb_dn_add_child_val(*dn,
920                                                           "DC",
921                                                           name_val)) {
922                                         talloc_free(*dn);
923                                         return ISC_R_NOMEMORY;
924                                 }
925                         }
926
927                         /*
928                          * Check if this is a plausibly valid DN early
929                          * (time spent here will be saved during the
930                          * search due to an internal cache)
931                          */
932                         casefold = ldb_dn_get_casefold(*dn);
933
934                         if (casefold == NULL) {
935                                 return ISC_R_NOTFOUND;
936                         }
937
938                         return ISC_R_SUCCESS;
939                 }
940                 p = strchr(p, '.');
941                 if (p == NULL) {
942                         break;
943                 }
944                 p++;
945         }
946         return ISC_R_NOTFOUND;
947 }
948
949
950 /*
951   see if we handle a given zone
952  */
953 #if DLZ_DLOPEN_VERSION < 3
954 _PUBLIC_ isc_result_t dlz_findzonedb(void *dbdata, const char *name)
955 #else
956 _PUBLIC_ isc_result_t dlz_findzonedb(void *dbdata, const char *name,
957                                      dns_clientinfomethods_t *methods,
958                                      dns_clientinfo_t *clientinfo)
959 #endif
960 {
961         struct timeval start = timeval_current();
962         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
963         isc_result_t result = ISC_R_SUCCESS;
964
965         result = b9_find_zone_dn(state, name, NULL, NULL);
966          DNS_COMMON_LOG_OPERATION(
967                 isc_result_str(result),
968                 &start,
969                 NULL,
970                 name,
971                 NULL);
972         return result;
973 }
974
975
976 /*
977   lookup one record
978  */
979 static isc_result_t dlz_lookup_types(struct dlz_bind9_data *state,
980                                      const char *zone, const char *name,
981                                      dns_sdlzlookup_t *lookup,
982                                      const char **types)
983 {
984         TALLOC_CTX *tmp_ctx = talloc_new(state);
985         struct ldb_dn *dn;
986         WERROR werr = WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
987         struct dnsp_DnssrvRpcRecord *records = NULL;
988         uint16_t num_records = 0, i;
989         struct ldb_val zone_name_val
990                 = data_blob_string_const(zone);
991         struct ldb_val name_val
992                 = data_blob_string_const(name);
993
994         for (i=0; zone_prefixes[i]; i++) {
995                 int ret;
996                 const char *casefold;
997                 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
998                 if (dn == NULL) {
999                         talloc_free(tmp_ctx);
1000                         return ISC_R_NOMEMORY;
1001                 }
1002
1003                 /*
1004                  * This dance ensures that it is not possible to put
1005                  * (eg) an extra DC=x, into the DNS name being
1006                  * queried
1007                  */
1008
1009                 if (!ldb_dn_add_child_fmt(dn,
1010                                           "DC=X,DC=X,%s",
1011                                           zone_prefixes[i])) {
1012                         talloc_free(tmp_ctx);
1013                         return ISC_R_NOMEMORY;
1014                 }
1015
1016                 ret = ldb_dn_set_component(dn,
1017                                            1,
1018                                            "DC",
1019                                            zone_name_val);
1020                 if (ret != LDB_SUCCESS) {
1021                         talloc_free(tmp_ctx);
1022                         return ISC_R_NOMEMORY;
1023                 }
1024
1025                 ret = ldb_dn_set_component(dn,
1026                                            0,
1027                                            "DC",
1028                                            name_val);
1029                 if (ret != LDB_SUCCESS) {
1030                         talloc_free(tmp_ctx);
1031                         return ISC_R_NOMEMORY;
1032                 }
1033
1034                 /*
1035                  * Check if this is a plausibly valid DN early
1036                  * (time spent here will be saved during the
1037                  * search due to an internal cache)
1038                  */
1039                 casefold = ldb_dn_get_casefold(dn);
1040
1041                 if (casefold == NULL) {
1042                         talloc_free(tmp_ctx);
1043                         return ISC_R_NOTFOUND;
1044                 }
1045
1046                 werr = dns_common_wildcard_lookup(state->samdb, tmp_ctx, dn,
1047                                          &records, &num_records);
1048                 if (W_ERROR_IS_OK(werr)) {
1049                         break;
1050                 }
1051         }
1052         if (!W_ERROR_IS_OK(werr)) {
1053                 talloc_free(tmp_ctx);
1054                 return ISC_R_NOTFOUND;
1055         }
1056
1057         for (i=0; i < num_records; i++) {
1058                 isc_result_t result;
1059
1060                 result = b9_putrr(state, lookup, &records[i], types);
1061                 if (result != ISC_R_SUCCESS) {
1062                         talloc_free(tmp_ctx);
1063                         return result;
1064                 }
1065         }
1066
1067         talloc_free(tmp_ctx);
1068         return ISC_R_SUCCESS;
1069 }
1070
1071 /*
1072   lookup one record
1073  */
1074 #if DLZ_DLOPEN_VERSION == 1
1075 _PUBLIC_ isc_result_t dlz_lookup(const char *zone, const char *name,
1076                                  void *dbdata, dns_sdlzlookup_t *lookup)
1077 #else
1078 _PUBLIC_ isc_result_t dlz_lookup(const char *zone, const char *name,
1079                                  void *dbdata, dns_sdlzlookup_t *lookup,
1080                                  dns_clientinfomethods_t *methods,
1081                                  dns_clientinfo_t *clientinfo)
1082 #endif
1083 {
1084         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1085         isc_result_t result = ISC_R_SUCCESS;
1086         struct timeval start = timeval_current();
1087
1088         result = dlz_lookup_types(state, zone, name, lookup, NULL);
1089         DNS_COMMON_LOG_OPERATION(
1090                 isc_result_str(result),
1091                 &start,
1092                 zone,
1093                 name,
1094                 NULL);
1095
1096         return result;
1097 }
1098
1099
1100 /*
1101   see if a zone transfer is allowed
1102  */
1103 _PUBLIC_ isc_result_t dlz_allowzonexfr(void *dbdata, const char *name, const char *client)
1104 {
1105         /* just say yes for all our zones for now */
1106         struct dlz_bind9_data *state = talloc_get_type(
1107                 dbdata, struct dlz_bind9_data);
1108         return b9_find_zone_dn(state, name, NULL, NULL);
1109 }
1110
1111 /*
1112   perform a zone transfer
1113  */
1114 _PUBLIC_ isc_result_t dlz_allnodes(const char *zone, void *dbdata,
1115                                    dns_sdlzallnodes_t *allnodes)
1116 {
1117         struct timeval start = timeval_current();
1118         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1119         const char *attrs[] = { "dnsRecord", NULL };
1120         int ret = LDB_ERR_NO_SUCH_OBJECT;
1121         size_t i, j;
1122         struct ldb_dn *dn = NULL;
1123         struct ldb_result *res;
1124         TALLOC_CTX *tmp_ctx = talloc_new(state);
1125         struct ldb_val zone_name_val = data_blob_string_const(zone);
1126         isc_result_t result = ISC_R_SUCCESS;
1127
1128         for (i=0; zone_prefixes[i]; i++) {
1129                 const char *casefold;
1130
1131                 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
1132                 if (dn == NULL) {
1133                         talloc_free(tmp_ctx);
1134                         result = ISC_R_NOMEMORY;
1135                         goto exit;
1136                 }
1137
1138                 /*
1139                  * This dance ensures that it is not possible to put
1140                  * (eg) an extra DC=x, into the DNS name being
1141                  * queried
1142                  */
1143
1144                 if (!ldb_dn_add_child_fmt(dn,
1145                                           "DC=X,%s",
1146                                           zone_prefixes[i])) {
1147                         talloc_free(tmp_ctx);
1148                         result = ISC_R_NOMEMORY;
1149                         goto exit;
1150                 }
1151
1152                 ret = ldb_dn_set_component(dn,
1153                                            0,
1154                                            "DC",
1155                                            zone_name_val);
1156                 if (ret != LDB_SUCCESS) {
1157                         talloc_free(tmp_ctx);
1158                         result = ISC_R_NOMEMORY;
1159                         goto exit;
1160                 }
1161
1162                 /*
1163                  * Check if this is a plausibly valid DN early
1164                  * (time spent here will be saved during the
1165                  * search due to an internal cache)
1166                  */
1167                 casefold = ldb_dn_get_casefold(dn);
1168
1169                 if (casefold == NULL) {
1170                         result = ISC_R_NOTFOUND;
1171                         goto exit;
1172                 }
1173
1174                 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
1175                                  attrs, "objectClass=dnsNode");
1176                 if (ret == LDB_SUCCESS) {
1177                         break;
1178                 }
1179         }
1180         if (ret != LDB_SUCCESS || dn == NULL) {
1181                 talloc_free(tmp_ctx);
1182                 result = ISC_R_NOTFOUND;
1183                 goto exit;
1184         }
1185
1186         for (i=0; i<res->count; i++) {
1187                 struct ldb_message_element *el;
1188                 TALLOC_CTX *el_ctx = talloc_new(tmp_ctx);
1189                 const char *rdn, *name;
1190                 const struct ldb_val *v;
1191                 WERROR werr;
1192                 struct dnsp_DnssrvRpcRecord *recs = NULL;
1193                 uint16_t num_recs = 0;
1194
1195                 el = ldb_msg_find_element(res->msgs[i], "dnsRecord");
1196                 if (el == NULL || el->num_values == 0) {
1197                         state->log(ISC_LOG_INFO, "failed to find dnsRecord for %s",
1198                                    ldb_dn_get_linearized(dn));
1199                         talloc_free(el_ctx);
1200                         continue;
1201                 }
1202
1203                 v = ldb_dn_get_rdn_val(res->msgs[i]->dn);
1204                 if (v == NULL) {
1205                         state->log(ISC_LOG_INFO, "failed to find RDN for %s",
1206                                    ldb_dn_get_linearized(dn));
1207                         talloc_free(el_ctx);
1208                         continue;
1209                 }
1210
1211                 rdn = talloc_strndup(el_ctx, (char *)v->data, v->length);
1212                 if (rdn == NULL) {
1213                         talloc_free(tmp_ctx);
1214                         result = ISC_R_NOMEMORY;
1215                         goto exit;
1216                 }
1217
1218                 if (strcmp(rdn, "@") == 0) {
1219                         name = zone;
1220                 } else {
1221                         name = talloc_asprintf(el_ctx, "%s.%s", rdn, zone);
1222                 }
1223                 name = b9_format_fqdn(el_ctx, name);
1224                 if (name == NULL) {
1225                         talloc_free(tmp_ctx);
1226                         result = ISC_R_NOMEMORY;
1227                         goto exit;
1228                 }
1229
1230                 werr = dns_common_extract(state->samdb, el, el_ctx, &recs, &num_recs);
1231                 if (!W_ERROR_IS_OK(werr)) {
1232                         state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s, %s",
1233                                    ldb_dn_get_linearized(dn), win_errstr(werr));
1234                         talloc_free(el_ctx);
1235                         continue;
1236                 }
1237
1238                 for (j=0; j < num_recs; j++) {
1239                         isc_result_t rc;
1240
1241                         rc = b9_putnamedrr(state, allnodes, name, &recs[j]);
1242                         if (rc != ISC_R_SUCCESS) {
1243                                 continue;
1244                         }
1245                 }
1246
1247                 talloc_free(el_ctx);
1248         }
1249
1250         talloc_free(tmp_ctx);
1251 exit:
1252         DNS_COMMON_LOG_OPERATION(
1253                 isc_result_str(result),
1254                 &start,
1255                 zone,
1256                 NULL,
1257                 NULL);
1258         return result;
1259 }
1260
1261
1262 /*
1263   start a transaction
1264  */
1265 _PUBLIC_ isc_result_t dlz_newversion(const char *zone, void *dbdata, void **versionp)
1266 {
1267         struct timeval start = timeval_current();
1268         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1269         isc_result_t result = ISC_R_SUCCESS;
1270
1271         state->log(ISC_LOG_INFO, "samba_dlz: starting transaction on zone %s", zone);
1272
1273         if (state->transaction_token != NULL) {
1274                 state->log(ISC_LOG_INFO, "samba_dlz: transaction already started for zone %s", zone);
1275                 result = ISC_R_FAILURE;
1276                 goto exit;
1277         }
1278
1279         state->transaction_token = talloc_zero(state, int);
1280         if (state->transaction_token == NULL) {
1281                 result = ISC_R_NOMEMORY;
1282                 goto exit;
1283         }
1284
1285         if (ldb_transaction_start(state->samdb) != LDB_SUCCESS) {
1286                 state->log(ISC_LOG_INFO, "samba_dlz: failed to start a transaction for zone %s", zone);
1287                 talloc_free(state->transaction_token);
1288                 state->transaction_token = NULL;
1289                 result = ISC_R_FAILURE;
1290                 goto exit;
1291         }
1292
1293         *versionp = (void *)state->transaction_token;
1294 exit:
1295         DNS_COMMON_LOG_OPERATION(
1296                 isc_result_str(result),
1297                 &start,
1298                 zone,
1299                 NULL,
1300                 NULL);
1301         return result;
1302 }
1303
1304 /*
1305   end a transaction
1306  */
1307 _PUBLIC_ void dlz_closeversion(const char *zone, isc_boolean_t commit,
1308                                void *dbdata, void **versionp)
1309 {
1310         struct timeval start = timeval_current();
1311         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1312         const char *data = NULL;
1313
1314         data = commit ? "commit" : "cancel";
1315
1316         if (state->transaction_token != (int *)*versionp) {
1317                 state->log(ISC_LOG_INFO, "samba_dlz: transaction not started for zone %s", zone);
1318                 goto exit;
1319         }
1320
1321         if (commit) {
1322                 if (ldb_transaction_commit(state->samdb) != LDB_SUCCESS) {
1323                         state->log(ISC_LOG_INFO, "samba_dlz: failed to commit a transaction for zone %s", zone);
1324                         goto exit;
1325                 }
1326                 state->log(ISC_LOG_INFO, "samba_dlz: committed transaction on zone %s", zone);
1327         } else {
1328                 if (ldb_transaction_cancel(state->samdb) != LDB_SUCCESS) {
1329                         state->log(ISC_LOG_INFO, "samba_dlz: failed to cancel a transaction for zone %s", zone);
1330                         goto exit;
1331                 }
1332                 state->log(ISC_LOG_INFO, "samba_dlz: cancelling transaction on zone %s", zone);
1333         }
1334
1335         talloc_free(state->transaction_token);
1336         state->transaction_token = NULL;
1337         *versionp = NULL;
1338
1339 exit:
1340         DNS_COMMON_LOG_OPERATION(
1341                 isc_result_str(ISC_R_SUCCESS),
1342                 &start,
1343                 zone,
1344                 NULL,
1345                 data);
1346 }
1347
1348
1349 /*
1350   see if there is a SOA record for a zone
1351  */
1352 static bool b9_has_soa(struct dlz_bind9_data *state, struct ldb_dn *dn, const char *zone)
1353 {
1354         TALLOC_CTX *tmp_ctx = talloc_new(state);
1355         WERROR werr;
1356         struct dnsp_DnssrvRpcRecord *records = NULL;
1357         uint16_t num_records = 0, i;
1358         struct ldb_val zone_name_val
1359                 = data_blob_string_const(zone);
1360
1361         /*
1362          * This dance ensures that it is not possible to put
1363          * (eg) an extra DC=x, into the DNS name being
1364          * queried
1365          */
1366
1367         if (!ldb_dn_add_child_val(dn,
1368                                   "DC",
1369                                   zone_name_val)) {
1370                 talloc_free(tmp_ctx);
1371                 return false;
1372         }
1373
1374         /*
1375          * The SOA record is alwas stored under DC=@,DC=zonename
1376          * This can probably be removed when dns_common_lookup makes a fallback
1377          * lookup on @ pseudo record
1378          */
1379
1380         if (!ldb_dn_add_child_fmt(dn,"DC=@")) {
1381                 talloc_free(tmp_ctx);
1382                 return false;
1383         }
1384
1385         werr = dns_common_lookup(state->samdb, tmp_ctx, dn,
1386                                  &records, &num_records, NULL);
1387         if (!W_ERROR_IS_OK(werr)) {
1388                 talloc_free(tmp_ctx);
1389                 return false;
1390         }
1391
1392         for (i=0; i < num_records; i++) {
1393                 if (records[i].wType == DNS_TYPE_SOA) {
1394                         talloc_free(tmp_ctx);
1395                         return true;
1396                 }
1397         }
1398
1399         talloc_free(tmp_ctx);
1400         return false;
1401 }
1402
1403 static bool b9_zone_add(struct dlz_bind9_data *state, const char *name)
1404 {
1405         struct b9_zone *zone;
1406
1407         zone = talloc_zero(state, struct b9_zone);
1408         if (zone == NULL) {
1409                 return false;
1410         }
1411
1412         zone->name = talloc_strdup(zone, name);
1413         if (zone->name == NULL) {
1414                 talloc_free(zone);
1415                 return false;
1416         }
1417
1418         DLIST_ADD(state->zonelist, zone);
1419         return true;
1420 }
1421
1422 static bool b9_zone_exists(struct dlz_bind9_data *state, const char *name)
1423 {
1424         struct b9_zone *zone = state->zonelist;
1425         bool found = false;
1426
1427         while (zone != NULL) {
1428                 if (strcasecmp(name, zone->name) == 0) {
1429                         found = true;
1430                         break;
1431                 }
1432                 zone = zone->next;
1433         }
1434
1435         return found;
1436 }
1437
1438
1439 /*
1440   configure a writeable zone
1441  */
1442 #if DLZ_DLOPEN_VERSION < 3
1443 _PUBLIC_ isc_result_t dlz_configure(dns_view_t *view, void *dbdata)
1444 #else
1445 _PUBLIC_ isc_result_t dlz_configure(dns_view_t *view, dns_dlzdb_t *dlzdb,
1446                                     void *dbdata)
1447 #endif
1448 {
1449         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1450         TALLOC_CTX *tmp_ctx;
1451         struct ldb_dn *dn;
1452         int i;
1453
1454         state->log(ISC_LOG_INFO, "samba_dlz: starting configure");
1455         if (state->writeable_zone == NULL) {
1456                 state->log(ISC_LOG_INFO, "samba_dlz: no writeable_zone method available");
1457                 return ISC_R_FAILURE;
1458         }
1459
1460         tmp_ctx = talloc_new(state);
1461
1462         for (i=0; zone_prefixes[i]; i++) {
1463                 const char *attrs[] = { "name", NULL };
1464                 int j, ret;
1465                 struct ldb_result *res;
1466
1467                 dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
1468                 if (dn == NULL) {
1469                         talloc_free(tmp_ctx);
1470                         return ISC_R_NOMEMORY;
1471                 }
1472
1473                 if (!ldb_dn_add_child_fmt(dn, "%s", zone_prefixes[i])) {
1474                         talloc_free(tmp_ctx);
1475                         return ISC_R_NOMEMORY;
1476                 }
1477
1478                 ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
1479                                  attrs, "objectClass=dnsZone");
1480                 if (ret != LDB_SUCCESS) {
1481                         continue;
1482                 }
1483
1484                 for (j=0; j<res->count; j++) {
1485                         isc_result_t result;
1486                         const char *zone = ldb_msg_find_attr_as_string(res->msgs[j], "name", NULL);
1487                         struct ldb_dn *zone_dn;
1488
1489                         if (zone == NULL) {
1490                                 continue;
1491                         }
1492                         /* Ignore zones that are not handled in BIND */
1493                         if ((strcmp(zone, "RootDNSServers") == 0) ||
1494                             (strcmp(zone, "..TrustAnchors") == 0)) {
1495                                 continue;
1496                         }
1497                         zone_dn = ldb_dn_copy(tmp_ctx, dn);
1498                         if (zone_dn == NULL) {
1499                                 talloc_free(tmp_ctx);
1500                                 return ISC_R_NOMEMORY;
1501                         }
1502
1503                         if (!b9_has_soa(state, zone_dn, zone)) {
1504                                 continue;
1505                         }
1506
1507                         if (b9_zone_exists(state, zone)) {
1508                                 state->log(ISC_LOG_WARNING, "samba_dlz: Ignoring duplicate zone '%s' from '%s'",
1509                                            zone, ldb_dn_get_linearized(zone_dn));
1510                                 continue;
1511                         }
1512
1513                         if (!b9_zone_add(state, zone)) {
1514                                 talloc_free(tmp_ctx);
1515                                 return ISC_R_NOMEMORY;
1516                         }
1517
1518 #if DLZ_DLOPEN_VERSION < 3
1519                         result = state->writeable_zone(view, zone);
1520 #else
1521                         result = state->writeable_zone(view, dlzdb, zone);
1522 #endif
1523                         if (result != ISC_R_SUCCESS) {
1524                                 state->log(ISC_LOG_ERROR, "samba_dlz: Failed to configure zone '%s'",
1525                                            zone);
1526                                 talloc_free(tmp_ctx);
1527                                 return result;
1528                         }
1529                         state->log(ISC_LOG_INFO, "samba_dlz: configured writeable zone '%s'", zone);
1530                 }
1531         }
1532
1533         talloc_free(tmp_ctx);
1534         return ISC_R_SUCCESS;
1535 }
1536
1537 /*
1538   authorize a zone update
1539  */
1540 _PUBLIC_ isc_boolean_t dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr,
1541                                     const char *type, const char *key, uint32_t keydatalen, uint8_t *keydata,
1542                                     void *dbdata)
1543 {
1544         struct timeval start = timeval_current();
1545         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1546         TALLOC_CTX *tmp_ctx;
1547         DATA_BLOB ap_req;
1548         struct cli_credentials *server_credentials;
1549         char *keytab_name;
1550         char *keytab_file = NULL;
1551         int ret;
1552         int ldb_ret;
1553         NTSTATUS nt_status;
1554         struct gensec_security *gensec_ctx;
1555         struct auth_session_info *session_info;
1556         struct ldb_dn *dn;
1557         isc_result_t rc;
1558         struct ldb_result *res;
1559         const char * attrs[] = { NULL };
1560         uint32_t access_mask;
1561         struct gensec_settings *settings = NULL;
1562         const struct gensec_security_ops **backends = NULL;
1563         size_t idx = 0;
1564         isc_boolean_t result = ISC_FALSE;
1565
1566         /* Remove cached credentials, if any */
1567         if (state->session_info) {
1568                 talloc_free(state->session_info);
1569                 state->session_info = NULL;
1570         }
1571         if (state->update_name) {
1572                 talloc_free(state->update_name);
1573                 state->update_name = NULL;
1574         }
1575
1576         tmp_ctx = talloc_new(NULL);
1577         if (tmp_ctx == NULL) {
1578                 state->log(ISC_LOG_ERROR, "samba_dlz: no memory");
1579                 result = ISC_FALSE;
1580                 goto exit;
1581         }
1582
1583         ap_req = data_blob_const(keydata, keydatalen);
1584         server_credentials = cli_credentials_init(tmp_ctx);
1585         if (!server_credentials) {
1586                 state->log(ISC_LOG_ERROR, "samba_dlz: failed to init server credentials");
1587                 talloc_free(tmp_ctx);
1588                 result = ISC_FALSE;
1589                 goto exit;
1590         }
1591
1592         cli_credentials_set_krb5_context(server_credentials, state->smb_krb5_ctx);
1593         cli_credentials_set_conf(server_credentials, state->lp);
1594
1595         keytab_file = talloc_asprintf(tmp_ctx,
1596                                       "%s/dns.keytab",
1597                                       lpcfg_binddns_dir(state->lp));
1598         if (keytab_file == NULL) {
1599                 state->log(ISC_LOG_ERROR, "samba_dlz: Out of memory!");
1600                 talloc_free(tmp_ctx);
1601                 result = ISC_FALSE;
1602                 goto exit;
1603         }
1604
1605         if (!file_exist(keytab_file)) {
1606                 keytab_file = talloc_asprintf(tmp_ctx,
1607                                               "%s/dns.keytab",
1608                                               lpcfg_private_dir(state->lp));
1609                 if (keytab_file == NULL) {
1610                         state->log(ISC_LOG_ERROR, "samba_dlz: Out of memory!");
1611                         talloc_free(tmp_ctx);
1612                         result = ISC_FALSE;
1613                         goto exit;
1614                 }
1615         }
1616
1617         keytab_name = talloc_asprintf(tmp_ctx, "FILE:%s", keytab_file);
1618         if (keytab_name == NULL) {
1619                 state->log(ISC_LOG_ERROR, "samba_dlz: Out of memory!");
1620                 talloc_free(tmp_ctx);
1621                 result = ISC_FALSE;
1622                 goto exit;
1623         }
1624
1625         ret = cli_credentials_set_keytab_name(server_credentials, state->lp, keytab_name,
1626                                                 CRED_SPECIFIED);
1627         if (ret != 0) {
1628                 state->log(ISC_LOG_ERROR, "samba_dlz: failed to obtain server credentials from %s",
1629                            keytab_name);
1630                 talloc_free(tmp_ctx);
1631                 result = ISC_FALSE;
1632                 goto exit;
1633         }
1634         talloc_free(keytab_name);
1635
1636         settings = lpcfg_gensec_settings(tmp_ctx, state->lp);
1637         if (settings == NULL) {
1638                 state->log(ISC_LOG_ERROR, "samba_dlz: lpcfg_gensec_settings failed");
1639                 talloc_free(tmp_ctx);
1640                 result = ISC_FALSE;
1641                 goto exit;
1642         }
1643         backends = talloc_zero_array(settings,
1644                                      const struct gensec_security_ops *, 3);
1645         if (backends == NULL) {
1646                 state->log(ISC_LOG_ERROR, "samba_dlz: talloc_zero_array gensec_security_ops failed");
1647                 talloc_free(tmp_ctx);
1648                 result = ISC_FALSE;
1649                 goto exit;
1650         }
1651         settings->backends = backends;
1652
1653         gensec_init();
1654
1655         backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_KERBEROS5);
1656         backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_SPNEGO);
1657
1658         nt_status = gensec_server_start(tmp_ctx, settings,
1659                                         state->auth_context, &gensec_ctx);
1660         if (!NT_STATUS_IS_OK(nt_status)) {
1661                 state->log(ISC_LOG_ERROR, "samba_dlz: failed to start gensec server");
1662                 talloc_free(tmp_ctx);
1663                 result = ISC_FALSE;
1664                 goto exit;
1665         }
1666
1667         gensec_set_credentials(gensec_ctx, server_credentials);
1668
1669         nt_status = gensec_start_mech_by_oid(gensec_ctx, GENSEC_OID_SPNEGO);
1670         if (!NT_STATUS_IS_OK(nt_status)) {
1671                 state->log(ISC_LOG_ERROR, "samba_dlz: failed to start spnego");
1672                 talloc_free(tmp_ctx);
1673                 result = ISC_FALSE;
1674                 goto exit;
1675         }
1676
1677         /*
1678          * We only allow SPNEGO/KRB5 and make sure the backend
1679          * to is RPC/IPC free.
1680          *
1681          * See gensec_gssapi_update_internal() as
1682          * GENSEC_SERVER.
1683          *
1684          * It allows gensec_update() not to block.
1685          *
1686          * If that changes in future we need to use
1687          * gensec_update_send/recv here!
1688          */
1689         nt_status = gensec_update(gensec_ctx, tmp_ctx, ap_req, &ap_req);
1690         if (!NT_STATUS_IS_OK(nt_status)) {
1691                 state->log(ISC_LOG_ERROR, "samba_dlz: spnego update failed");
1692                 talloc_free(tmp_ctx);
1693                 result = ISC_FALSE;
1694                 goto exit;
1695         }
1696
1697         nt_status = gensec_session_info(gensec_ctx, tmp_ctx, &session_info);
1698         if (!NT_STATUS_IS_OK(nt_status)) {
1699                 state->log(ISC_LOG_ERROR, "samba_dlz: failed to create session info");
1700                 talloc_free(tmp_ctx);
1701                 result = ISC_FALSE;
1702                 goto exit;
1703         }
1704
1705         /* Get the DN from name */
1706         rc = b9_find_name_dn(state, name, tmp_ctx, &dn);
1707         if (rc != ISC_R_SUCCESS) {
1708                 state->log(ISC_LOG_ERROR, "samba_dlz: failed to find name %s", name);
1709                 talloc_free(tmp_ctx);
1710                 result = ISC_FALSE;
1711                 goto exit;
1712         }
1713
1714         /* make sure the dn exists, or find parent dn in case new object is being added */
1715         ldb_ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
1716                                 attrs, "objectClass=dnsNode");
1717         if (ldb_ret == LDB_ERR_NO_SUCH_OBJECT) {
1718                 ldb_dn_remove_child_components(dn, 1);
1719                 access_mask = SEC_ADS_CREATE_CHILD;
1720                 talloc_free(res);
1721         } else if (ldb_ret == LDB_SUCCESS) {
1722                 access_mask = SEC_STD_REQUIRED | SEC_ADS_SELF_WRITE;
1723                 talloc_free(res);
1724         } else {
1725                 talloc_free(tmp_ctx);
1726                 result = ISC_FALSE;
1727                 goto exit;
1728         }
1729
1730         /* Do ACL check */
1731         ldb_ret = dsdb_check_access_on_dn(state->samdb, tmp_ctx, dn,
1732                                                 session_info->security_token,
1733                                                 access_mask, NULL);
1734         if (ldb_ret != LDB_SUCCESS) {
1735                 state->log(ISC_LOG_INFO,
1736                         "samba_dlz: disallowing update of signer=%s name=%s type=%s error=%s",
1737                         signer, name, type, ldb_strerror(ldb_ret));
1738                 talloc_free(tmp_ctx);
1739                 result = ISC_FALSE;
1740                 goto exit;
1741         }
1742
1743         /* Cache session_info, so it can be used in the actual add/delete operation */
1744         state->update_name = talloc_strdup(state, name);
1745         if (state->update_name == NULL) {
1746                 state->log(ISC_LOG_ERROR, "samba_dlz: memory allocation error");
1747                 talloc_free(tmp_ctx);
1748                 result = ISC_FALSE;
1749                 goto exit;
1750         }
1751         state->session_info = talloc_steal(state, session_info);
1752
1753         state->log(ISC_LOG_INFO, "samba_dlz: allowing update of signer=%s name=%s tcpaddr=%s type=%s key=%s",
1754                    signer, name, tcpaddr, type, key);
1755
1756         talloc_free(tmp_ctx);
1757         result = ISC_TRUE;
1758 exit:
1759         DNS_COMMON_LOG_OPERATION(
1760                 isc_result_str(result),
1761                 &start,
1762                 NULL,
1763                 name,
1764                 NULL);
1765         return result;
1766 }
1767
1768 /*
1769   see if two dns records match
1770  */
1771 static bool b9_record_match(struct dlz_bind9_data *state,
1772                             struct dnsp_DnssrvRpcRecord *rec1, struct dnsp_DnssrvRpcRecord *rec2)
1773 {
1774         bool status;
1775         int i;
1776         struct in6_addr rec1_in_addr6;
1777         struct in6_addr rec2_in_addr6;
1778
1779         if (rec1->wType != rec2->wType) {
1780                 return false;
1781         }
1782         /* see if this type is single valued */
1783         if (b9_single_valued(rec1->wType)) {
1784                 return true;
1785         }
1786
1787         /* see if the data matches */
1788         switch (rec1->wType) {
1789         case DNS_TYPE_A:
1790                 return strcmp(rec1->data.ipv4, rec2->data.ipv4) == 0;
1791         case DNS_TYPE_AAAA: {
1792                 int ret;
1793
1794                 ret = inet_pton(AF_INET6, rec1->data.ipv6, &rec1_in_addr6);
1795                 if (ret != 1) {
1796                         return false;
1797                 }
1798                 ret = inet_pton(AF_INET6, rec2->data.ipv6, &rec2_in_addr6);
1799                 if (ret != 1) {
1800                         return false;
1801                 }
1802
1803                 return memcmp(&rec1_in_addr6, &rec2_in_addr6, sizeof(rec1_in_addr6)) == 0;
1804         }
1805         case DNS_TYPE_CNAME:
1806                 return dns_name_equal(rec1->data.cname, rec2->data.cname);
1807         case DNS_TYPE_TXT:
1808                 status = (rec1->data.txt.count == rec2->data.txt.count);
1809                 if (!status) return status;
1810                 for (i=0; i<rec1->data.txt.count; i++) {
1811                         status &= (strcmp(rec1->data.txt.str[i], rec2->data.txt.str[i]) == 0);
1812                 }
1813                 return status;
1814         case DNS_TYPE_PTR:
1815                 return dns_name_equal(rec1->data.ptr, rec2->data.ptr);
1816         case DNS_TYPE_NS:
1817                 return dns_name_equal(rec1->data.ns, rec2->data.ns);
1818
1819         case DNS_TYPE_SRV:
1820                 return rec1->data.srv.wPriority == rec2->data.srv.wPriority &&
1821                         rec1->data.srv.wWeight  == rec2->data.srv.wWeight &&
1822                         rec1->data.srv.wPort    == rec2->data.srv.wPort &&
1823                         dns_name_equal(rec1->data.srv.nameTarget, rec2->data.srv.nameTarget);
1824
1825         case DNS_TYPE_MX:
1826                 return rec1->data.mx.wPriority == rec2->data.mx.wPriority &&
1827                         dns_name_equal(rec1->data.mx.nameTarget, rec2->data.mx.nameTarget);
1828
1829         case DNS_TYPE_HINFO:
1830                 return strcmp(rec1->data.hinfo.cpu, rec2->data.hinfo.cpu) == 0 &&
1831                         strcmp(rec1->data.hinfo.os, rec2->data.hinfo.os) == 0;
1832
1833         case DNS_TYPE_SOA:
1834                 return dns_name_equal(rec1->data.soa.mname, rec2->data.soa.mname) &&
1835                         dns_name_equal(rec1->data.soa.rname, rec2->data.soa.rname) &&
1836                         rec1->data.soa.serial == rec2->data.soa.serial &&
1837                         rec1->data.soa.refresh == rec2->data.soa.refresh &&
1838                         rec1->data.soa.retry == rec2->data.soa.retry &&
1839                         rec1->data.soa.expire == rec2->data.soa.expire &&
1840                         rec1->data.soa.minimum == rec2->data.soa.minimum;
1841         default:
1842                 state->log(ISC_LOG_ERROR, "samba_dlz b9_record_match: unhandled record type %u",
1843                            rec1->wType);
1844                 break;
1845         }
1846
1847         return false;
1848 }
1849
1850 /*
1851  * Update session_info on samdb using the cached credentials
1852  */
1853 static bool b9_set_session_info(struct dlz_bind9_data *state, const char *name)
1854 {
1855         int ret;
1856
1857         if (state->update_name == NULL || state->session_info == NULL) {
1858                 state->log(ISC_LOG_ERROR, "samba_dlz: invalid credentials");
1859                 return false;
1860         }
1861
1862         /* Do not use client credentials, if we're not updating the client specified name */
1863         if (strcmp(state->update_name, name) != 0) {
1864                 return true;
1865         }
1866
1867         ret = ldb_set_opaque(
1868                 state->samdb,
1869                 DSDB_SESSION_INFO,
1870                 state->session_info);
1871         if (ret != LDB_SUCCESS) {
1872                 state->log(ISC_LOG_ERROR, "samba_dlz: unable to set session info");
1873                 return false;
1874         }
1875
1876         return true;
1877 }
1878
1879 /*
1880  * Reset session_info on samdb as system session
1881  */
1882 static void b9_reset_session_info(struct dlz_bind9_data *state)
1883 {
1884         ldb_set_opaque(
1885                 state->samdb,
1886                 DSDB_SESSION_INFO,
1887                 system_session(state->lp));
1888 }
1889
1890 /*
1891   add or modify a rdataset
1892  */
1893 _PUBLIC_ isc_result_t dlz_addrdataset(const char *name, const char *rdatastr, void *dbdata, void *version)
1894 {
1895         struct timeval start = timeval_current();
1896         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1897         struct dnsp_DnssrvRpcRecord *rec;
1898         struct ldb_dn *dn;
1899         isc_result_t result = ISC_R_SUCCESS;
1900         bool tombstoned = false;
1901         bool needs_add = false;
1902         struct dnsp_DnssrvRpcRecord *recs = NULL;
1903         uint16_t num_recs = 0;
1904         uint16_t first = 0;
1905         uint16_t i;
1906         NTTIME t;
1907         WERROR werr;
1908
1909         if (state->transaction_token != (void*)version) {
1910                 state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version");
1911                 result = ISC_R_FAILURE;
1912                 goto exit;
1913         }
1914
1915         rec = talloc_zero(state, struct dnsp_DnssrvRpcRecord);
1916         if (rec == NULL) {
1917                 result = ISC_R_NOMEMORY;
1918                 goto exit;
1919         }
1920
1921         rec->rank        = DNS_RANK_ZONE;
1922
1923         if (!b9_parse(state, rdatastr, rec)) {
1924                 state->log(ISC_LOG_INFO, "samba_dlz: failed to parse rdataset '%s'", rdatastr);
1925                 talloc_free(rec);
1926                 result = ISC_R_FAILURE;
1927                 goto exit;
1928         }
1929
1930         /* find the DN of the record */
1931         result = b9_find_name_dn(state, name, rec, &dn);
1932         if (result != ISC_R_SUCCESS) {
1933                 talloc_free(rec);
1934                 goto exit;
1935         }
1936
1937         /* get any existing records */
1938         werr = dns_common_lookup(state->samdb, rec, dn,
1939                                  &recs, &num_recs, &tombstoned);
1940         if (W_ERROR_EQUAL(werr, WERR_DNS_ERROR_NAME_DOES_NOT_EXIST)) {
1941                 needs_add = true;
1942                 werr = WERR_OK;
1943         }
1944         if (!W_ERROR_IS_OK(werr)) {
1945                 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s, %s",
1946                            ldb_dn_get_linearized(dn), win_errstr(werr));
1947                 talloc_free(rec);
1948                 result = ISC_R_FAILURE;
1949                 goto exit;
1950         }
1951
1952         if (tombstoned) {
1953                 /*
1954                  * we need to keep the existing tombstone record
1955                  * and ignore it
1956                  */
1957                 first = num_recs;
1958         }
1959
1960         /* there are existing records. We need to see if this will
1961          * replace a record or add to it
1962          */
1963         for (i=first; i < num_recs; i++) {
1964                 if (b9_record_match(state, rec, &recs[i])) {
1965                         break;
1966                 }
1967         }
1968         if (i == UINT16_MAX) {
1969                 state->log(ISC_LOG_ERROR, "samba_dlz: failed to already %u dnsRecord values for %s",
1970                            i, ldb_dn_get_linearized(dn));
1971                 talloc_free(rec);
1972                 result = ISC_R_FAILURE;
1973                 goto exit;
1974         }
1975
1976         if (i == num_recs) {
1977                 /* adding a new value */
1978                 recs = talloc_realloc(rec, recs,
1979                                       struct dnsp_DnssrvRpcRecord,
1980                                       num_recs + 1);
1981                 if (recs == NULL) {
1982                         talloc_free(rec);
1983                         result = ISC_R_NOMEMORY;
1984                         goto exit;
1985                 }
1986                 num_recs++;
1987
1988                 if (dns_name_is_static(recs, num_recs)) {
1989                         rec->dwTimeStamp = 0;
1990                 } else {
1991                         unix_to_nt_time(&t, time(NULL));
1992                         t /= 10 * 1000 * 1000; /* convert to seconds */
1993                         t /= 3600;           /* convert to hours */
1994                         rec->dwTimeStamp = (uint32_t)t;
1995                 }
1996         }
1997
1998         recs[i] = *rec;
1999
2000         if (!b9_set_session_info(state, name)) {
2001                 talloc_free(rec);
2002                 result = ISC_R_FAILURE;
2003                 goto exit;
2004         }
2005
2006         /* modify the record */
2007         werr = dns_common_replace(state->samdb, rec, dn,
2008                                   needs_add,
2009                                   state->soa_serial,
2010                                   recs, num_recs);
2011         b9_reset_session_info(state);
2012         if (!W_ERROR_IS_OK(werr)) {
2013                 state->log(ISC_LOG_ERROR, "samba_dlz: failed to %s %s - %s",
2014                            needs_add ? "add" : "modify",
2015                            ldb_dn_get_linearized(dn), win_errstr(werr));
2016                 talloc_free(rec);
2017                 result = ISC_R_FAILURE;
2018                 goto exit;
2019         }
2020
2021         state->log(ISC_LOG_INFO, "samba_dlz: added rdataset %s '%s'", name, rdatastr);
2022
2023         talloc_free(rec);
2024 exit:
2025         DNS_COMMON_LOG_OPERATION(
2026                 isc_result_str(result),
2027                 &start,
2028                 NULL,
2029                 name,
2030                 rdatastr);
2031         return result;
2032 }
2033
2034 /*
2035   remove a rdataset
2036  */
2037 _PUBLIC_ isc_result_t dlz_subrdataset(const char *name, const char *rdatastr, void *dbdata, void *version)
2038 {
2039         struct timeval start = timeval_current();
2040         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
2041         struct dnsp_DnssrvRpcRecord *rec;
2042         struct ldb_dn *dn;
2043         isc_result_t result = ISC_R_SUCCESS;
2044         struct dnsp_DnssrvRpcRecord *recs = NULL;
2045         uint16_t num_recs = 0;
2046         uint16_t i;
2047         WERROR werr;
2048
2049         if (state->transaction_token != (void*)version) {
2050                 state->log(ISC_LOG_ERROR, "samba_dlz: bad transaction version");
2051                 result = ISC_R_FAILURE;
2052                 goto exit;
2053         }
2054
2055         rec = talloc_zero(state, struct dnsp_DnssrvRpcRecord);
2056         if (rec == NULL) {
2057                 result = ISC_R_NOMEMORY;
2058                 goto exit;
2059         }
2060
2061         if (!b9_parse(state, rdatastr, rec)) {
2062                 state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse rdataset '%s'", rdatastr);
2063                 talloc_free(rec);
2064                 result = ISC_R_FAILURE;
2065                 goto exit;
2066         }
2067
2068         /* find the DN of the record */
2069         result = b9_find_name_dn(state, name, rec, &dn);
2070         if (result != ISC_R_SUCCESS) {
2071                 talloc_free(rec);
2072                 goto exit;
2073         }
2074
2075         /* get the existing records */
2076         werr = dns_common_lookup(state->samdb, rec, dn,
2077                                  &recs, &num_recs, NULL);
2078         if (!W_ERROR_IS_OK(werr)) {
2079                 talloc_free(rec);
2080                 result = ISC_R_NOTFOUND;
2081                 goto exit;
2082         }
2083
2084         for (i=0; i < num_recs; i++) {
2085                 if (b9_record_match(state, rec, &recs[i])) {
2086                         recs[i] = (struct dnsp_DnssrvRpcRecord) {
2087                                 .wType = DNS_TYPE_TOMBSTONE,
2088                         };
2089                         break;
2090                 }
2091         }
2092         if (i == num_recs) {
2093                 talloc_free(rec);
2094                 result = ISC_R_NOTFOUND;
2095                 goto exit;
2096         }
2097
2098         if (!b9_set_session_info(state, name)) {
2099                 talloc_free(rec);
2100                 result = ISC_R_FAILURE;
2101                 goto exit;
2102         }
2103
2104         /* modify the record */
2105         werr = dns_common_replace(state->samdb, rec, dn,
2106                                   false,/* needs_add */
2107                                   state->soa_serial,
2108                                   recs, num_recs);
2109         b9_reset_session_info(state);
2110         if (!W_ERROR_IS_OK(werr)) {
2111                 state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s",
2112                            ldb_dn_get_linearized(dn), win_errstr(werr));
2113                 talloc_free(rec);
2114                 result = ISC_R_FAILURE;
2115                 goto exit;
2116         }
2117
2118         state->log(ISC_LOG_INFO, "samba_dlz: subtracted rdataset %s '%s'", name, rdatastr);
2119
2120         talloc_free(rec);
2121 exit:
2122         DNS_COMMON_LOG_OPERATION(
2123                 isc_result_str(result),
2124                 &start,
2125                 NULL,
2126                 name,
2127                 rdatastr);
2128         return result;
2129 }
2130
2131
2132 /*
2133   delete all records of the given type
2134  */
2135 _PUBLIC_ isc_result_t dlz_delrdataset(const char *name, const char *type, void *dbdata, void *version)
2136 {
2137         struct timeval start = timeval_current();
2138         struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
2139         TALLOC_CTX *tmp_ctx;
2140         struct ldb_dn *dn;
2141         isc_result_t result = ISC_R_SUCCESS;
2142         enum dns_record_type dns_type;
2143         bool found = false;
2144         struct dnsp_DnssrvRpcRecord *recs = NULL;
2145         uint16_t num_recs = 0;
2146         uint16_t ri = 0;
2147         WERROR werr;
2148
2149         if (state->transaction_token != (void*)version) {
2150                 state->log(ISC_LOG_ERROR, "samba_dlz: bad transaction version");
2151                 result = ISC_R_FAILURE;
2152                 goto exit;
2153         }
2154
2155         if (!b9_dns_type(type, &dns_type)) {
2156                 state->log(ISC_LOG_ERROR, "samba_dlz: bad dns type %s in delete", type);
2157                 result = ISC_R_FAILURE;
2158                 goto exit;
2159         }
2160
2161         tmp_ctx = talloc_new(state);
2162
2163         /* find the DN of the record */
2164         result = b9_find_name_dn(state, name, tmp_ctx, &dn);
2165         if (result != ISC_R_SUCCESS) {
2166                 talloc_free(tmp_ctx);
2167                 goto exit;
2168         }
2169
2170         /* get the existing records */
2171         werr = dns_common_lookup(state->samdb, tmp_ctx, dn,
2172                                  &recs, &num_recs, NULL);
2173         if (!W_ERROR_IS_OK(werr)) {
2174                 talloc_free(tmp_ctx);
2175                 result = ISC_R_NOTFOUND;
2176                 goto exit;
2177         }
2178
2179         for (ri=0; ri < num_recs; ri++) {
2180                 if (dns_type != recs[ri].wType) {
2181                         continue;
2182                 }
2183
2184                 found = true;
2185                 recs[ri] = (struct dnsp_DnssrvRpcRecord) {
2186                         .wType = DNS_TYPE_TOMBSTONE,
2187                 };
2188         }
2189
2190         if (!found) {
2191                 talloc_free(tmp_ctx);
2192                 result = ISC_R_FAILURE;
2193                 goto exit;
2194         }
2195
2196         if (!b9_set_session_info(state, name)) {
2197                 talloc_free(tmp_ctx);
2198                 result = ISC_R_FAILURE;
2199                 goto exit;
2200         }
2201
2202         /* modify the record */
2203         werr = dns_common_replace(state->samdb, tmp_ctx, dn,
2204                                   false,/* needs_add */
2205                                   state->soa_serial,
2206                                   recs, num_recs);
2207         b9_reset_session_info(state);
2208         if (!W_ERROR_IS_OK(werr)) {
2209                 state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s",
2210                            ldb_dn_get_linearized(dn), win_errstr(werr));
2211                 talloc_free(tmp_ctx);
2212                 result = ISC_R_FAILURE;
2213                 goto exit;
2214         }
2215
2216         state->log(ISC_LOG_INFO, "samba_dlz: deleted rdataset %s of type %s", name, type);
2217
2218         talloc_free(tmp_ctx);
2219 exit:
2220         DNS_COMMON_LOG_OPERATION(
2221                 isc_result_str(result),
2222                 &start,
2223                 NULL,
2224                 name,
2225                 type);
2226         return result;
2227 }