Home | History | Annotate | Line # | Download | only in dns
      1 /*	$NetBSD: acl.h,v 1.10 2025/01/26 16:25:26 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  *
      6  * SPDX-License-Identifier: MPL-2.0
      7  *
      8  * This Source Code Form is subject to the terms of the Mozilla Public
      9  * License, v. 2.0. If a copy of the MPL was not distributed with this
     10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11  *
     12  * See the COPYRIGHT file distributed with this work for additional
     13  * information regarding copyright ownership.
     14  */
     15 
     16 #pragma once
     17 
     18 /*****
     19 ***** Module Info
     20 *****/
     21 
     22 /*! \file dns/acl.h
     23  * \brief
     24  * Address match list handling.
     25  */
     26 
     27 /* Add -DDNS_ACL_TRACE=1 to CFLAGS for detailed reference tracing */
     28 
     29 /***
     30  *** Imports
     31  ***/
     32 
     33 #include <stdbool.h>
     34 
     35 #include <isc/lang.h>
     36 #include <isc/magic.h>
     37 #include <isc/netaddr.h>
     38 #include <isc/refcount.h>
     39 
     40 #include <dns/geoip.h>
     41 #include <dns/iptable.h>
     42 #include <dns/name.h>
     43 #include <dns/types.h>
     44 
     45 /***
     46  *** Types
     47  ***/
     48 
     49 typedef enum {
     50 	dns_aclelementtype_keyname,
     51 	dns_aclelementtype_nestedacl,
     52 	dns_aclelementtype_localhost,
     53 	dns_aclelementtype_localnets,
     54 #if defined(HAVE_GEOIP2)
     55 	dns_aclelementtype_geoip
     56 #endif /* HAVE_GEOIP2 */
     57 } dns_aclelementtype_t;
     58 
     59 typedef struct dns_acl_port_transports {
     60 	in_port_t port;
     61 	uint32_t  transports;
     62 	bool encrypted; /* for protocols with optional encryption (e.g. HTTP) */
     63 	bool negative;
     64 	ISC_LINK(struct dns_acl_port_transports) link;
     65 } dns_acl_port_transports_t;
     66 
     67 typedef struct dns_aclipprefix dns_aclipprefix_t;
     68 
     69 struct dns_aclipprefix {
     70 	isc_netaddr_t address; /* IP4/IP6 */
     71 	unsigned int  prefixlen;
     72 };
     73 
     74 struct dns_aclelement {
     75 	dns_aclelementtype_t type;
     76 	bool		     negative;
     77 	dns_name_t	     keyname;
     78 #if defined(HAVE_GEOIP2)
     79 	dns_geoip_elem_t geoip_elem;
     80 #endif /* HAVE_GEOIP2 */
     81 	dns_acl_t *nestedacl;
     82 	int	   node_num;
     83 };
     84 
     85 #define dns_acl_node_count(acl) acl->iptable->radix->num_added_node
     86 
     87 struct dns_acl {
     88 	unsigned int	  magic;
     89 	isc_mem_t	 *mctx;
     90 	isc_refcount_t	  references;
     91 	dns_iptable_t	 *iptable;
     92 	dns_aclelement_t *elements;
     93 	bool		  has_negatives;
     94 	unsigned int	  alloc;	 /*%< Elements allocated */
     95 	unsigned int	  length;	 /*%< Elements initialized */
     96 	char		 *name;		 /*%< Temporary use only */
     97 	ISC_LINK(dns_acl_t) nextincache; /*%< Ditto */
     98 	ISC_LIST(dns_acl_port_transports_t) ports_and_transports;
     99 	size_t port_proto_entries;
    100 };
    101 
    102 struct dns_aclenv {
    103 	unsigned int   magic;
    104 	isc_mem_t     *mctx;
    105 	isc_refcount_t references;
    106 
    107 	dns_acl_t *localhost;
    108 	dns_acl_t *localnets;
    109 
    110 	bool match_mapped;
    111 #if defined(HAVE_GEOIP2)
    112 	dns_geoip_databases_t *geoip;
    113 #endif /* HAVE_GEOIP2 */
    114 };
    115 
    116 #define DNS_ACL_MAGIC	 ISC_MAGIC('D', 'a', 'c', 'l')
    117 #define DNS_ACL_VALID(a) ISC_MAGIC_VALID(a, DNS_ACL_MAGIC)
    118 
    119 /***
    120  *** Functions
    121  ***/
    122 
    123 ISC_LANG_BEGINDECLS
    124 
    125 void
    126 dns_acl_create(isc_mem_t *mctx, int n, dns_acl_t **target);
    127 /*%<
    128  * Create a new ACL, including an IP table and an array with room
    129  * for 'n' ACL elements.  The elements are uninitialized and the
    130  * length is 0.
    131  */
    132 
    133 isc_result_t
    134 dns_acl_any(isc_mem_t *mctx, dns_acl_t **target);
    135 /*%<
    136  * Create a new ACL that matches everything.
    137  */
    138 
    139 isc_result_t
    140 dns_acl_none(isc_mem_t *mctx, dns_acl_t **target);
    141 /*%<
    142  * Create a new ACL that matches nothing.
    143  */
    144 
    145 bool
    146 dns_acl_isany(dns_acl_t *acl);
    147 /*%<
    148  * Test whether ACL is set to "{ any; }"
    149  */
    150 
    151 bool
    152 dns_acl_isnone(dns_acl_t *acl);
    153 /*%<
    154  * Test whether ACL is set to "{ none; }"
    155  */
    156 
    157 isc_result_t
    158 dns_acl_merge(dns_acl_t *dest, dns_acl_t *source, bool pos);
    159 /*%<
    160  * Merge the contents of one ACL into another.  Call dns_iptable_merge()
    161  * for the IP tables, then concatenate the element arrays.
    162  *
    163  * If pos is set to false, then the nested ACL is to be negated.  This
    164  * means reverse the sense of each *positive* element or IP table node,
    165  * but leave negatives alone, so as to prevent a double-negative causing
    166  * an unexpected positive match in the parent ACL.
    167  */
    168 
    169 #if DNS_ACL_TRACE
    170 #define dns_acl_ref(ptr)   dns_acl__ref(ptr, __func__, __FILE__, __LINE__)
    171 #define dns_acl_unref(ptr) dns_acl__unref(ptr, __func__, __FILE__, __LINE__)
    172 #define dns_acl_attach(ptr, ptrp) \
    173 	dns_acl__attach(ptr, ptrp, __func__, __FILE__, __LINE__)
    174 #define dns_acl_detach(ptrp) dns_acl__detach(ptrp, __func__, __FILE__, __LINE__)
    175 ISC_REFCOUNT_TRACE_DECL(dns_acl);
    176 #else
    177 ISC_REFCOUNT_DECL(dns_acl);
    178 #endif
    179 
    180 bool
    181 dns_acl_isinsecure(const dns_acl_t *a);
    182 /*%<
    183  * Return #true iff the acl 'a' is considered insecure, that is,
    184  * if it contains IP addresses other than those of the local host.
    185  * This is intended for applications such as printing warning
    186  * messages for suspect ACLs; it is not intended for making access
    187  * control decisions.  We make no guarantee that an ACL for which
    188  * this function returns #false is safe.
    189  */
    190 
    191 bool
    192 dns_acl_allowed(isc_netaddr_t *addr, const dns_name_t *signer, dns_acl_t *acl,
    193 		dns_aclenv_t *aclenv);
    194 /*%<
    195  * Return #true iff the 'addr', 'signer', or ECS values are
    196  * permitted by 'acl' in environment 'aclenv'.
    197  */
    198 
    199 void
    200 dns_aclenv_create(isc_mem_t *mctx, dns_aclenv_t **envp);
    201 /*%<
    202  * Create ACL environment, setting up localhost and localnets ACLs
    203  */
    204 
    205 void
    206 dns_aclenv_copy(dns_aclenv_t *t, dns_aclenv_t *s);
    207 /*%<
    208  * Copy the ACLs from one ACL environment object to another.
    209  *
    210  * Requires:
    211  *\li	both 's' and 't' are valid ACL environments.
    212  */
    213 
    214 void
    215 dns_aclenv_set(dns_aclenv_t *env, dns_acl_t *localhost, dns_acl_t *localnets);
    216 /*%<
    217  * Attach the 'localhost' and 'localnets' arguments to 'env' ACL environment
    218  */
    219 
    220 #if DNS_ACL_TRACE
    221 #define dns_aclenv_ref(ptr) dns_aclenv__ref(ptr, __func__, __FILE__, __LINE__)
    222 #define dns_aclenv_unref(ptr) \
    223 	dns_aclenv__unref(ptr, __func__, __FILE__, __LINE__)
    224 #define dns_aclenv_attach(ptr, ptrp) \
    225 	dns_aclenv__attach(ptr, ptrp, __func__, __FILE__, __LINE__)
    226 #define dns_aclenv_detach(ptrp) \
    227 	dns_aclenv__detach(ptrp, __func__, __FILE__, __LINE__)
    228 ISC_REFCOUNT_TRACE_DECL(dns_aclenv);
    229 #else
    230 ISC_REFCOUNT_DECL(dns_aclenv);
    231 #endif
    232 
    233 isc_result_t
    234 dns_acl_match(const isc_netaddr_t *reqaddr, const dns_name_t *reqsigner,
    235 	      const dns_acl_t *acl, dns_aclenv_t *env, int *match,
    236 	      const dns_aclelement_t **matchelt);
    237 /*%<
    238  * General, low-level ACL matching.  This is expected to
    239  * be useful even for weird stuff like the topology and sortlist statements.
    240  *
    241  * Match the address 'reqaddr', and optionally the key name 'reqsigner',
    242  * against 'acl'.  'reqsigner' may be NULL.
    243  *
    244  * If there is a match, '*match' will be set to an integer whose absolute
    245  * value corresponds to the order in which the matching value was inserted
    246  * into the ACL.  For a positive match, this value will be positive; for a
    247  * negative match, it will be negative.
    248  *
    249  * If there is no match, *match will be set to zero.
    250  *
    251  * If there is a match in the element list (either positive or negative)
    252  * and 'matchelt' is non-NULL, *matchelt will be pointed to the matching
    253  * element.
    254  *
    255  * 'env' points to the current ACL environment, including the
    256  * current values of localhost and localnets and (if applicable)
    257  * the GeoIP context.
    258  *
    259  * Returns:
    260  *\li	#ISC_R_SUCCESS		Always succeeds.
    261  */
    262 
    263 bool
    264 dns_aclelement_match(const isc_netaddr_t *reqaddr, const dns_name_t *reqsigner,
    265 		     const dns_aclelement_t *e, dns_aclenv_t *env,
    266 		     const dns_aclelement_t **matchelt);
    267 /*%<
    268  * Like dns_acl_match, but matches against the single ACL element 'e'
    269  * rather than a complete ACL, and returns true iff it matched.
    270  *
    271  * To determine whether the match was positive or negative, the
    272  * caller should examine e->negative.  Since the element 'e' may be
    273  * a reference to a named ACL or a nested ACL, a matching element
    274  * returned through 'matchelt' is not necessarily 'e' itself.
    275  */
    276 
    277 isc_result_t
    278 dns_acl_match_port_transport(const isc_netaddr_t      *reqaddr,
    279 			     const in_port_t	       local_port,
    280 			     const isc_nmsocket_type_t transport,
    281 			     const bool encrypted, const dns_name_t *reqsigner,
    282 			     const dns_acl_t *acl, dns_aclenv_t *env,
    283 			     int *match, const dns_aclelement_t **matchelt);
    284 /*%<
    285  * Like dns_acl_match, but able to match the server port and
    286  * transport, as well as encryption status.
    287  *
    288  * Requires:
    289  *\li		'reqaddr' is not 'NULL';
    290  *\li		'acl' is a valid ACL object.
    291  */
    292 
    293 void
    294 dns_acl_add_port_transports(dns_acl_t *acl, const in_port_t port,
    295 			    const uint32_t transports, const bool encrypted,
    296 			    const bool negative);
    297 /*%<
    298  * Adds a "port-transports" entry to the specified ACL. Transports
    299  * are specified as a bit-set 'transports' consisting of entries
    300  * defined in the isc_nmsocket_type enumeration.
    301  *
    302  * Requires:
    303  *\li		'acl' is a valid ACL object;
    304  *\li		either 'port' or 'transports' is not equal to 0.
    305  */
    306 
    307 void
    308 dns_acl_merge_ports_transports(dns_acl_t *dest, dns_acl_t *source, bool pos);
    309 /*%<
    310  * Merges "port-transports" entries from the 'dest' ACL into
    311  * the 'source' ACL. The 'pos' parameter works in a way similar to
    312  * 'dns_acl_merge()'.
    313  *
    314  * Requires:
    315  *\li		'dest' is a valid ACL object;
    316  *\li		'source' is a valid ACL object.
    317  */
    318 
    319 ISC_LANG_ENDDECLS
    320