1 /*
2 
3                           Firewall Builder
4 
5                  Copyright (C) 2009 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 "../../config.h"
27 
28 #include <fstream>
29 #include <iostream>
30 #include <algorithm>
31 #include <functional>
32 #include <stdexcept>
33 #include <memory>
34 
35 #include <assert.h>
36 #include <cstring>
37 #include <iomanip>
38 
39 #include "CompilerDriver_nxosacl.h"
40 #include "AutomaticRules_nxosacl.h"
41 
42 #include "PolicyCompiler_nxosacl.h"
43 #include "RoutingCompiler_nxosacl.h"
44 #include "OSConfigurator_nxos.h"
45 #include "NamedObjectsAndGroupsSupport.h"
46 #include "NamedObjectsManagerNXOS.h"
47 
48 #include "fwbuilder/Cluster.h"
49 #include "fwbuilder/ClusterGroup.h"
50 #include "fwbuilder/FWException.h"
51 #include "fwbuilder/FWObjectDatabase.h"
52 #include "fwbuilder/FailoverClusterGroup.h"
53 #include "fwbuilder/Firewall.h"
54 #include "fwbuilder/Interface.h"
55 #include "fwbuilder/Library.h"
56 #include "fwbuilder/NAT.h"
57 #include "fwbuilder/Policy.h"
58 #include "fwbuilder/Resources.h"
59 #include "fwbuilder/Routing.h"
60 #include "fwbuilder/StateSyncClusterGroup.h"
61 #include "fwbuilder/XMLTools.h"
62 
63 #include "fwcompiler/Preprocessor.h"
64 
65 #include <QStringList>
66 #include <QFileInfo>
67 #include <QFile>
68 #include <QTextStream>
69 
70 
71 
72 using namespace std;
73 using namespace libfwbuilder;
74 using namespace fwcompiler;
75 
76 
assembleManifest(Cluster *,Firewall *,bool)77 QString CompilerDriver_nxosacl::assembleManifest(Cluster *, Firewall* , bool )
78 {
79     QString script_buffer;
80     QTextStream script(&script_buffer, QIODevice::WriteOnly);
81 
82     script << "!" << MANIFEST_MARKER
83            << "* " << this->escapeFileName(file_names[FW_FILE]) << endl;
84     return script_buffer;
85 }
86 
printActivationCommands(Firewall *)87 QString CompilerDriver_nxosacl::printActivationCommands(Firewall*)
88 {
89     return "";
90 }
91 
assembleFwScript(Cluster * cluster,Firewall * fw,bool cluster_member,OSConfigurator * oscnf)92 QString CompilerDriver_nxosacl::assembleFwScript(Cluster *cluster,
93                                                 Firewall *fw,
94                                                 bool cluster_member,
95                                                 OSConfigurator *oscnf)
96 {
97     Configlet script_skeleton(fw, "cisco", "script_skeleton");
98     Configlet top_comment(fw, "cisco", "top_comment");
99 
100     script_skeleton.setVariable("system_configuration_script",
101                                 QString::fromUtf8(system_configuration_script.c_str()));
102     script_skeleton.setVariable("policy_script",
103                                 QString::fromUtf8(policy_script.c_str()));
104     script_skeleton.setVariable("nat_script",
105                                 QString::fromUtf8(nat_script.c_str()));
106     script_skeleton.setVariable("routing_script",
107                                 QString::fromUtf8(routing_script.c_str()));
108 
109     FWOptions* options = fw->getOptionsObject();
110     options->setStr("prolog_script", options->getStr("nxosacl_prolog_script"));
111     options->setStr("epilog_script", options->getStr("nxosacl_epilog_script"));
112 
113     // we do not offer user a choice of the place where to put prolog
114     // lines, therefore we can reset this attribute to make sure it
115     // does not interfere
116     options->setStr("prolog_place", "");
117 
118     assembleFwScriptInternal(cluster, fw, cluster_member,
119                              oscnf, &script_skeleton, &top_comment, "!", true);
120     return script_skeleton.expand();
121 }
122 
run(const std::string & cluster_id,const std::string & firewall_id,const std::string & single_rule_id)123 QString CompilerDriver_nxosacl::run(const std::string &cluster_id,
124                                    const std::string &firewall_id,
125                                    const std::string &single_rule_id)
126 {
127     Cluster *cluster = NULL;
128     Firewall *fw = NULL;
129 
130     getFirewallAndClusterObjects(cluster_id, firewall_id, &cluster, &fw);
131 
132     try
133     {
134         clearReadOnly(fw);
135 
136         // Copy rules from the cluster object
137         populateClusterElements(cluster, fw);
138 
139         commonChecks2(cluster, fw);
140 
141         // Note that fwobjectname may be different from the name of the
142         // firewall fw This happens when we compile a member of a cluster
143         current_firewall_name = fw->getName().c_str();
144 
145         determineOutputFileNames(cluster, fw, !cluster_id.empty(),
146                                  QStringList(""), QStringList("fw"),
147                                  QStringList(""));
148 
149         /* Now that all checks are done, we can drop copies of cluster
150          * interfaces that were added to the firewall by
151          * CompilerDriver::populateClusterElements()
152          */
153         list<FWObject*> all_interfaces = fw->getByTypeDeep(Interface::TYPENAME);
154         list<FWObject*> copies_of_cluster_interfaces;
155         for (std::list<FWObject*>::iterator i=all_interfaces.begin(); i!=all_interfaces.end(); ++i)
156         {
157             Interface *iface = Interface::cast(*i);
158             assert(iface);
159 
160             if (iface->getOptionsObject()->getBool("cluster_interface"))
161                 copies_of_cluster_interfaces.push_back(iface);
162         }
163         while (copies_of_cluster_interfaces.size())
164         {
165             fw->remove(copies_of_cluster_interfaces.front());
166             copies_of_cluster_interfaces.pop_front();
167         }
168 
169 
170         FWOptions* options = fw->getOptionsObject();
171 
172         string fwvers = fw->getStr("version");
173         if (fwvers == "") fw->setStr("version", "12.1");
174         if (fwvers == "12.x") fw->setStr("version", "12.1");
175 
176         string platform = fw->getStr("platform");
177         string clearACLCmd = Resources::platform_res[platform]->getResourceStr(
178             string("/FWBuilderResources/Target/options/") +
179             "version_" + fwvers + "/nxosacl_commands/clear_ip_acl");
180         if (clearACLCmd.empty())
181         {
182             // incorrect version. This could have happened if user converted
183             // firewall platform. See bug #2662290
184             fw->setStr("version", "12.1");
185         }
186 
187         bool nxos_acl_basic = options->getBool("nxos_acl_basic");
188         bool nxos_acl_no_clear = options->getBool("nxos_acl_no_clear");
189         bool nxos_acl_substitution = options->getBool("nxos_acl_substitution");
190         bool nxos_add_clear_statements = options->getBool("nxos_add_clear_statements");
191 
192         if ( !nxos_acl_basic &&
193              !nxos_acl_no_clear &&
194              !nxos_acl_substitution )
195         {
196             if ( nxos_add_clear_statements ) options->setBool("nxos_acl_basic",true);
197             else options->setBool("nxos_acl_no_clear",true);
198         }
199 
200         std::auto_ptr<OSConfigurator_nxos> oscnf(new OSConfigurator_nxos(objdb, fw, false));
201 
202         oscnf->prolog();
203         oscnf->processFirewallOptions();
204 
205         list<FWObject*> all_policies = fw->getByType(Policy::TYPENAME);
206 
207         try
208         {
209             AutomaticRules_nxosacl auto_rules(fw, persistent_objects);
210             auto_rules.addSshAccessRule();
211         } catch (FWException &ex)
212         {
213             abort(ex.toString());
214         }
215 
216         // assign unique rule ids that later will be used to generate
217         // chain names.  This should be done after calls to
218         // findImportedRuleSets()
219         // NB: these ids are not used by this compiler
220 
221         assignUniqueRuleIds(all_policies);
222 
223         vector<int> ipv4_6_runs;
224 
225         if (!single_rule_compile_on)
226             system_configuration_script = safetyNetInstall(fw);
227 
228         NamedObjectsManagerNXOS named_objects_manager(persistent_objects, fw);
229 
230         // command line options -4 and -6 control address family for which
231         // script will be generated. If "-4" is used, only ipv4 part will
232         // be generated. If "-6" is used, only ipv6 part will be generated.
233         // If neither is used, both parts will be done.
234 
235         if (options->getStr("ipv4_6_order").empty() ||
236             options->getStr("ipv4_6_order") == "ipv4_first")
237         {
238             if (ipv4_run) ipv4_6_runs.push_back(AF_INET);
239             if (ipv6_run) ipv4_6_runs.push_back(AF_INET6);
240         }
241 
242         if (options->getStr("ipv4_6_order") == "ipv6_first")
243         {
244             if (ipv6_run) ipv4_6_runs.push_back(AF_INET6);
245             if (ipv4_run) ipv4_6_runs.push_back(AF_INET);
246         }
247 
248         string clear_commands;
249         string object_groups_definitions;
250 
251         for (vector<int>::iterator i=ipv4_6_runs.begin();
252              i!=ipv4_6_runs.end(); ++i)
253         {
254             int policy_af = *i;
255             bool ipv6_policy = (policy_af == AF_INET6);
256 
257             // Count rules for each address family
258             int policy_count = 0;
259 
260             for (list<FWObject*>::iterator p=all_policies.begin();
261                  p!=all_policies.end(); ++p)
262             {
263                 Policy *policy = Policy::cast(*p);
264                 if (policy->matchingAddressFamily(policy_af)) policy_count++;
265             }
266             if (policy_count)
267             {
268                 std::auto_ptr<Preprocessor> prep(new Preprocessor(objdb, fw, false));
269                 if (inTestMode()) prep->setTestMode();
270                 if (inEmbeddedMode()) prep->setEmbeddedMode();
271                 prep->compile();
272             }
273 
274             for (list<FWObject*>::iterator p=all_policies.begin();
275                  p!=all_policies.end(); ++p )
276             {
277                 Policy *policy = Policy::cast(*p);
278 
279                 if (!policy->matchingAddressFamily(policy_af)) continue;
280 
281                 PolicyCompiler_nxosacl c(objdb, fw, ipv6_policy, oscnf.get());
282 
283                 c.setNamedObjectsManager(&named_objects_manager);
284                 c.setSourceRuleSet( policy );
285                 c.setRuleSetName(policy->getName());
286                 c.setPersistentObjects(persistent_objects);
287 
288                 c.setSingleRuleCompileMode(single_rule_id);
289                 if (inTestMode()) c.setTestMode();
290                 if (inEmbeddedMode()) c.setEmbeddedMode();
291                 c.setDebugLevel( dl );
292                 if (rule_debug_on) c.setDebugRule(  drp );
293                 c.setVerbose( verbose );
294 
295                 if ( c.prolog() > 0 )
296                 {
297                     c.compile();
298                     c.epilog();
299 
300                     if (!single_rule_compile_on)
301                     {
302                         if (ipv6_policy)
303                         {
304                             policy_script += "\n\n";
305                             policy_script += "! ================ IPv6\n";
306                             policy_script += "\n\n";
307                         } else
308                         {
309                             policy_script += "\n\n";
310                             policy_script += "! ================ IPv4\n";
311                             policy_script += "\n\n";
312                         }
313                     }
314 
315                     if (c.haveErrorsAndWarnings())
316                     {
317                         all_errors.push_back(c.getErrors("").c_str());
318                     }
319                     policy_script +=  c.getCompiledScript();
320                     clear_commands += c.printClearCommands();
321                     //named_objects_manager.saveObjectGroups();
322 
323                 } else
324                     info(" Nothing to compile in Policy");
325             }
326 
327             if (!ipv6_policy)
328             {
329                 list<FWObject*> all_routing = fw->getByType(Routing::TYPENAME);
330                 RuleSet *routing = RuleSet::cast(all_routing.front());
331 
332                 // currently routing is supported only for ipv4
333                 RoutingCompiler_nxosacl r(objdb, fw, false, oscnf.get());
334 
335                 r.setNamedObjectsManager(&named_objects_manager);
336                 r.setSourceRuleSet(routing);
337                 r.setRuleSetName(routing->getName());
338                 r.setPersistentObjects(persistent_objects);
339 
340                 r.setSingleRuleCompileMode(single_rule_id);
341                 if (inTestMode()) r.setTestMode();
342                 if (inEmbeddedMode()) r.setEmbeddedMode();
343                 r.setDebugLevel( dl );
344                 if (rule_debug_on) r.setDebugRule(  drp );
345                 r.setVerbose( verbose );
346 
347                 if ( r.prolog() > 0 )
348                 {
349                     r.compile();
350                     r.epilog();
351 
352                     if (r.haveErrorsAndWarnings())
353                     {
354                         all_errors.push_back(r.getErrors("").c_str());
355                     }
356 
357                     routing_script += r.getCompiledScript();
358                 } else
359                     info(" Nothing to compile in Routing");
360             }
361         }
362 
363         /*
364          * compilers detach persistent objects when they finish, this
365          * means at this point library persistent_objects is not part
366          * of any object tree.
367          */
368         objdb->reparent(persistent_objects);
369 
370         if (haveErrorsAndWarnings())
371         {
372             all_errors.push_front(getErrors("").c_str());
373         }
374 
375         object_groups_definitions +=
376             named_objects_manager.getNamedObjectsDefinitions();
377 
378         if (single_rule_compile_on)
379         {
380             return formSingleRuleCompileOutput(
381                 QString::fromUtf8(
382                     (object_groups_definitions +
383                      policy_script + routing_script).c_str()));
384         }
385 
386         if ( fw->getOptionsObject()->getBool("nxosacl_acl_basic") ||
387              fw->getOptionsObject()->getBool("nxosacl_acl_substitution"))
388         {
389             clear_commands += named_objects_manager.getClearCommands() + "\n";
390         }
391 
392         system_configuration_script += clear_commands;
393         system_configuration_script += object_groups_definitions;
394 
395         QString script_buffer = assembleFwScript(
396             cluster, fw, !cluster_id.empty(), oscnf.get());
397 
398         QString ofname = getAbsOutputFileName(file_names[FW_FILE]);
399 
400         info("Output file name: " + ofname.toStdString());
401         QFile fw_file(ofname);
402         if (fw_file.open(QIODevice::WriteOnly))
403         {
404             QTextStream fw_str(&fw_file);
405             fw_str << script_buffer;
406             fw_file.close();
407             fw_file.setPermissions(QFile::ReadOwner | QFile::WriteOwner |
408                                    QFile::ReadGroup | QFile::ReadOther |
409                                    QFile::ExeOwner |
410                                    QFile::ExeGroup |
411                                    QFile::ExeOther );
412 
413             info(" Compiled successfully");
414         } else
415         {
416             QString err(" Failed to open file %1 for writing: %2; Current dir: %3");
417             abort(err.arg(fw_file.fileName())
418                   .arg(fw_file.error()).arg(QDir::current().path()).toStdString());
419         }
420         if (!all_errors.isEmpty())
421             status = BaseCompiler::FWCOMPILER_WARNING;
422     }
423     catch (FWException &ex)
424     {
425         status = BaseCompiler::FWCOMPILER_ERROR;
426         return QString::fromUtf8(ex.toString().c_str());
427     }
428 
429     return "";
430 }
431 
432 
433