Asterisk - The Open Source Telephony Project GIT-master-8f1982c
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
Data Structures | Functions | Variables
res_pjsip_outbound_authenticator_digest.c File Reference

PJSIP UAC Authentication. More...

#include "asterisk.h"
#include <pjsip.h>
#include "asterisk/res_pjsip.h"
#include "asterisk/logger.h"
#include "asterisk/module.h"
#include "asterisk/strings.h"
#include "asterisk/vector.h"
Include dependency graph for res_pjsip_outbound_authenticator_digest.c:

Go to the source code of this file.

Data Structures

struct  cred_info_vector
 

Functions

static void __reg_module (void)
 
static void __unreg_module (void)
 
struct ast_moduleAST_MODULE_SELF_SYM (void)
 
static int digest_create_request_with_auth (const struct ast_sip_auth_vector *auth_ids_vector, pjsip_rx_data *challenge, pjsip_tx_data *old_request, pjsip_tx_data **new_request)
 
static pjsip_hdr_e get_auth_search_type (pjsip_rx_data *challenge)
 
static void get_creds_for_header (const char *id, const char *src_name, pjsip_www_authenticate_hdr *auth_hdr, size_t auth_object_count, const struct ast_sip_auth_objects_vector *auth_objects_vector, struct cred_info_vector *auth_creds, struct ast_str **realms)
 Get credentials (if any) from auth objects for a WWW/Proxy-Authenticate header. More...
 
static const pjsip_auth_algorithmget_supported_algorithm (pjsip_www_authenticate_hdr *auth_hdr)
 
static int load_module (void)
 
static pj_status_t set_auth_creds (const char *id, pjsip_auth_clt_sess *auth_sess, const struct ast_sip_auth_objects_vector *auth_objects_vector, pjsip_rx_data *challenge, struct ast_str **realms)
 
static int unload_module (void)
 

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "PJSIP authentication resource" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_CHANNEL_DEPEND, .requires = "res_pjsip", }
 
static const struct ast_module_infoast_module_info = &__mod_info
 
static struct ast_sip_outbound_authenticator digest_authenticator
 

Detailed Description

PJSIP UAC Authentication.

This module handles authentication when Asterisk is the UAC.

Definition in file res_pjsip_outbound_authenticator_digest.c.

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 684 of file res_pjsip_outbound_authenticator_digest.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 684 of file res_pjsip_outbound_authenticator_digest.c.

◆ AST_MODULE_SELF_SYM()

struct ast_module * AST_MODULE_SELF_SYM ( void  )

Definition at line 684 of file res_pjsip_outbound_authenticator_digest.c.

◆ digest_create_request_with_auth()

static int digest_create_request_with_auth ( const struct ast_sip_auth_vector auth_ids_vector,
pjsip_rx_data *  challenge,
pjsip_tx_data *  old_request,
pjsip_tx_data **  new_request 
)
static

Definition at line 470 of file res_pjsip_outbound_authenticator_digest.c.

