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