Adding functionality to mod_xml_radius. Now you can do conditional accounting. And you can pull values from the other leg, specify an alternate variable name in case the variable doesn't exist and you can have default values.

This commit is contained in:
William King 2012-08-15 14:52:02 -07:00
parent 8f22c4acef
commit c6d7417aec
3 changed files with 248 additions and 25 deletions

View File

@ -1,7 +1,7 @@
<include>
<extension name="auth" continue="true">
<condition>
<condition field="${radius_auth_result}" expression="^$">
<action application="radius_auth" inline="true"/>
</condition>
</extension>
@ -18,5 +18,10 @@
</condition>
</extension>
<extension name="originate_leg" continue="true">
<condition>
<action application="export" data="nolocal:h323-call-origin=originate"/>
</condition>
</extension>
</include>

View File

@ -34,6 +34,7 @@
static struct {
switch_memory_pool_t *pool;
switch_xml_t auth_invite_configs;
switch_xml_t auth_reg_configs;
switch_xml_t auth_app_configs;
switch_xml_t acct_start_configs;
switch_xml_t acct_end_configs;
@ -149,7 +150,42 @@ switch_status_t do_config()
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Could not find 'auth_invite' section in config file.\n");
}
if ((tmp = switch_xml_dup(switch_xml_child(cfg, "auth_app"))) == NULL ) {
serv = timeout = deadtime = retries = dict = seq = 0;
if ((tmp = switch_xml_dup(switch_xml_child(cfg, "auth_reg"))) != NULL ) {
if ( (server = switch_xml_child(tmp, "connection")) != NULL) {
for (param = switch_xml_child(server, "param"); param; param = param->next) {
char *var = (char *) switch_xml_attr_soft(param, "name");
if ( strncmp(var, "authserver", 10) == 0 ) {
serv = 1;
} else if ( strncmp(var, "radius_timeout", 14) == 0 ) {
timeout = 1;
} else if ( strncmp(var, "radius_deadtime", 15) == 0 ) {
deadtime = 1;
} else if ( strncmp(var, "radius_retries", 14) == 0 ) {
retries = 1;
} else if ( strncmp(var, "dictionary", 10) == 0 ) {
dict = 1;
} else if ( strncmp(var, "seqfile", 7) == 0 ) {
seq = 1;
}
}
if ( serv && timeout && deadtime && retries && dict && seq ) {
globals.auth_reg_configs = tmp;
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing a require section for radius connections\n");
goto err;
}
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not find 'connection' section for auth_invite\n");
goto err;
}
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Could not find 'auth_invite' section in config file.\n");
}
serv = timeout = deadtime = retries = dict = seq = 0;
if ((tmp = switch_xml_dup(switch_xml_child(cfg, "auth_app"))) != NULL ) {
if ( (server = switch_xml_child(tmp, "connection")) != NULL) {
for (param = switch_xml_child(server, "param"); param; param = param->next) {
char *var = (char *) switch_xml_attr_soft(param, "name");
@ -182,7 +218,8 @@ switch_status_t do_config()
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Could not find 'auth_app' section in config file.\n");
}
if (( tmp = switch_xml_dup(switch_xml_child(cfg, "acct_start"))) == NULL ) {
serv = timeout = deadtime = retries = dict = seq = 0;
if (( tmp = switch_xml_dup(switch_xml_child(cfg, "acct_start"))) != NULL ) {
if ( (server = switch_xml_child(tmp, "connection")) != NULL) {
for (param = switch_xml_child(server, "param"); param; param = param->next) {
char *var = (char *) switch_xml_attr_soft(param, "name");
@ -215,7 +252,8 @@ switch_status_t do_config()
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Could not find 'acct_start' section in config file.\n");
}
if (( tmp = switch_xml_dup(switch_xml_child(cfg, "acct_end"))) == NULL ) {
serv = timeout = deadtime = retries = dict = seq = 0;
if (( tmp = switch_xml_dup(switch_xml_child(cfg, "acct_end"))) != NULL ) {
if ( (server = switch_xml_child(tmp, "connection")) != NULL) {
for (param = switch_xml_child(server, "param"); param; param = param->next) {
char *var = (char *) switch_xml_attr_soft(param, "name");
@ -281,8 +319,11 @@ switch_status_t mod_xml_radius_add_params(switch_core_session_t *session, switch
char *var = (char *) switch_xml_attr(param, "name");
char *vend = (char *) switch_xml_attr(param, "vendor");
char *variable = (char *) switch_xml_attr(param, "variable");
char *variable_secondary = (char *) switch_xml_attr(param, "variable_secondary");
char *val_default = (char *) switch_xml_attr(param, "default");
char *format = (char *) switch_xml_attr(param, "format");
char *other_leg = (char *) switch_xml_attr(param, "other_leg");
attribute = rc_dict_findattr(handle, var);
if ( attribute == NULL ) {
@ -375,8 +416,36 @@ switch_status_t mod_xml_radius_add_params(switch_core_session_t *session, switch
}
} else {
if ( format == NULL ) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing format attribute for %s variable\n", variable);
goto err;
}
if ( attribute->type == 0 ) {
av_value = switch_mprintf(format, switch_channel_get_variable(channel, variable));
const char *val = NULL;
if ( other_leg ) {
val = switch_channel_get_variable_partner(channel, variable);
if ( val == NULL && variable_secondary != NULL) {
val = switch_channel_get_variable_partner(channel, variable_secondary);
}
} else {
val = switch_channel_get_variable(channel, variable);
if ( val == NULL && variable_secondary != NULL) {
val = switch_channel_get_variable(channel, variable_secondary);
}
}
if ( val == NULL && val_default != NULL) {
av_value = switch_mprintf(format, val_default);
} else {
av_value = switch_mprintf(format, val);
}
if ( GLOBAL_DEBUG ) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mod_xml_radius: value: %s\n", (char *) av_value);
}
if (rc_avpair_add(handle, send, attr_num, av_value, -1, vend_num) == NULL) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mod_xml_radius: failed to add option with val '%s' to handle\n", (char *) av_value);
goto err;
@ -487,7 +556,10 @@ switch_xml_t mod_xml_radius_auth_invite(switch_event_t *params) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mod_xml_radius: starting invite authentication\n");
}
mod_xml_radius_new_handle(&new_handle, globals.auth_invite_configs);
if ( mod_xml_radius_new_handle(&new_handle, globals.auth_invite_configs) != SWITCH_STATUS_SUCCESS ) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to load radius handle\n");
goto err;
}
if ( new_handle == NULL ) {
goto err;
@ -565,6 +637,100 @@ switch_xml_t mod_xml_radius_auth_invite(switch_event_t *params) {
return NULL;
}
switch_xml_t mod_xml_radius_auth_reg(switch_event_t *params) {
int result = 0, param_idx = 0;
VALUE_PAIR *send = NULL, *recv = NULL, *service_vp = NULL;
char msg[512 * 10 + 1] = {0};
uint32_t service = PW_AUTHENTICATE_ONLY;
rc_handle *new_handle = NULL;
switch_xml_t fields, xml, dir, dom, usr, vars, var;
char name[512], value[512], *strtmp;
if (GLOBAL_DEBUG ) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mod_xml_radius: starting registration authentication\n");
}
if ( mod_xml_radius_new_handle(&new_handle, globals.auth_invite_configs) != SWITCH_STATUS_SUCCESS ) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to load radius handle\n");
goto err;
}
if ( new_handle == NULL ) {
goto err;
}
if ((fields = switch_xml_child(globals.auth_reg_configs, "fields")) == NULL ) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not find 'fields' section in config file.\n");
goto err;
}
if ( mod_xml_radius_add_params(NULL, params, new_handle, &send, fields) != SWITCH_STATUS_SUCCESS ) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to add params to rc_handle\n");
goto err;
}
if (rc_avpair_add(new_handle, &send, PW_SERVICE_TYPE, &service, -1, 0) == NULL) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mod_xml_radius: failed to add option to handle\n");
goto err;
}
result = rc_auth(new_handle, 0, send, &recv, msg);
if ( GLOBAL_DEBUG ){
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mod_xml_radius: result(RC=%d) %s \n", result, msg);
}
if ( result != 0 ) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mod_xml_radius: Failed to authenticate\n");
goto err;
}
xml = switch_xml_new("document");
switch_xml_set_attr_d(xml, "type", "freeswitch/xml");
dir = switch_xml_add_child_d(xml, "section", 0);
switch_xml_set_attr_d(dir, "name", "directory");
dom = switch_xml_add_child_d(dir, "domain", 0);
switch_xml_set_attr_d(dom, "name", switch_event_get_header(params, "domain"));
usr = switch_xml_add_child_d(dom, "user", 0);
vars = switch_xml_add_child_d(usr, "variables", 0);
switch_xml_set_attr_d(usr, "id", switch_event_get_header(params, "user"));
service_vp = recv;
while (service_vp != NULL) {
rc_avpair_tostr(new_handle, service_vp, name, 512, value, 512);
if ( GLOBAL_DEBUG )
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "\tattribute (%s)[%s] found in radius packet\n", name, value);
var = switch_xml_add_child_d(vars, "variable", param_idx++);
strtmp = strdup(name);
switch_xml_set_attr_d(var, "name", strtmp);
free(strtmp);
strtmp = strdup(value);
switch_xml_set_attr_d(var, "value", strtmp);
free(strtmp);
service_vp = service_vp->next;
}
if ( GLOBAL_DEBUG ) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "XML: %s \n", switch_xml_toxml(xml, 1));
}
rc_avpair_free(recv);
rc_destroy(new_handle);
return xml;
err:
if ( recv ) {
rc_avpair_free(recv);
recv = NULL;
}
if ( new_handle ) {
rc_destroy(new_handle);
new_handle = NULL;
}
return NULL;
}
static switch_xml_t mod_xml_radius_directory_search(const char *section, const char *tag_name, const char *key_name, const char *key_value,
switch_event_t *params, void *user_data)
{
@ -584,9 +750,11 @@ static switch_xml_t mod_xml_radius_directory_search(const char *section, const c
if ( auth_method == NULL) {
return NULL;
}
if ( strncmp( "INVITE", auth_method, 6) == 0) {
xml = mod_xml_radius_auth_invite(params);
} else if ( strncmp( "REGISTER", auth_method, 8) == 0) {
xml = mod_xml_radius_auth_reg(params);
} else {
xml = NULL;
}
@ -622,6 +790,7 @@ switch_status_t mod_xml_radius_check_conditions(switch_channel_t *channel, switc
}
if ( switch_regex_match( switch_channel_get_variable(channel, channel_var), regex) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Didn't match: %s == %s \n", switch_channel_get_variable(channel, channel_var), regex);
all_matched = 0;
}
}
@ -643,6 +812,7 @@ switch_status_t mod_xml_radius_accounting_start(switch_core_session_t *session){
if (GLOBAL_DEBUG ) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mod_xml_radius: starting accounting start\n");
switch_core_session_execute_application(session, "info", NULL);
}
/* If there are conditions defined, and none of them pass, then skip this accounting */
@ -651,7 +821,10 @@ switch_status_t mod_xml_radius_accounting_start(switch_core_session_t *session){
goto end;
}
mod_xml_radius_new_handle(&new_handle, globals.acct_start_configs);
if ( mod_xml_radius_new_handle(&new_handle, globals.acct_start_configs) != SWITCH_STATUS_SUCCESS ) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to load radius handle\n");
goto end;
}
if ((fields = switch_xml_child(globals.acct_start_configs, "fields")) == NULL ) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not find 'fields' section in config file.\n");
@ -700,7 +873,10 @@ switch_status_t mod_xml_radius_accounting_end(switch_core_session_t *session){
goto end;
}
mod_xml_radius_new_handle(&new_handle, globals.acct_end_configs);
if ( mod_xml_radius_new_handle(&new_handle, globals.acct_end_configs) != SWITCH_STATUS_SUCCESS ) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to load radius handle\n");
goto end;
}
if ((fields = switch_xml_child(globals.acct_end_configs, "fields")) == NULL ) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not find 'fields' section in config file.\n");
@ -724,8 +900,10 @@ switch_status_t mod_xml_radius_accounting_end(switch_core_session_t *session){
}
end:
rc_destroy(new_handle);
if ( new_handle) {
rc_destroy(new_handle);
}
return SWITCH_STATUS_SUCCESS;
}

