res_stir_shaken: Add "ignore_sip_date_header" config option.

UserNote: A new STIR/SHAKEN verification option "ignore_sip_date_header" has
been added that when set to true, will cause the verification process to
not consider a missing or invalid SIP "Date" header to be a failure.  This
will make the IAT the sole "truth" for Date in the verification process.
The option can be set in the "verification" and "profile" sections of
stir_shaken.conf.

Also fixed a bug in the port match logic.

Resolves: #1251
Resolves: #1271
(cherry picked from commit 6e9c33caad)
This commit is contained in:
George Joseph
2025-06-15 14:43:13 -06:00
parent 5863873d10
commit 6c1417b228
8 changed files with 48 additions and 20 deletions

View File

@@ -330,6 +330,13 @@ considered "failed".
Default: 15 Default: 15
-- ignore_sip_date_header ---------------------------------------------
When set to true, will cause the verification process to not consider a
missing or invalid SIP "Date" header to be a failure. This will make
the IAT the sole "truth" for Date in the verification process.
Default: no
-- max_date_header_age ------------------------------------------------ -- max_date_header_age ------------------------------------------------
The sender MUST also send a SIP Date header in their request. If we The sender MUST also send a SIP Date header in their request. If we
receive one that is older than the current time by the number of seconds receive one that is older than the current time by the number of seconds

View File

@@ -277,22 +277,13 @@ static int stir_shaken_incoming_request(struct ast_sip_session *session, pjsip_r
} }
date_hdr_val = ast_sip_rdata_get_header_value(rdata, date_hdr_str); date_hdr_val = ast_sip_rdata_get_header_value(rdata, date_hdr_str);
if (ast_strlen_zero(date_hdr_val)) { if (!ast_strlen_zero(date_hdr_val)) {
p_rc = process_failure(ctx, caller_id, session, rdata, vs_rc = ast_stir_shaken_vs_ctx_add_date_hdr(ctx, date_hdr_val);
AST_STIR_SHAKEN_VS_NO_DATE_HDR); if (vs_rc != AST_STIR_SHAKEN_VS_SUCCESS) {
if (p_rc == PROCESS_FAILURE_CONTINUE) { reject_incoming_call(session, 500);
SCOPE_EXIT_RTN_VALUE(0, "%s: No Date header found. Call continuing\n", SCOPE_EXIT_LOG_RTN_VALUE(1, LOG_ERROR, "%s: Unable to add Date header. Call terminated.\n",
session_name); session_name);
} }
SCOPE_EXIT_LOG_RTN_VALUE(1, LOG_ERROR, "%s: No Date header found. Call terminated\n",
session_name);
}
ast_stir_shaken_vs_ctx_add_date_hdr(ctx, date_hdr_val);
if (vs_rc != AST_STIR_SHAKEN_VS_SUCCESS) {
reject_incoming_call(session, 500);
SCOPE_EXIT_LOG_RTN_VALUE(1, LOG_ERROR, "%s: Unable to add Date header. Call terminated.\n",
session_name);
} }
vs_rc = ast_stir_shaken_vs_verify(ctx); vs_rc = ast_stir_shaken_vs_verify(ctx);

View File

@@ -62,6 +62,7 @@ const char *param_name ## _to_str(enum param_name ## _enum value) \
} }
generate_bool_handler_functions(use_rfc9410_responses); generate_bool_handler_functions(use_rfc9410_responses);
generate_bool_handler_functions(ignore_sip_date_header);
generate_bool_handler_functions(send_mky); generate_bool_handler_functions(send_mky);
generate_bool_handler_functions(check_tn_cert_public_url); generate_bool_handler_functions(check_tn_cert_public_url);
generate_bool_handler_functions(relax_x5u_port_scheme_restrictions); generate_bool_handler_functions(relax_x5u_port_scheme_restrictions);

View File

