From c8d8456d4e7bb1ca436456e6bc5f2d94f08d1f60 Mon Sep 17 00:00:00 2001 From: Wenpeng Song <wenpeng.song@iopsys.eu> Date: Wed, 30 Apr 2025 09:48:55 +0200 Subject: [PATCH] Update for Authorization header handling, REF 14600 - Correct challenge handling for REGISTER - Add security_verify to subsequent REGISTER requests with cached authorization - Add support to resend the cached Authorization header for INVITE After properly set the initial registration outbound authentication credentials with e28a65c8555e206177265208a99d3ca0a6613d76, the 401/407 challenge was handled by libpjsip and resend directly without noticing asterisk, which skipped all the process that is expected to have within asterisk. This fix keeps the challenge handling that made under libpjsip (to make the cached auth work) but avoid to resend directly and invoke the callback function regardless the status of the challenge handling. --- include/asterisk/res_pjsip.h | 1 + .../res_pjsip_outbound_registration.h | 1 + res/res_pjsip_outbound_registration.c | 83 ++++++- ...res_pjsip_outbound_registration.exports.in | 1 + res/res_pjsip_session.c | 5 + ...te-for-Authorization-header-handling.patch | 233 ++++++++++++++++++ .../patches/0021-update-next-nonce.patch | 106 -------- 7 files changed, 323 insertions(+), 107 deletions(-) create mode 100644 third-party/pjproject/patches/0021-Update-for-Authorization-header-handling.patch delete mode 100644 third-party/pjproject/patches/0021-update-next-nonce.patch diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h index e77c4568d5..9ed32e2c9f 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 0abc4aa3d5..b371b7dda3 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 8e5d01a41a..abfcdddec6 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 bab765f772..be75cc4300 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 915b2c046a..e35701f8a1 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 0000000000..c575da1a46 --- /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 0ff1e9ca20..0000000000 --- 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 - - -- GitLab