Coverage Report

Created: 2026-08-31 07:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bind9/lib/dns/acl.c
Line
Count
Source
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
/*! \file */
15
16
#include <inttypes.h>
17
#include <stdbool.h>
18
19
#include <isc/mem.h>
20
#include <isc/once.h>
21
#include <isc/string.h>
22
#include <isc/urcu.h>
23
#include <isc/util.h>
24
25
#include <dns/acl.h>
26
#include <dns/iptable.h>
27
28
#include "acl_p.h"
29
30
2
#define DNS_ACLENV_MAGIC ISC_MAGIC('a', 'c', 'n', 'v')
31
#define VALID_ACLENV(a)  ISC_MAGIC_VALID(a, DNS_ACLENV_MAGIC)
32
33
/*
34
 * Create a new ACL, including an IP table and an array with room
35
 * for 'n' ACL elements.  The elements are uninitialized and the
36
 * length is 0.
37
 */
38
void
39
4
dns_acl_create(isc_mem_t *mctx, int n, dns_acl_t **target) {
40
4
  REQUIRE(target != NULL && *target == NULL);
41
42
4
  dns_acl_t *acl = isc_mem_get(mctx, sizeof(*acl));
43
4
  *acl = (dns_acl_t){
44
4
    .references = ISC_REFCOUNT_INITIALIZER(1),
45
4
    .nextincache = ISC_LINK_INITIALIZER,
46
4
    .elements = isc_mem_cget(mctx, n, sizeof(acl->elements[0])),
47
4
    .alloc = n,
48
4
    .ports_and_transports = ISC_LIST_INITIALIZER,
49
4
    .magic = DNS_ACL_MAGIC,
50
4
  };
51
52
4
  isc_mem_attach(mctx, &acl->mctx);
53
4
  dns_iptable_create(acl->mctx, &acl->iptable);
54
55
4
  *target = acl;
56
4
}
57
58
void
59
0
dns_acl_any(isc_mem_t *mctx, dns_acl_t **target) {
60
0
  dns_acl_create(mctx, 0, target);
61
0
  dns_iptable_addprefix((*target)->iptable, NULL, 0, RADIX_ALLOW);
62
0
}
63
64
void
65
0
dns_acl_none(isc_mem_t *mctx, dns_acl_t **target) {
66
0
  dns_acl_create(mctx, 0, target);
67
0
  dns_iptable_addprefix((*target)->iptable, NULL, 0, RADIX_DENY);
68
0
}
69
70
/*
71
 * If pos is true, test whether acl is set to "{ any; }"
72
 * If pos is false, test whether acl is set to "{ none; }"
73
 */
74
static bool
75
0
dns_acl_isanyornone(dns_acl_t *acl, bool pos) {
76
  /* Should never happen but let's be safe */
77
0
  if (acl == NULL || acl->iptable == NULL ||
78
0
      acl->iptable->radix == NULL || acl->iptable->radix->head == NULL ||
79
0
      acl->iptable->radix->head->prefix.family == 0)
80
0
  {
81
0
    return false;
82
0
  }
83
84
0
  if (acl->length != 0 || dns_acl_node_count(acl) != 2) {
85
0
    return false;
86
0
  }
87
88
0
  isc_radix_node_t *head = acl->iptable->radix->head;
89
0
  isc_radix_match_t expected = pos ? RADIX_ALLOW : RADIX_DENY;
90
91
0
  if (head->prefix.bitlen == 0 && head->match[RADIX_V4] == expected &&
92
0
      head->match[RADIX_V6] == expected)
93
0
  {
94
0
    return true;
95
0
  }
96
97
0
  return false;
98
0
}
99
100
/*
101
 * Test whether acl is set to "{ any; }"
102
 */
103
bool
104
0
dns_acl_isany(dns_acl_t *acl) {
105
0
  return dns_acl_isanyornone(acl, true);
106
0
}
107
108
/*
109
 * Test whether acl is set to "{ none; }"
110
 */
111
bool
112
0
dns_acl_isnone(dns_acl_t *acl) {
113
0
  return dns_acl_isanyornone(acl, false);
114
0
}
115
116
/*
117
 * Determine whether a given address or signer matches a given ACL.
118
 * For a match with a positive ACL element or iptable radix entry,
119
 * return with a positive value in match; for a match with a negated ACL
120
 * element or radix entry, return with a negative value in match.
121
 */
