Asterisk - The Open Source Telephony Project  GIT-master-a1fa8df
certificate.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2020, Sangoma Technologies Corporation
5  *
6  * Kevin Harwell <kharwell@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 <sys/stat.h>
22 
23 #include "asterisk/cli.h"
24 #include "asterisk/sorcery.h"
25 
26 #include "stir_shaken.h"
27 #include "certificate.h"
29 
30 #define CONFIG_TYPE "certificate"
31 
33  SORCERY_OBJECT(details);
35  /*! Path to a directory containing certificates */
37  /*! URL to the public certificate */
39  /*! The caller ID number associated with the certificate */
41  /*! The attestation level for this certificate */
43  );
44  /*! The private key for the certificate */
45  EVP_PKEY *private_key;
46 };
47 
49 {
51 }
52 
54 {
57 }
58 
59 static void stir_shaken_certificate_destructor(void *obj)
60 {
61  struct stir_shaken_certificate *cfg = obj;
62 
63  EVP_PKEY_free(cfg->private_key);
65 }
66 
67 static void *stir_shaken_certificate_alloc(const char *name)
68 {
69  struct stir_shaken_certificate *cfg;
70 
72  if (!cfg) {
73  return NULL;
74  }
75 
76  if (ast_string_field_init(cfg, 512)) {
77  ao2_ref(cfg, -1);
78  return NULL;
79  }
80 
81  return cfg;
82 }
83 
85 {
86  struct ast_variable fields = {
87  .name = "caller_id_number",
88  .value = caller_id_number,
89  .next = NULL,
90  };
91 
93  "certificate", AST_RETRIEVE_FLAG_DEFAULT, &fields);
94 }
95 
97 {
98  return cert ? cert->public_cert_url : NULL;
99 }
100 
102 {
103  return cert ? cert->attestation : NULL;
104 }
105 
107 {
108  return cert ? cert->private_key : NULL;
109 }
110 
111 static int stir_shaken_certificate_apply(const struct ast_sorcery *sorcery, void *obj)
112 {
113  EVP_PKEY *private_key;
114  struct stir_shaken_certificate *cert = obj;
115 
116  if (ast_strlen_zero(cert->caller_id_number)) {
117  ast_log(LOG_ERROR, "Caller ID must be present\n");
118  return -1;
119  }
120 
121  if (ast_strlen_zero(cert->attestation)) {
122  ast_log(LOG_ERROR, "Attestation must be present\n");
123  return -1;
124  }
125 
126  private_key = stir_shaken_read_key(cert->path, 1);
127  if (!private_key) {
128  return -1;
129  }
130 
131  cert->private_key = private_key;
132 
133  return 0;
134 }
135 
136 static char *stir_shaken_certificate_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
137 {
138  struct stir_shaken_certificate *cfg;
139 
140  switch(cmd) {
141  case CLI_INIT:
142  e->command = "stir_shaken show certificate";
143  e->usage =
144  "Usage: stir_shaken show certificate <id>\n"
145  " Show the certificate stir/shaken settings for a given id\n";
146  return NULL;
147  case CLI_GENERATE:
148  if (a->pos == 3) {
150  } else {
151  return NULL;
152  }
153  }
154 
155  if (a->argc != 4) {
156  return CLI_SHOWUSAGE;
157  }
158 
159  cfg = stir_shaken_certificate_get(a->argv[3]);
160  stir_shaken_cli_show(cfg, a, 0);
161  ao2_cleanup(cfg);
162 
163  return CLI_SUCCESS;
164 }
165 
166 static char *stir_shaken_certificate_show_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
167 {
168  struct ao2_container *container;
169 
170  switch(cmd) {
171  case CLI_INIT:
172  e->command = "stir_shaken show certificates";
173  e->usage =
174  "Usage: stir_shaken show certificates\n"
175  " Show all configured certificates for stir/shaken\n";
176  return NULL;
177  case CLI_GENERATE:
178  return NULL;
179  }
180 
181  if (a->argc != 3) {
182  return CLI_SHOWUSAGE;
183  }
184 
185  container = stir_shaken_certificate_get_all();
186  if (!container || ao2_container_count(container) == 0) {
187  ast_cli(a->fd, "No stir/shaken certificates found\n");
188  ao2_cleanup(container);
189  return CLI_SUCCESS;
190  }
191 
193  ao2_ref(container, -1);
194 
195  return CLI_SUCCESS;
196 }
197 
199  AST_CLI_DEFINE(stir_shaken_certificate_show, "Show stir/shaken certificate configuration by id"),
200  AST_CLI_DEFINE(stir_shaken_certificate_show_all, "Show all stir/shaken certificate configurations"),
201 };
202 
203 static int on_load_path(const struct aco_option *opt, struct ast_variable *var, void *obj)
204 {
205  struct stir_shaken_certificate *cfg = obj;
206  struct stat statbuf;
207 
208  if (stat(var->value, &statbuf)) {
209  ast_log(LOG_ERROR, "stir/shaken - path '%s' not found\n", var->value);
210  return -1;
211  }
212 
213  if (!S_ISREG(statbuf.st_mode)) {
214  ast_log(LOG_ERROR, "stir/shaken - path '%s' is not a file\n", var->value);
215  return -1;
216  }
217 
218  return ast_string_field_set(cfg, path, var->value);
219 }
220 
221 static int path_to_str(const void *obj, const intptr_t *args, char **buf)
222 {
223  const struct stir_shaken_certificate *cfg = obj;
224 
225  *buf = ast_strdup(cfg->path);
226 
227  return 0;
228 }
229 
230 static int on_load_public_cert_url(const struct aco_option *opt, struct ast_variable *var, void *obj)
231 {
232  struct stir_shaken_certificate *cfg = obj;
233 
234  if (!ast_begins_with(var->value, "http")) {
235  ast_log(LOG_ERROR, "stir/shaken - public_cert_url scheme must be 'http[s]'\n");
236  return -1;
237  }
238 
239  return ast_string_field_set(cfg, public_cert_url, var->value);
240 }
241 
242 static int public_cert_url_to_str(const void *obj, const intptr_t *args, char **buf)
243 {
244  const struct stir_shaken_certificate *cfg = obj;
245 
246  *buf = ast_strdup(cfg->public_cert_url);
247 
248  return 0;
249 }
250 
251 static int on_load_attestation(const struct aco_option *opt, struct ast_variable *var, void *obj)
252 {
253  struct stir_shaken_certificate *cfg = obj;
254 
255  if (strcmp(var->value, "A") && strcmp(var->value, "B") && strcmp(var->value, "C")) {
256  ast_log(LOG_ERROR, "stir/shaken - attestation level must be A, B, or C (object=%s)\n",
258  return -1;
259  }
260 
261  return ast_string_field_set(cfg, attestation, var->value);
262 }
263 
264 static int attestation_to_str(const void *obj, const intptr_t *args, char **buf)
265 {
266  const struct stir_shaken_certificate *cfg = obj;
267 
268  *buf = ast_strdup(cfg->attestation);
269 
270  return 0;
271 }
272 
273 #ifdef TEST_FRAMEWORK
274 
275 /* Name for test certificaate */
276 #define TEST_CONFIG_NAME "test_stir_shaken_certificate"
277 /* The public key URL to use for the test certificate */
278 #define TEST_CONFIG_URL "http://testing123"
279 
280 int test_stir_shaken_cleanup_cert(const char *caller_id_number)
281 {
282  struct stir_shaken_certificate *cert;
283  struct ast_sorcery *sorcery;
284  int res = 0;
285 
286  sorcery = ast_stir_shaken_sorcery();
287 
288  cert = stir_shaken_certificate_get_by_caller_id_number(caller_id_number);
289  if (!cert) {
290  return 0;
291  }
292 
293  res = ast_sorcery_delete(sorcery, cert);
294  ao2_cleanup(cert);
295  if (res) {
296  ast_log(LOG_ERROR, "Failed to delete sorcery object with caller ID "
297  "'%s'\n", caller_id_number);
298  return -1;
299  }
300 
301  res = ast_sorcery_remove_wizard_mapping(sorcery, CONFIG_TYPE, "memory");
302 
303  return res;
304 }
305 
306 int test_stir_shaken_create_cert(const char *caller_id_number, const char *file_path)
307 {
308  struct stir_shaken_certificate *cert;
309  struct ast_sorcery *sorcery;
310  EVP_PKEY *private_key;
311  int res = 0;
312 
313  sorcery = ast_stir_shaken_sorcery();
314 
315  res = ast_sorcery_insert_wizard_mapping(sorcery, CONFIG_TYPE, "memory", "testing", 0, 0);
316  if (res) {
317  ast_log(LOG_ERROR, "Failed to insert STIR/SHAKEN test certificate mapping\n");
318  return -1;
319  }
320 
321  cert = ast_sorcery_alloc(sorcery, CONFIG_TYPE, TEST_CONFIG_NAME);
322  if (!cert) {
323  ast_log(LOG_ERROR, "Failed to allocate test certificate\n");
324  return -1;
325  }
326 
327  ast_string_field_set(cert, path, file_path);
328  ast_string_field_set(cert, public_cert_url, TEST_CONFIG_URL);
330 
331  private_key = stir_shaken_read_key(cert->path, 1);
332  if (!private_key) {
333  ast_log(LOG_ERROR, "Failed to read test key from %s\n", cert->path);
334  test_stir_shaken_cleanup_cert(caller_id_number);
335  return -1;
336  }
337 
338  cert->private_key = private_key;
339 
340  ast_sorcery_create(sorcery, cert);
341 
342  return res;
343 }
344 
345 #endif /* TEST_FRAMEWORK */
346 
348 {
349  ast_cli_unregister_multiple(stir_shaken_certificate_cli,
350  ARRAY_LEN(stir_shaken_certificate_cli));
351 
352  return 0;
353 }
354 
356 {
358 
359  ast_sorcery_apply_default(sorcery, CONFIG_TYPE, "config", "stir_shaken.conf,criteria=type=certificate");
360 
363  ast_log(LOG_ERROR, "stir/shaken - failed to register '%s' sorcery object\n", CONFIG_TYPE);
364  return -1;
365  }
366 
367  ast_sorcery_object_field_register(sorcery, CONFIG_TYPE, "type", "", OPT_NOOP_T, 0, 0);
369  on_load_path, path_to_str, NULL, 0, 0);
370  ast_sorcery_object_field_register_custom(sorcery, CONFIG_TYPE, "public_cert_url", "",
372  ast_sorcery_object_field_register_custom(sorcery, CONFIG_TYPE, "attestation", "",
375 
376  ast_cli_register_multiple(stir_shaken_certificate_cli,
377  ARRAY_LEN(stir_shaken_certificate_cli));
378 
379  return 0;
380 }
static int on_load_path(const struct aco_option *opt, struct ast_variable *var, void *obj)
Definition: certificate.c:203
static char * stir_shaken_certificate_show_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: certificate.c:166
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:197
#define ast_sorcery_remove_wizard_mapping(sorcery, type, name)
Remove an object wizard mapping.
Definition: sorcery.h:758
static const char name[]
Definition: format_mp3.c:68
Asterisk main include file. File version handling, generic pbx functions.
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
#define CONFIG_TYPE
Definition: certificate.c:30
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
static int on_load_public_cert_url(const struct aco_option *opt, struct ast_variable *var, void *obj)
Definition: certificate.c:230
descriptor for a cli entry.
Definition: cli.h:171
const int argc
Definition: cli.h:160
#define ao2_callback(c, flags, cb_fn, arg)
Definition: astobj2.h:1716
Structure for variables, used for configurations and for channel variables.
EVP_PKEY * stir_shaken_read_key(const char *path, int priv)
Reads the public (or private) key from the specified path.
Definition: stir_shaken.c:89
#define var
Definition: ast_expr2f.c:614
Perform no matching, return all objects.
Definition: sorcery.h:123
Definition: cli.h:152
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
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
Definition: stringfields.h:337
Return all matching objects.
Definition: sorcery.h:120
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
const char * args
#define NULL
Definition: resample.c:96
#define ast_sorcery_insert_wizard_mapping(sorcery, type, name, data, caching, position)
Insert an additional object wizard mapping at a specific position in the wizard list.
Definition: sorcery.h:563
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
static void * stir_shaken_certificate_alloc(const char *name)
Definition: certificate.c:67
const ast_string_field attestation
Definition: certificate.c:43
const char * stir_shaken_certificate_get_attestation(struct stir_shaken_certificate *cert)
Get the attestation level associated with a certificate.
Definition: certificate.c:101
static void stir_shaken_certificate_destructor(void *obj)
Definition: certificate.c:59
char * stir_shaken_tab_complete_name(const char *word, struct ao2_container *container)
Tab completion for name matching with STIR/SHAKEN CLI commands.
Definition: stir_shaken.c:66
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
int stir_shaken_certificate_load(void)
Load time initialization for the stir/shaken &#39;certificate&#39; configuration.
Definition: certificate.c:355
#define ast_log
Definition: astobj2.c:42
int ast_sorcery_create(const struct ast_sorcery *sorcery, void *object)
Create and potentially persist an object using an available wizard.
Definition: sorcery.c:2057
#define ast_sorcery_object_field_register_custom(sorcery, type, name, default_val, config_handler, sorcery_handler, multiple_handler, flags,...)
Register a field within an object with custom handlers.
Definition: sorcery.h:1005
static int public_cert_url_to_str(const void *obj, const intptr_t *args, char **buf)
Definition: certificate.c:242
const int fd
Definition: cli.h:159
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:353
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
#define AST_STRING_FIELD(name)
Declare a string field.
Definition: stringfields.h:299
#define ao2_ref(o, delta)
Definition: astobj2.h:464
int stir_shaken_certificate_unload(void)
Unload time cleanup for the stir/shaken &#39;certificate&#39; configuration.
Definition: certificate.c:347
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2312
struct ao2_container * container
Definition: res_fax.c:502
int stir_shaken_cli_show(void *obj, void *arg, int flags)
Output configuration settings to the Asterisk CLI.
Definition: stir_shaken.c:35
static struct ast_cli_entry stir_shaken_certificate_cli[]
Definition: certificate.c:198
#define ast_sorcery_object_register(sorcery, type, alloc, transform, apply)
Register an object type.
Definition: sorcery.h:838
#define ARRAY_LEN(a)
Definition: utils.h:639
Default retrieval flags.
Definition: sorcery.h:117
const char *const * argv
Definition: cli.h:161
int ast_sorcery_delete(const struct ast_sorcery *sorcery, void *object)
Delete an object.
Definition: sorcery.c:2233
#define LOG_ERROR
Definition: logger.h:285
struct stir_shaken_certificate * stir_shaken_certificate_get_by_caller_id_number(const char *caller_id_number)
Get a STIR/SHAKEN certificate by caller ID number.
Definition: certificate.c:84
#define CLI_SHOWUSAGE
Definition: cli.h:45
#define ast_sorcery_apply_default(sorcery, type, name, data)
Definition: sorcery.h:477
static struct stir_shaken_certificate * stir_shaken_certificate_get(const char *id)
Definition: certificate.c:48
void * ast_sorcery_alloc(const struct ast_sorcery *sorcery, const char *type, const char *id)
Allocate an object.
Definition: sorcery.c:1744
static struct ao2_container * stir_shaken_certificate_get_all(void)
Definition: certificate.c:53
char * command
Definition: cli.h:186
struct ast_sorcery * ast_stir_shaken_sorcery(void)
Retrieve the stir/shaken sorcery context.
const char * word
Definition: cli.h:163
#define STRFLDSET(type,...)
Convert a struct and a list of stringfield fields to an argument list of field offsets.
const ast_string_field caller_id_number
Definition: certificate.c:43
const ast_string_field public_cert_url
Definition: certificate.c:43
const char * usage
Definition: cli.h:177
void * ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const char *type, unsigned int flags, struct ast_variable *fields)
Retrieve an object or multiple objects using specific fields.
Definition: sorcery.c:1897
#define ast_sorcery_object_field_register(sorcery, type, name, default_val, opt_type, flags,...)
Register a field within an object.
Definition: sorcery.h:955
#define CLI_SUCCESS
Definition: cli.h:44
static int path_to_str(const void *obj, const intptr_t *args, char **buf)
Definition: certificate.c:221
static struct ast_sorcery * sorcery
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
Standard Command Line Interface.
EVP_PKEY * stir_shaken_certificate_get_private_key(struct stir_shaken_certificate *cert)
Get the private key associated with a certificate.
Definition: certificate.c:106
const int pos
Definition: cli.h:164
static int force_inline attribute_pure ast_begins_with(const char *str, const char *prefix)
Definition: strings.h:94
static int stir_shaken_certificate_apply(const struct ast_sorcery *sorcery, void *obj)
Definition: certificate.c:111
Type for default option handler for stringfields.
const ast_string_field path
Definition: certificate.c:43
static char * stir_shaken_certificate_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: certificate.c:136
Generic container type.
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:368
static int attestation_to_str(const void *obj, const intptr_t *args, char **buf)
Definition: certificate.c:264
static int on_load_attestation(const struct aco_option *opt, struct ast_variable *var, void *obj)
Definition: certificate.c:251
const char * stir_shaken_certificate_get_public_cert_url(struct stir_shaken_certificate *cert)
Get the public key URL associated with a certificate.
Definition: certificate.c:96
Sorcery Data Access Layer API.
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:514
static struct test_val a