Asterisk - The Open Source Telephony Project GIT-master-7e7a603
res_pjsip_outbound_authenticator_digest.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2013, Digium, Inc.
5 *
6 * Mark Michelson <mmichelson@digium.com>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19/*** MODULEINFO
20 <depend>pjproject</depend>
21 <depend>res_pjsip</depend>
22 <support_level>core</support_level>
23 ***/
24
25#include "asterisk.h"
26
27#include <pjsip.h>
28
29#include "asterisk/res_pjsip.h"
30#include "asterisk/logger.h"
31#include "asterisk/module.h"
32#include "asterisk/strings.h"
33#include "asterisk/vector.h"
34
36 { "MD5", 3}
37};
38
39/*!
40 * \internal
41 * \brief Determine proper authenticate header
42 *
43 * We need to search for different headers depending on whether
44 * the response code from the UAS/Proxy was 401 or 407.
45 */
46static pjsip_hdr_e get_auth_search_type(pjsip_rx_data *challenge)
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}
59
60/*!
61 * \internal
62 * \brief Determine if digest algorithm in the header is one we support
63 *
64 * \retval 1 If we support the algorithm
65 * \retval 0 If we do not
66 *
67 */
68static int is_digest_algorithm_supported(pjsip_www_authenticate_hdr *auth_hdr)
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}
84
85/*!
86 * \internal
87 * \brief Initialize pjproject with a valid set of credentials
88 *
89 * RFC7616 and RFC8760 allow more than one WWW-Authenticate or
90 * Proxy-Authenticate header per realm, each with different digest
91 * algorithms (including new ones like SHA-256 and SHA-512-256). However,
92 * thankfully, a UAS can NOT send back multiple Authenticate headers for
93 * the same realm with the same digest algorithm. The UAS is also
94 * supposed to send the headers in order of preference with the first one
95 * being the most preferred.
96 *
97 * We're supposed to send an Authorization header for the first one we
98 * encounter for a realm that we can support.
99 *
100 * The UAS can also send multiple realms, especially when it's a proxy
101 * that has forked the request in which case the proxy will aggregate all
102 * of the Authenticate and then them all back to the UAC.
103 *
104 * It doesn't stop there though... Each realm can require a different
105 * username from the others. There's also nothing preventing each digest
106 * algorithm from having a unique password although I'm not sure if
107 * that adds any benefit.
108 *
109 * So now... For each Authenticate header we encounter, we have to
110 * determine if we support the digest algorithm and, if not, just skip the
111 * header. We then have to find an auth object that matches the realm AND
112 * the digest algorithm or find a wildcard object that matches the digest
113 * algorithm. If we find one, we add it to the results vector and read the
114 * next Authenticate header. If the next header is for the same realm AND
115 * we already added an auth object for that realm, we skip the header.
116 * Otherwise we repeat the process for the next header.
117 *
118 * In the end, we'll have accumulated a list of credentials we can pass to
119 * pjproject that it can use to add Authentication headers to a request.
120 *
121 * \note: Neither we nor pjproject can currently handle digest algorithms
122 * other than MD5. We don't even have a place for it in the ast_sip_auth
123 * object. For this reason, we just skip processing any Authenticate
124 * header that's not MD5. When we support the others, we'll move the
125 * check into the loop that searches the objects.
126 */
127static pj_status_t set_outbound_authentication_credentials(pjsip_auth_clt_sess *auth_sess,
128 const struct ast_sip_auth_objects_vector *auth_objects_vector, pjsip_rx_data *challenge,
129 struct ast_str **realms)
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}
396
397/*!
398 * \internal
399 * \brief Create new tdata with auth based on original tdata
400 * \param auth_ids_vector Vector of auth IDs retrieved from endpoint
401 * \param challenge rdata of the response from the UAS with challenge
402 * \param old_request tdata from the original request
403 * \param new_request tdata of the new request with the auth
404 *
405 * This function is what's registered with ast_sip_register_outbound_authenticator()
406 *
407 * \retval 0 success
408 * \retval -1 failure
409 */
410static int digest_create_request_with_auth(const struct ast_sip_auth_vector *auth_ids_vector,
411 pjsip_rx_data *challenge, pjsip_tx_data *old_request, pjsip_tx_data **new_request)
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}
578
581};
582
583static int load_module(void)
584{
587 }
589}
590
591static int unload_module(void)
592{
594 return 0;
595}
596
597AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP authentication resource",
598 .support_level = AST_MODULE_SUPPORT_CORE,
599 .load = load_module,
600 .unload = unload_module,
601 .load_pri = AST_MODPRI_CHANNEL_DEPEND,
602 .requires = "res_pjsip",
jack_status_t status
Definition: app_jack.c:146
Asterisk main include file. File version handling, generic pbx functions.
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define 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.
Support for logging to various files, console and syslog Configuration in file logger....
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define LOG_WARNING
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:317
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:543
@ AST_MODPRI_CHANNEL_DEPEND
Definition: module.h:326
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
#define AST_SOCKADDR_BUFLEN
Definition: netsock2.h:46
static void * cleanup(void *unused)
Definition: pbx_realtime.c:124
void ast_sip_unregister_outbound_authenticator(struct ast_sip_outbound_authenticator *auth)
Unregister an outbound SIP authenticator.
Definition: res_pjsip.c:203
pjsip_endpoint * ast_sip_get_pjsip_endpoint(void)
Get a pointer to the PJSIP endpoint.
Definition: res_pjsip.c:520
int ast_sip_register_outbound_authenticator(struct ast_sip_outbound_authenticator *outbound_auth)
Register an outbound SIP authenticator.
Definition: res_pjsip.c:191
#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.
@ 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 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 struct ast_sip_outbound_authenticator digest_authenticator
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 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 int load_module(void)
static int unload_module(void)
static int is_digest_algorithm_supported(pjsip_www_authenticate_hdr *auth_hdr)
static pjsip_hdr_e get_auth_search_type(pjsip_rx_data *challenge)
pj_str_t supported_digest_algorithms[]
#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
String manipulation functions.
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
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
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
An entity with which Asterisk communicates.
Definition: res_pjsip.h:963
an interchangeable way of responding to authentication challenges
Definition: res_pjsip.h:1272
int(* create_request_with_auth)(const struct ast_sip_auth_vector *auths, struct pjsip_rx_data *challenge, struct pjsip_tx_data *old_request, struct pjsip_tx_data **new_request)
Create a new request with authentication credentials.
Definition: res_pjsip.h:1283
Support for dynamic strings.
Definition: strings.h:623
#define ast_assert(a)
Definition: utils.h:739
#define ARRAY_LEN(a)
Definition: utils.h:666
Vector container support.
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:609
#define AST_VECTOR_STEAL_ELEMENTS(vec)
Steal the elements from a vector and reinitialize.
Definition: vector.h:140
#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
#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