--- /dev/null
+commit 523ed150b16d799bcf223b841abd82f25b8cd6a0
+Author: Kevin Harwell <kharwell@digium.com>
+Date: Mon Oct 19 17:21:57 2020 -0500
+
+ AST-2020-001 - res_pjsip: Return dialog locked and referenced
+
+ pjproject returns the dialog locked and with a reference. However,
+ in Asterisk the method that handles this decrements the reference
+ and removes the lock prior to returning. This makes it possible,
+ under some circumstances, for another thread to free said dialog
+ before the thread that created it attempts to use it again. Of
+ course when the thread that created it tries to use a freed dialog
+ a crash can occur.
+
+ This patch makes it so Asterisk now returns the newly created
+ dialog both locked, and with an added reference. This allows the
+ caller to de-reference, and unlock the dialog when it is safe to
+ do so.
+
+ In the case of a new SIP Invite the lock, and reference are now
+ held for the entirety of the new invite handling process.
+ Otherwise it's possible for the dialog, or its dependent objects,
+ like the transaction, to disappear. For example if there is a TCP
+ transport error.
+
+ Change-Id: I5ef645a47829596f402cf383dc02c629c618969e
+
+--- a/include/asterisk/res_pjsip.h
++++ b/include/asterisk/res_pjsip.h
+@@ -1908,6 +1908,11 @@ pjsip_dialog *ast_sip_create_dialog_uac(
+ /*!
+ * \brief General purpose method for creating a UAS dialog with an endpoint
+ *
++ * \deprecated This function is unsafe (due to the returned object not being locked nor
++ * having its reference incremented) and should no longer be used. Instead
++ * use ast_sip_create_dialog_uas_locked so a properly locked and referenced
++ * object is returned.
++ *
+ * \param endpoint A pointer to the endpoint
+ * \param rdata The request that is starting the dialog
+ * \param[out] status On failure, the reason for failure in creating the dialog
+@@ -1915,6 +1920,44 @@ pjsip_dialog *ast_sip_create_dialog_uac(
+ pjsip_dialog *ast_sip_create_dialog_uas(const struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, pj_status_t *status);
+
+ /*!
++ * \brief General purpose method for creating a UAS dialog with an endpoint
++ *
++ * This function creates and returns a locked, and referenced counted pjsip
++ * dialog object. The caller is thus responsible for freeing the allocated
++ * memory, decrementing the reference, and releasing the lock when done with
++ * the returned object.
++ *
++ * \note The safest way to unlock the object, and decrement its reference is by
++ * calling pjsip_dlg_dec_lock. Alternatively, pjsip_dlg_dec_session can be
++ * used to decrement the reference only.
++ *
++ * The dialog is returned locked and with a reference in order to ensure that the
++ * dialog object, and any of its associated objects (e.g. transaction) are not
++ * untimely destroyed. For instance, that could happen when a transport error
++ * occurs.
++ *
++ * As long as the caller maintains a reference to the dialog there should be no
++ * worry that it might unknowningly be destroyed. However, once the caller unlocks
++ * the dialog there is a danger that some of the dialog's internal objects could
++ * be lost and/or compromised. For example, when the aforementioned transport error
++ * occurs the dialog's associated transaction gets destroyed (see pjsip_dlg_on_tsx_state
++ * in sip_dialog.c, and mod_inv_on_tsx_state in sip_inv.c).
++ *
++ * In this case and before using the dialog again the caller should re-lock the
++ * dialog, check to make sure the dialog is still established, and the transaction
++ * still exists and has not been destroyed.
++ *
++ * \param endpoint A pointer to the endpoint
++ * \param rdata The request that is starting the dialog
++ * \param[out] status On failure, the reason for failure in creating the dialog
++ *
++ * \retval A locked, and reference counted pjsip_dialog object.
++ * \retval NULL on failure
++ */
++pjsip_dialog *ast_sip_create_dialog_uas_locked(const struct ast_sip_endpoint *endpoint,
++ pjsip_rx_data *rdata, pj_status_t *status);
++
++/*!
+ * \brief General purpose method for creating an rdata structure using specific information
+ * \since 13.15.0
+ *
+--- a/res/res_pjsip.c
++++ b/res/res_pjsip.c
+@@ -3645,7 +3645,11 @@ static int uas_use_sips_contact(pjsip_rx
+ return 0;
+ }
+
+-pjsip_dialog *ast_sip_create_dialog_uas(const struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, pj_status_t *status)
++typedef pj_status_t (*create_dlg_uac)(pjsip_user_agent *ua, pjsip_rx_data *rdata,
++ const pj_str_t *contact, pjsip_dialog **p_dlg);
++
++static pjsip_dialog *create_dialog_uas(const struct ast_sip_endpoint *endpoint,
++ pjsip_rx_data *rdata, pj_status_t *status, create_dlg_uac create_fun)
+ {
+ pjsip_dialog *dlg;
+ pj_str_t contact;
+@@ -3680,11 +3684,7 @@ pjsip_dialog *ast_sip_create_dialog_uas(
+ (type != PJSIP_TRANSPORT_UDP && type != PJSIP_TRANSPORT_UDP6) ? ";transport=" : "",
+ (type != PJSIP_TRANSPORT_UDP && type != PJSIP_TRANSPORT_UDP6) ? pjsip_transport_get_type_name(type) : "");
+
+-#ifdef HAVE_PJSIP_DLG_CREATE_UAS_AND_INC_LOCK
+- *status = pjsip_dlg_create_uas_and_inc_lock(pjsip_ua_instance(), rdata, &contact, &dlg);
+-#else
+- *status = pjsip_dlg_create_uas(pjsip_ua_instance(), rdata, &contact, &dlg);
+-#endif
++ *status = create_fun(pjsip_ua_instance(), rdata, &contact, &dlg);
+ if (*status != PJ_SUCCESS) {
+ char err[PJ_ERR_MSG_SIZE];
+
+@@ -3697,11 +3697,46 @@ pjsip_dialog *ast_sip_create_dialog_uas(
+ dlg->sess_count++;
+ pjsip_dlg_set_transport(dlg, &selector);
+ dlg->sess_count--;
++
++ return dlg;
++}
++
++pjsip_dialog *ast_sip_create_dialog_uas(const struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, pj_status_t *status)
++{
+ #ifdef HAVE_PJSIP_DLG_CREATE_UAS_AND_INC_LOCK
+- pjsip_dlg_dec_lock(dlg);
++ pjsip_dialog *dlg;
++
++ dlg = create_dialog_uas(endpoint, rdata, status, pjsip_dlg_create_uas_and_inc_lock);
++ if (dlg) {
++ pjsip_dlg_dec_lock(dlg);
++ }
++
++ return dlg;
++#else
++ return create_dialog_uas(endpoint, rdata, status, pjsip_dlg_create_uas);
+ #endif
++}
++
++pjsip_dialog *ast_sip_create_dialog_uas_locked(const struct ast_sip_endpoint *endpoint,
++ pjsip_rx_data *rdata, pj_status_t *status)
++{
++#ifdef HAVE_PJSIP_DLG_CREATE_UAS_AND_INC_LOCK
++ return create_dialog_uas(endpoint, rdata, status, pjsip_dlg_create_uas_and_inc_lock);
++#else
++ /*
++ * This is put here in order to be compatible with older versions of pjproject.
++ * Best we can do in this case is immediately lock after getting the dialog.
++ * However, that does leave a "gap" between creating and locking.
++ */
++ pjsip_dialog *dlg;
++
++ dlg = create_dialog_uas(endpoint, rdata, status, pjsip_dlg_create_uas);
++ if (dlg) {
++ pjsip_dlg_inc_lock(dlg);
++ }
+
+ return dlg;
++#endif
+ }
+
+ int ast_sip_create_rdata_with_contact(pjsip_rx_data *rdata, char *packet, const char *src_name, int src_port,
+--- a/res/res_pjsip_pubsub.c
++++ b/res/res_pjsip_pubsub.c
+@@ -1457,7 +1457,7 @@ static struct sip_subscription_tree *cre
+ }
+ sub_tree->role = AST_SIP_NOTIFIER;
+
+- dlg = ast_sip_create_dialog_uas(endpoint, rdata, dlg_status);
++ dlg = ast_sip_create_dialog_uas_locked(endpoint, rdata, dlg_status);
+ if (!dlg) {
+ if (*dlg_status != PJ_EEXISTS) {
+ ast_log(LOG_WARNING, "Unable to create dialog for SIP subscription\n");
+@@ -1478,8 +1478,16 @@ static struct sip_subscription_tree *cre
+ }
+
+ pjsip_evsub_create_uas(dlg, &pubsub_cb, rdata, 0, &sub_tree->evsub);
++
+ subscription_setup_dialog(sub_tree, dlg);
+
++ /*
++ * The evsub and subscription setup both add dialog refs, so the dialog ref that
++ * was added when the dialog was created (see ast_sip_create_dialog_uas_lock) can
++ * now be removed. The lock should no longer be needed so can be removed too.
++ */
++ pjsip_dlg_dec_lock(dlg);
++
+ #ifdef HAVE_PJSIP_EVSUB_GRP_LOCK
+ pjsip_evsub_add_ref(sub_tree->evsub);
+ #endif
+--- a/res/res_pjsip_session.c
++++ b/res/res_pjsip_session.c
+@@ -2942,6 +2942,75 @@ static enum sip_get_destination_result g
+ return SIP_GET_DEST_EXTEN_NOT_FOUND;
+ }
+
++/*
++ * /internal
++ * /brief Process initial answer for an incoming invite
++ *
++ * This function should only be called during the setup, and handling of a
++ * new incoming invite. Most, if not all of the time, this will be called
++ * when an error occurs and we need to respond as such.
++ *
++ * When a SIP session termination code is given for the answer it's assumed
++ * this call then will be the final bit of processing before ending session
++ * setup. As such, we've been holding a lock, and a reference on the invite
++ * session's dialog. So before returning this function removes that reference,
++ * and unlocks the dialog.
++ *
++ * \param inv_session The session on which to answer
++ * \param rdata The original request
++ * \param answer_code The answer's numeric code
++ * \param terminate_code The termination code if the answer fails
++ * \param notify Whether or not to call on_state_changed
++ *
++ * \retval 0 if invite successfully answered, -1 if an error occurred
++ */
++static int new_invite_initial_answer(pjsip_inv_session *inv_session, pjsip_rx_data *rdata,
++ int answer_code, int terminate_code, pj_bool_t notify)
++{
++ pjsip_tx_data *tdata = NULL;
++ int res = 0;
++
++ if (inv_session->state != PJSIP_INV_STATE_DISCONNECTED) {
++ if (pjsip_inv_initial_answer(
++ inv_session, rdata, answer_code, NULL, NULL, &tdata) != PJ_SUCCESS) {
++
++ pjsip_inv_terminate(inv_session, terminate_code ? terminate_code : answer_code, notify);
++ res = -1;
++ } else {
++ pjsip_inv_send_msg(inv_session, tdata);
++ }
++ }
++
++ if (answer_code >= 300) {
++ /*
++ * A session is ending. The dialog has a reference that needs to be
++ * removed and holds a lock that needs to be unlocked before returning.
++ */
++ pjsip_dlg_dec_lock(inv_session->dlg);
++ }
++
++ return res;
++}
++
++/*
++ * /internal
++ * /brief Create and initialize a pjsip invite session
++
++ * pjsip_inv_session adds, and maintains a reference to the dialog upon a successful
++ * invite session creation until the session is destroyed. However, we'll wait to
++ * remove the reference that was added for the dialog when it gets created since we're
++ * not ready to unlock the dialog in this function.
++ *
++ * So, if this function successfully returns that means it returns with its newly
++ * created, and associated dialog locked and with two references (i.e. dialog's
++ * reference count should be 2).
++ *
++ * \param endpoint A pointer to the endpoint
++ * \param rdata The request that is starting the dialog
++ *
++ * \retval A pjsip invite session object
++ * \retval NULL on error
++ */
+ static pjsip_inv_session *pre_session_setup(pjsip_rx_data *rdata, const struct ast_sip_endpoint *endpoint)
+ {
+ pjsip_tx_data *tdata;
+@@ -2960,15 +3029,28 @@ static pjsip_inv_session *pre_session_se
+ }
+ return NULL;
+ }
+- dlg = ast_sip_create_dialog_uas(endpoint, rdata, &dlg_status);
++
++ dlg = ast_sip_create_dialog_uas_locked(endpoint, rdata, &dlg_status);
+ if (!dlg) {
+ if (dlg_status != PJ_EEXISTS) {
+ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
+ }
+ return NULL;
+ }
++
++ /*
++ * The returned dialog holds a lock and has a reference added. Any paths where the
++ * dialog invite session is not returned must unlock the dialog and remove its reference.
++ */
++
+ if (pjsip_inv_create_uas(dlg, rdata, NULL, options, &inv_session) != PJ_SUCCESS) {
+ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
++ /*
++ * The acquired dialog holds a lock, and a reference. Since the dialog is not
++ * going to be returned here it must first be unlocked and de-referenced. This
++ * must be done prior to calling dialog termination.
++ */
++ pjsip_dlg_dec_lock(dlg);
+ pjsip_dlg_terminate(dlg);
+ return NULL;
+ }
+@@ -2977,12 +3059,13 @@ static pjsip_inv_session *pre_session_se
+ inv_session->sdp_neg_flags = PJMEDIA_SDP_NEG_ALLOW_MEDIA_CHANGE;
+ #endif
+ if (pjsip_dlg_add_usage(dlg, &session_module, NULL) != PJ_SUCCESS) {
+- if (pjsip_inv_initial_answer(inv_session, rdata, 500, NULL, NULL, &tdata) != PJ_SUCCESS) {
+- pjsip_inv_terminate(inv_session, 500, PJ_FALSE);
+- }
+- pjsip_inv_send_msg(inv_session, tdata);
++ /* Dialog's lock and a reference are removed in new_invite_initial_answer */
++ new_invite_initial_answer(inv_session, rdata, 500, 500, PJ_FALSE);
++ /* Remove 2nd reference added at inv_session creation */
++ pjsip_dlg_dec_session(inv_session->dlg, &session_module);
+ return NULL;
+ }
++
+ return inv_session;
+ }
+
+@@ -3121,7 +3204,6 @@ static void handle_new_invite_request(pj
+ {
+ RAII_VAR(struct ast_sip_endpoint *, endpoint,
+ ast_pjsip_rdata_get_endpoint(rdata), ao2_cleanup);
+- pjsip_tx_data *tdata = NULL;
+ pjsip_inv_session *inv_session = NULL;
+ struct ast_sip_session *session;
+ struct new_invite invite;
+@@ -3134,27 +3216,48 @@ static void handle_new_invite_request(pj
+ return;
+ }
+
++ /*
++ * Upon a successful pre_session_setup the associated dialog is returned locked
++ * and with an added reference. Well actually two references. One added when the
++ * dialog itself was created, and another added when the pjsip invite session was
++ * created and the dialog was added to it.
++ *
++ * In order to ensure the dialog's, and any of its internal attributes, lifetimes
++ * we'll hold the lock and maintain the reference throughout the entire new invite
++ * handling process. See ast_sip_create_dialog_uas_locked for more details but,
++ * basically we do this to make sure a transport failure does not destroy the dialog
++ * and/or transaction out from underneath us between pjsip calls. Alternatively, we
++ * could probably release the lock if we needed to, but then we'd have to re-lock and
++ * check the dialog and transaction prior to every pjsip call.
++ *
++ * That means any off nominal/failure paths in this function must remove the associated
++ * dialog reference added at dialog creation, and remove the lock. As well the
++ * referenced pjsip invite session must be "cleaned up", which should also then
++ * remove its reference to the dialog at that time.
++ *
++ * Nominally we'll unlock the dialog, and release the reference when all new invite
++ * process handling has successfully completed.
++ */
++
+ #ifdef HAVE_PJSIP_INV_SESSION_REF
+ if (pjsip_inv_add_ref(inv_session) != PJ_SUCCESS) {
+ ast_log(LOG_ERROR, "Can't increase the session reference counter\n");
+- if (inv_session->state != PJSIP_INV_STATE_DISCONNECTED) {
+- if (pjsip_inv_initial_answer(inv_session, rdata, 500, NULL, NULL, &tdata) == PJ_SUCCESS) {
+- pjsip_inv_terminate(inv_session, 500, PJ_FALSE);
+- } else {
+- pjsip_inv_send_msg(inv_session, tdata);
+- }
++ /* Dialog's lock and a reference are removed in new_invite_initial_answer */
++ if (!new_invite_initial_answer(inv_session, rdata, 500, 500, PJ_FALSE)) {
++ /* Terminate the session if it wasn't done in the answer */
++ pjsip_inv_terminate(inv_session, 500, PJ_FALSE);
+ }
+ return;
+ }
+ #endif
+-
+ session = ast_sip_session_alloc(endpoint, NULL, inv_session, rdata);
+ if (!session) {
+- if (pjsip_inv_initial_answer(inv_session, rdata, 500, NULL, NULL, &tdata) == PJ_SUCCESS) {
++ /* Dialog's lock and reference are removed in new_invite_initial_answer */
++ if (!new_invite_initial_answer(inv_session, rdata, 500, 500, PJ_FALSE)) {
++ /* Terminate the session if it wasn't done in the answer */
+ pjsip_inv_terminate(inv_session, 500, PJ_FALSE);
+- } else {
+- pjsip_inv_send_msg(inv_session, tdata);
+ }
++
+ #ifdef HAVE_PJSIP_INV_SESSION_REF
+ pjsip_inv_dec_ref(inv_session);
+ #endif
+@@ -3172,6 +3275,17 @@ static void handle_new_invite_request(pj
+ invite.rdata = rdata;
+ new_invite(&invite);
+
++ /*
++ * The dialog lock and reference added at dialog creation time must be
++ * maintained throughout the new invite process. Since we're pretty much
++ * done at this point with things it's safe to go ahead and remove the lock
++ * and the reference here. See ast_sip_create_dialog_uas_locked for more info.
++ *
++ * Note, any future functionality added that does work using the dialog must
++ * be done before this.
++ */
++ pjsip_dlg_dec_lock(inv_session->dlg);
++
+ ao2_ref(session, -1);
+ }
+
--- /dev/null
+From f83efa12f7411dd100f49933bf71297c8ed9f765 Mon Sep 17 00:00:00 2001
+From: Ben Ford <bford@digium.com>
+Date: Mon, 02 Nov 2020 10:29:31 -0600
+Subject: [PATCH] AST-2020-002 - res_pjsip: Stop sending INVITEs after challenge limit.
+
+If Asterisk sends out an INVITE and receives a challenge with a
+different nonce value each time, it will continuously send out INVITEs,
+even if the call is hung up. The endpoint must be configured for
+outbound authentication for this to occur. A limit has been set on
+outbound INVITEs so that, once reached, Asterisk will stop sending
+INVITEs and the transaction will terminate.
+
+ASTERISK-29013
+
+Change-Id: I2d001ca745b00ca8aa12030f2240cd72363b46f7
+---
+
+--- a/include/asterisk/res_pjsip.h
++++ b/include/asterisk/res_pjsip.h
+@@ -63,6 +63,9 @@ struct pjsip_tpselector;
+ /*! \brief Maximum number of ciphers supported for a TLS transport */
+ #define SIP_TLS_MAX_CIPHERS 64
+
++/*! Maximum number of challenges before assuming that we are in a loop */
++#define MAX_RX_CHALLENGES 10
++
+ /*!
+ * \brief Structure for SIP transport information
+ */
+--- a/include/asterisk/res_pjsip_session.h
++++ b/include/asterisk/res_pjsip_session.h
+@@ -215,8 +215,10 @@ struct ast_sip_session {
+ enum ast_sip_dtmf_mode dtmf;
+ /*! Initial incoming INVITE Request-URI. NULL otherwise. */
+ pjsip_uri *request_uri;
+- /* Media statistics for negotiated RTP streams */
++ /*! Media statistics for negotiated RTP streams */
+ AST_VECTOR(, struct ast_rtp_instance_stats *) media_stats;
++ /*! Number of challenges received during outgoing requests to determine if we are in a loop */
++ unsigned int authentication_challenge_count:4;
+ };
+
+ typedef int (*ast_sip_session_request_creation_cb)(struct ast_sip_session *session, pjsip_tx_data *tdata);
+--- a/res/res_pjsip.c
++++ b/res/res_pjsip.c
+@@ -4027,8 +4027,6 @@ static pj_bool_t does_method_match(const
+ return pj_stristr(&method, message_method) ? PJ_TRUE : PJ_FALSE;
+ }
+
+-/*! Maximum number of challenges before assuming that we are in a loop */
+-#define MAX_RX_CHALLENGES 10
+ #define TIMER_INACTIVE 0
+ #define TIMEOUT_TIMER2 5
+
+--- a/res/res_pjsip_session.c
++++ b/res/res_pjsip_session.c
+@@ -2052,7 +2052,6 @@ static pjsip_module session_reinvite_mod
+ .on_rx_request = session_reinvite_on_rx_request,
+ };
+
+-
+ void ast_sip_session_send_request_with_cb(struct ast_sip_session *session, pjsip_tx_data *tdata,
+ ast_sip_session_response_cb on_response)
+ {
+@@ -2301,6 +2300,9 @@ struct ast_sip_session *ast_sip_session_
+ return NULL;
+ }
+
++ /* Track the number of challenges received on outbound requests */
++ session->authentication_challenge_count = 0;
++
+ /* Fire seesion begin handlers */
+ handle_session_begin(session);
+
+@@ -2470,6 +2472,11 @@ static pj_bool_t outbound_invite_auth(pj
+
+ session = inv->mod_data[session_module.id];
+
++ if (++session->authentication_challenge_count > MAX_RX_CHALLENGES) {
++ ast_debug(1, "Initial INVITE reached maximum number of auth attempts.\n");
++ return PJ_FALSE;
++ }
++
+ if (ast_sip_create_request_with_auth(&session->endpoint->outbound_auths, rdata,
+ tsx->last_tx, &tdata)) {
+ return PJ_FALSE;
+@@ -3846,6 +3853,7 @@ static void session_inv_on_tsx_state_cha
+ ast_debug(1, "reINVITE received final response code %d\n",
+ tsx->status_code);
+ if ((tsx->status_code == 401 || tsx->status_code == 407)
++ && ++session->authentication_challenge_count < MAX_RX_CHALLENGES
+ && !ast_sip_create_request_with_auth(
+ &session->endpoint->outbound_auths,
+ e->body.tsx_state.src.rdata, tsx->last_tx, &tdata)) {
+@@ -3920,6 +3928,7 @@ static void session_inv_on_tsx_state_cha
+ (int) pj_strlen(&tsx->method.name), pj_strbuf(&tsx->method.name),
+ tsx->status_code);
+ if ((tsx->status_code == 401 || tsx->status_code == 407)
++ && ++session->authentication_challenge_count < MAX_RX_CHALLENGES
+ && !ast_sip_create_request_with_auth(
+ &session->endpoint->outbound_auths,
+ e->body.tsx_state.src.rdata, tsx->last_tx, &tdata)) {