Named ACLs: Introduces a system for creating and sharing ACLs

This patch adds Named ACL functionality to Asterisk. This allows system
administrators to define an ACL and refer to it by a unique name. Configurable
items can then refer to that name when specifying access control lists.
It also includes updates to all core supported consumers of ACLs. That includes
manager, chan_sip, and chan_iax2. This feature is based on the deluxepine-trunk
by Olle E. Johansson and provides a subset of the Named ACL functionality
implemented in that branch. For more information on this feature, see acl.conf
and/or the Asterisk wiki.

Review: https://reviewboard.asterisk.org/r/1978/


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@369959 65c4cc65-6c06-0410-ace0-fbb531ad65f3
certified/11.2
Jonathan Rose 14 years ago
parent 6190ae4430
commit 10afdf3a2a

@ -46,6 +46,12 @@ Core
have their own verbosity level. The command 'core set verbose' will now set
a separate level for each remote console without affecting any other
console.
* Named ACLs can now be specified in acl.conf and used in configurations that
use ACLs. As a general rule, if some derivative of 'permit' or 'deny' is
used to specify an ACL, a similar form of 'acl' will add a named ACL to the
working ACL. In addition, some CLI commands have
been added to provide informational and configuration reload capabilities to
this feature ('acl show [named acl]' and 'reload acl').
* Hangup handlers can be attached to channels using the CHANNEL(hangup_handler_xxx)
options. Hangup handlers will run when the channel is hung up similar to the
h extension.

