Asterisk - The Open Source Telephony Project  21.4.1
attestation_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 #include "asterisk.h"
19 
20 #include "asterisk/cli.h"
21 #include "asterisk/sorcery.h"
22 #include "asterisk/paths.h"
23 
24 #include "stir_shaken.h"
25 
26 #define CONFIG_TYPE "attestation"
27 
28 #define DEFAULT_global_disable 0
29 
30 #define DEFAULT_check_tn_cert_public_url check_tn_cert_public_url_NO
31 #define DEFAULT_private_key_file NULL
32 #define DEFAULT_public_cert_url NULL
33 #define DEFAULT_attest_level attest_level_NOT_SET
34 #define DEFAULT_send_mky send_mky_NO
35 
36 static struct attestation_cfg *empty_cfg = NULL;
37 
38 struct attestation_cfg *as_get_cfg(void)
39 {
40  struct attestation_cfg *cfg = ast_sorcery_retrieve_by_id(get_sorcery(),
41  CONFIG_TYPE, CONFIG_TYPE);
42  if (cfg) {
43  return cfg;
44  }
45 
46  return empty_cfg ? ao2_bump(empty_cfg) : NULL;
47 }
48 
49 int as_is_config_loaded(void)
50 {
51  struct attestation_cfg *cfg = ast_sorcery_retrieve_by_id(get_sorcery(),
52  CONFIG_TYPE, CONFIG_TYPE);
53  ao2_cleanup(cfg);
54 
55  return !!cfg;
56 }
57 
58 generate_acfg_common_sorcery_handlers(attestation_cfg);
59 
60 void acfg_cleanup(struct attestation_cfg_common *acfg_common)
61 {
62  if (!acfg_common) {
63  return;
64  }
65  ast_string_field_free_memory(acfg_common);
66  ao2_cleanup(acfg_common->raw_key);
67 }
68 
69 static void attestation_destructor(void *obj)
70 {
71  struct attestation_cfg *cfg = obj;
72 
74  acfg_cleanup(&cfg->acfg_common);
75 }
76 
77 static void *attestation_alloc(const char *name)
78 {
79  struct attestation_cfg *cfg;
80 
81  cfg = ast_sorcery_generic_alloc(sizeof(*cfg), attestation_destructor);
82  if (!cfg) {
83  return NULL;
84  }
85 
86  if (ast_string_field_init(cfg, 1024)) {
87  ao2_ref(cfg, -1);
88  return NULL;
89  }
90 
91  /*
92  * The memory for acfg_common actually comes from cfg
93  * due to the weirdness of the STRFLDSET macro used with
94  * sorcery. We just use a token amount of memory in
95  * this call so the initialize doesn't fail.
96  */
97  if (ast_string_field_init(&cfg->acfg_common, 8)) {
98  ao2_ref(cfg, -1);
99  return NULL;
100  }
101 
102  return cfg;
103 }
104 
105 int as_copy_cfg_common(const char *id, struct attestation_cfg_common *cfg_dst,
106  struct attestation_cfg_common *cfg_src)
107 {
108  int rc = 0;
109 
110  if (!cfg_dst || !cfg_src) {
111  return -1;
112  }
113 
114  cfg_sf_copy_wrapper(id, cfg_dst, cfg_src, private_key_file);
115  cfg_sf_copy_wrapper(id, cfg_dst, cfg_src, public_cert_url);
116 
117  cfg_enum_copy(cfg_dst, cfg_src, attest_level);
118  cfg_enum_copy(cfg_dst, cfg_src, check_tn_cert_public_url);
119  cfg_enum_copy(cfg_dst, cfg_src, send_mky);
120 
121  if (cfg_src->raw_key) {
122  /* Free and overwrite the destination */
123  ao2_cleanup(cfg_dst->raw_key);
124  cfg_dst->raw_key = ao2_bump(cfg_src->raw_key);
125  cfg_dst->raw_key_length = cfg_src->raw_key_length;
126  }
127 
128  return rc;
129 }
130 
131 int as_check_common_config(const char *id, struct attestation_cfg_common *acfg_common)
132 {
133  SCOPE_ENTER(3, "%s: Checking common config\n", id);
134 
135  if (!ast_strlen_zero(acfg_common->private_key_file)
136  && !ast_file_is_readable(acfg_common->private_key_file)) {
137  SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_ERROR, "%s: default_private_key_path %s is missing or not readable\n", id,
138  acfg_common->private_key_file);
139  }
140 
141  if (ENUM_BOOL(acfg_common->check_tn_cert_public_url,
142  check_tn_cert_public_url)
143  && !ast_strlen_zero(acfg_common->public_cert_url)) {
144  RAII_VAR(char *, public_cert_data, NULL, ast_std_free);
145  X509 *public_cert;
146  size_t public_cert_len;
147  int rc = 0;
148  long http_code;
149  SCOPE_ENTER(3 , "%s: Checking public cert url '%s'\n",
150  id, acfg_common->public_cert_url);
151 
152  http_code = curl_download_to_memory(acfg_common->public_cert_url,
153  &public_cert_len, &public_cert_data, NULL);
154  if (http_code / 100 != 2) {
155  SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_ERROR, "%s: public_cert '%s' could not be downloaded\n", id,
156  acfg_common->public_cert_url);
157  }
158 
159  public_cert = crypto_load_cert_from_memory(public_cert_data,
160  public_cert_len);
161  if (!public_cert) {
162  SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_ERROR, "%s: public_cert '%s' could not be parsed as a certificate\n", id,
163  acfg_common->public_cert_url);
164  }
165  rc = crypto_is_cert_time_valid(public_cert, 0);
166  X509_free(public_cert);
167  if (!rc) {
168  SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_ERROR, "%s: public_cert '%s' is not valid yet or has expired\n", id,
169  acfg_common->public_cert_url);
170  }
171 
172  rc = crypto_has_private_key_from_memory(public_cert_data, public_cert_len);
173  if (rc) {
174  SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_ERROR, "%s: DANGER!!! public_cert_url '%s' has a private key in the file!!!\n", id,
175  acfg_common->public_cert_url);
176  }
177  SCOPE_EXIT("%s: Done\n", id);
178  }
179 
180  if (!ast_strlen_zero(acfg_common->private_key_file)) {
181  EVP_PKEY *private_key;
182  RAII_VAR(unsigned char *, raw_key, NULL, ast_free);
183 
184  private_key = crypto_load_privkey_from_file(acfg_common->private_key_file);
185  if (!private_key) {
186  SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_ERROR, "%s: Could not extract raw private key from file '%s'\n", id,
187  acfg_common->private_key_file);
188  }
189 
190  acfg_common->raw_key_length = crypto_extract_raw_privkey(private_key, &raw_key);
191  EVP_PKEY_free(private_key);
192  if (acfg_common->raw_key_length == 0 || raw_key == NULL) {
193  SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_ERROR, "%s: Could not extract raw private key from file '%s'\n", id,
194  acfg_common->private_key_file);
195  }
196 
197  /*
198  * We're making this an ao2 object so it can be referenced
199  * by a profile instead of having to copy it.
200  */
201  acfg_common->raw_key = ao2_alloc(acfg_common->raw_key_length, NULL);
202  if (!acfg_common->raw_key) {
203  SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_ERROR,
204  "%s: Could not allocate memory for raw private key\n", id);
205  }
206  memcpy(acfg_common->raw_key, raw_key, acfg_common->raw_key_length);
207 
208  }
209 
210  SCOPE_EXIT_RTN_VALUE(0, "%s: Done\n", id);
211 }
212 
213 static int attestation_apply(const struct ast_sorcery *sorcery, void *obj)
214 {
215  struct attestation_cfg *cfg = obj;
216  const char *id = ast_sorcery_object_get_id(cfg);
217 
218  if (as_check_common_config(id, &cfg->acfg_common) != 0) {
219  return -1;
220  }
221 
222  return 0;
223 }
224 
225 static char *attestation_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
226 {
227  struct attestation_cfg *cfg;
228  struct config_object_cli_data data = {
229  .title = "Default Attestation",
230  .object_type = config_object_type_attestation,
231  };
232 
233  switch(cmd) {
234  case CLI_INIT:
235  e->command = "stir_shaken show attestation";
236  e->usage =
237  "Usage: stir_shaken show attestation\n"
238  " Show the stir/shaken attestation settings\n";
239  return NULL;
240  case CLI_GENERATE:
241  return NULL;
242  }
243 
244  if (a->argc != 3) {
245  return CLI_SHOWUSAGE;
246  }
247 
248  cfg = as_get_cfg();
249  config_object_cli_show(cfg, a, &data, 0);
250  ao2_cleanup(cfg);
251 
252  return CLI_SUCCESS;
253 }
254 
255 static struct ast_cli_entry attestation_cli[] = {
256  AST_CLI_DEFINE(attestation_show, "Show stir/shaken attestation configuration"),
257 };
258 
259 int as_config_reload(void)
260 {
261  struct ast_sorcery *sorcery = get_sorcery();
262  ast_sorcery_force_reload_object(sorcery, CONFIG_TYPE);
263 
264  if (!as_is_config_loaded()) {
265  ast_log(LOG_WARNING,"Stir/Shaken attestation service disabled. Either there were errors in the 'attestation' object in stir_shaken.conf or it was missing altogether.\n");
266  }
267  if (!empty_cfg) {
268  empty_cfg = attestation_alloc(CONFIG_TYPE);
269  if (!empty_cfg) {
270  return -1;
271  }
272  empty_cfg->global_disable = 1;
273  }
274 
275  return 0;
276 }
277 
278 int as_config_unload(void)
279 {
280  ast_cli_unregister_multiple(attestation_cli,
281  ARRAY_LEN(attestation_cli));
282  ao2_cleanup(empty_cfg);
283 
284  return 0;
285 }
286 
287 int as_config_load(void)
288 {
289  struct ast_sorcery *sorcery = get_sorcery();
290 
291  ast_sorcery_apply_default(sorcery, CONFIG_TYPE, "config",
292  "stir_shaken.conf,criteria=type=" CONFIG_TYPE ",single_object=yes,explicit_name=" CONFIG_TYPE);
293 
294  if (ast_sorcery_object_register(sorcery, CONFIG_TYPE, attestation_alloc,
295  NULL, attestation_apply)) {
296  ast_log(LOG_ERROR, "stir/shaken - failed to register '%s' sorcery object\n", CONFIG_TYPE);
297  return -1;
298  }
299 
300  ast_sorcery_object_field_register_nodoc(sorcery, CONFIG_TYPE, "type",
301  "", OPT_NOOP_T, 0, 0);
302 
303  ast_sorcery_object_field_register(sorcery, CONFIG_TYPE, "global_disable",
304  DEFAULT_global_disable ? "yes" : "no",
305  OPT_YESNO_T, 1, FLDSET(struct attestation_cfg, global_disable));
306 
307  register_common_attestation_fields(sorcery, attestation_cfg, CONFIG_TYPE,);
308 
309  ast_sorcery_load_object(sorcery, CONFIG_TYPE);
310 
311  if (!as_is_config_loaded()) {
312  ast_log(LOG_WARNING,"Stir/Shaken attestation service disabled. Either there were errors in the 'attestation' object in stir_shaken.conf or it was missing altogether.\n");
313  }
314  if (!empty_cfg) {
315  empty_cfg = attestation_alloc(CONFIG_TYPE);
316  if (!empty_cfg) {
317  return -1;
318  }
319  empty_cfg->global_disable = 1;
320  }
321 
322  ast_cli_register_multiple(attestation_cli,
323  ARRAY_LEN(attestation_cli));
324 
325  return 0;
326 }
#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
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.
Asterisk file paths, configured in asterisk.conf.
#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
char * command
Definition: cli.h:186
Attestation Service configuration for stir/shaken.
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
long curl_download_to_memory(const char *url, size_t *returned_length, char **returned_data, struct ast_variable **headers)
Really simple document retrieval to memory.
Definition: curl_utils.c:300
Standard Command Line Interface.
void * ast_sorcery_generic_alloc(size_t size, ao2_destructor_fn destructor)
Allocate a generic sorcery capable object.
Definition: sorcery.c:1728
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:374
Sorcery Data Access Layer API.