<p>Benjamin Keith Ford has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/14447">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">res_stir_shaken: Add inbound INVITE support.<br><br>Integrated STIR/SHAKEN support with incoming INVITES. Upon receiving an<br>INVITE, the Identity header is retrieved, parsing the message to verify<br>the signature. If any of the parsing fails,<br>AST_STIR_SHAKEN_VERIFY_NOT_PRESENT will be added to the channel for this<br>caller ID. If verification itself fails,<br>AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED will be added. If anything in<br>the payload does not line up with the SIP signaling,<br>AST_STIR_SHAKEN_VERIFY_MISMATCH will be added. If all of the above steps<br>pass, then AST_STIR_SHAKEN_VERIFY_PASSED will be added, completing the<br>verification process.<br><br>A new config option has been added to the general section for<br>stir_shaken.conf. "signature_timeout" is the amount of time a signature<br>will be considered valid. If an INVITE is received and the amount of<br>time between when it was received and when it was signed is greater than<br>signature_timeout, verification will fail.<br><br>Some changes were also made to signing and verification. There was an<br>error where the whole JSON string was being signed rather than the<br>header combined with the payload. This has been changed to sign the<br>correct thing. Verification has been changed to do this as well, and the<br>unit tests have been updated to reflect these changes.<br><br>Change-Id: I855f857be3d1c63b64812ac35d9ce0534085b913<br>---<br>M include/asterisk/res_stir_shaken.h<br>M res/res_pjsip_stir_shaken.c<br>M res/res_stir_shaken.c<br>A res/res_stir_shaken.exports.in<br>M res/res_stir_shaken/general.c<br>M res/res_stir_shaken/general.h<br>6 files changed, 370 insertions(+), 34 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/47/14447/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/include/asterisk/res_stir_shaken.h b/include/asterisk/res_stir_shaken.h</span><br><span>index 48bfa00..61a1c48 100644</span><br><span>--- a/include/asterisk/res_stir_shaken.h</span><br><span>+++ b/include/asterisk/res_stir_shaken.h</span><br><span>@@ -33,6 +33,31 @@</span><br><span> struct ast_json;</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Retrieve the value for 'algorithm' from an ast_stir_shaken_payload</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param payload The payload</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval The algorithm</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+char *ast_stir_shaken_payload_algorithm(const struct ast_stir_shaken_payload *payload);</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 Retrieve the value for 'public_key_url' from an ast_stir_shaken_payload</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param payload The payload</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval The public key URL</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+char *ast_stir_shaken_payload_public_key_url(const struct ast_stir_shaken_payload *payload);</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 Retrieve the value for 'signature_timeout' from 'general' config object</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval The signature timeout</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+unsigned int ast_stir_shaken_get_signature_timeout(void);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span> * \brief Add a STIR/SHAKEN verification result to a channel</span><br><span> *</span><br><span> * \param chan The channel</span><br><span>diff --git a/res/res_pjsip_stir_shaken.c b/res/res_pjsip_stir_shaken.c</span><br><span>index 702383c..e367eea 100644</span><br><span>--- a/res/res_pjsip_stir_shaken.c</span><br><span>+++ b/res/res_pjsip_stir_shaken.c</span><br><span>@@ -3,7 +3,7 @@</span><br><span> *</span><br><span> * Copyright (C) 2020, Sangoma Technologies Corporation</span><br><span> *</span><br><span style="color: hsl(0, 100%, 40%);">- * Kevin Harwell <kharwell@digium.com></span><br><span style="color: hsl(120, 100%, 40%);">+ * Ben Ford <bford@sangoma.com></span><br><span> *</span><br><span> * See http://www.asterisk.org for more information about</span><br><span> * the Asterisk project. Please do not directly contact</span><br><span>@@ -18,22 +18,267 @@</span><br><span> </span><br><span> /*** MODULEINFO</span><br><span> <depend>crypto</depend></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%);">+ <depend>res_stir_shaken</depend></span><br><span> <support_level>core</support_level></span><br><span> ***/</span><br><span> </span><br><span> #include "asterisk.h"</span><br><span> </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> #include "asterisk/module.h"</span><br><span> </span><br><span> #include "asterisk/res_stir_shaken.h"</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Decode a base 64 encoded string</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note This function just makes it easier to base64decode since it</span><br><span style="color: hsl(120, 100%, 40%);">+ * calculates the length for you</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param s The encoded string</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval NULL on failure</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval decoded string on success</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static char *decode_base64_string(const char *s)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ size_t encoded_len;</span><br><span style="color: hsl(120, 100%, 40%);">+ size_t decoded_len;</span><br><span style="color: hsl(120, 100%, 40%);">+ int padding = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned char *decoded_string;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(s)) {</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%);">+ encoded_len = strlen(s);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (encoded_len > 2 && s[encoded_len - 1] == '=') {</span><br><span style="color: hsl(120, 100%, 40%);">+ padding++;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (s[encoded_len - 2] == '=') {</span><br><span style="color: hsl(120, 100%, 40%);">+ padding++;</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%);">+ decoded_len = (encoded_len / 4 * 3) - padding;</span><br><span style="color: hsl(120, 100%, 40%);">+ decoded_string = ast_calloc(1, decoded_len);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!decoded_string) {</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%);">+ ast_base64decode(decoded_string, s, decoded_len);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return (char *)decoded_string;</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 Get the attestation from the payload</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param json_str The JSON string representation of the payload</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval Empty string on failure</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval The attestation on success</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static char *get_attestation_from_payload(const char *json_str)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(struct ast_json *, json, NULL, ast_json_free);</span><br><span style="color: hsl(120, 100%, 40%);">+ char *attestation;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ json = ast_json_load_string(json_str, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ attestation = (char *)ast_json_string_get(ast_json_object_get(json, "attest"));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(attestation)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return attestation;</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 "";</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 Compare the caller ID from the INVITE with the one in the payload</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param json_str The JSON string represntation of the payload</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval -1 on failure</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 on success</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int compare_caller_id(char *caller_id, const char *json_str)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(struct ast_json *, json, NULL, ast_json_free);</span><br><span style="color: hsl(120, 100%, 40%);">+ char *caller_id_other;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ json = ast_json_load_string(json_str, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ caller_id_other = (char *)ast_json_string_get(ast_json_object_get(</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_json_object_get(json, "orig"), "tn"));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(caller_id, caller_id_other)) {</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%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Compare the current timestamp with the one in the payload. If the difference</span><br><span style="color: hsl(120, 100%, 40%);">+ * is greater than the signature timeout, it's not valid anymore</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param json_str The JSON string representation of the payload</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval -1 on failure</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 on success</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int compare_timestamp(const char *json_str)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(struct ast_json *, json, NULL, ast_json_free);</span><br><span style="color: hsl(120, 100%, 40%);">+ long int timestamp;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct timeval now = ast_tvnow();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ json = ast_json_load_string(json_str, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ timestamp = ast_json_integer_get(ast_json_object_get(json, "iat"));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (now.tv_sec - timestamp > ast_stir_shaken_get_signature_timeout()) {</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%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Get a specific header value</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param rdata The INVITE</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param str The header to find</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval NULL on failure</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval The header value on success</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static char *get_value_from_header(pjsip_rx_data *rdata, const pj_str_t str)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ pjsip_generic_string_hdr *hdr;</span><br><span style="color: hsl(120, 100%, 40%);">+ pj_str_t hdr_val;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ hdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,</span><br><span style="color: hsl(120, 100%, 40%);">+ &str, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!hdr) {</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%);">+ pj_strdup_with_null(rdata->tp_info.pool, &hdr_val, &hdr->hvalue);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return hdr_val.ptr;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Session supplement callback on an incoming INVITE request</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * When we receive an INVITE, check it for STIR/SHAKEN information and</span><br><span style="color: hsl(120, 100%, 40%);">+ * decide what to do from there</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param session The session that has received an INVITE</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param rdata The incoming INVITE</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int stir_shaken_incoming_request(struct ast_sip_session *session, 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 call_id_str = { "Call-ID", 7 };</span><br><span style="color: hsl(120, 100%, 40%);">+ static const pj_str_t identity_str = { "Identity", 8 };</span><br><span style="color: hsl(120, 100%, 40%);">+ char *caller_id_hdr_val;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *identity_hdr_val;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *encoded_val;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_channel *chan = session->channel;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *caller_id = session->id.number.str;</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(char *, header, NULL, ast_free);</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(char *, payload, NULL, ast_free);</span><br><span style="color: hsl(120, 100%, 40%);">+ char *signature;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *algorithm;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *public_key_url;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *attestation;</span><br><span style="color: hsl(120, 100%, 40%);">+ int mismatch = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stir_shaken_payload *ss_payload;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ caller_id_hdr_val = get_value_from_header(rdata, call_id_str);</span><br><span style="color: hsl(120, 100%, 40%);">+ identity_hdr_val = get_value_from_header(rdata, identity_str);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(caller_id_hdr_val) || ast_strlen_zero(identity_hdr_val)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stir_shaken_add_verification(chan, caller_id, "", AST_STIR_SHAKEN_VERIFY_NOT_PRESENT);</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%);">+ encoded_val = strtok_r(identity_hdr_val, ".", &identity_hdr_val);</span><br><span style="color: hsl(120, 100%, 40%);">+ header = decode_base64_string(encoded_val);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(header)) {</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 style="color: hsl(120, 100%, 40%);">+ encoded_val = strtok_r(identity_hdr_val, ".", &identity_hdr_val);</span><br><span style="color: hsl(120, 100%, 40%);">+ payload = decode_base64_string(encoded_val);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(payload)) {</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 style="color: hsl(120, 100%, 40%);">+ /* It's fine to leave the signature encoded */</span><br><span style="color: hsl(120, 100%, 40%);">+ signature = strtok_r(identity_hdr_val, ";", &identity_hdr_val);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(signature)) {</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 style="color: hsl(120, 100%, 40%);">+ /* Trim "info=<" to get public key URL */</span><br><span style="color: hsl(120, 100%, 40%);">+ strtok_r(identity_hdr_val, "<", &identity_hdr_val);</span><br><span style="color: hsl(120, 100%, 40%);">+ public_key_url = strtok_r(identity_hdr_val, ">", &identity_hdr_val);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(public_key_url)) {</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 style="color: hsl(120, 100%, 40%);">+ algorithm = strtok_r(identity_hdr_val, ";", &identity_hdr_val);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(algorithm)) {</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 style="color: hsl(120, 100%, 40%);">+ attestation = get_attestation_from_payload(payload);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ss_payload = ast_stir_shaken_verify(header, payload, signature, algorithm, public_key_url);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ss_payload) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stir_shaken_add_verification(chan, caller_id, attestation, 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%);">+ ast_stir_shaken_payload_free(ss_payload);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ mismatch |= compare_caller_id(caller_id_hdr_val, payload);</span><br><span style="color: hsl(120, 100%, 40%);">+ mismatch |= compare_timestamp(payload);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (mismatch) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stir_shaken_add_verification(chan, caller_id, attestation, AST_STIR_SHAKEN_VERIFY_MISMATCH);</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_stir_shaken_add_verification(chan, caller_id, attestation, AST_STIR_SHAKEN_VERIFY_PASSED);</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 struct ast_sip_session_supplement stir_shaken_supplement = {</span><br><span style="color: hsl(120, 100%, 40%);">+ .method = "INVITE",</span><br><span style="color: hsl(120, 100%, 40%);">+ .priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL + 1, /* Run AFTER channel creation */</span><br><span style="color: hsl(120, 100%, 40%);">+ .incoming_request = stir_shaken_incoming_request,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int unload_module(void)</span><br><span> {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_unregister_supplement(&stir_shaken_supplement);</span><br><span> return 0;</span><br><span> }</span><br><span> </span><br><span> static int load_module(void)</span><br><span> {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_module_shutdown_ref(AST_MODULE_SELF);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_register_supplement(&stir_shaken_supplement);</span><br><span> return AST_MODULE_LOAD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span>diff --git a/res/res_stir_shaken.c b/res/res_stir_shaken.c</span><br><span>index 86117cd..4b5536c 100644</span><br><span>--- a/res/res_stir_shaken.c</span><br><span>+++ b/res/res_stir_shaken.c</span><br><span>@@ -65,6 +65,9 @@</span><br><span> <configOption name="curl_timeout" default="2"></span><br><span> <synopsis>Maximum time to wait to CURL certificates</synopsis></span><br><span> </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+ <configOption name="signature_timeout" default="15"></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis>Amount of time a signature is valid for</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ </configOption></span><br><span> </configObject></span><br><span> <configObject name="store"></span><br><span> <synopsis>STIR/SHAKEN certificate store options</synopsis></span><br><span>@@ -181,6 +184,21 @@</span><br><span> ast_free(payload);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+char *ast_stir_shaken_payload_algorithm(const struct ast_stir_shaken_payload *payload)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return payload ? payload->algorithm : 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%);">+char *ast_stir_shaken_payload_public_key_url(const struct ast_stir_shaken_payload *payload)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return payload ? payload->public_key_url : 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%);">+unsigned int ast_stir_shaken_get_signature_timeout(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return ast_stir_shaken_signature_timeout(stir_shaken_general_get());</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*!</span><br><span> * \brief Convert an ast_stir_shaken_verification_result to string representation</span><br><span> *</span><br><span>@@ -270,8 +288,8 @@</span><br><span> return -1;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- if (ast_strlen_zero(attestation)) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_log(LOG_ERROR, "No attestation to add STIR/SHAKEN verification to "</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!attestation) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Attestation cannot be NULL to add STIR/SHAKEN verification to "</span><br><span> "channel %s\n", chan_name);</span><br><span> return -1;</span><br><span> }</span><br><span>@@ -593,8 +611,9 @@</span><br><span> EVP_PKEY *public_key;</span><br><span> char *filename;</span><br><span> int curl = 0;</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_json_error err;</span><br><span> RAII_VAR(char *, file_path, NULL, ast_free);</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(char *, combined_str, NULL, ast_free);</span><br><span style="color: hsl(120, 100%, 40%);">+ size_t combined_size;</span><br><span> </span><br><span> if (ast_strlen_zero(header)) {</span><br><span> ast_log(LOG_ERROR, "'header' is required for STIR/SHAKEN verification\n");</span><br><span>@@ -697,7 +716,16 @@</span><br><span> }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- if (stir_shaken_verify_signature(payload, signature, public_key)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Combine the header and payload to get the original signed message: header.payload */</span><br><span style="color: hsl(120, 100%, 40%);">+ combined_size = strlen(header) + strlen(payload) + 2;</span><br><span style="color: hsl(120, 100%, 40%);">+ combined_str = ast_calloc(1, combined_size);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!combined_str) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Failed to allocate space for message to verify\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ EVP_PKEY_free(public_key);</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%);">+ snprintf(combined_str, combined_size, "%s.%s", header, payload);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (stir_shaken_verify_signature(combined_str, signature, public_key)) {</span><br><span> ast_log(LOG_ERROR, "Failed to verify signature\n");</span><br><span> EVP_PKEY_free(public_key);</span><br><span> return NULL;</span><br><span>@@ -712,14 +740,14 @@</span><br><span> return NULL;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- ret_payload->header = ast_json_load_string(header, &err);</span><br><span style="color: hsl(120, 100%, 40%);">+ ret_payload->header = ast_json_load_string(header, NULL);</span><br><span> if (!ret_payload->header) {</span><br><span> ast_log(LOG_ERROR, "Failed to create JSON from header\n");</span><br><span> ast_stir_shaken_payload_free(ret_payload);</span><br><span> return NULL;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- ret_payload->payload = ast_json_load_string(payload, &err);</span><br><span style="color: hsl(120, 100%, 40%);">+ ret_payload->payload = ast_json_load_string(payload, NULL);</span><br><span> if (!ret_payload->payload) {</span><br><span> ast_log(LOG_ERROR, "Failed to create JSON from payload\n");</span><br><span> ast_stir_shaken_payload_free(ret_payload);</span><br><span>@@ -1000,14 +1028,18 @@</span><br><span> </span><br><span> struct ast_stir_shaken_payload *ast_stir_shaken_sign(struct ast_json *json)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_stir_shaken_payload *payload;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stir_shaken_payload *ss_payload;</span><br><span> unsigned char *signature;</span><br><span> const char *caller_id_num;</span><br><span style="color: hsl(0, 100%, 40%);">- char *json_str = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *header;</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *payload;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_json *tmp_json;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *msg = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ size_t msg_len;</span><br><span> struct stir_shaken_certificate *cert = NULL;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- payload = stir_shaken_verify_json(json);</span><br><span style="color: hsl(0, 100%, 40%);">- if (!payload) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ss_payload = stir_shaken_verify_json(json);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ss_payload) {</span><br><span> return NULL;</span><br><span> }</span><br><span> </span><br><span>@@ -1052,27 +1084,34 @@</span><br><span> goto cleanup;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- json_str = ast_json_dump_string(json);</span><br><span style="color: hsl(0, 100%, 40%);">- if (!json_str) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_log(LOG_ERROR, "Failed to convert JSON to string\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Get the header and the payload. Combine them to get the message to sign */</span><br><span style="color: hsl(120, 100%, 40%);">+ tmp_json = ast_json_object_get(json, "header");</span><br><span style="color: hsl(120, 100%, 40%);">+ header = ast_json_dump_string(tmp_json);</span><br><span style="color: hsl(120, 100%, 40%);">+ tmp_json = ast_json_object_get(json, "payload");</span><br><span style="color: hsl(120, 100%, 40%);">+ payload = ast_json_dump_string(tmp_json);</span><br><span style="color: hsl(120, 100%, 40%);">+ msg_len = strlen(header) + strlen(payload) + 2;</span><br><span style="color: hsl(120, 100%, 40%);">+ msg = ast_calloc(1, msg_len);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!msg) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Failed to allocate space for message to sign\n");</span><br><span> goto cleanup;</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(msg, msg_len, "%s.%s", header, payload);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- signature = stir_shaken_sign(json_str, stir_shaken_certificate_get_private_key(cert));</span><br><span style="color: hsl(120, 100%, 40%);">+ signature = stir_shaken_sign(msg, stir_shaken_certificate_get_private_key(cert));</span><br><span> if (!signature) {</span><br><span> goto cleanup;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- payload->signature = signature;</span><br><span style="color: hsl(120, 100%, 40%);">+ ss_payload->signature = signature;</span><br><span> ao2_cleanup(cert);</span><br><span style="color: hsl(0, 100%, 40%);">- ast_json_free(json_str);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(msg);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- return payload;</span><br><span style="color: hsl(120, 100%, 40%);">+ return ss_payload;</span><br><span> </span><br><span> cleanup:</span><br><span> ao2_cleanup(cert);</span><br><span style="color: hsl(0, 100%, 40%);">- ast_stir_shaken_payload_free(payload);</span><br><span style="color: hsl(0, 100%, 40%);">- ast_json_free(json_str);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stir_shaken_payload_free(ss_payload);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(msg);</span><br><span> return NULL;</span><br><span> }</span><br><span> </span><br><span>@@ -1424,12 +1463,13 @@</span><br><span> {</span><br><span> char *caller_id_number = "1234567";</span><br><span> char *public_key_url = "http://testing123";</span><br><span style="color: hsl(0, 100%, 40%);">- char *header = "{\"header\": \"placeholder\"}";</span><br><span style="color: hsl(120, 100%, 40%);">+ char *header;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *payload;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_json *tmp_json;</span><br><span> char public_path[] = "/tmp/stir_shaken_public.XXXXXX";</span><br><span> char private_path[] = "/tmp/stir_shaken_public.XXXXXX";</span><br><span> RAII_VAR(char *, rm_on_exit_public, public_path, unlink);</span><br><span> RAII_VAR(char *, rm_on_exit_private, private_path, unlink);</span><br><span style="color: hsl(0, 100%, 40%);">- RAII_VAR(char *, json_str, NULL, ast_json_free);</span><br><span> RAII_VAR(struct ast_json *, json, NULL, ast_json_free);</span><br><span> RAII_VAR(struct ast_stir_shaken_payload *, signed_payload, NULL, ast_stir_shaken_payload_free);</span><br><span> RAII_VAR(struct ast_stir_shaken_payload *, returned_payload, NULL, ast_stir_shaken_payload_free);</span><br><span>@@ -1463,16 +1503,14 @@</span><br><span> return AST_TEST_FAIL;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- /* Get the message to use for verification */</span><br><span style="color: hsl(0, 100%, 40%);">- json_str = ast_json_dump_string(json);</span><br><span style="color: hsl(0, 100%, 40%);">- if (!json_str) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_test_status_update(test, "Failed to create string from JSON\n");</span><br><span style="color: hsl(0, 100%, 40%);">- test_stir_shaken_cleanup_cert(caller_id_number);</span><br><span style="color: hsl(0, 100%, 40%);">- return AST_TEST_FAIL;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Get the header and payload for ast_stir_shaken_verify */</span><br><span style="color: hsl(120, 100%, 40%);">+ tmp_json = ast_json_object_get(json, "header");</span><br><span style="color: hsl(120, 100%, 40%);">+ header = ast_json_dump_string(tmp_json);</span><br><span style="color: hsl(120, 100%, 40%);">+ tmp_json = ast_json_object_get(json, "payload");</span><br><span style="color: hsl(120, 100%, 40%);">+ payload = ast_json_dump_string(tmp_json);</span><br><span> </span><br><span> /* Test empty header parameter */</span><br><span style="color: hsl(0, 100%, 40%);">- returned_payload = ast_stir_shaken_verify("", json_str, (const char *)signed_payload->signature,</span><br><span style="color: hsl(120, 100%, 40%);">+ returned_payload = ast_stir_shaken_verify("", payload, (const char *)signed_payload->signature,</span><br><span> STIR_SHAKEN_ENCRYPTION_ALGORITHM, public_key_url);</span><br><span> if (returned_payload) {</span><br><span> ast_test_status_update(test, "Verified a signature with missing 'header'\n");</span><br><span>@@ -1490,7 +1528,7 @@</span><br><span> }</span><br><span> </span><br><span> /* Test empty signature parameter */</span><br><span style="color: hsl(0, 100%, 40%);">- returned_payload = ast_stir_shaken_verify(header, json_str, "",</span><br><span style="color: hsl(120, 100%, 40%);">+ returned_payload = ast_stir_shaken_verify(header, payload, "",</span><br><span> STIR_SHAKEN_ENCRYPTION_ALGORITHM, public_key_url);</span><br><span> if (returned_payload) {</span><br><span> ast_test_status_update(test, "Verified a signature with missing 'signature'\n");</span><br><span>@@ -1499,7 +1537,7 @@</span><br><span> }</span><br><span> </span><br><span> /* Test empty algorithm parameter */</span><br><span style="color: hsl(0, 100%, 40%);">- returned_payload = ast_stir_shaken_verify(header, json_str, (const char *)signed_payload->signature,</span><br><span style="color: hsl(120, 100%, 40%);">+ returned_payload = ast_stir_shaken_verify(header, payload, (const char *)signed_payload->signature,</span><br><span> "", public_key_url);</span><br><span> if (returned_payload) {</span><br><span> ast_test_status_update(test, "Verified a signature with missing 'algorithm'\n");</span><br><span>@@ -1508,7 +1546,7 @@</span><br><span> }</span><br><span> </span><br><span> /* Test empty public key URL */</span><br><span style="color: hsl(0, 100%, 40%);">- returned_payload = ast_stir_shaken_verify(header, json_str, (const char *)signed_payload->signature,</span><br><span style="color: hsl(120, 100%, 40%);">+ returned_payload = ast_stir_shaken_verify(header, payload, (const char *)signed_payload->signature,</span><br><span> STIR_SHAKEN_ENCRYPTION_ALGORITHM, "");</span><br><span> if (returned_payload) {</span><br><span> ast_test_status_update(test, "Verified a signature with missing 'public key URL'\n");</span><br><span>@@ -1520,7 +1558,7 @@</span><br><span> test_stir_shaken_add_fake_astdb_entry(public_key_url, public_path);</span><br><span> </span><br><span> /* Verify a valid signature */</span><br><span style="color: hsl(0, 100%, 40%);">- returned_payload = ast_stir_shaken_verify(header, json_str, (const char *)signed_payload->signature,</span><br><span style="color: hsl(120, 100%, 40%);">+ returned_payload = ast_stir_shaken_verify(header, payload, (const char *)signed_payload->signature,</span><br><span> STIR_SHAKEN_ENCRYPTION_ALGORITHM, public_key_url);</span><br><span> if (!returned_payload) {</span><br><span> ast_test_status_update(test, "Failed to verify a valid signature\n");</span><br><span>diff --git a/res/res_stir_shaken.exports.in b/res/res_stir_shaken.exports.in</span><br><span>new file mode 100644</span><br><span>index 0000000..10d214f</span><br><span>--- /dev/null</span><br><span>+++ b/res/res_stir_shaken.exports.in</span><br><span>@@ -0,0 +1,6 @@</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ global:</span><br><span style="color: hsl(120, 100%, 40%);">+ LINKER_SYMBOL_PREFIXast_stir_*;</span><br><span style="color: hsl(120, 100%, 40%);">+ local:</span><br><span style="color: hsl(120, 100%, 40%);">+ *;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span>diff --git a/res/res_stir_shaken/general.c b/res/res_stir_shaken/general.c</span><br><span>index edf8f85..d241082 100644</span><br><span>--- a/res/res_stir_shaken/general.c</span><br><span>+++ b/res/res_stir_shaken/general.c</span><br><span>@@ -31,6 +31,7 @@</span><br><span> #define DEFAULT_CA_PATH ""</span><br><span> #define DEFAULT_CACHE_MAX_SIZE 1000</span><br><span> #define DEFAULT_CURL_TIMEOUT 2</span><br><span style="color: hsl(120, 100%, 40%);">+#define DEFAULT_SIGNATURE_TIMEOUT 15</span><br><span> </span><br><span> struct stir_shaken_general {</span><br><span> SORCERY_OBJECT(details);</span><br><span>@@ -44,6 +45,8 @@</span><br><span> unsigned int cache_max_size;</span><br><span> /*! Maximum time to wait to CURL certificates */</span><br><span> unsigned int curl_timeout;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! Amount of time a signature is valid for */</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned int signature_timeout;</span><br><span> };</span><br><span> </span><br><span> static struct stir_shaken_general *default_config = NULL;</span><br><span>@@ -86,6 +89,11 @@</span><br><span> return cfg ? cfg->curl_timeout : DEFAULT_CURL_TIMEOUT;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+unsigned int ast_stir_shaken_signature_timeout(const struct stir_shaken_general *cfg)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return cfg ? cfg->signature_timeout : DEFAULT_SIGNATURE_TIMEOUT;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static void stir_shaken_general_destructor(void *obj)</span><br><span> {</span><br><span> struct stir_shaken_general *cfg = obj;</span><br><span>@@ -261,6 +269,9 @@</span><br><span> ast_sorcery_object_field_register(sorcery, CONFIG_TYPE, "curl_timeout",</span><br><span> __stringify(DEFAULT_CURL_TIMEOUT), OPT_UINT_T, 0,</span><br><span> FLDSET(struct stir_shaken_general, curl_timeout));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sorcery_object_field_register(sorcery, CONFIG_TYPE, "signature_timeout",</span><br><span style="color: hsl(120, 100%, 40%);">+ __stringify(DEFAULT_SIGNATURE_TIMEOUT), OPT_UINT_T, 0,</span><br><span style="color: hsl(120, 100%, 40%);">+ FLDSET(struct stir_shaken_general, signature_timeout));</span><br><span> </span><br><span> if (ast_sorcery_instance_observer_add(sorcery, &stir_shaken_general_observer)) {</span><br><span> ast_log(LOG_ERROR, "stir/shaken - failed to register loaded observer for '%s' "</span><br><span>diff --git a/res/res_stir_shaken/general.h b/res/res_stir_shaken/general.h</span><br><span>index 357933b..3ea1d69 100644</span><br><span>--- a/res/res_stir_shaken/general.h</span><br><span>+++ b/res/res_stir_shaken/general.h</span><br><span>@@ -84,6 +84,17 @@</span><br><span> unsigned int ast_stir_shaken_curl_timeout(const struct stir_shaken_general *cfg);</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Retrieve the 'signature_timeout' general configuration option value</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note if a NULL configuration is given, then the default value is returned</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param cfg A 'general' configuration object</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval The 'signature_timeout' value</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+unsigned int ast_stir_shaken_signature_timeout(const struct stir_shaken_general *cfg);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span> * \brief Load time initialization for the stir/shaken 'general' configuration</span><br><span> *</span><br><span> * \retval 0 on success, -1 on error</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/14447">change 14447</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/+/14447"/><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: I855f857be3d1c63b64812ac35d9ce0534085b913 </div>
<div style="display:none"> Gerrit-Change-Number: 14447 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Benjamin Keith Ford <bford@digium.com> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>