2006-03-22 18:22:34 +00:00
|
|
|
/**
|
|
|
|
* yradius.cpp
|
|
|
|
* This file is part of the YATE Project http://YATE.null.ro
|
|
|
|
*
|
|
|
|
* RADIUS Client functionality for YATE
|
2012-04-12 17:33:11 +00:00
|
|
|
*
|
2006-03-22 18:22:34 +00:00
|
|
|
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
2006-05-27 15:08:43 +00:00
|
|
|
* Copyright (C) 2004-2006 Null Team
|
2006-03-22 18:22:34 +00:00
|
|
|
*
|
|
|
|
* Largely based on the code sent by Faizan Naqvi (Tili)
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
2006-05-27 15:08:43 +00:00
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
2006-03-22 18:22:34 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include <yatephone.h>
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <math.h>
|
|
|
|
|
|
|
|
using namespace TelEngine;
|
2006-05-27 14:53:18 +00:00
|
|
|
namespace { // anonymous
|
2006-03-22 18:22:34 +00:00
|
|
|
|
|
|
|
#define RADIUS_MAXLEN 4096
|
|
|
|
|
2012-04-12 17:33:11 +00:00
|
|
|
enum
|
2006-03-22 18:22:34 +00:00
|
|
|
{
|
|
|
|
RadChanStart,
|
|
|
|
RadChanStop,
|
|
|
|
RadCallAnswered,
|
|
|
|
RadCallAuth,
|
|
|
|
RadCallExecute,
|
|
|
|
RadCallRinging,
|
|
|
|
RadUserLogin
|
2012-04-12 17:33:11 +00:00
|
|
|
};
|
2006-03-22 18:22:34 +00:00
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
NoError = 0,
|
|
|
|
AcctSuccess = NoError,
|
|
|
|
AuthSuccess = NoError,
|
|
|
|
AuthFailed,
|
|
|
|
ServerErr,
|
|
|
|
ConfErr,
|
|
|
|
UnknownErr,
|
|
|
|
};
|
|
|
|
|
|
|
|
static Configuration s_cfg;
|
2009-05-05 14:06:39 +00:00
|
|
|
static Mutex s_cfgMutex("YRadius::cfg");
|
2006-03-22 18:22:34 +00:00
|
|
|
static ObjList acctBuilders;
|
|
|
|
static SocketAddr s_localAddr(AF_INET);
|
|
|
|
static Socket s_localSock;
|
|
|
|
static bool s_localTime = false;
|
2006-03-23 03:23:23 +00:00
|
|
|
static bool s_shortnum = false;
|
|
|
|
static bool s_unisocket = false;
|
2011-05-12 18:35:06 +00:00
|
|
|
static bool s_printAttr = false;
|
2006-03-22 18:22:34 +00:00
|
|
|
static bool s_pb_enabled = false;
|
|
|
|
static bool s_pb_parallel = false;
|
|
|
|
static bool s_pb_simplify = false;
|
2012-09-14 15:15:06 +00:00
|
|
|
static bool s_cisco = true;
|
|
|
|
static bool s_quintum = false;
|
2006-04-03 17:52:13 +00:00
|
|
|
static String s_pb_stoperror;
|
|
|
|
static String s_pb_maxcall;
|
2006-03-22 18:22:34 +00:00
|
|
|
|
|
|
|
// Attribute types, not exactly as in RFC 2865
|
|
|
|
enum {
|
|
|
|
a_void = 0, // invalid, used in separators
|
|
|
|
a_binary, // rfc2865 string
|
|
|
|
a_string, // rfc2865 text
|
|
|
|
a_ipaddr, // rfc2865 address
|
|
|
|
a_int, // rfc2865 integer
|
|
|
|
a_date, // rfc2865 time
|
|
|
|
a_avpair // special text formatted as name=value
|
|
|
|
};
|
2012-04-12 17:33:11 +00:00
|
|
|
|
2006-03-22 18:22:34 +00:00
|
|
|
// Structure for building attribute tables
|
|
|
|
typedef struct {
|
|
|
|
int code;
|
|
|
|
const char* name;
|
2012-09-14 15:15:06 +00:00
|
|
|
const char* confName;
|
2006-03-22 18:22:34 +00:00
|
|
|
int type;
|
|
|
|
} rad_dict;
|
|
|
|
|
|
|
|
// Structure for building pointers to standard or vendor tables
|
|
|
|
typedef struct {
|
|
|
|
int vendor;
|
|
|
|
const char* name;
|
|
|
|
const rad_dict* dict;
|
|
|
|
} rad_vendor;
|
|
|
|
|
|
|
|
|
|
|
|
// Standard or vendor-specific RADIUS values and tables
|
|
|
|
|
|
|
|
// RADIUS packet codes
|
|
|
|
enum {
|
|
|
|
Access_Request = 1,
|
|
|
|
Access_Accept = 2,
|
|
|
|
Access_Reject = 3,
|
|
|
|
Accounting_Request = 4,
|
|
|
|
Accounting_Response = 5,
|
|
|
|
Access_Challenge = 11,
|
|
|
|
// experimental codes
|
|
|
|
Status_Server = 12,
|
|
|
|
Status_Client = 13,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Accounting status types
|
|
|
|
enum {
|
|
|
|
Acct_Start = 1,
|
|
|
|
Acct_Stop = 2,
|
|
|
|
Acct_Alive = 3,
|
|
|
|
Acct_On = 7,
|
|
|
|
Acct_Off = 8,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Subattribute type for Digest-Attributes - see draft-sterman-aaa-sip-00.txt
|
|
|
|
enum {
|
|
|
|
Digest_Realm = 1,
|
|
|
|
Digest_Nonce = 2,
|
|
|
|
Digest_Method = 3,
|
|
|
|
Digest_URI = 4,
|
|
|
|
Digest_QOP = 5,
|
|
|
|
Digest_Algo = 6,
|
|
|
|
Digest_Body = 7,
|
|
|
|
Digest_CNonce = 8,
|
|
|
|
Digest_NCount = 9,
|
|
|
|
Digest_UserName = 10
|
|
|
|
};
|
|
|
|
|
|
|
|
// Standard RADIUS attributes
|
|
|
|
static rad_dict radius_dict[] = {
|
2012-09-14 15:15:06 +00:00
|
|
|
{ 1, "User-Name", "User-Name", a_string },
|
|
|
|
{ 2, "User-Password", "User-Password", a_binary },
|
|
|
|
{ 3, "CHAP-Password", "CHAP-Password", a_binary },
|
|
|
|
{ 4, "NAS-IP-Address", "NAS-IP-Address", a_ipaddr },
|
|
|
|
{ 5, "NAS-Port", "NAS-Port", a_int },
|
|
|
|
{ 6, "Service-Type", "Service-Type", a_int },
|
|
|
|
{ 18, "Reply-Message", "Reply-Message", a_string },
|
|
|
|
{ 26, "Vendor-Specific", "Vendor-Specific", a_binary },
|
|
|
|
{ 27, "Session-Timeout", "Session-Timeout", a_int },
|
|
|
|
{ 30, "Called-Station-Id", "Called-Station-Id", a_string },
|
|
|
|
{ 31, "Calling-Station-Id", "Calling-Station-Id", a_string },
|
|
|
|
{ 32, "NAS-Identifier", "NAS-Identifier", a_string },
|
|
|
|
{ 40, "Acct-Status-Type", "Acct-Status-Type", a_int },
|
|
|
|
{ 41, "Acct-Delay-Time", "Acct-Delay-Time", a_int },
|
|
|
|
{ 42, "Acct-Input-Octets", "Acct-Input-Octets", a_int },
|
|
|
|
{ 43, "Acct-Output-Octets", "Acct-Output-Octets", a_int },
|
|
|
|
{ 44, "Acct-Session-Id", "Acct-Session-Id", a_string },
|
|
|
|
{ 45, "Acct-Authentic", "Acct-Authentic", a_int },
|
|
|
|
{ 46, "Acct-Session-Time", "Acct-Session-Time", a_int },
|
|
|
|
{ 47, "Acct-Input-Packets", "Acct-Input-Packets", a_int },
|
|
|
|
{ 48, "Acct-Output-Packets", "Acct-Output-Packets", a_int },
|
|
|
|
{ 49, "Acct-Terminate-Cause", "Acct-Terminate-Cause", a_int },
|
|
|
|
{ 50, "Acct-Multi-Session-Id", "Acct-Multi-Session-Id", a_string },
|
|
|
|
{ 51, "Acct-Link-Count", "Acct-Link-Count", a_int },
|
|
|
|
{ 60, "CHAP-Challenge", "CHAP-Challenge", a_binary },
|
|
|
|
{ 61, "NAS-Port-Type", "NAS-Port-Type", a_int },
|
|
|
|
{ 62, "Port-Limit", "Port-Limit", a_int },
|
|
|
|
{ 63, "Login-LAT-Port", "Login-LAT-Port", a_string },
|
|
|
|
{ 68, "Configuration-Token", "Configuration-Token", a_binary },
|
|
|
|
{ 206, "Digest-Response", "Digest-Response", a_string },
|
|
|
|
{ 207, "Digest-Attributes", "Digest-Attributes", a_binary },
|
|
|
|
{ 0, 0, 0, a_void }
|
2006-03-22 18:22:34 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Cisco vendor attributes
|
|
|
|
static rad_dict cisco_dict[]= {
|
2012-09-14 15:15:06 +00:00
|
|
|
{ 1, "Cisco-AVPair", "Cisco-AVPair", a_string },
|
|
|
|
{ 2, "Cisco-NAS-Port", "Cisco-NAS-Port", a_string },
|
|
|
|
{ 2, "NAS-Port-Name", "NAS-Port-Name", a_string }, // alternate name
|
|
|
|
{ 23, "h323-remote-address", "h323-remote-address", a_avpair },
|
|
|
|
{ 24, "h323-conf-id", "h323-conf-id", a_avpair },
|
|
|
|
{ 25, "h323-setup-time", "h323-setup-time", a_avpair },
|
|
|
|
{ 26, "h323-call-origin", "h323-call-origin", a_avpair },
|
|
|
|
{ 27, "h323-call-type", "h323-call-type", a_avpair },
|
|
|
|
{ 28, "h323-connect-time", "h323-connect-time", a_avpair },
|
|
|
|
{ 29, "h323-disconnect-time", "h323-disconnect-time", a_avpair },
|
|
|
|
{ 30, "h323-disconnect-cause", "h323-disconnect-cause", a_avpair },
|
|
|
|
{ 31, "h323-voice-quality", "h323-voice-quality", a_avpair },
|
|
|
|
{ 33, "h323-gw-id", "h323-gw-id", a_avpair },
|
|
|
|
{ 34, "h323-call-treatment", "h323-call-treatment", a_string },
|
|
|
|
{ 101, "h323-credit-amount", "h323-credit-amount", a_avpair },
|
|
|
|
{ 102, "h323-credit-time", "h323-credit-time", a_avpair },
|
|
|
|
{ 103, "h323-return-code", "h323-return-code", a_avpair },
|
|
|
|
{ 104, "h323-prompt-id", "h323-prompt-id", a_avpair },
|
|
|
|
{ 105, "h323-time-and-day", "h323-time-and-day", a_avpair },
|
|
|
|
{ 106, "h323-redirect-number", "h323-redirect-number", a_avpair },
|
|
|
|
{ 107, "h323-preferred-lang", "h323-preferred-lang", a_avpair },
|
|
|
|
{ 108, "h323-redirect-ip-address", "h323-redirect-ip-address", a_avpair },
|
|
|
|
{ 109, "h323-billing-model", "h323-billing-model", a_avpair },
|
|
|
|
{ 110, "h323-currency", "h323-currency", a_avpair },
|
|
|
|
{ 187, "Cisco-Multilink-ID", "Cisco-Multilink-ID", a_int },
|
|
|
|
{ 188, "Cisco-Num-In-Multilink", "Cisco-Num-In-Multilink", a_int },
|
|
|
|
{ 190, "Cisco-Pre-Input-Octets", "Cisco-Pre-Input-Octets", a_int },
|
|
|
|
{ 191, "Cisco-Pre-Output-Octets", "Cisco-Pre-Output-Octets", a_int },
|
|
|
|
{ 192, "Cisco-Pre-Input-Packets", "Cisco-Pre-Input-Packets", a_int },
|
|
|
|
{ 193, "Cisco-Pre-Output-Packets", "Cisco-Pre-Output-Packets", a_int },
|
|
|
|
{ 194, "Cisco-Maximum-Time", "Cisco-Maximum-Time", a_int },
|
|
|
|
{ 195, "Cisco-Disconnect-Cause", "Cisco-Disconnect-Cause", a_int },
|
|
|
|
{ 197, "Cisco-Data-Rate", "Cisco-Data-Rate", a_int },
|
|
|
|
{ 198, "Cisco-PreSession-Time", "Cisco-PreSession-Time", a_int },
|
|
|
|
{ 208, "Cisco-PW-Lifetime", "Cisco-PW-Lifetime", a_int },
|
|
|
|
{ 209, "Cisco-IP-Direct", "Cisco-IP-Direct", a_int },
|
|
|
|
{ 210, "Cisco-PPP-VJ-Slot-Comp", "Cisco-PPP-VJ-Slot-Comp", a_int },
|
|
|
|
{ 212, "Cisco-PPP-Async-Map", "Cisco-PPP-Async-Map", a_int },
|
|
|
|
{ 217, "Cisco-IP-Pool-Definition", "Cisco-IP-Pool-Definition", a_int },
|
|
|
|
{ 218, "Cisco-Assign-IP-Pool", "Cisco-Assign-IP-Pool", a_int },
|
|
|
|
{ 228, "Cisco-Route-IP", "Cisco-Route-IP", a_int },
|
|
|
|
{ 233, "Cisco-Link-Compression", "Cisco-Link-Compression", a_int },
|
|
|
|
{ 234, "Cisco-Target-Util", "Cisco-Target-Util", a_int },
|
|
|
|
{ 235, "Cisco-Maximum-Channels", "Cisco-Maximum-Channels", a_int },
|
|
|
|
{ 242, "Cisco-Data-Filter", "Cisco-Data-Filter", a_int },
|
|
|
|
{ 243, "Cisco-Call-Filter", "Cisco-Call-Filter", a_int },
|
|
|
|
{ 244, "Cisco-Idle-Limit", "Cisco-Idle-Limit", a_int },
|
|
|
|
{ 255, "Cisco-Xmit-Rate", "Cisco-Xmit-Rate", a_int },
|
|
|
|
{ 0, 0, 0, a_void }
|
|
|
|
};
|
|
|
|
|
|
|
|
static rad_dict quintum_dict[]= {
|
|
|
|
{ 1, "Quintum-AVPair", "Quintum-AVPair", a_string },
|
|
|
|
{ 2, "Tenor-NAS-Port", "Tenor-NAS-Port", a_string },
|
|
|
|
{ 23, "h323-remote-address", "Quintum-h323-remote-address", a_avpair },
|
|
|
|
{ 24, "h323-conf-id", "Quintum-h323-conf-id", a_avpair },
|
|
|
|
{ 25, "h323-setup-time", "Quintum-h323-setup-time", a_avpair },
|
|
|
|
{ 26, "h323-call-origin", "Quintum-h323-call-origin", a_avpair },
|
|
|
|
{ 27, "h323-call-type", "Quintum-h323-call-type", a_avpair },
|
|
|
|
{ 28, "h323-connect-time", "Quintum-h323-connect-time", a_avpair },
|
|
|
|
{ 29, "h323-disconnect-time", "Quintum-h323-disconnect-time", a_avpair },
|
|
|
|
{ 30, "h323-disconnect-cause", "Quintum-h323-disconnect-cause", a_avpair },
|
|
|
|
{ 31, "h323-voice-quality", "Quintum-h323-voice-quality", a_avpair },
|
|
|
|
{ 33, "h323-gw-id", "Quintum-h323-gw-id", a_avpair },
|
|
|
|
{ 101, "h323-credit-amount", "Quintum-h323-credit-amount", a_avpair },
|
|
|
|
{ 102, "h323-credit-time", "Quintum-h323-credit-time", a_avpair },
|
|
|
|
{ 103, "h323-return-code", "Quintum-h323-return-code", a_avpair },
|
|
|
|
{ 104, "h323-prompt-id", "Quintum-h323-prompt-id", a_avpair },
|
|
|
|
{ 106, "h323-redirect-number", "Quintum-h323-redirect-number", a_avpair },
|
|
|
|
{ 107, "h323-preferred-lang", "Quintum-h323-preferred-lang", a_avpair },
|
|
|
|
{ 109, "h323-billing-model", "Quintum-h323-billing-model", a_avpair },
|
|
|
|
{ 110, "h323-currency", "Quintum-h323-currency", a_avpair },
|
|
|
|
{ 230, "Trunkid-In", "Quintum-Trunkid-In", a_string },
|
|
|
|
{ 231, "Trunkid-Out", "Quintum-Trunkid-Out", a_string },
|
|
|
|
{ 0, 0, 0, a_void }
|
2006-03-22 18:22:34 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Microsoft vendor attributes
|
|
|
|
static rad_dict ms_dict[]= {
|
2012-09-14 15:15:06 +00:00
|
|
|
{ 1, "MS-CHAP-Response", "MS-CHAP-Response", a_binary },
|
|
|
|
{ 2, "MS-CHAP-Error", "MS-CHAP-Error", a_binary },
|
|
|
|
{ 3, "MS-CHAP-CPW-1", "MS-CHAP-CPW-1", a_binary },
|
|
|
|
{ 4, "MS-CHAP-CPW-2", "MS-CHAP-CPW-2", a_binary },
|
|
|
|
{ 5, "MS-CHAP-LM-Enc-PW", "MS-CHAP-LM-Enc-PW", a_binary },
|
|
|
|
{ 6, "MS-CHAP-NT-Enc-PW", "MS-CHAP-NT-Enc-PW", a_binary },
|
|
|
|
{ 7, "MS-MPPE-Encryption-Policy", "MS-MPPE-Encryption-Policy", a_binary },
|
|
|
|
{ 8, "MS-MPPE-Encryption-Types", "MS-MPPE-Encryption-Types", a_binary },
|
|
|
|
{ 9, "MS-RAS-Vendor", "MS-RAS-Vendor", a_int },
|
|
|
|
{ 10, "MS-CHAP-Domain", "MS-CHAP-Domain", a_binary },
|
|
|
|
{ 11, "MS-CHAP-Challenge", "MS-CHAP-Challenge", a_binary },
|
|
|
|
{ 12, "MS-CHAP-MPPE-Keys", "MS-CHAP-MPPE-Keys", a_binary },
|
|
|
|
{ 13, "MS-BAP-Usage", "MS-BAP-Usage", a_int },
|
|
|
|
{ 14, "MS-Link-Utilization-Threshold", "MS-Link-Utilization-Threshold", a_int },
|
|
|
|
{ 15, "MS-Link-Drop-Time-Limit", "MS-Link-Drop-Time-Limit", a_int },
|
|
|
|
{ 16, "MS-MPPE-Send-Key", "MS-MPPE-Send-Key", a_binary },
|
|
|
|
{ 17, "MS-MPPE-Recv-Key", "MS-MPPE-Recv-Key", a_binary },
|
|
|
|
{ 18, "MS-RAS-Version", "MS-RAS-Version", a_binary },
|
|
|
|
{ 22, "MS-Filter", "MS-Filter", a_binary },
|
|
|
|
{ 23, "MS-Acct-Auth-Type", "MS-Acct-Auth-Type", a_int },
|
|
|
|
{ 24, "MS-Acct-EAP-Type", "MS-Acct-EAP-Type", a_int },
|
|
|
|
{ 25, "MS-CHAP2-Response", "MS-CHAP2-Response", a_binary },
|
|
|
|
{ 26, "MS-CHAP2-Success", "MS-CHAP2-Success", a_binary },
|
|
|
|
{ 27, "MS-CHAP2-PW", "MS-CHAP2-PW", a_binary },
|
|
|
|
{ 30, "MS-Primary-NBNS-Server", "MS-Primary-NBNS-Server", a_ipaddr },
|
|
|
|
{ 31, "MS-Secondary-NBNS-Server", "MS-Secondary-NBNS-Server", a_ipaddr },
|
|
|
|
{ 0, 0, 0, a_void }
|
2006-03-22 18:22:34 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static rad_vendor vendors_dict[] = {
|
2012-09-14 15:15:06 +00:00
|
|
|
{ 0, 0, radius_dict },
|
|
|
|
{ 9, "cisco", cisco_dict },
|
|
|
|
{ 311, "microsoft", ms_dict },
|
|
|
|
{ 6618, "quintum", quintum_dict },
|
2006-03-22 18:22:34 +00:00
|
|
|
{ 0, 0, 0 }
|
|
|
|
};
|
|
|
|
|
|
|
|
// End of RADIUS values and tables
|
|
|
|
|
|
|
|
|
|
|
|
// map termination cause keywords to Acct-Terminate-Cause attribute values
|
|
|
|
static TokenDict dict_errors[] = {
|
|
|
|
{ "noanswer", 4 }, // Idle-Timeout
|
|
|
|
{ "timeout", 5 }, // Session-Timeout
|
|
|
|
{ "drop", 7 }, // Admin-Reset
|
|
|
|
{ "reboot", 7 }, // Admin-Reboot
|
|
|
|
{ "halt", 7 }, // Admin-Reboot
|
|
|
|
{ "offline", 8 }, // Port-Error
|
|
|
|
{ "congestion", 8 }, // Port-Error
|
|
|
|
{ "failure", 9 }, // NAS-Error
|
|
|
|
{ "noconn", 9 }, // NAS-Error
|
|
|
|
{ "busy", 13 }, // Port-Preempted
|
|
|
|
{ "nocall", 15 }, // Service-Unavailable
|
|
|
|
{ "noroute", 15 }, // Service-Unavailable
|
|
|
|
{ "forbidden", 17 }, // User-Error
|
|
|
|
{ "rejected", 18 }, // Host-Request
|
|
|
|
{ 0, 0 },
|
|
|
|
};
|
2012-02-22 20:43:57 +00:00
|
|
|
|
2006-03-22 18:22:34 +00:00
|
|
|
|
|
|
|
// Class to hold one RADIUS attribute
|
|
|
|
class RadAttrib : public GenObject
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
RadAttrib(const rad_dict* type, int vendor, void* value, unsigned int length);
|
|
|
|
RadAttrib(const rad_dict* type, int vendor, const char* value);
|
|
|
|
RadAttrib(const char* name, const char* value);
|
|
|
|
RadAttrib(const char* name, int value);
|
|
|
|
RadAttrib(const char* name, unsigned char subType, const char* value);
|
|
|
|
virtual ~RadAttrib();
|
|
|
|
bool packTo(DataBlock& data) const;
|
|
|
|
bool getString(String& retval) const;
|
|
|
|
inline bool isValid() const
|
2006-03-30 00:21:37 +00:00
|
|
|
{ return (m_type != 0); }
|
2006-03-22 18:22:34 +00:00
|
|
|
inline bool isVendor() const
|
|
|
|
{ return (m_type && (m_type->code == 26)) && (m_vendor == 0); }
|
|
|
|
inline int vendor() const
|
|
|
|
{ return m_vendor; }
|
|
|
|
inline const rad_dict* type() const
|
|
|
|
{ return m_type; }
|
|
|
|
inline const char* name() const
|
|
|
|
{ return m_type ? m_type->name : 0; }
|
|
|
|
inline int code() const
|
|
|
|
{ return m_type ? m_type->code : -1; }
|
|
|
|
inline const DataBlock& data() const
|
|
|
|
{ return m_value; }
|
|
|
|
static const rad_dict* find(const char* name, int* vendor = 0, const char** vendName = 0);
|
|
|
|
static const rad_dict* find(int code, int vendor = 0);
|
|
|
|
static bool decode(void* buf, unsigned int len, ObjList& list);
|
|
|
|
inline static bool decode(const DataBlock& data, ObjList& list)
|
|
|
|
{ return decode(data.data(),data.length(),list); }
|
|
|
|
static RadAttrib* decode(void*& buffer, unsigned int& length, int vendor = 0);
|
|
|
|
private:
|
|
|
|
bool assign(const char* value);
|
|
|
|
bool assign(int value);
|
|
|
|
bool assign(unsigned char subType, const char* value);
|
|
|
|
const rad_dict* m_type;
|
|
|
|
int m_vendor;
|
|
|
|
DataBlock m_value;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Class to encapsulate an entire client request operation
|
|
|
|
class RadiusClient : public GenObject
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
RadiusClient()
|
|
|
|
: m_socket(0), m_authPort(0), m_acctPort(0),
|
|
|
|
m_timeout(2000), m_retries(2)
|
|
|
|
{ }
|
|
|
|
virtual ~RadiusClient();
|
|
|
|
inline const String& server() const
|
|
|
|
{ return m_server; }
|
|
|
|
bool setRadServer(const char* host, int authport, int acctport, const char* secret, int timeoutms = 4000, int retries = 2);
|
|
|
|
bool setRadServer(const NamedList& sect);
|
2006-03-23 03:23:23 +00:00
|
|
|
bool addSocket();
|
2006-03-22 18:22:34 +00:00
|
|
|
int doAuthenticate(ObjList* result = 0);
|
|
|
|
int doAccounting(ObjList* result = 0);
|
|
|
|
bool addAttribute(const char* attrib, const char* val, bool emptyOk = false);
|
|
|
|
bool addAttribute(const char* attrib, int val);
|
|
|
|
bool addAttribute(const char* attrib, unsigned char subType, const char* val, bool emptyOk = false);
|
|
|
|
void addAttributes(NamedList& params, NamedList* list);
|
2006-04-04 19:06:02 +00:00
|
|
|
bool prepareAttributes(NamedList& params, bool forAcct = true, String* user = 0);
|
2011-05-12 18:55:24 +00:00
|
|
|
bool returnAttributes(NamedList& params, const ObjList* attributes, bool ok = true);
|
2006-03-22 18:22:34 +00:00
|
|
|
static bool fillRandom(DataBlock& data, int len);
|
|
|
|
|
|
|
|
private:
|
|
|
|
Socket* socket() const;
|
|
|
|
static unsigned char newSessionId();
|
|
|
|
int makeRequest(int port, unsigned char request, unsigned char* response = 0, ObjList* result = 0);
|
|
|
|
bool checkAuthenticator(const unsigned char* buffer, int length);
|
|
|
|
inline bool checkAuthenticator(const DataBlock& data)
|
|
|
|
{ return checkAuthenticator((const unsigned char*)data.data(),data.length()); }
|
|
|
|
|
|
|
|
static unsigned char s_sessionId;
|
|
|
|
Socket* m_socket;
|
|
|
|
ObjList m_attribs;
|
|
|
|
String m_server,m_secret,m_section;
|
|
|
|
unsigned int m_authPort,m_acctPort;
|
|
|
|
int m_timeout, m_retries;
|
|
|
|
DataBlock m_authdata;
|
|
|
|
};
|
|
|
|
|
2012-06-12 23:47:01 +00:00
|
|
|
class RadiusModule : public Module
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
RadiusModule();
|
|
|
|
virtual ~RadiusModule();
|
|
|
|
virtual void initialize();
|
|
|
|
protected:
|
|
|
|
bool m_init;
|
|
|
|
};
|
|
|
|
|
|
|
|
INIT_PLUGIN(RadiusModule);
|
|
|
|
|
|
|
|
|
2006-03-22 18:22:34 +00:00
|
|
|
class AuthHandler : public MessageHandler
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
inline AuthHandler(int prio)
|
2012-06-12 23:47:01 +00:00
|
|
|
: MessageHandler("user.auth",prio,__plugin.name())
|
2006-03-22 18:22:34 +00:00
|
|
|
{ }
|
|
|
|
virtual bool received(Message &msg);
|
|
|
|
};
|
|
|
|
|
|
|
|
class AcctHandler : public MessageHandler
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
inline AcctHandler(int prio)
|
2012-06-12 23:47:01 +00:00
|
|
|
: MessageHandler("call.cdr",prio,__plugin.name())
|
2006-03-22 18:22:34 +00:00
|
|
|
{ }
|
|
|
|
virtual bool received(Message &msg);
|
|
|
|
};
|
|
|
|
|
2012-09-14 15:15:06 +00:00
|
|
|
class RadiusHandler : public MessageHandler
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
inline RadiusHandler(int prio)
|
|
|
|
: MessageHandler("radius.generate",prio,__plugin.name())
|
|
|
|
{ }
|
|
|
|
virtual bool received(Message &msg);
|
|
|
|
};
|
2006-03-22 18:22:34 +00:00
|
|
|
|
|
|
|
// PortaOne specific routing
|
|
|
|
static void portaBillingRoute(NamedList& params, const ObjList* attributes)
|
|
|
|
{
|
|
|
|
String route;
|
|
|
|
// should we call multiple targets parallel or sequencial?
|
|
|
|
const char* rsep = s_pb_parallel ? " " : " | ";
|
|
|
|
for (; attributes; attributes = attributes->next()) {
|
|
|
|
const RadAttrib* attr = static_cast<const RadAttrib*>(attributes->get());
|
|
|
|
// PortaBilling uses only Cisco-AVPair
|
|
|
|
if (!(attr && (attr->vendor() == 9) && (attr->code() == 1)))
|
|
|
|
continue;
|
|
|
|
String tmp;
|
|
|
|
attr->getString(tmp);
|
|
|
|
if (tmp.startSkip("h323-ivr-in=DURATION:",false)) {
|
|
|
|
int sec = tmp.toInteger();
|
|
|
|
if (sec > 0) {
|
|
|
|
Debug(&__plugin,DebugCall,"PortaBilling setting timeout %d seconds",sec);
|
|
|
|
tmp = sec * 1000;
|
|
|
|
params.setParam("timeout",tmp);
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!tmp.startSkip("h323-ivr-in=PortaBilling_",false))
|
|
|
|
continue;
|
|
|
|
XDebug(&__plugin,DebugInfo,"PortaBilling '%s'",tmp.c_str());
|
|
|
|
if (tmp.startSkip("Routing:",false)) {
|
|
|
|
if (s_pb_simplify) {
|
|
|
|
int sep = tmp.find(';');
|
|
|
|
if (sep >= 0)
|
|
|
|
tmp.assign(tmp,sep);
|
|
|
|
}
|
|
|
|
if (tmp.null())
|
|
|
|
continue;
|
|
|
|
tmp = "sip/sip:" + tmp;
|
|
|
|
if (route.null())
|
|
|
|
route = tmp;
|
|
|
|
else {
|
|
|
|
if (!route.startsWith("fork",true))
|
|
|
|
route = "fork " + route;
|
|
|
|
route << rsep << tmp;
|
|
|
|
}
|
|
|
|
}
|
2006-04-04 19:06:02 +00:00
|
|
|
else if (tmp.startSkip("CLI:",false)) {
|
|
|
|
if (tmp) {
|
|
|
|
Debug(&__plugin,DebugCall,"PortaBilling setting caller '%s'",tmp.c_str());
|
|
|
|
params.setParam("caller",tmp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (tmp.startSkip("CompleteNumber:",false)) {
|
|
|
|
if (tmp) {
|
|
|
|
Debug(&__plugin,DebugCall,"PortaBilling setting called '%s'",tmp.c_str());
|
|
|
|
params.setParam("called",tmp);
|
|
|
|
}
|
|
|
|
}
|
2006-03-22 18:22:34 +00:00
|
|
|
}
|
|
|
|
if (route) {
|
|
|
|
Debug(&__plugin,DebugCall,"PortaBilling returned route '%s'",route.c_str());
|
|
|
|
params.setParam("callto",route);
|
2006-04-03 17:52:13 +00:00
|
|
|
if (s_pb_maxcall)
|
|
|
|
params.setParam("maxcall",s_pb_maxcall);
|
|
|
|
if (s_pb_stoperror && route.startsWith("fork",true))
|
|
|
|
params.setParam("stoperror",s_pb_stoperror);
|
2006-03-22 18:22:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// find one attribute by name, optionally store vendor ID and name
|
|
|
|
const rad_dict* RadAttrib::find(const char* name, int* vendor, const char** vendName)
|
|
|
|
{
|
|
|
|
for (rad_vendor* v = vendors_dict; v->dict; v++) {
|
|
|
|
for (const rad_dict* dict = v->dict; dict->name; dict++) {
|
2012-09-14 15:15:06 +00:00
|
|
|
if (!strcasecmp(name,dict->confName)) {
|
2006-03-22 18:22:34 +00:00
|
|
|
if (vendor)
|
|
|
|
*vendor = v->vendor;
|
|
|
|
if (vendName)
|
|
|
|
*vendName = v->name;
|
|
|
|
return dict;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// find one attribute by code and vendor ID
|
|
|
|
const rad_dict* RadAttrib::find(int code, int vendor)
|
|
|
|
{
|
|
|
|
for (rad_vendor* v = vendors_dict; v->dict; v++) {
|
|
|
|
if (v->vendor == vendor) {
|
|
|
|
for (const rad_dict* dict = v->dict; dict->name; dict++) {
|
|
|
|
if (dict->code == code)
|
|
|
|
return dict;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// decode one attribute, adjust buffer and length; on error set buffer to null
|
|
|
|
RadAttrib* RadAttrib::decode(void*& buffer, unsigned int& length, int vendor)
|
|
|
|
{
|
|
|
|
XDebug(&__plugin,DebugAll,"RadAttrib::decode(%p,%u,%d)",buffer,length,vendor);
|
|
|
|
if (!(buffer && length))
|
|
|
|
return 0;
|
|
|
|
if (length < 3) {
|
|
|
|
buffer = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
unsigned char* ptr = (unsigned char*)buffer;
|
|
|
|
int code = ptr[0];
|
|
|
|
unsigned int len = ptr[1];
|
|
|
|
if ((len < 3) || (len > length)) {
|
|
|
|
buffer = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
buffer = ptr + len;
|
|
|
|
length -= len;
|
|
|
|
const rad_dict* type = find(code,vendor);
|
|
|
|
if (!type)
|
|
|
|
return 0;
|
|
|
|
switch (type->type) {
|
|
|
|
case a_ipaddr:
|
|
|
|
case a_int:
|
|
|
|
case a_date:
|
|
|
|
if (len != 6) {
|
|
|
|
buffer = 0;
|
|
|
|
// roll back length to make it easier to debug
|
|
|
|
length += len;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return new RadAttrib(type,vendor,ptr+2,len-2);
|
|
|
|
}
|
|
|
|
|
|
|
|
// decode an entire received set of attributes
|
|
|
|
bool RadAttrib::decode(void* buf, unsigned int len, ObjList& list)
|
|
|
|
{
|
|
|
|
unsigned int len1 = len;
|
|
|
|
while (len) {
|
|
|
|
RadAttrib* attr = decode(buf,len);
|
|
|
|
if (!attr) {
|
|
|
|
if (!buf) {
|
|
|
|
Debug(&__plugin,DebugMild,"Invalid attribute at offset %u",len1 - len + 20);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (attr->isVendor()) {
|
|
|
|
unsigned int len2 = attr->data().length();
|
|
|
|
char* ptr = (char*)attr->data().data();
|
|
|
|
if ((len2 < 4) || !ptr) {
|
|
|
|
DDebug(&__plugin,DebugMild,"Invalid vendor attribute %u len=%u",attr->code(),len2);
|
2007-05-15 15:40:50 +00:00
|
|
|
attr->destruct();
|
2006-03-22 18:22:34 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
void* buf2 = ptr + 4;
|
|
|
|
len2 -= 4;
|
|
|
|
int vendor = ((int)ptr[0] << 24) | ((int)ptr[1] << 16) | ((int)ptr[2] << 8) | ptr[3];
|
|
|
|
while (len2) {
|
|
|
|
RadAttrib* attr2 = decode(buf2,len2,vendor);
|
|
|
|
if (!attr2) {
|
|
|
|
if (!buf2) {
|
|
|
|
DDebug(&__plugin,DebugMild,"Invalid vendor %u attribute",vendor);
|
2007-05-15 15:40:50 +00:00
|
|
|
attr->destruct();
|
2006-03-22 18:22:34 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
list.append(attr2);
|
|
|
|
}
|
2007-05-15 15:40:50 +00:00
|
|
|
attr->destruct();
|
2006-03-22 18:22:34 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
list.append(attr);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
RadAttrib::RadAttrib(const rad_dict* type, int vendor, void* value, unsigned int length)
|
|
|
|
: m_type(type), m_vendor(vendor), m_value(value,length)
|
|
|
|
{
|
|
|
|
XDebug(&__plugin,DebugAll,"RadAttrib::RadAttrib(%p,%d,%p,%u) [%p]",type,vendor,value,length,this);
|
|
|
|
}
|
|
|
|
|
|
|
|
RadAttrib::RadAttrib(const rad_dict* type, int vendor, const char* value)
|
|
|
|
: m_type(type), m_vendor(vendor)
|
|
|
|
{
|
|
|
|
XDebug(&__plugin,DebugAll,"RadAttrib::RadAttrib(%p,%d,'%s') [%p]",type,vendor,value,this);
|
|
|
|
if (m_type && value)
|
|
|
|
assign(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
RadAttrib::RadAttrib(const char* name, const char* value)
|
|
|
|
: m_type(0), m_vendor(0)
|
|
|
|
{
|
|
|
|
XDebug(&__plugin,DebugAll,"RadAttrib::RadAttrib('%s','%s') [%p]",name,value,this);
|
|
|
|
if (null(name) || null(value))
|
|
|
|
return;
|
|
|
|
m_type = find(name,&m_vendor);
|
|
|
|
if (!m_type) {
|
|
|
|
Debug(&__plugin,DebugGoOn,"Failed to find item %s in dictionary",name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
assign(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
RadAttrib::RadAttrib(const char* name, int value)
|
|
|
|
: m_type(0), m_vendor(0)
|
|
|
|
{
|
|
|
|
XDebug(&__plugin,DebugAll,"RadAttrib::RadAttrib('%s',%d) [%p]",name,value,this);
|
|
|
|
if (null(name))
|
|
|
|
return;
|
|
|
|
m_type = find(name,&m_vendor);
|
|
|
|
if (!m_type) {
|
|
|
|
Debug(&__plugin,DebugGoOn,"Failed to find item %s in dictionary",name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
assign(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
RadAttrib::RadAttrib(const char* name, unsigned char subType, const char* value)
|
|
|
|
{
|
|
|
|
XDebug(&__plugin,DebugAll,"RadAttrib::RadAttrib('%s',%u,'%s') [%p]",name,subType,value,this);
|
|
|
|
if (null(name) || null(value))
|
|
|
|
return;
|
|
|
|
m_type = find(name,&m_vendor);
|
|
|
|
if (!m_type) {
|
|
|
|
Debug(&__plugin,DebugGoOn,"Failed to find item %s in dictionary",name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
assign(subType,value);
|
|
|
|
}
|
|
|
|
|
|
|
|
RadAttrib::~RadAttrib()
|
|
|
|
{
|
|
|
|
XDebug(&__plugin,DebugAll,"RadAttrib::~RadAttrib type=%p vendor=%d [%p]",m_type,m_vendor,this);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RadAttrib::assign(const char* value)
|
|
|
|
{
|
|
|
|
if (null(value))
|
|
|
|
return false;
|
|
|
|
switch (m_type->type) {
|
|
|
|
case a_string:
|
|
|
|
m_value.assign((void*)value,strlen(value));
|
|
|
|
break;
|
|
|
|
case a_avpair:
|
|
|
|
{
|
|
|
|
String val(m_type->name);
|
|
|
|
val << "=" << value;
|
|
|
|
m_value.assign((void*)val.c_str(),val.length());
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case a_int:
|
|
|
|
{
|
|
|
|
unsigned int val = htonl((unsigned int)atoi(value));
|
|
|
|
m_value.assign((void*)&val,sizeof(int32_t));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case a_ipaddr:
|
|
|
|
{
|
|
|
|
uint32_t addr = inet_addr(value);
|
|
|
|
m_value.assign((void*)&addr,sizeof(int32_t));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
Debug(&__plugin,DebugGoOn,"Ignoring unknown attribute of type %d",m_type->type);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RadAttrib::assign(int value)
|
|
|
|
{
|
|
|
|
switch (m_type->type) {
|
|
|
|
case a_string:
|
|
|
|
{
|
|
|
|
String val(value);
|
|
|
|
m_value.assign((void*)val.c_str(),val.length());
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case a_avpair:
|
|
|
|
{
|
|
|
|
String val(m_type->name);
|
|
|
|
val << "=" << value;
|
|
|
|
m_value.assign((void*)val.c_str(),val.length());
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case a_int:
|
|
|
|
case a_ipaddr:
|
|
|
|
{
|
|
|
|
unsigned int val = htonl(value);
|
|
|
|
m_value.assign((void*)&val,sizeof(int32_t));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
Debug(&__plugin,DebugGoOn,"Ignoring unknown attribute of type %d",m_type->type);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RadAttrib::assign(unsigned char subType, const char* value)
|
|
|
|
{
|
|
|
|
if (null(value) || (m_type->type != a_binary))
|
|
|
|
return false;
|
|
|
|
// copy at most 253 characters so with header will be under 255
|
|
|
|
String val(value,253);
|
|
|
|
unsigned char header[2];
|
|
|
|
header[0] = subType;
|
|
|
|
header[1] = (val.length() + 2) & 0xff;
|
|
|
|
m_value.assign(header,2);
|
|
|
|
m_value.append(val);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RadAttrib::packTo(DataBlock& data) const
|
|
|
|
{
|
|
|
|
if (m_value.null() || !m_type)
|
|
|
|
return false; // invalid attribute.
|
|
|
|
|
|
|
|
// RADIUS cannot support attributes longer than 255
|
|
|
|
unsigned char buf[256];
|
|
|
|
unsigned char* ptr = buf;
|
|
|
|
unsigned int len = m_value.length();
|
|
|
|
if (len > 253)
|
|
|
|
len = 253;
|
|
|
|
unsigned int total = len + 2;
|
|
|
|
|
|
|
|
if (m_vendor) {
|
|
|
|
if (len > 247)
|
|
|
|
len = 247;
|
|
|
|
total = len + 8;
|
|
|
|
// put generic vendor type, full length, vendor code
|
|
|
|
*ptr++ = 26;
|
|
|
|
*ptr++ = 0xff & total;
|
|
|
|
*ptr++ = 0xff & (m_vendor >> 24);
|
|
|
|
*ptr++ = 0xff & (m_vendor >> 16);
|
|
|
|
*ptr++ = 0xff & (m_vendor >> 8);
|
|
|
|
*ptr++ = 0xff & m_vendor;
|
|
|
|
}
|
|
|
|
// put type, length, data
|
|
|
|
*ptr++ = m_type->code;
|
|
|
|
*ptr++ = 0xff & (len + 2);
|
|
|
|
memcpy(ptr,m_value.data(),len);
|
|
|
|
if (len != m_value.length())
|
|
|
|
Debug(&__plugin,DebugMild,"Attribute '%s' (%u) truncated from %u to %u bytes",
|
|
|
|
m_type->name,m_type->code,m_value.length(),len);
|
|
|
|
DataBlock tmp(buf,total,false);
|
|
|
|
data += tmp;
|
|
|
|
tmp.clear(false);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RadAttrib::getString(String& retval) const
|
|
|
|
{
|
|
|
|
if (!m_type)
|
|
|
|
return false;
|
|
|
|
retval.clear();
|
|
|
|
if (m_value.null())
|
|
|
|
return false;
|
|
|
|
switch (m_type->type) {
|
|
|
|
case a_string:
|
|
|
|
retval.assign((const char*)m_value.data(),m_value.length());
|
|
|
|
break;
|
|
|
|
case a_avpair:
|
|
|
|
retval.assign((const char*)m_value.data(),m_value.length());
|
|
|
|
{
|
|
|
|
// strip away any "name=" prefix
|
|
|
|
String tmp = m_type->name;
|
|
|
|
tmp << "=";
|
|
|
|
retval.startSkip(tmp,false);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case a_ipaddr:
|
|
|
|
{
|
|
|
|
struct in_addr addr;
|
|
|
|
addr.s_addr = *(int32_t*)m_value.data();
|
|
|
|
// FIXME: this should be globally thread safe
|
|
|
|
s_cfgMutex.lock();
|
|
|
|
retval = inet_ntoa(addr);
|
|
|
|
s_cfgMutex.unlock();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case a_int:
|
2006-03-30 00:21:37 +00:00
|
|
|
retval = (int)ntohl(*(int32_t*)m_value.data());
|
2006-03-22 18:22:34 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-03-30 00:21:37 +00:00
|
|
|
unsigned char RadiusClient::s_sessionId = (unsigned char)(Time::now() & 0xff);
|
2006-03-22 18:22:34 +00:00
|
|
|
|
|
|
|
RadiusClient::~RadiusClient()
|
|
|
|
{
|
|
|
|
delete m_socket;
|
|
|
|
}
|
|
|
|
|
|
|
|
Socket* RadiusClient::socket() const
|
|
|
|
{
|
|
|
|
return m_socket ? m_socket : &s_localSock;
|
|
|
|
}
|
|
|
|
|
2006-03-23 03:23:23 +00:00
|
|
|
// Create and add a local UDP socket to the client request
|
|
|
|
bool RadiusClient::addSocket()
|
|
|
|
{
|
|
|
|
if (m_socket)
|
|
|
|
return true;
|
|
|
|
SocketAddr localAddr(AF_INET);
|
|
|
|
localAddr.host(s_localAddr.host());
|
|
|
|
if (!(localAddr.valid() && localAddr.host())) {
|
|
|
|
Debug(&__plugin,DebugInfo,"Invalid address '%s' - falling back to global socket",
|
|
|
|
localAddr.host().c_str());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// we only have UDP support
|
|
|
|
Socket* s = new Socket(PF_INET,SOCK_DGRAM,IPPROTO_IP);
|
|
|
|
if (!(s && s->valid())) {
|
|
|
|
Debug(&__plugin,DebugWarn,"Error creating UDP socket - falling back to global socket");
|
|
|
|
delete s;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!s->bind(localAddr)) {
|
|
|
|
Debug(&__plugin,DebugWarn,"Error %d binding to %s - falling back to global socket",
|
|
|
|
s->error(),localAddr.host().c_str());
|
|
|
|
delete s;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
DDebug(&__plugin,DebugInfo,"Created new socket for request");
|
|
|
|
m_socket = s;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Build incremental session ID byte
|
2006-03-22 18:22:34 +00:00
|
|
|
unsigned char RadiusClient::newSessionId()
|
|
|
|
{
|
|
|
|
Lock lock(s_cfgMutex);
|
|
|
|
return s_sessionId++;
|
|
|
|
}
|
|
|
|
|
2006-03-23 03:23:23 +00:00
|
|
|
// Set the server parameters
|
2006-03-22 18:22:34 +00:00
|
|
|
bool RadiusClient::setRadServer(const char* host, int authport, int acctport, const char* secret, int timeoutms, int retries)
|
|
|
|
{
|
|
|
|
// adjust an absolute minimum of 1 try with a 500ms timeout
|
|
|
|
if (retries < 1)
|
|
|
|
retries = 1;
|
|
|
|
if (timeoutms < 500)
|
|
|
|
timeoutms = 500;
|
|
|
|
m_server = host;
|
|
|
|
m_authPort = authport;
|
|
|
|
m_acctPort = acctport;
|
|
|
|
m_secret = secret;
|
|
|
|
m_timeout = timeoutms;
|
|
|
|
m_retries = retries;
|
|
|
|
return (m_server && (m_authPort || m_acctPort));
|
|
|
|
}
|
|
|
|
|
2006-03-23 03:23:23 +00:00
|
|
|
// Set the server parameters from a config file section
|
2006-03-22 18:22:34 +00:00
|
|
|
bool RadiusClient::setRadServer(const NamedList& sect)
|
|
|
|
{
|
|
|
|
return setRadServer(sect.getValue("server"),
|
|
|
|
sect.getIntValue("auth_port",1812),
|
|
|
|
sect.getIntValue("acct_port",1813),
|
|
|
|
sect.getValue("secret"),
|
|
|
|
sect.getIntValue("timeout",2000),
|
|
|
|
sect.getIntValue("retries",2));
|
|
|
|
}
|
|
|
|
|
2006-03-23 03:23:23 +00:00
|
|
|
// Fill a data block with (pseudo) random data
|
2006-03-22 18:22:34 +00:00
|
|
|
bool RadiusClient::fillRandom(DataBlock& data, int len)
|
|
|
|
{
|
|
|
|
data.assign(0,len);
|
|
|
|
unsigned char *dd = (unsigned char*)data.data();
|
|
|
|
if (!dd)
|
|
|
|
return false;
|
|
|
|
unsigned int r = 0;
|
|
|
|
while (len--) {
|
|
|
|
while (!r)
|
2011-06-29 11:19:02 +00:00
|
|
|
r = Random::random();
|
2006-03-22 18:22:34 +00:00
|
|
|
*dd++ = r & 0xff;
|
|
|
|
r = r >> 8;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2006-03-23 03:23:23 +00:00
|
|
|
// Cryptographically check if the response is properly authenticated
|
2006-03-22 18:22:34 +00:00
|
|
|
bool RadiusClient::checkAuthenticator(const unsigned char* buffer, int length)
|
|
|
|
{
|
|
|
|
if (!buffer)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
const unsigned char* recauth = buffer+4;
|
|
|
|
const unsigned char* recattr = buffer+20;
|
|
|
|
int attrlen = length - 20;
|
|
|
|
|
|
|
|
MD5 md5(buffer,4);
|
|
|
|
md5.update(m_authdata);
|
|
|
|
if (attrlen > 0)
|
|
|
|
md5.update(recattr,attrlen);
|
|
|
|
md5.update(m_secret);
|
|
|
|
if (memcmp(md5.rawDigest(),recauth,16)) {
|
2006-03-23 03:23:23 +00:00
|
|
|
Debug(&__plugin,DebugMild,"Authenticators do not match");
|
2006-03-22 18:22:34 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
Debug(&__plugin,DebugAll,"Authenticator matched for response");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2006-03-23 03:23:23 +00:00
|
|
|
// Make one request, wait for answer and optionally decode it
|
2006-03-22 18:22:34 +00:00
|
|
|
int RadiusClient::makeRequest(int port, unsigned char request, unsigned char* response, ObjList* result)
|
|
|
|
{
|
|
|
|
if (!(port && socket() && socket()->valid()))
|
|
|
|
return ServerErr;
|
|
|
|
|
|
|
|
// create the address to send and receive packets
|
|
|
|
SocketAddr sockAddr(AF_INET);
|
|
|
|
sockAddr.host(m_server);
|
|
|
|
sockAddr.port(port);
|
|
|
|
|
|
|
|
// build the attribute block
|
|
|
|
DataBlock attrdata;
|
|
|
|
for (ObjList* l = &m_attribs; l; l = l->next()) {
|
|
|
|
const RadAttrib* attr = static_cast<const RadAttrib*>(l->get());
|
|
|
|
if (attr)
|
|
|
|
attr->packTo(attrdata);
|
|
|
|
}
|
|
|
|
int datalen = 20 + attrdata.length();
|
|
|
|
if (datalen > RADIUS_MAXLEN) {
|
|
|
|
Debug(&__plugin,DebugGoOn,"Packet of %u bytes exceeds RADIUS maximum",datalen);
|
|
|
|
return UnknownErr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// now we create the header for the RADIUS packet
|
|
|
|
unsigned char sessionId = newSessionId();
|
|
|
|
unsigned char tmp[4];
|
|
|
|
tmp[0] = request;
|
|
|
|
tmp[1] = sessionId;
|
|
|
|
tmp[2] = (datalen >> 8) & 0xff;
|
|
|
|
tmp[3] = datalen & 0xff;
|
|
|
|
|
|
|
|
// build the authenticator which is 16 octets long
|
|
|
|
switch (request) {
|
|
|
|
case Access_Request:
|
|
|
|
// random 16 octets used to authenticate the answer
|
|
|
|
if (!fillRandom(m_authdata,16))
|
|
|
|
return UnknownErr;
|
|
|
|
break;
|
|
|
|
case Accounting_Request:
|
|
|
|
// authenticate our packet to the server (see rfc2866)
|
|
|
|
{
|
|
|
|
DataBlock zeros(0,16);
|
|
|
|
MD5 md5(tmp,4);
|
|
|
|
md5 << zeros << attrdata << m_secret;
|
|
|
|
m_authdata.assign((void*)md5.rawDigest(),16);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
Debug(&__plugin,DebugFail,"Unknown request %u was asked. We only support Access and Accounting",request);
|
|
|
|
return UnknownErr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// now we build the packet out of header, authenticator and attributes
|
|
|
|
DataBlock radpckt(tmp,sizeof(tmp));
|
|
|
|
radpckt.append(m_authdata);
|
|
|
|
radpckt.append(attrdata);
|
|
|
|
|
2006-03-23 03:23:23 +00:00
|
|
|
if (!s_unisocket)
|
|
|
|
addSocket();
|
|
|
|
|
2006-03-22 18:22:34 +00:00
|
|
|
// we have the data ready, send it and wait for an answer
|
|
|
|
for (int r = m_retries; r > 0; r--) {
|
|
|
|
if (socket()->sendTo(radpckt.data(),radpckt.length(),sockAddr) == Socket::socketError()) {
|
2013-07-08 12:21:26 +00:00
|
|
|
Alarm(&__plugin,"socket",DebugGoOn,"Packet sending error %d to %s:%d",
|
2006-03-22 18:22:34 +00:00
|
|
|
socket()->error(),sockAddr.host().c_str(),sockAddr.port());
|
|
|
|
return UnknownErr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ok, we now must wait for receiving the matching answer
|
|
|
|
u_int64_t tryEnd = Time::now() + 1000 * m_timeout;
|
|
|
|
int64_t tout;
|
|
|
|
while ((tout = (tryEnd - Time::now())) > 0) {
|
|
|
|
bool canRead = false;
|
|
|
|
if (!socket()->select(&canRead,NULL,NULL,tout)) {
|
|
|
|
Debug(&__plugin,DebugWarn,"Error %d in select",socket()->error());
|
|
|
|
return UnknownErr;
|
|
|
|
}
|
|
|
|
if (!canRead) {
|
|
|
|
if (r > 1)
|
|
|
|
Debug(&__plugin,DebugMild,"Timeout waiting for server %s:%d, there are %d retries left",
|
|
|
|
sockAddr.host().c_str(),sockAddr.port(),r-1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
SocketAddr recvAddr;
|
|
|
|
unsigned char recdata[RADIUS_MAXLEN];
|
|
|
|
int readlen = socket()->recvFrom(recdata,sizeof(recdata),recvAddr);
|
|
|
|
if (readlen == Socket::socketError()) {
|
|
|
|
Debug(&__plugin,DebugWarn,"Packet reading error %d from %s:%d",
|
|
|
|
socket()->error(),sockAddr.host().c_str(),sockAddr.port());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (readlen < 20) {
|
|
|
|
Debug(&__plugin,DebugInfo,"Ignoring short (%d bytes) response from %s:%d",
|
|
|
|
readlen,recvAddr.host().c_str(),recvAddr.port());
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
datalen = ((unsigned int)recdata[2] << 8) | recdata[3];
|
|
|
|
if ((datalen < 20) || (datalen > readlen)) {
|
|
|
|
Debug(&__plugin,DebugInfo,"Ignoring packet with length %d (%d received) response from %s:%d",
|
|
|
|
datalen,readlen,recvAddr.host().c_str(),recvAddr.port());
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (recdata[1] != sessionId) {
|
|
|
|
DDebug(&__plugin,DebugAll,"Ignoring mismatched (%u vs %u) response from %s:%d",
|
|
|
|
recdata[1],sessionId,recvAddr.host().c_str(),recvAddr.port());
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!checkAuthenticator(recdata,datalen)) {
|
|
|
|
Debug(&__plugin,DebugMild,"Ignoring unauthenticated session %u response from %s:%d",
|
|
|
|
sessionId,recvAddr.host().c_str(),recvAddr.port());
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (result) {
|
|
|
|
// try to decode answer
|
|
|
|
if (!RadAttrib::decode(recdata+20,datalen-20,*result))
|
|
|
|
// authenticated but malformed - no reason to try again
|
|
|
|
return ServerErr;
|
|
|
|
}
|
|
|
|
if (response)
|
|
|
|
*response = recdata[0];
|
|
|
|
DDebug(&__plugin,DebugInfo,"Received valid response %u on session %u from %s:%d",
|
|
|
|
recdata[0],sessionId,recvAddr.host().c_str(),recvAddr.port());
|
|
|
|
return NoError;
|
2012-04-12 17:33:11 +00:00
|
|
|
}
|
2006-03-22 18:22:34 +00:00
|
|
|
}
|
|
|
|
Debug(&__plugin,DebugWarn,"Timeout receiving session %u from server %s:%d",
|
|
|
|
sessionId,sockAddr.host().c_str(),sockAddr.port());
|
|
|
|
return ServerErr;
|
|
|
|
}
|
|
|
|
|
2006-03-23 03:23:23 +00:00
|
|
|
// Make an authentication request, wait for answer
|
2006-03-22 18:22:34 +00:00
|
|
|
int RadiusClient::doAuthenticate(ObjList* result)
|
|
|
|
{
|
|
|
|
unsigned char response = 0;
|
|
|
|
int err = makeRequest(m_authPort,Access_Request,&response,result);
|
|
|
|
if (err != NoError) {
|
|
|
|
Debug(&__plugin,DebugWarn,"Aborting authentication with radius %s:%d",
|
|
|
|
m_server.c_str(),m_authPort);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
// we have the response of radius or some other app to which we accidentally sent data because user put wrong port or ip.
|
|
|
|
if (response != Access_Accept) {
|
|
|
|
Debug(&__plugin,DebugMild,"Server returned %u, assuming Access-Reject",response);
|
|
|
|
return AuthFailed;
|
|
|
|
}
|
2012-04-12 17:33:11 +00:00
|
|
|
|
2006-03-22 18:22:34 +00:00
|
|
|
Debug(&__plugin,DebugInfo,"Server returned Access-Accept");
|
|
|
|
return AuthSuccess;
|
|
|
|
}
|
|
|
|
|
2006-03-23 03:23:23 +00:00
|
|
|
// Make an accounting request, wait for answer
|
2006-03-22 18:22:34 +00:00
|
|
|
int RadiusClient::doAccounting(ObjList* result)
|
|
|
|
{
|
|
|
|
unsigned char response = 0;
|
|
|
|
int err = makeRequest(m_acctPort,Accounting_Request,&response,result);
|
|
|
|
if (err != NoError) {
|
|
|
|
Debug(&__plugin,DebugWarn,"Aborting accounting with radius %s:%d",
|
|
|
|
m_server.c_str(),m_acctPort);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
// we have the response of radius or some other app to which we accidentally sent data because user put wrong port or ip.
|
|
|
|
if (response != Accounting_Response) {
|
|
|
|
Debug(&__plugin,DebugWarn,"Server %s:%d returned %d but we were expecting Accounting_Response",
|
|
|
|
m_server.c_str(),m_acctPort,response);
|
|
|
|
return ServerErr;
|
|
|
|
}
|
|
|
|
Debug(&__plugin,DebugInfo,"Server returned Accounting-Response");
|
|
|
|
return AcctSuccess;
|
|
|
|
}
|
|
|
|
|
2006-03-23 03:23:23 +00:00
|
|
|
// Add one text attribute
|
2006-03-22 18:22:34 +00:00
|
|
|
bool RadiusClient::addAttribute(const char* attrib, const char* val, bool emptyOk)
|
|
|
|
{
|
|
|
|
if (null(attrib))
|
|
|
|
return false;
|
|
|
|
if (null(val))
|
|
|
|
return emptyOk;
|
|
|
|
RadAttrib* attr = new RadAttrib(attrib,val);
|
|
|
|
if (attr->isValid()) {
|
|
|
|
m_attribs.append(attr);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
attr->destruct();
|
2012-04-12 17:33:11 +00:00
|
|
|
return false;
|
2006-03-22 18:22:34 +00:00
|
|
|
}
|
|
|
|
|
2006-03-23 03:23:23 +00:00
|
|
|
// Add one numeric attribute
|
2006-03-22 18:22:34 +00:00
|
|
|
bool RadiusClient::addAttribute(const char* attrib, int val)
|
|
|
|
{
|
|
|
|
if (null(attrib))
|
|
|
|
return false;
|
|
|
|
RadAttrib* attr = new RadAttrib(attrib,val);
|
|
|
|
if (attr->isValid()) {
|
|
|
|
m_attribs.append(attr);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
attr->destruct();
|
2012-04-12 17:33:11 +00:00
|
|
|
return false;
|
2006-03-22 18:22:34 +00:00
|
|
|
}
|
|
|
|
|
2006-03-23 03:23:23 +00:00
|
|
|
// Add one text attribute with subtype
|
2006-03-22 18:22:34 +00:00
|
|
|
bool RadiusClient::addAttribute(const char* attrib, unsigned char subType, const char* val, bool emptyOk)
|
|
|
|
{
|
|
|
|
if (null(attrib))
|
|
|
|
return false;
|
|
|
|
if (null(val))
|
|
|
|
return emptyOk;
|
|
|
|
RadAttrib* attr = new RadAttrib(attrib,subType,val);
|
|
|
|
if (attr->isValid()) {
|
|
|
|
m_attribs.append(attr);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
attr->destruct();
|
2012-04-12 17:33:11 +00:00
|
|
|
return false;
|
2006-03-22 18:22:34 +00:00
|
|
|
}
|
|
|
|
|
2006-03-23 03:23:23 +00:00
|
|
|
// Copy from parameter list (usually message) to RADIUS attributes
|
2006-03-22 18:22:34 +00:00
|
|
|
void RadiusClient::addAttributes(NamedList& params, NamedList* list)
|
|
|
|
{
|
|
|
|
if (!list)
|
|
|
|
return;
|
|
|
|
DDebug(&__plugin,DebugInfo,"Adding attributes from section '%s'",list->c_str());
|
|
|
|
unsigned int n = list->length();
|
|
|
|
for (unsigned int i = 0; i < n; i++) {
|
|
|
|
NamedString* s = list->getParam(i);
|
|
|
|
if (!s)
|
|
|
|
continue;
|
|
|
|
if (s->name().startsWith("inc:",false)) {
|
|
|
|
String attr = s->name().substr(4).trimBlanks();
|
|
|
|
if (*s == *list)
|
|
|
|
Debug(&__plugin,DebugWarn,"Section '%s' includes itself!",s->c_str());
|
|
|
|
else
|
|
|
|
addAttributes(params,s_cfg.getSection(*s));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (s->name().startsWith("set:",false)) {
|
|
|
|
// set parameters in the params itself
|
|
|
|
String key = s->name().substr(4).trimBlanks();
|
|
|
|
if (key.null())
|
|
|
|
continue;
|
|
|
|
String val = *s;
|
|
|
|
params.replaceParams(val);
|
|
|
|
params.setParam(key,val);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!s->name().startsWith("add:",false))
|
|
|
|
continue;
|
|
|
|
// set RADIUS attributes
|
|
|
|
String key = s->name().substr(4).trimBlanks();
|
|
|
|
if (key.null())
|
|
|
|
continue;
|
|
|
|
String val = *s;
|
|
|
|
params.replaceParams(val);
|
2010-06-17 11:38:46 +00:00
|
|
|
static const Regexp r("^\\([0-9]\\+\\):\\(.*\\)");
|
2006-03-22 18:22:34 +00:00
|
|
|
if (key.matches(r)) {
|
|
|
|
int subType = key.matchString(1).toInteger(-1);
|
|
|
|
if ((subType >= 0) && (subType <= 255)) {
|
|
|
|
key = key.matchString(2);
|
|
|
|
addAttribute(key,subType,val);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
Debug(&__plugin,DebugWarn,"Invalid subtype in attribute '%s'",key.c_str());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
addAttribute(key,val);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-03-23 03:23:23 +00:00
|
|
|
// Find matching NAS section and populate attributes accordingly
|
2006-04-04 19:06:02 +00:00
|
|
|
bool RadiusClient::prepareAttributes(NamedList& params, bool forAcct, String* user)
|
2006-03-22 18:22:34 +00:00
|
|
|
{
|
|
|
|
const char* caller = params.getValue("caller");
|
2006-03-23 03:23:23 +00:00
|
|
|
const char* called = 0;
|
|
|
|
if (s_shortnum) {
|
|
|
|
// prefer short called over calledfull
|
|
|
|
called = params.getValue("called");
|
|
|
|
if (!called)
|
|
|
|
called = params.getValue("calledfull");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// prefer long calledfull over called
|
|
|
|
called = params.getValue("calledfull");
|
|
|
|
if (!called)
|
|
|
|
called = params.getValue("called");
|
|
|
|
}
|
2006-04-04 19:06:02 +00:00
|
|
|
const char* username = params.getValue("username");
|
|
|
|
if (!username)
|
|
|
|
username = params.getValue("authname");
|
|
|
|
if (!username) {
|
|
|
|
if (forAcct)
|
|
|
|
username = caller;
|
|
|
|
// we were unable to build an username
|
|
|
|
// don't even send such a request to PortaOne
|
|
|
|
if (s_pb_enabled && !username)
|
|
|
|
return false;
|
|
|
|
}
|
2006-03-22 18:22:34 +00:00
|
|
|
Lock lock(s_cfgMutex);
|
|
|
|
NamedList* nasSect = 0;
|
|
|
|
String nasName;
|
|
|
|
unsigned int n = s_cfg.sections();
|
|
|
|
for (unsigned int i = 0; i < n; i++) {
|
|
|
|
NamedList* sect = s_cfg.getSection(i);
|
|
|
|
if (!sect)
|
|
|
|
continue;
|
|
|
|
nasName = *sect;
|
|
|
|
// section must be [nas] or [nas SOMETHING]
|
|
|
|
if (!nasName.startSkip("nas"))
|
|
|
|
continue;
|
|
|
|
// found section - see if it's enabled for auth or accounting
|
|
|
|
if (!sect->getBoolValue(forAcct ? "rad_acct" : "rad_auth",true))
|
|
|
|
continue;
|
|
|
|
unsigned int n2 = sect->length();
|
|
|
|
for (unsigned int i2 = 0; i2 < n2; i2++) {
|
|
|
|
NamedString* pair = sect->getParam(i2);
|
|
|
|
if (!pair || pair->null())
|
|
|
|
continue;
|
|
|
|
// ignore keys like rad_SOMETHING or SOMETHING:SOMETHING
|
|
|
|
if (pair->name().startsWith("rad_",false) ||
|
|
|
|
(pair->name().find(':') >= 0))
|
|
|
|
continue;
|
|
|
|
Regexp r(pair->c_str());
|
|
|
|
const char* val = c_safe(params.getValue(pair->name()));
|
|
|
|
if (!r.matches(val)) {
|
|
|
|
// mismatch - skip this section
|
|
|
|
sect = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (sect) {
|
|
|
|
nasSect = sect;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!nasSect)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// find the name of the corresponding server section
|
|
|
|
NamedString* serv = nasSect->getParam("rad_server");
|
|
|
|
String servName("radius");
|
|
|
|
if (serv) {
|
|
|
|
// explicit empty name means get out
|
|
|
|
if (serv->null())
|
|
|
|
return false;
|
|
|
|
servName << " " << *serv;
|
|
|
|
}
|
|
|
|
else if (nasName)
|
|
|
|
servName << " " << nasName;
|
|
|
|
|
|
|
|
// find the section, exit if empty
|
|
|
|
NamedList* servSect = s_cfg.getSection(servName);
|
|
|
|
if (!servSect) {
|
|
|
|
Debug(&__plugin,DebugWarn,"Section [%s] does not exist",servName.c_str());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!setRadServer(*servSect)) {
|
|
|
|
Debug(&__plugin,DebugWarn,"Section [%s] does not hold a valid server",servName.c_str());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// remember the name of the NAS section
|
|
|
|
m_section = *nasSect;
|
|
|
|
|
2012-04-12 17:33:11 +00:00
|
|
|
Debug(&__plugin,DebugInfo,"Using sections [%s] and [%s] for %s",
|
2006-03-22 18:22:34 +00:00
|
|
|
m_section.c_str(),servName.c_str(),forAcct ? "accounting" : "authentication");
|
|
|
|
addAttribute("User-Name",username);
|
|
|
|
addAttribute("Calling-Station-Id",caller);
|
|
|
|
addAttribute("Called-Station-Id",called);
|
|
|
|
addAttributes(params,nasSect);
|
|
|
|
addAttributes(params,servSect);
|
2006-04-04 19:06:02 +00:00
|
|
|
if (user)
|
|
|
|
*user = username;
|
2006-03-22 18:22:34 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2006-03-23 03:23:23 +00:00
|
|
|
// Copy some attributes back from RADIUS answer to parameter list (message)
|
2011-05-12 18:55:24 +00:00
|
|
|
bool RadiusClient::returnAttributes(NamedList& params, const ObjList* attributes, bool ok)
|
2006-03-22 18:22:34 +00:00
|
|
|
{
|
|
|
|
Lock lock(s_cfgMutex);
|
|
|
|
NamedList* sect = s_cfg.getSection(m_section);
|
|
|
|
if (!sect)
|
|
|
|
return false;
|
|
|
|
|
2011-05-12 18:35:06 +00:00
|
|
|
String attrDump;
|
2006-03-22 18:22:34 +00:00
|
|
|
for (; attributes; attributes = attributes->next()) {
|
|
|
|
const RadAttrib* attr = static_cast<const RadAttrib*>(attributes->get());
|
|
|
|
if (!attr)
|
|
|
|
continue;
|
2011-05-12 18:35:06 +00:00
|
|
|
if (s_printAttr && __plugin.debugAt(DebugAll)) {
|
|
|
|
String val;
|
|
|
|
attr->getString(val);
|
|
|
|
attrDump << "\r\n " << attr->name() << "='" << val << "'";
|
|
|
|
}
|
2011-05-12 18:55:24 +00:00
|
|
|
String tmp(ok ? "ret:" : "ret-fail:");
|
2006-03-22 18:22:34 +00:00
|
|
|
tmp += attr->name();
|
|
|
|
String* par = sect->getParam(tmp);
|
|
|
|
if (par && *par) {
|
|
|
|
attr->getString(tmp);
|
2012-09-14 15:15:06 +00:00
|
|
|
if (!params.getParam(*par)) {
|
|
|
|
params.addParam(*par,tmp);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// Handle duplicate params
|
|
|
|
int count = 0;
|
|
|
|
while (++count) {
|
|
|
|
if (params.getParam(*par + "." + String(count)))
|
|
|
|
continue;
|
|
|
|
params.addParam(*par + "." + String(count),tmp);
|
|
|
|
break;
|
|
|
|
}
|
2006-03-22 18:22:34 +00:00
|
|
|
}
|
|
|
|
}
|
2011-05-12 18:35:06 +00:00
|
|
|
if (attrDump)
|
|
|
|
Debug(&__plugin,DebugAll,"Returned attributes:%s",attrDump.c_str());
|
2006-03-22 18:22:34 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2006-03-23 03:23:23 +00:00
|
|
|
|
2006-03-22 18:22:34 +00:00
|
|
|
bool AuthHandler::received(Message& msg)
|
|
|
|
{
|
2011-06-02 15:16:17 +00:00
|
|
|
if (!msg.getBoolValue("auth_radius",true))
|
|
|
|
return false;
|
2006-03-22 18:22:34 +00:00
|
|
|
String proto = msg.getValue("protocol",msg.getValue("module"));
|
|
|
|
if (proto.null())
|
|
|
|
return false;
|
|
|
|
RadiusClient radclient;
|
2006-04-04 19:06:02 +00:00
|
|
|
// preserve the actually authenticated username in case we succeed
|
|
|
|
String user;
|
|
|
|
if (!radclient.prepareAttributes(msg,false,&user))
|
2006-03-22 18:22:34 +00:00
|
|
|
return false;
|
2006-04-04 19:06:02 +00:00
|
|
|
// TODO: process plaintext password
|
2006-03-22 18:22:34 +00:00
|
|
|
if ((proto == "digest") || (proto == "sip")) {
|
2006-04-04 19:06:02 +00:00
|
|
|
const char* resp = msg.getValue("response");
|
|
|
|
const char* nonce = msg.getValue("nonce");
|
|
|
|
const char* method = msg.getValue("method");
|
|
|
|
const char* uri = msg.getValue("uri");
|
|
|
|
const char* user = msg.getValue("username");
|
|
|
|
if (resp && nonce && method && uri && user) {
|
|
|
|
// mandatory auth parameters
|
|
|
|
if (!(
|
|
|
|
radclient.addAttribute("Digest-Response",resp) &&
|
|
|
|
radclient.addAttribute("Digest-Attributes",Digest_Nonce,nonce) &&
|
|
|
|
radclient.addAttribute("Digest-Attributes",Digest_Method,method) &&
|
|
|
|
radclient.addAttribute("Digest-Attributes",Digest_URI,uri) &&
|
|
|
|
radclient.addAttribute("Digest-Attributes",Digest_UserName,user)
|
|
|
|
))
|
|
|
|
return false;
|
|
|
|
// optional auth parameters
|
|
|
|
radclient.addAttribute("Digest-Attributes",Digest_Realm,msg.getValue("realm"));
|
|
|
|
radclient.addAttribute("Digest-Attributes",Digest_Algo,msg.getValue("algorithm","MD5"));
|
|
|
|
radclient.addAttribute("Digest-Attributes",Digest_QOP,msg.getValue("qop"));
|
|
|
|
}
|
2006-03-22 18:22:34 +00:00
|
|
|
}
|
2006-04-04 19:06:02 +00:00
|
|
|
|
|
|
|
String address = msg.getValue("address");
|
|
|
|
// suppress any port number - IMHO this is stupid
|
|
|
|
int sep = address.find(':');
|
|
|
|
if (sep >= 0)
|
|
|
|
address = address.substr(0,sep);
|
2012-09-14 15:15:06 +00:00
|
|
|
if (s_cisco)
|
|
|
|
radclient.addAttribute("h323-remote-address",address);
|
|
|
|
if (s_quintum)
|
|
|
|
radclient.addAttribute("Quintum-h323-remote-address",address);
|
2011-05-19 09:34:09 +00:00
|
|
|
String billid = msg.getValue("billid");
|
|
|
|
if (billid) {
|
|
|
|
// create a Cisco-compatible conference ID
|
|
|
|
MD5 cid(billid);
|
|
|
|
String confid;
|
|
|
|
confid << cid.hexDigest().substr(0,8) << " ";
|
|
|
|
confid << cid.hexDigest().substr(8,8) << " ";
|
|
|
|
confid << cid.hexDigest().substr(16,8) << " ";
|
|
|
|
confid << cid.hexDigest().substr(24,8);
|
|
|
|
confid.toUpper();
|
2012-09-14 15:15:06 +00:00
|
|
|
if (s_cisco)
|
|
|
|
radclient.addAttribute("h323-conf-id",confid);
|
|
|
|
if (s_quintum)
|
|
|
|
radclient.addAttribute("Quintum-h323-conf-id",confid);
|
2011-05-19 09:34:09 +00:00
|
|
|
}
|
2006-04-04 19:06:02 +00:00
|
|
|
|
|
|
|
ObjList result;
|
2011-05-12 18:55:24 +00:00
|
|
|
if (radclient.doAuthenticate(&result) != AuthSuccess) {
|
|
|
|
radclient.returnAttributes(msg,&result,false);
|
2006-03-22 18:22:34 +00:00
|
|
|
return false;
|
2011-05-12 18:55:24 +00:00
|
|
|
}
|
2006-04-04 19:06:02 +00:00
|
|
|
// copy back the username we actually authenticated
|
|
|
|
if (user)
|
|
|
|
msg.setParam("username",user);
|
|
|
|
// and pick whatever other parameters we want to return
|
2011-05-12 18:55:24 +00:00
|
|
|
radclient.returnAttributes(msg,&result,true);
|
2006-04-04 19:06:02 +00:00
|
|
|
if (s_pb_enabled)
|
|
|
|
portaBillingRoute(msg,&result);
|
|
|
|
// signal we don't return a password
|
|
|
|
msg.retValue().clear();
|
|
|
|
return true;
|
2006-03-22 18:22:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-03-23 03:23:23 +00:00
|
|
|
// Build a Cisco style (like NTP) date/time string
|
2006-03-22 18:22:34 +00:00
|
|
|
static bool ciscoTime(double t, String& ret)
|
|
|
|
{
|
|
|
|
time_t sec = (time_t)floor(t);
|
|
|
|
unsigned int msec = (unsigned int)(1000.0 * (t - sec));
|
|
|
|
// we need to protect localtime/gmtime which may not be thread safe
|
2009-05-05 14:06:39 +00:00
|
|
|
static Mutex mutex(false,"YRadius::ciscoTime");
|
2006-03-22 18:22:34 +00:00
|
|
|
Lock lock(mutex);
|
|
|
|
struct tm* brokenTime = s_localTime ? localtime(&sec) : gmtime(&sec);
|
|
|
|
if (!brokenTime)
|
|
|
|
return false;
|
|
|
|
char buf[64];
|
|
|
|
ret.clear();
|
2006-04-03 18:23:51 +00:00
|
|
|
if (!strftime(buf,sizeof(buf),"%H:%M:%S",brokenTime))
|
2006-03-22 18:22:34 +00:00
|
|
|
return false;
|
|
|
|
ret = buf;
|
|
|
|
sprintf(buf,".%03u ",msec);
|
|
|
|
ret << buf;
|
|
|
|
if (!strftime(buf,sizeof(buf),"%Z %a %b %d %Y",brokenTime)) {
|
|
|
|
ret.clear();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
ret << buf;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool AcctHandler::received(Message& msg)
|
|
|
|
{
|
2011-06-02 15:16:17 +00:00
|
|
|
if (!msg.getBoolValue("cdrwrite_radius",true))
|
|
|
|
return false;
|
2006-03-22 18:22:34 +00:00
|
|
|
String op = msg.getValue("operation");
|
|
|
|
int acctStat = 0;
|
|
|
|
if (op == "initialize")
|
|
|
|
acctStat = Acct_Start;
|
|
|
|
else if (op == "finalize")
|
|
|
|
acctStat = Acct_Stop;
|
2012-09-14 15:15:06 +00:00
|
|
|
else if (op == "status")
|
|
|
|
acctStat = Acct_Alive;
|
2006-03-22 18:22:34 +00:00
|
|
|
else
|
|
|
|
return false;
|
|
|
|
|
|
|
|
String billid = msg.getValue("billid");
|
|
|
|
if (billid.null())
|
|
|
|
return false;
|
|
|
|
|
2006-04-03 17:52:13 +00:00
|
|
|
String address = msg.getValue("address");
|
|
|
|
int sep = address.find(':');
|
|
|
|
if (sep >= 0)
|
|
|
|
address = address.substr(0,sep);
|
|
|
|
|
2006-03-22 18:22:34 +00:00
|
|
|
String dir = msg.getValue("direction");
|
|
|
|
if (dir == "incoming")
|
|
|
|
dir = "answer";
|
|
|
|
else if (dir == "outgoing")
|
|
|
|
dir = "originate";
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
|
|
|
|
RadiusClient radclient;
|
2006-04-04 19:06:02 +00:00
|
|
|
if (!radclient.prepareAttributes(msg))
|
2006-03-22 18:22:34 +00:00
|
|
|
return false;
|
2006-04-03 17:52:13 +00:00
|
|
|
|
|
|
|
// create a Cisco-compatible conference ID
|
|
|
|
MD5 cid(billid);
|
|
|
|
String confid;
|
|
|
|
confid << cid.hexDigest().substr(0,8) << " ";
|
|
|
|
confid << cid.hexDigest().substr(8,8) << " ";
|
|
|
|
confid << cid.hexDigest().substr(16,8) << " ";
|
|
|
|
confid << cid.hexDigest().substr(24,8);
|
|
|
|
confid.toUpper();
|
|
|
|
|
2006-03-23 03:23:23 +00:00
|
|
|
// cryptographically generate an unique call leg ID
|
2006-03-22 18:22:34 +00:00
|
|
|
MD5 sid(billid);
|
|
|
|
sid << msg.getValue("chan");
|
|
|
|
|
|
|
|
radclient.addAttribute("Acct-Session-Id",sid.hexDigest());
|
|
|
|
radclient.addAttribute("Acct-Status-Type",acctStat);
|
2012-09-14 15:15:06 +00:00
|
|
|
if (s_cisco) {
|
|
|
|
radclient.addAttribute("h323-call-origin",dir);
|
|
|
|
radclient.addAttribute("h323-conf-id",confid);
|
|
|
|
radclient.addAttribute("h323-remote-address",address);
|
|
|
|
}
|
|
|
|
if (s_quintum) {
|
|
|
|
radclient.addAttribute("Quintum-h323-call-origin",dir);
|
|
|
|
radclient.addAttribute("Quintum-h323-conf-id",confid);
|
|
|
|
radclient.addAttribute("Quintum-h323-remote-address",address);
|
|
|
|
}
|
2006-03-22 18:22:34 +00:00
|
|
|
|
2006-04-03 17:52:13 +00:00
|
|
|
String tmp("call-id=");
|
|
|
|
if (address.null())
|
|
|
|
address = s_localAddr.host();
|
|
|
|
tmp << billid << "@" << address;
|
2012-09-14 15:15:06 +00:00
|
|
|
if (s_cisco)
|
|
|
|
radclient.addAttribute("Cisco-AVPair",tmp);
|
|
|
|
if (s_quintum)
|
|
|
|
radclient.addAttribute("Quintum-AVPair",tmp);
|
2006-04-03 17:52:13 +00:00
|
|
|
|
|
|
|
double t = msg.getDoubleValue("time");
|
2006-03-22 18:22:34 +00:00
|
|
|
ciscoTime(t,tmp);
|
2012-09-14 15:15:06 +00:00
|
|
|
if (s_cisco)
|
|
|
|
radclient.addAttribute("h323-setup-time",tmp);
|
|
|
|
if (s_quintum)
|
|
|
|
radclient.addAttribute("Quintum-h323-setup-time",tmp);
|
2006-03-22 18:22:34 +00:00
|
|
|
if (acctStat != Acct_Start) {
|
|
|
|
double d = msg.getDoubleValue("duration",-1);
|
|
|
|
if (d >= 0.0) {
|
|
|
|
double d1 = msg.getDoubleValue("billtime");
|
|
|
|
if (d1 > 0.0) {
|
|
|
|
ciscoTime(t+d-d1,tmp);
|
2012-09-14 15:15:06 +00:00
|
|
|
if (s_cisco)
|
|
|
|
radclient.addAttribute("h323-connect-time",tmp);
|
|
|
|
if (s_quintum)
|
|
|
|
radclient.addAttribute("Quintum-h323-connect-time",tmp);
|
2006-03-22 18:22:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (acctStat == Acct_Stop) {
|
|
|
|
double d = msg.getDoubleValue("duration",-1);
|
|
|
|
if (d >= 0.0) {
|
|
|
|
ciscoTime(t+d,tmp);
|
2012-09-14 15:15:06 +00:00
|
|
|
if (s_cisco)
|
|
|
|
radclient.addAttribute("h323-disconnect-time",tmp);
|
|
|
|
if (s_quintum)
|
|
|
|
radclient.addAttribute("Quintum-h323-disconnect-time",tmp);
|
2006-03-22 18:22:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
radclient.addAttribute("Acct-Session-Time",(int)msg.getDoubleValue("billtime"));
|
|
|
|
int cause = lookup(msg.getValue("status"),dict_errors,-1,10);
|
|
|
|
if (cause >= 0)
|
|
|
|
radclient.addAttribute("Acct-Terminate-Cause",cause);
|
|
|
|
String tmp = msg.getValue("reason");
|
|
|
|
if (tmp) {
|
|
|
|
tmp = "disconnect-text=" + tmp;
|
2012-09-14 15:15:06 +00:00
|
|
|
if (s_cisco)
|
|
|
|
radclient.addAttribute("Cisco-AVPair",tmp);
|
|
|
|
if (s_quintum)
|
|
|
|
radclient.addAttribute("Quintum-AVPair",tmp);
|
2006-03-22 18:22:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
radclient.doAccounting();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-09-14 15:15:06 +00:00
|
|
|
bool RadiusHandler::received(Message& msg)
|
|
|
|
{
|
|
|
|
bool auth = msg.getBoolValue(YSTRING("auth"),true);
|
|
|
|
int acctStat = 0;
|
|
|
|
if (!auth) {
|
|
|
|
String op = msg.getValue("operation");
|
|
|
|
if (op == "initialize")
|
|
|
|
acctStat = Acct_Start;
|
|
|
|
else if (op == "finalize")
|
|
|
|
acctStat = Acct_Stop;
|
|
|
|
else if (op == "status") {
|
|
|
|
acctStat = Acct_Alive;
|
|
|
|
} else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
RadiusClient radclient;
|
|
|
|
if (!radclient.prepareAttributes(msg))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!auth) {
|
|
|
|
radclient.addAttribute("Acct-Status-Type",acctStat);
|
|
|
|
}
|
|
|
|
|
|
|
|
ObjList result;
|
|
|
|
if (auth && radclient.doAuthenticate(&result) != AuthSuccess) {
|
|
|
|
radclient.returnAttributes(msg,&result,false);
|
|
|
|
return false;
|
|
|
|
} else if (!auth && radclient.doAccounting(&result) != AuthSuccess) {
|
|
|
|
radclient.returnAttributes(msg,&result,false);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
radclient.returnAttributes(msg,&result,true);
|
|
|
|
return true;
|
|
|
|
}
|
2006-03-22 18:22:34 +00:00
|
|
|
|
|
|
|
RadiusModule::RadiusModule()
|
|
|
|
: Module("yradius","misc"), m_init(false)
|
|
|
|
{
|
|
|
|
Output("Loaded module Radius client");
|
|
|
|
}
|
|
|
|
|
|
|
|
RadiusModule::~RadiusModule()
|
|
|
|
{
|
|
|
|
Output("Unloaded module Radius client");
|
|
|
|
}
|
|
|
|
|
|
|
|
void RadiusModule::initialize()
|
|
|
|
{
|
|
|
|
Output("Initializing module Radius client");
|
|
|
|
s_cfgMutex.lock();
|
|
|
|
s_cfg = Engine::configFile("yradius");
|
|
|
|
s_cfg.load();
|
2006-03-23 03:23:23 +00:00
|
|
|
s_localTime = s_cfg.getBoolValue("general","local_time",false);
|
|
|
|
s_shortnum = s_cfg.getBoolValue("general","short_number",false);
|
|
|
|
s_unisocket = s_cfg.getBoolValue("general","single_socket",false);
|
2011-05-12 18:35:06 +00:00
|
|
|
s_printAttr = s_cfg.getBoolValue("general","print_attributes",false);
|
2006-03-22 18:22:34 +00:00
|
|
|
s_pb_enabled = s_cfg.getBoolValue("portabill","enabled",false);
|
|
|
|
s_pb_parallel = s_cfg.getBoolValue("portabill","parallel",false);
|
|
|
|
s_pb_simplify = s_cfg.getBoolValue("portabill","simplify",false);
|
2012-09-14 15:15:06 +00:00
|
|
|
s_cisco = s_cfg.getBoolValue("general","cisco_format",true);
|
|
|
|
s_quintum = s_cfg.getBoolValue("general","quintum_format",true);
|
2006-04-03 17:52:13 +00:00
|
|
|
s_pb_stoperror = s_cfg.getValue("portabill","stoperror","busy");
|
|
|
|
s_pb_maxcall = s_cfg.getValue("portabill","maxcall");
|
|
|
|
s_cfgMutex.unlock();
|
2006-03-22 18:22:34 +00:00
|
|
|
|
2006-03-22 22:53:11 +00:00
|
|
|
if (m_init || !s_cfg.getBoolValue("general","enabled",true))
|
2006-03-22 18:22:34 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
s_localAddr.host(s_cfg.getValue("general","addr"));
|
|
|
|
s_localAddr.port(s_cfg.getIntValue("general","port",1810));
|
|
|
|
|
2006-03-24 15:45:53 +00:00
|
|
|
if (s_localAddr.host().null()) {
|
|
|
|
Debug(this,DebugNote,"Local address not set or invalid. Radius functions disabled");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2006-03-22 18:22:34 +00:00
|
|
|
if (!(s_localAddr.valid() && s_localAddr.host() && s_localAddr.port())) {
|
2006-03-24 15:45:53 +00:00
|
|
|
Debug(this,DebugWarn,"Invalid address %s:%d. Radius functions unavailable",
|
2006-03-22 18:22:34 +00:00
|
|
|
s_localAddr.host().c_str(),s_localAddr.port());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// we only have UDP support
|
|
|
|
if (!s_localSock.create(PF_INET,SOCK_DGRAM,IPPROTO_IP)) {
|
2013-07-08 12:21:26 +00:00
|
|
|
Alarm(this,"socket",DebugGoOn,"Error creating socket. Radius functions unavailable");
|
2006-03-22 18:22:34 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!s_localSock.bind(s_localAddr)) {
|
2013-07-08 12:21:26 +00:00
|
|
|
Alarm(this,"socket",DebugWarn,"Error %d binding to %s:%d. Radius functions unavailable",
|
2006-03-22 18:22:34 +00:00
|
|
|
s_localSock.error(),s_localAddr.host().c_str(),s_localAddr.port());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_init = true;
|
|
|
|
setup();
|
|
|
|
|
2006-04-04 19:06:02 +00:00
|
|
|
Engine::install(new AuthHandler(s_cfg.getIntValue("general","auth_priority",70)));
|
|
|
|
Engine::install(new AcctHandler(s_cfg.getIntValue("general","acct_priority",70)));
|
2012-09-14 15:15:06 +00:00
|
|
|
Engine::install(new RadiusHandler(100));
|
2006-03-22 18:22:34 +00:00
|
|
|
}
|
|
|
|
|
2006-05-27 14:53:18 +00:00
|
|
|
}; // anonymous namespace
|
|
|
|
|
2006-03-22 18:22:34 +00:00
|
|
|
/* vi: set ts=8 sw=4 sts=4 noet: */
|