// // C++ Implementation: kmfiptablesdocumentconverter // // Description: // // // Author: Christian Hubinger , (C) 2004 // // Copyright: See COPYING file that comes with this distribution // // License: GPL // #include "kmfiptablesdocumentconverter.h" // TQt includes // KDE includes #include #include #include "../../version.h" #include "../../core/kmfgenericdoc.h" #include "../../core/kmfiptdoc.h" #include "../../core/kmfnetzone.h" #include "../../core/kmfnethost.h" #include "../../core/kmfprotocol.h" #include "../../core/kmfprotocolusage.h" #include "../../core/iptable.h" #include "../../core/iptchain.h" #include "../../core/iptrule.h" #include "../../core/iptruleoption.h" #include "../../core/kmferror.h" #include "../../core/kmferrorhandler.h" #include "../../core/kmfconfig.h" #include "../../core/xmlnames.h" namespace KMF { KMFIPTablesDocumentConverter::KMFIPTablesDocumentConverter() { m_errorHandler = new KMFErrorHandler( "KMFIPTablesDocumentConverter" ); m_err = new KMFError(); m_iptdoc = 0; } KMFIPTablesDocumentConverter::~KMFIPTablesDocumentConverter() {} KMFIPTDoc* KMFIPTablesDocumentConverter::compileToIPTDoc( KMFGenericDoc* doc ) { kdDebug() << "const TQString& KMFIPTablesCompiler::compileToIPTDoc( KMFGenericDoc* doc )" << endl; if ( ! doc ) { kdDebug() << "No document Available to compile" << endl; return 0; } // kdDebug() << "Doc XLM:\n" << doc->getXMLSniplet() << endl; m_iptdoc = new KMFIPTDoc( 0, "iptdoc", doc->target() ); KMFNetZone *zone = 0; IPTable *table = 0; IPTChain *chain = 0; setupInAndOutHosts( m_iptdoc, doc->trustedHostsZone(), "ACCEPT" ); setupInAndOutHosts( m_iptdoc, doc->maliciousHostsZone(), "DROP" ); setupForbiddenHosts( m_iptdoc, doc->badClientsHostsZone(), "in" ); setupForbiddenHosts( m_iptdoc, doc->badServersHostsZone(), "out" ); setupICMPRules( doc, m_iptdoc ); setupLocalhostRules( doc, m_iptdoc ); if ( doc->allowIncomingConnections() ) { zone = doc->incomingZone(); table = m_iptdoc->table( Constants::FilterTable_Name ); chain = table->chainForName( Constants::InputChain_Name ); addToChains( zone, m_iptdoc, chain, Constants::InputChain_Name); } if ( doc->restrictOutgoingConnections() ) { zone = doc->outgoingZone(); table = m_iptdoc->table( Constants::FilterTable_Name ); chain = table->chainForName( Constants::OutputChain_Name ); addToChains( zone, m_iptdoc, chain, Constants::OutputChain_Name); } setupConnectionTracking( m_iptdoc ); setupPolicies( doc, m_iptdoc ); setupNatRules( doc, m_iptdoc ); setupLogging( doc, m_iptdoc ); return m_iptdoc; } void KMFIPTablesDocumentConverter::setupConnectionTracking( KMFIPTDoc* doc ) { kdDebug() << "void KMFIPTablesCompiler::setupConnectionTracking( KMFIPTDoc* doc )" << endl; IPTable *table = doc->table( Constants::FilterTable_Name ); IPTChain *chain = table->chainForName( Constants::InputChain_Name ); IPTRule *rule = chain->addRule( "CONNTRACK", m_err ); if ( ! m_errorHandler->showError( m_err ) ) { return; } TQPtrList args; args.append( new TQString(XML::BoolOn_Value) ); args.append( new TQString("RELATED,ESTABLISHED") ); TQString opt = "state_opt"; rule->addRuleOption( opt, args ); rule->setTarget( "ACCEPT" ); rule->setDescription( i18n( "This rule enables connection tracking\n" "in your firewall.\n" "It simply allows all traffic reaching\n" "your host, which is somehow related to\n" "connections you established e.g. answers\n" "others send you to your requests.") ); } void KMFIPTablesDocumentConverter::setupLocalhostRules( KMFGenericDoc* gendoc, KMFIPTDoc* doc ){ kdDebug() << "void KMFIPTablesCompiler::setupConnectionTracking( KMFGenericDoc* gendoc, KMFIPTDoc* doc )" << endl; IPTable *table = doc->table( Constants::FilterTable_Name ); IPTChain *chain = table->chainForName( Constants::InputChain_Name ); IPTRule *rule = chain->addRule( "LOCALHOST", m_err ); if ( ! m_errorHandler->showError( m_err ) ) { return; } TQPtrList args; args.append( new TQString( Constants::Localhost_IP ) ); args.append( new TQString( XML::BoolOff_Value ) ); TQString opt = "ip_opt"; rule->addRuleOption( opt, args ); rule->setTarget( "ACCEPT" ); args.clear(); opt = "interface_opt"; args.append( new TQString( "lo" ) ); args.append( new TQString( XML::BoolOff_Value ) ); rule->addRuleOption( opt, args ); rule->setDescription( i18n( "Allows all localhost traffic" ) ); if ( gendoc->restrictOutgoingConnections() ) { chain = table->chainForName( Constants::OutputChain_Name ); rule = chain->addRule( "LOCALHOST", m_err ); if ( ! m_errorHandler->showError( m_err ) ) { return; } args.clear(); opt = "interface_opt"; args.append( new TQString( XML::BoolOff_Value ) ); args.append( new TQString( "lo" ) ); rule->addRuleOption( opt, args ); rule->setTarget( "ACCEPT" ); rule->setDescription( i18n( "Allows all localhost traffic" ) ); } } void KMFIPTablesDocumentConverter::setupPolicies( KMFGenericDoc* gendoc, KMFIPTDoc* iptdoc ) { kdDebug() << "void KMFIPTablesCompiler::setupPolicies( KMFGenericDoc* gendoc, KMFIPTDoc* iptdoc )" << endl; IPTable *table = iptdoc->table( Constants::FilterTable_Name ); IPTChain *chain = table->chainForName( Constants::InputChain_Name ); chain->setDefaultTarget( "DROP" ); chain = table->chainForName( Constants::OutputChain_Name ); if ( gendoc->restrictOutgoingConnections() ) { chain->setDefaultTarget( "DROP" ); } else { chain->setDefaultTarget( "ACCEPT" ); } } void KMFIPTablesDocumentConverter::addToChains( KMFNetZone* zone, KMFIPTDoc* doc, IPTChain* chain, const TQString& root_chain ) { TQPtrList& children = zone->zones(); TQPtrListIterator it( children ); static int i = 0; while( it.current() ) { addToChains( it.current(), doc, chain, root_chain ); ++it; } IPTable *table = doc->table( Constants::FilterTable_Name ); TQString num = ""; num.setNum( i ); TQString name = ""; if ( root_chain == Constants::InputChain_Name ) { name = "IZ_" + num; } else if ( root_chain == Constants::OutputChain_Name ) { name = "OZ_" + num; } name.stripWhiteSpace(); TQString target = "ACCEPT"; if ( zone->address()->toString() != "0.0.0.0" ) { table->addChain( name, target, false, m_err ); if ( ! m_errorHandler->showError( m_err ) ) return; if ( ! chain ) { kdDebug() << "KMFIPTablesCompiler: WARNING Couldn't create chain: " << name << endl; return; } IPTRule* rule = 0; rule = chain->addRule( "Feed_" + num , m_err ); if ( ! m_errorHandler->showError( m_err ) ) return; rule->setDescription( i18n( "This rule forwards all traffic to\n" "chain: %1 which handles traffic for\n" "zone: %2.").arg( name ).arg( zone->guiName() ) ); i++; if ( ! rule ) { kdDebug() << "KMFIPTablesCompiler: WARNING Couldn't create rule: Feed in chain: " << chain->name() << endl; return; } IPTChain *ch = table->chainForName( name ); if ( ! ch ) { kdDebug() << "KMFIPTablesCompiler: WARNING Couldn't find chain: " << name << endl; return; } ch->setDescription( i18n("The Chain created to handle\nrules defined in zone %1.").arg( zone->guiName() ) ); TQPtrList args; if ( root_chain == Constants::InputChain_Name ) { args.append( new TQString( zone->address()->toString()+"/"+zone->mask()->toString() ) ); args.append( new TQString( XML::BoolOff_Value ) ); } else if ( root_chain == Constants::OutputChain_Name ) { args.append( new TQString( XML::BoolOff_Value ) ); args.append( new TQString( zone->address()->toString()+"/"+zone->mask()->toString() ) ); } TQString s ="ip_opt"; rule->addRuleOption( s , args ); rule->setTarget( name ); createRules( zone, ch, root_chain ); } else { createRules( zone, chain, root_chain ); } } void KMFIPTablesDocumentConverter::createRules( KMFNetZone* zone, IPTChain* chain, const TQString& root_chain ) { TQPtrList& prots = zone->protocols(); TQPtrListIterator it ( prots ); while ( it.current() ) { KMFProtocolUsage* prot = it.current(); if ( ! zone->protocolInherited( prot->protocol()->uuid() ) ) { createZoneProtocolRules( chain, prot ); } else { kdDebug() << "Skipping inherited Portocol: " << prot->protocol()->name() << " in zone: " << zone->guiName() << endl; } ++it; } TQPtrList& hosts = zone->hosts(); TQPtrListIterator it2 ( hosts ); while ( it2.current() ) { KMFNetHost* host = dynamic_cast ( it2.current() ); kdDebug() << "Will create rules for host: " << host->guiName() << " in zone:" << zone->guiName() << endl; TQPtrList& prots = host->protocols(); TQPtrListIterator it3 ( prots ); while ( it3.current() ) { KMFProtocolUsage* protUsage = it3.current(); kdDebug() << "Found Stored Usage" << endl; kdDebug() << " Name: " << protUsage->name() << endl; if ( ! host->protocolInherited( protUsage->protocol()->uuid() ) ) { kdDebug() << "Found Protocol: " << protUsage->protocol()->name() << endl; createHostProtocolRules( chain, host, protUsage, root_chain ); } else { kdDebug() << "Skipping inherited Portocol: " << protUsage->protocol()->name() << " in host: " << host->guiName() << endl; } ++it3; } ++it2; } } void KMFIPTablesDocumentConverter::createZoneProtocolRules( IPTChain* chain, KMFProtocolUsage* prot ) { kdDebug() << "void KMFIPTablesCompiler::createProtocolRules( ITPChain* chain, KMFProtocol* protocol )" << endl; const TQString& tcpPorts = prot->protocol()->tcpPortsList(); const TQString& udpPorts = prot->protocol()->udpPortsList(); if ( ! tcpPorts.isEmpty() ) { createZoneProtocol( chain, prot, "tcp", tcpPorts ); } if ( ! udpPorts.isEmpty() ) { createZoneProtocol( chain, prot, "udp", udpPorts ); } } void KMFIPTablesDocumentConverter::createZoneProtocol( IPTChain* chain, KMFProtocolUsage* prot, const TQString& option, const TQString& ports ) { kdDebug() << "void KMFIPTablesCompiler::createProtocol( IPTChain*, const TQString& option, TQStringList ports )" << endl; TQString s; TQPtrList args; args.clear(); args.append( new TQString( XML::BoolOn_Value ) ); args.append( new TQString( XML::BoolOff_Value ) ); IPTRule* rule; rule = chain->addRule( prot->protocol()->name()+ "_" + option , m_err ); if ( ports.contains( "," ) > 0 ) { s = option + "_multiport_opt"; } else { s = option + "_opt"; } if ( ! m_errorHandler->showError( m_err ) ) return; rule->addRuleOption( s , args ); rule->setDescription( i18n( "Allow Protocol: %1\n" "Protocol Description: %2" ).arg( prot->protocol()->name( ) ).arg( prot->protocol()->description() ) ); rule->setDescription( prot->protocol()->description() ); args.append( new TQString( ports ) ); rule->addRuleOption( s, args ); if ( prot->logging() ) { rule->setLogging( true ); } if ( prot->limit() > 0 ) { s = "limit_opt"; args.clear(); args.append( new TQString(XML::BoolOn_Value) ); TQString limit; limit.setNum( prot->limit() ); limit += "/" + prot->limitInterval(); kdDebug() << "Setting limit: " << limit << endl; args.append( new TQString( limit ) ); rule->addRuleOption( s, args ); } rule->setTarget("ACCEPT"); } void KMFIPTablesDocumentConverter::createHostProtocolRules( IPTChain* chain, KMFNetHost* host, KMFProtocolUsage* prot, const TQString& root_chain ) { kdDebug() << "void KMFIPTablesCompiler::createProtocolRules( ITPChain* chain, KMFProtocol* protocol )" << endl; const TQString& tcpPorts = prot->protocol()->tcpPortsList(); const TQString& udpPorts = prot->protocol()->udpPortsList(); if ( ! tcpPorts.isEmpty() ) { createHostProtocol( chain, host, prot, "tcp", tcpPorts, root_chain ); } if ( ! udpPorts.isEmpty() ) { createHostProtocol( chain, host, prot, "udp", udpPorts, root_chain ); } } void KMFIPTablesDocumentConverter::createHostProtocol( IPTChain* chain, KMFNetHost* host, KMFProtocolUsage* prot, const TQString& option, const TQString& ports, const TQString& root_chain ) { kdDebug() << "void KMFIPTablesCompiler::createProtocol( IPTChain*, const TQString& option, TQStringList ports )" << endl; TQString s; TQPtrList args; args.clear(); args.append( new TQString( XML::BoolOn_Value ) ); args.append( new TQString( XML::BoolOff_Value ) ); static int i = 0; IPTRule* rule; TQString hn = ""; hn = hn.setNum( i ); i++; hn = "H" + hn; rule = chain->addRule( hn + "_" + prot->protocol()->name() + "_" + option , m_err ); if ( ports.contains( "," ) > 0 ) { s = option + "_multiport_opt"; } else { s = option + "_opt"; } rule->setDescription( i18n( "Rule created to apply filters for host: %1\n" "Allow Protocol: %2\n" "Protocol Description: %3" ).arg( host->guiName() ).arg( prot->protocol()->name( ) ).arg( prot->protocol()->description() ) ); if ( ! m_errorHandler->showError( m_err ) ) return; rule->addRuleOption( s , args ); args.append( new TQString( ports ) ); rule->addRuleOption( s, args ); if ( prot->logging() ) { rule->setLogging( true ); } if ( prot->limit() > 0 ) { s = "limit_opt"; args.clear(); args.append( new TQString(XML::BoolOn_Value) ); TQString limit; limit.setNum( prot->limit() ); limit += "/" + prot->limitInterval(); kdDebug() << "Setting limit: " << limit << endl; args.append( new TQString( limit ) ); rule->addRuleOption( s, args ); } args.clear(); if ( root_chain == Constants::OutputChain_Name ) { args.append( new TQString( XML::BoolOff_Value ) ); } s = "ip_opt"; args.append( new TQString( host->address()->toString() ) ); rule->addRuleOption( s, args ); rule->setTarget("ACCEPT"); } void KMFIPTablesDocumentConverter::setupInAndOutHosts( KMFIPTDoc* iptdoc, KMFNetZone* zone, const TQString& target ) { kdDebug() << "KMFIPTablesCompiler::setupTrustedHosts( KMFNetZone* )" << endl; TQPtrListIterator it ( zone->hosts() ); int i = 0; while ( it.current() ) { KMFNetHost *host = dynamic_cast ( *it ); IPTable *table = iptdoc->table( Constants::FilterTable_Name ); IPTChain *chain; IPTRule *rule; TQString ruleName = ""; ruleName = ruleName.setNum( i ); if ( target == "ACCEPT" ) { ruleName = "Trusted_" + ruleName; } else { ruleName = "Malicious_" + ruleName; } TQString opt = "ip_opt"; TQPtrList args; chain = table->chainForName( Constants::InputChain_Name ); rule = chain->addRule( ruleName, m_err ); if ( ! m_errorHandler->showError( m_err ) ) return; args.append( new TQString( host->address()->toString() ) ); rule->addRuleOption( opt, args ); if ( target == "ACCEPT" ) { rule->setDescription( i18n("This rule allows incoming packets from trusted host: %1.").arg( host->guiName() ) ); } else { rule->setDescription( i18n("This rule drops incoming packets from malicious host: %1.").arg( host->guiName() ) ); } rule->setTarget( target ); if ( host->logIncoming() ) { rule->setLogging( true ); } chain = table->chainForName( Constants::OutputChain_Name ); rule = chain->addRule( ruleName, m_err ); args.clear(); args.append( new TQString( XML::BoolOff_Value ) ); args.append( new TQString( host->address()->toString() ) ); rule->addRuleOption( opt, args ); if ( ! m_errorHandler->showError( m_err ) ) return; if ( target == "ACCEPT" ) { rule->setDescription( i18n("This rule allows outgoing packets to trusted host: %1.").arg( host->guiName() ) ); } else { rule->setDescription( i18n("This rule drops outgoing packets to malicious host: %1.").arg( host->guiName() ) ); } rule->setTarget( target ); if ( host->logOutgoing() ) { rule->setLogging( true ); } ++it; i++; } } void KMFIPTablesDocumentConverter::setupForbiddenHosts( KMFIPTDoc* iptdoc , KMFNetZone* zone, const TQString& inOut ) { TQPtrListIterator it ( zone->hosts() ); int i = 0; while ( it.current() ) { KMFNetHost *host = dynamic_cast ( *it ); IPTable *table = iptdoc->table( Constants::FilterTable_Name ); IPTChain *chain; IPTRule *rule; TQString ruleName = ""; ruleName = ruleName.setNum( i ); if ( inOut == "in" ) { ruleName = "ForbiddenClient_" + ruleName; } else { ruleName = "ForbiddenServer_" + ruleName; } TQString opt = "ip_opt"; TQPtrList args; if ( inOut == "in" ) { chain = table->chainForName( Constants::InputChain_Name ); } else { chain = table->chainForName( Constants::OutputChain_Name ); } rule = chain->addRule( ruleName, m_err ); if ( ! m_errorHandler->showError( m_err ) ) return; if ( inOut == "out" ) { args.append( new TQString(XML::BoolOff_Value) ); } args.append( new TQString( host->address()->toString() ) ); rule->addRuleOption( opt, args ); if ( inOut =="in" ) { rule->setDescription( i18n("This rule drops packets from forbidden client: %1.").arg( host->guiName() ) ); } else { rule->setDescription( i18n("This rule drops packets to forbidden server: %1.").arg( host->guiName() ) ); } rule->setTarget( "DROP" ); if ( inOut =="in" ) { if ( host->logIncoming() ) { rule->setLogging( true ); } } else { if ( host->logOutgoing() ) { rule->setLogging( true ); } } ++it; i++; } } void KMFIPTablesDocumentConverter::setupNatRules( KMFGenericDoc* doc, KMFIPTDoc* iptdoc ) { if ( ! doc->useNat() ) { return; } IPTable* table = iptdoc->table( Constants::NatTable_Name ); if ( ! table ) { kdDebug() << "ERROR: Couldn't find table nat!!!" << endl; return; } IPTChain* chain = table->chainForName( Constants::PostRoutingChain_Name ); if ( ! chain ) { kdDebug() << "ERROR: Couldn't find chain POSTROUTING!!!" << endl; return; } iptdoc->setUseIPFwd( true ); iptdoc->useNat(); IPTRule* rule = chain->addRule( "NAT_RULE", m_err ); if ( ! m_errorHandler->showError( m_err ) ) { return; } rule->setDescription( i18n("Rule created for setting up\nthe nat router functionality.") ); TQString opt = "interface_opt"; TQPtrList args; args.append( new TQString( XML::BoolOff_Value ) ); args.append( new TQString( doc->outgoingInterface() ) ); rule->addRuleOption( opt, args ); setupNatTarget( doc, rule ); } void KMFIPTablesDocumentConverter::setupNatTarget( KMFGenericDoc* doc, IPTRule* rule ) { if ( doc->useMasquerade() ) { rule->setTarget( "MASQUERADE" ); } else { rule->setTarget( "SNAT" ); TQString opt = "target_snat_opt"; TQPtrList args; args.append( new TQString( doc->natAddress()->toString() ) ); rule->addRuleOption( opt, args ); } } void KMFIPTablesDocumentConverter::setupLogging( KMFGenericDoc* doc, KMFIPTDoc* iptdoc ) { if ( ! doc->logDropped() ) { return; } IPTable* table = iptdoc->table( Constants::FilterTable_Name ); if ( ! table ) { kdDebug() << "ERROR: Couldn't find table filter!!!" << endl; return; } IPTChain* chain = table->chainForName( Constants::InputChain_Name ); if ( ! chain ) { kdDebug() << "ERROR: Couldn't find chain INPUT!!!" << endl; return; } setupLoggingRules( doc, chain ); if ( doc->restrictOutgoingConnections() ) { chain = table->chainForName( Constants::OutputChain_Name ); if ( ! chain ) { kdDebug() << "ERROR: Couldn't find chain INPUT!!!" << endl; return; } setupLoggingRules( doc, chain ); } } void KMFIPTablesDocumentConverter::setupLoggingRules( KMFGenericDoc* doc, IPTChain* chain ) { TQString limit = ""; TQString burst = ""; TQString prefix = doc->logPrefix(); if ( doc->limitLog() ) { limit = "5/second"; burst = "5"; } chain->setDropLogging( true, limit, burst, prefix ); } void KMFIPTablesDocumentConverter::setupICMPRules( KMFGenericDoc* doc, KMFIPTDoc* iptdoc) { if ( ! doc->allowPingReply() ) { return; } IPTable* table = iptdoc->table( Constants::FilterTable_Name ); if ( ! table ) { kdDebug() << "ERROR: Couldn't find table filter!!!" << endl; return; } IPTChain* chain = table->chainForName( Constants::InputChain_Name ); if ( ! chain ) { kdDebug() << "ERROR: Couldn't find chain INPUT!!!" << endl; return; } IPTRule *rule = chain->addRule( "ICMP", m_err ); if ( ! m_errorHandler->showError( m_err ) ) { return; } rule->setDescription( i18n("Rule to setup the ICMP Ping policy.") ); TQString opt = "icmp_opt"; TQPtrList args; args.append( new TQString( XML::BoolOn_Value ) ); args.append( new TQString( "echo-request" ) ); rule->addRuleOption( opt, args ); rule->setTarget( "ACCEPT" ); if ( doc->limitPingReply() ) { args.clear(); TQString opt = "limit_opt"; args.append( new TQString( XML::BoolOn_Value ) ); args.append( new TQString( "5/second" ) ); args.append( new TQString( "5" ) ); rule->addRuleOption( opt, args ); } if ( doc->restrictOutgoingConnections() ) { chain = table->chainForName( Constants::OutputChain_Name ); if ( ! chain ) { kdDebug() << "ERROR: Couldn't find chain INPUT!!!" << endl; return; } rule = chain->addRule( "ICMP", m_err ); if ( ! m_errorHandler->showError( m_err ) ) { return; } rule->setDescription( i18n("Rule to setup the ICMP Ping policy.") ); TQString opt = "icmp_opt"; args.clear(); args.append( new TQString( XML::BoolOn_Value ) ); args.append( new TQString( "echo-request" ) ); rule->addRuleOption( opt, args ); rule->setTarget( "ACCEPT" ); } } }