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