@@ -79,6 +79,8 @@ generate_bool_string_prototypes(relax_x5u_path_restrictions);
generate_bool_string_prototypes(load_system_certs); generate_bool_string_prototypes(load_system_certs);
generate_bool_string_prototypes(ignore_sip_date_header);
generate_bool_string_prototypes(check_tn_cert_public_url); generate_bool_string_prototypes(check_tn_cert_public_url);
generate_bool_string_prototypes(send_mky); generate_bool_string_prototypes(send_mky);
@@ -365,7 +367,7 @@ struct verification_cfg_common {
enum relax_x5u_path_restrictions_enum enum relax_x5u_path_restrictions_enum
relax_x5u_path_restrictions; relax_x5u_path_restrictions;
enum load_system_certs_enum load_system_certs; enum load_system_certs_enum load_system_certs;
enum ignore_sip_date_header_enum ignore_sip_date_header;
struct ast_acl_list *acl; struct ast_acl_list *acl;
struct crypto_cert_store *tcs; struct crypto_cert_store *tcs;
}; };
@@ -381,6 +383,8 @@ struct verification_cfg_common {
generate_sorcery_enum_to_str(object, vcfg_common.,relax_x5u_path_restrictions); \ generate_sorcery_enum_to_str(object, vcfg_common.,relax_x5u_path_restrictions); \
generate_sorcery_enum_from_str(object, vcfg_common.,load_system_certs, UNKNOWN); \ generate_sorcery_enum_from_str(object, vcfg_common.,load_system_certs, UNKNOWN); \
generate_sorcery_enum_to_str(object, vcfg_common.,load_system_certs); \ generate_sorcery_enum_to_str(object, vcfg_common.,load_system_certs); \
generate_sorcery_enum_from_str(object, vcfg_common.,ignore_sip_date_header, UNKNOWN); \
generate_sorcery_enum_to_str(object, vcfg_common.,ignore_sip_date_header); \
generate_sorcery_acl_from_str(object, acl, NULL); \ generate_sorcery_acl_from_str(object, acl, NULL); \
generate_sorcery_acl_to_str(object, acl); generate_sorcery_acl_to_str(object, acl);
@@ -533,6 +537,7 @@ int tn_config_unload(void);
relax_x5u_path_restrictions, nodoc); \ relax_x5u_path_restrictions, nodoc); \
enum_option_register(sorcery, CONFIG_TYPE, \ enum_option_register(sorcery, CONFIG_TYPE, \
load_system_certs, nodoc); \ load_system_certs, nodoc); \
enum_option_register(sorcery, CONFIG_TYPE, ignore_sip_date_header, nodoc); \
\ \
ast_sorcery_object_field_register_custom ## nodoc(sorcery, CONFIG_TYPE, "x5u_deny", "", sorcery_acl_from_str, NULL, NULL, 0, 0); \ ast_sorcery_object_field_register_custom ## nodoc(sorcery, CONFIG_TYPE, "x5u_deny", "", sorcery_acl_from_str, NULL, NULL, 0, 0); \
ast_sorcery_object_field_register_custom ## nodoc(sorcery, CONFIG_TYPE, "x5u_permit", "", sorcery_acl_from_str, NULL, NULL, 0, 0); \ ast_sorcery_object_field_register_custom ## nodoc(sorcery, CONFIG_TYPE, "x5u_permit", "", sorcery_acl_from_str, NULL, NULL, 0, 0); \

View File

@@ -49,6 +49,7 @@
#define DEFAULT_relax_x5u_port_scheme_restrictions relax_x5u_port_scheme_restrictions_NOT_SET #define DEFAULT_relax_x5u_port_scheme_restrictions relax_x5u_port_scheme_restrictions_NOT_SET
#define DEFAULT_relax_x5u_path_restrictions relax_x5u_path_restrictions_NOT_SET #define DEFAULT_relax_x5u_path_restrictions relax_x5u_path_restrictions_NOT_SET
#define DEFAULT_load_system_certs load_system_certs_NOT_SET #define DEFAULT_load_system_certs load_system_certs_NOT_SET
#define DEFAULT_ignore_sip_date_header ignore_sip_date_header_NOT_SET
#define DEFAULT_check_tn_cert_public_url check_tn_cert_public_url_NOT_SET #define DEFAULT_check_tn_cert_public_url check_tn_cert_public_url_NOT_SET
#define DEFAULT_private_key_file NULL #define DEFAULT_private_key_file NULL

View File

