loader: Sync load- and build-time deps.
[asterisk/asterisk.git] / res / res_stir_shaken / general.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 "asterisk/cli.h"
22 #include "asterisk/sorcery.h"
23
24 #include "stir_shaken.h"
25 #include "general.h"
26 #include "asterisk/res_stir_shaken.h"
27
28 #define CONFIG_TYPE "general"
29
30 #define DEFAULT_CA_FILE ""
31 #define DEFAULT_CA_PATH ""
32 #define DEFAULT_CACHE_MAX_SIZE 1000
33 #define DEFAULT_CURL_TIMEOUT 2
34 #define DEFAULT_SIGNATURE_TIMEOUT 15
35
36 struct stir_shaken_general {
37         SORCERY_OBJECT(details);
38         AST_DECLARE_STRING_FIELDS(
39                 /*! File path to a certificate authority */
40                 AST_STRING_FIELD(ca_file);
41                 /*! File path to a chain of trust */
42                 AST_STRING_FIELD(ca_path);
43         );
44         /*! Maximum size of public keys cache */
45         unsigned int cache_max_size;
46         /*! Maximum time to wait to CURL certificates */
47         unsigned int curl_timeout;
48         /*! Amount of time a signature is valid for */
49         unsigned int signature_timeout;
50 };
51
52 static struct stir_shaken_general *default_config = NULL;
53
54 struct stir_shaken_general *stir_shaken_general_get()
55 {
56         struct stir_shaken_general *cfg;
57         struct ao2_container *container;
58
59         container = ast_sorcery_retrieve_by_fields(ast_stir_shaken_sorcery(), CONFIG_TYPE,
60                 AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
61         if (!container || ao2_container_count(container) == 0) {
62                 ao2_cleanup(container);
63                 return ao2_bump(default_config);
64         }
65
66         cfg = ao2_find(container, NULL, 0);
67         ao2_ref(container, -1);
68
69         return cfg;
70 }
71
72 const char *ast_stir_shaken_ca_file(const struct stir_shaken_general *cfg)
73 {
74         return cfg ? cfg->ca_file : DEFAULT_CA_FILE;
75 }
76
77 const char *ast_stir_shaken_ca_path(const struct stir_shaken_general *cfg)
78 {
79         return cfg ? cfg->ca_path : DEFAULT_CA_PATH;
80 }
81
82 unsigned int ast_stir_shaken_cache_max_size(const struct stir_shaken_general *cfg)
83 {
84         return cfg ? cfg->cache_max_size : DEFAULT_CACHE_MAX_SIZE;
85 }
86
87 unsigned int ast_stir_shaken_curl_timeout(const struct stir_shaken_general *cfg)
88 {
89         return cfg ? cfg->curl_timeout : DEFAULT_CURL_TIMEOUT;
90 }
91
92 unsigned int ast_stir_shaken_signature_timeout(const struct stir_shaken_general *cfg)
93 {
94         return cfg ? cfg->signature_timeout : DEFAULT_SIGNATURE_TIMEOUT;
95 }
96
97 static void stir_shaken_general_destructor(void *obj)
98 {
99         struct stir_shaken_general *cfg = obj;
100
101         ast_string_field_free_memory(cfg);
102 }
103
104 static void *stir_shaken_general_alloc(const char *name)
105 {
106         struct stir_shaken_general *cfg;
107
108         cfg = ast_sorcery_generic_alloc(sizeof(*cfg), stir_shaken_general_destructor);
109         if (!cfg) {
110                 return NULL;
111         }
112
113         if (ast_string_field_init(cfg, 512)) {
114                 ao2_ref(cfg, -1);
115                 return NULL;
116         }
117
118         return cfg;
119 }
120
121 static int stir_shaken_general_apply(const struct ast_sorcery *sorcery, void *obj)
122 {
123         return 0;
124 }
125
126 static void stir_shaken_general_loaded(const char *name, const struct ast_sorcery *sorcery,
127         const char *object_type, int reloaded)
128 {
129         struct stir_shaken_general *cfg;
130
131         if (strcmp(object_type, CONFIG_TYPE)) {
132                 /* Not interested */
133                 return;
134         }
135
136         if (default_config) {
137                 ao2_ref(default_config, -1);
138                 default_config = NULL;
139         }
140
141         cfg = stir_shaken_general_get();
142         if (cfg) {
143                 ao2_ref(cfg, -1);
144                 return;
145         }
146
147         /* Use the default configuration if on is not specified */
148         default_config = ast_sorcery_alloc(sorcery, CONFIG_TYPE, NULL);
149         if (default_config) {
150                 stir_shaken_general_apply(sorcery, default_config);
151         }
152 }
153
154 static const struct ast_sorcery_instance_observer stir_shaken_general_observer = {
155         .object_type_loaded = stir_shaken_general_loaded,
156 };
157
158 static char *stir_shaken_general_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
159 {
160         struct stir_shaken_general *cfg;
161
162         switch(cmd) {
163         case CLI_INIT:
164                 e->command = "stir_shaken show general";
165                 e->usage =
166                         "Usage: stir_shaken show general\n"
167                         "       Show the general stir/shaken settings\n";
168                 return NULL;
169         case CLI_GENERATE:
170                 return NULL;
171         }
172
173         if (a->argc != 3) {
174                 return CLI_SHOWUSAGE;
175         }
176
177         cfg = stir_shaken_general_get();
178         stir_shaken_cli_show(cfg, a, 0);
179         ao2_cleanup(cfg);
180
181         return CLI_SUCCESS;
182 }
183
184 static struct ast_cli_entry stir_shaken_general_cli[] = {
185         AST_CLI_DEFINE(stir_shaken_general_show, "Show stir/shaken general configuration"),
186 };
187
188 static int on_load_ca_file(const struct aco_option *opt, struct ast_variable *var, void *obj)
189 {
190         struct stir_shaken_general *cfg = obj;
191
192         if (!ast_file_is_readable(var->value)) {
193                 ast_log(LOG_ERROR, "stir/shaken - %s '%s' not found, or is unreadable\n",
194                                 var->name, var->value);
195                 return -1;
196         }
197
198         return ast_string_field_set(cfg, ca_file, var->value);
199 }
200
201 static int ca_file_to_str(const void *obj, const intptr_t *args, char **buf)
202 {
203         const struct stir_shaken_general *cfg = obj;
204
205         *buf = ast_strdup(cfg->ca_file);
206
207         return 0;
208 }
209
210 static int on_load_ca_path(const struct aco_option *opt, struct ast_variable *var, void *obj)
211 {
212         struct stir_shaken_general *cfg = obj;
213
214         if (!ast_file_is_readable(var->value)) {
215                 ast_log(LOG_ERROR, "stir/shaken - %s '%s' not found, or is unreadable\n",
216                                 var->name, var->value);
217                 return -1;
218         }
219
220         return ast_string_field_set(cfg, ca_path, var->value);
221 }
222
223 static int ca_path_to_str(const void *obj, const intptr_t *args, char **buf)
224 {
225         const struct stir_shaken_general *cfg = obj;
226
227         *buf = ast_strdup(cfg->ca_path);
228
229         return 0;
230 }
231
232 int stir_shaken_general_unload(void)
233 {
234         ast_cli_unregister_multiple(stir_shaken_general_cli,
235                 ARRAY_LEN(stir_shaken_general_cli));
236
237         ast_sorcery_instance_observer_remove(ast_stir_shaken_sorcery(),
238                 &stir_shaken_general_observer);
239
240         if (default_config) {
241                 ao2_ref(default_config, -1);
242                 default_config = NULL;
243         }
244
245         return 0;
246 }
247
248 int stir_shaken_general_load(void)
249 {
250         struct ast_sorcery *sorcery = ast_stir_shaken_sorcery();
251
252         ast_sorcery_apply_default(sorcery, CONFIG_TYPE, "config",
253                 "stir_shaken.conf,criteria=type=general,single_object=yes,explicit_name=general");
254
255         if (ast_sorcery_object_register(sorcery, CONFIG_TYPE, stir_shaken_general_alloc,
256                         NULL, stir_shaken_general_apply)) {
257                 ast_log(LOG_ERROR, "stir/shaken - failed to register '%s' sorcery object\n", CONFIG_TYPE);
258                 return -1;
259         }
260
261         ast_sorcery_object_field_register(sorcery, CONFIG_TYPE, "type", "", OPT_NOOP_T, 0, 0);
262         ast_sorcery_object_field_register_custom(sorcery, CONFIG_TYPE, "ca_file",
263                 DEFAULT_CA_FILE, on_load_ca_file, ca_file_to_str, NULL, 0, 0);
264         ast_sorcery_object_field_register_custom(sorcery, CONFIG_TYPE, "ca_path",
265                 DEFAULT_CA_PATH, on_load_ca_path, ca_path_to_str, NULL, 0, 0);
266         ast_sorcery_object_field_register(sorcery, CONFIG_TYPE, "cache_max_size",
267                 __stringify(DEFAULT_CACHE_MAX_SIZE), OPT_UINT_T, 0,
268                 FLDSET(struct stir_shaken_general, cache_max_size));
269         ast_sorcery_object_field_register(sorcery, CONFIG_TYPE, "curl_timeout",
270                 __stringify(DEFAULT_CURL_TIMEOUT), OPT_UINT_T, 0,
271                 FLDSET(struct stir_shaken_general, curl_timeout));
272         ast_sorcery_object_field_register(sorcery, CONFIG_TYPE, "signature_timeout",
273                 __stringify(DEFAULT_SIGNATURE_TIMEOUT), OPT_UINT_T, 0,
274                 FLDSET(struct stir_shaken_general, signature_timeout));
275
276         if (ast_sorcery_instance_observer_add(sorcery, &stir_shaken_general_observer)) {
277                 ast_log(LOG_ERROR, "stir/shaken - failed to register loaded observer for '%s' "
278                                 "sorcery object type\n", CONFIG_TYPE);
279                 return -1;
280         }
281
282         ast_cli_register_multiple(stir_shaken_general_cli,
283                 ARRAY_LEN(stir_shaken_general_cli));
284
285         return 0;
286 }