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