<p>Maximilian Fridrich has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/19389">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">res_pjsip: Add mediasec capabilities.<br><br>This patch adds support for mediasec SIP headers and SDP attributes.<br>These are defined in RFC 3329, 3GPP TS 24.229 and<br>draft-dawes-sipcore-mediasec-parameter. The new features are<br>implemented so that a backbone for RFC 3329 is present to streamline<br>future work on RFC 3329.<br><br>With this patch, Asterisk can communicate with Deutsche Telekom trunks<br>which require these fields.<br><br>ASTERISK-30032<br><br>Change-Id: Ia7f5b5ba42db18074fdd5428c4e1838728586be2<br>---<br>A contrib/ast-db-manage/config/versions/417c0247fd7e_add_security_negotiation_and_security_.py<br>A doc/CHANGES-staging/res_pjsip_rfc3329.txt<br>M include/asterisk/res_pjsip.h<br>M include/asterisk/res_pjsip_session.h<br>M res/res_pjsip/pjsip_config.xml<br>M res/res_pjsip/pjsip_configuration.c<br>M res/res_pjsip/pjsip_options.c<br>A res/res_pjsip/security_agreements.c<br>M res/res_pjsip_outbound_registration.c<br>A res/res_pjsip_rfc3329.c<br>M res/res_pjsip_sdp_rtp.c<br>M res/res_pjsip_session.c<br>12 files changed, 962 insertions(+), 4 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/89/19389/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/contrib/ast-db-manage/config/versions/417c0247fd7e_add_security_negotiation_and_security_.py b/contrib/ast-db-manage/config/versions/417c0247fd7e_add_security_negotiation_and_security_.py</span><br><span>new file mode 100644</span><br><span>index 0000000..34847fcb</span><br><span>--- /dev/null</span><br><span>+++ b/contrib/ast-db-manage/config/versions/417c0247fd7e_add_security_negotiation_and_security_.py</span><br><span>@@ -0,0 +1,49 @@</span><br><span style="color: hsl(120, 100%, 40%);">+"""add security_negotiation and security_mechanisms to endpoint</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Revision ID: 417c0247fd7e</span><br><span style="color: hsl(120, 100%, 40%);">+Revises: 539f68bede2c</span><br><span style="color: hsl(120, 100%, 40%);">+Create Date: 2022-08-08 15:35:31.416964</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%);">+# revision identifiers, used by Alembic.</span><br><span style="color: hsl(120, 100%, 40%);">+revision = '417c0247fd7e'</span><br><span style="color: hsl(120, 100%, 40%);">+down_revision = '539f68bede2c'</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+from alembic import op</span><br><span style="color: hsl(120, 100%, 40%);">+import sqlalchemy as sa</span><br><span style="color: hsl(120, 100%, 40%);">+from sqlalchemy.dialects.postgresql import ENUM</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+SECURITY_NEGOTIATION_NAME = 'security_negotiation_values'</span><br><span style="color: hsl(120, 100%, 40%);">+SECURITY_NEGOTIATION_VALUES = ['no', 'mediasec']</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+def upgrade():</span><br><span style="color: hsl(120, 100%, 40%);">+    context = op.get_context()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if context.bind.dialect.name == 'postgresql':</span><br><span style="color: hsl(120, 100%, 40%);">+        security_negotiation_values = ENUM(*SECURITY_NEGOTIATION_VALUES, name=SECURITY_NEGOTIATION_NAME)</span><br><span style="color: hsl(120, 100%, 40%);">+        security_negotiation_values.create(op.get_bind(), checkfirst=False)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    op.add_column('ps_endpoints', sa.Column('security_negotiation',</span><br><span style="color: hsl(120, 100%, 40%);">+        ENUM(*SECURITY_NEGOTIATION_VALUES, name=SECURITY_NEGOTIATION_NAME, create_type=False)))</span><br><span style="color: hsl(120, 100%, 40%);">+    op.add_column('ps_endpoints', sa.Column('security_mechanisms', sa.String(512)))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    op.add_column('ps_registrations', sa.Column('security_negotiation',</span><br><span style="color: hsl(120, 100%, 40%);">+        ENUM(*SECURITY_NEGOTIATION_VALUES, name=SECURITY_NEGOTIATION_NAME, create_type=False)))</span><br><span style="color: hsl(120, 100%, 40%);">+    op.add_column('ps_registrations', sa.Column('security_mechanisms', sa.String(512)))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+def downgrade():</span><br><span style="color: hsl(120, 100%, 40%);">+    context = op.get_context()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if context.bind.dialect.name == 'mssql':</span><br><span style="color: hsl(120, 100%, 40%);">+        op.drop_constraint('ck_ps_endpoints_security_negotiation_security_negotiation_values', 'ps_endpoints')</span><br><span style="color: hsl(120, 100%, 40%);">+        op.drop_constraint('ck_ps_registrations_security_negotiation_security_negotiation_values', 'ps_registrations')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    op.drop_column('ps_endpoints', 'security_negotiation')</span><br><span style="color: hsl(120, 100%, 40%);">+    op.drop_column('ps_endpoints', 'security_mechanisms')</span><br><span style="color: hsl(120, 100%, 40%);">+    op.drop_column('ps_registrations', 'security_negotiation')</span><br><span style="color: hsl(120, 100%, 40%);">+    op.drop_column('ps_registrations', 'security_mechanisms')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if context.bind.dialect.name == 'postgresql':</span><br><span style="color: hsl(120, 100%, 40%);">+        enum = ENUM(*SECURITY_NEGOTIATION_VALUES, name=SECURITY_NEGOTIATION_NAME)</span><br><span style="color: hsl(120, 100%, 40%);">+        enum.drop(op.get_bind(), checkfirst=False)</span><br><span>\ No newline at end of file</span><br><span>diff --git a/doc/CHANGES-staging/res_pjsip_rfc3329.txt b/doc/CHANGES-staging/res_pjsip_rfc3329.txt</span><br><span>new file mode 100644</span><br><span>index 0000000..06510b5</span><br><span>--- /dev/null</span><br><span>+++ b/doc/CHANGES-staging/res_pjsip_rfc3329.txt</span><br><span>@@ -0,0 +1,6 @@</span><br><span style="color: hsl(120, 100%, 40%);">+Subject: res_pjsip</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Added options "security_negotiation" and "security_mechanisms" to pjsip</span><br><span style="color: hsl(120, 100%, 40%);">+endpoints and registrations. "security_negotiation" can be set to "no" (default)</span><br><span style="color: hsl(120, 100%, 40%);">+or "mediasec", and "security_mechanisms" can be a list of comma-separated</span><br><span style="color: hsl(120, 100%, 40%);">+security_mechanisms in the form defined by RFC 3329 section 2.2.</span><br><span>diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h</span><br><span>index 3cba04a..743b19f 100644</span><br><span>--- a/include/asterisk/res_pjsip.h</span><br><span>+++ b/include/asterisk/res_pjsip.h</span><br><span>@@ -320,6 +320,45 @@</span><br><span> };</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief The kind of security negotiation</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+enum ast_sip_security_negotiation {</span><br><span style="color: hsl(120, 100%, 40%);">+       /*! No security mechanism negotiation */</span><br><span style="color: hsl(120, 100%, 40%);">+      AST_SIP_SECURITY_NEG_NONE = 0,</span><br><span style="color: hsl(120, 100%, 40%);">+        /*! Use mediasec security mechanism negotiation */</span><br><span style="color: hsl(120, 100%, 40%);">+    AST_SIP_SECURITY_NEG_MEDIASEC,</span><br><span style="color: hsl(120, 100%, 40%);">+        /* Add RFC 3329 (sec-agree) mechanism negotiation in the future */</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%);">+ * \brief The security mechanism type</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+enum ast_sip_security_mechanism_type {</span><br><span style="color: hsl(120, 100%, 40%);">+     AST_SIP_SECURITY_MECH_NONE = 0,</span><br><span style="color: hsl(120, 100%, 40%);">+       /* Use msrp-tls as security mechanism */</span><br><span style="color: hsl(120, 100%, 40%);">+      AST_SIP_SECURITY_MECH_MSRP_TLS,</span><br><span style="color: hsl(120, 100%, 40%);">+       /* Use sdes-srtp as security mechanism */</span><br><span style="color: hsl(120, 100%, 40%);">+     AST_SIP_SECURITY_MECH_SDES_SRTP,</span><br><span style="color: hsl(120, 100%, 40%);">+      /* Use dtls-srtp as security mechanism */</span><br><span style="color: hsl(120, 100%, 40%);">+     AST_SIP_SECURITY_MECH_DTLS_SRTP,</span><br><span style="color: hsl(120, 100%, 40%);">+      /* Add RFC 3329 (sec-agree) mechanisms like tle, digest, ipsec-ike in the future */</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%);">+ * \brief Structure representing a security mechanism as defined in RFC 3329</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_sip_security_mechanism {</span><br><span style="color: hsl(120, 100%, 40%);">+        /* Used to determine which security mechanism to use. */</span><br><span style="color: hsl(120, 100%, 40%);">+      enum ast_sip_security_mechanism_type type;</span><br><span style="color: hsl(120, 100%, 40%);">+    /* The preference of this security mechanism. The higher the value, the more preferred. */</span><br><span style="color: hsl(120, 100%, 40%);">+    float qvalue;</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Optional mechanism parameters. */</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ast_vector_string mechanism_parameters;</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_security_mechanism_vector, struct ast_sip_security_mechanism *);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span>  * \brief Contact associated with an address of record</span><br><span>  */</span><br><span> struct ast_sip_contact {</span><br><span>@@ -390,6 +429,13 @@</span><br><span>         );</span><br><span>   /*! The round trip time in microseconds */</span><br><span>   int64_t rtt;</span><br><span style="color: hsl(120, 100%, 40%);">+  /*!</span><br><span style="color: hsl(120, 100%, 40%);">+    * The security mechanism list of the contact (RFC 3329).</span><br><span style="color: hsl(120, 100%, 40%);">+      * Stores the values of Security-Server headers in 401/421/494 responses to an</span><br><span style="color: hsl(120, 100%, 40%);">+         * in-dialog request or successful outbound registration which will be used to</span><br><span style="color: hsl(120, 100%, 40%);">+         * set the Security-Verify headers of all subsequent requests to the contact.</span><br><span style="color: hsl(120, 100%, 40%);">+  */</span><br><span style="color: hsl(120, 100%, 40%);">+   struct ast_sip_security_mechanism_vector security_mechanisms;</span><br><span>        /*! Current status for a contact (default - unavailable) */</span><br><span>  enum ast_sip_contact_status_type status;</span><br><span>     /*! Last status for a contact (default - unavailable) */</span><br><span>@@ -987,6 +1033,10 @@</span><br><span>     unsigned int suppress_q850_reason_headers;</span><br><span>   /*! Ignore 183 if no SDP is present */</span><br><span>       unsigned int ignore_183_without_sdp;</span><br><span style="color: hsl(120, 100%, 40%);">+  /*! Type of security negotiation to use (RFC 3329). */</span><br><span style="color: hsl(120, 100%, 40%);">+        enum ast_sip_security_negotiation security_negotiation;</span><br><span style="color: hsl(120, 100%, 40%);">+       /*! Client security mechanisms (RFC 3329). */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_security_mechanism_vector security_mechanisms;</span><br><span>        /*! Set which STIR/SHAKEN behaviors we want on this endpoint */</span><br><span>      unsigned int stir_shaken;</span><br><span>    /*! Should we authenticate OPTIONS requests per RFC 3261? */</span><br><span>@@ -1038,6 +1088,87 @@</span><br><span> int ast_sip_is_media_type_in(pjsip_media_type *a, ...) attribute_sentinel;</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Add security headers to transmission data</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param security_mechanisms Vector of security mechanisms.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param header_name The header name under which to add the security mechanisms.</span><br><span style="color: hsl(120, 100%, 40%);">+ * One of Security-Client, Security-Server, Security-Verify.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param add_qval If zero, don't add the q-value to the header.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param tdata The transmission data.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 Success</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval non-zero Failure</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_sip_add_security_headers(struct ast_sip_security_mechanism_vector *security_mechanisms,</span><br><span style="color: hsl(120, 100%, 40%);">+         const char *header_name, int add_qval, pjsip_tx_data *tdata);</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 Append to security mechanism vector from SIP header</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param hdr The header of the security mechanisms.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param security_mechanisms Vector of security mechanisms to append to.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Header name must be one of Security-Client, Security-Server, Security-Verify.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void ast_sip_header_to_security_mechanism(const pjsip_generic_string_hdr *hdr,</span><br><span style="color: hsl(120, 100%, 40%);">+               struct ast_sip_security_mechanism_vector *security_mechanisms);</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 Initialize security mechanism vector from string of security mechanisms.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param security_mechanisms Pointer to vector of security mechanisms to initialize.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param value String of security mechanisms as defined in RFC 3329.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 Success</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval non-zero Failure</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_sip_security_mechanism_vector_init(struct ast_sip_security_mechanism_vector *security_mechanism, const char *value);</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 Removes all headers of a specific name and value from a pjsip_msg.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param msg PJSIP message from which to remove headers.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param hdr_name Name of the header to remove.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param value Optional string value of the header to remove.</span><br><span style="color: hsl(120, 100%, 40%);">+ * If NULL, remove all headers of given hdr_name.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void ast_sip_remove_headers_by_name_and_value(pjsip_msg *msg, const pj_str_t *hdr_name, const char* value);</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 Duplicate a security mechanism.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param dst Security mechanism to duplicate to.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param src Security mechanism to duplicate.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void ast_sip_security_mechanisms_vector_copy(struct ast_sip_security_mechanism_vector *dst,</span><br><span style="color: hsl(120, 100%, 40%);">+      const struct ast_sip_security_mechanism_vector *src);</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 Free contents of a security mechanism vector.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param security_mechanisms Vector whose contents are to be freed</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void ast_sip_security_mechanisms_vector_destroy(struct ast_sip_security_mechanism_vector *security_mechanisms);</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 Allocate a security mechanism from a string.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param security_mechanism Pointer-pointer to the security mechanism to allocate.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param value The security mechanism string as defined in RFC 3329 (section 2.2)</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param ... in the form <mechanism_name>;q=<q_value>;<mechanism_parameters></span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 Success</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval non-zero Failure</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_sip_str_to_security_mechanism(struct ast_sip_security_mechanism **security_mechanism, const char *value);</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 Set the security negotiation based on a given string.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param security_negotiation Security negotiation enum to set.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param val String that represents a security_negotiation value.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 Success</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval non-zero Failure</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_sip_set_security_negotiation(enum ast_sip_security_negotiation *security_negotiation, const char *val);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span>  * \brief Initialize an auth vector with the configured values.</span><br><span>  *</span><br><span>  * \param vector Vector to initialize</span><br><span>diff --git a/include/asterisk/res_pjsip_session.h b/include/asterisk/res_pjsip_session.h</span><br><span>index 49e6007..471ecd4 100644</span><br><span>--- a/include/asterisk/res_pjsip_session.h</span><br><span>+++ b/include/asterisk/res_pjsip_session.h</span><br><span>@@ -33,6 +33,8 @@</span><br><span> /* Needed for pjmedia_sdp_session and pjsip_inv_session */</span><br><span> #include <pjsip_ua.h></span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/* Needed for ast_sip_security_mechanism_vector */</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_pjsip.h"</span><br><span> </span><br><span> /* Forward declarations */</span><br><span> struct ast_sip_endpoint;</span><br><span>diff --git a/res/res_pjsip/pjsip_config.xml b/res/res_pjsip/pjsip_config.xml</span><br><span>index 3cec9a4..9e3b17d 100644</span><br><span>--- a/res/res_pjsip/pjsip_config.xml</span><br><span>+++ b/res/res_pjsip/pjsip_config.xml</span><br><span>@@ -1472,6 +1472,22 @@</span><br><span>                                                responses.</para></span><br><span>                                      </description></span><br><span>                                 </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+                         <configOption name="security_negotiation" default="no"></span><br><span style="color: hsl(120, 100%, 40%);">+                                     <synopsis>The kind of security agreement negotiation to use. Currently, only mediasec is supported.</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+                                    <description></span><br><span style="color: hsl(120, 100%, 40%);">+                                           <enumlist></span><br><span style="color: hsl(120, 100%, 40%);">+                                                      <enum name="no" /></span><br><span style="color: hsl(120, 100%, 40%);">+                                                    <enum name="mediasec" /></span><br><span style="color: hsl(120, 100%, 40%);">+                                              </enumlist></span><br><span style="color: hsl(120, 100%, 40%);">+                                     </description></span><br><span style="color: hsl(120, 100%, 40%);">+                          </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+                         <configOption name="security_mechanisms"></span><br><span style="color: hsl(120, 100%, 40%);">+                                     <synopsis>List of security mechanisms supported.</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+                                       <description><para></span><br><span style="color: hsl(120, 100%, 40%);">+                                               This is a comma-delimited list of security mechanisms to use. Each security mechanism</span><br><span style="color: hsl(120, 100%, 40%);">+                                         must be in the form defined by RFC 3329 section 2.2.</span><br><span style="color: hsl(120, 100%, 40%);">+                                  </para></description></span><br><span style="color: hsl(120, 100%, 40%);">+                             </configOption></span><br><span>                                <configOption name="geoloc_incoming_call_profile" default=""></span><br><span>                                      <synopsis>Geolocation profile to apply to incoming calls</synopsis></span><br><span>                                      <description><para></span><br><span>diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c</span><br><span>index cf42f88..e63018e 100644</span><br><span>--- a/res/res_pjsip/pjsip_configuration.c</span><br><span>+++ b/res/res_pjsip/pjsip_configuration.c</span><br><span>@@ -256,6 +256,31 @@</span><br><span>      return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static int security_mechanism_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct ast_sip_endpoint *endpoint = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    return ast_sip_security_mechanism_vector_init(&endpoint->security_mechanisms, var->value);</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%);">+int ast_sip_set_security_negotiation(enum ast_sip_security_negotiation *security_negotiation, const char *val) {</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!strcasecmp("no", val)) {</span><br><span style="color: hsl(120, 100%, 40%);">+               *security_negotiation = AST_SIP_SECURITY_NEG_NONE;</span><br><span style="color: hsl(120, 100%, 40%);">+    } else if (!strcasecmp("mediasec", val)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          *security_negotiation = AST_SIP_SECURITY_NEG_MEDIASEC;</span><br><span style="color: hsl(120, 100%, 40%);">+        } else {</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%);">+     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%);">+static int security_negotiation_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_endpoint *endpoint = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    return ast_sip_set_security_negotiation(&endpoint->security_negotiation, var->value);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> void ast_sip_auth_vector_destroy(struct ast_sip_auth_vector *auths)</span><br><span> {</span><br><span>     int i;</span><br><span>@@ -2236,6 +2261,8 @@</span><br><span>       ast_sorcery_object_field_register(sip_sorcery, "endpoint", "allow_unauthenticated_options", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, allow_unauthenticated_options));</span><br><span>      ast_sorcery_object_field_register(sip_sorcery, "endpoint", "geoloc_incoming_call_profile", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, geoloc_incoming_call_profile));</span><br><span>        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "geoloc_outgoing_call_profile", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, geoloc_outgoing_call_profile));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "security_mechanisms", "", security_mechanism_handler, NULL, NULL, 0, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "security_negotiation", "no", security_negotiation_handler, NULL, NULL, 0, 0);</span><br><span> </span><br><span>   if (ast_sip_initialize_sorcery_transport()) {</span><br><span>                ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");</span><br><span>diff --git a/res/res_pjsip/pjsip_options.c b/res/res_pjsip/pjsip_options.c</span><br><span>index 7632dea..220a947 100644</span><br><span>--- a/res/res_pjsip/pjsip_options.c</span><br><span>+++ b/res/res_pjsip/pjsip_options.c</span><br><span>@@ -348,6 +348,8 @@</span><br><span> {</span><br><span>         struct ast_sip_contact_status *contact_status = obj;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+      ast_sip_security_mechanisms_vector_destroy(&contact_status->security_mechanisms);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>   ast_string_field_free_memory(contact_status);</span><br><span> }</span><br><span> </span><br><span>@@ -365,6 +367,7 @@</span><br><span>                 ao2_ref(contact_status, -1);</span><br><span>                 return NULL;</span><br><span>         }</span><br><span style="color: hsl(120, 100%, 40%);">+     AST_VECTOR_INIT(&contact_status->security_mechanisms, 0);</span><br><span>     strcpy(contact_status->name, name); /* SAFE */</span><br><span>    return contact_status;</span><br><span> }</span><br><span>@@ -385,6 +388,8 @@</span><br><span>    dst->rtt = src->rtt;</span><br><span>   dst->status = src->status;</span><br><span>     dst->last_status = src->last_status;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_sip_security_mechanisms_vector_copy(&dst->security_mechanisms, &src->security_mechanisms);</span><br><span>         return dst;</span><br><span> }</span><br><span> </span><br><span>diff --git a/res/res_pjsip/security_agreements.c b/res/res_pjsip/security_agreements.c</span><br><span>new file mode 100644</span><br><span>index 0000000..e542e7c</span><br><span>--- /dev/null</span><br><span>+++ b/res/res_pjsip/security_agreements.c</span><br><span>@@ -0,0 +1,340 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2022, Commend International</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Maximilian Fridrich <m.fridrich@commend.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</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%);">+ * \file</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Interact with security agreement negotiations and mechanisms</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \author Maximilian Fridrich <m.fridrich@commend.com></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%);">+#include "asterisk.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <pjsip.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_pjsip.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_sip_security_mechanism *ast_sip_security_mechanisms_alloc(size_t n_params)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct ast_sip_security_mechanism *mech;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    mech = ast_calloc(1, sizeof(struct ast_sip_security_mechanism));</span><br><span style="color: hsl(120, 100%, 40%);">+      if (mech == NULL) {</span><br><span style="color: hsl(120, 100%, 40%);">+           return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+     mech->qvalue = 0.0;</span><br><span style="color: hsl(120, 100%, 40%);">+        if (AST_VECTOR_INIT(&mech->mechanism_parameters, n_params) != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_free(mech);</span><br><span style="color: hsl(120, 100%, 40%);">+               return NULL;</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 mech;</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 struct ast_sip_security_mechanism *ast_sip_security_mechanisms_copy(</span><br><span style="color: hsl(120, 100%, 40%);">+        const struct ast_sip_security_mechanism *src)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ast_sip_security_mechanism *dst = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+        int i, n_params;</span><br><span style="color: hsl(120, 100%, 40%);">+      char *param;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        n_params = AST_VECTOR_SIZE(&src->mechanism_parameters);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      dst = ast_sip_security_mechanisms_alloc(n_params);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (dst == NULL) {</span><br><span style="color: hsl(120, 100%, 40%);">+            return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+     dst->type = src->type;</span><br><span style="color: hsl(120, 100%, 40%);">+  dst->qvalue = src->qvalue;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    for (i = 0; i < n_params; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+           param = ast_strdup(AST_VECTOR_GET(&src->mechanism_parameters, i));</span><br><span style="color: hsl(120, 100%, 40%);">+             AST_VECTOR_APPEND(&dst->mechanism_parameters, param);</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 dst;</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 void ast_sip_security_mechanisms_destroy(struct ast_sip_security_mechanism *mech)</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(&mech->mechanism_parameters); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+                ast_free(AST_VECTOR_GET(&mech->mechanism_parameters, i));</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+     AST_VECTOR_FREE(&mech->mechanism_parameters);</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_free(mech);</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%);">+void ast_sip_security_mechanisms_vector_copy(struct ast_sip_security_mechanism_vector *dst,</span><br><span style="color: hsl(120, 100%, 40%);">+     const struct ast_sip_security_mechanism_vector *src)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct ast_sip_security_mechanism *mech;</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%);">+      ast_sip_security_mechanisms_vector_destroy(dst);</span><br><span style="color: hsl(120, 100%, 40%);">+      for (i = 0; i < AST_VECTOR_SIZE(src); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+               mech = AST_VECTOR_GET(src, i);</span><br><span style="color: hsl(120, 100%, 40%);">+                AST_VECTOR_APPEND(dst, ast_sip_security_mechanisms_copy(mech));</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%);">+void ast_sip_security_mechanisms_vector_destroy(struct ast_sip_security_mechanism_vector *security_mechanisms)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct ast_sip_security_mechanism *mech;</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%);">+      if (!security_mechanisms) {</span><br><span style="color: hsl(120, 100%, 40%);">+           return;</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%);">+   for (i = 0; i < AST_VECTOR_SIZE(security_mechanisms); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+               mech = AST_VECTOR_GET(security_mechanisms, i);</span><br><span style="color: hsl(120, 100%, 40%);">+                ast_sip_security_mechanisms_destroy(mech);</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+     AST_VECTOR_FREE(security_mechanisms);</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 ast_sip_str_to_security_mechanism_type(const char *security_mechanism) {</span><br><span style="color: hsl(120, 100%, 40%);">+       int result = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!strcasecmp(security_mechanism, "msrp-tls")) {</span><br><span style="color: hsl(120, 100%, 40%);">+          result = AST_SIP_SECURITY_MECH_MSRP_TLS;</span><br><span style="color: hsl(120, 100%, 40%);">+      } else if (!strcasecmp(security_mechanism, "sdes-srtp")) {</span><br><span style="color: hsl(120, 100%, 40%);">+          result = AST_SIP_SECURITY_MECH_SDES_SRTP;</span><br><span style="color: hsl(120, 100%, 40%);">+     } else if (!strcasecmp(security_mechanism, "dtls-srtp")) {</span><br><span style="color: hsl(120, 100%, 40%);">+          result = AST_SIP_SECURITY_MECH_DTLS_SRTP;</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 result;</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 char *ast_sip_security_mechanism_type_to_str(enum ast_sip_security_mechanism_type mech_type) {</span><br><span style="color: hsl(120, 100%, 40%);">+    if (mech_type == AST_SIP_SECURITY_MECH_MSRP_TLS) {</span><br><span style="color: hsl(120, 100%, 40%);">+            return "msrp-tls";</span><br><span style="color: hsl(120, 100%, 40%);">+  } else if (mech_type == AST_SIP_SECURITY_MECH_SDES_SRTP) {</span><br><span style="color: hsl(120, 100%, 40%);">+            return "sdes-srtp";</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (mech_type == AST_SIP_SECURITY_MECH_DTLS_SRTP) {</span><br><span style="color: hsl(120, 100%, 40%);">+            return "dtls-srtp";</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              return NULL;</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%);">+static int ast_sip_security_mechanism_to_str(const struct ast_sip_security_mechanism *security_mechanism, int add_qvalue, char **buf) {</span><br><span style="color: hsl(120, 100%, 40%);">+       char tmp[64];</span><br><span style="color: hsl(120, 100%, 40%);">+ size_t size;</span><br><span style="color: hsl(120, 100%, 40%);">+  size_t buf_size = 128;</span><br><span style="color: hsl(120, 100%, 40%);">+        int i;</span><br><span style="color: hsl(120, 100%, 40%);">+        char *ret = ast_calloc(buf_size, sizeof(char));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (ret == NULL) {</span><br><span style="color: hsl(120, 100%, 40%);">+            return ENOMEM;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (security_mechanism == NULL) {</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_free(ret);</span><br><span style="color: hsl(120, 100%, 40%);">+                return 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%);">+   strncat(ret, ast_sip_security_mechanism_type_to_str(security_mechanism->type), buf_size - strlen(ret) - 1);</span><br><span style="color: hsl(120, 100%, 40%);">+        if (add_qvalue) {</span><br><span style="color: hsl(120, 100%, 40%);">+             snprintf(tmp, sizeof(tmp), ";q=%f.4", security_mechanism->qvalue);</span><br><span style="color: hsl(120, 100%, 40%);">+               strncat(ret, tmp, buf_size - strlen(ret) - 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%);">+   size = AST_VECTOR_SIZE(&security_mechanism->mechanism_parameters);</span><br><span style="color: hsl(120, 100%, 40%);">+     for (i = 0; i < size; ++i) {</span><br><span style="color: hsl(120, 100%, 40%);">+               snprintf(tmp, sizeof(tmp), ";%s", AST_VECTOR_GET(&security_mechanism->mechanism_parameters, i));</span><br><span style="color: hsl(120, 100%, 40%);">+             strncat(ret, tmp, buf_size - strlen(ret) - 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%);">+   *buf = ret;</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%);">+void ast_sip_remove_headers_by_name_and_value(pjsip_msg *msg, const pj_str_t *hdr_name, const char* value)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct pjsip_generic_string_hdr *hdr = pjsip_msg_find_hdr_by_name(msg, hdr_name, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+       for (; hdr; hdr = pjsip_msg_find_hdr_by_name(msg, hdr_name, hdr->next)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          if (value == NULL || !pj_strcmp2(&hdr->hvalue, value)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       pj_list_erase(hdr);</span><br><span style="color: hsl(120, 100%, 40%);">+           }</span><br><span style="color: hsl(120, 100%, 40%);">+             if (hdr->next == hdr) {</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%);">+</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 Parses a string representing a q_value to a float.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Valid q values must be in the range from 0.0 to 1.0 inclusively.</span><br><span style="color: hsl(120, 100%, 40%);">+ * </span><br><span style="color: hsl(120, 100%, 40%);">+ * \param q_value</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval The parsed qvalue or -1.0 on failure.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static float parse_qvalue(const char *q_value) {</span><br><span style="color: hsl(120, 100%, 40%);">+     char *end;</span><br><span style="color: hsl(120, 100%, 40%);">+    float ret = strtof(q_value, &end);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      if (end == q_value) {</span><br><span style="color: hsl(120, 100%, 40%);">+         /* Not a number. */</span><br><span style="color: hsl(120, 100%, 40%);">+           return -1.0;</span><br><span style="color: hsl(120, 100%, 40%);">+  } else if ('\0' != *end) {</span><br><span style="color: hsl(120, 100%, 40%);">+            /* Extra character at end of input. */</span><br><span style="color: hsl(120, 100%, 40%);">+                return -1.0;</span><br><span style="color: hsl(120, 100%, 40%);">+  } else if (ret > 1.0 || ret < 0.0) {</span><br><span style="color: hsl(120, 100%, 40%);">+            /* Out of valid range. */</span><br><span style="color: hsl(120, 100%, 40%);">+             return -1.0;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+     return ret;</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%);">+int ast_sip_str_to_security_mechanism(struct ast_sip_security_mechanism **security_mechanism, const char *value) {</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ast_sip_security_mechanism *mech;</span><br><span style="color: hsl(120, 100%, 40%);">+      char *param;</span><br><span style="color: hsl(120, 100%, 40%);">+  char *tmp;</span><br><span style="color: hsl(120, 100%, 40%);">+    char *mechanism = ast_strdupa(value);</span><br><span style="color: hsl(120, 100%, 40%);">+ int err = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+  int type = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      mech = ast_sip_security_mechanisms_alloc(1);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!mech) {</span><br><span style="color: hsl(120, 100%, 40%);">+          err = ENOMEM;</span><br><span style="color: hsl(120, 100%, 40%);">+         goto out;</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%);">+   tmp = ast_strsep(&mechanism, ';', AST_STRSEP_ALL);</span><br><span style="color: hsl(120, 100%, 40%);">+        type = ast_sip_str_to_security_mechanism_type(tmp);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (type == -1) {</span><br><span style="color: hsl(120, 100%, 40%);">+             err = EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+         goto out;</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%);">+   mech->type = type;</span><br><span style="color: hsl(120, 100%, 40%);">+ while ((param = ast_strsep(&mechanism, ';', AST_STRSEP_ALL))) {</span><br><span style="color: hsl(120, 100%, 40%);">+           if (!param) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 err = EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+                 goto out;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+             if (!strncmp(param, "q=0", 4) || !strncmp(param, "q=1", 4)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     mech->qvalue = parse_qvalue(&param[2]);</span><br><span style="color: hsl(120, 100%, 40%);">+                        if (mech->qvalue < 0.0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                               goto out;</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</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%);">+             param = ast_strdup(param);</span><br><span style="color: hsl(120, 100%, 40%);">+            AST_VECTOR_APPEND(&mech->mechanism_parameters, param);</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%);">+   *security_mechanism = mech;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+out:</span><br><span style="color: hsl(120, 100%, 40%);">+   if (err && (mech != NULL)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_sip_security_mechanisms_destroy(mech);</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+     return err;</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%);">+int ast_sip_add_security_headers(struct ast_sip_security_mechanism_vector *security_mechanisms,</span><br><span style="color: hsl(120, 100%, 40%);">+             const char *header_name, int add_qval, pjsip_tx_data *tdata) {</span><br><span style="color: hsl(120, 100%, 40%);">+        struct ast_sip_security_mechanism *mech;</span><br><span style="color: hsl(120, 100%, 40%);">+      char *buf;</span><br><span style="color: hsl(120, 100%, 40%);">+    int mech_cnt;</span><br><span style="color: hsl(120, 100%, 40%);">+ int i;</span><br><span style="color: hsl(120, 100%, 40%);">+        int add_qvalue = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!security_mechanisms || !tdata) {</span><br><span style="color: hsl(120, 100%, 40%);">+         return 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%);">+   if (!strcmp(header_name, "Security-Client")) {</span><br><span style="color: hsl(120, 100%, 40%);">+              add_qvalue = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+       } else if (strcmp(header_name, "Security-Server") &&</span><br><span style="color: hsl(120, 100%, 40%);">+                        strcmp(header_name, "Security-Verify")) {</span><br><span style="color: hsl(120, 100%, 40%);">+           return EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+     /* If we're adding Security-Client headers, don't add q-value</span><br><span style="color: hsl(120, 100%, 40%);">+  * even if the function caller requested it. */</span><br><span style="color: hsl(120, 100%, 40%);">+       add_qvalue = add_qvalue && add_qval;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        mech_cnt = AST_VECTOR_SIZE(security_mechanisms);</span><br><span style="color: hsl(120, 100%, 40%);">+      for (i = 0; i < mech_cnt; ++i) {</span><br><span style="color: hsl(120, 100%, 40%);">+           mech = AST_VECTOR_GET(security_mechanisms, i);</span><br><span style="color: hsl(120, 100%, 40%);">+                if (ast_sip_security_mechanism_to_str(mech, add_qvalue, &buf)) {</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%);">+             ast_sip_add_header(tdata, header_name, buf);</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_free(buf);</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%);">+void ast_sip_header_to_security_mechanism(const pjsip_generic_string_hdr *hdr,</span><br><span style="color: hsl(120, 100%, 40%);">+                struct ast_sip_security_mechanism_vector *security_mechanisms) {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    struct ast_sip_security_mechanism *mech;</span><br><span style="color: hsl(120, 100%, 40%);">+      char buf[512];</span><br><span style="color: hsl(120, 100%, 40%);">+        char *hdr_val;</span><br><span style="color: hsl(120, 100%, 40%);">+        char *mechanism;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!security_mechanisms || !hdr) {</span><br><span style="color: hsl(120, 100%, 40%);">+           return;</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_stricmp2(&hdr->name, "Security-Client") && pj_stricmp2(&hdr->name, "Security-Server") &&</span><br><span style="color: hsl(120, 100%, 40%);">+                     pj_stricmp2(&hdr->name, "Security-Verify")) {</span><br><span style="color: hsl(120, 100%, 40%);">+                return;</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_copy_pj_str(buf, &hdr->hvalue, sizeof(buf));</span><br><span style="color: hsl(120, 100%, 40%);">+       hdr_val = ast_skip_blanks(buf);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     while ((mechanism = ast_strsep(&hdr_val, ',', AST_STRSEP_ALL))) {</span><br><span style="color: hsl(120, 100%, 40%);">+         if (!ast_sip_str_to_security_mechanism(&mech, mechanism)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       AST_VECTOR_APPEND(security_mechanisms, mech);</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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_sip_security_mechanism_vector_init(struct ast_sip_security_mechanism_vector *security_mechanisms, const char *value)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       char *val = value ? ast_strdupa(value) : NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct ast_sip_security_mechanism *mech;</span><br><span style="color: hsl(120, 100%, 40%);">+      char *mechanism;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    ast_sip_security_mechanisms_vector_destroy(security_mechanisms);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (AST_VECTOR_INIT(security_mechanisms, 1)) {</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%);">+   if (!val) {</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%);">+   while ((mechanism = ast_strsep(&val, ',', AST_STRSEP_ALL))) {</span><br><span style="color: hsl(120, 100%, 40%);">+             if (!ast_sip_str_to_security_mechanism(&mech, mechanism)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       AST_VECTOR_APPEND(security_mechanisms, mech);</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 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c</span><br><span>index 0c9b1ee..c85b5ee 100644</span><br><span>--- a/res/res_pjsip_outbound_registration.c</span><br><span>+++ b/res/res_pjsip_outbound_registration.c</span><br><span>@@ -92,6 +92,22 @@</span><br><span>                                           are made.</span><br><span>                                    </para></description></span><br><span>                            </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+                         <configOption name="security_negotiation" default="no"></span><br><span style="color: hsl(120, 100%, 40%);">+                                     <synopsis>The kind of security agreement negotiation to use. Currently, only mediasec is supported.</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+                                    <description></span><br><span style="color: hsl(120, 100%, 40%);">+                                           <enumlist></span><br><span style="color: hsl(120, 100%, 40%);">+                                                      <enum name="no" /></span><br><span style="color: hsl(120, 100%, 40%);">+                                                    <enum name="mediasec" /></span><br><span style="color: hsl(120, 100%, 40%);">+                                              </enumlist></span><br><span style="color: hsl(120, 100%, 40%);">+                                     </description></span><br><span style="color: hsl(120, 100%, 40%);">+                          </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+                         <configOption name="security_mechanisms"></span><br><span style="color: hsl(120, 100%, 40%);">+                                     <synopsis>List of security mechanisms supported.</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+                                       <description><para></span><br><span style="color: hsl(120, 100%, 40%);">+                                               This is a comma-delimited list of security mechanisms to use. Each security mechanism</span><br><span style="color: hsl(120, 100%, 40%);">+                                         must be in the form defined by RFC 3329 section 2.2.</span><br><span style="color: hsl(120, 100%, 40%);">+                                  </para></description></span><br><span style="color: hsl(120, 100%, 40%);">+                             </configOption></span><br><span>                                <configOption name="outbound_auth" default=""></span><br><span>                                     <synopsis>Authentication object(s) to be used for outbound registrations.</synopsis></span><br><span>                                     <description><para></span><br><span>@@ -342,6 +358,10 @@</span><br><span>       unsigned int max_retries;</span><br><span>    /*! \brief Whether to add a line parameter to the outbound Contact or not */</span><br><span>         unsigned int line;</span><br><span style="color: hsl(120, 100%, 40%);">+    /*! \brief Type of security negotiation to use (RFC 3329). */</span><br><span style="color: hsl(120, 100%, 40%);">+ enum ast_sip_security_negotiation security_negotiation;</span><br><span style="color: hsl(120, 100%, 40%);">+       /*! \brief Client security mechanisms (RFC 3329). */</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ast_sip_security_mechanism_vector security_mechanisms;</span><br><span>        /*! \brief Configured authentication credentials */</span><br><span>  struct ast_sip_auth_vector outbound_auths;</span><br><span>   /*! \brief Whether Path support is enabled */</span><br><span>@@ -389,6 +409,12 @@</span><br><span>         unsigned int support_path;</span><br><span>   /*! \brief Determines whether SIP Outbound support should be advertised */</span><br><span>   unsigned int support_outbound;</span><br><span style="color: hsl(120, 100%, 40%);">+        /*! \brief Type of security negotiation to use (RFC 3329). */</span><br><span style="color: hsl(120, 100%, 40%);">+ enum ast_sip_security_negotiation security_negotiation;</span><br><span style="color: hsl(120, 100%, 40%);">+       /*! \brief Client security mechanisms (RFC 3329). */</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ast_sip_security_mechanism_vector security_mechanisms;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! \brief Security mechanisms of the peer (RFC 3329). */</span><br><span style="color: hsl(120, 100%, 40%);">+     struct ast_sip_security_mechanism_vector server_security_mechanisms;</span><br><span>         /*! CSeq number of last sent auth request. */</span><br><span>        unsigned int auth_cseq;</span><br><span>      /*! \brief Serializer for stuff and things */</span><br><span>@@ -561,6 +587,117 @@</span><br><span> static pj_str_t PATH_NAME = { "path", 4 };</span><br><span> static pj_str_t OUTBOUND_NAME = { "outbound", 8 };</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+AST_VECTOR(pjsip_generic_string_hdr_vector, pjsip_generic_string_hdr *);</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 Callback function which finds a contact whose contact_status has security mechanisms.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param obj Pointer to the ast_sip_contact.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param arg Pointer-pointer to a contact_status that will be set to the contact_status found by this function.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param flags Flags used by the ao2_callback function.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note The refcount of the found contact_status must be decremented by the caller.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int contact_has_security_mechanisms(void *obj, void *arg, int flags)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct ast_sip_contact *contact = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct ast_sip_contact_status **ret = arg;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct ast_sip_contact_status *contact_status = ast_sip_get_contact_status(contact);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!contact_status) {</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%);">+     if (!AST_VECTOR_SIZE(&contact_status->security_mechanisms)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          ao2_cleanup(contact_status);</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%);">+     *ret = contact_status;</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%);">+static int contact_add_security_headers_to_status(void *obj, void *arg, int flags)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_contact *contact = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct pjsip_generic_string_hdr_vector *header_vector = arg;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ast_sip_contact_status *contact_status = ast_sip_get_contact_status(contact);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!contact_status) {</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%);">+     if (AST_VECTOR_SIZE(&contact_status->security_mechanisms)) {</span><br><span style="color: hsl(120, 100%, 40%);">+           goto out;</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%);">+   ao2_lock(contact_status);</span><br><span style="color: hsl(120, 100%, 40%);">+     AST_VECTOR_CALLBACK_VOID(header_vector, ast_sip_header_to_security_mechanism, &contact_status->security_mechanisms);</span><br><span style="color: hsl(120, 100%, 40%);">+   ao2_unlock(contact_status);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+out:</span><br><span style="color: hsl(120, 100%, 40%);">+   ao2_cleanup(contact_status);</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%);">+/*! \brief Adds security negotiation mechanisms of outbound registration client state as Security headers to tdata. */</span><br><span style="color: hsl(120, 100%, 40%);">+static void add_security_headers(struct sip_outbound_registration_client_state *client_state,</span><br><span style="color: hsl(120, 100%, 40%);">+ pjsip_tx_data *tdata)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct sip_outbound_registration *reg = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_endpoint *endpt = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct ao2_container *contact_container;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ast_sip_contact_status *contact_status = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_security_mechanism_vector *sec_mechs = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+   static const pj_str_t security_verify = { "Security-Verify", 15 };</span><br><span style="color: hsl(120, 100%, 40%);">+  static const pj_str_t security_client = { "Security-Client", 15 };</span><br><span style="color: hsl(120, 100%, 40%);">+  static const pj_str_t proxy_require = { "Proxy-Require", 13 };</span><br><span style="color: hsl(120, 100%, 40%);">+      static const pj_str_t require = { "Require", 7 };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (client_state->security_negotiation != AST_SIP_SECURITY_NEG_MEDIASEC) {</span><br><span style="color: hsl(120, 100%, 40%);">+         return;</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%);">+   /* Get contact status through registration -> endpoint name -> aor -> contact (if set) */</span><br><span style="color: hsl(120, 100%, 40%);">+    if ((reg = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "registration", client_state->registration_name))</span><br><span style="color: hsl(120, 100%, 40%);">+           && !ast_strlen_zero(reg->endpoint) && (endpt = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", reg->endpoint))</span><br><span style="color: hsl(120, 100%, 40%);">+          && (contact_container = ast_sip_location_retrieve_contacts_from_aor_list(endpt->aors))) {</span><br><span style="color: hsl(120, 100%, 40%);">+          /* Retrieve all contacts associated with aors from this endpoint</span><br><span style="color: hsl(120, 100%, 40%);">+               * and find the first one that has security mechanisms.</span><br><span style="color: hsl(120, 100%, 40%);">+                */</span><br><span style="color: hsl(120, 100%, 40%);">+           ao2_callback(contact_container, 0, contact_has_security_mechanisms, &contact_status);</span><br><span style="color: hsl(120, 100%, 40%);">+             if (contact_status) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 ao2_lock(contact_status);</span><br><span style="color: hsl(120, 100%, 40%);">+                     sec_mechs = &contact_status->security_mechanisms;</span><br><span style="color: hsl(120, 100%, 40%);">+              }</span><br><span style="color: hsl(120, 100%, 40%);">+             ao2_cleanup(contact_container);</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+     /* Use client_state->server_security_mechanisms if contact_status does not exist. */</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!contact_status && AST_VECTOR_SIZE(&client_state->server_security_mechanisms)) {</span><br><span style="color: hsl(120, 100%, 40%);">+           sec_mechs = &client_state->server_security_mechanisms;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (client_state->status == SIP_REGISTRATION_REGISTERED || client_state->status == SIP_REGISTRATION_REJECTED_TEMPORARY</span><br><span style="color: hsl(120, 100%, 40%);">+                  || client_state->auth_attempted) {</span><br><span style="color: hsl(120, 100%, 40%);">+         if (sec_mechs != NULL && pjsip_msg_find_hdr_by_name(tdata->msg, &security_verify, NULL) == NULL) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_sip_add_security_headers(sec_mechs, "Security-Verify", 0, tdata);</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_sip_remove_headers_by_name_and_value(tdata->msg, &security_client, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_sip_remove_headers_by_name_and_value(tdata->msg, &proxy_require, "mediasec");</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_sip_remove_headers_by_name_and_value(tdata->msg, &require, "mediasec");</span><br><span style="color: hsl(120, 100%, 40%);">+  } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_sip_add_security_headers(&client_state->security_mechanisms, "Security-Client", 0, tdata);</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_sip_add_header(tdata, "Require", "mediasec");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_add_header(tdata, "Proxy-Require", "mediasec");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Cleanup */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (contact_status) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ao2_unlock(contact_status);</span><br><span style="color: hsl(120, 100%, 40%);">+           ao2_cleanup(contact_status);</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+     ao2_cleanup(endpt);</span><br><span style="color: hsl(120, 100%, 40%);">+   ao2_cleanup(reg);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! \brief Helper function which sends a message and cleans up, if needed, on failure */</span><br><span> static pj_status_t registration_client_send(struct sip_outbound_registration_client_state *client_state,</span><br><span>       pjsip_tx_data *tdata)</span><br><span>@@ -585,6 +722,9 @@</span><br><span>   */</span><br><span>  pjsip_tx_data_add_ref(tdata);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+     /* Add Security-Verify or Security-Client headers */</span><br><span style="color: hsl(120, 100%, 40%);">+  add_security_headers(client_state, tdata);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>         /*</span><br><span>    * Set the transport in case transports were reloaded.</span><br><span>        * When pjproject removes the extraneous error messages produced,</span><br><span>@@ -832,6 +972,8 @@</span><br><span> </span><br><span>  update_client_state_status(client_state, SIP_REGISTRATION_STOPPED);</span><br><span>  ast_sip_auth_vector_destroy(&client_state->outbound_auths);</span><br><span style="color: hsl(120, 100%, 40%);">+    ast_sip_security_mechanisms_vector_destroy(&client_state->security_mechanisms);</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_sip_security_mechanisms_vector_destroy(&client_state->server_security_mechanisms);</span><br><span>        ao2_ref(client_state, -1);</span><br><span> </span><br><span>       return 0;</span><br><span>@@ -1089,19 +1231,60 @@</span><br><span>                          return 0;</span><br><span>                    }</span><br><span>            }</span><br><span style="color: hsl(0, 100%, 40%);">-       } else if ((response->code == 401 || response->code == 407)</span><br><span style="color: hsl(120, 100%, 40%);">+     } else if ((response->code == 401 || response->code == 407 || response->code == 494)</span><br><span>                && (!response->client_state->auth_attempted</span><br><span>                    || response->rdata->msg_info.cseq->cseq != response->client_state->auth_cseq)) {</span><br><span>              int res;</span><br><span>             pjsip_cseq_hdr *cseq_hdr;</span><br><span>            pjsip_tx_data *tdata;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-               if (!ast_sip_create_request_with_auth(&response->client_state->outbound_auths,</span><br><span style="color: hsl(120, 100%, 40%);">+              if (response->client_state->security_negotiation == AST_SIP_SECURITY_NEG_MEDIASEC) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    struct sip_outbound_registration *reg = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+                 struct ast_sip_endpoint *endpt = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+                        struct ao2_container *contact_container = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+                       pjsip_generic_string_hdr *header;</span><br><span style="color: hsl(120, 100%, 40%);">+                     struct pjsip_generic_string_hdr_vector header_vector;</span><br><span style="color: hsl(120, 100%, 40%);">+                 static const pj_str_t security_server = { "Security-Server", 15 };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                        if ((reg = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "registration",</span><br><span style="color: hsl(120, 100%, 40%);">+                                response->client_state->registration_name)) && reg->endpoint &&</span><br><span style="color: hsl(120, 100%, 40%);">+                              (endpt = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", reg->endpoint))) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                /* Retrieve all contacts associated with aors from this endpoint (if set). */</span><br><span style="color: hsl(120, 100%, 40%);">+                         contact_container = ast_sip_location_retrieve_contacts_from_aor_list(endpt->aors);</span><br><span style="color: hsl(120, 100%, 40%);">+                 }</span><br><span style="color: hsl(120, 100%, 40%);">+                     /* Add server list of security mechanism to client_state and contact status if exists. */</span><br><span style="color: hsl(120, 100%, 40%);">+                     AST_VECTOR_INIT(&header_vector, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+                       header = pjsip_msg_find_hdr_by_name(response->rdata->msg_info.msg, &security_server, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+                 for (; header;</span><br><span style="color: hsl(120, 100%, 40%);">+                                header = pjsip_msg_find_hdr_by_name(response->rdata->msg_info.msg, &security_server, header->next)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                            AST_VECTOR_APPEND(&header_vector, header);</span><br><span style="color: hsl(120, 100%, 40%);">+                                ast_sip_header_to_security_mechanism(header, &response->client_state->server_security_mechanisms);</span><br><span style="color: hsl(120, 100%, 40%);">+                  }</span><br><span style="color: hsl(120, 100%, 40%);">+                     if (contact_container) {</span><br><span style="color: hsl(120, 100%, 40%);">+                              /* Add server security mechanisms to contact status of all associated contacts to be able to send correct</span><br><span style="color: hsl(120, 100%, 40%);">+                              * Security-Verify headers on subsequent non-REGISTER requests through this outbound registration.</span><br><span style="color: hsl(120, 100%, 40%);">+                             */</span><br><span style="color: hsl(120, 100%, 40%);">+                           ao2_callback(contact_container, OBJ_NODATA, contact_add_security_headers_to_status, &header_vector);</span><br><span style="color: hsl(120, 100%, 40%);">+                              ao2_cleanup(contact_container);</span><br><span style="color: hsl(120, 100%, 40%);">+                       }</span><br><span style="color: hsl(120, 100%, 40%);">+                     AST_VECTOR_FREE(&header_vector);</span><br><span style="color: hsl(120, 100%, 40%);">+                  ao2_cleanup(endpt);</span><br><span style="color: hsl(120, 100%, 40%);">+                   ao2_cleanup(reg);</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 (response->code == 494) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_TEMPORARY);</span><br><span style="color: hsl(120, 100%, 40%);">+                   response->client_state->retries++;</span><br><span style="color: hsl(120, 100%, 40%);">+                      schedule_registration(response->client_state, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+                  ao2_ref(response, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+                        return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+             } else if (!ast_sip_create_request_with_auth(&response->client_state->outbound_auths,</span><br><span>                              response->rdata, response->old_request, &tdata)) {</span><br><span>                         response->client_state->auth_attempted = 1;</span><br><span>                    ast_debug(1, "Sending authenticated REGISTER to server '%s' from client '%s'\n",</span><br><span>                                   server_uri, client_uri);</span><br><span>                     pjsip_tx_data_add_ref(tdata);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>                      res = registration_client_send(response->client_state, tdata);</span><br><span> </span><br><span>                        /* Save the cseq that actually got sent. */</span><br><span>@@ -1372,6 +1555,7 @@</span><br><span>  struct sip_outbound_registration *registration = obj;</span><br><span> </span><br><span>    ast_sip_auth_vector_destroy(&registration->outbound_auths);</span><br><span style="color: hsl(120, 100%, 40%);">+    ast_sip_security_mechanisms_vector_destroy(&registration->security_mechanisms);</span><br><span> </span><br><span>   ast_string_field_free_memory(registration);</span><br><span> }</span><br><span>@@ -1721,6 +1905,8 @@</span><br><span> </span><br><span>         /* Just in case the client state is being reused for this registration, free the auth information */</span><br><span>         ast_sip_auth_vector_destroy(&state->client_state->outbound_auths);</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_sip_security_mechanisms_vector_destroy(&state->client_state->security_mechanisms);</span><br><span style="color: hsl(120, 100%, 40%);">+      ast_sip_security_mechanisms_vector_destroy(&state->client_state->server_security_mechanisms);</span><br><span> </span><br><span>  AST_VECTOR_INIT(&state->client_state->outbound_auths, AST_VECTOR_SIZE(&registration->outbound_auths));</span><br><span>      for (i = 0; i < AST_VECTOR_SIZE(&registration->outbound_auths); ++i) {</span><br><span>@@ -1730,6 +1916,8 @@</span><br><span>                     ast_free(name);</span><br><span>              }</span><br><span>    }</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_sip_security_mechanisms_vector_copy(&state->client_state->security_mechanisms,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                                                  &registration->security_mechanisms);</span><br><span>  state->client_state->retry_interval = registration->retry_interval;</span><br><span>         state->client_state->forbidden_retry_interval = registration->forbidden_retry_interval;</span><br><span>     state->client_state->fatal_retry_interval = registration->fatal_retry_interval;</span><br><span>@@ -1737,6 +1925,7 @@</span><br><span>     state->client_state->retries = 0;</span><br><span>      state->client_state->support_path = registration->support_path;</span><br><span>     state->client_state->support_outbound = registration->support_outbound;</span><br><span style="color: hsl(120, 100%, 40%);">+      state->client_state->security_negotiation = registration->security_negotiation;</span><br><span>     state->client_state->auth_rejection_permanent = registration->auth_rejection_permanent;</span><br><span>     max_delay = registration->max_random_initial_delay;</span><br><span> </span><br><span>@@ -1835,6 +2024,20 @@</span><br><span>  return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static int security_mechanisms_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct sip_outbound_registration *registration = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       return ast_sip_security_mechanism_vector_init(&registration->security_mechanisms, var->value);</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 security_negotiation_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct sip_outbound_registration *registration = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       return ast_sip_set_security_negotiation(&registration->security_negotiation, var->value);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int outbound_auth_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)</span><br><span> {</span><br><span>         struct sip_outbound_registration *registration = obj;</span><br><span>@@ -2564,6 +2767,8 @@</span><br><span>        ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "registration", "outbound_auth", "", outbound_auth_handler, outbound_auths_to_str, outbound_auths_to_var_list, 0, 0);</span><br><span>  ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "support_path", "no", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, support_path));</span><br><span>         ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "support_outbound", "no", OPT_YESNO_T, 1, FLDSET(struct sip_outbound_registration, support_outbound));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "registration", "security_negotiation", "no", security_negotiation_handler, NULL, NULL, 0, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "registration", "security_mechanisms", "", security_mechanisms_handler, NULL, NULL, 0, 0);</span><br><span>     ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "line", "no", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, line));</span><br><span>         ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "endpoint", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, endpoint));</span><br><span> </span><br><span>diff --git a/res/res_pjsip_rfc3329.c b/res/res_pjsip_rfc3329.c</span><br><span>new file mode 100644</span><br><span>index 0000000..167dfa0</span><br><span>--- /dev/null</span><br><span>+++ b/res/res_pjsip_rfc3329.c</span><br><span>@@ -0,0 +1,150 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2022, Commend International</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Maximilian Fridrich <m.fridrich@commend.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</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%);">+/*** MODULEINFO</span><br><span style="color: hsl(120, 100%, 40%);">+ <depend>pjproject</depend></span><br><span style="color: hsl(120, 100%, 40%);">+        <depend>res_pjsip</depend></span><br><span style="color: hsl(120, 100%, 40%);">+        <depend>res_pjsip_session</depend></span><br><span style="color: hsl(120, 100%, 40%);">+        <support_level>core</support_level></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%);">+#include "asterisk.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <pjsip.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <pjsip_ua.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_pjsip.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_pjsip_session.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/module.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/causes.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/threadpool.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void rfc3329_incoming_response(struct ast_sip_session *session, struct pjsip_rx_data *rdata)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      static const pj_str_t str_security_server = { "Security-Server", 15 };</span><br><span style="color: hsl(120, 100%, 40%);">+      struct ast_sip_contact_status *contact_status = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_security_mechanism *mech;</span><br><span style="color: hsl(120, 100%, 40%);">+      pjsip_generic_string_hdr *header;</span><br><span style="color: hsl(120, 100%, 40%);">+     char buf[128];</span><br><span style="color: hsl(120, 100%, 40%);">+        char *hdr_val;</span><br><span style="color: hsl(120, 100%, 40%);">+        char *mechanism;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!session || !session->endpoint || !session->endpoint->security_negotiation</span><br><span style="color: hsl(120, 100%, 40%);">+               || !session->contact || !(contact_status = ast_sip_get_contact_status(session->contact))) {</span><br><span style="color: hsl(120, 100%, 40%);">+             return;</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%);">+   ao2_lock(contact_status);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (AST_VECTOR_SIZE(&contact_status->security_mechanisms)) {</span><br><span style="color: hsl(120, 100%, 40%);">+           goto out;</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%);">+   header = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_security_server, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+  for (; header;</span><br><span style="color: hsl(120, 100%, 40%);">+                header = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_security_server, header->next)) {</span><br><span style="color: hsl(120, 100%, 40%);">+             /* Parse Security-Server headers and add to contact status to use for future requests. */</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_copy_pj_str(buf, &header->hvalue, sizeof(buf));</span><br><span style="color: hsl(120, 100%, 40%);">+            hdr_val = ast_skip_blanks(buf);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+             while ((mechanism = ast_strsep(&hdr_val, ',', AST_STRSEP_ALL))) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 if (!ast_sip_str_to_security_mechanism(&mech, mechanism)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                               AST_VECTOR_APPEND(&contact_status->security_mechanisms, mech);</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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+out:</span><br><span style="color: hsl(120, 100%, 40%);">+     ao2_unlock(contact_status);</span><br><span style="color: hsl(120, 100%, 40%);">+   ao2_cleanup(contact_status);</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 void add_outgoing_request_headers(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact, struct pjsip_tx_data *tdata)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       static const pj_str_t security_verify = { "Security-Verify", 15 };</span><br><span style="color: hsl(120, 100%, 40%);">+  struct pjsip_generic_string_hdr *hdr = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ast_sip_contact_status *contact_status = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (endpoint->security_negotiation != AST_SIP_SECURITY_NEG_MEDIASEC) {</span><br><span style="color: hsl(120, 100%, 40%);">+             return;</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%);">+   contact_status = ast_sip_get_contact_status(contact);</span><br><span style="color: hsl(120, 100%, 40%);">+ hdr = pjsip_msg_find_hdr_by_name(tdata->msg, &security_verify, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (contact_status == NULL) {</span><br><span style="color: hsl(120, 100%, 40%);">+         return;</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%);">+   ao2_lock(contact_status);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (AST_VECTOR_SIZE(&contact_status->security_mechanisms) && hdr == NULL) {</span><br><span style="color: hsl(120, 100%, 40%);">+            /* Add Security-Verify headers (with q-value) */</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_sip_add_security_headers(&contact_status->security_mechanisms, "Security-Verify", 0, tdata);</span><br><span style="color: hsl(120, 100%, 40%);">+     } else if (!hdr && AST_VECTOR_SIZE(&endpoint->security_mechanisms)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          /* Add Security-Client headers (no q-value) */</span><br><span style="color: hsl(120, 100%, 40%);">+                ast_sip_add_security_headers(&endpoint->security_mechanisms, "Security-Client", 0, tdata);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+     ao2_unlock(contact_status);</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_sip_add_header(tdata, "Require", "mediasec");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_add_header(tdata, "Proxy-Require", "mediasec");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(contact_status);</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 void rfc3329_outgoing_request(struct ast_sip_session *session, struct pjsip_tx_data *tdata)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      if (session->contact == NULL) {</span><br><span style="color: hsl(120, 100%, 40%);">+            return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+     add_outgoing_request_headers(session->endpoint, session->contact, tdata);</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 struct ast_sip_session_supplement rfc3329_supplement = {</span><br><span style="color: hsl(120, 100%, 40%);">+ .incoming_response = rfc3329_incoming_response,</span><br><span style="color: hsl(120, 100%, 40%);">+       .outgoing_request = rfc3329_outgoing_request,</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 void rfc3329_options_request(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact, struct pjsip_tx_data *tdata)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  add_outgoing_request_headers(endpoint, contact, tdata);</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 struct ast_sip_supplement rfc3329_options_supplement = {</span><br><span style="color: hsl(120, 100%, 40%);">+ .method = "OPTIONS",</span><br><span style="color: hsl(120, 100%, 40%);">+        .outgoing_request = rfc3329_options_request,</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 load_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_sip_session_register_supplement(&rfc3329_supplement);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_register_supplement(&rfc3329_options_supplement);</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_MODULE_LOAD_SUCCESS;</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 unload_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_sip_session_unregister_supplement(&rfc3329_supplement);</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%);">+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP RFC 3329 Support (partial)",</span><br><span style="color: hsl(120, 100%, 40%);">+       .support_level = AST_MODULE_SUPPORT_CORE,</span><br><span style="color: hsl(120, 100%, 40%);">+     .load = load_module,</span><br><span style="color: hsl(120, 100%, 40%);">+  .unload = unload_module,</span><br><span style="color: hsl(120, 100%, 40%);">+      .load_pri = AST_MODPRI_APP_DEPEND,</span><br><span style="color: hsl(120, 100%, 40%);">+    .requires = "res_pjsip,res_pjsip_session",</span><br><span style="color: hsl(120, 100%, 40%);">+);</span><br><span>diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c</span><br><span>index e7b19e4..75f1d54 100644</span><br><span>--- a/res/res_pjsip_sdp_rtp.c</span><br><span>+++ b/res/res_pjsip_sdp_rtp.c</span><br><span>@@ -1607,6 +1607,7 @@</span><br><span>    static const pj_str_t STR_PASSIVE = { "passive", 7 };</span><br><span>      static const pj_str_t STR_ACTPASS = { "actpass", 7 };</span><br><span>      static const pj_str_t STR_HOLDCONN = { "holdconn", 8 };</span><br><span style="color: hsl(120, 100%, 40%);">+     static const pj_str_t STR_MEDSECREQ = { "requested", 9 };</span><br><span>  enum ast_rtp_dtls_setup setup;</span><br><span> </span><br><span>   switch (session_media->encryption) {</span><br><span>@@ -1637,6 +1638,11 @@</span><br><span>                     media->attr[media->attr_count++] = attr;</span><br><span>               } while ((tmp = AST_LIST_NEXT(tmp, sdp_srtp_list)));</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+              if (session->endpoint->security_negotiation == AST_SIP_SECURITY_NEG_MEDIASEC) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 attr = pjmedia_sdp_attr_create(pool, "3ge2ae", &STR_MEDSECREQ);</span><br><span style="color: hsl(120, 100%, 40%);">+                 media->attr[media->attr_count++] = attr;</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>          break;</span><br><span>       case AST_SIP_MEDIA_ENCRYPT_DTLS:</span><br><span>             if (setup_dtls_srtp(session, session_media)) {</span><br><span>diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c</span><br><span>index 2b2d92e..fa3d813 100644</span><br><span>--- a/res/res_pjsip_session.c</span><br><span>+++ b/res/res_pjsip_session.c</span><br><span>@@ -4816,7 +4816,8 @@</span><br><span>                                              ast_debug(1, "%s: reINVITE received final response code %d\n",</span><br><span>                                                     ast_sip_session_get_name(session),</span><br><span>                                                   tsx->status_code);</span><br><span style="color: hsl(0, 100%, 40%);">-                                           if ((tsx->status_code == 401 || tsx->status_code == 407)</span><br><span style="color: hsl(120, 100%, 40%);">+                                                if ((tsx->status_code == 401 || tsx->status_code == 407</span><br><span style="color: hsl(120, 100%, 40%);">+                                                 || (session->endpoint->security_negotiation && tsx->status_code == 494))</span><br><span>                                                    && ++session->authentication_challenge_count < MAX_RX_CHALLENGES</span><br><span>                                                       && !ast_sip_create_request_with_auth(</span><br><span>                                                                &session->endpoint->outbound_auths,</span><br><span>@@ -4910,7 +4911,7 @@</span><br><span>                                                ast_sip_session_get_name(session),</span><br><span>                                           (int) pj_strlen(&tsx->method.name), pj_strbuf(&tsx->method.name),</span><br><span>                                              tsx->status_code);</span><br><span style="color: hsl(0, 100%, 40%);">-                                   if ((tsx->status_code == 401 || tsx->status_code == 407)</span><br><span style="color: hsl(120, 100%, 40%);">+                                        if ((tsx->status_code == 401 || tsx->status_code == 407 || tsx->status_code == 494)</span><br><span>                                                 && ++session->authentication_challenge_count < MAX_RX_CHALLENGES</span><br><span>                                               && !ast_sip_create_request_with_auth(</span><br><span>                                                        &session->endpoint->outbound_auths,</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/19389">change 19389</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/+/19389"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: Ia7f5b5ba42db18074fdd5428c4e1838728586be2 </div>
<div style="display:none"> Gerrit-Change-Number: 19389 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Maximilian Fridrich <m.fridrich@commend.com> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>