Asterisk - The Open Source Telephony Project GIT-master-7e7a603
Functions | Variables
res_pjsip_outbound_authenticator_digest.c File Reference
#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.

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 int is_digest_algorithm_supported (pjsip_www_authenticate_hdr *auth_hdr)
 
static int load_module (void)
 
static pj_status_t set_outbound_authentication_credentials (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
 
pj_str_t supported_digest_algorithms []
 

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 603 of file res_pjsip_outbound_authenticator_digest.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 603 of file res_pjsip_outbound_authenticator_digest.c.

◆ AST_MODULE_SELF_SYM()

struct ast_module * AST_MODULE_SELF_SYM ( void  )

Definition at line 603 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 410 of file res_pjsip_outbound_authenticator_digest.c.

412{
413 pjsip_auth_clt_sess auth_sess;
414 pjsip_cseq_hdr *cseq;
415 pj_status_t status;
416 struct ast_sip_auth_objects_vector auth_objects_vector;
417 size_t auth_object_count = 0;
418 struct ast_sip_endpoint *endpoint;
419 char *id = NULL;
420 const char *id_type;
421 struct ast_str *realms = NULL;
422 pjsip_dialog *dlg;
423 int res = -1;
424
425 /*
426 * Some older compilers have an issue with initializing structures with
427 * pjsip_auth_clt_sess auth_sess = { 0, };
428 * so we'll just do it the old fashioned way.
429 */
430 memset(&auth_sess, 0, sizeof(auth_sess));
431
432 dlg = pjsip_rdata_get_dlg(challenge);
433 if (dlg) {
434 /* The only thing we use endpoint for is to get an id for error/debug messages */
435 endpoint = ast_sip_dialog_get_endpoint(dlg);
436 id = endpoint ? ast_strdupa(ast_sorcery_object_get_id(endpoint)) : NULL;
437 ao2_cleanup(endpoint);
438 id_type = "Endpoint";
439 }
440
441 /* If there was no dialog, then this is probably a REGISTER so no endpoint */
442 if (!id) {
443 /* The only thing we use the address for is to get an id for error/debug messages */
445 pj_sockaddr_print(&challenge->pkt_info.src_addr, id, AST_SOCKADDR_BUFLEN, 3);
446 id_type = "Host";
447 }
448
449 if (!auth_ids_vector || AST_VECTOR_SIZE(auth_ids_vector) == 0) {
450 ast_log(LOG_ERROR, "%s: '%s': There were no auth ids available\n", id_type, id);
451 return -1;
452 }
453
454 if (AST_VECTOR_INIT(&auth_objects_vector, AST_VECTOR_SIZE(auth_ids_vector)) != 0) {
455 ast_log(LOG_ERROR, "%s: '%s': Couldn't initialize auth object vector\n", id_type, id);
456 return -1;
457 }
458
459 /*
460 * We don't really care about ast_sip_retrieve_auths_vector()'s return code
461 * because we're checking the count of objects in the vector.
462 *
463 * Don't forget to call
464 * ast_sip_cleanup_auth_objects_vector(&auth_objects_vector);
465 * AST_VECTOR_FREE(&auth_objects_vector);
466 * when you're done with the vector
467 */
468 ast_sip_retrieve_auths_vector(auth_ids_vector, &auth_objects_vector);
469 auth_object_count = AST_VECTOR_SIZE(&auth_objects_vector);
470 if (auth_object_count == 0) {
471 /*
472 * If none of the auth ids were found, we can't continue.
473 * We're OK if there's at least one left.
474 * ast_sip_retrieve_auths_vector() will print a warning for every
475 * id that wasn't found.
476 */
477 res = -1;
478 goto cleanup;
479 }
480
481 if (pjsip_auth_clt_init(&auth_sess, ast_sip_get_pjsip_endpoint(),
482 old_request->pool, 0) != PJ_SUCCESS) {
483 ast_log(LOG_ERROR, "%s: '%s': Failed to initialize client authentication session\n",
484 id_type, id);
485 res = -1;
486 goto cleanup;
487 }
488
489 /*
490 * realms is used only for displaying good error messages.
491 */
492 realms = ast_str_create(32);
493 if (!realms) {
494 res = -1;
495 goto cleanup;
496 }
497
498 /*
499 * Load pjproject with the valid credentials for the Authentication headers
500 * received on the 401 or 407 response.
501 */
502 status = set_outbound_authentication_credentials(&auth_sess, &auth_objects_vector, challenge, &realms);
503 switch (status) {
504 case PJ_SUCCESS:
505 break;
506 case PJSIP_ENOCREDENTIAL:
508 "%s: '%s': No auth objects matching realm(s) '%s' from challenge found.\n", id_type, id,
509 realms ? ast_str_buffer(realms) : "<none>");
510 res = -1;
511 goto cleanup;
512 default:
513 ast_log(LOG_WARNING, "%s: '%s': Failed to set authentication credentials\n", id_type, id);
514 res = -1;
515 goto cleanup;
516 }
517
518 /*
519 * reinit_req actually creates the Authorization headers to send on
520 * the next request. If reinit_req already has a cached credential
521 * from an earlier successful authorization, it'll use it. Otherwise
522 * it'll create a new authorization and cache it.
523 */
524 status = pjsip_auth_clt_reinit_req(&auth_sess, challenge, old_request, new_request);
525
526 switch (status) {
527 case PJ_SUCCESS:
528 /* PJSIP creates a new transaction for new_request (meaning it creates a new
529 * branch). However, it recycles the Call-ID, from-tag, and CSeq from the
530 * original request. Some SIP implementations will not process the new request
531 * since the CSeq is the same as the original request. Incrementing it here
532 * fixes the interop issue
533 */
534 cseq = pjsip_msg_find_hdr((*new_request)->msg, PJSIP_H_CSEQ, NULL);
535 ast_assert(cseq != NULL);
536 ++cseq->cseq;
537 res = 0;
538 goto cleanup;
539 case PJSIP_ENOCREDENTIAL:
540 /*
541 * This should be rare since set_outbound_authentication_credentials()
542 * did the matching but you never know.
543 */
545 "%s: '%s': No auth objects matching realm(s) '%s' from challenge found.\n", id_type, id,
546 realms ? ast_str_buffer(realms) : "<none>");
547 break;
548 case PJSIP_EAUTHSTALECOUNT:
550 "%s: '%s': Unable to create request with auth. Number of stale retries exceeded.\n",
551 id_type, id);
552 break;
553 case PJSIP_EFAILEDCREDENTIAL:
554 ast_log(LOG_WARNING, "%s: '%s': Authentication credentials not accepted by server.\n",
555 id_type, id);
556 break;
557 default:
558 ast_log(LOG_WARNING, "%s: '%s': Unable to create request with auth. Unknown failure.\n",
559 id_type, id);
560 break;
561 }
562 res = -1;
563
564cleanup:
565#if defined(HAVE_PJSIP_AUTH_CLT_DEINIT)
566 /* If we initialized the auth_sess, clean it up */
567 if (auth_sess.endpt) {
568 pjsip_auth_clt_deinit(&auth_sess);
569 }
570#endif
571
572 ast_sip_cleanup_auth_objects_vector(&auth_objects_vector);
573 AST_VECTOR_FREE(&auth_objects_vector);
574 ast_free(realms);
575
576 return res;
577}
jack_status_t status
Definition: app_jack.c:146
#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
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
#define AST_SOCKADDR_BUFLEN
Definition: netsock2.h:46
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:2848
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 *realm, pjsip_tx_data *tdata, const pjsip_rx_data *rdata, int is_stale)
astobj2 callback for adding digest challenges to responses
static pj_status_t set_outbound_authentication_credentials(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:963
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_SOCKADDR_BUFLEN, ast_sorcery_object_get_id(), ast_str_buffer(), ast_str_create, ast_strdupa, AST_VECTOR_FREE, AST_VECTOR_INIT, AST_VECTOR_SIZE, challenge(), cleanup(), LOG_ERROR, LOG_WARNING, NULL, set_outbound_authentication_credentials(), and status.

◆ get_auth_search_type()

static pjsip_hdr_e get_auth_search_type ( pjsip_rx_data *  challenge)
static

Definition at line 46 of file res_pjsip_outbound_authenticator_digest.c.

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

References ast_log, challenge(), and LOG_ERROR.

Referenced by set_outbound_authentication_credentials().

◆ is_digest_algorithm_supported()

static int is_digest_algorithm_supported ( pjsip_www_authenticate_hdr *  auth_hdr)
static

Definition at line 68 of file res_pjsip_outbound_authenticator_digest.c.

69{
70 int digest;
71
72 /* An empty digest is assumed to be md5 */
73 if (pj_strlen(&auth_hdr->challenge.digest.algorithm) == 0) {
74 return 1;
75 }
76
77 for (digest = 0; digest < ARRAY_LEN(supported_digest_algorithms); digest++) {
78 if (pj_stricmp(&auth_hdr->challenge.digest.algorithm, &supported_digest_algorithms[digest]) == 0) {
79 return 1;
80 }
81 }
82 return 0;
83}
pj_str_t supported_digest_algorithms[]
#define ARRAY_LEN(a)
Definition: utils.h:666

References ARRAY_LEN, and supported_digest_algorithms.

Referenced by set_outbound_authentication_credentials().

◆ load_module()

static int load_module ( void  )
static

Definition at line 583 of file res_pjsip_outbound_authenticator_digest.c.

584{
587 }
589}
@ 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_outbound_authentication_credentials()

