asterisk/res/res_stir_shaken/common_config.c

354 lines
9.6 KiB
C

/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2023, Sangoma Technologies Corporation
*
* George Joseph <gjoseph@sangoma.com>
*
* 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.
*/
#include "asterisk.h"
#include "asterisk/cli.h"
#include "asterisk/cli.h"
#include "asterisk/logger.h"
#include "asterisk/module.h"
#include "asterisk/utils.h"
#include "asterisk/stasis.h"
#include "asterisk/security_events.h"
#define AST_API_MODULE
#include "stir_shaken.h"
static struct ast_sorcery *sorcery;
struct stasis_subscription *named_acl_changed_sub = NULL;
struct ast_sorcery *get_sorcery(void)
{
return sorcery;
}
#define generate_bool_handler_functions(param_name) \
static const char *param_name ## _map[] = { \
[ param_name ## _NOT_SET ] = "not_set", \
[ param_name ## _YES ] = "yes", \
[ param_name ## _NO ] = "no", \
}; \
enum param_name ## _enum \
param_name ## _from_str(const char *value) \
{ \
if (!strcasecmp(value, param_name ## _map[param_name ## _NOT_SET])) { \
return param_name ## _NOT_SET; \
} else if (ast_true(value)) { \
return param_name ## _YES; \
} else if (ast_false(value)) { \
return param_name ## _NO; \
} \
ast_log(LOG_WARNING, "Unknown " #param_name " response value '%s'\n", value); \
return param_name ## _UNKNOWN; \
}\
const char *param_name ## _to_str(enum param_name ## _enum value) \
{ \
return ARRAY_IN_BOUNDS(value, param_name ## _map) ? \
param_name ## _map[value] : NULL; \
}
generate_bool_handler_functions(use_rfc9410_responses);
generate_bool_handler_functions(send_mky);
generate_bool_handler_functions(check_tn_cert_public_url);
generate_bool_handler_functions(relax_x5u_port_scheme_restrictions);
generate_bool_handler_functions(relax_x5u_path_restrictions);
generate_bool_handler_functions(load_system_certs);
struct enum_name_xref_entry {
int value;
const char *name;
};
#define generate_enum_string_functions(param_name, default_value, ...)\
static struct enum_name_xref_entry param_name ## _map[] = { \
__VA_ARGS__ \
} ; \
enum param_name ## _enum param_name ## _from_str( \
const char *value) \
{ \
int i; \
for (i = 0; i < ARRAY_LEN(param_name ## _map); i++) { \
if (strcasecmp(value, param_name ##_map[i].name) == 0) { \
return param_name ##_map[i].value; \
} \
} \
return param_name ## _ ## default_value; \
} \
const char *param_name ## _to_str( \
enum param_name ## _enum value) \
{ \
int i; \
for (i = 0; i < ARRAY_LEN(param_name ## _map); i++) { \
if (value == param_name ## _map[i].value) return param_name ## _map[i].name; \
} \
return NULL; \
}
generate_enum_string_functions(attest_level, UNKNOWN,
{attest_level_A, "A"},
{attest_level_B, "B"},
{attest_level_C, "C"},
);
generate_enum_string_functions(endpoint_behavior, OFF,
{endpoint_behavior_OFF, "off"},
{endpoint_behavior_OFF, "none"},
{endpoint_behavior_ATTEST, "attest"},
{endpoint_behavior_VERIFY, "verify"},
{endpoint_behavior_ON, "on"},
{endpoint_behavior_ON, "both"}
);
generate_enum_string_functions(stir_shaken_failure_action, CONTINUE,
{stir_shaken_failure_action_CONTINUE, "continue"},
{stir_shaken_failure_action_REJECT_REQUEST, "reject_request"},
{stir_shaken_failure_action_CONTINUE_RETURN_REASON, "continue_return_reason"},
);
static const char *translate_value(const char *val)
{
if (val[0] == '0'
|| val[0] == '\0'
|| strcmp(val, "not_set") == 0) {
return "";
}
return val;
}
static void print_acl(int fd, struct ast_acl_list *acl_list, const char *prefix)
{
struct ast_acl *acl;
AST_LIST_LOCK(acl_list);
AST_LIST_TRAVERSE(acl_list, acl, list) {
if (ast_strlen_zero(acl->name)) {
ast_cli(fd, "%s(permit/deny)\n", prefix);
} else {
ast_cli(fd, "%s%s\n", prefix, acl->name);
}
ast_ha_output(fd, acl->acl, prefix);
}
AST_LIST_UNLOCK(acl_list);
}
#define print_acl_cert_store(cfg, a, max_name_len) \
({ \
if (cfg->vcfg_common.acl) { \
ast_cli(a->fd, "x5u_acl:\n"); \
print_acl(a->fd, cfg->vcfg_common.acl, " "); \
} else { \
ast_cli(a->fd, "%-*s: (none)\n", max_name_len, "x5u_acl"); \
}\
if (cfg->vcfg_common.tcs) { \
int count = 0; \
ast_cli(a->fd, "%-*s:\n", max_name_len, "Verification CA certificate store"); \
count = crypto_show_cli_store(cfg->vcfg_common.tcs, a->fd); \
if (count == 0 && (!ast_strlen_zero(cfg->vcfg_common.ca_path) \
|| !ast_strlen_zero(cfg->vcfg_common.crl_path))) { \
ast_cli(a->fd, " Note: Certs in ca_path or crl_path won't show until used.\n"); \
} \
} else { \
ast_cli(a->fd, "%-*s: (none)\n", max_name_len, "Verification CA certificate store"); \
} \
})
int config_object_cli_show(void *obj, void *arg, void *data, int flags)
{
struct ast_cli_args *a = arg;
struct config_object_cli_data *cli_data = data;
struct ast_variable *options;
struct ast_variable *i;
const char *title = NULL;
const char *cfg_name = NULL;
int max_name_len = 0;
if (!obj) {
ast_cli(a->fd, "No stir/shaken configuration found\n");
return 0;
}
if (!ast_strlen_zero(cli_data->title)) {
title = cli_data->title;
} else {
title = ast_sorcery_object_get_type(obj);
}
max_name_len = strlen(title);
if (cli_data->object_type == config_object_type_profile
|| cli_data->object_type == config_object_type_tn) {
cfg_name = ast_sorcery_object_get_id(obj);
max_name_len += strlen(cfg_name) + 2 /* ": " */;
}
options = ast_variable_list_sort(ast_sorcery_objectset_create2(
get_sorcery(), obj, AST_HANDLER_ONLY_STRING));
if (!options) {
return 0;
}
for (i = options; i; i = i->next) {
int nlen = strlen(i->name);
max_name_len = (nlen > max_name_len) ? nlen : max_name_len;
}
ast_cli(a->fd, "\n==============================================================================\n");
if (ast_strlen_zero(cfg_name)) {
ast_cli(a->fd, "%s\n", title);
} else {
ast_cli(a->fd, "%s: %s\n", title, cfg_name);
}
ast_cli(a->fd, "------------------------------------------------------------------------------\n");
for (i = options; i; i = i->next) {
if (!ast_strings_equal(i->name, "x5u_acl")) {
ast_cli(a->fd, "%-*s: %s\n", max_name_len, i->name,
translate_value(i->value));
}
}
ast_variables_destroy(options);
if (cli_data->object_type == config_object_type_profile) {
struct profile_cfg *cfg = obj;
print_acl_cert_store(cfg, a, max_name_len);
} else if (cli_data->object_type == config_object_type_verification) {
struct verification_cfg *cfg = obj;
print_acl_cert_store(cfg, a, max_name_len);
}
ast_cli(a->fd, "---------------------------------------------\n\n"); \
return 0;
}
char *config_object_tab_complete_name(const char *word, struct ao2_container *container)
{
void *obj;
struct ao2_iterator it;
int wordlen = strlen(word);
int ret;
it = ao2_iterator_init(container, 0);
while ((obj = ao2_iterator_next(&it))) {
if (!strncasecmp(word, ast_sorcery_object_get_id(obj), wordlen)) {
ret = ast_cli_completion_add(ast_strdup(ast_sorcery_object_get_id(obj)));
if (ret) {
ao2_ref(obj, -1);
break;
}
}
ao2_ref(obj, -1);
}
ao2_iterator_destroy(&it);
return NULL;
}
int common_config_reload(void)
{
SCOPE_ENTER(2, "Stir Shaken Reload\n");
if (vs_reload()) {
SCOPE_EXIT_RTN_VALUE(AST_MODULE_LOAD_DECLINE, "Stir Shaken VS Reload failed\n");
}
if (as_reload()) {
SCOPE_EXIT_RTN_VALUE(AST_MODULE_LOAD_DECLINE, "Stir Shaken AS Reload failed\n");
}
if (tn_config_reload()) {
SCOPE_EXIT_RTN_VALUE(AST_MODULE_LOAD_DECLINE, "Stir Shaken TN Reload failed\n");
}
if (profile_reload()) {
SCOPE_EXIT_RTN_VALUE(AST_MODULE_LOAD_DECLINE, "Stir Shaken Profile Reload failed\n");
}
SCOPE_EXIT_RTN_VALUE(AST_MODULE_LOAD_SUCCESS, "Stir Shaken Reload Done\n");
}
int common_config_unload(void)
{
profile_unload();
tn_config_unload();
as_unload();
vs_unload();
if (named_acl_changed_sub) {
stasis_unsubscribe(named_acl_changed_sub);
named_acl_changed_sub = NULL;
}
ast_sorcery_unref(sorcery);
sorcery = NULL;
return 0;
}
static void named_acl_changed_cb(void *data,
struct stasis_subscription *sub, struct stasis_message *message)
{
if (stasis_message_type(message) != ast_named_acl_change_type()) {
return;
}
ast_log(LOG_NOTICE, "Named acl changed. Reloading verification and profile\n");
common_config_reload();
}
int common_config_load(void)
{
SCOPE_ENTER(2, "Stir Shaken Load\n");
if (!(sorcery = ast_sorcery_open())) {
common_config_unload();
SCOPE_EXIT_RTN_VALUE(AST_MODULE_LOAD_DECLINE, "Stir Shaken sorcery load failed\n");
}
if (vs_load()) {
common_config_unload();
SCOPE_EXIT_RTN_VALUE(AST_MODULE_LOAD_DECLINE, "Stir Shaken VS load failed\n");
}
if (as_load()) {
common_config_unload();
SCOPE_EXIT_RTN_VALUE(AST_MODULE_LOAD_DECLINE, "Stir Shaken AS load failed\n");
}
if (tn_config_load()) {
common_config_unload();
SCOPE_EXIT_RTN_VALUE(AST_MODULE_LOAD_DECLINE, "Stir Shaken TN load failed\n");
}
if (profile_load()) {
common_config_unload();
SCOPE_EXIT_RTN_VALUE(AST_MODULE_LOAD_DECLINE, "Stir Shaken profile load failed\n");
}
if (!named_acl_changed_sub) {
named_acl_changed_sub = stasis_subscribe(ast_security_topic(),
named_acl_changed_cb, NULL);
if (!named_acl_changed_sub) {
common_config_unload();
SCOPE_EXIT_RTN_VALUE(AST_MODULE_LOAD_DECLINE, "Stir Shaken acl change subscribe failed\n");
}
stasis_subscription_accept_message_type(
named_acl_changed_sub, ast_named_acl_change_type());
}
SCOPE_EXIT_RTN_VALUE(AST_MODULE_LOAD_SUCCESS, "Stir Shaken Load Done\n");
}