diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h index 9ed32e2c9fb9605e77ff75a50bd9589bb4797bc0..92b712536b17fdf8092e5774f5bd2fc83fae62a5 100644 --- a/include/asterisk/res_pjsip.h +++ b/include/asterisk/res_pjsip.h @@ -23,6 +23,7 @@ /* Needed for SUBSCRIBE, NOTIFY, and PUBLISH method definitions */ #include <pjsip_simple.h> #include <pjsip/sip_transaction.h> +#include <pjsip/sip_auth.h> #include <pj/timer.h> /* Needed for pj_sockaddr */ #include <pjlib.h> @@ -1107,6 +1108,11 @@ struct ast_sip_endpoint { unsigned int max_sessions; char *realms; bool cached_auth; + /* Authorization sessions. */ + pjsip_auth_clt_sess auth_sess_reg; + pjsip_auth_clt_sess auth_sess_inv; + ast_mutex_t auth_sess_reg_lock; + ast_mutex_t auth_sess_inv_lock; }; struct pjsip_register_dest { diff --git a/include/asterisk/res_pjsip_outbound_registration.h b/include/asterisk/res_pjsip_outbound_registration.h index b371b7dda3c074bd2f57b0d13e6e542fd7d95f3e..0a47b416d048293baeff8c15be0617508fabbe35 100644 --- a/include/asterisk/res_pjsip_outbound_registration.h +++ b/include/asterisk/res_pjsip_outbound_registration.h @@ -3,6 +3,7 @@ void queue_registration_recovery_flow(const char *registration_name); void update_emergency_registration_ongoing_status(const char *registration_name, bool action); -void add_cached_auth_header_from_reg(pjsip_tx_data *tdata, const char *registration_name); +void check_and_update_nextnonce(pjsip_auth_clt_sess *sess, const char *realm, pjsip_rx_data *rdata); +void sync_cached_auth_between_reg_inv(pjsip_auth_clt_sess *sess_from, pjsip_auth_clt_sess *sess_to, const pj_str_t *realm ); #endif /* _RES_PJSIP_OUTBOUND_REGISTRATION_H */ diff --git a/res/res_pjsip_outbound_authenticator_digest.c b/res/res_pjsip_outbound_authenticator_digest.c index aee4afc90ec5a23b9b4090ce1c278eb9c5f24380..d3a6b825ba79b72212dc131098a6a587ed96b5e3 100644 --- a/res/res_pjsip_outbound_authenticator_digest.c +++ b/res/res_pjsip_outbound_authenticator_digest.c @@ -31,6 +31,7 @@ #include "asterisk/module.h" #include "asterisk/strings.h" #include "asterisk/vector.h" +#include "asterisk/res_pjsip_outbound_registration.h" pj_str_t supported_digest_algorithms[] = { { "MD5", 3} @@ -478,16 +479,25 @@ static int digest_create_request_with_auth(const struct ast_sip_auth_vector *aut 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); - res = -1; - goto cleanup; + pjsip_hdr_e search_type = get_auth_search_type(challenge); + struct ast_sip_auth *auth = NULL; + if (search_type != PJSIP_H_OTHER) { + pjsip_www_authenticate_hdr *auth_hdr = NULL; + while ((auth_hdr = pjsip_msg_find_hdr(challenge->msg_info.msg, search_type, auth_hdr ? auth_hdr->next : NULL))) { + for (int i = 0; i < auth_object_count; ++i) { + auth = AST_VECTOR_GET(&auth_objects_vector, i); + + 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); + endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", ast_sorcery_object_get_id(auth)); + break; + } + } + } } /* - * realms is used only for displaying good error messages. + * realms is used for displaying good error messages, and for caching auth header */ realms = ast_str_create(32); if (!realms) { @@ -495,11 +505,25 @@ static int digest_create_request_with_auth(const struct ast_sip_auth_vector *aut goto cleanup; } - /* - * 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); + if (endpoint && (pjsip_method_cmp(&old_request->msg->line.req.method, &pjsip_register_method) ==0 || pjsip_method_cmp(&old_request->msg->line.req.method, &pjsip_invite_method) ==0)) { + if(auth && auth->realm){ + ast_str_append(&realms, 0, "%s ", auth->realm); + } + goto add_header; // auth_sess for REGISTER and INVITE has been inited during sip_outbound_registration_regc_alloc + } else 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); + res = -1; + goto cleanup; + } else { + /* + * 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); + } + switch (status) { case PJ_SUCCESS: break; @@ -515,13 +539,40 @@ static int digest_create_request_with_auth(const struct ast_sip_auth_vector *aut goto cleanup; } +add_header: /* * reinit_req actually creates the Authorization headers to send on * the next request. If reinit_req already has a cached credential * 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); + if(endpoint &&pjsip_method_cmp(&old_request->msg->line.req.method, &pjsip_register_method) ==0 ){ + ast_mutex_lock(&endpoint->auth_sess_reg_lock); + status = pjsip_auth_clt_reinit_req(&endpoint->auth_sess_reg, challenge, old_request, new_request); + //if success, sync cached info to inv + if (status == PJ_SUCCESS){ + ast_debug(3, "sync cached auth info from auth_sess_reg to auth_sess_inv\n"); + pj_str_t realms_pj_str = pj_str(endpoint->realms); + ast_mutex_lock(&endpoint->auth_sess_inv_lock); + sync_cached_auth_between_reg_inv(&endpoint->auth_sess_reg, &endpoint->auth_sess_inv, &realms_pj_str); + ast_mutex_unlock(&endpoint->auth_sess_inv_lock); + } + ast_mutex_unlock(&endpoint->auth_sess_reg_lock); + } else if (endpoint && pjsip_method_cmp(&old_request->msg->line.req.method, &pjsip_invite_method) ==0 ){ + ast_mutex_lock(&endpoint->auth_sess_inv_lock); + status = pjsip_auth_clt_reinit_req(&endpoint->auth_sess_inv, challenge, old_request, new_request); + //if success, sync cached info to reg + if (status == PJ_SUCCESS){ + ast_debug(3, "sync cached auth info from auth_sess_inv to auth_sess_reg\n"); + pj_str_t realms_pj_str = pj_str(endpoint->realms); + ast_mutex_lock(&endpoint->auth_sess_reg_lock); + sync_cached_auth_between_reg_inv(&endpoint->auth_sess_inv, &endpoint->auth_sess_reg, &realms_pj_str); + ast_mutex_unlock(&endpoint->auth_sess_reg_lock); + } + ast_mutex_unlock(&endpoint->auth_sess_inv_lock); + } else { + status = pjsip_auth_clt_reinit_req(&auth_sess, challenge, old_request, new_request); + } switch (status) { case PJ_SUCCESS: diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c index 9ed4056fea3b64dbcc9f3656447a4ac40e0fcbb4..5f84bee3124365064342d7d1038cf13cc57daa8d 100644 --- a/res/res_pjsip_outbound_registration.c +++ b/res/res_pjsip_outbound_registration.c @@ -515,7 +515,6 @@ struct sip_outbound_registration_client_state { bool Emergency_reg_ongoing; bool Emergency_reg_hangup; bool Emergency_call_ongoing; - char *cached_nonce; }; /*! \brief Outbound registration state information (persists for lifetime that registration should exist) */ @@ -951,6 +950,18 @@ static pj_status_t registration_client_send(struct sip_outbound_registration_cli ao2_cleanup(reg); } } + //if cached_auth, no auth header, call pjsip_auth_clt_init_req + if (endpoint->cached_auth == true && (pjsip_msg_find_hdr(tdata->msg, PJSIP_H_AUTHORIZATION, NULL) == NULL)){ + //check and sync info from auth_sess_inv, nc, nonce + pj_str_t realms_pj_str = pj_str(endpoint->realms); + ast_mutex_lock(&endpoint->auth_sess_reg_lock); + ast_mutex_lock(&endpoint->auth_sess_inv_lock); + sync_cached_auth_between_reg_inv(&endpoint->auth_sess_inv, &endpoint->auth_sess_reg, &realms_pj_str); + ast_mutex_unlock(&endpoint->auth_sess_inv_lock); + ast_debug(3, "Add cached auth header\n"); + pjsip_auth_clt_init_req(&endpoint->auth_sess_reg, tdata); + ast_mutex_unlock(&endpoint->auth_sess_reg_lock); + } client_state->attempts++; ast_sip_mod_data_set(tdata->pool, tdata->mod_data, log_register_module.id, @@ -1297,7 +1308,25 @@ static int handle_client_state_destruction(void *data) pjsip_regc_destroy(client_state->client); client_state->client = NULL; } - + struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", client_state->registration_name); + if(endpoint){ + if(endpoint->auth_sess_reg.endpt){ + pjsip_auth_clt_deinit(&endpoint->auth_sess_reg); + if (endpoint->auth_sess_reg.pool) { + pj_pool_release(endpoint->auth_sess_reg.pool); + endpoint->auth_sess_reg.pool = NULL; + } + ast_mutex_destroy(&endpoint->auth_sess_reg_lock); + } + if(endpoint->auth_sess_inv.endpt){ + pjsip_auth_clt_deinit(&endpoint->auth_sess_inv); + if (endpoint->auth_sess_inv.pool) { + pj_pool_release(endpoint->auth_sess_inv.pool); + endpoint->auth_sess_inv.pool = NULL; + } + ast_mutex_destroy(&endpoint->auth_sess_inv_lock); + } + } update_client_state_status(client_state, SIP_REGISTRATION_STOPPED); ast_sip_auth_vector_destroy(&client_state->outbound_auths); ast_sip_security_mechanisms_vector_destroy(&client_state->security_mechanisms); @@ -1927,51 +1956,12 @@ static int handle_registration_response(void *data) pjsip_cseq_hdr *cseq_hdr; pjsip_tx_data *tdata; - // remove the unwanted auth header generated from libpjsip in case of no stale flag in the challenge. - // old_request has been updated under libpjsip after received 401/407 and before calling the callback. - pjsip_authorization_hdr *auth = pjsip_msg_find_hdr(response->old_request->msg, PJSIP_H_AUTHORIZATION, NULL); - if(auth){ - char nonce[64]; - ast_copy_pj_str(nonce, &auth->credential.digest.nonce, sizeof(nonce)); - struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", - client_state->registration_name); - if (endpoint && endpoint->cached_auth != true ){ - ast_debug(8, "no cached_auth, remove the unwanted header from old_request\n"); - pjsip_msg_find_remove_hdr(response->old_request->msg, PJSIP_H_AUTHORIZATION, NULL); - } else if (endpoint && endpoint->cached_auth == true){ - pjsip_www_authenticate_hdr *challenge_auth = pjsip_msg_find_hdr(response->rdata->msg_info.msg, PJSIP_H_WWW_AUTHENTICATE, NULL); - if (pj_strcmp2(&challenge_auth->challenge.digest.nonce, (const char *)client_state->cached_nonce)){ - ast_debug(8, "nonce from challenge and cache are different, remove the unwanted header generated from libpjsip\n"); - pjsip_msg_find_remove_hdr(response->old_request->msg, PJSIP_H_AUTHORIZATION, NULL); - } - } - } - if (!ast_sip_create_request_with_auth(&client_state->outbound_auths, response->rdata, response->old_request, &tdata)) { client_state->auth_attempted = 1; ast_debug(1, "Sending authenticated REGISTER to server '%s' from client '%s'\n", server_uri, client_uri); - pjsip_authorization_hdr *auth = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_AUTHORIZATION, NULL); - if (auth && !pj_strcmp2(&auth->scheme, "Digest")) { - // sync the cnonce with pjproject - char cnonce[64]; - ast_copy_pj_str(cnonce, &auth->credential.digest.cnonce, sizeof(cnonce)); - ast_debug(3, "sync cnonce: %s\n", cnonce); - pj_str_t cnonce_pj_str = pj_str(cnonce); - struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", client_state->registration_name); - if(endpoint && endpoint->realms){ - pj_str_t realms_pj_str = pj_str(endpoint->realms); - int test_ret = pjsip_regc_update_cnonce(client_state->client, &cnonce_pj_str, &realms_pj_str); - ast_debug(3, "pjsip_regc_update_cnonce: %d, realms:'%s'\n, cnonce: %s", test_ret, endpoint->realms, cnonce); - } - // cache nonce for stale flag correction - char nonce[64]; - ast_copy_pj_str(nonce, &auth->credential.digest.nonce, sizeof(nonce)); - client_state->cached_nonce = ast_strdup(nonce); - ast_debug(3, "client_state->cached_nonce: %s\n", client_state->cached_nonce); - } /* Add MEDIASEC headers */ if (client_state->mediasec) { struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", @@ -2134,26 +2124,21 @@ static int handle_registration_response(void *data) if (strlen(endpoint->incoming_mwi_mailbox)) mwi_send_subscribe(endpoint, response->expiration); - if(endpoint && endpoint->realms){ - static const pj_str_t headerName = { "Authentication-Info", 19 }; - pjsip_generic_string_hdr *auth_info = NULL; - auth_info = pjsip_msg_find_hdr_by_name(response->rdata->msg_info.msg, &headerName, NULL); - if(auth_info){ - char value[pj_strlen(&((pjsip_generic_string_hdr*)auth_info)->hvalue) + 1]; - ast_copy_pj_str(value, &((pjsip_generic_string_hdr*)auth_info)->hvalue, sizeof(value)); - char nextnonce[64]; - char *_buf = strstr(value, "nextnonce="); - if (_buf) { - sscanf(_buf, "nextnonce=\"%s", nextnonce); - char *buf_s = strstr(nextnonce, "\""); - nextnonce[((int)strlen(nextnonce)-(int)strlen(buf_s))]='\0'; - ast_log(LOG_NOTICE, "received nextnonce: %s\n", nextnonce); - pj_str_t nextnonce_pj_str = pj_str(nextnonce); - pj_str_t realms_pj_str = pj_str(endpoint->realms); - int test_ret = pjsip_regc_update_nextnonce(client_state->client, &nextnonce_pj_str, &realms_pj_str); - ast_log(LOG_NOTICE, "pjsip_regc_update_nextnonce: %d, realms:'%s'\n, nextnonce: %s", test_ret, endpoint->realms, nextnonce); - client_state->cached_nonce = ast_strdup(nextnonce); - } + if (endpoint){ + if (endpoint->realms){ + ast_mutex_lock(&endpoint->auth_sess_reg_lock); + check_and_update_nextnonce(&endpoint->auth_sess_reg, endpoint->realms, response->rdata); + ast_mutex_unlock(&endpoint->auth_sess_reg_lock); + } + if (endpoint->cached_auth){ + //if cached, sync to auth_sess_inv, nc, nonce + pj_str_t realms_pj_str = pj_str(endpoint->realms); + ast_debug(3, "Sync cached auth sess from auth_sess_reg to auth_sess_inv\n"); + ast_mutex_lock(&endpoint->auth_sess_reg_lock); + ast_mutex_lock(&endpoint->auth_sess_inv_lock); + sync_cached_auth_between_reg_inv(&endpoint->auth_sess_reg, &endpoint->auth_sess_inv, &realms_pj_str); // (from, to, realm) + ast_mutex_unlock(&endpoint->auth_sess_inv_lock); + ast_mutex_unlock(&endpoint->auth_sess_reg_lock); } } @@ -2782,6 +2767,9 @@ static int set_outbound_initial_authentication_credentials(pjsip_regc *regc, pjsip_regc_set_credentials(regc, 1, auth_creds); struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", ast_sorcery_object_get_id(auths[idx])); endpoint->realms = ast_strdup(auths[idx]->realm); + // set credentials for local auth_sess endpoint->auth_sess_reg/auth_sess_inv + pjsip_auth_clt_set_credentials(&endpoint->auth_sess_reg, 1, auth_creds); + pjsip_auth_clt_set_credentials(&endpoint->auth_sess_inv, 1, auth_creds); default: /* other cases handled after receiving auth rejection */ break; @@ -2899,6 +2887,20 @@ static int sip_outbound_registration_regc_alloc(void *data) return -1; } + struct pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint(); + struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", state->client_state->registration_name); + pool = pjsip_endpt_create_pool(endpt, "sip_auth_session_reg%p", 1024, 1024); + if(pjsip_auth_clt_init(&endpoint->auth_sess_reg, endpt, pool, 0) != PJ_SUCCESS){ + ast_log(LOG_WARNING, "auth_sess_reg init failed!\n"); + }else{ + ast_mutex_init(&endpoint->auth_sess_reg_lock); + } + pool = pjsip_endpt_create_pool(endpt, "sip_auth_session_inv%p", 1024, 1024); + if(pjsip_auth_clt_init(&endpoint->auth_sess_inv, endpt, pool, 0) != PJ_SUCCESS){ + ast_log(LOG_WARNING, "auth_sess_inv init failed!\n"); + }else{ + ast_mutex_init(&endpoint->auth_sess_inv_lock); + } return 0; } /*! \brief Helper function which performs a single registration for recovery flow*/ @@ -3318,32 +3320,95 @@ void update_emergency_registration_ongoing_status(const char *registration_name, } -void add_cached_auth_header_from_reg(pjsip_tx_data *tdata, const char *registration_name) +void queue_registration_recovery_flow(const char *registration_name) { struct sip_outbound_registration_state *state; state = get_state(registration_name); if (!state) { ast_log(LOG_WARNING, "Unable to retrieve registration %s\n", registration_name); } else { - struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", registration_name); - if(endpoint && endpoint->realms){ - pj_str_t realms_pj_str = pj_str(endpoint->realms); - int test_ret = pjsip_regc_add_cached_auth_for_inv(tdata, state->client_state->client, &realms_pj_str); - ast_debug(3, "pjsip_regc_add_cached_auth_for_inv: %d, realms:'%s'\n", test_ret, endpoint->realms); + cancel_registration(state->client_state); + ast_log(LOG_NOTICE, "Queue registration with new state \n"); + re_register_with_new_state(ast_sip_get_sorcery(), state->registration); + } +} + +void check_and_update_nextnonce(pjsip_auth_clt_sess *sess, const char *realm, pjsip_rx_data *rdata){ + pjsip_generic_string_hdr *auth_info = NULL; + static const pj_str_t headerName = { "Authentication-Info", 19 }; + auth_info = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &headerName, NULL); + if(auth_info){ + char value[pj_strlen(&((pjsip_generic_string_hdr*)auth_info)->hvalue) + 1]; + ast_copy_pj_str(value, &((pjsip_generic_string_hdr*)auth_info)->hvalue, sizeof(value)); + char nextnonce[64]; + char *_buf = strstr(value, "nextnonce="); + if (_buf) { + sscanf(_buf, "nextnonce=\"%s", nextnonce); + char *buf_s = strstr(nextnonce, "\""); + nextnonce[((int)strlen(nextnonce)-(int)strlen(buf_s))]='\0'; + ast_log(LOG_NOTICE, "received nextnonce: %s\n", nextnonce); + pj_str_t nextnonce_pj_str = pj_str(nextnonce); + pj_str_t realms_pj_str = pj_str(realm); + pjsip_cached_auth *cached_auth; + cached_auth = pjsip_auth_find_cached_auth(sess, &realms_pj_str); + if (cached_auth) { + pj_strdup(cached_auth->pool, &cached_auth->last_chal->challenge.digest.nonce, &nextnonce_pj_str); + cached_auth->nc=0; + ast_debug(3, "update nextnonce to cached auth\n"); + } } } } -void queue_registration_recovery_flow(const char *registration_name) +void sync_cached_auth_between_reg_inv(pjsip_auth_clt_sess *sess_from, pjsip_auth_clt_sess *sess_to, const pj_str_t *realm ) { - struct sip_outbound_registration_state *state; - state = get_state(registration_name); - if (!state) { - ast_log(LOG_WARNING, "Unable to retrieve registration %s\n", registration_name); - } else { - cancel_registration(state->client_state); - ast_log(LOG_NOTICE, "Queue registration with new state \n"); - re_register_with_new_state(ast_sip_get_sorcery(), state->registration); + pjsip_cached_auth *cached_auth_from, *cached_auth_to; + cached_auth_from = pjsip_auth_find_cached_auth(sess_from, realm); + if (cached_auth_from) { + const pjsip_cred_info *cred; + pjsip_authorization_hdr *hauth; + pj_status_t status; + cred = pjsip_auth_find_cred(sess_from, &cached_auth_from->realm, &cached_auth_from->last_chal->scheme ); + if (!cred) + return; + // Find cached_auth for INVITE, create new if not exists. + cached_auth_to = pjsip_auth_find_cached_auth(sess_to, realm); + if (!cached_auth_to) { + cached_auth_to = PJ_POOL_ZALLOC_T(sess_to->pool, pjsip_cached_auth); + cached_auth_to->pool = pjsip_endpt_create_pool(sess_to->endpt, "auth_sync_cli%p", 1024, 1024); + pj_strdup(cached_auth_to->pool, &cached_auth_to->realm, realm); + cached_auth_to->is_proxy = PJ_TRUE; + pj_list_init(&cached_auth_to->cached_hdr); + pj_list_insert_before(&sess_to->cached_auth, cached_auth_to); + ast_debug(8, "New cached auth created\n"); + } + // Sync with cached_auth info + cached_auth_to->nc = cached_auth_from->nc; + cached_auth_to->qop_value = cached_auth_from->qop_value; + cached_auth_to->stale_cnt = cached_auth_from->stale_cnt; + pj_strdup(cached_auth_to->pool, &cached_auth_to->cnonce, &cached_auth_from->cnonce); + pj_pool_t *pool = pjsip_endpt_create_pool(sess_from->endpt, "m_auth_cli%p", 1024, 1024); + pjsip_www_authenticate_hdr *hdr; + if (cached_auth_from->last_chal->type == PJSIP_H_WWW_AUTHENTICATE) + hdr = pjsip_proxy_authenticate_hdr_create(pool); + else + hdr = pjsip_www_authenticate_hdr_create(pool); + + pj_strdup(pool, &hdr->scheme, &cached_auth_from->last_chal->scheme); + if (pj_stricmp2(&hdr->scheme, "digest") == 0) { + pj_strdup(pool, &hdr->challenge.digest.realm, &cached_auth_from->last_chal->challenge.digest.realm); + pj_strdup(pool, &hdr->challenge.digest.domain, &cached_auth_from->last_chal->challenge.digest.domain); + pj_strdup(pool, &hdr->challenge.digest.nonce, &cached_auth_from->last_chal->challenge.digest.nonce); + pj_strdup(pool, &hdr->challenge.digest.opaque, &cached_auth_from->last_chal->challenge.digest.opaque); + hdr->challenge.digest.stale = cached_auth_from->last_chal->challenge.digest.stale; + pj_strdup(pool, &hdr->challenge.digest.algorithm, &cached_auth_from->last_chal->challenge.digest.algorithm); + pj_strdup(pool, &hdr->challenge.digest.qop, &cached_auth_from->last_chal->challenge.digest.qop); + pjsip_param_clone(pool, &hdr->challenge.digest.other_param, + &cached_auth_from->last_chal->challenge.digest.other_param); + } + pjsip_endpt_release_pool(sess_from->endpt, pool); + cached_auth_to->last_chal = (pjsip_www_authenticate_hdr*) pjsip_hdr_clone(cached_auth_to->pool, hdr); + ast_debug(8, "cached auth synced\n"); } } diff --git a/res/res_pjsip_outbound_registration.exports.in b/res/res_pjsip_outbound_registration.exports.in index be75cc4300aa55afee2fc2fbc9559f7d60a0e764..05c3efb00a057c91db3019db17449cea5d69241a 100644 --- a/res/res_pjsip_outbound_registration.exports.in +++ b/res/res_pjsip_outbound_registration.exports.in @@ -2,7 +2,8 @@ global: LINKER_SYMBOL_PREFIXqueue_registration_recovery_flow; LINKER_SYMBOL_PREFIXupdate_emergency_registration_ongoing_status; - LINKER_SYMBOL_PREFIXadd_cached_auth_header_from_reg; + LINKER_SYMBOL_PREFIXcheck_and_update_nextnonce; + LINKER_SYMBOL_PREFIXsync_cached_auth_between_reg_inv; local: *; }; diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c index e35701f8a13da7718f29fa1502c0dd54bbf37663..710d519ef6e0e872ceb4f3205ebe123723bc6925 100644 --- a/res/res_pjsip_session.c +++ b/res/res_pjsip_session.c @@ -2966,7 +2966,11 @@ int ast_sip_session_create_invite(struct ast_sip_session *session, pjsip_tx_data if (session->endpoint->cached_auth){ ast_debug(3, "INVITE: Attach cached auth headers\n"); - add_cached_auth_header_from_reg(*tdata, ast_sorcery_object_get_id(session->endpoint)); + if (pjsip_msg_find_hdr((*tdata)->msg, PJSIP_H_PROXY_AUTHORIZATION, NULL) == NULL){ + ast_mutex_lock(&session->endpoint->auth_sess_inv_lock); + pjsip_auth_clt_init_req(&session->endpoint->auth_sess_inv, *tdata); + ast_mutex_unlock(&session->endpoint->auth_sess_inv_lock); + } } content_header = pjsip_msg_find_hdr_by_name((*tdata)->msg, &headerName, NULL); @@ -3343,6 +3347,25 @@ void ast_sip_session_unsuspend(struct ast_sip_session *session) ast_taskprocessor_unsuspend(session->serializer); } +static void invite_nextnonce_update(pjsip_rx_data *rdata){ + pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata); + pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata); + if (!dlg || !tsx) { + return; + } + if (tsx->method.id != PJSIP_INVITE_METHOD) { + /* Not an INVITE that needs authentication */ + return; + } + pjsip_inv_session *inv = pjsip_dlg_get_inv_session(dlg); + struct ast_sip_session *session = inv->mod_data[session_module.id]; + if (session->endpoint && session->endpoint->realms){ + ast_mutex_lock(&session->endpoint->auth_sess_inv_lock); + check_and_update_nextnonce(&session->endpoint->auth_sess_inv, session->endpoint->realms, rdata); + ast_mutex_unlock(&session->endpoint->auth_sess_inv_lock); + } +} + /*! * \internal * \brief Handle initial INVITE challenge response message. @@ -3363,6 +3386,8 @@ static pj_bool_t outbound_invite_auth(pjsip_rx_data *rdata) if (rdata->msg_info.msg->line.status.code != 401 && rdata->msg_info.msg->line.status.code != 407) { + // check and update for nextnonce if needed + invite_nextnonce_update(rdata); /* Doesn't pertain to us. Move on */ return PJ_FALSE; } diff --git a/third-party/pjproject/patches/0021-Update-for-Authorization-header-handling.patch b/third-party/pjproject/patches/0021-Update-for-Authorization-header-handling.patch index c575da1a467c1b85fca61349eeecbaca4b22801b..286675b2ef1980050eaf21e8d439bf51f636092f 100644 --- a/third-party/pjproject/patches/0021-Update-for-Authorization-header-handling.patch +++ b/third-party/pjproject/patches/0021-Update-for-Authorization-header-handling.patch @@ -1,131 +1,64 @@ -From 850281ea4ce63fe71434613916223190ae5522b9 Mon Sep 17 00:00:00 2001 +From d0879fbe17d213a267b6015b85f5810fb34ec78b Mon Sep 17 00:00:00 2001 From: "wenpeng.song" <wenpeng.song@iopsys.eu> -Date: Tue, 29 Apr 2025 11:02:24 +0200 -Subject: [PATCH] Update for Authorization header handling. +Date: Wed, 14 May 2025 15:14:16 +0200 +Subject: [PATCH] Update for Authorization header handling - * Add support for next nonce, and cached Authorization header. - * Call callback to let Asterisk control the REGISTER challenge handling. - * Add support for resend the cached Authorization header for INVITE. + * Call callback to let Asterisk control the REGISTER challenge handling + * Add wrapper api for find_cached_auth, auth_find_cred --- - pjsip/include/pjsip-ua/sip_regc.h | 12 ++++++ - pjsip/include/pjsip/sip_auth.h | 37 +++++++++++++++++++ - pjsip/src/pjsip-ua/sip_reg.c | 42 +++++++++++++++++---- - pjsip/src/pjsip/sip_auth_client.c | 61 +++++++++++++++++++++++++++++++ - 4 files changed, 144 insertions(+), 8 deletions(-) + build.symbian/pjsipU.def | 2 ++ + pjsip/include/pjsip/sip_auth.h | 8 ++++++++ + pjsip/src/pjsip-ua/sip_reg.c | 22 ++++++++++++---------- + pjsip/src/pjsip/sip_auth_client.c | 12 ++++++++++++ + 4 files changed, 34 insertions(+), 10 deletions(-) -diff --git a/pjsip/include/pjsip-ua/sip_regc.h b/pjsip/include/pjsip-ua/sip_regc.h -index df97a6011..53ba89abd 100644 ---- a/pjsip/include/pjsip-ua/sip_regc.h -+++ b/pjsip/include/pjsip-ua/sip_regc.h -@@ -312,6 +312,18 @@ PJ_DECL(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc, - int count, - const pjsip_cred_info cred[] ); - -+PJ_DECL(pj_status_t) pjsip_regc_update_nextnonce( pjsip_regc *regc, -+ const pj_str_t *nextnonce, -+ const pj_str_t *realm ); -+ -+PJ_DECL(pj_status_t) pjsip_regc_update_cnonce( pjsip_regc *regc, -+ const pj_str_t *cnonce, -+ const pj_str_t *realm ); -+ -+PJ_DECL(pj_status_t) pjsip_regc_add_cached_auth_for_inv( pjsip_tx_data *tdata, -+ pjsip_regc *regc, -+ const pj_str_t *realm ); -+ - /** - * Set authentication preference. - * +diff --git a/build.symbian/pjsipU.def b/build.symbian/pjsipU.def +index 68d31eba3..5490d1e33 100644 +--- a/build.symbian/pjsipU.def ++++ b/build.symbian/pjsipU.def +@@ -278,3 +278,5 @@ EXPORTS + pjsip_warning_hdr_create @ 277 NONAME + pjsip_warning_hdr_create_from_status @ 278 NONAME + pjsip_www_authenticate_hdr_create @ 279 NONAME ++ pjsip_auth_find_cached_auth @ 280 NONAME ++ pjsip_auth_find_cred @ 281 NONAME diff --git a/pjsip/include/pjsip/sip_auth.h b/pjsip/include/pjsip/sip_auth.h -index fa55830fd..0af4ec972 100644 +index fa55830fd..02a8a7cde 100644 --- a/pjsip/include/pjsip/sip_auth.h +++ b/pjsip/include/pjsip/sip_auth.h -@@ -342,7 +342,44 @@ PJ_DECL(pj_status_t) pjsip_auth_clt_init( pjsip_auth_clt_sess *sess, - pj_pool_t *pool, - unsigned options); +@@ -628,6 +628,14 @@ PJ_DEF(pj_status_t) pjsip_auth_create_digestSHA256(pj_str_t* result, + * @} + */ -+/** -+ * Update the nonce with the nextnonce received from response. -+ * -+ * @param sess The client authentication session. -+ * @param nextnonce The nextnonce received from response. -+ * @param realm The realm used for the authentication. -+ * -+ * @return PJ_SUCCESS on success. -+ */ -+PJ_DECL(pj_status_t) pjsip_auth_clt_update_cache( pjsip_auth_clt_sess *sess, -+ const pj_str_t *nextnonce, -+ const pj_str_t *realm ); ++/* Find cached authentication in the list for the specified realm. */ ++PJ_DECL(pjsip_cached_auth) *pjsip_auth_find_cached_auth( pjsip_auth_clt_sess *sess, ++ const pj_str_t *realm ); ++ ++/* Find credential to use for the specified realm and auth scheme. */ ++PJ_DECL(pjsip_cred_info*) pjsip_auth_find_cred( const pjsip_auth_clt_sess *sess, ++ const pj_str_t *realm, ++ const pj_str_t *auth_scheme); -+/** -+ * Update the cnonce with the cnonce generated from asterisk. -+ * -+ * @param sess The client authentication session. -+ * @param cnonce The cnonce generated from asterisk. -+ * @param realm The realm used for the authentication. -+ * -+ * @return PJ_SUCCESS on success. -+ */ -+PJ_DECL(pj_status_t) pjsip_auth_clt_update_cache_cnonce( pjsip_auth_clt_sess *sess, -+ const pj_str_t *cnonce, -+ const pj_str_t *realm ); -+ -+/** -+ * Use the cached auth header for subsequent invite request. -+ * -+ * @param tdata The outgoing invite request message. -+ * @param sess The client authentication session. -+ * @param realm The realm used for the authentication. -+ * -+ * @return PJ_SUCCESS on success. -+ */ -+PJ_DECL(pj_status_t) new_invite_req_with_auth( pjsip_tx_data *tdata, -+ pjsip_auth_clt_sess *sess, -+ const pj_str_t *realm ); - /** - * Deinitialize client authentication session data structure. - * + + PJ_END_DECL diff --git a/pjsip/src/pjsip-ua/sip_reg.c b/pjsip/src/pjsip-ua/sip_reg.c -index 947e9edb7..1911142ea 100644 +index 947e9edb7..6a3aad194 100644 --- a/pjsip/src/pjsip-ua/sip_reg.c +++ b/pjsip/src/pjsip-ua/sip_reg.c -@@ -434,6 +434,31 @@ PJ_DEF(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc, - return pjsip_auth_clt_set_credentials(®c->auth_sess, count, cred); - } - -+PJ_DEF(pj_status_t) pjsip_regc_update_nextnonce( pjsip_regc *regc, -+ const pj_str_t *nextnonce, -+ const pj_str_t *realm ) -+{ -+ PJ_ASSERT_RETURN(regc && nextnonce && realm, PJ_EINVAL); -+ return pjsip_auth_clt_update_cache(®c->auth_sess, nextnonce, realm); -+} -+ -+PJ_DEF(pj_status_t) pjsip_regc_update_cnonce( pjsip_regc *regc, -+ const pj_str_t *cnonce, -+ const pj_str_t *realm ) -+{ -+ PJ_ASSERT_RETURN(regc && cnonce && realm, PJ_EINVAL); -+ return pjsip_auth_clt_update_cache_cnonce(®c->auth_sess, cnonce, realm); -+} -+ -+PJ_DEF(pj_status_t) pjsip_regc_add_cached_auth_for_inv( pjsip_tx_data *tdata, -+ pjsip_regc *regc, -+ const pj_str_t *realm ) -+{ -+ PJ_ASSERT_RETURN(regc && tdata && realm, PJ_EINVAL); -+ return new_invite_req_with_auth(tdata, ®c->auth_sess, realm); -+} -+ -+ - PJ_DEF(pj_status_t) pjsip_regc_set_prefs( pjsip_regc *regc, - const pjsip_auth_clt_pref *pref) - { -@@ -1216,19 +1241,20 @@ static void regc_tsx_callback(void *token, pjsip_event *event) +@@ -1210,25 +1210,27 @@ static void regc_tsx_callback(void *token, pjsip_event *event) + pj_list_erase(chdr); + } + } +- ++ status = PJ_SUCCESS; ++/* + status = pjsip_auth_clt_reinit_req( ®c->auth_sess, + rdata, tsx->last_tx, &tdata); - +- - if (status == PJ_SUCCESS) { ++*/ +// Call callback anyway to let asterisk control the handling of the challenge +// if (status == PJ_SUCCESS) { /* Need to unlock the regc temporarily while sending the message @@ -149,7 +82,7 @@ index 947e9edb7..1911142ea 100644 /* Only call callback if application is still interested * in it. */ -@@ -1242,7 +1268,7 @@ static void regc_tsx_callback(void *token, pjsip_event *event) +@@ -1242,7 +1244,7 @@ static void regc_tsx_callback(void *token, pjsip_event *event) rdata, NOEXP, 0, NULL, is_unreg); pj_lock_acquire(regc->lock); } @@ -159,73 +92,24 @@ index 947e9edb7..1911142ea 100644 } else if (regc->_delete_flag) { diff --git a/pjsip/src/pjsip/sip_auth_client.c b/pjsip/src/pjsip/sip_auth_client.c -index ee2876d99..f8b4ff9d9 100644 +index ee2876d99..fd5c93007 100644 --- a/pjsip/src/pjsip/sip_auth_client.c +++ b/pjsip/src/pjsip/sip_auth_client.c -@@ -1528,3 +1528,64 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_reinit_req( pjsip_auth_clt_sess *sess, +@@ -1528,3 +1528,15 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_reinit_req( pjsip_auth_clt_sess *sess, } -+ -+PJ_DEF(pj_status_t) pjsip_auth_clt_update_cache( pjsip_auth_clt_sess *sess, -+ const pj_str_t *nextnonce, -+ const pj_str_t *realm ){ -+ -+ PJ_ASSERT_RETURN(sess && nextnonce && realm, PJ_EINVAL); -+ pjsip_cached_auth *cached_auth; -+ cached_auth = find_cached_auth(sess, realm); -+ if (cached_auth) { -+ pj_strdup(cached_auth->pool, &cached_auth->last_chal->challenge.digest.nonce, nextnonce); -+ cached_auth->nc=0; -+ return PJ_SUCCESS; -+ } -+ return PJ_EINVAL; -+} -+ -+PJ_DEF(pj_status_t) pjsip_auth_clt_update_cache_cnonce( pjsip_auth_clt_sess *sess, -+ const pj_str_t *cnonce, -+ const pj_str_t *realm ){ -+ -+ PJ_ASSERT_RETURN(sess && cnonce && realm, PJ_EINVAL); -+ pjsip_cached_auth *cached_auth; -+ cached_auth = find_cached_auth(sess, realm); -+ if (cached_auth) { -+ pj_strdup(cached_auth->pool, &cached_auth->cnonce, cnonce); -+ return PJ_SUCCESS; -+ } -+ return PJ_EINVAL; ++/* Find cached authentication in the list for the specified realm. */ ++PJ_DEF(pjsip_cached_auth) *pjsip_auth_find_cached_auth( pjsip_auth_clt_sess *sess, ++ const pj_str_t *realm ){ ++ return find_cached_auth(sess, realm); +} + -+PJ_DEF(pj_status_t) new_invite_req_with_auth( pjsip_tx_data *tdata, -+ pjsip_auth_clt_sess *sess, -+ const pj_str_t *realm ) -+{ -+ -+ PJ_ASSERT_RETURN(sess && tdata && realm, PJ_EINVAL); -+ pjsip_cached_auth *cached_auth; -+ cached_auth = find_cached_auth(sess, realm); -+ if (cached_auth) { -+ const pjsip_cred_info *cred; -+ pjsip_authorization_hdr *hauth; -+ pj_status_t status; -+ -+ PJ_ASSERT_RETURN(cached_auth->last_chal != NULL, PJSIP_EAUTHNOPREVCHAL); -+ -+ cred = auth_find_cred( sess, &cached_auth->realm, &cached_auth->last_chal->scheme ); -+ if (!cred) -+ return PJSIP_ENOCREDENTIAL; -+ -+ status = auth_respond( tdata->pool, cached_auth->last_chal, -+ tdata->msg->line.req.uri, -+ cred, &pjsip_register_method, -+ sess->pool, cached_auth, &hauth); -+ if (status != PJ_SUCCESS) -+ return status; -+ pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hauth); -+ return PJ_SUCCESS; -+ } -+ return PJ_EINVAL; -+ ++/* Find credential to use for the specified realm and auth scheme. */ ++PJ_DEF(pjsip_cred_info*) pjsip_auth_find_cred( const pjsip_auth_clt_sess *sess, ++ const pj_str_t *realm, ++ const pj_str_t *auth_scheme){ ++ return auth_find_cred(sess, realm, auth_scheme); +} -- 2.43.0