Asterisk - The Open Source Telephony Project  21.4.1
verification_config.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2023, Sangoma Technologies Corporation
5  *
6  * George Joseph <gjoseph@digium.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 
21 #include "asterisk/cli.h"
22 #include "stir_shaken.h"
23 
24 #define CONFIG_TYPE "verification"
25 
26 #define DEFAULT_global_disable 0
27 
28 #define DEFAULT_ca_file NULL
29 #define DEFAULT_ca_path NULL
30 #define DEFAULT_crl_file NULL
31 #define DEFAULT_crl_path NULL
32 static char DEFAULT_cert_cache_dir[PATH_MAX];
33 
34 #define DEFAULT_curl_timeout 2
35 #define DEFAULT_max_iat_age 15
36 #define DEFAULT_max_date_header_age 15
37 #define DEFAULT_max_cache_entry_age 3600
38 #define DEFAULT_max_cache_size 1000
39 #define DEFAULT_stir_shaken_failure_action stir_shaken_failure_action_CONTINUE
40 #define DEFAULT_use_rfc9410_responses use_rfc9410_responses_NO
41 #define DEFAULT_relax_x5u_port_scheme_restrictions relax_x5u_port_scheme_restrictions_NO
42 #define DEFAULT_relax_x5u_path_restrictions relax_x5u_path_restrictions_NO
43 #define DEFAULT_load_system_certs load_system_certs_NO
44 
45 static struct verification_cfg *empty_cfg = NULL;
46 
47 #define STIR_SHAKEN_DIR_NAME "stir_shaken"
48 
49 struct verification_cfg *vs_get_cfg(void)
50 {
51  struct verification_cfg *cfg = ast_sorcery_retrieve_by_id(get_sorcery(),
52  CONFIG_TYPE, CONFIG_TYPE);
53  if (cfg) {
54  return cfg;
55  }
56 
57  return empty_cfg ? ao2_bump(empty_cfg) : NULL;
58 }
59 
60 int vs_is_config_loaded(void)
61 {
62  struct verification_cfg *cfg = ast_sorcery_retrieve_by_id(get_sorcery(),
63  CONFIG_TYPE, CONFIG_TYPE);
64  ao2_cleanup(cfg);
65 
66  return !!cfg;
67 }
68 
69 generate_vcfg_common_sorcery_handlers(verification_cfg);
70 
71 void vcfg_cleanup(struct verification_cfg_common *vcfg_common)
72 {
73  if (!vcfg_common) {
74  return;
75  }
76  ast_string_field_free_memory(vcfg_common);
77  if (vcfg_common->tcs) {
78  crypto_free_cert_store(vcfg_common->tcs);
79  }
80  ast_free_acl_list(vcfg_common->acl);
81 }
82 
83 static void verification_destructor(void *obj)
84 {
85  struct verification_cfg *cfg = obj;
87  vcfg_cleanup(&cfg->vcfg_common);
88 }
89 
90 static void *verification_alloc(const char *name)
91 {
92  struct verification_cfg *cfg;
93 
94  cfg = ast_sorcery_generic_alloc(sizeof(*cfg), verification_destructor);
95  if (!cfg) {
96  return NULL;
97  }
98 
99  if (ast_string_field_init(cfg, 1024)) {
100  ao2_ref(cfg, -1);
101  return NULL;
102  }
103 
104  /*
105  * The memory for vcfg_common actually comes from cfg
106  * due to the weirdness of the STRFLDSET macro used with
107  * sorcery. We just use a token amount of memory in
108  * this call so the initialize doesn't fail.
109  */
110  if (ast_string_field_init(&cfg->vcfg_common, 8)) {
111  ao2_ref(cfg, -1);
112  return NULL;
113  }
114 
115  return cfg;
116 }
117 
118 int vs_copy_cfg_common(const char *id, struct verification_cfg_common *cfg_dst,
119  struct verification_cfg_common *cfg_src)
120 {
121  int rc = 0;
122 
123  if (!cfg_dst || !cfg_src) {
124  return -1;
125  }
126 
127  if (!cfg_dst->tcs && cfg_src->tcs) {
128  cfg_sf_copy_wrapper(id, cfg_dst, cfg_src, ca_file);
129  cfg_sf_copy_wrapper(id, cfg_dst, cfg_src, ca_path);
130  cfg_sf_copy_wrapper(id, cfg_dst, cfg_src, crl_file);
131  cfg_sf_copy_wrapper(id, cfg_dst, cfg_src, crl_path);
132  ao2_bump(cfg_src->tcs);
133  cfg_dst->tcs = cfg_src->tcs;
134  }
135 
136  cfg_sf_copy_wrapper(id, cfg_dst, cfg_src, cert_cache_dir);
137 
138  cfg_uint_copy(cfg_dst, cfg_src, curl_timeout);
139  cfg_uint_copy(cfg_dst, cfg_src, max_iat_age);
140  cfg_uint_copy(cfg_dst, cfg_src, max_date_header_age);
141  cfg_uint_copy(cfg_dst, cfg_src, max_cache_entry_age);
142  cfg_uint_copy(cfg_dst, cfg_src, max_cache_size);
143 
144  cfg_enum_copy(cfg_dst, cfg_src, stir_shaken_failure_action);
145  cfg_enum_copy(cfg_dst, cfg_src, use_rfc9410_responses);
146  cfg_enum_copy(cfg_dst, cfg_src, relax_x5u_port_scheme_restrictions);
147  cfg_enum_copy(cfg_dst, cfg_src, relax_x5u_path_restrictions);
148  cfg_enum_copy(cfg_dst, cfg_src, load_system_certs);
149 
150  if (cfg_src->acl) {
151  ast_free_acl_list(cfg_dst->acl);
152  cfg_dst->acl = ast_duplicate_acl_list(cfg_src->acl);
153  }
154 
155  return rc;
156 }
157 
158 int vs_check_common_config(const char *id,
159  struct verification_cfg_common *vcfg_common)
160 {
161  SCOPE_ENTER(3, "%s: Checking common config\n", id);
162 
163  if (!ast_strlen_zero(vcfg_common->ca_file)
164  && !ast_file_is_readable(vcfg_common->ca_file)) {
165  SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_ERROR,
166  "%s: ca_file '%s' not found, or is unreadable\n",
167  id, vcfg_common->ca_file);
168  }
169 
170  if (!ast_strlen_zero(vcfg_common->ca_path)
171  && !ast_file_is_readable(vcfg_common->ca_path)) {
172  SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_ERROR,
173  "%s: ca_path '%s' not found, or is unreadable\n",
174  id, vcfg_common->ca_path);
175  }
176 
177  if (!ast_strlen_zero(vcfg_common->crl_file)
178  && !ast_file_is_readable(vcfg_common->crl_file)) {
179  SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_ERROR,
180  "%s: crl_file '%s' not found, or is unreadable\n",
181  id, vcfg_common->crl_file);
182  }
183 
184  if (!ast_strlen_zero(vcfg_common->crl_path)
185  && !ast_file_is_readable(vcfg_common->crl_path)) {
186  SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_ERROR,
187  "%s: crl_path '%s' not found, or is unreadable\n",
188  id, vcfg_common->crl_path);
189  }
190 
191  if (!ast_strlen_zero(vcfg_common->ca_file)
192  || !ast_strlen_zero(vcfg_common->ca_path)) {
193  int rc = 0;
194 
195  if (!vcfg_common->tcs) {
196  vcfg_common->tcs = crypto_create_cert_store();
197  if (!vcfg_common->tcs) {
198  SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_ERROR,
199  "%s: Unable to create CA cert store\n", id);
200  }
201  }
202  rc = crypto_load_cert_store(vcfg_common->tcs,
203  vcfg_common->ca_file, vcfg_common->ca_path);
204  if (rc != 0) {
205  SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_ERROR,
206  "%s: Unable to load CA cert store from '%s' or '%s'\n",
207  id, vcfg_common->ca_file, vcfg_common->ca_path);
208  }
209  }
210 
211  if (!ast_strlen_zero(vcfg_common->crl_file)
212  || !ast_strlen_zero(vcfg_common->crl_path)) {
213  int rc = 0;
214 
215  if (!vcfg_common->tcs) {
216  vcfg_common->tcs = crypto_create_cert_store();
217  if (!vcfg_common->tcs) {
218  SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_ERROR,
219  "%s: Unable to create CA cert store\n", id);
220  }
221  }
222  rc = crypto_load_cert_store(vcfg_common->tcs,
223  vcfg_common->crl_file, vcfg_common->crl_path);
224  if (rc != 0) {
225  SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_ERROR,
226  "%s: Unable to load CA CRL store from '%s' or '%s'\n",
227  id, vcfg_common->crl_file, vcfg_common->crl_path);
228  }
229  }
230 
231  if (vcfg_common->tcs) {
232  if (ENUM_BOOL(vcfg_common->load_system_certs, load_system_certs)) {
233  X509_STORE_set_default_paths(vcfg_common->tcs->store);
234  }
235 
236  if (!ast_strlen_zero(vcfg_common->crl_file)
237  || !ast_strlen_zero(vcfg_common->crl_path)) {
238  X509_STORE_set_flags(vcfg_common->tcs->store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
239  }
240  }
241 
242  if (!ast_strlen_zero(vcfg_common->cert_cache_dir)) {
243  FILE *fp;
244  char *testfile;
245 
246  if (ast_asprintf(&testfile, "%s/testfile", vcfg_common->cert_cache_dir) <= 0) {
247  SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_ERROR,
248  "%s: Unable to allocate memory for testfile\n", id);
249  }
250 
251  fp = fopen(testfile, "w+");
252  if (!fp) {
253  ast_free(testfile);
254  SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_ERROR,
255  "%s: cert_cache_dir '%s' was not writable\n",
256  id, vcfg_common->cert_cache_dir);
257  }
258  fclose(fp);
259  remove(testfile);
260  ast_free(testfile);
261  }
262 
263  SCOPE_EXIT_RTN_VALUE(0, "%s: Done\n", id);
264 }
265 
266 static char *special_addresses[] = {
267  "0.0.0.0/8",
268  "10.0.0.0/8",
269  "100.64.0.0/10",
270  "127.0.0.0/8",
271  "169.254.0.0/16",
272  "172.16.0.0/12",
273  "192.0.0.0/24",
274  "192.0.0.0/29",
275  "192.88.99.0/24",
276  "192.168.0.0/16",
277  "198.18.0.0/15",
278  "198.51.100.0/24",
279  "203.0.113.0/24",
280  "240.0.0.0/4",
281  "255.255.255.255/32",
282  "::1/128",
283  "::/128",
284 /* "64:ff9b::/96", IPv4-IPv6 translation addresses should probably not be blocked by default */
285 /* "::ffff:0:0/96", IPv4 mapped addresses should probably not be blocked by default */
286  "100::/64",
287  "2001::/23",
288  "2001::/32",
289  "2001:2::/48",
290  "2001:db8::/32",
291  "2001:10::/28",
292 /* "2002::/16", 6to4 should problably not be blocked by default */
293  "fc00::/7",
294  "fe80::/10",
295 };
296 
297 static int verification_apply(const struct ast_sorcery *sorcery, void *obj)
298 {
299  struct verification_cfg *cfg = obj;
300  const char *id = ast_sorcery_object_get_id(cfg);
301 
302  if (vs_check_common_config("verification", &cfg->vcfg_common) !=0) {
303  return -1;
304  }
305 
306  if (!cfg->vcfg_common.acl) {
307  int error = 0;
308  int ignore;
309  int i;
310 
311  ast_append_acl("permit", "0.0.0.0/0", &cfg->vcfg_common.acl, &error, &ignore);
312  if (error) {
313  ast_free_acl_list(cfg->vcfg_common.acl);
314  cfg->vcfg_common.acl = NULL;
315  ast_log(LOG_ERROR, "%s: Unable to create default acl rule for '%s: %s'\n",
316  id, "permit", "0.0.0.0/0");
317  return -1;
318  }
319 
320  for (i = 0; i < ARRAY_LEN(special_addresses); i++) {
321  ast_append_acl("deny", special_addresses[i], &cfg->vcfg_common.acl, &error, &ignore);
322  if (error) {
323  ast_free_acl_list(cfg->vcfg_common.acl);
324  cfg->vcfg_common.acl = NULL;
325  ast_log(LOG_ERROR, "%s: Unable to create default acl rule for '%s: %s'\n",
326  id, "deny", special_addresses[i]);
327  return -1;
328  }
329  }
330  }
331 
332  return 0;
333 }
334 
335 static char *cli_verification_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
336 {
337  struct verification_cfg *cfg;
338  struct config_object_cli_data data = {
339  .title = "Default Verification",
340  .object_type = config_object_type_verification,
341  };
342 
343  switch(cmd) {
344  case CLI_INIT:
345  e->command = "stir_shaken show verification";
346  e->usage =
347  "Usage: stir_shaken show verification\n"
348  " Show the stir/shaken verification settings\n";
349  return NULL;
350  case CLI_GENERATE:
351  return NULL;
352  }
353 
354  if (a->argc != 3) {
355  return CLI_SHOWUSAGE;
356  }
357 
358  cfg = vs_get_cfg();
359  config_object_cli_show(cfg, a, &data, 0);
360 
361  ao2_cleanup(cfg);
362 
363  return CLI_SUCCESS;
364 }
365 
366 static struct ast_cli_entry verification_cli[] = {
367  AST_CLI_DEFINE(cli_verification_show, "Show stir/shaken verification configuration"),
368 };
369 
370 int vs_config_reload(void)
371 {
372  struct ast_sorcery *sorcery = get_sorcery();
373  ast_sorcery_force_reload_object(sorcery, CONFIG_TYPE);
374 
375  if (!vs_is_config_loaded()) {
376  ast_log(LOG_WARNING,"Stir/Shaken verification service disabled. Either there were errors in the 'verification' object in stir_shaken.conf or it was missing altogether.\n");
377  }
378  if (!empty_cfg) {
379  empty_cfg = verification_alloc(CONFIG_TYPE);
380  if (!empty_cfg) {
381  return -1;
382  }
383  empty_cfg->global_disable = 1;
384  }
385 
386  return 0;
387 }
388 
389 int vs_config_unload(void)
390 {
391  ast_cli_unregister_multiple(verification_cli,
392  ARRAY_LEN(verification_cli));
393  ao2_cleanup(empty_cfg);
394 
395  return 0;
396 }
397 
398 int vs_config_load(void)
399 {
400  struct ast_sorcery *sorcery = get_sorcery();
401 
402  snprintf(DEFAULT_cert_cache_dir, sizeof(DEFAULT_cert_cache_dir), "%s/keys/%s/cache",
403  ast_config_AST_DATA_DIR, STIR_SHAKEN_DIR_NAME);
404 
405  ast_sorcery_apply_default(sorcery, CONFIG_TYPE, "config",
406  "stir_shaken.conf,criteria=type=" CONFIG_TYPE ",single_object=yes,explicit_name=" CONFIG_TYPE);
407 
408  if (ast_sorcery_object_register(sorcery, CONFIG_TYPE, verification_alloc,
409  NULL, verification_apply)) {
410  ast_log(LOG_ERROR, "stir/shaken - failed to register '%s' sorcery object\n", CONFIG_TYPE);
411  return -1;
412  }
413 
414  ast_sorcery_object_field_register_nodoc(sorcery, CONFIG_TYPE, "type", "",
415  OPT_NOOP_T, 0, 0);
416 
417  ast_sorcery_object_field_register(sorcery, CONFIG_TYPE, "global_disable",
418  DEFAULT_global_disable ? "yes" : "no",
419  OPT_YESNO_T, 1, FLDSET(struct verification_cfg, global_disable));
420 
421  register_common_verification_fields(sorcery, verification_cfg, CONFIG_TYPE,);
422 
423  ast_sorcery_load_object(sorcery, CONFIG_TYPE);
424 
425  if (!vs_is_config_loaded()) {
426  ast_log(LOG_WARNING,"Stir/Shaken verification service disabled. Either there were errors in the 'verification' object in stir_shaken.conf or it was missing altogether.\n");
427  }
428  if (!empty_cfg) {
429  empty_cfg = verification_alloc(CONFIG_TYPE);
430  if (!empty_cfg) {
431  return -1;
432  }
433  empty_cfg->global_disable = 1;
434  }
435 
436  ast_cli_register_multiple(verification_cli,
437  ARRAY_LEN(verification_cli));
438 
439  return 0;
440 }
#define ast_sorcery_object_field_register_nodoc(sorcery, type, name, default_val, opt_type, flags,...)
Register a field within an object without documentation.
Definition: sorcery.h:987
Asterisk main include file. File version handling, generic pbx functions.
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
void ast_sorcery_force_reload_object(const struct ast_sorcery *sorcery, const char *type)
Inform any wizards of a specific object type to reload persistent objects even if no changes determin...
Definition: sorcery.c:1457
descriptor for a cli entry.
Definition: cli.h:171
int ast_file_is_readable(const char *filename)
Test that a file exists and is readable by the effective user.
Definition: utils.c:3107
Full structure for sorcery.
Definition: sorcery.c:230
Type for a default handler that should do nothing.
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
void ast_sorcery_load_object(const struct ast_sorcery *sorcery, const char *type)
Inform any wizards of a specific object type to load persistent objects.
Definition: sorcery.c:1393
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition: astmm.h:267
void * ast_sorcery_retrieve_by_id(const struct ast_sorcery *sorcery, const char *type, const char *id)
Retrieve an object using its unique identifier.
Definition: sorcery.c:1853
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition: astobj2.h:480
Type for default option handler for bools (ast_true/ast_false)
#define FLDSET(type,...)
Convert a struct and list of fields to an argument list of field offsets.
struct ast_acl_list * ast_duplicate_acl_list(struct ast_acl_list *original)
Duplicates the contests of a list of lists of host access rules.
Definition: acl.c:315
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:359
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2317
#define ast_sorcery_object_register(sorcery, type, alloc, transform, apply)
Register an object type.
Definition: sorcery.h:837
struct ast_acl_list * ast_free_acl_list(struct ast_acl_list *acl)
Free a list of ACLs.
Definition: acl.c:233
Verification Service configuration for stir/shaken.
char * command
Definition: cli.h:186
const char * usage
Definition: cli.h:177
#define ast_sorcery_object_field_register(sorcery, type, name, default_val, opt_type, flags,...)
Register a field within an object.
Definition: sorcery.h:955
Standard Command Line Interface.
void ast_append_acl(const char *sense, const char *stuff, struct ast_acl_list **path, int *error, int *named_acl_flag)
Add a rule to an ACL struct.
Definition: acl.c:429
void * ast_sorcery_generic_alloc(size_t size, ao2_destructor_fn destructor)
Allocate a generic sorcery capable object.
Definition: sorcery.c:1728
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:374