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