res_pjsip: Fix warning by deferring implicit type cast.
[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_container(const char *regex)
199 {
200         RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup);
201         struct ao2_container *s_container;
202
203         container = ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "auth", regex);
204         if (!container) {
205                 return NULL;
206         }
207
208         s_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
209                 ast_sorcery_object_id_sort, ast_sorcery_object_id_compare);
210         if (!s_container) {
211                 return NULL;
212         }
213
214         if (ao2_container_dup(s_container, container, 0)) {
215                 ao2_ref(s_container, -1);
216                 return NULL;
217         }
218
219         return s_container;
220 }
221
222 static int cli_iterator(void *container, ao2_callback_fn callback, void *args)
223 {
224         return ast_sip_for_each_auth(container, callback, args);
225 }
226
227 static void *cli_retrieve_by_id(const char *id)
228 {
229         return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), SIP_SORCERY_AUTH_TYPE, id);
230 }
231
232 static int cli_print_header(void *obj, void *arg, int flags)
233 {
234         struct ast_sip_cli_context *context = arg;
235         int indent = CLI_INDENT_TO_SPACES(context->indent_level);
236         int filler = CLI_MAX_WIDTH - indent - 20;
237
238         ast_assert(context->output_buffer != NULL);
239
240         ast_str_append(&context->output_buffer, 0,
241                 "%*s:  <AuthId/UserName%*.*s>\n", indent, "I/OAuth", filler, filler,
242                 CLI_HEADER_FILLER);
243
244         return 0;
245 }
246
247 static int cli_print_body(void *obj, void *arg, int flags)
248 {
249         struct ast_sip_auth *auth = obj;
250         struct ast_sip_cli_context *context = arg;
251         char title[32];
252
253         ast_assert(context->output_buffer != NULL);
254
255         snprintf(title, sizeof(title), "%sAuth",
256                 context->auth_direction ? context->auth_direction : "");
257
258         ast_str_append(&context->output_buffer, 0, "%*s:  %s/%s\n",
259                 CLI_INDENT_TO_SPACES(context->indent_level), title,
260                 ast_sorcery_object_get_id(auth), auth->auth_user);
261
262         if (context->show_details
263                 || (context->show_details_only_level_0 && context->indent_level == 0)) {
264                 ast_str_append(&context->output_buffer, 0, "\n");
265                 ast_sip_cli_print_sorcery_objectset(auth, context, 0);
266         }
267
268         return 0;
269 }
270
271 static struct ast_cli_entry cli_commands[] = {
272         AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "List PJSIP Auths",
273                 .command = "pjsip list auths",
274                 .usage = "Usage: pjsip list auths [ like <pattern> ]\n"
275                                 "       List the configured PJSIP Auths\n"
276                                 "       Optional regular expression pattern is used to filter the list.\n"),
277         AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Auths",
278                 .command = "pjsip show auths",
279                 .usage = "Usage: pjsip show auths [ like <pattern> ]\n"
280                                 "       Show the configured PJSIP Auths\n"
281                                 "       Optional regular expression pattern is used to filter the list.\n"),
282         AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Auth",
283                 .command = "pjsip show auth",
284                 .usage = "Usage: pjsip show auth <id>\n"
285                                  "       Show the configured PJSIP Auth\n"),
286 };
287
288 static struct ast_sip_cli_formatter_entry *cli_formatter;
289
290 /*! \brief Initialize sorcery with auth support */
291 int ast_sip_initialize_sorcery_auth(void)
292 {
293         struct ast_sorcery *sorcery = ast_sip_get_sorcery();
294
295         ast_sorcery_apply_default(sorcery, SIP_SORCERY_AUTH_TYPE, "config", "pjsip.conf,criteria=type=auth");
296
297         if (ast_sorcery_object_register(sorcery, SIP_SORCERY_AUTH_TYPE, auth_alloc, NULL, auth_apply)) {
298                 return -1;
299         }
300
301         ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "type", "",
302                         OPT_NOOP_T, 0, 0);
303         ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "username",
304                         "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, auth_user));
305         ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "password",
306                         "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, auth_pass));
307         ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "md5_cred",
308                         "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, md5_creds));
309         ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "realm",
310                         "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, realm));
311         ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "nonce_lifetime",
312                         "32", OPT_UINT_T, 0, FLDSET(struct ast_sip_auth, nonce_lifetime));
313         ast_sorcery_object_field_register_custom(sorcery, SIP_SORCERY_AUTH_TYPE, "auth_type",
314                         "userpass", auth_type_handler, auth_type_to_str, NULL, 0, 0);
315
316         internal_sip_register_endpoint_formatter(&endpoint_auth_formatter);
317
318         cli_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL);
319         if (!cli_formatter) {
320                 ast_log(LOG_ERROR, "Unable to allocate memory for cli formatter\n");
321                 return -1;
322         }
323         cli_formatter->name = SIP_SORCERY_AUTH_TYPE;
324         cli_formatter->print_header = cli_print_header;
325         cli_formatter->print_body = cli_print_body;
326         cli_formatter->get_container = cli_get_container;
327         cli_formatter->iterate = cli_iterator;
328         cli_formatter->get_id = ast_sorcery_object_get_id;
329         cli_formatter->retrieve_by_id = cli_retrieve_by_id;
330
331         ast_sip_register_cli_formatter(cli_formatter);
332         ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands));
333
334         return 0;
335 }
336
337 int ast_sip_destroy_sorcery_auth(void)
338 {
339         ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));
340         ast_sip_unregister_cli_formatter(cli_formatter);
341         internal_sip_unregister_endpoint_formatter(&endpoint_auth_formatter);
342
343         return 0;
344 }