Asterisk - The Open Source Telephony Project GIT-master-0deac78
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 = auth_hdr ?
188 ast_sip_auth_get_algorithm_by_iana_name(&auth_hdr->credential.digest.algorithm) : NULL;
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 " auth user: %s"
194 " hdr user: " PJSTR_PRINTF_SPEC
195 "\n",
196 auth_name, src_name,
197 PJSTR_PRINTF_VAR(param->realm),
198 realm,
199 auth->auth_user,
200 PJSTR_PRINTF_VAR(param->acc_name));
201
202 /*
203 * If a client is responding correctly, most of the error conditions below
204 * can't happen because we sent them the correct info in the 401 response.
205 * However, if a client is trying to authenticate with us without
206 * having received a challenge or if they are trying to
207 * authenticate with a different realm or algorithm than we sent them,
208 * we need to catch that.
209 */
210
211 if (!auth) {
212 /* This can only happen if the auth object was not saved to thread-local storage */
213 SCOPE_EXIT_RTN_VALUE(PJSIP_SC_FORBIDDEN, "%s:%s: No auth object found\n",
214 auth_name, src_name);
215 }
216
217 if (auth_hdr == NULL) {
218 /*
219 * This can only happen if the incoming request did not have an
220 * Authorization header or the realm in the header was missing or incorrect.
221 */
222 SCOPE_EXIT_RTN_VALUE(PJSIP_SC_FORBIDDEN,
223 "%s:%s: No Authorization header found for realm '%s'\n",
224 auth_name, src_name, realm);
225 }
226
227 if (algorithm == NULL) {
228 /*
229 * This can only happen if the incoming request had an algorithm
230 * we don't support.
231 */
232 SCOPE_EXIT_RTN_VALUE(PJSIP_SC_FORBIDDEN,
233 "%s:%s: Unsupported algorithm '" PJSTR_PRINTF_SPEC "'\n",
234 auth_name, src_name, PJSTR_PRINTF_VAR(auth_hdr->credential.digest.algorithm));
235 }
236
237 if (auth->type == AST_SIP_AUTH_TYPE_ARTIFICIAL) {
238 /*
239 * This shouldn't happen because this function can only be invoked
240 * if there was an Authorization header in the incoming request.
241 */
242 SCOPE_EXIT_RTN_VALUE(PJSIP_SC_FORBIDDEN, "%s:%s: Artificial auth object\n",
243 auth_name, src_name);
244 }
245
246 if (pj_strcmp2(&param->realm, realm) != 0) {
247 /*
248 * This shouldn't happen because param->realm was passed in from the auth
249 * when we called pjsip_auth_srv_init2.
250 */
251 SCOPE_EXIT_RTN_VALUE(PJSIP_SC_FORBIDDEN, "%s:%s: Realm '%s' mismatch\n",
252 auth_name, src_name, realm);
253 }
254
255 if (pj_strcmp2(&param->acc_name, auth->auth_user) != 0) {
256 SCOPE_EXIT_RTN_VALUE(PJSIP_SC_FORBIDDEN, "%s:%s: Username '%s' mismatch\n",
257 auth_name, src_name, auth->auth_user);
258 }
259
261 algorithm->algorithm_type)) {
262 /*
263 * This shouldn't happen because we shouldn't have sent a challenge for
264 * an unsupported algorithm.
265 */
266 SCOPE_EXIT_RTN_VALUE(PJSIP_SC_FORBIDDEN, "%s:%s: Algorithm '" PJSTR_PRINTF_SPEC
267 "' not supported or auth doesn't contain appropriate credentials\n",
268 auth_name, src_name, PJSTR_PRINTF_VAR(algorithm->iana_name));
269 }
270
271 pj_strdup2(pool, &cred_info->realm, realm);
272 pj_strdup2(pool, &cred_info->username, auth->auth_user);
273
274 creds = ast_sip_auth_get_creds(auth, algorithm->algorithm_type, &cred_info->data_type);
275 if (!creds) {
276 /*
277 * This shouldn't happen because we checked the auth object when we
278 * loaded it to make sure it had the appropriate credentials for each
279 * algorithm in supported_algorithms_uas.
280 */
281 SCOPE_EXIT_RTN_VALUE(PJSIP_SC_FORBIDDEN, "%s:%s: No plain text or digest password found for algorithm '" PJSTR_PRINTF_SPEC "'\n",
282 auth_name, src_name, PJSTR_PRINTF_VAR(algorithm->iana_name));
283 }
284 pj_strdup2(pool, &cred_info->data, creds);
285#ifdef HAVE_PJSIP_AUTH_NEW_DIGESTS
286 if (cred_info->data_type == PJSIP_CRED_DATA_DIGEST) {
287 cred_info->algorithm_type = algorithm->algorithm_type;
288 }
289#endif
290
291 SCOPE_EXIT_RTN_VALUE(PJ_SUCCESS, "%s:%s: Success. Data type: %s Algorithm '" PJSTR_PRINTF_SPEC "'\n",
292 auth_name, src_name, cred_info->data_type ? "digest" : "plain text", PJSTR_PRINTF_VAR(algorithm->iana_name));
293}
294
295/*!
296 * \brief Calculate a nonce
297 *
298 * We use this in order to create authentication challenges. We also use this in order
299 * to verify that an incoming request with credentials could be in response to one
300 * of our challenges.
301 *
302 * The nonce is calculated from a timestamp, the source IP address, the source port, a
303 * unique ID for us, and the realm. This helps to ensure that the incoming request
304 * is from the same source that the nonce was calculated for. Including the realm
305 * ensures that multiple challenges to the same request have different nonces.
306 *
307 * \param nonce
308 * \param timestamp A UNIX timestamp expressed as a string
309 * \param rdata The incoming request
310 * \param realm The realm for which authentication should occur
311 */
312static int build_nonce(struct ast_str **nonce, const char *timestamp,
313 const pjsip_rx_data *rdata, const char *realm)
314{
315 struct ast_str *str = ast_str_alloca(256);
316 RAII_VAR(char *, eid, ao2_global_obj_ref(entity_id), ao2_cleanup);
317 char hash[33];
318
319 /*
320 * Note you may be tempted to think why not include the port. The reason
321 * is that when using TCP the port can potentially differ from before.
322 */
323 ast_str_append(&str, 0, "%s", timestamp);
324 ast_str_append(&str, 0, ":%s", rdata->pkt_info.src_name);
325 ast_str_append(&str, 0, ":%s", eid);
326 ast_str_append(&str, 0, ":%s", realm);
328
329 ast_str_append(nonce, 0, "%s/%s", timestamp, hash);
330 return 0;
331}
332
333/*!
334 * \brief Ensure that a nonce on an incoming request is sane.
335 *
336 * The nonce in an incoming Authorization header needs to pass some scrutiny in order
337 * for us to consider accepting it. What we do is re-build a nonce based on request
338 * data and a realm and see if it matches the nonce they sent us.
339 * \param candidate The nonce on an incoming request
340 * \param rdata The incoming request
341 * \param auth The auth credentials we are trying to match against.
342 * \retval 0 Nonce does not pass validity checks
343 * \retval 1 Nonce passes validity check
344 */
345static int check_nonce(const char *candidate, const pjsip_rx_data *rdata, const struct ast_sip_auth *auth)
346{
347 char *copy = ast_strdupa(candidate);
348 char *timestamp = strsep(&copy, "/");
349 int timestamp_int;
350 time_t now = time(NULL);
351 struct ast_str *calculated = ast_str_alloca(64);
352
353 if (!copy) {
354 /* Clearly a bad nonce! */
355 return 0;
356 }
357
358 if (sscanf(timestamp, "%30d", &timestamp_int) != 1) {
359 return 0;
360 }
361
362 if ((int) now - timestamp_int > auth->nonce_lifetime) {
363 return 0;
364 }
365
366 build_nonce(&calculated, timestamp, rdata, S_OR(auth->realm, default_realm));
367 ast_debug(3, "Calculated nonce %s. Actual nonce is %s\n", ast_str_buffer(calculated), candidate);
368 if (strcmp(ast_str_buffer(calculated), candidate)) {
369 return 0;
370 }
371 return 1;
372}
373
374/*!
375 * \brief Result of digest verification
376 */
378 /*! Authentication credentials incorrect */
380 /*! Authentication credentials correct */
382 /*! Authentication credentials correct but nonce mismatch */
384 /*! Authentication credentials were not provided */
386};
387
388static char *verify_result_str[] = {
389 "FAIL",
390 "SUCCESS",
391 "STALE",
392 "NOAUTH"
393};
394
395static enum digest_verify_result find_authorization(const char *endpoint_id,
396 const struct ast_sip_auth *auth, const pjsip_rx_data *rdata)
397{
398 const char *auth_id = ast_sorcery_object_get_id(auth);
399 const char *src_name = rdata->pkt_info.src_name;
400 const char *realm = S_OR(auth->realm, default_realm);
401 struct pjsip_authorization_hdr *auth_hdr =
402 (pjsip_authorization_hdr *) &rdata->msg_info.msg->hdr;
404 int authorization_found = 0;
405 char nonce[64];
406 SCOPE_ENTER(3, "%s:%s:%s: realm: %s\n",
407 endpoint_id, auth_id, src_name, realm);
408
409 while ((auth_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg,
410 PJSIP_H_AUTHORIZATION, auth_hdr ? auth_hdr->next : NULL))) {
411 ast_copy_pj_str(nonce, &auth_hdr->credential.digest.nonce, sizeof(nonce));
412 ast_trace(-1, "%s:%s:%s: Checking nonce %s hdr-realm: " PJSTR_PRINTF_SPEC " hdr-algo: " PJSTR_PRINTF_SPEC " \n",
413 endpoint_id, auth_id, src_name, nonce,
414 PJSTR_PRINTF_VAR(auth_hdr->credential.digest.realm),
415 PJSTR_PRINTF_VAR(auth_hdr->credential.digest.algorithm));
416 authorization_found++;
417 if (check_nonce(nonce, rdata, auth)
418 && pj_strcmp2(&auth_hdr->credential.digest.realm, realm) == 0) {
419 res = AUTH_SUCCESS;
420 break;
421 } else {
422 res = AUTH_STALE;
423 }
424 }
425 if (!authorization_found) {
426 ast_trace(-1, "%s:%s:%s: No Authorization header found\n",
427 endpoint_id, auth_id, src_name);
428 res = AUTH_NOAUTH;
429 }
430
431 SCOPE_EXIT_RTN_VALUE(res, "%s:%s:%s: realm: %s Result %s\n",
432 endpoint_id, auth_id, src_name, realm, verify_result_str[res]);
433}
434
435/*!
436 * \brief Common code for initializing a pjsip_auth_srv
437 */
438static void setup_auth_srv(pj_pool_t *pool, pjsip_auth_srv *auth_server, const char *realm)
439{
440 pjsip_auth_srv_init_param *param = pj_pool_alloc(pool, sizeof(*param));
441 pj_str_t *pj_realm = pj_pool_alloc(pool, sizeof(*pj_realm));
442
443 pj_cstr(pj_realm, realm);
444 param->realm = pj_realm;
445 param->lookup2 = digest_lookup;
446 param->options = 0;
447
448 pjsip_auth_srv_init2(pool, auth_server, param);
449}
450
451/*!
452 * \brief Verify incoming credentials
453 *
454 * \param endpoint_id For logging
455 * \param auth The ast_sip_auth to check against
456 * \param rdata The incoming request
457 * \param pool A pool to use for the auth server
458 * \return One of digest_verify_result
459 */
460static int verify(const char *endpoint_id, const struct ast_sip_auth *auth,
461 pjsip_rx_data *rdata, pj_pool_t *pool)
462{
463 const char *auth_id = ast_sorcery_object_get_id(auth);
464 const char *realm = S_OR(auth->realm, default_realm);
465 const char *src_name = rdata->pkt_info.src_name;
466 pj_status_t authed;
467 int response_code;
468 pjsip_auth_srv auth_server;
469 int stale = 0;
471 SCOPE_ENTER(3, "%s:%s:%s: realm: %s\n",
472 endpoint_id, auth_id, src_name, realm);
473
474 res = find_authorization(endpoint_id, auth, rdata);
475 if (res == AUTH_NOAUTH)
476 {
477 ast_test_suite_event_notify("INCOMING_AUTH_VERIFY_RESULT",
478 "Realm: %s\r\n"
479 "Username: %s\r\n"
480 "Status: %s",
481 realm, auth->auth_user, verify_result_str[res]);
482 SCOPE_EXIT_RTN_VALUE(res, "%s:%s:%s: No Authorization header found\n",
483 endpoint_id, auth_id, src_name);
484 }
485
486 if (res == AUTH_STALE) {
487 /* Couldn't find an authorization with a sane nonce.
488 * Nonce mismatch may just be due to staleness.
489 */
490 stale = 1;
491 }
492
493 setup_auth_srv(pool, &auth_server, realm);
494 store_auth(auth);
495 /* pjsip_auth_srv_verify will invoke digest_lookup */
496 authed = SCOPE_CALL_WITH_RESULT(-1, pj_status_t, pjsip_auth_srv_verify, &auth_server, rdata, &response_code);
497 remove_auth();
498 if (authed == PJ_SUCCESS) {
499 if (stale) {
500 res = AUTH_STALE;
501 } else {
502 res = AUTH_SUCCESS;
503 }
504 } else {
505 char err[256];
506 res = AUTH_FAIL;
507 pj_strerror(authed, err, sizeof(err));
508 ast_trace(-1, "%s:%s:%s: authed: %s\n", endpoint_id, auth_id, src_name, err);
509 }
510
511 ast_test_suite_event_notify("INCOMING_AUTH_VERIFY_RESULT",
512 "Realm: %s\r\n"
513 "Username: %s\r\n"
514 "Status: %s",
515 realm, auth->auth_user, verify_result_str[res]);
516
517 SCOPE_EXIT_RTN_VALUE(res, "%s:%s:%s: Realm: %s Username: %s Result: %s\n",
518 endpoint_id, auth_id, src_name, realm,
519 auth->auth_user, verify_result_str[res]);
520}
521
522/*!
523 * \brief Send a WWW-Authenticate challenge
524 *
525 * \param endpoint_id For logging
526 * \param auth The auth object to use for the challenge
527 * \param tdata The response to add the challenge to
528 * \param rdata The request the challenge is in response to
529 * \param is_stale Indicates whether nonce on incoming request was stale
530 * \param algorithm_type The algorithm to use for the challenge
531 */
532static void challenge(const char *endpoint_id, struct ast_sip_auth *auth,
533 pjsip_tx_data *tdata, const pjsip_rx_data *rdata, int is_stale,
534 const pjsip_auth_algorithm *algorithm)
535{
536 pj_str_t qop;
537 pj_str_t pj_nonce;
538 pjsip_auth_srv auth_server;
539 struct ast_str *nonce = ast_str_alloca(256);
540 char time_buf[32];
541 time_t timestamp = time(NULL);
542 pj_status_t res;
543 const char *realm = S_OR(auth->realm, default_realm);
544 const char *auth_id = ast_sorcery_object_get_id(auth);
545 const char *src_name = rdata->pkt_info.src_name;
546 SCOPE_ENTER(5, "%s:%s:%s: realm: %s time: %d algorithm: " PJSTR_PRINTF_SPEC " stale? %s\n",
547 endpoint_id, auth_id, src_name, realm, (int)timestamp,
548 PJSTR_PRINTF_VAR(algorithm->iana_name), is_stale ? "yes" : "no");
549
550 snprintf(time_buf, sizeof(time_buf), "%d", (int) timestamp);
551
552 build_nonce(&nonce, time_buf, rdata, realm);
553
554 setup_auth_srv(tdata->pool, &auth_server, realm);
555
556 pj_cstr(&pj_nonce, ast_str_buffer(nonce));
557 pj_cstr(&qop, "auth");
558#ifdef HAVE_PJSIP_AUTH_NEW_DIGESTS
559 res = pjsip_auth_srv_challenge2(&auth_server, &qop, &pj_nonce,
560 NULL, is_stale ? PJ_TRUE : PJ_FALSE, tdata, algorithm->algorithm_type);
561#else
562 res = pjsip_auth_srv_challenge(&auth_server, &qop, &pj_nonce,
563 NULL, is_stale ? PJ_TRUE : PJ_FALSE, tdata);
564#endif
565 SCOPE_EXIT_RTN("%s:%s:%s: Sending challenge for realm: %s algorithm: " PJSTR_PRINTF_SPEC
566 " %s\n",
567 endpoint_id, auth_id, src_name, realm, PJSTR_PRINTF_VAR(algorithm->iana_name),
568 res == PJ_SUCCESS ? "succeeded" : "failed");
569}
570
571static char *check_auth_result_str[] = {
572 "CHALLENGE",
573 "SUCCESS",
574 "FAILED",
575 "ERROR",
576};
577
578
579/*!
580 * \brief Check authentication using Digest scheme
581 *
582 * This function will check an incoming message against configured authentication
583 * options. If \b any of the incoming Authorization headers result in successful
584 * authentication, then authentication is considered successful.
585 *
586 * \warning The return code from the function is used by the distributor to
587 * determine which log messages (if any) are emitted. Many admins will be
588 * using log parsers like fail2ban to block IPs that are repeatedly failing
589 * to authenticate so changing the return code could have unintended
590 * consequences.
591 *
592 * \retval AST_SIP_AUTHENTICATION_SUCCESS There was an Authorization header
593 * in the request and it verified successfully with at least one auth object
594 * on the endpoint. No further challenges sent.
595 *
596 * \retval AST_SIP_AUTHENTICATION_CHALLENGE There was NO Authorization header
597 * in the incoming request. We sent a 401 with one or more challenges.
598 *
599 * \retval AST_SIP_AUTHENTICATION_FAILED There were one or more Authorization
600 * headers in the request but they all failed to verify with any auth object
601 * on the endpoint. We sent a 401 with one or more challenges.
602 *
603 * \retval AST_SIP_AUTHENTICATION_ERROR An internal error occurred. No challenges
604 * were sent.
605 *
606 * \see ast_sip_check_authentication
607 */
609 pjsip_rx_data *rdata, pjsip_tx_data *tdata)
610{
611 struct ast_sip_auth **auths;
612 enum digest_verify_result *verify_res;
615 int idx;
616 int is_artificial;
617 int failures = 0;
618 size_t auth_size;
619 const char *endpoint_id = ast_sorcery_object_get_id(endpoint);
620 char *src_name = rdata->pkt_info.src_name;
621 SCOPE_ENTER(3, "%s:%s\n", endpoint_id, src_name);
622
623 auth_size = AST_VECTOR_SIZE(&endpoint->inbound_auths);
624 ast_assert(0 < auth_size);
625
626 auths = ast_alloca(auth_size * sizeof(*auths));
627 verify_res = ast_alloca(auth_size * sizeof(*verify_res));
628
630 if (!artificial_endpoint) {
631 /* Should not happen except possibly if we are shutting down. */
633 }
634
635 is_artificial = endpoint == artificial_endpoint;
637 if (is_artificial) {
638 ast_trace(3, "%s:%s: Using artificial endpoint for authentication\n",
639 endpoint_id, src_name);
640 ast_assert(auth_size == 1);
641 auths[0] = ast_sip_get_artificial_auth();
642 if (!auths[0]) {
643 /* Should not happen except possibly if we are shutting down. */
645 }
646 } else {
647 ast_trace(3, "%s:%s: Using endpoint for authentication\n",
648 endpoint_id, src_name);
649 memset(auths, 0, auth_size * sizeof(*auths));
650 /*
651 * If ast_sip_retrieve_auths returns a failure we still need
652 * to cleanup the auths array because it may have been partially
653 * filled in.
654 */
655 if (ast_sip_retrieve_auths(&endpoint->inbound_auths, auths)) {
656 ast_sip_cleanup_auths(auths, auth_size);
658 "%s:%s: Failed to retrieve some or all auth objects from endpoint\n",
659 endpoint_id, src_name);
660 }
661 }
662
663 /*
664 * Verify any Authorization headers in the incoming request against the
665 * auth objects on the endpoint. If there aren't any Authorization headers
666 * verify() will return AUTH_NOAUTH.
667 *
668 * NOTE: The only reason to use multiple auth objects as a UAS might
669 * be to send challenges for multiple realms however we currently don't
670 * know of anyone actually doing this.
671 */
672 for (idx = 0; idx < auth_size; ++idx) {
673 struct ast_sip_auth *auth = auths[idx];
674 const char *auth_id = ast_sorcery_object_get_id(auth);
675 SCOPE_ENTER(4, "%s:%s:%s: Auth %d of %d: Verifying\n",
676 endpoint_id, auth_id, src_name, idx + 1, (int)auth_size);
677
678 verify_res[idx] = SCOPE_CALL_WITH_RESULT(-1, int, verify, endpoint_id, auth, rdata, tdata->pool);
679 switch((int)verify_res[idx]) {
680 case AUTH_SUCCESS:
682 break;
683 case AUTH_FAIL:
684 failures++;
685 break;
686 case AUTH_NOAUTH:
687 case AUTH_STALE:
688 break;
689 }
690
691 SCOPE_EXIT("%s:%s:%s: Auth %d of %d: Result: %s Failure count: %d\n",
692 endpoint_id, auth_id, src_name, idx + 1, (int)auth_size,
693 verify_result_str[verify_res[idx]], failures);
694
695 /*
696 * If there was a success or there was no Authorization header in the
697 * incoming request, we can stop verifying the rest of the auth objects.
698 */
699 if (verify_res[idx] == AUTH_SUCCESS || verify_res[idx] == AUTH_NOAUTH) {
700 break;
701 }
702 }
703
705 ast_sip_cleanup_auths(auths, auth_size);
706 SCOPE_EXIT_RTN_VALUE(res, "%s:%s: Result: %s\n",
707 endpoint_id, src_name,
709 }
710 ast_trace(-1, "%s:%s: Done with verification. Failures: %d of %d\n",
711 endpoint_id, src_name, failures, (int)auth_size);
712
713 /*
714 * If none of the Authorization headers in the incoming request were
715 * successfully verified, or there were no Authorization headers in the
716 * request, we need to send challenges for each auth object
717 * on the endpoint.
718 */
719 for (idx = 0; idx < auth_size; ++idx) {
720 int i = 0;
721 struct ast_sip_auth *auth = auths[idx];
722 const char *realm = S_OR(auth->realm, default_realm);
723 const char *auth_id = ast_sorcery_object_get_id(auth);
724 SCOPE_ENTER(4, "%s:%s:%s: Auth %d of %d: Sending challenges\n",
725 endpoint_id, auth_id, src_name, idx + 1, (int)auth_size);
726
727 for (i = 0; i < AST_VECTOR_SIZE(&auth->supported_algorithms_uas); i++) {
729 const pjsip_auth_algorithm *algorithm = ast_sip_auth_get_algorithm_by_type(algorithm_type);
730 pjsip_www_authenticate_hdr *auth_hdr = NULL;
731 int already_sent_challenge = 0;
732 SCOPE_ENTER(5, "%s:%s:%s: Auth %d of %d: Challenging with " PJSTR_PRINTF_SPEC "\n",
733 endpoint_id, auth_id, src_name, idx + 1, (int)auth_size,
734 PJSTR_PRINTF_VAR(algorithm->iana_name));
735
736 /*
737 * Per RFC 7616, if we've already sent a challenge for this realm
738 * and algorithm, we must not send another.
739 */
740 while ((auth_hdr = pjsip_msg_find_hdr(tdata->msg,
741 PJSIP_H_WWW_AUTHENTICATE, auth_hdr ? auth_hdr->next : NULL))) {
742 if (pj_strcmp2(&auth_hdr->challenge.common.realm, realm) == 0 &&
743 !pj_stricmp(&auth_hdr->challenge.digest.algorithm, &algorithm->iana_name)) {
744 ast_trace(-1, "%s:%s:%s: Auth %d of %d: Not sending duplicate challenge for realm: %s algorithm: "
746 endpoint_id, auth_id, src_name, idx + 1, (int)auth_size,
747 realm, PJSTR_PRINTF_VAR(algorithm->iana_name));
748 already_sent_challenge = 1;
749 }
750 }
751 if (already_sent_challenge) {
752 SCOPE_EXIT_EXPR(continue);
753 }
754
755 SCOPE_CALL(5, challenge, endpoint_id, auth, tdata, rdata,
756 verify_res[idx] == AUTH_STALE, algorithm);
758
759 SCOPE_EXIT("%s:%s:%s: Auth %d of %d: Challenged with " PJSTR_PRINTF_SPEC "\n",
760 endpoint_id, auth_id, src_name, idx + 1, (int)auth_size,
761 PJSTR_PRINTF_VAR(algorithm->iana_name));
762 }
763 SCOPE_EXIT("%s:%s:%s: Auth %d of %d: Done with challenges\n",
764 endpoint_id, auth_id, src_name, idx + 1, (int)auth_size);
765 }
766
767 /*
768 * If we've sent challenges for multiple auth objects, we currently
769 * return SUCCESS when the first one succeeds. We may want to change
770 * this in the future to require that all succeed but as stated above,
771 * currently we don't have a use case for even using more than one
772 * auth object as a UAS.
773 */
774
775 /*
776 * If the authentication failed for any reason, we want to send
777 * a 401 with a challenge. If it was because there was no
778 * Authorization header or there was a stale nonce, fine. That's not
779 * unusual so we return AST_SIP_AUTHENTICATION_CHALLENGE. If it
780 * failed because of a user/password mismatch then we return
781 * AST_SIP_AUTHENTICATION_FAILED which causes the distributor to
782 * print a "Failed to authenticate" message.
783 */
784 if (failures == auth_size) {
786 }
787
788 ast_sip_cleanup_auths(auths, auth_size);
789 SCOPE_EXIT_RTN_VALUE(res, "%s:%s: Result: %s\n",
790 endpoint_id, src_name,
792
793}
794
797 .check_authentication = digest_check_auth,
798};
799
800static int build_entity_id(void)
801{
802 char *eid;
803
805 if (!eid) {
806 return -1;
807 }
808
810 ao2_global_obj_replace_unref(entity_id, eid);
811 ao2_ref(eid, -1);
812 return 0;
813}
814
815static void global_loaded(const char *object_type)
816{
818}
819
820/*! \brief Observer which is used to update our default_realm when the global setting changes */
823};
824
825static int reload_module(void)
826{
827 if (build_entity_id()) {
828 return -1;
829 }
830 return 0;
831}
832
833static int load_module(void)
834{
835 if (build_entity_id()) {
837 }
838
841
843 ao2_global_obj_release(entity_id);
845 }
847}
848
849static int unload_module(void)
850{
853 ao2_global_obj_release(entity_id);
854 return 0;
855}
856
857AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP authentication resource",
858 .support_level = AST_MODULE_SUPPORT_CORE,
859 .load = load_module,
860 .unload = unload_module,
862 .load_pri = AST_MODPRI_CHANNEL_DEPEND - 5,
863 .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:2380
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:2486
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:2454
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:1505
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:978
#define ast_assert(a)
Definition: utils.h:776
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:620
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition: vector.h:691