]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
STIR/SHAKEN: Add Date header, dest->tn, and URL checking.
authorBen Ford <bford@digium.com>
Wed, 19 May 2021 18:45:16 +0000 (13:45 -0500)
committerJoshua Colp <jcolp@sangoma.com>
Wed, 26 May 2021 17:46:10 +0000 (12:46 -0500)
STIR/SHAKEN requires a Date header alongside the Identity header, so
that has been added. Still on the outgoing side, we were missing the
dest->tn section of the JSON payload, so that has been added as well.
Moving to the incoming side, URL checking has been added to the public
cert URL to ensure that it starts with http.

https://wiki.asterisk.org/wiki/display/AST/OpenSIPit+2021

Change-Id: Idee5b1b5e45bc3b483b3070e46ce322dca5b3f1c

include/asterisk/res_pjsip.h
res/res_pjsip.c
res/res_pjsip_registrar.c
res/res_pjsip_stir_shaken.c

index afec9d2f19d07ffb7bb6485a5b7e30bba1741c4b..3d5b50420584694af868e2bb842c71a18097698e 100644 (file)
@@ -947,6 +947,17 @@ enum ast_sip_contact_filter {
        AST_SIP_CONTACT_FILTER_REACHABLE = (1 << 0),
 };
 
+/*!
+ * \brief Adds a Date header to the tdata, formatted like:
+ * Date: Wed, 01 Jan 2021 14:53:01 GMT
+ * \since 16.19.0
+ *
+ * \note There is no checking done to see if the header already exists
+ * before adding it. It's up to the caller of this function to determine
+ * if that needs to be done or not.
+ */
+void ast_sip_add_date_header(pjsip_tx_data *tdata);
+
 /*!
  * \brief Register a SIP service in Asterisk.
  *
index 0998fb354a922d873923a46b058825118bac1cc4..24eb884851ae4faebf3f16634aeeb2c6ff211c1a 100644 (file)
@@ -2967,6 +2967,18 @@ static pj_sockaddr host_ip_ipv6;
 /*! Local host address for IPv6 (string form) */
 static char host_ip_ipv6_string[PJ_INET6_ADDRSTRLEN];
 
+void ast_sip_add_date_header(pjsip_tx_data *tdata)
+{
+       char date[256];
+       struct tm tm;
+       time_t t = time(NULL);
+
+       gmtime_r(&t, &tm);
+       strftime(date, sizeof(date), "%a, %d %b %Y %T GMT", &tm);
+
+       ast_sip_add_header(tdata, "Date", date);
+}
+
 static int register_service(void *data)
 {
        pjsip_module **module = data;
index c4c091f744dc3b5c733c3c9bcd06894bb665f9db..6fe40588fdc322d583b1eaacd38a21e530c5d3e3 100644 (file)
@@ -247,19 +247,6 @@ static int registrar_add_contact(void *obj, void *arg, int flags)
        return 0;
 }
 
-/*! \brief Helper function which adds a Date header to a response */
-static void registrar_add_date_header(pjsip_tx_data *tdata)
-{
-       char date[256];
-       struct tm tm;
-       time_t t = time(NULL);
-
-       gmtime_r(&t, &tm);
-       strftime(date, sizeof(date), "%a, %d %b %Y %T GMT", &tm);
-
-       ast_sip_add_header(tdata, "Date", date);
-}
-
 static const pj_str_t path_hdr_name = { "Path", 4 };
 
 static int build_path_data(pjsip_rx_data *rdata, struct ast_str **path_str)
