1 /*
2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3  *
4  * SPDX-License-Identifier: MPL-2.0
5  *
6  * This Source Code Form is subject to the terms of the Mozilla Public
7  * License, v. 2.0. If a copy of the MPL was not distributed with this
8  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
9  *
10  * See the COPYRIGHT file distributed with this work for additional
11  * information regarding copyright ownership.
12  */
13 
14 #include <inttypes.h>
15 #include <stdbool.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <windows.h>
20 
21 #include <isc/mem.h>
22 #include <isc/print.h>
23 #include <isc/result.h>
24 #include <isc/util.h>
25 
26 #include <dns/dlz_dlopen.h>
27 #include <dns/log.h>
28 #include <dns/result.h>
29 
30 #include <dlz/dlz_dlopen_driver.h>
31 #include <named/globals.h>
32 
33 #ifdef ISC_DLZ_DLOPEN
34 static dns_sdlzimplementation_t *dlz_dlopen = NULL;
35 
36 typedef struct dlopen_data {
37 	isc_mem_t *mctx;
38 	char *dl_path;
39 	char *dlzname;
40 	HMODULE dl_handle;
41 	void *dbdata;
42 	unsigned int flags;
43 	isc_mutex_t lock;
44 	int version;
45 	bool in_configure;
46 
47 	dlz_dlopen_version_t *dlz_version;
48 	dlz_dlopen_create_t *dlz_create;
49 	dlz_dlopen_findzonedb_t *dlz_findzonedb;
50 	dlz_dlopen_lookup_t *dlz_lookup;
51 	dlz_dlopen_authority_t *dlz_authority;
52 	dlz_dlopen_allnodes_t *dlz_allnodes;
53 	dlz_dlopen_allowzonexfr_t *dlz_allowzonexfr;
54 	dlz_dlopen_newversion_t *dlz_newversion;
55 	dlz_dlopen_closeversion_t *dlz_closeversion;
56 	dlz_dlopen_configure_t *dlz_configure;
57 	dlz_dlopen_ssumatch_t *dlz_ssumatch;
58 	dlz_dlopen_addrdataset_t *dlz_addrdataset;
59 	dlz_dlopen_subrdataset_t *dlz_subrdataset;
60 	dlz_dlopen_delrdataset_t *dlz_delrdataset;
61 	dlz_dlopen_destroy_t *dlz_destroy;
62 } dlopen_data_t;
63 
64 /* Modules can choose whether they are lock-safe or not. */
65 #define MAYBE_LOCK(cd)                                            \
66 	do {                                                      \
67 		if ((cd->flags & DNS_SDLZFLAG_THREADSAFE) == 0 && \
68 		    !cd->in_configure)                            \
69 			LOCK(&cd->lock);                          \
70 	} while (0)
71 
72 #define MAYBE_UNLOCK(cd)                                          \
73 	do {                                                      \
74 		if ((cd->flags & DNS_SDLZFLAG_THREADSAFE) == 0 && \
75 		    !cd->in_configure)                            \
76 			UNLOCK(&cd->lock);                        \
77 	} while (0)
78 
79 /*
80  * Log a message at the given level.
81  */
82 static void
dlopen_log(int level,const char * fmt,...)83 dlopen_log(int level, const char *fmt, ...) {
84 	va_list ap;
85 	va_start(ap, fmt);
86 	isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DLZ,
87 		       ISC_LOG_DEBUG(level), fmt, ap);
88 	va_end(ap);
89 }
90 
91 /*
92  * SDLZ methods
93  */
94 
95 static isc_result_t
dlopen_dlz_allnodes(const char * zone,void * driverarg,void * dbdata,dns_sdlzallnodes_t * allnodes)96 dlopen_dlz_allnodes(const char *zone, void *driverarg, void *dbdata,
97 		    dns_sdlzallnodes_t *allnodes) {
98 	dlopen_data_t *cd = (dlopen_data_t *)dbdata;
99 	isc_result_t result;
100 
101 	UNUSED(driverarg);
102 
103 	if (cd->dlz_allnodes == NULL) {
104 		return (ISC_R_NOPERM);
105 	}
106 
107 	MAYBE_LOCK(cd);
108 	result = cd->dlz_allnodes(zone, cd->dbdata, allnodes);
109 	MAYBE_UNLOCK(cd);
110 	return (result);
111 }
112 
113 static isc_result_t
dlopen_dlz_allowzonexfr(void * driverarg,void * dbdata,const char * name,const char * client)114 dlopen_dlz_allowzonexfr(void *driverarg, void *dbdata, const char *name,
115 			const char *client) {
116 	dlopen_data_t *cd = (dlopen_data_t *)dbdata;
117 	isc_result_t result;
118 
119 	UNUSED(driverarg);
120 
121 	if (cd->dlz_allowzonexfr == NULL) {
122 		return (ISC_R_NOPERM);
123 	}
124 
125 	MAYBE_LOCK(cd);
126 	result = cd->dlz_allowzonexfr(cd->dbdata, name, client);
127 	MAYBE_UNLOCK(cd);
128 	return (result);
129 }
130 
131 static isc_result_t
dlopen_dlz_authority(const char * zone,void * driverarg,void * dbdata,dns_sdlzlookup_t * lookup)132 dlopen_dlz_authority(const char *zone, void *driverarg, void *dbdata,
133 		     dns_sdlzlookup_t *lookup) {
134 	dlopen_data_t *cd = (dlopen_data_t *)dbdata;
135 	isc_result_t result;
136 
137 	UNUSED(driverarg);
138 
139 	if (cd->dlz_authority == NULL) {
140 		return (ISC_R_NOTIMPLEMENTED);
141 	}
142 
143 	MAYBE_LOCK(cd);
144 	result = cd->dlz_authority(zone, cd->dbdata, lookup);
145 	MAYBE_UNLOCK(cd);
146 	return (result);
147 }
148 
149 static isc_result_t
dlopen_dlz_findzonedb(void * driverarg,void * dbdata,const char * name,dns_clientinfomethods_t * methods,dns_clientinfo_t * clientinfo)150 dlopen_dlz_findzonedb(void *driverarg, void *dbdata, const char *name,
151 		      dns_clientinfomethods_t *methods,
152 		      dns_clientinfo_t *clientinfo) {
153 	dlopen_data_t *cd = (dlopen_data_t *)dbdata;
154 	isc_result_t result;
155 
156 	UNUSED(driverarg);
157 
158 	MAYBE_LOCK(cd);
159 	result = cd->dlz_findzonedb(cd->dbdata, name, methods, clientinfo);
160 	MAYBE_UNLOCK(cd);
161 	return (result);
162 }
163 
164 static isc_result_t
dlopen_dlz_lookup(const char * zone,const char * name,void * driverarg,void * dbdata,dns_sdlzlookup_t * lookup,dns_clientinfomethods_t * methods,dns_clientinfo_t * clientinfo)165 dlopen_dlz_lookup(const char *zone, const char *name, void *driverarg,
166 		  void *dbdata, dns_sdlzlookup_t *lookup,
167 		  dns_clientinfomethods_t *methods,
168 		  dns_clientinfo_t *clientinfo) {
169 	dlopen_data_t *cd = (dlopen_data_t *)dbdata;
170 	isc_result_t result;
171 
172 	UNUSED(driverarg);
173 
174 	MAYBE_LOCK(cd);
175 	result = cd->dlz_lookup(zone, name, cd->dbdata, lookup, methods,
176 				clientinfo);
177 	MAYBE_UNLOCK(cd);
178 	return (result);
179 }
180 
181 /*
182  * Load a symbol from the library
183  */
184 static void *
dl_load_symbol(dlopen_data_t * cd,const char * symbol,bool mandatory)185 dl_load_symbol(dlopen_data_t *cd, const char *symbol, bool mandatory) {
186 	void *ptr = GetProcAddress(cd->dl_handle, symbol);
187 	if (ptr == NULL && mandatory) {
188 		dlopen_log(ISC_LOG_ERROR,
189 			   "dlz_dlopen: library '%s' is missing "
190 			   "required symbol '%s'",
191 			   cd->dl_path, symbol);
192 	}
193 	return (ptr);
194 }
195 
196 /*
197  * Called at startup for each dlopen zone in named.conf
198  */
199 static isc_result_t
dlopen_dlz_create(const char * dlzname,unsigned int argc,char * argv[],void * driverarg,void ** dbdata)200 dlopen_dlz_create(const char *dlzname, unsigned int argc, char *argv[],
201 		  void *driverarg, void **dbdata) {
202 	dlopen_data_t *cd;
203 	isc_mem_t *mctx = NULL;
204 	isc_result_t result = ISC_R_FAILURE;
205 	bool triedload = false;
206 
207 	UNUSED(driverarg);
208 
209 	if (argc < 2) {
210 		dlopen_log(ISC_LOG_ERROR,
211 			   "dlz_dlopen driver for '%s' needs a path to "
212 			   "the shared library",
213 			   dlzname);
214 		return (ISC_R_FAILURE);
215 	}
216 
217 	isc_mem_create(&mctx);
218 
219 	cd = isc_mem_get(mctx, sizeof(*cd));
220 	memset(cd, 0, sizeof(*cd));
221 
222 	cd->mctx = mctx;
223 
224 	cd->dl_path = isc_mem_strdup(cd->mctx, argv[1]);
225 
226 	cd->dlzname = isc_mem_strdup(cd->mctx, dlzname);
227 
228 	triedload = true;
229 
230 	/* Initialize the lock */
231 	isc_mutex_init(&cd->lock);
232 
233 	/* Open the library */
234 	cd->dl_handle = LoadLibraryA(cd->dl_path);
235 	if (cd->dl_handle == NULL) {
236 		unsigned int error = GetLastError();
237 
238 		dlopen_log(ISC_LOG_ERROR,
239 			   "dlz_dlopen failed to open library '%s' - %u",
240 			   cd->dl_path, error);
241 		result = ISC_R_FAILURE;
242 		goto cleanup_lock;
243 	}
244 
245 	/* Find the symbols */
246 	cd->dlz_version =
247 		(dlz_dlopen_version_t *)dl_load_symbol(cd, "dlz_version", true);
248 	cd->dlz_create = (dlz_dlopen_create_t *)dl_load_symbol(cd, "dlz_create",
249 							       true);
250 	cd->dlz_lookup = (dlz_dlopen_lookup_t *)dl_load_symbol(cd, "dlz_lookup",
251 							       true);
252 	cd->dlz_findzonedb = (dlz_dlopen_findzonedb_t *)dl_load_symbol(
253 		cd, "dlz_findzonedb", true);
254 
255 	if (cd->dlz_create == NULL || cd->dlz_version == NULL ||
256 	    cd->dlz_lookup == NULL || cd->dlz_findzonedb == NULL)
257 	{
258 		/* We're missing a required symbol */
259 		result = ISC_R_FAILURE;
260 		goto cleanup_lock;
261 	}
262 
263 	cd->dlz_allowzonexfr = (dlz_dlopen_allowzonexfr_t *)dl_load_symbol(
264 		cd, "dlz_allowzonexfr", false);
265 	cd->dlz_allnodes = (dlz_dlopen_allnodes_t *)dl_load_symbol(
266 		cd, "dlz_allnodes", (cd->dlz_allowzonexfr != NULL));
267 	cd->dlz_authority = (dlz_dlopen_authority_t *)dl_load_symbol(
268 		cd, "dlz_authority", false);
269 	cd->dlz_newversion = (dlz_dlopen_newversion_t *)dl_load_symbol(
270 		cd, "dlz_newversion", false);
271 	cd->dlz_closeversion = (dlz_dlopen_closeversion_t *)dl_load_symbol(
272 		cd, "dlz_closeversion", (cd->dlz_newversion != NULL));
273 	cd->dlz_configure = (dlz_dlopen_configure_t *)dl_load_symbol(
274 		cd, "dlz_configure", false);
275 	cd->dlz_ssumatch = (dlz_dlopen_ssumatch_t *)dl_load_symbol(
276 		cd, "dlz_ssumatch", false);
277 	cd->dlz_addrdataset = (dlz_dlopen_addrdataset_t *)dl_load_symbol(
278 		cd, "dlz_addrdataset", false);
279 	cd->dlz_subrdataset = (dlz_dlopen_subrdataset_t *)dl_load_symbol(
280 		cd, "dlz_subrdataset", false);
281 	cd->dlz_delrdataset = (dlz_dlopen_delrdataset_t *)dl_load_symbol(
282 		cd, "dlz_delrdataset", false);
283 
284 	/* Check the version of the API is the same */
285 	cd->version = cd->dlz_version(&cd->flags);
286 	if (cd->version < (DLZ_DLOPEN_VERSION - DLZ_DLOPEN_AGE) ||
287 	    cd->version > DLZ_DLOPEN_VERSION)
288 	{
289 		dlopen_log(ISC_LOG_ERROR,
290 			   "dlz_dlopen: %s: incorrect driver API version %d, "
291 			   "requires %d",
292 			   cd->dl_path, cd->version, DLZ_DLOPEN_VERSION);
293 		result = ISC_R_FAILURE;
294 		goto cleanup_lock;
295 	}
296 
297 	/*
298 	 * Call the library's create function. Note that this is an
299 	 * extended version of dlz create, with the addition of
300 	 * named function pointers for helper functions that the
301 	 * driver will need. This avoids the need for the backend to
302 	 * link the BIND9 libraries
303 	 */
304 	MAYBE_LOCK(cd);
305 	result = cd->dlz_create(dlzname, argc - 1, argv + 1, &cd->dbdata, "log",
306 				dlopen_log, "putrr", dns_sdlz_putrr,
307 				"putnamedrr", dns_sdlz_putnamedrr,
308 				"writeable_zone", dns_dlz_writeablezone, NULL);
309 	MAYBE_UNLOCK(cd);
310 	if (result != ISC_R_SUCCESS) {
311 		goto cleanup_lock;
312 	}
313 
314 	*dbdata = cd;
315 
316 	return (ISC_R_SUCCESS);
317 
318 cleanup_lock:
319 	isc_mutex_destroy(&cd->lock);
320 failed:
321 	dlopen_log(ISC_LOG_ERROR, "dlz_dlopen of '%s' failed", dlzname);
322 	if (cd->dl_path) {
323 		isc_mem_free(mctx, cd->dl_path);
324 	}
325 	if (cd->dlzname) {
326 		isc_mem_free(mctx, cd->dlzname);
327 	}
328 	if (triedload) {
329 		isc_mutex_destroy(&cd->lock);
330 	}
331 	if (cd->dl_handle) {
332 		FreeLibrary(cd->dl_handle);
333 	}
334 	isc_mem_put(mctx, cd, sizeof(*cd));
335 	isc_mem_destroy(&mctx);
336 	return (result);
337 }
338 
339 /*
340  * Called when bind is shutting down
341  */
342 static void
dlopen_dlz_destroy(void * driverarg,void * dbdata)343 dlopen_dlz_destroy(void *driverarg, void *dbdata) {
344 	dlopen_data_t *cd = (dlopen_data_t *)dbdata;
345 	isc_mem_t *mctx;
346 
347 	UNUSED(driverarg);
348 
349 	if (cd->dlz_destroy) {
350 		MAYBE_LOCK(cd);
351 		cd->dlz_destroy(cd->dbdata);
352 		MAYBE_UNLOCK(cd);
353 	}
354 
355 	if (cd->dl_path) {
356 		isc_mem_free(cd->mctx, cd->dl_path);
357 	}
358 	if (cd->dlzname) {
359 		isc_mem_free(cd->mctx, cd->dlzname);
360 	}
361 
362 	if (cd->dl_handle) {
363 		FreeLibrary(cd->dl_handle);
364 	}
365 
366 	isc_mutex_destroy(&cd->lock);
367 
368 	mctx = cd->mctx;
369 	isc_mem_put(mctx, cd, sizeof(*cd));
370 	isc_mem_destroy(&mctx);
371 }
372 
373 /*
374  * Called to start a transaction
375  */
376 static isc_result_t
dlopen_dlz_newversion(const char * zone,void * driverarg,void * dbdata,void ** versionp)377 dlopen_dlz_newversion(const char *zone, void *driverarg, void *dbdata,
378 		      void **versionp) {
379 	dlopen_data_t *cd = (dlopen_data_t *)dbdata;
380 	isc_result_t result;
381 
382 	UNUSED(driverarg);
383 
384 	if (cd->dlz_newversion == NULL) {
385 		return (ISC_R_NOTIMPLEMENTED);
386 	}
387 
388 	MAYBE_LOCK(cd);
389 	result = cd->dlz_newversion(zone, cd->dbdata, versionp);
390 	MAYBE_UNLOCK(cd);
391 	return (result);
392 }
393 
394 /*
395  * Called to end a transaction
396  */
397 static void
dlopen_dlz_closeversion(const char * zone,bool commit,void * driverarg,void * dbdata,void ** versionp)398 dlopen_dlz_closeversion(const char *zone, bool commit, void *driverarg,
399 			void *dbdata, void **versionp) {
400 	dlopen_data_t *cd = (dlopen_data_t *)dbdata;
401 
402 	UNUSED(driverarg);
403 
404 	if (cd->dlz_newversion == NULL) {
405 		*versionp = NULL;
406 		return;
407 	}
408 
409 	MAYBE_LOCK(cd);
410 	cd->dlz_closeversion(zone, commit, cd->dbdata, versionp);
411 	MAYBE_UNLOCK(cd);
412 }
413 
414 /*
415  * Called on startup to configure any writeable zones
416  */
417 static isc_result_t
dlopen_dlz_configure(dns_view_t * view,dns_dlzdb_t * dlzdb,void * driverarg,void * dbdata)418 dlopen_dlz_configure(dns_view_t *view, dns_dlzdb_t *dlzdb, void *driverarg,
419 		     void *dbdata) {
420 	dlopen_data_t *cd = (dlopen_data_t *)dbdata;
421 	isc_result_t result;
422 
423 	UNUSED(driverarg);
424 
425 	if (cd->dlz_configure == NULL) {
426 		return (ISC_R_SUCCESS);
427 	}
428 
429 	MAYBE_LOCK(cd);
430 	cd->in_configure = true;
431 	result = cd->dlz_configure(view, dlzdb, cd->dbdata);
432 	cd->in_configure = false;
433 	MAYBE_UNLOCK(cd);
434 
435 	return (result);
436 }
437 
438 /*
439  * Check for authority to change a name
440  */
441 static bool
dlopen_dlz_ssumatch(const char * signer,const char * name,const char * tcpaddr,const char * type,const char * key,uint32_t keydatalen,unsigned char * keydata,void * driverarg,void * dbdata)442 dlopen_dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr,
443 		    const char *type, const char *key, uint32_t keydatalen,
444 		    unsigned char *keydata, void *driverarg, void *dbdata) {
445 	dlopen_data_t *cd = (dlopen_data_t *)dbdata;
446 	bool ret;
447 
448 	UNUSED(driverarg);
449 
450 	if (cd->dlz_ssumatch == NULL) {
451 		return (false);
452 	}
453 
454 	MAYBE_LOCK(cd);
455 	ret = cd->dlz_ssumatch(signer, name, tcpaddr, type, key, keydatalen,
456 			       keydata, cd->dbdata);
457 	MAYBE_UNLOCK(cd);
458 
459 	return (ret);
460 }
461 
462 /*
463  * Add an rdataset
464  */
465 static isc_result_t
dlopen_dlz_addrdataset(const char * name,const char * rdatastr,void * driverarg,void * dbdata,void * version)466 dlopen_dlz_addrdataset(const char *name, const char *rdatastr, void *driverarg,
467 		       void *dbdata, void *version) {
468 	dlopen_data_t *cd = (dlopen_data_t *)dbdata;
469 	isc_result_t result;
470 
471 	UNUSED(driverarg);
472 
473 	if (cd->dlz_addrdataset == NULL) {
474 		return (ISC_R_NOTIMPLEMENTED);
475 	}
476 
477 	MAYBE_LOCK(cd);
478 	result = cd->dlz_addrdataset(name, rdatastr, cd->dbdata, version);
479 	MAYBE_UNLOCK(cd);
480 
481 	return (result);
482 }
483 
484 /*
485  * Subtract an rdataset
486  */
487 static isc_result_t
dlopen_dlz_subrdataset(const char * name,const char * rdatastr,void * driverarg,void * dbdata,void * version)488 dlopen_dlz_subrdataset(const char *name, const char *rdatastr, void *driverarg,
489 		       void *dbdata, void *version) {
490 	dlopen_data_t *cd = (dlopen_data_t *)dbdata;
491 	isc_result_t result;
492 
493 	UNUSED(driverarg);
494 
495 	if (cd->dlz_subrdataset == NULL) {
496 		return (ISC_R_NOTIMPLEMENTED);
497 	}
498 
499 	MAYBE_LOCK(cd);
500 	result = cd->dlz_subrdataset(name, rdatastr, cd->dbdata, version);
501 	MAYBE_UNLOCK(cd);
502 
503 	return (result);
504 }
505 
506 /*
507  * delete a rdataset
508  */
509 static isc_result_t
dlopen_dlz_delrdataset(const char * name,const char * type,void * driverarg,void * dbdata,void * version)510 dlopen_dlz_delrdataset(const char *name, const char *type, void *driverarg,
511 		       void *dbdata, void *version) {
512 	dlopen_data_t *cd = (dlopen_data_t *)dbdata;
513 	isc_result_t result;
514 
515 	UNUSED(driverarg);
516 
517 	if (cd->dlz_delrdataset == NULL) {
518 		return (ISC_R_NOTIMPLEMENTED);
519 	}
520 
521 	MAYBE_LOCK(cd);
522 	result = cd->dlz_delrdataset(name, type, cd->dbdata, version);
523 	MAYBE_UNLOCK(cd);
524 
525 	return (result);
526 }
527 
528 static dns_sdlzmethods_t dlz_dlopen_methods = {
529 	dlopen_dlz_create,	 dlopen_dlz_destroy,	dlopen_dlz_findzonedb,
530 	dlopen_dlz_lookup,	 dlopen_dlz_authority,	dlopen_dlz_allnodes,
531 	dlopen_dlz_allowzonexfr, dlopen_dlz_newversion, dlopen_dlz_closeversion,
532 	dlopen_dlz_configure,	 dlopen_dlz_ssumatch,	dlopen_dlz_addrdataset,
533 	dlopen_dlz_subrdataset,	 dlopen_dlz_delrdataset
534 };
535 #endif /* ifdef ISC_DLZ_DLOPEN */
536 
537 /*
538  * Register driver with BIND
539  */
540 isc_result_t
dlz_dlopen_init(isc_mem_t * mctx)541 dlz_dlopen_init(isc_mem_t *mctx) {
542 #ifndef ISC_DLZ_DLOPEN
543 	UNUSED(mctx);
544 	return (ISC_R_NOTIMPLEMENTED);
545 #else  /* ifndef ISC_DLZ_DLOPEN */
546 	isc_result_t result;
547 
548 	dlopen_log(2, "Registering DLZ_dlopen driver");
549 
550 	result = dns_sdlzregister("dlopen", &dlz_dlopen_methods, NULL,
551 				  DNS_SDLZFLAG_RELATIVEOWNER |
552 					  DNS_SDLZFLAG_RELATIVERDATA |
553 					  DNS_SDLZFLAG_THREADSAFE,
554 				  mctx, &dlz_dlopen);
555 
556 	if (result != ISC_R_SUCCESS) {
557 		UNEXPECTED_ERROR(__FILE__, __LINE__,
558 				 "dns_sdlzregister() failed: %s",
559 				 isc_result_totext(result));
560 		result = ISC_R_UNEXPECTED;
561 	}
562 
563 	return (result);
564 #endif /* ifndef ISC_DLZ_DLOPEN */
565 }
566 
567 /*
568  * Unregister the driver
569  */
570 void
dlz_dlopen_clear(void)571 dlz_dlopen_clear(void) {
572 #ifdef ISC_DLZ_DLOPEN
573 	dlopen_log(2, "Unregistering DLZ_dlopen driver");
574 	if (dlz_dlopen != NULL) {
575 		dns_sdlzunregister(&dlz_dlopen);
576 	}
577 #endif /* ifdef ISC_DLZ_DLOPEN */
578 }
579