Asterisk - The Open Source Telephony Project GIT-master-27fb039
Loading...
Searching...
No Matches
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.
 
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 = ASTERISK_GPL_KEY , .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
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)
static void cleanup(void)
Clean up any old apps that we don't need any more.
Definition res_stasis.c:327
#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:2381
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition strings.h:659
char *attribute_pure ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition strings.h:761
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:779
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition vector.h:620
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition vector.h:185
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition vector.h:124

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.
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.
#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:267
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition vector.h:691

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?

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
size_t attribute_pure ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition strings.h:730
#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_VECTOR_STEAL_ELEMENTS(vec)
Steal the elements from a vector and reinitialize.
Definition vector.h:151

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 = ASTERISK_GPL_KEY , .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.

660 {
661 .create_request_with_auth = digest_create_request_with_auth,
662};

Referenced by load_module(), and unload_module().