Asterisk - The Open Source Telephony Project  21.4.1
common_config.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2023, Sangoma Technologies Corporation
5  *
6  * George Joseph <gjoseph@sangoma.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 
19 #include "asterisk.h"
20 #include "asterisk/cli.h"
21 #include "asterisk/cli.h"
22 #include "asterisk/logger.h"
23 #include "asterisk/module.h"
24 #include "asterisk/utils.h"
25 #include "asterisk/stasis.h"
27 
28 #define AST_API_MODULE
29 #include "stir_shaken.h"
30 
31 static struct ast_sorcery *sorcery;
32 struct stasis_subscription *named_acl_changed_sub = NULL;
33 
34 struct ast_sorcery *get_sorcery(void)
35 {
36  return sorcery;
37 }
38 
39 #define generate_bool_handler_functions(param_name) \
40 static const char *param_name ## _map[] = { \
41  [ param_name ## _NOT_SET ] = "not_set", \
42  [ param_name ## _YES ] = "yes", \
43  [ param_name ## _NO ] = "no", \
44 }; \
45 enum param_name ## _enum \
46  param_name ## _from_str(const char *value) \
47 { \
48  if (!strcasecmp(value, param_name ## _map[param_name ## _NOT_SET])) { \
49  return param_name ## _NOT_SET; \
50  } else if (ast_true(value)) { \
51  return param_name ## _YES; \
52  } else if (ast_false(value)) { \
53  return param_name ## _NO; \
54  } \
55  ast_log(LOG_WARNING, "Unknown " #param_name " response value '%s'\n", value); \
56  return param_name ## _UNKNOWN; \
57 }\
58 const char *param_name ## _to_str(enum param_name ## _enum value) \
59 { \
60  return ARRAY_IN_BOUNDS(value, param_name ## _map) ? \
61  param_name ## _map[value] : NULL; \
62 }
63 
64 generate_bool_handler_functions(use_rfc9410_responses);
65 generate_bool_handler_functions(send_mky);
66 generate_bool_handler_functions(check_tn_cert_public_url);
67 generate_bool_handler_functions(relax_x5u_port_scheme_restrictions);
68 generate_bool_handler_functions(relax_x5u_path_restrictions);
69 
70 generate_bool_handler_functions(load_system_certs);
71 
73  int value;
74  const char *name;
75 };
76 
77 #define generate_enum_string_functions(param_name, default_value, ...)\
78 static struct enum_name_xref_entry param_name ## _map[] = { \
79  __VA_ARGS__ \
80 } ; \
81 enum param_name ## _enum param_name ## _from_str( \
82  const char *value) \
83 { \
84  int i; \
85  for (i = 0; i < ARRAY_LEN(param_name ## _map); i++) { \
86  if (strcasecmp(value, param_name ##_map[i].name) == 0) { \
87  return param_name ##_map[i].value; \
88  } \
89  } \
90  return param_name ## _ ## default_value; \
91 } \
92 const char *param_name ## _to_str( \
93  enum param_name ## _enum value) \
94 { \
95  int i; \
96  for (i = 0; i < ARRAY_LEN(param_name ## _map); i++) { \
97  if (value == param_name ## _map[i].value) return param_name ## _map[i].name; \
98  } \
99  return NULL; \
100 }
101 
102 generate_enum_string_functions(attest_level, UNKNOWN,
103  {attest_level_A, "A"},
104  {attest_level_B, "B"},
105  {attest_level_C, "C"},
106 );
107 
108 generate_enum_string_functions(endpoint_behavior, OFF,
109  {endpoint_behavior_OFF, "off"},
110  {endpoint_behavior_OFF, "none"},
111  {endpoint_behavior_ATTEST, "attest"},
112  {endpoint_behavior_VERIFY, "verify"},
113  {endpoint_behavior_ON, "on"},
114  {endpoint_behavior_ON, "both"}
115 );
116 
117 generate_enum_string_functions(stir_shaken_failure_action, CONTINUE,
118  {stir_shaken_failure_action_CONTINUE, "continue"},
119  {stir_shaken_failure_action_REJECT_REQUEST, "reject_request"},
120  {stir_shaken_failure_action_CONTINUE_RETURN_REASON, "continue_return_reason"},
121 );
122 
123 static const char *translate_value(const char *val)
124 {
125  if (val[0] == '0'
126  || val[0] == '\0'
127  || strcmp(val, "not_set") == 0) {
128  return "";
129  }
130 
131  return val;
132 }
133 
134 static void print_acl(int fd, struct ast_acl_list *acl_list, const char *prefix)
135 {
136  struct ast_acl *acl;
137 
138  AST_LIST_LOCK(acl_list);
139  AST_LIST_TRAVERSE(acl_list, acl, list) {
140  if (ast_strlen_zero(acl->name)) {
141  ast_cli(fd, "%s(permit/deny)\n", prefix);
142  } else {
143  ast_cli(fd, "%s%s\n", prefix, acl->name);
144  }
145  ast_ha_output(fd, acl->acl, prefix);
146  }
147  AST_LIST_UNLOCK(acl_list);
148 }
149 
150 #define print_acl_cert_store(cfg, a, max_name_len) \
151 ({ \
152  if (cfg->vcfg_common.acl) { \
153  ast_cli(a->fd, "x5u_acl:\n"); \
154  print_acl(a->fd, cfg->vcfg_common.acl, " "); \
155  } else { \
156  ast_cli(a->fd, "%-*s: (none)\n", max_name_len, "x5u_acl"); \
157  }\
158  if (cfg->vcfg_common.tcs) { \
159  int count = 0; \
160  ast_cli(a->fd, "%-*s:\n", max_name_len, "Verification CA certificate store"); \
161  count = crypto_show_cli_store(cfg->vcfg_common.tcs, a->fd); \
162  if (count == 0 && (!ast_strlen_zero(cfg->vcfg_common.ca_path) \
163  || !ast_strlen_zero(cfg->vcfg_common.crl_path))) { \
164  ast_cli(a->fd, " Note: Certs in ca_path or crl_path won't show until used.\n"); \
165  } \
166  } else { \
167  ast_cli(a->fd, "%-*s: (none)\n", max_name_len, "Verification CA certificate store"); \
168  } \
169 })
170 
171 int config_object_cli_show(void *obj, void *arg, void *data, int flags)
172 {
173  struct ast_cli_args *a = arg;
174  struct config_object_cli_data *cli_data = data;
175  struct ast_variable *options;
176  struct ast_variable *i;
177  const char *title = NULL;
178  const char *cfg_name = NULL;
179  int max_name_len = 0;
180 
181  if (!obj) {
182  ast_cli(a->fd, "No stir/shaken configuration found\n");
183  return 0;
184  }
185 
186  if (!ast_strlen_zero(cli_data->title)) {
187  title = cli_data->title;
188  } else {
189  title = ast_sorcery_object_get_type(obj);
190  }
191  max_name_len = strlen(title);
192 
193  if (cli_data->object_type == config_object_type_profile
194  || cli_data->object_type == config_object_type_tn) {
195  cfg_name = ast_sorcery_object_get_id(obj);
196  max_name_len += strlen(cfg_name) + 2 /* ": " */;
197  }
198 
200  get_sorcery(), obj, AST_HANDLER_ONLY_STRING));
201  if (!options) {
202  return 0;
203  }
204 
205  for (i = options; i; i = i->next) {
206  int nlen = strlen(i->name);
207  max_name_len = (nlen > max_name_len) ? nlen : max_name_len;
208  }
209 
210  ast_cli(a->fd, "\n==============================================================================\n");
211  if (ast_strlen_zero(cfg_name)) {
212  ast_cli(a->fd, "%s\n", title);
213  } else {
214  ast_cli(a->fd, "%s: %s\n", title, cfg_name);
215  }
216  ast_cli(a->fd, "------------------------------------------------------------------------------\n");
217 
218  for (i = options; i; i = i->next) {
219  if (!ast_strings_equal(i->name, "x5u_acl")) {
220  ast_cli(a->fd, "%-*s: %s\n", max_name_len, i->name,
221  translate_value(i->value));
222  }
223  }
224 
225  ast_variables_destroy(options);
226 
227  if (cli_data->object_type == config_object_type_profile) {
228  struct profile_cfg *cfg = obj;
229  print_acl_cert_store(cfg, a, max_name_len);
230  } else if (cli_data->object_type == config_object_type_verification) {
231  struct verification_cfg *cfg = obj;
232  print_acl_cert_store(cfg, a, max_name_len);
233  }
234  ast_cli(a->fd, "---------------------------------------------\n\n"); \
235 
236  return 0;
237 }
238 
239 char *config_object_tab_complete_name(const char *word, struct ao2_container *container)
240 {
241  void *obj;
242  struct ao2_iterator it;
243  int wordlen = strlen(word);
244  int ret;
245 
246  it = ao2_iterator_init(container, 0);
247  while ((obj = ao2_iterator_next(&it))) {
248  if (!strncasecmp(word, ast_sorcery_object_get_id(obj), wordlen)) {
250  if (ret) {
251  ao2_ref(obj, -1);
252  break;
253  }
254  }
255  ao2_ref(obj, -1);
256  }
258 
259  return NULL;
260 }
261 
262 int common_config_reload(void)
263 {
264  SCOPE_ENTER(2, "Stir Shaken Reload\n");
265  if (vs_reload()) {
266  SCOPE_EXIT_RTN_VALUE(AST_MODULE_LOAD_DECLINE, "Stir Shaken VS Reload failed\n");
267  }
268 
269  if (as_reload()) {
270  SCOPE_EXIT_RTN_VALUE(AST_MODULE_LOAD_DECLINE, "Stir Shaken AS Reload failed\n");
271  }
272 
273  if (tn_config_reload()) {
274  SCOPE_EXIT_RTN_VALUE(AST_MODULE_LOAD_DECLINE, "Stir Shaken TN Reload failed\n");
275  }
276 
277  if (profile_reload()) {
278  SCOPE_EXIT_RTN_VALUE(AST_MODULE_LOAD_DECLINE, "Stir Shaken Profile Reload failed\n");
279  }
280 
281  SCOPE_EXIT_RTN_VALUE(AST_MODULE_LOAD_SUCCESS, "Stir Shaken Reload Done\n");
282 }
283 
284 int common_config_unload(void)
285 {
286  profile_unload();
287  tn_config_unload();
288  as_unload();
289  vs_unload();
290 
291  if (named_acl_changed_sub) {
292  stasis_unsubscribe(named_acl_changed_sub);
293  named_acl_changed_sub = NULL;
294  }
295  ast_sorcery_unref(sorcery);
296  sorcery = NULL;
297 
298  return 0;
299 }
300 
301 static void named_acl_changed_cb(void *data,
302  struct stasis_subscription *sub, struct stasis_message *message)
303 {
305  return;
306  }
307  ast_log(LOG_NOTICE, "Named acl changed. Reloading verification and profile\n");
308  common_config_reload();
309 }
310 
311 int common_config_load(void)
312 {
313  SCOPE_ENTER(2, "Stir Shaken Load\n");
314 
315  if (!(sorcery = ast_sorcery_open())) {
316  common_config_unload();
317  SCOPE_EXIT_RTN_VALUE(AST_MODULE_LOAD_DECLINE, "Stir Shaken sorcery load failed\n");
318  }
319 
320  if (vs_load()) {
321  common_config_unload();
322  SCOPE_EXIT_RTN_VALUE(AST_MODULE_LOAD_DECLINE, "Stir Shaken VS load failed\n");
323  }
324 
325  if (as_load()) {
326  common_config_unload();
327  SCOPE_EXIT_RTN_VALUE(AST_MODULE_LOAD_DECLINE, "Stir Shaken AS load failed\n");
328  }
329 
330  if (tn_config_load()) {
331  common_config_unload();
332  SCOPE_EXIT_RTN_VALUE(AST_MODULE_LOAD_DECLINE, "Stir Shaken TN load failed\n");
333  }
334 
335  if (profile_load()) {
336  common_config_unload();
337  SCOPE_EXIT_RTN_VALUE(AST_MODULE_LOAD_DECLINE, "Stir Shaken profile load failed\n");
338  }
339 
340  if (!named_acl_changed_sub) {
341  named_acl_changed_sub = stasis_subscribe(ast_security_topic(),
342  named_acl_changed_cb, NULL);
343  if (!named_acl_changed_sub) {
344  common_config_unload();
345  SCOPE_EXIT_RTN_VALUE(AST_MODULE_LOAD_DECLINE, "Stir Shaken acl change subscribe failed\n");
346  }
348  named_acl_changed_sub, ast_named_acl_change_type());
349  }
350 
351  SCOPE_EXIT_RTN_VALUE(AST_MODULE_LOAD_SUCCESS, "Stir Shaken Load Done\n");
352 }
353 
354 
355 /* Remove everything except 0-9, *, and # in telephone number according to RFC 8224
356  * (required by RFC 8225 as part of canonicalization) */
357 char *canonicalize_tn(const char *tn, char *dest_tn)
358 {
359  int i;
360  const char *s = tn;
361  size_t len = tn ? strlen(tn) : 0;
362  char *new_tn = dest_tn;
363  SCOPE_ENTER(3, "tn: %s\n", S_OR(tn, "(null)"));
364 
365  if (ast_strlen_zero(tn)) {
366  *dest_tn = '\0';
367  SCOPE_EXIT_RTN_VALUE(NULL, "Empty TN\n");
368  }
369 
370  if (!dest_tn) {
371  SCOPE_EXIT_RTN_VALUE(NULL, "No destination buffer\n");
372  }
373 
374  for (i = 0; i < len; i++) {
375  if (isdigit(*s) || *s == '#' || *s == '*') { /* Only characters allowed */
376  *new_tn++ = *s;
377  }
378  s++;
379  }
380  *new_tn = '\0';
381  SCOPE_EXIT_RTN_VALUE(dest_tn, "Canonicalized '%s' -> '%s'\n", tn, dest_tn);
382 }
383 
384 char *canonicalize_tn_alloc(const char *tn)
385 {
386  char *canon_tn = ast_strlen_zero(tn) ? NULL : ast_malloc(strlen(tn) + 1);
387  if (!canon_tn) {
388  return NULL;
389  }
390  return canonicalize_tn(tn, canon_tn);
391 }
struct ast_variable * next
struct ast_variable * ast_variable_list_sort(struct ast_variable *head)
Performs an in-place sort on the variable list by ascending name.
Definition: main/config.c:620
Security Event Reporting API.
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:40
struct ast_variable * ast_sorcery_objectset_create2(const struct ast_sorcery *sorcery, const void *object, enum ast_sorcery_field_handler_flags flags)
Create an object set (KVP list) for an object.
Definition: sorcery.c:1511
Asterisk main include file. File version handling, generic pbx functions.
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
Stasis Message Bus API. See Stasis Message Bus API for detailed documentation.
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:140
char name[ACL_NAME_LENGTH]
Definition: acl.h:71
Structure for variables, used for configurations and for channel variables.
Full structure for sorcery.
Definition: sorcery.c:230
struct stasis_message_type * stasis_message_type(const struct stasis_message *msg)
Get the message type for a stasis_message.
struct stasis_message_type * ast_named_acl_change_type(void)
a stasis_message_type for changes against a named ACL or the set of all named ACLs ...
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
Wrapper for an ast_acl linked list.
Definition: acl.h:76
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
Utility functions.
const char * ast_sorcery_object_get_type(const void *object)
Get the type of a sorcery object.
Definition: sorcery.c:2329
#define ast_sorcery_unref(sorcery)
Decrease the reference count of a sorcery structure.
Definition: sorcery.h:1500
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
Use string handler only.
Definition: sorcery.h:137
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2317
struct ast_ha * acl
Definition: acl.h:68
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
struct ao2_container * container
Definition: res_fax.c:501
an ast_acl is a linked list node of ast_ha structs which may have names.
Definition: acl.h:67
int ast_strings_equal(const char *str1, const char *str2)
Compare strings for equality checking for NULL.
Definition: strings.c:238
struct stasis_subscription * stasis_unsubscribe(struct stasis_subscription *subscription)
Cancel a subscription.
Definition: stasis.c:971
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
Support for logging to various files, console and syslog Configuration in file logger.conf.
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
Standard Command Line Interface.
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:80
void ast_ha_output(int fd, const struct ast_ha *ha, const char *prefix)
output an HA to the provided fd
Definition: acl.c:1086
Profile configuration for stir/shaken.
int stasis_subscription_accept_message_type(struct stasis_subscription *subscription, const struct stasis_message_type *type)
Indicate to a subscription that we are interested in a message type.
Definition: stasis.c:1023
#define ast_sorcery_open()
Open a new sorcery structure.
Definition: sorcery.h:406
struct stasis_topic * ast_security_topic(void)
A stasis_topic which publishes messages for security related issues.
Generic container type.
Definition: common_config.c:72
int ast_cli_completion_add(char *value)
Add a result to a request for completion options.
Definition: main/cli.c:2761
Asterisk module definitions.
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.