@@ -350,6 +350,16 @@
<synopsis>RFC9410 uses the STIR protocol on Reason headers <synopsis>RFC9410 uses the STIR protocol on Reason headers
instead of the SIP protocol</synopsis> instead of the SIP protocol</synopsis>
</configOption> </configOption>
<configOption name="ignore_sip_date_header" default="no">
<since>
<version>20.15.0</version>
<version>21.10.0</version>
<version>22.5.0</version>
</since>
<synopsis>When set to true, will cause the verification process to not consider a
missing or invalid SIP "Date" header to be a failure. This will make the
IAT the sole "truth" for Date in the verification process.</synopsis>
</configOption>
<configOption name="relax_x5u_port_scheme_restrictions" default="no"> <configOption name="relax_x5u_port_scheme_restrictions" default="no">
<since> <since>
<version>18.22.0</version> <version>18.22.0</version>
@@ -422,6 +432,7 @@
<xi:include xpointer="xpointer(/docs/configInfo[@name='res_stir_shaken']/configFile[@name='stir_shaken.conf']/configObject[@name='verification']/configOption[@name='curl_timeout'])" /> <xi:include xpointer="xpointer(/docs/configInfo[@name='res_stir_shaken']/configFile[@name='stir_shaken.conf']/configObject[@name='verification']/configOption[@name='curl_timeout'])" />
<xi:include xpointer="xpointer(/docs/configInfo[@name='res_stir_shaken']/configFile[@name='stir_shaken.conf']/configObject[@name='verification']/configOption[@name='max_iat_age'])" /> <xi:include xpointer="xpointer(/docs/configInfo[@name='res_stir_shaken']/configFile[@name='stir_shaken.conf']/configObject[@name='verification']/configOption[@name='max_iat_age'])" />
<xi:include xpointer="xpointer(/docs/configInfo[@name='res_stir_shaken']/configFile[@name='stir_shaken.conf']/configObject[@name='verification']/configOption[@name='max_date_header_age'])" /> <xi:include xpointer="xpointer(/docs/configInfo[@name='res_stir_shaken']/configFile[@name='stir_shaken.conf']/configObject[@name='verification']/configOption[@name='max_date_header_age'])" />
<xi:include xpointer="xpointer(/docs/configInfo[@name='res_stir_shaken']/configFile[@name='stir_shaken.conf']/configObject[@name='verification']/configOption[@name='ignore_sip_date_header'])" />
<xi:include xpointer="xpointer(/docs/configInfo[@name='res_stir_shaken']/configFile[@name='stir_shaken.conf']/configObject[@name='verification']/configOption[@name='max_cache_entry_age'])" /> <xi:include xpointer="xpointer(/docs/configInfo[@name='res_stir_shaken']/configFile[@name='stir_shaken.conf']/configObject[@name='verification']/configOption[@name='max_cache_entry_age'])" />
<xi:include xpointer="xpointer(/docs/configInfo[@name='res_stir_shaken']/configFile[@name='stir_shaken.conf']/configObject[@name='verification']/configOption[@name='max_cache_size'])" /> <xi:include xpointer="xpointer(/docs/configInfo[@name='res_stir_shaken']/configFile[@name='stir_shaken.conf']/configObject[@name='verification']/configOption[@name='max_cache_size'])" />
<xi:include xpointer="xpointer(/docs/configInfo[@name='res_stir_shaken']/configFile[@name='stir_shaken.conf']/configObject[@name='verification']/configOption[@name='failure_action'])" /> <xi:include xpointer="xpointer(/docs/configInfo[@name='res_stir_shaken']/configFile[@name='stir_shaken.conf']/configObject[@name='verification']/configOption[@name='failure_action'])" />

View File

