/contrib/bind9/lib/dns/acl.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 630 lines · 400 code · 94 blank · 136 comment · 128 complexity · 54f2b2a451fd0c783e1a14fad9e8d43e MD5 · raw file

  1. /*
  2. * Copyright (C) 2004-2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC")
  3. * Copyright (C) 1999-2002 Internet Software Consortium.
  4. *
  5. * Permission to use, copy, modify, and/or distribute this software for any
  6. * purpose with or without fee is hereby granted, provided that the above
  7. * copyright notice and this permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  10. * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  11. * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  12. * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  13. * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  14. * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  15. * PERFORMANCE OF THIS SOFTWARE.
  16. */
  17. /* $Id$ */
  18. /*! \file */
  19. #include <config.h>
  20. #include <isc/mem.h>
  21. #include <isc/once.h>
  22. #include <isc/string.h>
  23. #include <isc/util.h>
  24. #include <dns/acl.h>
  25. #include <dns/iptable.h>
  26. /*
  27. * Create a new ACL, including an IP table and an array with room
  28. * for 'n' ACL elements. The elements are uninitialized and the
  29. * length is 0.
  30. */
  31. isc_result_t
  32. dns_acl_create(isc_mem_t *mctx, int n, dns_acl_t **target) {
  33. isc_result_t result;
  34. dns_acl_t *acl;
  35. /*
  36. * Work around silly limitation of isc_mem_get().
  37. */
  38. if (n == 0)
  39. n = 1;
  40. acl = isc_mem_get(mctx, sizeof(*acl));
  41. if (acl == NULL)
  42. return (ISC_R_NOMEMORY);
  43. acl->mctx = mctx;
  44. acl->name = NULL;
  45. result = isc_refcount_init(&acl->refcount, 1);
  46. if (result != ISC_R_SUCCESS) {
  47. isc_mem_put(mctx, acl, sizeof(*acl));
  48. return (result);
  49. }
  50. result = dns_iptable_create(mctx, &acl->iptable);
  51. if (result != ISC_R_SUCCESS) {
  52. isc_mem_put(mctx, acl, sizeof(*acl));
  53. return (result);
  54. }
  55. acl->elements = NULL;
  56. acl->alloc = 0;
  57. acl->length = 0;
  58. acl->has_negatives = ISC_FALSE;
  59. ISC_LINK_INIT(acl, nextincache);
  60. /*
  61. * Must set magic early because we use dns_acl_detach() to clean up.
  62. */
  63. acl->magic = DNS_ACL_MAGIC;
  64. acl->elements = isc_mem_get(mctx, n * sizeof(dns_aclelement_t));
  65. if (acl->elements == NULL) {
  66. result = ISC_R_NOMEMORY;
  67. goto cleanup;
  68. }
  69. acl->alloc = n;
  70. memset(acl->elements, 0, n * sizeof(dns_aclelement_t));
  71. *target = acl;
  72. return (ISC_R_SUCCESS);
  73. cleanup:
  74. dns_acl_detach(&acl);
  75. return (result);
  76. }
  77. /*
  78. * Create a new ACL and initialize it with the value "any" or "none",
  79. * depending on the value of the "neg" parameter.
  80. * "any" is a positive iptable entry with bit length 0.
  81. * "none" is the same as "!any".
  82. */
  83. static isc_result_t
  84. dns_acl_anyornone(isc_mem_t *mctx, isc_boolean_t neg, dns_acl_t **target) {
  85. isc_result_t result;
  86. dns_acl_t *acl = NULL;
  87. result = dns_acl_create(mctx, 0, &acl);
  88. if (result != ISC_R_SUCCESS)
  89. return (result);
  90. result = dns_iptable_addprefix(acl->iptable, NULL, 0, ISC_TF(!neg));
  91. if (result != ISC_R_SUCCESS) {
  92. dns_acl_detach(&acl);
  93. return (result);
  94. }
  95. *target = acl;
  96. return (result);
  97. }
  98. /*
  99. * Create a new ACL that matches everything.
  100. */
  101. isc_result_t
  102. dns_acl_any(isc_mem_t *mctx, dns_acl_t **target) {
  103. return (dns_acl_anyornone(mctx, ISC_FALSE, target));
  104. }
  105. /*
  106. * Create a new ACL that matches nothing.
  107. */
  108. isc_result_t
  109. dns_acl_none(isc_mem_t *mctx, dns_acl_t **target) {
  110. return (dns_acl_anyornone(mctx, ISC_TRUE, target));
  111. }
  112. /*
  113. * If pos is ISC_TRUE, test whether acl is set to "{ any; }"
  114. * If pos is ISC_FALSE, test whether acl is set to "{ none; }"
  115. */
  116. static isc_boolean_t
  117. dns_acl_isanyornone(dns_acl_t *acl, isc_boolean_t pos)
  118. {
  119. /* Should never happen but let's be safe */
  120. if (acl == NULL ||
  121. acl->iptable == NULL ||
  122. acl->iptable->radix == NULL ||
  123. acl->iptable->radix->head == NULL ||
  124. acl->iptable->radix->head->prefix == NULL)
  125. return (ISC_FALSE);
  126. if (acl->length != 0 || acl->node_count != 1)
  127. return (ISC_FALSE);
  128. if (acl->iptable->radix->head->prefix->bitlen == 0 &&
  129. acl->iptable->radix->head->data[0] != NULL &&
  130. acl->iptable->radix->head->data[0] ==
  131. acl->iptable->radix->head->data[1] &&
  132. *(isc_boolean_t *) (acl->iptable->radix->head->data[0]) == pos)
  133. return (ISC_TRUE);
  134. return (ISC_FALSE); /* All others */
  135. }
  136. /*
  137. * Test whether acl is set to "{ any; }"
  138. */
  139. isc_boolean_t
  140. dns_acl_isany(dns_acl_t *acl)
  141. {
  142. return (dns_acl_isanyornone(acl, ISC_TRUE));
  143. }
  144. /*
  145. * Test whether acl is set to "{ none; }"
  146. */
  147. isc_boolean_t
  148. dns_acl_isnone(dns_acl_t *acl)
  149. {
  150. return (dns_acl_isanyornone(acl, ISC_FALSE));
  151. }
  152. /*
  153. * Determine whether a given address or signer matches a given ACL.
  154. * For a match with a positive ACL element or iptable radix entry,
  155. * return with a positive value in match; for a match with a negated ACL
  156. * element or radix entry, return with a negative value in match.
  157. */
  158. isc_result_t
  159. dns_acl_match(const isc_netaddr_t *reqaddr,
  160. const dns_name_t *reqsigner,
  161. const dns_acl_t *acl,
  162. const dns_aclenv_t *env,
  163. int *match,
  164. const dns_aclelement_t **matchelt)
  165. {
  166. isc_uint16_t bitlen, family;
  167. isc_prefix_t pfx;
  168. isc_radix_node_t *node = NULL;
  169. const isc_netaddr_t *addr;
  170. isc_netaddr_t v4addr;
  171. isc_result_t result;
  172. int match_num = -1;
  173. unsigned int i;
  174. REQUIRE(reqaddr != NULL);
  175. REQUIRE(matchelt == NULL || *matchelt == NULL);
  176. if (env == NULL || env->match_mapped == ISC_FALSE ||
  177. reqaddr->family != AF_INET6 ||
  178. !IN6_IS_ADDR_V4MAPPED(&reqaddr->type.in6))
  179. addr = reqaddr;
  180. else {
  181. isc_netaddr_fromv4mapped(&v4addr, reqaddr);
  182. addr = &v4addr;
  183. }
  184. /* Always match with host addresses. */
  185. family = addr->family;
  186. bitlen = family == AF_INET6 ? 128 : 32;
  187. NETADDR_TO_PREFIX_T(addr, pfx, bitlen);
  188. /* Assume no match. */
  189. *match = 0;
  190. /* Search radix. */
  191. result = isc_radix_search(acl->iptable->radix, &node, &pfx);
  192. /* Found a match. */
  193. if (result == ISC_R_SUCCESS && node != NULL) {
  194. match_num = node->node_num[ISC_IS6(family)];
  195. if (*(isc_boolean_t *) node->data[ISC_IS6(family)] == ISC_TRUE)
  196. *match = match_num;
  197. else
  198. *match = -match_num;
  199. }
  200. /* Now search non-radix elements for a match with a lower node_num. */
  201. for (i = 0; i < acl->length; i++) {
  202. dns_aclelement_t *e = &acl->elements[i];
  203. /* Already found a better match? */
  204. if (match_num != -1 && match_num < e->node_num) {
  205. isc_refcount_destroy(&pfx.refcount);
  206. return (ISC_R_SUCCESS);
  207. }
  208. if (dns_aclelement_match(reqaddr, reqsigner,
  209. e, env, matchelt)) {
  210. if (match_num == -1 || e->node_num < match_num) {
  211. if (e->negative == ISC_TRUE)
  212. *match = -e->node_num;
  213. else
  214. *match = e->node_num;
  215. }
  216. isc_refcount_destroy(&pfx.refcount);
  217. return (ISC_R_SUCCESS);
  218. }
  219. }
  220. isc_refcount_destroy(&pfx.refcount);
  221. return (ISC_R_SUCCESS);
  222. }
  223. /*
  224. * Merge the contents of one ACL into another. Call dns_iptable_merge()
  225. * for the IP tables, then concatenate the element arrays.
  226. *
  227. * If pos is set to false, then the nested ACL is to be negated. This
  228. * means reverse the sense of each *positive* element or IP table node,
  229. * but leave negatives alone, so as to prevent a double-negative causing
  230. * an unexpected positive match in the parent ACL.
  231. */
  232. isc_result_t
  233. dns_acl_merge(dns_acl_t *dest, dns_acl_t *source, isc_boolean_t pos)
  234. {
  235. isc_result_t result;
  236. unsigned int newalloc, nelem, i;
  237. int max_node = 0, nodes;
  238. /* Resize the element array if needed. */
  239. if (dest->length + source->length > dest->alloc) {
  240. void *newmem;
  241. newalloc = dest->alloc + source->alloc;
  242. if (newalloc < 4)
  243. newalloc = 4;
  244. newmem = isc_mem_get(dest->mctx,
  245. newalloc * sizeof(dns_aclelement_t));
  246. if (newmem == NULL)
  247. return (ISC_R_NOMEMORY);
  248. /* Copy in the original elements */
  249. memcpy(newmem, dest->elements,
  250. dest->length * sizeof(dns_aclelement_t));
  251. /* Release the memory for the old elements array */
  252. isc_mem_put(dest->mctx, dest->elements,
  253. dest->alloc * sizeof(dns_aclelement_t));
  254. dest->elements = newmem;
  255. dest->alloc = newalloc;
  256. }
  257. /*
  258. * Now copy in the new elements, increasing their node_num
  259. * values so as to keep the new ACL consistent. If we're
  260. * negating, then negate positive elements, but keep negative
  261. * elements the same for security reasons.
  262. */
  263. nelem = dest->length;
  264. dest->length += source->length;
  265. for (i = 0; i < source->length; i++) {
  266. if (source->elements[i].node_num > max_node)
  267. max_node = source->elements[i].node_num;
  268. /* Copy type. */
  269. dest->elements[nelem + i].type = source->elements[i].type;
  270. /* Adjust node numbering. */
  271. dest->elements[nelem + i].node_num =
  272. source->elements[i].node_num + dest->node_count;
  273. /* Duplicate nested acl. */
  274. if (source->elements[i].type == dns_aclelementtype_nestedacl &&
  275. source->elements[i].nestedacl != NULL)
  276. dns_acl_attach(source->elements[i].nestedacl,
  277. &dest->elements[nelem + i].nestedacl);
  278. /* Duplicate key name. */
  279. if (source->elements[i].type == dns_aclelementtype_keyname) {
  280. dns_name_init(&dest->elements[nelem+i].keyname, NULL);
  281. result = dns_name_dup(&source->elements[i].keyname,
  282. dest->mctx,
  283. &dest->elements[nelem+i].keyname);
  284. if (result != ISC_R_SUCCESS)
  285. return result;
  286. }
  287. /* reverse sense of positives if this is a negative acl */
  288. if (!pos && source->elements[i].negative == ISC_FALSE) {
  289. dest->elements[nelem + i].negative = ISC_TRUE;
  290. } else {
  291. dest->elements[nelem + i].negative =
  292. source->elements[i].negative;
  293. }
  294. }
  295. /*
  296. * Merge the iptables. Make sure the destination ACL's
  297. * node_count value is set correctly afterward.
  298. */
  299. nodes = max_node + dest->node_count;
  300. result = dns_iptable_merge(dest->iptable, source->iptable, pos);
  301. if (result != ISC_R_SUCCESS)
  302. return (result);
  303. if (nodes > dest->node_count)
  304. dest->node_count = nodes;
  305. return (ISC_R_SUCCESS);
  306. }
  307. /*
  308. * Like dns_acl_match, but matches against the single ACL element 'e'
  309. * rather than a complete ACL, and returns ISC_TRUE iff it matched.
  310. *
  311. * To determine whether the match was positive or negative, the
  312. * caller should examine e->negative. Since the element 'e' may be
  313. * a reference to a named ACL or a nested ACL, a matching element
  314. * returned through 'matchelt' is not necessarily 'e' itself.
  315. */
  316. isc_boolean_t
  317. dns_aclelement_match(const isc_netaddr_t *reqaddr,
  318. const dns_name_t *reqsigner,
  319. const dns_aclelement_t *e,
  320. const dns_aclenv_t *env,
  321. const dns_aclelement_t **matchelt)
  322. {
  323. dns_acl_t *inner = NULL;
  324. int indirectmatch;
  325. isc_result_t result;
  326. switch (e->type) {
  327. case dns_aclelementtype_keyname:
  328. if (reqsigner != NULL &&
  329. dns_name_equal(reqsigner, &e->keyname)) {
  330. if (matchelt != NULL)
  331. *matchelt = e;
  332. return (ISC_TRUE);
  333. } else {
  334. return (ISC_FALSE);
  335. }
  336. case dns_aclelementtype_nestedacl:
  337. inner = e->nestedacl;
  338. break;
  339. case dns_aclelementtype_localhost:
  340. if (env == NULL || env->localhost == NULL)
  341. return (ISC_FALSE);
  342. inner = env->localhost;
  343. break;
  344. case dns_aclelementtype_localnets:
  345. if (env == NULL || env->localnets == NULL)
  346. return (ISC_FALSE);
  347. inner = env->localnets;
  348. break;
  349. default:
  350. /* Should be impossible. */
  351. INSIST(0);
  352. }
  353. result = dns_acl_match(reqaddr, reqsigner, inner, env,
  354. &indirectmatch, matchelt);
  355. INSIST(result == ISC_R_SUCCESS);
  356. /*
  357. * Treat negative matches in indirect ACLs as "no match".
  358. * That way, a negated indirect ACL will never become a
  359. * surprise positive match through double negation.
  360. * XXXDCL this should be documented.
  361. */
  362. if (indirectmatch > 0) {
  363. if (matchelt != NULL)
  364. *matchelt = e;
  365. return (ISC_TRUE);
  366. }
  367. /*
  368. * A negative indirect match may have set *matchelt, but we don't
  369. * want it set when we return.
  370. */
  371. if (matchelt != NULL)
  372. *matchelt = NULL;
  373. return (ISC_FALSE);
  374. }
  375. void
  376. dns_acl_attach(dns_acl_t *source, dns_acl_t **target) {
  377. REQUIRE(DNS_ACL_VALID(source));
  378. isc_refcount_increment(&source->refcount, NULL);
  379. *target = source;
  380. }
  381. static void
  382. destroy(dns_acl_t *dacl) {
  383. unsigned int i;
  384. INSIST(!ISC_LINK_LINKED(dacl, nextincache));
  385. for (i = 0; i < dacl->length; i++) {
  386. dns_aclelement_t *de = &dacl->elements[i];
  387. if (de->type == dns_aclelementtype_keyname) {
  388. dns_name_free(&de->keyname, dacl->mctx);
  389. } else if (de->type == dns_aclelementtype_nestedacl) {
  390. dns_acl_detach(&de->nestedacl);
  391. }
  392. }
  393. if (dacl->elements != NULL)
  394. isc_mem_put(dacl->mctx, dacl->elements,
  395. dacl->alloc * sizeof(dns_aclelement_t));
  396. if (dacl->name != NULL)
  397. isc_mem_free(dacl->mctx, dacl->name);
  398. if (dacl->iptable != NULL)
  399. dns_iptable_detach(&dacl->iptable);
  400. isc_refcount_destroy(&dacl->refcount);
  401. dacl->magic = 0;
  402. isc_mem_put(dacl->mctx, dacl, sizeof(*dacl));
  403. }
  404. void
  405. dns_acl_detach(dns_acl_t **aclp) {
  406. dns_acl_t *acl = *aclp;
  407. unsigned int refs;
  408. REQUIRE(DNS_ACL_VALID(acl));
  409. isc_refcount_decrement(&acl->refcount, &refs);
  410. if (refs == 0)
  411. destroy(acl);
  412. *aclp = NULL;
  413. }
  414. static isc_once_t insecure_prefix_once = ISC_ONCE_INIT;
  415. static isc_mutex_t insecure_prefix_lock;
  416. static isc_boolean_t insecure_prefix_found;
  417. static void
  418. initialize_action(void) {
  419. RUNTIME_CHECK(isc_mutex_init(&insecure_prefix_lock) == ISC_R_SUCCESS);
  420. }
  421. /*
  422. * Called via isc_radix_walk() to find IP table nodes that are
  423. * insecure.
  424. */
  425. static void
  426. is_insecure(isc_prefix_t *prefix, void **data) {
  427. isc_boolean_t secure;
  428. int bitlen, family;
  429. bitlen = prefix->bitlen;
  430. family = prefix->family;
  431. /* Negated entries are always secure. */
  432. secure = * (isc_boolean_t *)data[ISC_IS6(family)];
  433. if (!secure) {
  434. return;
  435. }
  436. /* If loopback prefix found, return */
  437. switch (family) {
  438. case AF_INET:
  439. if (bitlen == 32 &&
  440. htonl(prefix->add.sin.s_addr) == INADDR_LOOPBACK)
  441. return;
  442. break;
  443. case AF_INET6:
  444. if (bitlen == 128 && IN6_IS_ADDR_LOOPBACK(&prefix->add.sin6))
  445. return;
  446. break;
  447. default:
  448. break;
  449. }
  450. /* Non-negated, non-loopback */
  451. insecure_prefix_found = ISC_TRUE; /* LOCKED */
  452. return;
  453. }
  454. /*
  455. * Return ISC_TRUE iff the acl 'a' is considered insecure, that is,
  456. * if it contains IP addresses other than those of the local host.
  457. * This is intended for applications such as printing warning
  458. * messages for suspect ACLs; it is not intended for making access
  459. * control decisions. We make no guarantee that an ACL for which
  460. * this function returns ISC_FALSE is safe.
  461. */
  462. isc_boolean_t
  463. dns_acl_isinsecure(const dns_acl_t *a) {
  464. unsigned int i;
  465. isc_boolean_t insecure;
  466. RUNTIME_CHECK(isc_once_do(&insecure_prefix_once,
  467. initialize_action) == ISC_R_SUCCESS);
  468. /*
  469. * Walk radix tree to find out if there are any non-negated,
  470. * non-loopback prefixes.
  471. */
  472. LOCK(&insecure_prefix_lock);
  473. insecure_prefix_found = ISC_FALSE;
  474. isc_radix_process(a->iptable->radix, is_insecure);
  475. insecure = insecure_prefix_found;
  476. UNLOCK(&insecure_prefix_lock);
  477. if (insecure)
  478. return(ISC_TRUE);
  479. /* Now check non-radix elements */
  480. for (i = 0; i < a->length; i++) {
  481. dns_aclelement_t *e = &a->elements[i];
  482. /* A negated match can never be insecure. */
  483. if (e->negative)
  484. continue;
  485. switch (e->type) {
  486. case dns_aclelementtype_keyname:
  487. case dns_aclelementtype_localhost:
  488. continue;
  489. case dns_aclelementtype_nestedacl:
  490. if (dns_acl_isinsecure(e->nestedacl))
  491. return (ISC_TRUE);
  492. continue;
  493. case dns_aclelementtype_localnets:
  494. return (ISC_TRUE);
  495. default:
  496. INSIST(0);
  497. return (ISC_TRUE);
  498. }
  499. }
  500. /* No insecure elements were found. */
  501. return (ISC_FALSE);
  502. }
  503. /*
  504. * Initialize ACL environment, setting up localhost and localnets ACLs
  505. */
  506. isc_result_t
  507. dns_aclenv_init(isc_mem_t *mctx, dns_aclenv_t *env) {
  508. isc_result_t result;
  509. env->localhost = NULL;
  510. env->localnets = NULL;
  511. result = dns_acl_create(mctx, 0, &env->localhost);
  512. if (result != ISC_R_SUCCESS)
  513. goto cleanup_nothing;
  514. result = dns_acl_create(mctx, 0, &env->localnets);
  515. if (result != ISC_R_SUCCESS)
  516. goto cleanup_localhost;
  517. env->match_mapped = ISC_FALSE;
  518. return (ISC_R_SUCCESS);
  519. cleanup_localhost:
  520. dns_acl_detach(&env->localhost);
  521. cleanup_nothing:
  522. return (result);
  523. }
  524. void
  525. dns_aclenv_copy(dns_aclenv_t *t, dns_aclenv_t *s) {
  526. dns_acl_detach(&t->localhost);
  527. dns_acl_attach(s->localhost, &t->localhost);
  528. dns_acl_detach(&t->localnets);
  529. dns_acl_attach(s->localnets, &t->localnets);
  530. t->match_mapped = s->match_mapped;
  531. }
  532. void
  533. dns_aclenv_destroy(dns_aclenv_t *env) {
  534. dns_acl_detach(&env->localhost);
  535. dns_acl_detach(&env->localnets);
  536. }