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 "Helper.h"
28
29 #include <fwbuilder/Interface.h>
30 #include <fwbuilder/ObjectGroup.h>
31 #include <fwbuilder/InetAddr.h>
32 #include <fwbuilder/FWObjectDatabase.h>
33 #include <fwbuilder/RuleElement.h>
34 #include <fwbuilder/Rule.h>
35 #include "fwbuilder/Resources.h"
36 #include <fwbuilder/IPv6.h>
37 #include <fwbuilder/NetworkIPv6.h>
38 #include <fwbuilder/Network.h>
39 #include <fwbuilder/InetAddrMask.h>
40
41 #include <assert.h>
42 #include <limits.h>
43 #include <iostream>
44
45 #include <QObject>
46 #include <QString>
47
48 using namespace libfwbuilder;
49 using namespace fwcompiler;
50 using namespace std;
51
52
53 // #define DEBUG_NETZONE_OPS 1
54
calculateDimension(FWObject * obj)55 static unsigned long calculateDimension(FWObject* obj)
56 {
57 if (Group::cast(obj)!=NULL)
58 {
59 unsigned long res=0;
60 for (FWObject::iterator i1=obj->begin(); i1!=obj->end(); ++i1)
61 {
62 unsigned long n=calculateDimension( *i1 );
63 if (n==LONG_MAX) return n;
64 if (LONG_MAX-res<n) return LONG_MAX; // prevent overflow
65 res+=n;
66 }
67 return res;
68 } else
69 {
70 Address *a=Address::cast(obj);
71 if (a!=NULL)
72 {
73 if (a->isAny()) return LONG_MAX;
74 return a->dimension();
75 }
76 }
77 return 0;
78 }
79
expand_group_recursive(FWObject * o,list<FWObject * > & ol)80 void Helper::expand_group_recursive(FWObject *o,list<FWObject*> &ol)
81 {
82 if (Group::cast( o )!=NULL)
83 {
84 for (FWObject::iterator i2=o->begin(); i2!=o->end(); ++i2)
85 {
86 FWObject *o1= *i2;
87 if (FWReference::cast(o1)!=NULL) o1=FWReference::cast(o1)->getPointer();
88 assert(o1);
89
90 expand_group_recursive(o1,ol);
91 }
92 } else {
93 ol.push_back( o );
94 }
95 }
96
findInterfaceByAddress(Address * obj)97 int Helper::findInterfaceByAddress(Address *obj)
98 {
99 return findInterfaceByAddress(obj->getAddressPtr(), obj->getNetmaskPtr());
100 }
101
102 /*
103 * ticket #1293
104 * Weird corner case: user made a mistake setting netmask of an
105 * intrface to 0.0.0.0, which made this interface match any address.
106 * At the same time, this interface was marked as "unprotected". So,
107 * if we get an interface from helper.findInterfaceByNetzoneOrAll()
108 * but this interface is unprotected, issue a warning and use all
109 * interfaces instead.
110 */
111
findInterfaceByAddress(const InetAddr * addr,const InetAddr * nm)112 int Helper::findInterfaceByAddress(const InetAddr *addr,
113 const InetAddr *nm)
114 {
115 if (addr==NULL) return -1;
116
117 #if DEBUG_NETZONE_OPS
118 cerr << "Helper::findInterfaceByAddress";
119 cerr << " addr=" << addr->toString();
120 cerr << " nm=" << nm->toString();
121 cerr << endl;
122 #endif
123
124 Firewall *fw = compiler->fw;
125 list<FWObject*> l2 = fw->getByTypeDeep(Interface::TYPENAME);
126 for (list<FWObject*>::iterator i=l2.begin(); i!=l2.end(); ++i)
127 {
128 Interface *iface = Interface::cast(*i);
129 if (iface->isDedicatedFailover()) continue;
130 if (iface->isUnprotected()) continue;
131
132 #if DEBUG_NETZONE_OPS
133 cerr << "Helper::findInterfaceByAddress";
134 cerr << " intf=" << iface->getName();
135 cerr << endl;
136 #endif
137
138 FWObjectTypedChildIterator j =
139 iface->findByType((addr->isV4())?IPv4::TYPENAME:IPv6::TYPENAME);
140 for (; j!=j.end(); ++j)
141 {
142 const Address *i_addr = Address::constcast(*j);
143
144 #if DEBUG_NETZONE_OPS
145 cerr << "Helper::findInterfaceByAddress";
146 cerr << " i_addr=" << i_addr->getName();
147 cerr << endl;
148 cerr << " " << i_addr->getAddressPtr()->toString();
149 cerr << " " << i_addr->getNetmaskPtr()->toString();
150 cerr << endl;
151 #endif
152
153 if (nm != NULL)
154 {
155 InetAddrMask interface_subnet(*(i_addr->getAddressPtr()),
156 *(i_addr->getNetmaskPtr()));
157 InetAddrMask other_subnet(*addr, *nm);
158
159 #if DEBUG_NETZONE_OPS
160 cerr << "Helper::findInterfaceByAddress";
161 cerr << " addr=" << other_subnet.toString();
162 cerr << " intf=" << iface->getName()
163 << " " << interface_subnet.toString();
164 cerr << endl;
165 #endif
166
167 vector<InetAddrMask> ovr =
168 libfwbuilder::getOverlap(interface_subnet, other_subnet);
169
170 #if DEBUG_NETZONE_OPS
171 cerr << "Helper::findInterfaceByAddress";
172 cerr << " overlap:";
173 cerr << " ovr.size()=" << ovr.size();
174 if (ovr.size() > 0)
175 cerr << " ovr.front()=" << ovr.front().toString();
176 cerr << endl;
177 #endif
178 if (ovr.size()==0) continue;
179
180 // if interface_subnet is equal or wider than other_subnet,
181 // getOverlap() returns subnet object equal to other_subnet
182 // If other_subnet is wider, returned object is equal
183 // to interface_subnet. If they intersect but one does not fit
184 // completely in the other, returned object is not equal
185 // to either.
186 if (ovr.front() == other_subnet)
187 {
188 return iface->getId();
189 }
190 } else
191 {
192 if ( i_addr->belongs(*addr) ) return iface->getId();
193 }
194 }
195 }
196 return -1;
197 }
198
findInterfaceByNetzone(Address * obj)199 int Helper::findInterfaceByNetzone(Address *obj)
200 {
201 if (IPv4::isA(obj))
202 {
203 InetAddr host_netmask("255.255.255.255");
204 return findInterfaceByNetzone(obj->getAddressPtr(), &host_netmask);
205 } else
206 return findInterfaceByNetzone(obj->getAddressPtr(), obj->getNetmaskPtr());
207 }
208
209 /**
210 * finds interface of the firewall associated with the netzone
211 * that object 'obj' belongs to. Returns interface ID
212 *
213 */
findInterfaceByNetzone(const InetAddr * addr,const InetAddr * nm)214 int Helper::findInterfaceByNetzone(const InetAddr *addr, const InetAddr *nm)
215 throw(FWException)
216 {
217 #if DEBUG_NETZONE_OPS
218 cerr << "Helper::findInterfaceByNetzone";
219 cerr << " matching to";
220 cerr << " addr=" << addr;
221 if (addr) cerr << " " << addr->toString();
222 cerr << " nm=" << nm;
223 if (nm) cerr << " " << nm->toString();
224 cerr << endl;
225 #endif
226
227 Firewall *fw = compiler->fw;
228 map<int,FWObject*> zones;
229 list<FWObject*> l2 = fw->getByTypeDeep(Interface::TYPENAME);
230 for (list<FWObject*>::iterator i=l2.begin(); i!=l2.end(); ++i)
231 {
232 Interface *iface = Interface::cast(*i);
233 if (iface->isDedicatedFailover()) continue;
234 if (iface->isUnprotected()) continue;
235
236 // NOTE: "network_zone" is globally unique string ID
237 int netzone_id =
238 FWObjectDatabase::getIntId(iface->getStr("network_zone"));
239
240 if (netzone_id != -1)
241 {
242 FWObject *netzone = fw->getRoot()->findInIndex(netzone_id);
243 list<FWObject*> nz;
244 expand_group_recursive(netzone, nz);
245
246 #if DEBUG_NETZONE_OPS
247 cerr << "Helper::findInterfaceByNetzone";
248 cerr << " netzone_id=" << netzone_id
249 << " " << iface->getStr("network_zone")
250 << " " << netzone->getName()
251 << endl;
252 #endif
253
254 for (list<FWObject*>::iterator j=nz.begin(); j!=nz.end(); ++j)
255 {
256 Address *netzone_addr = Address::cast(*j);
257
258 if (netzone_addr == NULL) continue;
259
260 #if DEBUG_NETZONE_OPS
261 cerr << "Helper::findInterfaceByNetzone";
262 cerr << " " << netzone_addr->getName()
263 << " " << netzone_addr->getAddressPtr()->toString()
264 << endl;
265 #endif
266
267 // if addr==NULL, return id of the interfacce that has
268 // net_zone=="any"
269 if (addr==NULL)
270 {
271 if (netzone_addr->getId()==FWObjectDatabase::ANY_ADDRESS_ID)
272 return iface->getId(); // id of the interface
273 } else
274 {
275 // see SF bug 3213019
276 // skip ipv6 addresses in network zone group
277 if (netzone_addr->getAddressPtr()->addressFamily() !=
278 addr->addressFamily()) continue;
279
280 const InetAddr *nz_addr = netzone_addr->getAddressPtr();
281 const InetAddr *nz_netm = netzone_addr->getNetmaskPtr();
282 if (nm != NULL && nz_netm != NULL)
283 {
284 InetAddrMask nz_subnet(*nz_addr, *nz_netm);
285 InetAddrMask other_subnet(*addr, *nm);
286 vector<InetAddrMask> ovr =
287 libfwbuilder::getOverlap(nz_subnet,
288 other_subnet);
289 #if DEBUG_NETZONE_OPS
290 cerr << "Helper::findInterfaceByNetzone";
291 cerr << " addr=" << other_subnet.toString();
292 cerr << " nz=" << nz_subnet.toString();
293 cerr << " overlap:";
294 cerr << " ovr.size()=" << ovr.size();
295 if (ovr.size() > 0)
296 cerr << " ovr.front()=" << ovr.front().toString();
297 cerr << endl;
298 #endif
299 if (ovr.size()==0) continue;
300 // if nz_subnet is equal or wider than other_subnet,
301 // getOverlap() returns subnet object equal to other_subnet
302 // If other_subnet is wider, returned object is equal
303 // to nz_subnet. If they intersect but one does not fit
304 // completely in the other, returned object is not equal
305 // to either.
306 if (ovr.front() == other_subnet)
307 {
308 zones[iface->getId()] = netzone_addr;
309 #if DEBUG_NETZONE_OPS
310 cerr << "Helper::findInterfaceByNetzone";
311 cerr << " match" << endl;
312 #endif
313 }
314 } else
315 {
316 if (netzone_addr->belongs(*addr))
317 {
318 zones[iface->getId()] = netzone_addr;
319
320 #if DEBUG_NETZONE_OPS
321 cerr << "Helper::findInterfaceByNetzone";
322 cerr << " match" << endl;
323 #endif
324 }
325 }
326 }
327 }
328 }
329 }
330
331 /*
332 * now compare dimensions of all netzones that contain address obj and
333 * pick the one with smallest dimension
334 */
335 int res_id = -1;
336 unsigned long res_dim = LONG_MAX;
337 for (map<int,FWObject*>::iterator i=zones.begin(); i!=zones.end(); ++i)
338 {
339 int iface_id = (*i).first;
340 FWObject *netzone = (*i).second;
341 unsigned long dim = calculateDimension(netzone);
342
343 #if DEBUG_NETZONE_OPS
344 cerr << "Helper::findInterfaceByNetzone";
345 cerr << " netzone=" << netzone->getName()
346 << " dim=" << dim
347 << " res_dim=" << res_dim
348 << endl;
349 #endif
350
351 if (dim<=res_dim)
352 {
353 res_id = iface_id;
354 res_dim = dim;
355 }
356 }
357
358 #if DEBUG_NETZONE_OPS
359 cerr << "Helper::findInterfaceByNetzone";
360 cerr << " Result after scanning network zones: " << res_id << endl;
361 #endif
362
363 /*
364 * Subnets defined by addresses of interfaces are automatically part
365 * of the corresponding network zones
366 */
367 if (res_id == -1)
368 res_id = findInterfaceByAddress(addr, nm);
369
370 if (res_id == -1)
371 {
372 QString err = QObject::tr("Can not find interface with network zone "
373 "that includes address '%1%2'");
374 throw(FWException(err
375 .arg((addr)?addr->toString().c_str():"NULL")
376 .arg((nm)?QString("/%1").arg(nm->toString().c_str()):"")
377 .toStdString()));
378 }
379
380 #if DEBUG_NETZONE_OPS
381 cerr << "Helper::findInterfaceByNetzone";
382 cerr << " returning " << res_id << endl;
383 #endif
384
385 return res_id;
386 }
387
getAllInterfaceIDs()388 list<int> Helper::getAllInterfaceIDs()
389 {
390 Firewall *fw = compiler->fw;
391 list<int> intf_id_list;
392 FWObjectTypedChildIterator i=fw->findByType(Interface::TYPENAME);
393 for ( ; i!=i.end(); ++i)
394 {
395 Interface *ifs = Interface::cast(*i);
396 assert(ifs);
397 if (ifs->isUnprotected()) continue; // skip!
398 intf_id_list.push_back( (*i)->getId() );
399 }
400 return intf_id_list;
401 }
402
findInterfaceByNetzoneOrAll(RuleElement * re)403 list<int> Helper::findInterfaceByNetzoneOrAll(RuleElement *re)
404 {
405 list<int> intf_id_list;
406 if (re->isAny())
407 {
408 return getAllInterfaceIDs();
409 } else
410 {
411 FWObject *fo = re->front();
412 if (FWReference::cast(fo)!=NULL) fo=FWReference::cast(fo)->getPointer();
413 Address *a = Address::cast(fo);
414 if (a==NULL)
415 {
416 Rule *rule = Rule::cast(re->getParent());
417 Q_UNUSED(rule);
418 compiler->abort(
419 re->getParent(),
420 string("findInterfaceByNetzoneOrAll failed to retrieve first "
421 "object from the rule element; is argument not of "
422 "the type RuleElementSrc or RuleElementDst ?"));
423 return intf_id_list;
424 }
425
426 try
427 {
428 int intf_id = findInterfaceByNetzone(a);
429 intf_id_list.push_back(intf_id);
430 } catch(FWException &ex)
431 {
432 // could not find interface with netzone to match address 'a'
433 // will assign rule to all interfaces. Act as if all interfaces
434 // had network zone 'any' and each matches this address.
435
436 // issue warning only if platform uses netwrk zones.
437
438 bool supports_network_zones =
439 Resources::getTargetCapabilityBool(
440 compiler->fw->getStr("platform"), "network_zones");
441
442 if (supports_network_zones)
443 compiler->warning(ex.toString());
444
445 FWObjectTypedChildIterator i = compiler->fw->findByType(
446 Interface::TYPENAME);
447 for ( ; i!=i.end(); ++i)
448 {
449 Interface *ifs = Interface::cast(*i);
450 intf_id_list.push_back( ifs->getId() );
451 }
452 }
453 }
454 return intf_id_list;
455 }
456