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