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