Asterisk - The Open Source Telephony Project  18.5.0
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 <[email protected]>
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  */
46 static 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  */
68 static 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  */
127 static 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 structues, 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 %"PRIu64" credentials in auth session\n", cred_count);
388  } else {
389  ast_log(LOG_ERROR, "Failed to set %"PRIu64" credentials in auth session\n", cred_count);
390  }
391 
392 cleanup:
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  */
410 static 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 
564 cleanup:
565 #if defined(HAVE_PJSIP_AUTH_CLT_DEINIT)
566  /* Release any cached auths */
567  pjsip_auth_clt_deinit(&auth_sess);
568 #endif
569 
570  ast_sip_cleanup_auth_objects_vector(&auth_objects_vector);
571  AST_VECTOR_FREE(&auth_objects_vector);
572  ast_free(realms);
573 
574  return res;
575 }
576 
579 };
580 
581 static int load_module(void)
582 {
583  if (ast_sip_register_outbound_authenticator(&digest_authenticator)) {
585  }
587 }
588 
589 static int unload_module(void)
590 {
591  ast_sip_unregister_outbound_authenticator(&digest_authenticator);
592  return 0;
593 }
594 
595 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP authentication resource",
596  .support_level = AST_MODULE_SUPPORT_CORE,
597  .load = load_module,
598  .unload = unload_module,
599  .load_pri = AST_MODPRI_CHANNEL_DEPEND,
600  .requires = "res_pjsip",
601 );
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition: vector.h:174
#define AST_SOCKADDR_BUFLEN
Definition: netsock2.h:46
int ast_sip_register_outbound_authenticator(struct ast_sip_outbound_authenticator *outbound_auth)
Register an outbound SIP authenticator.
Definition: res_pjsip.c:3389
Asterisk main include file. File version handling, generic pbx functions.
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
String manipulation functions.
const ast_string_field md5_creds
Definition: res_pjsip.h:454
#define LOG_WARNING
Definition: logger.h:274
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition: vector.h:256
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:1091
void ast_sip_unregister_outbound_authenticator(struct ast_sip_outbound_authenticator *auth)
Unregister an outbound SIP authenticator.
Definition: res_pjsip.c:3401
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:986
#define ast_assert(a)
Definition: utils.h:695
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)
#define NULL
Definition: resample.c:96
char * ast_str_truncate(struct ast_str *buf, ssize_t len)
Truncates the enclosed string to the given length.
Definition: strings.h:738
#define ast_strlen_zero(foo)
Definition: strings.h:52
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition: vector.h:113
struct ast_sip_endpoint * ast_sip_dialog_get_endpoint(pjsip_dialog *dlg)
Get the endpoint associated with this dialog.
static int unload_module(void)
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2312
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
#define AST_VECTOR_STEAL_ELEMENTS(vec)
Steal the elements from a vector and reinitialize.
Definition: vector.h:140
#define AST_VECTOR(name, type)
Define a vector structure.
Definition: vector.h:44
static int load_module(void)
An entity with which Asterisk communicates.
Definition: res_pjsip.h:812
static pjsip_hdr_e get_auth_search_type(pjsip_rx_data *challenge)
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:290
#define LOG_ERROR
Definition: logger.h:285
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
const ast_string_field realm
Definition: res_pjsip.h:448
int ast_strings_equal(const char *str1, const char *str2)
Compare strings for equality checking for NULL.
Definition: strings.c:239
#define ast_free(a)
Definition: astmm.h:182
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)
const ast_string_field auth_pass
Definition: res_pjsip.h:452
enum ast_sip_auth_type type
Definition: res_pjsip.h:464
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
Vector container support.
static void * cleanup(void *unused)
Definition: pbx_realtime.c:124
Support for logging to various files, console and syslog Configuration in file logger.conf.
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS|AST_MODFLAG_LOAD_ORDER, "HTTP Phone Provisioning",.support_level=AST_MODULE_SUPPORT_EXTENDED,.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_CHANNEL_DEPEND,.requires="http",)
static struct ast_sip_outbound_authenticator digest_authenticator
pjsip_endpoint * ast_sip_get_pjsip_endpoint(void)
Get a pointer to the PJSIP endpoint.
Definition: res_pjsip.c:3718
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:688
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition: vector.h:682
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
pj_str_t supported_digest_algorithms[]
static int is_digest_algorithm_supported(pjsip_www_authenticate_hdr *auth_hdr)
int ast_sip_retrieve_auths_vector(const struct ast_sip_auth_vector *auth_ids, struct ast_sip_auth_objects_vector *auth_objects)
#define ast_sip_cleanup_auth_objects_vector(auth_objects)
Clean up retrieved auth objects in vector.
Definition: res_pjsip.h:2538
an interchangeable way of responding to authentication challenges
Definition: res_pjsip.h:975
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
const ast_string_field auth_user
Definition: res_pjsip.h:450
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:611
jack_status_t status
Definition: app_jack.c:146
#define ast_str_create(init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:620