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
34 #include <assert.h>
35 #include <cstring>
36 #include <iomanip>
37 #include <memory>
38
39 #include "CompilerDriver_ipf.h"
40
41 #include "PolicyCompiler_ipf.h"
42 #include "NATCompiler_ipf.h"
43 #include "AutomaticRules_pf.h"
44
45 #include "OSConfigurator_openbsd.h"
46 #include "OSConfigurator_freebsd.h"
47 #include "OSConfigurator_solaris.h"
48
49 #include "fwbuilder/Cluster.h"
50 #include "fwbuilder/ClusterGroup.h"
51 #include "fwbuilder/FWException.h"
52 #include "fwbuilder/FWObjectDatabase.h"
53 #include "fwbuilder/FailoverClusterGroup.h"
54 #include "fwbuilder/Firewall.h"
55 #include "fwbuilder/Interface.h"
56 #include "fwbuilder/Library.h"
57 #include "fwbuilder/NAT.h"
58 #include "fwbuilder/Policy.h"
59 #include "fwbuilder/Resources.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 #include <QtDebug>
70
71
72 using namespace std;
73 using namespace libfwbuilder;
74 using namespace fwcompiler;
75
76
composeActivationCommand(libfwbuilder::Firewall * fw,bool filter,const std::string & debug,const std::string &,const std::string & remote_file)77 QString CompilerDriver_ipf::composeActivationCommand(libfwbuilder::Firewall *fw,
78 bool filter,
79 const std::string &debug,
80 const std::string &,
81 const std::string &remote_file)
82 {
83 Configlet act(fw, "ipf", "activation");
84 act.removeComments();
85 act.collapseEmptyStrings(true);
86 act.setVariable("dyn_addr", fw->getOptionsObject()->getBool("dynAddr"));
87 act.setVariable("not_dyn_addr", !fw->getOptionsObject()->getBool("dynAddr"));
88 act.setVariable("filter", filter);
89 act.setVariable("nat", !filter);
90 act.setVariable("ipf_debug", debug.c_str());
91 act.setVariable("remote_file", remote_file.c_str());
92 act.setVariable("interface_name_substitution_commands",
93 printActivationCommandWithSubstitution(fw));
94 return act.expand();
95 }
96
assembleManifest(Cluster *,Firewall * fw,bool)97 QString CompilerDriver_ipf::assembleManifest(Cluster*, Firewall* fw, bool )
98 {
99 (void) fw; // Unused
100 QString remote_name = remote_file_names[FW_FILE];
101 QString remote_ipf_name = remote_file_names[CONF1_FILE];
102 QString remote_nat_name = remote_file_names[CONF2_FILE];
103
104 QString script_buffer;
105 QTextStream script(&script_buffer, QIODevice::WriteOnly);
106
107 script << MANIFEST_MARKER
108 << "* "
109 << this->escapeFileName(file_names[FW_FILE]);
110
111 if (remote_name != file_names[FW_FILE])
112 script << " " << this->escapeFileName(remote_name);
113 script << endl;
114
115 if (have_filter)
116 {
117 script << MANIFEST_MARKER
118 << " "
119 << this->escapeFileName(file_names[CONF1_FILE]);
120
121 if (remote_ipf_name != file_names[CONF1_FILE])
122 script << " " << this->escapeFileName(remote_ipf_name);
123 script << endl;
124 }
125
126 if (have_nat)
127 {
128 script << MANIFEST_MARKER
129 << " "
130 << this->escapeFileName(file_names[CONF2_FILE]);
131
132 if (remote_nat_name != file_names[CONF2_FILE])
133 script << " " << this->escapeFileName(remote_nat_name);
134 script << endl;
135 }
136
137 return script_buffer;
138 }
139
assembleFwScript(Cluster * cluster,Firewall * fw,bool cluster_member,OSConfigurator * oscnf)140 QString CompilerDriver_ipf::assembleFwScript(Cluster *cluster,
141 Firewall* fw,
142 bool cluster_member,
143 OSConfigurator *oscnf)
144 {
145 Configlet script_skeleton(fw, "ipf", "script_skeleton");
146 Configlet top_comment(fw, "ipf", "top_comment");
147
148 assembleFwScriptInternal(
149 cluster, fw, cluster_member, oscnf,
150 &script_skeleton, &top_comment, "#", true);
151 return script_skeleton.expand();
152 }
153
printActivationCommands(libfwbuilder::Firewall *)154 QString CompilerDriver_ipf::printActivationCommands(libfwbuilder::Firewall*)
155 {
156 return activation_commands.join("\n");
157 }
158
run(const std::string & cluster_id,const std::string & firewall_id,const std::string & single_rule_id)159 QString CompilerDriver_ipf::run(const std::string &cluster_id,
160 const std::string &firewall_id,
161 const std::string &single_rule_id)
162 {
163 Cluster *cluster = NULL;
164 Firewall *fw = NULL;
165
166 getFirewallAndClusterObjects(cluster_id, firewall_id, &cluster, &fw);
167
168 try
169 {
170 clearReadOnly(fw);
171
172 // Copy rules from the cluster object
173 populateClusterElements(cluster, fw);
174
175 commonChecks2(cluster, fw);
176
177 FWOptions* options = fw->getOptionsObject();
178 string fw_version = fw->getStr("version");
179
180 // Note that fwobjectname may be different from the name of the
181 // firewall fw This happens when we compile a member of a cluster
182 current_firewall_name = fw->getName().c_str();
183
184 string s;
185
186 bool debug = options->getBool("debug");
187 string ipf_dbg = (debug)?"-v":"";
188
189 std::auto_ptr<Preprocessor> prep(new Preprocessor(objdb , fw, false));
190 prep->compile();
191
192 /*
193 * Process firewall options, build OS network configuration script
194 */
195 std::auto_ptr<OSConfigurator_bsd> oscnf;
196 string host_os = fw->getStr("host_OS");
197 string family=Resources::os_res[host_os]->Resources::getResourceStr("/FWBuilderResources/Target/family");
198 if ( host_os == "solaris" )
199 oscnf = std::auto_ptr<OSConfigurator_bsd>(new OSConfigurator_solaris(objdb , fw, false));
200
201 if ( host_os == "openbsd")
202 oscnf = std::auto_ptr<OSConfigurator_bsd>(new OSConfigurator_openbsd(objdb , fw, false));
203
204 if ( host_os == "freebsd")
205 oscnf = std::auto_ptr<OSConfigurator_bsd>(new OSConfigurator_freebsd(objdb , fw, false));
206
207 if (oscnf.get()==NULL)
208 {
209 abort("Unrecognized host OS " + host_os + " (family " + family + ")");
210 return "";
211 }
212
213 oscnf->prolog();
214
215 list<FWObject*> all_policies = fw->getByType(Policy::TYPENAME);
216 list<FWObject*> all_nat = fw->getByType(NAT::TYPENAME);
217
218 try
219 {
220 AutomaticRules_pf auto_rules(fw, persistent_objects);
221 auto_rules.addSshAccessRule();
222 auto_rules.addCarpRules();
223 auto_rules.addPfsyncRules();
224 auto_rules.addFallbackRule();
225 } catch (FWException &ex)
226 {
227 abort(ex.toString());
228 }
229
230 PolicyCompiler_ipf c(objdb , fw, false , oscnf.get() );
231
232 FWObject *policy = all_policies.front();
233
234 c.setSourceRuleSet(Policy::cast(policy));
235 c.setRuleSetName(policy->getName());
236 c.setPersistentObjects(persistent_objects);
237
238 c.setSingleRuleCompileMode(single_rule_id);
239 c.setDebugLevel( dl );
240 if (rule_debug_on) c.setDebugRule( drp );
241 c.setVerbose( verbose );
242 if (inTestMode()) c.setTestMode();
243 if (inEmbeddedMode()) c.setEmbeddedMode();
244
245 if ( c.prolog() > 0 )
246 {
247 have_filter = true;
248 c.compile();
249 c.epilog();
250 }
251
252 NATCompiler_ipf n( objdb , fw, false , oscnf.get() );
253
254 FWObject *nat = all_nat.front();
255
256 n.setSourceRuleSet(NAT::cast(nat));
257 n.setRuleSetName(nat->getName());
258 n.setPersistentObjects(persistent_objects);
259
260 n.setSingleRuleCompileMode(single_rule_id);
261 n.setDebugLevel( dl );
262 if (rule_debug_on) n.setDebugRule( drn );
263 n.setVerbose( verbose );
264 if (inTestMode()) n.setTestMode();
265 if (inEmbeddedMode()) n.setEmbeddedMode();
266
267 if ( n.prolog() > 0 )
268 {
269 have_nat = true;
270 n.compile();
271 n.epilog();
272 }
273
274 /*
275 * compilers detach persistent objects when they finish, this
276 * means at this point library persistent_objects is not part
277 * of any object tree.
278 */
279 objdb->reparent(persistent_objects);
280
281 if (haveErrorsAndWarnings())
282 {
283 all_errors.push_front(getErrors("").c_str());
284 }
285
286 if (single_rule_compile_on)
287 {
288 // in single rule compile mode just return the result
289 ostringstream ostr;
290
291 if (have_filter)
292 {
293 if (c.haveErrorsAndWarnings())
294 {
295 all_errors.push_back(c.getErrors("").c_str());
296 }
297 ostr << c.getCompiledScript();
298 }
299
300 if (have_nat)
301 {
302 if (n.haveErrorsAndWarnings())
303 {
304 all_errors.push_back(n.getErrors("").c_str());
305 }
306 ostr << n.getCompiledScript();
307 }
308
309 return formSingleRuleCompileOutput(
310 QString::fromUtf8(ostr.str().c_str()));
311 }
312
313 determineOutputFileNames(cluster, fw, !cluster_id.empty(),
314 QStringList() << "" << "ipf" << "nat",
315 QStringList() << "fw" << "conf" << "conf",
316 QStringList() << "script_name_on_firewall"
317 << "ipf_conf_file_name_on_firewall"
318 << "nat_conf_file_name_on_firewall");
319
320 QString remote_ipf_name = remote_file_names[CONF1_FILE];
321 QString remote_nat_name = remote_file_names[CONF2_FILE];
322
323 if (have_filter)
324 {
325 QString output_file = getAbsOutputFileName(file_names[CONF1_FILE]);
326
327 info("Output file name: " + output_file.toStdString());
328 QFile ipf_file(output_file);
329 if (ipf_file.open(QIODevice::WriteOnly))
330 {
331 QTextStream ipf_str(&ipf_file);
332 if (c.haveErrorsAndWarnings())
333 {
334 all_errors.push_back(c.getErrors("").c_str());
335 ipf_str << "# Policy compiler errors and warnings:"
336 << endl;
337 ipf_str << QString::fromUtf8(c.getErrors("# ").c_str());
338 }
339 ipf_str << QString::fromUtf8(c.getCompiledScript().c_str());
340 ipf_file.close();
341 ipf_file.setPermissions(QFile::ReadOwner | QFile::WriteOwner |
342 QFile::ReadGroup | QFile::ReadOther |
343 QFile::ExeOwner |
344 QFile::ExeGroup |
345 QFile::ExeOther );
346 } else
347 {
348 QString err(" Failed to open file %1 for writing: %2; "
349 "Current dir: %3");
350 abort(err.arg(ipf_file.fileName())
351 .arg(ipf_file.error())
352 .arg(QDir::current().path()).toStdString());
353 }
354
355 QString remote_file_name = escapeFileName(remote_ipf_name);
356 activation_commands.push_back(
357 composeActivationCommand(
358 fw, true, ipf_dbg, fw_version,
359 remote_file_name.toUtf8().constData()));
360 }
361
362 if (have_nat)
363 {
364 QString output_file = getAbsOutputFileName(file_names[CONF2_FILE]);
365
366 info("Output file name: " + output_file.toStdString());
367 QFile nat_file(output_file);
368 if (nat_file.open(QIODevice::WriteOnly))
369 {
370 QTextStream nat_str(&nat_file);
371 if (n.haveErrorsAndWarnings())
372 {
373 all_errors.push_back(n.getErrors("").c_str());
374 nat_str << "# NAT compiler errors and warnings:"
375 << endl;
376 nat_str << QString::fromUtf8(n.getErrors("# ").c_str());
377 }
378 nat_str << QString::fromUtf8(n.getCompiledScript().c_str());
379 nat_file.close();
380 nat_file.setPermissions(QFile::ReadOwner | QFile::WriteOwner |
381 QFile::ReadGroup | QFile::ReadOther |
382 QFile::ExeOwner |
383 QFile::ExeGroup |
384 QFile::ExeOther );
385 } else
386 {
387 QString err(" Failed to open file %1 for writing: %2; "
388 "Current dir: %3");
389 abort(err.arg(nat_file.fileName())
390 .arg(nat_file.error())
391 .arg(QDir::current().path()).toStdString());
392 }
393
394 QString remote_file_name = escapeFileName(remote_nat_name);
395 activation_commands.push_back(
396 composeActivationCommand(
397 fw, false, ipf_dbg, fw_version,
398 remote_file_name.toUtf8().constData()));
399 }
400 /*
401 * assemble the script and then perhaps post-process it if needed
402 */
403 QString script_buffer = assembleFwScript(
404 cluster, fw, !cluster_id.empty(), oscnf.get());
405
406
407 file_names[FW_FILE] = getAbsOutputFileName(file_names[FW_FILE]);
408
409 info("Output file name: " + file_names[FW_FILE].toStdString());
410 QFile fw_file(file_names[FW_FILE]);
411 if (fw_file.open(QIODevice::WriteOnly))
412 {
413 QTextStream fw_str(&fw_file);
414 fw_str << script_buffer;
415 fw_file.close();
416 fw_file.setPermissions(QFile::ReadOwner | QFile::WriteOwner |
417 QFile::ReadGroup | QFile::ReadOther |
418 QFile::ExeOwner |
419 QFile::ExeGroup |
420 QFile::ExeOther );
421
422 info(" Compiled successfully");
423 } else
424 {
425 QString err(" Failed to open file %1 for writing: %2; Current dir: %3");
426 abort(err.arg(fw_file.fileName())
427 .arg(fw_file.error()).arg(QDir::current().path()).toStdString());
428 }
429
430 if (!all_errors.isEmpty())
431 status = BaseCompiler::FWCOMPILER_WARNING;
432
433 }
434 catch (FWException &ex)
435 {
436 status = BaseCompiler::FWCOMPILER_ERROR;
437 return QString::fromUtf8(ex.toString().c_str());
438 }
439
440
441 return "";
442 }
443
444
445