Asterisk - The Open Source Telephony Project GIT-master-754dea3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
res_pjsip_authenticator_digest.c
Go to the documentation of this file.
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
23#include "asterisk/res_pjsip.h"
24#include "asterisk/logger.h"
25#include "asterisk/module.h"
26#include "asterisk/strings.h"
27#include "asterisk/test.h"
28
29/*!
30 * \file
31 * \brief PJSIP UAS Authentication
32 *
33 * This module handles authentication when Asterisk is the UAS.
34 *
35 */
36
37/*** MODULEINFO
38 <depend>pjproject</depend>
39 <depend>res_pjsip</depend>
40 <support_level>core</support_level>
41 ***/
42
44
46
47/*!
48 * \brief Determine if authentication is required
49 *
50 * Authentication is required if the endpoint has at least one auth
51 * section specified
52 */
53static int digest_requires_authentication(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
54{
56
57 return endpoint == artificial || AST_VECTOR_SIZE(&endpoint->inbound_auths) > 0;
58}
59
60static void auth_store_cleanup(void *data)
61{
62 struct ast_sip_auth **auth = data;
63
64 ao2_cleanup(*auth);
65 ast_free(data);
66}
67
68/*!
69 * \brief Thread-local storage for \ref ast_sip_auth
70 *
71 * The PJSIP authentication API is a bit annoying. When you set
72 * up an authentication server, you specify a lookup callback to
73 * call into when verifying incoming credentials. The problem
74 * with this callback is that it only gives you the realm and
75 * authentication username. In 2.0.5, there is a new version of
76 * the callback you can use that gives the pjsip_rx_data in
77 * addition.
78 *
79 * Unfortunately, the data we actually \b need is the
80 * \ref ast_sip_auth we are currently observing. So we have two
81 * choices:
82 * 1) Use the current PJSIP API and use thread-local storage
83 * to temporarily store our SIP authentication information. Then
84 * in the callback, we can retrieve the authentication info and
85 * use as needed. Given our threading model, this is safe.
86 * 2) Use the 2.0.5 API and temporarily store the authentication
87 * information in the rdata's endpoint_info. Then in the callback,
88 * we can retrieve the authentication info from the rdata.
89 *
90 * I've chosen option 1 since it does not require backporting
91 * any APIs from future versions of PJSIP, plus I feel the
92 * thread-local option is a bit cleaner.
93 */
95
96/*!
97 * \brief Store shallow copy authentication information in thread-local storage
98 */
99static int store_auth(const struct ast_sip_auth *auth)
100{
101 const struct ast_sip_auth **pointing;
102
103 pointing = ast_threadstorage_get(&auth_store, sizeof(pointing));
104 if (!pointing) {
105 return -1;
106 }
107
108 *pointing = auth;
109 return 0;
110}
111
112/*!
113 * \brief Remove shallow copy authentication information from thread-local storage
114 */
115static int remove_auth(void)
116{
117 struct ast_sip_auth **pointing;
118
119 pointing = ast_threadstorage_get(&auth_store, sizeof(pointing));
120 if (!pointing) {
121 return -1;
122 }
123
124 *pointing = NULL;
125 return 0;
126}
127
128/*!
129 * \brief Retrieve shallow copy authentication information from thread-local storage
130 */
131static const struct ast_sip_auth *get_auth(void)
132{
133 struct ast_sip_auth **auth;
134
135 auth = ast_threadstorage_get(&auth_store, sizeof(auth));
136 if (auth) {
137 return *auth;
138 }
139 return NULL;
140}
141
142static struct pjsip_authorization_hdr *get_authorization_hdr(
143 const char *auth_id, const char *realm, const pjsip_rx_data *rdata)
144{
145 const char *src_name = rdata->pkt_info.src_name;
146 struct pjsip_authorization_hdr *auth_hdr =
147 (pjsip_authorization_hdr *) &rdata->msg_info.msg->hdr;
148 SCOPE_ENTER(3, "%s:%s: realm: %s\n", auth_id, src_name, realm);
149
150 while ((auth_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg,
151 PJSIP_H_AUTHORIZATION, auth_hdr ? auth_hdr->next : NULL))) {
152 if (pj_strcmp2(&auth_hdr->credential.common.realm, realm) == 0) {
153 SCOPE_EXIT_RTN_VALUE(auth_hdr, "%s:%s: realm: %s Found header\n",
154 auth_id, src_name, realm);
155 }
156 }
157 SCOPE_EXIT_RTN_VALUE(NULL, "%s:%s: realm: %s No auth header found\n",
158 auth_id, src_name, realm);
159}
160
161/*!
162 * \brief Lookup callback for authentication verification
163 *
164 * This function is called when we call pjsip_auth_srv_verify(). It
165 * expects us to verify that the realm and account name from the
166 * Authorization header are correct and that we can support the digest
167 * algorithm specified. We are then supposed to supply a password or
168 * password_digest for the algorithm.
169 *
170 * The auth object must have previously been saved to thread-local storage.
171 *
172 * \param pool A memory pool we can use for allocations
173 * \param param Contains the realm, username, rdata and auth header
174 * \param cred_info The credentials we need to fill in
175 * \retval PJ_SUCCESS Successful authentication
176 * \retval other Unsuccessful
177 */
178static pj_status_t digest_lookup(pj_pool_t *pool,
179 const pjsip_auth_lookup_cred_param *param,
180 pjsip_cred_info *cred_info)
181{
182 const struct ast_sip_auth *auth = get_auth();
183 const char *realm = S_OR(auth->realm, default_realm);
184 const char *creds;
185 const char *auth_name = (auth ? ast_sorcery_object_get_id(auth) : "none");
186 struct pjsip_authorization_hdr *auth_hdr = get_authorization_hdr(auth_name, realm, param->rdata);
187 const pjsip_auth_algorithm *algorithm =
188 ast_sip_auth_get_algorithm_by_iana_name(&auth_hdr->credential.digest.algorithm);
189 const char *src_name = param->rdata->pkt_info.src_name;
190 SCOPE_ENTER(4, "%s:%s:"
191 " srv realm: " PJSTR_PRINTF_SPEC
192 " auth realm: %s"
193 " hdr realm: " PJSTR_PRINTF_SPEC
194 " auth user: %s"
195 " hdr user: " PJSTR_PRINTF_SPEC
196 " algorithm: " PJSTR_PRINTF_SPEC
197 "\n",
198 auth_name, src_name,
199 PJSTR_PRINTF_VAR(param->realm),
200 realm,
201 PJSTR_PRINTF_VAR(auth_hdr->credential.common.realm),
202 auth->auth_user,
203 PJSTR_PRINTF_VAR(param->acc_name),
204 PJSTR_PRINTF_VAR(algorithm->iana_name));
205
206 if (!auth) {
207 /* This can only happen if the auth object was not saved to thread-local storage */
208 SCOPE_EXIT_RTN_VALUE(PJSIP_SC_FORBIDDEN, "%s:%s: No auth object found\n",
209 auth_name, src_name);
210 }
211
212 if (auth->type == AST_SIP_AUTH_TYPE_ARTIFICIAL) {
213 /*
214 * This shouldn't happen because this function can only be invoked
215 * if there was an Authorization header in the incoming request.
216 */
217 SCOPE_EXIT_RTN_VALUE(PJSIP_SC_FORBIDDEN, "%s:%s: Artificial auth object\n",
218 auth_name, src_name);
219 }
220
221 if (pj_strcmp2(&param->realm, realm) != 0) {
222 /*
223 * This shouldn't happen because param->realm was passed in from the auth
224 * when we called pjsip_auth_srv_init2.
225 */
226 SCOPE_EXIT_RTN_VALUE(PJSIP_SC_FORBIDDEN, "%s:%s: Realm '%s' mismatch\n",
227 auth_name, src_name, realm);
228 }
229
230 if (pj_strcmp2(&param->acc_name, auth->auth_user) != 0) {
231 SCOPE_EXIT_RTN_VALUE(PJSIP_SC_FORBIDDEN, "%s:%s: Username '%s' mismatch\n",
232 auth_name, src_name, auth->auth_user);
233 }
234
236 algorithm->algorithm_type)) {
237 /*
238 * This shouldn't happen because we shouldn't have sent a challenge for
239 * an unsupported algorithm.
240 */
241 SCOPE_EXIT_RTN_VALUE(PJSIP_SC_FORBIDDEN, "%s:%s: Algorithm '" PJSTR_PRINTF_SPEC
242 "' not supported or auth doesn't contain appropriate credentials\n",
243 auth_name, src_name, PJSTR_PRINTF_VAR(algorithm->iana_name));
244 }
245
246 pj_strdup2(pool, &cred_info->realm, realm);
247 pj_strdup2(pool, &cred_info->username, auth->auth_user);
248
249 creds = ast_sip_auth_get_creds(auth, algorithm->algorithm_type, &cred_info->data_type);
250 if (!creds) {
251 /*
252 * This shouldn't happen because we checked the auth object when we
253 * loaded it to make sure it had the appropriate credentials for each
254 * algorithm in supported_algorithms_uas.
255 */
256 SCOPE_EXIT_RTN_VALUE(PJSIP_SC_FORBIDDEN, "%s:%s: No plain text or digest password found for algorithm '" PJSTR_PRINTF_SPEC "'\n",
257 auth_name, src_name, PJSTR_PRINTF_VAR(algorithm->iana_name));
258 }
259 pj_strdup2(pool, &cred_info->data, creds);
260#ifdef HAVE_PJSIP_AUTH_NEW_DIGESTS
261 if (cred_info->data_type == PJSIP_CRED_DATA_DIGEST) {
262 cred_info->algorithm_type = algorithm->algorithm_type;
263 }
264#endif
265
266 SCOPE_EXIT_RTN_VALUE(PJ_SUCCESS, "%s:%s: Success. Data type: %s Algorithm '" PJSTR_PRINTF_SPEC "'\n",
267 auth_name, src_name, cred_info->data_type ? "digest" : "plain text", PJSTR_PRINTF_VAR(algorithm->iana_name));
268}
269
270/*!
271 * \brief Calculate a nonce
272 *
273 * We use this in order to create authentication challenges. We also use this in order
274 * to verify that an incoming request with credentials could be in response to one
275 * of our challenges.
276 *
277 * The nonce is calculated from a timestamp, the source IP address, the source port, a
278 * unique ID for us, and the realm. This helps to ensure that the incoming request
279 * is from the same source that the nonce was calculated for. Including the realm
280 * ensures that multiple challenges to the same request have different nonces.
281 *
282 * \param nonce
283 * \param timestamp A UNIX timestamp expressed as a string
284 * \param rdata The incoming request
285 * \param realm The realm for which authentication should occur
286 */
287static int build_nonce(struct ast_str **nonce, const char *timestamp,
288 const pjsip_rx_data *rdata, const char *realm)
289{
290 struct ast_str *str = ast_str_alloca(256);
291 RAII_VAR(char *, eid, ao2_global_obj_ref(entity_id), ao2_cleanup);
292 char hash[33];
293
294 /*
295 * Note you may be tempted to think why not include the port. The reason
296 * is that when using TCP the port can potentially differ from before.
297 */
298 ast_str_append(&str, 0, "%s", timestamp);
299 ast_str_append(&str, 0, ":%s", rdata->pkt_info.src_name);
300 ast_str_append(&str, 0, ":%s", eid);
301 ast_str_append(&str, 0, ":%s", realm);
303
304 ast_str_append(nonce, 0, "%s/%s", timestamp, hash);
305 return 0;
306}
307
308/*!
309 * \brief Ensure that a nonce on an incoming request is sane.
310 *
311 * The nonce in an incoming Authorization header needs to pass some scrutiny in order
312 * for us to consider accepting it. What we do is re-build a nonce based on request
313 * data and a realm and see if it matches the nonce they sent us.
314 * \param candidate The nonce on an incoming request
315 * \param rdata The incoming request
316 * \param auth The auth credentials we are trying to match against.
317 * \retval 0 Nonce does not pass validity checks
318 * \retval 1 Nonce passes validity check
319 */
320static int check_nonce(const char *candidate, const pjsip_rx_data *rdata, const struct ast_sip_auth *auth)
321{
322 char *copy = ast_strdupa(candidate);
323 char *timestamp = strsep(&copy, "/");
324 int timestamp_int;
325 time_t now = time(NULL);
326 struct ast_str *calculated = ast_str_alloca(64);
327
328 if (!copy) {
329 /* Clearly a bad nonce! */
330 return 0;
331 }
332
333 if (sscanf(timestamp, "%30d", &timestamp_int) != 1) {
334 return 0;
335 }
336
337 if ((int) now - timestamp_int > auth->nonce_lifetime) {
338 return 0;
339 }
340
341 build_nonce(&calculated, timestamp, rdata, S_OR(auth->realm, default_realm));
342 ast_debug(3, "Calculated nonce %s. Actual nonce is %s\n", ast_str_buffer(calculated), candidate);
343 if (strcmp(ast_str_buffer(calculated), candidate)) {
344 return 0;
345 }
346 return 1;
347}
348
349/*!
350 * \brief Result of digest verification
351 */
353 /*! Authentication credentials incorrect */
355 /*! Authentication credentials correct */
357 /*! Authentication credentials correct but nonce mismatch */
359 /*! Authentication credentials were not provided */
361};
362
363static char *verify_result_str[] = {
364 "FAIL",
365 "SUCCESS",
366 "STALE",
367 "NOAUTH"
368};
369
370static enum digest_verify_result find_authorization(const char *endpoint_id,
371 const struct ast_sip_auth *auth, const pjsip_rx_data *rdata)
372{
373 const char *auth_id = ast_sorcery_object_get_id(auth);
374 const char *src_name = rdata->pkt_info.src_name;
375 const char *realm = S_OR(auth->realm, default_realm);
376 struct pjsip_authorization_hdr *auth_hdr =
377 (pjsip_authorization_hdr *) &rdata->msg_info.msg->hdr;
379 int authorization_found = 0;
380 char nonce[64];
381 SCOPE_ENTER(3, "%s:%s:%s: realm: %s\n",
382 endpoint_id, auth_id, src_name, realm);
383
384 while ((auth_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg,
385 PJSIP_H_AUTHORIZATION, auth_hdr ? auth_hdr->next : NULL))) {
386 ast_copy_pj_str(nonce, &auth_hdr->credential.digest.nonce, sizeof(nonce));
387 ast_trace(-1, "%s:%s:%s: Checking nonce %s hdr-realm: " PJSTR_PRINTF_SPEC " hdr-algo: " PJSTR_PRINTF_SPEC " \n",
388 endpoint_id, auth_id, src_name, nonce,
389 PJSTR_PRINTF_VAR(auth_hdr->credential.digest.realm),
390 PJSTR_PRINTF_VAR(auth_hdr->credential.digest.algorithm));
391 authorization_found++;
392 if (check_nonce(nonce, rdata, auth)
393 && pj_strcmp2(&auth_hdr->credential.digest.realm, realm) == 0) {
394 res = AUTH_SUCCESS;
395 break;
396 } else {
397 res = AUTH_STALE;
398 }
399 }
400 if (!authorization_found) {
401 ast_trace(-1, "%s:%s:%s: No Authorization header found\n",
402 endpoint_id, auth_id, src_name);
403 res = AUTH_NOAUTH;
404 }
405
406 SCOPE_EXIT_RTN_VALUE(res, "%s:%s:%s: realm: %s Result %s\n",
407 endpoint_id, auth_id, src_name, realm, verify_result_str[res]);
408}
409
410/*!
411 * \brief Common code for initializing a pjsip_auth_srv
412 */
413static void setup_auth_srv(pj_pool_t *pool, pjsip_auth_srv *auth_server, const char *realm)
414{
415 pjsip_auth_srv_init_param *param = pj_pool_alloc(pool, sizeof(*param));
416 pj_str_t *pj_realm = pj_pool_alloc(pool, sizeof(*pj_realm));
417
418 pj_cstr(pj_realm, realm);
419 param->realm = pj_realm;
420 param->lookup2 = digest_lookup;
421 param->options = 0;
422
423 pjsip_auth_srv_init2(pool, auth_server, param);
424}
425
426/*!
427 * \brief Verify incoming credentials
428 *
429 * \param endpoint_id For logging
430 * \param auth The ast_sip_auth to check against
431 * \param rdata The incoming request
432 * \param pool A pool to use for the auth server
433 * \return One of digest_verify_result
434 */
435static int verify(const char *endpoint_id, const struct ast_sip_auth *auth,
436 pjsip_rx_data *rdata, pj_pool_t *pool)
437{
438 const char *auth_id = ast_sorcery_object_get_id(auth);
439 const char *realm = S_OR(auth->realm, default_realm);
440 const char *src_name = rdata->pkt_info.src_name;
441 pj_status_t authed;
442 int response_code;
443 pjsip_auth_srv auth_server;
444 int stale = 0;
446 SCOPE_ENTER(3, "%s:%s:%s: realm: %s\n",
447 endpoint_id, auth_id, src_name, realm);
448
449 res = find_authorization(endpoint_id, auth, rdata);
450 if (res == AUTH_NOAUTH)
451 {
452 ast_test_suite_event_notify("INCOMING_AUTH_VERIFY_RESULT",
453 "Realm: %s\r\n"
454 "Username: %s\r\n"
455 "Status: %s",
456 realm, auth->auth_user, verify_result_str[res]);
457 SCOPE_EXIT_RTN_VALUE(res, "%s:%s:%s: No Authorization header found\n",
458 endpoint_id, auth_id, src_name);
459 }
460
461 if (res == AUTH_STALE) {
462 /* Couldn't find an authorization with a sane nonce.
463 * Nonce mismatch may just be due to staleness.
464 */
465 stale = 1;
466 }
467
468 setup_auth_srv(pool, &auth_server, realm);
469 store_auth(auth);
470 /* pjsip_auth_srv_verify will invoke digest_lookup */
471 authed = SCOPE_CALL_WITH_RESULT(-1, pj_status_t, pjsip_auth_srv_verify, &auth_server, rdata, &response_code);
472 remove_auth();
473 if (authed == PJ_SUCCESS) {
474 if (stale) {
475 res = AUTH_STALE;
476 } else {
477 res = AUTH_SUCCESS;
478 }
479 } else {
480 char err[256];
481 res = AUTH_FAIL;
482 pj_strerror(authed, err, sizeof(err));
483 ast_trace(-1, "%s:%s:%s: authed: %s\n", endpoint_id, auth_id, src_name, err);
484 }
485
486 ast_test_suite_event_notify("INCOMING_AUTH_VERIFY_RESULT",
487 "Realm: %s\r\n"
488 "Username: %s\r\n"
489 "Status: %s",
490 realm, auth->auth_user, verify_result_str[res]);
491
492 SCOPE_EXIT_RTN_VALUE(res, "%s:%s:%s: Realm: %s Username: %s Result: %s\n",
493 endpoint_id, auth_id, src_name, realm,
494 auth->auth_user, verify_result_str[res]);
495}
496
497/*!
498 * \brief Send a WWW-Authenticate challenge
499 *
500 * \param endpoint_id For logging
501 * \param auth The auth object to use for the challenge
502 * \param tdata The response to add the challenge to
503 * \param rdata The request the challenge is in response to
504 * \param is_stale Indicates whether nonce on incoming request was stale
505 * \param algorithm_type The algorithm to use for the challenge
506 */
507static void challenge(const char *endpoint_id, struct ast_sip_auth *auth,
508 pjsip_tx_data *tdata, const pjsip_rx_data *rdata, int is_stale,
509 const pjsip_auth_algorithm *algorithm)
510{
511 pj_str_t qop;
512 pj_str_t pj_nonce;
513 pjsip_auth_srv auth_server;
514 struct ast_str *nonce = ast_str_alloca(256);
515 char time_buf[32];
516 time_t timestamp = time(NULL);
517 pj_status_t res;
518 const char *realm = S_OR(auth->realm, default_realm);
519 const char *auth_id = ast_sorcery_object_get_id(auth);
520 const char *src_name = rdata->pkt_info.src_name;
521 SCOPE_ENTER(5, "%s:%s:%s: realm: %s time: %d algorithm: " PJSTR_PRINTF_SPEC " stale? %s\n",
522 endpoint_id, auth_id, src_name, realm, (int)timestamp,
523 PJSTR_PRINTF_VAR(algorithm->iana_name), is_stale ? "yes" : "no");
524
525 snprintf(time_buf, sizeof(time_buf), "%d", (int) timestamp);
526
527 build_nonce(&nonce, time_buf, rdata, realm);
528
529 setup_auth_srv(tdata->pool, &auth_server, realm);
530
531 pj_cstr(&pj_nonce, ast_str_buffer(nonce));
532 pj_cstr(&qop, "auth");
533#ifdef HAVE_PJSIP_AUTH_NEW_DIGESTS
534 res = pjsip_auth_srv_challenge2(&auth_server, &qop, &pj_nonce,
535 NULL, is_stale ? PJ_TRUE : PJ_FALSE, tdata, algorithm->algorithm_type);
536#else
537 res = pjsip_auth_srv_challenge(&auth_server, &qop, &pj_nonce,
538 NULL, is_stale ? PJ_TRUE : PJ_FALSE, tdata);
539#endif
540 SCOPE_EXIT_RTN("%s:%s:%s: Sending challenge for realm: %s algorithm: " PJSTR_PRINTF_SPEC
541 " %s\n",
542 endpoint_id, auth_id, src_name, realm, PJSTR_PRINTF_VAR(algorithm->iana_name),
543 res == PJ_SUCCESS ? "succeeded" : "failed");
544}
545
546static char *check_auth_result_str[] = {
547 "CHALLENGE",
548 "SUCCESS",
549 "FAILED",
550 "ERROR",
551};
552
553
554/*!
555 * \brief Check authentication using Digest scheme
556 *
557 * This function will check an incoming message against configured authentication
558 * options. If \b any of the incoming Authorization headers result in successful
559 * authentication, then authentication is considered successful.
560 *
561 * \warning The return code from the function is used by the distributor to
562 * determine which log messages (if any) are emitted. Many admins will be
563 * using log parsers like fail2ban to block IPs that are repeatedly failing
564 * to authenticate so changing the return code could have unintended
565 * consequences.
566 *
567 * \retval AST_SIP_AUTHENTICATION_SUCCESS There was an Authorization header
568 * in the request and it verified successfully with at least one auth object
569 * on the endpoint. No further challenges sent.
570 *
571 * \retval AST_SIP_AUTHENTICATION_CHALLENGE There was NO Authorization header
572 * in the incoming request. We sent a 401 with one or more challenges.
573 *
574 * \retval AST_SIP_AUTHENTICATION_FAILED There were one or more Authorization
575 * headers in the request but they all failed to verify with any auth object
576 * on the endpoint. We sent a 401 with one or more challenges.
577 *
578 * \retval AST_SIP_AUTHENTICATION_ERROR An internal error occurred. No challenges
579 * were sent.
580 *
581 * \see ast_sip_check_authentication
582 */
584 pjsip_rx_data *rdata, pjsip_tx_data *tdata)
585{
586 struct ast_sip_auth **auths;
587 enum digest_verify_result *verify_res;
590 int idx;
591 int is_artificial;
592 int failures = 0;
593 size_t auth_size;
594 const char *endpoint_id = ast_sorcery_object_get_id(endpoint);
595 char *src_name = rdata->pkt_info.src_name;
596 SCOPE_ENTER(3, "%s:%s\n", endpoint_id, src_name);
597
598 auth_size = AST_VECTOR_SIZE(&endpoint->inbound_auths);
599 ast_assert(0 < auth_size);
600
601 auths = ast_alloca(auth_size * sizeof(*auths));
602 verify_res = ast_alloca(auth_size * sizeof(*verify_res));
603
605 if (!artificial_endpoint) {
606 /* Should not happen except possibly if we are shutting down. */
608 }
609
610 is_artificial = endpoint == artificial_endpoint;
612 if (is_artificial) {
613 ast_trace(3, "%s:%s: Using artificial endpoint for authentication\n",
614 endpoint_id, src_name);
615 ast_assert(auth_size == 1);
616 auths[0] = ast_sip_get_artificial_auth();
617 if (!auths[0]) {
618 /* Should not happen except possibly if we are shutting down. */
620 }
621 } else {
622 ast_trace(3, "%s:%s: Using endpoint for authentication\n",
623 endpoint_id, src_name);
624 memset(auths, 0, auth_size * sizeof(*auths));
625 /*
626 * If ast_sip_retrieve_auths returns a failure we still need
627 * to cleanup the auths array because it may have been partially
628 * filled in.
629 */
630 if (ast_sip_retrieve_auths(&endpoint->inbound_auths, auths)) {
631 ast_sip_cleanup_auths(auths, auth_size);
633 "%s:%s: Failed to retrieve some or all auth objects from endpoint\n",
634 endpoint_id, src_name);
635 }
636 }
637
638 /*
639 * Verify any Authorization headers in the incoming request against the
640 * auth objects on the endpoint. If there aren't any Authorization headers
641 * verify() will return AUTH_NOAUTH.
642 *
643 * NOTE: The only reason to use multiple auth objects as a UAS might
644 * be to send challenges for multiple realms however we currently don't
645 * know of anyone actually doing this.
646 */
647 for (idx = 0; idx < auth_size; ++idx) {
648 struct ast_sip_auth *auth = auths[idx];
649 const char *auth_id = ast_sorcery_object_get_id(auth);
650 SCOPE_ENTER(4, "%s:%s:%s: Auth %d of %d: Verifying\n",
651 endpoint_id, auth_id, src_name, idx + 1, (int)auth_size);
652
653 verify_res[idx] = SCOPE_CALL_WITH_RESULT(-1, int, verify, endpoint_id, auth, rdata, tdata->pool);
654 switch((int)verify_res[idx]) {
655 case AUTH_SUCCESS:
657 break;
658 case AUTH_FAIL:
659 failures++;
660 break;
661 case AUTH_NOAUTH:
662 case AUTH_STALE:
663 break;
664 }
665
666 SCOPE_EXIT("%s:%s:%s: Auth %d of %d: Result: %s Failure count: %d\n",
667 endpoint_id, auth_id, src_name, idx + 1, (int)auth_size,
668 verify_result_str[verify_res[idx]], failures);
669
670 /*
671 * If there was a success or there was no Authorization header in the
672 * incoming request, we can stop verifying the rest of the auth objects.
673 */
674 if (verify_res[idx] == AUTH_SUCCESS || verify_res[idx] == AUTH_NOAUTH) {
675 break;
676 }
677 }
678
680 ast_sip_cleanup_auths(auths, auth_size);
681 SCOPE_EXIT_RTN_VALUE(res, "%s:%s: Result: %s\n",
682 endpoint_id, src_name,
684 }
685 ast_trace(-1, "%s:%s: Done with verification. Failures: %d of %d\n",
686 endpoint_id, src_name, failures, (int)auth_size);
687
688 /*
689 * If none of the Authorization headers in the incoming request were
690 * successfully verified, or there were no Authorization headers in the
691 * request, we need to send challenges for each auth object
692 * on the endpoint.
693 */
694 for (idx = 0; idx < auth_size; ++idx) {
695 int i = 0;
696 struct ast_sip_auth *auth = auths[idx];
697 const char *realm = S_OR(auth->realm, default_realm);
698 const char *auth_id = ast_sorcery_object_get_id(auth);
699 SCOPE_ENTER(4, "%s:%s:%s: Auth %d of %d: Sending challenges\n",
700 endpoint_id, auth_id, src_name, idx + 1, (int)auth_size);
701
702 for (i = 0; i < AST_VECTOR_SIZE(&auth->supported_algorithms_uas); i++) {
704 const pjsip_auth_algorithm *algorithm = ast_sip_auth_get_algorithm_by_type(algorithm_type);
705 pjsip_www_authenticate_hdr *auth_hdr = NULL;
706 int already_sent_challenge = 0;
707 SCOPE_ENTER(5, "%s:%s:%s: Auth %d of %d: Challenging with " PJSTR_PRINTF_SPEC "\n",
708 endpoint_id, auth_id, src_name, idx + 1, (int)auth_size,
709 PJSTR_PRINTF_VAR(algorithm->iana_name));
710
711 /*
712 * Per RFC 7616, if we've already sent a challenge for this realm
713 * and algorithm, we must not send another.
714 */
715 while ((auth_hdr = pjsip_msg_find_hdr(tdata->msg,
716 PJSIP_H_WWW_AUTHENTICATE, auth_hdr ? auth_hdr->next : NULL))) {
717 if (pj_strcmp2(&auth_hdr->challenge.common.realm, realm) == 0 &&
718 !pj_stricmp(&auth_hdr->challenge.digest.algorithm, &algorithm->iana_name)) {
719 ast_trace(-1, "%s:%s:%s: Auth %d of %d: Not sending duplicate challenge for realm: %s algorithm: "
721 endpoint_id, auth_id, src_name, idx + 1, (int)auth_size,
722 realm, PJSTR_PRINTF_VAR(algorithm->iana_name));
723 already_sent_challenge = 1;
724 }
725 }
726 if (already_sent_challenge) {
727 SCOPE_EXIT_EXPR(continue);
728 }
729
730 SCOPE_CALL(5, challenge, endpoint_id, auth, tdata, rdata,
731 verify_res[idx] == AUTH_STALE, algorithm);
733
734 SCOPE_EXIT("%s:%s:%s: Auth %d of %d: Challenged with " PJSTR_PRINTF_SPEC "\n",
735 endpoint_id, auth_id, src_name, idx + 1, (int)auth_size,
736 PJSTR_PRINTF_VAR(algorithm->iana_name));
737 }
738 SCOPE_EXIT("%s:%s:%s: Auth %d of %d: Done with challenges\n",
739 endpoint_id, auth_id, src_name, idx + 1, (int)auth_size);
740 }
741
742 /*
743 * If we've sent challenges for multiple auth objects, we currently
744 * return SUCCESS when the first one succeeds. We may want to change
745 * this in the future to require that all succeed but as stated above,
746 * currently we don't have a use case for even using more than one
747 * auth object as a UAS.
748 */
749
750 /*
751 * If the authentication failed for any reason, we want to send
752 * a 401 with a challenge. If it was because there was no
753 * Authorization header or there was a stale nonce, fine. That's not
754 * unusual so we return AST_SIP_AUTHENTICATION_CHALLENGE. If it
755 * failed because of a user/password mismatch then we return
756 * AST_SIP_AUTHENTICATION_FAILED which causes the distributor to
757 * print a "Failed to authenticate" message.
758 */
759 if (failures == auth_size) {
761 }
762
763 ast_sip_cleanup_auths(auths, auth_size);
764 SCOPE_EXIT_RTN_VALUE(res, "%s:%s: Result: %s\n",
765 endpoint_id, src_name,
767
768}
769
772 .check_authentication = digest_check_auth,
773};
774
775static int build_entity_id(void)
776{
777 char *eid;
778
780 if (!eid) {
781 return -1;
782 }
783
785 ao2_global_obj_replace_unref(entity_id, eid);
786 ao2_ref(eid, -1);
787 return 0;
788}
789
790static void global_loaded(const char *object_type)
791{
793}
794
795/*! \brief Observer which is used to update our default_realm when the global setting changes */
798};
799
800static int reload_module(void)
801{
802 if (build_entity_id()) {
803 return -1;
804 }
805 return 0;
806}
807
808static int load_module(void)
809{
810 if (build_entity_id()) {
812 }
813
816
818 ao2_global_obj_release(entity_id);
820 }
822}
823
824static int unload_module(void)
825{
828 ao2_global_obj_release(entity_id);
829 return 0;
830}
831
832AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP authentication resource",
833 .support_level = AST_MODULE_SUPPORT_CORE,
834 .load = load_module,
835 .unload = unload_module,
837 .load_pri = AST_MODPRI_CHANNEL_DEPEND - 5,
838 .requires = "res_pjsip",
const char * str
Definition: app_jack.c:150
static int copy(char *infile, char *outfile)
Utility function to copy a file.
char * strsep(char **str, const char *delims)
Asterisk main include file. File version handling, generic pbx functions.
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ao2_global_obj_replace_unref(holder, obj)
Replace an ao2 object in the global holder, throwing away any old object.
Definition: astobj2.h:901
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_global_obj_ref(holder)
Get a reference to the object stored in the global holder.
Definition: astobj2.h:918
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define ao2_global_obj_release(holder)
Release the ao2 object held in the global holder.
Definition: astobj2.h:859
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:409
#define SCOPE_EXIT_RTN(...)
#define SCOPE_EXIT_RTN_VALUE(__return_value,...)
#define SCOPE_CALL_WITH_RESULT(level, __var, __funcname,...)
#define SCOPE_ENTER(level,...)
#define SCOPE_EXIT_EXPR(__expr,...)
#define SCOPE_CALL(level, __funcname,...)
#define SCOPE_EXIT(...)
#define ast_trace(level,...)
Support for logging to various files, console and syslog Configuration in file logger....
#define ast_debug(level,...)
Log a DEBUG message.
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:331
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:557
@ AST_MODPRI_CHANNEL_DEPEND
Definition: module.h:340
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
static struct ast_sip_endpoint * artificial_endpoint
static int reload(void)
struct ast_sip_auth * ast_sip_get_artificial_auth(void)
Retrieves a reference to the artificial auth.
pjsip_auth_algorithm_type
Definition: res_pjsip.h:607
const pjsip_auth_algorithm * ast_sip_auth_get_algorithm_by_iana_name(const pj_str_t *iana_name)
Get algorithm by IANA name.
Definition: config_auth.c:83
const pjsip_auth_algorithm * ast_sip_auth_get_algorithm_by_type(pjsip_auth_algorithm_type algorithm_type)
Get algorithm by algorithm type.
Definition: config_auth.c:66
struct ast_sip_endpoint * ast_sip_get_artificial_endpoint(void)
Retrieves a reference to the artificial endpoint.
void ast_sip_cleanup_auths(struct ast_sip_auth *auths[], size_t num_auths)
Clean up retrieved auth structures from memory.
int ast_sip_retrieve_auths(const struct ast_sip_auth_vector *auths, struct ast_sip_auth **out)
Retrieve relevant SIP auth structures from sorcery.
void ast_copy_pj_str(char *dest, const pj_str_t *src, size_t size)
Copy a pj_str_t into a standard character buffer.
Definition: res_pjsip.c:2201
void ast_sip_unregister_authenticator(struct ast_sip_authenticator *auth)
Unregister a SIP authenticator.
Definition: res_pjsip.c:152
void ast_sip_get_default_realm(char *realm, size_t size)
Retrieve the global default realm.
#define AST_SIP_AUTH_MAX_REALM_LENGTH
Definition: res_pjsip.h:74
int ast_sip_register_authenticator(struct ast_sip_authenticator *auth)
Register a SIP authenticator.
Definition: res_pjsip.c:140
@ AST_SIP_AUTH_TYPE_ARTIFICIAL
Definition: res_pjsip.h:585
const char * ast_sip_auth_get_creds(const struct ast_sip_auth *auth, const pjsip_auth_algorithm_type algorithm_type, int *cred_type)
Get the plain text or digest password from an auth object.
Definition: config_auth.c:407
int ast_sip_auth_is_algorithm_available(const struct ast_sip_auth *auth, const struct pjsip_auth_algorithm_type_vector *algorithms, pjsip_auth_algorithm_type algorithm_type)
Checks an pjsip_auth_algorithm_type_vector to see if it contains an algorithm.
Definition: config_auth.c:386
struct ast_sorcery * ast_sip_get_sorcery(void)
Get a pointer to the SIP sorcery structure.
ast_sip_check_auth_result
Possible returns from ast_sip_check_authentication.
Definition: res_pjsip.h:1321
@ AST_SIP_AUTHENTICATION_CHALLENGE
Definition: res_pjsip.h:1323
@ AST_SIP_AUTHENTICATION_ERROR
Definition: res_pjsip.h:1329
@ AST_SIP_AUTHENTICATION_SUCCESS
Definition: res_pjsip.h:1325
@ AST_SIP_AUTHENTICATION_FAILED
Definition: res_pjsip.h:1327
#define PJSTR_PRINTF_VAR(_v)
Definition: res_pjsip.h:72
#define PJSTR_PRINTF_SPEC
Definition: res_pjsip.h:71
static struct pjsip_authorization_hdr * get_authorization_hdr(const char *auth_id, const char *realm, const pjsip_rx_data *rdata)
static void setup_auth_srv(pj_pool_t *pool, pjsip_auth_srv *auth_server, const char *realm)
Common code for initializing a pjsip_auth_srv.
static struct ast_sorcery_observer global_observer
Observer which is used to update our default_realm when the global setting changes.
static int check_nonce(const char *candidate, const pjsip_rx_data *rdata, const struct ast_sip_auth *auth)
Ensure that a nonce on an incoming request is sane.
digest_verify_result
Result of digest verification.
static void global_loaded(const char *object_type)
AO2_GLOBAL_OBJ_STATIC(entity_id)
static enum digest_verify_result find_authorization(const char *endpoint_id, const struct ast_sip_auth *auth, const pjsip_rx_data *rdata)
static int verify(const char *endpoint_id, const struct ast_sip_auth *auth, pjsip_rx_data *rdata, pj_pool_t *pool)
Verify incoming credentials.
static struct ast_threadstorage auth_store
static int build_entity_id(void)
static const struct ast_sip_auth * get_auth(void)
Retrieve shallow copy authentication information from thread-local storage.
static int reload_module(void)
static int store_auth(const struct ast_sip_auth *auth)
Store shallow copy authentication information in thread-local storage.
static void challenge(const char *endpoint_id, struct ast_sip_auth *auth, pjsip_tx_data *tdata, const pjsip_rx_data *rdata, int is_stale, const pjsip_auth_algorithm *algorithm)
Send a WWW-Authenticate challenge.
static char default_realm[AST_SIP_AUTH_MAX_REALM_LENGTH+1]
static char * check_auth_result_str[]
static int remove_auth(void)
Remove shallow copy authentication information from thread-local storage.
static enum ast_sip_check_auth_result digest_check_auth(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, pjsip_tx_data *tdata)
Check authentication using Digest scheme.
static int load_module(void)
static char * verify_result_str[]
static int unload_module(void)
static void auth_store_cleanup(void *data)
static int digest_requires_authentication(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
Determine if authentication is required.
static struct ast_sip_authenticator digest_authenticator
static pj_status_t digest_lookup(pj_pool_t *pool, const pjsip_auth_lookup_cred_param *param, pjsip_cred_info *cred_info)
Lookup callback for authentication verification.
static int build_nonce(struct ast_str **nonce, const char *timestamp, const pjsip_rx_data *rdata, const char *realm)
Calculate a nonce.
#define NULL
Definition: resample.c:96
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2317
void ast_sorcery_observer_remove(const struct ast_sorcery *sorcery, const char *type, const struct ast_sorcery_observer *callbacks)
Remove an observer from a specific object type.
Definition: sorcery.c:2423
int ast_sorcery_observer_add(const struct ast_sorcery *sorcery, const char *type, const struct ast_sorcery_observer *callbacks)
Add an observer to a specific object type.
Definition: sorcery.c:2391
void ast_sorcery_reload_object(const struct ast_sorcery *sorcery, const char *type)
Inform any wizards of a specific object type to reload persistent objects.
Definition: sorcery.c:1442
String manipulation functions.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1139
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition: strings.h:80
#define ast_str_alloca(init_len)
Definition: strings.h:848
const ast_string_field realm
Definition: res_pjsip.h:681
unsigned int nonce_lifetime
Definition: res_pjsip.h:683
const ast_string_field auth_user
Definition: res_pjsip.h:681
struct pjsip_auth_algorithm_type_vector supported_algorithms_uas
Definition: res_pjsip.h:689
enum ast_sip_auth_type type
Definition: res_pjsip.h:685
An interchangeable way of handling digest authentication for SIP.
Definition: res_pjsip.h:1366
int(* requires_authentication)(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
Check if a request requires authentication See ast_sip_requires_authentication for more details.
Definition: res_pjsip.h:1371
An entity with which Asterisk communicates.
Definition: res_pjsip.h:1051
struct ast_sip_auth_vector inbound_auths
Definition: res_pjsip.h:1096
Interface for a sorcery object type observer.
Definition: sorcery.h:332
void(* loaded)(const char *object_type)
Callback for when an object type is loaded/reloaded.
Definition: sorcery.h:343
Support for dynamic strings.
Definition: strings.h:623
pjsip_auth_algorithm_type algorithm_type
Definition: res_pjsip.h:618
Test Framework API.
#define ast_test_suite_event_notify(s, f,...)
Definition: test.h:189
#define AST_THREADSTORAGE_CUSTOM(a, b, c)
Define a thread storage variable, with custom initialization and cleanup.
void * ast_threadstorage_get(struct ast_threadstorage *ts, size_t init_size)
Retrieve thread storage.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
#define ast_assert(a)
Definition: utils.h:739
void ast_md5_hash(char *output, const char *input)
Produces MD5 hash based on input string.
Definition: utils.c:250
#define AST_UUID_STR_LEN
Definition: uuid.h:27
char * ast_uuid_generate_str(char *buf, size_t size)
Generate a UUID string.
Definition: uuid.c:141
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:609
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition: vector.h:680