<p>George Joseph has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/15904">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">res_pjsip_outbound_authenticator_digest: Be tolerant of RFC8760 UASs<br><br>RFC7616 and RFC8760 allow more than one WWW-Authenticate or<br>Proxy-Autnenticate header per realm, each with different digest<br>algorithms (including new ones like SHA-256 and SHA-512-256).<br>Thankfully however a UAS can NOT send back multiple Authenticate<br>headers for the same realm with the same digest algorithm. The<br>UAS is also supposed to send the headers in order of preference<br>with the first one being the most preferred. We're supposed to<br>send an Authorization header for the first one we encounter for a<br>realm that we can support.<br><br>The UAS can also send multiple realms, especially when it's a<br>proxy that has forked the request in which case the proxy will<br>aggregate all of the Authenticate headers and then send them all<br>back to the UAC.<br><br>It doesn't stop there though... Each realm can require a<br>different username from the others. There's also nothing<br>preventing each digest algorithm from having a unique password<br>although I'm not sure if that adds any benefit.<br><br>So now... For each Authenticate header we encounter, we have to<br>determine if we support the digest algorithm and, if not, just<br>skip the header. We then have to find an auth object that<br>matches the realm AND the digest algorithm or find a wildcard<br>object that matches the digest algorithm. If we find one, we add<br>it to the results vector and read the next Authenticate header.<br>If the next header is for the same realm AND we already added an<br>auth object for that realm, we skip the header. Otherwise we<br>repeat the process for the next header.<br><br>In the end, we'll have accumulated a list of credentials we can<br>pass to pjproject that it can use to add Authentication headers<br>to a request.<br><br>NOTE: Neither we nor pjproject can currently handle digest<br>algorithms other than MD5. We don't even have a place for it in<br>the ast_sip_auth object. For this reason, we just skip processing<br>any Authenticate header that's not MD5. When we support the<br>others, we'll move the check into the loop that searches the<br>objects.<br><br>Changes:<br><br> * Added a new API ast_sip_retrieve_auths_vector() that takes in<br> a vector of auth ids (usually supplied on a call to<br> ast_sip_create_request_with_auth()) and populates another<br> vector with the actual objects.<br><br> * Refactored res_pjsip_outbound_authenticator_digest to handle<br> multiple Authenticate headers and set the stage for handling<br> additional digest algorithms.<br><br> * Added a pjproject patch that allows them to ignore digest<br> algorithms they don't support. This patch has already been<br> merged upstream.<br><br> * Updated documentation for auth objects in the XML and<br> in pjsip.conf.sample.<br><br> * Although res_pjsip_authenticator_digest isn't affected<br> by this change, some debugging and a testsuite AMI event<br> was added to facilitate testing.<br><br>Discovered during OpenSIPit 2021.<br><br>ASTERISK-29397<br><br>Change-Id: I3aef5ce4fe1d27e48d61268520f284d15d650281<br>---<br>M configs/samples/pjsip.conf.sample<br>M include/asterisk/res_pjsip.h<br>M res/res_pjsip.c<br>M res/res_pjsip/pjsip_configuration.c<br>M res/res_pjsip_authenticator_digest.c<br>M res/res_pjsip_outbound_authenticator_digest.c<br>A third-party/pjproject/patches/0090-Skip-unsupported-digest-algorithm-2408.patch<br>7 files changed, 771 insertions(+), 97 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/04/15904/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample</span><br><span>index 7b4c7aa..874fcc3 100644</span><br><span>--- a/configs/samples/pjsip.conf.sample</span><br><span>+++ b/configs/samples/pjsip.conf.sample</span><br><span>@@ -971,10 +971,11 @@</span><br><span> ; The input to the hash function must be in the</span><br><span> ; following format:</span><br><span> ; <username>:<realm>:<password></span><br><span style="color: hsl(0, 100%, 40%);">- ; For incoming authentication (asterisk is the server),</span><br><span style="color: hsl(120, 100%, 40%);">+ ; For incoming authentication (asterisk is the UAS),</span><br><span> ; the realm must match either the realm set in this object</span><br><span> ; or the default set in in the "global" object.</span><br><span style="color: hsl(0, 100%, 40%);">- ; For outgoing authentication (asterisk is the client),</span><br><span style="color: hsl(120, 100%, 40%);">+ ;</span><br><span style="color: hsl(120, 100%, 40%);">+ ; For outgoing authentication (asterisk is the UAC),</span><br><span> ; the realm must match what the server will be sending</span><br><span> ; in their WWW-Authenticate header. It can't be blank</span><br><span> ; unless you expect the server to be sending a blank</span><br><span>@@ -985,16 +986,22 @@</span><br><span> ; Note the '-n'. You don't want a newline to be part</span><br><span> ; of the hash. (default: "")</span><br><span> ;password= ; PlainText password used for authentication (default: "")</span><br><span style="color: hsl(0, 100%, 40%);">-;realm= ; For incoming authentication (asterisk is the server),</span><br><span style="color: hsl(120, 100%, 40%);">+;realm= ; For incoming authentication (asterisk is the UAS),</span><br><span> ; this is the realm to be sent on WWW-Authenticate</span><br><span> ; headers. If not specified, the global object's</span><br><span> ; "default_realm" will be used.</span><br><span style="color: hsl(0, 100%, 40%);">- ; For outgoing authentication (asterisk is the client), this</span><br><span style="color: hsl(120, 100%, 40%);">+ ;</span><br><span style="color: hsl(120, 100%, 40%);">+ ; For outgoing authentication (asterisk is the UAS), this</span><br><span> ; must either be the realm the server is expected to send,</span><br><span style="color: hsl(0, 100%, 40%);">- ; or blank to automatically use the realm sent by the server.</span><br><span style="color: hsl(0, 100%, 40%);">- ; If you have multiple auth object for an endpoint, the realm</span><br><span style="color: hsl(0, 100%, 40%);">- ; is also used to match the auth object to the realm the</span><br><span style="color: hsl(0, 100%, 40%);">- ; server sends. (default: "")</span><br><span style="color: hsl(120, 100%, 40%);">+ ; or left blank or contain a single '*' to automatically</span><br><span style="color: hsl(120, 100%, 40%);">+ ; use the realm sent by the server. If you have multiple</span><br><span style="color: hsl(120, 100%, 40%);">+ ; auth object for an endpoint, the realm is also used to</span><br><span style="color: hsl(120, 100%, 40%);">+ ; match the auth object to the realm the server sent.</span><br><span style="color: hsl(120, 100%, 40%);">+ ; Using the same auth section for inbound and outbound</span><br><span style="color: hsl(120, 100%, 40%);">+ ; authentication is not recommended. There is a difference in</span><br><span style="color: hsl(120, 100%, 40%);">+ ; meaning for an empty realm setting between inbound and outbound</span><br><span style="color: hsl(120, 100%, 40%);">+ ; authentication uses.</span><br><span style="color: hsl(120, 100%, 40%);">+ ; (default: "")</span><br><span> ;type= ; Must be auth (default: "")</span><br><span> ;username= ; Username to use for account (default: "")</span><br><span> </span><br><span>diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h</span><br><span>index 2020ca8..7083b25 100644</span><br><span>--- a/include/asterisk/res_pjsip.h</span><br><span>+++ b/include/asterisk/res_pjsip.h</span><br><span>@@ -2499,12 +2499,48 @@</span><br><span> * Call this function once you have completed operating on auths</span><br><span> * retrieved from \ref ast_sip_retrieve_auths</span><br><span> *</span><br><span style="color: hsl(0, 100%, 40%);">- * \param auths An vector of auth structures to clean up</span><br><span style="color: hsl(0, 100%, 40%);">- * \param num_auths The number of auths in the vector</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param auths An array of auth object pointers to clean up</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param num_auths The number of auths in the array</span><br><span> */</span><br><span> void ast_sip_cleanup_auths(struct ast_sip_auth *auths[], size_t num_auths);</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Retrieve relevant SIP auth structures from sorcery as a vector</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param auth_ids Vector of sorcery IDs of auth credentials to retrieve</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[out] auth_objects A pointer ast_sip_auth_objects_vector to hold the objects</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 Success</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval -1 Number of auth objects found is less than the number of names supplied.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \WARNING The number of auth objects retrieved may be less than the</span><br><span style="color: hsl(120, 100%, 40%);">+ * number of auth ids supplied if auth objects couldn't be found for</span><br><span style="color: hsl(120, 100%, 40%);">+ * some of them.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \NOTE Since the ref count on all auith objects returned has been</span><br><span style="color: hsl(120, 100%, 40%);">+ * bumped, you must call ast_sip_cleanup_auth_objects_vector() to decrement</span><br><span style="color: hsl(120, 100%, 40%);">+ * the ref count on all of the auth objects in the vector,</span><br><span style="color: hsl(120, 100%, 40%);">+ * then call AST_VECTOR_FREE() on the vector itself.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+AST_VECTOR(ast_sip_auth_objects_vector, struct ast_sip_auth *);</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_sip_retrieve_auths_vector(const struct ast_sip_auth_vector *auth_ids,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_auth_objects_vector *auth_objects);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Clean up retrieved auth objects in vector</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Call this function once you have completed operating on auths</span><br><span style="color: hsl(120, 100%, 40%);">+ * retrieved from \ref ast_sip_retrieve_auths_vector. All</span><br><span style="color: hsl(120, 100%, 40%);">+ * auth objects will have their reference counts decremented and</span><br><span style="color: hsl(120, 100%, 40%);">+ * the vector size will be reset to 0. You must still call</span><br><span style="color: hsl(120, 100%, 40%);">+ * AST_VECTOR_FREE() on the vector itself.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param auth_objects A vector of auth structures to clean up</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define ast_sip_cleanup_auth_objects_vector(auth_objects) AST_VECTOR_RESET(auth_objects, ao2_cleanup)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span> * \brief Checks if the given content type matches type/subtype.</span><br><span> *</span><br><span> * Compares the pjsip_media_type with the passed type and subtype and</span><br><span>diff --git a/res/res_pjsip.c b/res/res_pjsip.c</span><br><span>index 775b63f..8c6fe73 100644</span><br><span>--- a/res/res_pjsip.c</span><br><span>+++ b/res/res_pjsip.c</span><br><span>@@ -1504,13 +1504,24 @@</span><br><span> This option specifies which of the password style config options should be read</span><br><span> when trying to authenticate an endpoint inbound request. If set to <literal>userpass</literal></span><br><span> then we'll read from the 'password' option. For <literal>md5</literal> we'll read</span><br><span style="color: hsl(0, 100%, 40%);">- from 'md5_cred'. If set to <literal>google_oauth</literal> then we'll read from the refresh_token/oauth_clientid/oauth_secret fields.</span><br><span style="color: hsl(120, 100%, 40%);">+ from 'md5_cred'. The following values are valid:</span><br><span> </para></span><br><span> <enumlist></span><br><span> <enum name="md5"/></span><br><span> <enum name="userpass"/></span><br><span> <enum name="google_oauth"/></span><br><span> </enumlist></span><br><span style="color: hsl(120, 100%, 40%);">+ <para></span><br><span style="color: hsl(120, 100%, 40%);">+ If set to <literal>google_oauth</literal> then we'll read from the</span><br><span style="color: hsl(120, 100%, 40%);">+ refresh_token/oauth_clientid/oauth_secret fields. </para></span><br><span style="color: hsl(120, 100%, 40%);">+ <note></span><br><span style="color: hsl(120, 100%, 40%);">+ <para></span><br><span style="color: hsl(120, 100%, 40%);">+ This setting only describes whether the password is in</span><br><span style="color: hsl(120, 100%, 40%);">+ plain text or has been pre-hashed with MD5. It doesn't describe</span><br><span style="color: hsl(120, 100%, 40%);">+ the acceptable digest algorithms we'll accept in a received</span><br><span style="color: hsl(120, 100%, 40%);">+ challenge.</span><br><span style="color: hsl(120, 100%, 40%);">+ </para></span><br><span style="color: hsl(120, 100%, 40%);">+ </note></span><br><span> </description></span><br><span> </configOption></span><br><span> <configOption name="nonce_lifetime" default="32"></span><br><span>@@ -1542,11 +1553,12 @@</span><br><span> <para></span><br><span> </para></span><br><span> <para></span><br><span style="color: hsl(0, 100%, 40%);">- For outgoing authentication (asterisk is the client),</span><br><span style="color: hsl(120, 100%, 40%);">+ For outgoing authentication (asterisk is the UAC),</span><br><span> the realm must match what the server will be sending</span><br><span> in their WWW-Authenticate header. It can't be blank</span><br><span> unless you expect the server to be sending a blank</span><br><span style="color: hsl(0, 100%, 40%);">- realm in the header.</span><br><span style="color: hsl(120, 100%, 40%);">+ realm in the header. You can't use pre-hashed</span><br><span style="color: hsl(120, 100%, 40%);">+ paswords with a wildcard auth object.</span><br><span> You can generate the hash with the following shell</span><br><span> command:</span><br><span> </para></span><br><span>@@ -1578,7 +1590,7 @@</span><br><span> <configOption name="realm" default=""></span><br><span> <synopsis>SIP realm for endpoint</synopsis></span><br><span> <description><para></span><br><span style="color: hsl(0, 100%, 40%);">- For incoming authentication (asterisk is the server),</span><br><span style="color: hsl(120, 100%, 40%);">+ For incoming authentication (asterisk is the UAS),</span><br><span> this is the realm to be sent on WWW-Authenticate</span><br><span> headers. If not specified, the <replaceable>global</replaceable></span><br><span> object's <variable>default_realm</variable> will be used.</span><br><span>@@ -1586,12 +1598,12 @@</span><br><span> <para></span><br><span> </para></span><br><span> <para></span><br><span style="color: hsl(0, 100%, 40%);">- For outgoing authentication (asterisk is the client), this</span><br><span style="color: hsl(120, 100%, 40%);">+ For outgoing authentication (asterisk is the UAS), this</span><br><span> must either be the realm the server is expected to send,</span><br><span style="color: hsl(0, 100%, 40%);">- or blank to automatically use the realm sent by the server.</span><br><span style="color: hsl(0, 100%, 40%);">- If you have multiple auth object for an endpoint, the realm</span><br><span style="color: hsl(0, 100%, 40%);">- is also used to match the auth object to the realm the</span><br><span style="color: hsl(0, 100%, 40%);">- server sent.</span><br><span style="color: hsl(120, 100%, 40%);">+ or left blank or contain a single '*' to automatically</span><br><span style="color: hsl(120, 100%, 40%);">+ use the realm sent by the server. If you have multiple</span><br><span style="color: hsl(120, 100%, 40%);">+ auth object for an endpoint, the realm is also used to</span><br><span style="color: hsl(120, 100%, 40%);">+ match the auth object to the realm the server sent.</span><br><span> </para></span><br><span> <para></span><br><span> </para></span><br><span>@@ -1600,7 +1612,19 @@</span><br><span> Using the same auth section for inbound and outbound</span><br><span> authentication is not recommended. There is a difference in</span><br><span> meaning for an empty realm setting between inbound and outbound</span><br><span style="color: hsl(0, 100%, 40%);">- authentication uses.</para></note></span><br><span style="color: hsl(120, 100%, 40%);">+ authentication uses.</span><br><span style="color: hsl(120, 100%, 40%);">+ </para></span><br><span style="color: hsl(120, 100%, 40%);">+ </note></span><br><span style="color: hsl(120, 100%, 40%);">+ <para></span><br><span style="color: hsl(120, 100%, 40%);">+ </para></span><br><span style="color: hsl(120, 100%, 40%);">+ <note></span><br><span style="color: hsl(120, 100%, 40%);">+ <para></span><br><span style="color: hsl(120, 100%, 40%);">+ If more than one auth object with the same realm or</span><br><span style="color: hsl(120, 100%, 40%);">+ more than one wildcard auth object associated to</span><br><span style="color: hsl(120, 100%, 40%);">+ an endpoint, we can only use the first one of</span><br><span style="color: hsl(120, 100%, 40%);">+ each defined on the endpoint.</span><br><span style="color: hsl(120, 100%, 40%);">+ </para></span><br><span style="color: hsl(120, 100%, 40%);">+ </note></span><br><span> </description></span><br><span> </configOption></span><br><span> <configOption name="type"></span><br><span>diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c</span><br><span>index 6d12c6f..6defa7c 100644</span><br><span>--- a/res/res_pjsip/pjsip_configuration.c</span><br><span>+++ b/res/res_pjsip/pjsip_configuration.c</span><br><span>@@ -2382,6 +2382,25 @@</span><br><span> }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+int ast_sip_retrieve_auths_vector(const struct ast_sip_auth_vector *auth_ids,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_auth_objects_vector *auth_objects)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int i;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < AST_VECTOR_SIZE(auth_ids); ++i) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Using AST_VECTOR_GET is safe since the vector is immutable */</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *name = AST_VECTOR_GET(auth_ids, i);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_auth *auth_object = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), SIP_SORCERY_AUTH_TYPE, name);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!auth_object) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Auth object '%s' could not be found\n", name);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_APPEND(auth_objects, auth_object);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_VECTOR_SIZE(auth_objects) == AST_VECTOR_SIZE(auth_ids) ? 0 : -1;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> struct ast_sorcery *ast_sip_get_sorcery(void)</span><br><span> {</span><br><span> return sip_sorcery;</span><br><span>diff --git a/res/res_pjsip_authenticator_digest.c b/res/res_pjsip_authenticator_digest.c</span><br><span>index 518ef73..231a7e5 100644</span><br><span>--- a/res/res_pjsip_authenticator_digest.c</span><br><span>+++ b/res/res_pjsip_authenticator_digest.c</span><br><span>@@ -24,6 +24,7 @@</span><br><span> #include "asterisk/logger.h"</span><br><span> #include "asterisk/module.h"</span><br><span> #include "asterisk/strings.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/test.h"</span><br><span> </span><br><span> /*** MODULEINFO</span><br><span> <depend>pjproject</depend></span><br><span>@@ -296,7 +297,7 @@</span><br><span> */</span><br><span> enum digest_verify_result {</span><br><span> /*! Authentication credentials incorrect */</span><br><span style="color: hsl(0, 100%, 40%);">- AUTH_FAIL,</span><br><span style="color: hsl(120, 100%, 40%);">+ AUTH_FAIL = 0,</span><br><span> /*! Authentication credentials correct */</span><br><span> AUTH_SUCCESS,</span><br><span> /*! Authentication credentials correct but nonce mismatch */</span><br><span>@@ -305,6 +306,12 @@</span><br><span> AUTH_NOAUTH,</span><br><span> };</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static char *verify_result_str[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+ "FAIL",</span><br><span style="color: hsl(120, 100%, 40%);">+ "SUCCESS",</span><br><span style="color: hsl(120, 100%, 40%);">+ "STALE",</span><br><span style="color: hsl(120, 100%, 40%);">+ "NOAUTH"</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span> /*!</span><br><span> * \brief astobj2 callback for verifying incoming credentials</span><br><span> *</span><br><span>@@ -320,6 +327,7 @@</span><br><span> int response_code;</span><br><span> pjsip_auth_srv auth_server;</span><br><span> int stale = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ int res = AUTH_FAIL;</span><br><span> </span><br><span> if (!find_challenge(rdata, auth)) {</span><br><span> /* Couldn't find a challenge with a sane nonce.</span><br><span>@@ -336,17 +344,26 @@</span><br><span> </span><br><span> if (authed == PJ_SUCCESS) {</span><br><span> if (stale) {</span><br><span style="color: hsl(0, 100%, 40%);">- return AUTH_STALE;</span><br><span style="color: hsl(120, 100%, 40%);">+ res = AUTH_STALE;</span><br><span> } else {</span><br><span style="color: hsl(0, 100%, 40%);">- return AUTH_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+ res = AUTH_SUCCESS;</span><br><span> }</span><br><span> }</span><br><span> </span><br><span> if (authed == PJSIP_EAUTHNOAUTH) {</span><br><span style="color: hsl(0, 100%, 40%);">- return AUTH_NOAUTH;</span><br><span style="color: hsl(120, 100%, 40%);">+ res = AUTH_NOAUTH;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- return AUTH_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(3, "Realm: %s Username: %s Result: %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ auth->realm, auth->auth_user, verify_result_str[res]);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_suite_event_notify("INCOMING_AUTH_VERIFY_RESULT",</span><br><span style="color: hsl(120, 100%, 40%);">+ "Realm: %s\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "Username: %s\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "Status: %s",</span><br><span style="color: hsl(120, 100%, 40%);">+ auth->realm, auth->auth_user, verify_result_str[res]);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return res;</span><br><span> }</span><br><span> </span><br><span> /*!</span><br><span>diff --git a/res/res_pjsip_outbound_authenticator_digest.c b/res/res_pjsip_outbound_authenticator_digest.c</span><br><span>index b1011b0..9c523a0 100644</span><br><span>--- a/res/res_pjsip_outbound_authenticator_digest.c</span><br><span>+++ b/res/res_pjsip_outbound_authenticator_digest.c</span><br><span>@@ -30,131 +30,485 @@</span><br><span> #include "asterisk/logger.h"</span><br><span> #include "asterisk/module.h"</span><br><span> #include "asterisk/strings.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/vector.h"</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static pjsip_www_authenticate_hdr *get_auth_header(pjsip_rx_data *challenge,</span><br><span style="color: hsl(0, 100%, 40%);">- const void *start)</span><br><span style="color: hsl(120, 100%, 40%);">+pj_str_t supported_digest_algorithms[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+ { "MD5", 3}</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Determine proper authenticate header</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * We need to search for different headers dependging on whether</span><br><span style="color: hsl(120, 100%, 40%);">+ * the response code from the UAS/Proxy was 401 or 407.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static pjsip_hdr_e get_auth_search_type(pjsip_rx_data *challenge)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">- pjsip_hdr_e search_type;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> if (challenge->msg_info.msg->line.status.code == PJSIP_SC_UNAUTHORIZED) {</span><br><span style="color: hsl(0, 100%, 40%);">- search_type = PJSIP_H_WWW_AUTHENTICATE;</span><br><span style="color: hsl(120, 100%, 40%);">+ return PJSIP_H_WWW_AUTHENTICATE;</span><br><span> } else if (challenge->msg_info.msg->line.status.code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED) {</span><br><span style="color: hsl(0, 100%, 40%);">- search_type = PJSIP_H_PROXY_AUTHENTICATE;</span><br><span style="color: hsl(120, 100%, 40%);">+ return PJSIP_H_PROXY_AUTHENTICATE;</span><br><span> } else {</span><br><span> ast_log(LOG_ERROR,</span><br><span> "Status code %d was received when it should have been 401 or 407.\n",</span><br><span> challenge->msg_info.msg->line.status.code);</span><br><span style="color: hsl(0, 100%, 40%);">- return NULL ;</span><br><span style="color: hsl(120, 100%, 40%);">+ return PJSIP_H_OTHER;</span><br><span> }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return pjsip_msg_find_hdr(challenge->msg_info.msg, search_type, start);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static int set_outbound_authentication_credentials(pjsip_auth_clt_sess *auth_sess,</span><br><span style="color: hsl(0, 100%, 40%);">- const struct ast_sip_auth_vector *auth_vector, pjsip_rx_data *challenge,</span><br><span style="color: hsl(0, 100%, 40%);">- pjsip_www_authenticate_hdr *auth_hdr)</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Determine if digest algorithm in the header is one we support</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 1 If we support the algorithm</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 If we do not</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int is_digest_algorithm_supported(pjsip_www_authenticate_hdr *auth_hdr)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">- size_t auth_size = AST_VECTOR_SIZE(auth_vector);</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_sip_auth **auths = ast_alloca(auth_size * sizeof(*auths));</span><br><span style="color: hsl(0, 100%, 40%);">- pjsip_cred_info *auth_creds = ast_alloca(auth_size * sizeof(*auth_creds));</span><br><span style="color: hsl(0, 100%, 40%);">- int res = 0;</span><br><span style="color: hsl(0, 100%, 40%);">- int i;</span><br><span style="color: hsl(120, 100%, 40%);">+ int digest;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- if (ast_sip_retrieve_auths(auth_vector, auths)) {</span><br><span style="color: hsl(0, 100%, 40%);">- res = -1;</span><br><span style="color: hsl(0, 100%, 40%);">- goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ /* An empty digest is assumed to be md5 */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (pj_strlen(&auth_hdr->challenge.digest.algorithm) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return 1;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- for (i = 0; i < auth_size; ++i) {</span><br><span style="color: hsl(0, 100%, 40%);">- if (ast_strlen_zero(auths[i]->realm)) {</span><br><span style="color: hsl(0, 100%, 40%);">- auth_creds[i].realm = auth_hdr->challenge.common.realm;</span><br><span style="color: hsl(0, 100%, 40%);">- } else {</span><br><span style="color: hsl(0, 100%, 40%);">- pj_cstr(&auth_creds[i].realm, auths[i]->realm);</span><br><span style="color: hsl(120, 100%, 40%);">+ for (digest = 0; digest < ARRAY_LEN(supported_digest_algorithms); digest++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (pj_stricmp(&auth_hdr->challenge.digest.algorithm, &supported_digest_algorithms[digest]) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return 1;</span><br><span> }</span><br><span style="color: hsl(0, 100%, 40%);">- pj_cstr(&auth_creds[i].username, auths[i]->auth_user);</span><br><span style="color: hsl(0, 100%, 40%);">- pj_cstr(&auth_creds[i].scheme, "digest");</span><br><span style="color: hsl(0, 100%, 40%);">- switch (auths[i]->type) {</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Initialize pjproject with a valid set of credentials</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * RFC7616 and RFC8760 allow more than one WWW-Authenticate or</span><br><span style="color: hsl(120, 100%, 40%);">+ * Proxy-Autnenticate header per realm, each with different digest</span><br><span style="color: hsl(120, 100%, 40%);">+ * algorithms (including new ones like SHA-256 and SHA-512-256). However,</span><br><span style="color: hsl(120, 100%, 40%);">+ * thankfully, a UAS can NOT send back multiple Authenticate headers for</span><br><span style="color: hsl(120, 100%, 40%);">+ * the same realm with the same digest algorithm. The UAS is also</span><br><span style="color: hsl(120, 100%, 40%);">+ * supposed to send the headers in order of preference with the first one</span><br><span style="color: hsl(120, 100%, 40%);">+ * being the most preferred.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * We're supposed to send an Authorization header for the first one we</span><br><span style="color: hsl(120, 100%, 40%);">+ * encounter for a realm that we can support.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * The UAS can also send multiple realms, especially when it's a proxy</span><br><span style="color: hsl(120, 100%, 40%);">+ * that has forked the request in which case the proxy will aggregate all</span><br><span style="color: hsl(120, 100%, 40%);">+ * of the Authenticate and then them all back to the UAC.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * It doesn't stop there though... Each realm can require a different</span><br><span style="color: hsl(120, 100%, 40%);">+ * username from the others. There's also nothing preventing each digest</span><br><span style="color: hsl(120, 100%, 40%);">+ * algorithm from having a unique password although I'm not sure if</span><br><span style="color: hsl(120, 100%, 40%);">+ * that adds any benefit.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * So now... For each Authenticate header we encounter, we have to</span><br><span style="color: hsl(120, 100%, 40%);">+ * determine if we support the digest algorithm and, if not, just skip the</span><br><span style="color: hsl(120, 100%, 40%);">+ * header. We then have to find an auth object that matches the realm AND</span><br><span style="color: hsl(120, 100%, 40%);">+ * the digest algorithm or find a wildcard object that matches the digest</span><br><span style="color: hsl(120, 100%, 40%);">+ * algorithm. If we find one, we add it to the results vector and read the</span><br><span style="color: hsl(120, 100%, 40%);">+ * next Authenticate header. If the next header is for the same realm AND</span><br><span style="color: hsl(120, 100%, 40%);">+ * we already added an auth object for that realm, we skip the header.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Otherwise we repeat the process for the next header.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * In the end, we'll have accumulated a list of credentials we can pass to</span><br><span style="color: hsl(120, 100%, 40%);">+ * pjproject that it can use to add Authentication headers to a request.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \NOTE: Neither we nor pjproject can currently handle digest algorithms</span><br><span style="color: hsl(120, 100%, 40%);">+ * other than MD5. We don't even have a place for it in the ast_sip_auth</span><br><span style="color: hsl(120, 100%, 40%);">+ * object. For this reason, we just skip processing any Authenticate</span><br><span style="color: hsl(120, 100%, 40%);">+ * header that's not MD5. When we support the others, we'll move the</span><br><span style="color: hsl(120, 100%, 40%);">+ * check into the loop that searches the objects.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static pj_status_t set_outbound_authentication_credentials(pjsip_auth_clt_sess *auth_sess,</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct ast_sip_auth_objects_vector *auth_objects_vector, pjsip_rx_data *challenge,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str *realms)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int i;</span><br><span style="color: hsl(120, 100%, 40%);">+ size_t auth_object_count;</span><br><span style="color: hsl(120, 100%, 40%);">+ pjsip_www_authenticate_hdr *auth_hdr = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ pj_status_t res = PJ_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+ pjsip_hdr_e search_type;</span><br><span style="color: hsl(120, 100%, 40%);">+ size_t cred_count;</span><br><span style="color: hsl(120, 100%, 40%);">+ pjsip_cred_info *creds_array;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Normally vector elements are pointers to something else, usually</span><br><span style="color: hsl(120, 100%, 40%);">+ * structures. In this case however, the elements are the</span><br><span style="color: hsl(120, 100%, 40%);">+ * structures themselves instead of pointers to them. This is due</span><br><span style="color: hsl(120, 100%, 40%);">+ * to the fact that pjsip_auth_clt_set_credentials() expects an</span><br><span style="color: hsl(120, 100%, 40%);">+ * array of structues, not an array of pointers to structures.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Thankfully, vectors allow you to "steal" their underlying</span><br><span style="color: hsl(120, 100%, 40%);">+ * arrays, in this case an array of pjsip_cred_info structures,</span><br><span style="color: hsl(120, 100%, 40%);">+ * which we'll pass to pjsip_auth_clt_set_credentials() at the</span><br><span style="color: hsl(120, 100%, 40%);">+ * end.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR(cred_info, pjsip_cred_info) auth_creds;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ search_type = get_auth_search_type(challenge);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (search_type == PJSIP_H_OTHER) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * The status code on the response wasn't 401 or 407</span><br><span style="color: hsl(120, 100%, 40%);">+ * so there are no WWW-Authenticate or Proxy-Authenticate</span><br><span style="color: hsl(120, 100%, 40%);">+ * headers to process.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ return PJ_ENOTSUP;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ auth_object_count = AST_VECTOR_SIZE(auth_objects_vector);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (auth_object_count == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* This shouldn't happen but we'll check anyway. */</span><br><span style="color: hsl(120, 100%, 40%);">+ return PJ_EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * The number of pjsip_cred_infos we send to pjproject can</span><br><span style="color: hsl(120, 100%, 40%);">+ * vary based on the number of acceptable headers received</span><br><span style="color: hsl(120, 100%, 40%);">+ * and the number of acceptable auth objects on the endpoint</span><br><span style="color: hsl(120, 100%, 40%);">+ * so we just use a vector to accumulate them.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * NOTE: You have to call AST_VECTOR_FREE() on the vector</span><br><span style="color: hsl(120, 100%, 40%);">+ * but you don't have to free the elements because they're</span><br><span style="color: hsl(120, 100%, 40%);">+ * actual structures, not pointers to structures.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (AST_VECTOR_INIT(&auth_creds, 5) != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return PJ_ENOMEM;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * It's going to be rare that we actually have more than one</span><br><span style="color: hsl(120, 100%, 40%);">+ * WWW-Authentication header or more than one auth object to</span><br><span style="color: hsl(120, 100%, 40%);">+ * match to it so the following nested loop should be fine.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ while ((auth_hdr = pjsip_msg_find_hdr(challenge->msg_info.msg,</span><br><span style="color: hsl(120, 100%, 40%);">+ search_type, auth_hdr ? auth_hdr->next : NULL))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ int exact_match_index = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ int wildcard_match_index = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ int match_index = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ pjsip_cred_info auth_cred;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_auth *auth = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ memset(&auth_cred, 0, sizeof(auth_cred));</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Since we only support the MD5 algorithm at the current time,</span><br><span style="color: hsl(120, 100%, 40%);">+ * there's no sense searching for auth objects that match the algorithm.</span><br><span style="color: hsl(120, 100%, 40%);">+ * In fact, the auth_object structure doesn't even have a member</span><br><span style="color: hsl(120, 100%, 40%);">+ * for it.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * When we do support more algorithms, this check will need to be</span><br><span style="color: hsl(120, 100%, 40%);">+ * moved inside the auth object loop below.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Note: The header may not have specified an algorithm at all in which</span><br><span style="color: hsl(120, 100%, 40%);">+ * case it's assumed to be MD5. is_digest_algorithm_supported() returns</span><br><span style="color: hsl(120, 100%, 40%);">+ * true for that case.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!is_digest_algorithm_supported(auth_hdr)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(3, "Skipping header with realm '%.*s' and unsupported '%.*s' algorithm \n",</span><br><span style="color: hsl(120, 100%, 40%);">+ (int)auth_hdr->challenge.digest.realm.slen, auth_hdr->challenge.digest.realm.ptr,</span><br><span style="color: hsl(120, 100%, 40%);">+ (int)auth_hdr->challenge.digest.algorithm.slen, auth_hdr->challenge.digest.algorithm.ptr);</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Appending the realms is strictly so digest_create_request_with_auth()</span><br><span style="color: hsl(120, 100%, 40%);">+ * can display good error messages. Since we only support one algorithm,</span><br><span style="color: hsl(120, 100%, 40%);">+ * there can't be more than one header with the same realm. No need to worry</span><br><span style="color: hsl(120, 100%, 40%);">+ * about dupplicate realms until then.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (realms) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&realms, 0, "%.*s, ",</span><br><span style="color: hsl(120, 100%, 40%);">+ (int)auth_hdr->challenge.digest.realm.slen, auth_hdr->challenge.digest.realm.ptr);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(3, "Searching auths to find matching ones for header with realm '%.*s' and algorithm '%.*s'\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ (int)auth_hdr->challenge.digest.realm.slen, auth_hdr->challenge.digest.realm.ptr,</span><br><span style="color: hsl(120, 100%, 40%);">+ (int)auth_hdr->challenge.digest.algorithm.slen, auth_hdr->challenge.digest.algorithm.ptr);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Now that we have a valid header, we can loop over the auths available to</span><br><span style="color: hsl(120, 100%, 40%);">+ * find either an exact realm match or, failing that, a wildcard auth (an</span><br><span style="color: hsl(120, 100%, 40%);">+ * auth with an empty or "*" realm).</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * NOTE: We never use the global default realm when we're the UAC responding</span><br><span style="color: hsl(120, 100%, 40%);">+ * to a 401 or 407. We only use that when we're the UAS (handled elsewhere)</span><br><span style="color: hsl(120, 100%, 40%);">+ * and the auth object didn't have a realm.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < auth_object_count; ++i) {</span><br><span style="color: hsl(120, 100%, 40%);">+ auth = AST_VECTOR_GET(auth_objects_vector, i);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * If this auth object's realm exactly matches the one</span><br><span style="color: hsl(120, 100%, 40%);">+ * from the header, we can just break out and use it.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * NOTE: If there's more than one auth object for an endpoint with</span><br><span style="color: hsl(120, 100%, 40%);">+ * a matching realm it's a misconfiguration. We'll only use the first.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (pj_stricmp2(&auth_hdr->challenge.digest.realm, auth->realm) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(3, "Found matching auth '%s' with realm '%s'\n", ast_sorcery_object_get_id(auth),</span><br><span style="color: hsl(120, 100%, 40%);">+ auth->realm);</span><br><span style="color: hsl(120, 100%, 40%);">+ exact_match_index = i;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * If we found an exact realm match, there's no need to keep</span><br><span style="color: hsl(120, 100%, 40%);">+ * looking for a wildcard.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * If this auth object's realm is empty or a "*", it's a wildcard</span><br><span style="color: hsl(120, 100%, 40%);">+ * auth object. We going to save its index but keep iterating over</span><br><span style="color: hsl(120, 100%, 40%);">+ * the vector in case we find an exact match later.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * NOTE: If there's more than one wildcard auth object for an endpoint</span><br><span style="color: hsl(120, 100%, 40%);">+ * it's a misconfiguration. We'll only use the first.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (wildcard_match_index < 0</span><br><span style="color: hsl(120, 100%, 40%);">+ && (ast_strlen_zero(auth->realm) || ast_strings_equal(auth->realm, "*"))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(3, "Found wildcard auth '%s' for realm '%.*s'\n", ast_sorcery_object_get_id(auth),</span><br><span style="color: hsl(120, 100%, 40%);">+ (int)auth_hdr->challenge.digest.realm.slen, auth_hdr->challenge.digest.realm.ptr);</span><br><span style="color: hsl(120, 100%, 40%);">+ wildcard_match_index = i;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (exact_match_index < 0 && wildcard_match_index < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Didn't find either a wildcard or an exact realm match.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Move on to the next header.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(3, "No auth matching realm or no wildcard found for realm '%.*s'\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ (int)auth_hdr->challenge.digest.realm.slen, auth_hdr->challenge.digest.realm.ptr);</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (exact_match_index >= 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * If we found an exact match, we'll always prefer that.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ match_index = exact_match_index;</span><br><span style="color: hsl(120, 100%, 40%);">+ auth = AST_VECTOR_GET(auth_objects_vector, match_index);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(3, "Using matched auth '%s' with realm '%.*s'\n", ast_sorcery_object_get_id(auth),</span><br><span style="color: hsl(120, 100%, 40%);">+ (int)auth_hdr->challenge.digest.realm.slen, auth_hdr->challenge.digest.realm.ptr);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * We'll only use the wildcard if we didn't find an exact match.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ match_index = wildcard_match_index;</span><br><span style="color: hsl(120, 100%, 40%);">+ auth = AST_VECTOR_GET(auth_objects_vector, match_index);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(3, "Using wildcard auth '%s' for realm '%.*s'\n", ast_sorcery_object_get_id(auth),</span><br><span style="color: hsl(120, 100%, 40%);">+ (int)auth_hdr->challenge.digest.realm.slen, auth_hdr->challenge.digest.realm.ptr);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copy the fields from the auth_object to the</span><br><span style="color: hsl(120, 100%, 40%);">+ * pjsip_cred_info structure.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ auth_cred.realm = auth_hdr->challenge.common.realm;</span><br><span style="color: hsl(120, 100%, 40%);">+ pj_cstr(&auth_cred.username, auth->auth_user);</span><br><span style="color: hsl(120, 100%, 40%);">+ pj_cstr(&auth_cred.scheme, "digest");</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (auth->type) {</span><br><span> case AST_SIP_AUTH_TYPE_USER_PASS:</span><br><span style="color: hsl(0, 100%, 40%);">- pj_cstr(&auth_creds[i].data, auths[i]->auth_pass);</span><br><span style="color: hsl(0, 100%, 40%);">- auth_creds[i].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;</span><br><span style="color: hsl(120, 100%, 40%);">+ pj_cstr(&auth_cred.data, auth->auth_pass);</span><br><span style="color: hsl(120, 100%, 40%);">+ auth_cred.data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;</span><br><span> break;</span><br><span> case AST_SIP_AUTH_TYPE_MD5:</span><br><span style="color: hsl(0, 100%, 40%);">- pj_cstr(&auth_creds[i].data, auths[i]->md5_creds);</span><br><span style="color: hsl(0, 100%, 40%);">- auth_creds[i].data_type = PJSIP_CRED_DATA_DIGEST;</span><br><span style="color: hsl(120, 100%, 40%);">+ pj_cstr(&auth_cred.data, auth->md5_creds);</span><br><span style="color: hsl(120, 100%, 40%);">+ auth_cred.data_type = PJSIP_CRED_DATA_DIGEST;</span><br><span> break;</span><br><span> case AST_SIP_AUTH_TYPE_GOOGLE_OAUTH:</span><br><span> /* nothing to do. handled seperately in res_pjsip_outbound_registration */</span><br><span> break;</span><br><span> case AST_SIP_AUTH_TYPE_ARTIFICIAL:</span><br><span style="color: hsl(0, 100%, 40%);">- ast_log(LOG_ERROR, "Trying to set artificial outbound auth credentials shouldn't happen.\n");</span><br><span style="color: hsl(0, 100%, 40%);">- break;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR,</span><br><span style="color: hsl(120, 100%, 40%);">+ "Trying to set artificial outbound auth credentials shouldn't happen.\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span style="color: hsl(120, 100%, 40%);">+ } /* End auth object loop */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Because the vector contains actual structures and not pointers</span><br><span style="color: hsl(120, 100%, 40%);">+ * to structures, the call to AST_VECTOR_APPEND results in a simple</span><br><span style="color: hsl(120, 100%, 40%);">+ * assign of one structure to another, effectively copying the auth_cred</span><br><span style="color: hsl(120, 100%, 40%);">+ * structure contents to the array element.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ res = AST_VECTOR_APPEND(&auth_creds, auth_cred);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (res != PJ_SUCCESS) {</span><br><span style="color: hsl(120, 100%, 40%);">+ res = PJ_ENOMEM;</span><br><span style="color: hsl(120, 100%, 40%);">+ goto cleanup;</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+ } /* End header loop */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (realms && ast_str_strlen(realms)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Again, this is strictly so digest_create_request_with_auth()</span><br><span style="color: hsl(120, 100%, 40%);">+ * can display good error messages.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Chop off the trailing ", " on the last realm.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_truncate(realms, ast_str_strlen(realms) - 2);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- pjsip_auth_clt_set_credentials(auth_sess, auth_size, auth_creds);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (AST_VECTOR_SIZE(&auth_creds) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* No matching auth objects were found. */</span><br><span style="color: hsl(120, 100%, 40%);">+ res = PJSIP_ENOCREDENTIAL;</span><br><span style="color: hsl(120, 100%, 40%);">+ goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Here's where we steal the cred info structures from the vector.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * The steal effectively returns a pointer to the underlying</span><br><span style="color: hsl(120, 100%, 40%);">+ * array of pjsip_cred_info structures which is exactly what we need</span><br><span style="color: hsl(120, 100%, 40%);">+ * to pass to pjsip_auth_clt_set_credentials().</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * <struct cred info><struct cred info>...<struct cred info></span><br><span style="color: hsl(120, 100%, 40%);">+ * ^pointer</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Since we stole the array from the vector, we have to free it ourselves.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * We also have to copy the size before we steal because stealing</span><br><span style="color: hsl(120, 100%, 40%);">+ * resets the vector size to 0.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ cred_count = AST_VECTOR_SIZE(&auth_creds);</span><br><span style="color: hsl(120, 100%, 40%);">+ creds_array = AST_VECTOR_STEAL_ELEMENTS(&auth_creds);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ res = pjsip_auth_clt_set_credentials(auth_sess, cred_count, creds_array);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(creds_array);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (res == PJ_SUCCESS) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(3, "Set %"PRIu64" credentials in auth session\n", cred_count);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Failed to set %"PRIu64" credentials in auth session\n", cred_count);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span> </span><br><span> cleanup:</span><br><span style="color: hsl(0, 100%, 40%);">- ast_sip_cleanup_auths(auths, auth_size);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_FREE(&auth_creds);</span><br><span> return res;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static int digest_create_request_with_auth(const struct ast_sip_auth_vector *auths,</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Create new tdata with auth based on original tdata</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param auth_ids_vector Vector of auth IDs retrieved from endpoint</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param challenge rdata of the response from the UAS with challenge</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param old_request tdata from the original request</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param new_request tdata of the new request with the auth</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This function is what's registered with ast_sip_register_outbound_authenticator()</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 success</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval -1 failure</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int digest_create_request_with_auth(const struct ast_sip_auth_vector *auth_ids_vector,</span><br><span> pjsip_rx_data *challenge, pjsip_tx_data *old_request, pjsip_tx_data **new_request)</span><br><span> {</span><br><span> pjsip_auth_clt_sess auth_sess;</span><br><span> pjsip_cseq_hdr *cseq;</span><br><span> pj_status_t status;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_auth_objects_vector auth_objects_vector;</span><br><span style="color: hsl(120, 100%, 40%);">+ size_t auth_object_count = 0;</span><br><span> struct ast_sip_endpoint *endpoint;</span><br><span> char *id = NULL;</span><br><span> const char *id_type;</span><br><span style="color: hsl(0, 100%, 40%);">- pjsip_www_authenticate_hdr *auth_hdr;</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_str *realms;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str *realms = NULL;</span><br><span> pjsip_dialog *dlg;</span><br><span style="color: hsl(120, 100%, 40%);">+ int res = -1;</span><br><span> </span><br><span> dlg = pjsip_rdata_get_dlg(challenge);</span><br><span> if (dlg) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* The only thing we use endpoint for is to get an id for error/debug messages */</span><br><span> endpoint = ast_sip_dialog_get_endpoint(dlg);</span><br><span> id = endpoint ? ast_strdupa(ast_sorcery_object_get_id(endpoint)) : NULL;</span><br><span> ao2_cleanup(endpoint);</span><br><span> id_type = "Endpoint";</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* If there was no dialog, then this is probably a REGISTER so no endpoint */</span><br><span> if (!id) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* The only thing we use the address for is to get an id for error/debug messages */</span><br><span> id = ast_alloca(AST_SOCKADDR_BUFLEN);</span><br><span> pj_sockaddr_print(&challenge->pkt_info.src_addr, id, AST_SOCKADDR_BUFLEN, 3);</span><br><span> id_type = "Host";</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- auth_hdr = get_auth_header(challenge, NULL);</span><br><span style="color: hsl(0, 100%, 40%);">- if (auth_hdr == NULL) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_log(LOG_ERROR, "%s: '%s': Unable to find authenticate header in challenge.\n",</span><br><span style="color: hsl(0, 100%, 40%);">- id_type, id);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!auth_ids_vector || AST_VECTOR_SIZE(auth_ids_vector) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "%s: '%s': There were no auth ids available\n", id_type, id);</span><br><span> return -1;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if (AST_VECTOR_INIT(&auth_objects_vector, AST_VECTOR_SIZE(auth_ids_vector)) != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "%s: '%s': Couldn't initialize auth object vector\n", id_type, id);</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * We don't really care about ast_sip_retrieve_auths_vector()'s return code</span><br><span style="color: hsl(120, 100%, 40%);">+ * because we're checking the count of objects in the vector.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Don't forget to call</span><br><span style="color: hsl(120, 100%, 40%);">+ * ast_sip_cleanup_auth_objects_vector(&auth_objects_vector);</span><br><span style="color: hsl(120, 100%, 40%);">+ * AST_VECTOR_FREE(&auth_objects_vector);</span><br><span style="color: hsl(120, 100%, 40%);">+ * when you're done with the vector</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_retrieve_auths_vector(auth_ids_vector, &auth_objects_vector);</span><br><span style="color: hsl(120, 100%, 40%);">+ auth_object_count = AST_VECTOR_SIZE(&auth_objects_vector);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (auth_object_count == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * If none of the auth ids were found, we can't continue.</span><br><span style="color: hsl(120, 100%, 40%);">+ * We're OK if there's at least one left.</span><br><span style="color: hsl(120, 100%, 40%);">+ * ast_sip_retrieve_auths_vector() will print a warning for every</span><br><span style="color: hsl(120, 100%, 40%);">+ * id that wasn't found.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ res = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ memset(&auth_sess, 0, sizeof(auth_sess));</span><br><span> if (pjsip_auth_clt_init(&auth_sess, ast_sip_get_pjsip_endpoint(),</span><br><span> old_request->pool, 0) != PJ_SUCCESS) {</span><br><span> ast_log(LOG_ERROR, "%s: '%s': Failed to initialize client authentication session\n",</span><br><span> id_type, id);</span><br><span style="color: hsl(0, 100%, 40%);">- return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ res = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ goto cleanup;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- if (set_outbound_authentication_credentials(&auth_sess, auths, challenge, auth_hdr)) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_log(LOG_WARNING, "%s: '%s': Failed to set authentication credentials\n",</span><br><span style="color: hsl(0, 100%, 40%);">- id_type, id);</span><br><span style="color: hsl(0, 100%, 40%);">-#if defined(HAVE_PJSIP_AUTH_CLT_DEINIT)</span><br><span style="color: hsl(0, 100%, 40%);">- /* In case it is not a noop here in the future. */</span><br><span style="color: hsl(0, 100%, 40%);">- pjsip_auth_clt_deinit(&auth_sess);</span><br><span style="color: hsl(0, 100%, 40%);">-#endif</span><br><span style="color: hsl(0, 100%, 40%);">- return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * realms is used only for displaying good error messages.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ realms = ast_str_create(32);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!realms) {</span><br><span style="color: hsl(120, 100%, 40%);">+ res = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ goto cleanup;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Load pjproject with the valid credentials for the Authentication headers</span><br><span style="color: hsl(120, 100%, 40%);">+ * received on the 401 or 407 response.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ status = set_outbound_authentication_credentials(&auth_sess, &auth_objects_vector, challenge, realms);</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (status) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case PJ_SUCCESS:</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ case PJSIP_ENOCREDENTIAL:</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING,</span><br><span style="color: hsl(120, 100%, 40%);">+ "%s: '%s': No auth objects matching realm(s) '%s' from challenge found.\n", id_type, id,</span><br><span style="color: hsl(120, 100%, 40%);">+ realms ? ast_str_buffer(realms) : "<none>");</span><br><span style="color: hsl(120, 100%, 40%);">+ res = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ default:</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "%s: '%s': Failed to set authentication credentials\n", id_type, id);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * reinit_req actually creates the Authorization headers to send on</span><br><span style="color: hsl(120, 100%, 40%);">+ * the next request. If reinit_req already has a cached credential</span><br><span style="color: hsl(120, 100%, 40%);">+ * from an earlier successful authorization, it'll use it. Otherwise</span><br><span style="color: hsl(120, 100%, 40%);">+ * it'll create a new authorization and cache it.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span> status = pjsip_auth_clt_reinit_req(&auth_sess, challenge, old_request, new_request);</span><br><span style="color: hsl(0, 100%, 40%);">-#if defined(HAVE_PJSIP_AUTH_CLT_DEINIT)</span><br><span style="color: hsl(0, 100%, 40%);">- /* Release any cached auths */</span><br><span style="color: hsl(0, 100%, 40%);">- pjsip_auth_clt_deinit(&auth_sess);</span><br><span style="color: hsl(0, 100%, 40%);">-#endif</span><br><span> </span><br><span> switch (status) {</span><br><span> case PJ_SUCCESS:</span><br><span>@@ -167,22 +521,16 @@</span><br><span> cseq = pjsip_msg_find_hdr((*new_request)->msg, PJSIP_H_CSEQ, NULL);</span><br><span> ast_assert(cseq != NULL);</span><br><span> ++cseq->cseq;</span><br><span style="color: hsl(0, 100%, 40%);">- return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ goto cleanup;</span><br><span> case PJSIP_ENOCREDENTIAL:</span><br><span style="color: hsl(0, 100%, 40%);">- realms = ast_str_create(32);</span><br><span style="color: hsl(0, 100%, 40%);">- if (realms) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_str_append(&realms, 0, "%.*s", (int)auth_hdr->challenge.common.realm.slen,</span><br><span style="color: hsl(0, 100%, 40%);">- auth_hdr->challenge.common.realm.ptr);</span><br><span style="color: hsl(0, 100%, 40%);">- while((auth_hdr = get_auth_header(challenge, auth_hdr->next))) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_str_append(&realms, 0, ",%.*s", (int)auth_hdr->challenge.common.realm.slen,</span><br><span style="color: hsl(0, 100%, 40%);">- auth_hdr->challenge.common.realm.ptr);</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * This should be rare since set_outbound_authentication_credentials()</span><br><span style="color: hsl(120, 100%, 40%);">+ * did the matching but you never know.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span> ast_log(LOG_WARNING,</span><br><span style="color: hsl(0, 100%, 40%);">- "%s: '%s': Unable to create request with auth. "</span><br><span style="color: hsl(0, 100%, 40%);">- "No auth credentials for realm(s) '%s' in challenge.\n", id_type, id,</span><br><span style="color: hsl(0, 100%, 40%);">- realms ? ast_str_buffer(realms) : "<unknown>");</span><br><span style="color: hsl(0, 100%, 40%);">- ast_free(realms);</span><br><span style="color: hsl(120, 100%, 40%);">+ "%s: '%s': No auth objects matching realm(s) '%s' from challenge found.\n", id_type, id,</span><br><span style="color: hsl(120, 100%, 40%);">+ realms ? ast_str_buffer(realms) : "<none>");</span><br><span> break;</span><br><span> case PJSIP_EAUTHSTALECOUNT:</span><br><span> ast_log(LOG_WARNING,</span><br><span>@@ -198,8 +546,19 @@</span><br><span> id_type, id);</span><br><span> break;</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+ res = -1;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+cleanup:</span><br><span style="color: hsl(120, 100%, 40%);">+#if defined(HAVE_PJSIP_AUTH_CLT_DEINIT)</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Release any cached auths */</span><br><span style="color: hsl(120, 100%, 40%);">+ pjsip_auth_clt_deinit(&auth_sess);</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_cleanup_auth_objects_vector(&auth_objects_vector);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_FREE(&auth_objects_vector);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(realms);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return res;</span><br><span> }</span><br><span> </span><br><span> static struct ast_sip_outbound_authenticator digest_authenticator = {</span><br><span>diff --git a/third-party/pjproject/patches/0090-Skip-unsupported-digest-algorithm-2408.patch b/third-party/pjproject/patches/0090-Skip-unsupported-digest-algorithm-2408.patch</span><br><span>new file mode 100644</span><br><span>index 0000000..a2db220</span><br><span>--- /dev/null</span><br><span>+++ b/third-party/pjproject/patches/0090-Skip-unsupported-digest-algorithm-2408.patch</span><br><span>@@ -0,0 +1,212 @@</span><br><span style="color: hsl(120, 100%, 40%);">+From bdbeb7c4b2b11efc2e59f5dee7aa4360a2bc9fff Mon Sep 17 00:00:00 2001</span><br><span style="color: hsl(120, 100%, 40%);">+From: sauwming <ming@teluu.com></span><br><span style="color: hsl(120, 100%, 40%);">+Date: Thu, 22 Apr 2021 14:03:28 +0800</span><br><span style="color: hsl(120, 100%, 40%);">+Subject: [PATCH 90/90] Skip unsupported digest algorithm (#2408)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Co-authored-by: Nanang Izzuddin <nanang@teluu.com></span><br><span style="color: hsl(120, 100%, 40%);">+---</span><br><span style="color: hsl(120, 100%, 40%);">+ pjsip/src/pjsip/sip_auth_client.c | 32 +++++--</span><br><span style="color: hsl(120, 100%, 40%);">+ tests/pjsua/scripts-sipp/uas-auth-two-algo.py | 7 ++</span><br><span style="color: hsl(120, 100%, 40%);">+ .../pjsua/scripts-sipp/uas-auth-two-algo.xml | 83 +++++++++++++++++++</span><br><span style="color: hsl(120, 100%, 40%);">+ 3 files changed, 117 insertions(+), 5 deletions(-)</span><br><span style="color: hsl(120, 100%, 40%);">+ create mode 100644 tests/pjsua/scripts-sipp/uas-auth-two-algo.py</span><br><span style="color: hsl(120, 100%, 40%);">+ create mode 100644 tests/pjsua/scripts-sipp/uas-auth-two-algo.xml</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+diff --git a/pjsip/src/pjsip/sip_auth_client.c b/pjsip/src/pjsip/sip_auth_client.c</span><br><span style="color: hsl(120, 100%, 40%);">+index 828b04db9..7eb2f5cd1 100644</span><br><span style="color: hsl(120, 100%, 40%);">+--- a/pjsip/src/pjsip/sip_auth_client.c</span><br><span>++++ b/pjsip/src/pjsip/sip_auth_client.c</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -1042,7 +1042,7 @@ static pj_status_t process_auth( pj_pool_t *req_pool,</span><br><span style="color: hsl(120, 100%, 40%);">+ pjsip_hdr *hdr;</span><br><span style="color: hsl(120, 100%, 40%);">+ pj_status_t status;</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+- /* See if we have sent authorization header for this realm */</span><br><span style="color: hsl(120, 100%, 40%);">++ /* See if we have sent authorization header for this realm (and scheme) */</span><br><span style="color: hsl(120, 100%, 40%);">+ hdr = tdata->msg->hdr.next;</span><br><span style="color: hsl(120, 100%, 40%);">+ while (hdr != &tdata->msg->hdr) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if ((hchal->type == PJSIP_H_WWW_AUTHENTICATE &&</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -1052,7 +1052,8 @@ static pj_status_t process_auth( pj_pool_t *req_pool,</span><br><span style="color: hsl(120, 100%, 40%);">+ {</span><br><span style="color: hsl(120, 100%, 40%);">+ sent_auth = (pjsip_authorization_hdr*) hdr;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (pj_stricmp(&hchal->challenge.common.realm,</span><br><span style="color: hsl(120, 100%, 40%);">+- &sent_auth->credential.common.realm )==0)</span><br><span style="color: hsl(120, 100%, 40%);">++ &sent_auth->credential.common.realm)==0 &&</span><br><span style="color: hsl(120, 100%, 40%);">++ pj_stricmp(&hchal->scheme, &sent_auth->scheme)==0)</span><br><span style="color: hsl(120, 100%, 40%);">+ {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* If this authorization has empty response, remove it. */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (pj_stricmp(&sent_auth->scheme, &pjsip_DIGEST_STR)==0 &&</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -1062,6 +1063,14 @@ static pj_status_t process_auth( pj_pool_t *req_pool,</span><br><span style="color: hsl(120, 100%, 40%);">+ hdr = hdr->next;</span><br><span style="color: hsl(120, 100%, 40%);">+ pj_list_erase(sent_auth);</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span style="color: hsl(120, 100%, 40%);">++ } else</span><br><span style="color: hsl(120, 100%, 40%);">++ if (pj_stricmp(&sent_auth->scheme, &pjsip_DIGEST_STR)==0 &&</span><br><span style="color: hsl(120, 100%, 40%);">++ pj_stricmp(&sent_auth->credential.digest.algorithm,</span><br><span style="color: hsl(120, 100%, 40%);">++ &hchal->challenge.digest.algorithm)!=0)</span><br><span style="color: hsl(120, 100%, 40%);">++ {</span><br><span style="color: hsl(120, 100%, 40%);">++ /* Same 'digest' scheme but different algo */</span><br><span style="color: hsl(120, 100%, 40%);">++ hdr = hdr->next;</span><br><span style="color: hsl(120, 100%, 40%);">++ continue;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Found previous authorization attempt */</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -1155,9 +1164,10 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_reinit_req( pjsip_auth_clt_sess *sess,</span><br><span style="color: hsl(120, 100%, 40%);">+ {</span><br><span style="color: hsl(120, 100%, 40%);">+ pjsip_tx_data *tdata;</span><br><span style="color: hsl(120, 100%, 40%);">+ const pjsip_hdr *hdr;</span><br><span style="color: hsl(120, 100%, 40%);">+- unsigned chal_cnt;</span><br><span style="color: hsl(120, 100%, 40%);">++ unsigned chal_cnt, auth_cnt;</span><br><span style="color: hsl(120, 100%, 40%);">+ pjsip_via_hdr *via;</span><br><span style="color: hsl(120, 100%, 40%);">+ pj_status_t status;</span><br><span style="color: hsl(120, 100%, 40%);">++ pj_status_t last_auth_err;</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+ PJ_ASSERT_RETURN(sess && rdata && old_request && new_request,</span><br><span style="color: hsl(120, 100%, 40%);">+ PJ_EINVAL);</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -1178,6 +1188,8 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_reinit_req( pjsip_auth_clt_sess *sess,</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ hdr = rdata->msg_info.msg->hdr.next;</span><br><span style="color: hsl(120, 100%, 40%);">+ chal_cnt = 0;</span><br><span style="color: hsl(120, 100%, 40%);">++ auth_cnt = 0;</span><br><span style="color: hsl(120, 100%, 40%);">++ last_auth_err = PJSIP_EAUTHNOAUTH;</span><br><span style="color: hsl(120, 100%, 40%);">+ while (hdr != &rdata->msg_info.msg->hdr) {</span><br><span style="color: hsl(120, 100%, 40%);">+ pjsip_cached_auth *cached_auth;</span><br><span style="color: hsl(120, 100%, 40%);">+ const pjsip_www_authenticate_hdr *hchal;</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -1222,8 +1234,13 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_reinit_req( pjsip_auth_clt_sess *sess,</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ status = process_auth(tdata->pool, hchal, tdata->msg->line.req.uri,</span><br><span style="color: hsl(120, 100%, 40%);">+ tdata, sess, cached_auth, &hauth);</span><br><span style="color: hsl(120, 100%, 40%);">+- if (status != PJ_SUCCESS)</span><br><span style="color: hsl(120, 100%, 40%);">+- return status;</span><br><span style="color: hsl(120, 100%, 40%);">++ if (status != PJ_SUCCESS) {</span><br><span style="color: hsl(120, 100%, 40%);">++ last_auth_err = status;</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++ /* Process next header. */</span><br><span style="color: hsl(120, 100%, 40%);">++ hdr = hdr->next;</span><br><span style="color: hsl(120, 100%, 40%);">++ continue;</span><br><span style="color: hsl(120, 100%, 40%);">++ }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+ if (pj_pool_get_used_size(cached_auth->pool) ></span><br><span style="color: hsl(120, 100%, 40%);">+ PJSIP_AUTH_CACHED_POOL_MAX_SIZE) </span><br><span style="color: hsl(120, 100%, 40%);">+@@ -1236,12 +1253,17 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_reinit_req( pjsip_auth_clt_sess *sess,</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+ /* Process next header. */</span><br><span style="color: hsl(120, 100%, 40%);">+ hdr = hdr->next;</span><br><span style="color: hsl(120, 100%, 40%);">++ auth_cnt++;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+ /* Check if challenge is present */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (chal_cnt == 0)</span><br><span style="color: hsl(120, 100%, 40%);">+ return PJSIP_EAUTHNOCHAL;</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">++ /* Check if any authorization header has been created */</span><br><span style="color: hsl(120, 100%, 40%);">++ if (auth_cnt == 0)</span><br><span style="color: hsl(120, 100%, 40%);">++ return last_auth_err;</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Remove branch param in Via header. */</span><br><span style="color: hsl(120, 100%, 40%);">+ via = (pjsip_via_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ via->branch_param.slen = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+diff --git a/tests/pjsua/scripts-sipp/uas-auth-two-algo.py b/tests/pjsua/scripts-sipp/uas-auth-two-algo.py</span><br><span style="color: hsl(120, 100%, 40%);">+new file mode 100644</span><br><span style="color: hsl(120, 100%, 40%);">+index 000000000..c79c9f6d3</span><br><span style="color: hsl(120, 100%, 40%);">+--- /dev/null</span><br><span>++++ b/tests/pjsua/scripts-sipp/uas-auth-two-algo.py</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -0,0 +1,7 @@</span><br><span style="color: hsl(120, 100%, 40%);">++# $Id$</span><br><span style="color: hsl(120, 100%, 40%);">++#</span><br><span style="color: hsl(120, 100%, 40%);">++import inc_const as const</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++PJSUA = ["--null-audio --max-calls=1 --id=sip:a@localhost --username=a --realm=* --registrar=$SIPP_URI"]</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++PJSUA_EXPECTS = [[0, "registration success", ""]]</span><br><span style="color: hsl(120, 100%, 40%);">+diff --git a/tests/pjsua/scripts-sipp/uas-auth-two-algo.xml b/tests/pjsua/scripts-sipp/uas-auth-two-algo.xml</span><br><span style="color: hsl(120, 100%, 40%);">+new file mode 100644</span><br><span style="color: hsl(120, 100%, 40%);">+index 000000000..bd4871940</span><br><span style="color: hsl(120, 100%, 40%);">+--- /dev/null</span><br><span>++++ b/tests/pjsua/scripts-sipp/uas-auth-two-algo.xml</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -0,0 +1,83 @@</span><br><span style="color: hsl(120, 100%, 40%);">++<?xml version="1.0" encoding="ISO-8859-1" ?></span><br><span style="color: hsl(120, 100%, 40%);">++<!DOCTYPE scenario SYSTEM "sipp.dtd"></span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++<scenario name="Basic UAS responder"></span><br><span style="color: hsl(120, 100%, 40%);">++ <recv request="REGISTER" crlf="true"></span><br><span style="color: hsl(120, 100%, 40%);">++ </recv></span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++ <send></span><br><span style="color: hsl(120, 100%, 40%);">++ <![CDATA[</span><br><span style="color: hsl(120, 100%, 40%);">++ SIP/2.0 100 Trying</span><br><span style="color: hsl(120, 100%, 40%);">++ [last_Via:];received=1.1.1.1;rport=1111</span><br><span style="color: hsl(120, 100%, 40%);">++ [last_From:]</span><br><span style="color: hsl(120, 100%, 40%);">++ [last_To:];tag=[call_number]</span><br><span style="color: hsl(120, 100%, 40%);">++ [last_Call-ID:]</span><br><span style="color: hsl(120, 100%, 40%);">++ [last_CSeq:]</span><br><span style="color: hsl(120, 100%, 40%);">++ Content-Length: 0</span><br><span style="color: hsl(120, 100%, 40%);">++ ]]></span><br><span style="color: hsl(120, 100%, 40%);">++ </send></span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++ <send></span><br><span style="color: hsl(120, 100%, 40%);">++ <![CDATA[</span><br><span style="color: hsl(120, 100%, 40%);">++ SIP/2.0 401 Unauthorized</span><br><span style="color: hsl(120, 100%, 40%);">++ [last_Via:];received=1.1.1.1;rport=1111</span><br><span style="color: hsl(120, 100%, 40%);">++ [last_From:]</span><br><span style="color: hsl(120, 100%, 40%);">++ [last_To:];tag=[call_number]</span><br><span style="color: hsl(120, 100%, 40%);">++ [last_Call-ID:]</span><br><span style="color: hsl(120, 100%, 40%);">++ [last_CSeq:]</span><br><span style="color: hsl(120, 100%, 40%);">++ WWW-Authenticate: Digest realm="sip.linphone.org", nonce="PARV4gAAAADgw3asAADW8zsi5BEAAAAA", opaque="+GNywA==", algorithm=SHA-256, qop="auth"</span><br><span style="color: hsl(120, 100%, 40%);">++ WWW-Authenticate: Digest realm="sip.linphone.org", nonce="PARV4gAAAADgw3asAADW8zsi5BEAAAAA", opaque="+GNywA==", algorithm=MD5, qop="auth"</span><br><span style="color: hsl(120, 100%, 40%);">++ WWW-Authenticate: Digest realm="sip.linphone.org", nonce="PARV4gAAAADgw3asAADW8zsi5BEAAAAA", opaque="+GNywA==", algorithm=MD2, qop="auth"</span><br><span style="color: hsl(120, 100%, 40%);">++ Content-Length: 0</span><br><span style="color: hsl(120, 100%, 40%);">++ ]]></span><br><span style="color: hsl(120, 100%, 40%);">++ </send></span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++ <recv request="REGISTER" crlf="true"></span><br><span style="color: hsl(120, 100%, 40%);">++ <action></span><br><span style="color: hsl(120, 100%, 40%);">++ <ereg regexp=".*"</span><br><span style="color: hsl(120, 100%, 40%);">++ search_in="hdr"</span><br><span style="color: hsl(120, 100%, 40%);">++ header="Authorization:"</span><br><span style="color: hsl(120, 100%, 40%);">++ assign_to="have_auth" /></span><br><span style="color: hsl(120, 100%, 40%);">++ </action></span><br><span style="color: hsl(120, 100%, 40%);">++ </recv></span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++ <nop next="resp_okay" test="have_auth" /></span><br><span style="color: hsl(120, 100%, 40%);">++ </span><br><span style="color: hsl(120, 100%, 40%);">++ <send next="end"></span><br><span style="color: hsl(120, 100%, 40%);">++ <![CDATA[</span><br><span style="color: hsl(120, 100%, 40%);">++ SIP/2.0 403 no auth</span><br><span style="color: hsl(120, 100%, 40%);">++ [last_Via:];received=1.1.1.1;rport=1111</span><br><span style="color: hsl(120, 100%, 40%);">++ [last_From:]</span><br><span style="color: hsl(120, 100%, 40%);">++ [last_To:];tag=[call_number]</span><br><span style="color: hsl(120, 100%, 40%);">++ [last_Call-ID:]</span><br><span style="color: hsl(120, 100%, 40%);">++ [last_CSeq:]</span><br><span style="color: hsl(120, 100%, 40%);">++ [last_Contact:]</span><br><span style="color: hsl(120, 100%, 40%);">++ Content-Length: 0</span><br><span style="color: hsl(120, 100%, 40%);">++ ]]></span><br><span style="color: hsl(120, 100%, 40%);">++ </send></span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++ <label id="resp_okay" /></span><br><span style="color: hsl(120, 100%, 40%);">++ </span><br><span style="color: hsl(120, 100%, 40%);">++ <send></span><br><span style="color: hsl(120, 100%, 40%);">++ <![CDATA[</span><br><span style="color: hsl(120, 100%, 40%);">++ SIP/2.0 200 OK</span><br><span style="color: hsl(120, 100%, 40%);">++ [last_Via:];received=1.1.1.1;rport=1111</span><br><span style="color: hsl(120, 100%, 40%);">++ [last_From:]</span><br><span style="color: hsl(120, 100%, 40%);">++ [last_To:];tag=[call_number]</span><br><span style="color: hsl(120, 100%, 40%);">++ [last_Call-ID:]</span><br><span style="color: hsl(120, 100%, 40%);">++ [last_CSeq:]</span><br><span style="color: hsl(120, 100%, 40%);">++ [last_Contact:]</span><br><span style="color: hsl(120, 100%, 40%);">++ Content-Length: 0</span><br><span style="color: hsl(120, 100%, 40%);">++ ]]></span><br><span style="color: hsl(120, 100%, 40%);">++ </send></span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++ <label id="end" /></span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++ <!-- definition of the response time repartition table (unit is ms) --></span><br><span style="color: hsl(120, 100%, 40%);">++ <ResponseTimeRepartition value="10, 20, 30, 40, 50, 100, 150, 200"/></span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++ <!-- definition of the call length repartition table (unit is ms) --></span><br><span style="color: hsl(120, 100%, 40%);">++ <CallLengthRepartition value="10, 50, 100, 500, 1000, 5000, 10000"/></span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++</scenario></span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">+-- </span><br><span style="color: hsl(120, 100%, 40%);">+2.31.1</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/15904">change 15904</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/c/asterisk/+/15904"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: I3aef5ce4fe1d27e48d61268520f284d15d650281 </div>
<div style="display:none"> Gerrit-Change-Number: 15904 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>