122
123
isc_result_t
124
dns_acl_match(const isc_netaddr_t *reqaddr, const dns_name_t *reqsigner,
125
        const dns_acl_t *acl, dns_aclenv_t *env, int *match,
126
0
        const dns_aclelement_t **matchelt) {
127
0
  uint16_t bitlen;
128
0
  isc_prefix_t pfx;
129
0
  isc_radix_node_t *node = NULL;
130
0
  const isc_netaddr_t *addr = reqaddr;
131
0
  isc_netaddr_t v4addr;
132
0
  isc_result_t result;
133
0
  int32_t match_num = -1;
134
0
  unsigned int i;
135
136
0
  REQUIRE(reqaddr != NULL);
137
0
  REQUIRE(matchelt == NULL || *matchelt == NULL);
138
139
0
  if (env != NULL && env->match_mapped && addr->family == AF_INET6 &&
140
0
      IN6_IS_ADDR_V4MAPPED(&addr->type.in6))
141
0
  {
142
0
    isc_netaddr_fromv4mapped(&v4addr, addr);
143
0
    addr = &v4addr;
144
0
  }
145
146
  /* Always match with host addresses. */
147
0
  bitlen = (addr->family == AF_INET6) ? 128 : 32;
148
0
  isc_prefix_from_netaddr(&pfx, addr, bitlen);
149
150
  /* Assume no match. */
151
0
  *match = 0;
152
153
  /* Search radix. */
154
0
  result = isc_radix_search(acl->iptable->radix, &node, &pfx);
155
156
  /* Found a match. */
157
0
  if (result == ISC_R_SUCCESS && node != NULL) {
158
0
    int fam = ISC_RADIX_FAMILY(&pfx);
159
0
    match_num = node->node_num[fam];
160
0
    if (node->match[fam] == RADIX_ALLOW) {
161
0
      *match = match_num;
162
0
    } else {
163
0
      *match = -match_num;
164
0
    }
165
0
  }
166
167
  /* Now search non-radix elements for a match with a lower node_num. */
168
0
  for (i = 0; i < acl->length; i++) {
169
0
    dns_aclelement_t *e = &acl->elements[i];
170
171
    /* Already found a better match? */
172
0
    if (match_num != -1 && match_num < e->node_num) {
173
0
      break;
174
0
    }
175
176
0
    if (dns_aclelement_match(reqaddr, reqsigner, e, env, matchelt))
177
0
    {
178
0
      if (match_num == -1 || e->node_num < match_num) {
179
0
        if (e->negative) {
180
0
          *match = -e->node_num;
181
0
        } else {
182
0
          *match = e->node_num;
183
0
        }
184
0
      }
185
0
      break;
186
0
    }
187
0
  }
188
189
0
  return ISC_R_SUCCESS;
190
0
}
191
192
isc_result_t
193
dns_acl_match_port_transport(const isc_netaddr_t *reqaddr,
194
           const in_port_t local_port,
195
           const isc_nmsocket_type_t transport,
196
           const bool encrypted, const dns_name_t *reqsigner,
197
           const dns_acl_t *acl, dns_aclenv_t *env,
198
0
           int *match, const dns_aclelement_t **matchelt) {
199
0
  isc_result_t result = ISC_R_SUCCESS;
200
201
0
  REQUIRE(reqaddr != NULL);
202
0
  REQUIRE(DNS_ACL_VALID(acl));
203
204
0
  dns_acl_t *a = UNCONST(acl); /* for ISC_LIST_FOREACH */
205
0
  ISC_LIST_FOREACH(a->ports_and_transports, next, link) {
206
0
    bool match_port = true;
207
0
    bool match_transport = true;
208
0
    result = ISC_R_FAILURE;
209
210
0
    if (next->port != 0) {
211
      /* Port is specified. */
212
0
      match_port = (local_port == next->port);
213
0
    }
214
0
    if (next->transports != 0) {
215
      /* Transport protocol is specified. */
216
0
      match_transport = ((transport & next->transports) ==
217
0
               transport &&
218
0
             next->encrypted == encrypted);
219
0
    }
220
221
0
    if (match_port && match_transport) {
222
0
      result = next->negative ? ISC_R_FAILURE : ISC_R_SUCCESS;
223
0
      break;
224
0
    }
225
0
  }
226
227
0
  if (result != ISC_R_SUCCESS) {
228
0
    return result;
229
0
  }
230
231
0
  return dns_acl_match(reqaddr, reqsigner, acl, env, match, matchelt);
232
0
}
233
234
/*
235
 * Merge the contents of one ACL into another.  Call dns_iptable_merge()
236
 * for the IP tables, then concatenate the element arrays.
237
 *
238
 * If pos is set to false, then the nested ACL is to be negated.  This
239
 * means reverse the sense of each *positive* element or IP table node,
240
 * but leave negatives alone, so as to prevent a double-negative causing
241
 * an unexpected positive match in the parent ACL.
242
 */
