/*
* radauth.cxx
*
* RADIUS protocol authenticator module for GNU Gatekeeper.
* Please see docs/radauth.txt for more details.
*
* Copyright (c) 2003, Quarcom FHU, Michal Zygmuntowicz
*
* This work is published under the GNU Public License (GPL)
* see file COPYING for details.
* We also explicitely grant the right to link this code
* with the OpenH323 library.
*
* $Log: radauth.cxx,v $
* Revision 1.35 2006/06/08 08:58:48 willamowius
* gcc 4.1 compile fixes
*
* Revision 1.34 2006/04/14 13:56:19 willamowius
* call failover code merged
*
* Revision 1.2 2005/12/05 13:29:02 zvision
* Accept multiple routes from RADIUS/SQL auth modules
*
* Revision 1.1.1.1 2005/11/21 20:19:58 willamowius
*
*
* Revision 1.4 2005/11/15 19:52:56 jan
* Michal v1 (works, but on in routed, not proxy mode)
*
* Revision 1.33 2005/04/24 16:39:44 zvision
* MSVC6.0 compatibility fixed
*
* Revision 1.32 2005/02/10 23:26:39 zvision
* Accounting updates/call disconnect handling does not lock the whole call table
*
* Revision 1.31 2005/02/01 14:28:10 zvision
* Parts of signaling code rewritten
*
* Revision 1.30 2005/01/28 11:19:42 zvision
* All passwords in the config can be stored in an encrypted form
*
* Revision 1.29 2005/01/26 23:50:26 zvision
* Framed-IP-Address could not be determined to unregistered calls without
* Setup-UUIE.sourceCallSignalAddress field
*
* Revision 1.28 2005/01/25 18:59:08 zvision
* Aliases handling fixed, alias type is not appended anymore
*
* Revision 1.27 2005/01/25 00:37:35 zvision
* Handle aliases of type partyNumber properly
*
* Revision 1.26 2005/01/10 22:49:29 willamowius
* typo
*
* Revision 1.25 2004/12/08 13:02:56 zvision
* Better Calling/Called-Station-Id handling
*
* Revision 1.24 2004/11/15 23:57:42 zvision
* Ability to choose between the original and the rewritten dialed number
*
* Revision 1.23 2004/11/03 10:40:17 zvision
* Add/remove RRQ aliases using h323-ivr-in=terminal-alias Cisco AV-Pair attr
*
* Revision 1.22 2004/08/09 21:52:23 zvision
* RADIUS based call routing
*
* Revision 1.21 2004/07/26 12:19:41 zvision
* New faster Radius implementation, thanks to Pavel Pavlov for ideas!
*
* Revision 1.20.2.2 2004/07/07 23:11:07 zvision
* Faster and more elegant handling of Cisco VSA
*
* Revision 1.20.2.1 2004/07/07 20:50:14 zvision
* New, faster, Radius client implementation. Thanks to Pavel Pavlov for ideas!
*
* Revision 1.20 2004/07/06 23:46:20 zvision
* gcc 2.95.x compilation errors fixed
*
* Revision 1.19 2004/07/05 16:39:45 zvision
* Support for CallCreditServiceControl
*
* Revision 1.18 2004/06/25 13:33:19 zvision
* Better Username, Calling-Station-Id and Called-Station-Id handling.
* New SetupUnreg option in Gatekeeper::Auth section.
*
* Revision 1.17 2004/06/17 10:47:13 zvision
* New h323-ivr-out=h323-call-id accounting attribute
*
* Revision 1.16 2004/06/17 10:03:17 zvision
* Better Framed-IP-Address handling in RadAliasAuth Setup check
*
* Revision 1.15 2004/06/16 23:46:47 zvision
* RadAliasAuth will work even when Setup-UUIE does not contain sourceAddress
*
* Revision 1.14 2004/05/22 12:25:17 zvision
* Check aliases only when authenticating RRQ message
*
* Revision 1.13 2004/04/17 11:43:43 zvision
* Auth/acct API changes.
* Header file usage more consistent.
*
* Revision 1.12 2004/03/17 00:00:38 zvision
* Conditional compilation to allow to control RADIUS on Windows just by setting HA_RADIUS macro
*
* Revision 1.11 2004/02/20 14:44:11 zvision
* Changed API for GkAuthenticator class. Modified RadAuth/RadAliasAuth classes.
* Added Q.931 Setup authentication for RadAuth module.
*
* Revision 1.10 2003/11/14 00:27:30 zvision
* Q.931/H.225 Setup authentication added
*
* Revision 1.9 2003/10/31 00:01:28 zvision
* Improved accounting modules stacking control, optimized radacct/radauth a bit
*
* Revision 1.8 2003/10/21 15:55:27 zvision
* Fixed compiler warnings for gcc < 3
*
* Revision 1.7 2003/10/15 10:16:57 zvision
* Fixed VC6 compiler warnings. Thanks to Hu Yuxin.
*
* Revision 1.6 2003/10/08 12:40:48 zvision
* Realtime accounting updates added
*
* Revision 1.5 2003/09/28 16:24:31 zvision
* Introduced call duration limit feature for registered endpoints (ARQ)
*
* Revision 1.4 2003/08/25 12:53:38 zvision
* Introduced includeTerminalAliases config option. Changed visibility
* of some member variables to private.
*
* Revision 1.3 2003/08/20 14:46:19 zvision
* Avoid PString reference copying. Small code improvements.
*
* Revision 1.2 2003/08/19 10:47:37 zvision
* Initially added to 2.2 brach. Completely redesigned.
* Redundant code removed. Added h323-return-code, h323-credit-time
* and Session-Timeout respone attributes processing.
*
* Revision 1.1.2.17 2003/07/31 22:59:24 zvision
* Fixed IP address retrieval for unregistered endpoints
*
* Revision 1.1.2.16 2003/07/31 13:09:15 zvision
* Added Q.931 Setup message authentication and call duration limit feature
*
* Revision 1.1.2.15 2003/07/17 14:40:39 zvision
* Conditional compilation of features available only when HAS_ACCT is defined.
*
* Revision 1.1.2.14 2003/07/16 22:13:21 zvision
* Fixed Radius attributes for answer call ARQs.
*
* Revision 1.1.2.13 2003/07/07 14:28:30 zvision
* Added missing NAS-Identifier attribute in RadAliasAuth. Thanks Julius Stavaris.
*
* Revision 1.1.2.12 2003/07/07 12:02:55 zvision
* Improved H.235 handling.
*
* Revision 1.1.2.11 2003/06/19 15:33:29 zvision
* Removed static modifier from GetConferenceIDString function.
*
* Revision 1.1.2.10 2003/06/11 13:06:57 zvision
* Added gk_const.h include directive (OPENH323_NEWVERSION macro definition)
*
* Revision 1.1.2.9 2003/06/11 12:14:35 zvision
* Cosmetic changes
*
* Revision 1.1.2.8 2003/06/05 10:03:04 zvision
* Small fix to h323-gw-id attribute.
*
* Revision 1.1.2.7 2003/05/29 17:21:22 zvision
* Fixed compilation errors with OpenH323 versions prior to 1.11.5 (no H235AuthCAT)
*
* Revision 1.1.2.6 2003/05/28 13:25:19 zvision
* Added alias based authentication (RadAliasAuth)
*
* Revision 1.1.2.5 2003/05/27 00:13:05 zvision
* Smart Calling and Called -Station-Id selection (dialedDigits and partyNumber alias types preferred)
*
* Revision 1.1.2.4 2003/05/26 23:09:59 zvision
* Added new OnSend and OnReceive hooks. LocalInterface config parameter introduced.
*
* Revision 1.1.2.3 2003/05/13 17:49:49 zvision
* Removed acctPort. New includeFramedIP feature. Better tracing. Bug-fixes
*
* Revision 1.1.2.2 2003/04/29 14:56:27 zvision
* Added H.235 capability matching
*
* Revision 1.1.2.1 2003/04/23 20:15:37 zvision
* Initial revision
*
*/
#if HAS_RADIUS
#if defined(_WIN32) && (_MSC_VER <= 1200)
#pragma warning(disable:4786) // warning about too long debug symbol off
#endif
#include <vector>
#include <ptlib.h>
#include <h225ras.h>
#include <h323pdu.h>
#include <h235.h>
#include <h235auth.h>
#include "gk_const.h"
#include "h323util.h"
#include "stl_supp.h"
#include "Toolkit.h"
#include "RasTbl.h"
#include "RasPDU.h"
#include "Routing.h"
#include "sigmsg.h"
#include "radproto.h"
#include "gkauth.h"
#include "radauth.h"
using std::vector;
using Routing::Route;
namespace {
// Settings for H.235 based module will be stored inside [RadAuth] config section
const char* const RadAuthConfigSectionName = "RadAuth";
// Settings for alias based module will be stored inside [RadAliasAuth] config section
const char* const RadAliasAuthConfigSectionName = "RadAliasAuth";
}
// OID for CAT (Cisco Access Token) algorithm
PString RadAuth::OID_CAT("1.2.840.113548.10.1.2.1");
RadAuthBase::RadAuthBase(
const char* authName,
const char* configSectionName,
unsigned supportedRasChecks,
unsigned supportedMiscChecks
)
:
GkAuthenticator(authName, supportedRasChecks, supportedMiscChecks),
m_radiusClient(NULL),
m_attrH323CallType(RadiusAttr::CiscoVSA_h323_call_type, false,
PString("VoIP")),
m_attrH323CallOriginOriginate(RadiusAttr::CiscoVSA_h323_call_origin, false,
PString("originate")),
m_attrH323CallOriginAnswer(RadiusAttr::CiscoVSA_h323_call_origin, false,
PString("answer"))
{
// read settings from the config
m_appendCiscoAttributes = Toolkit::AsBool(GetConfig()->GetString(
configSectionName,"AppendCiscoAttributes", "1"
));
m_includeTerminalAliases = Toolkit::AsBool(GetConfig()->GetString(
configSectionName, "IncludeTerminalAliases", "1"
));
m_nasIdentifier = Toolkit::Instance()->GKName();
/// build RADIUS client
m_radiusClient = new RadiusClient(*GetConfig(), configSectionName);
m_nasIpAddress = m_radiusClient->GetLocalAddress();
if (m_nasIpAddress == INADDR_ANY) {
vector<PIPSocket::Address> interfaces;
Toolkit::Instance()->GetGKHome(interfaces);
if (!interfaces.empty())
m_nasIpAddress = interfaces.front();
else
PTRACE(1, "RADAUTH\t" << GetName() << " cannot determine "
" NAS IP address"
);
}
m_useDialedNumber = Toolkit::AsBool(GetConfig()->GetString(
configSectionName, "UseDialedNumber", "0"
));
m_attrH323GwId = RadiusAttr(RadiusAttr::CiscoVSA_h323_gw_id, false, m_nasIdentifier);
m_attrNasIdentifier = RadiusAttr(RadiusAttr::NasIdentifier, m_nasIdentifier);
}
RadAuthBase::~RadAuthBase()
{
delete m_radiusClient;
}
int RadAuthBase::Check(
/// RRQ RAS message to be authenticated
RasPDU<H225_RegistrationRequest>& rrqPdu,
/// authorization data (reject reason, ...)
RRQAuthData& authData
)
{
H225_RegistrationRequest& rrq = (H225_RegistrationRequest&)rrqPdu;
// build RADIUS Access-Request
RadiusPDU* const pdu = new RadiusPDU(RadiusPDU::AccessRequest);
// Append User-Name and a password related attributes
// (User-Password or Chap-Password and Chap-Timestamp)
const int status = AppendUsernameAndPassword(*pdu, rrqPdu, authData);
if (status != e_ok) {
delete pdu;
return status;
}
// Gk works as NAS point, so append NAS IP
pdu->AppendAttr(RadiusAttr::NasIpAddress, m_nasIpAddress);
// NAS-Identifier as Gk name
pdu->AppendAttr(m_attrNasIdentifier);
// Gk does not have a concept of physical ports,
// so define port type as NAS-Port-Virtual
pdu->AppendAttr(RadiusAttr::NasPortType, RadiusAttr::NasPort_Virtual);
// RRQ service type is Login-User
pdu->AppendAttr(RadiusAttr::ServiceType, RadiusAttr::ST_Login);
// append Framed-IP-Address
PIPSocket::Address addr;
bool ipFound = false;
if (rrq.m_callSignalAddress.GetSize() > 0) {
if (GetIPFromTransportAddr(rrq.m_callSignalAddress[0], addr)
&& addr.IsValid())
ipFound = true;
} else if (rrq.m_rasAddress.GetSize() > 0) {
if (GetIPFromTransportAddr(rrq.m_rasAddress[0], addr)
&& addr.IsValid())
ipFound = true;
}
if (!ipFound) {
PTRACE(2, "RADAUTH\t" << GetName() << " RRQ auth failed: "
"could not determine Framed-IP-Address"
);
authData.m_rejectReason = H225_RegistrationRejectReason::e_invalidCallSignalAddress;
delete pdu;
return e_fail;
} else
pdu->AppendAttr(RadiusAttr::FramedIpAddress, addr);
if (m_appendCiscoAttributes && m_includeTerminalAliases
&& rrq.HasOptionalField(H225_RegistrationRequest::e_terminalAlias)) {
PString aliasList("terminal-alias:");
for (PINDEX i = 0; i < rrq.m_terminalAlias.GetSize(); i++) {
if(i > 0)
aliasList += ",";
aliasList += AsString(rrq.m_terminalAlias[i], FALSE);
}
// Cisco-AV-Pair
pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_AV_Pair,
PString("h323-ivr-out=") + aliasList + ";",
true
);
}
if (!OnSendPDU(*pdu, rrqPdu, authData)) {
delete pdu;
return e_fail;
}
// send request and wait for response
RadiusPDU* response = NULL;
bool result = m_radiusClient->MakeRequest(*pdu, response) && response;
delete pdu;
if (!result) {
PTRACE(2, "RADAUTH\t" << GetName() << " RRQ auth failed: "
" could not receive or decode response from RADIUS"
);
delete response;
authData.m_rejectReason = H225_RegistrationRejectReason::e_undefinedReason;
return GetDefaultStatus();
}
result = (response->GetCode() == RadiusPDU::AccessAccept);
PString value;
const RadiusAttr* attr;
// test for h323-return-code attribute (has to be 0 if accept)
if (result) {
attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
RadiusAttr::CiscoVSA_h323_return_code
);
if (attr != NULL) {
value = attr->AsCiscoString();
if (value.GetLength() > 0
&& strspn((const char*)value, "0123456789") == (size_t)value.GetLength()) {
const unsigned retcode = value.AsUnsigned();
if (retcode != 0) {
PTRACE(3, "RADAUTH\t" << GetName() << " RRQ check failed: "
"return code " << retcode
);
result = false;
}
} else {
PTRACE(2, "RADAUTH\t" << GetName() << " RRQ check failed: "
"invalid h323-return-code attribute '" << value << '\''
);
result = false;
}
}
}
// check for h323-billing-model
if (result) {
attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
RadiusAttr::CiscoVSA_h323_billing_model
);
if (attr != NULL) {
value = attr->AsCiscoString();
if (value.GetLength() > 0
&& strspn((const char*)value,"0123456789") == (size_t)value.GetLength()) {
const int intVal = value.AsInteger();
if (intVal == 0)
authData.m_billingMode = H225_CallCreditServiceControl_billingMode::e_credit;
else if (intVal == 1 || intVal == 2)
authData.m_billingMode = H225_CallCreditServiceControl_billingMode::e_debit;
} else {
PTRACE(3, "RADAUTH\t" << GetName() << " invalid h323-billing-model "
"attribute '" << value << '\''
);
}
}
}
// check for h323-credit-amount
if (result) {
attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
RadiusAttr::CiscoVSA_h323_credit_amount
);
if (attr != NULL) {
value = attr->AsCiscoString();
if (value.GetLength() > 0
&& strspn((const char*)value,"0123456789.") == (size_t)value.GetLength()) {
if (value.Find('.') == P_MAX_INDEX) {
PTRACE(3, "RADAUTH\t" << GetName() << " h323-credit-amount "
"without a decimal dot is ambiguous '" << value << '\''
);
authData.m_amountString = psprintf(PString("%d.%d"),
value.AsInteger() / 100, value.AsInteger() % 100
);
} else
authData.m_amountString = value;
attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
RadiusAttr::CiscoVSA_h323_currency
);
if (attr != NULL)
authData.m_amountString += attr->AsCiscoString();
} else {
PTRACE(3, "RADAUTH\t" << GetName() << " invalid h323-credit-amount "
"attribute '" << value << '\''
);
}
}
}
// process h323-ivr-in=terminal-alias attribute
if (result) {
attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
RadiusAttr::CiscoVSA_AV_Pair
);
while (attr != NULL) {
PINDEX index;
value = attr->AsCiscoString();
if (value.Find("h323-ivr-in=") == 0
&& ((index = value.Find("terminal-alias:")) != P_MAX_INDEX)) {
index += strlen("terminal-alias:");
const PINDEX semicolonpos = value.Find(';', index);
value = value.Mid(index, semicolonpos == P_MAX_INDEX
? P_MAX_INDEX : (semicolonpos-index)
);
PStringArray aliases = value.Tokenise(",");
if (aliases.GetSize() > 0
&& rrq.HasOptionalField(H225_RegistrationRequest::e_terminalAlias)) {
PINDEX i = 0;
while (i < rrq.m_terminalAlias.GetSize()) {
PINDEX j = aliases.GetStringsIndex(AsString(rrq.m_terminalAlias[i], FALSE));
if( j == P_MAX_INDEX )
rrq.m_terminalAlias.RemoveAt(i);
else {
i++;
aliases.RemoveAt(j);
}
}
}
for (PINDEX i = 0; i < aliases.GetSize(); i++) {
if (rrq.HasOptionalField(H225_RegistrationRequest::e_terminalAlias))
rrq.m_terminalAlias.SetSize(rrq.m_terminalAlias.GetSize()+1);
else {
rrq.IncludeOptionalField(H225_RegistrationRequest::e_terminalAlias);
rrq.m_terminalAlias.SetSize(1);
}
H323SetAliasAddress(aliases[i], rrq.m_terminalAlias[rrq.m_terminalAlias.GetSize()-1]);
}
break;
}
attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
RadiusAttr::CiscoVSA_AV_Pair, attr
);
}
}
if (result)
result = OnReceivedPDU(*response, rrqPdu, authData);
else
authData.m_rejectReason = H225_RegistrationRejectReason::e_securityDenial;
delete response;
return result ? e_ok : e_fail;
}
int RadAuthBase::Check(
/// ARQ nessage to be authenticated
RasPDU<H225_AdmissionRequest> & arqPdu,
/// authorization data (call duration limit, reject reason, ...)
ARQAuthData& authData
)
{
H225_AdmissionRequest& arq = (H225_AdmissionRequest&)arqPdu;
// build RADIUS Access-Request packet
RadiusPDU* const pdu = new RadiusPDU(RadiusPDU::AccessRequest);
const bool hasCall = authData.m_call.operator->() != NULL;
PIPSocket::Address addr;
endptr callingEP, calledEP;
// try to extract calling/called endpoints from RegistrationTable
// (unregistered endpoints will not be present there)
if (arq.m_answerCall) {
calledEP = authData.m_requestingEP;
if (hasCall)
callingEP = authData.m_call->GetCallingParty();
} else {
callingEP = authData.m_requestingEP;
if (hasCall)
calledEP = authData.m_call->GetCalledParty();
if (!calledEP && arq.HasOptionalField(H225_AdmissionRequest::e_destCallSignalAddress))
calledEP = RegistrationTable::Instance()->FindBySignalAdr(arq.m_destCallSignalAddress);
}
// at least requesting endpoint (the one that is sending ARQ)
// has to be present in the RegistrationTable
if (arq.m_answerCall ? !calledEP : !callingEP) {
delete pdu;
PTRACE(3, "RADAUTH\t" << GetName() << " ARQ auth failed: "
"requesting endpoint " << arq.m_endpointIdentifier
<< " not registered"
);
authData.m_rejectReason = arq.m_answerCall
? H225_AdmissionRejectReason::e_calledPartyNotRegistered
: H225_AdmissionRejectReason::e_callerNotRegistered;
return e_fail;
}
// Append User-Name and a password related attributes
// (User-Password or Chap-Password and Chap-Timestamp)
PString username;
const int status = AppendUsernameAndPassword(*pdu, arqPdu, authData, &username);
if (status != e_ok) {
delete pdu;
return status;
}
// Gk acts as NAS, so include NAS IP
pdu->AppendAttr(RadiusAttr::NasIpAddress, m_nasIpAddress);
// NAS-Identifier as Gk name
pdu->AppendAttr(m_attrNasIdentifier);
// NAS-Port-Type as Virtual, since Gk does
// not care about physical ports concept
pdu->AppendAttr(RadiusAttr::NasPortType, RadiusAttr::NasPort_Virtual);
// Service-Type is Login-User if originating the call
// and Call Check if answering the call
pdu->AppendAttr(RadiusAttr::ServiceType,
arq.m_answerCall ? RadiusAttr::ST_CallCheck : RadiusAttr::ST_Login
);
// append Frame-IP-Address
bool ipFound = false;
if (arq.m_answerCall) {
if (calledEP
&& GetIPFromTransportAddr(calledEP->GetCallSignalAddress(), addr)
&& addr.IsValid())
ipFound = true;
else if (arq.HasOptionalField(arq.e_destCallSignalAddress)
&& GetIPFromTransportAddr(arq.m_destCallSignalAddress, addr)
&& addr.IsValid())
ipFound = true;
} else {
if (callingEP
&& GetIPFromTransportAddr(callingEP->GetCallSignalAddress(), addr)
&& addr.IsValid())
ipFound = true;
else if(arq.HasOptionalField(arq.e_srcCallSignalAddress)
&& GetIPFromTransportAddr(arq.m_srcCallSignalAddress, addr)
&& addr.IsValid())
ipFound = true;
}
if (!ipFound) {
PTRACE(2, "RADAUTH\t" << GetName() << " ARQ auth failed: "
"could not setup Framed-IP-Address"
);
authData.m_rejectReason = H225_AdmissionRejectReason::e_securityDenial;
delete pdu;
return e_fail;
} else
pdu->AppendAttr(RadiusAttr::FramedIpAddress, addr);
// fill Calling-Station-Id and Called-Station-Id fields
PString stationId = GetCallingStationId(arqPdu, authData);
if (!stationId) {
pdu->AppendAttr(RadiusAttr::CallingStationId, stationId);
}
const PString dialedNumber = GetDialedNumber(arqPdu, authData);
const PString calledStationId = GetCalledStationId(arqPdu, authData);
stationId = m_useDialedNumber ? dialedNumber : calledStationId;
if (stationId.IsEmpty()) {
delete pdu;
PTRACE(2, "RADAUTH\t" << GetName() << " ARQ auth failed: "
"no suitable alias for Calling-Station-Id has been found"
);
authData.m_rejectReason = H225_AdmissionRejectReason::e_securityDenial;
return e_fail;
} else
pdu->AppendAttr(RadiusAttr::CalledStationId, stationId);
if (m_appendCiscoAttributes) {
pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_h323_conf_id,
GetGUIDString(arq.m_conferenceID)
);
if (arq.m_answerCall)
pdu->AppendAttr(m_attrH323CallOriginAnswer);
else
pdu->AppendAttr(m_attrH323CallOriginOriginate);
pdu->AppendAttr(m_attrH323CallType);
pdu->AppendAttr(m_attrH323GwId);
}
if (!OnSendPDU(*pdu, arqPdu, authData)) {
delete pdu;
return e_fail;
}
// send the request and wait for a response
RadiusPDU* response = NULL;
bool result = m_radiusClient->MakeRequest(*pdu, response) && response;
delete pdu;
if (!result) {
PTRACE(2, "RADAUTH\t" << GetName() << " ARQ auth failed: "
" could not receive or decode response from RADIUS"
);
delete response;
authData.m_rejectReason = H225_AdmissionRejectReason::e_undefinedReason;
return GetDefaultStatus();
}
// authenticated?
result = (response->GetCode() == RadiusPDU::AccessAccept);
PString value;
const RadiusAttr* attr;
// test for h323-return-code attribute (has to be 0 if accept)
if (result) {
attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
RadiusAttr::CiscoVSA_h323_return_code
);
if (attr != NULL) {
value = attr->AsCiscoString();
if (value.GetLength() > 0
&& strspn((const char*)value, "0123456789") == (size_t)value.GetLength()) {
const unsigned retcode = value.AsUnsigned();
if (retcode != 0) {
PTRACE(3, "RADAUTH\t" << GetName() << " ARQ check failed: "
"return code " << retcode
);
result = false;
}
} else {
PTRACE(2, "RADAUTH\t" << GetName() << " ARQ check failed: "
"invalid h323-return-code attribute '" << value << '\''
);
result = false;
}
}
}
// check for h323-credit-time attribute (call duration limit)
if (result) {
attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
RadiusAttr::CiscoVSA_h323_credit_time
);
if (attr != NULL) {
value = attr->AsCiscoString();
if (value.GetLength() > 0
&& strspn((const char*)value,"0123456789") == (size_t)value.GetLength()) {
authData.m_callDurationLimit = value.AsInteger();
PTRACE(5, "RADAUTH\t" << GetName() << " ARQ check set duration "
"limit: " << authData.m_callDurationLimit
);
if (authData.m_callDurationLimit == 0)
result = false;
} else {
PTRACE(2, "RADAUTH\t" << GetName() << " ARQ check failed: "
"invalid h323-credit-time attribute '" << value << '\''
);
result = false;
}
}
}
// check for Session-Timeout attribute (alternate call duration limit)
if (result) {
const RadiusAttr* const attr = response->FindAttr(RadiusAttr::SessionTimeout);
if (attr != NULL) {
const long sessionTimeout = attr->AsInteger();
if (authData.m_callDurationLimit < 0
|| authData.m_callDurationLimit > sessionTimeout) {
authData.m_callDurationLimit = sessionTimeout;
PTRACE(5, "RADAUTH\t" << GetName() << " ARQ check set "
"duration limit set " << authData.m_callDurationLimit
);
}
if (authData.m_callDurationLimit == 0)
result = false;
}
}
// check for h323-billing-model
if (result) {
attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
RadiusAttr::CiscoVSA_h323_billing_model
);
if (attr != NULL) {
value = attr->AsCiscoString();
if (value.GetLength() > 0
&& strspn((const char*)value,"0123456789") == (size_t)value.GetLength()) {
const int intVal = value.AsInteger();
if (intVal == 0)
authData.m_billingMode = H225_CallCreditServiceControl_billingMode::e_credit;
else if (intVal == 1 || intVal == 2)
authData.m_billingMode = H225_CallCreditServiceControl_billingMode::e_debit;
} else {
PTRACE(3, "RADAUTH\t" << GetName() << " invalid h323-billing-model "
"attribute '" << value << '\''
);
}
}
}
// check for h323-credit-amount
if (result) {
attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
RadiusAttr::CiscoVSA_h323_credit_amount
);
if (attr != NULL) {
value = attr->AsCiscoString();
if (value.GetLength() > 0
&& strspn((const char*)value,"0123456789.") == (size_t)value.GetLength()) {
if (value.Find('.') == P_MAX_INDEX) {
PTRACE(3, "RADAUTH\t" << GetName() << " h323-credit-amount "
"without a decimal dot is ambiguous '" << value << '\''
);
authData.m_amountString = psprintf(PString("%d.%d"),
value.AsInteger() / 100, value.AsInteger() % 100
);
} else
authData.m_amountString = value;
attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
RadiusAttr::CiscoVSA_h323_currency
);
if (attr != NULL)
authData.m_amountString += attr->AsCiscoString();
} else {
PTRACE(3, "RADAUTH\t" << GetName() << " invalid h323-credit-amount "
"attribute '" << value << '\''
);
}
}
}
// check for h323-redirect-number
if (result) {
attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
RadiusAttr::CiscoVSA_h323_redirect_number
);
if (attr != NULL) {
value = attr->AsCiscoString();
if (!value) {
authData.SetRouteToAlias(value);
PTRACE(5, "RADAUTH\t" << GetName() << " ARQ check redirect "
"to the number " << value
);
}
}
}
// check for h323-redirect-ip-address
if (result) {
attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
RadiusAttr::CiscoVSA_h323_redirect_ip_address
);
if (attr != NULL) {
value = attr->AsCiscoString();
if (!value) {
PStringArray tokens(value.Tokenise("; \t", FALSE));
for (PINDEX i = 0; i < tokens.GetSize(); ++i) {
PIPSocket::Address addr;
WORD port = 0;
if (GetTransportAddress(tokens[i], GK_DEF_ENDPOINT_SIGNAL_PORT, addr, port)
&& addr.IsValid() && port != 0) {
Route route("RADIUS", addr, port);
route.m_destEndpoint = RegistrationTable::Instance()->FindBySignalAdr(
SocketToH225TransportAddr(addr, port)
);
authData.m_destinationRoutes.push_back(route);
PTRACE(5, "RADAUTH\t" << GetName() << " ARQ check redirect "
"to the address " << route.AsString()
);
}
}
}
}
}
if (result)
result = OnReceivedPDU(*response, arqPdu, authData);
else
authData.m_rejectReason = H225_AdmissionRejectReason::e_securityDenial;
delete response;
return result ? e_ok : e_fail;
}
int RadAuthBase::Check(
SetupMsg &setup,
SetupAuthData &authData
)
{
// build RADIUS Access-Request packet
RadiusPDU* const pdu = new RadiusPDU(RadiusPDU::AccessRequest);
H225_Setup_UUIE& setupBody = setup.GetUUIEBody();
const bool hasCall = authData.m_call.operator->() != NULL;
PIPSocket::Address addr;
endptr callingEP, calledEP;
if (hasCall)
callingEP = authData.m_call->GetCallingParty();
if (!callingEP && setupBody.HasOptionalField(H225_Setup_UUIE::e_endpointIdentifier))
callingEP = RegistrationTable::Instance()->FindByEndpointId(
setupBody.m_endpointIdentifier
);
// Append User-Name and a password related attributes
// (User-Password or Chap-Password and Chap-Timestamp)
PString username;
const int status = AppendUsernameAndPassword(*pdu, setup, callingEP,
authData, &username
);
if (status != e_ok) {
delete pdu;
return status;
}
// Gk acts as NAS, so include NAS IP
pdu->AppendAttr(RadiusAttr::NasIpAddress, m_nasIpAddress);
// NAS-Identifier as Gk name
pdu->AppendAttr(m_attrNasIdentifier);
// NAS-Port-Type as Virtual, since Gk does
// not care about physical ports concept
pdu->AppendAttr(RadiusAttr::NasPortType, RadiusAttr::NasPort_Virtual);
// Service-Type is Login-User if originating the call
// and Call Check if answering the call
pdu->AppendAttr(RadiusAttr::ServiceType, RadiusAttr::ST_Login);
// append Frame-IP-Address
bool ipFound = false;
WORD dummyPort;
if (hasCall && authData.m_call->GetSrcSignalAddr(addr, dummyPort)
&& addr.IsValid())
ipFound = true;
else if (callingEP
&& GetIPFromTransportAddr(callingEP->GetCallSignalAddress(), addr)
&& addr.IsValid())
ipFound = true;
else if (setupBody.HasOptionalField(H225_Setup_UUIE::e_sourceCallSignalAddress)
&& GetIPFromTransportAddr(setupBody.m_sourceCallSignalAddress, addr)
&& addr.IsValid())
ipFound = true;
else {
setup.GetPeerAddr(addr);
ipFound = addr.IsValid();
}
if (!ipFound) {
PTRACE(2, "RADAUTH\t" << GetName() << " Setup auth failed: "
"could not setup Framed-IP-Address"
);
delete pdu;
authData.m_rejectCause = Q931::CallRejected;
return e_fail;
} else
pdu->AppendAttr(RadiusAttr::FramedIpAddress, addr);
// fill Calling-Station-Id and Called-Station-Id fields
PString stationId = GetCallingStationId(setup, authData);
if (!stationId) {
pdu->AppendAttr(RadiusAttr::CallingStationId, stationId);
}
const PString calledStationId = GetCalledStationId(setup, authData);
const PString dialedNumber = GetDialedNumber(setup, authData);
stationId = m_useDialedNumber ? dialedNumber : calledStationId;
if (stationId.IsEmpty()) {
delete pdu;
PTRACE(2, "RADAUTH\t" << GetName() << " Setup check failed: "
"no called station id found"
);
authData.m_rejectReason = H225_ReleaseCompleteReason::e_badFormatAddress;
return e_fail;
} else
pdu->AppendAttr(RadiusAttr::CalledStationId, stationId);
if (m_appendCiscoAttributes) {
pdu->AppendCiscoAttr(RadiusAttr::CiscoVSA_h323_conf_id,
GetGUIDString(setupBody.m_conferenceID)
);
pdu->AppendAttr(m_attrH323CallOriginOriginate);
pdu->AppendAttr(m_attrH323CallType);
pdu->AppendAttr(m_attrH323GwId);
}
if (!OnSendPDU(*pdu, setup, authData)) {
delete pdu;
return e_fail;
}
// send the request and wait for a response
RadiusPDU* response = NULL;
bool result = m_radiusClient->MakeRequest(*pdu, response) && response;
delete pdu;
if (!result) {
PTRACE(2, "RADAUTH\t" << GetName() << " Setup auth failed: "
" could not receive or decode response from RADIUS"
);
delete response;
authData.m_rejectCause = Q931::TemporaryFailure;
return GetDefaultStatus();
}
// authenticated?
result = (response->GetCode() == RadiusPDU::AccessAccept);
PString value;
const RadiusAttr* attr;
// test for h323-return-code attribute (has to be 0 if accept)
if (result) {
attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
RadiusAttr::CiscoVSA_h323_return_code
);
if (attr != NULL) {
value = attr->AsCiscoString();
if (value.GetLength() > 0
&& strspn((const char*)value, "0123456789") == (size_t)value.GetLength()) {
const unsigned retcode = value.AsUnsigned();
if (retcode != 0) {
PTRACE(5, "RADAUTH\t" << GetName() << " Setup check failed: "
"return code " << retcode
);
result = false;
}
} else {
PTRACE(2, "RADAUTH\t" << GetName() << " Setup check failed: "
"invalid h323-return-code attribute '" << value << '\''
);
result = false;
}
}
}
// check for h323-credit-time attribute (call duration limit)
if (result) {
attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
RadiusAttr::CiscoVSA_h323_credit_time
);
if (attr != NULL) {
value = attr->AsCiscoString();
if (value.GetLength() > 0
&& strspn((const char*)value,"0123456789") == (size_t)value.GetLength() ) {
authData.m_callDurationLimit = value.AsInteger();
PTRACE(5, "RADAUTH\t" << GetName() << " Setup check set duration "
"limit: " << authData.m_callDurationLimit
);
if (authData.m_callDurationLimit == 0)
result = false;
} else {
PTRACE(2, "RADAUTH\t" << GetName() << " Setup check failed: "
"invalid h323-credit-time attribute '" << value << '\''
);
result = false;
}
}
}
// check for Session-Timeout attribute (alternate call duration limit)
if (result) {
const RadiusAttr* const attr = response->FindAttr(RadiusAttr::SessionTimeout);
if (attr != NULL) {
const long sessionTimeout = attr->AsInteger();
if (authData.m_callDurationLimit < 0
|| authData.m_callDurationLimit > sessionTimeout) {
authData.m_callDurationLimit = sessionTimeout;
PTRACE(5, "RADAUTH\t" << GetName() << " Setup check "
"set duration limit: " << authData.m_callDurationLimit
);
}
if (authData.m_callDurationLimit == 0)
result = false;
}
}
// check for h323-redirect-number
if (result) {
attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
RadiusAttr::CiscoVSA_h323_redirect_number
);
if (attr != NULL) {
value = attr->AsCiscoString();
if (!value) {
authData.SetRouteToAlias(value);
PTRACE(5, "RADAUTH\t" << GetName() << " ARQ check redirect "
"to the number " << value
);
}
}
}
// check for h323-redirect-ip-address
if (result) {
attr = response->FindVsaAttr(RadiusAttr::CiscoVendorId,
RadiusAttr::CiscoVSA_h323_redirect_ip_address
);
if (attr != NULL) {
value = attr->AsCiscoString();
if (!value) {
PStringArray tokens(value.Tokenise("; \t", FALSE));
for (PINDEX i = 0; i < tokens.GetSize(); ++i) {
PIPSocket::Address addr;
WORD port = 0;
if (GetTransportAddress(tokens[i], GK_DEF_ENDPOINT_SIGNAL_PORT, addr, port)
&& addr.IsValid() && port != 0) {
Route route("RADIUS", addr, port);
route.m_destEndpoint = RegistrationTable::Instance()->FindBySignalAdr(
SocketToH225TransportAddr(addr, port)
);
authData.m_destinationRoutes.push_back(route);
PTRACE(5, "RADAUTH\t" << GetName() << " Setup check redirect "
"to the address " << route.AsString()
);
}
}
}
}
}
if (result)
result = OnReceivedPDU(*response, setup, authData);
else
authData.m_rejectCause = Q931::CallRejected;
delete response;
return result ? e_ok : e_fail;
}
bool RadAuthBase::OnSendPDU(
RadiusPDU& /*pdu*/,
RasPDU<H225_RegistrationRequest>& /*rrqPdu*/,
RRQAuthData& /*authData*/
)
{
return true;
}
bool RadAuthBase::OnSendPDU(
RadiusPDU& /*pdu*/,
RasPDU<H225_AdmissionRequest>& /*arqPdu*/,
ARQAuthData& /*authData*/
)
{
return true;
}
bool RadAuthBase::OnSendPDU(
RadiusPDU& /*pdu*/,
SetupMsg &/*setup*/,
SetupAuthData& /*authData*/
)
{
return true;
}
bool RadAuthBase::OnReceivedPDU(
RadiusPDU& /*pdu*/,
RasPDU<H225_RegistrationRequest>& /*rrqPdu*/,
RRQAuthData& /*authData*/
)
{
return true;
}
bool RadAuthBase::OnReceivedPDU(
RadiusPDU& /*pdu*/,
RasPDU<H225_AdmissionRequest>& /*arqPdu*/,
ARQAuthData& /*authData*/
)
{
return true;
}
bool RadAuthBase::OnReceivedPDU(
RadiusPDU& /*pdu*/,
SetupMsg &/*setup*/,
SetupAuthData& /*authData*/
)
{
return true;
}
int RadAuthBase::AppendUsernameAndPassword(
RadiusPDU& /*pdu*/,
RasPDU<H225_RegistrationRequest>& /*rrqPdu*/,
RRQAuthData& /*authData*/,
PString* /*username*/
) const
{
return GetDefaultStatus();
}
int RadAuthBase::AppendUsernameAndPassword(
RadiusPDU& /*pdu*/,
RasPDU<H225_AdmissionRequest>& /*arqPdu*/,
ARQAuthData& /*authData*/,
PString* /*username*/
) const
{
return GetDefaultStatus();
}
int RadAuthBase::AppendUsernameAndPassword(
RadiusPDU &/*pdu*/,
SetupMsg &/*setup*/,
endptr &/*callingEP*/,
SetupAuthData &/*authData*/,
PString * /*username*/
) const
{
return GetDefaultStatus();
}
RadAuth::RadAuth(
const char* authName
)
:
RadAuthBase(authName, RadAuthConfigSectionName)
{
// setup H.235 algorithm and method types used
// by this authenticator - this will make sure
// GCF H.235 alogirthm selection will not skip
// information required by this authenticator
H235AuthCAT* authenticator = new H235AuthCAT;
authenticator->SetLocalId("dummy");
authenticator->SetRemoteId("dummy");
authenticator->SetPassword("dummy");
AppendH235Authenticator(authenticator);
}
RadAuth::~RadAuth()
{
}
int RadAuth::CheckTokens(
RadiusPDU& pdu,
const H225_ArrayOf_ClearToken& tokens,
const H225_ArrayOf_AliasAddress* aliases,
PString* username
) const
{
// scan ClearTokens and find CATs
for (PINDEX i = 0; i < tokens.GetSize(); i++) {
const H235_ClearToken& token = tokens[i];
// is this CAT?
if (token.m_tokenOID != OID_CAT)
continue;
// these field are required for CAT
if (!(token.HasOptionalField(H235_ClearToken::e_generalID)
&& token.HasOptionalField(H235_ClearToken::e_random)
&& token.HasOptionalField(H235_ClearToken::e_timeStamp)
&& token.HasOptionalField(H235_ClearToken::e_challenge)))
{
PTRACE(3, "RADAUTH\t" << GetName() << " auth failed: "
"CAT without all required fields"
);
return e_fail;
}
// generalID should be present in the list of terminal aliases
const PString id = token.m_generalID;
if (aliases && FindAlias(*aliases, id) == P_MAX_INDEX) {
PTRACE(3, "RADAUTH\t" << GetName() << " auth failed: "
"CAT m_generalID is not a valid alias"
);
return e_fail;
}
// CAT pseudo-random has to be one byte only
const int randomInt = token.m_random;
if (randomInt < -127 || randomInt > 255) {
PTRACE(3, "RADAUTH\t" << GetName() << " auth failed: "
"CAT m_random out of range"
);
return e_fail;
}
// CAT challenge has to be 16 bytes
if (token.m_challenge.GetValue().GetSize() < 16) {
PTRACE(3, "RADAUTH\t" << GetName() << " auth failed: "
"m_challenge less than 16 bytes"
);
return e_fail;
}
// append User-Name
pdu.AppendAttr(RadiusAttr::UserName, id);
if (username != NULL)
*username = (const char*)id;
// build CHAP-Password
char password[17] = { (BYTE)randomInt };
memcpy(password + 1, (const BYTE*)(token.m_challenge), 16);
pdu.AppendAttr(RadiusAttr::ChapPassword, password, sizeof(password));
pdu.AppendAttr(RadiusAttr::ChapChallenge, (int)(DWORD)token.m_timeStamp);
return e_ok;
}
PTRACE(3, "RADAUTH\t" << GetName() << " auth failed: no CAT token found");
return GetDefaultStatus();
}
int RadAuth::AppendUsernameAndPassword(
RadiusPDU& pdu,
RasPDU<H225_RegistrationRequest>& rrqPdu,
RRQAuthData& authData,
PString* username
) const
{
H225_RegistrationRequest& rrq = (H225_RegistrationRequest&)rrqPdu;
// RRQ has to carry at least one terminalAlias
if (!rrq.HasOptionalField(H225_RegistrationRequest::e_terminalAlias)) {
PTRACE(3, "RADAUTH\t" << GetName() << " RRQ auth failed: "
"no m_terminalAlias field"
);
authData.m_rejectReason = H225_RegistrationRejectReason::e_securityDenial;
return GetDefaultStatus();
}
// check for ClearTokens (CAT uses ClearTokens)
if (!rrq.HasOptionalField(H225_RegistrationRequest::e_tokens)) {
PTRACE(3, "RADAUTH\t" << GetName() << " RRQ auth failed: "
"tokens not found"
);
authData.m_rejectReason = H225_RegistrationRejectReason::e_securityDenial;
return GetDefaultStatus();
}
const int result = CheckTokens(pdu, rrq.m_tokens, &rrq.m_terminalAlias, username);
if (result != e_ok)
authData.m_rejectReason = H225_RegistrationRejectReason::e_securityDenial;
return result;
}
int RadAuth::AppendUsernameAndPassword(
RadiusPDU& pdu,
RasPDU<H225_AdmissionRequest>& arqPdu,
ARQAuthData& authData,
PString* username
) const
{
H225_AdmissionRequest& arq = (H225_AdmissionRequest&)arqPdu;
// check for ClearTokens
if (!arq.HasOptionalField(H225_AdmissionRequest::e_tokens)) {
PTRACE(3, "RADAUTH\t" << GetName() << " ARQ auth failed: "
"tokens not found"
);
authData.m_rejectReason = H225_AdmissionRejectReason::e_securityDenial;
return GetDefaultStatus();
}
const int result = CheckTokens(pdu, arq.m_tokens, NULL, username);
if (result != e_ok)
authData.m_rejectReason = H225_AdmissionRejectReason::e_securityDenial;
return result;
}
int RadAuth::AppendUsernameAndPassword(
RadiusPDU& pdu,
SetupMsg &setup,
endptr& /*callingEP*/,
SetupAuthData& authData,
PString* username
) const
{
H225_Setup_UUIE &setupBody = setup.GetUUIEBody();
// check for ClearTokens (CAT uses ClearTokens)
if (!setupBody.HasOptionalField(H225_Setup_UUIE::e_tokens)) {
PTRACE(3, "RADAUTH\t" << GetName() << " Setup auth failed: no tokens");
authData.m_rejectReason = H225_ReleaseCompleteReason::e_securityDenied;
return GetDefaultStatus();
}
const int result = CheckTokens(pdu, setupBody.m_tokens, NULL, username);
if (result != e_ok)
authData.m_rejectCause = Q931::CallRejected;
return result;
}
RadAliasAuth::RadAliasAuth(
const char* authName
)
:
RadAuthBase(authName, RadAliasAuthConfigSectionName)
{
m_fixedUsername = GetConfig()->GetString(
RadAliasAuthConfigSectionName, "FixedUsername", ""
);
m_fixedPassword = Toolkit::Instance()->ReadPassword(
RadAliasAuthConfigSectionName, "FixedPassword"
);
}
RadAliasAuth::~RadAliasAuth()
{
}
int RadAliasAuth::AppendUsernameAndPassword(
RadiusPDU& pdu,
RasPDU<H225_RegistrationRequest>& rrqPdu,
RRQAuthData& authData,
PString* username
) const
{
const PString id = GetUsername(rrqPdu);
if (id.IsEmpty() && m_fixedUsername.IsEmpty()) {
PTRACE(3, "RADAUTH\t" << GetName() << " RRQ check failed: "
"neither FixedUsername nor alias inside RRQ were found"
);
authData.m_rejectReason = H225_RegistrationRejectReason::e_securityDenial;
return GetDefaultStatus();
}
// append User-Name
pdu.AppendAttr(RadiusAttr::UserName,
m_fixedUsername.IsEmpty() ? id : m_fixedUsername
);
if (username != NULL)
*username = (const char*)id;
// append User-Password
if (!m_fixedPassword)
pdu.AppendAttr(RadiusAttr::UserPassword, m_fixedPassword);
else
pdu.AppendAttr(RadiusAttr::UserPassword,
m_fixedUsername.IsEmpty() ? id : m_fixedUsername
);
return e_ok;
}
int RadAliasAuth::AppendUsernameAndPassword(
RadiusPDU& pdu,
RasPDU<H225_AdmissionRequest>& arqPdu,
ARQAuthData& authData,
PString* username
) const
{
const PString id = GetUsername(arqPdu, authData);
if (id.IsEmpty() && m_fixedUsername.IsEmpty()) {
PTRACE(3, "RADAUTH\t" << GetName() << " ARQ check failed: "
"neither FixedUsername nor alias inside ARQ were found"
);
authData.m_rejectReason = H225_AdmissionRejectReason::e_securityDenial;
return GetDefaultStatus();
}
// append User-Name
pdu.AppendAttr(RadiusAttr::UserName,
m_fixedUsername.IsEmpty() ? id : m_fixedUsername
);
if (username != NULL)
*username = (const char*)id;
if (!m_fixedPassword)
pdu.AppendAttr(RadiusAttr::UserPassword, m_fixedPassword);
else
pdu.AppendAttr(RadiusAttr::UserPassword,
m_fixedUsername.IsEmpty() ? id : m_fixedUsername
);
return e_ok;
}
int RadAliasAuth::AppendUsernameAndPassword(
RadiusPDU& pdu,
SetupMsg &setup,
endptr& /*callingEP*/,
SetupAuthData& authData,
PString* username
) const
{
const PString id = GetUsername(setup, authData);
if (id.IsEmpty() && m_fixedUsername.IsEmpty()) {
PTRACE(3, "RADAUTH\t" << GetName() << " Setup check failed: "
"neither FixedUsername nor alias inside Setup were found"
);
authData.m_rejectReason = H225_ReleaseCompleteReason::e_badFormatAddress;
return GetDefaultStatus();
}
// append User-Name
pdu.AppendAttr(RadiusAttr::UserName,
m_fixedUsername.IsEmpty() ? id : m_fixedUsername
);
if (username != NULL)
*username = (const char*)id;
if (!m_fixedPassword)
pdu.AppendAttr(RadiusAttr::UserPassword, m_fixedPassword);
else
pdu.AppendAttr(RadiusAttr::UserPassword,
m_fixedUsername.IsEmpty() ? id : m_fixedUsername
);
return e_ok;
}
namespace {
GkAuthCreator<RadAuth> RadAuthCreator("RadAuth");
GkAuthCreator<RadAliasAuth> RadAliasAuthCreator("RadAliasAuth");
}
#endif /* HAS_RADIUS */
syntax highlighted by Code2HTML, v. 0.9.1