diff -uraN asterisk-1.4.8/cdr/cdr_radius.c asterisk-1.4.8-radius-1.0a/cdr/cdr_radius.c --- asterisk-1.4.8/cdr/cdr_radius.c 2007-06-15 01:50:40.000000000 +0400 +++ asterisk-1.4.8-radius-1.0a/cdr/cdr_radius.c 2007-07-25 04:58:22.000000000 +0400 @@ -112,10 +112,16 @@ if (!rc_avpair_add(rh, send, PW_AST_SRC, &cdr->src, strlen(cdr->src), VENDOR_CODE)) return -1; + if (!rc_avpair_add(rh, send, PW_CALLING_STATION_ID, &cdr->src, strlen(cdr->src), 0)) + return -1; + /* Destination */ if (!rc_avpair_add(rh, send, PW_AST_DST, &cdr->dst, strlen(cdr->dst), VENDOR_CODE)) return -1; + if (!rc_avpair_add(rh, send, PW_CALLED_STATION_ID, &cdr->dst, strlen(cdr->dst), 0)) + return -1; + /* Destination context */ if (!rc_avpair_add(rh, send, PW_AST_DST_CTX, &cdr->dcontext, strlen(cdr->dcontext), VENDOR_CODE)) return -1; @@ -176,6 +182,9 @@ if (!rc_avpair_add(rh, send, PW_AST_BILL_SEC, &cdr->billsec, 0, VENDOR_CODE)) return -1; + if (!rc_avpair_add(rh, send, PW_ACCT_SESSION_TIME, &cdr->billsec, 0, 0)) + return -1; + /* Disposition */ tmp = ast_cdr_disp2str(cdr->disposition); if (!rc_avpair_add(rh, send, PW_AST_DISPOSITION, tmp, strlen(tmp), VENDOR_CODE)) @@ -208,9 +217,38 @@ if (!rc_avpair_add(rh, send, PW_ACCT_SESSION_ID, &cdr->uniqueid, strlen(cdr->uniqueid), 0)) return -1; + ast_localtime(&(cdr->answer.tv_sec), &tm, NULL); + strftime(timestr, sizeof(timestr), "h323-setup-time=*%T.000 %Z %a %b %d %Y", &tm); + if(!rc_avpair_add(rh, send, 25, timestr, strlen(timestr), 9)) return -1; + return 0; } +static int send_start(struct ast_cdr *cdr) +{ + int result = ERROR_RC; + VALUE_PAIR *send = NULL; + + int recordtype = PW_STATUS_START; + char timestr[128]; + struct tm tm; + + if(!rc_avpair_add(rh, &send, PW_ACCT_STATUS_TYPE, &recordtype, 0, 0)) return -1; + if(!rc_avpair_add(rh, &send, PW_ACCT_SESSION_TIME, &cdr->billsec, 0, 0)) return -1; + if(!rc_avpair_add(rh, &send, PW_CALLING_STATION_ID, &cdr->src, strlen(cdr->src), 0)) return -1; + if(!rc_avpair_add(rh, &send, PW_CALLED_STATION_ID, &cdr->dst, strlen(cdr->dst), 0)) return -1; + if(!rc_avpair_add(rh, &send, PW_USER_NAME, &cdr->src, strlen(cdr->src), 0)) return -1; + if(!rc_avpair_add(rh, &send, PW_ACCT_SESSION_ID, &cdr->uniqueid, strlen(cdr->uniqueid), 0)) return -1; + if(!rc_avpair_add(rh, &send, 24, &cdr->uniqueid, strlen(cdr->uniqueid), 9)) return -1; + ast_localtime(&(cdr->answer.tv_sec), &tm, NULL); + strftime(timestr, sizeof(timestr), "h323-setup-time=*%T.000 %Z %a %b %d %Y", &tm); + if(!rc_avpair_add(rh, &send, 25, timestr, strlen(timestr), 9)) return -1; + + result = rc_acct(rh, 0, send); + + return result; +} + static int radius_log(struct ast_cdr *cdr) { int result = ERROR_RC; @@ -222,6 +260,7 @@ return result; } + send_start(cdr); result = rc_acct(rh, 0, send); if (result != OK_RC) ast_log(LOG_ERROR, "Failed to record Radius CDR record!\n"); diff -uraN asterisk-1.4.8/channels/chan_sip.c asterisk-1.4.8-radius-1.0a/channels/chan_sip.c --- asterisk-1.4.8/channels/chan_sip.c 2007-07-13 00:42:08.000000000 +0400 +++ asterisk-1.4.8-radius-1.0a/channels/chan_sip.c 2007-07-25 06:00:32.000000000 +0400 @@ -140,6 +140,7 @@ #include "asterisk/linkedlists.h" #include "asterisk/stringfields.h" #include "asterisk/monitor.h" +#include "asterisk/auth.h" #include "asterisk/localtime.h" #include "asterisk/abstract_jb.h" #include "asterisk/compiler.h" @@ -536,6 +537,8 @@ static int srvlookup; /*!< SRV Lookup on or off. Default is off, RFC behavior is on */ static int pedanticsipchecking; /*!< Extra checking ? Default off */ static int autocreatepeer; /*!< Auto creation of peers at registration? Default off. */ +static int external_peers; +static char external_method[64]; static int global_relaxdtmf; /*!< Relax DTMF */ static int global_rtptimeout; /*!< Time out call if no RTP */ static int global_rtpholdtimeout; @@ -1006,6 +1009,8 @@ struct sip_pvt *next; /*!< Next dialog in chain */ struct sip_invite_param *options; /*!< Options for INVITE */ int autoframing; + char max_dialog_time[255]; + char balance[255]; } *iflist = NULL; #define FLAG_RESPONSE (1 << 0) @@ -8110,11 +8115,13 @@ const char *authtoken; char a1_hash[256]; char resp_hash[256]=""; - char tmp[BUFSIZ * 2]; /* Make a large enough buffer */ - char *c; + char tmp[BUFSIZ * 2], tmp1[BUFSIZ * 2]; /* Make a large enough buffer */ + char *c, *to; int wrongnonce = FALSE; int good_response; const char *usednonce = p->randdata; + char pwd[256]; /* used by res_auth */ + struct ast_flags res = {0}; /* used by res_auth */ /* table of recognised keywords, and their value in the digest */ enum keys { K_RESP, K_URI, K_USER, K_NONCE, K_LAST }; @@ -8191,6 +8198,12 @@ strsep(&c, " ,"); } + ast_copy_string(tmp1, get_header(req, "To"), sizeof(tmp)); + c = tmp1; + + strsep(&c,":"); + to = strsep(&c,"@"); + /* Verify that digest username matches the username we auth as */ if (strcmp(username, keys[K_USER].s)) { ast_log(LOG_WARNING, "username mismatch, have <%s>, digest has <%s>\n", @@ -8199,6 +8212,82 @@ return AUTH_USERNAME_MISMATCH; } + + res.flags = ast_parse_secret(username, secret, pwd); + + /* password has been retrieved from config file, or from + an external source. Let's now process it */ + if (ast_test_flag(&res, PW_SET)) { + char a1[256]; + + if (ast_test_flag(&res, PW_FILE)) { + ast_log(LOG_DEBUG, "Doing LOCAL authentication for user '%s'\n", username); + } + snprintf(a1, sizeof(a1), "%s:%s:%s", username, global_realm, pwd); + ast_md5_hash(a1_hash, a1); + + } + /* no password retrieved, let's try to directly authenticate + the user */ + + /* With RADIUS SIP DIGEST authentication + (draft-sterman-aaa-sip-00.txt etc) */ + else if (ast_test_flag(&res, AUTH_RADIUS)) { + int retval; + const char* user = username; + struct digest_parameters params; + digest_attr_t aux; + + ast_log(LOG_NOTICE, "Doing RADIUS authentication for user '%s'\n", username); + + memset(¶ms, 0, sizeof(params)); + + /* Build up the params structure with the attributes we got after having parsed the URI */ + params.num_attrs = 0; + ast_copy_string(params.digest_response, keys[K_RESP].s, sizeof(params.digest_response)); + + + /* realm attribute */ + aux.type = PW_DIGEST_REALM; + aux.length = strlen(global_realm); + ast_copy_string(aux.value, global_realm, sizeof(aux.value)); + ast_push_digest_attribute(¶ms, &aux); + + /* nonce attribute */ + aux.type = PW_DIGEST_NONCE; + aux.length = strlen(keys[K_NONCE].s); + ast_copy_string(aux.value, keys[K_NONCE].s, sizeof(aux.value)); + ast_push_digest_attribute(¶ms, &aux); + + /* method attribute */ + aux.type = PW_DIGEST_METHOD; + aux.length = strlen(sip_methods[sipmethod].text); + ast_copy_string(aux.value, sip_methods[sipmethod].text, sizeof(aux.value)); + ast_push_digest_attribute(¶ms, &aux); + + /* uri attribute */ + aux.type = PW_DIGEST_URI; + aux.length = strlen(keys[K_URI].s); + ast_copy_string(aux.value, keys[K_URI].s, sizeof(aux.value)); + ast_push_digest_attribute(¶ms, &aux); + + /* algo attribute */ + aux.type = PW_DIGEST_ALGORITHM; + aux.length = strlen("MD5"); + ast_copy_string(aux.value, "MD5", sizeof("MD5")); + ast_push_digest_attribute(¶ms, &aux); + + /* Username attribute */ + aux.type = PW_DIGEST_USER_NAME; + aux.length = strlen(keys[K_USER].s); + ast_copy_string(aux.value, keys[K_USER].s, sizeof(aux.value)); + ast_push_digest_attribute(¶ms, &aux); + + retval = ast_digest_authenticate(user, to, ¶ms, p->max_dialog_time, p->balance); + return retval; + + } + /* Verify nonce from request matches our nonce. If not, send 401 with new nonce */ if (strcasecmp(p->randdata, keys[K_NONCE].s)) { /* XXX it was 'n'casecmp ? */ wrongnonce = TRUE; @@ -8451,6 +8540,47 @@ } } } + if (!peer && external_peers && external_method[0] != '\0') { + /* Create peer if we have external_peers mode enabled */ + peer = temp_peer(name); + if (peer) { + ast_copy_string(peer->secret, external_method, sizeof(peer->secret)); + int pos = strlen(peer->secret); + peer->secret[pos+1]='\0'; + peer->secret[pos]=':'; + ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_NAT); + transmit_response(p, "100 Trying", req); + if (!(res = check_auth(p, req, peer->name, peer->secret, peer->md5secret, SIP_REGISTER, uri, XMIT_UNRELIABLE, ast_test_flag(req, SIP_PKT_IGNORE)))) { + ASTOBJ_CONTAINER_LINK(&peerl, peer); + sip_cancel_destroy(p); + + /* We have a successful registration attempt with proper authentication, + now, update the peer */ + switch (parse_register_contact(p, peer, req)) { + case PARSE_REGISTER_FAILED: + ast_log(LOG_WARNING, "Failed to parse contact info\n"); + transmit_response_with_date(p, "400 Bad Request", req); + peer->lastmsgssent = -1; + res = 0; + break; + case PARSE_REGISTER_QUERY: + transmit_response_with_date(p, "200 OK", req); + peer->lastmsgssent = -1; + res = 0; + break; + case PARSE_REGISTER_UPDATE: + /* Say OK and ask subsystem to retransmit msg counter */ + transmit_response_with_date(p, "200 OK", req); + manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: SIP/%s\r\nPeerStatus: Registered\r\n", peer->name); + peer->lastmsgssent = -1; + res = 0; + break; + } + }/*else{ + sip_destroy_peer(peer); + }*/ + } + } if (!res) { ast_device_state_changed("SIP/%s", peer->name); } @@ -10363,6 +10493,8 @@ ast_cli(fd, " Bindaddress: %s\n", ast_inet_ntoa(bindaddr.sin_addr)); ast_cli(fd, " Videosupport: %s\n", ast_test_flag(&global_flags[1], SIP_PAGE2_VIDEOSUPPORT) ? "Yes" : "No"); ast_cli(fd, " AutoCreatePeer: %s\n", autocreatepeer ? "Yes" : "No"); + ast_cli(fd, " External Peers: %s\n", external_peers ? "Yes" : "No"); + ast_cli(fd, " External Method: %s\n", external_method); ast_cli(fd, " Allow unknown access: %s\n", global_allowguest ? "Yes" : "No"); ast_cli(fd, " Allow subscriptions: %s\n", ast_test_flag(&global_flags[1], SIP_PAGE2_ALLOWSUBSCRIBE) ? "Yes" : "No"); ast_cli(fd, " Allow overlap dialing: %s\n", ast_test_flag(&global_flags[1], SIP_PAGE2_ALLOWOVERLAP) ? "Yes" : "No"); @@ -13608,6 +13740,9 @@ if (c) { /* We have a call -either a new call or an old one (RE-INVITE) */ + if (p->max_dialog_time) pbx_builtin_setvar_helper(c, "MAX_DIALOG_TIME", p->max_dialog_time); + if (p->balance) pbx_builtin_setvar_helper(c, "BALANCE", p->balance); + switch(c->_state) { case AST_STATE_DOWN: if (option_debug > 1) @@ -16414,6 +16549,8 @@ pedanticsipchecking = DEFAULT_PEDANTIC; global_mwitime = DEFAULT_MWITIME; autocreatepeer = DEFAULT_AUTOCREATEPEER; + external_peers = 0; + external_method[0] = '\0'; global_autoframing = 0; global_allowguest = DEFAULT_ALLOWGUEST; global_rtptimeout = 0; @@ -16565,6 +16702,10 @@ outboundproxyip.sin_port = htons(format); } else if (!strcasecmp(v->name, "autocreatepeer")) { autocreatepeer = ast_true(v->value); + } else if (!strcasecmp(v->name, "external_peers")) { + external_peers = ast_true(v->value); + } else if (!strcasecmp(v->name, "external_method")) { + ast_copy_string(external_method, v->value, sizeof(external_method)-1); } else if (!strcasecmp(v->name, "srvlookup")) { srvlookup = ast_true(v->value); } else if (!strcasecmp(v->name, "pedantic")) { diff -uraN asterisk-1.4.8/include/asterisk/auth.h asterisk-1.4.8-radius-1.0a/include/asterisk/auth.h --- asterisk-1.4.8/include/asterisk/auth.h 1970-01-01 03:00:00.000000000 +0300 +++ asterisk-1.4.8-radius-1.0a/include/asterisk/auth.h 2007-07-25 04:58:22.000000000 +0400 @@ -0,0 +1,61 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2006, Digium, Inc. + * + * Mark Spencer + * + * 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 User Authentication Module + */ + +#define MAX_PW_LEN 128 + +enum { + AUTH_ERR = -1, + AUTH_LOCAL = (1 << 0), + AUTH_PAM = (1 << 2), + AUTH_RADIUS = (1 << 3) +}; + +enum { + PW_SET = (1 << 4), + PW_FILE = (1 << 5), +}; + +int ast_parse_secret(const char*, const char *, char *); + +#ifdef HAVE_RADIUS +#include + +#define MAX_DIGEST_ATTRS 10 /* The maximum number of digest attributes in the RADIUS request we might send */ + +typedef struct digest_attr { + unsigned short type; + char length; + char value[256]; +} digest_attr_t; + +struct digest_parameters { + char digest_response[33]; + int num_attrs; + digest_attr_t d_attrs_list[MAX_DIGEST_ATTRS]; +}; + +int ast_push_digest_attribute(struct digest_parameters* , digest_attr_t*); + +int ast_digest_authenticate(const char*, const char*, struct digest_parameters *, char*, char*); + +#endif /* HAVE_RADIUS */ + diff -uraN asterisk-1.4.8/res/res_auth.c asterisk-1.4.8-radius-1.0a/res/res_auth.c --- asterisk-1.4.8/res/res_auth.c 1970-01-01 03:00:00.000000000 +0300 +++ asterisk-1.4.8-radius-1.0a/res/res_auth.c 2007-07-25 04:58:22.000000000 +0400 @@ -0,0 +1,333 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2006, Digium, Inc. + * + * Mark Spencer + * + * 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 User Authentication Module + * + */ + +/*** MODULEINFO + radius + ***/ + + +#include "asterisk.h" + +#include +#include +#include +#include +#include + + +ASTERISK_FILE_VERSION(__FILE__, "$Rev: 45739 $") + +#include "asterisk/logger.h" +#include "asterisk/config.h" +#include "asterisk/pbx.h" +#include "asterisk/module.h" +#include "asterisk/cli.h" +#include "asterisk/lock.h" +#include "asterisk/md5.h" +#include "asterisk/astobj.h" +#include "asterisk/utils.h" +#include "asterisk/auth.h" + +#define RES_AUTH_CONF "auth.conf" + +#ifdef HAVE_RADIUS +AST_MUTEX_DEFINE_STATIC(rc_lock); +#define RC_CONFIG_FILE "/etc/radiusclient-ng/radiusclient.conf" +static rc_handle *rh = NULL; +#endif /* HAVE_RADIUS */ + +/*! \brief Parses the secret configuration string for Asterisk resources + The user credentials format should be in the form : + :[auth_db:[password]] + + 'auth_proxy' indicates the actual location of the authentication process. + Asterisk may retrieve user passwords from an external database depending + on the value of 'auth_db', and then authenticate the users. Alternatively, + Asterisk can proxy the authentication to an external server such as RADIUS. + Values for this parameter are : + local: if the authenticaiton process runs on Asterisk + radius: if an external RADIUS server is used for authentication + pam: -- for future use -- + + 'auth_db' refers to the database/protocol where user authentication + information can be pulled from, in case the authentication process runs + on Asterisk. + Values for this parameter are : + file: if the password is stored in the matching configuration file + (ex. sip.conf) + + 'password' : the password string, or its MD5 hash (for SIP users) + + \param username the user we want to authenticate + \param secret the string that needs parsing + \param pwd set to the fetch password if applicable, NULL instead + + \return a set of flags that needs to be interpreted by the resource + resquesting authentication +*/ +int ast_parse_secret(const char *username, const char *secret, char *pwd) +{ + char buf[256]; + char *auth_proxy = NULL; + char *auth_db = NULL; + char *password = NULL; + unsigned int res = 0; + struct ast_flags locflags = {0}; + + ast_copy_string(buf, secret, sizeof(buf)); + + auth_proxy = buf; + + /* split the auth string into pieces */ + auth_db = strchr(buf, ':'); + + if (auth_db) { + *auth_db = '\0'; + auth_db++; + password = strchr(auth_db, ':'); + if (password) { + *password = '\0'; + password ++; + } + } + if (!ast_strlen_zero(auth_proxy)) + ast_log(LOG_DEBUG, "auth_proxy : %s\n", auth_proxy); + if (!ast_strlen_zero(auth_db)) + ast_log(LOG_DEBUG, "auth_db : %s\n", auth_db); + if (!ast_strlen_zero(password)) + ast_log(LOG_DEBUG, "password : %s\n", password); + + if (!auth_db) { + /* let's kindly accept old formatted secrets */ + ast_log(LOG_DEBUG, "Old style password : %s\n", secret); + ast_copy_string(pwd, secret, strlen(secret) + 1); + ast_set_flag(&locflags, AUTH_LOCAL); + ast_set_flag(&locflags, PW_SET); + ast_set_flag(&locflags, PW_FILE); + + res = locflags.flags; + return res; + } + + /* process 'auth_proxy' string. Return if authentication is proxied */ + if (!ast_strlen_zero(auth_proxy)) { + if (!strcasecmp(auth_proxy, "radius")) { +#ifdef HAVE_RADIUS + ast_set_flag(&locflags, AUTH_RADIUS); + return res = locflags.flags; +#else + ast_log(LOG_ERROR, "radiusclient-ng library was not found. Cannot use RADIUS to authenticate users\n"); + return res = AUTH_ERR; +#endif /* HAVE_RADIUS */ + } + else if (!strcasecmp(auth_proxy, "pam")) { + ast_set_flag(&locflags, AUTH_PAM); + return res = locflags.flags; + } + else if (!strcasecmp(auth_proxy, "local")) { + ast_set_flag(&locflags, AUTH_LOCAL); + } + else { + res = AUTH_ERR; + return res; + } + } + + /* check auth_db */ + if (!ast_strlen_zero(auth_db)) { + /* password set in config file */ + if (!strcasecmp(auth_db, "file")) { + ast_set_flag(&locflags, PW_FILE); + if (!ast_strlen_zero(password)) { + /* fill pwd and inform that password is set */ + ast_copy_string(pwd, password, strlen(password) + 1); + ast_set_flag(&locflags, PW_SET); + } + else { + ast_log(LOG_ERROR, "Password is missing.\n"); + res = AUTH_ERR; + } + } + } + + res = locflags.flags; + return res; +} + +#ifdef HAVE_RADIUS + +/*!\brief A utility function that helps filling correctly a digest_parameters + structure +*/ +int ast_push_digest_attribute(struct digest_parameters *params, digest_attr_t *attr) +{ + params->d_attrs_list[params->num_attrs].type = attr->type; + params->d_attrs_list[params->num_attrs].length = attr->length; + strncpy(params->d_attrs_list[params->num_attrs].value, attr->value, sizeof(attr->value)); + params->num_attrs ++; + return 1; +} + +/*!\brief Authenticate using the radiusclient-ng library + Acting as a RADIUS client, Asterisk sends a RADIUS authentication request + to a RADIUS server, and waits for the answer. + + The RADIUS client configuration file location is set in RC_CONFIG_FILE + + \param user the user we want to authenticate + \param params a structure that contains the RADIUS attributes needed + to build the authentication request + + \return -1 on error, 0 on authentication success + */ +int ast_digest_authenticate(const char* user, const char* to, struct digest_parameters *params, char *max_dialog_time, char *balance) +{ + int res; + int i; + struct digest_parameters aux; + VALUE_PAIR *send, *received; + char msg[4096]; + char tmp[256]; + char *ptmp; + + + ast_mutex_lock(&rc_lock); + + send = NULL; + + aux.num_attrs = params->num_attrs; + + /* Add a User-Name attribute */ + if (rc_avpair_add(rh, &send, PW_USER_NAME, (char *)user, -1, 0) == NULL) { + return -1; + } + + + if (to && strcmp(to,user)) + if (rc_avpair_add(rh, &send, PW_CALLED_STATION_ID, (char *)to, -1, 0) == NULL) { + return -1; + } + + /* Fill in Digest-Response */ + ast_copy_string(aux.digest_response, params->digest_response, sizeof(aux.digest_response)); + if (rc_avpair_add(rh, &send, PW_DIGEST_RESPONSE, aux.digest_response, 32, 0) == NULL) { + return -1; + } + + + /* Fill in Digest attributes */ + for (i = 0; i < aux.num_attrs; i++) { + aux.d_attrs_list[i].type = params->d_attrs_list[i].type; + aux.d_attrs_list[i].length = params->d_attrs_list[i].length; + ast_copy_string(aux.d_attrs_list[i].value, params->d_attrs_list[i].value, sizeof(params->d_attrs_list[i].value)); + + /* Add the digest attribute to the RADIUS request */ + if (rc_avpair_add(rh, &send, aux.d_attrs_list[i].type, aux.d_attrs_list[i].value, aux.d_attrs_list[i].length, 0) == NULL) { + return -1; + } + + } + + + res = rc_auth(rh, 0, send, &received, msg); + + + for(i = 0; i < sizeof(tmp); i++) tmp[i]='\0'; + + if(res != NULL){ + if(rc_avpair_tostr(rh, rc_avpair_get(received,102,9), msg, sizeof(msg), tmp, sizeof(tmp)) == NULL){ + ptmp=tmp; + strsep(&ptmp,"="); + ast_copy_string(max_dialog_time, ptmp, sizeof(tmp)); + } + if(rc_avpair_tostr(rh, rc_avpair_get(received,101,9), msg, sizeof(msg), tmp, sizeof(tmp)) == NULL){ + ptmp=tmp; + strsep(&ptmp,"="); + ast_copy_string(balance, ptmp, sizeof(tmp)); + ast_verbose(VERBOSE_PREFIX_3 "Balance - %s\n", balance); + } + } + + /* free resources */ + rc_avpair_free(send); + if (received) + rc_avpair_free(received); + + ast_mutex_unlock(&rc_lock); + + /* send result */ + return res; +} +#endif /* HAVE_RADIUS */ + +static int parse_config(void) +{ + const struct ast_config *config; + + config = ast_config_load(RES_AUTH_CONF); + + ast_config_destroy(config); + return 1; +} + + +static int unload_module(void) +{ + ast_log(LOG_NOTICE, "res_auth unloaded.\n"); + return 0; +} + +static int load_module(void) +{ + + parse_config(); + +#ifdef HAVE_RADIUS + /* start logging */ + rc_openlog("asterisk"); + + /* read radiusclient-ng config file */ + if ((rh = rc_read_config(RC_CONFIG_FILE)) == NULL) { + ast_log(LOG_ERROR, "Cannot load radiusclient-ng configuration file %s.\n", RC_CONFIG_FILE); + return -1; + } + + /* read radiusclient-ng dictionaries */ + if (rc_read_dictionary(rh, rc_conf_str(rh, "dictionary")) != 0) { + ast_log(LOG_ERROR, "Cannot load radiusclient-ng dictionary file.\n"); + return -1; + } + +#endif /* HAVE_RADIUS */ + + + ast_log(LOG_NOTICE, "res_auth loaded.\n"); + return 0; +} + + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Authentication resource", + .load = load_module, + .unload = unload_module, + );