diff --git a/configs/samples/stir_shaken.conf.sample b/configs/samples/stir_shaken.conf.sample
index d5e97979fd..3fe9d7e955 100644
--- a/configs/samples/stir_shaken.conf.sample
+++ b/configs/samples/stir_shaken.conf.sample
@@ -99,6 +99,20 @@ One of "A", "B", "C"
Default: none
+-- unknown_tn_attest_level --------------------------------------------
+Attestation level to use for unknown TNs.
+One of "A", "B", "C"
+
+Normally if a callerid TN isn't configured in stir_shaken.conf
+no Identity header will be created. If this option is set,
+however, an Identity header will be sent using this
+attestation level. Since there's no TN object, you must
+ensure that a private_key_file and public_cert_url are
+configured in the attestation or profile objects for
+this to work.
+
+Default: none
+
-- send_mky -----------------------------------------------------------
If set and an outgoing call uses DTLS, an "mky" Media Key grant will
be added to the Identity header. Although RFC8224/8225 require this,
@@ -116,6 +130,7 @@ Example "attestation" object:
;private_key_file = /var/lib/asterisk/keys/stir_shaken/tns/multi-tns-key.pem
;public_cert_url = https://example.com/tncerts/multi-tns-cert.pem
;attest_level = C
+;unknown_tn_attest_level = C
;--
=======================================================================
diff --git a/res/res_stir_shaken/attestation_config.c b/res/res_stir_shaken/attestation_config.c
index 7a5743c9f7..46ebb90399 100644
--- a/res/res_stir_shaken/attestation_config.c
+++ b/res/res_stir_shaken/attestation_config.c
@@ -15,9 +15,13 @@
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
+
+#define _TRACE_PREFIX_ "ac",__LINE__, ""
+
#include "asterisk.h"
#include "asterisk/cli.h"
+#include "asterisk/logger.h"
#include "asterisk/sorcery.h"
#include "asterisk/paths.h"
@@ -31,6 +35,7 @@
#define DEFAULT_private_key_file NULL
#define DEFAULT_public_cert_url NULL
#define DEFAULT_attest_level attest_level_NOT_SET
+#define DEFAULT_unknown_tn_attest_level attest_level_NOT_SET
#define DEFAULT_send_mky send_mky_NO
static struct attestation_cfg *empty_cfg = NULL;
@@ -57,6 +62,9 @@ int as_is_config_loaded(void)
generate_acfg_common_sorcery_handlers(attestation_cfg);
+generate_sorcery_enum_from_str_ex(attestation_cfg,,unknown_tn_attest_level, attest_level, UNKNOWN);
+generate_sorcery_enum_to_str_ex(attestation_cfg,,unknown_tn_attest_level, attest_level);
+
void acfg_cleanup(struct attestation_cfg_common *acfg_common)
{
if (!acfg_common) {
@@ -309,6 +317,9 @@ int as_config_load(void)
DEFAULT_global_disable ? "yes" : "no",
OPT_YESNO_T, 1, FLDSET(struct attestation_cfg, global_disable));
+ enum_option_register_ex(sorcery, CONFIG_TYPE, unknown_tn_attest_level,
+ unknown_tn_attest_level, attest_level,);
+
register_common_attestation_fields(sorcery, attestation_cfg, CONFIG_TYPE,);
ast_sorcery_load_object(sorcery, CONFIG_TYPE);
diff --git a/res/res_stir_shaken/common_config.h b/res/res_stir_shaken/common_config.h
index 3cb1795d78..e390b548b6 100644
--- a/res/res_stir_shaken/common_config.h
+++ b/res/res_stir_shaken/common_config.h
@@ -116,6 +116,12 @@ generate_enum_string_prototypes(attest_level,
attest_level_C,
);
+/*
+ * unknown_tn_attest_level uses the same enum as attest_level.
+ */
+enum attest_level_enum unknown_tn_attest_level_from_str(const char *value);
+const char *unknown_tn_attest_level_to_str(enum attest_level_enum value);
+
/*
* enum stir_shaken_failure_action is defined in
* res_stir_shaken.h because res_pjsip_stir_shaken needs it
@@ -136,20 +142,23 @@ const char *stir_shaken_failure_action_to_str(
* are _to_str and _from_str functions defined elsewhere.
*
*/
-#define generate_sorcery_enum_to_str(__struct, __substruct, __lc_param) \
+#define generate_sorcery_enum_to_str_ex(__struct, __substruct, __lc_param, __base_enum) \
static int sorcery_ ## __lc_param ## _to_str(const void *obj, const intptr_t *args, char **buf) \
{ \
const struct __struct *cfg = obj; \
- *buf = ast_strdup(__lc_param ## _to_str(cfg->__substruct __lc_param)); \
+ *buf = ast_strdup(__base_enum ## _to_str(cfg->__substruct __lc_param)); \
return *buf ? 0 : -1; \
}
-#define generate_sorcery_enum_from_str_ex(__struct, __substruct, __lc_param, __unknown) \
+#define generate_sorcery_enum_to_str(__struct, __substruct, __lc_param) \
+ generate_sorcery_enum_to_str_ex(__struct, __substruct, __lc_param, __lc_param)
+
+#define generate_sorcery_enum_from_str_ex(__struct, __substruct, __lc_param, __base_enum, __unknown) \
static int sorcery_ ## __lc_param ## _from_str(const struct aco_option *opt, struct ast_variable *var, void *obj) \
{ \
struct __struct *cfg = obj; \
- cfg->__substruct __lc_param = __lc_param ## _from_str (var->value); \
- if (cfg->__substruct __lc_param == __unknown) { \
+ cfg->__substruct __lc_param = __base_enum ## _from_str (var->value); \
+ if (cfg->__substruct __lc_param == __base_enum ## _ ## __unknown) { \
ast_log(LOG_WARNING, "Unknown value '%s' specified for %s\n", \
var->value, var->name); \
return -1; \
@@ -158,7 +167,7 @@ static int sorcery_ ## __lc_param ## _from_str(const struct aco_option *opt, str
}
#define generate_sorcery_enum_from_str(__struct, __substruct, __lc_param, __unknown) \
- generate_sorcery_enum_from_str_ex(__struct, __substruct, __lc_param, __lc_param ## _ ## __unknown) \
+ generate_sorcery_enum_from_str_ex(__struct, __substruct, __lc_param, __lc_param, __unknown) \
#define generate_sorcery_acl_to_str(__struct, __lc_param) \
@@ -263,14 +272,18 @@ struct ast_acl_list *get_default_acl_list(void);
* Copy an enum from the source to the dest only if the source is
* neither NOT_SET nor UNKNOWN
*/
-#define cfg_enum_copy(__cfg_dst, __cfg_src, __field) \
+#define cfg_enum_copy_ex(__cfg_dst, __cfg_src, __field, __not_set, __unknown) \
({ \
- if (__cfg_src->__field != __field ## _NOT_SET \
- && __cfg_src->__field != __field ## _UNKNOWN) { \
+ if (__cfg_src->__field != __not_set \
+ && __cfg_src->__field != __unknown) { \
__cfg_dst->__field = __cfg_src->__field; \
} \
})
+#define cfg_enum_copy(__cfg_dst, __cfg_src, __field) \
+ cfg_enum_copy_ex(__cfg_dst, __cfg_src, __field, __field ## _NOT_SET, __field ## _UNKNOWN)
+
+
/*!
* \brief Attestation Service configuration for stir/shaken
*
@@ -314,6 +327,7 @@ struct attestation_cfg {
*/
AST_DECLARE_STRING_FIELDS();
struct attestation_cfg_common acfg_common;
+ enum attest_level_enum unknown_tn_attest_level;
int global_disable;
};
@@ -412,6 +426,7 @@ struct profile_cfg {
struct attestation_cfg_common acfg_common;
struct verification_cfg_common vcfg_common;
enum endpoint_behavior_enum endpoint_behavior;
+ enum attest_level_enum unknown_tn_attest_level;
struct profile_cfg *eprofile;
};
@@ -486,13 +501,13 @@ int tn_config_unload(void);
__stringify(DEFAULT_ ## name), OPT_UINT_T, 0, \
FLDSET(struct object, field))
-#define enum_option_register_ex(sorcery, CONFIG_TYPE, name, field, nodoc) \
+#define enum_option_register_ex(sorcery, CONFIG_TYPE, name, field, function_prefix, nodoc) \
ast_sorcery_object_field_register_custom ## nodoc(sorcery, CONFIG_TYPE, \
- #name, field ## _to_str(DEFAULT_ ## field), \
+ #name, function_prefix ## _to_str(DEFAULT_ ## field), \
sorcery_ ## field ## _from_str, sorcery_ ## field ## _to_str, NULL, 0, 0)
#define enum_option_register(sorcery, CONFIG_TYPE, name, nodoc) \
- enum_option_register_ex(sorcery, CONFIG_TYPE, name, name, nodoc)
+ enum_option_register_ex(sorcery, CONFIG_TYPE, name, name, name, nodoc)
#define register_common_verification_fields(sorcery, object, CONFIG_TYPE, nodoc) \
({ \
@@ -510,7 +525,7 @@ int tn_config_unload(void);
uint_option_register(sorcery, CONFIG_TYPE, object, max_cache_entry_age, vcfg_common.max_cache_entry_age, nodoc);\
uint_option_register(sorcery, CONFIG_TYPE, object, max_cache_size, vcfg_common.max_cache_size, nodoc);\
\
- enum_option_register_ex(sorcery, CONFIG_TYPE, failure_action, stir_shaken_failure_action, nodoc); \
+ enum_option_register_ex(sorcery, CONFIG_TYPE, failure_action, stir_shaken_failure_action, stir_shaken_failure_action, nodoc); \
enum_option_register(sorcery, CONFIG_TYPE, use_rfc9410_responses, nodoc); \
enum_option_register(sorcery, CONFIG_TYPE, \
relax_x5u_port_scheme_restrictions, nodoc); \
diff --git a/res/res_stir_shaken/crypto_utils.c b/res/res_stir_shaken/crypto_utils.c
index 9ba26688fb..f00d72f9b3 100644
--- a/res/res_stir_shaken/crypto_utils.c
+++ b/res/res_stir_shaken/crypto_utils.c
@@ -498,11 +498,13 @@ static int pem_file_cb(const char *dir_name, const char *filename, void *obj)
if (lstat(filename_merged, &statbuf)) {
printf("Error reading path stats - %s: %s\n",
filename_merged, strerror(errno));
+ ast_free(filename_merged);
return -1;
}
/* We only want the symlinks from the directory */
if (!S_ISLNK(statbuf.st_mode)) {
+ ast_free(filename_merged);
return 0;
}
@@ -512,6 +514,7 @@ static int pem_file_cb(const char *dir_name, const char *filename, void *obj)
rc = crypto_load_store_from_cert_file(data->store, filename_merged);
}
+ ast_free(filename_merged);
return rc;
}
diff --git a/res/res_stir_shaken/profile_config.c b/res/res_stir_shaken/profile_config.c
index 6e5a78a448..e3379ebe3b 100644
--- a/res/res_stir_shaken/profile_config.c
+++ b/res/res_stir_shaken/profile_config.c
@@ -54,6 +54,7 @@
#define DEFAULT_private_key_file NULL
#define DEFAULT_public_cert_url NULL
#define DEFAULT_attest_level attest_level_NOT_SET
+#define DEFAULT_unknown_tn_attest_level attest_level_NOT_SET
#define DEFAULT_send_mky send_mky_NOT_SET
static void profile_destructor(void *obj)
@@ -167,6 +168,9 @@ static struct profile_cfg *create_effective_profile(
return NULL;
}
+ cfg_enum_copy_ex(eprofile, acfg, unknown_tn_attest_level,
+ attest_level_NOT_SET, attest_level_UNKNOWN);
+
rc = as_copy_cfg_common(id, &eprofile->acfg_common,
&base_profile->acfg_common);
if (rc != 0) {
@@ -174,6 +178,10 @@ static struct profile_cfg *create_effective_profile(
return NULL;
}
+ cfg_enum_copy_ex(eprofile, base_profile, unknown_tn_attest_level,
+ attest_level_NOT_SET, attest_level_UNKNOWN);
+
+
eprofile->endpoint_behavior = base_profile->endpoint_behavior;
if (eprofile->endpoint_behavior == endpoint_behavior_ON) {
@@ -252,6 +260,9 @@ generate_vcfg_common_sorcery_handlers(profile_cfg);
generate_sorcery_enum_from_str(profile_cfg, , endpoint_behavior, UNKNOWN);
generate_sorcery_enum_to_str(profile_cfg, , endpoint_behavior);
+generate_sorcery_enum_from_str_ex(profile_cfg,,unknown_tn_attest_level, attest_level, UNKNOWN);
+generate_sorcery_enum_to_str_ex(profile_cfg,,unknown_tn_attest_level, attest_level);
+
static char *cli_profile_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct profile_cfg *profile;
@@ -445,6 +456,9 @@ int profile_load(void)
ast_sorcery_object_field_register_nodoc(sorcery, "eprofile", "type", "", OPT_NOOP_T, 0, 0);
enum_option_register(sorcery, "eprofile", endpoint_behavior, _nodoc);
+ enum_option_register_ex(sorcery, "eprofile", unknown_tn_attest_level,
+ unknown_tn_attest_level, attest_level,_nodoc);
+
register_common_verification_fields(sorcery, profile_cfg, "eprofile", _nodoc);
register_common_attestation_fields(sorcery, profile_cfg, "eprofile", _nodoc);
@@ -460,6 +474,9 @@ int profile_load(void)
ast_sorcery_object_field_register(sorcery, CONFIG_TYPE, "type", "", OPT_NOOP_T, 0, 0);
enum_option_register(sorcery, CONFIG_TYPE, endpoint_behavior,);
+ enum_option_register_ex(sorcery, CONFIG_TYPE, unknown_tn_attest_level,
+ unknown_tn_attest_level, attest_level,);
+
register_common_verification_fields(sorcery, profile_cfg, CONFIG_TYPE,);
register_common_attestation_fields(sorcery, profile_cfg, CONFIG_TYPE,);
diff --git a/res/res_stir_shaken/stir_shaken_doc.xml b/res/res_stir_shaken/stir_shaken_doc.xml
index 6663ce9a16..618c114d00 100644
--- a/res/res_stir_shaken/stir_shaken_doc.xml
+++ b/res/res_stir_shaken/stir_shaken_doc.xml
@@ -21,6 +21,18 @@
Attestation level
+
+ Attestation level to use for unknown TNs
+
+ Normally if a callerid TN isn't configured in stir_shaken.conf
+ no Identity header will be created. If this option is set,
+ however, an Identity header will be sent using this
+ attestation level. Since there's no TN object, you must
+ ensure that a private_key_file and public_cert_url are
+ configured in the attestation or profile objects for
+ this to work.
+
+
On load, Retrieve all TN's certificates and validate their dates
@@ -228,6 +240,7 @@
+
Actions performed when an endpoint references this profile
diff --git a/res/res_stir_shaken/tn_config.c b/res/res_stir_shaken/tn_config.c
index e23f1df191..7396a5091a 100644
--- a/res/res_stir_shaken/tn_config.c
+++ b/res/res_stir_shaken/tn_config.c
@@ -16,6 +16,8 @@
* at the top of the source tree.
*/
+#define _TRACE_PREFIX_ "tc",__LINE__, ""
+
#include "asterisk.h"
#include
@@ -108,31 +110,46 @@ static void *etn_alloc(const char *name)
struct tn_cfg *tn_get_etn(const char *id, struct profile_cfg *eprofile)
{
+ const char *profile_id = eprofile ? ast_sorcery_object_get_id(eprofile) : "unknown";
RAII_VAR(struct tn_cfg *, tn,
ast_sorcery_retrieve_by_id(get_sorcery(), CONFIG_TYPE, S_OR(id, "")),
ao2_cleanup);
- struct tn_cfg *etn = etn_alloc(id);
+ RAII_VAR(struct tn_cfg *, etn, etn_alloc(id), ao2_cleanup);
+ enum attest_level_enum effective_al = attest_level_NOT_SET;
int rc = 0;
+ SCOPE_ENTER(3, "%s:%s: Getting effective TN\n", profile_id, S_OR(id, ""));
- if (!tn || !eprofile || !etn) {
- ao2_cleanup(etn);
- return NULL;
+ if (ast_strlen_zero(id) || !eprofile || !etn) {
+ SCOPE_EXIT_RTN_VALUE(NULL, "Missing params\n");
+ }
+
+ if (!tn) {
+ if (eprofile->unknown_tn_attest_level != attest_level_NOT_SET
+ && eprofile->unknown_tn_attest_level != attest_level_UNKNOWN) {
+ effective_al = eprofile->unknown_tn_attest_level;
+ ast_trace(-1, "%s:%s: TN not found. Using unknown_tn_attest_level %s\n",
+ profile_id, id, attest_level_to_str(effective_al));
+ } else {
+ SCOPE_EXIT_RTN_VALUE(NULL, "%s:%s: TN not found and unknown_tn_attest_level not set\n", profile_id, id);
+ }
}
/* Initialize with the acfg from the eprofile first */
rc = as_copy_cfg_common(id, &etn->acfg_common,
&eprofile->acfg_common);
if (rc != 0) {
- ao2_cleanup(etn);
- return NULL;
+ SCOPE_EXIT_RTN_VALUE(NULL, "%s:%s: Couldn't copy from eprofile\n", profile_id, id);
}
/* Overwrite with anything in the TN itself */
- rc = as_copy_cfg_common(id, &etn->acfg_common,
- &tn->acfg_common);
- if (rc != 0) {
- ao2_cleanup(etn);
- return NULL;
+ if (tn) {
+ rc = as_copy_cfg_common(id, &etn->acfg_common,
+ &tn->acfg_common);
+ if (rc != 0) {
+ SCOPE_EXIT_RTN_VALUE(NULL, "%s:%s: Couldn't copy from tn\n", profile_id, id);
+ }
+ } else {
+ etn->acfg_common.attest_level = effective_al;
}
/*
@@ -141,7 +158,7 @@ struct tn_cfg *tn_get_etn(const char *id, struct profile_cfg *eprofile)
* the same TN could be used with multiple profiles.
*/
- return etn;
+ SCOPE_EXIT_RTN_VALUE(ao2_bump(etn), "%s:%s: Done\n", profile_id, id);
}
static int tn_apply(const struct ast_sorcery *sorcery, void *obj)
diff --git a/res/res_stir_shaken/verification_config.c b/res/res_stir_shaken/verification_config.c
index ef68ffc83e..81199747c3 100644
--- a/res/res_stir_shaken/verification_config.c
+++ b/res/res_stir_shaken/verification_config.c
@@ -16,9 +16,12 @@
* at the top of the source tree.
*/
+#define _TRACE_PREFIX_ "vc",__LINE__, ""
+
#include "asterisk.h"
#include "asterisk/cli.h"
+#include "asterisk/logger.h"
#include "stir_shaken.h"
#define CONFIG_TYPE "verification"