diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h index e77c4568d52e832e7629fc7e7d45f8483ec9411d..9ed32e2c9fb9605e77ff75a50bd9589bb4797bc0 100644 --- a/include/asterisk/res_pjsip.h +++ b/include/asterisk/res_pjsip.h @@ -1106,6 +1106,7 @@ struct ast_sip_endpoint { char *register_transport; unsigned int max_sessions; char *realms; + bool cached_auth; }; struct pjsip_register_dest { diff --git a/include/asterisk/res_pjsip_outbound_registration.h b/include/asterisk/res_pjsip_outbound_registration.h index 0abc4aa3d58e8cdb816441036ad9980ca779579a..b371b7dda3c074bd2f57b0d13e6e542fd7d95f3e 100644 --- a/include/asterisk/res_pjsip_outbound_registration.h +++ b/include/asterisk/res_pjsip_outbound_registration.h @@ -3,5 +3,6 @@ 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); #endif /* _RES_PJSIP_OUTBOUND_REGISTRATION_H */ diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c index 8e5d01a41a41c5d82d95759d6786461def588afe..abfcdddec601dd7b75ad39654d5874adb45dce06 100644 --- a/res/res_pjsip_outbound_registration.c +++ b/res/res_pjsip_outbound_registration.c @@ -499,6 +499,8 @@ struct sip_outbound_registration_client_state { unsigned int destroy:1; /*! \brief Non-zero if we have attempted sending a REGISTER with authentication */ unsigned int auth_attempted:1; + /*! \brief Non-zero if we have sent a REGISTER successful with security_verify */ + unsigned int cached_security_verify:1; /*! \brief Status code of last response if we have tried to register before */ int last_status_code; /*! \brief The name of the transport to be used for the registration */ @@ -513,6 +515,7 @@ 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) */ @@ -758,9 +761,15 @@ static void add_security_headers(struct sip_outbound_registration_client_state * if (!contact_status && AST_VECTOR_SIZE(&client_state->server_security_mechanisms)) { sec_mechs = &client_state->server_security_mechanisms; } - if (client_state->status == SIP_REGISTRATION_REJECTED_TEMPORARY || client_state->auth_attempted) { + if (client_state->status == SIP_REGISTRATION_REJECTED_TEMPORARY || client_state->auth_attempted || client_state->cached_security_verify ) { if (sec_mechs != NULL && pjsip_msg_find_hdr_by_name(tdata->msg, &security_verify, NULL) == NULL) { ast_sip_add_security_headers(sec_mechs, "Security-Verify", 0, tdata); + } else if ((endpt = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", client_state->registration_name)) && !AST_LIST_EMPTY(&endpt->secur_mechanisms) && pjsip_msg_find_hdr_by_name(tdata->msg, &security_verify, NULL) == NULL) { + struct security_mechanism *sec_mechanism; + AST_LIST_TRAVERSE(&endpt->secur_mechanisms, sec_mechanism, entry) { + ast_debug(3, "Adding security header: %s\n", sec_mechanism->value); + ast_sip_add_header(tdata,"Security-Verify",sec_mechanism->value); + } } if (client_state->last_status_code == 494) { ast_sip_remove_headers_by_name_and_value(tdata->msg, &security_client, NULL); @@ -1918,12 +1927,51 @@ 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", @@ -1968,10 +2016,26 @@ static int handle_registration_response(void *data) } client_state->auth_attempted = 0; + client_state->cached_security_verify = 0; if (PJSIP_IS_STATUS_IN_CLASS(response->code, 200)) { struct ast_sip_endpoint *endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", client_state->registration_name); + static const pj_str_t security_verify = { "Security-Verify", 15 }; + if(response->old_request && pjsip_msg_find_hdr(response->old_request->msg, PJSIP_H_AUTHORIZATION, NULL)){ + if(endpoint){ + endpoint->cached_auth = true; + } + ast_debug(8, "Outbound registration request success with Authorization.\n"); + if(pjsip_msg_find_hdr_by_name(response->old_request->msg, &security_verify, NULL)){ + client_state->cached_security_verify = 1; + ast_debug(8, "Outbound registration request success with Security-Verify.\n"); + } + } else { + if(endpoint){ + endpoint->cached_auth = false; + } + } /* Check if this is in regards to registering or unregistering * Retrieve the requested expiration if possible, * and check it combined with the received expiration to identify if it is reg or un-reg @@ -2088,6 +2152,7 @@ static int handle_registration_response(void *data) 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); } } } @@ -3253,6 +3318,22 @@ 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) +{ + 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); + } + } +} + void queue_registration_recovery_flow(const char *registration_name) { struct sip_outbound_registration_state *state; diff --git a/res/res_pjsip_outbound_registration.exports.in b/res/res_pjsip_outbound_registration.exports.in index bab765f772021eee504e56d08ddf432043defd3d..be75cc4300aa55afee2fc2fbc9559f7d60a0e764 100644 --- a/res/res_pjsip_outbound_registration.exports.in +++ b/res/res_pjsip_outbound_registration.exports.in @@ -2,6 +2,7 @@ global: LINKER_SYMBOL_PREFIXqueue_registration_recovery_flow; LINKER_SYMBOL_PREFIXupdate_emergency_registration_ongoing_status; + LINKER_SYMBOL_PREFIXadd_cached_auth_header_from_reg; local: *; }; diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c index 915b2c046a8c9d88dcff83e206e64dc679dcde2b..e35701f8a13da7718f29fa1502c0dd54bbf37663 100644 --- a/res/res_pjsip_session.c +++ b/res/res_pjsip_session.c @@ -2964,6 +2964,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)); + } + content_header = pjsip_msg_find_hdr_by_name((*tdata)->msg, &headerName, NULL); /*If content dispostion is present ,P-Early-Media header header is not added */ if (content_header) 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 new file mode 100644 index 0000000000000000000000000000000000000000..c575da1a467c1b85fca61349eeecbaca4b22801b --- /dev/null +++ b/third-party/pjproject/patches/0021-Update-for-Authorization-header-handling.patch @@ -0,0 +1,233 @@ +From 850281ea4ce63fe71434613916223190ae5522b9 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. + + * 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. +--- + 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(-) + +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/pjsip/include/pjsip/sip_auth.h b/pjsip/include/pjsip/sip_auth.h +index fa55830fd..0af4ec972 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); + ++/** ++ * 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 ); + ++/** ++ * 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. + * +diff --git a/pjsip/src/pjsip-ua/sip_reg.c b/pjsip/src/pjsip-ua/sip_reg.c +index 947e9edb7..1911142ea 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) + 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 + * to prevent deadlock (see ticket #2260 and #1247). + * It should be safe to do this since the regc's refcount has been + * incremented. + */ +- pj_lock_release(regc->lock); +- status = pjsip_regc_send(regc, tdata); +- pj_lock_acquire(regc->lock); +- } ++// pj_lock_release(regc->lock); ++// status = pjsip_regc_send(regc, tdata); ++// pj_lock_acquire(regc->lock); ++// } + +- if (status != PJ_SUCCESS) { +- ++// if (status != PJ_SUCCESS) { ++// + /* Only call callback if application is still interested + * in it. + */ +@@ -1242,7 +1268,7 @@ static void regc_tsx_callback(void *token, pjsip_event *event) + rdata, NOEXP, 0, NULL, is_unreg); + pj_lock_acquire(regc->lock); + } +- } ++// } + + } 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 +--- 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, + + } + ++ ++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; ++} ++ ++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; ++ ++} +-- +2.43.0 + + diff --git a/third-party/pjproject/patches/0021-update-next-nonce.patch b/third-party/pjproject/patches/0021-update-next-nonce.patch deleted file mode 100644 index 0ff1e9ca2048302ea715bcbaac5db6d8b43617a2..0000000000000000000000000000000000000000 --- a/third-party/pjproject/patches/0021-update-next-nonce.patch +++ /dev/null @@ -1,106 +0,0 @@ -From e5f39a2faca1155d029d0c922df40b15e9fe3b2c Mon Sep 17 00:00:00 2001 -From: "wenpeng.song" <wenpeng.song@iopsys.eu> -Date: Tue, 15 Apr 2025 09:58:29 +0200 -Subject: [PATCH] update next nonce - ---- - build.symbian/pjsip_uaU.def | 1 + - pjsip/include/pjsip-ua/sip_regc.h | 4 ++++ - pjsip/include/pjsip/sip_auth.h | 12 ++++++++++++ - pjsip/src/pjsip-ua/sip_reg.c | 8 ++++++++ - pjsip/src/pjsip/sip_auth_client.c | 15 +++++++++++++++ - 5 files changed, 40 insertions(+) - -diff --git a/build.symbian/pjsip_uaU.def b/build.symbian/pjsip_uaU.def -index 0c847a994..c9208a198 100644 ---- a/build.symbian/pjsip_uaU.def -+++ b/build.symbian/pjsip_uaU.def -@@ -56,3 +56,4 @@ EXPORTS - pjsip_xfer_initiate @ 55 NONAME - pjsip_xfer_notify @ 56 NONAME - pjsip_xfer_send_request @ 57 NONAME -+ pjsip_regc_update_nextnonce @ 58 NONAME -diff --git a/pjsip/include/pjsip-ua/sip_regc.h b/pjsip/include/pjsip-ua/sip_regc.h -index df97a6011..e4f825baa 100644 ---- a/pjsip/include/pjsip-ua/sip_regc.h -+++ b/pjsip/include/pjsip-ua/sip_regc.h -@@ -312,6 +312,10 @@ 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 ); -+ - /** - * Set authentication preference. - * -diff --git a/pjsip/include/pjsip/sip_auth.h b/pjsip/include/pjsip/sip_auth.h -index fa55830fd..d9d8e0e04 100644 ---- a/pjsip/include/pjsip/sip_auth.h -+++ b/pjsip/include/pjsip/sip_auth.h -@@ -342,6 +342,18 @@ PJ_DECL(pj_status_t) pjsip_auth_clt_init( pjsip_auth_clt_sess *sess, - pj_pool_t *pool, - unsigned options); - -+/** -+ * 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 ); - - /** - * Deinitialize client authentication session data structure. -diff --git a/pjsip/src/pjsip-ua/sip_reg.c b/pjsip/src/pjsip-ua/sip_reg.c -index 947e9edb7..d5ff8020b 100644 ---- a/pjsip/src/pjsip-ua/sip_reg.c -+++ b/pjsip/src/pjsip-ua/sip_reg.c -@@ -434,6 +434,14 @@ 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_set_prefs( pjsip_regc *regc, - const pjsip_auth_clt_pref *pref) - { -diff --git a/pjsip/src/pjsip/sip_auth_client.c b/pjsip/src/pjsip/sip_auth_client.c -index ee2876d99..e31156d97 100644 ---- a/pjsip/src/pjsip/sip_auth_client.c -+++ b/pjsip/src/pjsip/sip_auth_client.c -@@ -1528,3 +1528,18 @@ 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; -+} --- -2.43.0 - -