/* * ipauth.cxx * * IP based authentication modules * * @(#) $Id: ipauth.cxx,v 1.3 2006/06/12 12:32:44 zvision Exp $ * * Copyright (c) 2005, 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. */ #include #include #include "gk_const.h" #include "h323util.h" #include "stl_supp.h" #include "Toolkit.h" #include "RasPDU.h" #include "RasTbl.h" #include "sigmsg.h" #include "ipauth.h" class IPAuthPrefix { public: IPAuthPrefix(); IPAuthPrefix(bool a, const PString &); IPAuthPrefix(const IPAuthPrefix &); void AddPrefix(const PString &); void SortPrefix(bool greater = true); int PrefixMatch(const PString &) const; std::string PrintOn(void) const; std::string PrintPrefix(void) const; IPAuthPrefix& operator=(const IPAuthPrefix&); IPAuthPrefix& operator=(bool); typedef std::vector::iterator prefix_iterator; typedef std::vector::const_iterator const_prefix_iterator; bool auth; protected: std::vector Prefixs; }; /// Text file based IP authentication class FileIPAuth : public IPAuthBase { public: typedef std::pair IPAuthEntry; /// Create text file based authenticator FileIPAuth( /// authenticator name from Gatekeeper::Auth section const char *authName ); /// Destroy the authenticator virtual ~FileIPAuth(); protected: /// Overriden from IPAuthBase virtual int CheckAddress( const PIPSocket::Address &addr, /// IP address the request comes from WORD port, /// port number the request comes from const PString &number ); private: FileIPAuth(); /* No copy constructor allowed */ FileIPAuth(const FileIPAuth&); /* No operator= allowed */ FileIPAuth& operator=(const FileIPAuth&); private: typedef std::vector IPAuthList; IPAuthList m_authList; }; IPAuthBase::IPAuthBase( /// authenticator name from Gatekeeper::Auth section const char *authName, /// bitmask with supported RAS checks unsigned supportedRasChecks, /// bitmask with supported non-RAS checks unsigned supportedMiscChecks ) : GkAuthenticator(authName, supportedRasChecks, supportedMiscChecks) { } IPAuthBase::~IPAuthBase() { } int IPAuthBase::Check( /// GRQ RAS message to be authenticated RasPDU &grqPdu, /// gatekeeper request reject reason unsigned &rejectReason ) { return CheckAddress(grqPdu->m_peerAddr, grqPdu->m_peerPort, PString()); } int IPAuthBase::Check( /// RRQ RAS message to be authenticated RasPDU &rrqPdu, /// authorization data (reject reason, ...) RRQAuthData &authData ) { return CheckAddress(rrqPdu->m_peerAddr, rrqPdu->m_peerPort, PString()); } int IPAuthBase::Check( /// LRQ nessage to be authenticated RasPDU &lrqPdu, /// location request reject reason unsigned &rejectReason ) { return CheckAddress(lrqPdu->m_peerAddr, lrqPdu->m_peerPort, PString()); } int IPAuthBase::Check( /// Q.931/H.225 Setup message to be authenticated SetupMsg &setup, /// authorization data (call duration limit, reject reason, ...) SetupAuthData& authData ) { PIPSocket::Address addr; WORD port = 0; setup.GetPeerAddr(addr, port); PString number; setup.GetQ931().GetCalledPartyNumber(number); return CheckAddress(addr, port, number); } ///////////// // FileIPAuth ///////////// namespace { const char *FileIPAuthSecName = "FileIPAuth"; struct IPAuthEntry_greater : public binary_function { bool operator()( const FileIPAuth::IPAuthEntry &a, const FileIPAuth::IPAuthEntry &b ) const { const int diff = a.first.Compare(b.first); if (diff == 0) return !a.second.auth; return diff > 0; } }; } /* anonymous namespace */ FileIPAuth::FileIPAuth( /// authenticator name from Gatekeeper::Auth section const char *authName ) : IPAuthBase(authName) { bool dynamicCfg = false; PConfig *cfg = GkConfig(); if (cfg->HasKey(FileIPAuthSecName, "include")) { const PFilePath fp(cfg->GetString(FileIPAuthSecName, "include", "")); if (!PFile::Exists(fp)) { PTRACE(0, GetName() << "\tCould not read the include file '" << fp << "': the file does not exist" ); return; } cfg = new PConfig(fp, authName); dynamicCfg = true; } PStringToString kv = cfg->GetAllKeyValues(FileIPAuthSecName); m_authList.reserve(kv.GetSize()); for (PINDEX i = 0; i < kv.GetSize(); i++) { const PString &key = kv.GetKeyAt(i); int position = 0; if (key[0] == '#') continue; IPAuthEntry entry; entry.first = (key == "*" || key == "any") ? NetworkAddress() : NetworkAddress(key); PString auth(kv.GetDataAt(i)); PString prefix("."); if ((position = auth.Find(';', position)) != P_MAX_INDEX) { position++; prefix = auth(position, auth.GetLength()); position--; auth.Delete(position, auth.GetLength() - position); } entry.second.auth = PCaselessString("allow") == auth; entry.second.AddPrefix(prefix); entry.second.SortPrefix(false); m_authList.push_back(entry); } std::stable_sort(m_authList.begin(), m_authList.end(), IPAuthEntry_greater()); PTRACE(5, GetName() << "\t" << m_authList.size() << " entries loaded"); #if PTRACING if (PTrace::CanTrace(6)) { ostream &strm = PTrace::Begin(6, __FILE__, __LINE__); strm << GetName() << " entries:\n"; IPAuthList::const_iterator entry = m_authList.begin(); while (entry != m_authList.end()) { strm << "\t" << entry->first.AsString() << " = " << (entry->second.auth ? "allow" : "reject") << (entry->second.auth ? entry->second.PrintOn() : "") << endl; entry++; } PTrace::End(strm); } #endif if (dynamicCfg) delete cfg; } FileIPAuth::~FileIPAuth() { } int FileIPAuth::CheckAddress( const PIPSocket::Address &addr, /// IP address the request comes from WORD port, /// port number the request comes from const PString &number ) { IPAuthList::const_iterator entry = m_authList.begin(); while (entry != m_authList.end()) { if (entry->first.IsAny() || addr << entry->first) { if (entry->second.auth && !number.IsEmpty()) { int len = entry->second.PrefixMatch(number); PTRACE(5, GetName() << "\tIP " << addr.AsString() << (len ? " accepted" : " rejected") << " for Called " << number ); return len ? e_ok : e_fail; } return entry->second.auth ? e_ok : e_fail; } entry++; } return GetDefaultStatus(); } IPAuthPrefix::IPAuthPrefix() : auth(false) {} IPAuthPrefix::IPAuthPrefix(bool a, const PString & prefixes) { auth = a; AddPrefix(prefixes); } IPAuthPrefix::IPAuthPrefix(const IPAuthPrefix & obj) { auth = obj.auth; const_prefix_iterator Iter = obj.Prefixs.begin(); const_prefix_iterator eIter = obj.Prefixs.end(); while (Iter != eIter) { Prefixs.push_back(Iter->c_str()); ++Iter; } } IPAuthPrefix& IPAuthPrefix::operator=(const IPAuthPrefix & obj) { auth = obj.auth; Prefixs.clear(); const_prefix_iterator Iter = obj.Prefixs.begin(); const_prefix_iterator eIter = obj.Prefixs.end(); while (Iter != eIter) { Prefixs.push_back(Iter->c_str()); ++Iter; } return *this; } IPAuthPrefix& IPAuthPrefix::operator=(bool a) { auth = a; return *this; } void IPAuthPrefix::AddPrefix(const PString & prefixes) { PStringArray p(prefixes.Tokenise(" ,;\t\n", false)); for (PINDEX i = 0; i < p.GetSize(); ++i) Prefixs.push_back((const char *)p[i]); } void IPAuthPrefix::SortPrefix(bool greater) { // remove duplicate aliases if (greater) sort(Prefixs.begin(), Prefixs.end(), str_prefix_greater()); else sort(Prefixs.begin(), Prefixs.end(), str_prefix_lesser()); prefix_iterator Iter = std::unique(Prefixs.begin(), Prefixs.end()); Prefixs.erase(Iter, Prefixs.end()); } int IPAuthPrefix::PrefixMatch(const PString & number) const { if (number.IsEmpty()) return 0; const char * alias = (const char*)(number); if (!alias) return 0; const_prefix_iterator Iter = Prefixs.begin(); const_prefix_iterator eIter = Prefixs.end(); if (Iter == eIter) return 1; while (Iter != eIter) { const int len = MatchPrefix(alias, Iter->c_str()); if (len > 0) { return len; } ++Iter; } return 0; } std::string IPAuthPrefix::PrintOn(void) const { if (!auth) return std::string(" to called any"); std::string prefix = PrintPrefix(); if (prefix == ".") prefix = "any"; std::string ret(" to called "); ret += prefix; return ret; } std::string IPAuthPrefix::PrintPrefix(void) const { std::string prefix; const_prefix_iterator Iter = Prefixs.begin(); const_prefix_iterator eIter = Prefixs.end(); while (Iter != eIter) { prefix += *Iter; prefix += ","; ++Iter; } return prefix; } namespace { // anonymous namespace GkAuthCreator FileIPAuthCreator("FileIPAuth"); } // end of anonymous namespace