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