AST-2018-005: res_pjsip_transport_management: Move to core
[asterisk/asterisk.git] / res / res_pjsip / config_auth.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * Mark Michelson <mmichelson@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 <pjsip.h>
22 #include <pjlib.h>
23 #include "asterisk/res_pjsip.h"
24 #include "asterisk/logger.h"
25 #include "asterisk/sorcery.h"
26 #include "asterisk/cli.h"
27 #include "include/res_pjsip_private.h"
28 #include "asterisk/res_pjsip_cli.h"
29
30 static void auth_destroy(void *obj)
31 {
32         struct ast_sip_auth *auth = obj;
33         ast_string_field_free_memory(auth);
34 }
35
36 static void *auth_alloc(const char *name)
37 {
38         struct ast_sip_auth *auth = ast_sorcery_generic_alloc(sizeof(*auth), auth_destroy);
39
40         if (!auth) {
41                 return NULL;
42         }
43
44         if (ast_string_field_init(auth, 64)) {
45                 ao2_cleanup(auth);
46                 return NULL;
47         }
48
49         return auth;
50 }
51
52 static int auth_type_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
53 {
54         struct ast_sip_auth *auth = obj;
55         if (!strcasecmp(var->value, "userpass")) {
56                 auth->type = AST_SIP_AUTH_TYPE_USER_PASS;
57         } else if (!strcasecmp(var->value, "md5")) {
58                 auth->type = AST_SIP_AUTH_TYPE_MD5;
59         } else {
60                 ast_log(LOG_WARNING, "Unknown authentication storage type '%s' specified for %s\n",
61                                 var->value, var->name);
62                 return -1;
63         }
64         return 0;
65 }
66
67 static const char *auth_types_map[] = {
68         [AST_SIP_AUTH_TYPE_USER_PASS] = "userpass",
69         [AST_SIP_AUTH_TYPE_MD5] = "md5"
70 };
71
72 const char *ast_sip_auth_type_to_str(enum ast_sip_auth_type type)
73 {
74         return ARRAY_IN_BOUNDS(type, auth_types_map) ?
75                 auth_types_map[type] : "";
76 }
77
78 static int auth_type_to_str(const void *obj, const intptr_t *args, char **buf)
79 {
80         const struct ast_sip_auth *auth = obj;
81         *buf = ast_strdup(ast_sip_auth_type_to_str(auth->type));
82         return 0;
83 }
84
85 static int auth_apply(const struct ast_sorcery *sorcery, void *obj)
86 {
87         struct ast_sip_auth *auth = obj;
88         int res = 0;
89
90         if (ast_strlen_zero(auth->auth_user)) {
91                 ast_log(LOG_ERROR, "No authentication username for auth '%s'\n",
92                                 ast_sorcery_object_get_id(auth));
93                 return -1;
94         }
95
96         switch (auth->type) {
97         case AST_SIP_AUTH_TYPE_MD5:
98                 if (ast_strlen_zero(auth->md5_creds)) {
99                         ast_log(LOG_ERROR, "'md5' authentication specified but no md5_cred "
100                                         "specified for auth '%s'\n", ast_sorcery_object_get_id(auth));
101                         res = -1;
102                 } else if (strlen(auth->md5_creds) != PJSIP_MD5STRLEN) {
103                         ast_log(LOG_ERROR, "'md5' authentication requires digest of size '%d', but "
104                                 "digest is '%d' in size for auth '%s'\n", PJSIP_MD5STRLEN, (int)strlen(auth->md5_creds),
105                                 ast_sorcery_object_get_id(auth));
106                         res = -1;
107                 }
108                 break;
109         case AST_SIP_AUTH_TYPE_USER_PASS:
110         case AST_SIP_AUTH_TYPE_ARTIFICIAL:
111                 break;
112         }
113
114         return res;
115 }
116
117 int ast_sip_for_each_auth(const struct ast_sip_auth_vector *vector,
118                           ao2_callback_fn on_auth, void *arg)
119 {
120         int i;
121
122         if (!vector || !AST_VECTOR_SIZE(vector)) {
123                 return 0;
124         }
125
126         for (i = 0; i < AST_VECTOR_SIZE(vector); ++i) {
127                 /* AST_VECTOR_GET is safe to use since the vector is immutable */
128                 RAII_VAR(struct ast_sip_auth *, auth, ast_sorcery_retrieve_by_id(
129                                  ast_sip_get_sorcery(), SIP_SORCERY_AUTH_TYPE,
130                                  AST_VECTOR_GET(vector,i)), ao2_cleanup);
131
132                 if (!auth) {
133                         continue;
134                 }
135
136                 if (on_auth(auth, arg, 0)) {
137                         return -1;
138                 }
139         }
140
141         return 0;
142 }
143
144 static int sip_auth_to_ami(const struct ast_sip_auth *auth,
145                            struct ast_str **buf)
146 {
147         return ast_sip_sorcery_object_to_ami(auth, buf);
148 }
149
150 static int format_ami_auth_handler(void *obj, void *arg, int flags)
151 {
152         const struct ast_sip_auth *auth = obj;
153         struct ast_sip_ami *ami = arg;
154         const struct ast_sip_endpoint *endpoint = ami->arg;
155         RAII_VAR(struct ast_str *, buf,
156                  ast_sip_create_ami_event("AuthDetail", ami), ast_free);
157
158         if (!buf) {
159                 return -1;
160         }
161
162         if (sip_auth_to_ami(auth, &buf)) {
163                 return -1;
164         }
165
166         if (endpoint) {
167                 ast_str_append(&buf, 0, "EndpointName: %s\r\n",
168                        ast_sorcery_object_get_id(endpoint));
169         }
170
171         astman_append(ami->s, "%s\r\n", ast_str_buffer(buf));
172         ami->count++;
173
174         return 0;
175 }
176
177 int ast_sip_format_auths_ami(const struct ast_sip_auth_vector *auths,
178                              struct ast_sip_ami *ami)
179 {
180         return ast_sip_for_each_auth(auths, format_ami_auth_handler, ami);
181 }
182
183 static int format_ami_endpoint_auth(const struct ast_sip_endpoint *endpoint,
184                                     struct ast_sip_ami *ami)
185 {
186         ami->arg = (void *)endpoint;
187         if (ast_sip_format_auths_ami(&endpoint->inbound_auths, ami)) {
188                 return -1;
189         }
190
191         return ast_sip_format_auths_ami(&endpoint->outbound_auths, ami);
192 }
193
194 static struct ast_sip_endpoint_formatter endpoint_auth_formatter = {
195         .format_ami = format_ami_endpoint_auth
196 };
197
198 static struct ao2_container *cli_get_auths(void)
199 {
200         struct ao2_container *auths;
201
202         auths = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "auth",
203                         AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
204
205         return auths;
206 }
207
208 static int format_ami_authlist_handler(void *obj, void *arg, int flags)
209 {
210         struct ast_sip_auth *auth = obj;
211         struct ast_sip_ami *ami = arg;
212         struct ast_str *buf;
213
214         buf = ast_sip_create_ami_event("AuthList", ami);
215         if (!buf) {
216                 return CMP_STOP;
217         }
218
219         sip_auth_to_ami(auth, &buf);
220
221         astman_append(ami->s, "%s\r\n", ast_str_buffer(buf));
222         ami->count++;
223
224         ast_free(buf);
225
226         return 0;
227 }
228
229 static int ami_show_auths(struct mansession *s, const struct message *m)
230 {
231         struct ast_sip_ami ami = { .s = s, .m = m, .action_id = astman_get_header(m, "ActionID"), };
232         struct ao2_container *auths;
233
234         auths = cli_get_auths();
235         if (!auths) {
236                 astman_send_error(s, m, "Could not get Auths\n");
237                 return 0;
238         }
239
240         if (!ao2_container_count(auths)) {
241                 astman_send_error(s, m, "No Auths found\n");
242                 ao2_ref(auths, -1);
243                 return 0;
244         }
245
246         astman_send_listack(s, m, "A listing of Auths follows, presented as AuthList events",
247                         "start");
248
249         ao2_callback(auths, OBJ_NODATA, format_ami_authlist_handler, &ami);
250
251         astman_send_list_complete_start(s, m, "AuthListComplete", ami.count);
252         astman_send_list_complete_end(s);
253
254         ao2_ref(auths, -1);
255
256         return 0;
257 }
258
259 static struct ao2_container *cli_get_container(const char *regex)
260 {
261         RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup);
262         struct ao2_container *s_container;
263
264         container = ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "auth", regex);
265         if (!container) {
266                 return NULL;
267         }
268
269         s_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
270                 ast_sorcery_object_id_sort, ast_sorcery_object_id_compare);
271         if (!s_container) {
272                 return NULL;
273         }
274
275         if (ao2_container_dup(s_container, container, 0)) {
276                 ao2_ref(s_container, -1);
277                 return NULL;
278         }
279
280         return s_container;
281 }
282
283 static int cli_iterator(void *container, ao2_callback_fn callback, void *args)
284 {
285         return ast_sip_for_each_auth(container, callback, args);
286 }
287
288 static void *cli_retrieve_by_id(const char *id)
289 {
290         return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), SIP_SORCERY_AUTH_TYPE, id);
291 }
292
293 static int cli_print_header(void *obj, void *arg, int flags)
294 {
295         struct ast_sip_cli_context *context = arg;
296         int indent = CLI_INDENT_TO_SPACES(context->indent_level);
297         int filler = CLI_MAX_WIDTH - indent - 20;
298
299         ast_assert(context->output_buffer != NULL);
300
301         ast_str_append(&context->output_buffer, 0,
302                 "%*s:  <AuthId/UserName%*.*s>\n", indent, "I/OAuth", filler, filler,
303                 CLI_HEADER_FILLER);
304
305         return 0;
306 }
307
308 static int cli_print_body(void *obj, void *arg, int flags)
309 {
310         struct ast_sip_auth *auth = obj;
311         struct ast_sip_cli_context *context = arg;
312         char title[32];
313
314         ast_assert(context->output_buffer != NULL);
315
316         snprintf(title, sizeof(title), "%sAuth",
317                 context->auth_direction ? context->auth_direction : "");
318
319         ast_str_append(&context->output_buffer, 0, "%*s:  %s/%s\n",
320                 CLI_INDENT_TO_SPACES(context->indent_level), title,
321                 ast_sorcery_object_get_id(auth), auth->auth_user);
322
323         if (context->show_details
324                 || (context->show_details_only_level_0 && context->indent_level == 0)) {
325                 ast_str_append(&context->output_buffer, 0, "\n");
326                 ast_sip_cli_print_sorcery_objectset(auth, context, 0);
327         }
328
329         return 0;
330 }
331
332 static struct ast_cli_entry cli_commands[] = {
333         AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "List PJSIP Auths",
334                 .command = "pjsip list auths",
335                 .usage = "Usage: pjsip list auths [ like <pattern> ]\n"
336                                 "       List the configured PJSIP Auths\n"
337                                 "       Optional regular expression pattern is used to filter the list.\n"),
338         AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Auths",
339                 .command = "pjsip show auths",
340                 .usage = "Usage: pjsip show auths [ like <pattern> ]\n"
341                                 "       Show the configured PJSIP Auths\n"
342                                 "       Optional regular expression pattern is used to filter the list.\n"),
343         AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Auth",
344                 .command = "pjsip show auth",
345                 .usage = "Usage: pjsip show auth <id>\n"
346                                  "       Show the configured PJSIP Auth\n"),
347 };
348
349 static struct ast_sip_cli_formatter_entry *cli_formatter;
350
351 /*! \brief Initialize sorcery with auth support */
352 int ast_sip_initialize_sorcery_auth(void)
353 {
354         struct ast_sorcery *sorcery = ast_sip_get_sorcery();
355
356         ast_sorcery_apply_default(sorcery, SIP_SORCERY_AUTH_TYPE, "config", "pjsip.conf,criteria=type=auth");
357
358         if (ast_sorcery_object_register(sorcery, SIP_SORCERY_AUTH_TYPE, auth_alloc, NULL, auth_apply)) {
359                 return -1;
360         }
361
362         ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "type", "",
363                         OPT_NOOP_T, 0, 0);
364         ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "username",
365                         "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, auth_user));
366         ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "password",
367                         "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, auth_pass));
368         ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "md5_cred",
369                         "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, md5_creds));
370         ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "realm",
371                         "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, realm));
372         ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "nonce_lifetime",
373                         "32", OPT_UINT_T, 0, FLDSET(struct ast_sip_auth, nonce_lifetime));
374         ast_sorcery_object_field_register_custom(sorcery, SIP_SORCERY_AUTH_TYPE, "auth_type",
375                         "userpass", auth_type_handler, auth_type_to_str, NULL, 0, 0);
376
377         ast_sip_register_endpoint_formatter(&endpoint_auth_formatter);
378
379         cli_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL);
380         if (!cli_formatter) {
381                 ast_log(LOG_ERROR, "Unable to allocate memory for cli formatter\n");
382                 return -1;
383         }
384         cli_formatter->name = SIP_SORCERY_AUTH_TYPE;
385         cli_formatter->print_header = cli_print_header;
386         cli_formatter->print_body = cli_print_body;
387         cli_formatter->get_container = cli_get_container;
388         cli_formatter->iterate = cli_iterator;
389         cli_formatter->get_id = ast_sorcery_object_get_id;
390         cli_formatter->retrieve_by_id = cli_retrieve_by_id;
391
392         ast_sip_register_cli_formatter(cli_formatter);
393         ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands));
394
395         if (ast_manager_register_xml("PJSIPShowAuths", EVENT_FLAG_SYSTEM, ami_show_auths)) {
396                 return -1;
397         }
398
399         return 0;
400 }
401
402 int ast_sip_destroy_sorcery_auth(void)
403 {
404         ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));
405         ast_sip_unregister_cli_formatter(cli_formatter);
406         ast_sip_unregister_endpoint_formatter(&endpoint_auth_formatter);
407
408         ast_manager_unregister("PJSIPShowAuths");
409
410         return 0;
411 }