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