Switch PJSIP auth to use a vector.
[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_vector *vector,
122                           ao2_callback_fn on_auth, void *arg)
123 {
124         int i;
125
126         if (!vector || !AST_VECTOR_SIZE(vector)) {
127                 return 0;
128         }
129
130         for (i = 0; i < AST_VECTOR_SIZE(vector); ++i) {
131                 /* AST_VECTOR_GET is safe to use since the vector is immutable */
132                 RAII_VAR(struct ast_sip_auth *, auth, ast_sorcery_retrieve_by_id(
133                                  ast_sip_get_sorcery(), SIP_SORCERY_AUTH_TYPE,
134                                  AST_VECTOR_GET(vector,i)), ao2_cleanup);
135
136                 if (!auth) {
137                         continue;
138                 }
139
140                 if (on_auth(auth, arg, 0)) {
141                         return -1;
142                 }
143         }
144
145         return 0;
146 }
147
148 static int sip_auth_to_ami(const struct ast_sip_auth *auth,
149                            struct ast_str **buf)
150 {
151         return ast_sip_sorcery_object_to_ami(auth, buf);
152 }
153
154 static int format_ami_auth_handler(void *obj, void *arg, int flags)
155 {
156         const struct ast_sip_auth *auth = obj;
157         struct ast_sip_ami *ami = arg;
158         const struct ast_sip_endpoint *endpoint = ami->arg;
159         RAII_VAR(struct ast_str *, buf,
160                  ast_sip_create_ami_event("AuthDetail", ami), ast_free);
161
162         if (!buf) {
163                 return -1;
164         }
165
166         if (sip_auth_to_ami(auth, &buf)) {
167                 return -1;
168         }
169
170         if (endpoint) {
171                 ast_str_append(&buf, 0, "EndpointName: %s\r\n",
172                        ast_sorcery_object_get_id(endpoint));
173         }
174
175         astman_append(ami->s, "%s\r\n", ast_str_buffer(buf));
176         return 0;
177 }
178
179 int ast_sip_format_auths_ami(const struct ast_sip_auth_vector *auths,
180                              struct ast_sip_ami *ami)
181 {
182         return ast_sip_for_each_auth(auths, format_ami_auth_handler, ami);
183 }
184
185 static int format_ami_endpoint_auth(const struct ast_sip_endpoint *endpoint,
186                                     struct ast_sip_ami *ami)
187 {
188         ami->arg = (void *)endpoint;
189         if (ast_sip_format_auths_ami(&endpoint->inbound_auths, ami)) {
190                 return -1;
191         }
192
193         return ast_sip_format_auths_ami(&endpoint->outbound_auths, ami);
194 }
195
196 static struct ast_sip_endpoint_formatter endpoint_auth_formatter = {
197         .format_ami = format_ami_endpoint_auth
198 };
199
200 /*! \brief Initialize sorcery with auth support */
201 int ast_sip_initialize_sorcery_auth(struct ast_sorcery *sorcery)
202 {
203         ast_sorcery_apply_default(sorcery, SIP_SORCERY_AUTH_TYPE, "config", "pjsip.conf,criteria=type=auth");
204
205         if (ast_sorcery_object_register(sorcery, SIP_SORCERY_AUTH_TYPE, auth_alloc, NULL, auth_apply)) {
206                 return -1;
207         }
208
209         ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "type", "",
210                         OPT_NOOP_T, 0, 0);
211         ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "username",
212                         "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, auth_user));
213         ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "password",
214                         "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, auth_pass));
215         ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "md5_cred",
216                         "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, md5_creds));
217         ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "realm",
218                         "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, realm));
219         ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "nonce_lifetime",
220                         "32", OPT_UINT_T, 0, FLDSET(struct ast_sip_auth, nonce_lifetime));
221         ast_sorcery_object_field_register_custom(sorcery, SIP_SORCERY_AUTH_TYPE, "auth_type",
222                         "userpass", auth_type_handler, auth_type_to_str, 0, 0);
223
224         ast_sip_register_endpoint_formatter(&endpoint_auth_formatter);
225         return 0;
226 }