243
isc_result_t
244
0
dns_acl_merge(dns_acl_t *dest, dns_acl_t *source, bool pos) {
245
0
  unsigned int nelem, i;
246
0
  int32_t max_node = 0, nodes;
247
248
  /* Resize the element array if needed. */
249
0
  if (dest->length + source->length > dest->alloc) {
250
0
    size_t newalloc = dest->alloc + source->alloc;
251
0
    if (newalloc < 4) {
252
0
      newalloc = 4;
253
0
    }
254
255
0
    dest->elements = isc_mem_creget(dest->mctx, dest->elements,
256
0
            dest->alloc, newalloc,
257
0
            sizeof(dest->elements[0]));
258
0
    dest->alloc = newalloc;
259
0
  }
260
261
  /*
262
   * Now copy in the new elements, increasing their node_num
263
   * values so as to keep the new ACL consistent.  If we're
264
   * negating, then negate positive elements, but keep negative
265
   * elements the same for security reasons.
266
   */
267
0
  nelem = dest->length;
268
0
  dest->length += source->length;
269
0
  for (i = 0; i < source->length; i++) {
270
0
    if (source->elements[i].node_num > max_node) {
271
0
      max_node = source->elements[i].node_num;
272
0
    }
273
274
    /* Copy type. */
275
0
    dest->elements[nelem + i].type = source->elements[i].type;
276
277
    /* Adjust node numbering. */
278
0
    dest->elements[nelem + i].node_num =
279
0
      source->elements[i].node_num + dns_acl_node_count(dest);
280
281
    /* Duplicate nested acl. */
282
0
    if (source->elements[i].type == dns_aclelementtype_nestedacl &&
283
0
        source->elements[i].nestedacl != NULL)
284
0
    {
285
0
      dns_acl_attach(source->elements[i].nestedacl,
286
0
               &dest->elements[nelem + i].nestedacl);
287
0
    }
288
289
    /* Duplicate key name. */
290
0
    if (source->elements[i].type == dns_aclelementtype_keyname) {
291
0
      dns_name_init(&dest->elements[nelem + i].keyname);
292
0
      dns_name_dup(&source->elements[i].keyname, dest->mctx,
293
0
             &dest->elements[nelem + i].keyname);
294
0
    }
295
296
#if defined(HAVE_GEOIP2)
297
    /* Duplicate GeoIP data */
298
    if (source->elements[i].type == dns_aclelementtype_geoip) {
299
      dest->elements[nelem + i].geoip_elem =
300
        source->elements[i].geoip_elem;
301
    }
302
#endif /* if defined(HAVE_GEOIP2) */
303
304
    /* reverse sense of positives if this is a negative acl */
305
0
    if (!pos && !source->elements[i].negative) {
306
0
      dest->elements[nelem + i].negative = true;
307
0
    } else {
308
0
      dest->elements[nelem + i].negative =
309
0
        source->elements[i].negative;
310
0
    }
311
0
  }
312
313
  /*
314
   * Merge the iptables.  Make sure the destination ACL's
315
   * node_count value is set correctly afterward.
316
   */
317
0
  nodes = max_node + dns_acl_node_count(dest);
318
0
  dns_iptable_merge(dest->iptable, source->iptable, !pos);
319
0
  if (nodes > dns_acl_node_count(dest)) {
320
0
    dns_acl_node_count(dest) = nodes;
321
0
  }
322
323
  /*
324
   * Merge ports and transports
325
   */
326
0
  dns_acl_merge_ports_transports(dest, source, pos);
327
328
0
  return ISC_R_SUCCESS;
329
0
}
330
331
/*
332
 * Like dns_acl_match, but matches against the single ACL element 'e'
333
 * rather than a complete ACL, and returns true iff it matched.
334
 *
335
 * To determine whether the match was positive or negative, the
336
 * caller should examine e->negative.  Since the element 'e' may be
337
 * a reference to a named ACL or a nested ACL, a matching element
338
 * returned through 'matchelt' is not necessarily 'e' itself.
339
 */