@ -275,6 +275,7 @@ static char language[MAX_LANGUAGE] = "";
static char regcontext[AST_MAX_CONTEXT] = "";
static struct ast_event_sub *network_change_event_subscription; /*!< subscription id for network change events */
static struct ast_event_sub *acl_change_event_subscription; /*!< subscription id for ACL change events */
static int network_change_event_sched_id = -1;
static int maxauthreq = 3;
@ -438,7 +439,7 @@ struct iax2_context {
#define IAX_SHRINKCALLERID (uint64_t)(1 << 31) /*!< Turn on and off caller id shrinking */
static int global_rtautoclear = 120;
static int reload_config(void);
static int reload_config(int forced_reload);
/*!
* \brief Call token validation settings.
@ -479,7 +480,7 @@ struct iax2_user {
int maxauthreq; /*!< Maximum allowed outstanding AUTHREQs */
int curauthreq; /*!< Current number of outstanding AUTHREQs */
struct ast_codec_pref prefs;
struct ast_ha *ha;
struct ast_acl_list *acl;
struct iax2_context *contexts;
struct ast_variable *vars;
enum calltoken_peer_enum calltoken_required; /*!< Is calltoken validation required or not, can be YES, NO, or AUTO */
@ -539,7 +540,7 @@ struct iax2_peer {
struct ast_event_sub *mwi_event_sub;
struct ast_ha *ha;
struct ast_acl_list *acl;
enum calltoken_peer_enum calltoken_required; /*!< Is calltoken validation required or not, can be YES, NO, or AUTO */
};
@ -1242,6 +1243,7 @@ static struct callno_entry *get_unused_callno(int trunk, int validated);
static int replace_callno(const void *obj);
static void sched_delay_remove(struct sockaddr_in *sin, struct callno_entry *callno_entry);
static void network_change_event_cb(const struct ast_event *, void *);
static void acl_change_event_cb(const struct ast_event *, void *);
static struct ast_channel_tech iax2_tech = {
.type = "IAX2",
@ -1324,6 +1326,21 @@ static void network_change_event_unsubscribe(void)
}
}
static void acl_change_event_subscribe(void)
{
if (!acl_change_event_subscription) {
acl_change_event_subscription = ast_event_subscribe(AST_EVENT_ACL_CHANGE,
acl_change_event_cb, "IAX2 ACL Change", NULL, AST_EVENT_IE_END);
}
}
static void acl_change_event_unsubscribe(void)
{
if (acl_change_event_subscription) {
acl_change_event_subscription = ast_event_unsubscribe(acl_change_event_subscription);
}
}
static int network_change_event_sched_cb(const void *data)
{
struct iax2_registry *reg;
@ -1346,6 +1363,12 @@ static void network_change_event_cb(const struct ast_event *event, void *userdat
}
static void acl_change_event_cb(const struct ast_event *event, void *userdata)
{
ast_log(LOG_NOTICE, "Reloading chan_iax2 in response to ACL change event.\n");
reload_config(1);
}
/*! \brief Send manager event at call setup to link between Asterisk channel name
and IAX2 call identifiers */
@ -3818,7 +3841,7 @@ static char *handle_cli_iax2_show_peer(struct ast_cli_entry *e, int cmd, struct
ast_cli(a->fd, " Encryption : %s\n", peer->encmethods ? ast_str_buffer(encmethods) : "No");
ast_cli(a->fd, " Callerid : %s\n", ast_callerid_merge(cbuf, sizeof(cbuf), peer->cid_name, peer->cid_num, "<unspecified>"));
ast_cli(a->fd, " Expire : %d\n", peer->expire);
ast_cli(a->fd, " ACL : %s\n", (peer->ha ? "Yes" : "No"));
ast_cli(a->fd, " ACL : %s\n", (ast_acl_list_is_empty(peer->acl) ? "No" : "Yes"));
ast_cli(a->fd, " Addr->IP : %s Port %d\n", peer_addr.sin_addr.s_addr ? ast_inet_ntoa(peer_addr.sin_addr) : "(Unspecified)", ntohs(peer_addr.sin_port));
ast_cli(a->fd, " Defaddr->IP : %s Port %d\n", ast_inet_ntoa(peer->defaddr.sin_addr), ntohs(peer->defaddr.sin_port));
ast_cli(a->fd, " Username : %s\n", peer->username);
@ -6701,7 +6724,7 @@ static char *handle_cli_iax2_show_users(struct ast_cli_entry *e, int cmd, struct
ast_cli(a->fd, FORMAT2, user->name, auth, user->authmethods,
user->contexts ? user->contexts->context : DEFAULT_CONTEXT,
user->ha ? "Yes" : "No", pstr);
ast_acl_list_is_empty(user->acl) ? "No" : "Yes", pstr);
}
ao2_iterator_destroy(&i);
@ -7681,7 +7704,7 @@ static int check_access(int callno, struct sockaddr_in *sin, struct iax_ies *ies
while ((user = ao2_iterator_next(&i))) {
if ((ast_strlen_zero(iaxs[callno]->username) || /* No username specified */
!strcmp(iaxs[callno]->username, user->name)) /* Or this username specified */
&& ast_apply_ha(user->ha, &addr) /* Access is permitted from this IP */
&& ast_apply_acl(user->acl, &addr, "IAX2 user ACL: ") /* Access is permitted from this IP */
&& (ast_strlen_zero(iaxs[callno]->context) || /* No context specified */
apply_context(user->contexts, iaxs[callno]->context))) { /* Context is permitted */
if (!ast_strlen_zero(iaxs[callno]->username)) {
@ -7692,7 +7715,7 @@ static int check_access(int callno, struct sockaddr_in *sin, struct iax_ies *ies
break;
} else if (ast_strlen_zero(user->secret) && ast_strlen_zero(user->dbsecret) && ast_strlen_zero(user->inkeys)) {
/* No required authentication */
if (user->ha) {
if (user->acl) {
/* There was host authentication and we passed, bonus! */
if (bestscore < 4) {
bestscore = 4;
@ -7712,7 +7735,7 @@ static int check_access(int callno, struct sockaddr_in *sin, struct iax_ies *ies
}
}
} else {
if (user->ha) {
if (user->acl) {
/* Authentication, but host access too, eh, it's something.. */
if (bestscore < 2) {
bestscore = 2;
@ -8072,7 +8095,7 @@ static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies *
}
ast_sockaddr_from_sin(&addr, sin);
if (!ast_apply_ha(p->ha, &addr)) {
if (!ast_apply_acl(p->acl, &addr, "IAX2 Peer ACL: ")) {
if (authdebug)
ast_log(LOG_NOTICE, "Host %s denied access to register peer '%s'\n", ast_inet_ntoa(sin->sin_addr), p->name);
goto return_unref;
@ -12514,7 +12537,7 @@ static void peer_destructor(void *obj)
struct iax2_peer *peer = obj;
int callno = peer->callno;
ast_free_ha(peer->ha);
ast_free_acl_list(peer->acl);
if (callno > 0) {
ast_mutex_lock(&iaxsl[callno]);
@ -12537,10 +12560,11 @@ static void peer_destructor(void *obj)
static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int temponly)
{
struct iax2_peer *peer = NULL;
struct ast_ha *oldha = NULL;
struct ast_acl_list *oldacl = NULL;
int maskfound = 0;
int found = 0;
int firstpass = 1;
int subscribe_acl_change = 0;
if (!temponly) {
peer = ao2_find(peers, name, OBJ_KEY);
@ -12551,8 +12575,8 @@ static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, st
if (peer) {
found++;
if (firstpass) {
oldha = peer->ha;
peer->ha = NULL;
oldacl = peer->acl;
peer->acl = NULL;
}
unlink_peer(peer);
} else if ((peer = ao2_alloc(sizeof(*peer), peer_destructor))) {
@ -12683,8 +12707,9 @@ static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, st
} else if (!strcasecmp(v->name, "sourceaddress")) {
peer_set_srcaddr(peer, v->value);
} else if (!strcasecmp(v->name, "permit") ||
!strcasecmp(v->name, "deny")) {
peer->ha = ast_append_ha(v->name, v->value, peer->ha, NULL);
!strcasecmp(v->name, "deny") ||
!strcasecmp(v->name, "acl")) {
ast_append_acl(v->name, v->value, &peer->acl, NULL, &subscribe_acl_change);
} else if (!strcasecmp(v->name, "mask")) {
maskfound++;
inet_aton(v->value, &peer->mask);
@ -12795,8 +12820,8 @@ static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, st
ast_clear_flag64(peer, IAX_DELME);
}
if (oldha)
ast_free_ha(oldha);
if (oldacl)
ast_free_acl_list(oldacl);
if (!ast_strlen_zero(peer->mailbox)) {
char *mailbox, *context;
@ -12810,6 +12835,10 @@ static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, st
AST_EVENT_IE_END);
}
if (subscribe_acl_change) {
acl_change_event_subscribe();
}
return peer;
}
@ -12817,7 +12846,7 @@ static void user_destructor(void *obj)
{
struct iax2_user *user = obj;
ast_free_ha(user->ha);
ast_free_acl_list(user->acl);
free_context(user->contexts);
if(user->vars) {
ast_variables_destroy(user->vars);
@ -12831,11 +12860,12 @@ static struct iax2_user *build_user(const char *name, struct ast_variable *v, st
{
struct iax2_user *user = NULL;
struct iax2_context *con, *conl = NULL;
struct ast_ha *oldha = NULL;
struct ast_acl_list *oldacl = NULL;
struct iax2_context *oldcon = NULL;
int format;
int firstpass=1;
int oldcurauthreq = 0;
int subscribe_acl_change = 0;
char *varname = NULL, *varval = NULL;
struct ast_variable *tmpvar = NULL;
@ -12848,9 +12878,9 @@ static struct iax2_user *build_user(const char *name, struct ast_variable *v, st
if (user) {
if (firstpass) {
oldcurauthreq = user->curauthreq;
oldha = user->ha;
oldacl = user->acl;
oldcon = user->contexts;
user->ha = NULL;
user->acl = NULL;
user->contexts = NULL;
}
/* Already in the list, remove it and it will be added back (or FREE'd) */
@ -12899,8 +12929,9 @@ static struct iax2_user *build_user(const char *name, struct ast_variable *v, st
conl = con;
}
} else if (!strcasecmp(v->name, "permit") ||
!strcasecmp(v->name, "deny")) {
user->ha = ast_append_ha(v->name, v->value, user->ha, NULL);
!strcasecmp(v->name, "deny") ||
!strcasecmp(v->name, "acl")) {
ast_append_acl(v->name, v->value, &user->acl, NULL, &subscribe_acl_change);
} else if (!strcasecmp(v->name, "setvar")) {
varname = ast_strdupa(v->value);
if (varname && (varval = strchr(varname,'='))) {
@ -13069,10 +13100,17 @@ static struct iax2_user *build_user(const char *name, struct ast_variable *v, st
ast_clear_flag64(user, IAX_DELME);
}
cleanup:
if (oldha)
ast_free_ha(oldha);
if (oldcon)
if (oldacl) {
ast_free_acl_list(oldacl);
}
if (oldcon) {
free_context(oldcon);
}
if (subscribe_acl_change) {
acl_change_event_subscribe();
}
return user;
}
@ -13171,7 +13209,7 @@ static void set_config_destroy(void)
}
/*! \brief Load configuration */
static int set_config(const char *config_file, int reload)
static int set_config(const char *config_file, int reload, int forced)
{
struct ast_config *cfg, *ucfg;
iax2_format capability = iax2_capability;
@ -13187,7 +13225,7 @@ static int set_config(const char *config_file, int reload)
struct iax2_user *user;
struct iax2_peer *peer;
struct ast_netsock *ns;
struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
struct ast_flags config_flags = { (reload && !forced) ? CONFIG_FLAG_FILEUNCHANGED : 0 };
#if 0
static unsigned short int last_port=0;
#endif
@ -13667,12 +13705,12 @@ static void poke_all_peers(void)
}
ao2_iterator_destroy(&i);
}
static int reload_config(void)
static int reload_config(int forced_reload)
{
static const char config[] = "iax.conf";
struct iax2_registry *reg;
if (set_config(config, 1) > 0) {
if (set_config(config, 1, forced_reload) > 0) {
prune_peers();
prune_users();
ao2_callback(callno_limits, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, prune_addr_range_cb, NULL);
@ -13711,14 +13749,14 @@ static char *handle_cli_iax2_reload(struct ast_cli_entry *e, int cmd, struct ast
return NULL;
}
reload_config();
reload_config(0);
return CLI_SUCCESS;
}
static int reload(void)
{
return reload_config();
return reload_config(0);
}
static int cache_get_callno_locked(const char *data)
@ -14483,6 +14521,7 @@ static int __unload_module(void)
int x;
network_change_event_unsubscribe();
acl_change_event_unsubscribe();
ast_manager_unregister("IAXpeers");
ast_manager_unregister("IAXpeerlist");
@ -14818,7 +14857,7 @@ static int users_data_provider_get(const struct ast_data_search *search,
ast_data_add_int(data_enum_node, "value", user->amaflags);
ast_data_add_str(data_enum_node, "text", ast_cdr_flags2str(user->amaflags));
ast_data_add_bool(data_user, "access-control", user->ha ? 1 : 0);
ast_data_add_bool(data_user, "access-control", ast_acl_list_is_empty(user->acl) ? 0 : 1);
if (ast_test_flag64(user, IAX_CODEC_NOCAP)) {
pstr = "REQ only";
@ -14922,7 +14961,7 @@ static int load_module(void)
ast_timer_set_rate(timer, 1000 / trunkfreq);
}
if (set_config(config, 0) == -1) {
if (set_config(config, 0, 0) == -1) {
if (timer) {
ast_timer_close(timer);
}

@ -793,6 +793,7 @@ static struct ast_flags global_flags[3] = {{0}}; /*!< global SIP_ flags */
static int global_t38_maxdatagram; /*!< global T.38 FaxMaxDatagram override */
static struct ast_event_sub *network_change_event_subscription; /*!< subscription id for network change events */
static struct ast_event_sub *acl_change_event_subscription; /*!< subscription id for named ACL system change events */
static int network_change_event_sched_id = -1;
static char used_context[AST_MAX_CONTEXT]; /*!< name of automatically created context for unloading */
@ -1407,6 +1408,7 @@ static void sip_poke_all_peers(void);
static void sip_peer_hold(struct sip_pvt *p, int hold);
static void mwi_event_cb(const struct ast_event *, void *);
static void network_change_event_cb(const struct ast_event *, void *);
static void acl_change_event_cb(const struct ast_event *event, void *userdata);
static void sip_keepalive_all_peers(void);
/*--- Applications, functions, CLI and manager command helpers */
@ -4711,8 +4713,8 @@ static void sip_destroy_peer(struct sip_peer *peer)
}
register_peer_exten(peer, FALSE);
ast_free_ha(peer->ha);
ast_free_ha(peer->directmediaha);
ast_free_acl_list(peer->acl);
ast_free_acl_list(peer->directmediaacl);
if (peer->selfdestruct)
ast_atomic_fetchadd_int(&apeerobjs, -1);
else if (!ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS) && peer->is_realtime) {
@ -5499,7 +5501,9 @@ static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer)
dialog->noncodeccapability |= AST_RTP_DTMF;
else
dialog->noncodeccapability &= ~AST_RTP_DTMF;
dialog->directmediaha = ast_duplicate_ha_list(peer->directmediaha);
dialog->directmediaacl = ast_duplicate_acl_list(peer->directmediaacl);
if (peer->call_limit)
ast_set_flag(&dialog->flags[0], SIP_CALL_LIMIT);
if (!dialog->portinuri)
@ -5992,9 +5996,8 @@ void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist)
p->tsrtp = NULL;
}
if (p->directmediaha) {
ast_free_ha(p->directmediaha);
p->directmediaha = NULL;
if (p->directmediaacl) {
p->directmediaacl = ast_free_acl_list(p->directmediaacl);
}
ast_string_field_free_memory(p);
@ -15034,8 +15037,8 @@ static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, st
}
/* Check that they're allowed to register at this IP */
if (ast_apply_ha(sip_cfg.contact_ha, &peer->addr) != AST_SENSE_ALLOW ||
ast_apply_ha(peer->contactha, &peer->addr) != AST_SENSE_ALLOW) {
if (ast_apply_acl(sip_cfg.contact_acl, &peer->addr, "SIP contact ACL: ") != AST_SENSE_ALLOW ||
ast_apply_acl(peer->contactacl, &peer->addr, "SIP contact ACL: ") != AST_SENSE_ALLOW) {
ast_log(LOG_WARNING, "Domain '%s' disallowed by contact ACL (violating IP %s)\n", hostport,
ast_sockaddr_stringify_addr(&testsa));
ast_string_field_set(peer, fullcontact, "");
@ -15485,6 +15488,22 @@ static void network_change_event_unsubscribe(void)
}
}
static void acl_change_event_subscribe(void)
{
if (!acl_change_event_subscription) {
acl_change_event_subscription = ast_event_subscribe(AST_EVENT_ACL_CHANGE,
acl_change_event_cb, "Manager must react to Named ACL changes", NULL, AST_EVENT_IE_END);
}
}
static void acl_change_event_unsubscribe(void)
{
if (acl_change_event_subscription) {
acl_change_event_subscription = ast_event_unsubscribe(acl_change_event_subscription);
}
}
static int network_change_event_sched_cb(const void *data)
{
network_change_event_sched_id = -1;
@ -15796,7 +15815,7 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sock
}
peer = sip_find_peer(name, NULL, TRUE, FINDPEERS, FALSE, 0);
if (!(peer && ast_apply_ha(peer->ha, addr))) {
if (!(peer && ast_apply_acl(peer->acl, addr, "SIP Peer ACL: "))) {
/* Peer fails ACL check */
if (peer) {
sip_unref_peer(peer, "register_verify: sip_unref_peer: from sip_find_peer operation");
@ -16950,7 +16969,7 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of,
return AUTH_DONT_KNOW;
}
if (!ast_apply_ha(peer->ha, addr)) {
if (!ast_apply_acl(peer->acl, addr, "SIP Peer ACL: ")) {
ast_debug(2, "Found peer '%s' for '%s', but fails host access\n", peer->name, of);
sip_unref_peer(peer, "sip_unref_peer: check_peer_ok: from sip_find_peer call, early return of AUTH_ACL_FAILED");
return AUTH_ACL_FAILED;
@ -17754,7 +17773,7 @@ static char *sip_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
user->secret,
user->accountcode,
user->context,
AST_CLI_YESNO(user->ha != NULL),
AST_CLI_YESNO(ast_acl_list_is_empty(user->acl) == 0),
AST_CLI_YESNO(ast_test_flag(&user->flags[0], SIP_NAT_FORCE_RPORT)));
ao2_unlock(user);
sip_unref_peer(user, "sip show users");
@ -18013,7 +18032,7 @@ static char *_sip_show_peers(int fd, int *total, struct mansession *s, const str
ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_RPORT) ?
ast_test_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT) ? " A " : " a " :
ast_test_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT) ? " N " : " ", /* NAT=yes? */
peer->ha ? " A " : " ", /* permit/deny */
(!ast_acl_list_is_empty(peer->acl)) ? " A " : " ", /* permit/deny */
tmp_port, status,
peer->description ? peer->description : "",
realtimepeers ? (peer->is_realtime ? "Cached RT" : "") : "");
@ -18048,7 +18067,7 @@ static char *_sip_show_peers(int fd, int *total, struct mansession *s, const str
ast_test_flag(&peer->flags[1], SIP_PAGE2_SYMMETRICRTP) ? "yes" : "no",
ast_test_flag(&peer->flags[1], SIP_PAGE2_VIDEOSUPPORT) ? "yes" : "no", /* VIDEOSUPPORT=yes? */
ast_test_flag(&peer->flags[1], SIP_PAGE2_TEXTSUPPORT) ? "yes" : "no", /* TEXTSUPPORT=yes? */
peer->ha ? "yes" : "no", /* permit/deny */
ast_acl_list_is_empty(peer->acl) ? "no" : "yes", /* permit/deny/acl */
status,
realtimepeers ? (peer->is_realtime ? "yes" : "no") : "no",
peer->description);
@ -18719,8 +18738,8 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct
ast_cli(fd, " Insecure : %s\n", insecure2str(ast_test_flag(&peer->flags[0], SIP_INSECURE)));
ast_cli(fd, " Force rport : %s\n", force_rport_string(peer->flags));
ast_cli(fd, " Symmetric RTP: %s\n", comedia_string(peer->flags));
ast_cli(fd, " ACL : %s\n", AST_CLI_YESNO(peer->ha != NULL));
ast_cli(fd, " DirectMedACL : %s\n", AST_CLI_YESNO(peer->directmediaha != NULL));
ast_cli(fd, " ACL : %s\n", AST_CLI_YESNO(ast_acl_list_is_empty(peer->acl) == 0));
ast_cli(fd, " DirectMedACL : %s\n", AST_CLI_YESNO(ast_acl_list_is_empty(peer->directmediaacl) == 0));
ast_cli(fd, " T.38 support : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT)));
ast_cli(fd, " T.38 EC mode : %s\n", faxec2str(ast_test_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT)));
ast_cli(fd, " T.38 MaxDtgrm: %d\n", peer->t38_maxdatagram);
@ -18838,7 +18857,7 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct
astman_append(s, "SIP-Comedia: %s\r\n", ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA) ?
(ast_test_flag(&peer->flags[1], SIP_PAGE2_SYMMETRICRTP) ? "A" : "a") :
(ast_test_flag(&peer->flags[1], SIP_PAGE2_SYMMETRICRTP) ? "Y" : "N"));
astman_append(s, "ACL: %s\r\n", (peer->ha?"Y":"N"));
astman_append(s, "ACL: %s\r\n", (ast_acl_list_is_empty(peer->acl) ? "N" : "Y"));
astman_append(s, "SIP-CanReinvite: %s\r\n", (ast_test_flag(&peer->flags[0], SIP_DIRECT_MEDIA)?"Y":"N"));
astman_append(s, "SIP-DirectMedia: %s\r\n", (ast_test_flag(&peer->flags[0], SIP_DIRECT_MEDIA)?"Y":"N"));
astman_append(s, "SIP-PromiscRedir: %s\r\n", (ast_test_flag(&peer->flags[0], SIP_PROMISCREDIR)?"Y":"N"));
@ -18989,7 +19008,7 @@ static char *sip_show_user(struct ast_cli_entry *e, int cmd, struct ast_cli_args
ast_cli(a->fd, " Pickupgroup : ");
print_group(a->fd, user->pickupgroup, 0);
ast_cli(a->fd, " Callerid : %s\n", ast_callerid_merge(cbuf, sizeof(cbuf), user->cid_name, user->cid_num, "<unspecified>"));
ast_cli(a->fd, " ACL : %s\n", AST_CLI_YESNO(user->ha != NULL));
ast_cli(a->fd, " ACL : %s\n", AST_CLI_YESNO(ast_acl_list_is_empty(user->acl) == 0));
ast_cli(a->fd, " Sess-Timers : %s\n", stmode2str(user->stimer.st_mode_oper));
ast_cli(a->fd, " Sess-Refresh : %s\n", strefresher2str(user->stimer.st_ref));
ast_cli(a->fd, " Sess-Expires : %d secs\n", user->stimer.st_max_se);
@ -27643,6 +27662,23 @@ static int restart_monitor(void)
return 0;
}
static void acl_change_event_cb(const struct ast_event *event, void *userdata)
{
ast_log(LOG_NOTICE, "Reloading chan_sip in response to ACL change event.\n");
ast_mutex_lock(&sip_reload_lock);
if (sip_reloading) {
ast_verbose("Previous SIP reload not yet done\n");
} else {
sip_reloading = TRUE;
sip_reloadreason = CHANNEL_ACL_RELOAD;
}
ast_mutex_unlock(&sip_reload_lock);
restart_monitor();
}
/*! \brief Session-Timers: Restart session timer */
static void restart_session_timer(struct sip_pvt *p)
@ -29041,8 +29077,8 @@ static void add_peer_mailboxes(struct sip_peer *peer, const char *value)
static struct sip_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int realtime, int devstate_only)
{
struct sip_peer *peer = NULL;
struct ast_ha *oldha = NULL;
struct ast_ha *olddirectmediaha = NULL;
struct ast_acl_list *oldacl = NULL;
struct ast_acl_list *olddirectmediaacl = NULL;
int found = 0;
int firstpass = 1;
uint16_t port = 0;
@ -29056,6 +29092,7 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
static int deprecation_warning = 1;
int alt_fullcontact = alt ? 1 : 0, headercount = 0;
struct ast_str *fullcontact = ast_str_alloca(512);
int acl_change_subscription_needed = 0;
if (!realtime || ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS)) {
/* Note we do NOT use sip_find_peer here, to avoid realtime recursion */
@ -29104,10 +29141,10 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
/* Note that our peer HAS had its reference count increased */
if (firstpass) {
oldha = peer->ha;
peer->ha = NULL;
olddirectmediaha = peer->directmediaha;
peer->directmediaha = NULL;
oldacl = peer->acl;
peer->acl = NULL;
olddirectmediaacl = peer->directmediaacl;
peer->directmediaacl = NULL;
set_peer_defaults(peer); /* Set peer defaults */
peer->type = 0;
}
@ -29308,25 +29345,25 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
sip_unref_peer(peer, "sip_unref_peer: from build_peer defaultip");
return NULL;
}
} else if (!strcasecmp(v->name, "permit") || !strcasecmp(v->name, "deny")) {
} else if (!strcasecmp(v->name, "permit") || !strcasecmp(v->name, "deny") || !strcasecmp(v->name, "acl")) {
int ha_error = 0;
if (!ast_strlen_zero(v->value)) {
peer->ha = ast_append_ha(v->name, v->value, peer->ha, &ha_error);
ast_append_acl(v->name, v->value, &peer->acl, &ha_error, &acl_change_subscription_needed);
}
if (ha_error) {
ast_log(LOG_ERROR, "Bad ACL entry in configuration line %d : %s\n", v->lineno, v->value);
}
} else if (!strcasecmp(v->name, "contactpermit") || !strcasecmp(v->name, "contactdeny")) {
} else if (!strcasecmp(v->name, "contactpermit") || !strcasecmp(v->name, "contactdeny") || !strcasecmp(v->name, "contactacl")) {
int ha_error = 0;
if (!ast_strlen_zero(v->value)) {
peer->contactha = ast_append_ha(v->name + 7, v->value, peer->contactha, &ha_error);
ast_append_acl(v->name + 7, v->value, &peer->contactacl, &ha_error, &acl_change_subscription_needed);
}
if (ha_error) {
ast_log(LOG_ERROR, "Bad ACL entry in configuration line %d : %s\n", v->lineno, v->value);
}
} else if (!strcasecmp(v->name, "directmediapermit") || !strcasecmp(v->name, "directmediadeny")) {
} else if (!strcasecmp(v->name, "directmediapermit") || !strcasecmp(v->name, "directmediadeny") || !strcasecmp(v->name, "directmediaacl")) {
int ha_error = 0;
peer->directmediaha = ast_append_ha(v->name + 11, v->value, peer->directmediaha, &ha_error);
ast_append_acl(v->name + 11, v->value, &peer->directmediaacl, &ha_error, &acl_change_subscription_needed);
if (ha_error) {
ast_log(LOG_ERROR, "Bad directmedia ACL entry in configuration line %d : %s\n", v->lineno, v->value);
}
@ -29674,8 +29711,8 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
if (global_dynamic_exclude_static && !ast_sockaddr_isnull(&peer->addr)) {
int ha_error = 0;
sip_cfg.contact_ha = ast_append_ha("deny", ast_sockaddr_stringify_addr(&peer->addr),
sip_cfg.contact_ha, &ha_error);
ast_append_acl("deny", ast_sockaddr_stringify_addr(&peer->addr), &sip_cfg.contact_acl, &ha_error, NULL);
if (ha_error) {
ast_log(LOG_ERROR, "Bad or unresolved host/IP entry in configuration for peer %s, cannot add to contact ACL\n", peer->name);
}
@ -29746,8 +29783,8 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
peer->the_mark = 0;
ast_free_ha(oldha);
ast_free_ha(olddirectmediaha);
oldacl = ast_free_acl_list(oldacl);
olddirectmediaacl = ast_free_acl_list(olddirectmediaacl);
if (!ast_strlen_zero(peer->callback)) { /* build string from peer info */
char *reg_string;
if (asprintf(&reg_string, "%s?%s:%s@%s/%s", peer->name, peer->username, !ast_strlen_zero(peer->remotesecret) ? peer->remotesecret : peer->secret, peer->tohost, peer->callback) < 0) {
@ -29757,6 +29794,12 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
ast_free(reg_string);
}
}
/* If an ACL change subscription is needed and doesn't exist, we need one. */
if (acl_change_subscription_needed) {
acl_change_event_subscribe();
}
return peer;
}
@ -29838,13 +29881,14 @@ static int reload_config(enum channelreloadreason reason)
char *cat, *stringp, *context, *oldregcontext;
char newcontexts[AST_MAX_CONTEXT], oldcontexts[AST_MAX_CONTEXT];
struct ast_flags dummy[3];
struct ast_flags config_flags = { reason == CHANNEL_MODULE_LOAD ? 0 : ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS) ? 0 : CONFIG_FLAG_FILEUNCHANGED };
struct ast_flags config_flags = { (reason == CHANNEL_MODULE_LOAD || reason == CHANNEL_ACL_RELOAD) ? 0 : ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS) ? 0 : CONFIG_FLAG_FILEUNCHANGED };
int auto_sip_domains = FALSE;
struct ast_sockaddr old_bindaddr = bindaddr;
int registry_count = 0, peer_count = 0, timerb_set = 0, timert1_set = 0;
int subscribe_network_change = 1;
time_t run_start, run_end;
int bindport = 0;
int acl_change_subscription_needed = 0;
run_start = time(0);
ast_unload_realtime("sipregs");
@ -29887,8 +29931,7 @@ static int reload_config(enum channelreloadreason reason)
}
}
ast_free_ha(sip_cfg.contact_ha);
sip_cfg.contact_ha = NULL;
sip_cfg.contact_acl = ast_free_acl_list(sip_cfg.contact_acl);
default_tls_cfg.enabled = FALSE; /* Default: Disable TLS */
@ -29981,7 +30024,7 @@ static int reload_config(enum channelreloadreason reason)
sip_cfg.accept_outofcall_message = DEFAULT_ACCEPT_OUTOFCALL_MESSAGE;
sip_cfg.allowsubscribe = FALSE;
sip_cfg.disallowed_methods = SIP_UNKNOWN;
sip_cfg.contact_ha = NULL; /* Reset the contact ACL */
sip_cfg.contact_acl = NULL; /* Reset the contact ACL */
snprintf(global_useragent, sizeof(global_useragent), "%s %s", DEFAULT_USERAGENT, ast_get_version());
snprintf(global_sdpsession, sizeof(global_sdpsession), "%s %s", DEFAULT_SDPSESSION, ast_get_version());
snprintf(global_sdpowner, sizeof(global_sdpowner), "%s", DEFAULT_SDPOWNER);
@ -30179,9 +30222,9 @@ static int reload_config(enum channelreloadreason reason)
ast_sockaddr_stringify(&sip_tcp_desc.local_address));
} else if (!strcasecmp(v->name, "dynamic_exclude_static") || !strcasecmp(v->name, "dynamic_excludes_static")) {
global_dynamic_exclude_static = ast_true(v->value);
} else if (!strcasecmp(v->name, "contactpermit") || !strcasecmp(v->name, "contactdeny")) {
} else if (!strcasecmp(v->name, "contactpermit") || !strcasecmp(v->name, "contactdeny") || !strcasecmp(v->name, "contactacl")) {
int ha_error = 0;
sip_cfg.contact_ha = ast_append_ha(v->name + 7, v->value, sip_cfg.contact_ha, &ha_error);
ast_append_acl(v->name + 7, v->value, &sip_cfg.contact_acl, &ha_error, &acl_change_subscription_needed);
if (ha_error) {
ast_log(LOG_ERROR, "Bad ACL entry in configuration line %d : %s\n", v->lineno, v->value);
}
@ -30959,10 +31002,15 @@ static int reload_config(enum channelreloadreason reason)
run_end = time(0);
ast_debug(4, "SIP reload_config done...Runtime= %d sec\n", (int)(run_end-run_start));
/* If an ACL change subscription is needed and doesn't exist, we need one. */
if (acl_change_subscription_needed) {
acl_change_event_subscribe();
}
return 0;
}
static int apply_directmedia_ha(struct sip_pvt *p, struct ast_ha *directmediaha, const char *op)
static int apply_directmedia_acl(struct sip_pvt *p, struct ast_acl_list *directmediaacl, const char *op)
{
struct ast_sockaddr us = { { 0, }, }, them = { { 0, }, };
int res = AST_SENSE_ALLOW;
@ -30970,7 +31018,7 @@ static int apply_directmedia_ha(struct sip_pvt *p, struct ast_ha *directmediaha,
ast_rtp_instance_get_remote_address(p->rtp, &them);
ast_rtp_instance_get_local_address(p->rtp, &us);
if ((res = ast_apply_ha(directmediaha, &them)) == AST_SENSE_DENY) {
if ((res = ast_apply_acl(directmediaacl, &them, "SIP Direct Media ACL: ")) == AST_SENSE_DENY) {
const char *us_addr = ast_strdupa(ast_sockaddr_stringify(&us));
const char *them_addr = ast_strdupa(ast_sockaddr_stringify(&them));
@ -31045,8 +31093,8 @@ static int sip_set_udptl_peer(struct ast_channel *chan, struct ast_udptl *udptl)
static int sip_allow_anyrtp_remote(struct ast_channel *chan1, struct ast_channel *chan2, char *rtptype)
{
struct sip_pvt *p1 = NULL, *p2 = NULL;
struct ast_ha *p2_directmediaha = NULL; /* opposed directmediaha for comparing against first channel host address */
struct ast_ha *p1_directmediaha = NULL; /* opposed directmediaha for comparing against second channel host address */
struct ast_acl_list *p2_directmediaacl = NULL; /* opposed directmediaha for comparing against first channel host address */
struct ast_acl_list *p1_directmediaacl = NULL; /* opposed directmediaha for comparing against second channel host address */
int res = 1;
if (!(p1 = ast_channel_tech_pvt(chan1))) {
@ -31058,18 +31106,18 @@ static int sip_allow_anyrtp_remote(struct ast_channel *chan1, struct ast_channel
}
sip_pvt_lock(p2);
if (p2->relatedpeer && p2->relatedpeer->directmediaha) {
p2_directmediaha = ast_duplicate_ha_list(p2->relatedpeer->directmediaha);
if (p2->relatedpeer && p2->relatedpeer->directmediaacl) {
p2_directmediaacl = ast_duplicate_acl_list(p2->relatedpeer->directmediaacl);
}
sip_pvt_unlock(p2);
sip_pvt_lock(p1);
if (p1->relatedpeer && p1->relatedpeer->directmediaha) {
p1_directmediaha = ast_duplicate_ha_list(p1->relatedpeer->directmediaha);
if (p1->relatedpeer && p1->relatedpeer->directmediaacl) {
p1_directmediaacl = ast_duplicate_acl_list(p1->relatedpeer->directmediaacl);
}
if (p2_directmediaha && ast_test_flag(&p1->flags[0], SIP_DIRECT_MEDIA)) {
if (!apply_directmedia_ha(p1, p2_directmediaha, rtptype)) {
if (p2_directmediaacl && ast_test_flag(&p1->flags[0], SIP_DIRECT_MEDIA)) {
if (!apply_directmedia_acl(p1, p2_directmediaacl, rtptype)) {
res = 0;
}
}
@ -31080,8 +31128,8 @@ static int sip_allow_anyrtp_remote(struct ast_channel *chan1, struct ast_channel
}
sip_pvt_lock(p2);
if (p1_directmediaha && ast_test_flag(&p2->flags[0], SIP_DIRECT_MEDIA)) {
if (!apply_directmedia_ha(p2, p1_directmediaha, rtptype)) {
if (p1_directmediaacl && ast_test_flag(&p2->flags[0], SIP_DIRECT_MEDIA)) {
if (!apply_directmedia_acl(p2, p1_directmediaacl, rtptype)) {
res = 0;
}
}
@ -31089,12 +31137,12 @@ static int sip_allow_anyrtp_remote(struct ast_channel *chan1, struct ast_channel
allow_anyrtp_remote_end:
if (p2_directmediaha) {
ast_free_ha(p2_directmediaha);
if (p2_directmediaacl) {
p2_directmediaacl = ast_free_acl_list(p2_directmediaacl);
}
if (p1_directmediaha) {
ast_free_ha(p1_directmediaha);
if (p1_directmediaacl) {
p1_directmediaacl = ast_free_acl_list(p1_directmediaacl);
}
return res;
@ -32574,6 +32622,7 @@ static int unload_module(void)
int wait_count;
network_change_event_unsubscribe();
acl_change_event_unsubscribe();
ast_sched_dump(sched);
@ -32723,7 +32772,7 @@ static int unload_module(void)
ao2_t_ref(sip_monitor_instances, -1, "unref the sip_monitor_instances table");
clear_sip_domains();
ast_free_ha(sip_cfg.contact_ha);
sip_cfg.contact_acl = ast_free_acl_list(sip_cfg.contact_acl);
close(sipsock);
ast_sched_context_destroy(sched);
con = ast_context_find(used_context);

@ -757,7 +757,7 @@ struct sip_settings {
char default_subscribecontext[AST_MAX_CONTEXT];
char default_record_on_feature[FEATURE_MAX_LEN];
char default_record_off_feature[FEATURE_MAX_LEN];
struct ast_ha *contact_ha; /*! \brief Global list of addresses dynamic peers are not allowed to use */
struct ast_acl_list *contact_acl; /*! \brief Global list of addresses dynamic peers are not allowed to use */
struct ast_format_cap *caps; /*!< Supported codecs */
int tcp_enabled;
int default_max_forwards; /*!< Default max forwards (SIP Anti-loop) */
@ -1117,7 +1117,7 @@ struct sip_pvt {
int rtptimeout; /*!< RTP timeout time */
int rtpholdtimeout; /*!< RTP timeout time on hold*/
int rtpkeepalive; /*!< RTP send packets for keepalive */
struct ast_ha *directmediaha; /*!< Which IPs are allowed to interchange direct media with this peer - copied from sip_peer */
struct ast_acl_list *directmediaacl; /*!< Which IPs are allowed to interchange direct media with this peer - copied from sip_peer */
struct ast_sockaddr recv; /*!< Received as */
struct ast_sockaddr ourip; /*!< Our IP (as seen from the outside) */
enum transfermodes allowtransfer; /*!< REFER: restriction scheme */
@ -1335,9 +1335,9 @@ struct sip_peer {
int keepalive; /*!< Keepalive: How often to send keep alive packet */
int keepalivesend; /*!< Keepalive: Scheduled item for sending keep alive packet */
struct ast_sockaddr defaddr; /*!< Default IP address, used until registration */
struct ast_ha *ha; /*!< Access control list */
struct ast_ha *contactha; /*!< Restrict what IPs are allowed in the Contact header (for registration) */
struct ast_ha *directmediaha; /*!< Restrict what IPs are allowed to interchange direct media with */
struct ast_acl_list *acl; /*!< Access control list */
struct ast_acl_list *contactacl; /*!< Restrict what IPs are allowed in the Contact header (for registration) */
struct ast_acl_list *directmediaacl; /*!< Restrict what IPs are allowed to interchange direct media with */
struct ast_variable *chanvars; /*!< Variables to set for channel created by user */
struct sip_pvt *mwipvt; /*!< Subscription for MWI */
struct sip_st_cfg stimer; /*!< SIP Session-Timers */

@ -0,0 +1,86 @@
;
; Named Access Control Lists (ACLs)
;
; A convenient way to share acl definitions
;
; This configuration file is read on startup
;
; CLI Commands
; -----------------------------------------------------------
; acl show Show all named ACLs configured
; acl show <name> Show contents of a particular named ACL
; reload acl Reload configuration file
;
;[general]
;systemname=asterisksystem1 ; If a system name is specified, realtime
; ; ACLs will only be retrieved if they have
; ; a systemname field that matches this value.
; ; If it's less blank, the field is ignored.
;
; Any configuration that uses ACLs which has been made to be able to use named
; ACLs will specify a named ACL with the 'acl' option in its configuration in
; a similar fashion to the usual 'permit' and 'deny' options. Example:
; acl=my_named_acl
;
; Multiple named ACLs can be applied by either comma separating the arguments or
; just by adding additional ACL lines. Example:
; acl=my_named_acl
; acl=my_named_acl2
;
; or
;
; acl=my_named_acl,my_named_acl2
;
; ACLs specified by name are evaluated independently from the ACL specified via
; permit/deny. In order for an address to pass a given ACL, it must pass both
; the ACL specified by permit/deny for a given item as well as any named ACLs
; that were specified.
;
;[example_named_acl1]
;deny=0.0.0.0/0.0.0.0
;permit=209.16.236.0
;permit=209.16.236.1
;
;[example_named_acl2]
;permit=0.0.0.0/0.0.0.0
;deny=10.24.20.171
;deny=10.24.20.103
;deny=209.16.236.1
;
; example_named_acl1 above shows an example of whitelisting. When whitelisting, the
; named ACLs should follow a deny that blocks everything (like deny=0.0.0.0/0.0.0.0)
; The following example explains how combining the ACLs works:
; <in another configuration>
; [example_item_with_acl]
; acl=example_named_acl1
; acl=example_named_acl2
;
; Suppose 209.16.236.0 tries to communicate and the ACL for that example is applied to it...
; First, example_named_acl1 is evaluated. The address is allowed by that ACL.
; Next, example_named_acl2 is evaluated. The address isn't blocked by example_named_acl2
; either, so it passes.
;
; Suppose instead 209.16.236.1 tries to communicate and the same ACL is applied.
; First, example_named_acl1 is evaluated and the address is allowed.
; However, it is blocked by example_named_acl2, so the address is blocked from the combined
; ACL.
;
; Similarly, the permits/denies in specific configurations that make up an ACL definition
; are also treated as a separate ACL for evaluation. So if we change the example above to:
; <in another configuration>
; [example_item_with_acl]
; acl=example_named_acl1
; acl=example_named_acl2
; deny=209.16.236.0
;
; Then 209.16.236.0 will be rejected by the non-named component of the combined ACL even
; though it passes the two named components.
;
;
; Named ACLs can use ipv6 addresses just like normal ACLs.
;[ipv6_example_1]
;deny = ::
;permit = ::1/128
;
;[ipv6_example_2]
;permit = fe80::21d:bad:fad:2323

@ -32,6 +32,11 @@
; cdr.conf
; rtp.conf
;
; Named ACLs specified in realtime also can not be used
; from manager.conf unless the storage driver is preloaded.
; Attempting to use a realtime stored named ACL before the
; driver is loaded will result in an invalid ACL which
; rejects all addresses.
;
; Realtime configuration engine
;
@ -74,6 +79,7 @@
;meetme => mysql,general
;queues => odbc,asterisk
;queue_members => odbc,asterisk
;acls => odbc,asterisk
;musiconhold => mysql,general
;queue_log => mysql,general
;

@ -518,11 +518,11 @@ inkeys=freeworlddialup
;
; Further user sections may be added, specifying a context and a secret used
; for connections with that given authentication name. Limited IP based
; access control is allowed by use of "permit" and "deny" keywords. Multiple
; rules are permitted. Multiple permitted contexts may be specified, in
; which case the first will be the default. You can also override Caller*ID
; so that when you receive a call you set the Caller*ID to be what you want
; instead of trusting what the remote user provides
; access control is allowed by use of "permit", "deny", and "acl" keywords.
; Multiple rules are permitted. Multiple permitted contexts may be specified,
; in which case the first will be the default. You can also override
; Caller*ID so that when you receive a call you set the Caller*ID to be what
; you want instead of trusting what the remote user provides
;
; There are three authentication methods that are supported: md5, plaintext,
; and rsa. The least secure is "plaintext", which sends passwords cleartext
@ -639,6 +639,7 @@ description=Demo System At Digium ; Description of this peer, as listed by
;secret=shazbot ; only the last specified secret will be used.
;context=default
;permit=0.0.0.0/0.0.0.0
;acl=example_named_acl
;
; With immediate=yes, an IAX2 phone or a phone on an IAXy acts as a hot-line

@ -85,6 +85,7 @@ bindaddr = 0.0.0.0
;secret = mysecret
;deny=0.0.0.0/0.0.0.0
;permit=209.16.236.73/255.255.255.0
;acl=named_acl_example ; use a named ACL from acl.conf
;
;eventfilter=Event: Newchannel
;eventfilter=!Channel: DAHDI*

@ -10,8 +10,8 @@
;
; Especially note the following settings:
; - allowguest (default enabled)
; - permit/deny - IP address filters
; - contactpermit/contactdeny - IP address filters for registrations
; - permit/deny/acl - IP address filters
; - contactpermit/contactdeny/contactacl - IP address filters for registrations
; - context - Which set of services you offer various users
;
; SIP dial strings
@ -454,6 +454,7 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls
;contactdeny=0.0.0.0/0.0.0.0 ; Use contactpermit and contactdeny to
;contactpermit=172.16.0.0/255.255.0.0 ; restrict at what IPs your users may
; register their phones.
;contactacl=named_acl_example ; Use named ACLs defined in acl.conf
;engine=asterisk ; RTP engine to use when communicating with the device
@ -956,6 +957,7 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls
; Use this if some of your phones are on IP addresses that
; can not reach each other directly. This way you can force
; RTP to always flow through asterisk in such cases.
;directmediaacl=acl_example ; Use named ACLs defined in acl.conf
;ignoresdpversion=yes ; By default, Asterisk will honor the session version
; number in SDP packets and will only modify the SDP
@ -1211,10 +1213,11 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls
; t38pt_usertpsource
; contactpermit ; Limit what a host may register as (a neat trick
; contactdeny ; is to register at the same IP as a SIP provider,
; ; then call oneself, and get redirected to that
; contactacl ; then call oneself, and get redirected to that
; ; same location).
; directmediapermit
; directmediadeny
; directmediaacl
; unsolicited_mailbox
; use_q850_reason
; maxforwards
@ -1419,6 +1422,7 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls
;permit=2001:db8::/32 ; IPv6 ACLs can be specified if desired. IPv6 ACLs
; apply only to IPv6 addresses, and IPv4 ACLs apply
; only to IPv4 addresses.
;acl=named_acl_example ; Use named ACLs defined in acl.conf
;[cisco1]
;type=friend

@ -1,7 +1,7 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2006, Digium, Inc.
* Copyright (C) 1999 - 2012, Digium, Inc.
*
* Mark Spencer <markster@digium.com>
*
@ -29,15 +29,18 @@ extern "C" {
#endif
#include "asterisk/network.h"
#include "asterisk/linkedlists.h"
#include "asterisk/netsock2.h"
#include "asterisk/io.h"
#define AST_SENSE_DENY 0
#define AST_SENSE_ALLOW 1
enum ast_acl_sense {
AST_SENSE_DENY,
AST_SENSE_ALLOW
};
/* Host based access control */
/*! \brief internal representation of acl entries
/*! \brief internal representation of ACL entries
* In principle user applications would have no need for this,
* but there is sometimes a need to extract individual items,
* e.g. to print them, and rather than defining iterators to
@ -49,10 +52,29 @@ struct ast_ha {
/* Host access rule */
struct ast_sockaddr addr;
struct ast_sockaddr netmask;
int sense;
enum ast_acl_sense sense;
struct ast_ha *next;
};
#define ACL_NAME_LENGTH 80
/*!
* \brief an ast_acl is a linked list node of ast_ha structs which may have names.
*
* \note These shouldn't be used directly by ACL consumers. Consumers should handle
* ACLs via ast_acl_list structs.
*/
struct ast_acl {
struct ast_ha *acl; /*!< Rules contained by the ACL */
int is_realtime; /*!< If raised, this named ACL was retrieved from realtime storage */
int is_invalid; /*!< If raised, this is an invalid ACL which will automatically reject everything. */
char name[ACL_NAME_LENGTH]; /*!< If this was retrieved from the named ACL subsystem, this is the name of the ACL. */
AST_LIST_ENTRY(ast_acl) list;
};
/*! \brief Wrapper for an ast_acl linked list. */
AST_LIST_HEAD(ast_acl_list, ast_acl);
/*!
* \brief Free a list of HAs
*
@ -65,6 +87,18 @@ struct ast_ha {
*/
void ast_free_ha(struct ast_ha *ha);
/*!
* \brief Free a list of ACLs
*
* \details
* Given the head of a list of ast_acl structs, it and all appended
* acl structs will be freed. This includes the ast_ha structs within
* the individual nodes.
* \param acl The list of ACLs to free
* \retval NULL
*/
struct ast_acl_list *ast_free_acl_list(struct ast_acl_list *acl);
/*!
* \brief Copy the contents of one HA to another
*
@ -100,6 +134,35 @@ void ast_copy_ha(const struct ast_ha *from, struct ast_ha *to);
*/
struct ast_ha *ast_append_ha(const char *sense, const char *stuff, struct ast_ha *path, int *error);
/*!
* \brief Add a rule to an ACL struct
*
* \details
* This adds a named ACL or an ACL rule to an ast_acl container.
* It works in a similar way to ast_append_ha.
*
* \param sense Can be any among "permit", "deny", or "acl"
* this controls whether the rule being added will simply modify the unnamed ACL at the head of the list
* or if a new named ACL will be added to that ast_acl.
* \param stuff If sense is 'permit'/'deny', this is the ip address and subnet mask separated with a '/' like in ast_append ha.
* If it sense is 'acl', then this will be the name of the ACL being appended to the container.
* \param path Address of the ACL list being appended
* \param[out] error The int that error points to will be set to 1 if an error occurs.
* \param[out] named_acl_flag This will raise a flag under certain conditions to indicate that a named ACL has been added by this
* operation. This may be used to indicate that an event subscription should be made against the named ACL subsystem.
* Note: This flag may be raised by this function, but it will never be lowered by it.
*/
void ast_append_acl(const char *sense, const char *stuff, struct ast_acl_list **path, int *error, int *named_acl_flag);
/*!
* \brief Determines if an ACL is empty or if it contains entries
*
* \param acl_list The ACL list being checked
* \retval 0 - the list is not empty
* \retval 1 - the list is empty
*/
int ast_acl_list_is_empty(struct ast_acl_list *acl_list);
/*!
* \brief Apply a set of rules to a given IP address
*
@ -115,7 +178,25 @@ struct ast_ha *ast_append_ha(const char *sense, const char *stuff, struct ast_ha
* \retval AST_SENSE_ALLOW The IP address passes our ACL
* \retval AST_SENSE_DENY The IP address fails our ACL
*/
int ast_apply_ha(const struct ast_ha *ha, const struct ast_sockaddr *addr);
enum ast_acl_sense ast_apply_ha(const struct ast_ha *ha, const struct ast_sockaddr *addr);
/*!
* \brief Apply a set of rules to a given IP address
*
* \details
* Similar to the above, only uses an acl container, which is a whole slew
* of ast_ha lists. It runs ast_apply_ha on each of the ast_ha structs
* contained in the acl container. It will deny if any of the ast_ha lists
* fail, and it will pass only if all of the rules pass.
*
* \param acl The head of the list of ACLs to evaluate
* \param addr An ast_sockaddr whose address is considered when matching rules
* \param purpose Context for which the ACL is being applied - Establishes purpose of a notice when rejected
*
* \retval AST_SENSE_ALLOW The IP address passes our ACLs
* \retval AST_SENSE_DENY The IP address fails our ACLs
*/
enum ast_acl_sense ast_apply_acl(struct ast_acl_list *acl_list, const struct ast_sockaddr *addr, const char *purpose);
/*!
* \brief Get the IP address given a hostname
@ -199,14 +280,25 @@ int ast_lookup_iface(char *iface, struct ast_sockaddr *address);
* value is allocated on the heap and must be freed independently
* of the input parameter when finished.
*
* \note
* This function is not actually used anywhere.
*
* \param original The ast_ha to copy
* \retval The head of the list of duplicated ast_has
*/
struct ast_ha *ast_duplicate_ha_list(struct ast_ha *original);
/*!
* \brief Duplicates the contests of a list of lists of host access rules.
*
* \details
* A deep copy of an ast_acl list is made (which in turn means a deep copy of
* each of the ast_ha structs contained within). The returned value is allocated
* on the heap and must be freed independently of the input paramater when
* finished.
*
* \param original The ast_acl_list to copy
* \retval The new duplicated ast_acl_list
*/
struct ast_acl_list *ast_duplicate_acl_list(struct ast_acl_list *original);
/*!
* \brief Find our IP address
*
@ -258,6 +350,41 @@ int ast_str2tos(const char *value, unsigned int *tos);
*/
const char *ast_tos2str(unsigned int tos);
/*!
* \brief Retrieve a named ACL
*
* \details
* This function attempts to find a named ACL. If found, a copy
* of the requested ACL will be made which must be freed by
* the caller.
*
* \param name Name of the ACL sought
* \param[out] is_realtime will be true if the ACL being returned is from realtime
* \param[out] is_undefined will be true if no ACL profile can be found for the requested name
*
* \retval A copy of the named ACL as an ast_ha
* \retval NULL if no ACL could be found.
*/
struct ast_ha *ast_named_acl_find(const char *name, int *is_realtime, int *is_undefined);
/*!
* \brief Initialize and configure the named ACL system.
*
* \details
* This function will prepare the named ACL system for use.
* For this reason, it needs to be called before other things that use ACLs are initialized.
*/
int ast_named_acl_init(void);
/*!
* \brief reload/reconfigure the named ACL system.
*
* \details
* This function is designed to trigger an event upon a successful reload that may update
* ACL consumers.
*/
int ast_named_acl_reload(void);
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif

@ -970,6 +970,7 @@ enum channelreloadreason {
CHANNEL_MODULE_RELOAD,
CHANNEL_CLI_RELOAD,
CHANNEL_MANAGER_RELOAD,
CHANNEL_ACL_RELOAD,
};
/*!

@ -189,6 +189,19 @@ void ast_config_destroy(struct ast_config *config);
*/
struct ast_variable *ast_category_root(struct ast_config *config, char *cat);
/*!
* \brief Sorts categories in a config in the order of a numerical value contained within them.
*
* \param config The config structure you wish to sort
* \param variable Which numerical value you wish to sort by
* \param descending If true, we sort highest to lowest instead of lowest to highest
*
* \details
* This function will assume a value of 0 for any non-numerical strings and NULL fields.
*/
void ast_config_sort_categories(struct ast_config *config, int descending,
int (*comparator)(struct ast_category *p, struct ast_category *q));
/*!
* \brief Goes through categories
*
@ -481,6 +494,15 @@ int ast_config_engine_register(struct ast_config_engine *newconfig);
*/
int ast_config_engine_deregister(struct ast_config_engine *del);
/*!
* \brief Determine if a mapping exists for a given family
*
* \param family which family you are looking to see if a mapping exists for
* \retval 1 if it is mapped
* \retval 0 if it is not
*/
int ast_realtime_is_mapping_defined(const char *family);
/*!
* \brief Exposed initialization method for core process
*

@ -56,8 +56,10 @@ enum ast_event_type {
AST_EVENT_NETWORK_CHANGE = 0x09,
/*! The presence state for a presence provider */
AST_EVENT_PRESENCE_STATE = 0x0a,
/*! Used to alert listeners when a named ACL has changed. */
AST_EVENT_ACL_CHANGE = 0x0b,
/*! Number of event types. This should be the last event type + 1 */
AST_EVENT_TOTAL = 0x0b,
AST_EVENT_TOTAL = 0x0c,
};
/*! \brief Event Information Element types */

@ -1,7 +1,7 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2006, Digium, Inc.
* Copyright (C) 1999 - 2012, Digium, Inc.
*
* Mark Spencer <markster@digium.com>
*
@ -230,6 +230,28 @@ void ast_free_ha(struct ast_ha *ha)
}
}
/* Free ACL list structure */
struct ast_acl_list *ast_free_acl_list(struct ast_acl_list *acl_list)
{
struct ast_acl *current;
if (!acl_list) {
return NULL;
}
AST_LIST_LOCK(acl_list);
while ((current = AST_LIST_REMOVE_HEAD(acl_list, list))) {
ast_free_ha(current->acl);
ast_free(current);
}
AST_LIST_UNLOCK(acl_list);
AST_LIST_HEAD_DESTROY(acl_list);
ast_free(acl_list);
return NULL;
}
/* Copy HA structure */
void ast_copy_ha(const struct ast_ha *from, struct ast_ha *to)
{
@ -275,6 +297,56 @@ struct ast_ha *ast_duplicate_ha_list(struct ast_ha *original)
return ret; /* Return start of list */
}
static int acl_new(struct ast_acl **pointer, const char *name) {
struct ast_acl *acl;
if (!(acl = ast_calloc(1, sizeof(*acl)))) {
return 1;
}
*pointer = acl;
ast_copy_string(acl->name, name, ACL_NAME_LENGTH);
return 0;
}
struct ast_acl_list *ast_duplicate_acl_list(struct ast_acl_list *original)
{
struct ast_acl_list *clone;
struct ast_acl *current_cursor;
struct ast_acl *current_clone;
/* Early return if we receive a duplication request for a NULL original. */
if (!original) {
return NULL;
}
if (!(clone = ast_calloc(1, sizeof(*clone)))) {
ast_log(LOG_WARNING, "Failed to allocate ast_acl_list struct while cloning an ACL\n");
return NULL;
}
AST_LIST_HEAD_INIT(clone);
AST_LIST_LOCK(original);
AST_LIST_TRAVERSE(original, current_cursor, list) {
if ((acl_new(&current_clone, current_cursor->name))) {
ast_log(LOG_WARNING, "Failed to allocate ast_acl struct while cloning an ACL.");
continue;
}
/* Copy data from original ACL to clone ACL */
current_clone->acl = ast_duplicate_ha_list(current_cursor->acl);
current_clone->is_invalid = current_cursor->is_invalid;
current_clone->is_realtime = current_cursor->is_realtime;
AST_LIST_INSERT_TAIL(clone, current_clone, list);
}
AST_LIST_UNLOCK(original);
return clone;
}
/*!
* \brief
* Isolate a 32-bit section of an IPv6 address
@ -396,6 +468,135 @@ static int parse_cidr_mask(struct ast_sockaddr *addr, int is_v4, const char *mas
return 0;
}
void ast_append_acl(const char *sense, const char *stuff, struct ast_acl_list **path, int *error, int *named_acl_flag)
{
struct ast_acl *acl = NULL;
struct ast_acl *current;
struct ast_acl_list *working_list;
char *tmp, *list;
/* If the ACL list is currently uninitialized, it must be initialized. */
if (*path == NULL) {
struct ast_acl_list *list;
list = ast_calloc(1, sizeof(*list));
if (!list) {
/* Allocation Error */
if (error) {
*error = 1;
}
return;
}
AST_LIST_HEAD_INIT(list);
*path = list;
}
working_list = *path;
AST_LIST_LOCK(working_list);
/* First we need to determine if we will need to add a new ACL node or if we can use an existing one. */
if (strncasecmp(sense, "a", 1)) {
/* The first element in the path should be the unnamed, base ACL. If that's the case, we use it. If not,
* we have to make one and link it up appropriately. */
current = AST_LIST_FIRST(working_list);
if (!current || !ast_strlen_zero(current->name)) {
if (acl_new(&acl, "")) {
if (error) {
*error = 1;
}
}
// Need to INSERT the ACL at the head here.
AST_LIST_INSERT_HEAD(working_list, acl, list);
} else {
/* If the first element was already the unnamed base ACL, we just use that one. */
acl = current;
}
/* With the proper ACL set for modification, we can just pass this off to the ast_ha append function. */
acl->acl = ast_append_ha(sense, stuff, acl->acl, error);
AST_LIST_UNLOCK(working_list);
return;
}
/* We are in ACL append mode, so we know we'll be adding one or more named ACLs. */
list = ast_strdupa(stuff);
while ((tmp = strsep(&list, ","))) {
struct ast_ha *named_ha;
int already_included = 0;
/* Remove leading whitespace from the string in case the user put spaces between items */
tmp = ast_skip_blanks(tmp);
/* The first step is to check for a duplicate */
AST_LIST_TRAVERSE(working_list, current, list) {
if (!strcasecmp(current->name, tmp)) { /* ACL= */
/* Inclusion of the same ACL multiple times isn't a catastrophic error, but it will raise the error flag and skip the entry. */
ast_log(LOG_ERROR, "Named ACL '%s' is already included in the ast_acl container.", tmp);
if (error) {
*error = 1;
}
already_included = 1;
break;
}
}
if (already_included) {
continue;
}
if (acl_new(&acl, tmp)) {
/* This is a catastrophic allocation error and we'll return immediately if this happens. */
if (error) {
*error = 1;
}
AST_LIST_UNLOCK(working_list);
return;
}
/* Attempt to grab the Named ACL we are looking for. */
named_ha = ast_named_acl_find(tmp, &acl->is_realtime, &acl->is_invalid);
/* Set the ACL's ast_ha to the duplicated named ACL retrieved above. */
acl->acl = named_ha;
/* Raise the named_acl_flag since we are adding a named ACL to the ACL container. */
if (named_acl_flag) {
*named_acl_flag = 1;
}
/* Now insert the new ACL at the end of the list. */
AST_LIST_INSERT_TAIL(working_list, acl, list);
}
AST_LIST_UNLOCK(working_list);
}
int ast_acl_list_is_empty(struct ast_acl_list *acl_list)
{
struct ast_acl *head;
if (!acl_list) {
return 1;
}
AST_LIST_LOCK(acl_list);
head = AST_LIST_FIRST(acl_list);
AST_LIST_UNLOCK(acl_list);
if (head) {
return 0;
}
return 1;
}
struct ast_ha *ast_append_ha(const char *sense, const char *stuff, struct ast_ha *path, int *error)
{
struct ast_ha *ha;
@ -509,16 +710,51 @@ struct ast_ha *ast_append_ha(const char *sense, const char *stuff, struct ast_ha
const char *addr = ast_strdupa(ast_sockaddr_stringify(&ha->addr));
const char *mask = ast_strdupa(ast_sockaddr_stringify(&ha->netmask));
ast_debug(1, "%s/%s sense %d appended to acl for peer\n", addr, mask, ha->sense);
ast_debug(3, "%s/%s sense %d appended to acl\n", addr, mask, ha->sense);
}
return ret;
}
int ast_apply_ha(const struct ast_ha *ha, const struct ast_sockaddr *addr)
enum ast_acl_sense ast_apply_acl(struct ast_acl_list *acl_list, const struct ast_sockaddr *addr, const char *purpose)
{
struct ast_acl *acl;
/* If the list is NULL, there are no rules, so we'll allow automatically. */
if (!acl_list) {
return AST_SENSE_ALLOW;
}
AST_LIST_LOCK(acl_list);
AST_LIST_TRAVERSE(acl_list, acl, list) {
if (acl->is_invalid) {
/* In this case, the baseline ACL shouldn't ever trigger this, but if that somehow happens, it'll still be shown. */
ast_log(LOG_WARNING, "%sRejecting '%s' due to use of an invalid ACL '%s'.\n", purpose ? purpose : "", ast_sockaddr_stringify_addr(addr),
ast_strlen_zero(acl->name) ? "(BASELINE)" : acl->name);
AST_LIST_UNLOCK(acl_list);
return AST_SENSE_DENY;
}
if (acl->acl) {
if (ast_apply_ha(acl->acl, addr) == AST_SENSE_DENY) {
ast_log(LOG_NOTICE, "%sRejecting '%s' due to a failure to pass ACL '%s'\n", purpose ? purpose : "", ast_sockaddr_stringify_addr(addr),
ast_strlen_zero(acl->name) ? "(BASELINE)" : acl->name);
AST_LIST_UNLOCK(acl_list);
return AST_SENSE_DENY;
}
}
}
AST_LIST_UNLOCK(acl_list);
return AST_SENSE_ALLOW;
}
enum ast_acl_sense ast_apply_ha(const struct ast_ha *ha, const struct ast_sockaddr *addr)
{
/* Start optimistic */
int res = AST_SENSE_ALLOW;
enum ast_acl_sense res = AST_SENSE_ALLOW;
const struct ast_ha *current_ha;
for (current_ha = ha; current_ha; current_ha = current_ha->next) {

@ -117,6 +117,7 @@ int daemon(int, int); /* defined in libresolv of all places */
#include "asterisk/channel.h"
#include "asterisk/translate.h"
#include "asterisk/features.h"
#include "asterisk/acl.h"
#include "asterisk/ulaw.h"
#include "asterisk/alaw.h"
#include "asterisk/callerid.h"
@ -4033,6 +4034,11 @@ int main(int argc, char *argv[])
exit(1);
}
if (ast_named_acl_init()) { /* Initialize the Named ACL system */
printf("%s", term_quit());
exit(1);
}
ast_http_init(); /* Start the HTTP server, if needed */
if (init_manager()) {

@ -773,6 +773,124 @@ struct ast_variable *ast_category_root(struct ast_config *config, char *cat)
return NULL;
}
void ast_config_sort_categories(struct ast_config *config, int descending,
int (*comparator)(struct ast_category *p, struct ast_category *q))
{
/*
* The contents of this function are adapted from
* an example of linked list merge sorting
* copyright 2001 Simon Tatham.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
int insize = 1;
struct ast_category *p, *q, *e, *tail;
int nmerges, psize, qsize, i;
/* If the descending flag was sent, we'll apply inversion to the comparison function's return. */
if (descending) {
descending = -1;
} else {
descending = 1;
}
if (!config->root) {
return;
}
while (1) {
p = config->root;
config->root = NULL;
tail = NULL;
nmerges = 0; /* count number of merges we do in this pass */
while (p) {
nmerges++; /* there exists a merge to be done */
/* step `insize' places along from p */
q = p;
psize = 0;
for (i = 0; i < insize; i++) {
psize++;
q = q->next;
if (!q) {
break;
}
}
/* if q hasn't fallen off end, we have two lists to merge */
qsize = insize;
/* now we have two lists; merge them */
while (psize > 0 || (qsize > 0 && q)) {
/* decide whether next element of merge comes from p or q */
if (psize == 0) {
/* p is empty; e must come from q. */
e = q;
q = q->next;
qsize--;
} else if (qsize == 0 || !q) {
/* q is empty; e must come from p. */
e = p; p = p->next; psize--;
} else if ((comparator(p,q) * descending) <= 0) {
/* First element of p is lower (or same) e must come from p. */
e = p;
p = p->next;
psize--;
} else {
/* First element of q is lower; e must come from q. */
e = q;
q = q->next;
qsize--;
}
/* add the next element to the merged list */
if (tail) {
tail->next = e;
} else {
config->root = e;
}
tail = e;
}
/* now p has stepped `insize' places along, and q has too */
p = q;
}
tail->next = NULL;
/* If we have done only one merge, we're finished. */
if (nmerges <= 1) { /* allow for nmerges==0, the empty list case */
return;
}
/* Otherwise repeat, merging lists twice the size */
insize *= 2;
}
}
char *ast_category_browse(struct ast_config *config, const char *prev)
{
struct ast_category *cat;
@ -2244,6 +2362,23 @@ int ast_config_engine_deregister(struct ast_config_engine *del)
return 0;
}
int ast_realtime_is_mapping_defined(const char *family)
{
struct ast_config_map *map;
ast_mutex_lock(&config_lock);
for (map = config_maps; map; map = map->next) {
if (!strcasecmp(family, map->name)) {
ast_mutex_unlock(&config_lock);
return 1;
}
}
ast_mutex_unlock(&config_lock);
return 0;
}
/*! \brief Find realtime engine for realtime family */
static struct ast_config_engine *find_engine(const char *family, int priority, char *database, int dbsiz, char *table, int tabsiz)
{

@ -44,6 +44,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/config.h"
#include "asterisk/channel.h"
#include "asterisk/term.h"
#include "asterisk/acl.h"
#include "asterisk/manager.h"
#include "asterisk/cdr.h"
#include "asterisk/enum.h"
@ -270,6 +271,7 @@ static struct reload_classes {
{ "dnsmgr", dnsmgr_reload },
{ "extconfig", read_config_maps },
{ "enum", ast_enum_reload },
{ "acl", ast_named_acl_reload },
{ "manager", reload_manager },
{ "http", ast_http_reload },
{ "logger", logger_reload },

@ -80,6 +80,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/astobj2.h"
#include "asterisk/features.h"
#include "asterisk/security_events.h"
#include "asterisk/event.h"
#include "asterisk/aoc.h"
#include "asterisk/stringfields.h"
#include "asterisk/presencestate.h"
@ -990,7 +991,7 @@ static char global_realm[MAXHOSTNAMELEN]; /*!< Default realm */
static int block_sockets;
static int unauth_sessions = 0;
static struct ast_event_sub *acl_change_event_subscription;
/*! \brief
* Descriptor for a manager session, either on the AMI socket or over HTTP.
@ -1011,6 +1012,23 @@ static const struct {
{{ "restart", "gracefully", NULL }},
};
static void acl_change_event_cb(const struct ast_event *event, void *userdata);
static void acl_change_event_subscribe(void)
{
if (!acl_change_event_subscription) {
acl_change_event_subscription = ast_event_subscribe(AST_EVENT_ACL_CHANGE,
acl_change_event_cb, "Manager must react to Named ACL changes", NULL, AST_EVENT_IE_END);
}
}
static void acl_change_event_unsubscribe(void)
{
if (acl_change_event_subscription) {
acl_change_event_subscription = ast_event_unsubscribe(acl_change_event_subscription);
}
}
/* In order to understand what the heck is going on with the
* mansession_session and mansession structs, we need to have a bit of a history
* lesson.
@ -1110,7 +1128,6 @@ static AST_RWLIST_HEAD_STATIC(channelvars, manager_channel_variable);
struct ast_manager_user {
char username[80];
char *secret; /*!< Secret for logging in */
struct ast_ha *ha; /*!< ACL setting */
int readperm; /*!< Authorization for reading */
int writeperm; /*!< Authorization for writing */
int writetimeout; /*!< Per user Timeout for ast_carefulwrite() */
@ -1118,6 +1135,7 @@ struct ast_manager_user {
int keep; /*!< mark entries created on a reload */
struct ao2_container *whitefilters; /*!< Manager event filters - white list */
struct ao2_container *blackfilters; /*!< Manager event filters - black list */
struct ast_acl_list *acl; /*!< ACL setting */
char *a1_hash; /*!< precalculated A1 for Digest auth */
AST_RWLIST_ENTRY(ast_manager_user) list;
};
@ -1666,13 +1684,13 @@ static char *handle_showmanager(struct ast_cli_entry *e, int cmd, struct ast_cli
ast_cli(a->fd,
" username: %s\n"
" secret: %s\n"
" acl: %s\n"
" ACL: %s\n"
" read perm: %s\n"
" write perm: %s\n"
"displayconnects: %s\n",
(user->username ? user->username : "(N/A)"),
(user->secret ? "<Set>" : "(N/A)"),
(user->ha ? "yes" : "no"),
((user->acl && !ast_acl_list_is_empty(user->acl)) ? "yes" : "no"),
authority_to_str(user->readperm, &rauthority),
authority_to_str(user->writeperm, &wauthority),
(user->displayconnects ? "yes" : "no"));
@ -2478,7 +2496,7 @@ static int authenticate(struct mansession *s, const struct message *m)
if (!(user = get_manager_by_name_locked(username))) {
report_invalid_user(s, username);
ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_sockaddr_stringify_addr(&s->session->addr), username);
} else if (user->ha && !ast_apply_ha(user->ha, &s->session->addr)) {
} else if (user->acl && (ast_apply_acl(user->acl, &s->session->addr, "Manager User ACL: ") == AST_SENSE_DENY)) {
report_failed_acl(s, username);
ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_sockaddr_stringify_addr(&s->session->addr), username);
} else if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) {
@ -6402,7 +6420,7 @@ static int auth_http_callback(struct ast_tcptls_session_instance *ser,
}
/* --- We have User for this auth, now check ACL */
if (user->ha && !ast_apply_ha(user->ha, remote_address)) {
if (user->acl && !ast_apply_acl(user->acl, remote_address, "Manager User ACL:")) {
AST_RWLIST_UNLOCK(&users);
ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_sockaddr_stringify_addr(&session->addr), d.username);
ast_http_error(ser, 403, "Permission denied", "Permission denied\n");
@ -7070,7 +7088,7 @@ static void load_channelvars(struct ast_variable *var)
AST_RWLIST_UNLOCK(&channelvars);
}
static int __init_manager(int reload)
static int __init_manager(int reload, int by_external_config)
{
struct ast_config *ucfg = NULL, *cfg = NULL;
#ifdef AST_XML_DOCS
@ -7081,12 +7099,13 @@ static int __init_manager(int reload)
int newhttptimeout = 60;
struct ast_manager_user *user = NULL;
struct ast_variable *var;
struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
struct ast_flags config_flags = { (reload && !by_external_config) ? CONFIG_FLAG_FILEUNCHANGED : 0 };
char a1[256];
char a1_hash[256];
struct ast_sockaddr ami_desc_local_address_tmp;
struct ast_sockaddr amis_desc_local_address_tmp;
int tls_was_enabled = 0;
int acl_subscription_flag = 0;
manager_enabled = 0;
@ -7160,6 +7179,11 @@ static int __init_manager(int reload)
return 0;
}
/* If this wasn't performed due to a forced reload (because those can be created by ACL change events, we need to unsubscribe to ACL change events. */
if (!by_external_config) {
acl_change_event_unsubscribe();
}
/* default values */
ast_copy_string(global_realm, S_OR(ast_config_AST_SYSTEM_NAME, DEFAULT_REALM), sizeof(global_realm));
ast_sockaddr_setnull(&ami_desc.local_address);
@ -7310,7 +7334,7 @@ static int __init_manager(int reload)
ast_copy_string(user->username, cat, sizeof(user->username));
/* Insert into list */
AST_LIST_INSERT_TAIL(&users, user, list);
user->ha = NULL;
user->acl = NULL;
user->keep = 1;
user->readperm = -1;
user->writeperm = -1;
@ -7367,7 +7391,7 @@ static int __init_manager(int reload)
/* cat is NULL here in any case */
while ((cat = ast_category_browse(cfg, cat))) {
struct ast_ha *oldha;
struct ast_acl_list *oldacl;
if (!strcasecmp(cat, "general")) {
continue;
@ -7381,7 +7405,7 @@ static int __init_manager(int reload)
/* Copy name over */
ast_copy_string(user->username, cat, sizeof(user->username));
user->ha = NULL;
user->acl = NULL;
user->readperm = 0;
user->writeperm = 0;
/* Default displayconnect from [general] */
@ -7399,8 +7423,8 @@ static int __init_manager(int reload)
/* Make sure we keep this user and don't destroy it during cleanup */
user->keep = 1;
oldha = user->ha;
user->ha = NULL;
oldacl = user->acl;
user->acl = NULL;
var = ast_variable_browse(cfg, cat);
for (; var; var = var->next) {
@ -7410,8 +7434,9 @@ static int __init_manager(int reload)
}
user->secret = ast_strdup(var->value);
} else if (!strcasecmp(var->name, "deny") ||
!strcasecmp(var->name, "permit")) {
user->ha = ast_append_ha(var->name, var->value, user->ha, NULL);
!strcasecmp(var->name, "permit") ||
!strcasecmp(var->name, "acl")) {
ast_append_acl(var->name, var->value, &user->acl, NULL, &acl_subscription_flag);
} else if (!strcasecmp(var->name, "read") ) {
user->readperm = get_perm(var->value);
} else if (!strcasecmp(var->name, "write") ) {
@ -7432,10 +7457,16 @@ static int __init_manager(int reload)
ast_debug(1, "%s is an unknown option.\n", var->name);
}
}
ast_free_ha(oldha);
oldacl = ast_free_acl_list(oldacl);
}
ast_config_destroy(cfg);
/* Check the flag for named ACL event subscription and if we need to, register a subscription. */
if (acl_subscription_flag && !by_external_config) {
acl_change_event_subscribe();
}
/* Perform cleanup - essentially prune out old users that no longer exist */
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&users, user, list) {
if (user->keep) { /* valid record. clear flag for the next round */
@ -7464,7 +7495,7 @@ static int __init_manager(int reload)
ao2_t_callback(user->blackfilters, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "unlink all black filters");
ao2_t_ref(user->whitefilters, -1, "decrement ref for white container, should be last one");
ao2_t_ref(user->blackfilters, -1, "decrement ref for black container, should be last one");
ast_free_ha(user->ha);
user->acl = ast_free_acl_list(user->acl);
ast_free(user);
}
AST_RWLIST_TRAVERSE_SAFE_END;
@ -7516,6 +7547,13 @@ static int __init_manager(int reload)
return 0;
}
static void acl_change_event_cb(const struct ast_event *event, void *userdata)
{
/* For now, this is going to be performed simply and just execute a forced reload. */
ast_log(LOG_NOTICE, "Reloading manager in response to ACL change event.\n");
__init_manager(1, 1);
}
/* clear out every entry in the channelvar list */
static void free_channelvars(void)
{
@ -7529,12 +7567,12 @@ static void free_channelvars(void)
int init_manager(void)
{
return __init_manager(0);
return __init_manager(0, 0);
}
int reload_manager(void)
{
return __init_manager(1);
return __init_manager(1, 0);
}
int astman_datastore_add(struct mansession *s, struct ast_datastore *datastore)

@ -0,0 +1,558 @@
/*
* Asterisk -- A telephony toolkit for Linux.
*
* Copyright (C) 2012, Digium, Inc.
*
* Mark Spencer <markster@digium.com>
* Jonathan Rose <jrose@digium.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.
*/
/*! \file
*
* \brief Named Access Control Lists
*
* \author Jonathan Rose <jrose@digium.com>
*
* \note Based on a feature proposed by
* Olle E. Johansson <oej@edvina.net>
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/config.h"
#include "asterisk/config_options.h"
#include "asterisk/event.h"
#include "asterisk/utils.h"
#include "asterisk/module.h"
#include "asterisk/cli.h"
#include "asterisk/acl.h"
#include "asterisk/astobj2.h"
#define NACL_CONFIG "acl.conf"
#define ACL_FAMILY "acls"
struct named_acl_global_config {
AST_DECLARE_STRING_FIELDS(
AST_STRING_FIELD(systemname);
);
};
/*
* Configuration structure - holds pointers to ao2 containers used for configuration
* Since there isn't a general level or any other special levels for acl.conf at this
* time, it's really a config options friendly wrapper for the named ACL container
*/
struct named_acl_config {
struct named_acl_global_config *global;
struct ao2_container *named_acl_list;
};
static AO2_GLOBAL_OBJ_STATIC(globals);
/*! \note These functions are used for placing/retrieving named ACLs in their ao2_container. */
static void *named_acl_config_alloc(void);
static void *named_acl_alloc(const char *cat);
static void *named_acl_find(struct ao2_container *container, const char *cat);
/* Config type for named ACL profiles (must not be named general) */
static struct aco_type named_acl_type = {
.type = ACO_ITEM, /*!< named_acls are items stored in containers, not individual global objects */
.category_match = ACO_BLACKLIST,
.category = "^general$", /*!< Match everything but "general" */
.item_alloc = named_acl_alloc, /*!< A callback to allocate a new named_acl based on category */
.item_find = named_acl_find, /*!< A callback to find a named_acl in some container of named_acls */
.item_offset = offsetof(struct named_acl_config, named_acl_list), /*!< Could leave this out since 0 */
};
/* Config type for the general part of the ACL profile (must be named general) */
static struct aco_type global_option = {
.type = ACO_GLOBAL,
.item_offset = offsetof(struct named_acl_config, global),
.category_match = ACO_WHITELIST,
.category = "^general$",
};
/* This array of aco_type structs is necessary to use aco_option_register */
struct aco_type *named_acl_types[] = ACO_TYPES(&named_acl_type);
struct aco_type *global_options[] = ACO_TYPES(&global_option);
struct aco_file named_acl_conf = {
.filename = "acl.conf",
.types = ACO_TYPES(&named_acl_type, &global_option),
};
/* Create a config info struct that describes the config processing for named ACLs. */
CONFIG_INFO_STANDARD(cfg_info, globals, named_acl_config_alloc,
.files = ACO_FILES(&named_acl_conf),
);
struct named_acl {
struct ast_ha *ha;
char name[ACL_NAME_LENGTH]; /* Same max length as a configuration category */
};
static int named_acl_hash_fn(const void *obj, const int flags)
{
const struct named_acl *entry = obj;
return ast_str_hash(entry->name);
}
static int named_acl_cmp_fn(void *obj, void *arg, const int flags)
{
struct named_acl *entry1 = obj;
struct named_acl *entry2 = arg;
return (!strcmp(entry1->name, entry2->name)) ? (CMP_MATCH | CMP_STOP) : 0;
}
/*! \brief destructor for named_acl_config */
static void named_acl_config_destructor(void *obj)
{
struct named_acl_config *cfg = obj;
ao2_cleanup(cfg->named_acl_list);
ao2_cleanup(cfg->global);
}
static void named_acl_global_config_destructor(void *obj)
{
struct named_acl_global_config *global = obj;
ast_string_field_free_memory(global);
}
/*! \brief allocator callback for named_acl_config. Notice it returns void * since it is used by
* the backend config code
*/
static void *named_acl_config_alloc(void)
{
struct named_acl_config *cfg;
if (!(cfg = ao2_alloc(sizeof(*cfg), named_acl_config_destructor))) {
return NULL;
}
if (!(cfg->global = ao2_alloc(sizeof(*cfg->global), named_acl_global_config_destructor))) {
goto error;
}
if (ast_string_field_init(cfg->global, 128)) {
goto error;
}
if (!(cfg->named_acl_list = ao2_container_alloc(37, named_acl_hash_fn, named_acl_cmp_fn))) {
goto error;
}
return cfg;
error:
ao2_ref(cfg, -1);
return NULL;
}
/*! \brief Destroy a named ACL object */
static void destroy_named_acl(void *obj)
{
struct named_acl *named_acl = obj;
ast_free_ha(named_acl->ha);
}
/*!
* \brief Create a named ACL structure
*
* \param cat name given to the ACL
* \retval NULL failure
*\retval non-NULL successfully allocated named ACL
*/
void *named_acl_alloc(const char *cat)
{
struct named_acl *named_acl;
named_acl = ao2_alloc(sizeof(*named_acl), destroy_named_acl);
if (!named_acl) {
return NULL;
}
ast_copy_string(named_acl->name, cat, sizeof(named_acl->name));
return named_acl;
}
/*!
* \brief Find a named ACL in a container by its name
*
* \param container ao2container holding the named ACLs
* \param name of the ACL wanted to be found
* \retval pointer to the named ACL if available. Null if not found.
*/
void *named_acl_find(struct ao2_container *container, const char *cat)
{
struct named_acl tmp;
ast_copy_string(tmp.name, cat, sizeof(tmp.name));
return ao2_find(container, &tmp, OBJ_POINTER);
}
/*!
* \internal
* \brief Callback function to compare the ACL order of two given categories.
* This function is used to sort lists of ACLs received from realtime.
*
* \param p first category being compared
* \param q second category being compared
*
* \retval -1 (p < q)
* \retval 0 (p == q)
* \retval 1 (p > q)
*/
static int acl_order_comparator(struct ast_category *p, struct ast_category *q)
{
int p_value = 0, q_value = 0;
struct ast_variable *p_var = ast_category_first(p);
struct ast_variable *q_var = ast_category_first(q);
while (p_var) {
if (!strcasecmp(p_var->name, "rule_order")) {
p_value = atoi(p_var->value);
break;
}
p_var = p_var->next;
}
while (q_var) {
if (!strcasecmp(q_var->name, "rule_order")) {
q_value = atoi(q_var->value);
break;
}
q_var = q_var->next;
}
if (p_value < q_value) {
return -1;
} else if (q_value < p_value) {
return 1;
}
return 0;
}
/*!
* \internal
* \brief Search for a named ACL via realtime Database and build the named_acl
* if it is valid.
*
* \param name of the ACL wanted to be found
* \retval pointer to the named ACL if available. Null if the ACL subsystem is unconfigured.
*/
static struct named_acl *named_acl_find_realtime(const char *name)
{
struct ast_config *cfg;
char *item = NULL;
const char *systemname = NULL;
struct ast_ha *built_ha = NULL;
struct named_acl *acl;
RAII_VAR(struct named_acl_config *, acl_options, ao2_global_obj_ref(globals), ao2_cleanup);
/* If we have a systemname set in the global options, we only want to retrieve entries with a matching systemname field. */
if (acl_options) {
systemname = acl_options->global->systemname;
}
if (ast_strlen_zero(systemname)) {
cfg = ast_load_realtime_multientry(ACL_FAMILY, "name", name, SENTINEL);
} else {
cfg = ast_load_realtime_multientry(ACL_FAMILY, "name", name, "systemname", systemname, SENTINEL);
}
if (!cfg) {
return NULL;
}
/* At this point, the configuration must be sorted by the order field. */
ast_config_sort_categories(cfg, 0, acl_order_comparator);
while ((item = ast_category_browse(cfg, item))) {
int append_ha_error = 0;
const char *order = ast_variable_retrieve(cfg, item, "rule_order");
const char *sense = ast_variable_retrieve(cfg, item, "sense");
const char *rule = ast_variable_retrieve(cfg, item, "rule");
built_ha = ast_append_ha(sense, rule, built_ha, &append_ha_error);
if (append_ha_error) {
/* We need to completely reject an ACL that contains any bad rules. */
ast_log(LOG_ERROR, "Rejecting realtime ACL due to bad ACL definition '%s': %s - %s - %s\n", name, order, sense, rule);
ast_free_ha(built_ha);
return NULL;
}
}
ast_config_destroy(cfg);
acl = named_acl_alloc(name);
if (!acl) {
ast_log(LOG_ERROR, "allocation error\n");
ast_free_ha(built_ha);
return NULL;
}
acl->ha = built_ha;
return acl;
}
struct ast_ha *ast_named_acl_find(const char *name, int *is_realtime, int *is_undefined) {
struct ast_ha *ha = NULL;
RAII_VAR(struct named_acl_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
RAII_VAR(struct named_acl *, named_acl, NULL, ao2_cleanup);
if (is_realtime) {
*is_realtime = 0;
}
if (is_undefined) {
*is_undefined = 0;
}
/* If the config or its named_acl_list hasn't been initialized, abort immediately. */
if ((!cfg) || (!(cfg->named_acl_list))) {
ast_log(LOG_ERROR, "Attempted to find named ACL '%s', but the ACL configuration isn't available.\n", name);
return NULL;
}
named_acl = named_acl_find(cfg->named_acl_list, name);
/* If a named ACL couldn't be retrieved locally, we need to try realtime storage. */
if (!named_acl) {
RAII_VAR(struct named_acl *, realtime_acl, NULL, ao2_cleanup);
/* Attempt to create from realtime */
if ((realtime_acl = named_acl_find_realtime(name))) {
if (is_realtime) {
*is_realtime = 1;
}
ha = ast_duplicate_ha_list(realtime_acl->ha);
return ha;
}
/* Couldn't create from realtime. Raise relevant flags and print relevant warnings. */
if (ast_realtime_is_mapping_defined(ACL_FAMILY) && !ast_check_realtime(ACL_FAMILY)) {
ast_log(LOG_WARNING, "ACL '%s' does not exist. The ACL will be marked as undefined and will automatically fail if applied.\n"
"This ACL may exist in the configured realtime backend, but that backend hasn't been registered yet. "
"Fix this establishing preload for the backend in 'modules.conf'.\n", name);
} else {
ast_log(LOG_WARNING, "ACL '%s' does not exist. The ACL will be marked as undefined and will automatically fail if applied.\n", name);
}
if (is_undefined) {
*is_undefined = 1;
}
return NULL;
}
ha = ast_duplicate_ha_list(named_acl->ha);
if (!ha) {
ast_log(LOG_NOTICE, "ACL '%s' contains no rules. It is valid, but it will accept addresses unconditionally.\n", name);
}
return ha;
}
/*!
* \internal
* \brief Sends an update event corresponding to a given named ACL that has changed.
*
* \param name Name of the ACL that has changed. May be an empty string (but not NULL)
* If name is an empty string, then all ACLs must be refreshed.
*
* \retval 0 success
* \retval 1 failure
*/
static int push_acl_change_event(char *name)
{
struct ast_event *event = ast_event_new(AST_EVENT_ACL_CHANGE,
AST_EVENT_IE_DESCRIPTION, AST_EVENT_IE_PLTYPE_STR, name,
AST_EVENT_IE_END);
if (!event) {
ast_log(LOG_ERROR, "Failed to allocate acl.conf reload event. Some modules will have out of date ACLs.\n");
return -1;
}
if (ast_event_queue(event)) {
ast_event_destroy(event);
ast_log(LOG_ERROR, "Failed to queue acl.conf reload event. Some modules will have out of date ACLs.\n");
return -1;
}
return 0;
}
/*!
* \internal
* \brief reload configuration for named ACLs
*
* \param fd file descriptor for CLI client
*/
int ast_named_acl_reload(void)
{
enum aco_process_status status;
status = aco_process_config(&cfg_info, 1);
if (status == ACO_PROCESS_ERROR) {
ast_log(LOG_WARNING, "Could not reload ACL config\n");
return 0;
}
if (status == ACO_PROCESS_UNCHANGED) {
/* We don't actually log anything if the config was unchanged,
* but we don't need to send a config change event either.
*/
return 0;
}
/* We need to push an ACL change event with no ACL name so that all subscribers update with all ACLs */
push_acl_change_event("");
return 0;
}
/*!
* \internal
* \brief secondary handler for the 'acl show <name>' command (with arg)
*
* \param fd file descriptor of the cli
* \name name of the ACL requested for display
*/
static void cli_display_named_acl(int fd, const char *name)
{
struct ast_ha *ha;
int ha_index = 0;
int is_realtime = 0;
RAII_VAR(struct named_acl_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
RAII_VAR(struct named_acl *, named_acl, NULL, ao2_cleanup);
/* If the configuration or the configuration's named_acl_list is unavailable, abort. */
if ((!cfg) || (!cfg->named_acl_list)) {
ast_log(LOG_ERROR, "Attempted to show named ACL '%s', but the acl configuration isn't available.\n", name);
return;
}
named_acl = named_acl_find(cfg->named_acl_list, name);
/* If the named_acl couldn't be found with the search, also abort. */
if (!named_acl) {
if (!(named_acl = named_acl_find_realtime(name))) {
ast_cli(fd, "\nCould not find ACL named '%s'\n", name);
return;
}
is_realtime = 1;
}
ast_cli(fd, "\nACL: %s%s\n---------------------------------------------\n", name, is_realtime ? " (realtime)" : "");
for (ha = named_acl->ha; ha; ha = ha->next) {
char *addr = ast_strdupa(ast_sockaddr_stringify_addr(&ha->addr));
char *mask = ast_sockaddr_stringify_addr(&ha->netmask);
ast_cli(fd, "%3d: %s - %s/%s\n", ha_index, ha->sense == AST_SENSE_ALLOW ? "allow" : " deny", addr, mask);
ha_index++;
}
}
/*!
* \internal
* \brief secondary handler for the 'acl show' command (no args)
*
* \param fd file descriptor of the cli
*/
static void cli_display_named_acl_list(int fd)
{
struct ao2_iterator i;
void *o;
RAII_VAR(struct named_acl_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
ast_cli(fd, "\nacl\n---\n");
if (!cfg || !cfg->named_acl_list) {
ast_cli(fd, "ACL configuration isn't available.\n");
return;
}
i = ao2_iterator_init(cfg->named_acl_list, 0);
while ((o = ao2_iterator_next(&i))) {
struct named_acl *named_acl = o;
ast_cli(fd, "%s\n", named_acl->name);
ao2_ref(o, -1);
}
ao2_iterator_destroy(&i);
}
/* \brief ACL command show <name> */
static char *handle_show_named_acl_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
switch (cmd) {
case CLI_INIT:
e->command = "acl show";
e->usage =
"Usage: acl show [name]\n"
" Shows a list of named ACLs or lists all entries in a given named ACL.\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (a->argc == 2) {
cli_display_named_acl_list(a->fd);
return CLI_SUCCESS;
}
if (a->argc == 3) {
cli_display_named_acl(a->fd, a->argv[2]);
return CLI_SUCCESS;
}
return CLI_SHOWUSAGE;
}
static struct ast_cli_entry cli_named_acl[] = {
AST_CLI_DEFINE(handle_show_named_acl_cmd, "Show a named ACL or list all named ACLs"),
};
int ast_named_acl_init()
{
ast_cli_register_multiple(cli_named_acl, ARRAY_LEN(cli_named_acl));
if (aco_info_init(&cfg_info)) {
return 0;
}
/* Register the global options */
aco_option_register(&cfg_info, "systemname", ACO_EXACT, global_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct named_acl_global_config, systemname));
/* Register the per level options. */
aco_option_register(&cfg_info, "permit", ACO_EXACT, named_acl_types, NULL, OPT_ACL_T, 1, FLDSET(struct named_acl, ha));
aco_option_register(&cfg_info, "deny", ACO_EXACT, named_acl_types, NULL, OPT_ACL_T, 0, FLDSET(struct named_acl, ha));
if (aco_process_config(&cfg_info, 0)) {
return 0;
}
return 0;
}
Loading…
Cancel
Save