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_junosacl.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 #include "fwbuilder/CustomService.h"
46
47 #include "compiler_lib/junosInterfaces.h"
48
49 #include <assert.h>
50
51 using namespace libfwbuilder;
52 using namespace fwcompiler;
53 using namespace std;
54
myPlatformName()55 string PolicyCompiler_junosacl::myPlatformName() { return "junosacl"; }
56
PolicyCompiler_junosacl(FWObjectDatabase * _db,Firewall * fw,bool ipv6_policy,OSConfigurator * _oscnf)57 PolicyCompiler_junosacl::PolicyCompiler_junosacl(FWObjectDatabase *_db,
58 Firewall *fw,
59 bool ipv6_policy,
60 OSConfigurator *_oscnf) :
61 PolicyCompiler_cisco(_db, fw, ipv6_policy, _oscnf)
62 {
63 resetinbound = false;
64 fragguard = false;
65 comment_symbol = "#";
66 }
67
prolog()68 int PolicyCompiler_junosacl::prolog()
69 {
70 string version = fw->getStr("version");
71 string platform = fw->getStr("platform");
72 string host_os = fw->getStr("host_OS");
73
74 if (platform!="junosacl")
75 abort("Unsupported platform " + platform );
76
77 fw->getOptionsObject()->setBool("generate_out_acl", true);
78
79 fw->getOptionsObject()->setBool(
80 "use_acl_remarks",
81 fw->getOptionsObject()->getBool("iosacl_use_acl_remarks"));
82
83 // object_groups = new Group();
84 // persistent_objects->add( object_groups );
85
86 setAllNetworkZonesToNone();
87
88 return PolicyCompiler::prolog();
89 }
90
findDynamicInterface(PolicyRule * rule,RuleElement * rel)91 bool PolicyCompiler_junosacl::checkForDynamicInterface::findDynamicInterface(
92 PolicyRule *rule, RuleElement *rel)
93 {
94 string vers=compiler->fw->getStr("version");
95 for (list<FWObject*>::iterator i1=rel->begin(); i1!=rel->end(); ++i1)
96 {
97 FWObject *o = *i1;
98 FWObject *obj = NULL;
99 if (FWReference::cast(o)!=NULL) obj=FWReference::cast(o)->getPointer();
100 Interface *iface=Interface::cast(obj);
101 if (iface!=NULL && iface->isDyn())
102 compiler->abort(
103 rule,
104 "Dynamic interface can not be used in the IOS ACL rules.");
105 }
106
107 return true;
108 }
109
processNext()110 bool PolicyCompiler_junosacl::checkForDynamicInterface::processNext()
111 {
112 PolicyRule *rule = getNext(); if (rule==NULL) return false;
113
114 findDynamicInterface(rule,rule->getSrc());
115 findDynamicInterface(rule,rule->getDst());
116
117 tmp_queue.push_back(rule);
118 return true;
119 }
120
processNext()121 bool PolicyCompiler_junosacl::ValidateInterfaceUnitName::processNext()
122 {
123 assert(compiler!=NULL);
124 assert(prev_processor!=NULL);
125
126 slurp();
127 if (tmp_queue.size()==0) return false;
128
129 junosInterfaces * jInterface = new junosInterfaces();
130
131 for (std::deque<Rule*>::iterator i=tmp_queue.begin(); i!=tmp_queue.end(); ++i)
132 {
133 if (PolicyRule *rule = PolicyRule::cast(*i))
134 if (FWObject *obj = FWReference::getObject(*rule->getItf()->begin())) {
135 if (!jInterface->parseVlan(QString::fromStdString(obj->getName()), NULL, NULL))
136 compiler->abort(rule, QString("junosacl policy rules must use a 'unit <value>' subinterface, not the main interface. You used: ")
137 .append(QString::fromStdString(obj->getName()))
138 .toStdString());
139 }
140 }
141
142 return true;
143 }
144
145 /*
146 * Copy all references from rule element re1 to rule element re2.
147 */
duplicateRuleElement(RuleElement * re1,RuleElement * re2)148 void PolicyCompiler_junosacl::mirrorRule::duplicateRuleElement(
149 RuleElement *re1, RuleElement *re2)
150 {
151 re2->clearChildren();
152 for (list<FWObject*>::iterator i1=re1->begin(); i1!=re1->end(); ++i1)
153 {
154 FWObject *obj = FWReference::getObject(*i1);
155 re2->addRef(obj);
156 }
157 }
158
processNext()159 bool PolicyCompiler_junosacl::mirrorRule::processNext()
160 {
161 //PolicyCompiler_iosacl *iosacl_comp=dynamic_cast<PolicyCompiler_iosacl*>(compiler);
162 PolicyRule *rule = getNext(); if (rule==NULL) return false;
163 if (rule->getOptionsObject()->getBool("iosacl_add_mirror_rule"))
164 {
165 PolicyRule *r= compiler->dbcopy->createPolicyRule();
166 compiler->temp_ruleset->add(r);
167 r->duplicate(rule);
168
169 r->setAction(rule->getAction());
170
171 switch (rule->getDirection())
172 {
173 case PolicyRule::Inbound: r->setDirection(PolicyRule::Outbound); break;
174 case PolicyRule::Outbound: r->setDirection(PolicyRule::Inbound); break;
175 default: r->setDirection(PolicyRule::Both); break;
176 }
177
178 RuleElementSrc *osrc = rule->getSrc();
179 RuleElementDst *odst = rule->getDst();
180 RuleElementSrv *osrv = rule->getSrv();
181 RuleElementItf *oitf = rule->getItf();
182
183 RuleElementSrc *nsrc = r->getSrc();
184 RuleElementDst *ndst = r->getDst();
185 RuleElementSrv *nsrv = r->getSrv();
186 RuleElementItf *nitf = r->getItf();
187
188 duplicateRuleElement(osrc, ndst);
189 duplicateRuleElement(odst, nsrc);
190 duplicateRuleElement(oitf, nitf);
191
192 if (!osrv->isAny())
193 {
194 ObjectMirror mirror;
195 nsrv->clearChildren();
196 for (list<FWObject*>::iterator i1=osrv->begin(); i1!=osrv->end(); ++i1)
197 {
198 Service *nobj = mirror.getMirroredService(
199 Service::cast(FWReference::getObject(*i1)));
200 if (nobj->getParent() == NULL)
201 compiler->persistent_objects->add(nobj, false);
202 nsrv->addRef(nobj);
203 }
204 }
205
206 tmp_queue.push_back(r);
207 }
208 tmp_queue.push_back(rule);
209 return true;
210 }
211
processNext()212 bool PolicyCompiler_junosacl::SpecialServices::processNext()
213 {
214 //PolicyCompiler_iosacl *iosacl_comp=dynamic_cast<PolicyCompiler_iosacl*>(compiler);
215 PolicyRule *rule=getNext(); if (rule==NULL) return false;
216 Service *s = compiler->getFirstSrv(rule);
217
218 if (IPService::cast(s)!=NULL)
219 {
220 if (s->getBool("rr") ||
221 s->getBool("ssrr") ||
222 s->getBool("ts") )
223 compiler->abort(
224 rule,
225 "IOS ACL does not support checking for IP options in ACLs.");
226 }
227 if (TCPService::cast(s)!=NULL && TCPService::cast(s)->inspectFlags())
228 {
229 string version = compiler->fw->getStr("version");
230 if (XMLTools::version_compare(version, "12.4")<0)
231 compiler->abort(rule, "TCP flags match requires IOS v12.4 or later.");
232 }
233
234 tmp_queue.push_back(rule);
235 return true;
236 }
237
238 /*
239 * This rule processor is used to separate TCP service objects that
240 * match tcp flags when generated config uses object-group clause
241 */
processNext()242 bool PolicyCompiler_junosacl::splitTCPServiceWithFlags::processNext()
243 {
244 PolicyRule *rule=getNext(); if (rule==NULL) return false;
245 RuleElementSrv *srv = rule->getSrv();
246
247 if (srv->size() > 1)
248 {
249 std::list<FWObject*> cl;
250 for (list<FWObject*>::iterator i1=srv->begin(); i1!=srv->end(); ++i1)
251 {
252 FWObject *o = *i1;
253 FWObject *obj = NULL;
254 if (FWReference::cast(o)!=NULL) obj=FWReference::cast(o)->getPointer();
255 Service *s=Service::cast(obj);
256 assert(s!=NULL);
257
258 TCPService *tcp_srv = TCPService::cast(s);
259 if (tcp_srv && (tcp_srv->inspectFlags() || tcp_srv->getEstablished()))
260 cl.push_back(s);
261 }
262
263 while (!cl.empty())
264 {
265 PolicyRule *r = compiler->dbcopy->createPolicyRule();
266 compiler->temp_ruleset->add(r);
267 r->duplicate(rule);
268
269 RuleElementSrv *nsrv = r->getSrv();
270 nsrv->clearChildren();
271 nsrv->addRef( cl.front() );
272 tmp_queue.push_back(r);
273
274 srv->removeRef( cl.front() );
275 cl.pop_front();
276 }
277 if (srv->size()>0) tmp_queue.push_back(rule);
278
279 } else
280 tmp_queue.push_back(rule);
281
282 return true;
283 }
284
processNext()285 bool PolicyCompiler_junosacl::checkIPv4FragmentService::processNext()
286 {
287 PolicyRule *rule=getNext(); if (rule==NULL) return false;
288 RuleElementSrv *srv = rule->getSrv();
289
290 if (srv->size() > 1)
291 {
292 CustomService *fragment_srv = NULL;
293 for (list<FWObject*>::iterator i1=srv->begin(); i1!=srv->end(); ++i1)
294 {
295 FWObject *o = *i1;
296 FWObject *obj = NULL;
297 if (FWReference::cast(o)!=NULL) obj=FWReference::cast(o)->getPointer();
298 Service *s=Service::cast(obj);
299 assert(s!=NULL);
300
301 CustomService *custom_srv = CustomService::cast(s);
302 if (custom_srv && (!custom_srv->getCodeForPlatform(compiler->myPlatformName()).substr(0, 15).compare("fragment-offset")) ) {
303 if (!fragment_srv) {
304 fragment_srv = custom_srv;
305 } else {
306 if (fragment_srv->getId() != custom_srv->getId())
307 compiler->abort(
308 rule,
309 "You have contradicting IPv4 fragmentation services in the same rule.");
310 }
311 }
312 }
313 }
314
315 tmp_queue.push_back(rule);
316
317 return true;
318 }
319
320
compile()321 void PolicyCompiler_junosacl::compile()
322 {
323 string banner = " Compiling ruleset " + getSourceRuleSet()->getName();
324 if (ipv6) banner += ", IPv6";
325 info(banner);
326
327 string version = fw->getStr("version");
328 bool supports_object_groups = XMLTools::version_compare(version, "12.4")>=0 &&
329 fw->getOptionsObject()->getBool("iosacl_use_object_groups") && ! ipv6;
330 Q_UNUSED(supports_object_groups);
331
332 string vers = fw->getStr("version");
333 string platform = fw->getStr("platform");
334
335 Compiler::compile();
336
337 if ( fw->getOptionsObject()->getBool ("check_shading") &&
338 ! inSingleRuleCompileMode())
339 {
340 add( new Begin("Detecting rule shadowing" ) );
341 add( new printTotalNumberOfRules());
342
343 add( new ItfNegation("process negation in Itf" ) );
344 add( new InterfacePolicyRules(
345 "process interface policy rules and store interface ids"));
346
347 add( new recursiveGroupsInSrc("check for recursive groups in SRC"));
348 add( new recursiveGroupsInDst("check for recursive groups in DST"));
349 add( new recursiveGroupsInSrv("check for recursive groups in SRV"));
350
351 add( new emptyGroupsInSrc( "check for empty groups in SRC" ) );
352 add( new emptyGroupsInDst( "check for empty groups in DST" ) );
353 add( new emptyGroupsInSrv( "check for empty groups in SRV" ) );
354
355 add( new ExpandGroups("expand groups"));
356 add( new dropRuleWithEmptyRE(
357 "drop rules with empty rule elements"));
358 add( new eliminateDuplicatesInSRC("eliminate duplicates in SRC"));
359 add( new eliminateDuplicatesInDST("eliminate duplicates in DST"));
360 add( new eliminateDuplicatesInSRV("eliminate duplicates in SRV"));
361
362 add( new checkIPv4FragmentService("Avoid contradiction IPv4 fragmentation services"));
363
364 //add( new ExpandMultipleAddressesInSrc(
365 // "expand objects with multiple addresses in SRC" ) );
366 //add( new ExpandMultipleAddressesInDst(
367 // "expand objects with multiple addresses in DST" ) );
368 add( new dropRuleWithEmptyRE(
369 "drop rules with empty rule elements"));
370
371 add( new mirrorRule("Add mirrored rules"));
372
373 //add( new ConvertToAtomic("convert to atomic rules" ) );
374
375 add( new checkForObjectsWithErrors(
376 "check if we have objects with errors in rule elements"));
377
378 add( new DetectShadowing("Detect shadowing" ) );
379 add( new simplePrintProgress() );
380
381 runRuleProcessors();
382 deleteRuleProcessors();
383 }
384
385
386 add( new Begin (" Start processing rules" ) );
387 add( new printTotalNumberOfRules ( ) );
388
389
390 add( new singleRuleFilter());
391
392 add( new recursiveGroupsInSrc( "check for recursive groups in SRC" ) );
393 add( new recursiveGroupsInDst( "check for recursive groups in DST" ) );
394 add( new recursiveGroupsInSrv( "check for recursive groups in SRV" ) );
395
396 add( new emptyGroupsInSrc( "check for empty groups in SRC" ) );
397 add( new emptyGroupsInDst( "check for empty groups in DST" ) );
398 add( new emptyGroupsInSrv( "check for empty groups in SRV" ) );
399
400 add( new ExpandGroups ("expand groups" ) );
401 add( new dropRuleWithEmptyRE(
402 "drop rules with empty rule elements"));
403 add( new eliminateDuplicatesInSRC( "eliminate duplicates in SRC" ) );
404 add( new eliminateDuplicatesInDST( "eliminate duplicates in DST" ) );
405 add( new eliminateDuplicatesInSRV( "eliminate duplicates in SRV" ) );
406
407 add( new checkIPv4FragmentService("Avoid contradiction IPv4 fragmentation services"));
408
409 // TODO: fix processMultiAddressObjects
410 // add( new processMultiAddressObjectsInSrc(
411 // "process MultiAddress objects in Src") );
412 // add( new processMultiAddressObjectsInDst(
413 // "process MultiAddress objects in Dst") );
414
415 add( new expandGroupsInItf("expand groups in Interface" ));
416
417 add( new replaceClusterInterfaceInItf(
418 "replace cluster interfaces with member interfaces in the Interface rule element"));
419
420 add( new ItfNegation( "process negation in Itf" ) );
421
422 // TODO: does this function do what we want it to do?
423 add( new InterfacePolicyRules(
424 "process interface policy rules and store interface ids") );
425
426 add( new groupServicesByProtocol ("split rules with different protocols" ) );
427
428 add( new ExpandMultipleAddressesInSrc(
429 "expand objects with multiple addresses in SRC" ) );
430 // add( new MACFiltering ("check for MAC address filtering" ) );
431 //// add( new splitByNetworkZonesForSrc ("split rule if objects in Src belong to different network zones " ) );
432 //// add( new replaceFWinDSTPolicy ("replace fw with its interface in DST in global policy rules") );
433
434 add( new ExpandMultipleAddressesInDst(
435 "expand objects with multiple addresses in DST" ) );
436 // add( new MACFiltering(
437 // "check for MAC address filtering" ) );
438 add( new dropRuleWithEmptyRE("drop rules with empty rule elements"));
439
440 //// add( new splitByNetworkZonesForDst ("split rule if objects in Dst belong to different network zones " ) );
441
442 if (ipv6)
443 add( new DropIPv4Rules("drop ipv4 rules"));
444 else
445 add( new DropIPv6Rules("drop ipv6 rules"));
446 add( new dropRuleWithEmptyRE("drop rules with empty rule elements"));
447
448 add( new checkForUnnumbered("check for unnumbered interfaces"));
449
450 add( new separateSrcAndDstPort("check for services with both src and dst port specified"));
451
452 add( new separateSrcPort("split services with src port specified"));
453
454 // if ( ! supports_object_groups)
455 // add( new addressRanges("process address ranges"));
456
457 // add( new mirrorRule("Add mirrored rules"));
458
459 // add( new dropRuleWithEmptyRE("drop rules with empty rule elements"));
460
461 // add( new setInterfaceAndDirectionBySrc(
462 // "Set interface and direction for rules with interface 'all' using SRC"));
463 // add( new setInterfaceAndDirectionByDst(
464 // "Set interface and direction for rules with interface 'all' using DST"));
465 // add( new setInterfaceAndDirectionIfInterfaceSet(
466 // "Set direction for rules with interface not 'all'"));
467
468 // add( new specialCaseWithDynInterface(
469 // "check for a special cases with dynamic interface" ) );
470
471 // first arg is true because we use "ip access-list" for IOS.
472 // add( new ConvertToAtomic ("convert to atomic rules" ) );
473
474 add( new ValidateInterfaceUnitName("validate interface unit name") );
475
476 add( new pickACL( true, "assign ACLs" ) );
477
478 // add( new SpecialServices( "check for special services" ) );
479 // add( new CheckForUnsupportedUserService("check for user service") );
480
481 // add( new checkForZeroAddr( "check for zero addresses" ) );
482 // add( new checkForDynamicInterface("check for dynamic interfaces" ) );
483
484 // /* remove redundant objects only after all splits has been
485 // * done, right before object groups are created
486 // */
487 // add( new removeRedundantAddressesFromSrc(
488 // "remove redundant addresses from Src") );
489 // add( new removeRedundantAddressesFromDst(
490 // "remove redundant addresses from Dst") );
491
492 // add( new checkForObjectsWithErrors(
493 // "check if we have objects with errors in rule elements"));
494
495 // if (supports_object_groups)
496 // {
497 // // "object-group service" does not seem to support
498 // // matching of tcp flags and "established". Need to
499 // // separate objects using these into separate rules to avoid
500 // // object-group
501
502 // add( new splitTCPServiceWithFlags(
503 // "separate TCP service with tcp flags"));
504
505 // add( new CreateObjectGroupsForSrc("create object groups for Src",
506 // named_objects_manager));
507 // add( new CreateObjectGroupsForDst("create object groups for Dst",
508 // named_objects_manager));
509 // add( new CreateObjectGroupsForSrv("create object groups for Srv",
510 // named_objects_manager));
511 // } else
512 // {
513 // add( new ConvertToAtomic ("convert to atomic rules" ) );
514 // }
515
516 // add( new simplePrintProgress());
517 // add( new createNewCompilerPass("Creating object groups and ACLs"));
518
519 // This processor prints each ACL separately in one block.
520 // It adds comments inside to denote original rules.
521 //
522 add( new PrintCompleteACLs("Print ACLs"));
523 add( new simplePrintProgress());
524
525 runRuleProcessors();
526
527 }
528
printAccessGroupCmd(ciscoACL * acl,bool neg)529 string PolicyCompiler_junosacl::printAccessGroupCmd(ciscoACL *acl, bool neg)
530 {
531 (void) neg; // Unused
532
533 ostringstream str;
534
535 string addr_family_prefix = "inet";
536 if (ipv6) addr_family_prefix = "inet6";
537
538 if (getSourceRuleSet()->isTop())
539 {
540 string dir;
541 if (acl->direction()=="in" || acl->direction()=="Inbound") dir="input";
542 if (acl->direction()=="out" || acl->direction()=="Outbound") dir="output";
543
544 str << "interfaces {\n";
545 str << " " << acl->getInterface()->getParent()->getName() << " {\n";
546 str << " " << acl->getInterface()->getName() << " {\n";
547 str << " family " << addr_family_prefix << " {\n";
548 str << " filter {\n";
549
550 string filter_prefix = fw->getOptionsObject()->getStr("filter_prefix");
551 if (filter_prefix.empty()) filter_prefix = "fwbfilter";
552 filter_prefix += "_";
553
554 str << " " << dir << " " << filter_prefix << acl->workName() << ";\n";
555 str << " }\n";
556 str << " }\n";
557 str << " }\n";
558 str << " }\n";
559 str << "}\n";
560 /*
561
562 str << "interface " << acl->getInterface()->getName() << endl;
563 if (neg) str << " no";
564 str << " " << addr_family_prefix << " ";
565 str << getAccessGroupCommandForAddressFamily(ipv6);
566 str << " " << acl->workName() << " " << dir << endl;
567 str << "exit" << endl;
568 */
569 }
570 return str.str();
571 }
572
epilog()573 void PolicyCompiler_junosacl::epilog()
574 {
575 output << endl;
576
577 // output << "Epilog, acls size: " << acls.size() << endl;
578
579 for (map<string,ciscoACL*>::iterator i=acls.begin(); i!=acls.end(); ++i)
580 {
581 ciscoACL *acl=(*i).second;
582 if (acl->size()!=0) output << printAccessGroupCmd(acl, false);
583 }
584 output << endl;
585
586 if ( fw->getOptionsObject()->getBool("iosacl_regroup_commands") )
587 {
588 info(" Regrouping commands");
589 regroup();
590 }
591 }
592
getAccessGroupCommandForAddressFamily(bool ipv6)593 string PolicyCompiler_junosacl::getAccessGroupCommandForAddressFamily(bool ipv6)
594 {
595 if (ipv6) return "traffic-filter";
596 return "access-group";
597 }
598
printClearCommands()599 string PolicyCompiler_junosacl::printClearCommands()
600 {
601 ostringstream output;
602
603 string version = fw->getStr("version");
604 string platform = fw->getStr("platform");
605
606 string xml_element = "clear_ip_acl";
607 if (ipv6) xml_element = "clear_ipv6_acl";
608
609 string clearACLCmd = Resources::platform_res[platform]->getResourceStr(
610 string("/FWBuilderResources/Target/options/") +
611 "version_" + version + "/iosacl_commands/" + xml_element);
612
613 assert( !clearACLCmd.empty());
614
615 // No need to output "clear" commands in single rule compile mode
616 if ( fw->getOptionsObject()->getBool("iosacl_acl_basic") ||
617 fw->getOptionsObject()->getBool("iosacl_acl_substitution"))
618 {
619 for (map<string,ciscoACL*>::iterator i=acls.begin(); i!=acls.end(); ++i)
620 {
621 ciscoACL *acl = (*i).second;
622 output << clearACLCmd << " " << acl->workName() << endl;
623 }
624 }
625
626 return output.str();
627 }
628
629