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