1 /*
2 
3                           Firewall Builder
4 
5                  Copyright (C) 2002 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 
27 #include "config.h"
28 
29 #include "PolicyCompiler_cisco.h"
30 #include "NamedObjectsManager.h"
31 
32 #include "fwbuilder/FWObjectDatabase.h"
33 #include "fwbuilder/RuleElement.h"
34 #include "fwbuilder/IPService.h"
35 #include "fwbuilder/ICMPService.h"
36 #include "fwbuilder/TCPService.h"
37 #include "fwbuilder/UDPService.h"
38 #include "fwbuilder/Network.h"
39 #include "fwbuilder/Policy.h"
40 #include "fwbuilder/Interface.h"
41 #include "fwbuilder/Management.h"
42 #include "fwbuilder/Resources.h"
43 #include "fwbuilder/AddressTable.h"
44 #include "fwbuilder/Cluster.h"
45 
46 #include <algorithm>
47 #include <assert.h>
48 
49 using namespace libfwbuilder;
50 using namespace fwcompiler;
51 using namespace std;
52 
53 
54 /*
55  * Call this rule processor after splitIfSrcMatchesFw and
56  * splitIfDstMatchesFw to make sure that if firewall or its interface
57  * or address is in src or dst, it is the only object there.
58  */
processNext()59 bool PolicyCompiler_cisco::setInterfaceAndDirectionBySrc::processNext()
60 {
61     PolicyRule *rule=getNext(); if (rule==NULL) return false;
62     Helper helper(compiler);
63 
64     list<int> intf_id_list;
65 
66     RuleElementItf *intf_re = rule->getItf();
67 
68     if (intf_re->isAny())
69     {
70         bool cluster_member =
71             compiler->fw->getOptionsObject()->getBool("cluster_member");
72 
73         Cluster *cluster = NULL;
74         if (cluster_member)
75             cluster = Cluster::cast(
76                 compiler->dbcopy->findInIndex(
77                     compiler->fw->getInt("parent_cluster_id")));
78 
79         RuleElementSrc *srcre = rule->getSrc();
80         RuleElementDst *dstre = rule->getDst();
81         Address *srcobj = compiler->getFirstSrc(rule);
82 
83         if (rule->getDirection()==PolicyRule::Both &&
84             ! compiler->complexMatch(srcobj, compiler->fw) &&
85             ! compiler->complexMatch(srcobj, cluster))
86         {
87             intf_id_list = helper.findInterfaceByNetzoneOrAll( srcre );
88         }
89 
90         if (rule->getDirection()==PolicyRule::Inbound)
91             intf_id_list = helper.getAllInterfaceIDs();
92 
93         for (list<int>::iterator i = intf_id_list.begin();
94              i!=intf_id_list.end(); ++i)
95         {
96             int intf_id = *i;
97             Interface *ifs = Interface::cast(rule->getRoot()->findInIndex(intf_id));
98             assert(ifs);
99             if (ifs->isUnprotected()) continue;   // skip!
100             if (ifs->isLoopback()) continue;   // skip!
101 
102             PolicyRule *new_rule = compiler->dbcopy->createPolicyRule();
103             compiler->temp_ruleset->add(new_rule);
104             new_rule->duplicate(rule);
105 //            new_rule->setInterfaceId(intf_id);
106 
107             RuleElementItf *itf_re = new_rule->getItf(); assert(itf_re!=NULL);
108             itf_re->reset();
109             itf_re->addRef(ifs);
110 
111             new_rule->setDirection(PolicyRule::Inbound);
112             new_rule->setBool("interface_and_direction_set_from_src",true);
113             tmp_queue.push_back(new_rule);
114         }
115         // If dst does not match firewall, preserve original rule as
116         // well to let setInterfaceAndDirectionByDst work on it.
117         //
118         // Note for #1298
119         //
120         // if address in dst is multicast, it can be forwarded and so
121         // we need to preserve the rule. But broadcasts are not
122         // forwarded so we should consider them as matching the fw.
123         //
124         FWObject *d = dstre->front();
125         if (FWReference::cast(d)!=NULL) d = FWReference::cast(d)->getPointer();
126         if (!compiler->complexMatch(Address::cast(d), compiler->fw, true, false))
127             tmp_queue.push_back(rule);
128         return true;
129     }
130 
131     tmp_queue.push_back(rule);
132     return true;
133 }
134 
processNext()135 bool PolicyCompiler_cisco::setInterfaceAndDirectionByDst::processNext()
136 {
137     PolicyRule *rule=getNext(); if (rule==NULL) return false;
138     Helper helper(compiler);
139 
140     if (rule->getBool("interface_and_direction_set_from_src"))
141     {
142         tmp_queue.push_back(rule);
143         return true;
144     }
145 
146     RuleElementItf *intf_re = rule->getItf();
147 
148     list<int> intf_id_list;
149 
150     if (intf_re->isAny())
151     {
152         bool cluster_member =
153             compiler->fw->getOptionsObject()->getBool("cluster_member");
154 
155         Cluster *cluster = NULL;
156         if (cluster_member)
157             cluster = Cluster::cast(
158                 compiler->dbcopy->findInIndex(
159                     compiler->fw->getInt("parent_cluster_id")));
160 
161         RuleElementDst *dstre = rule->getDst();
162         Address *dstobj = compiler->getFirstDst(rule);
163 
164         if (rule->getDirection()==PolicyRule::Both &&
165             ! compiler->complexMatch(dstobj, compiler->fw) &&
166             ! compiler->complexMatch(dstobj, cluster))
167         {
168             intf_id_list = helper.findInterfaceByNetzoneOrAll( dstre );
169         }
170 
171         if (rule->getDirection()==PolicyRule::Outbound)
172             intf_id_list = helper.getAllInterfaceIDs();
173 
174         for (list<int>::iterator i = intf_id_list.begin();
175              i!=intf_id_list.end(); ++i)
176         {
177             int intf_id = *i;
178             Interface *ifs = Interface::cast(rule->getRoot()->findInIndex(intf_id));
179             assert(ifs);
180             if (ifs->isUnprotected()) continue;   // skip!
181             if (ifs->isLoopback()) continue;   // skip!
182 
183             PolicyRule *new_rule = compiler->dbcopy->createPolicyRule();
184             compiler->temp_ruleset->add(new_rule);
185             new_rule->duplicate(rule);
186 
187 //            new_rule->setInterfaceId(intf_id);
188 
189             RuleElementItf *itf_re = new_rule->getItf(); assert(itf_re!=NULL);
190             itf_re->reset();
191             itf_re->addRef(ifs);
192 
193             new_rule->setDirection(PolicyRule::Outbound);
194             new_rule->setBool("interface_and_direction_set_from_dst",true);
195             tmp_queue.push_back(new_rule);
196         }
197         return true;
198     }
199     tmp_queue.push_back(rule);
200     return true;
201 }
202 
processNext()203 bool PolicyCompiler_cisco::setInterfaceAndDirectionIfInterfaceSet::processNext()
204 {
205     PolicyRule *rule=getNext(); if (rule==NULL) return false;
206 
207     //RuleElementItf *itfre=rule->getItf();
208 
209     RuleElementItf *intf_re = rule->getItf();
210 
211     if (intf_re->isAny() ||
212         rule->getBool("interface_and_direction_set_from_src") ||
213         rule->getBool("interface_and_direction_set_from_dst"))
214     {
215         tmp_queue.push_back(rule);
216         return true;
217     }
218 
219     PolicyRule *new_rule;
220 
221     if ( ! intf_re->isAny())
222     {
223         FWObject *rule_iface = FWObjectReference::getObject(intf_re->front());
224         RuleElementItf *itf_re;
225 
226         if (rule->getDirection()==PolicyRule::Both)
227         {
228             new_rule =compiler->dbcopy->createPolicyRule();
229             compiler->temp_ruleset->add(new_rule);
230             new_rule->duplicate(rule);
231 
232 //            new_rule->setInterfaceId( rule_iface_id );
233             itf_re = new_rule->getItf(); assert(itf_re!=NULL);
234             itf_re->reset();
235             itf_re->addRef(rule_iface);
236 
237             new_rule->setDirection(PolicyRule::Inbound);
238             new_rule->setBool("interface_and_direction_set",true);
239             tmp_queue.push_back(new_rule);
240 
241             new_rule =compiler->dbcopy->createPolicyRule();
242             compiler->temp_ruleset->add(new_rule);
243             new_rule->duplicate(rule);
244 
245 //            new_rule->setInterfaceId( rule_iface_id );
246             itf_re = new_rule->getItf(); assert(itf_re!=NULL);
247             itf_re->reset();
248             itf_re->addRef(rule_iface);
249 
250             new_rule->setDirection(PolicyRule::Outbound);
251             new_rule->setBool("interface_and_direction_set",true);
252             tmp_queue.push_back(new_rule);
253         } else
254         {
255             new_rule =compiler->dbcopy->createPolicyRule();
256             compiler->temp_ruleset->add(new_rule);
257             new_rule->duplicate(rule);
258 
259 //            new_rule->setInterfaceId( rule_iface_id );
260             itf_re = new_rule->getItf(); assert(itf_re!=NULL);
261             itf_re->reset();
262             itf_re->addRef(rule_iface);
263 
264             // direction is copied from the original rule
265             new_rule->setBool("interface_and_direction_set",true);
266             tmp_queue.push_back(new_rule);
267         }
268     }
269     return true;
270 }
271 
processNext()272 bool PolicyCompiler_cisco::pickACL::processNext()
273 {
274     PolicyCompiler_cisco *cisco_comp = dynamic_cast<PolicyCompiler_cisco*>(
275         compiler);
276     PolicyRule *rule = getNext(); if (rule==NULL) return false;
277 
278 //    Interface  *rule_iface = Interface::cast(compiler->dbcopy->findInIndex(
279 //                                                 rule->getInterfaceId()));
280 
281     RuleElementItf *intf_re = rule->getItf();
282     Interface *rule_iface = Interface::cast(
283         FWObjectReference::getObject(intf_re->front()));
284 
285     if(rule_iface==NULL)
286     {
287         compiler->abort(rule, "Missing interface assignment");
288         return true;
289     }
290 
291     /*
292      * option 'Generate outbound access lists' is called
293      * 'pix_generate_out_acl' for PIX and 'generate_out_acl' for
294      * IOS. Need to check the right one depending on what platform
295      * this compiler is for. Class PolicyCompiler_cisco is base class
296      * and can be used for both.
297      */
298 
299     /*
300      * TODO: Here we hardcode this option to True for IOS. Instead of
301      * doing it here, just set option "generate_out_acl" to true in
302      * PolicyCompiler_iosacl::prolog(). It is done that way in
303      * PolicyCompiler_procurveacl already. This way, base class
304      * PolicyCompiler_cisco does not need to be aware of the actual
305      * platform.
306      */
307 
308     bool generate_out_acl = false;
309 
310     if (compiler->myPlatformName()=="pix")
311         generate_out_acl = compiler->fw->getOptionsObject()->
312             getBool("pix_generate_out_acl");
313     else
314     {
315         if (compiler->myPlatformName()=="iosacl")
316             generate_out_acl = true;
317         else
318             generate_out_acl = compiler->fw->getOptionsObject()->
319                 getBool("generate_out_acl");
320     }
321 
322     if (rule->getDirection() == PolicyRule::Outbound && !generate_out_acl)
323     {
324         compiler->abort(
325                 rule,
326                 "Rule with direction 'Outbound' requires outbound ACL "
327                 "but option 'Generate outbound access lists' is OFF.");
328         return true;
329     }
330 
331     /* The choice of the ACL name depends on whether this is a named
332      * acl or not. If not, should use unique numbers. Also need to
333      * pass this flag to the ciscoACL object.
334      */
335     string acl_name = rule_iface->getLabel();
336     if (acl_name.empty()) acl_name = rule_iface->getName();
337     acl_name = cisco_comp->mangleInterfaceName(acl_name);
338     string dir = "in";
339     if (rule->getDirection() == PolicyRule::Inbound)
340     {
341         acl_name += "_in"; dir="in";
342     }
343     if (rule->getDirection() == PolicyRule::Outbound)
344     {
345         acl_name += "_out"; dir="out";
346     }
347 
348     // if this is not the "main" rule set, use its name for the acl name
349     if (!compiler->getSourceRuleSet()->isTop())
350         acl_name = compiler->getSourceRuleSet()->getName() + "_" + acl_name;
351 
352     if (cisco_comp->ipv6) acl_name = "ipv6_" + acl_name;
353 
354     rule->setStr("acl",acl_name);
355 
356     ciscoACL *acl = cisco_comp->createACLObject(acl_name, rule_iface, dir, using_named_acl);
357     cisco_comp->acls[acl_name] = acl;
358 
359     acl->setWorkName(acl_name);
360 
361     tmp_queue.push_back(rule);
362     return true;
363 }
364 
365 /*
366  * Take interface name as an argument and produce
367  * shortened, space-free string that uniquely identifies interface
368  * in a human-readable way.
369  */
370 
mangleInterfaceName(const string & interface_name)371 std::string PolicyCompiler_cisco::mangleInterfaceName(
372     const string &interface_name)
373 {
374     string::size_type n;
375     string s = interface_name;
376 
377     // lowercase all characters
378     transform (s.begin(), s.end(),    // source
379                s.begin(),             // destination
380                ::tolower);              // operation
381 
382     map<string,string> name_mapping;
383     map<string,string>::iterator nmi;
384 
385     name_mapping["async"] = "as";
386     name_mapping["atm"] = "atm";
387     name_mapping["bri"] = "bri";
388     name_mapping["ethernet"] = "e";
389     name_mapping["fastethernet"] = "fe";
390     name_mapping["fddi"] = "fddi";
391     name_mapping["gigabitethernet"] = "ge";
392     name_mapping["hssi"] = "h";
393     name_mapping["loopback"] = "l";
394     name_mapping["port-channel"] = "pc";
395     name_mapping["pos"] = "pos";
396     name_mapping["serial"] = "s";
397     name_mapping["switch"] = "sw";
398     name_mapping["tokenring"] = "tr";
399     name_mapping["tunnel"] = "tun";
400     name_mapping["tengigabitethernet"] = "te";
401     name_mapping["sonet"] = "so";
402     name_mapping["vg-anylan"] = "vg";
403 
404     for (nmi=name_mapping.begin(); nmi!=name_mapping.end(); nmi++)
405     {
406         if (s.find( nmi->first )==0)
407         {
408             s.replace(0, nmi->first.size(), nmi->second);
409             break;
410         }
411     }
412 
413     while ( (n=s.find(" "))!=string::npos)
414     {
415         s.replace(n,1,"_");
416     }
417     while ( (n=s.find("/"))!=string::npos)
418     {
419         s.replace(n,1,"_");
420     }
421     return s;
422 }
423 
424