340
341
bool
342
dns_aclelement_match(const isc_netaddr_t *reqaddr, const dns_name_t *reqsigner,
343
         const dns_aclelement_t *e, dns_aclenv_t *env,
344
0
         const dns_aclelement_t **matchelt) {
345
0
  dns_acl_t *inner = NULL;
346
0
  int indirectmatch;
347
0
  isc_result_t result;
348
349
0
  switch (e->type) {
350
0
  case dns_aclelementtype_keyname:
351
0
    if (reqsigner != NULL && dns_name_equal(reqsigner, &e->keyname))
352
0
    {
353
0
      if (matchelt != NULL) {
354
0
        *matchelt = e;
355
0
      }
356
0
      return true;
357
0
    } else {
358
0
      return false;
359
0
    }
360
361
0
  case dns_aclelementtype_nestedacl:
362
0
    dns_acl_attach(e->nestedacl, &inner);
363
0
    break;
364
365
0
  case dns_aclelementtype_localhost:
366
0
    if (env == NULL) {
367
0
      return false;
368
0
    }
369
0
    rcu_read_lock();
370
0
    dns_acl_attach(rcu_dereference(env->localhost), &inner);
371
0
    rcu_read_unlock();
372
0
    break;
373
374
0
  case dns_aclelementtype_localnets:
375
0
    if (env == NULL) {
376
0
      return false;
377
0
    }
378
0
    rcu_read_lock();
379
0
    dns_acl_attach(rcu_dereference(env->localnets), &inner);
380
0
    rcu_read_unlock();
381
0
    break;
382
383
#if defined(HAVE_GEOIP2)
384
  case dns_aclelementtype_geoip:
385
    if (env == NULL || env->geoip == NULL) {
386
      return false;
387
    }
388
    return dns_geoip_match(reqaddr, env->geoip, &e->geoip_elem);
389
#endif /* if defined(HAVE_GEOIP2) */
390
0
  default:
391
0
    UNREACHABLE();
392
0
  }
393
394
0
  result = dns_acl_match(reqaddr, reqsigner, inner, env, &indirectmatch,
395
0
             matchelt);
396
0
  INSIST(result == ISC_R_SUCCESS);
397
398
0
  dns_acl_detach(&inner);
399
400
  /*
401
   * Treat negative matches in indirect ACLs as "no match".
402
   * That way, a negated indirect ACL will never become a
403
   * surprise positive match through double negation.
404
   * XXXDCL this should be documented.
405
   */
406
0
  if (indirectmatch > 0) {
407
0
    if (matchelt != NULL) {
408
0
      *matchelt = e;
409
0
    }
410
0
    return true;
411
0
  }
412
413
  /*
414
   * A negative indirect match may have set *matchelt, but we don't
415
   * want it set when we return.
416
   */
417
0
  if (matchelt != NULL) {
418
0
    *matchelt = NULL;
419
0
  }
420
421
0
  return false;
422
0
}
423
424
static void
425
0
dns__acl_destroy_port_transports(dns_acl_t *acl) {
426
0
  ISC_LIST_FOREACH(acl->ports_and_transports, port_proto, link) {
427
0
    ISC_LIST_DEQUEUE(acl->ports_and_transports, port_proto, link);
428
0
    isc_mem_put(acl->mctx, port_proto, sizeof(*port_proto));
429
0
  }
430
0
}
431
432
static void
433
0
dns__acl_destroy(dns_acl_t *dacl) {
434
0
  INSIST(!ISC_LINK_LINKED(dacl, nextincache));
435
436
0
  isc_refcount_destroy(&dacl->references);
437
0
  dacl->magic = 0;
438
439
0
  for (size_t i = 0; i < dacl->length; i++) {
440
0
    dns_aclelement_t *de = &dacl->elements[i];
441
0
    if (de->type == dns_aclelementtype_keyname) {
442
0
      dns_name_free(&de->keyname, dacl->mctx);
443
0
    } else if (de->type == dns_aclelementtype_nestedacl) {
444
0
      dns_acl_detach(&de->nestedacl);
445
0
    }
446
0
  }
447
0
  if (dacl->elements != NULL) {
448
0
    isc_mem_cput(dacl->mctx, dacl->elements, dacl->alloc,
449
0
           sizeof(dacl->elements[0]));
450
0
  }
451
0
  if (dacl->name != NULL) {
452
0
    isc_mem_free(dacl->mctx, dacl->name);
453
0
  }
454
0
  if (dacl->iptable != NULL) {
455
0
    dns_iptable_detach(&dacl->iptable);
456
0
  }
457
458
0
  dns__acl_destroy_port_transports(dacl);
459
460
0
  isc_mem_putanddetach(&dacl->mctx, dacl, sizeof(*dacl));
461
0
}
462
463
#if DNS_ACL_TRACE
464
ISC_REFCOUNT_TRACE_IMPL(dns_acl, dns__acl_destroy);
465
#else
466
0
ISC_REFCOUNT_IMPL(dns_acl, dns__acl_destroy);
Unexecuted instantiation: dns_acl_ref
Unexecuted instantiation: dns_acl_unref
Unexecuted instantiation: dns_acl_detach
467
0
#endif
468
0
469
0
void
470
22
dns__acl_initialize(void) {}
471
472
void
473
0
dns__acl_shutdown(void) {}
474
475
/*
476
 * Check whether a radix node represents an insecure prefix
477
 * (non-negated, non-loopback).
478
 */
