<p>Joshua Colp <strong>submitted</strong> this change.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/15900">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Joshua Colp: Looks good to me, but someone else must approve; Approved for Submit
  George Joseph: Looks good to me, approved

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">STIR/SHAKEN: Add Date header, dest->tn, and URL checking.<br><br>STIR/SHAKEN requires a Date header alongside the Identity header, so<br>that has been added. Still on the outgoing side, we were missing the<br>dest->tn section of the JSON payload, so that has been added as well.<br>Moving to the incoming side, URL checking has been added to the public<br>cert URL to ensure that it starts with http.<br><br>https://wiki.asterisk.org/wiki/display/AST/OpenSIPit+2021<br><br>Change-Id: Idee5b1b5e45bc3b483b3070e46ce322dca5b3f1c<br>---<br>M include/asterisk/res_pjsip.h<br>M res/res_pjsip.c<br>M res/res_pjsip_registrar.c<br>M res/res_pjsip_stir_shaken.c<br>4 files changed, 89 insertions(+), 26 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h</span><br><span>index 7083b25..351ce09 100644</span><br><span>--- a/include/asterisk/res_pjsip.h</span><br><span>+++ b/include/asterisk/res_pjsip.h</span><br><span>@@ -1024,6 +1024,17 @@</span><br><span> };</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Adds a Date header to the tdata, formatted like:</span><br><span style="color: hsl(120, 100%, 40%);">+ * Date: Wed, 01 Jan 2021 14:53:01 GMT</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 16.19.0</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note There is no checking done to see if the header already exists</span><br><span style="color: hsl(120, 100%, 40%);">+ * before adding it. It's up to the caller of this function to determine</span><br><span style="color: hsl(120, 100%, 40%);">+ * if that needs to be done or not.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void ast_sip_add_date_header(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>  * \brief Register a SIP service in Asterisk.</span><br><span>  *</span><br><span>  * This is more-or-less a wrapper around pjsip_endpt_register_module().</span><br><span>diff --git a/res/res_pjsip.c b/res/res_pjsip.c</span><br><span>index 809096d..84c2559 100644</span><br><span>--- a/res/res_pjsip.c</span><br><span>+++ b/res/res_pjsip.c</span><br><span>@@ -3285,6 +3285,18 @@</span><br><span> /*! Local host address for IPv6 (string form) */</span><br><span> static char host_ip_ipv6_string[PJ_INET6_ADDRSTRLEN];</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+void ast_sip_add_date_header(pjsip_tx_data *tdata)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    char date[256];</span><br><span style="color: hsl(120, 100%, 40%);">+       struct tm tm;</span><br><span style="color: hsl(120, 100%, 40%);">+ time_t t = time(NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      gmtime_r(&t, &tm);</span><br><span style="color: hsl(120, 100%, 40%);">+    strftime(date, sizeof(date), "%a, %d %b %Y %T GMT", &tm);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_sip_add_header(tdata, "Date", date);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int register_service(void *data)</span><br><span> {</span><br><span>      pjsip_module **module = data;</span><br><span>diff --git a/res/res_pjsip_registrar.c b/res/res_pjsip_registrar.c</span><br><span>index c4c091f..6fe4058 100644</span><br><span>--- a/res/res_pjsip_registrar.c</span><br><span>+++ b/res/res_pjsip_registrar.c</span><br><span>@@ -247,19 +247,6 @@</span><br><span>        return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-/*! \brief Helper function which adds a Date header to a response */</span><br><span style="color: hsl(0, 100%, 40%);">-static void registrar_add_date_header(pjsip_tx_data *tdata)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-      char date[256];</span><br><span style="color: hsl(0, 100%, 40%);">- struct tm tm;</span><br><span style="color: hsl(0, 100%, 40%);">-   time_t t = time(NULL);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-  gmtime_r(&t, &tm);</span><br><span style="color: hsl(0, 100%, 40%);">-      strftime(date, sizeof(date), "%a, %d %b %Y %T GMT", &tm);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- ast_sip_add_header(tdata, "Date", date);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> static const pj_str_t path_hdr_name = { "Path", 4 };</span><br><span> </span><br><span> static int build_path_data(pjsip_rx_data *rdata, struct ast_str **path_str)</span><br><span>@@ -898,7 +885,7 @@</span><br><span>       ao2_cleanup(response_contact);</span><br><span> </span><br><span>   /* Add the date header to the response, some UAs use this to set their date and time */</span><br><span style="color: hsl(0, 100%, 40%);">- registrar_add_date_header(tdata);</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_sip_add_date_header(tdata);</span><br><span> </span><br><span>  ao2_callback(contacts, 0, registrar_add_contact, tdata);</span><br><span> </span><br><span>diff --git a/res/res_pjsip_stir_shaken.c b/res/res_pjsip_stir_shaken.c</span><br><span>index a90b821..df2aaf0 100644</span><br><span>--- a/res/res_pjsip_stir_shaken.c</span><br><span>+++ b/res/res_pjsip_stir_shaken.c</span><br><span>@@ -166,7 +166,7 @@</span><br><span>          return 0;</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   /* Trim "info=<" to get public key URL */</span><br><span style="color: hsl(120, 100%, 40%);">+        /* Trim "info=<" to get public cert URL */</span><br><span>      strtok_r(identity_hdr_val, "<", &identity_hdr_val);</span><br><span>         public_cert_url = strtok_r(identity_hdr_val, ">", &identity_hdr_val);</span><br><span>       if (ast_strlen_zero(public_cert_url)) {</span><br><span>@@ -174,6 +174,12 @@</span><br><span>               return 0;</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ /* Make sure the public URL is actually a URL */</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!ast_begins_with(public_cert_url, "http")) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_stir_shaken_add_verification(chan, caller_id, "", AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED);</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>  algorithm = strtok_r(identity_hdr_val, ";", &identity_hdr_val);</span><br><span>        if (ast_strlen_zero(algorithm)) {</span><br><span>            ast_stir_shaken_add_verification(chan, caller_id, "", AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED);</span><br><span>@@ -202,17 +208,20 @@</span><br><span>    return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static void add_identity_header(const struct ast_sip_session *session, pjsip_tx_data *tdata)</span><br><span style="color: hsl(120, 100%, 40%);">+static int add_identity_header(const struct ast_sip_session *session, pjsip_tx_data *tdata)</span><br><span> {</span><br><span>     static const pj_str_t identity_str = { "Identity", 8 };</span><br><span>    pjsip_generic_string_hdr *identity_hdr;</span><br><span>      pj_str_t identity_val;</span><br><span>       pjsip_fromto_hdr *old_identity;</span><br><span style="color: hsl(120, 100%, 40%);">+       pjsip_fromto_hdr *to;</span><br><span style="color: hsl(120, 100%, 40%);">+ pjsip_sip_uri *uri;</span><br><span>  char *signature;</span><br><span>     char *public_cert_url;</span><br><span>       struct ast_json *header;</span><br><span>     struct ast_json *payload;</span><br><span>    char *dumped_string;</span><br><span style="color: hsl(120, 100%, 40%);">+  RAII_VAR(char *, dest_tn, NULL, ast_free);</span><br><span>   RAII_VAR(struct ast_json *, json, NULL, ast_json_free);</span><br><span>      RAII_VAR(struct ast_stir_shaken_payload *, ss_payload, NULL, ast_stir_shaken_payload_free);</span><br><span>  RAII_VAR(char *, encoded_header, NULL, ast_free);</span><br><span>@@ -222,21 +231,43 @@</span><br><span> </span><br><span>        old_identity = pjsip_msg_find_hdr_by_name(tdata->msg, &identity_str, NULL);</span><br><span>   if (old_identity) {</span><br><span style="color: hsl(0, 100%, 40%);">-             return;</span><br><span style="color: hsl(120, 100%, 40%);">+               return 0;</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ to = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_TO, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!to) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_log(LOG_ERROR, "Failed to find To header while adding STIR/SHAKEN Identity header\n");</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%);">+   uri = pjsip_uri_get_uri(to->uri);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!uri) {</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_log(LOG_ERROR, "Failed to retrieve URI from To header while adding STIR/SHAKEN Identity header\n");</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%);">+   dest_tn = ast_malloc(uri->user.slen + 1);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!dest_tn) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_log(LOG_ERROR, "Failed to allocate memory for STIR/SHAKEN dest->tn\n");</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%);">+   ast_copy_pj_str(dest_tn, &uri->user, uri->user.slen + 1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>        /* x5u (public key URL), attestation, and origid will be added by ast_stir_shaken_sign */</span><br><span style="color: hsl(0, 100%, 40%);">-       json = ast_json_pack("{s: {s: s, s: s, s: s}, s: {s: {s: s}}}", "header", "alg", "ES256", "ppt", "shaken", "typ", "passport",</span><br><span style="color: hsl(0, 100%, 40%);">-           "payload", "orig", "tn", session->id.number.str);</span><br><span style="color: hsl(120, 100%, 40%);">+    json = ast_json_pack("{s: {s: s, s: s, s: s}, s: {s: {s: s}, s: {s: s}}}",</span><br><span style="color: hsl(120, 100%, 40%);">+          "header", "alg", "ES256", "ppt", "shaken", "typ", "passport",</span><br><span style="color: hsl(120, 100%, 40%);">+           "payload", "dest", "tn", dest_tn, "orig", "tn",</span><br><span style="color: hsl(120, 100%, 40%);">+             session->id.number.str);</span><br><span>  if (!json) {</span><br><span>                 ast_log(LOG_ERROR, "Failed to allocate memory for STIR/SHAKEN JSON\n");</span><br><span style="color: hsl(0, 100%, 40%);">-               return;</span><br><span style="color: hsl(120, 100%, 40%);">+               return -1;</span><br><span>   }</span><br><span> </span><br><span>        ss_payload = ast_stir_shaken_sign(json);</span><br><span>     if (!ss_payload) {</span><br><span>           ast_log(LOG_ERROR, "Failed to allocate memory for STIR/SHAKEN payload\n");</span><br><span style="color: hsl(0, 100%, 40%);">-            return;</span><br><span style="color: hsl(120, 100%, 40%);">+               return -1;</span><br><span>   }</span><br><span> </span><br><span>        header = ast_json_object_get(json, "header");</span><br><span>@@ -245,7 +276,7 @@</span><br><span>        ast_json_free(dumped_string);</span><br><span>        if (!encoded_header) {</span><br><span>               ast_log(LOG_ERROR, "Failed to encode STIR/SHAKEN header\n");</span><br><span style="color: hsl(0, 100%, 40%);">-          return;</span><br><span style="color: hsl(120, 100%, 40%);">+               return -1;</span><br><span>   }</span><br><span> </span><br><span>        payload = ast_json_object_get(json, "payload");</span><br><span>@@ -254,7 +285,7 @@</span><br><span>      ast_json_free(dumped_string);</span><br><span>        if (!encoded_payload) {</span><br><span>              ast_log(LOG_ERROR, "Failed to encode STIR/SHAKEN payload\n");</span><br><span style="color: hsl(0, 100%, 40%);">-         return;</span><br><span style="color: hsl(120, 100%, 40%);">+               return -1;</span><br><span>   }</span><br><span> </span><br><span>        signature = (char *)ast_stir_shaken_payload_get_signature(ss_payload);</span><br><span>@@ -269,7 +300,7 @@</span><br><span>         combined_str = ast_calloc(1, combined_size);</span><br><span>         if (!combined_str) {</span><br><span>                 ast_log(LOG_ERROR, "Failed to allocate memory for STIR/SHAKEN identity string\n");</span><br><span style="color: hsl(0, 100%, 40%);">-            return;</span><br><span style="color: hsl(120, 100%, 40%);">+               return -1;</span><br><span>   }</span><br><span>    snprintf(combined_str, combined_size, "%s.%s.%s;info=<%s>alg=%s;ppt=%s", encoded_header,</span><br><span>             encoded_payload, signature, public_cert_url, STIR_SHAKEN_ENCRYPTION_ALGORITHM, STIR_SHAKEN_PPT);</span><br><span>@@ -278,10 +309,26 @@</span><br><span>     identity_hdr = pjsip_generic_string_hdr_create(tdata->pool, &identity_str, &identity_val);</span><br><span>        if (!identity_hdr) {</span><br><span>                 ast_log(LOG_ERROR, "Failed to create STIR/SHAKEN Identity header\n");</span><br><span style="color: hsl(0, 100%, 40%);">-         return;</span><br><span style="color: hsl(120, 100%, 40%);">+               return -1;</span><br><span>   }</span><br><span> </span><br><span>        pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)identity_hdr);</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 void add_date_header(const struct ast_sip_session *session, 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 date_str = { "Date", 4 };</span><br><span style="color: hsl(120, 100%, 40%);">+     pjsip_fromto_hdr *old_date;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ old_date = pjsip_msg_find_hdr_by_name(tdata->msg, &date_str, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (old_date) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_debug(3, "Found old STIR/SHAKEN date header, no need to add one\n");</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_sip_add_date_header(tdata);</span><br><span> }</span><br><span> </span><br><span> static void stir_shaken_outgoing_request(struct ast_sip_session *session, pjsip_tx_data *tdata)</span><br><span>@@ -294,7 +341,13 @@</span><br><span>           return;</span><br><span>      }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   add_identity_header(session, tdata);</span><br><span style="color: hsl(120, 100%, 40%);">+  /* If adding the Identity header fails for some reason, there's no point</span><br><span style="color: hsl(120, 100%, 40%);">+   * adding the Date header.</span><br><span style="color: hsl(120, 100%, 40%);">+     */</span><br><span style="color: hsl(120, 100%, 40%);">+   if ((add_identity_header(session, tdata)) != 0) {</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_date_header(session, tdata);</span><br><span> }</span><br><span> </span><br><span> static struct ast_sip_session_supplement stir_shaken_supplement = {</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/15900">change 15900</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/+/15900"/><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: Idee5b1b5e45bc3b483b3070e46ce322dca5b3f1c </div>
<div style="display:none"> Gerrit-Change-Number: 15900 </div>
<div style="display:none"> Gerrit-PatchSet: 4 </div>
<div style="display:none"> Gerrit-Owner: Benjamin Keith Ford <bford@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Friendly Automation </div>
<div style="display:none"> Gerrit-Reviewer: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Joshua Colp <jcolp@sangoma.com> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>