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