Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : bind9 dlz driver for Samba
5 :
6 : Copyright (C) 2010 Andrew Tridgell
7 :
8 : This program is free software; you can redistribute it and/or modify
9 : it under the terms of the GNU General Public License as published by
10 : the Free Software Foundation; either version 3 of the License, or
11 : (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program. If not, see <http://www.gnu.org/licenses/>.
20 : */
21 :
22 : #include "includes.h"
23 : #include "talloc.h"
24 : #include "param/param.h"
25 : #include "lib/events/events.h"
26 : #include "dsdb/samdb/samdb.h"
27 : #include "dsdb/common/util.h"
28 : #include "auth/auth.h"
29 : #include "auth/session.h"
30 : #include "auth/gensec/gensec.h"
31 : #include "librpc/gen_ndr/security.h"
32 : #include "auth/credentials/credentials.h"
33 : #include "system/kerberos.h"
34 : #include "auth/kerberos/kerberos.h"
35 : #include "gen_ndr/ndr_dnsp.h"
36 : #include "gen_ndr/server_id.h"
37 : #include "messaging/messaging.h"
38 : #include <popt.h>
39 : #include "lib/util/dlinklist.h"
40 : #include "dlz_minimal.h"
41 : #include "dnsserver_common.h"
42 : #include "lib/util/smb_strtox.h"
43 : #include "lib/util/access.h"
44 :
45 : #undef strcasecmp
46 :
47 : struct b9_options {
48 : const char *url;
49 : const char *debug;
50 : };
51 :
52 : struct b9_zone {
53 : char *name;
54 : struct b9_zone *prev, *next;
55 : };
56 :
57 : struct dlz_bind9_data {
58 : struct b9_options options;
59 : struct ldb_context *samdb;
60 : struct tevent_context *ev_ctx;
61 : struct loadparm_context *lp;
62 : int *transaction_token;
63 : uint32_t soa_serial;
64 : struct b9_zone *zonelist;
65 :
66 : /* Used for dynamic update */
67 : struct smb_krb5_context *smb_krb5_ctx;
68 : struct auth4_context *auth_context;
69 : struct auth_session_info *session_info;
70 : char *update_name;
71 :
72 : /* helper functions from the dlz_dlopen driver */
73 : log_t *log;
74 : dns_sdlz_putrr_t *putrr;
75 : dns_sdlz_putnamedrr_t *putnamedrr;
76 : dns_dlz_writeablezone_t *writeable_zone;
77 : };
78 :
79 : static struct dlz_bind9_data *dlz_bind9_state = NULL;
80 : static int dlz_bind9_state_ref_count = 0;
81 :
82 : static const char *zone_prefixes[] = {
83 : "CN=MicrosoftDNS,DC=DomainDnsZones",
84 : "CN=MicrosoftDNS,DC=ForestDnsZones",
85 : "CN=MicrosoftDNS,CN=System",
86 : NULL
87 : };
88 :
89 : /*
90 : * Get a printable string representation of an isc_result_t
91 : */
92 0 : static const char *isc_result_str( const isc_result_t result) {
93 0 : switch (result) {
94 0 : case ISC_R_SUCCESS:
95 0 : return "ISC_R_SUCCESS";
96 0 : case ISC_R_NOMEMORY:
97 0 : return "ISC_R_NOMEMORY";
98 0 : case ISC_R_NOPERM:
99 0 : return "ISC_R_NOPERM";
100 0 : case ISC_R_NOSPACE:
101 0 : return "ISC_R_NOSPACE";
102 0 : case ISC_R_NOTFOUND:
103 0 : return "ISC_R_NOTFOUND";
104 0 : case ISC_R_FAILURE:
105 0 : return "ISC_R_FAILURE";
106 0 : case ISC_R_NOTIMPLEMENTED:
107 0 : return "ISC_R_NOTIMPLEMENTED";
108 0 : case ISC_R_NOMORE:
109 0 : return "ISC_R_NOMORE";
110 0 : case ISC_R_INVALIDFILE:
111 0 : return "ISC_R_INVALIDFILE";
112 0 : case ISC_R_UNEXPECTED:
113 0 : return "ISC_R_UNEXPECTED";
114 0 : case ISC_R_FILENOTFOUND:
115 0 : return "ISC_R_FILENOTFOUND";
116 0 : default:
117 0 : return "UNKNOWN";
118 : }
119 : }
120 :
121 : /*
122 : return the version of the API
123 : */
124 1 : _PUBLIC_ int dlz_version(unsigned int *flags)
125 : {
126 1 : return DLZ_DLOPEN_VERSION;
127 : }
128 :
129 : /*
130 : remember a helper function from the bind9 dlz_dlopen driver
131 : */
132 39 : static void b9_add_helper(struct dlz_bind9_data *state, const char *helper_name, void *ptr)
133 : {
134 39 : if (strcmp(helper_name, "log") == 0) {
135 15 : state->log = ptr;
136 : }
137 39 : if (strcmp(helper_name, "putrr") == 0) {
138 5 : state->putrr = ptr;
139 : }
140 39 : if (strcmp(helper_name, "putnamedrr") == 0) {
141 5 : state->putnamedrr = ptr;
142 : }
143 39 : if (strcmp(helper_name, "writeable_zone") == 0) {
144 14 : state->writeable_zone = ptr;
145 : }
146 39 : }
147 :
148 : /*
149 : * Add a trailing '.' if it's missing
150 : */
151 56 : static const char *b9_format_fqdn(TALLOC_CTX *mem_ctx, const char *str)
152 : {
153 0 : size_t len;
154 0 : const char *tmp;
155 :
156 56 : if (str == NULL || str[0] == '\0') {
157 0 : return str;
158 : }
159 :
160 56 : len = strlen(str);
161 56 : if (str[len-1] != '.') {
162 56 : tmp = talloc_asprintf(mem_ctx, "%s.", str);
163 : } else {
164 0 : tmp = str;
165 : }
166 56 : return tmp;
167 : }
168 :
169 : /*
170 : * Format a record for bind9.
171 : *
172 : * On failure/error returns false, OR sets *data to NULL.
173 : * Callers should check for both!
174 : */
175 93 : static bool b9_format(struct dlz_bind9_data *state,
176 : TALLOC_CTX *mem_ctx,
177 : struct dnsp_DnssrvRpcRecord *rec,
178 : const char **type, const char **data)
179 : {
180 0 : uint32_t i;
181 0 : char *tmp;
182 0 : const char *fqdn;
183 :
184 93 : switch (rec->wType) {
185 39 : case DNS_TYPE_A:
186 39 : *type = "a";
187 39 : *data = rec->data.ipv4;
188 39 : break;
189 :
190 16 : case DNS_TYPE_AAAA:
191 16 : *type = "aaaa";
192 16 : *data = rec->data.ipv6;
193 16 : break;
194 :
195 0 : case DNS_TYPE_CNAME:
196 0 : *type = "cname";
197 0 : *data = b9_format_fqdn(mem_ctx, rec->data.cname);
198 0 : break;
199 :
200 0 : case DNS_TYPE_TXT:
201 0 : *type = "txt";
202 0 : tmp = talloc_asprintf(mem_ctx, "\"%s\"", rec->data.txt.str[0]);
203 0 : for (i=1; i<rec->data.txt.count; i++) {
204 0 : talloc_asprintf_addbuf(&tmp, " \"%s\"", rec->data.txt.str[i]);
205 : }
206 0 : *data = tmp;
207 0 : break;
208 :
209 10 : case DNS_TYPE_PTR:
210 10 : *type = "ptr";
211 10 : *data = b9_format_fqdn(mem_ctx, rec->data.ptr);
212 10 : break;
213 :
214 13 : case DNS_TYPE_SRV:
215 13 : *type = "srv";
216 13 : fqdn = b9_format_fqdn(mem_ctx, rec->data.srv.nameTarget);
217 13 : if (fqdn == NULL) {
218 0 : return false;
219 : }
220 26 : *data = talloc_asprintf(mem_ctx, "%u %u %u %s",
221 13 : rec->data.srv.wPriority,
222 13 : rec->data.srv.wWeight,
223 13 : rec->data.srv.wPort,
224 : fqdn);
225 13 : break;
226 :
227 10 : case DNS_TYPE_MX:
228 10 : *type = "mx";
229 10 : fqdn = b9_format_fqdn(mem_ctx, rec->data.mx.nameTarget);
230 10 : if (fqdn == NULL) {
231 0 : return false;
232 : }
233 20 : *data = talloc_asprintf(mem_ctx, "%u %s",
234 10 : rec->data.mx.wPriority, fqdn);
235 10 : break;
236 :
237 3 : case DNS_TYPE_NS:
238 3 : *type = "ns";
239 3 : *data = b9_format_fqdn(mem_ctx, rec->data.ns);
240 3 : break;
241 :
242 2 : case DNS_TYPE_SOA: {
243 0 : const char *mname;
244 2 : *type = "soa";
245 :
246 : /* we need to fake the authoritative nameserver to
247 : * point at ourselves. This is how AD DNS servers
248 : * force clients to send updates to the right local DC
249 : */
250 2 : mname = talloc_asprintf(mem_ctx, "%s.%s.",
251 : lpcfg_netbios_name(state->lp),
252 : lpcfg_dnsdomain(state->lp));
253 2 : if (mname == NULL) {
254 0 : return false;
255 : }
256 2 : mname = strlower_talloc(mem_ctx, mname);
257 2 : if (mname == NULL) {
258 0 : return false;
259 : }
260 :
261 2 : fqdn = b9_format_fqdn(mem_ctx, rec->data.soa.rname);
262 2 : if (fqdn == NULL) {
263 0 : return false;
264 : }
265 :
266 2 : state->soa_serial = rec->data.soa.serial;
267 :
268 2 : *data = talloc_asprintf(mem_ctx, "%s %s %u %u %u %u %u",
269 : mname, fqdn,
270 : rec->data.soa.serial,
271 : rec->data.soa.refresh,
272 : rec->data.soa.retry,
273 : rec->data.soa.expire,
274 : rec->data.soa.minimum);
275 2 : break;
276 : }
277 :
278 0 : default:
279 0 : state->log(ISC_LOG_ERROR, "samba_dlz b9_format: unhandled record type %u",
280 0 : rec->wType);
281 0 : return false;
282 : }
283 :
284 93 : return true;
285 : }
286 :
287 : static const struct {
288 : enum dns_record_type dns_type;
289 : const char *typestr;
290 : bool single_valued;
291 : } dns_typemap[] = {
292 : { DNS_TYPE_A, "A" , false},
293 : { DNS_TYPE_AAAA, "AAAA" , false},
294 : { DNS_TYPE_CNAME, "CNAME" , true},
295 : { DNS_TYPE_TXT, "TXT" , false},
296 : { DNS_TYPE_PTR, "PTR" , false},
297 : { DNS_TYPE_SRV, "SRV" , false},
298 : { DNS_TYPE_MX, "MX" , false},
299 : { DNS_TYPE_NS, "NS" , false},
300 : { DNS_TYPE_SOA, "SOA" , true},
301 : };
302 :
303 :
304 : /*
305 : see if a DNS type is single valued
306 : */
307 46 : static bool b9_single_valued(enum dns_record_type dns_type)
308 : {
309 : int i;
310 103 : for (i=0; i<ARRAY_SIZE(dns_typemap); i++) {
311 103 : if (dns_typemap[i].dns_type == dns_type) {
312 46 : return dns_typemap[i].single_valued;
313 : }
314 : }
315 0 : return false;
316 : }
317 :
318 : /*
319 : get a DNS_TYPE_* value from the corresponding string
320 : */
321 3 : static bool b9_dns_type(const char *type, enum dns_record_type *dtype)
322 : {
323 0 : int i;
324 6 : for (i=0; i<ARRAY_SIZE(dns_typemap); i++) {
325 6 : if (strcasecmp(dns_typemap[i].typestr, type) == 0) {
326 3 : *dtype = dns_typemap[i].dns_type;
327 3 : return true;
328 : }
329 : }
330 0 : return false;
331 : }
332 :
333 :
334 : #define DNS_PARSE_STR(ret, str, sep, saveptr) do { \
335 : (ret) = strtok_r(str, sep, &saveptr); \
336 : if ((ret) == NULL) return false; \
337 : } while (0)
338 :
339 : #define DNS_PARSE_UINT(ret, str, sep, saveptr) do { \
340 : char *istr = strtok_r(str, sep, &saveptr); \
341 : int error = 0;\
342 : if ((istr) == NULL) return false; \
343 : (ret) = smb_strtoul(istr, NULL, 10, &error, SMB_STR_STANDARD); \
344 : if (error != 0) {\
345 : return false;\
346 : }\
347 : } while (0)
348 :
349 : /*
350 : parse a record from bind9
351 : */
352 47 : static bool b9_parse(struct dlz_bind9_data *state,
353 : const char *rdatastr,
354 : struct dnsp_DnssrvRpcRecord *rec)
355 : {
356 0 : char *full_name, *dclass, *type;
357 47 : char *str, *tmp, *saveptr=NULL;
358 0 : int i;
359 :
360 47 : str = talloc_strdup(rec, rdatastr);
361 47 : if (str == NULL) {
362 0 : return false;
363 : }
364 :
365 : /* parse the SDLZ string form */
366 47 : DNS_PARSE_STR(full_name, str, "\t", saveptr);
367 47 : DNS_PARSE_UINT(rec->dwTtlSeconds, NULL, "\t", saveptr);
368 47 : DNS_PARSE_STR(dclass, NULL, "\t", saveptr);
369 47 : DNS_PARSE_STR(type, NULL, "\t", saveptr);
370 :
371 : /* construct the record */
372 125 : for (i=0; i<ARRAY_SIZE(dns_typemap); i++) {
373 125 : if (strcasecmp(type, dns_typemap[i].typestr) == 0) {
374 47 : rec->wType = dns_typemap[i].dns_type;
375 47 : break;
376 : }
377 : }
378 47 : if (i == ARRAY_SIZE(dns_typemap)) {
379 0 : state->log(ISC_LOG_ERROR, "samba_dlz: unsupported record type '%s' for '%s'",
380 : type, full_name);
381 0 : return false;
382 : }
383 :
384 47 : switch (rec->wType) {
385 25 : case DNS_TYPE_A:
386 25 : DNS_PARSE_STR(rec->data.ipv4, NULL, " ", saveptr);
387 25 : break;
388 :
389 8 : case DNS_TYPE_AAAA:
390 8 : DNS_PARSE_STR(rec->data.ipv6, NULL, " ", saveptr);
391 8 : break;
392 :
393 0 : case DNS_TYPE_CNAME:
394 0 : DNS_PARSE_STR(rec->data.cname, NULL, " ", saveptr);
395 0 : break;
396 :
397 0 : case DNS_TYPE_TXT:
398 0 : rec->data.txt.count = 0;
399 0 : rec->data.txt.str = talloc_array(rec, const char *, rec->data.txt.count);
400 0 : tmp = strtok_r(NULL, "\t", &saveptr);
401 0 : while (tmp) {
402 0 : rec->data.txt.str = talloc_realloc(rec, rec->data.txt.str, const char *,
403 : rec->data.txt.count+1);
404 0 : if (tmp[0] == '"') {
405 : /* Strip quotes */
406 0 : rec->data.txt.str[rec->data.txt.count] = talloc_strndup(rec, &tmp[1], strlen(tmp)-2);
407 : } else {
408 0 : rec->data.txt.str[rec->data.txt.count] = talloc_strdup(rec, tmp);
409 : }
410 0 : rec->data.txt.count++;
411 0 : tmp = strtok_r(NULL, " ", &saveptr);
412 : }
413 0 : break;
414 :
415 7 : case DNS_TYPE_PTR:
416 7 : DNS_PARSE_STR(rec->data.ptr, NULL, " ", saveptr);
417 7 : break;
418 :
419 0 : case DNS_TYPE_SRV:
420 0 : DNS_PARSE_UINT(rec->data.srv.wPriority, NULL, " ", saveptr);
421 0 : DNS_PARSE_UINT(rec->data.srv.wWeight, NULL, " ", saveptr);
422 0 : DNS_PARSE_UINT(rec->data.srv.wPort, NULL, " ", saveptr);
423 0 : DNS_PARSE_STR(rec->data.srv.nameTarget, NULL, " ", saveptr);
424 0 : break;
425 :
426 7 : case DNS_TYPE_MX:
427 7 : DNS_PARSE_UINT(rec->data.mx.wPriority, NULL, " ", saveptr);
428 7 : DNS_PARSE_STR(rec->data.mx.nameTarget, NULL, " ", saveptr);
429 7 : break;
430 :
431 0 : case DNS_TYPE_NS:
432 0 : DNS_PARSE_STR(rec->data.ns, NULL, " ", saveptr);
433 0 : break;
434 :
435 0 : case DNS_TYPE_SOA:
436 0 : DNS_PARSE_STR(rec->data.soa.mname, NULL, " ", saveptr);
437 0 : DNS_PARSE_STR(rec->data.soa.rname, NULL, " ", saveptr);
438 0 : DNS_PARSE_UINT(rec->data.soa.serial, NULL, " ", saveptr);
439 0 : DNS_PARSE_UINT(rec->data.soa.refresh, NULL, " ", saveptr);
440 0 : DNS_PARSE_UINT(rec->data.soa.retry, NULL, " ", saveptr);
441 0 : DNS_PARSE_UINT(rec->data.soa.expire, NULL, " ", saveptr);
442 0 : DNS_PARSE_UINT(rec->data.soa.minimum, NULL, " ", saveptr);
443 0 : break;
444 :
445 0 : default:
446 0 : state->log(ISC_LOG_ERROR, "samba_dlz b9_parse: unhandled record type %u",
447 0 : rec->wType);
448 0 : return false;
449 : }
450 :
451 : /* we should be at the end of the buffer now */
452 47 : if (strtok_r(NULL, "\t ", &saveptr) != NULL) {
453 0 : state->log(ISC_LOG_ERROR, "samba_dlz b9_parse: unexpected data at end of string for '%s'",
454 : rdatastr);
455 0 : return false;
456 : }
457 :
458 47 : return true;
459 : }
460 :
461 : /*
462 : send a resource record to bind9
463 : */
464 69 : static isc_result_t b9_putrr(struct dlz_bind9_data *state,
465 : void *handle, struct dnsp_DnssrvRpcRecord *rec,
466 : const char **types)
467 : {
468 0 : isc_result_t result;
469 0 : const char *type, *data;
470 69 : TALLOC_CTX *tmp_ctx = talloc_new(state);
471 :
472 69 : if (!b9_format(state, tmp_ctx, rec, &type, &data)) {
473 0 : return ISC_R_FAILURE;
474 : }
475 :
476 69 : if (data == NULL) {
477 0 : talloc_free(tmp_ctx);
478 0 : return ISC_R_NOMEMORY;
479 : }
480 :
481 69 : if (types) {
482 : int i;
483 0 : for (i=0; types[i]; i++) {
484 0 : if (strcmp(types[i], type) == 0) break;
485 : }
486 0 : if (types[i] == NULL) {
487 : /* skip it */
488 0 : return ISC_R_SUCCESS;
489 : }
490 : }
491 :
492 69 : result = state->putrr(handle, type, rec->dwTtlSeconds, data);
493 69 : if (result != ISC_R_SUCCESS) {
494 0 : state->log(ISC_LOG_ERROR, "Failed to put rr");
495 : }
496 69 : talloc_free(tmp_ctx);
497 69 : return result;
498 : }
499 :
500 :
501 : /*
502 : send a named resource record to bind9
503 : */
504 24 : static isc_result_t b9_putnamedrr(struct dlz_bind9_data *state,
505 : void *handle, const char *name,
506 : struct dnsp_DnssrvRpcRecord *rec)
507 : {
508 0 : isc_result_t result;
509 0 : const char *type, *data;
510 24 : TALLOC_CTX *tmp_ctx = talloc_new(state);
511 :
512 24 : if (!b9_format(state, tmp_ctx, rec, &type, &data)) {
513 0 : return ISC_R_FAILURE;
514 : }
515 :
516 24 : if (data == NULL) {
517 0 : talloc_free(tmp_ctx);
518 0 : return ISC_R_NOMEMORY;
519 : }
520 :
521 24 : result = state->putnamedrr(handle, name, type, rec->dwTtlSeconds, data);
522 24 : if (result != ISC_R_SUCCESS) {
523 0 : state->log(ISC_LOG_ERROR, "Failed to put named rr '%s'", name);
524 : }
525 24 : talloc_free(tmp_ctx);
526 24 : return result;
527 : }
528 :
529 : /*
530 : parse options
531 : */
532 15 : static isc_result_t parse_options(struct dlz_bind9_data *state,
533 : unsigned int argc, const char **argv,
534 : struct b9_options *options)
535 : {
536 0 : int opt;
537 0 : poptContext pc;
538 15 : struct poptOption long_options[] = {
539 15 : { "url", 'H', POPT_ARG_STRING, &options->url, 0, "database URL", "URL" },
540 15 : { "debug", 'd', POPT_ARG_STRING, &options->debug, 0, "debug level", "DEBUG" },
541 : {0}
542 : };
543 :
544 15 : pc = poptGetContext("dlz_bind9", argc, argv, long_options,
545 : POPT_CONTEXT_KEEP_FIRST);
546 15 : while ((opt = poptGetNextOpt(pc)) != -1) {
547 0 : switch (opt) {
548 0 : default:
549 0 : state->log(ISC_LOG_ERROR, "dlz_bind9: Invalid option %s: %s",
550 : poptBadOption(pc, 0), poptStrerror(opt));
551 0 : poptFreeContext(pc);
552 0 : return ISC_R_FAILURE;
553 : }
554 : }
555 :
556 15 : poptFreeContext(pc);
557 15 : return ISC_R_SUCCESS;
558 : }
559 :
560 :
561 : /*
562 : * Create session info from PAC
563 : * This is called as auth_context->generate_session_info_pac()
564 : */
565 4 : static NTSTATUS b9_generate_session_info_pac(struct auth4_context *auth_context,
566 : TALLOC_CTX *mem_ctx,
567 : struct smb_krb5_context *smb_krb5_context,
568 : DATA_BLOB *pac_blob,
569 : const char *principal_name,
570 : const struct tsocket_address *remote_addr,
571 : uint32_t session_info_flags,
572 : struct auth_session_info **session_info)
573 : {
574 0 : NTSTATUS status;
575 0 : struct auth_user_info_dc *user_info_dc;
576 0 : TALLOC_CTX *tmp_ctx;
577 :
578 4 : tmp_ctx = talloc_new(mem_ctx);
579 4 : NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
580 :
581 4 : status = kerberos_pac_blob_to_user_info_dc(tmp_ctx,
582 : *pac_blob,
583 : smb_krb5_context->krb5_context,
584 : &user_info_dc,
585 : NULL,
586 : NULL);
587 4 : if (!NT_STATUS_IS_OK(status)) {
588 0 : talloc_free(tmp_ctx);
589 0 : return status;
590 : }
591 :
592 4 : if (!(user_info_dc->info->user_flags & NETLOGON_GUEST)) {
593 4 : session_info_flags |= AUTH_SESSION_INFO_AUTHENTICATED;
594 : }
595 :
596 4 : session_info_flags |= AUTH_SESSION_INFO_SIMPLE_PRIVILEGES;
597 :
598 4 : status = auth_generate_session_info(mem_ctx, auth_context->lp_ctx, NULL, user_info_dc,
599 : session_info_flags, session_info);
600 4 : if (!NT_STATUS_IS_OK(status)) {
601 0 : talloc_free(tmp_ctx);
602 0 : return status;
603 : }
604 :
605 4 : talloc_free(tmp_ctx);
606 4 : return status;
607 : }
608 :
609 : /* Callback for the DEBUG() system, to catch the remaining messages */
610 2 : static void b9_debug(void *private_ptr, int msg_level, const char *msg)
611 : {
612 0 : static const int isc_log_map[] = {
613 : ISC_LOG_CRITICAL, /* 0 */
614 : ISC_LOG_ERROR, /* 1 */
615 : ISC_LOG_WARNING, /* 2 */
616 : ISC_LOG_NOTICE /* 3 */
617 : };
618 2 : struct dlz_bind9_data *state = private_ptr;
619 0 : int isc_log_level;
620 :
621 2 : if (msg_level >= ARRAY_SIZE(isc_log_map) || msg_level < 0) {
622 0 : isc_log_level = ISC_LOG_INFO;
623 : } else {
624 2 : isc_log_level = isc_log_map[msg_level];
625 : }
626 2 : state->log(isc_log_level, "samba_dlz: %s", msg);
627 2 : }
628 :
629 15 : static int dlz_state_debug_unregister(struct dlz_bind9_data *state)
630 : {
631 : /* Stop logging (to the bind9 logs) */
632 15 : debug_set_callback(NULL, NULL);
633 15 : return 0;
634 : }
635 :
636 : /*
637 : called to initialise the driver
638 : */
639 21 : _PUBLIC_ isc_result_t dlz_create(const char *dlzname,
640 : unsigned int argc, const char **argv,
641 : void **dbdata, ...)
642 : {
643 0 : struct dlz_bind9_data *state;
644 0 : const char *helper_name;
645 0 : va_list ap;
646 0 : isc_result_t result;
647 0 : struct ldb_dn *dn;
648 0 : NTSTATUS nt_status;
649 0 : int ret;
650 21 : char *errstring = NULL;
651 :
652 21 : if (dlz_bind9_state != NULL) {
653 6 : dlz_bind9_state->log(ISC_LOG_ERROR,
654 : "samba_dlz: dlz_create ignored, #refs=%d",
655 : dlz_bind9_state_ref_count);
656 6 : *dbdata = dlz_bind9_state;
657 6 : dlz_bind9_state_ref_count++;
658 6 : return ISC_R_SUCCESS;
659 : }
660 :
661 15 : state = talloc_zero(NULL, struct dlz_bind9_data);
662 15 : if (state == NULL) {
663 0 : return ISC_R_NOMEMORY;
664 : }
665 :
666 15 : talloc_set_destructor(state, dlz_state_debug_unregister);
667 :
668 : /* fill in the helper functions */
669 15 : va_start(ap, dbdata);
670 54 : while ((helper_name = va_arg(ap, const char *)) != NULL) {
671 39 : b9_add_helper(state, helper_name, va_arg(ap, void*));
672 : }
673 15 : va_end(ap);
674 :
675 : /* Do not install samba signal handlers */
676 15 : fault_setup_disable();
677 :
678 : /* Start logging (to the bind9 logs) */
679 15 : debug_set_callback(state, b9_debug);
680 :
681 15 : state->ev_ctx = s4_event_context_init(state);
682 15 : if (state->ev_ctx == NULL) {
683 0 : result = ISC_R_NOMEMORY;
684 0 : goto failed;
685 : }
686 :
687 15 : result = parse_options(state, argc, argv, &state->options);
688 15 : if (result != ISC_R_SUCCESS) {
689 0 : goto failed;
690 : }
691 :
692 15 : state->lp = loadparm_init_global(true);
693 15 : if (state->lp == NULL) {
694 0 : result = ISC_R_NOMEMORY;
695 0 : goto failed;
696 : }
697 :
698 15 : if (state->options.debug) {
699 0 : lpcfg_do_global_parameter(state->lp, "log level", state->options.debug);
700 : } else {
701 15 : lpcfg_do_global_parameter(state->lp, "log level", "0");
702 : }
703 :
704 15 : if (smb_krb5_init_context(state, state->lp, &state->smb_krb5_ctx) != 0) {
705 0 : result = ISC_R_NOMEMORY;
706 0 : goto failed;
707 : }
708 :
709 15 : nt_status = gensec_init();
710 15 : if (!NT_STATUS_IS_OK(nt_status)) {
711 0 : result = ISC_R_NOMEMORY;
712 0 : goto failed;
713 : }
714 :
715 15 : state->auth_context = talloc_zero(state, struct auth4_context);
716 15 : if (state->auth_context == NULL) {
717 0 : result = ISC_R_NOMEMORY;
718 0 : goto failed;
719 : }
720 :
721 15 : if (state->options.url == NULL) {
722 0 : state->options.url = talloc_asprintf(state,
723 : "%s/dns/sam.ldb",
724 : lpcfg_binddns_dir(state->lp));
725 0 : if (state->options.url == NULL) {
726 0 : result = ISC_R_NOMEMORY;
727 0 : goto failed;
728 : }
729 :
730 0 : if (!file_exist(state->options.url)) {
731 0 : state->options.url = talloc_asprintf(state,
732 : "%s/dns/sam.ldb",
733 : lpcfg_private_dir(state->lp));
734 0 : if (state->options.url == NULL) {
735 0 : result = ISC_R_NOMEMORY;
736 0 : goto failed;
737 : }
738 : }
739 : }
740 :
741 15 : ret = samdb_connect_url(state,
742 : state->ev_ctx,
743 : state->lp,
744 : system_session(state->lp),
745 : 0,
746 : state->options.url,
747 : NULL,
748 : &state->samdb,
749 : &errstring);
750 15 : if (ret != LDB_SUCCESS) {
751 0 : state->log(ISC_LOG_ERROR,
752 : "samba_dlz: Failed to connect to %s: %s",
753 : errstring, ldb_strerror(ret));
754 0 : result = ISC_R_FAILURE;
755 0 : goto failed;
756 : }
757 :
758 15 : dn = ldb_get_default_basedn(state->samdb);
759 15 : if (dn == NULL) {
760 0 : state->log(ISC_LOG_ERROR, "samba_dlz: Unable to get basedn for %s - %s",
761 : state->options.url, ldb_errstring(state->samdb));
762 0 : result = ISC_R_FAILURE;
763 0 : goto failed;
764 : }
765 :
766 15 : state->log(ISC_LOG_INFO, "samba_dlz: started for DN %s",
767 : ldb_dn_get_linearized(dn));
768 :
769 15 : state->auth_context->event_ctx = state->ev_ctx;
770 15 : state->auth_context->lp_ctx = state->lp;
771 15 : state->auth_context->sam_ctx = state->samdb;
772 15 : state->auth_context->generate_session_info_pac = b9_generate_session_info_pac;
773 :
774 15 : *dbdata = state;
775 15 : dlz_bind9_state = state;
776 15 : dlz_bind9_state_ref_count++;
777 :
778 15 : return ISC_R_SUCCESS;
779 :
780 0 : failed:
781 0 : state->log(ISC_LOG_INFO,
782 : "samba_dlz: FAILED dlz_create call result=%d #refs=%d",
783 : result,
784 : dlz_bind9_state_ref_count);
785 0 : talloc_free(state);
786 0 : return result;
787 : }
788 :
789 : /*
790 : shutdown the backend
791 : */
792 21 : _PUBLIC_ void dlz_destroy(void *dbdata)
793 : {
794 21 : struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
795 :
796 21 : dlz_bind9_state_ref_count--;
797 21 : if (dlz_bind9_state_ref_count == 0) {
798 15 : state->log(ISC_LOG_INFO, "samba_dlz: shutting down");
799 15 : talloc_unlink(state, state->samdb);
800 15 : talloc_free(state);
801 15 : dlz_bind9_state = NULL;
802 : } else {
803 6 : state->log(ISC_LOG_INFO,
804 : "samba_dlz: dlz_destroy called. %d refs remaining.",
805 : dlz_bind9_state_ref_count);
806 : }
807 21 : }
808 :
809 :
810 : /*
811 : return the base DN for a zone
812 : */
813 106 : static isc_result_t b9_find_zone_dn(struct dlz_bind9_data *state, const char *zone_name,
814 : TALLOC_CTX *mem_ctx, struct ldb_dn **zone_dn)
815 : {
816 0 : int ret;
817 106 : TALLOC_CTX *tmp_ctx = talloc_new(state);
818 106 : const char *attrs[] = { NULL };
819 0 : int i;
820 :
821 244 : for (i=0; zone_prefixes[i]; i++) {
822 0 : const char *casefold;
823 0 : struct ldb_dn *dn;
824 0 : struct ldb_result *res;
825 0 : struct ldb_val zone_name_val
826 198 : = data_blob_string_const(zone_name);
827 :
828 198 : dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
829 198 : if (dn == NULL) {
830 0 : talloc_free(tmp_ctx);
831 60 : return ISC_R_NOMEMORY;
832 : }
833 :
834 : /*
835 : * This dance ensures that it is not possible to put
836 : * (eg) an extra DC=x, into the DNS name being
837 : * queried
838 : */
839 :
840 198 : if (!ldb_dn_add_child_fmt(dn,
841 : "DC=X,%s",
842 : zone_prefixes[i])) {
843 0 : talloc_free(tmp_ctx);
844 0 : return ISC_R_NOMEMORY;
845 : }
846 :
847 198 : ret = ldb_dn_set_component(dn,
848 : 0,
849 : "DC",
850 : zone_name_val);
851 198 : if (ret != LDB_SUCCESS) {
852 0 : talloc_free(tmp_ctx);
853 0 : return ISC_R_NOMEMORY;
854 : }
855 :
856 : /*
857 : * Check if this is a plausibly valid DN early
858 : * (time spent here will be saved during the
859 : * search due to an internal cache)
860 : */
861 198 : casefold = ldb_dn_get_casefold(dn);
862 :
863 198 : if (casefold == NULL) {
864 0 : talloc_free(tmp_ctx);
865 0 : return ISC_R_NOTFOUND;
866 : }
867 :
868 198 : ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsZone");
869 198 : if (ret == LDB_SUCCESS) {
870 60 : if (zone_dn != NULL) {
871 54 : *zone_dn = talloc_steal(mem_ctx, dn);
872 : }
873 60 : talloc_free(tmp_ctx);
874 60 : return ISC_R_SUCCESS;
875 : }
876 138 : talloc_free(dn);
877 : }
878 :
879 46 : talloc_free(tmp_ctx);
880 46 : return ISC_R_NOTFOUND;
881 : }
882 :
883 :
884 : /*
885 : return the DN for a name. The record does not need to exist, but the
886 : zone must exist
887 : */
888 54 : static isc_result_t b9_find_name_dn(struct dlz_bind9_data *state, const char *name,
889 : TALLOC_CTX *mem_ctx, struct ldb_dn **dn)
890 : {
891 0 : const char *p;
892 :
893 : /* work through the name piece by piece, until we find a zone */
894 100 : for (p=name; p; ) {
895 0 : isc_result_t result;
896 100 : result = b9_find_zone_dn(state, p, mem_ctx, dn);
897 100 : if (result == ISC_R_SUCCESS) {
898 0 : const char *casefold;
899 :
900 : /* we found a zone, now extend the DN to get
901 : * the full DN
902 : */
903 0 : bool ret;
904 54 : if (p == name) {
905 8 : ret = ldb_dn_add_child_fmt(*dn, "DC=@");
906 8 : if (ret == false) {
907 0 : talloc_free(*dn);
908 0 : return ISC_R_NOMEMORY;
909 : }
910 : } else {
911 0 : struct ldb_val name_val
912 46 : = data_blob_const(name,
913 46 : (int)(p-name)-1);
914 :
915 46 : if (!ldb_dn_add_child_val(*dn,
916 : "DC",
917 : name_val)) {
918 0 : talloc_free(*dn);
919 0 : return ISC_R_NOMEMORY;
920 : }
921 : }
922 :
923 : /*
924 : * Check if this is a plausibly valid DN early
925 : * (time spent here will be saved during the
926 : * search due to an internal cache)
927 : */
928 54 : casefold = ldb_dn_get_casefold(*dn);
929 :
930 54 : if (casefold == NULL) {
931 0 : return ISC_R_NOTFOUND;
932 : }
933 :
934 54 : return ISC_R_SUCCESS;
935 : }
936 46 : p = strchr(p, '.');
937 46 : if (p == NULL) {
938 0 : break;
939 : }
940 46 : p++;
941 : }
942 0 : return ISC_R_NOTFOUND;
943 : }
944 :
945 :
946 : /*
947 : see if we handle a given zone
948 : */
949 0 : _PUBLIC_ isc_result_t dlz_findzonedb(void *dbdata, const char *name,
950 : dns_clientinfomethods_t *methods,
951 : dns_clientinfo_t *clientinfo)
952 : {
953 0 : struct timeval start = timeval_current();
954 0 : struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
955 0 : isc_result_t result = ISC_R_SUCCESS;
956 :
957 0 : result = b9_find_zone_dn(state, name, NULL, NULL);
958 0 : DNS_COMMON_LOG_OPERATION(
959 : isc_result_str(result),
960 : &start,
961 : NULL,
962 : name,
963 0 : NULL);
964 0 : return result;
965 : }
966 :
967 :
968 : /*
969 : lookup one record
970 : */
971 29 : static isc_result_t dlz_lookup_types(struct dlz_bind9_data *state,
972 : const char *zone, const char *name,
973 : dns_sdlzlookup_t *lookup,
974 : const char **types)
975 : {
976 29 : TALLOC_CTX *tmp_ctx = talloc_new(state);
977 0 : struct ldb_dn *dn;
978 29 : WERROR werr = WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
979 29 : struct dnsp_DnssrvRpcRecord *records = NULL;
980 29 : uint16_t num_records = 0, i;
981 0 : struct ldb_val zone_name_val
982 29 : = data_blob_string_const(zone);
983 0 : struct ldb_val name_val
984 29 : = data_blob_string_const(name);
985 :
986 44 : for (i=0; zone_prefixes[i]; i++) {
987 0 : int ret;
988 0 : const char *casefold;
989 39 : dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
990 39 : if (dn == NULL) {
991 0 : talloc_free(tmp_ctx);
992 0 : return ISC_R_NOMEMORY;
993 : }
994 :
995 : /*
996 : * This dance ensures that it is not possible to put
997 : * (eg) an extra DC=x, into the DNS name being
998 : * queried
999 : */
1000 :
1001 39 : if (!ldb_dn_add_child_fmt(dn,
1002 : "DC=X,DC=X,%s",
1003 : zone_prefixes[i])) {
1004 0 : talloc_free(tmp_ctx);
1005 0 : return ISC_R_NOMEMORY;
1006 : }
1007 :
1008 39 : ret = ldb_dn_set_component(dn,
1009 : 1,
1010 : "DC",
1011 : zone_name_val);
1012 39 : if (ret != LDB_SUCCESS) {
1013 0 : talloc_free(tmp_ctx);
1014 0 : return ISC_R_NOMEMORY;
1015 : }
1016 :
1017 39 : ret = ldb_dn_set_component(dn,
1018 : 0,
1019 : "DC",
1020 : name_val);
1021 39 : if (ret != LDB_SUCCESS) {
1022 0 : talloc_free(tmp_ctx);
1023 0 : return ISC_R_NOMEMORY;
1024 : }
1025 :
1026 : /*
1027 : * Check if this is a plausibly valid DN early
1028 : * (time spent here will be saved during the
1029 : * search due to an internal cache)
1030 : */
1031 39 : casefold = ldb_dn_get_casefold(dn);
1032 :
1033 39 : if (casefold == NULL) {
1034 0 : talloc_free(tmp_ctx);
1035 0 : return ISC_R_NOTFOUND;
1036 : }
1037 :
1038 39 : werr = dns_common_wildcard_lookup(state->samdb, tmp_ctx, dn,
1039 : &records, &num_records);
1040 39 : if (W_ERROR_IS_OK(werr)) {
1041 24 : break;
1042 : }
1043 : }
1044 29 : if (!W_ERROR_IS_OK(werr)) {
1045 5 : talloc_free(tmp_ctx);
1046 5 : return ISC_R_NOTFOUND;
1047 : }
1048 :
1049 93 : for (i=0; i < num_records; i++) {
1050 0 : isc_result_t result;
1051 :
1052 69 : result = b9_putrr(state, lookup, &records[i], types);
1053 69 : if (result != ISC_R_SUCCESS) {
1054 0 : talloc_free(tmp_ctx);
1055 0 : return result;
1056 : }
1057 : }
1058 :
1059 24 : talloc_free(tmp_ctx);
1060 24 : return ISC_R_SUCCESS;
1061 : }
1062 :
1063 : /*
1064 : lookup one record
1065 : */
1066 29 : _PUBLIC_ isc_result_t dlz_lookup(const char *zone, const char *name,
1067 : void *dbdata, dns_sdlzlookup_t *lookup,
1068 : dns_clientinfomethods_t *methods,
1069 : dns_clientinfo_t *clientinfo)
1070 : {
1071 29 : struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1072 29 : isc_result_t result = ISC_R_SUCCESS;
1073 29 : struct timeval start = timeval_current();
1074 :
1075 29 : result = dlz_lookup_types(state, zone, name, lookup, NULL);
1076 29 : DNS_COMMON_LOG_OPERATION(
1077 : isc_result_str(result),
1078 : &start,
1079 : zone,
1080 : name,
1081 0 : NULL);
1082 :
1083 29 : return result;
1084 : }
1085 :
1086 :
1087 : /*
1088 : see if a zone transfer is allowed
1089 : */
1090 6 : _PUBLIC_ isc_result_t dlz_allowzonexfr(void *dbdata, const char *name, const char *client)
1091 : {
1092 6 : struct dlz_bind9_data *state = talloc_get_type(
1093 : dbdata, struct dlz_bind9_data);
1094 0 : isc_result_t ret;
1095 0 : const char **authorized_clients, **denied_clients;
1096 6 : const char *cname="";
1097 :
1098 : /* check that the zone is known */
1099 6 : ret = b9_find_zone_dn(state, name, NULL, NULL);
1100 6 : if (ret != ISC_R_SUCCESS) {
1101 0 : return ret;
1102 : }
1103 :
1104 : /* default is to deny all transfers */
1105 :
1106 6 : authorized_clients = lpcfg_dns_zone_transfer_clients_allow(state->lp);
1107 6 : denied_clients = lpcfg_dns_zone_transfer_clients_deny(state->lp);
1108 :
1109 : /* The logic of allow_access() when both allow and deny lists are given
1110 : * does not match our expectation here: it would allow clients that are
1111 : * neither allowed nor denied.
1112 : * Here, we want to deny clients by default.
1113 : * Using the allow_access() function is still useful as it takes care of
1114 : * parsing IP addresses and subnets in a consistent way with other options
1115 : * from smb.conf.
1116 : *
1117 : * We will then check the deny list first, then the allow list, so that
1118 : * we accept only clients that are explicitly allowed AND not explicitly
1119 : * denied.
1120 : */
1121 6 : if ((authorized_clients == NULL) && (denied_clients == NULL)) {
1122 : /* No "allow" or "deny" lists given. Deny by default. */
1123 1 : return ISC_R_NOPERM;
1124 : }
1125 :
1126 5 : if (denied_clients != NULL) {
1127 5 : bool ok = allow_access(denied_clients, NULL, cname, client);
1128 5 : if (!ok) {
1129 : /* client on deny list. Deny. */
1130 1 : return ISC_R_NOPERM;
1131 : }
1132 : }
1133 :
1134 4 : if (authorized_clients != NULL) {
1135 4 : bool ok = allow_access(NULL, authorized_clients, cname, client);
1136 4 : if (ok) {
1137 : /*
1138 : * client is not on deny list and is on allow list.
1139 : * This is the only place we should return "allow".
1140 : */
1141 3 : return ISC_R_SUCCESS;
1142 : }
1143 : }
1144 : /* We shouldn't get here, but deny by default. */
1145 1 : return ISC_R_NOPERM;
1146 : }
1147 :
1148 : /*
1149 : perform a zone transfer
1150 : */
1151 1 : _PUBLIC_ isc_result_t dlz_allnodes(const char *zone, void *dbdata,
1152 : dns_sdlzallnodes_t *allnodes)
1153 : {
1154 1 : struct timeval start = timeval_current();
1155 1 : struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1156 1 : const char *attrs[] = { "dnsRecord", NULL };
1157 1 : int ret = LDB_ERR_NO_SUCH_OBJECT;
1158 0 : size_t i, j;
1159 1 : struct ldb_dn *dn = NULL;
1160 0 : struct ldb_result *res;
1161 1 : TALLOC_CTX *tmp_ctx = talloc_new(state);
1162 1 : struct ldb_val zone_name_val = data_blob_string_const(zone);
1163 1 : isc_result_t result = ISC_R_SUCCESS;
1164 :
1165 1 : for (i=0; zone_prefixes[i]; i++) {
1166 0 : const char *casefold;
1167 :
1168 1 : dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
1169 1 : if (dn == NULL) {
1170 0 : talloc_free(tmp_ctx);
1171 0 : result = ISC_R_NOMEMORY;
1172 0 : goto exit;
1173 : }
1174 :
1175 : /*
1176 : * This dance ensures that it is not possible to put
1177 : * (eg) an extra DC=x, into the DNS name being
1178 : * queried
1179 : */
1180 :
1181 1 : if (!ldb_dn_add_child_fmt(dn,
1182 : "DC=X,%s",
1183 : zone_prefixes[i])) {
1184 0 : talloc_free(tmp_ctx);
1185 0 : result = ISC_R_NOMEMORY;
1186 0 : goto exit;
1187 : }
1188 :
1189 1 : ret = ldb_dn_set_component(dn,
1190 : 0,
1191 : "DC",
1192 : zone_name_val);
1193 1 : if (ret != LDB_SUCCESS) {
1194 0 : talloc_free(tmp_ctx);
1195 0 : result = ISC_R_NOMEMORY;
1196 0 : goto exit;
1197 : }
1198 :
1199 : /*
1200 : * Check if this is a plausibly valid DN early
1201 : * (time spent here will be saved during the
1202 : * search due to an internal cache)
1203 : */
1204 1 : casefold = ldb_dn_get_casefold(dn);
1205 :
1206 1 : if (casefold == NULL) {
1207 0 : result = ISC_R_NOTFOUND;
1208 0 : goto exit;
1209 : }
1210 :
1211 1 : ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
1212 : attrs, "objectClass=dnsNode");
1213 1 : if (ret == LDB_SUCCESS) {
1214 1 : break;
1215 : }
1216 : }
1217 1 : if (ret != LDB_SUCCESS || dn == NULL) {
1218 0 : talloc_free(tmp_ctx);
1219 0 : result = ISC_R_NOTFOUND;
1220 0 : goto exit;
1221 : }
1222 :
1223 19 : for (i=0; i<res->count; i++) {
1224 0 : struct ldb_message_element *el;
1225 18 : TALLOC_CTX *el_ctx = talloc_new(tmp_ctx);
1226 0 : const char *rdn, *name;
1227 0 : const struct ldb_val *v;
1228 0 : WERROR werr;
1229 18 : struct dnsp_DnssrvRpcRecord *recs = NULL;
1230 18 : uint16_t num_recs = 0;
1231 :
1232 18 : el = ldb_msg_find_element(res->msgs[i], "dnsRecord");
1233 18 : if (el == NULL || el->num_values == 0) {
1234 0 : state->log(ISC_LOG_INFO, "failed to find dnsRecord for %s",
1235 : ldb_dn_get_linearized(dn));
1236 0 : talloc_free(el_ctx);
1237 0 : continue;
1238 : }
1239 :
1240 18 : v = ldb_dn_get_rdn_val(res->msgs[i]->dn);
1241 18 : if (v == NULL) {
1242 0 : state->log(ISC_LOG_INFO, "failed to find RDN for %s",
1243 : ldb_dn_get_linearized(dn));
1244 0 : talloc_free(el_ctx);
1245 0 : continue;
1246 : }
1247 :
1248 18 : rdn = talloc_strndup(el_ctx, (char *)v->data, v->length);
1249 18 : if (rdn == NULL) {
1250 0 : talloc_free(tmp_ctx);
1251 0 : result = ISC_R_NOMEMORY;
1252 0 : goto exit;
1253 : }
1254 :
1255 18 : if (strcmp(rdn, "@") == 0) {
1256 1 : name = zone;
1257 : } else {
1258 17 : name = talloc_asprintf(el_ctx, "%s.%s", rdn, zone);
1259 : }
1260 18 : name = b9_format_fqdn(el_ctx, name);
1261 18 : if (name == NULL) {
1262 0 : talloc_free(tmp_ctx);
1263 0 : result = ISC_R_NOMEMORY;
1264 0 : goto exit;
1265 : }
1266 :
1267 18 : werr = dns_common_extract(state->samdb, el, el_ctx, &recs, &num_recs);
1268 18 : if (!W_ERROR_IS_OK(werr)) {
1269 0 : state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s, %s",
1270 : ldb_dn_get_linearized(dn), win_errstr(werr));
1271 0 : talloc_free(el_ctx);
1272 0 : continue;
1273 : }
1274 :
1275 42 : for (j=0; j < num_recs; j++) {
1276 0 : isc_result_t rc;
1277 :
1278 24 : rc = b9_putnamedrr(state, allnodes, name, &recs[j]);
1279 24 : if (rc != ISC_R_SUCCESS) {
1280 0 : continue;
1281 : }
1282 : }
1283 :
1284 18 : talloc_free(el_ctx);
1285 : }
1286 :
1287 1 : talloc_free(tmp_ctx);
1288 1 : exit:
1289 1 : DNS_COMMON_LOG_OPERATION(
1290 : isc_result_str(result),
1291 : &start,
1292 : zone,
1293 : NULL,
1294 0 : NULL);
1295 1 : return result;
1296 : }
1297 :
1298 :
1299 : /*
1300 : start a transaction
1301 : */
1302 50 : _PUBLIC_ isc_result_t dlz_newversion(const char *zone, void *dbdata, void **versionp)
1303 : {
1304 50 : struct timeval start = timeval_current();
1305 50 : struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1306 50 : isc_result_t result = ISC_R_SUCCESS;
1307 :
1308 50 : state->log(ISC_LOG_INFO, "samba_dlz: starting transaction on zone %s", zone);
1309 :
1310 50 : if (state->transaction_token != NULL) {
1311 0 : state->log(ISC_LOG_INFO, "samba_dlz: transaction already started for zone %s", zone);
1312 0 : result = ISC_R_FAILURE;
1313 0 : goto exit;
1314 : }
1315 :
1316 50 : state->transaction_token = talloc_zero(state, int);
1317 50 : if (state->transaction_token == NULL) {
1318 0 : result = ISC_R_NOMEMORY;
1319 0 : goto exit;
1320 : }
1321 :
1322 50 : if (ldb_transaction_start(state->samdb) != LDB_SUCCESS) {
1323 0 : state->log(ISC_LOG_INFO, "samba_dlz: failed to start a transaction for zone %s", zone);
1324 0 : talloc_free(state->transaction_token);
1325 0 : state->transaction_token = NULL;
1326 0 : result = ISC_R_FAILURE;
1327 0 : goto exit;
1328 : }
1329 :
1330 50 : *versionp = (void *)state->transaction_token;
1331 50 : exit:
1332 50 : DNS_COMMON_LOG_OPERATION(
1333 : isc_result_str(result),
1334 : &start,
1335 : zone,
1336 : NULL,
1337 0 : NULL);
1338 50 : return result;
1339 : }
1340 :
1341 : /*
1342 : end a transaction
1343 : */
1344 50 : _PUBLIC_ void dlz_closeversion(const char *zone, isc_boolean_t commit,
1345 : void *dbdata, void **versionp)
1346 : {
1347 50 : struct timeval start = timeval_current();
1348 50 : struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1349 50 : const char *data = NULL;
1350 :
1351 50 : data = commit ? "commit" : "cancel";
1352 :
1353 50 : if (state->transaction_token != (int *)*versionp) {
1354 0 : state->log(ISC_LOG_INFO, "samba_dlz: transaction not started for zone %s", zone);
1355 0 : goto exit;
1356 : }
1357 :
1358 50 : if (commit) {
1359 47 : if (ldb_transaction_commit(state->samdb) != LDB_SUCCESS) {
1360 0 : state->log(ISC_LOG_INFO, "samba_dlz: failed to commit a transaction for zone %s", zone);
1361 0 : goto exit;
1362 : }
1363 47 : state->log(ISC_LOG_INFO, "samba_dlz: committed transaction on zone %s", zone);
1364 : } else {
1365 3 : if (ldb_transaction_cancel(state->samdb) != LDB_SUCCESS) {
1366 0 : state->log(ISC_LOG_INFO, "samba_dlz: failed to cancel a transaction for zone %s", zone);
1367 0 : goto exit;
1368 : }
1369 3 : state->log(ISC_LOG_INFO, "samba_dlz: cancelling transaction on zone %s", zone);
1370 : }
1371 :
1372 50 : talloc_free(state->transaction_token);
1373 50 : state->transaction_token = NULL;
1374 50 : *versionp = NULL;
1375 :
1376 50 : exit:
1377 50 : DNS_COMMON_LOG_OPERATION(
1378 : isc_result_str(ISC_R_SUCCESS),
1379 : &start,
1380 : zone,
1381 : NULL,
1382 0 : data);
1383 50 : }
1384 :
1385 :
1386 : /*
1387 : see if there is a SOA record for a zone
1388 : */
1389 40 : static bool b9_has_soa(struct dlz_bind9_data *state, struct ldb_dn *dn, const char *zone)
1390 : {
1391 40 : TALLOC_CTX *tmp_ctx = talloc_new(state);
1392 0 : WERROR werr;
1393 40 : struct dnsp_DnssrvRpcRecord *records = NULL;
1394 40 : uint16_t num_records = 0, i;
1395 0 : struct ldb_val zone_name_val
1396 40 : = data_blob_string_const(zone);
1397 :
1398 : /*
1399 : * This dance ensures that it is not possible to put
1400 : * (eg) an extra DC=x, into the DNS name being
1401 : * queried
1402 : */
1403 :
1404 40 : if (!ldb_dn_add_child_val(dn,
1405 : "DC",
1406 : zone_name_val)) {
1407 0 : talloc_free(tmp_ctx);
1408 0 : return false;
1409 : }
1410 :
1411 : /*
1412 : * The SOA record is always stored under DC=@,DC=zonename
1413 : * This can probably be removed when dns_common_lookup makes a fallback
1414 : * lookup on @ pseudo record
1415 : */
1416 :
1417 40 : if (!ldb_dn_add_child_fmt(dn,"DC=@")) {
1418 0 : talloc_free(tmp_ctx);
1419 0 : return false;
1420 : }
1421 :
1422 40 : werr = dns_common_lookup(state->samdb, tmp_ctx, dn,
1423 : &records, &num_records, NULL);
1424 40 : if (!W_ERROR_IS_OK(werr)) {
1425 0 : talloc_free(tmp_ctx);
1426 0 : return false;
1427 : }
1428 :
1429 44 : for (i=0; i < num_records; i++) {
1430 44 : if (records[i].wType == DNS_TYPE_SOA) {
1431 40 : talloc_free(tmp_ctx);
1432 40 : return true;
1433 : }
1434 : }
1435 :
1436 0 : talloc_free(tmp_ctx);
1437 0 : return false;
1438 : }
1439 :
1440 28 : static bool b9_zone_add(struct dlz_bind9_data *state, const char *name)
1441 : {
1442 0 : struct b9_zone *zone;
1443 :
1444 28 : zone = talloc_zero(state, struct b9_zone);
1445 28 : if (zone == NULL) {
1446 0 : return false;
1447 : }
1448 :
1449 28 : zone->name = talloc_strdup(zone, name);
1450 28 : if (zone->name == NULL) {
1451 0 : talloc_free(zone);
1452 0 : return false;
1453 : }
1454 :
1455 28 : DLIST_ADD(state->zonelist, zone);
1456 28 : return true;
1457 : }
1458 :
1459 40 : static bool b9_zone_exists(struct dlz_bind9_data *state, const char *name)
1460 : {
1461 40 : struct b9_zone *zone = state->zonelist;
1462 40 : bool found = false;
1463 :
1464 60 : while (zone != NULL) {
1465 32 : if (strcasecmp(name, zone->name) == 0) {
1466 12 : found = true;
1467 12 : break;
1468 : }
1469 20 : zone = zone->next;
1470 : }
1471 :
1472 40 : return found;
1473 : }
1474 :
1475 :
1476 : /*
1477 : configure a writeable zone
1478 : */
1479 20 : _PUBLIC_ isc_result_t dlz_configure(dns_view_t *view, dns_dlzdb_t *dlzdb,
1480 : void *dbdata)
1481 : {
1482 20 : struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1483 0 : TALLOC_CTX *tmp_ctx;
1484 0 : struct ldb_dn *dn;
1485 0 : int i;
1486 :
1487 20 : state->log(ISC_LOG_INFO, "samba_dlz: starting configure");
1488 20 : if (state->writeable_zone == NULL) {
1489 0 : state->log(ISC_LOG_INFO, "samba_dlz: no writeable_zone method available");
1490 0 : return ISC_R_FAILURE;
1491 : }
1492 :
1493 20 : tmp_ctx = talloc_new(state);
1494 :
1495 80 : for (i=0; zone_prefixes[i]; i++) {
1496 60 : const char *attrs[] = { "name", NULL };
1497 0 : int j, ret;
1498 0 : struct ldb_result *res;
1499 :
1500 60 : dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
1501 60 : if (dn == NULL) {
1502 0 : talloc_free(tmp_ctx);
1503 0 : return ISC_R_NOMEMORY;
1504 : }
1505 :
1506 60 : if (!ldb_dn_add_child_fmt(dn, "%s", zone_prefixes[i])) {
1507 0 : talloc_free(tmp_ctx);
1508 0 : return ISC_R_NOMEMORY;
1509 : }
1510 :
1511 60 : ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
1512 : attrs, "objectClass=dnsZone");
1513 60 : if (ret != LDB_SUCCESS) {
1514 20 : continue;
1515 : }
1516 :
1517 100 : for (j=0; j<res->count; j++) {
1518 0 : isc_result_t result;
1519 60 : const char *zone = ldb_msg_find_attr_as_string(res->msgs[j], "name", NULL);
1520 0 : struct ldb_dn *zone_dn;
1521 :
1522 60 : if (zone == NULL) {
1523 0 : continue;
1524 : }
1525 : /* Ignore zones that are not handled in BIND */
1526 60 : if ((strcmp(zone, "RootDNSServers") == 0) ||
1527 40 : (strcmp(zone, "..TrustAnchors") == 0)) {
1528 20 : continue;
1529 : }
1530 40 : zone_dn = ldb_dn_copy(tmp_ctx, dn);
1531 40 : if (zone_dn == NULL) {
1532 0 : talloc_free(tmp_ctx);
1533 0 : return ISC_R_NOMEMORY;
1534 : }
1535 :
1536 40 : if (!b9_has_soa(state, zone_dn, zone)) {
1537 0 : continue;
1538 : }
1539 :
1540 40 : if (b9_zone_exists(state, zone)) {
1541 12 : state->log(ISC_LOG_WARNING, "samba_dlz: Ignoring duplicate zone '%s' from '%s'",
1542 : zone, ldb_dn_get_linearized(zone_dn));
1543 12 : continue;
1544 : }
1545 :
1546 28 : if (!b9_zone_add(state, zone)) {
1547 0 : talloc_free(tmp_ctx);
1548 0 : return ISC_R_NOMEMORY;
1549 : }
1550 :
1551 28 : result = state->writeable_zone(view, dlzdb, zone);
1552 28 : if (result != ISC_R_SUCCESS) {
1553 0 : state->log(ISC_LOG_ERROR, "samba_dlz: Failed to configure zone '%s'",
1554 : zone);
1555 0 : talloc_free(tmp_ctx);
1556 0 : return result;
1557 : }
1558 28 : state->log(ISC_LOG_INFO, "samba_dlz: configured writeable zone '%s'", zone);
1559 : }
1560 : }
1561 :
1562 20 : talloc_free(tmp_ctx);
1563 20 : return ISC_R_SUCCESS;
1564 : }
1565 :
1566 : /*
1567 : authorize a zone update
1568 : */
1569 4 : _PUBLIC_ isc_boolean_t dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr,
1570 : const char *type, const char *key, uint32_t keydatalen, uint8_t *keydata,
1571 : void *dbdata)
1572 : {
1573 4 : struct timeval start = timeval_current();
1574 4 : struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1575 0 : TALLOC_CTX *tmp_ctx;
1576 0 : DATA_BLOB ap_req;
1577 0 : struct cli_credentials *server_credentials;
1578 0 : char *keytab_name;
1579 4 : char *keytab_file = NULL;
1580 0 : int ret;
1581 0 : int ldb_ret;
1582 0 : NTSTATUS nt_status;
1583 0 : struct gensec_security *gensec_ctx;
1584 0 : struct auth_session_info *session_info;
1585 0 : struct ldb_dn *dn;
1586 0 : isc_result_t rc;
1587 0 : struct ldb_result *res;
1588 4 : const char * attrs[] = { NULL };
1589 0 : uint32_t access_mask;
1590 4 : struct gensec_settings *settings = NULL;
1591 4 : const struct gensec_security_ops **backends = NULL;
1592 4 : size_t idx = 0;
1593 4 : isc_boolean_t result = ISC_FALSE;
1594 0 : NTSTATUS status;
1595 0 : bool ok;
1596 :
1597 : /* Remove cached credentials, if any */
1598 4 : if (state->session_info) {
1599 0 : talloc_free(state->session_info);
1600 0 : state->session_info = NULL;
1601 : }
1602 4 : if (state->update_name) {
1603 0 : talloc_free(state->update_name);
1604 0 : state->update_name = NULL;
1605 : }
1606 :
1607 4 : tmp_ctx = talloc_new(state);
1608 4 : if (tmp_ctx == NULL) {
1609 0 : state->log(ISC_LOG_ERROR, "samba_dlz: no memory");
1610 0 : result = ISC_FALSE;
1611 0 : goto exit;
1612 : }
1613 :
1614 4 : ap_req = data_blob_const(keydata, keydatalen);
1615 4 : server_credentials = cli_credentials_init(tmp_ctx);
1616 4 : if (!server_credentials) {
1617 0 : state->log(ISC_LOG_ERROR, "samba_dlz: failed to init server credentials");
1618 0 : talloc_free(tmp_ctx);
1619 0 : result = ISC_FALSE;
1620 0 : goto exit;
1621 : }
1622 :
1623 4 : status = cli_credentials_set_krb5_context(server_credentials,
1624 : state->smb_krb5_ctx);
1625 4 : if (!NT_STATUS_IS_OK(status)) {
1626 0 : state->log(ISC_LOG_ERROR,
1627 : "samba_dlz: failed to set krb5 context");
1628 0 : talloc_free(tmp_ctx);
1629 0 : result = ISC_FALSE;
1630 0 : goto exit;
1631 : }
1632 :
1633 4 : ok = cli_credentials_set_conf(server_credentials, state->lp);
1634 4 : if (!ok) {
1635 0 : state->log(ISC_LOG_ERROR,
1636 : "samba_dlz: failed to load smb.conf");
1637 0 : talloc_free(tmp_ctx);
1638 0 : result = ISC_FALSE;
1639 0 : goto exit;
1640 : }
1641 :
1642 4 : keytab_file = talloc_asprintf(tmp_ctx,
1643 : "%s/dns.keytab",
1644 : lpcfg_binddns_dir(state->lp));
1645 4 : if (keytab_file == NULL) {
1646 0 : state->log(ISC_LOG_ERROR, "samba_dlz: Out of memory!");
1647 0 : talloc_free(tmp_ctx);
1648 0 : result = ISC_FALSE;
1649 0 : goto exit;
1650 : }
1651 :
1652 4 : if (!file_exist(keytab_file)) {
1653 0 : keytab_file = talloc_asprintf(tmp_ctx,
1654 : "%s/dns.keytab",
1655 : lpcfg_private_dir(state->lp));
1656 0 : if (keytab_file == NULL) {
1657 0 : state->log(ISC_LOG_ERROR, "samba_dlz: Out of memory!");
1658 0 : talloc_free(tmp_ctx);
1659 0 : result = ISC_FALSE;
1660 0 : goto exit;
1661 : }
1662 : }
1663 :
1664 4 : keytab_name = talloc_asprintf(tmp_ctx, "FILE:%s", keytab_file);
1665 4 : if (keytab_name == NULL) {
1666 0 : state->log(ISC_LOG_ERROR, "samba_dlz: Out of memory!");
1667 0 : talloc_free(tmp_ctx);
1668 0 : result = ISC_FALSE;
1669 0 : goto exit;
1670 : }
1671 :
1672 4 : ret = cli_credentials_set_keytab_name(server_credentials, state->lp, keytab_name,
1673 : CRED_SPECIFIED);
1674 4 : if (ret != 0) {
1675 0 : state->log(ISC_LOG_ERROR, "samba_dlz: failed to obtain server credentials from %s",
1676 : keytab_name);
1677 0 : talloc_free(tmp_ctx);
1678 0 : result = ISC_FALSE;
1679 0 : goto exit;
1680 : }
1681 4 : talloc_free(keytab_name);
1682 :
1683 4 : settings = lpcfg_gensec_settings(tmp_ctx, state->lp);
1684 4 : if (settings == NULL) {
1685 0 : state->log(ISC_LOG_ERROR, "samba_dlz: lpcfg_gensec_settings failed");
1686 0 : talloc_free(tmp_ctx);
1687 0 : result = ISC_FALSE;
1688 0 : goto exit;
1689 : }
1690 4 : backends = talloc_zero_array(settings,
1691 : const struct gensec_security_ops *, 3);
1692 4 : if (backends == NULL) {
1693 0 : state->log(ISC_LOG_ERROR, "samba_dlz: talloc_zero_array gensec_security_ops failed");
1694 0 : talloc_free(tmp_ctx);
1695 0 : result = ISC_FALSE;
1696 0 : goto exit;
1697 : }
1698 4 : settings->backends = backends;
1699 :
1700 4 : gensec_init();
1701 :
1702 4 : backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_KERBEROS5);
1703 4 : backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_SPNEGO);
1704 :
1705 4 : nt_status = gensec_server_start(tmp_ctx, settings,
1706 : state->auth_context, &gensec_ctx);
1707 4 : if (!NT_STATUS_IS_OK(nt_status)) {
1708 0 : state->log(ISC_LOG_ERROR, "samba_dlz: failed to start gensec server");
1709 0 : talloc_free(tmp_ctx);
1710 0 : result = ISC_FALSE;
1711 0 : goto exit;
1712 : }
1713 :
1714 4 : gensec_set_credentials(gensec_ctx, server_credentials);
1715 :
1716 4 : nt_status = gensec_start_mech_by_oid(gensec_ctx, GENSEC_OID_SPNEGO);
1717 4 : if (!NT_STATUS_IS_OK(nt_status)) {
1718 0 : state->log(ISC_LOG_ERROR, "samba_dlz: failed to start spnego");
1719 0 : talloc_free(tmp_ctx);
1720 0 : result = ISC_FALSE;
1721 0 : goto exit;
1722 : }
1723 :
1724 : /*
1725 : * We only allow SPNEGO/KRB5 and make sure the backend
1726 : * to is RPC/IPC free.
1727 : *
1728 : * See gensec_gssapi_update_internal() as
1729 : * GENSEC_SERVER.
1730 : *
1731 : * It allows gensec_update() not to block.
1732 : *
1733 : * If that changes in future we need to use
1734 : * gensec_update_send/recv here!
1735 : */
1736 4 : nt_status = gensec_update(gensec_ctx, tmp_ctx, ap_req, &ap_req);
1737 4 : if (!NT_STATUS_IS_OK(nt_status)) {
1738 0 : state->log(ISC_LOG_ERROR, "samba_dlz: spnego update failed");
1739 0 : talloc_free(tmp_ctx);
1740 0 : result = ISC_FALSE;
1741 0 : goto exit;
1742 : }
1743 :
1744 4 : nt_status = gensec_session_info(gensec_ctx, tmp_ctx, &session_info);
1745 4 : if (!NT_STATUS_IS_OK(nt_status)) {
1746 0 : state->log(ISC_LOG_ERROR, "samba_dlz: failed to create session info");
1747 0 : talloc_free(tmp_ctx);
1748 0 : result = ISC_FALSE;
1749 0 : goto exit;
1750 : }
1751 :
1752 : /* Get the DN from name */
1753 4 : rc = b9_find_name_dn(state, name, tmp_ctx, &dn);
1754 4 : if (rc != ISC_R_SUCCESS) {
1755 0 : state->log(ISC_LOG_ERROR, "samba_dlz: failed to find name %s", name);
1756 0 : talloc_free(tmp_ctx);
1757 0 : result = ISC_FALSE;
1758 0 : goto exit;
1759 : }
1760 :
1761 : /* make sure the dn exists, or find parent dn in case new object is being added */
1762 4 : ldb_ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
1763 : attrs, "objectClass=dnsNode");
1764 4 : if (ldb_ret == LDB_ERR_NO_SUCH_OBJECT) {
1765 1 : ldb_dn_remove_child_components(dn, 1);
1766 1 : access_mask = SEC_ADS_CREATE_CHILD;
1767 1 : talloc_free(res);
1768 3 : } else if (ldb_ret == LDB_SUCCESS) {
1769 3 : access_mask = SEC_STD_REQUIRED | SEC_ADS_SELF_WRITE;
1770 3 : talloc_free(res);
1771 : } else {
1772 0 : talloc_free(tmp_ctx);
1773 0 : result = ISC_FALSE;
1774 0 : goto exit;
1775 : }
1776 :
1777 : /* Do ACL check */
1778 4 : ldb_ret = dsdb_check_access_on_dn(state->samdb, tmp_ctx, dn,
1779 4 : session_info->security_token,
1780 : access_mask, NULL);
1781 4 : if (ldb_ret != LDB_SUCCESS) {
1782 0 : state->log(ISC_LOG_INFO,
1783 : "samba_dlz: disallowing update of signer=%s name=%s type=%s error=%s",
1784 : signer, name, type, ldb_strerror(ldb_ret));
1785 0 : talloc_free(tmp_ctx);
1786 0 : result = ISC_FALSE;
1787 0 : goto exit;
1788 : }
1789 :
1790 : /* Cache session_info, so it can be used in the actual add/delete operation */
1791 4 : state->update_name = talloc_strdup(state, name);
1792 4 : if (state->update_name == NULL) {
1793 0 : state->log(ISC_LOG_ERROR, "samba_dlz: memory allocation error");
1794 0 : talloc_free(tmp_ctx);
1795 0 : result = ISC_FALSE;
1796 0 : goto exit;
1797 : }
1798 4 : state->session_info = talloc_steal(state, session_info);
1799 :
1800 4 : state->log(ISC_LOG_INFO, "samba_dlz: allowing update of signer=%s name=%s tcpaddr=%s type=%s key=%s",
1801 : signer, name, tcpaddr, type, key);
1802 :
1803 4 : talloc_free(tmp_ctx);
1804 4 : result = ISC_TRUE;
1805 4 : exit:
1806 4 : DNS_COMMON_LOG_OPERATION(
1807 : isc_result_str(result),
1808 : &start,
1809 : NULL,
1810 : name,
1811 0 : NULL);
1812 4 : return result;
1813 : }
1814 :
1815 :
1816 : /*
1817 : see if two dns records match
1818 : */
1819 128 : static bool b9_record_match(struct dnsp_DnssrvRpcRecord *rec1,
1820 : struct dnsp_DnssrvRpcRecord *rec2)
1821 : {
1822 128 : if (rec1->wType != rec2->wType) {
1823 82 : return false;
1824 : }
1825 : /* see if this type is single valued */
1826 46 : if (b9_single_valued(rec1->wType)) {
1827 0 : return true;
1828 : }
1829 :
1830 46 : return dns_record_match(rec1, rec2);
1831 : }
1832 :
1833 : /*
1834 : * Update session_info on samdb using the cached credentials
1835 : */
1836 46 : static bool b9_set_session_info(struct dlz_bind9_data *state, const char *name)
1837 : {
1838 0 : int ret;
1839 :
1840 46 : if (state->update_name == NULL || state->session_info == NULL) {
1841 0 : state->log(ISC_LOG_ERROR, "samba_dlz: invalid credentials");
1842 0 : return false;
1843 : }
1844 :
1845 : /* Do not use client credentials, if we're not updating the client specified name */
1846 46 : if (strcmp(state->update_name, name) != 0) {
1847 33 : return true;
1848 : }
1849 :
1850 13 : ret = ldb_set_opaque(
1851 : state->samdb,
1852 : DSDB_SESSION_INFO,
1853 13 : state->session_info);
1854 13 : if (ret != LDB_SUCCESS) {
1855 0 : state->log(ISC_LOG_ERROR, "samba_dlz: unable to set session info");
1856 0 : return false;
1857 : }
1858 :
1859 13 : return true;
1860 : }
1861 :
1862 : /*
1863 : * Reset session_info on samdb as system session
1864 : */
1865 46 : static void b9_reset_session_info(struct dlz_bind9_data *state)
1866 : {
1867 46 : ldb_set_opaque(
1868 : state->samdb,
1869 : DSDB_SESSION_INFO,
1870 46 : system_session(state->lp));
1871 46 : }
1872 :
1873 : /*
1874 : add or modify a rdataset
1875 : */
1876 38 : _PUBLIC_ isc_result_t dlz_addrdataset(const char *name, const char *rdatastr, void *dbdata, void *version)
1877 : {
1878 38 : struct timeval start = timeval_current();
1879 38 : struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
1880 0 : struct dnsp_DnssrvRpcRecord *rec;
1881 0 : struct ldb_dn *dn;
1882 38 : isc_result_t result = ISC_R_SUCCESS;
1883 38 : bool tombstoned = false;
1884 38 : bool needs_add = false;
1885 38 : struct dnsp_DnssrvRpcRecord *recs = NULL;
1886 38 : uint16_t num_recs = 0;
1887 38 : uint16_t first = 0;
1888 0 : uint16_t i;
1889 0 : WERROR werr;
1890 :
1891 38 : if (state->transaction_token != (void*)version) {
1892 0 : state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version");
1893 0 : result = ISC_R_FAILURE;
1894 0 : goto exit;
1895 : }
1896 :
1897 38 : rec = talloc_zero(state, struct dnsp_DnssrvRpcRecord);
1898 38 : if (rec == NULL) {
1899 0 : result = ISC_R_NOMEMORY;
1900 0 : goto exit;
1901 : }
1902 :
1903 38 : rec->rank = DNS_RANK_ZONE;
1904 :
1905 38 : if (!b9_parse(state, rdatastr, rec)) {
1906 0 : state->log(ISC_LOG_INFO, "samba_dlz: failed to parse rdataset '%s'", rdatastr);
1907 0 : talloc_free(rec);
1908 0 : result = ISC_R_FAILURE;
1909 0 : goto exit;
1910 : }
1911 :
1912 : /* find the DN of the record */
1913 38 : result = b9_find_name_dn(state, name, rec, &dn);
1914 38 : if (result != ISC_R_SUCCESS) {
1915 0 : talloc_free(rec);
1916 0 : goto exit;
1917 : }
1918 :
1919 : /* get any existing records */
1920 38 : werr = dns_common_lookup(state->samdb, rec, dn,
1921 : &recs, &num_recs, &tombstoned);
1922 38 : if (W_ERROR_EQUAL(werr, WERR_DNS_ERROR_NAME_DOES_NOT_EXIST)) {
1923 2 : needs_add = true;
1924 2 : werr = WERR_OK;
1925 : }
1926 38 : if (!W_ERROR_IS_OK(werr)) {
1927 0 : state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s, %s",
1928 : ldb_dn_get_linearized(dn), win_errstr(werr));
1929 0 : talloc_free(rec);
1930 0 : result = ISC_R_FAILURE;
1931 0 : goto exit;
1932 : }
1933 :
1934 38 : if (tombstoned) {
1935 : /*
1936 : * we need to keep the existing tombstone record
1937 : * and ignore it
1938 : */
1939 1 : first = num_recs;
1940 : }
1941 :
1942 : /* there may be existing records. We need to see if this will
1943 : * replace a record or add to it
1944 : */
1945 127 : for (i=first; i < num_recs; i++) {
1946 113 : if (b9_record_match(rec, &recs[i])) {
1947 24 : break;
1948 : }
1949 : }
1950 38 : if (i == UINT16_MAX) {
1951 0 : state->log(ISC_LOG_ERROR,
1952 : "samba_dlz: failed to find record to modify, and "
1953 : "there are already %u dnsRecord values for %s",
1954 : i, ldb_dn_get_linearized(dn));
1955 0 : talloc_free(rec);
1956 0 : result = ISC_R_FAILURE;
1957 0 : goto exit;
1958 : }
1959 :
1960 38 : if (i == num_recs) {
1961 : /* set dwTimeStamp before increasing num_recs */
1962 14 : if (dns_name_is_static(recs, num_recs)) {
1963 5 : rec->dwTimeStamp = 0;
1964 : } else {
1965 9 : rec->dwTimeStamp = unix_to_dns_timestamp(time(NULL));
1966 : }
1967 : /* adding space for a new value */
1968 14 : recs = talloc_realloc(rec, recs,
1969 : struct dnsp_DnssrvRpcRecord,
1970 : num_recs + 1);
1971 14 : if (recs == NULL) {
1972 0 : talloc_free(rec);
1973 0 : result = ISC_R_NOMEMORY;
1974 0 : goto exit;
1975 : }
1976 14 : num_recs++;
1977 : } else {
1978 : /*
1979 : * We are updating a record. Depending on whether aging is
1980 : * enabled, and how old the old timestamp is,
1981 : * dns_common_replace() will work out whether to bump the
1982 : * timestamp or not. But to do that, we need to tell it the
1983 : * old timestamp.
1984 : */
1985 24 : if (! dns_name_is_static(recs, num_recs)) {
1986 16 : rec->dwTimeStamp = recs[i].dwTimeStamp;
1987 : }
1988 : }
1989 :
1990 38 : recs[i] = *rec;
1991 :
1992 38 : if (!b9_set_session_info(state, name)) {
1993 0 : talloc_free(rec);
1994 0 : result = ISC_R_FAILURE;
1995 0 : goto exit;
1996 : }
1997 :
1998 : /* modify the record */
1999 38 : werr = dns_common_replace(state->samdb, rec, dn,
2000 : needs_add,
2001 : state->soa_serial,
2002 : recs, num_recs);
2003 38 : b9_reset_session_info(state);
2004 38 : if (!W_ERROR_IS_OK(werr)) {
2005 0 : state->log(ISC_LOG_ERROR, "samba_dlz: failed to %s %s - %s",
2006 : needs_add ? "add" : "modify",
2007 : ldb_dn_get_linearized(dn), win_errstr(werr));
2008 0 : talloc_free(rec);
2009 0 : result = ISC_R_FAILURE;
2010 0 : goto exit;
2011 : }
2012 :
2013 38 : state->log(ISC_LOG_INFO, "samba_dlz: added rdataset %s '%s'", name, rdatastr);
2014 :
2015 38 : talloc_free(rec);
2016 38 : exit:
2017 38 : DNS_COMMON_LOG_OPERATION(
2018 : isc_result_str(result),
2019 : &start,
2020 : NULL,
2021 : name,
2022 0 : rdatastr);
2023 38 : return result;
2024 : }
2025 :
2026 : /*
2027 : remove a rdataset
2028 : */
2029 9 : _PUBLIC_ isc_result_t dlz_subrdataset(const char *name, const char *rdatastr, void *dbdata, void *version)
2030 : {
2031 9 : struct timeval start = timeval_current();
2032 9 : struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
2033 0 : struct dnsp_DnssrvRpcRecord *rec;
2034 0 : struct ldb_dn *dn;
2035 9 : isc_result_t result = ISC_R_SUCCESS;
2036 9 : struct dnsp_DnssrvRpcRecord *recs = NULL;
2037 9 : uint16_t num_recs = 0;
2038 0 : uint16_t i;
2039 0 : WERROR werr;
2040 :
2041 9 : if (state->transaction_token != (void*)version) {
2042 0 : state->log(ISC_LOG_ERROR, "samba_dlz: bad transaction version");
2043 0 : result = ISC_R_FAILURE;
2044 0 : goto exit;
2045 : }
2046 :
2047 9 : rec = talloc_zero(state, struct dnsp_DnssrvRpcRecord);
2048 9 : if (rec == NULL) {
2049 0 : result = ISC_R_NOMEMORY;
2050 0 : goto exit;
2051 : }
2052 :
2053 9 : if (!b9_parse(state, rdatastr, rec)) {
2054 0 : state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse rdataset '%s'", rdatastr);
2055 0 : talloc_free(rec);
2056 0 : result = ISC_R_FAILURE;
2057 0 : goto exit;
2058 : }
2059 :
2060 : /* find the DN of the record */
2061 9 : result = b9_find_name_dn(state, name, rec, &dn);
2062 9 : if (result != ISC_R_SUCCESS) {
2063 0 : talloc_free(rec);
2064 0 : goto exit;
2065 : }
2066 :
2067 : /* get the existing records */
2068 9 : werr = dns_common_lookup(state->samdb, rec, dn,
2069 : &recs, &num_recs, NULL);
2070 9 : if (!W_ERROR_IS_OK(werr)) {
2071 1 : talloc_free(rec);
2072 1 : result = ISC_R_NOTFOUND;
2073 1 : goto exit;
2074 : }
2075 :
2076 16 : for (i=0; i < num_recs; i++) {
2077 15 : if (b9_record_match(rec, &recs[i])) {
2078 7 : recs[i] = (struct dnsp_DnssrvRpcRecord) {
2079 : .wType = DNS_TYPE_TOMBSTONE,
2080 : };
2081 7 : break;
2082 : }
2083 : }
2084 8 : if (i == num_recs) {
2085 1 : talloc_free(rec);
2086 1 : result = ISC_R_NOTFOUND;
2087 1 : goto exit;
2088 : }
2089 :
2090 7 : if (!b9_set_session_info(state, name)) {
2091 0 : talloc_free(rec);
2092 0 : result = ISC_R_FAILURE;
2093 0 : goto exit;
2094 : }
2095 :
2096 : /* modify the record */
2097 7 : werr = dns_common_replace(state->samdb, rec, dn,
2098 : false,/* needs_add */
2099 : state->soa_serial,
2100 : recs, num_recs);
2101 7 : b9_reset_session_info(state);
2102 7 : if (!W_ERROR_IS_OK(werr)) {
2103 0 : state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s",
2104 : ldb_dn_get_linearized(dn), win_errstr(werr));
2105 0 : talloc_free(rec);
2106 0 : result = ISC_R_FAILURE;
2107 0 : goto exit;
2108 : }
2109 :
2110 7 : state->log(ISC_LOG_INFO, "samba_dlz: subtracted rdataset %s '%s'", name, rdatastr);
2111 :
2112 7 : talloc_free(rec);
2113 9 : exit:
2114 9 : DNS_COMMON_LOG_OPERATION(
2115 : isc_result_str(result),
2116 : &start,
2117 : NULL,
2118 : name,
2119 0 : rdatastr);
2120 9 : return result;
2121 : }
2122 :
2123 :
2124 : /*
2125 : delete all records of the given type
2126 : */
2127 3 : _PUBLIC_ isc_result_t dlz_delrdataset(const char *name, const char *type, void *dbdata, void *version)
2128 : {
2129 3 : struct timeval start = timeval_current();
2130 3 : struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
2131 0 : TALLOC_CTX *tmp_ctx;
2132 0 : struct ldb_dn *dn;
2133 3 : isc_result_t result = ISC_R_SUCCESS;
2134 0 : enum dns_record_type dns_type;
2135 3 : bool found = false;
2136 3 : struct dnsp_DnssrvRpcRecord *recs = NULL;
2137 3 : uint16_t num_recs = 0;
2138 3 : uint16_t ri = 0;
2139 0 : WERROR werr;
2140 :
2141 3 : if (state->transaction_token != (void*)version) {
2142 0 : state->log(ISC_LOG_ERROR, "samba_dlz: bad transaction version");
2143 0 : result = ISC_R_FAILURE;
2144 0 : goto exit;
2145 : }
2146 :
2147 3 : if (!b9_dns_type(type, &dns_type)) {
2148 0 : state->log(ISC_LOG_ERROR, "samba_dlz: bad dns type %s in delete", type);
2149 0 : result = ISC_R_FAILURE;
2150 0 : goto exit;
2151 : }
2152 :
2153 3 : tmp_ctx = talloc_new(state);
2154 :
2155 : /* find the DN of the record */
2156 3 : result = b9_find_name_dn(state, name, tmp_ctx, &dn);
2157 3 : if (result != ISC_R_SUCCESS) {
2158 0 : talloc_free(tmp_ctx);
2159 0 : goto exit;
2160 : }
2161 :
2162 : /* get the existing records */
2163 3 : werr = dns_common_lookup(state->samdb, tmp_ctx, dn,
2164 : &recs, &num_recs, NULL);
2165 3 : if (!W_ERROR_IS_OK(werr)) {
2166 1 : talloc_free(tmp_ctx);
2167 1 : result = ISC_R_NOTFOUND;
2168 1 : goto exit;
2169 : }
2170 :
2171 6 : for (ri=0; ri < num_recs; ri++) {
2172 4 : if (dns_type != recs[ri].wType) {
2173 2 : continue;
2174 : }
2175 :
2176 2 : found = true;
2177 2 : recs[ri] = (struct dnsp_DnssrvRpcRecord) {
2178 : .wType = DNS_TYPE_TOMBSTONE,
2179 : };
2180 : }
2181 :
2182 2 : if (!found) {
2183 1 : talloc_free(tmp_ctx);
2184 1 : result = ISC_R_FAILURE;
2185 1 : goto exit;
2186 : }
2187 :
2188 1 : if (!b9_set_session_info(state, name)) {
2189 0 : talloc_free(tmp_ctx);
2190 0 : result = ISC_R_FAILURE;
2191 0 : goto exit;
2192 : }
2193 :
2194 : /* modify the record */
2195 1 : werr = dns_common_replace(state->samdb, tmp_ctx, dn,
2196 : false,/* needs_add */
2197 : state->soa_serial,
2198 : recs, num_recs);
2199 1 : b9_reset_session_info(state);
2200 1 : if (!W_ERROR_IS_OK(werr)) {
2201 0 : state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s",
2202 : ldb_dn_get_linearized(dn), win_errstr(werr));
2203 0 : talloc_free(tmp_ctx);
2204 0 : result = ISC_R_FAILURE;
2205 0 : goto exit;
2206 : }
2207 :
2208 1 : state->log(ISC_LOG_INFO, "samba_dlz: deleted rdataset %s of type %s", name, type);
2209 :
2210 1 : talloc_free(tmp_ctx);
2211 3 : exit:
2212 3 : DNS_COMMON_LOG_OPERATION(
2213 : isc_result_str(result),
2214 : &start,
2215 : NULL,
2216 : name,
2217 0 : type);
2218 3 : return result;
2219 : }
|