@ -16,6 +16,14 @@
* at the top of the source tree .
*/
/*!
* \ file
* \ brief PJSIP UAC Authentication
*
* This module handles authentication when Asterisk is the UAC .
*
*/
/*** MODULEINFO
< depend > pjproject < / depend >
< depend > res_pjsip < / depend >
@ -32,10 +40,6 @@
# include "asterisk/strings.h"
# include "asterisk/vector.h"
pj_str_t supported_digest_algorithms [ ] = {
{ " MD5 " , 3 }
} ;
/*!
* \ internal
* \ brief Determine proper authenticate header
@ -59,27 +63,240 @@ static pjsip_hdr_e get_auth_search_type(pjsip_rx_data *challenge)
/*!
* \ internal
* \ brief Determine if digest algorithm in the header is one we support
* \ brief Determine if digest algorithm in the header is one supported by
* pjproject and OpenSSL .
*/
static const pjsip_auth_algorithm * get_supported_algorithm ( pjsip_www_authenticate_hdr * auth_hdr )
{
const pjsip_auth_algorithm * algo = NULL ;
algo = ast_sip_auth_get_algorithm_by_iana_name ( & auth_hdr - > challenge . digest . algorithm ) ;
if ( ! algo ) {
return NULL ;
}
if ( ast_sip_auth_is_algorithm_supported ( algo - > algorithm_type ) ) {
return algo ;
}
return NULL ;
}
AST_VECTOR ( cred_info_vector , pjsip_cred_info ) ;
/*!
* \ brief Get credentials ( if any ) from auth objects for a WWW / Proxy - Authenticate header
*
* \ retval 1 If we support the algorithm
* \ retval 0 If we do not
* \ param id For logging
* \ param src_name For logging
* \ param auth_hdr The * - Authenticate header to check
* \ param auth_object_count The number of auth objects available
* \ param auth_objects_vector The vector of available auth objects
* \ param auth_creds The vector to store the credentials in
* \ param realms For logging
*
*/
static int is_digest_algorithm_supported ( pjsip_www_authenticate_hdr * auth_hdr )
static void get_creds_for_header ( const char * id , const char * src_name ,
pjsip_www_authenticate_hdr * auth_hdr , size_t auth_object_count ,
const struct ast_sip_auth_objects_vector * auth_objects_vector ,
struct cred_info_vector * auth_creds , struct ast_str * * realms )
{
int digest ;
int exact_match_index = - 1 ;
int wildcard_match_index = - 1 ;
struct ast_sip_auth * found_auth = NULL ;
const pjsip_auth_algorithm * challenge_algorithm =
get_supported_algorithm ( auth_hdr ) ;
int i = 0 ;
pjsip_cred_info auth_cred ;
const char * cred_data ;
int res = 0 ;
SCOPE_ENTER ( 4 , " %s:%s: Testing header realm: ' " PJSTR_PRINTF_SPEC " ' algorithm: ' "
PJSTR_PRINTF_SPEC " ' \n " , id , src_name ,
PJSTR_PRINTF_VAR ( auth_hdr - > challenge . digest . realm ) ,
PJSTR_PRINTF_VAR ( auth_hdr - > challenge . digest . algorithm ) ) ;
if ( ! challenge_algorithm ) {
SCOPE_EXIT_RTN ( " %s:%s: Skipping header with realm ' " PJSTR_PRINTF_SPEC " ' "
" and unsupported " PJSTR_PRINTF_SPEC " ' algorithm \n " , id , src_name ,
PJSTR_PRINTF_VAR ( auth_hdr - > challenge . digest . realm ) ,
PJSTR_PRINTF_VAR ( auth_hdr - > challenge . digest . algorithm ) ) ;
}
/*
* If we already have credentials for this realm , we don ' t need to
* process this header . We can just skip it .
*/
for ( i = 0 ; i < AST_VECTOR_SIZE ( auth_creds ) ; i + + ) {
pjsip_cred_info auth_cred = AST_VECTOR_GET ( auth_creds , i ) ;
if ( pj_stricmp ( & auth_cred . realm , & auth_hdr - > challenge . common . realm ) = = 0 ) {
SCOPE_EXIT_RTN ( " %s:%s: Skipping header with realm ' " PJSTR_PRINTF_SPEC " ' "
" because we already have credentials for it \n " , id , src_name ,
PJSTR_PRINTF_VAR ( auth_hdr - > challenge . digest . realm ) ) ;
}
}
/* An empty digest is assumed to be md5 */
if ( pj_strlen ( & auth_hdr - > challenge . digest . algorithm ) = = 0 ) {
return 1 ;
/*
* Appending " realm/agorithm " to realms is strictly so
* digest_create_request_with_auth ( ) can display good error messages .
*/
if ( * realms ) {
ast_str_append ( realms , 0 , PJSTR_PRINTF_SPEC " / " PJSTR_PRINTF_SPEC " , " ,
PJSTR_PRINTF_VAR ( auth_hdr - > challenge . digest . realm ) ,
PJSTR_PRINTF_VAR ( auth_hdr - > challenge . digest . algorithm ) ) ;
}
for ( digest = 0 ; digest < ARRAY_LEN ( supported_digest_algorithms ) ; digest + + ) {
if ( pj_stricmp ( & auth_hdr - > challenge . digest . algorithm , & supported_digest_algorithms [ digest ] ) = = 0 ) {
return 1 ;
/*
* Now that we have a valid header , we can loop over the auths available to
* find either an exact realm match or , failing that , a wildcard auth ( an
* auth with an empty or " * " realm ) .
*
* NOTE : We never use the global default realm when we ' re the UAC responding
* to a 401 or 407. We only use that when we ' re the UAS ( handled elsewhere )
* and the auth object didn ' t have a realm .
*/
ast_trace ( - 1 , " %s:%s: Searching %zu auths to find matching ones for header with realm "
" ' " PJSTR_PRINTF_SPEC " ' and algorithm ' " PJSTR_PRINTF_SPEC " ' \n " ,
id , src_name , auth_object_count ,
PJSTR_PRINTF_VAR ( auth_hdr - > challenge . digest . realm ) ,
PJSTR_PRINTF_VAR ( auth_hdr - > challenge . digest . algorithm ) ) ;
for ( i = 0 ; i < auth_object_count ; + + i ) {
struct ast_sip_auth * auth = AST_VECTOR_GET ( auth_objects_vector , i ) ;
const char * auth_id = ast_sorcery_object_get_id ( auth ) ;
SCOPE_ENTER ( 5 , " %s:%s: Checking auth '%s' with realm '%s' \n " ,
id , src_name , auth_id , auth - > realm ) ;
/*
* Is the challenge algorithm in the auth ' s supported_algorithms_uac
* and is there either a plain text password or a password_digest
* for the algorithm ?
*/
if ( ! ast_sip_auth_is_algorithm_available ( auth , & auth - > supported_algorithms_uac ,
challenge_algorithm - > algorithm_type ) ) {
SCOPE_EXIT_EXPR ( continue , " %s:%s: Skipping auth '%s' with realm '%s' because it doesn't support "
" algorithm ' " PJSTR_PRINTF_SPEC " ' \n " , id , src_name ,
auth_id , auth - > realm ,
PJSTR_PRINTF_VAR ( challenge_algorithm - > iana_name ) ) ;
}
/*
* If this auth object ' s realm exactly matches the one
* from the header , we can just break out and use it .
*
* NOTE : If there ' s more than one auth object for an endpoint with
* a matching realm it ' s a misconfiguration . We ' ll only use the first .
*/
if ( pj_stricmp2 ( & auth_hdr - > challenge . digest . realm , auth - > realm ) = = 0 ) {
exact_match_index = i ;
/*
* If we found an exact realm match , there ' s no need to keep
* looking for a wildcard .
*/
SCOPE_EXIT_EXPR ( break , " %s:%s: Found matching auth '%s' with realm '%s' \n " ,
id , src_name , auth_id , auth - > realm ) ;
}
/*
* If this auth object ' s realm is empty or a " * " , it ' s a wildcard
* auth object . We going to save its index but keep iterating over
* the vector in case we find an exact match later .
*
* NOTE : If there ' s more than one wildcard auth object for an endpoint
* it ' s a misconfiguration . We ' ll only use the first .
*/
if ( wildcard_match_index < 0
& & ( ast_strlen_zero ( auth - > realm ) | | ast_strings_equal ( auth - > realm , " * " ) ) ) {
ast_trace ( - 1 , " %s:%s: Found wildcard auth '%s' for realm ' " PJSTR_PRINTF_SPEC " ' \n " ,
id , src_name , auth_id ,
PJSTR_PRINTF_VAR ( auth_hdr - > challenge . digest . realm ) ) ;
wildcard_match_index = i ;
}
SCOPE_EXIT ( " %s:%s: Done checking auth '%s' with realm '%s'. "
" Found exact? %s Found wildcard? %s \n " , id , src_name ,
auth_id , auth - > realm , exact_match_index > = 0 ? " yes " : " no " ,
wildcard_match_index > = 0 ? " yes " : " no " ) ;
} /* End auth object loop */
if ( exact_match_index < 0 & & wildcard_match_index < 0 ) {
/*
* Didn ' t find either a wildcard or an exact realm match .
* Move on to the next header .
*/
SCOPE_EXIT_RTN ( " %s:%s: No auth matching realm or no wildcard found for realm ' " PJSTR_PRINTF_SPEC " ' \n " ,
id , src_name , PJSTR_PRINTF_VAR ( auth_hdr - > challenge . digest . realm ) ) ;
}
return 0 ;
if ( exact_match_index > = 0 ) {
/*
* If we found an exact match , we ' ll always prefer that .
*/
found_auth = AST_VECTOR_GET ( auth_objects_vector , exact_match_index ) ;
ast_trace ( - 1 , " %s:%s: Using matched auth '%s' with realm ' " PJSTR_PRINTF_SPEC " ' \n " ,
id , src_name , ast_sorcery_object_get_id ( found_auth ) ,
PJSTR_PRINTF_VAR ( auth_hdr - > challenge . digest . realm ) ) ;
} else {
/*
* We ' ll only use the wildcard if we didn ' t find an exact match .
*/
found_auth = AST_VECTOR_GET ( auth_objects_vector , wildcard_match_index ) ;
ast_trace ( - 1 , " %s:%s: Using wildcard auth '%s' for realm ' " PJSTR_PRINTF_SPEC " ' \n " ,
id , src_name , ast_sorcery_object_get_id ( found_auth ) ,
PJSTR_PRINTF_VAR ( auth_hdr - > challenge . digest . realm ) ) ;
}
/*
* Now that we have an auth object to use , we need to create a
* pjsip_cred_info structure for each algorithm we support .
*/
memset ( & auth_cred , 0 , sizeof ( auth_cred ) ) ;
/*
* Copy the fields from the auth_object to the
* pjsip_cred_info structure .
*/
auth_cred . realm = auth_hdr - > challenge . common . realm ;
pj_cstr ( & auth_cred . username , found_auth - > auth_user ) ;
pj_cstr ( & auth_cred . scheme , " digest " ) ;
/*
* auth_cred . data_type tells us whether the credential is a plain text
* password or a pre - digested one .
*/
cred_data = SCOPE_CALL_WITH_RESULT ( - 1 , const char * , ast_sip_auth_get_creds ,
found_auth , challenge_algorithm - > algorithm_type , & auth_cred . data_type ) ;
/*
* This can ' t really fail because we already called
* ast_sip_auth_is_algorithm_available ( ) for the auth
* but we check anyway .
*/
if ( ! cred_data ) {
SCOPE_EXIT_RTN ( " %s:%s: Shouldn't have happened \n " , id , src_name ) ;
}
pj_cstr ( & auth_cred . data , cred_data ) ;
# ifdef HAVE_PJSIP_AUTH_NEW_DIGESTS
if ( auth_cred . data_type = = PJSIP_CRED_DATA_DIGEST ) {
auth_cred . algorithm_type = challenge_algorithm - > algorithm_type ;
}
# endif
/*
* Because the vector contains actual structures and not pointers
* to structures , the call to AST_VECTOR_APPEND results in a simple
* assign of one structure to another , effectively copying the auth_cred
* structure contents to the array element .
*
* Also note that the calls to pj_cstr above set their respective
* auth_cred fields to the _pointers_ of their corresponding auth
* object fields . This is safe because the call to
* pjsip_auth_clt_set_credentials ( ) below strdups them before we
* return to the calling function which decrements the reference
* counts .
*/
res = AST_VECTOR_APPEND ( auth_creds , auth_cred ) ;
SCOPE_EXIT_RTN ( " %s:%s: %s credential for realm: ' " PJSTR_PRINTF_SPEC " ' algorithm: ' "
PJSTR_PRINTF_SPEC " ' \n " , id , src_name ,
res = = 0 ? " Added " : " Failed to add " ,
PJSTR_PRINTF_VAR ( auth_hdr - > challenge . digest . realm ) ,
PJSTR_PRINTF_VAR ( auth_hdr - > challenge . digest . algorithm ) ) ;
}
/*!
@ -89,7 +306,7 @@ static int is_digest_algorithm_supported(pjsip_www_authenticate_hdr *auth_hdr)
* RFC7616 and RFC8760 allow more than one WWW - Authenticate or
* Proxy - Authenticate header per realm , each with different digest
* algorithms ( including new ones like SHA - 256 and SHA - 512 - 256 ) . However ,
* thankfully, a UAS can NOT send back multiple Authenticate headers for
* a UAS can NOT send back multiple Authenticate headers for
* the same realm with the same digest algorithm . The UAS is also
* supposed to send the headers in order of preference with the first one
* being the most preferred .
@ -99,14 +316,14 @@ static int is_digest_algorithm_supported(pjsip_www_authenticate_hdr *auth_hdr)
*
* The UAS can also send multiple realms , especially when it ' s a proxy
* that has forked the request in which case the proxy will aggregate all
* of the Authenticate and then them all back to the UAC .
* of the Authenticate headers into one response back to the UAC .
*
* It doesn ' t stop there though . . . Each realm can require a different
* username from the others . There ' s also nothing preventing each digest
* algorithm from having a unique password although I ' m not sure if
* that adds any benefit .
*
* So now . . . For each Authenticate header we encounter , we have to
* So now . . . For each WWW/ Proxy - Authenticate header we encounter , we have to
* determine if we support the digest algorithm and , if not , just skip the
* header . We then have to find an auth object that matches the realm AND
* the digest algorithm or find a wildcard object that matches the digest
@ -115,27 +332,22 @@ static int is_digest_algorithm_supported(pjsip_www_authenticate_hdr *auth_hdr)
* we already added an auth object for that realm , we skip the header .
* Otherwise we repeat the process for the next header .
*
* In the end , we ' ll have accumulated a list of credentials we can pass to
* pjproject that it can use to add Authentication headers to a request .
*
* \ note : Neither we nor pjproject can currently handle digest algorithms
* other than MD5 . We don ' t even have a place for it in the ast_sip_auth
* object . For this reason , we just skip processing any Authenticate
* header that ' s not MD5 . When we support the others , we ' ll move the
* check into the loop that searches the objects .
* In the end , we ' ll have accumulated a list of credentials , one per realm ,
* we can pass to pjproject that it can use to add Authentication headers
* to a request .
*/
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 pj_status_t set_auth_creds ( const char * id , pjsip_auth_clt_sess * auth_sess ,
const struct ast_sip_auth_objects_vector * auth_objects_vector ,
pjsip_rx_data * challenge , struct ast_str * * realms )
{
int i ;
size_t auth_object_count ;
pjsip_www_authenticate_hdr * auth_hdr = NULL ;
pj_status_t res = PJ_SUCCESS ;
pjsip_hdr_e search_type ;
size_t cred_count ;
size_t cred_count = 0 ;
pjsip_cred_info * creds_array ;
char * pj_err = NULL ;
const char * src_name = challenge - > pkt_info . src_name ;
/*
* Normally vector elements are pointers to something else , usually
* structures . In this case however , the elements are the
@ -147,7 +359,8 @@ static pj_status_t set_outbound_authentication_credentials(pjsip_auth_clt_sess *
* which we ' ll pass to pjsip_auth_clt_set_credentials ( ) at the
* end .
*/
AST_VECTOR ( cred_info , pjsip_cred_info ) auth_creds ;
struct cred_info_vector auth_creds ;
SCOPE_ENTER ( 3 , " %s:%s \n " , id , src_name ) ;
search_type = get_auth_search_type ( challenge ) ;
if ( search_type = = PJSIP_H_OTHER ) {
@ -156,13 +369,14 @@ static pj_status_t set_outbound_authentication_credentials(pjsip_auth_clt_sess *
* so there are no WWW - Authenticate or Proxy - Authenticate
* headers to process .
*/
return PJ_ENOTSUP ;
SCOPE_EXIT_RTN_VALUE ( PJ_ENOTSUP , " %s:%s: Status code %d was received when it should have been 401 or 407. \n " ,
id , src_name , challenge - > msg_info . msg - > line . status . code ) ;
}
auth_object_count = AST_VECTOR_SIZE ( auth_objects_vector ) ;
if ( auth_object_count = = 0 ) {
/* This shouldn't happen but we'll check anyway. */
return PJ_EINVAL ;
SCOPE_EXIT_RTN_VALUE ( PJ_EINVAL , " %s:%s No auth objects available \n " , id , src_name ) ;
}
/*
@ -176,183 +390,29 @@ static pj_status_t set_outbound_authentication_credentials(pjsip_auth_clt_sess *
* actual structures , not pointers to structures .
*/
if ( AST_VECTOR_INIT ( & auth_creds , 5 ) ! = 0 ) {
return PJ_ENOMEM ;
SCOPE_EXIT_RTN_VALUE ( PJ_ENOMEM ) ;
}
/*
* It ' s going to be rare that we actually have more than one
* WWW - Authentication header or more than one auth object to
* match to it so the following nested loop should be fine .
* There may be multiple WWW / Proxy - Authenticate headers each one having
* a different realm / algorithm pair . Test each to see if we have credentials
* for it and accumulate them in the auth_creds vector .
* The code doesn ' t really care but just for reference , RFC - 7616 says
* a UAS can ' t send multiple headers for the same realm with the same
* algorithm . It also says the UAS should send the headers in order
* of preference with the first one being the most preferred .
*/
while ( ( auth_hdr = pjsip_msg_find_hdr ( challenge - > msg_info . msg ,
search_type , auth_hdr ? auth_hdr - > next : NULL ) ) ) {
int exact_match_index = - 1 ;
int wildcard_match_index = - 1 ;
int match_index = 0 ;
pjsip_cred_info auth_cred ;
struct ast_sip_auth * auth = NULL ;
memset ( & auth_cred , 0 , sizeof ( auth_cred ) ) ;
/*
* Since we only support the MD5 algorithm at the current time ,
* there ' s no sense searching for auth objects that match the algorithm .
* In fact , the auth_object structure doesn ' t even have a member
* for it .
*
* When we do support more algorithms , this check will need to be
* moved inside the auth object loop below .
*
* Note : The header may not have specified an algorithm at all in which
* case it ' s assumed to be MD5 . is_digest_algorithm_supported ( ) returns
* true for that case .
*/
if ( ! is_digest_algorithm_supported ( auth_hdr ) ) {
ast_debug ( 3 , " Skipping header with realm '%.*s' and unsupported '%.*s' algorithm \n " ,
( int ) auth_hdr - > challenge . digest . realm . slen , auth_hdr - > challenge . digest . realm . ptr ,
( int ) auth_hdr - > challenge . digest . algorithm . slen , auth_hdr - > challenge . digest . algorithm . ptr ) ;
continue ;
}
get_creds_for_header ( id , src_name , auth_hdr , auth_object_count ,
auth_objects_vector , & auth_creds , realms ) ;
/*
* Appending the realms is strictly so digest_create_request_with_auth ( )
* can display good error messages . Since we only support one algorithm ,
* there can ' t be more than one header with the same realm . No need to worry
* about duplicate realms until then .
*/
if ( * realms ) {
ast_str_append ( realms , 0 , " %.*s, " ,
( int ) auth_hdr - > challenge . digest . realm . slen , auth_hdr - > challenge . digest . realm . ptr ) ;
}
ast_debug ( 3 , " Searching auths to find matching ones for header with realm '%.*s' and algorithm '%.*s' \n " ,
( int ) auth_hdr - > challenge . digest . realm . slen , auth_hdr - > challenge . digest . realm . ptr ,
( int ) auth_hdr - > challenge . digest . algorithm . slen , auth_hdr - > challenge . digest . algorithm . ptr ) ;
/*
* Now that we have a valid header , we can loop over the auths available to
* find either an exact realm match or , failing that , a wildcard auth ( an
* auth with an empty or " * " realm ) .
*
* NOTE : We never use the global default realm when we ' re the UAC responding
* to a 401 or 407. We only use that when we ' re the UAS ( handled elsewhere )
* and the auth object didn ' t have a realm .
*/
for ( i = 0 ; i < auth_object_count ; + + i ) {
auth = AST_VECTOR_GET ( auth_objects_vector , i ) ;
/*
* If this auth object ' s realm exactly matches the one
* from the header , we can just break out and use it .
*
* NOTE : If there ' s more than one auth object for an endpoint with
* a matching realm it ' s a misconfiguration . We ' ll only use the first .
*/
if ( pj_stricmp2 ( & auth_hdr - > challenge . digest . realm , auth - > realm ) = = 0 ) {
ast_debug ( 3 , " Found matching auth '%s' with realm '%s' \n " , ast_sorcery_object_get_id ( auth ) ,
auth - > realm ) ;
exact_match_index = i ;
/*
* If we found an exact realm match , there ' s no need to keep
* looking for a wildcard .
*/
break ;
}
/*
* If this auth object ' s realm is empty or a " * " , it ' s a wildcard
* auth object . We going to save its index but keep iterating over
* the vector in case we find an exact match later .
*
* NOTE : If there ' s more than one wildcard auth object for an endpoint
* it ' s a misconfiguration . We ' ll only use the first .
*/
if ( wildcard_match_index < 0
& & ( ast_strlen_zero ( auth - > realm ) | | ast_strings_equal ( auth - > realm , " * " ) ) ) {
ast_debug ( 3 , " Found wildcard auth '%s' for realm '%.*s' \n " , ast_sorcery_object_get_id ( auth ) ,
( int ) auth_hdr - > challenge . digest . realm . slen , auth_hdr - > challenge . digest . realm . ptr ) ;
wildcard_match_index = i ;
}
}
if ( exact_match_index < 0 & & wildcard_match_index < 0 ) {
/*
* Didn ' t find either a wildcard or an exact realm match .
* Move on to the next header .
*/
ast_debug ( 3 , " No auth matching realm or no wildcard found for realm '%.*s' \n " ,
( int ) auth_hdr - > challenge . digest . realm . slen , auth_hdr - > challenge . digest . realm . ptr ) ;
continue ;
}
if ( exact_match_index > = 0 ) {
/*
* If we found an exact match , we ' ll always prefer that .
*/
match_index = exact_match_index ;
auth = AST_VECTOR_GET ( auth_objects_vector , match_index ) ;
ast_debug ( 3 , " Using matched auth '%s' with realm '%.*s' \n " , ast_sorcery_object_get_id ( auth ) ,
( int ) auth_hdr - > challenge . digest . realm . slen , auth_hdr - > challenge . digest . realm . ptr ) ;
} else {
/*
* We ' ll only use the wildcard if we didn ' t find an exact match .
*/
match_index = wildcard_match_index ;
auth = AST_VECTOR_GET ( auth_objects_vector , match_index ) ;
ast_debug ( 3 , " Using wildcard auth '%s' for realm '%.*s' \n " , ast_sorcery_object_get_id ( auth ) ,
( int ) auth_hdr - > challenge . digest . realm . slen , auth_hdr - > challenge . digest . realm . ptr ) ;
}
/*
* Copy the fields from the auth_object to the
* pjsip_cred_info structure .
*/
auth_cred . realm = auth_hdr - > challenge . common . realm ;
pj_cstr ( & auth_cred . username , auth - > auth_user ) ;
pj_cstr ( & auth_cred . scheme , " digest " ) ;
switch ( auth - > type ) {
case AST_SIP_AUTH_TYPE_USER_PASS :
pj_cstr ( & auth_cred . data , auth - > auth_pass ) ;
auth_cred . data_type = PJSIP_CRED_DATA_PLAIN_PASSWD ;
break ;
case AST_SIP_AUTH_TYPE_MD5 :
pj_cstr ( & auth_cred . data , auth - > md5_creds ) ;
auth_cred . data_type = PJSIP_CRED_DATA_DIGEST ;
break ;
case AST_SIP_AUTH_TYPE_GOOGLE_OAUTH :
/* nothing to do. handled seperately in res_pjsip_outbound_registration */
break ;
case AST_SIP_AUTH_TYPE_ARTIFICIAL :
ast_log ( LOG_ERROR ,
" Trying to set artificial outbound auth credentials shouldn't happen. \n " ) ;
continue ;
} /* End auth object loop */
/*
* Because the vector contains actual structures and not pointers
* to structures , the call to AST_VECTOR_APPEND results in a simple
* assign of one structure to another , effectively copying the auth_cred
* structure contents to the array element .
*
* Also note that the calls to pj_cstr above set their respective
* auth_cred fields to the _pointers_ of their corresponding auth
* object fields . This is safe because the call to
* pjsip_auth_clt_set_credentials ( ) below strdups them before we
* return to the calling function which decrements the reference
* counts .
*/
res = AST_VECTOR_APPEND ( & auth_creds , auth_cred ) ;
if ( res ! = PJ_SUCCESS ) {
res = PJ_ENOMEM ;
goto cleanup ;
}
} /* End header loop */
if ( * realms & & ast_str_strlen ( * realms ) ) {
/*
* Again , this is strictly so digest_create_request_with_auth ( )
* can display good error messages .
*
* Chop off the trailing " , " on the last realm .
* Chop off the trailing " , " on the last realm - algorithm .
*/
ast_str_truncate ( * realms , ast_str_strlen ( * realms ) - 2 ) ;
}
@ -383,15 +443,15 @@ static pj_status_t set_outbound_authentication_credentials(pjsip_auth_clt_sess *
res = pjsip_auth_clt_set_credentials ( auth_sess , cred_count , creds_array ) ;
ast_free ( creds_array ) ;
if ( res = = PJ_SUCCESS ) {
ast_debug ( 3 , " Set %zu credentials in auth session \n " , cred_count ) ;
} else {
ast_log ( LOG_ERROR , " Failed to set %zu credentials in auth session \n " , cred_count ) ;
}
cleanup :
AST_VECTOR_FREE ( & auth_creds ) ;
return res ;
if ( res ! = PJ_SUCCESS ) {
pj_err = ast_alloca ( PJ_ERR_MSG_SIZE ) ;
pj_strerror ( res , pj_err , PJ_ERR_MSG_SIZE ) ;
}
SCOPE_EXIT_RTN_VALUE ( res , " %s:%s: Set %zu credentials in auth session: %s \n " ,
id , src_name , cred_count , S_OR ( pj_err , " success " ) ) ;
}
/*!
@ -415,12 +475,23 @@ static int digest_create_request_with_auth(const struct ast_sip_auth_vector *aut
pj_status_t status ;
struct ast_sip_auth_objects_vector auth_objects_vector ;
size_t auth_object_count = 0 ;
struct ast_sip_endpoint * endpoint ;
char * id = NULL ;
const char * id_type ;
pjsip_dialog * dlg = pjsip_rdata_get_dlg ( challenge ) ;
struct ast_sip_endpoint * endpoint = ( dlg ? ast_sip_dialog_get_endpoint ( dlg ) : NULL ) ;
/*
* We ' re ast_strdupa ' ing the endpoint id because we ' re going to
* clean up the endpoint immediately after this . We only needed
* it to get the id for logging .
*/
char * endpoint_id = endpoint ? ast_strdupa ( ast_sorcery_object_get_id ( endpoint ) ) : NULL ;
char * id = endpoint_id ? : " noendpoint " ;
char * src_name = challenge - > pkt_info . src_name ;
struct ast_str * realms = NULL ;
pjsip_dialog * dlg ;
int res = - 1 ;
char * pj_err = NULL ;
SCOPE_ENTER ( 3 , " %s:%s \n " , id , src_name ) ;
/* We only needed endpoint to get the id */
ao2_cleanup ( endpoint ) ;
/*
* Some older compilers have an issue with initializing structures with
@ -429,31 +500,18 @@ static int digest_create_request_with_auth(const struct ast_sip_auth_vector *aut
*/
memset ( & auth_sess , 0 , sizeof ( auth_sess ) ) ;
dlg = pjsip_rdata_get_dlg ( challenge ) ;
if ( dlg ) {
/* The only thing we use endpoint for is to get an id for error/debug messages */
endpoint = ast_sip_dialog_get_endpoint ( dlg ) ;
id = endpoint ? ast_strdupa ( ast_sorcery_object_get_id ( endpoint ) ) : NULL ;
ao2_cleanup ( endpoint ) ;
id_type = " Endpoint " ;
}
/* If there was no dialog, then this is probably a REGISTER so no endpoint */
if ( ! id ) {
/* The only thing we use the address for is to get an id for error/debug messages */
id = ast_alloca ( AST_SOCKADDR_BUFLEN ) ;
pj_sockaddr_print ( & challenge - > pkt_info . src_addr , id , AST_SOCKADDR_BUFLEN , 3 ) ;
id_type = " Host " ;
}
if ( ! auth_ids_vector | | AST_VECTOR_SIZE ( auth_ids_vector ) = = 0 ) {
ast_log ( LOG_ERROR , " %s: '%s': There were no auth ids available \n " , id_type , id ) ;
SCOPE_EXIT_LOG_RTN_VALUE ( - 1 , LOG_ERROR , " %s:%s: There were no auth ids available \n " ,
id , src_name ) ;
return - 1 ;
}
/*
* auth_ids_vector contains only ids but we need the complete objects .
*/
if ( AST_VECTOR_INIT ( & auth_objects_vector , AST_VECTOR_SIZE ( auth_ids_vector ) ) ! = 0 ) {
ast_log ( LOG_ERROR , " %s: '%s': Couldn't initialize auth object vector \n " , id_type , id ) ;
return - 1 ;
SCOPE_EXIT_LOG_RTN_VALUE ( - 1 , LOG_ERROR , " %s:%s: Couldn't initialize auth object vector \n " ,
id , src_name ) ;
}
/*
@ -465,6 +523,8 @@ static int digest_create_request_with_auth(const struct ast_sip_auth_vector *aut
* AST_VECTOR_FREE ( & auth_objects_vector ) ;
* when you ' re done with the vector
*/
ast_trace ( - 1 , " %s:%s: Retrieving %d auth objects \n " , id , src_name ,
( int ) AST_VECTOR_SIZE ( auth_ids_vector ) ) ;
ast_sip_retrieve_auths_vector ( auth_ids_vector , & auth_objects_vector ) ;
auth_object_count = AST_VECTOR_SIZE ( & auth_objects_vector ) ;
if ( auth_object_count = = 0 ) {
@ -475,13 +535,19 @@ static int digest_create_request_with_auth(const struct ast_sip_auth_vector *aut
* id that wasn ' t found .
*/
res = - 1 ;
ast_trace ( - 1 , " %s:%s: No auth objects found \n " , id , src_name ) ;
goto cleanup ;
}
if ( pjsip_auth_clt_init ( & auth_sess , ast_sip_get_pjsip_endpoint ( ) ,
old_request - > pool , 0 ) ! = PJ_SUCCESS ) {
ast_log ( LOG_ERROR , " %s: '%s': Failed to initialize client authentication session \n " ,
id_type , id ) ;
ast_trace ( - 1 , " %s:%s: Retrieved %d auth objects \n " , id , src_name ,
( int ) auth_object_count ) ;
status = pjsip_auth_clt_init ( & auth_sess , ast_sip_get_pjsip_endpoint ( ) ,
old_request - > pool , 0 ) ;
if ( status ! = PJ_SUCCESS ) {
pj_err = ast_alloca ( PJ_ERR_MSG_SIZE ) ;
pj_strerror ( status , pj_err , PJ_ERR_MSG_SIZE ) ;
ast_log ( LOG_ERROR , " %s:%s: Failed to initialize client authentication session: %s \n " ,
id , src_name , pj_err ) ;
res = - 1 ;
goto cleanup ;
}
@ -499,18 +565,24 @@ static int digest_create_request_with_auth(const struct ast_sip_auth_vector *aut
* Load pjproject with the valid credentials for the Authentication headers
* received on the 401 or 407 response .
*/
status = set_outbound_authentication_credentials ( & auth_sess , & auth_objects_vector , challenge , & realms ) ;
status = SCOPE_CALL_WITH_RESULT ( - 1 , pj_status_t , set_auth_creds , id , & auth_sess , & auth_objects_vector , challenge , & realms ) ;
if ( status ! = PJ_SUCCESS & & status ! = PJSIP_ENOCREDENTIAL ) {
pj_err = ast_alloca ( PJ_ERR_MSG_SIZE ) ;
}
switch ( status ) {
case PJ_SUCCESS :
break ;
case PJSIP_ENOCREDENTIAL :
ast_log ( LOG_WARNING ,
" %s: ' %s' : No auth objects matching realm(s) '%s' from challenge found.\n " , id_type , id ,
realms ? ast_str_buffer ( realms ) : " <none> " ) ;
" %s: %s: No auth objects matching realm/algorithm (s) '%s' from challenge found.\n " ,
id, src_name , realms ? ast_str_buffer ( realms ) : " <none> " ) ;
res = - 1 ;
goto cleanup ;
default :
ast_log ( LOG_WARNING , " %s: '%s': Failed to set authentication credentials \n " , id_type , id ) ;
pj_strerror ( status , pj_err , PJ_ERR_MSG_SIZE ) ;
ast_log ( LOG_WARNING , " %s:%s: Failed to set authentication credentials: %s \n " ,
id , src_name , pj_err ) ;
res = - 1 ;
goto cleanup ;
}
@ -521,7 +593,11 @@ static int digest_create_request_with_auth(const struct ast_sip_auth_vector *aut
* from an earlier successful authorization , it ' ll use it . Otherwise
* it ' ll create a new authorization and cache it .
*/
status = pjsip_auth_clt_reinit_req ( & auth_sess , challenge , old_request , new_request ) ;
status = SCOPE_CALL_WITH_RESULT ( - 1 , pj_status_t , pjsip_auth_clt_reinit_req ,
& auth_sess , challenge , old_request , new_request ) ;
if ( status ! = PJ_SUCCESS ) {
pj_err = ast_alloca ( PJ_ERR_MSG_SIZE ) ;
}
switch ( status ) {
case PJ_SUCCESS :
@ -535,6 +611,7 @@ static int digest_create_request_with_auth(const struct ast_sip_auth_vector *aut
ast_assert ( cseq ! = NULL ) ;
+ + cseq - > cseq ;
res = 0 ;
ast_trace ( - 1 , " %s:%s: Created new request with auth \n " , id , src_name ) ;
goto cleanup ;
case PJSIP_ENOCREDENTIAL :
/*
@ -542,21 +619,24 @@ static int digest_create_request_with_auth(const struct ast_sip_auth_vector *aut
* did the matching but you never know .
*/
ast_log ( LOG_WARNING ,
" %s: ' %s' : No auth objects matching realm(s) '%s' from challenge found.\n " , id_type , id ,
realms ? ast_str_buffer ( realms ) : " <none> " ) ;
" %s: %s: No auth objects matching realm(s) '%s' from challenge found.\n " ,
id, src_name , realms ? ast_str_buffer ( realms ) : " <none> " ) ;
break ;
case PJSIP_EAUTHSTALECOUNT :
pj_strerror ( status , pj_err , PJ_ERR_MSG_SIZE ) ;
ast_log ( LOG_WARNING ,
" %s: '%s': Unable to create request with auth. Number of stale retries exceeded. \n " ,
id _type, id ) ;
" %s: %s: Unable to create request with auth: %s \n " ,
id , src_name , pj_err ) ;
break ;
case PJSIP_EFAILEDCREDENTIAL :
ast_log ( LOG_WARNING , " %s: '%s': Authentication credentials not accepted by server. \n " ,
id_type , id ) ;
pj_strerror ( status , pj_err , PJ_ERR_MSG_SIZE ) ;
ast_log ( LOG_WARNING , " %s:%s: Authentication credentials not accepted by server. %s \n " ,
id , src_name , pj_err ) ;
break ;
default :
ast_log ( LOG_WARNING , " %s: '%s': Unable to create request with auth. Unknown failure. \n " ,
id_type , id ) ;
pj_strerror ( status , pj_err , PJ_ERR_MSG_SIZE ) ;
ast_log ( LOG_WARNING , " %s:%s: Unable to create request with auth: %s \n " ,
id , src_name , pj_err ) ;
break ;
}
res = - 1 ;
@ -573,7 +653,8 @@ cleanup:
AST_VECTOR_FREE ( & auth_objects_vector ) ;
ast_free ( realms ) ;
return res ;
SCOPE_EXIT_RTN_VALUE ( res , " %s:%s: result: %s \n " , id , src_name ,
res = = 0 ? " success " : " failure " ) ;
}
static struct ast_sip_outbound_authenticator digest_authenticator = {