loader: Sync load- and build-time deps.
[asterisk/asterisk.git] / res / res_stir_shaken / certificate.c
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"
28 #include "asterisk/res_stir_shaken.h"
29
30 #define CONFIG_TYPE "certificate"
31
32 struct stir_shaken_certificate {
33         SORCERY_OBJECT(details);
34         AST_DECLARE_STRING_FIELDS(
35                 /*! Path to a directory containing certificates */
36                 AST_STRING_FIELD(path);
37                 /*! URL to the public key */
38                 AST_STRING_FIELD(public_key_url);
39                 /*! The caller ID number associated with the certificate */
40                 AST_STRING_FIELD(caller_id_number);
41                 /*! The attestation level for this certificate */
42                 AST_STRING_FIELD(attestation);
43                 /*! The origination ID for this certificate */
44                 AST_STRING_FIELD(origid);
45         );
46         /*! The private key for the certificate */
47         EVP_PKEY *private_key;
48 };
49
50 static struct stir_shaken_certificate *stir_shaken_certificate_get(const char *id)
51 {
52         return ast_sorcery_retrieve_by_id(ast_stir_shaken_sorcery(), CONFIG_TYPE, id);
53 }
54
55 static struct ao2_container *stir_shaken_certificate_get_all(void)
56 {
57         return ast_sorcery_retrieve_by_fields(ast_stir_shaken_sorcery(), CONFIG_TYPE,
58                 AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
59 }
60
61 static void stir_shaken_certificate_destructor(void *obj)
62 {
63         struct stir_shaken_certificate *cfg = obj;
64
65         EVP_PKEY_free(cfg->private_key);
66         ast_string_field_free_memory(cfg);
67 }
68
69 static void *stir_shaken_certificate_alloc(const char *name)
70 {
71         struct stir_shaken_certificate *cfg;
72
73         cfg = ast_sorcery_generic_alloc(sizeof(*cfg), stir_shaken_certificate_destructor);
74         if (!cfg) {
75                 return NULL;
76         }
77
78         if (ast_string_field_init(cfg, 512)) {
79                 ao2_ref(cfg, -1);
80                 return NULL;
81         }
82
83         return cfg;
84 }
85
86 struct stir_shaken_certificate *stir_shaken_certificate_get_by_caller_id_number(const char *caller_id_number)
87 {
88         struct ast_variable fields = {
89                 .name = "caller_id_number",
90                 .value = caller_id_number,
91                 .next = NULL,
92         };
93
94         return ast_sorcery_retrieve_by_fields(ast_stir_shaken_sorcery(),
95                 "certificate", AST_RETRIEVE_FLAG_DEFAULT, &fields);
96 }
97
98 const char *stir_shaken_certificate_get_public_key_url(struct stir_shaken_certificate *cert)
99 {
100         return cert ? cert->public_key_url : NULL;
101 }
102
103 const char *stir_shaken_certificate_get_attestation(struct stir_shaken_certificate *cert)
104 {
105         return cert ? cert->attestation : NULL;
106 }
107
108 const char *stir_shaken_certificate_get_origid(struct stir_shaken_certificate *cert)
109 {
110         return cert ? cert->origid : NULL;
111 }
112
113 EVP_PKEY *stir_shaken_certificate_get_private_key(struct stir_shaken_certificate *cert)
114 {
115         return cert ? cert->private_key : NULL;
116 }
117
118 static int stir_shaken_certificate_apply(const struct ast_sorcery *sorcery, void *obj)
119 {
120         EVP_PKEY *private_key;
121         struct stir_shaken_certificate *cert = obj;
122
123         if (ast_strlen_zero(cert->caller_id_number)) {
124                 ast_log(LOG_ERROR, "Caller ID must be present\n");
125                 return -1;
126         }
127
128         if (ast_strlen_zero(cert->attestation)) {
129                 ast_log(LOG_ERROR, "Attestation must be present\n");
130                 return -1;
131         }
132
133         private_key = stir_shaken_read_key(cert->path, 1);
134         if (!private_key) {
135                 return -1;
136         }
137
138         cert->private_key = private_key;
139
140         return 0;
141 }
142
143 static char *stir_shaken_certificate_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
144 {
145         struct stir_shaken_certificate *cfg;
146
147         switch(cmd) {
148         case CLI_INIT:
149                 e->command = "stir_shaken show certificate";
150                 e->usage =
151                         "Usage: stir_shaken show certificate <id>\n"
152                         "       Show the certificate stir/shaken settings for a given id\n";
153                 return NULL;
154         case CLI_GENERATE:
155                 if (a->pos == 3) {
156                         return stir_shaken_tab_complete_name(a->word, stir_shaken_certificate_get_all());
157                 } else {
158                         return NULL;
159                 }
160         }
161
162         if (a->argc != 4) {
163                 return CLI_SHOWUSAGE;
164         }
165
166         cfg = stir_shaken_certificate_get(a->argv[3]);
167         stir_shaken_cli_show(cfg, a, 0);
168         ao2_cleanup(cfg);
169
170         return CLI_SUCCESS;
171 }
172
173 static char *stir_shaken_certificate_show_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
174 {
175         struct ao2_container *container;
176
177         switch(cmd) {
178         case CLI_INIT:
179                 e->command = "stir_shaken show certificates";
180                 e->usage =
181                         "Usage: stir_shaken show certificates\n"
182                         "       Show all configured certificates for stir/shaken\n";
183                 return NULL;
184         case CLI_GENERATE:
185                 return NULL;
186         }
187
188         if (a->argc != 3) {
189                 return CLI_SHOWUSAGE;
190         }
191
192         container = stir_shaken_certificate_get_all();
193         if (!container || ao2_container_count(container) == 0) {
194                 ast_cli(a->fd, "No stir/shaken certificates found\n");
195                 ao2_cleanup(container);
196                 return CLI_SUCCESS;
197         }
198
199         ao2_callback(container, OBJ_NODATA, stir_shaken_cli_show, a);
200         ao2_ref(container, -1);
201
202         return CLI_SUCCESS;
203 }
204
205 static struct ast_cli_entry stir_shaken_certificate_cli[] = {
206         AST_CLI_DEFINE(stir_shaken_certificate_show, "Show stir/shaken certificate configuration by id"),
207         AST_CLI_DEFINE(stir_shaken_certificate_show_all, "Show all stir/shaken certificate configurations"),
208 };
209
210 static int on_load_path(const struct aco_option *opt, struct ast_variable *var, void *obj)
211 {
212         struct stir_shaken_certificate *cfg = obj;
213         struct stat statbuf;
214
215         if (stat(var->value, &statbuf)) {
216                 ast_log(LOG_ERROR, "stir/shaken - path '%s' not found\n", var->value);
217                 return -1;
218         }
219
220         if (!S_ISREG(statbuf.st_mode)) {
221                 ast_log(LOG_ERROR, "stir/shaken - path '%s' is not a file\n", var->value);
222                 return -1;
223         }
224
225         return ast_string_field_set(cfg, path, var->value);
226 }
227
228 static int path_to_str(const void *obj, const intptr_t *args, char **buf)
229 {
230         const struct stir_shaken_certificate *cfg = obj;
231
232         *buf = ast_strdup(cfg->path);
233
234         return 0;
235 }
236
237 static int on_load_public_key_url(const struct aco_option *opt, struct ast_variable *var, void *obj)
238 {
239         struct stir_shaken_certificate *cfg = obj;
240
241         if (!ast_begins_with(var->value, "http")) {
242                 ast_log(LOG_ERROR, "stir/shaken - public_key_url scheme must be 'http[s]'\n");
243                 return -1;
244         }
245
246         return ast_string_field_set(cfg, public_key_url, var->value);
247 }
248
249 static int public_key_url_to_str(const void *obj, const intptr_t *args, char **buf)
250 {
251         const struct stir_shaken_certificate *cfg = obj;
252
253         *buf = ast_strdup(cfg->public_key_url);
254
255         return 0;
256 }
257
258 static int on_load_attestation(const struct aco_option *opt, struct ast_variable *var, void *obj)
259 {
260         struct stir_shaken_certificate *cfg = obj;
261
262         if (strcmp(var->value, "A") && strcmp(var->value, "B") && strcmp(var->value, "C")) {
263                 ast_log(LOG_ERROR, "stir/shaken - attestation level must be A, B, or C (object=%s)\n",
264                         ast_sorcery_object_get_id(cfg));
265                 return -1;
266         }
267
268         return ast_string_field_set(cfg, attestation, var->value);
269 }
270
271 static int attestation_to_str(const void *obj, const intptr_t *args, char **buf)
272 {
273         const struct stir_shaken_certificate *cfg = obj;
274
275         *buf = ast_strdup(cfg->attestation);
276
277         return 0;
278 }
279
280 #ifdef TEST_FRAMEWORK
281
282 /* Name for test certificaate */
283 #define TEST_CONFIG_NAME "test_stir_shaken_certificate"
284 /* The public key URL to use for the test certificate */
285 #define TEST_CONFIG_URL "http://testing123"
286
287 int test_stir_shaken_cleanup_cert(const char *caller_id_number)
288 {
289         struct stir_shaken_certificate *cert;
290         struct ast_sorcery *sorcery;
291         int res = 0;
292
293         sorcery = ast_stir_shaken_sorcery();
294
295         cert = stir_shaken_certificate_get_by_caller_id_number(caller_id_number);
296         if (!cert) {
297                 return 0;
298         }
299
300         res = ast_sorcery_delete(sorcery, cert);
301         ao2_cleanup(cert);
302         if (res) {
303                 ast_log(LOG_ERROR, "Failed to delete sorcery object with caller ID "
304                         "'%s'\n", caller_id_number);
305                 return -1;
306         }
307
308         res = ast_sorcery_remove_wizard_mapping(sorcery, CONFIG_TYPE, "memory");
309
310         return res;
311 }
312
313 int test_stir_shaken_create_cert(const char *caller_id_number, const char *file_path)
314 {
315         struct stir_shaken_certificate *cert;
316         struct ast_sorcery *sorcery;
317         EVP_PKEY *private_key;
318         int res = 0;
319
320         sorcery = ast_stir_shaken_sorcery();
321
322         res = ast_sorcery_insert_wizard_mapping(sorcery, CONFIG_TYPE, "memory", "testing", 0, 0);
323         if (res) {
324                 ast_log(LOG_ERROR, "Failed to insert STIR/SHAKEN test certificate mapping\n");
325                 return -1;
326         }
327
328         cert = ast_sorcery_alloc(sorcery, CONFIG_TYPE, TEST_CONFIG_NAME);
329         if (!cert) {
330                 ast_log(LOG_ERROR, "Failed to allocate test certificate\n");
331                 return -1;
332         }
333
334         ast_string_field_set(cert, path, file_path);
335         ast_string_field_set(cert, public_key_url, TEST_CONFIG_URL);
336         ast_string_field_set(cert, caller_id_number, caller_id_number);
337
338         private_key = stir_shaken_read_key(cert->path, 1);
339         if (!private_key) {
340                 ast_log(LOG_ERROR, "Failed to read test key from %s\n", cert->path);
341                 test_stir_shaken_cleanup_cert(caller_id_number);
342                 return -1;
343         }
344
345         cert->private_key = private_key;
346
347         ast_sorcery_create(sorcery, cert);
348
349         return res;
350 }
351
352 #endif /* TEST_FRAMEWORK */
353
354 int stir_shaken_certificate_unload(void)
355 {
356         ast_cli_unregister_multiple(stir_shaken_certificate_cli,
357                 ARRAY_LEN(stir_shaken_certificate_cli));
358
359         return 0;
360 }
361
362 int stir_shaken_certificate_load(void)
363 {
364         struct ast_sorcery *sorcery = ast_stir_shaken_sorcery();
365
366         ast_sorcery_apply_default(sorcery, CONFIG_TYPE, "config", "stir_shaken.conf,criteria=type=certificate");
367
368         if (ast_sorcery_object_register(sorcery, CONFIG_TYPE, stir_shaken_certificate_alloc,
369                         NULL, stir_shaken_certificate_apply)) {
370                 ast_log(LOG_ERROR, "stir/shaken - failed to register '%s' sorcery object\n", CONFIG_TYPE);
371                 return -1;
372         }
373
374         ast_sorcery_object_field_register(sorcery, CONFIG_TYPE, "type", "", OPT_NOOP_T, 0, 0);
375         ast_sorcery_object_field_register_custom(sorcery, CONFIG_TYPE, "path", "",
376                 on_load_path, path_to_str, NULL, 0, 0);
377         ast_sorcery_object_field_register_custom(sorcery, CONFIG_TYPE, "public_key_url", "",
378                 on_load_public_key_url, public_key_url_to_str, NULL, 0, 0);
379         ast_sorcery_object_field_register_custom(sorcery, CONFIG_TYPE, "attestation", "",
380                 on_load_attestation, attestation_to_str, NULL, 0, 0);
381         ast_sorcery_object_field_register(sorcery, CONFIG_TYPE, "origid", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct stir_shaken_certificate, origid));
382         ast_sorcery_object_field_register(sorcery, CONFIG_TYPE, "caller_id_number", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct stir_shaken_certificate, caller_id_number));
383
384         ast_cli_register_multiple(stir_shaken_certificate_cli,
385                 ARRAY_LEN(stir_shaken_certificate_cli));
386
387         return 0;
388 }