res_pjsip: AMI commands and events.
[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 "include/res_pjsip_private.h"
27
28 static void auth_destroy(void *obj)
29 {
30         struct ast_sip_auth *auth = obj;
31         ast_string_field_free_memory(auth);
32 }
33
34 static void *auth_alloc(const char *name)
35 {
36         struct ast_sip_auth *auth = ast_sorcery_generic_alloc(sizeof(*auth), auth_destroy);
37
38         if (!auth) {
39                 return NULL;
40         }
41
42         if (ast_string_field_init(auth, 64)) {
43                 ao2_cleanup(auth);
44                 return NULL;
45         }
46
47         return auth;
48 }
49
50 static int auth_type_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
51 {
52         struct ast_sip_auth *auth = obj;
53         if (!strcasecmp(var->value, "userpass")) {
54                 auth->type = AST_SIP_AUTH_TYPE_USER_PASS;
55         } else if (!strcasecmp(var->value, "md5")) {
56                 auth->type = AST_SIP_AUTH_TYPE_MD5;
57         } else {
58                 ast_log(LOG_WARNING, "Unknown authentication storage type '%s' specified for %s\n",
59                                 var->value, var->name);
60                 return -1;
61         }
62         return 0;
63 }
64
65 static const char *auth_types_map[] = {
66         [AST_SIP_AUTH_TYPE_USER_PASS] = "userpass",
67         [AST_SIP_AUTH_TYPE_MD5] = "md5"
68 };
69
70 const char *ast_sip_auth_type_to_str(enum ast_sip_auth_type type)
71 {
72         return ARRAY_IN_BOUNDS(type, auth_types_map) ?
73                 auth_types_map[type] : "";
74 }
75
76 static int auth_type_to_str(const void *obj, const intptr_t *args, char **buf)
77 {
78         const struct ast_sip_auth *auth = obj;
79         *buf = ast_strdup(ast_sip_auth_type_to_str(auth->type));
80         return 0;
81 }
82
83 static int auth_apply(const struct ast_sorcery *sorcery, void *obj)
84 {
85         struct ast_sip_auth *auth = obj;
86         int res = 0;
87
88         if (ast_strlen_zero(auth->auth_user)) {
89                 ast_log(LOG_ERROR, "No authentication username for auth '%s'\n",
90                                 ast_sorcery_object_get_id(auth));
91                 return -1;
92         }
93
94         switch (auth->type) {
95         case AST_SIP_AUTH_TYPE_USER_PASS:
96                 if (ast_strlen_zero(auth->auth_pass)) {
97                         ast_log(LOG_ERROR, "'userpass' authentication specified but no"
98                                         "password specified for auth '%s'\n", ast_sorcery_object_get_id(auth));
99                         res = -1;
100                 }
101                 break;
102         case AST_SIP_AUTH_TYPE_MD5:
103                 if (ast_strlen_zero(auth->md5_creds)) {
104                         ast_log(LOG_ERROR, "'md5' authentication specified but no md5_cred"
105                                         "specified for auth '%s'\n", ast_sorcery_object_get_id(auth));
106                         res = -1;
107                 } else if (strlen(auth->md5_creds) != PJSIP_MD5STRLEN) {
108                         ast_log(LOG_ERROR, "'md5' authentication requires digest of size '%d', but"
109                                 "digest is '%d' in size for auth '%s'\n", PJSIP_MD5STRLEN, (int)strlen(auth->md5_creds),
110                                 ast_sorcery_object_get_id(auth));
111                         res = -1;
112                 }
113                 break;
114         case AST_SIP_AUTH_TYPE_ARTIFICIAL:
115                 break;
116         }
117
118         return res;
119 }
120
121 int ast_sip_for_each_auth(const struct ast_sip_auth_array *array,
122                           ao2_callback_fn on_auth, void *arg)
123 {
124         int i;
125
126         if (!array || !array->num) {
127                 return 0;
128         }
129
130         for (i = 0; i < array->num; ++i) {
131                 RAII_VAR(struct ast_sip_auth *, auth, ast_sorcery_retrieve_by_id(
132                                  ast_sip_get_sorcery(), SIP_SORCERY_AUTH_TYPE,
133                                  array->names[i]), ao2_cleanup);
134
135                 if (!auth) {
136                         continue;
137                 }
138
139                 if (on_auth(auth, arg, 0)) {
140                         return -1;
141                 }
142         }
143
144         return 0;
145 }
146
147 static int sip_auth_to_ami(const struct ast_sip_auth *auth,
148                            struct ast_str **buf)
149 {
150         return ast_sip_sorcery_object_to_ami(auth, buf);
151 }
152
153 static int format_ami_auth_handler(void *obj, void *arg, int flags)
154 {
155         const struct ast_sip_auth *auth = obj;
156         struct ast_sip_ami *ami = arg;
157         const struct ast_sip_endpoint *endpoint = ami->arg;
158         RAII_VAR(struct ast_str *, buf,
159                  ast_sip_create_ami_event("AuthDetail", ami), ast_free);
160
161         if (!buf) {
162                 return -1;
163         }
164
165         if (sip_auth_to_ami(auth, &buf)) {
166                 return -1;
167         }
168
169         if (endpoint) {
170                 ast_str_append(&buf, 0, "EndpointName: %s\r\n",
171                        ast_sorcery_object_get_id(endpoint));
172         }
173
174         astman_append(ami->s, "%s\r\n", ast_str_buffer(buf));
175         return 0;
176 }
177
178 int ast_sip_format_auths_ami(const struct ast_sip_auth_array *auths,
179                              struct ast_sip_ami *ami)
180 {
181         return ast_sip_for_each_auth(auths, format_ami_auth_handler, ami);
182 }
183
184 static int format_ami_endpoint_auth(const struct ast_sip_endpoint *endpoint,
185                                     struct ast_sip_ami *ami)
186 {
187         ami->arg = (void *)endpoint;
188         if (ast_sip_format_auths_ami(&endpoint->inbound_auths, ami)) {
189                 return -1;
190         }
191
192         return ast_sip_format_auths_ami(&endpoint->outbound_auths, ami);
193 }
194
195 static struct ast_sip_endpoint_formatter endpoint_auth_formatter = {
196         .format_ami = format_ami_endpoint_auth
197 };
198
199 /*! \brief Initialize sorcery with auth support */
200 int ast_sip_initialize_sorcery_auth(struct ast_sorcery *sorcery)
201 {
202         ast_sorcery_apply_default(sorcery, SIP_SORCERY_AUTH_TYPE, "config", "pjsip.conf,criteria=type=auth");
203
204         if (ast_sorcery_object_register(sorcery, SIP_SORCERY_AUTH_TYPE, auth_alloc, NULL, auth_apply)) {
205                 return -1;
206         }
207
208         ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "type", "",
209                         OPT_NOOP_T, 0, 0);
210         ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "username",
211                         "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, auth_user));
212         ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "password",
213                         "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, auth_pass));
214         ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "md5_cred",
215                         "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, md5_creds));
216         ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "realm",
217                         "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, realm));
218         ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "nonce_lifetime",
219                         "32", OPT_UINT_T, 0, FLDSET(struct ast_sip_auth, nonce_lifetime));
220         ast_sorcery_object_field_register_custom(sorcery, SIP_SORCERY_AUTH_TYPE, "auth_type",
221                         "userpass", auth_type_handler, auth_type_to_str, 0, 0);
222
223         ast_sip_register_endpoint_formatter(&endpoint_auth_formatter);
224         return 0;
225 }