479
static void
480
0
check_insecure(isc_radix_node_t *node, void *arg) {
481
0
  bool *found = arg;
482
0
  isc_prefix_t *prefix = &node->prefix;
483
484
  /*
485
   * If all nonexistent or negative then this node is secure.
486
   */
487
0
  if (node->match[RADIX_V4] != RADIX_ALLOW &&
488
0
      node->match[RADIX_V6] != RADIX_ALLOW)
489
0
  {
490
0
    return;
491
0
  }
492
493
  /*
494
   * If a loopback address found and the other family
495
   * entry doesn't exist or is negative, return.
496
   */
497
0
  if (prefix->bitlen == 32 &&
498
0
      htonl(prefix->add.sin.s_addr) == INADDR_LOOPBACK &&
499
0
      node->match[RADIX_V6] != RADIX_ALLOW)
500
0
  {
501
0
    return;
502
0
  }
503
504
0
  if (prefix->bitlen == 128 && IN6_IS_ADDR_LOOPBACK(&prefix->add.sin6) &&
505
0
      node->match[RADIX_V4] != RADIX_ALLOW)
506
0
  {
507
0
    return;
508
0
  }
509
510
  /* Non-negated, non-loopback */
511
0
  *found = true;
512
0
}
513
514
/*
515
 * Return true iff the acl 'a' is considered insecure, that is,
516
 * if it contains IP addresses other than those of the local host.
517
 * This is intended for applications such as printing warning
518
 * messages for suspect ACLs; it is not intended for making access
519
 * control decisions.  We make no guarantee that an ACL for which
520
 * this function returns false is safe.
521
 */