472{
473 pjsip_auth_clt_sess auth_sess;
474 pjsip_cseq_hdr *cseq;
475 pj_status_t status;
476 struct ast_sip_auth_objects_vector auth_objects_vector;
477 size_t auth_object_count = 0;
478 pjsip_dialog *dlg = pjsip_rdata_get_dlg(challenge);
479 struct ast_sip_endpoint *endpoint = (dlg ? ast_sip_dialog_get_endpoint(dlg) : NULL);
480 /*
481 * We're ast_strdupa'ing the endpoint id because we're going to
482 * clean up the endpoint immediately after this. We only needed
483 * it to get the id for logging.
484 */
485 char *endpoint_id = endpoint ? ast_strdupa(ast_sorcery_object_get_id(endpoint)) : NULL;
486 char *id = endpoint_id ?: "noendpoint";
487 char *src_name = challenge->pkt_info.src_name;
488 struct ast_str *realms = NULL;
489 int res = -1;
490 char *pj_err = NULL;
491 SCOPE_ENTER(3, "%s:%s\n", id, src_name);
492
493 /* We only needed endpoint to get the id */
494 ao2_cleanup(endpoint);
495
496 /*
497 * Some older compilers have an issue with initializing structures with
498 * pjsip_auth_clt_sess auth_sess = { 0, };
499 * so we'll just do it the old fashioned way.
500 */
501 memset(&auth_sess, 0, sizeof(auth_sess));
502
503 if (!auth_ids_vector || AST_VECTOR_SIZE(auth_ids_vector) == 0) {
504 SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_ERROR, "%s:%s: There were no auth ids available\n",
505 id, src_name);
506 return -1;
507 }
508
509 /*
510 * auth_ids_vector contains only ids but we need the complete objects.
511 */
512 if (AST_VECTOR_INIT(&auth_objects_vector, AST_VECTOR_SIZE(auth_ids_vector)) != 0) {
513 SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_ERROR, "%s:%s: Couldn't initialize auth object vector\n",
514 id, src_name);
515 }
516
517 /*
518 * We don't really care about ast_sip_retrieve_auths_vector()'s return code
519 * because we're checking the count of objects in the vector.
520 *
521 * Don't forget to call
522 * ast_sip_cleanup_auth_objects_vector(&auth_objects_vector);
523 * AST_VECTOR_FREE(&auth_objects_vector);
524 * when you're done with the vector
525 */
526 ast_trace(-1, "%s:%s: Retrieving %d auth objects\n", id, src_name,
527 (int)AST_VECTOR_SIZE(auth_ids_vector));
528 ast_sip_retrieve_auths_vector(auth_ids_vector, &auth_objects_vector);
529 auth_object_count = AST_VECTOR_SIZE(&auth_objects_vector);
530 if (auth_object_count == 0) {
531 /*
532 * If none of the auth ids were found, we can't continue.
533 * We're OK if there's at least one left.
534 * ast_sip_retrieve_auths_vector() will print a warning for every
535 * id that wasn't found.
536 */
537 res = -1;
538 ast_trace(-1, "%s:%s: No auth objects found\n", id, src_name);
539 goto cleanup;
540 }
541 ast_trace(-1, "%s:%s: Retrieved %d auth objects\n", id, src_name,
542 (int)auth_object_count);
543
544 status = pjsip_auth_clt_init(&auth_sess, ast_sip_get_pjsip_endpoint(),
545 old_request->pool, 0);
546 if (status != PJ_SUCCESS) {
547 pj_err = ast_alloca(PJ_ERR_MSG_SIZE);
548 pj_strerror(status, pj_err, PJ_ERR_MSG_SIZE);
549 ast_log(LOG_ERROR, "%s:%s: Failed to initialize client authentication session: %s\n",
550 id, src_name, pj_err);
551 res = -1;
552 goto cleanup;
553 }
554
555 /*
556 * realms is used only for displaying good error messages.
557 */
558 realms = ast_str_create(32);
559 if (!realms) {
560 res = -1;
561 goto cleanup;
562 }
563
564 /*
565 * Load pjproject with the valid credentials for the Authentication headers
566 * received on the 401 or 407 response.
567 */
568 status = SCOPE_CALL_WITH_RESULT(-1, pj_status_t, set_auth_creds, id, &auth_sess, &auth_objects_vector, challenge, &realms);
569 if (status != PJ_SUCCESS && status != PJSIP_ENOCREDENTIAL) {
570 pj_err = ast_alloca(PJ_ERR_MSG_SIZE);
571 }
572
573 switch (status) {
574 case PJ_SUCCESS:
575 break;
576 case PJSIP_ENOCREDENTIAL:
578 "%s:%s: No auth objects matching realm/algorithm(s) '%s' from challenge found.\n",
579 id, src_name, realms ? ast_str_buffer(realms) : "<none>");
580 res = -1;
581 goto cleanup;
582 default:
583 pj_strerror(status, pj_err, PJ_ERR_MSG_SIZE);
584 ast_log(LOG_WARNING, "%s:%s: Failed to set authentication credentials: %s\n",
585 id, src_name, pj_err);
586 res = -1;
587 goto cleanup;
588 }
589
590 /*
591 * reinit_req actually creates the Authorization headers to send on
592 * the next request. If reinit_req already has a cached credential
593 * from an earlier successful authorization, it'll use it. Otherwise
594 * it'll create a new authorization and cache it.
595 */
596 status = SCOPE_CALL_WITH_RESULT(-1, pj_status_t, pjsip_auth_clt_reinit_req,
597 &auth_sess, challenge, old_request, new_request);
598 if (status != PJ_SUCCESS) {
599 pj_err = ast_alloca(PJ_ERR_MSG_SIZE);
600 }
601
602 switch (status) {
603 case PJ_SUCCESS:
604 /* PJSIP creates a new transaction for new_request (meaning it creates a new
605 * branch). However, it recycles the Call-ID, from-tag, and CSeq from the
606 * original request. Some SIP implementations will not process the new request
607 * since the CSeq is the same as the original request. Incrementing it here
608 * fixes the interop issue
609 */
610 cseq = pjsip_msg_find_hdr((*new_request)->msg, PJSIP_H_CSEQ, NULL);
611 ast_assert(cseq != NULL);
612 ++cseq->cseq;
613 res = 0;
614 ast_trace(-1, "%s:%s: Created new request with auth\n", id, src_name);
615 goto cleanup;
616 case PJSIP_ENOCREDENTIAL:
617 /*
618 * This should be rare since set_outbound_authentication_credentials()
619 * did the matching but you never know.
620 */
622 "%s:%s: No auth objects matching realm(s) '%s' from challenge found.\n",
623 id, src_name, realms ? ast_str_buffer(realms) : "<none>");
624 break;
625 case PJSIP_EAUTHSTALECOUNT:
626 pj_strerror(status, pj_err, PJ_ERR_MSG_SIZE);
628 "%s:%s: Unable to create request with auth: %s\n",
629 id, src_name, pj_err);
630 break;
631 case PJSIP_EFAILEDCREDENTIAL:
632 pj_strerror(status, pj_err, PJ_ERR_MSG_SIZE);
633 ast_log(LOG_WARNING, "%s:%s: Authentication credentials not accepted by server. %s\n",
634 id, src_name, pj_err);
635 break;
636 default:
637 pj_strerror(status, pj_err, PJ_ERR_MSG_SIZE);
638 ast_log(LOG_WARNING, "%s:%s: Unable to create request with auth: %s\n",
639 id, src_name, pj_err);
640 break;
641 }
642 res = -1;
643
644cleanup:
645#if defined(HAVE_PJSIP_AUTH_CLT_DEINIT)
646 /* If we initialized the auth_sess, clean it up */
647 if (auth_sess.endpt) {
648 pjsip_auth_clt_deinit(&auth_sess);
649 }
650#endif
651
652 ast_sip_cleanup_auth_objects_vector(&auth_objects_vector);
653 AST_VECTOR_FREE(&auth_objects_vector);
654 ast_free(realms);
655
656 SCOPE_EXIT_RTN_VALUE(res, "%s:%s: result: %s\n", id, src_name,
657 res == 0 ? "success" : "failure");
658}
jack_status_t status
Definition: app_jack.c:149
#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 ast_log
Definition: astobj2.c:42
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define SCOPE_EXIT_RTN_VALUE(__return_value,...)
#define SCOPE_EXIT_LOG_RTN_VALUE(__value, __log_level,...)
#define SCOPE_CALL_WITH_RESULT(level, __var, __funcname,...)
#define SCOPE_ENTER(level,...)
#define ast_trace(level,...)
struct ast_sip_endpoint * ast_sip_dialog_get_endpoint(pjsip_dialog *dlg)
Get the endpoint associated with this dialog.
#define LOG_ERROR
#define LOG_WARNING
static void * cleanup(void *unused)
Definition: pbx_realtime.c:124
pjsip_endpoint * ast_sip_get_pjsip_endpoint(void)
Get a pointer to the PJSIP endpoint.
Definition: res_pjsip.c:520
#define ast_sip_cleanup_auth_objects_vector(auth_objects)
Clean up retrieved auth objects in vector.
Definition: res_pjsip.h:2967
int ast_sip_retrieve_auths_vector(const struct ast_sip_auth_vector *auth_ids, struct ast_sip_auth_objects_vector *auth_objects)
Retrieve relevant SIP auth structures from sorcery as a vector.
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 pj_status_t set_auth_creds(const char *id, pjsip_auth_clt_sess *auth_sess, const struct ast_sip_auth_objects_vector *auth_objects_vector, pjsip_rx_data *challenge, struct ast_str **realms)
#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
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
An entity with which Asterisk communicates.
Definition: res_pjsip.h:1051
Support for dynamic strings.
Definition: strings.h:623
#define ast_assert(a)
Definition: utils.h:739
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:609
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition: vector.h:174
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition: vector.h:113

