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