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