References ao2_cleanup, ast_alloca, ast_assert, ast_free, ast_log, ast_sip_cleanup_auth_objects_vector, ast_sip_dialog_get_endpoint(), ast_sip_get_pjsip_endpoint(), ast_sip_retrieve_auths_vector(), ast_sorcery_object_get_id(), ast_str_buffer(), ast_str_create, ast_strdupa, ast_trace, AST_VECTOR_FREE, AST_VECTOR_INIT, AST_VECTOR_SIZE, challenge(), cleanup(), LOG_ERROR, LOG_WARNING, NULL, SCOPE_CALL_WITH_RESULT, SCOPE_ENTER, SCOPE_EXIT_LOG_RTN_VALUE, SCOPE_EXIT_RTN_VALUE, set_auth_creds(), and status.

◆ get_auth_search_type()

static pjsip_hdr_e get_auth_search_type ( pjsip_rx_data *  challenge)
static

Definition at line 50 of file res_pjsip_outbound_authenticator_digest.c.

51{
52 if (challenge->msg_info.msg->line.status.code == PJSIP_SC_UNAUTHORIZED) {
53 return PJSIP_H_WWW_AUTHENTICATE;
54 } else if (challenge->msg_info.msg->line.status.code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED) {
55 return PJSIP_H_PROXY_AUTHENTICATE;
56 } else {
58 "Status code %d was received when it should have been 401 or 407.\n",
59 challenge->msg_info.msg->line.status.code);
60 return PJSIP_H_OTHER;
61 }
62}