View File

@ -22,10 +22,48 @@
<param vendor="Cisco" name="Cisco-AVPair" variable="ip" format="src-gw-ip=%s"/>
<param vendor="Cisco" name="Cisco-AVPair" variable="sip_from_user" format="src-gw-name=%s"/>
<param vendor="Cisco" name="h323-conf-id" variable="Core-UUID" format="%s"/>
<param vendor="Cisco" name="Cisco-AVPair" variable="ip" format="number"/>
<param name="Called-Station-Id" variable="sip_to_user" format="%s"/>
<param name="Calling-Station-Id" variable="sip_from_user" format="%s"/>
<param name="User-Name" variable="sip_from_user" format="%s"/>
<param name="Digest-Response" variable="sip_auth_response" format="%s"/>
<param name="Digest-Realm" variable="sip_auth_realm" format="%s"/>
<param name="Digest-Nonce" variable="sip_auth_nonce" format="%s"/>
<param name="Digest-Username" variable="sip_auth_username" format="%s"/>
<param name="Digest-URI" variable="sip_auth_uri" format="%s"/>
<param name="Digest-Method" variable="sip_auth_method" format="%s"/>
<param name="Digest-Algorithm" variable="sip_auth_method" format="MD5"/>
<param name="Digest-Qop" variable="sip_auth_qop" format="%s"/>
<param name="Digest-CNonce" variable="sip_auth_cnonce" format="%s"/>
<param name="Digest-Nonce-Count" variable="sip_auth_nc" format="%s"/>
</fields>
</auth_invite>
<auth_reg>
<connection name="testing">
<param name="authserver" value="127.0.0.1:1812:testing123"/>
<param name="radius_timeout" value="10"/>
<param name="radius_retries" value="2"/>
<param name="radius_deadtime" value="0"/>
<param name="dictionary" value="/usr/local/src/freeswitch/src/mod/xml_int/mod_xml_radius/dictionaries/dictionary"/>
<param name="seqfile" value="/var/run/radius.seq"/>
</connection>
<fields>
<param vendor="Cisco" name="Cisco-AVPair" variable="ip" format="user"/>
<param vendor="Cisco" name="Cisco-AVPair" variable="ip" format="src-gw-ip=%s"/>
<param vendor="Cisco" name="Cisco-AVPair" variable="sip_from_user" format="src-gw-name=%s"/>
<param name="User-Name" variable="sip_from_user" format="%s"/>
<param name="Digest-Response" variable="sip_auth_response" format="%s"/>
<param name="Digest-Realm" variable="sip_auth_realm" format="%s"/>
<param name="Digest-Nonce" variable="sip_auth_nonce" format="%s"/>
<param name="Digest-Username" variable="sip_auth_username" format="%s"/>
<param name="Digest-URI" variable="sip_auth_uri" format="%s"/>
<param name="Digest-Method" variable="sip_auth_method" format="%s"/>
<param name="Digest-Algorithm" variable="sip_auth_method" format="MD5"/>
<param name="Digest-Qop" variable="sip_auth_qop" format="%s"/>
<param name="Digest-CNonce" variable="sip_auth_cnonce" format="%s"/>
<param name="Digest-Nonce-Count" variable="sip_auth_nc" format="%s"/>
</fields>
</auth_reg>
<auth_app>
<connection name="testing">
<param name="authserver" value="127.0.0.1:1812:testing123"/>
@ -36,7 +74,7 @@
<param name="seqfile" value="/var/run/radius.seq"/>
</connection>
<fields>
<param vendor="Cisco" name="Cisco-AVPair" variable="sip_network_ip" format="src-gw-ip=%s"/>
<param vendor="Cisco" name="Cisco-AVPair" variable="sip_from_host" format="src-gw-ip=%s"/>
<param vendor="Cisco" name="Cisco-AVPair" variable="sip_from_user" format="src-gw-name=%s"/>
<param vendor="Cisco" name="h323-conf-id" variable="Core-UUID" format="%s"/>
<param name="Called-Station-Id" variable="sip_to_user" format="%s"/>
@ -67,18 +105,19 @@
<fields>
<param vendor="Cisco" name="Cisco-AVPair" variable="sip_from_host" format="src-gw-ip=%s"/>
<param vendor="Cisco" name="Cisco-AVPair" variable="sip_from_user" format="src-gw-name=%s"/>
<param vendor="Cisco" name="Cisco-AVPair" variable="sip_to_host" format="dst-gw-ip=%s"/>
<param vendor="Cisco" name="h323-conf-id" variable="uuid" format="%s"/>
<param vendor="Cisco" name="Cisco-AVPair" variable="sip_to_host" format="dst-gw-ip=%s"/>
<param vendor="Cisco" name="h323-conf-id" variable_secondary="uuid" variable="originating_leg_uuid" format="%s"/>
<param vendor="Cisco" name="h323-setup-time"/>
<param vendor="Cisco" name="h323-connect-time"/>
<param vendor="Cisco" name="h323-call-origin" variable="h323-call-origin" default="answer" format="%s"/>
<param name="Called-Station-Id" variable="sip_to_user" format="%s"/>
<param name="Calling-Station-Id" variable="sip_from_user" format="%s"/>
</fields>
<conditions>
<!-- <conditions>
<condition>
<param var="direction" regex="inbound"/>
</condition>
</conditions>
<param var="direction" regex="^outbound$"/>
</condition>
</conditions> -->
</acct_start>
<acct_end>
<connection name="testing">
@ -92,22 +131,23 @@
<fields>
<param vendor="Cisco" name="Cisco-AVPair" variable="sip_from_host" format="src-gw-ip=%s"/>
<param vendor="Cisco" name="Cisco-AVPair" variable="sip_from_user" format="src-gw-name=%s"/>
<param vendor="Cisco" name="Cisco-AVPair" variable="sip_to_host" format="dst-gw-ip=%s"/>
<param vendor="Cisco" name="h323-conf-id" variable="uuid" format="%s"/>
<param vendor="Cisco" name="Cisco-AVPair" variable="sip_to_host" format="dst-gw-ip=%s"/>
<param vendor="Cisco" name="h323-conf-id" variable_secondary="uuid" variable="originating_leg_uuid" format="%s"/>
<param vendor="Cisco" name="h323-setup-time"/>
<param vendor="Cisco" name="h323-connect-time"/>
<param vendor="Cisco" name="h323-disconnect-time"/>
<param vendor="Cisco" name="h323-disconnect-cause"/>
<param vendor="Cisco" name="h323-call-origin" variable="h323-call-origin" format="%s" default="answer"/>
<param name="Called-Station-Id" variable="sip_to_user" format="%s"/>
<param name="Acct-Session-Time" variable="billsec" format="%s"/>
<param name="Calling-Station-Id" variable="sip_from_user" format="%s"/>
<param vendor="Cisco" name="Cisco-AVPair" variable="sip_from_user" format="src-number-out=%s"/>
<param vendor="Cisco" name="Cisco-AVPair" variable="sip_to_user" format="dst-number-out=%s"/>
</fields>
<conditions>
<!-- <conditions>
<condition>
<param var="direction" regex="inbound"/>
</condition>
</conditions>
<param var="direction" regex="^outbound$"/>
</condition>
</conditions> -->
</acct_end>
</configuration>