1 /*
2  * Portions Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3  *
4  * This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
7  *
8  * See the COPYRIGHT file distributed with this work for additional
9  * information regarding copyright ownership.
10  */
11 
12 /*
13  * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
14  *
15  * Permission to use, copy, modify, and distribute this software for any
16  * purpose with or without fee is hereby granted, provided that the
17  * above copyright notice and this permission notice appear in all
18  * copies.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS" AND STICHTING NLNET
21  * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
23  * STICHTING NLNET BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
24  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
25  * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
26  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
27  * USE OR PERFORMANCE OF THIS SOFTWARE.
28  *
29  * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was
30  * conceived and contributed by Rob Butler.
31  *
32  * Permission to use, copy, modify, and distribute this software for any
33  * purpose with or without fee is hereby granted, provided that the
34  * above copyright notice and this permission notice appear in all
35  * copies.
36  *
37  * THE SOFTWARE IS PROVIDED "AS IS" AND ROB BUTLER
38  * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
39  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
40  * ROB BUTLER BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
41  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
42  * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
43  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
44  * USE OR PERFORMANCE OF THIS SOFTWARE.
45  */
46 
47 
48 /*! \file */
49 
50 /***
51  *** Imports
52  ***/
53 
54 #include <config.h>
55 
56 #include <stdbool.h>
57 
58 #include <dns/db.h>
59 #include <dns/dlz.h>
60 #include <dns/fixedname.h>
61 #include <dns/log.h>
62 #include <dns/master.h>
63 #include <dns/ssu.h>
64 #include <dns/zone.h>
65 
66 
67 #include <isc/buffer.h>
68 #include <isc/commandline.h>
69 #include <isc/magic.h>
70 #include <isc/mem.h>
71 #include <isc/once.h>
72 #include <isc/rwlock.h>
73 #include <isc/string.h>
74 #include <isc/util.h>
75 
76 /***
77  *** Supported DLZ DB Implementations Registry
78  ***/
79 
80 static ISC_LIST(dns_dlzimplementation_t) dlz_implementations;
81 static isc_rwlock_t dlz_implock;
82 static isc_once_t once = ISC_ONCE_INIT;
83 
84 static void
dlz_initialize(void)85 dlz_initialize(void) {
86 	RUNTIME_CHECK(isc_rwlock_init(&dlz_implock, 0, 0) == ISC_R_SUCCESS);
87 	ISC_LIST_INIT(dlz_implementations);
88 }
89 
90 /*%
91  * Searches the dlz_implementations list for a driver matching name.
92  */
93 static inline dns_dlzimplementation_t *
dlz_impfind(const char * name)94 dlz_impfind(const char *name) {
95 	dns_dlzimplementation_t *imp;
96 
97 	for (imp = ISC_LIST_HEAD(dlz_implementations);
98 	     imp != NULL;
99 	     imp = ISC_LIST_NEXT(imp, link))
100 		if (strcasecmp(name, imp->name) == 0)
101 			return (imp);
102 	return (NULL);
103 }
104 
105 /***
106  *** Basic DLZ Methods
107  ***/
108 
109 isc_result_t
dns_dlzallowzonexfr(dns_view_t * view,dns_name_t * name,isc_sockaddr_t * clientaddr,dns_db_t ** dbp)110 dns_dlzallowzonexfr(dns_view_t *view, dns_name_t *name,
111 		    isc_sockaddr_t *clientaddr, dns_db_t **dbp)
112 {
113 	isc_result_t result = ISC_R_NOTFOUND;
114 	dns_dlzallowzonexfr_t allowzonexfr;
115 	dns_dlzdb_t *dlzdb;
116 
117 	/*
118 	 * Performs checks to make sure data is as we expect it to be.
119 	 */
120 	REQUIRE(name != NULL);
121 	REQUIRE(dbp != NULL && *dbp == NULL);
122 
123 	/*
124 	 * Find a driver in which the zone exists and transfer is supported
125 	 */
126 	for (dlzdb = ISC_LIST_HEAD(view->dlz_searched);
127 	     dlzdb != NULL;
128 	     dlzdb = ISC_LIST_NEXT(dlzdb, link))
129 	{
130 		REQUIRE(DNS_DLZ_VALID(dlzdb));
131 
132 		allowzonexfr = dlzdb->implementation->methods->allowzonexfr;
133 		result = (*allowzonexfr)(dlzdb->implementation->driverarg,
134 					 dlzdb->dbdata, dlzdb->mctx,
135 					 view->rdclass, name, clientaddr, dbp);
136 
137 		/*
138 		 * if ISC_R_NOPERM, we found the right database but
139 		 * the zone may not transfer.
140 		 */
141 		if (result == ISC_R_SUCCESS || result == ISC_R_NOPERM)
142 			return (result);
143 	}
144 
145 	if (result == ISC_R_NOTIMPLEMENTED)
146 		result = ISC_R_NOTFOUND;
147 
148 	return (result);
149 }
150 
151 isc_result_t
dns_dlzcreate(isc_mem_t * mctx,const char * dlzname,const char * drivername,unsigned int argc,char * argv[],dns_dlzdb_t ** dbp)152 dns_dlzcreate(isc_mem_t *mctx, const char *dlzname, const char *drivername,
153 	      unsigned int argc, char *argv[], dns_dlzdb_t **dbp)
154 {
155 	dns_dlzimplementation_t *impinfo;
156 	isc_result_t result;
157 	dns_dlzdb_t *db = NULL;
158 
159 	/*
160 	 * initialize the dlz_implementations list, this is guaranteed
161 	 * to only really happen once.
162 	 */
163 	RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS);
164 
165 	/*
166 	 * Performs checks to make sure data is as we expect it to be.
167 	 */
168 	REQUIRE(dbp != NULL && *dbp == NULL);
169 	REQUIRE(dlzname != NULL);
170 	REQUIRE(drivername != NULL);
171 	REQUIRE(mctx != NULL);
172 
173 	/* write log message */
174 	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
175 		      DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
176 		      "Loading '%s' using driver %s", dlzname, drivername);
177 
178 	/* lock the dlz_implementations list so we can search it. */
179 	RWLOCK(&dlz_implock, isc_rwlocktype_read);
180 
181 	/* search for the driver implementation	 */
182 	impinfo = dlz_impfind(drivername);
183 	if (impinfo == NULL) {
184 		RWUNLOCK(&dlz_implock, isc_rwlocktype_read);
185 
186 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
187 			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
188 			      "unsupported DLZ database driver '%s'."
189 			      "  %s not loaded.",
190 			      drivername, dlzname);
191 
192 		return (ISC_R_NOTFOUND);
193 	}
194 
195 	/* Allocate memory to hold the DLZ database driver */
196 	db = isc_mem_get(mctx, sizeof(dns_dlzdb_t));
197 	if (db == NULL) {
198 		RWUNLOCK(&dlz_implock, isc_rwlocktype_read);
199 		return (ISC_R_NOMEMORY);
200 	}
201 
202 	/* Make sure memory region is set to all 0's */
203 	memset(db, 0, sizeof(dns_dlzdb_t));
204 
205 	ISC_LINK_INIT(db, link);
206 	db->implementation = impinfo;
207 	if (dlzname != NULL)
208 		db->dlzname = isc_mem_strdup(mctx, dlzname);
209 
210 	/* Create a new database using implementation 'drivername'. */
211 	result = ((impinfo->methods->create)(mctx, dlzname, argc, argv,
212 					     impinfo->driverarg,
213 					     &db->dbdata));
214 
215 	/* mark the DLZ driver as valid */
216 	if (result == ISC_R_SUCCESS) {
217 		RWUNLOCK(&dlz_implock, isc_rwlocktype_read);
218 		db->magic = DNS_DLZ_MAGIC;
219 		isc_mem_attach(mctx, &db->mctx);
220 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
221 			      DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
222 			      "DLZ driver loaded successfully.");
223 		*dbp = db;
224 		return (ISC_R_SUCCESS);
225 	} else {
226 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
227 			      DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
228 			      "DLZ driver failed to load.");
229 	}
230 
231 	/* impinfo->methods->create failed. */
232 	RWUNLOCK(&dlz_implock, isc_rwlocktype_read);
233 	isc_mem_put(mctx, db, sizeof(dns_dlzdb_t));
234 	return (result);
235 }
236 
237 void
dns_dlzdestroy(dns_dlzdb_t ** dbp)238 dns_dlzdestroy(dns_dlzdb_t **dbp) {
239 	dns_dlzdestroy_t destroy;
240 	dns_dlzdb_t *db;
241 
242 	/* Write debugging message to log */
243 	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
244 		      DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
245 		      "Unloading DLZ driver.");
246 
247 	/*
248 	 * Perform checks to make sure data is as we expect it to be.
249 	 */
250 	REQUIRE(dbp != NULL && DNS_DLZ_VALID(*dbp));
251 
252 	db = *dbp;
253 	*dbp = NULL;
254 
255 	if (db->ssutable != NULL)
256 		dns_ssutable_detach(&db->ssutable);
257 
258 	/* call the drivers destroy method */
259 	if (db->dlzname != NULL)
260 		isc_mem_free(db->mctx, db->dlzname);
261 	destroy = db->implementation->methods->destroy;
262 	(*destroy)(db->implementation->driverarg, db->dbdata);
263 	/* return memory and detach */
264 	isc_mem_putanddetach(&db->mctx, db, sizeof(dns_dlzdb_t));
265 }
266 
267 /*%
268  * Registers a DLZ driver.  This basically just adds the dlz
269  * driver to the list of available drivers in the dlz_implementations list.
270  */
271 isc_result_t
dns_dlzregister(const char * drivername,const dns_dlzmethods_t * methods,void * driverarg,isc_mem_t * mctx,dns_dlzimplementation_t ** dlzimp)272 dns_dlzregister(const char *drivername, const dns_dlzmethods_t *methods,
273 		void *driverarg, isc_mem_t *mctx,
274 		dns_dlzimplementation_t **dlzimp)
275 {
276 
277 	dns_dlzimplementation_t *dlz_imp;
278 
279 	/* Write debugging message to log */
280 	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
281 		      DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
282 		      "Registering DLZ driver '%s'", drivername);
283 
284 	/*
285 	 * Performs checks to make sure data is as we expect it to be.
286 	 */
287 	REQUIRE(drivername != NULL);
288 	REQUIRE(methods != NULL);
289 	REQUIRE(methods->create != NULL);
290 	REQUIRE(methods->destroy != NULL);
291 	REQUIRE(methods->findzone != NULL);
292 	REQUIRE(mctx != NULL);
293 	REQUIRE(dlzimp != NULL && *dlzimp == NULL);
294 
295 	/*
296 	 * initialize the dlz_implementations list, this is guaranteed
297 	 * to only really happen once.
298 	 */
299 	RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS);
300 
301 	/* lock the dlz_implementations list so we can modify it. */
302 	RWLOCK(&dlz_implock, isc_rwlocktype_write);
303 
304 	/*
305 	 * check that another already registered driver isn't using
306 	 * the same name
307 	 */
308 	dlz_imp = dlz_impfind(drivername);
309 	if (dlz_imp != NULL) {
310 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
311 			      DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
312 			      "DLZ Driver '%s' already registered",
313 			      drivername);
314 		RWUNLOCK(&dlz_implock, isc_rwlocktype_write);
315 		return (ISC_R_EXISTS);
316 	}
317 
318 	/*
319 	 * Allocate memory for a dlz_implementation object.  Error if
320 	 * we cannot.
321 	 */
322 	dlz_imp = isc_mem_get(mctx, sizeof(dns_dlzimplementation_t));
323 	if (dlz_imp == NULL) {
324 		RWUNLOCK(&dlz_implock, isc_rwlocktype_write);
325 		return (ISC_R_NOMEMORY);
326 	}
327 
328 	/* Make sure memory region is set to all 0's */
329 	memset(dlz_imp, 0, sizeof(dns_dlzimplementation_t));
330 
331 	/* Store the data passed into this method */
332 	dlz_imp->name = drivername;
333 	dlz_imp->methods = methods;
334 	dlz_imp->mctx = NULL;
335 	dlz_imp->driverarg = driverarg;
336 
337 	/* attach the new dlz_implementation object to a memory context */
338 	isc_mem_attach(mctx, &dlz_imp->mctx);
339 
340 	/*
341 	 * prepare the dlz_implementation object to be put in a list,
342 	 * and append it to the list
343 	 */
344 	ISC_LINK_INIT(dlz_imp, link);
345 	ISC_LIST_APPEND(dlz_implementations, dlz_imp, link);
346 
347 	/* Unlock the dlz_implementations list.	 */
348 	RWUNLOCK(&dlz_implock, isc_rwlocktype_write);
349 
350 	/* Pass back the dlz_implementation that we created. */
351 	*dlzimp = dlz_imp;
352 
353 	return (ISC_R_SUCCESS);
354 }
355 
356 /*%
357  * Tokenize the string "s" into whitespace-separated words,
358  * return the number of words in '*argcp' and an array
359  * of pointers to the words in '*argvp'.  The caller
360  * must free the array using isc_mem_put().  The string
361  * is modified in-place.
362  */
363 isc_result_t
dns_dlzstrtoargv(isc_mem_t * mctx,char * s,unsigned int * argcp,char *** argvp)364 dns_dlzstrtoargv(isc_mem_t *mctx, char *s,
365 		 unsigned int *argcp, char ***argvp)
366 {
367 	return(isc_commandline_strtoargv(mctx, s, argcp, argvp, 0));
368 }
369 
370 /*%
371  * Unregisters a DLZ driver.  This basically just removes the dlz
372  * driver from the list of available drivers in the dlz_implementations list.
373  */
374 void
dns_dlzunregister(dns_dlzimplementation_t ** dlzimp)375 dns_dlzunregister(dns_dlzimplementation_t **dlzimp) {
376 	dns_dlzimplementation_t *dlz_imp;
377 	isc_mem_t *mctx;
378 
379 	/* Write debugging message to log */
380 	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
381 		      DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
382 		      "Unregistering DLZ driver.");
383 
384 	/*
385 	 * Performs checks to make sure data is as we expect it to be.
386 	 */
387 	REQUIRE(dlzimp != NULL && *dlzimp != NULL);
388 
389 	/*
390 	 * initialize the dlz_implementations list, this is guaranteed
391 	 * to only really happen once.
392 	 */
393 	RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS);
394 
395 	dlz_imp = *dlzimp;
396 
397 	/* lock the dlz_implementations list so we can modify it. */
398 	RWLOCK(&dlz_implock, isc_rwlocktype_write);
399 
400 	/* remove the dlz_implementation object from the list */
401 	ISC_LIST_UNLINK(dlz_implementations, dlz_imp, link);
402 	mctx = dlz_imp->mctx;
403 
404 	/*
405 	 * Return the memory back to the available memory pool and
406 	 * remove it from the memory context.
407 	 */
408 	isc_mem_put(mctx, dlz_imp, sizeof(dns_dlzimplementation_t));
409 	isc_mem_detach(&mctx);
410 
411 	/* Unlock the dlz_implementations list. */
412 	RWUNLOCK(&dlz_implock, isc_rwlocktype_write);
413 }
414 
415 /*
416  * Create a writeable DLZ zone. This can be called by DLZ drivers
417  * during configure() to create a zone that can be updated. The zone
418  * type is set to dns_zone_dlz, which is equivalent to a master zone
419  *
420  * This function uses a callback setup in dns_dlzconfigure() to call
421  * into the server zone code to setup the remaining pieces of server
422  * specific functionality on the zone
423  */
424 isc_result_t
dns_dlz_writeablezone(dns_view_t * view,dns_dlzdb_t * dlzdb,const char * zone_name)425 dns_dlz_writeablezone(dns_view_t *view, dns_dlzdb_t *dlzdb,
426 		      const char *zone_name)
427 {
428 	dns_zone_t *zone = NULL;
429 	dns_zone_t *dupzone = NULL;
430 	isc_result_t result;
431 	isc_buffer_t buffer;
432 	dns_fixedname_t fixorigin;
433 	dns_name_t *origin;
434 
435 	REQUIRE(DNS_DLZ_VALID(dlzdb));
436 
437 	REQUIRE(dlzdb->configure_callback != NULL);
438 
439 	isc_buffer_constinit(&buffer, zone_name, strlen(zone_name));
440 	isc_buffer_add(&buffer, strlen(zone_name));
441 	dns_fixedname_init(&fixorigin);
442 	result = dns_name_fromtext(dns_fixedname_name(&fixorigin),
443 				   &buffer, dns_rootname, 0, NULL);
444 	if (result != ISC_R_SUCCESS)
445 		goto cleanup;
446 	origin = dns_fixedname_name(&fixorigin);
447 
448 	if (!dlzdb->search) {
449 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
450 			      DNS_LOGMODULE_DLZ, ISC_LOG_WARNING,
451 			      "DLZ %s has 'search no;', but attempted to "
452 			      "register writeable zone %s.",
453 			      dlzdb->dlzname, zone_name);
454 		result = ISC_R_SUCCESS;
455 		goto cleanup;
456 	}
457 
458 	/* See if the zone already exists */
459 	result = dns_view_findzone(view, origin, &dupzone);
460 	if (result == ISC_R_SUCCESS) {
461 		dns_zone_detach(&dupzone);
462 		result = ISC_R_EXISTS;
463 		goto cleanup;
464 	}
465 	INSIST(dupzone == NULL);
466 
467 	/* Create it */
468 	result = dns_zone_create(&zone, view->mctx);
469 	if (result != ISC_R_SUCCESS)
470 		goto cleanup;
471 	result = dns_zone_setorigin(zone, origin);
472 	if (result != ISC_R_SUCCESS)
473 		goto cleanup;
474 	dns_zone_setview(zone, view);
475 
476 	dns_zone_setadded(zone, true);
477 
478 	if (dlzdb->ssutable == NULL) {
479 		result = dns_ssutable_createdlz(dlzdb->mctx,
480 						&dlzdb->ssutable, dlzdb);
481 		if (result != ISC_R_SUCCESS)
482 			goto cleanup;
483 	}
484 	dns_zone_setssutable(zone, dlzdb->ssutable);
485 
486 	result = dlzdb->configure_callback(view, dlzdb, zone);
487 	if (result != ISC_R_SUCCESS)
488 		goto cleanup;
489 
490 	result = dns_view_addzone(view, zone);
491 
492 
493  cleanup:
494 	if (zone != NULL)
495 		dns_zone_detach(&zone);
496 
497 	return (result);
498 }
499 
500 /*%
501  * Configure a DLZ driver. This is optional, and if supplied gives
502  * the backend an opportunity to configure parameters related to DLZ.
503  */
504 isc_result_t
dns_dlzconfigure(dns_view_t * view,dns_dlzdb_t * dlzdb,dlzconfigure_callback_t callback)505 dns_dlzconfigure(dns_view_t *view, dns_dlzdb_t *dlzdb,
506 		 dlzconfigure_callback_t callback)
507 {
508 	dns_dlzimplementation_t *impl;
509 	isc_result_t result;
510 
511 	REQUIRE(DNS_DLZ_VALID(dlzdb));
512 	REQUIRE(dlzdb->implementation != NULL);
513 
514 	impl = dlzdb->implementation;
515 
516 	if (impl->methods->configure == NULL)
517 		return (ISC_R_SUCCESS);
518 
519 	dlzdb->configure_callback = callback;
520 
521 	result = impl->methods->configure(impl->driverarg, dlzdb->dbdata,
522 					  view, dlzdb);
523 	return (result);
524 }
525 
526 bool
dns_dlz_ssumatch(dns_dlzdb_t * dlzdatabase,dns_name_t * signer,dns_name_t * name,isc_netaddr_t * tcpaddr,dns_rdatatype_t type,const dst_key_t * key)527 dns_dlz_ssumatch(dns_dlzdb_t *dlzdatabase, dns_name_t *signer,
528 		 dns_name_t *name, isc_netaddr_t *tcpaddr,
529 		 dns_rdatatype_t type, const dst_key_t *key)
530 {
531 	dns_dlzimplementation_t *impl;
532 	bool r;
533 
534 	REQUIRE(dlzdatabase != NULL);
535 	REQUIRE(dlzdatabase->implementation != NULL);
536 	REQUIRE(dlzdatabase->implementation->methods != NULL);
537 	impl = dlzdatabase->implementation;
538 
539 	if (impl->methods->ssumatch == NULL) {
540 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
541 			      DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
542 			      "No ssumatch method for DLZ database");
543 		return (false);
544 	}
545 
546 	r = impl->methods->ssumatch(signer, name, tcpaddr, type, key,
547 				    impl->driverarg, dlzdatabase->dbdata);
548 	return (r);
549 }
550