References ast_log, challenge(), and LOG_ERROR.

Referenced by set_auth_creds().

◆ get_creds_for_header()

static void get_creds_for_header ( const char *  id,
const char *  src_name,
pjsip_www_authenticate_hdr *  auth_hdr,
size_t  auth_object_count,
const struct ast_sip_auth_objects_vector auth_objects_vector,
struct cred_info_vector auth_creds,
struct ast_str **  realms 
)
static

Get credentials (if any) from auth objects for a WWW/Proxy-Authenticate header.

Parameters
idFor logging
src_nameFor logging
auth_hdrThe *-Authenticate header to check
auth_object_countThe number of auth objects available
auth_objects_vectorThe vector of available auth objects
auth_credsThe vector to store the credentials in
realmsFor logging

Definition at line 98 of file res_pjsip_outbound_authenticator_digest.c.

102{
103 int exact_match_index = -1;
104 int wildcard_match_index = -1;
105 struct ast_sip_auth *found_auth = NULL;
106 const pjsip_auth_algorithm *challenge_algorithm =
107 get_supported_algorithm(auth_hdr);
108 int i = 0;
109 pjsip_cred_info auth_cred;
110 const char *cred_data;
111 int res = 0;
112 SCOPE_ENTER(4, "%s:%s: Testing header realm: '" PJSTR_PRINTF_SPEC "' algorithm: '"
113 PJSTR_PRINTF_SPEC "'\n", id, src_name,
114 PJSTR_PRINTF_VAR(auth_hdr->challenge.digest.realm),
115 PJSTR_PRINTF_VAR(auth_hdr->challenge.digest.algorithm));
116
117 if (!challenge_algorithm) {
118 SCOPE_EXIT_RTN("%s:%s: Skipping header with realm '" PJSTR_PRINTF_SPEC "' "
119 "and unsupported " PJSTR_PRINTF_SPEC "' algorithm \n", id, src_name,
120 PJSTR_PRINTF_VAR(auth_hdr->challenge.digest.realm),
121 PJSTR_PRINTF_VAR(auth_hdr->challenge.digest.algorithm));
122 }
123
124 /*
125 * If we already have credentials for this realm, we don't need to
126 * process this header. We can just skip it.
127 */
128 for (i = 0; i < AST_VECTOR_SIZE(auth_creds); i++) {
129 pjsip_cred_info auth_cred = AST_VECTOR_GET(auth_creds, i);
130 if (pj_stricmp(&auth_cred.realm, &auth_hdr->challenge.common.realm) == 0) {
131 SCOPE_EXIT_RTN("%s:%s: Skipping header with realm '" PJSTR_PRINTF_SPEC "' "
132 "because we already have credentials for it\n", id, src_name,
133 PJSTR_PRINTF_VAR(auth_hdr->challenge.digest.realm));
134 }
135 }
136
137 /*
138 * Appending "realm/agorithm" to realms is strictly so
139 * digest_create_request_with_auth() can display good error messages.
140 */
141 if (*realms) {
143 PJSTR_PRINTF_VAR(auth_hdr->challenge.digest.realm),
144 PJSTR_PRINTF_VAR(auth_hdr->challenge.digest.algorithm));
145 }
146
147 /*
148 * Now that we have a valid header, we can loop over the auths available to
149 * find either an exact realm match or, failing that, a wildcard auth (an
150 * auth with an empty or "*" realm).
151 *
152 * NOTE: We never use the global default realm when we're the UAC responding
153 * to a 401 or 407. We only use that when we're the UAS (handled elsewhere)
154 * and the auth object didn't have a realm.
155 */
156 ast_trace(-1, "%s:%s: Searching %zu auths to find matching ones for header with realm "
157 "'" PJSTR_PRINTF_SPEC "' and algorithm '" PJSTR_PRINTF_SPEC "'\n",
158 id, src_name, auth_object_count,
159 PJSTR_PRINTF_VAR(auth_hdr->challenge.digest.realm),
160 PJSTR_PRINTF_VAR(auth_hdr->challenge.digest.algorithm));
161
162 for (i = 0; i < auth_object_count; ++i) {
163 struct ast_sip_auth *auth = AST_VECTOR_GET(auth_objects_vector, i);
164 const char *auth_id = ast_sorcery_object_get_id(auth);
165 SCOPE_ENTER(5, "%s:%s: Checking auth '%s' with realm '%s'\n",
166 id, src_name, auth_id, auth->realm);
167
168 /*
169 * Is the challenge algorithm in the auth's supported_algorithms_uac
170 * and is there either a plain text password or a password_digest
171 * for the algorithm?
172 */
174 challenge_algorithm->algorithm_type)) {
175 SCOPE_EXIT_EXPR(continue, "%s:%s: Skipping auth '%s' with realm '%s' because it doesn't support "
176 " algorithm '" PJSTR_PRINTF_SPEC "'\n", id, src_name,
177 auth_id, auth->realm,
178 PJSTR_PRINTF_VAR(challenge_algorithm->iana_name));
179 }
180
181 /*
182 * If this auth object's realm exactly matches the one
183 * from the header, we can just break out and use it.
184 *
185 * NOTE: If there's more than one auth object for an endpoint with
186 * a matching realm it's a misconfiguration. We'll only use the first.
187 */
188 if (pj_stricmp2(&auth_hdr->challenge.digest.realm, auth->realm) == 0) {
189 exact_match_index = i;
190 /*
191 * If we found an exact realm match, there's no need to keep
192 * looking for a wildcard.
193 */
194 SCOPE_EXIT_EXPR(break, "%s:%s: Found matching auth '%s' with realm '%s'\n",
195 id, src_name, auth_id, auth->realm);
196 }
197
198 /*
199 * If this auth object's realm is empty or a "*", it's a wildcard
200 * auth object. We going to save its index but keep iterating over
201 * the vector in case we find an exact match later.
202 *
203 * NOTE: If there's more than one wildcard auth object for an endpoint
204 * it's a misconfiguration. We'll only use the first.
205 */
206 if (wildcard_match_index < 0
207 && (ast_strlen_zero(auth->realm) || ast_strings_equal(auth->realm, "*"))) {
208 ast_trace(-1, "%s:%s: Found wildcard auth '%s' for realm '" PJSTR_PRINTF_SPEC "'\n",
209 id, src_name, auth_id,
210 PJSTR_PRINTF_VAR(auth_hdr->challenge.digest.realm));
211 wildcard_match_index = i;
212 }
213 SCOPE_EXIT("%s:%s: Done checking auth '%s' with realm '%s'. "
214 "Found exact? %s Found wildcard? %s\n", id, src_name,
215 auth_id, auth->realm, exact_match_index >= 0 ? "yes" : "no",
216 wildcard_match_index >= 0 ? "yes" : "no");
217 } /* End auth object loop */
218
219 if (exact_match_index < 0 && wildcard_match_index < 0) {
220 /*
221 * Didn't find either a wildcard or an exact realm match.
222 * Move on to the next header.
223 */
224 SCOPE_EXIT_RTN("%s:%s: No auth matching realm or no wildcard found for realm '" PJSTR_PRINTF_SPEC "'\n",
225 id, src_name, PJSTR_PRINTF_VAR(auth_hdr->challenge.digest.realm));
226 }
227
228 if (exact_match_index >= 0) {
229 /*
230 * If we found an exact match, we'll always prefer that.
231 */
232 found_auth = AST_VECTOR_GET(auth_objects_vector, exact_match_index);
233 ast_trace(-1, "%s:%s: Using matched auth '%s' with realm '" PJSTR_PRINTF_SPEC "'\n",
234 id, src_name, ast_sorcery_object_get_id(found_auth),
235 PJSTR_PRINTF_VAR(auth_hdr->challenge.digest.realm));
236 } else {
237 /*
238 * We'll only use the wildcard if we didn't find an exact match.
239 */
240 found_auth = AST_VECTOR_GET(auth_objects_vector, wildcard_match_index);
241 ast_trace(-1, "%s:%s: Using wildcard auth '%s' for realm '" PJSTR_PRINTF_SPEC "'\n",
242 id, src_name, ast_sorcery_object_get_id(found_auth),
243 PJSTR_PRINTF_VAR(auth_hdr->challenge.digest.realm));
244 }
245
246 /*
247 * Now that we have an auth object to use, we need to create a
248 * pjsip_cred_info structure for each algorithm we support.
249 */
250
251 memset(&auth_cred, 0, sizeof(auth_cred));
252 /*
253 * Copy the fields from the auth_object to the
254 * pjsip_cred_info structure.
255 */
256 auth_cred.realm = auth_hdr->challenge.common.realm;
257 pj_cstr(&auth_cred.username, found_auth->auth_user);
258 pj_cstr(&auth_cred.scheme, "digest");
259
260 /*
261 * auth_cred.data_type tells us whether the credential is a plain text
262 * password or a pre-digested one.
263 */
264 cred_data = SCOPE_CALL_WITH_RESULT(-1, const char *, ast_sip_auth_get_creds,
265 found_auth, challenge_algorithm->algorithm_type, &auth_cred.data_type);
266 /*
267 * This can't really fail because we already called
268 * ast_sip_auth_is_algorithm_available() for the auth
269 * but we check anyway.
270 */
271 if (!cred_data) {
272 SCOPE_EXIT_RTN("%s:%s: Shouldn't have happened\n", id, src_name);
273 }
274
275 pj_cstr(&auth_cred.data, cred_data);
276#ifdef HAVE_PJSIP_AUTH_NEW_DIGESTS
277 if (auth_cred.data_type == PJSIP_CRED_DATA_DIGEST) {
278 auth_cred.algorithm_type = challenge_algorithm->algorithm_type;
279 }
280#endif
281 /*
282 * Because the vector contains actual structures and not pointers
283 * to structures, the call to AST_VECTOR_APPEND results in a simple
284 * assign of one structure to another, effectively copying the auth_cred
285 * structure contents to the array element.
286 *
287 * Also note that the calls to pj_cstr above set their respective
288 * auth_cred fields to the _pointers_ of their corresponding auth
289 * object fields. This is safe because the call to
290 * pjsip_auth_clt_set_credentials() below strdups them before we
291 * return to the calling function which decrements the reference
292 * counts.
293 */
294 res = AST_VECTOR_APPEND(auth_creds, auth_cred);
295 SCOPE_EXIT_RTN("%s:%s: %s credential for realm: '" PJSTR_PRINTF_SPEC "' algorithm: '"
296 PJSTR_PRINTF_SPEC "'\n", id, src_name,
297 res == 0 ? "Added" : "Failed to add",
298 PJSTR_PRINTF_VAR(auth_hdr->challenge.digest.realm),
299 PJSTR_PRINTF_VAR(auth_hdr->challenge.digest.algorithm));
300}
#define SCOPE_EXIT_RTN(...)
#define SCOPE_EXIT_EXPR(__expr,...)
#define SCOPE_EXIT(...)
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
#define PJSTR_PRINTF_VAR(_v)
Definition: res_pjsip.h:72
#define PJSTR_PRINTF_SPEC
Definition: res_pjsip.h:71
static const pjsip_auth_algorithm * get_supported_algorithm(pjsip_www_authenticate_hdr *auth_hdr)
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
int ast_strings_equal(const char *str1, const char *str2)
Compare strings for equality checking for NULL.
Definition: strings.c:238
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
struct pjsip_auth_algorithm_type_vector supported_algorithms_uac
Definition: res_pjsip.h:687
const ast_string_field realm
Definition: res_pjsip.h:681
const ast_string_field auth_user
Definition: res_pjsip.h:681
pjsip_auth_algorithm_type algorithm_type
Definition: res_pjsip.h:618
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition: vector.h:256
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition: vector.h:680