static pj_status_t set_outbound_authentication_credentials ( 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 127 of file res_pjsip_outbound_authenticator_digest.c.

130{
131 int i;
132 size_t auth_object_count;
133 pjsip_www_authenticate_hdr *auth_hdr = NULL;
134 pj_status_t res = PJ_SUCCESS;
135 pjsip_hdr_e search_type;
136 size_t cred_count;
137 pjsip_cred_info *creds_array;
138
139 /*
140 * Normally vector elements are pointers to something else, usually
141 * structures. In this case however, the elements are the
142 * structures themselves instead of pointers to them. This is due
143 * to the fact that pjsip_auth_clt_set_credentials() expects an
144 * array of structures, not an array of pointers to structures.
145 * Thankfully, vectors allow you to "steal" their underlying
146 * arrays, in this case an array of pjsip_cred_info structures,
147 * which we'll pass to pjsip_auth_clt_set_credentials() at the
148 * end.
149 */
150 AST_VECTOR(cred_info, pjsip_cred_info) auth_creds;
151
152 search_type = get_auth_search_type(challenge);
153 if (search_type == PJSIP_H_OTHER) {
154 /*
155 * The status code on the response wasn't 401 or 407
156 * so there are no WWW-Authenticate or Proxy-Authenticate
157 * headers to process.
158 */
159 return PJ_ENOTSUP;
160 }
161
162 auth_object_count = AST_VECTOR_SIZE(auth_objects_vector);
163 if (auth_object_count == 0) {
164 /* This shouldn't happen but we'll check anyway. */
165 return PJ_EINVAL;
166 }
167
168 /*
169 * The number of pjsip_cred_infos we send to pjproject can
170 * vary based on the number of acceptable headers received
171 * and the number of acceptable auth objects on the endpoint
172 * so we just use a vector to accumulate them.
173 *
174 * NOTE: You have to call AST_VECTOR_FREE() on the vector
175 * but you don't have to free the elements because they're
176 * actual structures, not pointers to structures.
177 */
178 if (AST_VECTOR_INIT(&auth_creds, 5) != 0) {
179 return PJ_ENOMEM;
180 }
181
182 /*
183 * It's going to be rare that we actually have more than one
184 * WWW-Authentication header or more than one auth object to
185 * match to it so the following nested loop should be fine.
186 */
187 while ((auth_hdr = pjsip_msg_find_hdr(challenge->msg_info.msg,
188 search_type, auth_hdr ? auth_hdr->next : NULL))) {
189 int exact_match_index = -1;
190 int wildcard_match_index = -1;
191 int match_index = 0;
192 pjsip_cred_info auth_cred;
193 struct ast_sip_auth *auth = NULL;
194
195 memset(&auth_cred, 0, sizeof(auth_cred));
196 /*
197 * Since we only support the MD5 algorithm at the current time,
198 * there's no sense searching for auth objects that match the algorithm.
199 * In fact, the auth_object structure doesn't even have a member
200 * for it.
201 *
202 * When we do support more algorithms, this check will need to be
203 * moved inside the auth object loop below.
204 *
205 * Note: The header may not have specified an algorithm at all in which
206 * case it's assumed to be MD5. is_digest_algorithm_supported() returns
207 * true for that case.
208 */
209 if (!is_digest_algorithm_supported(auth_hdr)) {
210 ast_debug(3, "Skipping header with realm '%.*s' and unsupported '%.*s' algorithm \n",
211 (int)auth_hdr->challenge.digest.realm.slen, auth_hdr->challenge.digest.realm.ptr,
212 (int)auth_hdr->challenge.digest.algorithm.slen, auth_hdr->challenge.digest.algorithm.ptr);
213 continue;
214 }
215
216 /*
217 * Appending the realms is strictly so digest_create_request_with_auth()
218 * can display good error messages. Since we only support one algorithm,
219 * there can't be more than one header with the same realm. No need to worry
220 * about duplicate realms until then.
221 */
222 if (*realms) {
223 ast_str_append(realms, 0, "%.*s, ",
224 (int)auth_hdr->challenge.digest.realm.slen, auth_hdr->challenge.digest.realm.ptr);
225 }
226
227 ast_debug(3, "Searching auths to find matching ones for header with realm '%.*s' and algorithm '%.*s'\n",
228 (int)auth_hdr->challenge.digest.realm.slen, auth_hdr->challenge.digest.realm.ptr,
229 (int)auth_hdr->challenge.digest.algorithm.slen, auth_hdr->challenge.digest.algorithm.ptr);
230
231 /*
232 * Now that we have a valid header, we can loop over the auths available to
233 * find either an exact realm match or, failing that, a wildcard auth (an
234 * auth with an empty or "*" realm).
235 *
236 * NOTE: We never use the global default realm when we're the UAC responding
237 * to a 401 or 407. We only use that when we're the UAS (handled elsewhere)
238 * and the auth object didn't have a realm.
239 */
240 for (i = 0; i < auth_object_count; ++i) {
241 auth = AST_VECTOR_GET(auth_objects_vector, i);
242
243 /*
244 * If this auth object's realm exactly matches the one
245 * from the header, we can just break out and use it.
246 *
247 * NOTE: If there's more than one auth object for an endpoint with
248 * a matching realm it's a misconfiguration. We'll only use the first.
249 */
250 if (pj_stricmp2(&auth_hdr->challenge.digest.realm, auth->realm) == 0) {
251 ast_debug(3, "Found matching auth '%s' with realm '%s'\n", ast_sorcery_object_get_id(auth),
252 auth->realm);
253 exact_match_index = i;
254 /*
255 * If we found an exact realm match, there's no need to keep
256 * looking for a wildcard.
257 */
258 break;
259 }
260
261 /*
262 * If this auth object's realm is empty or a "*", it's a wildcard
263 * auth object. We going to save its index but keep iterating over
264 * the vector in case we find an exact match later.
265 *
266 * NOTE: If there's more than one wildcard auth object for an endpoint
267 * it's a misconfiguration. We'll only use the first.
268 */
269 if (wildcard_match_index < 0
270 && (ast_strlen_zero(auth->realm) || ast_strings_equal(auth->realm, "*"))) {
271 ast_debug(3, "Found wildcard auth '%s' for realm '%.*s'\n", ast_sorcery_object_get_id(auth),
272 (int)auth_hdr->challenge.digest.realm.slen, auth_hdr->challenge.digest.realm.ptr);
273 wildcard_match_index = i;
274 }
275 }
276
277 if (exact_match_index < 0 && wildcard_match_index < 0) {
278 /*
279 * Didn't find either a wildcard or an exact realm match.
280 * Move on to the next header.
281 */
282 ast_debug(3, "No auth matching realm or no wildcard found for realm '%.*s'\n",
283 (int)auth_hdr->challenge.digest.realm.slen, auth_hdr->challenge.digest.realm.ptr);
284 continue;
285 }
286
287 if (exact_match_index >= 0) {
288 /*
289 * If we found an exact match, we'll always prefer that.
290 */
291 match_index = exact_match_index;
292 auth = AST_VECTOR_GET(auth_objects_vector, match_index);
293 ast_debug(3, "Using matched auth '%s' with realm '%.*s'\n", ast_sorcery_object_get_id(auth),
294 (int)auth_hdr->challenge.digest.realm.slen, auth_hdr->challenge.digest.realm.ptr);
295 } else {
296 /*
297 * We'll only use the wildcard if we didn't find an exact match.
298 */
299 match_index = wildcard_match_index;
300 auth = AST_VECTOR_GET(auth_objects_vector, match_index);
301 ast_debug(3, "Using wildcard auth '%s' for realm '%.*s'\n", ast_sorcery_object_get_id(auth),
302 (int)auth_hdr->challenge.digest.realm.slen, auth_hdr->challenge.digest.realm.ptr);
303 }
304
305 /*
306 * Copy the fields from the auth_object to the
307 * pjsip_cred_info structure.
308 */
309 auth_cred.realm = auth_hdr->challenge.common.realm;
310 pj_cstr(&auth_cred.username, auth->auth_user);
311 pj_cstr(&auth_cred.scheme, "digest");
312 switch (auth->type) {
314 pj_cstr(&auth_cred.data, auth->auth_pass);
315 auth_cred.data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
316 break;
318 pj_cstr(&auth_cred.data, auth->md5_creds);
319 auth_cred.data_type = PJSIP_CRED_DATA_DIGEST;
320 break;
322 /* nothing to do. handled seperately in res_pjsip_outbound_registration */
323 break;
326 "Trying to set artificial outbound auth credentials shouldn't happen.\n");
327 continue;
328 } /* End auth object loop */
329
330 /*
331 * Because the vector contains actual structures and not pointers
332 * to structures, the call to AST_VECTOR_APPEND results in a simple
333 * assign of one structure to another, effectively copying the auth_cred
334 * structure contents to the array element.
335 *
336 * Also note that the calls to pj_cstr above set their respective
337 * auth_cred fields to the _pointers_ of their corresponding auth
338 * object fields. This is safe because the call to
339 * pjsip_auth_clt_set_credentials() below strdups them before we
340 * return to the calling function which decrements the reference
341 * counts.
342 */
343 res = AST_VECTOR_APPEND(&auth_creds, auth_cred);
344 if (res != PJ_SUCCESS) {
345 res = PJ_ENOMEM;
346 goto cleanup;
347 }
348 } /* End header loop */
349
350 if (*realms && ast_str_strlen(*realms)) {
351 /*
352 * Again, this is strictly so digest_create_request_with_auth()
353 * can display good error messages.
354 *
355 * Chop off the trailing ", " on the last realm.
356 */
357 ast_str_truncate(*realms, ast_str_strlen(*realms) - 2);
358 }
359
360 if (AST_VECTOR_SIZE(&auth_creds) == 0) {
361 /* No matching auth objects were found. */
362 res = PJSIP_ENOCREDENTIAL;
363 goto cleanup;
364 }
365
366 /*
367 * Here's where we steal the cred info structures from the vector.
368 *
369 * The steal effectively returns a pointer to the underlying
370 * array of pjsip_cred_info structures which is exactly what we need
371 * to pass to pjsip_auth_clt_set_credentials().
372 *
373 * <struct cred info><struct cred info>...<struct cred info>
374 * ^pointer
375 *
376 * Since we stole the array from the vector, we have to free it ourselves.
377 *
378 * We also have to copy the size before we steal because stealing
379 * resets the vector size to 0.
380 */
381 cred_count = AST_VECTOR_SIZE(&auth_creds);
382 creds_array = AST_VECTOR_STEAL_ELEMENTS(&auth_creds);
383
384 res = pjsip_auth_clt_set_credentials(auth_sess, cred_count, creds_array);
385 ast_free(creds_array);
386 if (res == PJ_SUCCESS) {
387 ast_debug(3, "Set %zu credentials in auth session\n", cred_count);
388 } else {
389 ast_log(LOG_ERROR, "Failed to set %zu credentials in auth session\n", cred_count);
390 }
391
392cleanup:
393 AST_VECTOR_FREE(&auth_creds);
394 return res;
395}
#define ast_debug(level,...)
Log a DEBUG message.
@ AST_SIP_AUTH_TYPE_GOOGLE_OAUTH
Definition: res_pjsip.h:572
@ AST_SIP_AUTH_TYPE_ARTIFICIAL
Definition: res_pjsip.h:574
@ AST_SIP_AUTH_TYPE_MD5
Definition: res_pjsip.h:570
@ AST_SIP_AUTH_TYPE_USER_PASS
Definition: res_pjsip.h:568
static int is_digest_algorithm_supported(pjsip_www_authenticate_hdr *auth_hdr)
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
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
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:730
const ast_string_field md5_creds
Definition: res_pjsip.h:597
const ast_string_field realm
Definition: res_pjsip.h:597
const ast_string_field auth_user
Definition: res_pjsip.h:597
const ast_string_field auth_pass
Definition: res_pjsip.h:597
enum ast_sip_auth_type type
Definition: res_pjsip.h:601
#define AST_VECTOR_STEAL_ELEMENTS(vec)
Steal the elements from a vector and reinitialize.
Definition: vector.h:140
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition: vector.h:256
#define AST_VECTOR(name, type)
Define a vector structure.
Definition: vector.h:44
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition: vector.h:680

