/* * Asterisk -- An open source telephony toolkit. * * Copyright (C) 2023, Sangoma Technologies Corporation * * George Joseph * * See http://www.asterisk.org for more information about * the Asterisk project. Please do not directly contact * any of the maintainers of this project for assistance; * the project provides a web site, mailing lists and IRC * channels for your use. * * This program is free software, distributed under the terms of * the GNU General Public License Version 2. See the LICENSE file * at the top of the source tree. */ #ifndef COMMON_CONFIG_H_ #define COMMON_CONFIG_H_ #include #include "asterisk.h" #include "asterisk/paths.h" #include "asterisk/sorcery.h" #include "asterisk/stringfields.h" /*! * \brief Boolean field to/from string prototype generator * * Most of the boolean fields that appear in the verification and * attestation objects can be ovrridden in the profile object; * "use_rfc9410_responses" for instance. If they were registered as * normal YESNO types, we couldn't tell if a "0" value in the profile * object meant the user set it to "no" to override a value of "yes" * in the verification object, or it just defaulted to "0". By making * the _NOT_SET enum a non-0/1 and making it the default value, we can * tell the difference. The _UNKNOWN enum gets set if the string value * provided to the _from_str function wasn't recognized as one of the * values acceptable to ast_true() or ast_false(). * * The result of calling the generator for a field will look like: * \code enum use_rfc9410_responses_enum { use_rfc9410_responses_UNKNOWN = -1, use_rfc9410_responses_NO = 0, use_rfc9410_responses_YES, use_rfc9410_responses_NOT_SET, }; enum use_rfc9410_responses_enum use_rfc9410_responses_from_str(const char *value); const char *use_rfc9410_responses_to_str(enum use_rfc9410_responses_enum value); \endcode Most of the macros that follow depend on enum values formatted as _SOMETHING and their defaults as DEFAULT_. */ #define generate_bool_string_prototypes(param_name) \ enum param_name ## _enum { \ param_name ## _UNKNOWN = -1, \ param_name ## _NO = 0, \ param_name ## _YES, \ param_name ## _NOT_SET, \ }; \ enum param_name ## _enum \ param_name ## _from_str(const char *value); \ const char *param_name ## _to_str(enum param_name ## _enum value); /* * Run the generators */ generate_bool_string_prototypes(use_rfc9410_responses); generate_bool_string_prototypes(relax_x5u_port_scheme_restrictions); generate_bool_string_prototypes(relax_x5u_path_restrictions); generate_bool_string_prototypes(load_system_certs); generate_bool_string_prototypes(check_tn_cert_public_url); generate_bool_string_prototypes(send_mky); /*! * \brief Enum field to/from string prototype generator * * This operates like the bool generator except you supply * a list of the enum values. The first one MUST be * param_name_UNKNOWN with a value of -1 and the rest running * sequentially with the last being param_name_NOT_SET. */ #define generate_enum_string_prototypes(param_name, ...) \ enum param_name ## _enum { \ __VA_ARGS__ \ }; \ enum param_name ## _enum \ param_name ## _from_str(const char *value); \ const char *param_name ## _to_str(enum param_name ## _enum value); generate_enum_string_prototypes(endpoint_behavior, endpoint_behavior_UNKNOWN = -1, endpoint_behavior_OFF = 0, endpoint_behavior_ATTEST, endpoint_behavior_VERIFY, endpoint_behavior_ON, endpoint_behavior_NOT_SET ); generate_enum_string_prototypes(attest_level, attest_level_UNKNOWN = -1, attest_level_A = 0, attest_level_B, attest_level_C, attest_level_NOT_SET, ); /* * enum stir_shaken_failure_action is defined in * res_stir_shaken.h because res_pjsip_stir_shaken needs it * we we need to just declare the function prototypes. */ enum stir_shaken_failure_action_enum stir_shaken_failure_action_from_str(const char *action_str); const char *stir_shaken_failure_action_to_str( enum stir_shaken_failure_action_enum action); /*! * \brief Enum sorcery handler generator * * These macros can create the two functions needed to * register an enum field with sorcery as long as there * are _to_str and _from_str functions defined elsewhere. * */ #define generate_sorcery_enum_to_str(__struct, __substruct, __lc_param) \ 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)); \ return *buf ? 0 : -1; \ } #define generate_sorcery_enum_from_str_ex(__struct, __substruct, __lc_param, __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) { \ ast_log(LOG_WARNING, "Unknown value '%s' specified for %s\n", \ var->value, var->name); \ return -1; \ } \ return 0; \ } #define generate_sorcery_enum_from_str(__struct, __substruct, __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) \ static int sorcery_acl_to_str(const void *obj, const intptr_t *args, char **buf) \ { \ const struct __struct *cfg = obj; \ struct ast_acl *first_acl; \ if (!ast_acl_list_is_empty(cfg->vcfg_common.acl)) { \ AST_LIST_LOCK(cfg->vcfg_common.acl); \ first_acl = AST_LIST_FIRST(cfg->vcfg_common.acl); \ if (ast_strlen_zero(first_acl->name)) { \ *buf = "deny/permit"; \ } else { \ *buf = first_acl->name; \ } \ AST_LIST_UNLOCK(cfg->vcfg_common.acl); \ } \ *buf = ast_strdup(*buf); \ return 0; \ } #define generate_sorcery_acl_from_str(__struct, __lc_param, __unknown) \ static int sorcery_acl_from_str(const struct aco_option *opt, struct ast_variable *var, void *obj) \ { \ struct __struct *cfg = obj; \ int error = 0; \ int ignore; \ const char *name = var->name + strlen("x5u_"); \ if (ast_strlen_zero(var->value)) { \ return 0; \ } \ ast_append_acl(name, var->value, &cfg->vcfg_common.acl, &error, &ignore); \ return error; \ } struct ast_acl_list *get_default_acl_list(void); #define EFFECTIVE_ENUM(__enum1, __enum2, __field, __default) \ ( __enum1 != ( __field ## _ ## NOT_SET ) ? __enum1 : \ (__enum2 != __field ## _ ## NOT_SET ? \ __enum2 : __default )) #define EFFECTIVE_ENUM_BOOL(__enum1, __enum2, __field, __default) \ (( __enum1 != ( __field ## _ ## NOT_SET ) ? __enum1 : \ (__enum2 != __field ## _ ## NOT_SET ? \ __enum2 : __field ## _ ## __default )) == __field ## _ ## YES) #define ENUM_BOOL(__enum1, __field) \ (__enum1 == ( __field ## _ ## YES )) /*! * \brief Common config copy utilities * * These macros are designed to be called from as_copy_cfg_common * and vs_copy_cfg_common only. They'll only copy a field if the * field contains a vaild value. Thus a NOT_SET value in the source * won't override a pre-existing good value in the dest. A good * value in the source WILL overwrite a good value in the dest. * */ #define cfg_stringfield_copy(__cfg_dst, __cfg_src, __field) \ ({ \ int __res = 0; \ if (!ast_strlen_zero(__cfg_src->__field)) { \ __res = ast_string_field_set(__cfg_dst, __field, __cfg_src->__field); \ } \ __res; \ }) /*! * \brief cfg_copy_wrapper * * Invoke cfg_stringfield_copy and cause the calling runction to * return a -1 of the copy fails. */ #define cfg_sf_copy_wrapper(id, __cfg_dst, __cfg_src, __field) \ { \ int rc = cfg_stringfield_copy(__cfg_dst, __cfg_src, __field); \ if (rc != 0) { \ ast_log(LOG_ERROR, "%s: Unable to copy field %s from %s to %s\n", \ id, #__field, #__cfg_src, #__cfg_dst); \ return -1; \ } \ } /*! * \brief cfg_uint_copy * * Copy a uint from the source to the dest only if the source > 0. * For stir-shaken, 0 isn't a valid value for any uint fields. */ #define cfg_uint_copy(__cfg_dst, __cfg_src, __field) \ ({ \ if (__cfg_src->__field > 0) { \ __cfg_dst->__field = __cfg_src->__field; \ } \ }) /*! * \brief cfg_enum_copy * * 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) \ ({ \ if (__cfg_src->__field != __field ## _NOT_SET \ && __cfg_src->__field != __field ## _UNKNOWN) { \ __cfg_dst->__field = __cfg_src->__field; \ } \ }) /*! * \brief Attestation Service configuration for stir/shaken * * The common structure also appears in profile_cfg. */ struct attestation_cfg_common { AST_DECLARE_STRING_FIELDS( AST_STRING_FIELD(private_key_file); AST_STRING_FIELD(public_cert_url); ); enum attest_level_enum attest_level; enum check_tn_cert_public_url_enum check_tn_cert_public_url; enum send_mky_enum send_mky; unsigned char *raw_key; size_t raw_key_length; }; #define generate_acfg_common_sorcery_handlers(object) \ generate_sorcery_enum_from_str(object, acfg_common., check_tn_cert_public_url, UNKNOWN); \ generate_sorcery_enum_to_str(object, acfg_common., check_tn_cert_public_url); \ generate_sorcery_enum_from_str(object, acfg_common., send_mky, UNKNOWN); \ generate_sorcery_enum_to_str(object, acfg_common., send_mky); \ generate_sorcery_enum_from_str(object, acfg_common., attest_level, UNKNOWN); \ generate_sorcery_enum_to_str(object, acfg_common., attest_level); int as_check_common_config(const char *id, struct attestation_cfg_common *acfg_common); int as_copy_cfg_common(const char *id, struct attestation_cfg_common *cfg_dst, struct attestation_cfg_common *cfg_src); void acfg_cleanup(struct attestation_cfg_common *cfg); struct attestation_cfg { SORCERY_OBJECT(details); /* * We need an empty AST_DECLARE_STRING_FIELDS() here * because when STRFLDSET is used with sorcery, the * memory for all sub-structures that have stringfields * is allocated from the parent's stringfield pool. */ AST_DECLARE_STRING_FIELDS(); struct attestation_cfg_common acfg_common; int global_disable; }; struct attestation_cfg *as_get_cfg(void); int as_is_config_loaded(void); int as_config_load(void); int as_config_reload(void); int as_config_unload(void); /*! * \brief Verification Service configuration for stir/shaken * * The common structure also appears in profile_cfg. */ struct verification_cfg_common { AST_DECLARE_STRING_FIELDS( AST_STRING_FIELD(ca_file); AST_STRING_FIELD(ca_path); AST_STRING_FIELD(crl_file); AST_STRING_FIELD(crl_path); AST_STRING_FIELD(cert_cache_dir); ); unsigned int curl_timeout; unsigned int max_iat_age; unsigned int max_date_header_age; unsigned int max_cache_entry_age; unsigned int max_cache_size; enum stir_shaken_failure_action_enum stir_shaken_failure_action; enum use_rfc9410_responses_enum use_rfc9410_responses; enum relax_x5u_port_scheme_restrictions_enum relax_x5u_port_scheme_restrictions; enum relax_x5u_path_restrictions_enum relax_x5u_path_restrictions; enum load_system_certs_enum load_system_certs; struct ast_acl_list *acl; X509_STORE *tcs; }; #define generate_vcfg_common_sorcery_handlers(object) \ generate_sorcery_enum_from_str(object, vcfg_common.,use_rfc9410_responses, UNKNOWN); \ generate_sorcery_enum_to_str(object, vcfg_common.,use_rfc9410_responses); \ generate_sorcery_enum_from_str(object, vcfg_common.,stir_shaken_failure_action, UNKNOWN); \ generate_sorcery_enum_to_str(object, vcfg_common.,stir_shaken_failure_action); \ generate_sorcery_enum_from_str(object, vcfg_common.,relax_x5u_port_scheme_restrictions, UNKNOWN); \ generate_sorcery_enum_to_str(object, vcfg_common.,relax_x5u_port_scheme_restrictions); \ generate_sorcery_enum_from_str(object, vcfg_common.,relax_x5u_path_restrictions, UNKNOWN); \ 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_to_str(object, vcfg_common.,load_system_certs); \ generate_sorcery_acl_from_str(object, acl, NULL); \ generate_sorcery_acl_to_str(object, acl); int vs_check_common_config(const char *id, struct verification_cfg_common *vcfg_common); int vs_copy_cfg_common(const char *id, struct verification_cfg_common *cfg_dst, struct verification_cfg_common *cfg_src); void vcfg_cleanup(struct verification_cfg_common *cfg); struct verification_cfg { SORCERY_OBJECT(details); /* * We need an empty AST_DECLARE_STRING_FIELDS() here * because when STRFLDSET is used with sorcery, the * memory for all sub-structures that have stringfields * is allocated from the parent's stringfield pool. */ AST_DECLARE_STRING_FIELDS(); struct verification_cfg_common vcfg_common; int global_disable; }; struct verification_cfg *vs_get_cfg(void); int vs_is_config_loaded(void); int vs_config_load(void); int vs_config_reload(void); int vs_config_unload(void); /*! * \brief Profile configuration for stir/shaken */ struct profile_cfg { SORCERY_OBJECT(details); /* * We need an empty AST_DECLARE_STRING_FIELDS() here * because when STRFLDSET is used with sorcery, the * memory for all sub-structures that have stringfields * is allocated from the parent's stringfield pool. */ AST_DECLARE_STRING_FIELDS(); struct attestation_cfg_common acfg_common; struct verification_cfg_common vcfg_common; enum endpoint_behavior_enum endpoint_behavior; struct profile_cfg *eprofile; }; struct profile_cfg *profile_get_cfg(const char *id); struct profile_cfg *eprofile_get_cfg(const char *id); int profile_load(void); int profile_reload(void); int profile_unload(void); #define PROFILE_ALLOW_ATTEST(__profile) \ (__profile->endpoint_behavior == endpoint_behavior_ON || \ __profile->endpoint_behavior == endpoint_behavior_ATTEST) #define PROFILE_ALLOW_VERIFY(__profile) \ (__profile->endpoint_behavior == endpoint_behavior_ON || \ __profile->endpoint_behavior == endpoint_behavior_VERIFY) /*! * \brief TN configuration for stir/shaken * * TN-specific attestation_cfg. */ struct tn_cfg { SORCERY_OBJECT(details); /* * We need an empty AST_DECLARE_STRING_FIELDS() here * because when STRFLDSET is used with sorcery, the * memory for all sub-structures that have stringfields * is allocated from the parent's stringfield pool. */ AST_DECLARE_STRING_FIELDS(); struct attestation_cfg_common acfg_common; }; struct tn_cfg *tn_get_cfg(const char *tn); struct tn_cfg *tn_get_etn(const char *tn, struct profile_cfg *eprofile); int tn_config_load(void); int tn_config_reload(void); int tn_config_unload(void); /*! * \brief Sorcery fields register helpers * * Most of the fields on attestation_cfg and verification_cfg are also * in profile_cfg. To prevent having to maintain duplicate sets of * sorcery register statements, we can do this once here and call * register_common_verification_fields() from both profile_config and * verification_config and call register_common_attestation_fields() * from profile_cfg and attestation_config. * * Most of the fields in question are in sub-structures like * verification_cfg.vcfg_common which is why there are separate name * and field parameters. For verification_cfg.vcfg_common.ca_file * for instance, name would be ca_file and field would be * vcfg_common.ca_file. * *\note These macros depend on default values being defined * in the 4 _config.c files as DEFAULT_. * */ #define stringfield_option_register(sorcery, CONFIG_TYPE, object, name, field, nodoc) \ ast_sorcery_object_field_register ## nodoc(sorcery, CONFIG_TYPE, #name, \ DEFAULT_ ## name, OPT_STRINGFIELD_T, 0, \ STRFLDSET(struct object, field)) #define uint_option_register(sorcery, CONFIG_TYPE, object, name, field, nodoc) \ ast_sorcery_object_field_register ## nodoc(sorcery, CONFIG_TYPE, #name, \ __stringify(DEFAULT_ ## name), OPT_UINT_T, 0, \ FLDSET(struct object, field)) #define enum_option_register_ex(sorcery, CONFIG_TYPE, name, field, nodoc) \ ast_sorcery_object_field_register_custom ## nodoc(sorcery, CONFIG_TYPE, \ #name, field ## _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) #define register_common_verification_fields(sorcery, object, CONFIG_TYPE, nodoc) \ ({ \ stringfield_option_register(sorcery, CONFIG_TYPE, object, ca_file, vcfg_common.ca_file, nodoc); \ stringfield_option_register(sorcery, CONFIG_TYPE, object, ca_path, vcfg_common.ca_path, nodoc); \ stringfield_option_register(sorcery, CONFIG_TYPE, object, crl_file, vcfg_common.crl_file, nodoc); \ stringfield_option_register(sorcery, CONFIG_TYPE, object, crl_path, vcfg_common.crl_path, nodoc); \ stringfield_option_register(sorcery, CONFIG_TYPE, object, cert_cache_dir, vcfg_common.cert_cache_dir, nodoc); \ \ uint_option_register(sorcery, CONFIG_TYPE, object, curl_timeout, vcfg_common.curl_timeout, nodoc);\ uint_option_register(sorcery, CONFIG_TYPE, object, max_iat_age, vcfg_common.max_iat_age, nodoc);\ uint_option_register(sorcery, CONFIG_TYPE, object, max_date_header_age, vcfg_common.max_date_header_age, nodoc);\ 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(sorcery, CONFIG_TYPE, use_rfc9410_responses, nodoc); \ enum_option_register(sorcery, CONFIG_TYPE, \ relax_x5u_port_scheme_restrictions, nodoc); \ enum_option_register(sorcery, CONFIG_TYPE, \ relax_x5u_path_restrictions, nodoc); \ enum_option_register(sorcery, CONFIG_TYPE, \ load_system_certs, 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_permit", "", sorcery_acl_from_str, NULL, NULL, 0, 0); \ ast_sorcery_object_field_register_custom ## nodoc(sorcery, CONFIG_TYPE, "x5u_acl", "", sorcery_acl_from_str, sorcery_acl_to_str, NULL, 0, 0); \ }) #define register_common_attestation_fields(sorcery, object, CONFIG_TYPE, nodoc) \ ({ \ stringfield_option_register(sorcery, CONFIG_TYPE, object, private_key_file, acfg_common.private_key_file, nodoc); \ stringfield_option_register(sorcery, CONFIG_TYPE, object, public_cert_url, acfg_common.public_cert_url, nodoc); \ enum_option_register(sorcery, CONFIG_TYPE, attest_level, nodoc); \ enum_option_register(sorcery, CONFIG_TYPE, check_tn_cert_public_url, nodoc); \ enum_option_register(sorcery, CONFIG_TYPE, send_mky, nodoc); \ }) int common_config_load(void); int common_config_unload(void); int common_config_reload(void); enum config_object_type { config_object_type_attestation = 0, config_object_type_verification, config_object_type_profile, config_object_type_tn, }; struct config_object_cli_data { const char *title; enum config_object_type object_type; }; /*! * \brief Output configuration settings to the Asterisk CLI * * \param obj A sorcery object containing configuration data * \param arg Asterisk CLI argument object * \param flags ao2 container flags * * \retval 0 */ int config_object_cli_show(void *obj, void *arg, void *data, int flags); /*! * \brief Tab completion for name matching with STIR/SHAKEN CLI commands * * \param word The word to tab complete on * \param container The sorcery container to iterate through * * \retval The tab completion options */ char *config_object_tab_complete_name(const char *word, struct ao2_container *container); #endif /* COMMON_CONFIG_H_ */