References pjsip_auth_algorithm::algorithm_type, ast_sip_auth_get_creds(), ast_sip_auth_is_algorithm_available(), ast_sorcery_object_get_id(), ast_str_append(), ast_strings_equal(), ast_strlen_zero(), ast_trace, AST_VECTOR_APPEND, AST_VECTOR_GET, AST_VECTOR_SIZE, ast_sip_auth::auth_user, get_supported_algorithm(), pjsip_auth_algorithm::iana_name, NULL, PJSTR_PRINTF_SPEC, PJSTR_PRINTF_VAR, ast_sip_auth::realm, SCOPE_CALL_WITH_RESULT, SCOPE_ENTER, SCOPE_EXIT, SCOPE_EXIT_EXPR, SCOPE_EXIT_RTN, and ast_sip_auth::supported_algorithms_uac.

Referenced by set_auth_creds().

◆ get_supported_algorithm()

static const pjsip_auth_algorithm * get_supported_algorithm ( pjsip_www_authenticate_hdr *  auth_hdr)
static

Definition at line 69 of file res_pjsip_outbound_authenticator_digest.c.

70{
71 const pjsip_auth_algorithm *algo = NULL;
72
73 algo = ast_sip_auth_get_algorithm_by_iana_name(&auth_hdr->challenge.digest.algorithm);
74 if (!algo) {
75 return NULL;
76 }
77
79 return algo;
80 }
81 return NULL;
82}
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
pj_bool_t ast_sip_auth_is_algorithm_supported(pjsip_auth_algorithm_type algorithm_type)
Is algorithm supported by OpenSSL and pjproject?
Definition: config_auth.c:104