522
bool
523
0
dns_acl_isinsecure(const dns_acl_t *a) {
524
0
  unsigned int i;
525
0
  bool insecure;
526
527
  /*
528
   * Walk radix tree to find out if there are any non-negated,
529
   * non-loopback prefixes.
530
   */
531
0
  insecure = false;
532
0
  isc_radix_foreach(a->iptable->radix, check_insecure, &insecure);
533
0
  if (insecure) {
534
0
    return true;
535
0
  }
536
537
  /* Now check non-radix elements */
538
0
  for (i = 0; i < a->length; i++) {
539
0
    dns_aclelement_t *e = &a->elements[i];
540
541
    /* A negated match can never be insecure. */
542
0
    if (e->negative) {
543
0
      continue;
544
0
    }
545
546
0
    switch (e->type) {
547
0
    case dns_aclelementtype_keyname:
548
0
    case dns_aclelementtype_localhost:
549
0
      continue;
550
551
0
    case dns_aclelementtype_nestedacl:
552
0
      if (dns_acl_isinsecure(e->nestedacl)) {
553
0
        return true;
554
0
      }
555
0
      continue;
556
557
#if defined(HAVE_GEOIP2)
558
    case dns_aclelementtype_geoip:
559
#endif /* if defined(HAVE_GEOIP2) */
560
0
    case dns_aclelementtype_localnets:
561
0
      return true;
562
563
0
    default:
564
0
      UNREACHABLE();
565
0
    }
566
0
  }
567
568
  /* No insecure elements were found. */
569
0
  return false;
570
0
}
571
572
/*%
573
 * Check whether an address/signer is allowed by a given acl/aclenv.
574
 */
575
bool
576
dns_acl_allowed(isc_netaddr_t *addr, const dns_name_t *signer, dns_acl_t *acl,
577
0
    dns_aclenv_t *aclenv) {
578
0
  int match;
579
0
  isc_result_t result;
580
581
0
  if (acl == NULL) {
582
0
    return true;
583
0
  }
584
0
  result = dns_acl_match(addr, signer, acl, aclenv, &match, NULL);
585
0
  if (result == ISC_R_SUCCESS && match > 0) {
586
0
    return true;
587
0
  }
588
0
  return false;
589
0
}
590
591
/*
592
 * Initialize ACL environment, setting up localhost and localnets ACLs
593
 */
