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