named_acl.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 2012, Digium, Inc.
  5. *
  6. * Mark Spencer <markster@digium.com>
  7. * Jonathan Rose <jrose@digium.com>
  8. *
  9. * See http://www.asterisk.org for more information about
  10. * the Asterisk project. Please do not directly contact
  11. * any of the maintainers of this project for assistance;
  12. * the project provides a web site, mailing lists and IRC
  13. * channels for your use.
  14. *
  15. * This program is free software, distributed under the terms of
  16. * the GNU General Public License Version 2. See the LICENSE file
  17. * at the top of the source tree.
  18. */
  19. /*! \file
  20. *
  21. * \brief Named Access Control Lists
  22. *
  23. * \author Jonathan Rose <jrose@digium.com>
  24. *
  25. * \note Based on a feature proposed by
  26. * Olle E. Johansson <oej@edvina.net>
  27. */
  28. #include "asterisk.h"
  29. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  30. #include "asterisk/config.h"
  31. #include "asterisk/config_options.h"
  32. #include "asterisk/utils.h"
  33. #include "asterisk/module.h"
  34. #include "asterisk/cli.h"
  35. #include "asterisk/acl.h"
  36. #include "asterisk/astobj2.h"
  37. #include "asterisk/paths.h"
  38. #include "asterisk/stasis.h"
  39. #include "asterisk/json.h"
  40. #include "asterisk/security_events.h"
  41. #define NACL_CONFIG "acl.conf"
  42. #define ACL_FAMILY "acls"
  43. /*** DOCUMENTATION
  44. <configInfo name="named_acl" language="en_US">
  45. <configFile name="named_acl.conf">
  46. <configObject name="named_acl">
  47. <synopsis>Options for configuring a named ACL</synopsis>
  48. <configOption name="permit">
  49. <synopsis>An address/subnet from which to allow access</synopsis>
  50. </configOption>
  51. <configOption name="deny">
  52. <synopsis>An address/subnet from which to disallow access</synopsis>
  53. </configOption>
  54. </configObject>
  55. </configFile>
  56. </configInfo>
  57. ***/
  58. /*
  59. * Configuration structure - holds pointers to ao2 containers used for configuration
  60. * Since there isn't a general level or any other special levels for acl.conf at this
  61. * time, it's really a config options friendly wrapper for the named ACL container
  62. */
  63. struct named_acl_config {
  64. struct ao2_container *named_acl_list;
  65. };
  66. static AO2_GLOBAL_OBJ_STATIC(globals);
  67. /*! \note These functions are used for placing/retrieving named ACLs in their ao2_container. */
  68. static void *named_acl_config_alloc(void);
  69. static void *named_acl_alloc(const char *cat);
  70. static void *named_acl_find(struct ao2_container *container, const char *cat);
  71. /* Config type for named ACL profiles (must not be named general) */
  72. static struct aco_type named_acl_type = {
  73. .type = ACO_ITEM, /*!< named_acls are items stored in containers, not individual global objects */
  74. .name = "named_acl",
  75. .category_match = ACO_BLACKLIST_EXACT,
  76. .category = "general", /*!< Match everything but "general" */
  77. .item_alloc = named_acl_alloc, /*!< A callback to allocate a new named_acl based on category */
  78. .item_find = named_acl_find, /*!< A callback to find a named_acl in some container of named_acls */
  79. .item_offset = offsetof(struct named_acl_config, named_acl_list), /*!< Could leave this out since 0 */
  80. };
  81. /* This array of aco_type structs is necessary to use aco_option_register */
  82. struct aco_type *named_acl_types[] = ACO_TYPES(&named_acl_type);
  83. struct aco_file named_acl_conf = {
  84. .filename = "acl.conf",
  85. .types = ACO_TYPES(&named_acl_type),
  86. };
  87. /* Create a config info struct that describes the config processing for named ACLs. */
  88. CONFIG_INFO_CORE("named_acl", cfg_info, globals, named_acl_config_alloc,
  89. .files = ACO_FILES(&named_acl_conf),
  90. );
  91. struct named_acl {
  92. struct ast_ha *ha;
  93. char name[ACL_NAME_LENGTH]; /* Same max length as a configuration category */
  94. };
  95. AO2_STRING_FIELD_HASH_FN(named_acl, name)
  96. AO2_STRING_FIELD_CMP_FN(named_acl, name)
  97. /*! \brief destructor for named_acl_config */
  98. static void named_acl_config_destructor(void *obj)
  99. {
  100. struct named_acl_config *cfg = obj;
  101. ao2_cleanup(cfg->named_acl_list);
  102. }
  103. /*! \brief allocator callback for named_acl_config. Notice it returns void * since it is used by
  104. * the backend config code
  105. */
  106. static void *named_acl_config_alloc(void)
  107. {
  108. struct named_acl_config *cfg;
  109. if (!(cfg = ao2_alloc(sizeof(*cfg), named_acl_config_destructor))) {
  110. return NULL;
  111. }
  112. cfg->named_acl_list = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, 37,
  113. named_acl_hash_fn, NULL, named_acl_cmp_fn);
  114. if (!cfg->named_acl_list) {
  115. goto error;
  116. }
  117. return cfg;
  118. error:
  119. ao2_ref(cfg, -1);
  120. return NULL;
  121. }
  122. /*! \brief Destroy a named ACL object */
  123. static void destroy_named_acl(void *obj)
  124. {
  125. struct named_acl *named_acl = obj;
  126. ast_free_ha(named_acl->ha);
  127. }
  128. /*!
  129. * \brief Create a named ACL structure
  130. *
  131. * \param cat name given to the ACL
  132. * \retval NULL failure
  133. *\retval non-NULL successfully allocated named ACL
  134. */
  135. static void *named_acl_alloc(const char *cat)
  136. {
  137. struct named_acl *named_acl;
  138. named_acl = ao2_alloc(sizeof(*named_acl), destroy_named_acl);
  139. if (!named_acl) {
  140. return NULL;
  141. }
  142. ast_copy_string(named_acl->name, cat, sizeof(named_acl->name));
  143. return named_acl;
  144. }
  145. /*!
  146. * \brief Find a named ACL in a container by its name
  147. *
  148. * \param container ao2container holding the named ACLs
  149. * \param cat name of the ACL wanted to be found
  150. * \retval pointer to the named ACL if available. Null if not found.
  151. */
  152. static void *named_acl_find(struct ao2_container *container, const char *cat)
  153. {
  154. struct named_acl tmp;
  155. ast_copy_string(tmp.name, cat, sizeof(tmp.name));
  156. return ao2_find(container, &tmp, OBJ_POINTER);
  157. }
  158. /*!
  159. * \internal
  160. * \brief Callback function to compare the ACL order of two given categories.
  161. * This function is used to sort lists of ACLs received from realtime.
  162. *
  163. * \param p first category being compared
  164. * \param q second category being compared
  165. *
  166. * \retval -1 (p < q)
  167. * \retval 0 (p == q)
  168. * \retval 1 (p > q)
  169. */
  170. static int acl_order_comparator(struct ast_category *p, struct ast_category *q)
  171. {
  172. int p_value = 0, q_value = 0;
  173. struct ast_variable *p_var = ast_category_first(p);
  174. struct ast_variable *q_var = ast_category_first(q);
  175. while (p_var) {
  176. if (!strcasecmp(p_var->name, "rule_order")) {
  177. p_value = atoi(p_var->value);
  178. break;
  179. }
  180. p_var = p_var->next;
  181. }
  182. while (q_var) {
  183. if (!strcasecmp(q_var->name, "rule_order")) {
  184. q_value = atoi(q_var->value);
  185. break;
  186. }
  187. q_var = q_var->next;
  188. }
  189. if (p_value < q_value) {
  190. return -1;
  191. } else if (q_value < p_value) {
  192. return 1;
  193. }
  194. return 0;
  195. }
  196. /*!
  197. * \internal
  198. * \brief Search for a named ACL via realtime Database and build the named_acl
  199. * if it is valid.
  200. *
  201. * \param name of the ACL wanted to be found
  202. * \retval pointer to the named ACL if available. Null if the ACL subsystem is unconfigured.
  203. */
  204. static struct named_acl *named_acl_find_realtime(const char *name)
  205. {
  206. struct ast_config *cfg;
  207. char *item = NULL;
  208. const char *systemname = NULL;
  209. struct ast_ha *built_ha = NULL;
  210. struct named_acl *acl;
  211. /* If we have a systemname set in the global options, we only want to retrieve entries with a matching systemname field. */
  212. systemname = ast_config_AST_SYSTEM_NAME;
  213. if (ast_strlen_zero(systemname)) {
  214. cfg = ast_load_realtime_multientry(ACL_FAMILY, "name", name, SENTINEL);
  215. } else {
  216. cfg = ast_load_realtime_multientry(ACL_FAMILY, "name", name, "systemname", systemname, SENTINEL);
  217. }
  218. if (!cfg) {
  219. return NULL;
  220. }
  221. /* At this point, the configuration must be sorted by the order field. */
  222. ast_config_sort_categories(cfg, 0, acl_order_comparator);
  223. while ((item = ast_category_browse(cfg, item))) {
  224. int append_ha_error = 0;
  225. const char *order = ast_variable_retrieve(cfg, item, "rule_order");
  226. const char *sense = ast_variable_retrieve(cfg, item, "sense");
  227. const char *rule = ast_variable_retrieve(cfg, item, "rule");
  228. built_ha = ast_append_ha(sense, rule, built_ha, &append_ha_error);
  229. if (append_ha_error) {
  230. /* We need to completely reject an ACL that contains any bad rules. */
  231. ast_log(LOG_ERROR, "Rejecting realtime ACL due to bad ACL definition '%s': %s - %s - %s\n", name, order, sense, rule);
  232. ast_free_ha(built_ha);
  233. return NULL;
  234. }
  235. }
  236. ast_config_destroy(cfg);
  237. acl = named_acl_alloc(name);
  238. if (!acl) {
  239. ast_log(LOG_ERROR, "allocation error\n");
  240. ast_free_ha(built_ha);
  241. return NULL;
  242. }
  243. acl->ha = built_ha;
  244. return acl;
  245. }
  246. struct ast_ha *ast_named_acl_find(const char *name, int *is_realtime, int *is_undefined)
  247. {
  248. struct ast_ha *ha = NULL;
  249. RAII_VAR(struct named_acl_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
  250. RAII_VAR(struct named_acl *, named_acl, NULL, ao2_cleanup);
  251. if (is_realtime) {
  252. *is_realtime = 0;
  253. }
  254. if (is_undefined) {
  255. *is_undefined = 0;
  256. }
  257. /* If the config or its named_acl_list hasn't been initialized, abort immediately. */
  258. if ((!cfg) || (!(cfg->named_acl_list))) {
  259. ast_log(LOG_ERROR, "Attempted to find named ACL '%s', but the ACL configuration isn't available.\n", name);
  260. return NULL;
  261. }
  262. named_acl = named_acl_find(cfg->named_acl_list, name);
  263. /* If a named ACL couldn't be retrieved locally, we need to try realtime storage. */
  264. if (!named_acl) {
  265. RAII_VAR(struct named_acl *, realtime_acl, NULL, ao2_cleanup);
  266. /* Attempt to create from realtime */
  267. if ((realtime_acl = named_acl_find_realtime(name))) {
  268. if (is_realtime) {
  269. *is_realtime = 1;
  270. }
  271. ha = ast_duplicate_ha_list(realtime_acl->ha);
  272. return ha;
  273. }
  274. /* Couldn't create from realtime. Raise relevant flags and print relevant warnings. */
  275. if (ast_realtime_is_mapping_defined(ACL_FAMILY) && !ast_check_realtime(ACL_FAMILY)) {
  276. ast_log(LOG_WARNING, "ACL '%s' does not exist. The ACL will be marked as undefined and will automatically fail if applied.\n"
  277. "This ACL may exist in the configured realtime backend, but that backend hasn't been registered yet. "
  278. "Fix this establishing preload for the backend in 'modules.conf'.\n", name);
  279. } else {
  280. ast_log(LOG_WARNING, "ACL '%s' does not exist. The ACL will be marked as undefined and will automatically fail if applied.\n", name);
  281. }
  282. if (is_undefined) {
  283. *is_undefined = 1;
  284. }
  285. return NULL;
  286. }
  287. ha = ast_duplicate_ha_list(named_acl->ha);
  288. if (!ha) {
  289. ast_log(LOG_NOTICE, "ACL '%s' contains no rules. It is valid, but it will accept addresses unconditionally.\n", name);
  290. }
  291. return ha;
  292. }
  293. /*! \brief Message type for named ACL changes */
  294. STASIS_MESSAGE_TYPE_DEFN(ast_named_acl_change_type);
  295. /*!
  296. * \internal
  297. * \brief Sends a stasis message corresponding to a given named ACL that has changed or
  298. * that all ACLs have been updated and old copies must be refreshed. Consumers of
  299. * named ACLs should subscribe to the ast_security_topic and respond to messages
  300. * of the ast_named_acl_change_type stasis message type in order to be able to
  301. * accommodate changes to named ACLs.
  302. *
  303. * \param name Name of the ACL that has changed. May be an empty string (but not NULL)
  304. * If name is an empty string, then all ACLs must be refreshed.
  305. *
  306. * \retval 0 success
  307. * \retval 1 failure
  308. */
  309. static int publish_acl_change(const char *name)
  310. {
  311. RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
  312. RAII_VAR(struct ast_json_payload *, json_payload, NULL, ao2_cleanup);
  313. RAII_VAR(struct ast_json *, json_object, ast_json_object_create(), ast_json_unref);
  314. if (!json_object || !ast_named_acl_change_type()) {
  315. goto publish_failure;
  316. }
  317. if (ast_json_object_set(json_object, "name", ast_json_string_create(name))) {
  318. goto publish_failure;
  319. }
  320. if (!(json_payload = ast_json_payload_create(json_object))) {
  321. goto publish_failure;
  322. }
  323. msg = stasis_message_create(ast_named_acl_change_type(), json_payload);
  324. if (!msg) {
  325. goto publish_failure;
  326. }
  327. stasis_publish(ast_security_topic(), msg);
  328. return 0;
  329. publish_failure:
  330. ast_log(LOG_ERROR, "Failed to issue ACL change message for %s.\n",
  331. ast_strlen_zero(name) ? "all named ACLs" : name);
  332. return -1;
  333. }
  334. /*!
  335. * \internal
  336. * \brief reload configuration for named ACLs
  337. *
  338. * \param fd file descriptor for CLI client
  339. */
  340. int ast_named_acl_reload(void)
  341. {
  342. enum aco_process_status status;
  343. status = aco_process_config(&cfg_info, 1);
  344. if (status == ACO_PROCESS_ERROR) {
  345. ast_log(LOG_WARNING, "Could not reload ACL config\n");
  346. return 0;
  347. }
  348. if (status == ACO_PROCESS_UNCHANGED) {
  349. /* We don't actually log anything if the config was unchanged,
  350. * but we don't need to send a config change event either.
  351. */
  352. return 0;
  353. }
  354. /* We need to push an ACL change event with no ACL name so that all subscribers update with all ACLs */
  355. publish_acl_change("");
  356. return 0;
  357. }
  358. /*!
  359. * \internal
  360. * \brief secondary handler for the 'acl show <name>' command (with arg)
  361. *
  362. * \param fd file descriptor of the cli
  363. * \name name of the ACL requested for display
  364. */
  365. static void cli_display_named_acl(int fd, const char *name)
  366. {
  367. struct ast_ha *ha;
  368. int ha_index = 0;
  369. int is_realtime = 0;
  370. RAII_VAR(struct named_acl_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
  371. RAII_VAR(struct named_acl *, named_acl, NULL, ao2_cleanup);
  372. /* If the configuration or the configuration's named_acl_list is unavailable, abort. */
  373. if ((!cfg) || (!cfg->named_acl_list)) {
  374. ast_log(LOG_ERROR, "Attempted to show named ACL '%s', but the acl configuration isn't available.\n", name);
  375. return;
  376. }
  377. named_acl = named_acl_find(cfg->named_acl_list, name);
  378. /* If the named_acl couldn't be found with the search, also abort. */
  379. if (!named_acl) {
  380. if (!(named_acl = named_acl_find_realtime(name))) {
  381. ast_cli(fd, "\nCould not find ACL named '%s'\n", name);
  382. return;
  383. }
  384. is_realtime = 1;
  385. }
  386. ast_cli(fd, "\nACL: %s%s\n---------------------------------------------\n", name, is_realtime ? " (realtime)" : "");
  387. for (ha = named_acl->ha; ha; ha = ha->next) {
  388. char *addr = ast_strdupa(ast_sockaddr_stringify_addr(&ha->addr));
  389. char *mask = ast_sockaddr_stringify_addr(&ha->netmask);
  390. ast_cli(fd, "%3d: %s - %s/%s\n", ha_index, ha->sense == AST_SENSE_ALLOW ? "allow" : " deny", addr, mask);
  391. ha_index++;
  392. }
  393. }
  394. /*!
  395. * \internal
  396. * \brief secondary handler for the 'acl show' command (no args)
  397. *
  398. * \param fd file descriptor of the cli
  399. */
  400. static void cli_display_named_acl_list(int fd)
  401. {
  402. struct ao2_iterator i;
  403. void *o;
  404. RAII_VAR(struct named_acl_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
  405. ast_cli(fd, "\nacl\n---\n");
  406. if (!cfg || !cfg->named_acl_list) {
  407. ast_cli(fd, "ACL configuration isn't available.\n");
  408. return;
  409. }
  410. i = ao2_iterator_init(cfg->named_acl_list, 0);
  411. while ((o = ao2_iterator_next(&i))) {
  412. struct named_acl *named_acl = o;
  413. ast_cli(fd, "%s\n", named_acl->name);
  414. ao2_ref(o, -1);
  415. }
  416. ao2_iterator_destroy(&i);
  417. }
  418. /* \brief ACL command show <name> */
  419. static char *handle_show_named_acl_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  420. {
  421. struct named_acl_config *cfg;
  422. int length;
  423. struct ao2_iterator i;
  424. struct named_acl *named_acl;
  425. switch (cmd) {
  426. case CLI_INIT:
  427. e->command = "acl show";
  428. e->usage =
  429. "Usage: acl show [name]\n"
  430. " Shows a list of named ACLs or lists all entries in a given named ACL.\n";
  431. return NULL;
  432. case CLI_GENERATE:
  433. if (a->pos != 2) {
  434. return NULL;
  435. }
  436. cfg = ao2_global_obj_ref(globals);
  437. if (!cfg) {
  438. return NULL;
  439. }
  440. length = strlen(a->word);
  441. i = ao2_iterator_init(cfg->named_acl_list, 0);
  442. while ((named_acl = ao2_iterator_next(&i))) {
  443. if (!strncasecmp(a->word, named_acl->name, length)) {
  444. if (ast_cli_completion_add(ast_strdup(named_acl->name))) {
  445. ao2_ref(named_acl, -1);
  446. break;
  447. }
  448. }
  449. ao2_ref(named_acl, -1);
  450. }
  451. ao2_iterator_destroy(&i);
  452. ao2_ref(cfg, -1);
  453. return NULL;
  454. }
  455. if (a->argc == 2) {
  456. cli_display_named_acl_list(a->fd);
  457. return CLI_SUCCESS;
  458. }
  459. if (a->argc == 3) {
  460. cli_display_named_acl(a->fd, a->argv[2]);
  461. return CLI_SUCCESS;
  462. }
  463. return CLI_SHOWUSAGE;
  464. }
  465. static struct ast_cli_entry cli_named_acl[] = {
  466. AST_CLI_DEFINE(handle_show_named_acl_cmd, "Show a named ACL or list all named ACLs"),
  467. };
  468. static void named_acl_cleanup(void)
  469. {
  470. ast_cli_unregister_multiple(cli_named_acl, ARRAY_LEN(cli_named_acl));
  471. STASIS_MESSAGE_TYPE_CLEANUP(ast_named_acl_change_type);
  472. aco_info_destroy(&cfg_info);
  473. ao2_global_obj_release(globals);
  474. }
  475. int ast_named_acl_init()
  476. {
  477. ast_cli_register_multiple(cli_named_acl, ARRAY_LEN(cli_named_acl));
  478. STASIS_MESSAGE_TYPE_INIT(ast_named_acl_change_type);
  479. ast_register_cleanup(named_acl_cleanup);
  480. if (aco_info_init(&cfg_info)) {
  481. return 0;
  482. }
  483. /* Register the per level options. */
  484. aco_option_register(&cfg_info, "permit", ACO_EXACT, named_acl_types, NULL, OPT_ACL_T, 1, FLDSET(struct named_acl, ha));
  485. aco_option_register(&cfg_info, "deny", ACO_EXACT, named_acl_types, NULL, OPT_ACL_T, 0, FLDSET(struct named_acl, ha));
  486. aco_process_config(&cfg_info, 0);
  487. return 0;
  488. }