References ast_debug, ast_free, ast_log, AST_SIP_AUTH_TYPE_ARTIFICIAL, AST_SIP_AUTH_TYPE_GOOGLE_OAUTH, AST_SIP_AUTH_TYPE_MD5, AST_SIP_AUTH_TYPE_USER_PASS, ast_sorcery_object_get_id(), ast_str_append(), ast_str_strlen(), ast_str_truncate(), ast_strings_equal(), ast_strlen_zero(), AST_VECTOR, AST_VECTOR_APPEND, AST_VECTOR_FREE, AST_VECTOR_GET, AST_VECTOR_INIT, AST_VECTOR_SIZE, AST_VECTOR_STEAL_ELEMENTS, ast_sip_auth::auth_pass, ast_sip_auth::auth_user, challenge(), cleanup(), get_auth_search_type(), is_digest_algorithm_supported(), LOG_ERROR, ast_sip_auth::md5_creds, NULL, ast_sip_auth::realm, and ast_sip_auth::type.

Referenced by digest_create_request_with_auth().

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 591 of file res_pjsip_outbound_authenticator_digest.c.

592{
594 return 0;
595}
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 603 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 603 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 579 of file res_pjsip_outbound_authenticator_digest.c.

Referenced by load_module(), and unload_module().

◆ supported_digest_algorithms

pj_str_t supported_digest_algorithms[]
Initial value:
= {
{ "MD5", 3}
}

Definition at line 35 of file res_pjsip_outbound_authenticator_digest.c.

Referenced by is_digest_algorithm_supported().