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