//////////////////////////////////////////////////////////////////
//
// gkauth.cxx
//
// 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.
//
// History:
// 2001/09/19 initial version (Chih-Wei Huang)
// 2003/07/16 revision for thread-safe
//
//////////////////////////////////////////////////////////////////
#if defined(_WIN32) && (_MSC_VER <= 1200)
#pragma warning(disable:4786) // warning about too long debug symbol off
#pragma warning(disable:4284)
#endif
#include <ptlib.h>
#include <h235.h>
#include <h323pdu.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 "sigmsg.h"
#include "Routing.h"
#include "gkauth.h"
namespace {
const char* const GkAuthSectionName = "Gatekeeper::Auth";
const char OID_CAT[] = "1.2.840.113548.10.1.2.1";
}
using std::stable_sort;
using std::for_each;
using std::find_if;
using std::greater;
ARQAuthData::ARQAuthData(
/// an endpoint requesting admission
const endptr& ep,
/// call record matching this ARQ (if any)
const callptr& call
) : m_rejectReason(-1), m_callDurationLimit(-1),
m_requestingEP(ep), m_call(call), m_billingMode(-1),
m_routeToAlias(NULL), m_proxyMode(CallRec::ProxyDetect)
{
}
ARQAuthData::ARQAuthData(
const ARQAuthData& obj
) : m_rejectReason(obj.m_rejectReason),
m_callDurationLimit(obj.m_callDurationLimit),
m_requestingEP(obj.m_requestingEP), m_call(obj.m_call),
m_billingMode(obj.m_billingMode), m_routeToAlias(NULL),
m_destinationRoutes(obj.m_destinationRoutes), m_proxyMode(obj.m_proxyMode)
{
if (obj.m_routeToAlias)
m_routeToAlias = new H225_AliasAddress(*obj.m_routeToAlias);
}
ARQAuthData& ARQAuthData::operator=(const ARQAuthData& obj)
{
m_callDurationLimit = obj.m_callDurationLimit;
m_requestingEP = obj.m_requestingEP;
m_call = obj.m_call;
m_billingMode = obj.m_billingMode;
m_proxyMode = obj.m_proxyMode;
delete m_routeToAlias;
m_routeToAlias = NULL;
if (obj.m_routeToAlias)
m_routeToAlias = new H225_AliasAddress(*obj.m_routeToAlias);
m_destinationRoutes = obj.m_destinationRoutes;
return *this;
}
ARQAuthData::~ARQAuthData()
{
delete m_routeToAlias;
}
void ARQAuthData::SetRouteToAlias(H225_AliasAddress* alias)
{
delete m_routeToAlias;
m_routeToAlias = alias;
}
void ARQAuthData::SetRouteToAlias(const H225_AliasAddress& alias)
{
SetRouteToAlias(new H225_AliasAddress(alias));
}
void ARQAuthData::SetRouteToAlias(const PString& alias, int tag)
{
SetRouteToAlias(new H225_AliasAddress);
H323SetAliasAddress(alias, *m_routeToAlias, tag);
}
SetupAuthData::SetupAuthData(
/// call associated with the message (if any)
const callptr& call,
/// is the Setup message from a registered endpoint
bool fromRegistered
) : m_rejectReason(-1), m_rejectCause(-1), m_callDurationLimit(-1),
m_call(call), m_fromRegistered(fromRegistered),
m_routeToAlias(NULL), m_proxyMode(CallRec::ProxyDetect)
{
}
SetupAuthData::SetupAuthData(
const SetupAuthData& obj
) : m_rejectReason(obj.m_rejectReason), m_rejectCause(obj.m_rejectCause),
m_callDurationLimit(obj.m_callDurationLimit), m_call(obj.m_call),
m_fromRegistered(obj.m_fromRegistered),
m_routeToAlias(NULL), m_destinationRoutes(obj.m_destinationRoutes),
m_proxyMode(obj.m_proxyMode)
{
if (obj.m_routeToAlias)
m_routeToAlias = new H225_AliasAddress(*obj.m_routeToAlias);
}
SetupAuthData& SetupAuthData::operator=(const SetupAuthData& obj)
{
m_rejectReason = obj.m_rejectReason;
m_rejectCause = obj.m_rejectCause;
m_callDurationLimit = obj.m_callDurationLimit;
m_call = obj.m_call;
m_fromRegistered = obj.m_fromRegistered;
m_proxyMode = obj.m_proxyMode;
delete m_routeToAlias;
m_routeToAlias = NULL;
if (obj.m_routeToAlias)
m_routeToAlias = new H225_AliasAddress(*obj.m_routeToAlias);
m_destinationRoutes = obj.m_destinationRoutes;
return *this;
}
SetupAuthData::~SetupAuthData()
{
delete m_routeToAlias;
}
void SetupAuthData::SetRouteToAlias(H225_AliasAddress* alias)
{
delete m_routeToAlias;
m_routeToAlias = alias;
}
void SetupAuthData::SetRouteToAlias(const H225_AliasAddress& alias)
{
SetRouteToAlias(new H225_AliasAddress(alias));
}
void SetupAuthData::SetRouteToAlias(const PString& alias, int tag)
{
SetRouteToAlias(new H225_AliasAddress);
H323SetAliasAddress(alias, *m_routeToAlias, tag);
}
// class GkAuthenticator
GkAuthenticator::GkAuthenticator(
const char* name, /// a name for the module (to be used in the config file)
unsigned supportedRasChecks, /// RAS checks supported by this module
unsigned supportedMiscChecks /// non-RAS checks supported by this module
)
: NamedObject(name), m_defaultStatus(e_fail), m_controlFlag(e_Required),
m_enabledRasChecks(~0U), m_supportedRasChecks(supportedRasChecks),
m_enabledMiscChecks(~0U), m_supportedMiscChecks(supportedMiscChecks),
m_config(GkConfig()), m_h235Authenticators(NULL)
{
const PStringArray control(
m_config->GetString(GkAuthSectionName, name, "").Tokenise(";,")
);
if (control.GetSize() > 0) {
const PString controlStr = control[0].Trim();
if (strcasecmp(name, "default") == 0)
m_controlFlag = e_Sufficient,
m_defaultStatus = Toolkit::AsBool(controlStr) ? e_ok : e_fail;
else if (controlStr *= "optional")
m_controlFlag = e_Optional, m_defaultStatus = e_next;
else if (controlStr *= "required")
m_controlFlag = e_Required, m_defaultStatus = e_fail;
else if (controlStr *= "sufficient")
m_controlFlag = e_Sufficient, m_defaultStatus = e_fail;
else
PTRACE(1, "GKAUTH\tInvalid control flag '" << controlStr
<< "' specified in the config for " << GetName()
);
} else
PTRACE(1, "GKAUTH\tNo control flag specified in the config for module '"
<< GetName() << '\''
);
std::map<PString, unsigned> rasmap;
rasmap["GRQ"] = RasInfo<H225_GatekeeperRequest>::flag,
rasmap["RRQ"] = RasInfo<H225_RegistrationRequest>::flag,
rasmap["URQ"] = RasInfo<H225_UnregistrationRequest>::flag,
rasmap["ARQ"] = RasInfo<H225_AdmissionRequest>::flag,
rasmap["BRQ"] = RasInfo<H225_BandwidthRequest>::flag,
rasmap["DRQ"] = RasInfo<H225_DisengageRequest>::flag,
rasmap["LRQ"] = RasInfo<H225_LocationRequest>::flag,
rasmap["IRQ"] = RasInfo<H225_InfoRequest>::flag;
std::map<PString, unsigned> miscmap;
miscmap["SETUP"] = e_Setup;
miscmap["SETUPUNREG"] = e_SetupUnreg;
if (control.GetSize() > 1) {
m_enabledRasChecks = 0;
m_enabledMiscChecks = 0;
for (PINDEX i = 1; i < control.GetSize(); ++i) {
const PString checkStr = control[i].Trim().ToUpper();
if (rasmap.find(checkStr) != rasmap.end()) {
m_enabledRasChecks |= rasmap[checkStr];
if ((m_supportedRasChecks & rasmap[checkStr]) != rasmap[checkStr])
PTRACE(1, "GKAUTH\t" << GetName() << " does not support '"
<< control[i] << "' check"
);
} else if(miscmap.find(checkStr) != miscmap.end()) {
m_enabledMiscChecks |= miscmap[checkStr];
if ((m_supportedMiscChecks & miscmap[checkStr]) != miscmap[checkStr])
PTRACE(1, "GKAUTH\t" << GetName() << " does not support '"
<< control[i] << "' check"
);
} else
PTRACE(1, "GKAUTH\tInvalid check flag '" << control[i]
<< "' specified in the config for " << GetName()
);
}
if ((m_enabledRasChecks & m_supportedRasChecks) == 0
&& (m_enabledMiscChecks & m_supportedMiscChecks) == 0)
PTRACE(1, "GKAUTH\tNo check flags have been specified "
"in the config for " << GetName() << " - it will be disabled"
);
}
#if PTRACING
// convert bit flags to human readable names
PString rasFlagsStr, miscFlagsStr;
std::map<PString, unsigned>::const_iterator iter = rasmap.begin();
while (iter != rasmap.end()) {
if (m_enabledRasChecks & iter->second) {
if (!rasFlagsStr)
rasFlagsStr += ' ';
rasFlagsStr += iter->first;
}
iter++;
}
iter = miscmap.begin();
while (iter != miscmap.end()) {
if (m_enabledMiscChecks & iter->second) {
if (!miscFlagsStr)
miscFlagsStr += ' ';
miscFlagsStr += iter->first;
}
iter++;
}
if (rasFlagsStr.IsEmpty())
rasFlagsStr = "NONE";
if (miscFlagsStr.IsEmpty())
miscFlagsStr = "NONE";
PTRACE(1, "GKAUTH\t" << GetName() << " rule added to check RAS: "
<< rasFlagsStr << ", OTHER: " << miscFlagsStr
);
#endif
}
GkAuthenticator::~GkAuthenticator()
{
delete m_h235Authenticators;
PTRACE(1, "GKAUTH\t" << GetName() << " rule removed");
}
int GkAuthenticator::Check(RasPDU<H225_GatekeeperRequest> &, unsigned &)
{
return IsRasCheckEnabled(RasInfo<H225_GatekeeperRequest>::flag)
? m_defaultStatus : e_next;
}
int GkAuthenticator::Check(
/// a request to be authenticated
RasPDU<H225_RegistrationRequest>& /*request*/,
/// authorization data (reject reason, ...)
RRQAuthData& /*authData*/
)
{
return IsRasCheckEnabled(RasInfo<H225_RegistrationRequest>::flag)
? m_defaultStatus : e_next;
}
int GkAuthenticator::Check(RasPDU<H225_UnregistrationRequest> &, unsigned &)
{
return IsRasCheckEnabled(RasInfo<H225_UnregistrationRequest>::flag)
? m_defaultStatus : e_next;
}
int GkAuthenticator::Check(
/// a request to be authenticated
RasPDU<H225_AdmissionRequest>& /*req*/,
/// authorization data (call duration limit, reject reason, ...)
ARQAuthData& /*authData*/
)
{
return IsRasCheckEnabled(RasInfo<H225_AdmissionRequest>::flag)
? m_defaultStatus : e_next;
}
int GkAuthenticator::Check(RasPDU<H225_BandwidthRequest> &, unsigned &)
{
return IsRasCheckEnabled(RasInfo<H225_BandwidthRequest>::flag)
? m_defaultStatus : e_next;
}
int GkAuthenticator::Check(RasPDU<H225_DisengageRequest> &, unsigned &)
{
return IsRasCheckEnabled(RasInfo<H225_DisengageRequest>::flag)
? m_defaultStatus : e_next;
}
int GkAuthenticator::Check(RasPDU<H225_LocationRequest> &, unsigned &)
{
return IsRasCheckEnabled(RasInfo<H225_LocationRequest>::flag)
? m_defaultStatus : e_next;
}
int GkAuthenticator::Check(RasPDU<H225_InfoRequest> &, unsigned &)
{
return IsRasCheckEnabled(RasInfo<H225_InfoRequest>::flag)
? m_defaultStatus : e_next;
}
int GkAuthenticator::Check(
SetupMsg &/*setup*/,
/// authorization data (call duration limit, reject reason, ...)
SetupAuthData& /*authData*/
)
{
return (IsMiscCheckEnabled(e_Setup) || IsMiscCheckEnabled(e_SetupUnreg))
? m_defaultStatus : e_next;
}
bool GkAuthenticator::GetH235Capability(
/// append supported authentication mechanism to this array
H225_ArrayOf_AuthenticationMechanism& mechanisms,
/// append supported algorithm OIDs for the given authentication
/// mechanism
H225_ArrayOf_PASN_ObjectId& algorithmOIDs
) const
{
if (m_h235Authenticators && m_h235Authenticators->GetSize() > 0) {
for (PINDEX i = 0; i < m_h235Authenticators->GetSize(); i++)
(*m_h235Authenticators)[i].SetCapability(mechanisms, algorithmOIDs);
return true;
} else
return false;
}
bool GkAuthenticator::IsH235Capability(
/// authentication mechanism
const H235_AuthenticationMechanism& mechanism,
/// algorithm OID for the given authentication mechanism
const PASN_ObjectId& algorithmOID
) const
{
if (m_h235Authenticators)
for (PINDEX i = 0; i < m_h235Authenticators->GetSize(); i++)
if ((*m_h235Authenticators)[i].IsCapability(mechanism, algorithmOID))
return true;
return false;
}
bool GkAuthenticator::IsH235Capable() const
{
return m_h235Authenticators && m_h235Authenticators->GetSize() > 0;
}
void GkAuthenticator::SetSupportedChecks(
unsigned supportedRasChecks, /// RAS checks supported by this module
unsigned supportedMiscChecks /// non-RAS checks supported by this module
)
{
m_supportedRasChecks = supportedRasChecks;
m_supportedMiscChecks = supportedMiscChecks;
}
void GkAuthenticator::AppendH235Authenticator(
H235Authenticator* h235Auth /// H.235 authenticator to append
)
{
if (h235Auth) {
if (m_h235Authenticators == NULL)
m_h235Authenticators = new H235Authenticators();
m_h235Authenticators->Append(h235Auth);
}
}
PString GkAuthenticator::GetUsername(
/// RRQ message with additional data
const RasPDU<H225_RegistrationRequest>& request
) const
{
const H225_RegistrationRequest& rrq = request;
PString username;
if (rrq.HasOptionalField(H225_RegistrationRequest::e_terminalAlias))
username = GetBestAliasAddressString(rrq.m_terminalAlias, false,
AliasAddressTagMask(H225_AliasAddress::e_h323_ID),
AliasAddressTagMask(H225_AliasAddress::e_email_ID)
| AliasAddressTagMask(H225_AliasAddress::e_url_ID)
);
if (username.IsEmpty()) {
PIPSocket::Address addr;
if (rrq.m_callSignalAddress.GetSize() > 0
&& GetIPFromTransportAddr(rrq.m_callSignalAddress[0], addr)
&& addr.IsValid())
username = addr.AsString();
else if (rrq.m_rasAddress.GetSize() > 0
&& GetIPFromTransportAddr(rrq.m_rasAddress[0], addr)
&& addr.IsValid())
username = addr.AsString();
}
return username;
}
PString GkAuthenticator::GetUsername(
/// ARQ message with additional data
const RasPDU<H225_AdmissionRequest>& request,
/// additional data
ARQAuthData& authData
) const
{
const H225_AdmissionRequest& arq = request;
const bool hasCall = authData.m_call.operator->() != NULL;
PString username;
/// try to find h323_ID, email_ID or url_ID to use for User-Name
if (!arq.m_answerCall)
username = GetBestAliasAddressString(arq.m_srcInfo, true,
AliasAddressTagMask(H225_AliasAddress::e_h323_ID),
AliasAddressTagMask(H225_AliasAddress::e_email_ID)
| AliasAddressTagMask(H225_AliasAddress::e_url_ID)
);
else if (hasCall)
username = GetBestAliasAddressString(authData.m_call->GetSourceAddress(), true,
AliasAddressTagMask(H225_AliasAddress::e_h323_ID),
AliasAddressTagMask(H225_AliasAddress::e_email_ID)
| AliasAddressTagMask(H225_AliasAddress::e_url_ID)
);
if (authData.m_requestingEP && (username.IsEmpty()
|| FindAlias(authData.m_requestingEP->GetAliases(), username) == P_MAX_INDEX))
username = GetBestAliasAddressString(authData.m_requestingEP->GetAliases(), false,
AliasAddressTagMask(H225_AliasAddress::e_h323_ID),
AliasAddressTagMask(H225_AliasAddress::e_email_ID)
| AliasAddressTagMask(H225_AliasAddress::e_url_ID)
);
/// if no h323_ID, email_ID or url_ID has been found, try to find any alias
if (username.IsEmpty())
if (!arq.m_answerCall)
username = GetBestAliasAddressString(arq.m_srcInfo, false,
AliasAddressTagMask(H225_AliasAddress::e_h323_ID),
AliasAddressTagMask(H225_AliasAddress::e_email_ID)
| AliasAddressTagMask(H225_AliasAddress::e_url_ID)
);
else if (hasCall)
username = GetBestAliasAddressString(
authData.m_call->GetSourceAddress(), true,
AliasAddressTagMask(H225_AliasAddress::e_h323_ID),
AliasAddressTagMask(H225_AliasAddress::e_email_ID)
| AliasAddressTagMask(H225_AliasAddress::e_url_ID)
);
if (username.IsEmpty()) {
PIPSocket::Address addr;
if (arq.HasOptionalField(H225_AdmissionRequest::e_srcCallSignalAddress)
&& GetIPFromTransportAddr(arq.m_srcCallSignalAddress, addr)
&& addr.IsValid())
username = addr.AsString();
else if (authData.m_requestingEP
&& GetIPFromTransportAddr(authData.m_requestingEP->GetCallSignalAddress(), addr)
&& addr.IsValid())
username = addr.AsString();
}
return username;
}
PString GkAuthenticator::GetUsername(
const SetupMsg &setup,
/// additional data
SetupAuthData& authData
) const
{
const bool hasCall = authData.m_call.operator->() != NULL;
PString username;
endptr callingEP;
Q931& q931pdu = setup.GetQ931();
H225_Setup_UUIE &setupBody = setup.GetUUIEBody();
if (hasCall)
callingEP = authData.m_call->GetCallingParty();
if (setupBody.HasOptionalField(H225_Setup_UUIE::e_sourceAddress)) {
username = GetBestAliasAddressString(setupBody.m_sourceAddress, true,
AliasAddressTagMask(H225_AliasAddress::e_h323_ID),
AliasAddressTagMask(H225_AliasAddress::e_email_ID)
| AliasAddressTagMask(H225_AliasAddress::e_url_ID)
);
if (!username && callingEP
&& FindAlias(callingEP->GetAliases(), username) == P_MAX_INDEX)
username = PString();
}
if (username.IsEmpty() && hasCall) {
username = GetBestAliasAddressString(
authData.m_call->GetSourceAddress(), true,
AliasAddressTagMask(H225_AliasAddress::e_h323_ID),
AliasAddressTagMask(H225_AliasAddress::e_email_ID)
| AliasAddressTagMask(H225_AliasAddress::e_url_ID)
);
if (!username && callingEP
&& FindAlias(callingEP->GetAliases(), username) == P_MAX_INDEX)
username = PString();
}
if (username.IsEmpty() && callingEP)
username = GetBestAliasAddressString(callingEP->GetAliases(), false,
AliasAddressTagMask(H225_AliasAddress::e_h323_ID),
AliasAddressTagMask(H225_AliasAddress::e_email_ID)
| AliasAddressTagMask(H225_AliasAddress::e_url_ID)
);
if (username.IsEmpty() && hasCall)
username = GetBestAliasAddressString(
authData.m_call->GetSourceAddress(), false,
AliasAddressTagMask(H225_AliasAddress::e_h323_ID),
AliasAddressTagMask(H225_AliasAddress::e_email_ID)
| AliasAddressTagMask(H225_AliasAddress::e_url_ID)
);
if (username.IsEmpty() && setupBody.HasOptionalField(H225_Setup_UUIE::e_sourceAddress))
username = GetBestAliasAddressString(setupBody.m_sourceAddress, false,
AliasAddressTagMask(H225_AliasAddress::e_h323_ID),
AliasAddressTagMask(H225_AliasAddress::e_email_ID)
| AliasAddressTagMask(H225_AliasAddress::e_url_ID)
);
if (username.IsEmpty())
q931pdu.GetCallingPartyNumber(username);
if (username.IsEmpty()) {
PIPSocket::Address addr(0);
WORD port = 0;
bool addrValid = false;
if (hasCall)
addrValid = authData.m_call->GetSrcSignalAddr(addr, port) && addr.IsValid();
if (!addrValid && setupBody.HasOptionalField(H225_Setup_UUIE::e_sourceCallSignalAddress))
addrValid = GetIPFromTransportAddr(setupBody.m_sourceCallSignalAddress, addr)
&& addr.IsValid();
if (!addrValid && callingEP)
addrValid = GetIPFromTransportAddr(callingEP->GetCallSignalAddress(), addr)
&& addr.IsValid();
if (addrValid)
username = addr.AsString();
}
return username;
}
PString GkAuthenticator::GetCallingStationId(
/// ARQ message with additional data
const RasPDU<H225_AdmissionRequest> &/*request*/,
/// additional data
ARQAuthData &authData
) const
{
return authData.m_callingStationId;
}
PString GkAuthenticator::GetCallingStationId(
const SetupMsg &/*setup*/,
/// additional data
SetupAuthData &authData
) const
{
return authData.m_callingStationId;
}
PString GkAuthenticator::GetCalledStationId(
/// ARQ message with additional data
const RasPDU<H225_AdmissionRequest> &/*request*/,
/// additional data
ARQAuthData &authData
) const
{
return authData.m_calledStationId;
}
PString GkAuthenticator::GetCalledStationId(
const SetupMsg &/*setup*/,
/// additional data
SetupAuthData &authData
) const
{
return authData.m_calledStationId;
}
PString GkAuthenticator::GetDialedNumber(
/// ARQ message with additional data
const RasPDU<H225_AdmissionRequest> &/*request*/,
/// additional data
ARQAuthData &authData
) const
{
return authData.m_dialedNumber;
}
PString GkAuthenticator::GetDialedNumber(
const SetupMsg &/*setup*/,
/// additional data
SetupAuthData &authData
) const
{
return authData.m_dialedNumber;
}
// class GkAuthenticatorList
GkAuthenticatorList::GkAuthenticatorList()
: m_mechanisms(new H225_ArrayOf_AuthenticationMechanism),
m_algorithmOIDs(new H225_ArrayOf_PASN_ObjectId)
{
}
GkAuthenticatorList::~GkAuthenticatorList()
{
WriteLock lock(m_reloadMutex);
DeleteObjectsInContainer(m_authenticators);
m_authenticators.clear();
delete m_mechanisms;
delete m_algorithmOIDs;
}
void GkAuthenticatorList::OnReload()
{
// lock here to prevent too early authenticator destruction
// from another thread
WriteLock lock(m_reloadMutex);
std::list<GkAuthenticator*> authenticators;
GkAuthenticator *auth;
const PStringArray authRules = GkConfig()->GetKeys(GkAuthSectionName);
for (PINDEX r = 0; r < authRules.GetSize(); r++) {
auth = Factory<GkAuthenticator>::Create(authRules[r]);
if (auth)
authenticators.push_back(auth);
}
H225_ArrayOf_AuthenticationMechanism mechanisms;
H225_ArrayOf_PASN_ObjectId algorithmOIDs;
bool found = false;
int i, j, k;
// scan all authenticators that are either "required" or "sufficient"
// (skip "optional") and fill #mechanisms# and #algorithmOIDs# arrays
// with H.235 capabilities that are supported by all these authenticators
std::list<GkAuthenticator*>::const_iterator iter = authenticators.begin();
while (iter != authenticators.end()) {
auth = *iter++;
if (auth->IsH235Capable()
&& (auth->GetControlFlag() == GkAuthenticator::e_Required
|| auth->GetControlFlag() == GkAuthenticator::e_Sufficient)) {
if (mechanisms.GetSize() == 0) {
// append H.235 capability to empty arrays
auth->GetH235Capability(mechanisms, algorithmOIDs);
// should never happen, but we should check just for a case
if (algorithmOIDs.GetSize() == 0)
mechanisms.RemoveAll();
else
found = true;
continue;
}
// Already have H.235 capabilities - check the current
// authenticator if it supports any of the capabilities.
// Remove capabilities that are not supported
H225_ArrayOf_AuthenticationMechanism matchedMechanisms;
for (i = 0; i < algorithmOIDs.GetSize(); i++) {
bool matched = false;
for (j = 0; j < mechanisms.GetSize(); j++)
if (auth->IsH235Capability(mechanisms[j], algorithmOIDs[i])) {
for (k = 0; k < matchedMechanisms.GetSize(); k++)
if (matchedMechanisms[k].GetTag() == mechanisms[j].GetTag())
break;
if (k == matchedMechanisms.GetSize()) {
matchedMechanisms.SetSize(k+1);
matchedMechanisms[k].SetTag(mechanisms[j].GetTag());
}
matched = true;
}
if (!matched) {
PTRACE(5, "GKAUTH\tAlgorithm OID: " << algorithmOIDs[i]
<< " removed from GCF list"
);
algorithmOIDs.RemoveAt(i--);
}
}
for (i = 0; i < mechanisms.GetSize(); i++) {
for (j = 0; j < matchedMechanisms.GetSize(); j++)
if (mechanisms[i].GetTag() == matchedMechanisms[j].GetTag())
break;
if (j == matchedMechanisms.GetSize()) {
PTRACE(5, "GKAUTH\tAuth method: " << mechanisms[i]
<< " removed from GCF list"
);
mechanisms.RemoveAt(i--);
}
}
if (mechanisms.GetSize() == 0 || algorithmOIDs.GetSize() == 0)
break;
}
}
// Scan "optional" authenticators if the above procedure has not found
// any H.235 capabilities or has found more than one
if ((!found) || mechanisms.GetSize() > 1 || algorithmOIDs.GetSize() > 1) {
iter = authenticators.begin();
while (iter != authenticators.end()) {
auth = *iter++;
if (auth->IsH235Capable()
&& auth->GetControlFlag() == GkAuthenticator::e_Optional) {
if (mechanisms.GetSize() == 0) {
auth->GetH235Capability(mechanisms, algorithmOIDs);
if (algorithmOIDs.GetSize() == 0 )
mechanisms.RemoveAll();
else
found = true;
continue;
}
H225_ArrayOf_AuthenticationMechanism matchedMechanisms;
for (i = 0; i < algorithmOIDs.GetSize(); i++) {
bool matched = false;
for (j = 0; j < mechanisms.GetSize(); j++)
if (auth->IsH235Capability(mechanisms[j], algorithmOIDs[i])) {
for (k = 0; k < matchedMechanisms.GetSize(); k++)
if (matchedMechanisms[k].GetTag() == mechanisms[j].GetTag())
break;
if (k == matchedMechanisms.GetSize()) {
matchedMechanisms.SetSize(k+1);
matchedMechanisms[k].SetTag(mechanisms[j].GetTag());
}
matched = true;
}
if (!matched) {
PTRACE(5, "GKAUTH\tAlgorithm OID: " << algorithmOIDs[i]
<< " removed from GCF list"
);
algorithmOIDs.RemoveAt(i--);
}
}
for (i = 0; i < mechanisms.GetSize(); i++) {
for (j = 0; j < matchedMechanisms.GetSize(); j++)
if (mechanisms[i].GetTag() == matchedMechanisms[j].GetTag())
break;
if (j == matchedMechanisms.GetSize()) {
PTRACE(5, "GKAUTH\tAuth method: " << mechanisms[i]
<< " removed from GCF list"
);
mechanisms.RemoveAt(i--);
}
}
if ((mechanisms.GetSize() == 0) || (algorithmOIDs.GetSize() == 0))
break;
}
}
}
if (mechanisms.GetSize() > 0 && algorithmOIDs.GetSize() > 0) {
if (PTrace::CanTrace(4)) {
#if PTRACING
ostream& strm = PTrace::Begin(4,__FILE__,__LINE__);
strm <<"GkAuth\tH.235 capabilities selected for GCF:\n";
strm <<"\tAuthentication mechanisms: \n";
for (i = 0; i < mechanisms.GetSize(); i++)
strm << "\t\t" << mechanisms[i] << '\n';
strm <<"\tAuthentication algorithm OIDs: \n";
for (i = 0; i < algorithmOIDs.GetSize(); i++)
strm << "\t\t" << algorithmOIDs[i] << '\n';
PTrace::End(strm);
#endif
}
} else {
PTRACE(4, "GKAUTH\tH.235 security is not active or conflicting "
"H.235 capabilities are active - GCF will not select "
"any particular capability"
);
mechanisms.RemoveAll();
algorithmOIDs.RemoveAll();
}
// now switch to new setting
*m_mechanisms = mechanisms;
*m_algorithmOIDs = algorithmOIDs;
DeleteObjectsInContainer(m_authenticators);
m_authenticators.clear();
m_authenticators = authenticators;
}
void GkAuthenticatorList::SelectH235Capability(
const H225_GatekeeperRequest& grq,
H225_GatekeeperConfirm& gcf
)
{
ReadLock lock(m_reloadMutex);
if (m_authenticators.empty())
return;
// if GRQ does not contain a list of authentication mechanisms simply return
if (!(grq.HasOptionalField(H225_GatekeeperRequest::e_authenticationCapability)
&& grq.HasOptionalField(H225_GatekeeperRequest::e_algorithmOIDs)
&& grq.m_authenticationCapability.GetSize() > 0
&& grq.m_algorithmOIDs.GetSize() > 0))
return;
H225_ArrayOf_AuthenticationMechanism & mechanisms = *m_mechanisms;
H225_ArrayOf_PASN_ObjectId & algorithmOIDs = *m_algorithmOIDs;
// And now match H.235 capabilities found with those from GRQ
// to find the one to be returned in GCF
for (int i = 0; i < grq.m_authenticationCapability.GetSize(); i++)
for (int j = 0; j < mechanisms.GetSize(); j++)
if (grq.m_authenticationCapability[i].GetTag() == mechanisms[j].GetTag())
for (int l = 0; l < algorithmOIDs.GetSize(); l++)
for (int k = 0; k < grq.m_algorithmOIDs.GetSize(); k++)
if (grq.m_algorithmOIDs[k] == algorithmOIDs[l]) {
std::list<GkAuthenticator*>::const_iterator iter = m_authenticators.begin();
while (iter != m_authenticators.end()) {
GkAuthenticator* auth = *iter++;
if (auth->IsH235Capable() && auth->IsH235Capability(mechanisms[j], algorithmOIDs[l])) {
gcf.IncludeOptionalField(H225_GatekeeperConfirm::e_authenticationMode);
gcf.m_authenticationMode = mechanisms[j];
gcf.IncludeOptionalField(H225_GatekeeperConfirm::e_algorithmOID);
gcf.m_algorithmOID = algorithmOIDs[l];
PTRACE(4, "GKAUTH\tGCF will select authentication "
"mechanism: " << mechanisms[j]
<< " and algorithm OID: "<< algorithmOIDs[l]
);
return;
}
}
PTRACE(5, "GKAUTH\tAuthentication mechanism: "
<< mechanisms[j] << " and algorithm OID: "
<< algorithmOIDs[l] << " removed from GCF list"
);
}
}
bool GkAuthenticatorList::Validate(
/// RRQ to be validated by authenticators
RasPDU<H225_RegistrationRequest>& request,
/// authorization data (reject reason, ...)
RRQAuthData& authData
)
{
ReadLock lock(m_reloadMutex);
std::list<GkAuthenticator*>::const_iterator i = m_authenticators.begin();
while (i != m_authenticators.end()) {
GkAuthenticator* auth = *i++;
if (auth->IsRasCheckEnabled(RasInfo<H225_RegistrationRequest>::flag)) {
const int result = auth->Check(request, authData);
if (result == GkAuthenticator::e_ok) {
PTRACE(3, "GKAUTH\t" << auth->GetName() << " RRQ check ok");
if (auth->GetControlFlag() != GkAuthenticator::e_Required)
return true;
} else if (result == GkAuthenticator::e_fail) {
PTRACE(3, "GKAUTH\t" << auth->GetName() << " RRQ check failed");
return false;
}
}
}
return true;
}
bool GkAuthenticatorList::Validate(
/// ARQ to be validated by authenticators
RasPDU<H225_AdmissionRequest>& request,
/// authorization data (call duration limit, reject reason, ...)
ARQAuthData& authData
)
{
ReadLock lock(m_reloadMutex);
std::list<GkAuthenticator*>::const_iterator i = m_authenticators.begin();
while (i != m_authenticators.end()) {
GkAuthenticator* auth = *i++;
if (auth->IsRasCheckEnabled(RasInfo<H225_AdmissionRequest>::flag)) {
const long oldDurationLimit = authData.m_callDurationLimit;
const int result = auth->Check(request, authData);
if (authData.m_callDurationLimit == 0) {
PTRACE(3, "GKAUTH\t" << auth->GetName() << " ARQ check failed: "
"call duration 0"
);
return false;
}
if (authData.m_callDurationLimit >= 0 && oldDurationLimit >= 0)
authData.m_callDurationLimit = PMIN(
authData.m_callDurationLimit, oldDurationLimit
);
else
authData.m_callDurationLimit = PMAX(
authData.m_callDurationLimit, oldDurationLimit
);
if (result == GkAuthenticator::e_ok) {
PTRACE(3, "GKAUTH\t" << auth->GetName() << " ARQ check ok");
if (auth->GetControlFlag() != GkAuthenticator::e_Required)
return true;
} else if (result == GkAuthenticator::e_fail) {
PTRACE(3, "GKAUTH\t" << auth->GetName() << " ARQ check failed");
return false;
}
}
}
return true;
}
bool GkAuthenticatorList::Validate(
SetupMsg &setup,
/// authorization data (call duration limit, reject reason, ...)
SetupAuthData& authData
)
{
ReadLock lock(m_reloadMutex);
std::list<GkAuthenticator*>::const_iterator i = m_authenticators.begin();
while (i != m_authenticators.end()) {
GkAuthenticator* auth = *i++;
if (auth->IsMiscCheckEnabled(GkAuthenticator::e_Setup)
|| (!authData.m_fromRegistered
&& auth->IsMiscCheckEnabled(GkAuthenticator::e_SetupUnreg))) {
const long oldDurationLimit = authData.m_callDurationLimit;
const int result = auth->Check(setup, authData);
if (authData.m_callDurationLimit == 0) {
PTRACE(3, "GKAUTH\t" << auth->GetName() << " Setup check failed: "
"call duration limit 0"
);
return false;
}
if (authData.m_callDurationLimit >= 0 && oldDurationLimit >= 0)
authData.m_callDurationLimit = PMIN(
authData.m_callDurationLimit, oldDurationLimit
);
else
authData.m_callDurationLimit = PMAX(
authData.m_callDurationLimit, oldDurationLimit
);
if (result == GkAuthenticator::e_ok) {
PTRACE(3, "GKAUTH\t" << auth->GetName() << " Setup check ok");
if (auth->GetControlFlag() != GkAuthenticator::e_Required)
return true;
} else if (result == GkAuthenticator::e_fail) {
PTRACE(3, "GKAUTH\t" << auth->GetName() << " Setup check failed");
return false;
}
}
}
return true;
}
// class CacheManager
bool CacheManager::Retrieve(
const PString& key, /// the key to look for
PString& value /// filled with the value on return
) const
{
// quick check
if (m_ttl == 0)
return false;
ReadLock lock(m_rwmutex);
std::map<PString, PString>::const_iterator iter = m_cache.find(key);
if (iter == m_cache.end())
return false;
if (m_ttl >= 0) {
std::map<PString, long>::const_iterator i = m_ctime.find(key);
if (i == m_ctime.end() || (time(NULL) - i->second) >= m_ttl)
return false; // cache expired
}
value = (const char *)(iter->second);
return true;
}
void CacheManager::Save(
const PString& key, /// a key to be stored
const PString& value /// a value to be associated with the key
)
{
if (m_ttl != 0) {
WriteLock lock(m_rwmutex);
m_cache[key] = (const char*)value;
m_ctime[key] = time(NULL);
}
}
// class SimplePasswordAuth
SimplePasswordAuth::SimplePasswordAuth(
const char* name,
unsigned supportedRasChecks,
unsigned supportedMiscChecks
)
: GkAuthenticator(name, supportedRasChecks, supportedMiscChecks),
m_cache(NULL)
{
if (!GetConfig()->HasKey(name, "KeyFilled"))
PTRACE(1, "GKAUTH\t" << GetName() << " KeyFilled config variable "
"is missing"
);
m_encryptionKey = GetConfig()->GetInteger(name, "KeyFilled", 0);
m_checkID = Toolkit::AsBool(GetConfig()->GetString(name, "CheckID", "0"));
m_cache = new CacheManager(GetConfig()->GetInteger(name, "PasswordTimeout", -1));
H235Authenticator* authenticator;
authenticator = new H235AuthSimpleMD5;
authenticator->SetLocalId("dummy");
authenticator->SetRemoteId("dummy");
authenticator->SetPassword("dummy");
AppendH235Authenticator(authenticator);
authenticator = new H235AuthCAT;
authenticator->SetLocalId("dummy");
authenticator->SetRemoteId("dummy");
authenticator->SetPassword("dummy");
AppendH235Authenticator(authenticator);
/*
#if P_SSL
authenticator = new H235AuthProcedure1;
authenticator->SetLocalId("dummy");
authenticator->SetRemoteId("dummy");
authenticator->SetPassword("dummy");
AppendH235Authenticator(authenticator);
#endif
*/
}
SimplePasswordAuth::~SimplePasswordAuth()
{
delete m_cache;
}
int SimplePasswordAuth::Check(RasPDU<H225_GatekeeperRequest> & request, unsigned &)
{
return doCheck(request);
}
int SimplePasswordAuth::Check(
RasPDU<H225_RegistrationRequest> & request,
RRQAuthData& /*authData*/
)
{
H225_RegistrationRequest& rrq = request;
return doCheck(request,
rrq.HasOptionalField(H225_RegistrationRequest::e_terminalAlias)
? &rrq.m_terminalAlias : NULL
);
}
int SimplePasswordAuth::Check(RasPDU<H225_UnregistrationRequest> & request, unsigned &)
{
return doCheck(request);
}
int SimplePasswordAuth::Check(RasPDU<H225_BandwidthRequest> & request, unsigned &)
{
return doCheck(request);
}
int SimplePasswordAuth::Check(RasPDU<H225_DisengageRequest> & request, unsigned &)
{
return doCheck(request);
}
int SimplePasswordAuth::Check(RasPDU<H225_LocationRequest> & request, unsigned &)
{
return doCheck(request);
}
int SimplePasswordAuth::Check(RasPDU<H225_InfoRequest> & request, unsigned &)
{
return doCheck(request);
}
int SimplePasswordAuth::Check(
/// ARQ to be authenticated/authorized
RasPDU<H225_AdmissionRequest>& request,
/// authorization data (call duration limit, reject reason, ...)
ARQAuthData& /*authData*/
)
{
H225_AdmissionRequest& arq = request;
return doCheck(request, &arq.m_srcInfo);
}
bool SimplePasswordAuth::GetPassword(
const PString& id, /// get the password for this id
PString& passwd /// filled with the password on return
)
{
if (id.IsEmpty())
return false;
if (!GetConfig()->HasKey(GetName(), id))
return false;
if (strcasecmp(id, "KeyFilled") == 0 || strcasecmp(id, "CheckID") == 0
|| strcasecmp(id, "PasswordTimeout") == 0) {
PTRACE(2, "GKAUTH\t" << GetName() << " trying to get password for "
" the forbidden alias '" << id << '\''
);
return false;
}
passwd = Toolkit::Instance()->ReadPassword(GetName(), id, true);
return true;
}
bool SimplePasswordAuth::InternalGetPassword(
const PString& id, /// get the password for this id
PString& passwd /// filled with the password on return
)
{
if (m_cache->Retrieve(id, passwd)) {
PTRACE(5, "GKAUTH\t" << GetName() << " cached password found for '"
<< id << '\''
);
return true;
}
if (GetPassword(id, passwd)) {
m_cache->Save(id, passwd);
return true;
} else
return false;
}
int SimplePasswordAuth::CheckTokens(
/// an array of tokens to be checked
const H225_ArrayOf_ClearToken& tokens,
/// aliases for the endpoint that generated the tokens
const H225_ArrayOf_AliasAddress* aliases
)
{
for (PINDEX i = 0; i < tokens.GetSize(); i++) {
H235_ClearToken& token = tokens[i];
// check for Cisco Access Token
if (token.m_tokenOID == OID_CAT) {
if (!token.HasOptionalField(H235_ClearToken::e_generalID)) {
PTRACE(3, "GKAUTH\t" << GetName() << " generalID field "
"not found inside CAT token"
);
return e_fail;
}
const PString id = token.m_generalID;
if (m_checkID && (aliases == NULL || FindAlias(*aliases, id) == P_MAX_INDEX)) {
PTRACE(3, "GKAUTH\t" << GetName() << " generalID '" << id
<< "' of CAT token does not match any alias for the endpoint"
);
return e_fail;
}
PString passwd;
if (!InternalGetPassword(id, passwd)) {
PTRACE(3, "GKAUTH\t" << GetName() << " password not found for '"
<< id << '\''
);
return e_fail;
}
H235AuthCAT authCAT;
authCAT.SetLocalId(id);
authCAT.SetPassword(passwd);
if (authCAT.ValidateClearToken(token) == H235Authenticator::e_OK) {
PTRACE(5, "GKAUTH\t" << GetName() << " CAT password match for '"
<< id << '\''
);
return e_ok;
} else
return e_fail;
}
if (token.HasOptionalField(H235_ClearToken::e_password)) {
if (!token.HasOptionalField(H235_ClearToken::e_generalID)) {
PTRACE(3, "GKAUTH\t"<< GetName() << " generalID field not found"
<<" inside the clear text token"
);
return e_fail;
}
const PString id = token.m_generalID;
if (m_checkID && (aliases == NULL || FindAlias(*aliases, id) == P_MAX_INDEX)) {
PTRACE(3, "GKAUTH\t" << GetName() << " generalID '"
<<"' does not match any alias for the endpoint"
);
return e_fail;
}
PString passwd;
if (!InternalGetPassword(id, passwd)) {
PTRACE(3, "GKAUTH\t" << GetName() << " password not found for '"
<< id << '\''
);
return e_fail;
}
const PString tokenpasswd = token.m_password;
if (passwd == tokenpasswd) {
PTRACE(5, "GKAUTH\t" << GetName() << " clear text password "
"match for '" << id << '\''
);
return e_ok;
} else
return e_fail;
}
}
return e_next;
}
int SimplePasswordAuth::CheckCryptoTokens(
/// an array of cryptoTokens to be checked
const H225_ArrayOf_CryptoH323Token& tokens,
/// aliases for the endpoint that generated the tokens
const H225_ArrayOf_AliasAddress* aliases,
/// raw data for RAS PDU - required to validate some tokens
/// like H.235 Auth Procedure I
const PBYTEArray& rawPDU
)
{
for (PINDEX i = 0; i < tokens.GetSize(); i++) {
if (tokens[i].GetTag() == H225_CryptoH323Token::e_cryptoEPPwdHash) {
H225_CryptoH323Token_cryptoEPPwdHash& pwdhash = tokens[i];
const PString id = AsString(pwdhash.m_alias, false);
if (m_checkID && (aliases == NULL || FindAlias(*aliases, id) == P_MAX_INDEX)) {
PTRACE(3, "GKAUTH\t" << GetName() << " alias '" << id
<< "' of the cryptoEPPwdHash token does not match "
"any alias for the endpoint"
);
return e_fail;
}
PString passwd;
if (!InternalGetPassword(id, passwd)) {
PTRACE(3, "GKAUTH\t" << GetName() << " password not found for '"
<< id << '\''
);
return e_fail;
}
H235AuthSimpleMD5 authMD5;
authMD5.SetLocalId(id);
authMD5.SetPassword(passwd);
if (authMD5.ValidateCryptoToken(tokens[i], rawPDU) == H235Authenticator::e_OK) {
PTRACE(5, "GKAUTH\t" << GetName() << " MD5 password match for '"
<< id << '\''
);
return e_ok;
} else
return e_fail;
#if P_SSL
} else if (tokens[i].GetTag() == H225_CryptoH323Token::e_nestedcryptoToken) {
const H235_CryptoToken& nestedCryptoToken = tokens[i];
if (nestedCryptoToken.GetTag() != H235_CryptoToken::e_cryptoHashedToken)
continue;
const H235_CryptoToken_cryptoHashedToken& cryptoHashedToken = nestedCryptoToken;
const H235_ClearToken& clearToken = cryptoHashedToken.m_hashedVals;
if (!clearToken.HasOptionalField(H235_ClearToken::e_sendersID)) {
PTRACE(5, "GKAUTH\t" << GetName() << " hashedVals of nested "
" cryptoHashedToken do not contain sendersID"
);
continue;
}
PString id = clearToken.m_sendersID;
if (m_checkID && (aliases == NULL || FindAlias(*aliases, id) == P_MAX_INDEX)) {
PTRACE(3, "GKAUTH\t" << GetName() << " sendersID '" << id
<< "' of the cryptoHashedToken hasgedVals does not match "
"any alias for the endpoint"
);
return e_fail;
}
PString passwd;
bool passwordFound = InternalGetPassword(id, passwd);
//if a password is not found: senderID == endpointIdentifier?
if (!passwordFound) {
H225_EndpointIdentifier epId;
epId = id;
endptr ep = RegistrationTable::Instance()->FindByEndpointId(epId);
if (!ep) {
PTRACE(3, "GKAUTH\t" << GetName() << " sendersID '" << id
<< "' of the cryptoHashedToken hashedVals does not match "
"any endpoint identifier"
);
return e_fail;
}
// check all endpoint aliases for a password
const H225_ArrayOf_AliasAddress aliases = ep->GetAliases();
for (PINDEX i = 0; i < aliases.GetSize(); i++) {
id = AsString(aliases[i], FALSE);
passwordFound = InternalGetPassword(id, passwd);
if (passwordFound)
break;
}
}
if (!passwordFound) {
PTRACE(3, "GKAUTH\t" << GetName() << " password not found for '"
<< id << '\''
);
return e_fail;
}
H235AuthProcedure1 authProcedure1;
authProcedure1.SetLocalId(Toolkit::GKName());
authProcedure1.SetPassword(passwd);
const int result = authProcedure1.ValidateCryptoToken(tokens[i], rawPDU);
if (result == H235Authenticator::e_OK) {
PTRACE(5, "GKAUTH\t" << GetName() << " SHA-1 password match for '"
<< id << '\''
);
return e_ok;
} else if (result == H235Authenticator::e_Absent)
continue;
else
return e_fail;
#endif
}
}
return e_next;
}
// class AliasAuth
AliasAuth::AliasAuth(
const char* name,
unsigned supportedRasChecks,
unsigned supportedMiscChecks
)
: GkAuthenticator(name, supportedRasChecks, supportedMiscChecks),
m_cache(NULL)
{
m_cache = new CacheManager(GetConfig()->GetInteger(name, "CacheTimeout", -1));
}
AliasAuth::~AliasAuth()
{
delete m_cache;
}
int AliasAuth::Check(
RasPDU<H225_RegistrationRequest>& request,
RRQAuthData& /*authData*/
)
{
H225_RegistrationRequest& rrq = request;
if (!rrq.HasOptionalField(H225_RegistrationRequest::e_terminalAlias)) {
PTRACE(3, "GKAUTH\t" << GetName() << " - terminalAlias field not found "
"in RRQ message"
);
return GetDefaultStatus();
}
const H225_ArrayOf_AliasAddress& aliases = rrq.m_terminalAlias;
for (PINDEX i = 0; i <= aliases.GetSize(); i++) {
const PString alias = (i < aliases.GetSize())
? AsString(aliases[i], false) : PString("default");
PString authcond;
if (InternalGetAuthConditionString(alias, authcond)) {
if (doCheck(rrq.m_callSignalAddress, authcond)) {
PTRACE(5, "GKAUTH\t" << GetName() << " auth condition '"
<< authcond <<"' accepted RRQ from '" << alias << '\''
);
return e_ok;
} else {
PTRACE(3, "GKAUTH\t" << GetName() << " auth condition '"
<< authcond <<"' rejected RRQ from '" << alias << '\''
);
return e_fail;
}
} else
PTRACE(4, "GKAUTH\t" << GetName() << " auth condition not found "
<< "for alias '" << alias << '\''
);
}
return GetDefaultStatus();
}
bool AliasAuth::GetAuthConditionString(
/// an alias the condition string is to be retrieved for
const PString& alias,
/// filled with auth condition string that has been found
PString& authCond
)
{
if (alias.IsEmpty())
return false;
if (!GetConfig()->HasKey("RasSrv::RRQAuth", alias))
return false;
if (strcasecmp(alias, "CacheTimeout") == 0) {
PTRACE(2, "GKAUTH\t" << GetName() << " trying to get auth condition "
" string for the forbidden alias '" << alias << '\''
);
return false;
}
authCond = GetConfig()->GetString("RasSrv::RRQAuth", alias, "");
return true;
}
bool AliasAuth::InternalGetAuthConditionString(
const PString& id, /// get the password for this id
PString& authCond /// filled with the auth condition string on return
)
{
if (m_cache->Retrieve(id, authCond)) {
PTRACE(5, "GKAUTH\t" << GetName() << " cached auth condition string "
"found for '" << id << '\''
);
return true;
}
if (GetAuthConditionString(id, authCond)) {
m_cache->Save(id, authCond);
return true;
} else
return false;
}
bool AliasAuth::doCheck(
/// an array of source signalling addresses for an endpoint that sent the request
const H225_ArrayOf_TransportAddress& sigaddr,
/// auth condition string as returned by GetAuthConditionString
const PString& condition
)
{
const PStringArray authrules(condition.Tokenise("&|", FALSE));
#if PTRACING
if (authrules.GetSize() < 1) {
PTRACE(2, "GKAUTH\t" << GetName() << " contains an empty auth condition");
return false;
}
#endif
for (PINDEX i = 0; i < authrules.GetSize(); ++i)
for (PINDEX j = 0; j < sigaddr.GetSize(); ++j)
if (CheckAuthRule(sigaddr[j], authrules[i])) {
PTRACE(5, "GKAUTH\t" << GetName() << " auth rule '"
<< authrules[i] << "' applied successfully to RRQ "
" from " << AsDotString(sigaddr[j])
);
return true;
}
return false;
}
bool AliasAuth::CheckAuthRule(
/// a signalling address for the endpoint that sent the request
const H225_TransportAddress& sigaddr,
/// the auth rule to be used for checking
const PString& authrule
)
{
const PStringArray rule = authrule.Tokenise(":", false);
if (rule.GetSize() < 1) {
PTRACE(1, "GKAUTH\t" << GetName() << " found invalid empty auth rule '"
<< authrule << '\''
);
return false;
}
// authrule = rName[:params...]
const PString rName = rule[0].Trim();
if (strcasecmp(rName, "confirm") == 0 || strcasecmp(rName, "allow") == 0)
return true;
else if (strcasecmp(rName, "reject") == 0 || strcasecmp(rName, "deny") == 0
|| strcasecmp(rName, "forbid") == 0)
return false;
else if (strcasecmp(rName, "sigaddr") == 0) {
// condition 'sigaddr' example:
// sigaddr:.*ipAddress .* ip = .* c3 47 e2 a2 .*port = 1720.*
if (rule.GetSize() < 2) {
PTRACE(1, "GKAUTH\t" << GetName() << " found invalid empty sigaddr "
"auth rule '" << authrule << '\''
);
return false;
}
return Toolkit::MatchRegex(AsString(sigaddr), rule[1].Trim()) != 0;
} else if (strcasecmp(rName, "sigip") == 0) {
// condition 'sigip' example:
// sigip:195.71.129.69:1720
if (rule.GetSize() < 2) {
PTRACE(1, "GKAUTH\t" << GetName() << " found invalid empty sigip "
"auth rule '" << authrule << '\''
);
return false;
}
PIPSocket::Address ip;
PIPSocket::GetHostAddress(rule[1].Trim(), ip);
const WORD port = (WORD)((rule.GetSize() < 3)
? GK_DEF_ENDPOINT_SIGNAL_PORT : rule[2].Trim().AsInteger());
return (sigaddr == SocketToH225TransportAddr(ip, port));
} else {
PTRACE(1, "GKAUTH\t" << GetName() << " found unknown auth rule '"
<< rName << '\''
);
return false;
}
}
// class PrefixAuth
// Initial author: Michael Rubashenkkov 2002/01/14 (GkAuthorize)
// Completely rewrite by Chih-Wei Huang 2002/05/01
class AuthRule;
class AuthObj;
class PrefixAuth : public GkAuthenticator
{
public:
typedef std::map< PString, AuthRule *, greater<PString> > Rules;
enum SupportedRasChecks {
PrefixAuthRasChecks = RasInfo<H225_AdmissionRequest>::flag
| RasInfo<H225_LocationRequest>::flag
};
PrefixAuth(
const char* name,
unsigned supportedRasChecks = PrefixAuthRasChecks,
unsigned supportedMiscChecks = 0
);
virtual ~PrefixAuth();
// override from class GkAuthenticator
virtual int Check(RasPDU<H225_LocationRequest>& request, unsigned& rejectReason);
/** Authenticate/Authorize ARQ message. Override from GkAuthenticator.
@return
e_fail - authentication failed
e_ok - authenticated with this authenticator
e_next - authentication could not be determined
*/
virtual int Check(
/// ARQ to be authenticated/authorized
RasPDU<H225_AdmissionRequest>& request,
/// authorization data (call duration limit, reject reason, ...)
ARQAuthData& authData
);
protected:
virtual int doCheck(
const AuthObj& aobj
);
private:
PrefixAuth();
PrefixAuth(const PrefixAuth&);
PrefixAuth& operator=(const PrefixAuth&);
private:
Rules m_prefrules;
int m_defaultRule;
};
// Help classes for PrefixAuth
class AuthObj // abstract class
{
public:
virtual ~AuthObj() {}
virtual bool IsValid() const { return true; }
virtual PStringArray GetPrefixes() const = 0;
virtual PIPSocket::Address GetIP() const = 0;
virtual PString GetAliases() const = 0;
};
class ARQAuthObj : public AuthObj
{
public:
ARQAuthObj(
const H225_AdmissionRequest& arq
);
virtual bool IsValid() const { return m_ep; }
virtual PStringArray GetPrefixes() const;
virtual PIPSocket::Address GetIP() const;
virtual PString GetAliases() const;
private:
ARQAuthObj();
ARQAuthObj(const ARQAuthObj&);
ARQAuthObj& operator=(const ARQAuthObj&);
private:
const H225_AdmissionRequest& m_arq;
endptr m_ep;
};
class LRQAuthObj : public AuthObj
{
public:
LRQAuthObj(
const H225_LocationRequest& lrq
);
virtual PStringArray GetPrefixes() const;
virtual PIPSocket::Address GetIP() const;
virtual PString GetAliases() const;
private:
LRQAuthObj();
LRQAuthObj(const LRQAuthObj&);
LRQAuthObj& operator=(const LRQAuthObj&);
private:
const H225_LocationRequest& m_lrq;
PIPSocket::Address m_ipAddress;
};
class AuthRule : public NamedObject
{
public:
enum Result {
e_nomatch,
e_allow,
e_deny
};
AuthRule(
Result fate,
bool inverted
) : m_priority(1000), m_fate(fate), m_inverted(inverted), m_next(NULL) {}
virtual ~AuthRule() { delete m_next; }
virtual bool Match(
const AuthObj& aobj
) = 0;
int Check(
const AuthObj& aobj
);
bool operator<(
const AuthRule& obj
) const { return m_priority < obj.m_priority; }
void SetNext(
AuthRule* next
) { m_next = next; }
private:
AuthRule();
AuthRule(const AuthRule&);
AuthRule& operator=(const AuthRule&);
protected:
/// the lesser the value, the higher the priority
int m_priority;
private:
Result m_fate;
bool m_inverted;
AuthRule* m_next;
};
class NullRule : public AuthRule
{
public:
NullRule() : AuthRule(e_nomatch, false) { SetName("NULL"); }
virtual bool Match(
const AuthObj& /*aobj*/
) { return false; }
private:
NullRule(const NullRule&);
NullRule& operator=(const NullRule&);
};
class IPv4AuthRule : public AuthRule
{
public:
IPv4AuthRule(
Result fate,
const PString& ipStr,
bool inverted
);
virtual bool Match(
const AuthObj& aobj
);
private:
IPv4AuthRule();
IPv4AuthRule(const IPv4AuthRule&);
IPv4AuthRule& operator=(const IPv4AuthRule&);
private:
PIPSocket::Address m_network, m_netmask;
};
class AliasAuthRule : public AuthRule
{
public:
AliasAuthRule(
Result fate,
const PString& aliasStr,
bool inverted
) : AuthRule(fate, inverted), m_pattern(aliasStr)
{
m_priority = -1;
#if PTRACING
SetName(PString((fate == e_allow) ? "allow alias" : "deny alias")
+ (inverted ? ":!" : ":") + aliasStr
);
#endif
}
virtual bool Match(
const AuthObj& aobj
);
private:
AliasAuthRule();
AliasAuthRule(const AliasAuthRule&);
AliasAuthRule& operator=(const AliasAuthRule&);
private:
PString m_pattern;
};
ARQAuthObj::ARQAuthObj(
const H225_AdmissionRequest& arq
)
: m_arq(arq), m_ep(RegistrationTable::Instance()->FindByEndpointId(arq.m_endpointIdentifier))
{
}
PStringArray ARQAuthObj::GetPrefixes() const
{
PStringArray array;
if (m_arq.HasOptionalField(H225_AdmissionRequest::e_destinationInfo)) {
const PINDEX ss = m_arq.m_destinationInfo.GetSize();
if (ss > 0) {
array.SetSize(ss);
for (PINDEX i = 0; i < ss; ++i)
array[i] = AsString(m_arq.m_destinationInfo[i], false);
}
}
if (array.GetSize() == 0)
array.AppendString(PString());
return array;
}
PIPSocket::Address ARQAuthObj::GetIP() const
{
PIPSocket::Address result;
const H225_TransportAddress& addr =
m_arq.HasOptionalField(H225_AdmissionRequest::e_srcCallSignalAddress)
? m_arq.m_srcCallSignalAddress : m_ep->GetCallSignalAddress();
GetIPFromTransportAddr(addr, result);
return result;
}
PString ARQAuthObj::GetAliases() const
{
return AsString(m_ep->GetAliases());
}
LRQAuthObj::LRQAuthObj(
const H225_LocationRequest& lrq
)
: m_lrq(lrq)
{
GetIPFromTransportAddr(m_lrq.m_replyAddress, m_ipAddress);
}
PStringArray LRQAuthObj::GetPrefixes() const
{
PStringArray array;
const PINDEX ss = m_lrq.m_destinationInfo.GetSize();
if (ss > 0) {
array.SetSize(ss);
for (PINDEX i = 0; i < ss; ++i)
array[i] = AsString(m_lrq.m_destinationInfo[i], false);
}
return array;
}
PIPSocket::Address LRQAuthObj::GetIP() const
{
return m_ipAddress;
}
PString LRQAuthObj::GetAliases() const
{
return m_lrq.HasOptionalField(H225_LocationRequest::e_sourceInfo)
? AsString(m_lrq.m_sourceInfo) : PString();
}
int AuthRule::Check(
const AuthObj& aobj
)
{
if (Match(aobj) ^ m_inverted) {
PTRACE(5, "GKAUTH\tPrefix auth rule '" << GetName() << "' matched");
return m_fate;
} else
return m_next ? m_next->Check(aobj) : e_nomatch;
}
inline void delete_rule(PrefixAuth::Rules::value_type r)
{
delete r.second;
}
IPv4AuthRule::IPv4AuthRule(
Result fate,
const PString& ipStr,
bool inverted
)
: AuthRule(fate, inverted)
{
Toolkit::GetNetworkFromString(ipStr, m_network, m_netmask);
DWORD n = ~PIPSocket::Net2Host(DWORD(m_netmask));
for (m_priority = 0; n; n >>= 1)
++m_priority;
#if PTRACING
SetName(PString((fate == e_allow) ? "allow ipv4(" : "deny ipv4(")
+ PString(m_priority) + (inverted ? "):!" : "):") + ipStr
);
#endif
}
bool IPv4AuthRule::Match(
const AuthObj& aobj
)
{
return ((aobj.GetIP() & m_netmask) == m_network);
}
bool AliasAuthRule::Match(
const AuthObj& aobj
)
{
return aobj.GetAliases().FindRegEx(m_pattern) != P_MAX_INDEX;
}
inline bool is_inverted(const PString & cfg, PINDEX p)
{
return (p > 1) ? cfg[p-1] == '!' : false;
}
inline bool comp_authrule_priority(AuthRule *a1, AuthRule *a2)
{
return *a1 < *a2;
}
namespace {
const char* const prfflag="prf:";
const char* const allowflag="allow";
const char* const denyflag="deny";
const char* const ipflag="ipv4:";
const char* const aliasflag="alias:";
}
// class PrefixAuth
PrefixAuth::PrefixAuth(
const char* name,
unsigned supportedRasChecks,
unsigned supportedMiscChecks
)
: GkAuthenticator(name, supportedRasChecks, supportedMiscChecks)
{
m_defaultRule = GetDefaultStatus();
const int ipfl = strlen(ipflag);
const int aliasfl = strlen(aliasflag);
const PStringToString cfgs = GetConfig()->GetAllKeyValues(name);
for (PINDEX i = 0; i < cfgs.GetSize(); ++i) {
PString key = cfgs.GetKeyAt(i);
if (key *= "default") {
m_defaultRule = Toolkit::AsBool(cfgs.GetDataAt(i)) ? e_ok : e_fail;
continue;
} else if (key *= "ALL") {
// use space (0x20) as the key so it will be the last resort
key = " ";
}
if (m_prefrules.find(key) != m_prefrules.end()) {
PTRACE(1, "GKAUTH\t" << GetName() << " duplicate entry for "
"destination '" << key << '\''
);
continue; //rule already exists? ignore
}
const PStringArray rules = cfgs.GetDataAt(i).Tokenise("|", false);
const PINDEX sz = rules.GetSize();
if (sz < 1) {
PTRACE(1, "GKAUTH\t" << GetName() << " no rules found for "
"destination '" << key << '\''
);
continue;
}
//AuthRule *rls[sz];
AuthRule **rls = new AuthRule *[sz];
for (PINDEX j = 0; j < sz; ++j) {
// if not allowed, assume denial
const AuthRule::Result fate = (rules[j].Find(allowflag) != P_MAX_INDEX)
? AuthRule::e_allow : AuthRule::e_deny;
PINDEX pp;
if ((pp = rules[j].Find(ipflag)) != P_MAX_INDEX)
rls[j] = new IPv4AuthRule(fate, rules[j].Mid(pp + ipfl).Trim(),
is_inverted(rules[j], pp)
);
else if ((pp = rules[j].Find(aliasflag)) != P_MAX_INDEX)
rls[j] = new AliasAuthRule(fate, rules[j].Mid(pp+aliasfl).Trim(),
is_inverted(rules[j], pp)
);
else {
rls[j] = new NullRule;
}
}
// sort the rules by priority
stable_sort(rls, rls + sz, comp_authrule_priority);
for (PINDEX k = 1; k < sz; ++k)
rls[k-1]->SetNext(rls[k]);
m_prefrules[key] = rls[0];
delete [] rls;
}
if (m_prefrules.size() == 0)
PTRACE(1, "GKAUTH\t" << GetName() << " contains no rules - "
"check the config"
);
}
PrefixAuth::~PrefixAuth()
{
for_each(m_prefrules.begin(), m_prefrules.end(), delete_rule);
}
int PrefixAuth::Check(RasPDU<H225_LocationRequest> & request, unsigned &)
{
LRQAuthObj tmpObj((const H225_LocationRequest&)request); // fix for GCC 3.4.2
return doCheck(tmpObj);
}
int PrefixAuth::Check(
/// ARQ to be authenticated/authorized
RasPDU<H225_AdmissionRequest>& request,
/// authorization data (call duration limit, reject reason, ...)
ARQAuthData& /*authData*/
)
{
H225_AdmissionRequest& arq = request;
if (arq.m_answerCall
&& arq.HasOptionalField(H225_AdmissionRequest::e_callIdentifier)
&& CallTable::Instance()->FindCallRec(arq.m_callIdentifier)) {
PTRACE(5, "GKAUTH\t" << GetName() << " ARQ check skipped - call "
" already admitted and present in the call table"
);
return e_ok;
}
ARQAuthObj tmpObj(arq); // fix for GCC 3.4.2
return doCheck(tmpObj);
}
struct comp_pref { // function object
comp_pref(const PString & s) : value(s) {}
bool operator()(const PrefixAuth::Rules::value_type & v) const;
const PString & value;
};
inline bool comp_pref::operator()(const PrefixAuth::Rules::value_type & v) const
{
return (value.Find(v.first) == 0) || (v.first *= " ");
}
int PrefixAuth::doCheck(
const AuthObj& aobj
)
{
if (!aobj.IsValid())
return e_fail;
const PStringArray destinationInfo(aobj.GetPrefixes());
for (PINDEX i = 0; i < destinationInfo.GetSize(); ++i) {
// find the first match rule
// since prefrules is descendently sorted
// it must be the most specific prefix
for (Rules::iterator j = m_prefrules.begin(); j != m_prefrules.end(); ++j) {
Rules::iterator iter = find_if(j, m_prefrules.end(),
comp_pref(destinationInfo[i])
);
if (iter == m_prefrules.end())
break;
switch (iter->second->Check(aobj))
{
case AuthRule::e_allow:
PTRACE(4, "GKAUTH\t" << GetName() << " rule matched and "
"accepted destination prefix '"
<< ((iter->first == " ") ? PString("ALL") : iter->first)
<< "' for alias '" << destinationInfo[i] << '\''
);
return e_ok;
case AuthRule::e_deny:
PTRACE(4, "GKAUTH\t" << GetName() << " rule matched and "
"rejected destination prefix '"
<< ((iter->first == " ") ? PString("ALL") : iter->first)
<< "' for alias '" << destinationInfo[i] << '\''
);
return e_fail;
default: // try next prefix...
j = iter;
PTRACE(4, "GKAUTH\t" << GetName() << " rule matched and "
" could not reject or accept destination prefix '"
<< ((iter->first == " ") ? PString("ALL") : iter->first)
<< "' for alias '" << destinationInfo[i] << '\''
);
}
}
}
#if PTRACING
if (m_defaultRule == e_ok)
PTRACE(4, "GKAUTH\t" << GetName() << " default rule accepted "
"the request"
);
else if (m_defaultRule == e_fail)
PTRACE(4, "GKAUTH\t" << GetName() << " default rule rejected "
"the request"
);
else
PTRACE(4, "GKAUTH\t" << GetName() << " could not reject or "
"accept the request"
);
#endif
return m_defaultRule;
}
namespace { // anonymous namespace
GkAuthCreator<GkAuthenticator> DefaultAuthenticatorCreator("default");
GkAuthCreator<SimplePasswordAuth> SimplePasswordAuthCreator("SimplePasswordAuth");
GkAuthCreator<AliasAuth> AliasAuthCreator("AliasAuth");
GkAuthCreator<PrefixAuth> PrefixAuthCreator("PrefixAuth");
} // end of anonymous namespace
/* This is OBSOLETE
#if ((defined(__GNUC__) && __GNUC__ <= 2) && !defined(_WIN32))
#include <unistd.h>
#include <procbuf.h>
class ExternalPasswordAuth : public SimplePasswordAuth {
public:
ExternalPasswordAuth(const char *);
private:
bool ExternalInit();
virtual bool GetPassword(const PString &, PString &);
PString Program;
};
// class ExternalPasswordAuth
ExternalPasswordAuth::ExternalPasswordAuth(const char *name) : SimplePasswordAuth(name)
{
ExternalInit();
}
bool ExternalPasswordAuth::ExternalInit()
{
const char *ExternalSec = GetName();
// Read the configuration
Program = config->GetString(ExternalSec, "PasswordProgram", "");
return true;
}
bool ExternalPasswordAuth::GetPassword(const PString & id, PString & passwd)
{
const int BUFFSIZE = 256;
char buff[BUFFSIZE] = "";
if (Program.IsEmpty()) {
PTRACE(1, "GkAuth\tProgram is not defined");
return false;
}
procbuf proc(Program + " " + id, ios::in);
istream istr(&proc);
istr.getline(buff, BUFFSIZE);
PTRACE(3, "EXT\tget " << buff);
passwd = buff;
return true;
}
namespace {
GkAuthCreator<ExternalPasswordAuth> ExternalPasswordAuthCreator("ExternalPasswordAuth");
}
#endif
*/
syntax highlighted by Code2HTML, v. 0.9.1