1 /*
2 
3                           Firewall Builder
4 
5                  Copyright (C) 2007 NetCitadel, LLC
6 
7   Author:  Vadim Kurland     vadim@vk.crocodile.org
8 
9   $Id$
10 
11   This program is free software which we release under the GNU General Public
12   License. You may redistribute and/or modify this program under the terms
13   of that license as published by the Free Software Foundation; either
14   version 2 of the License, or (at your option) any later version.
15 
16   This program is distributed in the hope that it will be useful,
17   but WITHOUT ANY WARRANTY; without even the implied warranty of
18   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19   GNU General Public License for more details.
20 
21   To get a copy of the GNU General Public License, write to the Free Software
22   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23 
24 */
25 
26 #include "config.h"
27 
28 #include "PolicyCompiler_nxosacl.h"
29 #include "NamedObjectsAndGroupsSupport.h"
30 
31 #include "fwbuilder/AddressTable.h"
32 #include "fwbuilder/FWObjectDatabase.h"
33 #include "fwbuilder/ICMPService.h"
34 #include "fwbuilder/IPService.h"
35 #include "fwbuilder/Interface.h"
36 #include "fwbuilder/Library.h"
37 #include "fwbuilder/Management.h"
38 #include "fwbuilder/Network.h"
39 #include "fwbuilder/ObjectMirror.h"
40 #include "fwbuilder/Policy.h"
41 #include "fwbuilder/Resources.h"
42 #include "fwbuilder/RuleElement.h"
43 #include "fwbuilder/TCPService.h"
44 #include "fwbuilder/UDPService.h"
45 
46 #include <assert.h>
47 
48 using namespace libfwbuilder;
49 using namespace fwcompiler;
50 using namespace std;
51 
myPlatformName()52 string PolicyCompiler_nxosacl::myPlatformName() { return "nxosacl"; }
53 
PolicyCompiler_nxosacl(FWObjectDatabase * _db,Firewall * fw,bool ipv6_policy,OSConfigurator * _oscnf)54 PolicyCompiler_nxosacl::PolicyCompiler_nxosacl(FWObjectDatabase *_db,
55                                              Firewall *fw,
56                                              bool ipv6_policy,
57                                              OSConfigurator *_oscnf) :
58     PolicyCompiler_cisco(_db, fw, ipv6_policy, _oscnf)
59 {
60     resetinbound = false;
61     fragguard = false;
62     comment_symbol = "!";
63 }
64 
prolog()65 int PolicyCompiler_nxosacl::prolog()
66 {
67     string version = fw->getStr("version");
68     string platform = fw->getStr("platform");
69     string host_os = fw->getStr("host_OS");
70 
71     if (platform!="nxosacl")
72 	abort("Unsupported platform " + platform );
73 
74     fw->getOptionsObject()->setBool("generate_out_acl", true);
75 
76     fw->getOptionsObject()->setBool(
77         "use_acl_remarks",
78         fw->getOptionsObject()->getBool("nxosacl_use_acl_remarks"));
79 
80     // object_groups = new Group();
81     // persistent_objects->add( object_groups );
82 
83     setAllNetworkZonesToNone();
84 
85     return PolicyCompiler::prolog();
86 }
87 
findDynamicInterface(PolicyRule * rule,RuleElement * rel)88 bool PolicyCompiler_nxosacl::checkForDynamicInterface::findDynamicInterface(
89     PolicyRule *rule, RuleElement *rel)
90 {
91     string vers=compiler->fw->getStr("version");
92     for (list<FWObject*>::iterator i1=rel->begin(); i1!=rel->end(); ++i1)
93     {
94 	FWObject *o   = *i1;
95 	FWObject *obj = NULL;
96 	if (FWReference::cast(o)!=NULL) obj=FWReference::cast(o)->getPointer();
97         Interface *iface=Interface::cast(obj);
98         if (iface!=NULL && iface->isDyn())
99             compiler->abort(
100                 rule,
101                 "Dynamic interface can not be used in the NXOS ACL rules.");
102     }
103 
104     return true;
105 }
106 
processNext()107 bool PolicyCompiler_nxosacl::checkForDynamicInterface::processNext()
108 {
109     PolicyRule *rule = getNext(); if (rule==NULL) return false;
110 
111     findDynamicInterface(rule,rule->getSrc());
112     findDynamicInterface(rule,rule->getDst());
113 
114     tmp_queue.push_back(rule);
115     return true;
116 }
117 
118 /*
119  * Copy all references from rule element re1 to rule element re2.
120  */
duplicateRuleElement(RuleElement * re1,RuleElement * re2)121 void PolicyCompiler_nxosacl::mirrorRule::duplicateRuleElement(
122     RuleElement *re1, RuleElement *re2)
123 {
124     re2->clearChildren();
125     for (list<FWObject*>::iterator i1=re1->begin(); i1!=re1->end(); ++i1)
126     {
127         FWObject *obj = FWReference::getObject(*i1);
128         re2->addRef(obj);
129     }
130 }
131 
processNext()132 bool PolicyCompiler_nxosacl::mirrorRule::processNext()
133 {
134     //PolicyCompiler_nxosacl *nxosacl_comp=dynamic_cast<PolicyCompiler_nxosacl*>(compiler);
135     PolicyRule *rule = getNext(); if (rule==NULL) return false;
136     if (rule->getOptionsObject()->getBool("nxosacl_add_mirror_rule"))
137     {
138         PolicyRule *r= compiler->dbcopy->createPolicyRule();
139         compiler->temp_ruleset->add(r);
140         r->duplicate(rule);
141 
142         r->setAction(rule->getAction());
143 
144         switch (rule->getDirection())
145         {
146         case PolicyRule::Inbound: r->setDirection(PolicyRule::Outbound); break;
147         case PolicyRule::Outbound: r->setDirection(PolicyRule::Inbound); break;
148         default: r->setDirection(PolicyRule::Both); break;
149         }
150 
151 	RuleElementSrc *osrc = rule->getSrc();
152 	RuleElementDst *odst = rule->getDst();
153 	RuleElementSrv *osrv = rule->getSrv();
154 	RuleElementItf *oitf = rule->getItf();
155 
156 	RuleElementSrc *nsrc = r->getSrc();
157 	RuleElementDst *ndst = r->getDst();
158 	RuleElementSrv *nsrv = r->getSrv();
159 	RuleElementItf *nitf = r->getItf();
160 
161         duplicateRuleElement(osrc, ndst);
162         duplicateRuleElement(odst, nsrc);
163         duplicateRuleElement(oitf, nitf);
164 
165         if (!osrv->isAny())
166         {
167             ObjectMirror mirror;
168             nsrv->clearChildren();
169             for (list<FWObject*>::iterator i1=osrv->begin(); i1!=osrv->end(); ++i1)
170             {
171                 Service *nobj = mirror.getMirroredService(
172                     Service::cast(FWReference::getObject(*i1)));
173                 if (nobj->getParent() == NULL)
174                     compiler->persistent_objects->add(nobj, false);
175                 nsrv->addRef(nobj);
176             }
177         }
178 
179         tmp_queue.push_back(r);
180     }
181     tmp_queue.push_back(rule);
182     return true;
183 }
184 
processNext()185 bool PolicyCompiler_nxosacl::SpecialServices::processNext()
186 {
187     //PolicyCompiler_nxosacl *nxosacl_comp=dynamic_cast<PolicyCompiler_nxosacl*>(compiler);
188     PolicyRule *rule=getNext(); if (rule==NULL) return false;
189     Service *s = compiler->getFirstSrv(rule);
190 
191     if (IPService::cast(s)!=NULL)
192     {
193 	if (s->getBool("rr")        ||
194 	    s->getBool("ssrr")      ||
195 	    s->getBool("ts") )
196 	    compiler->abort(
197                     rule,
198                     "NXOS ACL does not support checking for IP options in ACLs.");
199     }
200     if (TCPService::cast(s)!=NULL && TCPService::cast(s)->inspectFlags())
201     {
202         string version = compiler->fw->getStr("version");
203         if (XMLTools::version_compare(version, "12.4")<0)
204             compiler->abort(rule, "TCP flags match requires NXOS v12.4 or later.");
205     }
206 
207     tmp_queue.push_back(rule);
208     return true;
209 }
210 
211 /*
212  * This rule processor is used to separate TCP service objects that
213  * match tcp flags when generated config uses object-group clause
214  */
processNext()215 bool PolicyCompiler_nxosacl::splitTCPServiceWithFlags::processNext()
216 {
217     PolicyRule *rule=getNext(); if (rule==NULL) return false;
218     RuleElementSrv *srv = rule->getSrv();
219 
220     if (srv->size() > 1)
221     {
222         std::list<FWObject*> cl;
223         for (list<FWObject*>::iterator i1=srv->begin(); i1!=srv->end(); ++i1)
224         {
225             FWObject *o   = *i1;
226             FWObject *obj = NULL;
227             if (FWReference::cast(o)!=NULL) obj=FWReference::cast(o)->getPointer();
228             Service *s=Service::cast(obj);
229             assert(s!=NULL);
230 
231             TCPService *tcp_srv = TCPService::cast(s);
232             if (tcp_srv && (tcp_srv->inspectFlags() || tcp_srv->getEstablished()))
233                 cl.push_back(s);
234         }
235 
236         while (!cl.empty())
237         {
238             PolicyRule  *r = compiler->dbcopy->createPolicyRule();
239             compiler->temp_ruleset->add(r);
240             r->duplicate(rule);
241 
242             RuleElementSrv *nsrv = r->getSrv();
243             nsrv->clearChildren();
244             nsrv->addRef( cl.front() );
245             tmp_queue.push_back(r);
246 
247             srv->removeRef( cl.front() );
248             cl.pop_front();
249         }
250         if (srv->size()>0) tmp_queue.push_back(rule);
251 
252     } else
253         tmp_queue.push_back(rule);
254 
255     return true;
256 }
257 
258 
259 
compile()260 void PolicyCompiler_nxosacl::compile()
261 {
262     string banner = " Compiling ruleset " + getSourceRuleSet()->getName();
263     if (ipv6) banner += ", IPv6";
264     info(banner);
265 
266     string version = fw->getStr("version");
267     bool supports_object_groups = XMLTools::version_compare(version, "12.4")>=0 &&
268         fw->getOptionsObject()->getBool("nxosacl_use_object_groups") && ! ipv6;
269 
270     string vers = fw->getStr("version");
271     string platform = fw->getStr("platform");
272 
273     Compiler::compile();
274 
275     if ( fw->getOptionsObject()->getBool ("check_shading") &&
276          ! inSingleRuleCompileMode())
277     {
278         add( new Begin("Detecting rule shadowing"               ) );
279         add( new printTotalNumberOfRules());
280 
281         add( new ItfNegation("process negation in Itf"  ) );
282         add( new InterfacePolicyRules(
283                  "process interface policy rules and store interface ids"));
284 
285         add( new recursiveGroupsInSrc("check for recursive groups in SRC"));
286         add( new recursiveGroupsInDst("check for recursive groups in DST"));
287         add( new recursiveGroupsInSrv("check for recursive groups in SRV"));
288 
289         add( new ExpandGroups("expand groups"));
290         add( new dropRuleWithEmptyRE(
291                  "drop rules with empty rule elements"));
292         add( new eliminateDuplicatesInSRC("eliminate duplicates in SRC"));
293         add( new eliminateDuplicatesInDST("eliminate duplicates in DST"));
294         add( new eliminateDuplicatesInSRV("eliminate duplicates in SRV"));
295         add( new ExpandMultipleAddressesInSrc(
296                  "expand objects with multiple addresses in SRC" ) );
297         add( new ExpandMultipleAddressesInDst(
298                  "expand objects with multiple addresses in DST" ) );
299         add( new dropRuleWithEmptyRE(
300                  "drop rules with empty rule elements"));
301 
302         add( new mirrorRule("Add mirrored rules"));
303 
304         add( new ConvertToAtomic("convert to atomic rules"       ) );
305 
306         add( new checkForObjectsWithErrors(
307                  "check if we have objects with errors in rule elements"));
308 
309         add( new DetectShadowing("Detect shadowing"              ) );
310         add( new simplePrintProgress() );
311 
312         runRuleProcessors();
313         deleteRuleProcessors();
314     }
315 
316 
317     add( new Begin (" Start processing rules" ) );
318     add( new printTotalNumberOfRules ( ) );
319 
320     add( new singleRuleFilter());
321 
322     add( new recursiveGroupsInSrc( "check for recursive groups in SRC" ) );
323     add( new recursiveGroupsInDst( "check for recursive groups in DST" ) );
324     add( new recursiveGroupsInSrv( "check for recursive groups in SRV" ) );
325 
326     add( new emptyGroupsInSrc( "check for empty groups in SRC" ) );
327     add( new emptyGroupsInDst( "check for empty groups in DST" ) );
328     add( new emptyGroupsInSrv( "check for empty groups in SRV" ) );
329 
330     add( new ExpandGroups ("expand groups" ) );
331     add( new dropRuleWithEmptyRE(
332              "drop rules with empty rule elements"));
333     add( new eliminateDuplicatesInSRC( "eliminate duplicates in SRC" ) );
334     add( new eliminateDuplicatesInDST( "eliminate duplicates in DST" ) );
335     add( new eliminateDuplicatesInSRV( "eliminate duplicates in SRV" ) );
336 
337     add( new processMultiAddressObjectsInSrc(
338              "process MultiAddress objects in Src") );
339     add( new processMultiAddressObjectsInDst(
340              "process MultiAddress objects in Dst") );
341 
342     add( new expandGroupsInItf("expand groups in Interface" ));
343     add( new replaceClusterInterfaceInItf(
344              "replace cluster interfaces with member interfaces in the Interface rule element"));
345 
346     add( new ItfNegation( "process negation in Itf" ) );
347     add( new InterfacePolicyRules(
348              "process interface policy rules and store interface ids") );
349 
350     add( new groupServicesByProtocol ("split rules with different protocols" ) );
351 
352     add( new ExpandMultipleAddressesInSrc(
353              "expand objects with multiple addresses in SRC" ) );
354     add( new MACFiltering ("check for MAC address filtering" ) );
355 //        add( new splitByNetworkZonesForSrc ("split rule if objects in Src belong to different network zones " ) );
356 //        add( new replaceFWinDSTPolicy ("replace fw with its interface in DST in global policy rules") );
357 
358     add( new ExpandMultipleAddressesInDst(
359              "expand objects with multiple addresses in DST" ) );
360     add( new MACFiltering(
361              "check for MAC address filtering" ) );
362     add( new dropRuleWithEmptyRE("drop rules with empty rule elements"));
363 
364 //        add( new splitByNetworkZonesForDst ("split rule if objects in Dst belong to different network zones " ) );
365 
366     if (ipv6)
367         add( new DropIPv4Rules("drop ipv4 rules"));
368     else
369         add( new DropIPv6Rules("drop ipv6 rules"));
370     add( new dropRuleWithEmptyRE("drop rules with empty rule elements"));
371 
372     add( new checkForUnnumbered("check for unnumbered interfaces"));
373 
374     if ( ! supports_object_groups)
375         add( new addressRanges("process address ranges"));
376 
377     add( new mirrorRule("Add mirrored rules"));
378 
379     add( new dropRuleWithEmptyRE("drop rules with empty rule elements"));
380 
381     add( new setInterfaceAndDirectionBySrc(
382              "Set interface and direction for rules with interface 'all' using SRC"));
383     add( new setInterfaceAndDirectionByDst(
384              "Set interface and direction for rules with interface 'all' using DST"));
385     add( new setInterfaceAndDirectionIfInterfaceSet(
386              "Set direction for rules with interface not 'all'"));
387 
388     add( new specialCaseWithDynInterface(
389              "check for a special cases with dynamic interface" ) );
390 
391     // first arg is true because we use "ip access-list" for NXOS.
392     add( new pickACL( true, "assign ACLs" ) );
393 
394     add( new SpecialServices( "check for special services" ) );
395     add( new CheckForUnsupportedUserService("check for user service") );
396 
397     add( new checkForZeroAddr(    "check for zero addresses" ) );
398     add( new checkForDynamicInterface("check for dynamic interfaces" ) );
399 
400     /* remove redundant objects only after all splits has been
401      * done, right before object groups are created
402      */
403     add( new removeRedundantAddressesFromSrc(
404              "remove redundant addresses from Src") );
405     add( new removeRedundantAddressesFromDst(
406              "remove redundant addresses from Dst") );
407 
408     add( new checkForObjectsWithErrors(
409              "check if we have objects with errors in rule elements"));
410 
411     if (supports_object_groups)
412     {
413         // "object-group service" does not seem to support
414         // matching of tcp flags and "established". Need to
415         // separate objects using these into separate rules to avoid
416         // object-group
417 
418         add( new splitTCPServiceWithFlags(
419                  "separate TCP service with tcp flags"));
420 
421         add( new CreateObjectGroupsForSrc("create object groups for Src",
422                                           named_objects_manager));
423         add( new CreateObjectGroupsForDst("create object groups for Dst",
424                                           named_objects_manager));
425         add( new CreateObjectGroupsForSrv("create object groups for Srv",
426                                           named_objects_manager));
427     } else
428     {
429         add( new ConvertToAtomic ("convert to atomic rules" ) );
430     }
431 
432     add( new simplePrintProgress());
433     add( new createNewCompilerPass("Creating object groups and ACLs"));
434 
435     // This processor prints each ACL separately in one block.
436     // It adds comments inside to denote original rules.
437     //
438     add( new PrintCompleteACLs("Print ACLs"));
439     add( new simplePrintProgress());
440 
441     runRuleProcessors();
442 
443 }
444 
printAccessGroupCmd(ciscoACL * acl,bool neg)445 string PolicyCompiler_nxosacl::printAccessGroupCmd(ciscoACL *acl, bool neg)
446 {
447     ostringstream str;
448 
449     string addr_family_prefix = "ip";
450     if (ipv6) addr_family_prefix = "ipv6";
451 
452     if (getSourceRuleSet()->isTop())
453     {
454         string dir;
455         if (acl->direction()=="in"  || acl->direction()=="Inbound")  dir="in";
456         if (acl->direction()=="out" || acl->direction()=="Outbound") dir="out";
457 
458         str << "interface " << acl->getInterface()->getName() << endl;
459         if (neg) str << "  no";
460         str << "  " << addr_family_prefix << " ";
461         str << getAccessGroupCommandForAddressFamily(ipv6);
462         str << " " << acl->workName() << " " << dir << endl;
463         str << "exit" << endl;
464     }
465     return str.str();
466 }
467 
epilog()468 void PolicyCompiler_nxosacl::epilog()
469 {
470     output << endl;
471 
472     for (map<string,ciscoACL*>::iterator i=acls.begin(); i!=acls.end(); ++i)
473     {
474         ciscoACL *acl=(*i).second;
475         if (acl->size()!=0) output << printAccessGroupCmd(acl, false);
476     }
477     output << endl;
478 
479     if ( fw->getOptionsObject()->getBool("nxosacl_regroup_commands") )
480     {
481         info(" Regrouping commands");
482         regroup();
483     }
484 }
485 
getAccessGroupCommandForAddressFamily(bool ipv6)486 string PolicyCompiler_nxosacl::getAccessGroupCommandForAddressFamily(bool ipv6)
487 {
488     if (ipv6) return "traffic-filter";
489     return "access-group";
490 }
491 
printClearCommands()492 string PolicyCompiler_nxosacl::printClearCommands()
493 {
494     ostringstream output;
495 
496     string version = fw->getStr("version");
497     string platform = fw->getStr("platform");
498 
499     string xml_element = "clear_ip_acl";
500     if (ipv6) xml_element = "clear_ipv6_acl";
501 
502     string clearACLCmd = Resources::platform_res[platform]->getResourceStr(
503         string("/FWBuilderResources/Target/options/") +
504         "version_" + version + "/nxosacl_commands/" + xml_element);
505 
506     assert( !clearACLCmd.empty());
507 
508     // No need to output "clear" commands in single rule compile mode
509     if ( fw->getOptionsObject()->getBool("nxosacl_acl_basic") ||
510          fw->getOptionsObject()->getBool("nxosacl_acl_substitution"))
511     {
512         for (map<string,ciscoACL*>::iterator i=acls.begin(); i!=acls.end(); ++i)
513         {
514             ciscoACL *acl = (*i).second;
515             output << clearACLCmd << " " << acl->workName() << endl;
516         }
517     }
518 
519     return output.str();
520 }
521 
522