////////////////////////////////////////////////////////////////// // // 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 #include #include #include #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 rasmap; rasmap["GRQ"] = RasInfo::flag, rasmap["RRQ"] = RasInfo::flag, rasmap["URQ"] = RasInfo::flag, rasmap["ARQ"] = RasInfo::flag, rasmap["BRQ"] = RasInfo::flag, rasmap["DRQ"] = RasInfo::flag, rasmap["LRQ"] = RasInfo::flag, rasmap["IRQ"] = RasInfo::flag; std::map 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::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 &, unsigned &) { return IsRasCheckEnabled(RasInfo::flag) ? m_defaultStatus : e_next; } int GkAuthenticator::Check( /// a request to be authenticated RasPDU& /*request*/, /// authorization data (reject reason, ...) RRQAuthData& /*authData*/ ) { return IsRasCheckEnabled(RasInfo::flag) ? m_defaultStatus : e_next; } int GkAuthenticator::Check(RasPDU &, unsigned &) { return IsRasCheckEnabled(RasInfo::flag) ? m_defaultStatus : e_next; } int GkAuthenticator::Check( /// a request to be authenticated RasPDU& /*req*/, /// authorization data (call duration limit, reject reason, ...) ARQAuthData& /*authData*/ ) { return IsRasCheckEnabled(RasInfo::flag) ? m_defaultStatus : e_next; } int GkAuthenticator::Check(RasPDU &, unsigned &) { return IsRasCheckEnabled(RasInfo::flag) ? m_defaultStatus : e_next; } int GkAuthenticator::Check(RasPDU &, unsigned &) { return IsRasCheckEnabled(RasInfo::flag) ? m_defaultStatus : e_next; } int GkAuthenticator::Check(RasPDU &, unsigned &) { return IsRasCheckEnabled(RasInfo::flag) ? m_defaultStatus : e_next; } int GkAuthenticator::Check(RasPDU &, unsigned &) { return IsRasCheckEnabled(RasInfo::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& 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& 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 &/*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 &/*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 &/*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 authenticators; GkAuthenticator *auth; const PStringArray authRules = GkConfig()->GetKeys(GkAuthSectionName); for (PINDEX r = 0; r < authRules.GetSize(); r++) { auth = Factory::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::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::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& request, /// authorization data (reject reason, ...) RRQAuthData& authData ) { ReadLock lock(m_reloadMutex); std::list::const_iterator i = m_authenticators.begin(); while (i != m_authenticators.end()) { GkAuthenticator* auth = *i++; if (auth->IsRasCheckEnabled(RasInfo::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& request, /// authorization data (call duration limit, reject reason, ...) ARQAuthData& authData ) { ReadLock lock(m_reloadMutex); std::list::const_iterator i = m_authenticators.begin(); while (i != m_authenticators.end()) { GkAuthenticator* auth = *i++; if (auth->IsRasCheckEnabled(RasInfo::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::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::const_iterator iter = m_cache.find(key); if (iter == m_cache.end()) return false; if (m_ttl >= 0) { std::map::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 & request, unsigned &) { return doCheck(request); } int SimplePasswordAuth::Check( RasPDU & request, RRQAuthData& /*authData*/ ) { H225_RegistrationRequest& rrq = request; return doCheck(request, rrq.HasOptionalField(H225_RegistrationRequest::e_terminalAlias) ? &rrq.m_terminalAlias : NULL ); } int SimplePasswordAuth::Check(RasPDU & request, unsigned &) { return doCheck(request); } int SimplePasswordAuth::Check(RasPDU & request, unsigned &) { return doCheck(request); } int SimplePasswordAuth::Check(RasPDU & request, unsigned &) { return doCheck(request); } int SimplePasswordAuth::Check(RasPDU & request, unsigned &) { return doCheck(request); } int SimplePasswordAuth::Check(RasPDU & request, unsigned &) { return doCheck(request); } int SimplePasswordAuth::Check( /// ARQ to be authenticated/authorized RasPDU& 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& 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 > Rules; enum SupportedRasChecks { PrefixAuthRasChecks = RasInfo::flag | RasInfo::flag }; PrefixAuth( const char* name, unsigned supportedRasChecks = PrefixAuthRasChecks, unsigned supportedMiscChecks = 0 ); virtual ~PrefixAuth(); // override from class GkAuthenticator virtual int Check(RasPDU& 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& 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 & 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& 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 DefaultAuthenticatorCreator("default"); GkAuthCreator SimplePasswordAuthCreator("SimplePasswordAuth"); GkAuthCreator AliasAuthCreator("AliasAuth"); GkAuthCreator PrefixAuthCreator("PrefixAuth"); } // end of anonymous namespace /* This is OBSOLETE #if ((defined(__GNUC__) && __GNUC__ <= 2) && !defined(_WIN32)) #include #include 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 ExternalPasswordAuthCreator("ExternalPasswordAuth"); } #endif */