Asterisk - The Open Source Telephony Project  18.5.0
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 = "30ef0c93b36035ec78c9cfd712d36d9b" , .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 601 of file res_pjsip_outbound_authenticator_digest.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 601 of file res_pjsip_outbound_authenticator_digest.c.

◆ AST_MODULE_SELF_SYM()

struct ast_module* AST_MODULE_SELF_SYM ( void  )

Definition at line 601 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.

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, cleanup(), LOG_ERROR, LOG_WARNING, NULL, set_outbound_authentication_credentials(), and status.

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 }
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition: vector.h:174
#define AST_SOCKADDR_BUFLEN
Definition: netsock2.h:46
#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_assert(a)
Definition: utils.h:695
#define NULL
Definition: resample.c:96
#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.
#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
An entity with which Asterisk communicates.
Definition: res_pjsip.h:812
#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
#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)
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:3718
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
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
#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

◆ 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.

References ast_log, and LOG_ERROR.

Referenced by set_outbound_authentication_credentials().

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 }
#define ast_log
Definition: astobj2.c:42
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 LOG_ERROR
Definition: logger.h:285

◆ 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.

References ARRAY_LEN, and supported_digest_algorithms.

Referenced by set_outbound_authentication_credentials().

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 }
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
pj_str_t supported_digest_algorithms[]

◆ load_module()

static int load_module ( void  )
static

Definition at line 581 of file res_pjsip_outbound_authenticator_digest.c.

References AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, and ast_sip_register_outbound_authenticator().

Referenced by unload_module().

582 {
585  }
587 }
int ast_sip_register_outbound_authenticator(struct ast_sip_outbound_authenticator *outbound_auth)
Register an outbound SIP authenticator.
Definition: res_pjsip.c:3389
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
static struct ast_sip_outbound_authenticator 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.

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, 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().

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 }
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition: vector.h:174
const ast_string_field md5_creds
Definition: res_pjsip.h:454
#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
#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
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 pjsip_hdr_e get_auth_search_type(pjsip_rx_data *challenge)
#define LOG_ERROR
Definition: logger.h:285
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
const ast_string_field auth_pass
Definition: res_pjsip.h:452
enum ast_sip_auth_type type
Definition: res_pjsip.h:464
static void * cleanup(void *unused)
Definition: pbx_realtime.c:124
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
static int is_digest_algorithm_supported(pjsip_www_authenticate_hdr *auth_hdr)
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

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 589 of file res_pjsip_outbound_authenticator_digest.c.

References AST_MODFLAG_LOAD_ORDER, AST_MODPRI_CHANNEL_DEPEND, AST_MODULE_INFO(), AST_MODULE_SUPPORT_CORE, ast_sip_unregister_outbound_authenticator(), ASTERISK_GPL_KEY, and load_module().

590 {
592  return 0;
593 }
void ast_sip_unregister_outbound_authenticator(struct ast_sip_outbound_authenticator *auth)
Unregister an outbound SIP authenticator.
Definition: res_pjsip.c:3401
static struct ast_sip_outbound_authenticator 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 = "30ef0c93b36035ec78c9cfd712d36d9b" , .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 601 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 601 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 577 of file res_pjsip_outbound_authenticator_digest.c.

◆ 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().