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 #include "PolicyCompiler_pix.h"
27 #include "PIXObjectGroup.h"
28 #include "NamedObjectsManager.h"
29 #include "PortRangeConverter.h"
30 
31 #include "fwbuilder/Firewall.h"
32 #include "fwbuilder/AddressRange.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/CustomService.h"
39 #include "fwbuilder/Policy.h"
40 #include "fwbuilder/FWOptions.h"
41 #include "fwbuilder/FWObjectDatabase.h"
42 #include "fwbuilder/Interface.h"
43 #include "fwbuilder/IPv4.h"
44 #include "fwbuilder/Network.h"
45 #include "fwbuilder/Management.h"
46 #include "fwbuilder/Resources.h"
47 
48 #include <iostream>
49 #include <iomanip>
50 #include <fstream>
51 #include <sstream>
52 #include <algorithm>
53 #include <functional>
54 
55 #include <assert.h>
56 
57 #include <QStringList>
58 
59 
60 using namespace libfwbuilder;
61 using namespace fwcompiler;
62 using namespace std;
63 
64 
_printAction(PolicyRule * rule)65 string PolicyCompiler_pix::PrintRule::_printAction(PolicyRule *rule)
66 {
67     ostringstream  str;
68 
69     switch (rule->getAction()) {
70     case PolicyRule::Accept:  str << "permit "; break;
71     case PolicyRule::Deny:    str << "deny   "; break;
72     case PolicyRule::Reject:  str << "deny   "; break;
73     default:		      str << rule->getActionAsString() << " ";
74     }
75     return str.str();
76 }
77 
_printACL(PolicyRule * rule)78 string PolicyCompiler_pix::PrintRule::_printACL(PolicyRule *rule)
79 {
80 //    PolicyCompiler_pix *pix_comp=dynamic_cast<PolicyCompiler_pix*>(compiler);
81 
82     string acl_name=rule->getStr("acl");
83     assert (acl_name!="");
84 
85     return acl_name+" ";
86 }
87 
_printLog(PolicyRule * rule)88 string PolicyCompiler_pix::PrintRule::_printLog(PolicyRule *rule)
89 {
90     string platform = compiler->fw->getStr("platform");
91     string vers = compiler->fw->getStr("version");
92     if (platform=="pix" && (vers=="6.1" || vers=="6.2")) return "";
93 
94 //    PolicyCompiler_pix *pix_comp=dynamic_cast<PolicyCompiler_pix*>(compiler);
95     FWOptions  *ruleopt =rule->getOptionsObject();
96     QStringList str;
97 
98     if (ruleopt->getBool("disable_logging_for_this_rule"))
99         return "log disable ";
100 
101     if (rule->getLogging())
102     {
103         string level = ruleopt->getStr("log_level");
104         int    logint = ruleopt->getInt("log_interval");
105 /*
106  *  PIX always adds logging interval in "show * access-list" command,
107  *  so we should always add it, too. Otherwise ACL lines look
108  *  different when diff is generated.
109  */
110         if (logint<=0)
111             logint = Resources::platform_res[platform]->getResourceInt(
112                 string("/FWBuilderResources/Target/options/") +
113                 "version_" + compiler->fw->getStr("version") +
114                 "/pix_default_logint");
115 
116         if (level.empty())
117             level = compiler->fw->getOptionsObject()->getStr(
118                 "pix_logging_trap_level");
119 
120         if (level.empty())
121             level = Resources::platform_res[platform]->getResourceStr(
122                 string("/FWBuilderResources/Target/options/") +
123                 "version_" + compiler->fw->getStr("version") +
124                 "/pix_default_loglevel");
125 
126         if (level=="alert")   level = "1";
127         if (level=="crit")    level = "2";
128         if (level=="error")   level = "3";
129         if (level=="warning") level = "4";
130         if (level=="notice")  level = "5";
131         if (level=="info")    level = "6";
132         if (level=="debug")   level = "7";
133 
134         str << "log" << level.c_str();
135 
136         if (logint>0 || platform=="pix") // can't use "interval 0" on fwsm
137         {
138             str << "interval" << QString().setNum(logint);
139         }
140     }
141     return str.join(" ").toStdString();
142 }
143 
_printPortRangeOp(int rs,int re)144 string PolicyCompiler_pix::PrintRule::_printPortRangeOp(int rs, int re)
145 {
146     return PortRangeConverter(rs, re).toString();
147 }
148 
_printSrcService(Service * srv)149 string PolicyCompiler_pix::PrintRule::_printSrcService(Service *srv)
150 {
151     if (TCPService::isA(srv) || UDPService::isA(srv))
152     {
153 	int rs = TCPUDPService::cast(srv)->getSrcRangeStart();
154 	int re = TCPUDPService::cast(srv)->getSrcRangeEnd();
155         return _printPortRangeOp(rs, re);
156     }
157     return "";
158 }
159 
_printDstService(Service * srv)160 string PolicyCompiler_pix::PrintRule::_printDstService(Service *srv)
161 {
162     ostringstream  str;
163 
164     if (TCPService::isA(srv) || UDPService::isA(srv))
165     {
166 	int rs=TCPUDPService::cast(srv)->getDstRangeStart();
167 	int re=TCPUDPService::cast(srv)->getDstRangeEnd();
168         str <<  _printPortRangeOp(rs, re);
169     }
170 
171     if (ICMPService::isA(srv) && srv->getInt("type")!=-1)
172     {
173         str << srv->getStr("type") << " ";
174     }
175 
176     if (CustomService::isA(srv))
177     {
178 	str << CustomService::cast(srv)->getCodeForPlatform(
179             compiler->myPlatformName() ) << " ";
180     }
181 
182     const IPService *ip_srv = IPService::constcast(srv);
183     if (ip_srv && ip_srv->hasIpOptions())
184         compiler->warning("PIX can not match IP options");
185 
186     return str.str();
187 }
188 
_printAddr(libfwbuilder::Address * o)189 string PolicyCompiler_pix::PrintRule::_printAddr(libfwbuilder::Address  *o)
190 {
191     if (Interface::cast(o)!=NULL)
192     {
193 	Interface *interface_=Interface::cast(o);
194 	if (interface_->isDyn())
195         {
196 	    return string("interface ") + interface_->getLabel() + " ";
197 	}
198     }
199 
200     ostringstream  str;
201 
202     const InetAddr *srcaddr = o->getAddressPtr();
203     if (srcaddr)
204     {
205         InetAddr srcmask = *(o->getNetmaskPtr());
206 
207         if (Interface::cast(o)!=NULL)
208             srcmask = InetAddr(InetAddr::getAllOnes());
209 
210         if (IPv4::cast(o)!=NULL)
211             srcmask = InetAddr(InetAddr::getAllOnes());
212 
213 
214         if (srcaddr->isAny() && srcmask.isAny())
215         {
216             str << "any ";
217         } else {
218             if (srcmask.isHostMask())
219             {
220                 str << "host " << srcaddr->toString() << " ";
221             } else
222             {
223                 str << srcaddr->toString() << " ";
224                 str << srcmask.toString() << " ";
225             }
226         }
227         return str.str();
228     }
229     ostringstream errstr;
230     errstr << "Object "
231            << o->getName()
232            << " (id="
233            << o->getId()
234            << ") "
235            << " has no ip address and can not be used "
236            << "in the rule.";
237     compiler->abort(errstr.str());
238     return ""; // to make compiler happy
239 }
240 
suppressDuplicateICMPCommands(const string & cmd)241 bool PolicyCompiler_pix::PrintRule::suppressDuplicateICMPCommands(const string &cmd)
242 {
243     list<string>::iterator i;
244     i = std::find(seen_icmp_commands.begin(), seen_icmp_commands.end(), cmd);
245     if (i!=seen_icmp_commands.end()) return true;
246     seen_icmp_commands.push_back(cmd);
247     return false;
248 }
249 
_printICMPCommand(PolicyRule * rule)250 string PolicyCompiler_pix::PrintRule::_printICMPCommand(PolicyRule *rule)
251 {
252     ostringstream  str;
253     Address *src = compiler->getFirstSrc(rule);
254     RuleElementSrv *srvrel = rule->getSrv();
255     FWObject *srv = srvrel->front();
256     if (FWReference::cast(srv)!=NULL) srv = FWReference::cast(srv)->getPointer();
257 
258     // Interface *rule_iface =
259     //     Interface::cast(compiler->dbcopy->findInIndex(rule->getInterfaceId()));
260 
261     RuleElementItf *intf_re = rule->getItf();
262     Interface *rule_iface = Interface::cast(
263         FWObjectReference::getObject(intf_re->front()));
264 
265     assert(rule_iface);
266 
267     if (PIXObjectGroup::cast(srv)!=NULL)
268     {
269         for (FWObject::iterator i1=srv->begin(); i1!=srv->end(); ++i1)
270         {
271             ICMPService *s = ICMPService::cast(FWReference::getObject(*i1));
272             assert(s!=NULL);
273 
274             ostringstream  str1;
275             str1 << "icmp ";
276             str1 << _printAction(rule);
277             str1 << _printAddr( src );
278             str1 << s->getStr("type");
279             str1 << " ";
280             str1 << rule_iface->getLabel();
281             str1 << endl;
282 
283             if ( ! suppressDuplicateICMPCommands(str1.str()))  str << str1.str();
284         }
285         return str.str();
286 
287     } else
288     {
289         str << "icmp ";
290         str << _printAction(rule);
291         str << _printAddr( src );
292         str << _printDstService( Service::cast(srv) );
293         str << " ";
294         str << rule_iface->getLabel();
295         str << endl;
296 
297         if ( ! suppressDuplicateICMPCommands(str.str()))  return str.str();
298     }
299 
300     return "";
301 }
302 
_printSSHTelnetCommand(PolicyRule * rule)303 string PolicyCompiler_pix::PrintRule::_printSSHTelnetCommand(PolicyRule *rule)
304 {
305     ostringstream  str;
306     int port;
307 
308     RuleElementSrc *rel = rule->getSrc();
309     Service *srv = compiler->getFirstSrv(rule);
310 
311     RuleElementItf *intf_re = rule->getItf();
312     Interface *rule_iface = Interface::cast(
313         FWObjectReference::getObject(intf_re->front()));
314     assert(rule_iface);
315 
316     port = TCPUDPService::cast(srv)->getDstRangeStart();
317 
318     for (FWObject::iterator i=rel->begin(); i!=rel->end(); ++i)
319     {
320         FWObject *o = FWReference::getObject(*i);
321 
322         if (dynamic_cast<PIXObjectGroup*>(o)!=NULL)
323         {
324             for (FWObject::iterator j=o->begin(); j!=o->end(); ++j)
325             {
326                 Address *a = Address::cast(FWReference::getObject(*j));
327                 assert(a!=NULL);
328                 str << _printSingleSSHTelnetCommand(port, a, rule_iface->getLabel());
329             }
330         } else
331         {
332             Address *a = Address::cast(o);
333             assert(a!=NULL);
334             str << _printSingleSSHTelnetCommand(port, a, rule_iface->getLabel());
335         }
336     }
337 
338     return str.str();
339 }
340 
_printSingleSSHTelnetCommand(int port,Address * a,const string & interfaceLabel)341 string PolicyCompiler_pix::PrintRule::_printSingleSSHTelnetCommand(
342     int port, Address *a, const string &interfaceLabel)
343 {
344     string res;
345 
346     if (port==22) res = "ssh ";
347     if (port==23) res = "telnet ";
348     if (port==80) res = "http ";
349 
350     if (!res.empty())
351     {
352         res += a->getAddressPtr()->toString() + " "
353             + a->getNetmaskPtr()->toString() + " "
354             + interfaceLabel     + "\n";
355     }
356 
357     return res;
358 }
359 
360 /*
361  *   the following additional attributes should have been defined by now:
362  *
363  *   "acl"  - string, name of the access list
364  *            choices are: outside-in, outside-out, inside-in, indside-out,
365  *                         dmz-in, dmz-out etc.
366  *            General rule for the acl name: "iface_name-{in,out}"
367  */
processNext()368 bool PolicyCompiler_pix::PrintRule::processNext()
369 {
370     PolicyCompiler_pix *pix_comp = dynamic_cast<PolicyCompiler_pix*>(compiler);
371     PolicyRule *rule = getNext(); if (rule==NULL) return false;
372 
373     tmp_queue.push_back(rule);
374 
375     ostringstream comment;
376 
377     compiler->output << compiler->printComment(rule, current_rule_label1, "!");
378 
379     if (rule->getBool("icmp_cmd"))
380     {
381         compiler->output << _printICMPCommand(rule);
382 // need to generate access list command as well as icmp command
383 // in order to properly serve icmp  through nat
384 //   04/21/06 --vk
385 //        return true;
386     }
387 
388     if (rule->getBool("tcp_service_to_fw"))
389     {
390         compiler->output << _printSSHTelnetCommand(rule);
391         return true;
392     }
393 
394     /*
395      * all three rule elements contain exactly one object, which can
396      * be either group (in case processor CreateObjectGroups created
397      * object group for it) or a regular object
398      */
399     RuleElementSrc *src = rule->getSrc();
400     RuleElementDst *dst = rule->getDst();
401     RuleElementSrv *srv = rule->getSrv();
402 
403     assert(src->size()==1);
404     assert(dst->size()==1);
405     assert(srv->size()==1);
406 
407     FWObject *srcobj = FWReference::getObject(src->front());
408     FWObject *dstobj = FWReference::getObject(dst->front());
409     FWObject *srvobj = FWReference::getObject(srv->front());
410 
411     assert(srcobj);
412     assert(dstobj);
413     assert(srvobj);
414 
415     ostringstream  aclstr;
416 
417     string acl_name = rule->getStr("acl");
418     assert(acl_name!="");
419 
420     ciscoACL *acl = pix_comp->acls[acl_name];
421     assert(acl!=NULL);
422 
423     if (compiler->fw->getOptionsObject()->getBool("pix_use_acl_remarks"))
424     {
425         compiler->output << acl->addRemark(rule->getLabel(), rule->getComment());
426     }
427 
428     /*
429      * Assemble ACL command in aclstr
430      */
431 
432     aclstr << _printAction(rule);
433 
434     /*
435      * processor groupServicesByProtocol guaranties that rule has
436      * services of the same type (that is, the same protocol, like all
437      * tcp, all udp, all icmp or all IP with the same protocol
438      * number). PIX can use object-group for protocol only if protocol
439      * numbers are different and these are not icmp/tcp/udp
440      * protocols. This means that because of processor
441      * groupServicesByProtocol we never use object-group in protocol
442      * part of ACL.
443      */
444 
445     PIXObjectGroup *pgsrv = PIXObjectGroup::cast(srvobj);
446     PIXObjectGroup *pgsrc = PIXObjectGroup::cast(srcobj);
447     PIXObjectGroup *pgdst = PIXObjectGroup::cast(dstobj);
448     Service *srv_s = Service::cast(srvobj);
449     assert(pgsrv!=NULL || srv_s!=NULL);
450 
451     if ( pgsrv!=NULL && pgsrv->isServiceGroup())
452     {
453         aclstr << pgsrv->getSrvTypeName();
454     } else
455         aclstr << srv_s->getProtocolName();
456 
457     aclstr << " ";
458 
459     NamedObject* asa8_object;
460 
461     asa8_object = pix_comp->named_objects_manager->getNamedObject(srcobj);
462     if (asa8_object)
463     {
464         aclstr << "object " << asa8_object->getCommandWord().toStdString() << " ";
465     } else
466     {
467         if (pgsrc!=NULL)
468         {
469             aclstr << "object-group " << srcobj->getName() << " ";
470         } else
471         {
472             aclstr << _printAddr(Address::cast(srcobj));
473         }
474     }
475 
476     if ( pgsrv==NULL )
477         aclstr << _printSrcService( compiler->getFirstSrv(rule) );
478 
479     asa8_object = pix_comp->named_objects_manager->getNamedObject(dstobj);
480     if (asa8_object)
481     {
482         aclstr << "object "  << asa8_object->getCommandWord().toStdString() << " ";
483     } else
484     {
485         if (pgdst!=NULL)
486         {
487             aclstr << "object-group " << dstobj->getName() << " ";
488         } else
489         {
490             aclstr << _printAddr(Address::cast(dstobj));
491         }
492     }
493 
494     if (pgsrv!=NULL)
495     {
496         aclstr << "object-group " << srvobj->getName() << " ";
497     } else
498     {
499         aclstr << _printDstService(Service::cast(srvobj));
500     }
501 
502     aclstr << _printLog( rule );
503 
504 //    aclstr << endl;
505 
506     compiler->output << acl->addLine(aclstr.str());
507 
508     return true;
509 }
510 
511