@@ -733,6 +733,15 @@ static enum ast_stir_shaken_vs_response_code check_date_header(
SCOPE_ENTER(3, "%s: Checking date header: '%s'\n", SCOPE_ENTER(3, "%s: Checking date header: '%s'\n",
ctx->tag, ctx->date_hdr); ctx->tag, ctx->date_hdr);
if (ast_strlen_zero(ctx->date_hdr)) {
if (ctx->eprofile->vcfg_common.ignore_sip_date_header) {
SCOPE_EXIT_RTN_VALUE(AST_STIR_SHAKEN_VS_SUCCESS,
"%s: ignore_sip_date_header set\n", ctx->tag);
}
SCOPE_EXIT_LOG_RTN_VALUE(AST_STIR_SHAKEN_VS_NO_DATE_HDR,
LOG_ERROR, "%s: No date header provided\n", ctx->tag);
}
if (!(remainder = ast_strptime(ctx->date_hdr, "%a, %d %b %Y %T", &date_hdr_tm))) { if (!(remainder = ast_strptime(ctx->date_hdr, "%a, %d %b %Y %T", &date_hdr_tm))) {
SCOPE_EXIT_LOG_RTN_VALUE(AST_STIR_SHAKEN_VS_DATE_HDR_PARSE_FAILURE, SCOPE_EXIT_LOG_RTN_VALUE(AST_STIR_SHAKEN_VS_DATE_HDR_PARSE_FAILURE,
LOG_ERROR, "%s: Failed to parse: '%s'\n", LOG_ERROR, "%s: Failed to parse: '%s'\n",
@@ -853,7 +862,7 @@ static int check_x5u_url(struct ast_stir_shaken_vs_ctx * ctx,
} }
if (!ast_strlen_zero(port)) { if (!ast_strlen_zero(port)) {
if (!ast_strings_equal(port, "443") if (!ast_strings_equal(port, "443")
|| !ast_strings_equal(port, "8443")) { && !ast_strings_equal(port, "8443")) {
DUMP_X5U_MATCH(); DUMP_X5U_MATCH();
SCOPE_EXIT_LOG_RTN_VALUE(AST_STIR_SHAKEN_VS_INVALID_OR_NO_X5U, LOG_ERROR, SCOPE_EXIT_LOG_RTN_VALUE(AST_STIR_SHAKEN_VS_INVALID_OR_NO_X5U, LOG_ERROR,
"%s: x5u '%s': port '%s' not port 443 or 8443\n", "%s: x5u '%s': port '%s' not port 443 or 8443\n",
@@ -940,8 +949,8 @@ enum ast_stir_shaken_vs_response_code
"%s: No x5u in Identity header\n", ctx->tag); "%s: No x5u in Identity header\n", ctx->tag);
} }
rc = check_x5u_url(ctx, x5u); vs_rc = check_x5u_url(ctx, x5u);
if (rc != AST_STIR_SHAKEN_VS_SUCCESS) { if (vs_rc != AST_STIR_SHAKEN_VS_SUCCESS) {
SCOPE_EXIT_RTN_VALUE(vs_rc, SCOPE_EXIT_RTN_VALUE(vs_rc,
"%s: x5u URL verification failed\n", ctx->tag); "%s: x5u URL verification failed\n", ctx->tag);
} }
@@ -957,8 +966,9 @@ enum ast_stir_shaken_vs_response_code
SCOPE_EXIT_LOG_RTN_VALUE(AST_STIR_SHAKEN_VS_NO_IAT, LOG_ERROR, SCOPE_EXIT_LOG_RTN_VALUE(AST_STIR_SHAKEN_VS_NO_IAT, LOG_ERROR,
"%s: No 'iat' in Identity header\n", ctx->tag); "%s: No 'iat' in Identity header\n", ctx->tag);
} }
ast_trace(1, "date_hdr: %zu iat: %zu diff: %zu\n", ast_trace(1, "date_hdr: %zu iat: %zu\n",
ctx->date_hdr_time, iat, ctx->date_hdr_time - iat); ctx->date_hdr_time, iat);
if (iat + ctx->eprofile->vcfg_common.max_iat_age < now_s) { if (iat + ctx->eprofile->vcfg_common.max_iat_age < now_s) {
SCOPE_EXIT_RTN_VALUE(AST_STIR_SHAKEN_VS_IAT_EXPIRED, SCOPE_EXIT_RTN_VALUE(AST_STIR_SHAKEN_VS_IAT_EXPIRED,
"%s: iat %ld older than %u seconds\n", ctx->tag, "%s: iat %ld older than %u seconds\n", ctx->tag,

View File

@@ -46,6 +46,7 @@ static char DEFAULT_cert_cache_dir[PATH_MAX];
#define DEFAULT_relax_x5u_port_scheme_restrictions relax_x5u_port_scheme_restrictions_NO #define DEFAULT_relax_x5u_port_scheme_restrictions relax_x5u_port_scheme_restrictions_NO
#define DEFAULT_relax_x5u_path_restrictions relax_x5u_path_restrictions_NO #define DEFAULT_relax_x5u_path_restrictions relax_x5u_path_restrictions_NO
#define DEFAULT_load_system_certs load_system_certs_NO #define DEFAULT_load_system_certs load_system_certs_NO
#define DEFAULT_ignore_sip_date_header ignore_sip_date_header_NO
static struct verification_cfg *empty_cfg = NULL; static struct verification_cfg *empty_cfg = NULL;
@@ -153,6 +154,7 @@ int vs_copy_cfg_common(const char *id, struct verification_cfg_common *cfg_dst,
cfg_enum_copy(cfg_dst, cfg_src, relax_x5u_port_scheme_restrictions); cfg_enum_copy(cfg_dst, cfg_src, relax_x5u_port_scheme_restrictions);
cfg_enum_copy(cfg_dst, cfg_src, relax_x5u_path_restrictions); cfg_enum_copy(cfg_dst, cfg_src, relax_x5u_path_restrictions);
cfg_enum_copy(cfg_dst, cfg_src, load_system_certs); cfg_enum_copy(cfg_dst, cfg_src, load_system_certs);
cfg_enum_copy(cfg_dst, cfg_src, ignore_sip_date_header);
if (cfg_src->acl) { if (cfg_src->acl) {
ast_free_acl_list(cfg_dst->acl); ast_free_acl_list(cfg_dst->acl);