References pjsip_auth_algorithm::algorithm_type, ast_sip_auth_get_algorithm_by_iana_name(), ast_sip_auth_is_algorithm_supported(), and NULL.

Referenced by get_creds_for_header().

◆ load_module()

static int load_module ( void  )
static

Definition at line 664 of file res_pjsip_outbound_authenticator_digest.c.

665{
668 }
670}
@ 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
int ast_sip_register_outbound_authenticator(struct ast_sip_outbound_authenticator *outbound_auth)
Register an outbound SIP authenticator.
Definition: res_pjsip.c:191
static struct ast_sip_outbound_authenticator digest_authenticator

References AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, ast_sip_register_outbound_authenticator(), and digest_authenticator.

◆ set_auth_creds()

static pj_status_t set_auth_creds ( const char *  id,
pjsip_auth_clt_sess *  auth_sess,
const struct ast_sip_auth_objects_vector auth_objects_vector,
pjsip_rx_data *  challenge,
struct ast_str **  realms 
)
static

Definition at line 339 of file res_pjsip_outbound_authenticator_digest.c.

342{
343 size_t auth_object_count;
344 pjsip_www_authenticate_hdr *auth_hdr = NULL;
345 pj_status_t res = PJ_SUCCESS;
346 pjsip_hdr_e search_type;
347 size_t cred_count = 0;
348 pjsip_cred_info *creds_array;
349 char *pj_err = NULL;
350 const char *src_name = challenge->pkt_info.src_name;
351 /*
352 * Normally vector elements are pointers to something else, usually
353 * structures. In this case however, the elements are the
354 * structures themselves instead of pointers to them. This is due
355 * to the fact that pjsip_auth_clt_set_credentials() expects an
356 * array of structures, not an array of pointers to structures.
357 * Thankfully, vectors allow you to "steal" their underlying
358 * arrays, in this case an array of pjsip_cred_info structures,
359 * which we'll pass to pjsip_auth_clt_set_credentials() at the
360 * end.
361 */
362 struct cred_info_vector auth_creds;
363 SCOPE_ENTER(3, "%s:%s\n", id, src_name);
364
365 search_type = get_auth_search_type(challenge);
366 if (search_type == PJSIP_H_OTHER) {
367 /*
368 * The status code on the response wasn't 401 or 407
369 * so there are no WWW-Authenticate or Proxy-Authenticate
370 * headers to process.
371 */
372 SCOPE_EXIT_RTN_VALUE(PJ_ENOTSUP, "%s:%s: Status code %d was received when it should have been 401 or 407.\n",
373 id, src_name, challenge->msg_info.msg->line.status.code);
374 }
375
376 auth_object_count = AST_VECTOR_SIZE(auth_objects_vector);
377 if (auth_object_count == 0) {
378 /* This shouldn't happen but we'll check anyway. */
379 SCOPE_EXIT_RTN_VALUE(PJ_EINVAL, "%s:%s No auth objects available\n", id, src_name);
380 }
381
382 /*
383 * The number of pjsip_cred_infos we send to pjproject can
384 * vary based on the number of acceptable headers received
385 * and the number of acceptable auth objects on the endpoint
386 * so we just use a vector to accumulate them.
387 *
388 * NOTE: You have to call AST_VECTOR_FREE() on the vector
389 * but you don't have to free the elements because they're
390 * actual structures, not pointers to structures.
391 */
392 if (AST_VECTOR_INIT(&auth_creds, 5) != 0) {
393 SCOPE_EXIT_RTN_VALUE(PJ_ENOMEM);
394 }
395
396 /*
397 * There may be multiple WWW/Proxy-Authenticate headers each one having
398 * a different realm/algorithm pair. Test each to see if we have credentials
399 * for it and accumulate them in the auth_creds vector.
400 * The code doesn't really care but just for reference, RFC-7616 says
401 * a UAS can't send multiple headers for the same realm with the same
402 * algorithm. It also says the UAS should send the headers in order
403 * of preference with the first one being the most preferred.
404 */
405 while ((auth_hdr = pjsip_msg_find_hdr(challenge->msg_info.msg,
406 search_type, auth_hdr ? auth_hdr->next : NULL))) {
407
408 get_creds_for_header(id, src_name, auth_hdr, auth_object_count,
409 auth_objects_vector, &auth_creds, realms);
410
411 } /* End header loop */
412
413 if (*realms && ast_str_strlen(*realms)) {
414 /*
415 * Chop off the trailing ", " on the last realm-algorithm.
416 */
417 ast_str_truncate(*realms, ast_str_strlen(*realms) - 2);
418 }
419
420 if (AST_VECTOR_SIZE(&auth_creds) == 0) {
421 /* No matching auth objects were found. */
422 res = PJSIP_ENOCREDENTIAL;
423 goto cleanup;
424 }
425
426 /*
427 * Here's where we steal the cred info structures from the vector.
428 *
429 * The steal effectively returns a pointer to the underlying
430 * array of pjsip_cred_info structures which is exactly what we need
431 * to pass to pjsip_auth_clt_set_credentials().
432 *
433 * <struct cred info><struct cred info>...<struct cred info>
434 * ^pointer
435 *
436 * Since we stole the array from the vector, we have to free it ourselves.
437 *
438 * We also have to copy the size before we steal because stealing
439 * resets the vector size to 0.
440 */
441 cred_count = AST_VECTOR_SIZE(&auth_creds);
442 creds_array = AST_VECTOR_STEAL_ELEMENTS(&auth_creds);
443
444 res = pjsip_auth_clt_set_credentials(auth_sess, cred_count, creds_array);
445 ast_free(creds_array);
446
447cleanup:
448 AST_VECTOR_FREE(&auth_creds);
449 if (res != PJ_SUCCESS) {
450 pj_err = ast_alloca(PJ_ERR_MSG_SIZE);
451 pj_strerror(res, pj_err, PJ_ERR_MSG_SIZE);
452 }
453 SCOPE_EXIT_RTN_VALUE(res, "%s:%s: Set %zu credentials in auth session: %s\n",
454 id, src_name, cred_count, S_OR(pj_err, "success"));
455}
static void get_creds_for_header(const char *id, const char *src_name, pjsip_www_authenticate_hdr *auth_hdr, size_t auth_object_count, const struct ast_sip_auth_objects_vector *auth_objects_vector, struct cred_info_vector *auth_creds, struct ast_str **realms)
Get credentials (if any) from auth objects for a WWW/Proxy-Authenticate header.
static pjsip_hdr_e get_auth_search_type(pjsip_rx_data *challenge)
char * ast_str_truncate(struct ast_str *buf, ssize_t len)
Truncates the enclosed string to the given length.
Definition: strings.h:786
#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
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:730
#define AST_VECTOR_STEAL_ELEMENTS(vec)
Steal the elements from a vector and reinitialize.
Definition: vector.h:140

