res_pjsip_config_wizard: Force reload on Named ACL change events

Currently, endpoints created via the PJSIP Config Wizard do not update
their ACL rules if the underlying Named ACL (in acl.conf) changes.
This occurs because the wizard relies on file timestamp and content
caching of pjsip_wizard.conf, which remains unchanged during an external
ACL update. As a result, endpoints retain stale ACL rules even after
a reload.

This patch updates res_pjsip_config_wizard to subscribe to the
ast_named_acl_change_type Stasis event. A local generation counter is
incremented whenever an ACL change event is received.

During a reload, the wizard compares the current local generation against
the generation stored in the wizard object. If a change is detected:
1. The file cache optimization (CONFIG_FLAG_FILEUNCHANGED) is bypassed.
2. Wizard objects utilizing 'acl' or 'contact_acl' are forced to update,
   ensuring they pick up the new IP rules.

Signed-off-by: Michal Hajek michal.hajek@daktela.com

Fixes: #1641
This commit is contained in:
Michal Hajek
2025-12-10 14:51:40 +01:00
parent 836696ad06
commit 25bcbc3cfc

View File

@@ -49,6 +49,9 @@
#include "asterisk/pbx.h"
#include "asterisk/sorcery.h"
#include "asterisk/vector.h"
#include "asterisk/stasis.h"
#include "asterisk/acl.h"
#include "asterisk/security_events.h"
/*** DOCUMENTATION
<configInfo name="res_pjsip_config_wizard" language="en_US">
@@ -297,6 +300,22 @@ static AST_VECTOR_RW(object_type_wizards, struct object_type_wizard *) object_ty
const static char *object_types[] = {"phoneprov", "registration", "identify", "endpoint", "aor", "auth", NULL};
static int acl_change_detected = 0;
static struct stasis_subscription *acl_change_sub;
/*! \brief Callback for Named ACL changed */
static void acl_change_stasis_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
{
if (stasis_message_type(message) != ast_named_acl_change_type()) {
return;
}
ast_debug(3, "PJSIP Wizard: Named ACL change detected via Stasis. Triggering reload.\n");
acl_change_detected = 1;
ast_sorcery_reload(ast_sip_get_sorcery());
acl_change_detected = 0;
}
static int is_one_of(const char *needle, const char *haystack[])
{
int i;
@@ -1064,7 +1083,9 @@ static void object_type_loaded_observer(const char *name,
return;
}
if (reloaded && otw->last_config) {
/* Only use the FILEUNCHANGED optimization if the ACLs haven't changed.
* If ACLs changed, we force a reload of the config file to re-evaluate rules. */
if (reloaded && otw->last_config && !acl_change_detected) {
flags.flags = CONFIG_FLAG_FILEUNCHANGED;
}
@@ -1089,6 +1110,14 @@ static void object_type_loaded_observer(const char *name,
if (otw->last_config) {
last_cat = ast_category_get(otw->last_config, id, "type=^wizard$");
changes = !ast_variable_lists_match(ast_category_first(category), ast_category_first(last_cat), 1);
/* If the ACL has changed, we assume EVERYTHING might have changed.
* We force an update for all wizard objects. */
if (!changes && reloaded && acl_change_detected) {
ast_debug(3, "Forcing update of wizard '%s' due to global ACL change.\n", id);
changes = 1;
}
if (last_cat) {
ast_category_delete(otw->last_config, last_cat);
}
@@ -1314,6 +1343,8 @@ static int load_module(void)
ast_sorcery_global_observer_add(&global_observer);
ast_cli_register_multiple(config_wizard_cli, ARRAY_LEN(config_wizard_cli));
acl_change_sub = stasis_subscribe(ast_security_topic(), acl_change_stasis_cb, NULL);
/* If the PJSIP sorcery instance exists it means that we have been explicitly
* loaded and things are potentially already set up. Since we won't receive any
* observer callbacks informing us of this we add ourselves to the instance
@@ -1347,6 +1378,10 @@ static int load_module(void)
static int unload_module(void)
{
if (acl_change_sub) {
acl_change_sub = stasis_unsubscribe_and_join(acl_change_sub);
}
ast_cli_unregister_multiple(config_wizard_cli, ARRAY_LEN(config_wizard_cli));
ast_sorcery_global_observer_remove(&global_observer);
AST_VECTOR_REMOVE_ALL_CMP_UNORDERED(&object_type_wizards, NULL, NOT_EQUALS, OTW_DELETE_CB);