<p>George Joseph has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/15891">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/91/15891/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/+/15891">change 15891</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/+/15891"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 18 </div>
<div style="display:none"> Gerrit-Change-Id: I3aef5ce4fe1d27e48d61268520f284d15d650281 </div>
<div style="display:none"> Gerrit-Change-Number: 15891 </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>