References ast_alloca, ast_free, ast_str_strlen(), ast_str_truncate(), AST_VECTOR_FREE, AST_VECTOR_INIT, AST_VECTOR_SIZE, AST_VECTOR_STEAL_ELEMENTS, challenge(), cleanup(), get_auth_search_type(), get_creds_for_header(), NULL, S_OR, SCOPE_ENTER, and SCOPE_EXIT_RTN_VALUE.

Referenced by digest_create_request_with_auth().

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 672 of file res_pjsip_outbound_authenticator_digest.c.

673{
675 return 0;
676}
void ast_sip_unregister_outbound_authenticator(struct ast_sip_outbound_authenticator *auth)
Unregister an outbound SIP authenticator.
Definition: res_pjsip.c:203

References ast_sip_unregister_outbound_authenticator(), and digest_authenticator.

Variable Documentation

◆ __mod_info

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "PJSIP authentication resource" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_CHANNEL_DEPEND, .requires = "res_pjsip", }
static

Definition at line 684 of file res_pjsip_outbound_authenticator_digest.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 684 of file res_pjsip_outbound_authenticator_digest.c.

◆ digest_authenticator

struct ast_sip_outbound_authenticator digest_authenticator
static
Initial value:
= {
.create_request_with_auth = digest_create_request_with_auth,
}
static int digest_create_request_with_auth(const struct ast_sip_auth_vector *auth_ids_vector, pjsip_rx_data *challenge, pjsip_tx_data *old_request, pjsip_tx_data **new_request)

Definition at line 660 of file res_pjsip_outbound_authenticator_digest.c.

Referenced by load_module(), and unload_module().