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