[Asterisk-code-review] res_stir_shaken: Add stir_shaken option and general improvements. (asterisk[master])

George Joseph asteriskteam at digium.com
Fri Jul 10 09:57:11 CDT 2020


George Joseph has submitted this change. ( https://gerrit.asterisk.org/c/asterisk/+/14574 )

Change subject: res_stir_shaken: Add stir_shaken option and general improvements.
......................................................................

res_stir_shaken: Add stir_shaken option and general improvements.

Added a new configuration option for PJSIP endpoints - stir_shaken. If
set to yes, then STIR/SHAKEN support will be added to inbound and
outbound INVITEs. The default is no. Alembic has been updated to include
this option.

Previously the dialplan function was not trimming the whitespace from
the parameters it recieved. Now it does.

Also added a conditional that, when TEST_FRAMEWORK is enabled, the
timestamp in the identity header will be overlooked. This is just for
testing, since the testsuite will rely on a SIPp scenario with a preset
identity header to trigger the MISMATCH result.

Change-Id: I43d67f1489b8c1c5729ed3ca8d71e35ddf438df1
---
M configs/samples/pjsip.conf.sample
M configs/samples/stir_shaken.conf.sample
A contrib/ast-db-manage/config/versions/61797b9fced6_add_stir_shaken.py
A doc/CHANGES-staging/stir_shaken.txt
M include/asterisk/res_pjsip.h
M res/res_pjsip.c
M res/res_pjsip/pjsip_configuration.c
M res/res_pjsip_stir_shaken.c
M res/res_stir_shaken.c
9 files changed, 127 insertions(+), 14 deletions(-)

Approvals:
  Joshua Colp: Looks good to me, but someone else must approve
  George Joseph: Looks good to me, approved; Approved for Submit



diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample
index cdb585d..2636f63 100644
--- a/configs/samples/pjsip.conf.sample
+++ b/configs/samples/pjsip.conf.sample
@@ -345,6 +345,10 @@
 ;device_state_busy_at=1
 ;allow_subscribe=yes
 ;sub_min_expiry=30
+;
+; STIR/SHAKEN support.
+;
+;stir_shaken=no
 
 ;[6001]
 ;type=auth
@@ -961,6 +965,20 @@
                            ; chan_sip and prevents these 183 responses from
                            ; being forwarded.
                            ; (default: no)
+;stir_shaken =
+                           ; If this is enabled, STIR/SHAKEN operations will be
+                           ; performed on this endpoint. This includes inbound
+                           ; and outbound INVITEs. On an inbound INVITE, Asterisk
+                           ; will check for an Identity header and attempt to
+                           ; verify the call. On an outbound INVITE, Asterisk will
+                           ; add an Identity header that others can use to verify
+                           ; calls from this endpoint. Additional configuration is
+                           ; done in stir_shaken.conf.
+                           ; The STIR_SHAKEN dialplan function must be used to get
+                           ; the verification results on inbound INVITEs. Nothing
+                           ; happens to the call if verification fails; it's up to
+                           ; you to determine what to do with the results.
+                           ; (default: no)
 
 ;==========================AUTH SECTION OPTIONS=========================
 ;[auth]
diff --git a/configs/samples/stir_shaken.conf.sample b/configs/samples/stir_shaken.conf.sample
index 71acad2..957fd14 100644
--- a/configs/samples/stir_shaken.conf.sample
+++ b/configs/samples/stir_shaken.conf.sample
@@ -14,8 +14,11 @@
 ; Maximum size to use for caching public keys
 ;cache_max_size=1000
 ;
-; Maximum time to wait to CURL certificates
-;curl_timeout
+; Maximum time (in seconds) to wait to CURL certificates
+;curl_timeout=2
+;
+; Amount of time (in seconds) a signature is valid for
+;signature_timeout=15
 ;
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;
@@ -48,6 +51,9 @@
 ; URL to the public key
 ;public_key_url=http://mycompany.com/alice.pub
 ;
+; The caller ID number to match on
+;caller_id_number=1234567
+;
 ; Must have an attestation of A, B, or C
 ;attestation=C
 ;
diff --git a/contrib/ast-db-manage/config/versions/61797b9fced6_add_stir_shaken.py b/contrib/ast-db-manage/config/versions/61797b9fced6_add_stir_shaken.py
new file mode 100644
index 0000000..018937c
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/61797b9fced6_add_stir_shaken.py
@@ -0,0 +1,31 @@
+"""add stir shaken
+
+Revision ID: 61797b9fced6
+Revises: fbb7766f17bc
+Create Date: 2020-06-29 11:52:59.946929
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '61797b9fced6'
+down_revision = 'b80485ff4dd0'
+
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects.postgresql import ENUM
+
+YESNO_NAME = 'yesno_values'
+YESNO_VALUES = ['yes', 'no']
+
+AST_BOOL_NAME = 'ast_bool_values'
+AST_BOOL_VALUES = [ '0', '1',
+                    'off', 'on',
+                    'false', 'true',
+                    'no', 'yes' ]
+
+def upgrade():
+    ast_bool_values = ENUM(*AST_BOOL_VALUES, name=AST_BOOL_NAME, create_type=False)
+    op.add_column('ps_endpoints', sa.Column('stir_shaken', ast_bool_values))
+
+def downgrade():
+    op.drop_column('ps_endpoints', 'stir_shaken')
diff --git a/doc/CHANGES-staging/stir_shaken.txt b/doc/CHANGES-staging/stir_shaken.txt
new file mode 100644
index 0000000..3ad1784
--- /dev/null
+++ b/doc/CHANGES-staging/stir_shaken.txt
@@ -0,0 +1,20 @@
+Subject: STIR/SHAKEN
+
+STIR/SHAKEN support has been added to Asterisk. Configuration is done in
+stir_shaken.conf. There is a sample configuration file to help you get
+started (asterisk/configs/samples/stir_shaken.conf.sample). Once that's
+set up, you can enable STIR/SHAKEN on any endpoint by setting stir_shaken
+to yes on the endpoint configuration object. This will add an Identity
+header on outgoing INVITEs, and check for an Identity header on incoming
+INVITEs. This option has been added to Alembic as well.
+
+The information received on an incoming INVITE can be checked using the
+STIR_SHAKEN dialplan function. There are two variations:
+
+STIR_SHAKEN(count)
+STIR_SHAKEN(0, verify_result)
+
+The first variation will tell you how many STIR/SHAKEN results are on the
+channel. The second fetches information for a specific result. The first
+parameter is the index, followed by what information you want to retrieve.
+The available options are 'verify_result', 'identity', and 'attestation'.
diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h
index 0ca29ff..eaa9b21 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -908,6 +908,8 @@
 	unsigned int suppress_q850_reason_headers;
 	/*! Ignore 183 if no SDP is present */
 	unsigned int ignore_183_without_sdp;
+	/*! Enable STIR/SHAKEN support on this endpoint */
+	unsigned int stir_shaken;
 };
 
 /*! URI parameter for symmetric transport */
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index 847a14c..bb77e54 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -1458,6 +1458,14 @@
 						being forwarded.</para>
 					</description>
 				</configOption>
+				<configOption name="stir_shaken" default="no">
+					<synopsis>Enable STIR/SHAKEN support on this endpoint</synopsis>
+					<description><para>
+						Enable STIR/SHAKEN support on this endpoint. On incoming INVITEs,
+						the Identity header will be checked for validity. On outgoing
+						INVITEs, an Identity header will be added.</para>
+					</description>
+				</configOption>
 			</configObject>
 			<configObject name="auth">
 				<synopsis>Authentication type</synopsis>
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index e3eab8a..89ae3e1 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -2140,6 +2140,7 @@
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "outgoing_answer_codec_prefs",
 		"prefer: pending, operation: intersect, keep: all",
 		codec_prefs_handler, outgoing_answer_codec_prefs_to_str, NULL, 0, 0);
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "stir_shaken", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, stir_shaken));
 
 	if (ast_sip_initialize_sorcery_transport()) {
 		ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");
diff --git a/res/res_pjsip_stir_shaken.c b/res/res_pjsip_stir_shaken.c
index 3620579..60dfdc4 100644
--- a/res/res_pjsip_stir_shaken.c
+++ b/res/res_pjsip_stir_shaken.c
@@ -95,6 +95,11 @@
 	long int timestamp;
 	struct timeval now = ast_tvnow();
 
+#ifdef TEST_FRAMEWORK
+	ast_debug(3, "Ignoring STIR/SHAKEN timestamp\n");
+	return 0;
+#endif
+
 	json = ast_json_load_string(json_str, NULL);
 	timestamp = ast_json_integer_get(ast_json_object_get(json, "iat"));
 
@@ -131,6 +136,10 @@
 	int mismatch = 0;
 	struct ast_stir_shaken_payload *ss_payload;
 
+	if (!session->endpoint->stir_shaken) {
+		return 0;
+	}
+
 	identity_hdr_val = ast_sip_rdata_get_header_value(rdata, identity_str);
 	if (ast_strlen_zero(identity_hdr_val)) {
 		ast_stir_shaken_add_verification(chan, caller_id, "", AST_STIR_SHAKEN_VERIFY_NOT_PRESENT);
@@ -278,6 +287,10 @@
 
 static void stir_shaken_outgoing_request(struct ast_sip_session *session, pjsip_tx_data *tdata)
 {
+	if (!session->endpoint->stir_shaken) {
+		return;
+	}
+
 	if (ast_strlen_zero(session->id.number.str) && session->id.number.valid) {
 		return;
 	}
diff --git a/res/res_stir_shaken.c b/res/res_stir_shaken.c
index 632fd1b..7a141f7 100644
--- a/res/res_stir_shaken.c
+++ b/res/res_stir_shaken.c
@@ -153,6 +153,11 @@
 /* The maximum length for path storage */
 #define MAX_PATH_LEN 256
 
+/* The default amount of time (in seconds) to use for certificate expiration
+ * if no cache data is available
+ */
+#define EXPIRATION_BUFFER 15
+
 struct ast_stir_shaken_payload {
 	/*! The JWT header */
 	struct ast_json *header;
@@ -381,6 +386,10 @@
 		}
 	}
 
+	if (ast_strlen_zero(value)) {
+		actual_expires.tv_sec += EXPIRATION_BUFFER;
+	}
+
 	snprintf(time_buf, sizeof(time_buf), "%30lu", actual_expires.tv_sec);
 
 	ast_db_put(hash, "expiration", time_buf);
@@ -1133,6 +1142,8 @@
 	struct stir_shaken_datastore *ss_datastore;
 	struct ast_datastore *datastore;
 	char *parse;
+	char *first;
+	char *second;
 	unsigned int target_index, current_index = 0;
 	AST_DECLARE_APP_ARGS(args,
 		AST_APP_ARG(first_param);
@@ -1153,17 +1164,20 @@
 
 	AST_STANDARD_APP_ARGS(args, parse);
 
-	if (ast_strlen_zero(args.first_param)) {
+	first = ast_strip(args.first_param);
+	if (ast_strlen_zero(first)) {
 		ast_log(LOG_ERROR, "An argument must be passed to %s\n", function);
 		return -1;
 	}
 
+	second = ast_strip(args.second_param);
+
 	/* Check if we are only looking for the number of STIR/SHAKEN verification results */
-	if (!strcasecmp(args.first_param, "count")) {
+	if (!strcasecmp(first, "count")) {
 
 		size_t count = 0;
 
-		if (!ast_strlen_zero(args.second_param)) {
+		if (!ast_strlen_zero(second)) {
 			ast_log(LOG_ERROR, "%s only takes 1 paramater for 'count'\n", function);
 			return -1;
 		}
@@ -1184,15 +1198,15 @@
 	/* If we aren't doing a count, then there should be two parameters. The field
 	 * we are searching for will be the second parameter. The index is the first.
 	 */
-	if (ast_strlen_zero(args.second_param)) {
+	if (ast_strlen_zero(second)) {
 		ast_log(LOG_ERROR, "Retrieving a value using %s requires two paramaters (index, value) "
-			"- only index was given (%s)\n", function, args.second_param);
+			"- only index was given (%s)\n", function, second);
 		return -1;
 	}
 
-	if (ast_str_to_uint(args.first_param, &target_index)) {
+	if (ast_str_to_uint(first, &target_index)) {
 		ast_log(LOG_ERROR, "Failed to convert index %s to integer for function %s\n",
-			args.first_param, function);
+			first, function);
 		return -1;
 	}
 
@@ -1211,19 +1225,19 @@
 	}
 	ast_channel_unlock(chan);
 	if (current_index != target_index || !datastore) {
-		ast_log(LOG_WARNING, "No STIR/SHAKEN results for index '%s'\n", args.first_param);
+		ast_log(LOG_WARNING, "No STIR/SHAKEN results for index '%s'\n", first);
 		return -1;
 	}
 	ss_datastore = datastore->data;
 
-	if (!strcasecmp(args.second_param, "identity")) {
+	if (!strcasecmp(second, "identity")) {
 		ast_copy_string(buf, ss_datastore->identity, len);
-	} else if (!strcasecmp(args.second_param, "attestation")) {
+	} else if (!strcasecmp(second, "attestation")) {
 		ast_copy_string(buf, ss_datastore->attestation, len);
-	} else if (!strcasecmp(args.second_param, "verify_result")) {
+	} else if (!strcasecmp(second, "verify_result")) {
 		ast_copy_string(buf, stir_shaken_verification_result_to_string(ss_datastore->verify_result), len);
 	} else {
-		ast_log(LOG_ERROR, "No such value '%s' for %s\n", args.second_param, function);
+		ast_log(LOG_ERROR, "No such value '%s' for %s\n", second, function);
 		return -1;
 	}
 

-- 
To view, visit https://gerrit.asterisk.org/c/asterisk/+/14574
To unsubscribe, or for help writing mail filters, visit https://gerrit.asterisk.org/settings

Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-Change-Id: I43d67f1489b8c1c5729ed3ca8d71e35ddf438df1
Gerrit-Change-Number: 14574
Gerrit-PatchSet: 10
Gerrit-Owner: Benjamin Keith Ford <bford at digium.com>
Gerrit-Reviewer: Friendly Automation
Gerrit-Reviewer: George Joseph <gjoseph at digium.com>
Gerrit-Reviewer: Joshua Colp <jcolp at sangoma.com>
Gerrit-Reviewer: Kevin Harwell <kharwell at digium.com>
Gerrit-Reviewer: Sean Bright <sean.bright at gmail.com>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20200710/5d538f72/attachment-0001.html>


More information about the asterisk-code-review mailing list