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