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