Coverage Report

Created: 2026-01-24 06:24

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