According to yr suggestion, I have added one param
to gkauthenticator, add same param to radius check proc on RRQ
But now its not going to radius, it simply before
says security denial.
it failes in this procedure
bool GkAuthenticator::CheckRas(
PBYTEArray &rawPDU, H225_RegistrationRequest& rrq, unsigned& rejectReason, const PIPSocket::Address& rxaddr,MemBlock *m ) { setLastReceivedRawPDU(rawPDU); if (checkFlag & RasValue(rrq)) { int r = Check(rrq, rejectReason,m); (It returns e_fail) if (r == e_ok) { PTRACE(4, "GkAuth\t" << name << " check ok"); if (controlFlag != e_Required) return true; } else if (r == e_fail) { PTRACE(2, "GkAuth\t" << name << " check failed"); return false; } } return next == NULL || next->CheckRas(rawPDU,rrq,rejectReason,rxaddr,m); } Can anyone plz help me regarding this problem. As it getting hard and hard for me to do this. As u people know much more abt gnugk code other than me .I am just new to it. What I am trying to do that I want to send some extra param from onRRQ event to radius check proc on RRQ. |
/* * radauth.cxx * * RADIUS protocol authenticator module for GNU Gatekeeper. * Please see docs/radauth.txt for more details. * * Copyright (c) 2003, Quarcom FHU, Michal Zygmuntowicz * * This work is published under the GNU Public License (GPL) * see file COPYING for details. * We also explicitely grant the right to link this code * with the OpenH323 library. * * $Log: radauth.cxx,v $ * Revision 1.1.2.41 2004/06/22 18:41:16 zvision * Username, Calling-Station-Id and Called-Station-Id handling rewritten. * Radius modules optimized. * * Revision 1.1.2.40 2004/06/18 15:42:51 zvision * Better User-Name and Calling-Station-Id handling for unregistered endpoints * * Revision 1.1.2.39 2004/06/17 10:46:13 zvision * New RADIUS h323-ivr-out=h323-call-id attribute * * Revision 1.1.2.38 2004/05/25 19:54:05 zvision * Some minor warnings removed * * Revision 1.1.2.37 2004/05/21 11:40:37 zvision * Do not declare H.235 Auth Procedure I support as it is not implemented * * Revision 1.1.2.36 2004/05/12 14:00:47 zvision * Header file usage more consistent. Solaris std::map problems fixed. * Compilation warnings removed. VSNET2003 project files added. ANSI.h removed. * * Revision 1.1.2.35 2004/04/14 21:52:57 zvision * Fixed referencing RRQ terminalAlias field without checking for its presence * * Revision 1.1.2.34 2004/03/31 11:13:36 zvision * New CheckSetupUnregisteredOnly option for RadAliasAuth module * * Revision 1.1.2.33 2004/02/18 20:45:16 zvision * Prototype changed for GkAuthenticator::Check ARQ method * * Revision 1.1.2.32 2004/01/16 13:05:01 zvision * Better h323-ivr-in attrbiute handling * * Revision 1.1.2.31 2004/01/08 11:46:47 zvision * Fixed semicolon processing for h323-ivr-in reply attribute * * Revision 1.1.2.30 2004/01/07 20:37:26 zvision * RRQ endpoint alias control (add/remove) through RadAuth/RadAliasAuth modules * * Revision 1.1.2.29 2003/12/26 13:59:31 zvision * Fixed destination call signaling address handling * * Revision 1.1.2.28 2003/12/21 12:20:35 zvision * Fixed conditional compilation * * Revision 1.1.2.27 2003/12/02 12:47:37 zvision * Q.931 Setup authenticator now returns (and documents) Q.931 cause values instead of H225_ReleaseCompleteReason * * Revision 1.1.2.26 2003/11/22 13:43:48 zvision * Q.931 Setup Check now accepts const callptr to prevent it from modifications * * Revision 1.1.2.25 2003/11/18 23:38:51 zvision * Q.931 Setup authentication optimized * * Revision 1.1.2.24 2003/11/12 13:34:55 zvision * Fixed Session-Timeout handling in RadAliasAuth Setup check * * Revision 1.1.2.23 2003/10/03 00:59:49 zvision * Fixed bus errors on some system with dword alignment checking enabled. * Small optimizations and RadiusPDU API changes. * * Revision 1.1.2.22 2003/09/30 11:08:16 zvision * Fixed non-working Q.931 authenticator due to previous Check(...) signature * changes. Many thanks to Oleg Ustinov! * * Revision 1.1.2.21 2003/09/29 16:23:45 zvision * Cisco h323-xxx response attributes handling improved * * Revision 1.1.2.20 2003/09/25 11:12:46 zvision * Fixed Session-Timeout bug and more flexible h323-credit-time processing * * Revision 1.1.2.19 2003/09/24 10:19:44 zvision * Call duration limit for registered endpoints (through ARQ authenticators) * * Revision 1.1.2.18 2003/08/25 12:18:35 zvision * Added h323-ivr-out attribute with an alias list (thanks Mark Lipscombe) * * Revision 1.1.2.17 2003/07/31 22:59:24 zvision * Fixed IP address retrieval for unregistered endpoints * * Revision 1.1.2.16 2003/07/31 13:09:15 zvision * Added Q.931 Setup message authentication and call duration limit feature * * Revision 1.1.2.15 2003/07/17 14:40:39 zvision * Conditional compilation of features available only when HAS_ACCT is defined. * * Revision 1.1.2.14 2003/07/16 22:13:21 zvision * Fixed Radius attributes for answer call ARQs. * * Revision 1.1.2.13 2003/07/07 14:28:30 zvision * Added missing NAS-Identifier attribute in RadAliasAuth. Thanks Julius Stavaris. * * Revision 1.1.2.12 2003/07/07 12:02:55 zvision * Improved H.235 handling. * * Revision 1.1.2.11 2003/06/19 15:33:29 zvision * Removed static modifier from GetConferenceIDString function. * * Revision 1.1.2.10 2003/06/11 13:06:57 zvision * Added gk_const.h include directive (OPENH323_NEWVERSION macro definition) * * Revision 1.1.2.9 2003/06/11 12:14:35 zvision * Cosmetic changes * * Revision 1.1.2.8 2003/06/05 10:03:04 zvision * Small fix to h323-gw-id attribute. * * Revision 1.1.2.7 2003/05/29 17:21:22 zvision * Fixed compilation errors with OpenH323 versions prior to 1.11.5 (no H235AuthCAT) * * Revision 1.1.2.6 2003/05/28 13:25:19 zvision * Added alias based authentication (RadAliasAuth) * * Revision 1.1.2.5 2003/05/27 00:13:05 zvision * Smart Calling and Called -Station-Id selection (dialedDigits and partyNumber alias types preferred) * * Revision 1.1.2.4 2003/05/26 23:09:59 zvision * Added new OnSend and OnReceive hooks. LocalInterface config parameter introduced. * * Revision 1.1.2.3 2003/05/13 17:49:49 zvision * Removed acctPort. New includeFramedIP feature. Better tracing. Bug-fixes * * Revision 1.1.2.2 2003/04/29 14:56:27 zvision * Added H.235 capability matching * * Revision 1.1.2.1 2003/04/23 20:15:37 zvision * Initial revision * */ #ifdef HAS_RADIUS #if (_MSC_VER >= 1200) #pragma warning( disable : 4786 ) // warning about too long debug symbol off #endif #include <ptlib.h> #include <h225ras.h> #include <h323pdu.h> #include "gk_const.h" #include "gkauth.h" #include "Toolkit.h" #include "RasTbl.h" #include "h323util.h" #include "ProxyChannel.h" #include "radauth.h" namespace { // Settings for H.235 based module will be stored inside [RadAuth] config section const char* const RadAuthConfigSectionName = "RadAuth"; // Settings for alias based module will be stored inside [RadAliasAuth] config section const char* const RadAliasAuthConfigSectionName = "RadAliasAuth"; // append RADIUS H.235 authenticator to the global list of authenticators GkAuthInit<RadAuth> RAD_A("RadAuth"); // append RADIUS Alias authenticator to the global list of authenticators GkAuthInit<RadAliasAuth> RAD_A_A("RadAliasAuth"); } // OID for CAT (Cisco Access Token) algorithm PString RadAuth::OID_CAT( "1.2.840.113548.10.1.2.1" ); RadAuth::RadAuth( PConfig* cfg, const char* authName ) : GkAuthenticator(cfg, authName), radiusServers(cfg->GetString( RadAuthConfigSectionName,"Servers","" ).Tokenise(";, |\t", FALSE)), sharedSecret(cfg->GetString( RadAuthConfigSectionName, "SharedSecret", "" )), authPort((WORD)(cfg->GetInteger( RadAuthConfigSectionName, "DefaultAuthPort" ))), portBase(1024), portMax(65535), requestTimeout(cfg->GetInteger( RadAuthConfigSectionName, "RequestTimeout" )), idCacheTimeout(cfg->GetInteger( RadAuthConfigSectionName, "IdCacheTimeout" )), socketDeleteTimeout(cfg->GetInteger( RadAuthConfigSectionName, "SocketDeleteTimeout" )), numRequestRetransmissions(cfg->GetInteger( RadAuthConfigSectionName, "RequestRetransmissions" )), roundRobin(Toolkit::AsBool(cfg->GetString( RadAuthConfigSectionName, "RoundRobinServers", "1" ))), appendCiscoAttributes(Toolkit::AsBool(cfg->GetString( RadAuthConfigSectionName, "AppendCiscoAttributes", "1" ))), includeTerminalAliases(Toolkit::AsBool(cfg->GetString( RadAuthConfigSectionName, "IncludeTerminalAliases", "1" ))), localInterface(cfg->GetString( RadAuthConfigSectionName, "LocalInterface", "" )), radiusClient(NULL), attrH323GwId(NULL), attrH323CallType(NULL), attrH323CallOriginOriginate(NULL), attrH323CallOriginAnswer(NULL), attrNASIdentifier(NULL) { PAssert(radiusServers.GetSize() > 0, "Cannot build RADIUS authenticator " " - no RADIUS servers specified in the config" ); if (!localInterface && !PIPSocket::IsLocalHost(localInterface)) { PTRACE(1, "RADAUTH\tSpecified local interface - " << localInterface << " - does not belong to this machine" ); localInterface = PString(); } /// build RADIUS client radiusClient = new RadiusClient(radiusServers[0], radiusServers.GetSize() > 1 ? radiusServers[1] : PString(), localInterface ); /// if there were specified more than two RADIUS servers, append them for (PINDEX i = 2; i < radiusServers.GetSize(); i++) radiusClient->AppendServer(radiusServers[i]); radiusClient->SetSharedSecret(sharedSecret); radiusClient->SetRoundRobinServers(roundRobin); if (authPort > 0) radiusClient->SetAuthPort(authPort); if (requestTimeout > 0) radiusClient->SetRequestTimeout(requestTimeout); if (idCacheTimeout > 0) radiusClient->SetIdCacheTimeout(idCacheTimeout); if (socketDeleteTimeout > 0) radiusClient->SetSocketDeleteTimeout(socketDeleteTimeout); if (numRequestRetransmissions > 0) radiusClient->SetRetryCount(numRequestRetransmissions); const PStringArray s = cfg->GetString( RadAuthConfigSectionName, "RadiusPortRange", "" ).Tokenise("-"); // parse port range if (s.GetSize() >= 2) { unsigned p1 = s[0].AsUnsigned(); unsigned p2 = s[1].AsUnsigned(); // swap if base is greater than max if (p2 < p1) { const unsigned temp = p1; p1 = p2; p2 = temp; } if (p1 > 65535) p1 = 65535; if (p2 > 65535) p2 = 65535; if (p1 > 0 && p2 > 0) { portBase = (WORD)p1; portMax = (WORD)p2; } } radiusClient->SetClientPortRange(portBase, portMax - portBase + 1); if (localInterface.IsEmpty()) localInterfaceAddr = Toolkit::Instance()->GetRouteTable()->GetLocalAddress(); else localInterfaceAddr = PIPSocket::Address(localInterface); NASIdentifier = Toolkit::Instance()->GKName(); if (h235Authenticators == NULL) h235Authenticators = new H235Authenticators; H235AuthCAT* authenticator = new H235AuthCAT; authenticator->SetLocalId("dummy"); authenticator->SetRemoteId("dummy"); authenticator->SetPassword("dummy"); h235Authenticators->Append(authenticator); attrH323GwId = new RadiusAttr(PString("h323-gw-id=") + NASIdentifier, 9 /* Cisco */, 33 /* h323-gw-id */ ); attrH323CallType = new RadiusAttr(PString("h323-call-type=VoIP"), 9 /* Cisco */, 27 /* h323-call-type */ ); attrH323CallOriginOriginate = new RadiusAttr( PString("h323-call-origin=originate"), 9 /* Cisco */, 26 /* h323-call-origin */ ); attrH323CallOriginAnswer = new RadiusAttr( PString("h323-call-origin=answer"), 9 /* Cisco */, 26 /* h323-call-origin */ ); attrNASIdentifier = new RadiusAttr(RadiusAttr::NasIdentifier, NASIdentifier); } RadAuth::~RadAuth() { delete radiusClient; delete attrH323GwId; delete attrH323CallType; delete attrH323CallOriginOriginate; delete attrH323CallOriginAnswer; delete attrNASIdentifier; } int RadAuth::Check( H225_RegistrationRequest& rrq, unsigned& rejectReason,MemBlock * m ) { PAssertNULL(radiusClient); if (!rrq.HasOptionalField(H225_RegistrationRequest::e_tokens)) { PTRACE(4, "RADAUTH\tRRQ Auth not possible - no 'tokens' field"); rejectReason = H225_RegistrationRejectReason::e_securityDenial; return defaultStatus; } // RRQ has to carry at least one terminalAlias if (!rrq.HasOptionalField(H225_RegistrationRequest::e_terminalAlias)) { PTRACE(4, "RADAUTH\tRRQ Auth not possible - no 'terminalAlias' field"); rejectReason = H225_RegistrationRejectReason::e_invalidTerminalAliases; return defaultStatus; } // build RADIUS Access-Request RadiusPDU* pdu = radiusClient->BuildPDU(); if (pdu == NULL) { PTRACE(3, "RADAUTH\tRRQ auth failed - could not create Access-Request PDU"); rejectReason = H225_RegistrationRejectReason::e_undefinedReason; return defaultStatus; } pdu->SetCode(RadiusPDU::AccessRequest); // check for ClearTokens (CAT uses ClearTokens) const H225_ArrayOf_AliasAddress& aliases = rrq.m_terminalAlias; const H225_ArrayOf_ClearToken& tokens = rrq.m_tokens; bool foundCAT = false; // scan ClearTokens and find CATs for (PINDEX i = 0; i < tokens.GetSize(); i++) { const H235_ClearToken& token = tokens[i]; // is it CAT? if (token.m_tokenOID != OID_CAT) continue; // these field are required for CAT if (!(token.HasOptionalField(H235_ClearToken::e_generalID) && token.HasOptionalField(H235_ClearToken::e_random) && token.HasOptionalField(H235_ClearToken::e_timeStamp) && token.HasOptionalField(H235_ClearToken::e_challenge))) { PTRACE(4, "RADAUTH\tRRQ Auth failed - CAT without all required fields"); rejectReason = H225_RegistrationRejectReason::e_securityDenial; delete pdu; return e_fail; } // generalID should be present in the list of terminal aliases const PString id = token.m_generalID; if (FindAlias(aliases, id) == P_MAX_INDEX) { PTRACE(4, "RADAUTH\tRRQ Auth failed - CAT generalID is not a valid alias"); rejectReason = H225_RegistrationRejectReason::e_invalidTerminalAliases; delete pdu; return e_fail; } // CAT pseudo-random has to be one byte only const int randomInt = token.m_random; if (randomInt < -127 || randomInt > 255) { PTRACE(4,"RADAUTH\tRRQ Auth failed - CAT m_random out of range"); rejectReason = H225_RegistrationRejectReason::e_securityDenial; delete pdu; return e_fail; } // CAT challenge has to be 16 bytes if (token.m_challenge.GetValue().GetSize() < 16) { PTRACE(4,"RADAUTH\tRRQ Auth failed - m_challenge less than 16 bytes"); rejectReason = H225_RegistrationRejectReason::e_securityDenial; delete pdu; return e_fail; } // append User-Name *pdu += new RadiusAttr(RadiusAttr::UserName, id); // build CHAP-Password char password[17]; password[0] = (BYTE)randomInt; memcpy(password+1, (const BYTE*)(token.m_challenge), 16); *pdu += new RadiusAttr( RadiusAttr::ChapPassword, password, sizeof(password) ); *pdu += new RadiusAttr(RadiusAttr::ChapChallenge, (int)(DWORD)token.m_timeStamp ); foundCAT = true; break; } if (!foundCAT) { PTRACE(4, "RADAUTH\tRRQ Auth not possible - no CAT token found"); rejectReason = H225_RegistrationRejectReason::e_securityDenial; delete pdu; return defaultStatus; } // Gk works as NAS point, so append NAS IP *pdu += new RadiusAttr(RadiusAttr::NasIpAddress, localInterfaceAddr); // NAS-Identifier as Gk name *pdu += new RadiusAttr(*attrNASIdentifier); // Gk does not have a concept of physical ports, // so define port type as NAS-Port-Virtual *pdu += new RadiusAttr(RadiusAttr::NasPortType, RadiusAttr::NasPort_Virtual); // RRQ service type is Login-User *pdu += new RadiusAttr(RadiusAttr::ServiceType, RadiusAttr::ST_Login); PIPSocket::Address addr; bool addrValid = false; if (rrq.m_callSignalAddress.GetSize() > 0) addrValid = GetIPFromTransportAddr(rrq.m_callSignalAddress[0], addr) && addr.IsValid(); if (!addrValid && rrq.m_rasAddress.GetSize() > 0) addrValid = GetIPFromTransportAddr(rrq.m_rasAddress[0], addr) && addr.IsValid(); if (!addrValid) { PTRACE(3, "RADAUTH\tRRQ check failed - could not determine Framed-IP-Address"); rejectReason = H225_RegistrationRejectReason::e_invalidCallSignalAddress; delete pdu; return defaultStatus; } *pdu += new RadiusAttr(RadiusAttr::FramedIpAddress, addr); if (appendCiscoAttributes && includeTerminalAliases) { PString aliasList( "terminal-alias:" ); for (PINDEX i = 0; i < aliases.GetSize(); i++) { if (i > 0) aliasList += ","; aliasList += H323GetAliasAddressString(aliases[i]); } *pdu += new RadiusAttr( PString("h323-ivr-out=") + aliasList + PString(";"), 9 /* Cisco */, 1 /* Cisco-AV-Pair */ ); } // send request and wait for response RadiusPDU* response = NULL; bool result = OnSendPDU(*pdu, rrq, rejectReason) && radiusClient->MakeRequest(*pdu, response) && (response != NULL); delete pdu; if (!result) { delete response; return defaultStatus; } result = (response->GetCode() == RadiusPDU::AccessAccept); // process h323-ivr-in=terminal-alias attribute if (result) { PINDEX index = response->FindVsaAttr(9, 1); bool found = false; while (index != P_MAX_INDEX && !found) { PString h323ivrin; const RadiusAttr* attr = response->GetAttrAt(index); if (attr && attr->IsValid()) h323ivrin = attr->AsVsaString(); if (h323ivrin.Find("h323-ivr-in=") == 0 && ((index = h323ivrin.Find("terminal-alias:")) != P_MAX_INDEX)) { found = true; index += strlen("terminal-alias:"); const PINDEX semicolonpos = h323ivrin.Find(';',index); h323ivrin = h323ivrin.Mid( index, semicolonpos == P_MAX_INDEX ? P_MAX_INDEX : (semicolonpos-index) ); PStringArray aliases = h323ivrin.Tokenise(","); if (aliases.GetSize() > 0 && rrq.HasOptionalField(H225_RegistrationRequest::e_terminalAlias)) { PINDEX i = 0; while (i < rrq.m_terminalAlias.GetSize()) { PINDEX j = aliases.GetStringsIndex(H323GetAliasAddressString(rrq.m_terminalAlias[i])); if( j == P_MAX_INDEX ) rrq.m_terminalAlias.RemoveAt(i); else { i++; aliases.RemoveAt(j); } } } for( PINDEX i = 0; i < aliases.GetSize(); i++ ) { if( rrq.HasOptionalField(H225_RegistrationRequest::e_terminalAlias) ) rrq.m_terminalAlias.SetSize(rrq.m_terminalAlias.GetSize()+1); else { rrq.IncludeOptionalField(H225_RegistrationRequest::e_terminalAlias); rrq.m_terminalAlias.SetSize(1); } H323SetAliasAddress(aliases[i],rrq.m_terminalAlias[rrq.m_terminalAlias.GetSize()-1]); } } if( !found ) index = response->FindVsaAttr( 9, 1, index + 1 ); } } if (result) { result = OnReceivedPDU(*response, rrq, rejectReason); m->BalanceString="12.15"; m->callDuration =24; } else rejectReason = H225_RegistrationRejectReason::e_securityDenial; delete response; return result ? e_ok : e_fail; } int RadAuth::Check( H225_AdmissionRequest& arq, unsigned& rejectReason, long& callDurationLimit ) { PAssertNULL(radiusClient); // check for ClearTokens if (!arq.HasOptionalField(H225_AdmissionRequest::e_tokens)) { PTRACE(4, "RADAUTH\tARQ Auth not possible - no 'tokens' field"); rejectReason = H225_AdmissionRejectReason::e_securityDenial; return defaultStatus; } // build RADIUS Access-Request packet RadiusPDU* pdu = radiusClient->BuildPDU(); if (pdu == NULL) { PTRACE(3, "RADAUTH\tARQ auth failed - could not to create Access-Request PDU"); rejectReason = H225_AdmissionRejectReason::e_securityDenial; return defaultStatus; } pdu->SetCode(RadiusPDU::AccessRequest); const H225_ArrayOf_ClearToken& tokens = arq.m_tokens; bool foundCAT = false; PString username; // scan ClearTokens for CATs for (PINDEX i = 0; i < tokens.GetSize(); i++) { const H235_ClearToken& token = tokens[i]; // is it CAT? if (token.m_tokenOID != OID_CAT) continue; // these field are required for CAT if (!(token.HasOptionalField(H235_ClearToken::e_generalID) && token.HasOptionalField(H235_ClearToken::e_random) && token.HasOptionalField(H235_ClearToken::e_timeStamp) && token.HasOptionalField(H235_ClearToken::e_challenge)) ) { PTRACE(4, "RADAUTH\tARQ Auth failed - CAT without all required fields"); rejectReason = H225_AdmissionRejectReason::e_securityDenial; delete pdu; return e_fail; } username = token.m_generalID; // CAT random has to be one byte only const int randomInt = token.m_random; if (randomInt < -127 || randomInt > 255) { PTRACE(4, "RADAUTH\tARQ Auth failed - CAT random out of range"); rejectReason = H225_AdmissionRejectReason::e_securityDenial; delete pdu; return e_fail; } // CAT challenge has to be 16 bytes if (token.m_challenge.GetValue().GetSize() < 16) { PTRACE(4, "RADAUTH\tARQ Auth failed - CAT challenge less than 16 bytes"); rejectReason = H225_AdmissionRejectReason::e_securityDenial; delete pdu; return e_fail; } *pdu += new RadiusAttr(RadiusAttr::UserName, username); // build CHAP-Password char password[17]; password[0] = (BYTE)randomInt; memcpy(password+1, (const BYTE*)(token.m_challenge), 16); *pdu += new RadiusAttr(RadiusAttr::ChapPassword, password, sizeof(password) ); // append CHAP-Challenge *pdu += new RadiusAttr(RadiusAttr::ChapChallenge, (int)(DWORD)token.m_timeStamp ); foundCAT = true; break; } if (!foundCAT) { PTRACE(4, "RADAUTH\tARQ Auth not possible - tokens without CAT"); rejectReason = H225_AdmissionRejectReason::e_securityDenial; delete pdu; return defaultStatus; } // Gk acts as NAS, so include NAS IP *pdu += new RadiusAttr(RadiusAttr::NasIpAddress, localInterfaceAddr); // NAS-Identifier as Gk name *pdu += new RadiusAttr(*attrNASIdentifier); // NAS-Port-Type as Virtual, since Gk does // not care about physical ports concept *pdu += new RadiusAttr(RadiusAttr::NasPortType, RadiusAttr::NasPort_Virtual); // Service-Type is Login-User if originating the call // and Call Check if answering the call *pdu += new RadiusAttr(RadiusAttr::ServiceType, arq.m_answerCall ? RadiusAttr::ST_CallCheck : RadiusAttr::ST_Login ); endptr ep = RegistrationTable::Instance()->FindByEndpointId( arq.m_endpointIdentifier ); PIPSocket::Address addr; bool addrValid = false; if (arq.HasOptionalField(arq.m_answerCall ? arq.e_destCallSignalAddress : arq.e_srcCallSignalAddress)) addrValid = (arq.m_answerCall ? GetIPFromTransportAddr(arq.m_destCallSignalAddress, addr) : GetIPFromTransportAddr(arq.m_srcCallSignalAddress, addr)) && addr.IsValid(); if (!addrValid && ep) addrValid = GetIPFromTransportAddr(ep->GetCallSignalAddress(), addr) && addr.IsValid(); if (!addrValid) { PTRACE(3, "RADAUTH\tARQ check failed - could not determine Framed-IP-Address"); rejectReason = H225_AdmissionRejectReason::e_securityDenial; delete pdu; return defaultStatus; } *pdu += new RadiusAttr(RadiusAttr::FramedIpAddress, addr); callptr call; if (arq.m_answerCall) if (arq.HasOptionalField(arq.e_callIdentifier)) call = CallTable::Instance()->FindCallRec(arq.m_callIdentifier); else { H225_CallReferenceValue crv; crv.SetValue((WORD)arq.m_callReferenceValue & 0x7fffu); call = CallTable::Instance()->FindCallRec(crv); } // fill Calling-Station-Id and Called-Station-Id PString stationId = GetCallingStationId(call, arq, ep); if (stationId.IsEmpty()) { PTRACE(4, "RADAUTH\tARQ Auth failed - could not determine Calling-Station-Id"); delete pdu; return e_fail; } *pdu += new RadiusAttr(RadiusAttr::CallingStationId, stationId); // Called-Station-Id stationId = GetCalledStationId(call, arq, ep); if (stationId.IsEmpty()) { PTRACE(3, "RADAUTH\tARQ Auth failed - could not determine Called-Station-Id"); delete pdu; return e_fail; } *pdu += new RadiusAttr(RadiusAttr::CalledStationId, stationId); if (appendCiscoAttributes) { *pdu += new RadiusAttr(PString("h323-conf-id=") + GetGUIDString(arq.m_conferenceID), 9 /* Cisco */, 24 /* h323-conf-id */ ); *pdu += new RadiusAttr(arq.m_answerCall ? *attrH323CallOriginAnswer : *attrH323CallOriginOriginate ); *pdu += new RadiusAttr(*attrH323CallType); *pdu += new RadiusAttr(*attrH323GwId); } // send the request and wait for a response RadiusPDU* response = NULL; bool result = OnSendPDU(*pdu, arq, rejectReason) && radiusClient->MakeRequest(*pdu, response) && (response != NULL); delete pdu; if (!result) { delete response; return defaultStatus; } // authenticated? result = (response->GetCode() == RadiusPDU::AccessAccept); if (result) { const PINDEX index = response->FindVsaAttr(9, 103); if (index != P_MAX_INDEX) { const RadiusAttr* attr = response->GetAttrAt(index); bool valid = false; if (attr && attr->IsValid()) { PString s = attr->AsVsaString(); if (s.Find("h323-return-code=") == 0) s = s.Mid(s.FindOneOf("=") + 1); if (s.GetLength() > 0 && strspn((const char*)s, "0123456789") == (size_t)s.GetLength()) { const unsigned retcode = s.AsUnsigned(); if (retcode != 0) { PTRACE(5, "RADAUTH\t" << GetName() << " ARQ check failed " "- return code " << retcode ); result = false; } valid = true; } } if (!valid) { PTRACE(5, "RADAUTH\t" << GetName() << " check failed - " "invalid h323-return-code attribute" ); result = false; } } } if (result) { bool found = false; const PINDEX index = response->FindVsaAttr(9, 102); if (index != P_MAX_INDEX) { const RadiusAttr* attr = response->GetAttrAt(index); if (attr && attr->IsValid()) { PString s = attr->AsVsaString(); if (s.Find("h323-credit-time=") == 0) s = s.Mid(s.FindOneOf("=") + 1); if (s.GetLength() > 0 && strspn((const char*)s, "0123456789") == (size_t)s.GetLength()) { found = true; callDurationLimit = s.AsInteger(); PTRACE(5, "RADAUTH\t" << GetName() << " ARQ check set " "duration limit set: " << callDurationLimit ); if (callDurationLimit == 0) result = false; } } if (!found) { PTRACE(5, "RADAUTH\t" << GetName() << " ARQ check failed - " "invalid h323-credit-time attribute" ); result = false; } } } if (result) { bool found = false; const PINDEX index = response->FindAttr(RadiusAttr::SessionTimeout); if (index != P_MAX_INDEX) { const RadiusAttr* attr = response->GetAttrAt(index); if (attr && attr->IsValid()) { found = true; const long sessionTimeout = attr->AsInteger(); if (callDurationLimit < 0 || callDurationLimit > sessionTimeout) { callDurationLimit = sessionTimeout; PTRACE(5, "RADAUTH\t" << GetName() << " ARQ check set " "duration limit set: " << callDurationLimit ); } if (callDurationLimit == 0) result = false; } if (!found) { PTRACE(5, "RADAUTH\t" << GetName() << " ARQ check failed - " "invalid Session-Timeout attribute" ); result = false; } } } if (result) result = OnReceivedPDU(*response, arq, rejectReason, callDurationLimit); else rejectReason = H225_AdmissionRejectReason::e_securityDenial; delete response; return result ? e_ok : e_fail; } bool RadAuth::OnSendPDU( RadiusPDU& /*pdu*/, const H225_RegistrationRequest& /*rrq*/, unsigned& /*rejectReason*/ ) { return true; } bool RadAuth::OnSendPDU( RadiusPDU& /*pdu*/, const H225_AdmissionRequest& /*arq*/, unsigned& /*rejectReason*/ ) { return true; } bool RadAuth::OnReceivedPDU( RadiusPDU& /*pdu*/, H225_RegistrationRequest& /*rrq*/, unsigned& /*rejectReason*/ ) { return true; } bool RadAuth::OnReceivedPDU( RadiusPDU& /*pdu*/, H225_AdmissionRequest& /*arq*/, unsigned& /*rejectReason*/, long& /*durationLimit*/ ) { return true; } RadAliasAuth::RadAliasAuth( PConfig* cfg, const char* authName ) : GkAuthenticator( cfg, authName ), radiusServers(cfg->GetString( RadAliasAuthConfigSectionName,"Servers","" ).Tokenise( ";, |\t", FALSE )), sharedSecret(cfg->GetString( RadAliasAuthConfigSectionName,"SharedSecret","" )), authPort((WORD)(cfg->GetInteger( RadAliasAuthConfigSectionName,"DefaultAuthPort" ))), portBase(1024), portMax(65535), requestTimeout(cfg->GetInteger( RadAliasAuthConfigSectionName,"RequestTimeout" )), idCacheTimeout(cfg->GetInteger( RadAliasAuthConfigSectionName,"IdCacheTimeout" )), socketDeleteTimeout(cfg->GetInteger( RadAliasAuthConfigSectionName,"SocketDeleteTimeout" )), numRequestRetransmissions(cfg->GetInteger( RadAliasAuthConfigSectionName,"RequestRetransmissions" )), roundRobin(cfg->GetBoolean( RadAliasAuthConfigSectionName,"RoundRobinServers", TRUE )), appendCiscoAttributes(cfg->GetBoolean( RadAliasAuthConfigSectionName,"AppendCiscoAttributes", TRUE )), includeTerminalAliases(cfg->GetBoolean( RadAliasAuthConfigSectionName,"IncludeTerminalAliases", TRUE )), localInterface(cfg->GetString( RadAliasAuthConfigSectionName, "LocalInterface", "" )), fixedUsername(cfg->GetString( RadAliasAuthConfigSectionName, "FixedUsername", "" )), fixedPassword(cfg->GetString( RadAliasAuthConfigSectionName, "FixedPassword", "" )), radiusClient(NULL), attrH323GwId(NULL), attrH323CallType(NULL), attrH323CallOriginOriginate(NULL), attrH323CallOriginAnswer(NULL), attrNASIdentifier(NULL) { PAssert(radiusServers.GetSize() > 0, "Cannot build RADIUS Alias " "authenticator - no RADIUS servers specified in the config" ); if (!localInterface && !PIPSocket::IsLocalHost(localInterface)) { PTRACE(1, "RADAUTH\tSpecified local interface - " << localInterface <<" - does not belong to this machine" ); localInterface = PString(); } /// build RADIUS client radiusClient = new RadiusClient(radiusServers[0], radiusServers.GetSize() > 1 ? radiusServers[1] : PString(), localInterface ); /// if there were specified more than two RADIUS servers, append them for (PINDEX i = 2; i < radiusServers.GetSize(); i++) radiusClient->AppendServer(radiusServers[i]); radiusClient->SetSharedSecret(sharedSecret); radiusClient->SetRoundRobinServers(roundRobin); if (authPort > 0) radiusClient->SetAuthPort(authPort); if (requestTimeout > 0) radiusClient->SetRequestTimeout(requestTimeout); if (idCacheTimeout > 0) radiusClient->SetIdCacheTimeout(idCacheTimeout); if (socketDeleteTimeout > 0) radiusClient->SetSocketDeleteTimeout(socketDeleteTimeout); if (numRequestRetransmissions > 0) radiusClient->SetRetryCount(numRequestRetransmissions); const PStringArray s = cfg->GetString( RadAliasAuthConfigSectionName, "RadiusPortRange", "" ).Tokenise("-"); // parse port range if (s.GetSize() >= 2) { unsigned p1 = s[0].AsUnsigned(); unsigned p2 = s[1].AsUnsigned(); // swap if base is greater than max if (p2 < p1) { const unsigned temp = p1; p1 = p2; p2 = temp; } if (p1 > 65535) p1 = 65535; if (p2 > 65535) p2 = 65535; if (p1 > 0 && p2 > 0) { portBase = (WORD)p1; portMax = (WORD)p2; } } if (localInterface.IsEmpty()) localInterfaceAddr = Toolkit::Instance()->GetRouteTable()->GetLocalAddress(); else localInterfaceAddr = PIPSocket::Address(localInterface); NASIdentifier = Toolkit::Instance()->GKName(); radiusClient->SetClientPortRange(portBase, (WORD)(portMax-portBase+1)); attrH323GwId = new RadiusAttr(PString("h323-gw-id=") + NASIdentifier, 9 /* Cisco */, 33 /* h323-gw-id */ ); attrH323CallType = new RadiusAttr(PString("h323-call-type=VoIP"), 9 /* Cisco */, 27 /* h323-call-type */ ); attrH323CallOriginOriginate = new RadiusAttr( PString("h323-call-origin=originate"), 9 /* Cisco */, 26 /* h323-call-origin */ ); attrH323CallOriginAnswer = new RadiusAttr( PString("h323-call-origin=answer"), 9 /* Cisco */, 26 /* h323-call-origin */ ); attrNASIdentifier = new RadiusAttr(RadiusAttr::NasIdentifier, NASIdentifier); } RadAliasAuth::~RadAliasAuth() { delete radiusClient; delete attrH323GwId; delete attrH323CallType; delete attrH323CallOriginOriginate; delete attrH323CallOriginAnswer; delete attrNASIdentifier; } int RadAliasAuth::Check( H225_RegistrationRequest& rrq, unsigned& rejectReason ) { PAssertNULL(radiusClient); PIPSocket::Address framedIP; bool addrValid = false; if (rrq.m_callSignalAddress.GetSize() > 0) addrValid = GetIPFromTransportAddr(rrq.m_callSignalAddress[0], framedIP) && framedIP.IsValid(); if (!addrValid && rrq.m_rasAddress.GetSize() > 0) addrValid = GetIPFromTransportAddr(rrq.m_rasAddress[0], framedIP) && framedIP.IsValid(); if (!addrValid) { PTRACE(3, "RADAUTH\tRRQ AliasAuth failed - could not determine Framed-IP-Address"); rejectReason = H225_RegistrationRejectReason::e_invalidCallSignalAddress; return defaultStatus; } const PString username = GetUsername(rrq); if (username.IsEmpty() && fixedUsername.IsEmpty()) { PTRACE(3, "RADAUTH\tRRQ AliasAuth failed - neither FixedUsername" " nor alias inside RRQ were found" ); rejectReason = H225_RegistrationRejectReason::e_securityDenial; return defaultStatus; } // build RADIUS Access-Request RadiusPDU* pdu = radiusClient->BuildPDU(); if (pdu == NULL) { PTRACE(3, "RADAUTH\tRRQ AliasAuth failed - could not to create Access-Request PDU"); rejectReason = H225_RegistrationRejectReason::e_undefinedReason; return defaultStatus; } pdu->SetCode(RadiusPDU::AccessRequest); // append User-Name *pdu += new RadiusAttr(RadiusAttr::UserName, fixedUsername.IsEmpty() ? username : fixedUsername ); *pdu += new RadiusAttr(RadiusAttr::UserPassword, fixedPassword.IsEmpty() ? (fixedUsername.IsEmpty() ? username : fixedUsername) : fixedPassword ); // Gk works as NAS point, so append NAS IP *pdu += new RadiusAttr(RadiusAttr::NasIpAddress, localInterfaceAddr); // NAS-Identifier as Gk name *pdu += new RadiusAttr(*attrNASIdentifier); *pdu += new RadiusAttr(RadiusAttr::NasPortType, RadiusAttr::NasPort_Virtual); *pdu += new RadiusAttr(RadiusAttr::ServiceType, RadiusAttr::ST_Login); *pdu += new RadiusAttr(RadiusAttr::FramedIpAddress, framedIP); if (appendCiscoAttributes && includeTerminalAliases && rrq.HasOptionalField(H225_RegistrationRequest::e_terminalAlias)) { PString aliasList( "terminal-alias:" ); for (PINDEX i = 0; i < rrq.m_terminalAlias.GetSize(); i++) { if (i > 0) aliasList += ","; aliasList += H323GetAliasAddressString(rrq.m_terminalAlias[i]); } *pdu += new RadiusAttr( PString("h323-ivr-out=") + aliasList + PString(";"), 9 /* Cisco */, 1 /* Cisco-AV-Pair */ ); } // send request and wait for response RadiusPDU* response = NULL; bool result = OnSendPDU(*pdu, rrq, rejectReason) && radiusClient->MakeRequest(*pdu, response) && (response != NULL); delete pdu; if (!result) { delete response; PTRACE(3, "RADAUTH\tRRQ AliasAuth failed - could not send Access-Request PDU"); return defaultStatus; } result = (response->GetCode() == RadiusPDU::AccessAccept); // process h323-ivr-in=terminal-alias attribute if (result) { PINDEX index = response->FindVsaAttr(9, 1); bool found = false; while (index != P_MAX_INDEX && !found) { PString h323ivrin; const RadiusAttr* attr = response->GetAttrAt(index); if (attr && attr->IsValid()) h323ivrin = attr->AsVsaString(); if (h323ivrin.Find("h323-ivr-in=") == 0 && (index = h323ivrin.Find("terminal-alias:")) != P_MAX_INDEX) { found = true; index += strlen("terminal-alias:"); const PINDEX semicolonpos = h323ivrin.Find(';', index); h323ivrin = h323ivrin.Mid( index, semicolonpos == P_MAX_INDEX ? P_MAX_INDEX : (semicolonpos-index) ); PStringArray aliases = h323ivrin.Tokenise(","); if (aliases.GetSize() > 0 && rrq.HasOptionalField(H225_RegistrationRequest::e_terminalAlias)) { PINDEX i = 0; while (i < rrq.m_terminalAlias.GetSize()) { PINDEX j = aliases.GetStringsIndex(H323GetAliasAddressString(rrq.m_terminalAlias[i])); if (j == P_MAX_INDEX) rrq.m_terminalAlias.RemoveAt(i); else { i++; aliases.RemoveAt(j); } } } for (PINDEX i = 0; i < aliases.GetSize(); i++) { if (rrq.HasOptionalField(H225_RegistrationRequest::e_terminalAlias)) rrq.m_terminalAlias.SetSize(rrq.m_terminalAlias.GetSize()+1); else { rrq.IncludeOptionalField(H225_RegistrationRequest::e_terminalAlias); rrq.m_terminalAlias.SetSize(1); } H323SetAliasAddress(aliases[i], rrq.m_terminalAlias[rrq.m_terminalAlias.GetSize()-1]); } } if (!found) index = response->FindVsaAttr(9, 1, index + 1); } } if (result) result = OnReceivedPDU(*response, rrq, rejectReason); else rejectReason = H225_RegistrationRejectReason::e_securityDenial; delete response; return result ? e_ok : e_fail; } int RadAliasAuth::Check( H225_AdmissionRequest& arq, unsigned& rejectReason, long& callDurationLimit ) { PAssertNULL(radiusClient); endptr ep = RegistrationTable::Instance()->FindByEndpointId( arq.m_endpointIdentifier ); callptr call; if (arq.m_answerCall) if (arq.HasOptionalField(arq.e_callIdentifier)) call = CallTable::Instance()->FindCallRec(arq.m_callIdentifier); else { H225_CallReferenceValue crv; crv.SetValue((WORD)arq.m_callReferenceValue & 0x7fffu); call = CallTable::Instance()->FindCallRec(crv); } const PString username = GetUsername(call, arq, ep); if (username.IsEmpty() && fixedUsername.IsEmpty()) { PTRACE(3, "RADAUTH\tARQ AliasAuth failed - neither FixedUsername" " nor alias inside ARQ were found" ); rejectReason = H225_AdmissionRejectReason::e_securityDenial; return defaultStatus; } // build RADIUS Access-Request packet RadiusPDU* pdu = radiusClient->BuildPDU(); if (pdu == NULL) { PTRACE(3, "RADAUTH\tARQ AliasAuth failed - could not to create Access-Request PDU"); return defaultStatus; } pdu->SetCode(RadiusPDU::AccessRequest); // append User-Name *pdu += new RadiusAttr(RadiusAttr::UserName, fixedUsername.IsEmpty() ? username : fixedUsername ); *pdu += new RadiusAttr(RadiusAttr::UserPassword, fixedPassword.IsEmpty() ? (fixedUsername.IsEmpty() ? username : fixedUsername) : fixedPassword ); // Gk works as NAS point, so append NAS IP *pdu += new RadiusAttr(RadiusAttr::NasIpAddress, localInterfaceAddr); // NAS-Identifier as Gk name *pdu += new RadiusAttr(*attrNASIdentifier); *pdu += new RadiusAttr(RadiusAttr::NasPortType, RadiusAttr::NasPort_Virtual); // Service-Type is Login-User if originating the call // and Call Check if answering the call *pdu += new RadiusAttr(RadiusAttr::ServiceType, arq.m_answerCall ? RadiusAttr::ST_CallCheck : RadiusAttr::ST_Login ); PIPSocket::Address framedIP; bool addrValid = false; if (arq.HasOptionalField(arq.m_answerCall ? arq.e_destCallSignalAddress : arq.e_srcCallSignalAddress)) addrValid = (arq.m_answerCall ? GetIPFromTransportAddr(arq.m_destCallSignalAddress, framedIP) : GetIPFromTransportAddr(arq.m_srcCallSignalAddress, framedIP)) && framedIP.IsValid(); if (!addrValid && ep) addrValid = GetIPFromTransportAddr(ep->GetCallSignalAddress(), framedIP) && framedIP.IsValid(); if (!addrValid) { PTRACE(3, "RADAUTH\tARQ check failed - could not determine Framed-IP-Address"); rejectReason = H225_AdmissionRejectReason::e_securityDenial; delete pdu; return defaultStatus; } *pdu += new RadiusAttr(RadiusAttr::FramedIpAddress, framedIP); // build Calling-Station-Id and Called-Station-Id PString stationId = GetCallingStationId(call, arq, ep); if (stationId.IsEmpty()) { PTRACE(3, "RADAUTH\t" << GetName() << " ARQ check failed - " "could not determine Calling-Station-Id" ); delete pdu; return e_fail; } *pdu += new RadiusAttr(RadiusAttr::CallingStationId, stationId); // Called-Station-Id stationId = GetCalledStationId(call, arq, ep); if (stationId.IsEmpty()) { delete pdu; PTRACE(3, "RADAUTH\t" << GetName() << " ARQ check failed - " "could not determine Called-Station-Id" ); return e_fail; } *pdu += new RadiusAttr(RadiusAttr::CalledStationId, stationId); if (appendCiscoAttributes) { *pdu += new RadiusAttr( PString("h323-conf-id=") + GetGUIDString(arq.m_conferenceID), 9 /* Cisco */, 24 /* h323-conf-id */ ); *pdu += new RadiusAttr(arq.m_answerCall ? *attrH323CallOriginAnswer : *attrH323CallOriginOriginate ); *pdu += new RadiusAttr(*attrH323CallType); *pdu += new RadiusAttr(*attrH323GwId); } // send the request and wait for a response RadiusPDU* response = NULL; bool result = OnSendPDU(*pdu, arq, rejectReason) && radiusClient->MakeRequest(*pdu, response) && (response != NULL); delete pdu; if (!result) { delete response; return defaultStatus; } // authenticated? result = (response->GetCode() == RadiusPDU::AccessAccept); if (result) { const PINDEX index = response->FindVsaAttr( 9, 103 ); if( index != P_MAX_INDEX ) { const RadiusAttr* attr = response->GetAttrAt(index); BOOL valid = FALSE; if( attr && attr->IsValid() ) { PString s = attr->AsVsaString(); if( s.Find("h323-return-code=") == 0 ) s = s.Mid( s.FindOneOf("=") + 1 ); if( s.GetLength() > 0 && strspn((const char*)s,"0123456789") == (size_t)s.GetLength() ) { unsigned retcode = s.AsUnsigned(); if( retcode != 0 ) { PTRACE(5,"RADAUTH\t"<<GetName()<<" ARQ check failed - return code "<<retcode); result = FALSE; } valid = TRUE; } } if( !valid ) { PTRACE(5,"RADAUTH\t"<<GetName()<<" check failed - invalid h323-return-code attribute"); result = FALSE; } } } if( result ) { BOOL found = FALSE; const PINDEX index = response->FindVsaAttr( 9, 102 ); if( index != P_MAX_INDEX ) { const RadiusAttr* attr = response->GetAttrAt(index); if( attr && attr->IsValid() ) { PString s = attr->AsVsaString(); if( s.Find("h323-credit-time=") == 0 ) s = s.Mid( s.FindOneOf("=") + 1 ); if( s.GetLength() > 0 && strspn((const char*)s,"0123456789") == (size_t)s.GetLength() ) { found = TRUE; callDurationLimit = s.AsInteger(); PTRACE(5,"RADAUTH\t"<<GetName()<<" ARQ check set duration limit set: "<<callDurationLimit); if( callDurationLimit == 0 ) result = FALSE; } } if( !found ) { PTRACE(5,"RADAUTH\t"<<GetName()<<" ARQ check failed - invalid h323-credit-time attribute: "); result = FALSE; } } } if( result ) { BOOL found = FALSE; const PINDEX index = response->FindAttr( RadiusAttr::SessionTimeout ); if( index != P_MAX_INDEX ) { const RadiusAttr* attr = response->GetAttrAt(index); if( attr && attr->IsValid() ) { found = TRUE; const long sessionTimeout = attr->AsInteger(); if( (callDurationLimit < 0) || (callDurationLimit > sessionTimeout) ) { callDurationLimit = sessionTimeout; PTRACE(5,"RADAUTH\t"<<GetName()<<" ARQ check set duration limit set: "<<callDurationLimit); } if( callDurationLimit == 0 ) result = FALSE; } if( !found ) { PTRACE(5,"RADAUTH\t"<<GetName()<<" ARQ check failed - invalid Session-Timeout attribute"); result = FALSE; } } } if (result) result = OnReceivedPDU(*response, arq, rejectReason, callDurationLimit); else rejectReason = H225_AdmissionRejectReason::e_securityDenial; delete response; return result ? e_ok : e_fail; } int RadAliasAuth::Check( Q931& q931pdu, H225_Setup_UUIE& setup, callptr& call, unsigned& releaseCompleteCause, long& durationLimit ) { PAssertNULL(radiusClient); CallRec* callrec = call.operator->(); callptr tempcall; if (!callrec && setup.HasOptionalField(H225_Setup_UUIE::e_callIdentifier)) { tempcall = CallTable::Instance()->FindCallRec(setup.m_callIdentifier); callrec = tempcall.operator->(); } endptr callingEP; if (callrec) callingEP = callrec->GetCallingParty(); if (!callingEP && setup.HasOptionalField(H225_Setup_UUIE::e_endpointIdentifier)) callingEP = RegistrationTable::Instance()->FindByEndpointId( setup.m_endpointIdentifier ); // get the IP address for Framed-IP-Address attribute PIPSocket::Address framedIP(0); WORD framedPort = 0; bool addrValid = false; if (callrec && callrec->HasSrcSignalAddr()) addrValid = callrec->GetSrcSignalAddr(framedIP, framedPort) && framedIP.IsValid(); if (!addrValid && setup.HasOptionalField(setup.e_sourceCallSignalAddress)) addrValid = GetIPFromTransportAddr(setup.m_sourceCallSignalAddress, framedIP) && framedIP.IsValid(); if (!addrValid && callingEP) addrValid = GetIPFromTransportAddr(callingEP->GetCallSignalAddress(), framedIP) && framedIP.IsValid(); if (!addrValid) { PTRACE(2, "RADAUTH\t" << GetName() << " Setup check could not " "determine Framed-IP-Address" ); releaseCompleteCause = Q931::CallRejected; return defaultStatus; } // get the username for User-Name attribute const PString username = GetUsername(call, q931pdu, setup); if (username.IsEmpty() && fixedUsername.IsEmpty()) { PTRACE(2,"RADAUTH\t"<<GetName()<<" Setup check failed - neither FixedUsername" " nor alias and IP address inside Setup were found" ); releaseCompleteCause = Q931::CallRejected; return defaultStatus; } // build RADIUS Access-Request RadiusPDU* pdu = radiusClient->BuildPDU(); if (pdu == NULL) { PTRACE(3, "RADAUTH\t" << GetName() << " Setup check failed - " "could not to create Access-Request PDU" ); releaseCompleteCause = Q931::TemporaryFailure; return defaultStatus; } pdu->SetCode(RadiusPDU::AccessRequest); // append User-Name *pdu += new RadiusAttr(RadiusAttr::UserName, fixedUsername.IsEmpty() ? username : fixedUsername ); *pdu += new RadiusAttr(RadiusAttr::UserPassword, fixedPassword.IsEmpty() ? (fixedUsername.IsEmpty() ? username : fixedUsername) : fixedPassword ); // Gk works as NAS point, so append NAS IP *pdu += new RadiusAttr(RadiusAttr::NasIpAddress, localInterfaceAddr); // NAS-Identifier as Gk name *pdu += new RadiusAttr(*attrNASIdentifier); *pdu += new RadiusAttr(RadiusAttr::NasPortType, RadiusAttr::NasPort_Virtual); // Service-Type is Login-User if originating the call *pdu += new RadiusAttr(RadiusAttr::ServiceType, RadiusAttr::ST_Login); *pdu += new RadiusAttr(RadiusAttr::FramedIpAddress, framedIP); PString stationId = GetCallingStationId(call, q931pdu, setup); if (stationId.IsEmpty()) { PTRACE(3,"RADAUTH\t"<<GetName()<<" Setup check failed - no calling station id found"); releaseCompleteCause = Q931::CallRejected; delete pdu; return e_fail; } #ifdef HAS_ACCT if (callrec && callrec->GetCallingStationId().IsEmpty()) callrec->SetCallingStationId(stationId); #endif *pdu += new RadiusAttr(RadiusAttr::CallingStationId, stationId); stationId = GetCalledStationId(call, q931pdu, setup); if (stationId.IsEmpty()) { PTRACE(3,"RADAUTH\t"<<GetName()<<" Setup check failed - no called station id found"); releaseCompleteCause = Q931::CallRejected; delete pdu; return e_fail; } #ifdef HAS_ACCT if (callrec && callrec->GetCalledStationId().IsEmpty()) callrec->SetCalledStationId(stationId); #endif *pdu += new RadiusAttr(RadiusAttr::CalledStationId, stationId); if (appendCiscoAttributes) { *pdu += new RadiusAttr( PString("h323-conf-id=") + GetGUIDString(setup.m_conferenceID), 9 /* Cisco */, 24 /* h323-conf-id */ ); *pdu += new RadiusAttr(*attrH323CallOriginOriginate); *pdu += new RadiusAttr(*attrH323CallType); *pdu += new RadiusAttr(*attrH323GwId); } // send the request and wait for a response RadiusPDU* response = NULL; bool result = OnSendPDU(*pdu, q931pdu, setup, releaseCompleteCause, durationLimit) && radiusClient->MakeRequest(*pdu, response) && (response != NULL); delete pdu; if( !result ) { delete response; PTRACE(3, "RADAUTH\t" << GetName() << " Setup check failed - " "could not send Access-Request PDU" ); releaseCompleteCause = Q931::TemporaryFailure; return defaultStatus; } // authenticated? result = (response->GetCode() == RadiusPDU::AccessAccept); // process h323-return-code reply attribute if( result ) { const PINDEX index = response->FindVsaAttr( 9, 103 ); if( index != P_MAX_INDEX ) { const RadiusAttr* attr = response->GetAttrAt(index); BOOL valid = FALSE; if( attr && attr->IsValid() ) { PString s = attr->AsVsaString(); if( s.Find("h323-return-code=") == 0 ) s = s.Mid( s.FindOneOf("=") + 1 ); if( s.GetLength() > 0 && strspn((const char*)s,"0123456789") == (size_t)s.GetLength() ) { unsigned retcode = s.AsUnsigned(); if( retcode != 0 ) { PTRACE(5,"RADAUTH\t"<<GetName()<<" Setup check failed - return code "<<retcode); result = FALSE; } valid = TRUE; } } if( !valid ) { PTRACE(5,"RADAUTH\t"<<GetName()<<" check failed - invalid h323-return-code attribute"); result = FALSE; } } } // process h323-credit-time attribute if( result ) { BOOL found = FALSE; const PINDEX index = response->FindVsaAttr( 9, 102 ); if( index != P_MAX_INDEX ) { const RadiusAttr* attr = response->GetAttrAt(index); if( attr && attr->IsValid() ) { PString s = attr->AsVsaString(); if( s.Find("h323-credit-time=") == 0 ) s = s.Mid( s.FindOneOf("=") + 1 ); if( s.GetLength() > 0 && strspn((const char*)s,"0123456789") == (size_t)s.GetLength() ) { found = TRUE; durationLimit = s.AsInteger(); PTRACE(5,"RADAUTH\t"<<GetName()<<" Setup check set duration limit set: "<<durationLimit); if( durationLimit == 0 ) result = FALSE; } } if( !found ) { PTRACE(5,"RADAUTH\t"<<GetName()<<" Setup check failed - invalid h323-credit-time attribute: "); result = FALSE; } } } // process Session-Timeout attribute if( result ) { BOOL found = FALSE; const PINDEX index = response->FindAttr( RadiusAttr::SessionTimeout ); if( index != P_MAX_INDEX ) { const RadiusAttr* attr = response->GetAttrAt(index); if( attr && attr->IsValid() ) { found = TRUE; const long sessionTimeout = attr->AsInteger(); if( (durationLimit < 0) || (durationLimit > sessionTimeout) ) { durationLimit = sessionTimeout; PTRACE(5,"RADAUTH\t"<<GetName()<<" Setup check set duration limit set: "<<durationLimit); } if( durationLimit == 0 ) result = FALSE; } if( !found ) { PTRACE(5,"RADAUTH\t"<<GetName()<<" Setup check failed - invalid Session-Timeout attribute"); result = FALSE; } } } if (result) result = OnReceivedPDU(*response, q931pdu, setup, releaseCompleteCause, durationLimit); else releaseCompleteCause = Q931::CallRejected; delete response; return result ? e_ok : e_fail; } bool RadAliasAuth::OnSendPDU( RadiusPDU& /*pdu*/, const H225_RegistrationRequest& /*rrq*/, unsigned& /*rejectReason*/ ) { return true; } bool RadAliasAuth::OnSendPDU( RadiusPDU& /*pdu*/, const H225_AdmissionRequest& /*rrq*/, unsigned& /*rejectReason*/ ) { return true; } bool RadAliasAuth::OnSendPDU( RadiusPDU& /*pdu*/, const Q931& /*q931pdu*/, const H225_Setup_UUIE& /*setup*/, unsigned& /*releaseCompleteCause*/, long& /*durationLimit*/ ) { return true; } bool RadAliasAuth::OnReceivedPDU( RadiusPDU& /*pdu*/, H225_RegistrationRequest& /*rrq*/, unsigned& /*rejectReason*/ ) { return true; } bool RadAliasAuth::OnReceivedPDU( RadiusPDU& /*pdu*/, H225_AdmissionRequest& /*arq*/, unsigned& /*rejectReason*/, long& /*durationLimit*/ ) { return true; } bool RadAliasAuth::OnReceivedPDU( RadiusPDU& /*pdu*/, Q931& /*q931pdu*/, H225_Setup_UUIE& /*setup*/, unsigned& /*releaseCompleteCause*/, long& /*durationLimit*/ ) { return true; } #endif /* HAS_RADIUS */
/* * radauth.h * * RADIUS protocol authenticator modules for GNU Gatekeeper. * H.235 based and alias based authentication schemes are supported. * Please see docs/radauth.txt for more details. * * Copyright (c) 2003, Quarcom FHU, Michal Zygmuntowicz * * This work is published under the GNU Public License (GPL) * see file COPYING for details. * We also explicitely grant the right to link this code * with the OpenH323 library. * * $Log: radauth.h,v $ * Revision 1.1.2.21 2004/06/22 18:41:16 zvision * Username, Calling-Station-Id and Called-Station-Id handling rewritten. * Radius modules optimized. * * Revision 1.1.2.20 2004/06/18 15:42:51 zvision * Better User-Name and Calling-Station-Id handling for unregistered endpoints * * Revision 1.1.2.19 2004/05/12 17:46:40 zvision * Fixes CVS keyword substitution. New ParseEmailAliases config option. * * Revision 1.1.2.18 2004/05/12 14:00:47 zvision * Header file usage more consistent. Solaris std::map problems fixed. * Compilation warnings removed. VSNET2003 project files added. ANSI.h removed. * * Revision 1.1.2.17 2004/03/31 11:13:36 zvision * New CheckSetupUnregisteredOnly option for RadAliasAuth module * * Revision 1.1.2.16 2004/02/18 20:45:16 zvision * Prototype changed for GkAuthenticator::Check ARQ method * * Revision 1.1.2.15 2004/01/07 20:37:26 zvision * RRQ endpoint alias control (add/remove) through RadAuth/RadAliasAuth modules * * Revision 1.1.2.14 2003/12/21 12:20:38 zvision * Fixed conditional compilation * * Revision 1.1.2.13 2003/12/02 12:47:37 zvision * Q.931 Setup authenticator now returns (and documents) Q.931 cause values instead of H225_ReleaseCompleteReason * * Revision 1.1.2.12 2003/11/22 13:43:48 zvision * Q.931 Setup Check now accepts const callptr to prevent it from modifications * * Revision 1.1.2.11 2003/11/18 23:38:51 zvision * Q.931 Setup authentication optimized * * Revision 1.1.2.10 2003/09/30 11:08:16 zvision * Fixed non-working Q.931 authenticator due to previous Check(...) signature * changes. Many thanks to Oleg Ustinov! * * Revision 1.1.2.9 2003/09/24 10:19:44 zvision * Call duration limit for registered endpoints (through ARQ authenticators) * VS: ---------------------------------------------------------------------- * * Revision 1.1.2.8 2003/08/25 12:18:35 zvision * Added h323-ivr-out attribute with an alias list (thanks Mark Lipscombe) * * Revision 1.1.2.7 2003/07/31 13:09:15 zvision * Added Q.931 Setup message authentication and call duration limit feature * * Revision 1.1.2.6 2003/07/07 12:02:55 zvision * Improved H.235 handling. * * Revision 1.1.2.5 2003/05/28 13:25:19 zvision * Added alias based authentication (RadAliasAuth) * * Revision 1.1.2.4 2003/05/26 23:08:18 zvision * New OnSend and OnReceive hooks. * LocalInterface config parameter Introduced. * * Revision 1.1.2.3 2003/05/13 17:48:43 zvision * Removed acctPort. New includeFramedIP feature. * * Revision 1.1.2.2 2003/04/29 14:56:26 zvision * Added H.235 capability matching * * Revision 1.1.2.1 2003/04/23 20:16:25 zvision * Initial revision * */ #ifndef RADAUTH_H #define RADAUTH_H "#(@) $Id: radauth.h,v 1.1.2.21 2004/06/22 18:41:16 zvision Exp $" #ifdef HAS_RADIUS #include "gkauth.h" #include "radproto.h" /** * Gatekeeper authenticator module for RADIUS protocol. * Currently it supports user authentication through * CATs (Cisco Access Tokens) carried inside RRQ and ARQ * RAS messages. If your software does not support CATs, * please take a look at OpenH323 H235AuthCAT authenticator class * - it provides an implementation for CATs. */ class RadAuth : public GkAuthenticator { public: /** Create GkAuthenticator for RADIUS protocol */ RadAuth( /// authenticator settings PConfig* cfg, /// authenticator name from Gatekeeper::Auth section const char* authName ); /// Destroy the authenticator virtual ~RadAuth(); struct MemBlock { long callDuration; PString BalanceString; }; protected: /** Authenticate using data from RRQ RAS message. @return: #GkAuthenticator::Status enum# with the result of authentication. */ virtual int Check( /// RRQ RAS message to be authenticated H225_RegistrationRequest & rrq, /// reference to the variable, that can be set /// to custom H225_RegistrationRejectReason /// if the check fails unsigned& rejectReason,MemBlock * ); /** Authenticate using data from ARQ RAS message. @return: #GkAuthenticator::Status enum# with the result of authentication. */ virtual int Check( /// ARQ nessage to be authenticated H225_AdmissionRequest &, /// reference to the variable, that can be set /// to custom H225_AdmissionRejectReason /// if the check fails unsigned& rejectReason, /// call duration limit to be set (-1 for no duration limit) long& callDurationLimit ); /** Hook for adding/modifying pdu before it is sent. It can be used to add custom attributes, for example. @return TRUE if PDU should be sent, FALSE to reject RRQ (rejectReason can be set to indicate a particular reason). */ virtual bool OnSendPDU( RadiusPDU& pdu, /// PDU to be sent const H225_RegistrationRequest& rrq, /// RRQ being processed unsigned& rejectReason /// reject reason on return FALSE ); /** Hook for adding/modifying pdu before it is sent. It can be used to add custom attributes, for example. @return TRUE if PDU should be sent, FALSE to reject ARQ (rejectReason can be set to indicate a particular reason). */ virtual bool OnSendPDU( RadiusPDU& pdu, /// PDU to be sent const H225_AdmissionRequest& rrq, /// ARQ being processed unsigned& rejectReason /// reject reason on return FALSE ); /** Hook for processing pdu after it is received. It can be used to process custom attributes, for example. @return TRUE if PDU should be accepted, FALSE to reject RRQ (rejectReason can be set to indicate a particular reason). */ virtual bool OnReceivedPDU( RadiusPDU& pdu, /// received PDU H225_RegistrationRequest& rrq, /// RRQ being processed unsigned& rejectReason /// reject reason on return FALSE ); /** Hook for processing pdu after it is received. It can be used to process custom attributes, for example. @return TRUE if PDU should be accepted, FALSE to reject ARQ (rejectReason can be set to indicate a particular reason). */ virtual bool OnReceivedPDU( RadiusPDU& pdu, /// PDU received H225_AdmissionRequest& arq, /// ARQ being processed unsigned& rejectReason, /// reject reason on return FALSE long& durationLimit /// call duration limit to set (-1 means no limit) ); private: /* No copy constructor allowed */ RadAuth(const RadAuth&); /* No operator= allowed */ RadAuth& operator=(const RadAuth&); private: /// array of configured RADIUS server names PStringArray radiusServers; /// shared secret for gk client<->RADIUS server authorization PString sharedSecret; /// default port that will be used for sending RADIUS auth /// requests WORD authPort; /// base port number for UDP client socket allocation WORD portBase; /// max port number for UDP client socket allocation WORD portMax; /// timeout (ms) for a single RADIUS request unsigned requestTimeout; /// timeout (ms) for RADIUS requests IDs to be unique unsigned idCacheTimeout; /// timeout (ms) for unused sockets to be deleted unsigned socketDeleteTimeout; /// how many times to transmit a single request (1==no retransmission) /// to a single RADIUS server unsigned numRequestRetransmissions; /// retransmission fashion: /// FALSE - do #numRequestRetransmissions# for server A, /// then do #numRequestRetransmissions# for server B, etc. /// TRUE - transmit request to server A, then to server B, etc. /// the whole procedure repeat #numRequestRetransmissions# times bool roundRobin; /// if TRUE Cisco VSAs are appended to the RADIUS packets bool appendCiscoAttributes; /// if TRUE an h323-ivr-out attribute will be sent with every alias /// found inside RRQ.m_terminalAlias bool includeTerminalAliases; /// Local interface RADIUS client should be bound to (multihomed hosts) PString localInterface; /// IP address of the local interface PIPSocket::Address localInterfaceAddr; /// NAS identifier (GK name) PString NASIdentifier; /// RADIUS protocol client class associated with this authenticator RadiusClient* radiusClient; /// radius attributes that do not change - x4 performance boost RadiusAttr* attrH323GwId; RadiusAttr* attrH323CallType; RadiusAttr* attrH323CallOriginOriginate; RadiusAttr* attrH323CallOriginAnswer; RadiusAttr* attrNASIdentifier; /// OID (Object Identifier) for CAT alghoritm static PString OID_CAT; }; /** RADIUS Alias Authentication module. It authenticates endpoints/calls using non-H.235 attributes (alias,address,etc). */ class RadAliasAuth : public GkAuthenticator { public: /** Create GkAuthenticator for RADIUS Alias authenticator */ RadAliasAuth( /// authenticator settings PConfig* cfg, /// authenticator name from Gatekeeper::Auth section const char* authName ); /// Destroy the authenticator virtual ~RadAliasAuth(); protected: /** Authenticate using data from RRQ RAS message. @return: #GkAuthenticator::Status enum# with the result of authentication. */ virtual int Check( /// RRQ RAS message to be authenticated H225_RegistrationRequest & rrq, /// reference to the variable, that can be set /// to custom H225_RegistrationRejectReason /// if the check fails unsigned& rejectReason ); /** Authenticate using data from ARQ RAS message. @return: #GkAuthenticator::Status enum# with the result of authentication. */ virtual int Check( /// ARQ message to be authenticated H225_AdmissionRequest &, /// reference to the variable, that can be set /// to custom H225_AdmissionRejectReason /// if the check fails unsigned& rejectReason, /// call duration limit to be set (-1 for no duration limit) long& callDurationLimit ); /** Authenticate/Authorize Setup signalling message. @return e_fail - authentication failed e_ok - authenticated with this authenticator e_next - authentication could not be determined */ virtual int Check( /// received Q.931 Setup message Q931& q931pdu, /// received H.225 Setup message H225_Setup_UUIE& setup, /// CallRec for the call being authenticated callptr& call, /// Q.931 cause to set, if authentication failed unsigned& releaseCompleteCause, /// call duration limit to set (-1 for no duration limit) long& durationLimit ); /** Hook for adding/modifying pdu before it is sent. It can be used to add custom attributes, for example. @return TRUE if PDU should be sent, FALSE to reject RRQ (rejectReason can be set to indicate a particular reason). */ virtual bool OnSendPDU( RadiusPDU& pdu, /// PDU to be sent const H225_RegistrationRequest& rrq, /// RRQ being processed unsigned& rejectReason /// reject reason on return FALSE ); /** Hook for adding/modifying pdu before it is sent. It can be used to add custom attributes, for example. @return TRUE if PDU should be sent, FALSE to reject ARQ (rejectReason can be set to indicate a particular reason). */ virtual bool OnSendPDU( RadiusPDU& pdu, /// PDU to be sent const H225_AdmissionRequest& rrq, /// ARQ being processed unsigned& rejectReason /// reject reason on return FALSE ); /** Hook for adding/modifying pdu before it is sent. It can be used to add custom attributes, for example. @return TRUE if PDU should be sent, FALSE to reject Setup (releaseCompleteCode can be set to indicate a particular reason). */ virtual bool OnSendPDU( RadiusPDU& pdu, /// PDU to be sent const Q931& q931pdu, /// Q.931 Setup message being processed const H225_Setup_UUIE& setup, /// H.225 Setup message being processed unsigned& releaseCompleteCause, /// Q.931 cause on return FALSE long& durationLimit /// call duration limit to set (-1 means no limit) ); /** Hook for processing pdu after it is received. It can be used to process custom attributes, for example. @return TRUE if PDU should be accepted, FALSE to reject RRQ (rejectReason can be set to indicate a particular reason). */ virtual bool OnReceivedPDU( RadiusPDU& pdu, /// received PDU H225_RegistrationRequest& rrq, /// RRQ being processed unsigned& rejectReason /// reject reason on return FALSE ); /** Hook for processing pdu after it is received. It can be used to process custom attributes, for example. @return TRUE if PDU should be accepted, FALSE to reject ARQ (rejectReason can be set to indicate a particular reason). */ virtual bool OnReceivedPDU( RadiusPDU& pdu, /// PDU received H225_AdmissionRequest& arq, /// ARQ being processed unsigned& rejectReason, /// reject reason on return FALSE long& durationLimit /// call duration limit to set (-1 means no limit) ); /** Hook for processing pdu after it is received. It can be used to process custom attributes, for example. @return TRUE if PDU should be accepted, FALSE to reject Setup (releaseCompleteCode can be set to indicate a particular reason). */ virtual bool OnReceivedPDU( RadiusPDU& pdu, /// PDU received Q931& q931pdu, /// Q.931 Setup message being processed H225_Setup_UUIE& setup, /// H.225 Setup message being processed unsigned& releaseCompleteCause, /// Q.931 cause on return FALSE long& durationLimit /// call duration limit to set (-1 means no limit) ); private: /* No copy constructor allowed */ RadAliasAuth(const RadAliasAuth&); /* No operator= allowed */ RadAliasAuth& operator=(const RadAliasAuth&); private: /// array of configured RADIUS server names PStringArray radiusServers; /// shared secret for gk client<->RADIUS server authorization PString sharedSecret; /// default port that will be used for sending RADIUS auth /// requests WORD authPort; /// base port number for UDP client socket allocation WORD portBase; /// max port number for UDP client socket allocation WORD portMax; /// timeout (ms) for a single RADIUS request unsigned requestTimeout; /// timeout (ms) for RADIUS requests IDs to be unique unsigned idCacheTimeout; /// timeout (ms) for unused sockets to be deleted unsigned socketDeleteTimeout; /// how many times to transmit a single request (1==no retransmission) /// 2to a single RADIUS server unsigned numRequestRetransmissions; /// retransmission fashion: /// FALSE - do #numRequestRetransmissions# for server A, /// then do #numRequestRetransmissions# for server B, etc. /// TRUE - transmit request to server A, then to server B, etc. /// the whole procedure repeat #numRequestRetransmissions# times bool roundRobin; /// if TRUE Cisco VSAs are appended to the RADIUS packets bool appendCiscoAttributes; /// if TRUE an h323-ivr-out attribute will be sent with every alias /// found inside RRQ.m_terminalAlias bool includeTerminalAliases; /// check Q.931 Setup messages coming from unregistered endpoints only /// local interface RADIUS client should be bound to (multihomed hosts) PString localInterface; /// IP address of the local interface PIPSocket::Address localInterfaceAddr; /// NAS identifier (GK name) PString NASIdentifier; /// fixed username to be send in RADIUS requests instead of alias PString fixedUsername; /// fixed password to be send in RADIUS requests instead of duplicating User-Name PString fixedPassword; /// RADIUS protocol client class associated with this authenticator RadiusClient* radiusClient; /// radius attributes that do not change - x4 performance boost RadiusAttr* attrH323GwId; RadiusAttr* attrH323CallType; RadiusAttr* attrH323CallOriginOriginate; RadiusAttr* attrH323CallOriginAnswer; RadiusAttr* attrNASIdentifier; }; #endif /* HAS_RADIUS */ #endif /* RADAUTH_H */
////////////////////////////////////////////////////////////////// // // gkauth.cxx // // $Id // // 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: // 2003/10/23 LDAP support for Window (HAS_WLDAP) added (Franz J Ehrengruber) // 2001/09/19 initial version (Chih-Wei Huang) // ////////////////////////////////////////////////////////////////// #if (_MSC_VER >= 1200) #pragma warning( disable : 4786 ) // warning about too long debug symbol off #pragma warning( disable : 4800 ) // warning about forcing value to bool #endif #include <ptlib.h> #include <h323pdu.h> #include <h225.h> #include <h235.h> #include <h235auth.h> #include "gk_const.h" #include "h323util.h" #include "stl_supp.h" #include "Toolkit.h" #include "RasTbl.h" #include "gksql.h" #include "gkauth.h" #if defined(HAS_LDAP) // shall use LDAP (UNIX) #include "gkldap.h" #endif #if defined(HAS_WLDAP) // shall use WLDAP (Windows) #include "gk_wldap.h" #endif namespace { const char* const GkAuthSectionName = "Gatekeeper::Auth"; } GkAuthenticator *GkAuthenticator::head = 0; ////////////////////////////////////////////////////////////////////// // Definition of authentication rules class AliasAuth : public GkAuthenticator { public: AliasAuth(PConfig *, const char *); protected: virtual int Check(const H225_GatekeeperRequest &, unsigned &); virtual int Check(H225_RegistrationRequest &, unsigned &); virtual int Check(const H225_UnregistrationRequest &, unsigned &); virtual int Check(H225_AdmissionRequest &, unsigned &); virtual int Check(const H225_BandwidthRequest &, unsigned &); virtual int Check(const H225_DisengageRequest &, unsigned &); virtual int Check(const H225_LocationRequest &, unsigned &); virtual int Check(const H225_InfoRequest &, unsigned &); virtual bool doCheck(const H225_ArrayOf_TransportAddress &, const PString &); virtual bool AuthCondition(const H225_TransportAddress & SignalAdr, const PString &); private: /** @returns the value for a given alias from section #RasSrv::RRQAuth# in ini-file */ virtual PString GetConfigString(const PString & alias); }; #if HAS_MYSQL #include <mysql.h> class MySQLPasswordAuth : public SimplePasswordAuth { public: MySQLPasswordAuth( PConfig* cfg, const char* authName ); virtual ~MySQLPasswordAuth(); protected: virtual PString GetPassword( const PString& alias ); protected: GkSQLConnection* m_sqlConn; PString m_query; }; class MySQLAliasAuth : public AliasAuth { public: MySQLAliasAuth( PConfig* cfg, const char* authName ); virtual ~MySQLAliasAuth(); protected: virtual PString GetConfigString( const PString& alias ); protected: GkSQLConnection* m_sqlConn; CacheManager* m_cache; PString m_query; }; #endif // HAS_MYSQL /// Generic SQL authenticator for H.235 enabled endpoints class SQLPasswordAuth : public SimplePasswordAuth { public: /// build authenticator reading settings from the config SQLPasswordAuth( /// config to read settings from PConfig* cfg, /// name for this authenticator and for the config section to read settings from const char* authName ); virtual ~SQLPasswordAuth(); protected: /** Override from SimplePasswordAuth. @return Clear text password associated with the given alias or an empty string if no password is found. */ virtual PString GetPassword( /// alias to check the password for const PString& alias ); protected: /// connection to the SQL database GkSQLConnection* m_sqlConn; /// parametrized query string for password retrieval PString m_query; }; /// Generic SQL authenticator for alias/IP based authentication class SQLAliasAuth : public AliasAuth { public: /// build authenticator reading settings from the config SQLAliasAuth( /// config to read settings from PConfig* cfg, /// name for this authenticator and for the config section to read settings from const char* authName ); virtual ~SQLAliasAuth(); protected: /** Override from AliasAuth. @return Auth condition string associated with the given alias or an empty string if no match is found for the given alias. */ virtual PString GetConfigString( /// alias to retrieve the auth condition string for const PString& alias ); protected: /// connection to the SQL database GkSQLConnection* m_sqlConn; /// auth condition string cache CacheManager* m_cache; /// parametrized query string for the auth condition string retrieval PString m_query; }; #if ((defined(__GNUC__) && __GNUC__ <= 2) && !defined(WIN32)) #include <unistd.h> #include <procbuf.h> class ExternalPasswordAuth : public SimplePasswordAuth { public: ExternalPasswordAuth(PConfig *, const char *); virtual PString GetPassword(const PString &); private: bool ExternalInit(); PString Program; }; namespace { GkAuthInit<ExternalPasswordAuth> ExternalPasswordAuthFactory("ExternalPasswordAuth"); } #endif // LDAP authentification #if defined(HAS_LDAP) // shall use LDAP class LDAPPasswordAuth : public SimplePasswordAuth { public: LDAPPasswordAuth(PConfig *, const char *); virtual ~LDAPPasswordAuth(); virtual PString GetPassword(const PString &alias); virtual int Check(H225_RegistrationRequest & rrq, unsigned & reason); }; // ISO 14882:1998 (C++), ISO9899:1999 (C), ISO9945-1:1996 (POSIX) have a // very clear oppinion regarding user symbols starting or ending with '_' class LDAPAliasAuth : public AliasAuth { public: LDAPAliasAuth(PConfig *, const char *); virtual ~LDAPAliasAuth(); virtual int Check(H225_RegistrationRequest & rrq, unsigned &); private: /** Searchs for an alias in LDAP and converts it to a valid config string (the expected return value from LDAP is only an IP-address!). @returns config-string (format: see description in ini-file) */ virtual PString GetConfigString(const PString &alias); }; #endif // HAS_LDAP #if defined HAS_WLDAP class LDAPPasswordAuth : public SimplePasswordAuth { public: LDAPPasswordAuth(PConfig *, const char *); virtual ~LDAPPasswordAuth(); /** Searchs for an h323id/e164 alias in LDAP then: 1) mandatory: retrieves the H235Password attribute 2) optional: retrieves the AccountStatus attribute and converts it to a valid config string. (format: see description in ini-file) 3) optionally: retrieves the h323id/e164 alias attributes @returns the H323Password attribute or an empty string; */ virtual PString GetPassword(const PString &alias); /** calls SimplePasswordAuth::Check(rrq, reason) and then: 1) optionally converts AccountStatus attribute to a valid config string. (format: see description in ini-file) and calls doCheckAccountStatus(rrq.m_callSignalAddress, cfgString) to verify the account status 2) optionally: checks the supplied h323id/e164 alias are valid. @returns (result) ? e_ok : e_fail; */ virtual int Check(H225_RegistrationRequest & rrq, unsigned & reason); private: // see above virtual bool doCheckAccountStatus(const H225_ArrayOf_TransportAddress & addrs, const PString & cfgString); // see above virtual bool AuthCondition(const H225_TransportAddress & SignalAdr, const PString & Condition); int attribute_count; PString epAccountStatus_value; PString epH323ID_value; PStringList epTelephoneNo_value; PString epH235Password_value; }; // ISO 14882:1998 (C++), ISO9899:1999 (C), ISO9945-1:1996 (POSIX) have a // very clear oppinion regarding user symbols starting or ending with '_' class LDAPAliasAuth : public AliasAuth { public: LDAPAliasAuth(PConfig *, const char *); virtual ~LDAPAliasAuth(); /** Searchs for an h323id/e164 alias in LDAP then: 1) mandatory: retrieves the AccountStatus attribute and converts it to a valid config string. (format: see description in ini-file) 2) optionally: retrieves the h323id/e164 alias attributes and checks if the aliases are valid. @returns (AliasFoundInLDAP) ? e_ok : defaultStatus; */ virtual int Check(H225_RegistrationRequest & rrq, unsigned &); }; #endif // HAS_WLDAP // Initial author: Michael Rubashenkkov 2002/01/14 (GkAuthorize) // Completely rewrite by Chih-Wei Huang 2002/05/01 class AuthObj; class AuthRule; class PrefixAuth : public GkAuthenticator { public: PrefixAuth(PConfig *, const char *); ~PrefixAuth(); typedef std::map< PString, AuthRule *, greater<PString> > Rules; private: virtual int Check(const H225_GatekeeperRequest &, unsigned &); virtual int Check(H225_RegistrationRequest &, unsigned &); virtual int Check(const H225_UnregistrationRequest &, unsigned &); virtual int Check(H225_AdmissionRequest &, unsigned &); virtual int Check(const H225_BandwidthRequest &, unsigned &); virtual int Check(const H225_DisengageRequest &, unsigned &); virtual int Check(const H225_LocationRequest &, unsigned &); virtual int Check(const H225_InfoRequest &, unsigned &); virtual int doCheck(const AuthObj &); Rules prefrules; }; class CacheManager { public: CacheManager( long ttl /// cache expiration timeout (seconds) ) : m_ttl(ttl) {} bool Retrieve(const PString & key, PString & value); void Save(const PString & key, const PString & value); void SetTimeout( long newTimeout /// new cache expiration timeout ) { m_ttl = newTimeout; } private: std::map<PString, PString> m_cache; std::map<PString, long> m_ctime; // 0 means don't cache, -1 means never expires long m_ttl; // seconds CacheManager(const CacheManager &); CacheManager & operator=(const CacheManager &); }; bool CacheManager::Retrieve(const PString & key, PString & value) { std::map<PString, PString>::iterator iter = m_cache.find(key); if (iter == m_cache.end()) return false; if (m_ttl > 0) { std::map<PString, long>::iterator i = m_ctime.find(key); if ((time(NULL) - i->second) > m_ttl) return false; // cache expired } value = iter->second; PTRACE(5, "GkAuth\tCache found for " << key); return true; } void CacheManager::Save(const PString & key, const PString & value) { if (m_ttl != 0) { m_cache[key] = value; m_ctime[key] = time(NULL); } } ////////////////////////////////////////////////////////////////////// GkAuthenticator::GkAuthenticator(PConfig *cfg, const char *authName) : config(cfg), h235Authenticators(NULL), name(authName), checkFlag(e_ALL) { PStringArray control(config->GetString(GkAuthSectionName, name, "").Tokenise(";,")); if (PString(name) == "default") controlFlag = e_Sufficient, defaultStatus = Toolkit::AsBool(control[0]) ? e_ok : e_fail; else if (control[0] *= "optional") controlFlag = e_Optional, defaultStatus = e_next; else if (control[0] *= "required") controlFlag = e_Required, defaultStatus = e_fail; else controlFlag = e_Sufficient, defaultStatus = e_fail; if (control.GetSize() > 1) { checkFlag = 0; std::map<PString, int> rasmap; rasmap["GRQ"] = e_GRQ, rasmap["RRQ"] = e_RRQ, rasmap["URQ"] = e_URQ, rasmap["ARQ"] = e_ARQ, rasmap["BRQ"] = e_BRQ, rasmap["DRQ"] = e_DRQ, rasmap["LRQ"] = e_LRQ, rasmap["IRQ"] = e_IRQ, rasmap["Setup"] = e_Setup; rasmap["SetupUnreg"] = e_SetupUnreg; for (PINDEX i=1; i < control.GetSize(); ++i) { if (rasmap.find(control[i]) != rasmap.end()) checkFlag |= rasmap[control[i]]; } } next = head; head = this; PTRACE(1, "GkAuth\tAdd " << name << " rule with flag " << hex << checkFlag << dec); } GkAuthenticator::~GkAuthenticator() { PTRACE(1, "GkAuth\tRemove " << name << " rule"); delete next; // delete whole list recursively delete h235Authenticators; } bool GkAuthenticator::CheckRas( PBYTEArray &rawPDU, H225_RegistrationRequest& rrq, unsigned& rejectReason, const PIPSocket::Address& rxaddr,MemBlock *m ) { setLastReceivedRawPDU(rawPDU); if (checkFlag & RasValue(rrq)) { int r = Check(rrq, rejectReason,m); if (r == e_ok) { PTRACE(4, "GkAuth\t" << name << " check ok"); if (controlFlag != e_Required) return true; } else if (r == e_fail) { PTRACE(2, "GkAuth\t" << name << " check failed"); return false; } } // try next rule return next == NULL || next->CheckRas(rawPDU,rrq,rejectReason,rxaddr,m); } bool GkAuthenticator::CheckRas( PBYTEArray &rawPDU, H225_RegistrationRequest& rrq, unsigned& rejectReason, const PIPSocket::Address& rxaddr ) { setLastReceivedRawPDU(rawPDU); if (checkFlag & RasValue(rrq)) { int r = Check(rrq, rejectReason); if (r == e_ok) { PTRACE(4, "GkAuth\t" << name << " check ok"); if (controlFlag != e_Required) return true; } else if (r == e_fail) { PTRACE(2, "GkAuth\t" << name << " check failed"); return false; } } // try next rule return next == NULL || next->CheckRas(rawPDU,rrq,rejectReason,rxaddr); } bool GkAuthenticator::CheckRas( PBYTEArray &rawPDU, H225_AdmissionRequest& req, unsigned& rejectReason, long& callDurationLimit ) { callDurationLimit = -1; setLastReceivedRawPDU(rawPDU); if (checkFlag & RasValue(req)) { int r = Check(req, rejectReason, callDurationLimit); if( callDurationLimit == 0 ) { PTRACE(2,"GkAuth\t" << name << " - call duration limit is 0"); return false; } if (r == e_ok) { PTRACE(4, "GkAuth\t" << name << " check ok"); if (controlFlag != e_Required) return true; } else if (r == e_fail) { PTRACE(2, "GkAuth\t" << name << " check failed"); return false; } } // try next rule if( next ) { long limit = -1; if( !next->CheckRas(rawPDU,req,rejectReason,limit) ) return false; if( callDurationLimit >= 0 && limit >= 0 ) callDurationLimit = PMIN(limit,callDurationLimit); else callDurationLimit = PMAX(limit,callDurationLimit); } if( callDurationLimit == 0 ) { PTRACE(2,"GkAuth\t" << name << " - call duration limit is 0"); return false; } return true; } bool GkAuthenticator::CheckSig( /// received Q.931 Setup message Q931& q931pdu, /// received H.225 Setup message H225_Setup_UUIE& setup, /// CallRec for the call being authenticated callptr& call, /// Q.931 cause to set, if authentication fails unsigned& releaseCompleteCause, /// call duration limit (should be set initially to -1, /// what means no limit) long& callDurationLimit, /// is the call from a registered endpoint bool registered ) { callDurationLimit = -1; if (checkFlag & (registered ? e_Setup : (e_Setup | e_SetupUnreg))) { const int r = Check( q931pdu, setup, call, releaseCompleteCause, callDurationLimit ); if( callDurationLimit == 0 ) { PTRACE(2,"GkAuth\t"<<name<<" - call duration limit is 0"); return false; } if( r == e_ok ) { PTRACE(4,"GkAuth\t"<<name<<" Q.931 Setup check ok"); if( controlFlag != e_Required ) return true; } else if( r == e_fail ) { PTRACE(2,"GkAuth\t"<<name<<" Q.931 Setup check failed"); return false; } } // try next rule if( next ) { long limit = -1; if (!next->CheckSig(q931pdu, setup, call, releaseCompleteCause, limit, registered)) return false; if( callDurationLimit >= 0 && limit >= 0 ) callDurationLimit = PMIN(limit,callDurationLimit); else callDurationLimit = PMAX(limit,callDurationLimit); } if( callDurationLimit == 0 ) { PTRACE(2,"GkAuth\t"<<name<<" - call duration limit is 0"); return false; } return true; } int GkAuthenticator::Check(const H225_GatekeeperRequest &, unsigned &) { return defaultStatus; } int GkAuthenticator::Check(H225_RegistrationRequest &, unsigned &) { return defaultStatus; } int GkAuthenticator::Check(H225_RegistrationRequest & request, unsigned & rejectReason,MemBlock *) { return Check(request,rejectReason); } int GkAuthenticator::Check(const H225_UnregistrationRequest &, unsigned &) { return defaultStatus; } int GkAuthenticator::Check(H225_AdmissionRequest &, unsigned &) { return defaultStatus; } int GkAuthenticator::Check( H225_AdmissionRequest& request, unsigned& rejectReason, long& /*callDurationLimit*/ ) { // for backward compatibility with derived authenticators // that do not understand call duration limit feature return Check(request,rejectReason); } int GkAuthenticator::Check(const H225_BandwidthRequest &, unsigned &) { return defaultStatus; } int GkAuthenticator::Check(const H225_DisengageRequest &, unsigned &) { return defaultStatus; } int GkAuthenticator::Check(const H225_LocationRequest &, unsigned &) { return defaultStatus; } int GkAuthenticator::Check(const H225_InfoRequest &, unsigned &) { return defaultStatus; } int GkAuthenticator::Check( Q931&, H225_Setup_UUIE&, callptr&, unsigned&, long& ) { return defaultStatus; } BOOL GkAuthenticator::GetH235Capability( H225_ArrayOf_AuthenticationMechanism& mechanisms, H225_ArrayOf_PASN_ObjectId& algorithmOIDs ) const { if( h235Authenticators == NULL ) return FALSE; for( int i = 0; i < h235Authenticators->GetSize(); i++ ) (*h235Authenticators)[i].SetCapability(mechanisms,algorithmOIDs); return TRUE; } BOOL GkAuthenticator::IsH235Capability( const H235_AuthenticationMechanism& mechanism, const PASN_ObjectId& algorithmOID ) const { if( h235Authenticators == NULL ) return FALSE; for( int i = 0; i < h235Authenticators->GetSize(); i++ ) if( (*h235Authenticators)[i].IsCapability(mechanism,algorithmOID) ) return TRUE; return FALSE; } BOOL GkAuthenticator::IsH235Capable() const { return (h235Authenticators != NULL) && (h235Authenticators->GetSize() > 0); } PString GkAuthenticator::GetUsername( /// RRQ message with additional data const H225_RegistrationRequest& rrq ) const { 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( /// call (if any) associated with the RAS message const callptr& call, /// ARQ message with additional data const H225_AdmissionRequest& arq, /// endpoint sending the ARQ request const endptr& ep ) const { 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 (call) username = GetBestAliasAddressString(call->GetSourceAddress(), true, AliasAddressTagMask(H225_AliasAddress::e_h323_ID), AliasAddressTagMask(H225_AliasAddress::e_email_ID) | AliasAddressTagMask(H225_AliasAddress::e_url_ID) ); if (ep && (username.IsEmpty() || FindAlias(ep->GetAliases(), username) == P_MAX_INDEX)) username = GetBestAliasAddressString(ep->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 (call) username = GetBestAliasAddressString(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 (ep && GetIPFromTransportAddr(ep->GetCallSignalAddress(), addr) && addr.IsValid()) username = addr.AsString(); } return username; } PString GkAuthenticator::GetUsername( /// call (if any) associated with the Setup message const callptr& call, /// Q.931 Setup message with additional data const Q931& q931pdu, /// Setup-UUIE element extracted from the Q.931 Setup message const H225_Setup_UUIE& setup ) const { PString username; endptr callingEP; if (call) callingEP = call->GetCallingParty(); if (setup.HasOptionalField(setup.e_sourceAddress)) { username = GetBestAliasAddressString(setup.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() && call) { username = GetBestAliasAddressString(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() && call) username = GetBestAliasAddressString(call->GetSourceAddress(), false, AliasAddressTagMask(H225_AliasAddress::e_h323_ID), AliasAddressTagMask(H225_AliasAddress::e_email_ID) | AliasAddressTagMask(H225_AliasAddress::e_url_ID) ); if (username.IsEmpty() && setup.HasOptionalField(setup.e_sourceAddress)) username = GetBestAliasAddressString(setup.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; bool addrValid = false; if (call && call->HasSrcSignalAddr()) addrValid = call->GetSrcSignalAddr(addr, port) && addr.IsValid(); if (!addrValid && setup.HasOptionalField(setup.e_sourceCallSignalAddress)) addrValid = GetIPFromTransportAddr(setup.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( /// call (if any) associated with the RAS message const callptr& call, /// ARQ message with additional data const H225_AdmissionRequest& arq, /// endpoint sending the ARQ request const endptr& ep ) const { PString id; // Calling-Station-Id if (!arq.m_answerCall) // srcInfo is meaningful only in an originating ARQ id = GetBestAliasAddressString(arq.m_srcInfo, false, AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber) ); #ifdef HAS_ACCT else if (call) id = call->GetCallingStationId(); #endif if (!id) return id; if (id.IsEmpty() && call) id = GetBestAliasAddressString(call->GetSourceAddress(), false, AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber) ); if (id.IsEmpty() && ep && !arq.m_answerCall) id = GetBestAliasAddressString(ep->GetAliases(), false, AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber) ); if (id.IsEmpty() && arq.m_answerCall && call) { endptr callingEP = call->GetCallingParty(); if (callingEP) id = GetBestAliasAddressString( callingEP->GetAliases(), false, AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber) ); } if (id.IsEmpty() && call && call->HasSrcSignalAddr()) { PIPSocket::Address addr(0); WORD port = 0; if (call->GetSrcSignalAddr(addr, port) && addr.IsValid()) id = AsString(addr, port); } return id; } PString GkAuthenticator::GetCallingStationId( /// call (if any) associated with the Setup message const callptr& call, /// Q.931 Setup message with additional data const Q931& q931pdu, /// Setup-UUIE element extracted from the Q.931 Setup message const H225_Setup_UUIE& setup ) const { PString id; #ifdef HAS_ACCT if (call) id = call->GetCallingStationId(); #endif if (id.IsEmpty()) q931pdu.GetCallingPartyNumber(id); if (!id) return id; if (id.IsEmpty() && setup.HasOptionalField(setup.e_sourceAddress)) id = GetBestAliasAddressString(setup.m_sourceAddress, false, AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber) ); if (id.IsEmpty() && call) id = GetBestAliasAddressString(call->GetSourceAddress(), false, AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber) ); if (id.IsEmpty() && call) { endptr callingEP = call->GetCallingParty(); if (callingEP) id = GetBestAliasAddressString(callingEP->GetAliases(), false, AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber) ); } return id; } PString GkAuthenticator::GetCalledStationId( /// call (if any) associated with the RAS message const callptr& call, /// ARQ message with additional data const H225_AdmissionRequest& arq, /// endpoint sending the ARQ request const endptr& ep ) const { PString id; if (!arq.m_answerCall) { if (arq.HasOptionalField(H225_AdmissionRequest::e_destinationInfo)) id = GetBestAliasAddressString(arq.m_destinationInfo, false, AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber) ); } #ifdef HAS_ACCT else if (call) id = call->GetCalledStationId(); #endif if (!id) return id; if (id.IsEmpty() && call) id = GetBestAliasAddressString(call->GetDestinationAddress(), false, AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber) ); PIPSocket::Address addr; if (id.IsEmpty() && arq.m_answerCall) { if (arq.HasOptionalField(H225_AdmissionRequest::e_destinationInfo)) id = GetBestAliasAddressString(arq.m_destinationInfo, false, AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber) ); if (id.IsEmpty() && ep) id = GetBestAliasAddressString(ep->GetAliases(), false, AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber) ); if (id.IsEmpty() && ep && GetIPFromTransportAddr(ep->GetCallSignalAddress(), addr) && addr.IsValid()) id = addr.AsString(); } // this does not work well in routed mode, when destCallSignalAddress // is usually the gatekeeper address if (id.IsEmpty() && arq.HasOptionalField(H225_AdmissionRequest::e_destCallSignalAddress)) { const H225_TransportAddress& tsap = arq.m_destCallSignalAddress; id = AsDotString(tsap); } return id; } PString GkAuthenticator::GetCalledStationId( /// call (if any) associated with the Setup message const callptr& call, /// Q.931 Setup message with additional data const Q931& q931pdu, /// Setup-UUIE element extracted from the Q.931 Setup message const H225_Setup_UUIE& setup ) const { PString id; #ifdef HAS_ACCT if (call) id = call->GetCalledStationId(); #endif if (id.IsEmpty()) q931pdu.GetCalledPartyNumber(id); if (!id) return id; if (id.IsEmpty() && setup.HasOptionalField(setup.e_destinationAddress)) id = GetBestAliasAddressString(setup.m_destinationAddress, false, AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber) ); if (id.IsEmpty() && call) id = GetBestAliasAddressString(call->GetDestinationAddress(), false, AliasAddressTagMask(H225_AliasAddress::e_dialedDigits) | AliasAddressTagMask(H225_AliasAddress::e_partyNumber) ); if (id.IsEmpty()) { PIPSocket::Address addr; WORD port; if (call && call->HasDestSignalAddr() && call->GetDestSignalAddr(addr, port)) id = AsString(addr, port); else if (setup.HasOptionalField(setup.e_destCallSignalAddress) && GetIPAndPortFromTransportAddr(setup.m_destCallSignalAddress, addr, port) && addr.IsValid()) id = AsString(addr, port); } return id; } const char *passwdsec = "Password"; // SimplePasswordAuth SimplePasswordAuth::SimplePasswordAuth(PConfig *cfg, const char *authName) : GkAuthenticator(cfg, authName), aliases(0) { filled = config->GetInteger(passwdsec, "KeyFilled", 0); checkid = Toolkit::AsBool(config->GetString(passwdsec, "CheckID", "0")); m_cache = new CacheManager(config->GetInteger(passwdsec, "PasswordTimeout", -1)); if( h235Authenticators == NULL ) h235Authenticators = new H235Authenticators; H235Authenticator* authenticator; authenticator = new H235AuthSimpleMD5; authenticator->SetLocalId("dummy"); authenticator->SetRemoteId("dummy"); authenticator->SetPassword("dummy"); h235Authenticators->Append(authenticator); authenticator = new H235AuthCAT; authenticator->SetLocalId("dummy"); authenticator->SetRemoteId("dummy"); authenticator->SetPassword("dummy"); h235Authenticators->Append(authenticator); /* #if P_SSL authenticator = new H235AuthProcedure1; authenticator->SetLocalId("dummy"); authenticator->SetRemoteId("dummy"); authenticator->SetPassword("dummy"); h235Authenticators->Append(authenticator); #endif */ } SimplePasswordAuth::~SimplePasswordAuth() { delete m_cache; } int SimplePasswordAuth::Check(const H225_GatekeeperRequest & grq, unsigned &) { return doCheck(grq); } int SimplePasswordAuth::Check(H225_RegistrationRequest & rrq, unsigned &) { if (checkid) { if (!rrq.HasOptionalField(H225_RegistrationRequest::e_terminalAlias)) return e_fail; aliases = &rrq.m_terminalAlias; } return doCheck(rrq); } int SimplePasswordAuth::Check(const H225_UnregistrationRequest & urq, unsigned &) { return doCheck(urq); } int SimplePasswordAuth::Check(H225_AdmissionRequest & arq, unsigned &) { return doCheck(arq); } int SimplePasswordAuth::Check(const H225_BandwidthRequest & brq, unsigned &) { return doCheck(brq); } int SimplePasswordAuth::Check(const H225_DisengageRequest & drq, unsigned &) { return doCheck(drq); } int SimplePasswordAuth::Check(const H225_LocationRequest & lrq, unsigned &) { return doCheck(lrq); } int SimplePasswordAuth::Check(const H225_InfoRequest & drq, unsigned &) { return doCheck(drq); } PString SimplePasswordAuth::GetPassword(const PString & id) { return Toolkit::CypherDecode(id, config->GetString(passwdsec, id, ""), filled); } #if defined HAS_WLDAP // ini file section name extern const char *ldap_attr_name_sec; // [GkLDAP::LDAPAttributeNames] extern const char *ldap_auth_sec; // [GkLDAP::Settings] bool SimplePasswordAuth::InternalGetPassword(PString & passwd) { PStringList AliasList; // alias search presedence e164/h323id if (Toolkit::AsBool(GkConfig()->GetString(ldap_auth_sec, "LdapPasswAuthSearchPrecedenceE164", "0"))) { PString h323id; for (PINDEX j = 0; j < aliases->GetSize(); ++j) { if ((*aliases)[j].GetTag() == H225_AliasAddress::e_dialedDigits) { const PString & e164 = H323GetAliasAddressString((*aliases)[j]); PTRACE(5, "GkAuth\tSimplePasswordAuth::InternalGetPassword() e164 '" << e164 <<"'"); AliasList.AppendString(e164); } else if ((*aliases)[j].GetTag() == H225_AliasAddress::e_h323_ID) { h323id = H323GetAliasAddressString((*aliases)[j]); PTRACE(5, "GkAuth\tSimplePasswordAuth::InternalGetPassword() h323id '" << h323id <<"'"); } } AliasList.AppendString(h323id); } else { for (PINDEX j = 0; j < aliases->GetSize(); ++j) { PTRACE(5, "GkAuth\tSimplePasswordAuth::InternalGetPassword() H323GetAliasAddressString((*aliases)[j]) '" << H323GetAliasAddressString((*aliases)[j]) <<"'"); AliasList.AppendString(H323GetAliasAddressString((*aliases)[j])); } } // step through the list of aliases and search // LDAP for a password. If the returned string // 'passwd' is not empty we return result. bool result = false; for (PINDEX i = 0; i < AliasList.GetSize(); i++) { passwd = ""; result = m_cache->Retrieve(AliasList[i], passwd); if (!result) { passwd = GetPassword(AliasList[i]); PTRACE(5, "GkAuth\tSimplePasswordAuth::InternalGetPassword() passwd '" << passwd <<"'"); if(!passwd.IsEmpty()) { aliases = 0; return result; } } } aliases = 0; return result; } #else // any authenticator bool SimplePasswordAuth::InternalGetPassword(const PString & id, PString & passwd) { bool result = m_cache->Retrieve(id, passwd); if (!result) passwd = GetPassword(id); return result; } #endif // HAS_WLDAP bool SimplePasswordAuth::CheckAliases(const PString & id) { bool r = false; for (PINDEX i = 0; i < aliases->GetSize(); i++) if (H323GetAliasAddressString((*aliases)[i]) == id) { r = true; break; } #if !defined HAS_WLDAP aliases = 0; #endif return r; } static const char OID_CAT[] = "1.2.840.113548.10.1.2.1"; bool SimplePasswordAuth::CheckTokens(const H225_ArrayOf_ClearToken & tokens) { 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) ) return false; PString id = token.m_generalID; if (aliases && !CheckAliases(id)) return false; PString passwd; bool cached = false; #if defined HAS_WLDAP cached = InternalGetPassword(passwd); #else cached = InternalGetPassword(id, passwd); #endif // HAS_WLDAP H235AuthCAT authCAT; authCAT.SetLocalId(id); authCAT.SetPassword(passwd); if (authCAT.ValidateClearToken(token) == H235Authenticator::e_OK) { PTRACE(4, "GkAuth\t" << id << " password match (CAT)"); if (!cached) m_cache->Save(id, passwd); return true; } return false; } if (token.HasOptionalField(H235_ClearToken::e_generalID) && token.HasOptionalField(H235_ClearToken::e_password)) { PString id = token.m_generalID; if (aliases && !CheckAliases(id)) return false; PString passwd, tokenpasswd = token.m_password; bool cached = false; #if defined HAS_WLDAP cached = InternalGetPassword(passwd); #else cached = InternalGetPassword(id, passwd); #endif if (passwd == tokenpasswd) { PTRACE(4, "GkAuth\t" << id << " password match"); if (!cached) m_cache->Save(id, passwd); return true; } } } return false; } bool SimplePasswordAuth::CheckCryptoTokens(const H225_ArrayOf_CryptoH323Token & tokens) { for (PINDEX i = 0; i < tokens.GetSize(); ++i) { if (tokens[i].GetTag() == H225_CryptoH323Token::e_cryptoEPPwdHash) { H225_CryptoH323Token_cryptoEPPwdHash & pwdhash = tokens[i]; PString id = AsString(pwdhash.m_alias, FALSE); if (aliases && !CheckAliases(id)) return false; PString passwd; bool cached = false; #if defined HAS_WLDAP cached = InternalGetPassword(passwd); #else cached = InternalGetPassword(id, passwd); #endif // HAS_WLDAP H235AuthSimpleMD5 authMD5; authMD5.SetLocalId(id); authMD5.SetPassword(passwd); PBYTEArray nullPDU; if (authMD5.ValidateCryptoToken(tokens[i], nullPDU) == H235Authenticator::e_OK) { PTRACE(4, "GkAuth\t" << id << " password match (MD5)"); if (!cached) m_cache->Save(id, passwd); return true; } #if P_SSL } else if (tokens[i].GetTag() == H225_CryptoH323Token::e_nestedcryptoToken){ H235_CryptoToken & nestedCryptoToken = tokens[i]; H235_CryptoToken_cryptoHashedToken & cryptoHashedToken = nestedCryptoToken; H235_ClearToken & clearToken = cryptoHashedToken.m_hashedVals; PString gk_id = clearToken.m_generalID; //assumption: sendersID == endpoint alias (RRQ) PString ep_alias = clearToken.m_sendersID; if (aliases && !CheckAliases(ep_alias)) return false; PString passwd; bool cached = false; #if defined HAS_WLDAP cached = InternalGetPassword(passwd); #else cached = InternalGetPassword(ep_alias, passwd); #endif // HAS_WLDAP //if a password is not found: senderID == endpointIdentifier? if (passwd.IsEmpty()){ //get endpoint by endpointIdentifier H225_EndpointIdentifier ep_id; ep_id = clearToken.m_sendersID; endptr ep = RegistrationTable::Instance()->FindByEndpointId(ep_id); if(!ep){ return false; } //check all endpoint aliases for a password H225_ArrayOf_AliasAddress ep_aliases = ep->GetAliases(); for (PINDEX i = 0; i < ep_aliases.GetSize(); i++){ ep_alias = H323GetAliasAddressString(ep_aliases[i]); cached = InternalGetPassword(ep_alias, passwd); if (!passwd) break; } } H235AuthProcedure1 authProcedure1; authProcedure1.SetLocalId(gk_id); authProcedure1.SetPassword(passwd); if (authProcedure1.ValidateCryptoToken(tokens[i], getLastReceivedRawPDU()) == H235Authenticator::e_OK) { PTRACE(4, "GkAuth\t" << ep_alias << " password match (SHA-1)"); if (!cached) m_cache->Save(ep_alias, passwd); return true; } #endif } } return false; } #if HAS_MYSQL // MySQLPasswordAuth MySQLPasswordAuth::MySQLPasswordAuth( PConfig* cfg, const char* authName ) : SimplePasswordAuth(cfg, authName), m_sqlConn(NULL) { GkSQLCreator* driverCreator = GkSQLCreator::FindDriver("MySQL"); if (driverCreator == NULL) { PTRACE(1, GetName() << "\tModule creation failed: could not find driver for MySQL database"); return; } if (m_cache) m_cache->SetTimeout(cfg->GetInteger("MySQLAuth", "CacheTimeout", -1)); const PString password = cfg->GetString("MySQLAuth", "Password", ""); PString host = cfg->GetString("MySQLAuth", "Host", "localhost"); if (cfg->GetInteger("MySQLAuth", "Port", -1) > 0) host += ":" + PString(cfg->GetInteger("MySQLAuth", "Port", MYSQL_PORT)); m_sqlConn = driverCreator->CreateConnection(authName); if (!m_sqlConn->Initialize( host, cfg->GetString("MySQLAuth", "Database", "mysql"), cfg->GetString("MySQLAuth", "User", "mysql"), password.IsEmpty() ? (const char*)NULL : (const char*)password, 1, 1)) { delete m_sqlConn; m_sqlConn = NULL; PTRACE(2, GetName() << "\tModule creation failed: could not connect to the database"); return; } const PString table = cfg->GetString("MySQLAuth", "Table", ""); const PString passwordField = cfg->GetString("MySQLAuth", "PasswordField", ""); const PString aliasField = cfg->GetString("MySQLAuth", "IDField", ""); if (table.IsEmpty() || passwordField.IsEmpty() || aliasField.IsEmpty()) { PTRACE(1, GetName() << "\tCannot build query: Table, PasswordField or IDField not specified"); return; } m_query = "SELECT " + passwordField + " FROM " + table + " WHERE " + aliasField + " = '%1'"; const PString extraCrit = cfg->GetString("MySQLAuth", "ExtraCriterion", ""); if (!extraCrit.IsEmpty()) m_query += " AND " + extraCrit; PTRACE(4, GetName() << "\tConfigured query: " << m_query); } MySQLPasswordAuth::~MySQLPasswordAuth() { delete m_sqlConn; } PString MySQLPasswordAuth::GetPassword( const PString& alias ) { PString password; if (m_sqlConn == NULL) { PTRACE(2, GetName() << "\tPassword query for alias '" << alias << "' failed: SQL connection not active" ); return password; } if (m_query.IsEmpty()) { PTRACE(2, GetName() << "\tPassword query for alias '" << alias << "' failed: Query string not configured" ); return password; } PStringArray params; params += alias; GkSQLResult* result = m_sqlConn->ExecuteQuery(m_query, ¶ms); if (result == NULL) PTRACE(2, GetName() << "\tPassword query for alias '" << alias << "' failed: Timeout or fatal error" ); else { if (result->IsValid()) { PStringArray fields; if (result->GetNumRows() < 1) PTRACE(4, GetName() << "\tPassword not found for alias '" << alias << '\''); else if (result->GetNumFields() < 1) PTRACE(4, GetName() << "\tBad-formed query: no columns found in the result set"); else if ((!result->FetchRow(fields)) || fields.GetSize() < 1) PTRACE(4, GetName() << "\tPassword query for alias '" << alias << "' failed: could not fetch the result row" ); else password = fields[0]; } else PTRACE(2, GetName() << "\tPassword query for alias '" << alias << "' failed (" << result->GetErrorCode() << "): " << result->GetErrorMessage() ); delete result; } return password; } // MySQLAliasAuth MySQLAliasAuth::MySQLAliasAuth( PConfig* cfg, const char* authName ) : AliasAuth(cfg, authName), m_sqlConn(NULL), m_cache(NULL) { GkSQLCreator* driverCreator = GkSQLCreator::FindDriver("MySQL"); if (driverCreator == NULL) { PTRACE(1, GetName() << "\tModule creation failed: could not find driver for MySQL database"); return; } m_cache = new CacheManager(cfg->GetInteger(authName, "CacheTimeout", -1)); const PString password = cfg->GetString(authName, "Password", ""); PString host = cfg->GetString(authName, "Host", "localhost"); if (cfg->GetInteger(authName, "Port", -1) > 0) host += ":" + PString(cfg->GetInteger(authName, "Port", MYSQL_PORT)); m_sqlConn = driverCreator->CreateConnection(authName); if (!m_sqlConn->Initialize( host, cfg->GetString(authName, "Database", "mysql"), cfg->GetString(authName, "User", "mysql"), password.IsEmpty() ? (const char*)NULL : (const char*)password, 1, 1)) { delete m_sqlConn; m_sqlConn = NULL; PTRACE(2, GetName() << "\tModule creation failed: could not connect to the database"); return; } const PString table = cfg->GetString(authName, "Table", ""); const PString ipField = cfg->GetString(authName, "IPField", ""); const PString aliasField = cfg->GetString(authName, "IDField", ""); if (table.IsEmpty() || ipField.IsEmpty() || aliasField.IsEmpty()) { PTRACE(1, GetName() << "\tCannot build query: Table, IPField or IDField not specified"); return; } m_query = "SELECT " + ipField + " FROM " + table + " WHERE " + aliasField + " = '%1'"; const PString extraCrit = cfg->GetString(authName, "ExtraCriterion", ""); if (!extraCrit.IsEmpty()) m_query += " AND " + extraCrit; PTRACE(4, GetName() << "\tConfigured query: " << m_query); } MySQLAliasAuth::~MySQLAliasAuth() { delete m_cache; delete m_sqlConn; } PString MySQLAliasAuth::GetConfigString( const PString& alias ) { PString authCondition; if (m_sqlConn == NULL) { PTRACE(2, GetName() << "\tQuery for alias '" << alias << "' failed: SQL connection not active" ); return authCondition; } if (m_query.IsEmpty()) { PTRACE(2, GetName() << "\tQuery for alias '" << alias << "' failed: Query string not configured" ); return authCondition; } if (m_cache == NULL || m_cache->Retrieve(alias, authCondition)) return authCondition; PStringArray params; params += alias; GkSQLResult* result = m_sqlConn->ExecuteQuery(m_query, ¶ms); if (result == NULL) PTRACE(2, GetName() << "\tQuery for alias '" << alias << "' failed: Timeout or fatal error" ); else { if (result->IsValid()) { PStringArray fields; if (result->GetNumRows() < 1) PTRACE(4, GetName() << "\tEntry not found for alias '" << alias << '\''); else if (result->GetNumFields() < 1) PTRACE(4, GetName() << "\tBad-formed query: no columns found in the result set"); else if ((!result->FetchRow(fields)) || fields.GetSize() < 1) PTRACE(4, GetName() << "\tQuery for alias '" << alias << "' failed: could not fetch the result row" ); else { authCondition = fields[0]; m_cache->Save(alias, authCondition); } } else PTRACE(2, GetName() << "\tQuery for alias '" << alias << "' failed (" << result->GetErrorCode() << "): " << result->GetErrorMessage() ); delete result; } return authCondition; } #endif // HAS_MYSQL SQLPasswordAuth::SQLPasswordAuth( PConfig* cfg, const char* authName ) : SimplePasswordAuth(cfg, authName), m_sqlConn(NULL) { const PString driverName = cfg->GetString(authName, "Driver", ""); if (driverName.IsEmpty()) { PTRACE(1, GetName() << "\tModule creation failed: no SQL driver selected"); return; } GkSQLCreator* driverCreator = GkSQLCreator::FindDriver(driverName); if (driverCreator == NULL) { PTRACE(1, GetName() << "\tModule creation failed: could not find " << driverName << " database driver" ); return; } if (m_cache) m_cache->SetTimeout(cfg->GetInteger(authName, "CacheTimeout", 0)); m_sqlConn = driverCreator->CreateConnection(authName); if (!m_sqlConn->Initialize(cfg, authName)) { delete m_sqlConn; m_sqlConn = NULL; PTRACE(2, GetName() << "\tModule creation failed: could not connect to the database"); return; } m_query = cfg->GetString(authName, "Query", ""); if (m_query.IsEmpty()) PTRACE(1, GetName() << "\tModule creation failed: no query configured"); else PTRACE(4, GetName() << "\tConfigured query: " << m_query); } SQLPasswordAuth::~SQLPasswordAuth() { delete m_sqlConn; } PString SQLPasswordAuth::GetPassword( const PString& alias ) { PString password; if (m_sqlConn == NULL) { PTRACE(2, GetName() << "\tPassword query for alias '" << alias << "' failed: SQL connection not active" ); return password; } if (m_query.IsEmpty()) { PTRACE(2, GetName() << "\tPassword query for alias '" << alias << "' failed: Query string not configured" ); return password; } PStringArray params; params += alias; params += Toolkit::GKName(); GkSQLResult* result = m_sqlConn->ExecuteQuery(m_query, ¶ms); if (result == NULL) PTRACE(2, GetName() << "\tPassword query for alias '" << alias << "' failed: Timeout or fatal error" ); else { if (result->IsValid()) { PStringArray fields; if (result->GetNumRows() < 1) PTRACE(4, GetName() << "\tPassword not found for alias '" << alias << '\''); else if (result->GetNumFields() < 1) PTRACE(4, GetName() << "\tBad-formed query: no columns found in the result set"); else if ((!result->FetchRow(fields)) || fields.GetSize() < 1) PTRACE(4, GetName() << "\tPassword query for alias '" << alias << "' failed: could not fetch the result row" ); else password = fields[0]; } else PTRACE(2, GetName() << "\tPassword query for alias '" << alias << "' failed (" << result->GetErrorCode() << "): " << result->GetErrorMessage() ); delete result; } return password; } SQLAliasAuth::SQLAliasAuth( PConfig* cfg, const char* authName ) : AliasAuth(cfg, authName), m_sqlConn(NULL), m_cache(NULL) { const PString driverName = cfg->GetString(authName, "Driver", ""); if (driverName.IsEmpty()) { PTRACE(1, GetName() << "\tModule creation failed: no SQL driver selected"); return; } GkSQLCreator* driverCreator = GkSQLCreator::FindDriver(driverName); if (driverCreator == NULL) { PTRACE(1, GetName() << "\tModule creation failed: could not find " << driverName << " database driver" ); return; } m_cache = new CacheManager(config->GetInteger(authName, "CacheTimeout", 0)); m_sqlConn = driverCreator->CreateConnection(authName); if (!m_sqlConn->Initialize(cfg, authName)) { delete m_sqlConn; m_sqlConn = NULL; PTRACE(2, GetName() << "\tModule creation failed: could not connect to the database"); return; } m_query = cfg->GetString(authName, "Query", ""); if (m_query.IsEmpty()) PTRACE(1, GetName() << "\tModule creation failed: no query configured"); else PTRACE(4, GetName() << "\tConfigured query: " << m_query); } SQLAliasAuth::~SQLAliasAuth() { delete m_sqlConn; delete m_cache; } PString SQLAliasAuth::GetConfigString( const PString& alias ) { PString authCondition; if (m_sqlConn == NULL) { PTRACE(2, GetName() << "\tQuery for alias '" << alias << "' failed: SQL connection not active" ); return authCondition; } if (m_query.IsEmpty()) { PTRACE(2, GetName() << "\tQuery for alias '" << alias << "' failed: Query string not configured" ); return authCondition; } if (m_cache == NULL || m_cache->Retrieve(alias, authCondition)) return authCondition; PStringArray params; params += alias; params += Toolkit::GKName(); GkSQLResult* result = m_sqlConn->ExecuteQuery(m_query, ¶ms); if (result == NULL) PTRACE(2, GetName() << "\tQuery for alias '" << alias << "' failed: Timeout or fatal error" ); else { if (result->IsValid()) { PStringArray fields; if (result->GetNumRows() < 1) PTRACE(4, GetName() << "\tEntry not found for alias '" << alias << '\''); else if (result->GetNumFields() < 1) PTRACE(4, GetName() << "\tBad-formed query: no columns found in the result set"); else if ((!result->FetchRow(fields)) || fields.GetSize() < 1) PTRACE(4, GetName() << "\tQuery for alias '" << alias << "' failed: could not fetch the result row" ); else { authCondition = fields[0]; m_cache->Save(alias, authCondition); } } else PTRACE(2, GetName() << "\tQuery for alias '" << alias << "' failed (" << result->GetErrorCode() << "): " << result->GetErrorMessage() ); delete result; } return authCondition; } #if ((defined(__GNUC__) && __GNUC__ <= 2) && !defined(WIN32)) // ExternalPasswordAuth ExternalPasswordAuth::ExternalPasswordAuth(PConfig * cfg, const char * authName) : SimplePasswordAuth(cfg, authName) { ExternalInit(); } bool ExternalPasswordAuth::ExternalInit() { const char *ExternalSec = "ExternalAuth"; // Read the configuration Program = config->GetString(ExternalSec, "PasswordProgram", ""); return true; } PString ExternalPasswordAuth::GetPassword(const PString & id) { const int BUFFSIZE = 256; char buff[BUFFSIZE] = ""; if (!Program) { procbuf proc(Program + " " + id, ios::in); istream istr(&proc); istr.getline(buff, BUFFSIZE); } else { PTRACE(2, "GkAuth\tProgram is not defined"); } return PString(buff); } #endif // class ExternalPasswordAuth // LDAP authentification #if defined(HAS_LDAP) LDAPPasswordAuth::LDAPPasswordAuth(PConfig * cfg, const char * authName) : SimplePasswordAuth(cfg, authName) { } LDAPPasswordAuth::~LDAPPasswordAuth() { } PString LDAPPasswordAuth::GetPassword(const PString & alias) { PStringList attr_values; using namespace lctn; // LDAP config tags and names // get pointer to new answer object if(GkLDAP::Instance()->getAttribute(alias, H235PassWord, attr_values) && !attr_values.IsEmpty()){ return attr_values[0]; } return ""; } int LDAPPasswordAuth::Check(H225_RegistrationRequest & rrq, unsigned & reason) { int result = SimplePasswordAuth::Check(rrq, reason); if(result == e_ok) { // check if all aliases in RRQ exists in LDAP entry const H225_ArrayOf_AliasAddress & aliases = rrq.m_terminalAlias; if(!GkLDAP::Instance()->validAliases(aliases)) { result = e_fail; } } return result; } LDAPAliasAuth::LDAPAliasAuth(PConfig *cfg, const char *authName) : AliasAuth(cfg, authName) { } LDAPAliasAuth::~LDAPAliasAuth() { } PString LDAPAliasAuth::GetConfigString(const PString &alias) { PStringList attr_values; using namespace lctn; // LDAP config tags and names // get pointer to new answer object if (GkLDAP::Instance()->getAttribute(alias, IPAddress, attr_values) && (!attr_values.IsEmpty())) { PString ip = attr_values[0]; if(!ip.IsEmpty()){ PString port = GK_DEF_ENDPOINT_SIGNAL_PORT; return "sigip:" + ip + ":" + port; } } return ""; } int LDAPAliasAuth::Check(H225_RegistrationRequest & rrq, unsigned & reason) { int result = AliasAuth::Check(rrq, reason); if(result == e_ok) { // check if all aliases in RRQ exists in LDAP entry const H225_ArrayOf_AliasAddress & aliases = rrq.m_terminalAlias; if(!GkLDAP::Instance()->validAliases(aliases)) { result = e_fail; } } return result; } #endif // HAS_LDAP #if defined HAS_WLDAP LDAPPasswordAuth::LDAPPasswordAuth(PConfig * cfg, const char * authName) : SimplePasswordAuth(cfg, authName) { } LDAPPasswordAuth::~LDAPPasswordAuth() { } PString LDAPPasswordAuth::GetPassword(const PString & alias) // called by SimplePasswordAuth::InternalGetPassword() { LDAPAnswer * answer = new LDAPAnswer; bool CheckAliases = false; bool CheckStatus = false; PStringArray attributes; // attributes to retrieve using namespace lctn; attributes += LDAPAttrTags[H235PassWord]; if (CheckStatus = (Toolkit::AsBool(GkConfig()->GetString(ldap_auth_sec, "LdapPasswAuthCheckAccountStatus", "0")))) attributes += LDAPAttrTags[AccountStatus]; if (CheckAliases = (Toolkit::AsBool(GkConfig()->GetString(ldap_auth_sec, "LdapAliasAuthCheckAliases", "0")))) { attributes += LDAPAttrTags[H323ID]; attributes += LDAPAttrTags[TelephonNo]; } attribute_count = GkLDAP::Instance()->getAttributes(alias, attributes, answer); // don't bother to get attr values // if attribute_count is zero if(attribute_count) { // extract attribute values PStringList attrib_values; int attrvalues_num = (GkLDAP::Instance()->getAttributeValues(answer, H235PassWord, attrib_values)); if(!attrib_values.IsEmpty()) epH235Password_value = attrib_values[0]; if(CheckStatus) { attrib_values.RemoveAll(); attrvalues_num = (GkLDAP::Instance()->getAttributeValues(answer, AccountStatus, attrib_values)); if(!attrib_values.IsEmpty()) epAccountStatus_value = attrib_values[0]; } if(CheckAliases) { attrib_values.RemoveAll(); attrvalues_num = (GkLDAP::Instance()->getAttributeValues(answer, H323ID, attrib_values)); if(!attrib_values.IsEmpty()) epH323ID_value = attrib_values[0]; attrvalues_num = (GkLDAP::Instance()->getAttributeValues(answer, TelephonNo, epTelephoneNo_value)); } } delete answer; // success if(attribute_count && !epH235Password_value.IsEmpty()) return epH235Password_value; // failed return ""; } int LDAPPasswordAuth::Check(H225_RegistrationRequest & rrq, unsigned & reason) { // reset data class members attribute_count = 0; epAccountStatus_value = ""; epH323ID_value = ""; epTelephoneNo_value.RemoveAll(); epH235Password_value = ""; // reqired, sets global aliases checkid = true; // get the password for alias int result = SimplePasswordAuth::Check(rrq, reason); // success - do further checks if(result == e_ok && attribute_count > 0) { // check account status // Note: empty authentication attribute values indicate the // password was cached, so the check below would fail! if (Toolkit::AsBool(GkConfig()->GetString(ldap_auth_sec, "LdapPasswAuthCheckAccountStatus", "0"))) { if(!epAccountStatus_value.IsEmpty()) { const PString cfgString = epAccountStatus_value; PTRACE(4, "Gk\tLDAPPasswordAuth::Check() '" << cfgString << "' " << epH323ID_value); if (!cfgString) { if (doCheckAccountStatus(rrq.m_callSignalAddress, cfgString)) { result = e_ok; } else { PTRACE(4, "Gk\tLDAPAliasAuth::Check() condition '" << cfgString << "' rejected endpoint " << epH323ID_value); return e_fail; } } } } // check account status if (Toolkit::AsBool(GkConfig()->GetString(ldap_auth_sec, "LdapPasswAuthCheckAliases", "0"))) { // check if all aliases in RRQ exists in LDAP entry only if // autentication attribute values are valid. see note below // Note: empty authentication attribute values indicate the // password was cached, so the check below would fail! if(!epH323ID_value.IsEmpty() && !epTelephoneNo_value.IsEmpty()) { const H225_ArrayOf_AliasAddress & aliases = rrq.m_terminalAlias; if(!GkLDAP::Instance()->validateAliases(aliases, epH323ID_value, epTelephoneNo_value)) { PTRACE(4, "Gk\tLDAPPasswordAuth::Check() validateAliases() failed"); return e_fail; } PTRACE(4, "Gk\tLDAPPasswordAuth::Check() validateAliases() success"); } } } // result == e_ok & attribute_count return result; } bool LDAPPasswordAuth::doCheckAccountStatus(const H225_ArrayOf_TransportAddress & addrs, const PString & cfgString) { const PStringArray conditions(cfgString.Tokenise("&|", FALSE)); for (PINDEX i = 0; i < conditions.GetSize(); ++i) { for (PINDEX j = 0; j < addrs.GetSize(); ++j) { if (AuthCondition(addrs[j], conditions[i])) { PTRACE(4, "Gk\tLDAPPasswordAuth::doCheckAccountStatus() '" << conditions[i] << "' applied successfully for endpoint " << AsDotString(addrs[j])); return true; } } } return false; } bool LDAPPasswordAuth::AuthCondition(const H225_TransportAddress & SignalAdr, const PString & Condition) { const bool ON_ERROR = false; // return value on parse error in condition const PStringArray rule = Condition.Tokenise(":", FALSE); if (rule.GetSize() < 1) { PTRACE(1, "LDAPPasswordAuth::AuthCondition() Errornous RRQAuth rule: " << Condition); return ON_ERROR; } // // condition = rule[0]:rule[1]... = rName:params... // const PString &rName = rule[0]; if (rName=="confirm" || rName=="allow") { return true; } else if (rName=="reject" || rName=="deny" || rName=="forbid") { return false; } // // condition 'sigaddr' example: // sigaddr:.*ipAddress .* ip = .* c3 47 e2 a2 .*port = 1720.* // else if (rName=="sigaddr") { if(rule.GetSize() < 2) return false; return Toolkit::MatchRegex(AsString(SignalAdr), rule[1]) != 0; } // // condition 'sigip' example: // sigip:195.71.129.69:1720 // else if (rName=="sigip") { if (rule.GetSize() < 2) return false; PIPSocket::Address ip; PIPSocket::GetHostAddress(rule[1], ip); WORD port = (rule.GetSize() < 3) ? GK_DEF_ENDPOINT_SIGNAL_PORT : (WORD)rule[2].AsInteger(); return (SignalAdr == SocketToH225TransportAddr(ip, port)); } else { PTRACE(4, "Gk\tLDAPPasswordAuth::AuthCondition() Unknown RRQAuth condition: " << Condition); return ON_ERROR; } // not reached... return false; } LDAPAliasAuth::LDAPAliasAuth(PConfig *cfg, const char *authName) : AliasAuth(cfg, authName) { } LDAPAliasAuth::~LDAPAliasAuth() { } int LDAPAliasAuth::Check(H225_RegistrationRequest & rrq, unsigned & reason) { int result; bool AliasFoundInLDAP = false; if (!rrq.HasOptionalField(H225_RegistrationRequest::e_terminalAlias)) return defaultStatus; const H225_ArrayOf_AliasAddress & NewAliases = rrq.m_terminalAlias; PStringList AliasList; // alias search presedence e164/h323id if (Toolkit::AsBool(GkConfig()->GetString(ldap_auth_sec, "LdapAliasAuthSearchPrecedenceE164", "0"))) { PString h323id; for (PINDEX j = 0; j < NewAliases.GetSize(); ++j) { if (NewAliases[j].GetTag() == H225_AliasAddress::e_dialedDigits) AliasList.AppendString(H323GetAliasAddressString(NewAliases[j])); else if (NewAliases[j].GetTag() == H225_AliasAddress::e_h323_ID) h323id = H323GetAliasAddressString(NewAliases[j]); } AliasList.AppendString(h323id); } else { for (PINDEX j = 0; j < NewAliases.GetSize(); ++j) { AliasList.AppendString(H323GetAliasAddressString(NewAliases[j])); } } // step through alias list and search LDAP for (PINDEX i = 0; i < AliasList.GetSize(); ++i) { // attributes values int attribute_count; PString epAccountStatus_value; PString epH323ID_value; PStringList epTelephoneNo_value; LDAPAnswer * answer = new LDAPAnswer; bool CheckAliases = false; PStringArray attributes; // attributes to return using namespace lctn; attributes += LDAPAttrTags[AccountStatus]; if (CheckAliases = (Toolkit::AsBool(GkConfig()->GetString(ldap_auth_sec, "LdapAliasAuthCheckAliases", "0")))) { attributes += LDAPAttrTags[H323ID]; attributes += LDAPAttrTags[TelephonNo]; } attribute_count = GkLDAP::Instance()->getAttributes(AliasList[i], attributes, answer); // don't bother to get attr values // if attribute_count is zero if(attribute_count) { // extract attribute values PStringList attrib_values; int attrvalues_num = (GkLDAP::Instance()->getAttributeValues(answer, AccountStatus, attrib_values)); if(!attrib_values.IsEmpty()) epAccountStatus_value = attrib_values[0]; if(CheckAliases) { attrib_values.RemoveAll(); attrvalues_num = (GkLDAP::Instance()->getAttributeValues(answer, H323ID, attrib_values)); if(!attrib_values.IsEmpty()) epH323ID_value = attrib_values[0]; attrvalues_num = (GkLDAP::Instance()->getAttributeValues(answer, TelephonNo, epTelephoneNo_value)); } } delete answer; // LDAP success if(attribute_count > 0 && !epAccountStatus_value.IsEmpty()) { const PString cfgString = epAccountStatus_value; PTRACE(5, "Gk\tLDAPAliasAuth::Check() '" << cfgString << "' " << AliasList[i]); if (!cfgString) { if (doCheck(rrq.m_callSignalAddress, cfgString)) { AliasFoundInLDAP = true; result = e_ok; // check if all aliases in RRQ exists in LDAP entry = second step if(CheckAliases) { if(!epH323ID_value.IsEmpty() && !epTelephoneNo_value.IsEmpty()) { const H225_ArrayOf_AliasAddress & aliases = rrq.m_terminalAlias; if(!GkLDAP::Instance()->validateAliases(aliases, epH323ID_value, epTelephoneNo_value)) { PTRACE(4, "Gk\tLDAPAliasAuth::Check() validateAliases() failed"); return e_fail; } PTRACE(4, "Gk\tLDAPAliasAuth::Check() validateAliases() success"); } else { PTRACE(4, "Gk\tLDAPAliasAuth::Check() one or more aliases missing"); return e_fail; } } break; } else { PTRACE(4, "Gk\tLDAPAliasAuth::Check() condition '" << cfgString << "' rejected endpoint " << AliasList[i]); return e_fail; } } } // if } // for // return success/failure result = (AliasFoundInLDAP) ? e_ok : defaultStatus; return result; } #endif // HAS_WLDAP // AliasAuth AliasAuth::AliasAuth(PConfig *cfg, const char *authName) : GkAuthenticator(cfg, authName) { } int AliasAuth::Check(const H225_GatekeeperRequest &, unsigned &) { return e_next; } int AliasAuth::Check(H225_RegistrationRequest & rrq, unsigned &) { bool AliasFoundInConfig = false; if (!rrq.HasOptionalField(H225_RegistrationRequest::e_terminalAlias)) return defaultStatus; const H225_ArrayOf_AliasAddress & NewAliases = rrq.m_terminalAlias; // alias is the config file entry of this endpoint for (PINDEX i = 0; i <= NewAliases.GetSize(); ++i) { const PString alias = (i < NewAliases.GetSize()) ? AsString(NewAliases[i], FALSE) : PString("default"); const PString cfgString = GetConfigString(alias); if (!cfgString) { if (doCheck(rrq.m_callSignalAddress, cfgString)) { AliasFoundInConfig = true; break; } else { PTRACE(4, "Gk\tRRQAuth condition '" << cfgString << "' rejected endpoint " << alias); return e_fail; } } } return (AliasFoundInConfig) ? e_ok : defaultStatus; } int AliasAuth::Check(const H225_UnregistrationRequest &, unsigned &) { return e_next; } int AliasAuth::Check(H225_AdmissionRequest &, unsigned &) { return e_next; } int AliasAuth::Check(const H225_BandwidthRequest &, unsigned &) { return e_next; } int AliasAuth::Check(const H225_DisengageRequest &, unsigned &) { return e_next; } int AliasAuth::Check(const H225_LocationRequest &, unsigned &) { return e_next; } int AliasAuth::Check(const H225_InfoRequest &, unsigned &) { return e_next; } PString AliasAuth::GetConfigString(const PString & alias) { return config->GetString("RasSrv::RRQAuth", alias, ""); } bool AliasAuth::doCheck(const H225_ArrayOf_TransportAddress & addrs, const PString & cfgString) { const PStringArray conditions(cfgString.Tokenise("&|", FALSE)); for (PINDEX i = 0; i < conditions.GetSize(); ++i) { for (PINDEX j = 0; j < addrs.GetSize(); ++j) { if (AuthCondition(addrs[j], conditions[i])) { PTRACE(4, "Gk\tRRQAuth condition '" << conditions[i] << "' applied successfully for endpoint " << AsDotString(addrs[j])); return true; } } } return false; } bool AliasAuth::AuthCondition(const H225_TransportAddress & SignalAdr, const PString & Condition) { const bool ON_ERROR = false; // return value on parse error in condition const PStringArray rule = Condition.Tokenise(":", FALSE); if (rule.GetSize() < 1) { PTRACE(1, "Errornous RRQAuth rule: " << Condition); return ON_ERROR; } // // condition = rule[0]:rule[1]... = rName:params... // const PString &rName = rule[0]; if (rName=="confirm" || rName=="allow") { return true; } else if (rName=="reject" || rName=="deny" || rName=="forbid") { return false; } // // condition 'sigaddr' example: // sigaddr:.*ipAddress .* ip = .* c3 47 e2 a2 .*port = 1720.* // else if (rName=="sigaddr") { if(rule.GetSize() < 2) return false; return Toolkit::MatchRegex(AsString(SignalAdr), rule[1]) != 0; } // // condition 'sigip' example: // sigip:195.71.129.69:1720 // else if (rName=="sigip") { if (rule.GetSize() < 2) return false; PIPSocket::Address ip; PIPSocket::GetHostAddress(rule[1], ip); WORD port = (rule.GetSize() < 3) ? GK_DEF_ENDPOINT_SIGNAL_PORT : (WORD)rule[2].AsInteger(); return (SignalAdr == SocketToH225TransportAddr(ip, port)); } else { PTRACE(4, "Unknown RRQAuth condition: " << Condition); return ON_ERROR; } // not reached... return false; } // Help classes for PrefixAuth static const char* const prfflag="prf:"; static const char* const allowflag="allow"; static const char* const denyflag="deny"; static const char* const ipflag="ipv4:"; static const char* const aliasflag="alias:"; 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 RRQAuthObj : public AuthObj { public: RRQAuthObj(const H225_RegistrationRequest & ras) : rrq(ras) {} virtual PStringArray GetPrefixes() const; virtual PIPSocket::Address GetIP() const; virtual PString GetAliases() const; private: const H225_RegistrationRequest & rrq; }; class ARQAuthObj : public AuthObj { public: ARQAuthObj(const H225_AdmissionRequest & ras); virtual bool IsValid() const { return ep; } virtual PStringArray GetPrefixes() const; virtual PIPSocket::Address GetIP() const; virtual PString GetAliases() const; private: const H225_AdmissionRequest & arq; endptr ep; }; ARQAuthObj::ARQAuthObj(const H225_AdmissionRequest & ras) : arq(ras) { ep = RegistrationTable::Instance()->FindByEndpointId(arq.m_endpointIdentifier); } PStringArray ARQAuthObj::GetPrefixes() const { PStringArray array; if (arq.HasOptionalField(H225_AdmissionRequest::e_destinationInfo)) if (PINDEX ss = arq.m_destinationInfo.GetSize() > 0) { array.SetSize(ss); for (PINDEX i = 0; i < ss; ++i) array[i] = AsString(arq.m_destinationInfo[i], FALSE); } if (array.GetSize() == 0) // let empty destinationInfo match the ALL rule array.SetSize(1); return array; } PIPSocket::Address ARQAuthObj::GetIP() const { PIPSocket::Address result; const H225_TransportAddress & addr = (arq.HasOptionalField(H225_AdmissionRequest::e_srcCallSignalAddress)) ? arq.m_srcCallSignalAddress : ep->GetCallSignalAddress(); GetIPFromTransportAddr(addr, result); return result; } PString ARQAuthObj::GetAliases() const { return AsString(ep->GetAliases()); } class LRQAuthObj : public AuthObj { public: LRQAuthObj(const H225_LocationRequest & ras); virtual PStringArray GetPrefixes() const; virtual PIPSocket::Address GetIP() const; virtual PString GetAliases() const; private: const H225_LocationRequest & lrq; PIPSocket::Address ipaddress; }; LRQAuthObj::LRQAuthObj(const H225_LocationRequest & ras) : lrq(ras) { GetIPFromTransportAddr(lrq.m_replyAddress, ipaddress); } PStringArray LRQAuthObj::GetPrefixes() const { PStringArray array; if (PINDEX ss = lrq.m_destinationInfo.GetSize() > 0) { array.SetSize(ss); for (PINDEX i = 0; i < ss; ++i) array[i] = AsString(lrq.m_destinationInfo[i], FALSE); } return array; } PIPSocket::Address LRQAuthObj::GetIP() const { return ipaddress; } PString LRQAuthObj::GetAliases() const { return (lrq.HasOptionalField(H225_LocationRequest::e_sourceInfo)) ? AsString(lrq.m_sourceInfo) : PString(); } class AuthRule { public: enum Result { e_nomatch, e_allow, e_deny }; AuthRule(Result f, bool r) : priority(1000), fate(f), inverted(r), next(0) {} virtual ~AuthRule() { delete next; } virtual bool Match(const AuthObj &) = 0; int Check(const AuthObj &); bool operator<(const AuthRule & o) const { return priority < o.priority; } void SetNext(AuthRule *n) { next = n; } // virtual PString GetName() const { return PString(); } protected: int priority; // the lesser the value, the higher the priority private: Result fate; bool inverted; AuthRule *next; }; int AuthRule::Check(const AuthObj & aobj) { // PTRACE(3, "auth\t" << GetName()); return (Match(aobj) ^ inverted) ? fate : (next) ? next->Check(aobj) : e_nomatch; } inline void delete_rule(PrefixAuth::Rules::value_type r) { delete r.second; } class NullRule : public AuthRule { public: NullRule() : AuthRule(e_nomatch, false) {} virtual bool Match(const AuthObj &) { return false; } }; class IPv4AuthRule : public AuthRule { public: IPv4AuthRule(Result, const PString &, bool); private: virtual bool Match(const AuthObj &); // virtual PString GetName() const { return network.AsString() + "/" + PString(PString::Unsigned, 32-priority); } PIPSocket::Address network, netmask; }; IPv4AuthRule::IPv4AuthRule(Result f, const PString & cfg, bool r) : AuthRule(f, r) { Toolkit::GetNetworkFromString(cfg, network, netmask); DWORD n = ~PIPSocket::Net2Host(DWORD(netmask)); for (priority = 0; n; n >>= 1) ++priority; } bool IPv4AuthRule::Match(const AuthObj & aobj) { return ((aobj.GetIP() & netmask) == network); } class AliasAuthRule : public AuthRule { public: AliasAuthRule(Result f, const PString & cfg, bool r) : AuthRule(f, r), pattern(cfg) { priority = -1; } private: virtual bool Match(const AuthObj &); // virtual PString GetName() const { return pattern; } PString pattern; }; bool AliasAuthRule::Match(const AuthObj & aobj) { return (aobj.GetAliases().FindRegEx(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; } // PrefixAuth PrefixAuth::PrefixAuth(PConfig *cfg, const char *authName) : GkAuthenticator(cfg, authName) { int ipfl = strlen(ipflag), aliasfl = strlen(aliasflag); PStringToString cfgs=cfg->GetAllKeyValues("PrefixAuth"); for (PINDEX i = 0; i < cfgs.GetSize(); ++i) { PString key = cfgs.GetKeyAt(i); if (key *= "default") { defaultStatus = 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 (prefrules.find(key) != prefrules.end()) continue; //rule already exists? ignore PStringArray rules = cfgs.GetDataAt(i).Tokenise("|", FALSE); PINDEX sz = rules.GetSize(); if (sz < 1) continue; //AuthRule *rls[sz]; AuthRule **rls = new AuthRule *[sz]; for (PINDEX j = 0; j < sz; ++j) { PINDEX pp; // if not allowed, assume denial AuthRule::Result ft = (rules[j].Find(allowflag) != P_MAX_INDEX) ? AuthRule::e_allow : AuthRule::e_deny; if ((pp=rules[j].Find(ipflag)) != P_MAX_INDEX) rls[j] = new IPv4AuthRule(ft, rules[j].Mid(pp+ipfl), is_inverted(rules[j], pp)); else if ((pp=rules[j].Find(aliasflag)) != P_MAX_INDEX) rls[j] = new AliasAuthRule(ft, rules[j].Mid(pp+aliasfl), 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]); prefrules[key] = rls[0]; delete [] rls; } } PrefixAuth::~PrefixAuth() { for_each(prefrules.begin(), prefrules.end(), delete_rule); } int PrefixAuth::Check(const H225_GatekeeperRequest &, unsigned &) { return e_next; } int PrefixAuth::Check(H225_RegistrationRequest & /*rrq*/, unsigned &) { return e_next; } int PrefixAuth::Check(const H225_UnregistrationRequest &, unsigned &) { return e_next; } int PrefixAuth::Check(H225_AdmissionRequest & arq, unsigned &) { return CallTable::Instance()->FindCallRec(arq.m_callIdentifier) ? e_ok : doCheck(ARQAuthObj(arq)); } int PrefixAuth::Check(const H225_BandwidthRequest &, unsigned &) { return e_next; } int PrefixAuth::Check(const H225_DisengageRequest &, unsigned &) { return e_next; } int PrefixAuth::Check(const H225_LocationRequest & lrq, unsigned &) { return doCheck(LRQAuthObj(lrq)); } int PrefixAuth::Check(const H225_InfoRequest &, unsigned &) { return e_next; } 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; PStringArray ary(aobj.GetPrefixes()); for (PINDEX i = 0; i < ary.GetSize(); ++i) { // find the first match rule // since prefrules is descendently sorted // it must be the most specific prefix for (Rules::iterator j = prefrules.begin(); j != prefrules.end(); ++j) { Rules::iterator iter = find_if(j, prefrules.end(), comp_pref(ary[i])); if (iter == prefrules.end()) break; switch (iter->second->Check(aobj)) { case AuthRule::e_allow: return e_ok; case AuthRule::e_deny: return e_fail; default: // try next prefix... j = iter; } } } return defaultStatus; } namespace { std::list<GkAuthInitializer *> *AuthNameList; } GkAuthInitializer::GkAuthInitializer(const char *n) : name(n) { static std::list<GkAuthInitializer *> aList; AuthNameList = &aList; AuthNameList->push_back(this); } GkAuthInitializer::~GkAuthInitializer() { } bool GkAuthInitializer::Compare(PString n) const { return n == name; } GkAuthenticatorList::GkAuthenticatorList(PConfig *cfg) { PStringList authList(cfg->GetKeys(GkAuthSectionName)); for (PINDEX i=authList.GetSize(); i-- > 0; ) { PString authName(authList[i]); std::list<GkAuthInitializer *>::iterator Iter = find_if(AuthNameList->begin(), AuthNameList->end(), bind2nd(mem_fun(&GkAuthInitializer::Compare), authName)); if (Iter != AuthNameList->end()) (*Iter)->CreateAuthenticator(cfg); #if PTRACING else PTRACE(1, "GkAuth\tUnknown auth " << authName << ", ignore!"); #endif } m_mechanisms = new H225_ArrayOf_AuthenticationMechanism; m_algorithmOIDs = new H225_ArrayOf_PASN_ObjectId; GkAuthenticator* authenticator = GetHead(); BOOL found = FALSE; // 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 while( authenticator ) { if( authenticator->IsH235Capable() && ((authenticator->GetControlFlag() == GkAuthenticator::e_Required) || (authenticator->GetControlFlag() == GkAuthenticator::e_Sufficient)) ) { if( m_mechanisms->GetSize() == 0 ) { // append H.235 capability to empty arrays authenticator->GetH235Capability( *m_mechanisms, *m_algorithmOIDs ); // should never happen, but we should check just for a case if( m_algorithmOIDs->GetSize() == 0 ) m_mechanisms->RemoveAll(); else found = TRUE; authenticator = authenticator->GetNext(); 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; int i, j, k; for( i = 0; i < m_algorithmOIDs->GetSize(); i++ ) { BOOL matched = FALSE; for( j = 0; j < m_mechanisms->GetSize(); j++ ) if( authenticator->IsH235Capability( (*m_mechanisms)[j], (*m_algorithmOIDs)[i] ) ) { for( k = 0; k < matchedMechanisms.GetSize(); k++ ) if( matchedMechanisms[k].GetTag() == (*m_mechanisms)[j].GetTag() ) break; if( k == matchedMechanisms.GetSize() ) { matchedMechanisms.SetSize(k+1); matchedMechanisms[k].SetTag((*m_mechanisms)[j].GetTag()); } matched = TRUE; } if( !matched ) { PTRACE(5,"GkAuth\tRemoved from GCF list algorithm OID: "<<(*m_algorithmOIDs)[i]); m_algorithmOIDs->RemoveAt(i); i--; } } for( i = 0; i < m_mechanisms->GetSize(); i++ ) { for( j = 0; j < matchedMechanisms.GetSize(); j++ ) if( (*m_mechanisms)[i].GetTag() == matchedMechanisms[j].GetTag() ) break; if( j == matchedMechanisms.GetSize() ) { PTRACE(5,"GkAuth\tRemoved from GCF list mechanism: "<<(*m_mechanisms)[i]); m_mechanisms->RemoveAt(i); i--; } } if( (m_mechanisms->GetSize() == 0) || (m_algorithmOIDs->GetSize() == 0) ) { PTRACE(4,"GkAuth\tConflicting H.235 capabilities are active" <<" - GCF will not select any particular capability" ); m_mechanisms->RemoveAll(); m_algorithmOIDs->RemoveAll(); break; } } authenticator = authenticator->GetNext(); } // Scan "optional" authenticators if the above procedure has not found // any H.235 capabilities or has found more than one if( (!found) || (m_mechanisms->GetSize() > 1) || (m_algorithmOIDs->GetSize() > 1) ) { authenticator = GetHead(); while( authenticator ) { if( authenticator->IsH235Capable() && (authenticator->GetControlFlag() == GkAuthenticator::e_Optional) ) { if( m_mechanisms->GetSize() == 0 ) { authenticator->GetH235Capability( (*m_mechanisms), (*m_algorithmOIDs) ); if( m_algorithmOIDs->GetSize() == 0 ) m_mechanisms->RemoveAll(); else found = TRUE; authenticator = authenticator->GetNext(); continue; } H225_ArrayOf_AuthenticationMechanism matchedMechanisms; int i, j, k; for( i = 0; i < m_algorithmOIDs->GetSize(); i++ ) { BOOL matched = FALSE; for( j = 0; j < m_mechanisms->GetSize(); j++ ) if( authenticator->IsH235Capability( (*m_mechanisms)[j], (*m_algorithmOIDs)[i] ) ) { for( k = 0; k < matchedMechanisms.GetSize(); k++ ) if( matchedMechanisms[k].GetTag() == (*m_mechanisms)[j].GetTag() ) break; if( k == matchedMechanisms.GetSize() ) { matchedMechanisms.SetSize(k+1); matchedMechanisms[k].SetTag((*m_mechanisms)[j].GetTag()); } matched = TRUE; } if( !matched ) { PTRACE(5,"GkAuth\tRemoved from GCF list algorithm OID: "<<(*m_algorithmOIDs)[i]); m_algorithmOIDs->RemoveAt(i); i--; } } for( i = 0; i < m_mechanisms->GetSize(); i++ ) { for( j = 0; j < matchedMechanisms.GetSize(); j++ ) if( (*m_mechanisms)[i].GetTag() == matchedMechanisms[j].GetTag() ) break; if( j == matchedMechanisms.GetSize() ) { PTRACE(5,"GkAuth\tRemoved from GCF list mechanism: "<<(*m_mechanisms)[i]); m_mechanisms->RemoveAt(i); i--; } } if( (m_mechanisms->GetSize() == 0) || (m_algorithmOIDs->GetSize() == 0) ) { PTRACE(4,"GkAuth\tConflicting H.235 capabilities are active" <<" - GCF will not select any particular capability" ); m_mechanisms->RemoveAll(); m_algorithmOIDs->RemoveAll(); break; } } authenticator = authenticator->GetNext(); } } if( PTrace::CanTrace(5) ) if( (m_mechanisms->GetSize() > 0) && (m_algorithmOIDs->GetSize() > 0) ) { ostream& strm = PTrace::Begin(5,__FILE__,__LINE__); strm <<"GkAuth\tH.235 capabilities selected for GCF:\n"; strm <<"\tAuthentication mechanisms: \n"; int i; for( i = 0; i < m_mechanisms->GetSize(); i++ ) strm << "\t\t" << (*m_mechanisms)[i] << '\n'; strm <<"\tAuthentication algorithm OIDs: \n"; for( i = 0; i < m_algorithmOIDs->GetSize(); i++ ) strm << "\t\t" << (*m_algorithmOIDs)[i] << '\n'; PTrace::End(strm); } } GkAuthenticatorList::~GkAuthenticatorList() { delete GkAuthenticator::head; GkAuthenticator::head = 0; delete m_mechanisms; delete m_algorithmOIDs; } void GkAuthenticatorList::GetH235Capabilities( H225_ArrayOf_AuthenticationMechanism& mechanisms, H225_ArrayOf_PASN_ObjectId& algorithmOIDs ) const { mechanisms = *m_mechanisms; algorithmOIDs = *m_algorithmOIDs; } namespace { GkAuthInit<GkAuthenticator> DefaultAuthFactory("default"); GkAuthInit<AliasAuth> AliasAuthFactory("AliasAuth"); GkAuthInit<PrefixAuth> PrefixAuthFactory("PrefixAuth"); GkAuthInit<SimplePasswordAuth> SimplePasswordAuthFactory("SimplePasswordAuth"); GkAuthInit<SQLPasswordAuth> SQLPasswordAuthFactory("SQLPasswordAuth"); GkAuthInit<SQLAliasAuth> SQLAliasAuthFactory("SQLAliasAuth"); #if HAS_MYSQL GkAuthInit<MySQLPasswordAuth> MySQLPasswordAuthFactory("MySQLPasswordAuth"); GkAuthInit<MySQLAliasAuth> MySQLAliasAuthFactory("MySQLAliasAuth"); #endif #if defined(HAS_LDAP) || defined(HAS_WLDAP) GkAuthInit<LDAPPasswordAuth> LDAPPasswordAuthFactory("LDAPPasswordAuth"); GkAuthInit<LDAPAliasAuth> LDAPAliasAuthFactory("LDAPAliasAuth"); #endif }
////////////////////////////////////////////////////////////////// // // gkauth.h // // Gatekeeper authentication modules // // 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) // ////////////////////////////////////////////////////////////////// #ifndef GKAUTH_H #define GKAUTH_H "#(@) $Id: gkauth.h,v 1.6.2.16 2004/06/22 18:41:15 zvision Exp $" #include <map> #include <list> #include "RasTbl.h" class H225_GatekeeperRequest; class H225_RegistrationRequest; class H225_UnregistrationRequest; class H225_AdmissionRequest; class H225_BandwidthRequest; class H225_DisengageRequest; class H225_LocationRequest; class H225_InfoRequest; class H225_ArrayOf_ClearToken; class H225_ArrayOf_CryptoH323Token; class H225_ArrayOf_AliasAddress; class H225_ArrayOf_AuthenticationMechanism; class H225_ArrayOf_PASN_ObjectId; class H235_AuthenticationMechanism; class PASN_ObjectId; class H235Authenticators; class Q931; class H225_Setup_UUIE; class CacheManager; struct MemBlock { long callDuration; PString BalanceString; }; class GkAuthenticator { public: enum Control { e_Optional, e_Required, e_Sufficient }; enum Status { e_ok = 1, // the request is authenticated e_fail = -1, // the request should be rejected e_next = 0 // the request is undetermined }; enum { e_GRQ = 0x0001, e_RRQ = 0x0002, e_URQ = 0x0004, e_ARQ = 0x0008, e_BRQ = 0x0010, e_DRQ = 0x0020, e_LRQ = 0x0040, e_IRQ = 0x0080, e_ALL = 0x00FF, e_Setup = 0x1000, e_SetupUnreg = 0x2000 }; GkAuthenticator(PConfig *, const char *authName = "default"); virtual ~GkAuthenticator(); template<class RasType> bool CheckRas(PBYTEArray &rawPDU, const RasType & req, unsigned & reason) { setLastReceivedRawPDU(rawPDU); if (checkFlag & RasValue(req)) { int r = Check(req, reason); if (r == e_ok) { PTRACE(4, "GkAuth\t" << name << " check ok"); if (controlFlag != e_Required) return true; } else if (r == e_fail) { PTRACE(2, "GkAuth\t" << name << " check failed"); return false; } } // try next rule return (next) ? next->CheckRas(rawPDU, req, reason) : true; } bool CheckRas( /// received raw H.225 ARQ message PBYTEArray &rawPDU, /// decoded H.225 ARQ message H225_RegistrationRequest& req, /// RRJ code if the authentication fails unsigned& rejectReason, /// the address which the RRQ has been received from const PIPSocket::Address& rxaddr,MemBlock * ); bool CheckRas( /// received raw H.225 ARQ message PBYTEArray &rawPDU, /// decoded H.225 ARQ message H225_RegistrationRequest& req, /// RRJ code if the authentication fails unsigned& rejectReason, /// the address which the RRQ has been received from const PIPSocket::Address& rxaddr ); bool CheckRas( /// received raw H.225 ARQ message PBYTEArray &rawPDU, /// decoded H.225 ARQ message H225_AdmissionRequest& req, /// ARJ code if authentication fails unsigned& rejectReason, /// call duration limit to be set (-1 for no limit) long& callDurationLimit ); /** Authenticate/Authorize Setup signalling message. @return true if the message has been authenticated, false otherwise (if authentication failed or call duration limit is 0) */ bool CheckSig( /// received Q.931 Setup message Q931& q931pdu, /// received H.225 Setup message H225_Setup_UUIE& setup, /// CallRec for the call being authenticated callptr& call, /// Q.931 cause to set, if authentication fails unsigned& releaseCompleteCause, /// call duration limit to be set (-1 for no limit) long& callDurationLimit, /// is the call from a registered endpoint bool registered ); const char *GetName() { return name; } /** @return TRUE if this authenticator provides H.235 compatible security. It simply checks if h235Authenticators list is not empty. */ virtual BOOL IsH235Capable() const; /** If the authenticator supports H.235 security, this call returns H.235 security capabilities associated with it. It scans list pointed by h235Authenticators. @return TRUE is H.235 security is supported and capabilities has been set. */ virtual BOOL 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; /** Check if this authenticator supports the given H.235 capability (mechanism+algorithmOID) by scanning list pointed by h235Authenticators. @return TRUE if the capability is supported. */ virtual BOOL IsH235Capability( /// authentication mechanism const H235_AuthenticationMechanism& mechanism, /// algorithm OID for the given authentication mechanism const PASN_ObjectId& algorithmOID ) const; /** @return Control flag determining authenticator behaviour (optional,sufficient,required). */ Control GetControlFlag() const { return controlFlag; } /** @return Next authenticator on the list. */ GkAuthenticator* GetNext() const { return next; } protected: // the second argument is the reject reason, if any virtual int Check(const H225_GatekeeperRequest &, unsigned &); virtual int Check(H225_RegistrationRequest &, unsigned &); virtual int Check(H225_RegistrationRequest &, unsigned &,MemBlock *); virtual int Check(const H225_UnregistrationRequest &, unsigned &); virtual int Check(H225_AdmissionRequest &, unsigned &); virtual int Check( /// received H.225 ARJ message H225_AdmissionRequest& request, /// ARJ reason to set, if authentication failed unsigned& rejectReason, /// call duration limit to set (-1 for no duration limit) long& callDurationLimit ); virtual int Check(const H225_BandwidthRequest &, unsigned &); virtual int Check(const H225_DisengageRequest &, unsigned &); virtual int Check(const H225_LocationRequest &, unsigned &); virtual int Check(const H225_InfoRequest &, unsigned &); /** Authenticate/Authorize Setup signalling message. @return e_fail - authentication failed e_ok - authenticated with this authenticator e_next - authentication could not be determined */ virtual int Check( /// received Q.931 Setup message Q931& q931pdu, /// received H.225 Setup message H225_Setup_UUIE& setup, /// CallRec for the call being authenticated callptr& call, /// Q.931 cause to set, if authentication failed unsigned& releaseCompleteCause, /// call duration limit to set (-1 for no duration limit) long& callDurationLimit ); int RasValue(const H225_GatekeeperRequest &) { return e_GRQ; } int RasValue(const H225_RegistrationRequest &) { return e_RRQ; } int RasValue(const H225_UnregistrationRequest &) { return e_URQ; } int RasValue(const H225_AdmissionRequest &) { return e_ARQ; } int RasValue(const H225_BandwidthRequest &) { return e_BRQ; } int RasValue(const H225_DisengageRequest &) { return e_DRQ; } int RasValue(const H225_LocationRequest &) { return e_LRQ; } int RasValue(const H225_InfoRequest &) { return e_IRQ; } PBYTEArray& getLastReceivedRawPDU(){ return m_lastReceivedRawPDU; } /** @return A string that can be used to identify an account name associated with the call. */ virtual PString GetUsername( /// RRQ message with additional data const H225_RegistrationRequest& rrq ) const; virtual PString GetUsername( /// call (if any) associated with the RAS message const callptr& call, /// ARQ message with additional data const H225_AdmissionRequest& arq, /// endpoint sending the ARQ request const endptr& ep ) const; virtual PString GetUsername( /// call (if any) associated with the Setup message const callptr& call, /// Q.931 Setup message with additional data const Q931& q931pdu, /// Setup-UUIE element extracted from the Q.931 Setup message const H225_Setup_UUIE& setup ) const; /** @return A string that can be used to identify a calling number. */ virtual PString GetCallingStationId( /// call (if any) associated with the RAS message const callptr& call, /// ARQ message with additional data const H225_AdmissionRequest& arq, /// endpoint sending the ARQ request const endptr& ep ) const; virtual PString GetCallingStationId( /// call (if any) associated with the Setup message const callptr& call, /// Q.931 Setup message with additional data const Q931& q931pdu, /// Setup-UUIE element extracted from the Q.931 Setup message const H225_Setup_UUIE& setup ) const; /** @return A string that can be used to identify a calling number. */ virtual PString GetCalledStationId( /// call (if any) associated with the RAS message const callptr& call, /// ARQ message with additional data const H225_AdmissionRequest& arq, /// endpoint sending the ARQ message const endptr& ep ) const; virtual PString GetCalledStationId( /// call (if any) associated with the Setup message const callptr& call, /// Q.931 Setup message with additional data const Q931& q931pdu, /// Setup-UUIE element extracted from the Q.931 Setup message const H225_Setup_UUIE& setup ) const; protected: Control controlFlag; Status defaultStatus; PConfig *config; H235Authenticators* h235Authenticators; private: const char *name; int checkFlag; GkAuthenticator *next; static GkAuthenticator *head; GkAuthenticator(const GkAuthenticator &); GkAuthenticator & operator=(const GkAuthenticator &); void setLastReceivedRawPDU(PBYTEArray &rawPDU){ m_lastReceivedRawPDU = rawPDU; } PBYTEArray m_lastReceivedRawPDU; friend class GkAuthenticatorList; }; class SimplePasswordAuth : public GkAuthenticator { public: SimplePasswordAuth(PConfig *, const char *); ~SimplePasswordAuth(); protected: virtual int Check(const H225_GatekeeperRequest &, unsigned &); virtual int Check(H225_RegistrationRequest &, unsigned &); virtual int Check(const H225_UnregistrationRequest &, unsigned &); virtual int Check(H225_AdmissionRequest &, unsigned &); virtual int Check(const H225_BandwidthRequest &, unsigned &); virtual int Check(const H225_DisengageRequest &, unsigned &); virtual int Check(const H225_LocationRequest &, unsigned &); virtual int Check(const H225_InfoRequest &, unsigned &); virtual PString GetPassword(const PString & id); virtual bool CheckAliases(const PString &); virtual bool CheckTokens(const H225_ArrayOf_ClearToken &); virtual bool CheckCryptoTokens(const H225_ArrayOf_CryptoH323Token &); template<class RasType> int doCheck(const RasType & req) { if (req.HasOptionalField(RasType::e_cryptoTokens)) return CheckCryptoTokens(req.m_cryptoTokens) ? e_ok : e_fail; else if (req.HasOptionalField(RasType::e_tokens)) return CheckTokens(req.m_tokens) ? e_ok : e_fail; return (controlFlag == e_Optional) ? e_next : e_fail; } #if defined HAS_WLDAP bool InternalGetPassword(PString & passwd); #else bool InternalGetPassword(const PString & id, PString & passwd); #endif // HAS_WLDAP protected: int filled; bool checkid; const H225_ArrayOf_AliasAddress *aliases; CacheManager* m_cache; }; class GkAuthInitializer { public: GkAuthInitializer(const char *); virtual ~GkAuthInitializer(); // virtual constructor virtual GkAuthenticator *CreateAuthenticator(PConfig *) = 0; bool Compare(PString n) const; protected: const char *name; }; template<class GkAuth> class GkAuthInit : public GkAuthInitializer { public: GkAuthInit(const char *n) : GkAuthInitializer(n) {} virtual GkAuthenticator *CreateAuthenticator(PConfig *config) { return new GkAuth(config, name); } }; class GkAuthenticatorList { public: GkAuthenticatorList(PConfig *); virtual ~GkAuthenticatorList(); template<class RasType> bool Check(const RasType & req, unsigned & reason) { return (GkAuthenticator::head) ? GkAuthenticator::head->CheckRas(getLastReceivedRawPDU(), req, reason) : true; } bool Check( /// received H.225 RRQ message H225_RegistrationRequest& request, /// RRJ reason to set, if the authentication fails unsigned& rejectReason, /// the address which the RRQ has been received from const PIPSocket::Address& rxaddr ) { return (GkAuthenticator::head) ? GkAuthenticator::head->CheckRas( getLastReceivedRawPDU(), request, rejectReason, rxaddr ) : true; } bool Check( /// received H.225 RRQ message H225_RegistrationRequest& request, /// RRJ reason to set, if the authentication fails unsigned& rejectReason, /// the address which the RRQ has been received from const PIPSocket::Address& rxaddr,MemBlock *m ) { return (GkAuthenticator::head) ? GkAuthenticator::head->CheckRas( getLastReceivedRawPDU(), request, rejectReason, rxaddr,m ) : true; } bool Check( /// received H.225 ARQ message H225_AdmissionRequest& request, /// ARJ reason to set, if authentication fails unsigned& rejectReason, /// call duration limit to set (-1 for no duration limit) long& callDurationLimit ) { callDurationLimit = -1; return (GkAuthenticator::head) ? GkAuthenticator::head->CheckRas( getLastReceivedRawPDU(), request, rejectReason, callDurationLimit ) : true; } /** Authenticate/Authorize Setup signalling message. Iterate through all authenticators. @return true if the message has been authenticated, false if not or if call duration limit is 0 */ bool CheckSig( /// received Q.931 Setup message Q931& q931pdu, /// received H.225 Setup message H225_Setup_UUIE& setup, /// CallRec for the call being authenticated callptr& call, /// Q.931 cause value to set, if authentication fails unsigned& releaseCompleteCause, /// call duration limit to set (-1 for no duration limit) long& callDurationLimit, /// is the call from a registered endpoint bool registered = true ) { callDurationLimit = -1; return (GkAuthenticator::head != NULL) ? GkAuthenticator::head->CheckSig( q931pdu, setup, call, releaseCompleteCause, callDurationLimit, registered ) : true; } virtual void setLastReceivedRawPDU(PBYTEArray &rawPDU){ m_lastReceivedRawPDU = rawPDU; } /** @return Head of the authenticators list. Use GkAuthenticator::GetNext() to traverse the list. */ GkAuthenticator* GetHead() const { return GkAuthenticator::head; } void GetH235Capabilities( H225_ArrayOf_AuthenticationMechanism& mechanisms, H225_ArrayOf_PASN_ObjectId& algorithmOIDs ) const; private: GkAuthenticatorList(const GkAuthenticatorList &); GkAuthenticatorList & operator=(const GkAuthenticatorList &); virtual PBYTEArray& getLastReceivedRawPDU(){ return m_lastReceivedRawPDU; } PBYTEArray m_lastReceivedRawPDU; /// the most common authentication capabilities /// shared by all authenticators on the list H225_ArrayOf_AuthenticationMechanism* m_mechanisms; H225_ArrayOf_PASN_ObjectId* m_algorithmOIDs; }; #endif // GKAUTH_H