594
void
595
2
dns_aclenv_create(isc_mem_t *mctx, dns_aclenv_t **envp) {
596
2
  dns_aclenv_t *env = isc_mem_get(mctx, sizeof(*env));
597
2
  *env = (dns_aclenv_t){
598
2
    .references = ISC_REFCOUNT_INITIALIZER(1),
599
2
    .magic = DNS_ACLENV_MAGIC,
600
2
  };
601
602
2
  isc_mem_attach(mctx, &env->mctx);
603
2
  isc_refcount_init(&env->references, 1);
604
605
2
  dns_acl_create(mctx, 0, &env->localhost);
606
2
  dns_acl_create(mctx, 0, &env->localnets);
607
608
2
  *envp = env;
609
2
}
610
611
void
612
0
dns_aclenv_set(dns_aclenv_t *env, dns_acl_t *localhost, dns_acl_t *localnets) {
613
0
  REQUIRE(VALID_ACLENV(env));
614
0
  REQUIRE(DNS_ACL_VALID(localhost));
615
0
  REQUIRE(DNS_ACL_VALID(localnets));
616
617
0
  localhost = rcu_xchg_pointer(&env->localhost, dns_acl_ref(localhost));
618
0
  localnets = rcu_xchg_pointer(&env->localnets, dns_acl_ref(localnets));
619
620
  /*
621
   * This function is called only during interface scanning, so blocking
622
   * a bit is acceptable. Wait until all ongoing attachments to old
623
   * 'localhost' and 'localnets' are finished before we can detach and
624
   * possibly destroy them.
625
   *
626
   * The problem here isn't the memory reclamation per se, but
627
   * the reference counting race - we need to wait for the
628
   * critical section to end before we decrement the value and
629
   * possibly destroy the acl objects.
630
   */
631
0
  synchronize_rcu();
632
633
0
  dns_acl_detach(&localhost);
634
0
  dns_acl_detach(&localnets);
635
0
}
636
637
void
638
0
dns_aclenv_copy(dns_aclenv_t *target, dns_aclenv_t *source) {
639
0
  REQUIRE(VALID_ACLENV(source));
640
0
  REQUIRE(VALID_ACLENV(target));
641
642
0
  rcu_read_lock();
643
644
  /*
645
   * We need to acquire the reference inside the critical section.
646
   */
647
648
0
  dns_acl_t *localhost = dns_acl_ref(rcu_dereference(source->localhost));
649
0
  INSIST(DNS_ACL_VALID(localhost));
650
651
0
  dns_acl_t *localnets = dns_acl_ref(rcu_dereference(source->localnets));
652
0
  INSIST(DNS_ACL_VALID(localnets));
653
654
0
  rcu_read_unlock();
655
656
0
  localhost = rcu_xchg_pointer(&target->localhost, localhost);
657
0
  localnets = rcu_xchg_pointer(&target->localnets, localnets);
658
659
  /*
660
   * This function is called only during (re)configuration, so blocking
661
   * a bit is acceptable.
662
   *
663
   * See the comment above in dns_aclenv_set() for more detail.
664
   */
665
0
  synchronize_rcu();
666
667
0
  target->match_mapped = source->match_mapped;
668
#if defined(HAVE_GEOIP2)
669
  target->geoip = source->geoip;
670
#endif /* if defined(HAVE_GEOIP2) */
671
672
0
  dns_acl_detach(&localhost);
673
0
  dns_acl_detach(&localnets);
674
0
}
675
676
static void
677
0
dns__aclenv_destroy(dns_aclenv_t *aclenv) {
678
0
  REQUIRE(VALID_ACLENV(aclenv));
679
680
0
  aclenv->magic = 0;
681
682
  /*
683
   * The last reference to the aclenv has been detached, so nobody should
684
   * be reading from this aclenv.  We can destroy the localhost and
685
   * localnet directly without swapping the pointers.
686
   */
687
688
0
  dns_acl_detach(&aclenv->localhost);
689
0
  dns_acl_detach(&aclenv->localnets);
690
691
0
  isc_mem_putanddetach(&aclenv->mctx, aclenv, sizeof(*aclenv));
692
0
}
693
694
#if DNS_ACL_TRACE
695
ISC_REFCOUNT_TRACE_IMPL(dns_aclenv, dns__aclenv_destroy);
696
#else
697
0
ISC_REFCOUNT_IMPL(dns_aclenv, dns__aclenv_destroy);
Unexecuted instantiation: dns_aclenv_ref
Unexecuted instantiation: dns_aclenv_unref
Unexecuted instantiation: dns_aclenv_detach
698
0
#endif
699
0
700
0
void
701
0
dns_acl_add_port_transports(dns_acl_t *acl, const in_port_t port,
702
0
          const uint32_t transports, const bool encrypted,
703
0
          const bool negative) {
704
0
  dns_acl_port_transports_t *port_proto;
705
0
  REQUIRE(DNS_ACL_VALID(acl));
706
0
  REQUIRE(port != 0 || transports != 0);
707
708
0
  port_proto = isc_mem_get(acl->mctx, sizeof(*port_proto));
709
0
  *port_proto = (dns_acl_port_transports_t){ .port = port,
710
0
               .transports = transports,
711
0
               .encrypted = encrypted,
712
0
               .negative = negative };
713
714
0
  ISC_LINK_INIT(port_proto, link);
715
716
0
  ISC_LIST_APPEND(acl->ports_and_transports, port_proto, link);
717
0
  acl->port_proto_entries++;
718
0
}
719
720
void
721
0
dns_acl_merge_ports_transports(dns_acl_t *dest, dns_acl_t *source, bool pos) {
722
0
  REQUIRE(DNS_ACL_VALID(dest));
723
0
  REQUIRE(DNS_ACL_VALID(source));
724
725
0
  const bool negative = !pos;
726
727
  /*
728
   * Merge ports and transports
729
   */
730
0
  ISC_LIST_FOREACH(source->ports_and_transports, next, link) {
731
0
    const bool next_positive = !next->negative;
732
0
    bool add_negative;
733
734
    /*
735
     * Reverse sense of positives if this is a negative acl.  The
736
     * logic is used (and, thus, enforced) by dns_acl_merge(),
737
     * from which dns_acl_merge_ports_transports() is called.
738
     */
739
0
    if (negative && next_positive) {
740
0
      add_negative = true;
741
0
    } else {
742
0
      add_negative = next->negative;
743
0
    }
744
745
0
    dns_acl_add_port_transports(dest, next->port, next->transports,
746
0
              next->encrypted, add_negative);
747
0
  }
748
0
}