/* Firewall Builder Copyright (C) 2002-2011 NetCitadel, LLC Author: Vadim Kurland vadim@fwbuilder.org This program is free software which we release under the GNU General Public License. You may redistribute and/or modify this program under the terms of that license as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. To get a copy of the GNU General Public License, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include "NATCompiler_pix.h" #include "NamedObjectsAndGroupsSupport.h" #include "NamedObjectsManager.h" #include "fwbuilder/FWObjectDatabase.h" #include "fwbuilder/RuleElement.h" #include "fwbuilder/NAT.h" #include "fwbuilder/AddressRange.h" #include "fwbuilder/ICMPService.h" #include "fwbuilder/TCPService.h" #include "fwbuilder/UDPService.h" #include "fwbuilder/Interface.h" #include "fwbuilder/IPv4.h" #include "fwbuilder/IPv6.h" #include "fwbuilder/InetAddr.h" #include "fwbuilder/Network.h" #include "fwbuilder/Resources.h" #include "fwbuilder/AddressTable.h" #include "fwbuilder/Cluster.h" #include "fwbuilder/FailoverClusterGroup.h" #include #include using namespace libfwbuilder; using namespace fwcompiler; using namespace std; /* * this processor uses slurp to make sure all previous processors ran before * it starts scanning rules. */ bool NATCompiler_pix::mergeNATCmd::processNext() { NATCompiler_pix *pix_comp = dynamic_cast(compiler); slurp(); if (tmp_queue.size()==0) return false; for (deque::iterator k=tmp_queue.begin(); k!=tmp_queue.end(); ++k) { NATRule *rule = NATRule::cast( *k ); if (rule->getRuleType() == NATRule::DNAT) { StaticCmd *scmd = pix_comp->static_commands[rule->getInt("sc_cmd")]; map::iterator i1; for (i1=pix_comp->static_commands.begin(); i1!=pix_comp->static_commands.end(); ++i1) { StaticCmd *sc = (*i1).second; if (scmd==sc) break; if ( *scmd == *sc) { /* * rule 'sc' is above rule 'scmd', we need to print 'static' command * only in the last rule using the same access list. That's why we set * flag ignore_scmd_and_print acl in sc and not in scmd */ scmd->acl_name = sc->acl_name; sc->ignore_scmd_and_print_acl = true; } } } if (rule->getRuleType()==NATRule::SNAT) { NATCmd *natcmd = pix_comp->nat_commands[ rule->getInt("nat_cmd") ]; map::iterator i1; for (i1 = pix_comp->nat_commands.begin(); i1 != pix_comp->nat_commands.end(); ++i1) { NATCmd *nc = (*i1).second; /* since map nat_commands is sorted by the key, we only have to scan it * until we hit natcmd */ if (natcmd==nc) break; const InetAddr *a1 = natcmd->t_addr->getAddressPtr(); const InetAddr *a2 = nc->t_addr->getAddressPtr(); Interface *int1 = natcmd->o_iface; Interface *int2 = nc->o_iface; if ((natcmd->t_addr == nc->t_addr || (a1 && a2 && *a1 == *a2)) && int1->getId() == int2->getId() ) { natcmd->ignore_global = true; natcmd->nat_id = nc->nat_id; } } for (map::iterator i1=pix_comp->nat_commands.begin(); i1!=pix_comp->nat_commands.end(); ++i1) { NATCmd *nc = (*i1).second; /* since map nat_commands is sorted by the key, we only have to scan it * until we hit natcmd */ if (natcmd == nc) break; if (nc->ignore_nat) continue; /* using operator==(const Address &o1,const Address &o2) here */ if ( *(natcmd->o_src) == *(nc->o_src) && *(natcmd->o_dst) == *(nc->o_dst) && *(natcmd->o_srv) == *(nc->o_srv) && natcmd->i_iface->getId() == nc->i_iface->getId() ) { /* * there is another nat rule (rule #2) with the same "original" * addresses and the same interface. We can drop this nat rule, but need * to merge its global pool with pool of the rule #2. * * This nat rule could have been sharing a global pool with some other * nat rule; in this case we need to find this other rule and also * reassign it to the global pool of the rule #2. */ natcmd->ignore_nat = true; map::iterator i2; for (i2 = pix_comp->nat_commands.begin(); i2 != pix_comp->nat_commands.end(); ++i2) { NATCmd *nc2 = i2->second; if (natcmd->nat_id == nc2->nat_id) nc2->nat_id = nc->nat_id; } natcmd->nat_id = nc->nat_id; } } if (!natcmd->use_nat_0_0) { map::iterator i1; for (i1 = pix_comp->nat_commands.begin(); i1 != pix_comp->nat_commands.end(); ++i1) { NATCmd *nc=(*i1).second; /* since map nat_commands is sorted by the key, we only have to scan it * until we hit natcmd */ if (natcmd==nc) break; /* ignore nat natcmd entries for rules where we won't print 'nat' * command or use 'nat 0' command since this means we won't print * access-list for those rules and hense can not merge lists */ if (nc->ignore_nat) continue; if (nc->use_nat_0_0) continue; if ( natcmd->nat_id == nc->nat_id && natcmd->t_addr == nc->t_addr && natcmd->i_iface->getId() == nc->i_iface->getId() ) { /* two nat commands with the same id, the same interface and the same * translated address, but different osrc and odst. OSrc and ODst must * be different, otherwise these two commands would have been merged * in the previous cycle. We can merge access lists and drop one of * these nat commands. We merge ACLs by assigning them the same name. */ natcmd->nat_acl_name = nc->nat_acl_name; nc->ignore_nat_and_print_acl = true; } } } } } return true; } /* * The goal of this processor is to find SNAT rules that could be * translated as "nat (interface) 0.0.0.0 0.0.0.0. These rules should * have the same network object in OSrc that is used to define * interface's network zone. The logic is simple: if network "A" is a * network zone for internal interface, then only packets from this * network can hit it and therefore there is no need to check source * address once more in the "nat" rule. * * We also check for ODst and OSrv, because if the destination or the * service are defined, then this optimization can not be done. * * This optimization can be turned off using checkbutton in the * "Firewall" tab. * * call this processor really early, when groups have not been * expanded yet. At this point both NAT rule type and interfaces it * is associated with are unknown yet. We have to partially repeat * algorithms used in other rule processors to determine NAT rule type * and interface. * * We do this optimization in two steps: * * 1. in this rule processor we replace object in OSrc with firewall's * interface. This way we can still use other rule processors that * determine rule type and assign it to interfaces, but rule won't be * split onto multiple rules because of objects in OSrc. We also set * boolean flags "clear_osrc" and "use_nat_0_0" on the rule. * * 2. further down in rule processor clearOSrc we check the flag and * clear OSrc if it is set. * * 3. flag "use_nat_0_0" is used in printRule processor. */ bool NATCompiler_pix::optimizeDefaultNAT::processNext() { // NATCompiler_pix *pix_comp=dynamic_cast(compiler); NATRule *rule=getNext(); if (rule==NULL) return false; tmp_queue.push_back(rule); RuleElementOSrc *osrc=rule->getOSrc(); RuleElementOSrv *osrv=rule->getOSrv(); RuleElementODst *odst=rule->getODst(); RuleElementTSrc *tsrc=rule->getTSrc(); RuleElementTDst *tdst=rule->getTDst(); if (osrc->size()>1) return true; if (osrc->isAny()) return true; if (!osrv->isAny()) return true; if (!odst->isAny()) return true; /* * can't use RuleElementOSrc::getFirst(bool dereference) because it * returns Address::cast(o), but child element of rule element may be * a group when this processor is called. */ FWObject *o=osrc->front(); string osrc_id; if (FWReference::cast(o)!=NULL) osrc_id = FWObjectDatabase::getStringId(FWReference::cast(o)->getPointerId()); else osrc_id = FWObjectDatabase::getStringId(o->getId()); if ( ( !tsrc->isAny() && tdst->isAny()) || ( !osrc->isAny() && odst->isAny() && tsrc->isAny() && tdst->isAny() ) ) { // this rule type is SNAT or NONAT list l2=compiler->fw->getByType(Interface::TYPENAME); for (list::iterator i=l2.begin(); i!=l2.end(); ++i) { Interface *iface=Interface::cast(*i); if (iface->getStr("orig_netzone_id")==osrc_id ) { rule->setBool("clear_osrc",true); rule->setBool("use_nat_0_0",true); osrc->clearChildren(); osrc->addRef(iface); break; } } } return true; } bool NATCompiler_pix::SuppressDuplicateNONATStatics::processNext() { Helper helper(compiler); // NATCompiler_pix *pix_comp=dynamic_cast(compiler); NATRule *rule=getNext(); if (rule==NULL) return false; if (rule->getRuleType()== NATRule::NONAT && rule->getInt("nonat_type")==NONAT_STATIC) { Address *osrc=compiler->getFirstOSrc(rule); assert(osrc); Address *odst=compiler->getFirstODst(rule); assert(odst); nonat_static_parameters sp; sp.iface1 = helper.findInterfaceByNetzone(osrc ); sp.iface2 = helper.findInterfaceByNetzone(odst ); sp.addr = *(odst->getAddressPtr()); sp.mask = *(odst->getNetmaskPtr()); for (deque::iterator i=all_nonat_statics.begin(); i!=all_nonat_statics.end(); ++i ) { if ( i->iface1==sp.iface1 && i->iface2==sp.iface2 && i->addr==sp.addr && i->mask==sp.mask ) return true; } all_nonat_statics.push_back(sp); } tmp_queue.push_back(rule); return true; } NATCompiler_pix::DetectOverlap::~DetectOverlap() {}; bool NATCompiler_pix::DetectOverlap::checkOverlapping( const libfwbuilder::Address &addr1, const libfwbuilder::InetAddr &addr2) { if (AddressRange::isA(&addr1)) { const InetAddr a1 = AddressRange::constcast(&addr1)->getRangeStart(); const InetAddr a2 = AddressRange::constcast(&addr1)->getRangeEnd(); return (addr2==a1 || addr2==a2 || (addr2>a1 && addr2getRangeStart(); const InetAddr a2=AddressRange::constcast(&pool)->getRangeEnd(); return a1.toString()+"-"+a2.toString(); } else { return pool.getAddressPtr()->toString() + "/" + pool.getNetmaskPtr()->toString(); } } bool NATCompiler_pix::DetectGlobalPoolProblems::processNext() { NATCompiler_pix *pix_comp=dynamic_cast(compiler); NATRule *rule=getNext(); if (rule==NULL) return false; tmp_queue.push_back(rule); if (rule->getRuleType()== NATRule::SNAT ) { NATCmd *natcmd = pix_comp->nat_commands[ rule->getInt("nat_cmd") ]; if (natcmd->ignore_global) return true; if (natcmd->type != INTERFACE) { if (checkOverlapping(*(natcmd->t_addr), *(natcmd->o_iface->getAddressPtr()))) compiler->abort( rule, "Global pool " + printGlobalPoolAddress(*(natcmd->t_addr)) + " overlaps with interface address."); if (checkOverlapping(*(natcmd->t_addr), *(natcmd->o_iface->getBroadcastAddressPtr())) || checkOverlapping(*(natcmd->t_addr), *(natcmd->o_iface->getAddressPtr())) ) compiler->warning( rule, "Global pool " + printGlobalPoolAddress(*(natcmd->t_addr)) + " overlaps with broadcast address."); } for (map::iterator i1=pix_comp->nat_commands.begin(); i1!=pix_comp->nat_commands.end(); ++i1) { NATCmd *nc = (*i1).second; /* since map nat_commands is sorted by the key, we only have to scan it * until we hit natcmd */ if (nc->ignore_global) continue; if (natcmd==nc) break; Interface *int1 = natcmd->o_iface; Interface *int2 = nc->o_iface; if ( int1->getId()==int2->getId() ) { if ( ! fwcompiler::_find_obj_intersection(natcmd->t_addr,nc->t_addr).empty() ) { compiler->abort( rule, string("Global pool overlap: ") + rule->getLabel() + " : " + printGlobalPoolAddress(*(natcmd->t_addr)) + nc->rule_label + " : " + printGlobalPoolAddress(*(nc->t_addr)) ); } } } } return true; } bool NATCompiler_pix::DetectOverlappingGlobalPoolsAndStaticRules::processNext() { NATCompiler_pix *pix_comp=dynamic_cast(compiler); NATRule *rule=getNext(); if (rule==NULL) return false; tmp_queue.push_back(rule); if (rule->getRuleType()== NATRule::DNAT ) { Address *outa=compiler->getFirstODst(rule); assert(outa); Address *insa=compiler->getFirstTDst(rule); assert(insa); for (map::iterator i=pix_comp->nat_commands.begin(); i!=pix_comp->nat_commands.end(); ++i) { NATCmd *natcmd=(*i).second; if (natcmd->ignore_global) return true; /* in this case natcmd->t_addr is interface. Interface creates * single-address global pool, but since it has netmask, * method checkOverlapping would treat it as network. I create * temporary substitution Address object to avoid this . * * If interface is used for a global pool (SNAT rule) and * for a static (DNAT rule), then this is ok even though * such global pool overlaps with such static (added 10/17/03) * * But first I need to check if this interface has dynamic * address, in which case I can not really do this check * at all. */ IPv4 addr; Interface *iface=Interface::cast(natcmd->t_addr); if (iface!=NULL && iface->isDyn()) return true; if (iface!=NULL && iface->getId()==outa->getId()) return true; addr.setAddress(*(natcmd->t_addr->getAddressPtr())); addr.setNetmask(*(natcmd->t_addr->getNetmaskPtr())); if (natcmd->type== INTERFACE) { addr.setNetmask(InetAddr(InetAddr::getAllOnes())); } if ( checkOverlapping( addr, *(outa->getAddressPtr())) || checkOverlapping( *outa, *(addr.getAddressPtr())) ) compiler->abort( rule, "Global pool " +printGlobalPoolAddress(addr) +" from rule " +natcmd->rule_label +" overlaps with static translation address in rule " +rule->getLabel()); } } return true; } bool NATCompiler_pix::DetectDuplicateNAT::processNext() { NATCompiler_pix *pix_comp=dynamic_cast(compiler); NATRule *rule=getNext(); if (rule==NULL) return false; tmp_queue.push_back(rule); if (rule->getRuleType()== NATRule::SNAT) { NATCmd *natcmd=pix_comp->nat_commands[ rule->getInt("nat_cmd") ]; if (natcmd->ignore_nat) return true; for (map::iterator i1=pix_comp->nat_commands.begin(); i1!=pix_comp->nat_commands.end(); ++i1) { NATCmd *nc = (*i1).second; /* since map nat_commands is sorted by the key, we only have to scan it * until we hit natcmd */ if (nc->ignore_nat) continue; if (natcmd==nc) break; Interface *int1 = natcmd->o_iface; Interface *int2 = nc->o_iface; // InetAddr a1=natcmd->o_addr->getAddress(); // InetAddr a2=nc->o_addr->getAddress(); // // InetAddr m1=natcmd->o_addr->getInetAddr(); // InetAddr m2=nc->o_addr->getNetmask(); if ( int1->getId()==int2->getId() && natcmd->o_src==nc->o_src && natcmd->o_dst==nc->o_dst && *(natcmd->o_srv)==*(nc->o_srv) ) { ostringstream str; str << "Duplicate NAT detected: rules " << rule->getLabel() << " and "<< nc->rule_label << " : "<< natcmd->o_src->getAddressPtr()->toString() << "/"<< natcmd->o_src->getNetmaskPtr()->toString() << " " << natcmd->o_srv->getProtocolName() << " " << TCPUDPService::cast(natcmd->o_srv)->getSrcRangeStart() << ":" << TCPUDPService::cast(natcmd->o_srv)->getSrcRangeEnd() << " " << "->"<< natcmd->o_dst->getAddressPtr()->toString() << "/"<< natcmd->o_dst->getNetmaskPtr()->toString() << " " << TCPUDPService::cast(natcmd->o_srv)->getDstRangeStart() << "/" << TCPUDPService::cast(natcmd->o_srv)->getDstRangeEnd(); compiler->abort(rule, str.str()); } } } return true; } bool NATCompiler_pix::DetectOverlappingStatics::processNext() { NATCompiler_pix *pix_comp=dynamic_cast(compiler); NATRule *rule=getNext(); if (rule==NULL) return false; tmp_queue.push_back(rule); if (rule->getRuleType()== NATRule::DNAT ) { StaticCmd *scmd=pix_comp->static_commands[ rule->getInt("sc_cmd") ]; for (map::iterator i1=pix_comp->static_commands.begin(); i1!=pix_comp->static_commands.end(); i1++ ) { // int scid=i1->first; StaticCmd *sc= i1->second; if (sc->ignore_scmd_and_print_acl) continue; if (sc==scmd) break; if (Interface::isA(scmd->oaddr) && Interface::isA(sc->oaddr)) { if ( *(sc->osrv) == *(scmd->osrv) && *(sc->tsrv) == *(scmd->tsrv) && *(sc->osrc) == *(scmd->osrc) && sc->oaddr->getId() == scmd->oaddr->getId()) compiler->abort( rule, "Static NAT rules overlap or are redundant : rules "+ sc->rule+" and "+scmd->rule+" : "+ "outside address: "+ "interface "+Interface::cast(scmd->oaddr)->getLabel()+ " inside address: "+ scmd->iaddr->getAddressPtr()->toString()+"/"+ scmd->iaddr->getNetmaskPtr()->toString()); } else { if ( *(sc->osrv) == *(scmd->osrv) && *(sc->tsrv) == *(scmd->tsrv) && *(sc->osrc) == *(scmd->osrc)) { const InetAddrMask *ia1 = scmd->iaddr->getInetAddrMaskObjectPtr(); const InetAddrMask *ia2 = sc->iaddr->getInetAddrMaskObjectPtr(); const InetAddrMask *oa1 = scmd->oaddr->getInetAddrMaskObjectPtr(); const InetAddrMask *oa2 = sc->oaddr->getInetAddrMaskObjectPtr(); if ( ! getOverlap(*(ia1), *(ia2)).empty() || ! getOverlap(*(oa1), *(oa2)).empty() ) compiler->abort( rule, "Static NAT rules overlap or are redundant: rules "+ sc->rule+" and "+scmd->rule+" : "+ "outside address: "+ scmd->oaddr->getAddressPtr()->toString()+"/"+ scmd->oaddr->getNetmaskPtr()->toString()+ " inside address: "+ scmd->iaddr->getAddressPtr()->toString()+"/"+ scmd->iaddr->getNetmaskPtr()->toString()); } } } } return true; }