Coverage Report

Created: 2023-06-07 06:23

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