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