@@ -898,7 +885,7 @@ static void register_aor_core(pjsip_rx_data *rdata,
        ao2_cleanup(response_contact);
 
        /* Add the date header to the response, some UAs use this to set their date and time */
-       registrar_add_date_header(tdata);
+       ast_sip_add_date_header(tdata);
 
        ao2_callback(contacts, 0, registrar_add_contact, tdata);
 
index 5a380738f59111715248513b614c084dc5df0a2c..ea9f35a5f1e8a58e78a063f1ce45370cb5642449 100644 (file)
@@ -169,7 +169,7 @@ static int stir_shaken_incoming_request(struct ast_sip_session *session, pjsip_r
                return 0;
        }
 
-       /* Trim "info=<" to get public key URL */
+       /* Trim "info=<" to get public cert URL */
        strtok_r(identity_hdr_val, "<", &identity_hdr_val);
        public_cert_url = strtok_r(identity_hdr_val, ">", &identity_hdr_val);
        if (ast_strlen_zero(public_cert_url)) {
@@ -177,6 +177,12 @@ static int stir_shaken_incoming_request(struct ast_sip_session *session, pjsip_r
                return 0;
        }
 
+       /* Make sure the public URL is actually a URL */
+       if (!ast_begins_with(public_cert_url, "http")) {
+               ast_stir_shaken_add_verification(chan, caller_id, "", AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED);
+               return 0;
+       }
+
        algorithm = strtok_r(identity_hdr_val, ";", &identity_hdr_val);
        if (ast_strlen_zero(algorithm)) {
                ast_stir_shaken_add_verification(chan, caller_id, "", AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED);
@@ -205,17 +211,20 @@ static int stir_shaken_incoming_request(struct ast_sip_session *session, pjsip_r
        return 0;
 }
 
-static void add_identity_header(const struct ast_sip_session *session, pjsip_tx_data *tdata)
+static int add_identity_header(const struct ast_sip_session *session, pjsip_tx_data *tdata)
 {
        static const pj_str_t identity_str = { "Identity", 8 };
        pjsip_generic_string_hdr *identity_hdr;
        pj_str_t identity_val;
        pjsip_fromto_hdr *old_identity;
+       pjsip_fromto_hdr *to;
+       pjsip_sip_uri *uri;
        char *signature;
        char *public_cert_url;
        struct ast_json *header;
        struct ast_json *payload;
        char *dumped_string;
+       RAII_VAR(char *, dest_tn, NULL, ast_free);
        RAII_VAR(struct ast_json *, json, NULL, ast_json_free);
        RAII_VAR(struct ast_stir_shaken_payload *, ss_payload, NULL, ast_stir_shaken_payload_free);
        RAII_VAR(char *, encoded_header, NULL, ast_free);
@@ -225,21 +234,43 @@ static void add_identity_header(const struct ast_sip_session *session, pjsip_tx_
 
        old_identity = pjsip_msg_find_hdr_by_name(tdata->msg, &identity_str, NULL);
        if (old_identity) {
-               return;
+               return 0;
+       }
+
+       to = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_TO, NULL);
+       if (!to) {
+               ast_log(LOG_ERROR, "Failed to find To header while adding STIR/SHAKEN Identity header\n");
+               return -1;
+       }
+
+       uri = pjsip_uri_get_uri(to->uri);
+       if (!uri) {
+               ast_log(LOG_ERROR, "Failed to retrieve URI from To header while adding STIR/SHAKEN Identity header\n");
+               return -1;
+       }
+
+       dest_tn = ast_malloc(uri->user.slen + 1);
+       if (!dest_tn) {
+               ast_log(LOG_ERROR, "Failed to allocate memory for STIR/SHAKEN dest->tn\n");
+               return -1;
        }
 
+       ast_copy_pj_str(dest_tn, &uri->user, uri->user.slen + 1);
+
        /* x5u (public key URL), attestation, and origid will be added by ast_stir_shaken_sign */
-       json = ast_json_pack("{s: {s: s, s: s, s: s}, s: {s: {s: s}}}", "header", "alg", "ES256", "ppt", "shaken", "typ", "passport",
-               "payload", "orig", "tn", session->id.number.str);
+       json = ast_json_pack("{s: {s: s, s: s, s: s}, s: {s: {s: s}, s: {s: s}}}",
+               "header", "alg", "ES256", "ppt", "shaken", "typ", "passport",
+               "payload", "dest", "tn", dest_tn, "orig", "tn",
+               session->id.number.str);
        if (!json) {
                ast_log(LOG_ERROR, "Failed to allocate memory for STIR/SHAKEN JSON\n");
-               return;
+               return -1;
        }
 
        ss_payload = ast_stir_shaken_sign(json);
        if (!ss_payload) {
                ast_log(LOG_ERROR, "Failed to allocate memory for STIR/SHAKEN payload\n");
-               return;
+               return -1;
        }
 
        header = ast_json_object_get(json, "header");
@@ -248,7 +279,7 @@ static void add_identity_header(const struct ast_sip_session *session, pjsip_tx_
        ast_json_free(dumped_string);
        if (!encoded_header) {
                ast_log(LOG_ERROR, "Failed to encode STIR/SHAKEN header\n");
-               return;
+               return -1;
        }
 
        payload = ast_json_object_get(json, "payload");
@@ -257,7 +288,7 @@ static void add_identity_header(const struct ast_sip_session *session, pjsip_tx_
        ast_json_free(dumped_string);
        if (!encoded_payload) {
                ast_log(LOG_ERROR, "Failed to encode STIR/SHAKEN payload\n");
-               return;
+               return -1;
        }
 
        signature = (char *)ast_stir_shaken_payload_get_signature(ss_payload);
@@ -272,7 +303,7 @@ static void add_identity_header(const struct ast_sip_session *session, pjsip_tx_
        combined_str = ast_calloc(1, combined_size);
        if (!combined_str) {
                ast_log(LOG_ERROR, "Failed to allocate memory for STIR/SHAKEN identity string\n");
-               return;
+               return -1;
        }
        snprintf(combined_str, combined_size, "%s.%s.%s;info=<%s>alg=%s;ppt=%s", encoded_header,
                encoded_payload, signature, public_cert_url, STIR_SHAKEN_ENCRYPTION_ALGORITHM, STIR_SHAKEN_PPT);
@@ -281,10 +312,26 @@ static void add_identity_header(const struct ast_sip_session *session, pjsip_tx_
        identity_hdr = pjsip_generic_string_hdr_create(tdata->pool, &identity_str, &identity_val);
        if (!identity_hdr) {
                ast_log(LOG_ERROR, "Failed to create STIR/SHAKEN Identity header\n");
-               return;
+               return -1;
        }
 
        pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)identity_hdr);
+
+       return 0;
+}
+
+static void add_date_header(const struct ast_sip_session *session, pjsip_tx_data *tdata)
+{
+       static const pj_str_t date_str = { "Date", 4 };
+       pjsip_fromto_hdr *old_date;
+
+       old_date = pjsip_msg_find_hdr_by_name(tdata->msg, &date_str, NULL);
+       if (old_date) {
+               ast_debug(3, "Found old STIR/SHAKEN date header, no need to add one\n");
+               return;
+       }
+
+       ast_sip_add_date_header(tdata);
 }
 
 static void stir_shaken_outgoing_request(struct ast_sip_session *session, pjsip_tx_data *tdata)
@@ -297,7 +344,13 @@ static void stir_shaken_outgoing_request(struct ast_sip_session *session, pjsip_
                return;
        }
 
-       add_identity_header(session, tdata);
+       /* If adding the Identity header fails for some reason, there's no point
+        * adding the Date header.
+        */
+       if ((add_identity_header(session, tdata)) != 0) {
+               return;
+       }
+       add_date_header(session, tdata);
 }
 
 static struct ast_sip_session_supplement stir_shaken_supplement = {