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 <string>
36 #include <cstring>
37 #include <iomanip>
38 
39 #include "fwbuilder/FWOptions.h"
40 #include "fwbuilder/Firewall.h"
41 #include "fwbuilder/Interface.h"
42 
43 #include "CompilerDriver_pf.h"
44 
45 #include <QStringList>
46 #include <QFileInfo>
47 #include <QDir>
48 #include <QtDebug>
49 
50 
51 using namespace std;
52 using namespace libfwbuilder;
53 using namespace fwcompiler;
54 
55 
CompilerDriver_pf(FWObjectDatabase * db)56 CompilerDriver_pf::CompilerDriver_pf(FWObjectDatabase *db) :
57     CompilerDriver(db)
58 {
59     have_nat = false;
60     have_filter = false;
61 }
62 
63 // create a copy of itself, including objdb
clone()64 CompilerDriver* CompilerDriver_pf::clone()
65 {
66     CompilerDriver_pf* new_cd = new CompilerDriver_pf(objdb);
67     if (inEmbeddedMode()) new_cd->setEmbeddedMode();
68     return new_cd;
69 }
70 
71 /*
72  * Generate file name for the ruleset .conf file using general conf
73  * file name as a prototype.
74  */
getConfFileNameForRuleset(const QString & ruleset_name,const QString & conf_file_name,const QString & ext)75 QString CompilerDriver_pf::getConfFileNameForRuleset(const QString &ruleset_name,
76                                                      const QString &conf_file_name,
77                                                      const QString &ext)
78 {
79     assert(!conf_file_name.isEmpty());
80 
81     if (ruleset_name == "__main__") return conf_file_name;
82 
83     QString suffix = QString("-") + ruleset_name;
84 
85     QFileInfo fi(conf_file_name);
86     QString path = fi.path();
87 
88     // qDebug() << "getConfFileNameForRuleset:"
89     //          << "conf_file_name=" << conf_file_name
90     //          << "path=" << path;
91 
92     QString using_suffix = fi.completeSuffix();
93     if (!ext.isEmpty()) using_suffix = ext;
94 
95     QString new_name = fi.completeBaseName() + suffix + "." + using_suffix;
96     if (!path.isEmpty() && path != ".") new_name = path + "/" + new_name;
97     return new_name;
98 }
99 
getRemoteConfFileName(const QString & ruleset_name,const QString & local_conf_name,const QString & remote_fw_name,const QString & remote_conf_name)100 QString CompilerDriver_pf::getRemoteConfFileName(const QString &ruleset_name,
101                                                  const QString &local_conf_name,
102                                                  const QString &remote_fw_name,
103                                                  const QString &remote_conf_name)
104 {
105     QString conf_file_name;
106     QString suffix = QString("-") + ruleset_name;
107     if (ruleset_name == "__main__") suffix = "";
108 
109     if (remote_conf_name.isEmpty() && remote_fw_name.isEmpty())
110     {
111         // local_conf_name may be a relative or absolute path. Return
112         // just the file name
113         QFileInfo fi(local_conf_name);
114         return fi.fileName();
115     }
116 
117     QFileInfo fi;
118 
119     if (!remote_conf_name.isEmpty()) fi = QFileInfo(remote_conf_name);
120     else
121         if (!remote_fw_name.isEmpty()) fi = QFileInfo(remote_fw_name);
122 
123     QString new_name = fi.completeBaseName() + suffix + ".conf";
124     QString path = fi.path();
125 
126     if (path == ".") return new_name;
127     else return path + "/" + new_name;
128 }
129 
printTimeout(FWOptions * options,const string & OnOffOption,const string & ValOption,const string & pfCode)130 string CompilerDriver_pf::printTimeout(FWOptions* options,
131                                        const string &OnOffOption,
132                                        const string &ValOption,
133                                        const string &pfCode)
134 {
135     std::ostringstream res;
136     if (options->getBool(OnOffOption) && options->getInt(ValOption)>0)
137     {
138         res << "set timeout "
139             << pfCode << " " << options->getInt(ValOption) << endl;
140     }
141     return res.str();
142 }
143 
printProlog(QTextStream & file,const string & prolog_code)144 void CompilerDriver_pf::printProlog(QTextStream &file, const string &prolog_code)
145 {
146     file << endl;
147     file << "#" << endl;
148     file << "# Prolog script" << endl;
149     file << "#" << endl;
150     file << prolog_code << endl;
151     file << "#" << endl;
152     file << "# End of prolog script" << endl;
153     file << "#" << endl;
154 }
155 
printStaticOptions(QTextStream & file,Firewall * fw)156 void CompilerDriver_pf::printStaticOptions(QTextStream &file, Firewall* fw)
157 {
158     FWOptions* options = fw->getOptionsObject();
159     list<FWObject*> all_interfaces=fw->getByType(Interface::TYPENAME);
160     string prolog_place = options->getStr("prolog_place");
161     if (prolog_place.empty()) prolog_place = "fw_file";  // old default
162     string pre_hook = options->getStr("prolog_script");
163 
164     if (prolog_place == "pf_file_top")
165         printProlog(file, pre_hook);
166 
167     file << endl;
168 
169     string set_debug = options->getStr("pf_set_debug");
170     if (!set_debug.empty())
171     {
172         file << "set debug " << set_debug << endl;
173     }
174 
175     string state_policy = options->getStr("pf_state_policy");
176     if (!state_policy.empty())
177     {
178         file << "set state-policy " << state_policy << endl;
179     }
180 
181     string block_policy = options->getStr("pf_block_policy");
182     if (!block_policy.empty())
183     {
184         file << "set block-policy " << block_policy << endl;
185     }
186 
187     QStringList limits;
188     if (options->getBool("pf_do_limit_frags") &&
189         options->getInt("pf_limit_frags")>0 )
190         limits.push_back(QString("frags ") +
191                          options->getStr("pf_limit_frags").c_str());
192 
193     if (options->getBool("pf_do_limit_states") &&
194         options->getInt("pf_limit_states")>0 )
195         limits.push_back(QString("states ") +
196                          options->getStr("pf_limit_states").c_str());
197 
198     if (options->getBool("pf_do_limit_src_nodes") &&
199         options->getInt("pf_limit_src_nodes")>0 )
200         limits.push_back(QString("src-nodes ") +
201                          options->getStr("pf_limit_src_nodes").c_str());
202 
203     if (options->getBool("pf_do_limit_tables") &&
204         options->getInt("pf_limit_tables")>0 )
205         limits.push_back(QString("tables ") +
206                          options->getStr("pf_limit_tables").c_str());
207 
208     if (options->getBool("pf_do_limit_table_entries") &&
209         options->getInt("pf_limit_table_entries")>0 )
210         limits.push_back(QString("table-entries ") +
211                          options->getStr("pf_limit_table_entries").c_str());
212 
213     if (limits.size() > 0)
214     {
215         file << "set limit ";
216         if (limits.size() > 1 ) file << "{ ";
217         file << limits.join(", ");
218         if (limits.size() > 1 ) file << " }";
219         file << endl;
220     }
221 
222     if ( ! options->getStr("pf_optimization").empty() )
223         file << "set optimization "
224              << options->getStr("pf_optimization") << endl;
225 
226     file << printTimeout(options,
227                          "pf_do_timeout_interval","pf_timeout_interval",
228                          "interval");
229     file << printTimeout(options,
230                          "pf_do_timeout_frag","pf_timeout_frag",
231                          "frag");
232 
233     file << printTimeout(options,
234                          "pf_set_tcp_first","pf_tcp_first",
235                          "tcp.first" );
236     file << printTimeout(options,
237                          "pf_set_tcp_opening","pf_tcp_opening",
238                          "tcp.opening" );
239     file << printTimeout(options,
240                          "pf_set_tcp_established","pf_tcp_established",
241                          "tcp.established" );
242     file << printTimeout(options,
243                          "pf_set_tcp_closing","pf_tcp_closing",
244                          "tcp.closing" );
245     file << printTimeout(options,
246                          "pf_set_tcp_finwait","pf_tcp_finwait",
247                          "tcp.finwait" );
248     file << printTimeout(options,
249                          "pf_set_tcp_closed","pf_tcp_closed",
250                          "tcp.closed" );
251     file << printTimeout(options,
252                          "pf_set_udp_first","pf_udp_first",
253                          "udp.first" );
254     file << printTimeout(options,
255                          "pf_set_udp_single","pf_udp_single",
256                          "udp.single" );
257     file << printTimeout(options,
258                          "pf_set_udp_multiple","pf_udp_multiple",
259                          "udp.multiple" );
260     file << printTimeout(options,
261                          "pf_set_icmp_first","pf_icmp_first",
262                          "icmp.first" );
263     file << printTimeout(options,
264                          "pf_set_icmp_error","pf_icmp_error",
265                          "icmp.error" );
266     file << printTimeout(options,
267                          "pf_set_other_first","pf_other_first",
268                          "other.first" );
269     file << printTimeout(options,
270                          "pf_set_other_single","pf_other_single",
271                          "other.single" );
272     file << printTimeout(options,
273                          "pf_set_other_multiple","pf_other_multiple",
274                          "other.multiple" );
275 
276     file << printTimeout(options,
277                          "pf_set_adaptive","pf_adaptive_start",
278                          "adaptive.start" );
279     file << printTimeout(options,
280                          "pf_set_adaptive","pf_adaptive_end",
281                          "adaptive.end");
282 
283     // check if any interface is marked as 'unprotected'
284     // and generate 'set skip on <ifspec>' commands
285 
286     if (fw->getStr("version")=="ge_3.7" ||
287 //        fw->getStr("version")=="4.x")
288         XMLTools::version_compare(fw->getStr("version"), "4.0")>=0)
289     {
290         for (list<FWObject*>::iterator i=all_interfaces.begin();
291              i!=all_interfaces.end(); ++i)
292         {
293             Interface *iface=dynamic_cast<Interface*>(*i);
294             assert(iface);
295 
296             if ( iface->isUnprotected())
297                 file << "set skip on " << iface->getName() << endl;
298         }
299     }
300 
301     file << endl;
302 
303     if (prolog_place == "pf_file_after_set")
304         printProlog(file, pre_hook);
305 
306     QStringList scrub_options;
307 
308     string scrub_rule_direction = "in ";
309 
310     if (options->getBool("pf_do_scrub"))
311     {
312         if (XMLTools::version_compare(fw->getStr("version"), "4.6")<0)
313         {
314             if (options->getBool("pf_scrub_reassemble"))
315                 scrub_options << "fragment reassemble";
316             if (options->getBool("pf_scrub_fragm_crop"))
317                 scrub_options << "fragment crop";
318             if (options->getBool("pf_scrub_fragm_drop_ovl"))
319                 scrub_options << "fragment drop-ovl";
320         }
321         if (options->getBool("pf_scrub_reassemble_tcp"))
322         {
323             // "scrub all reassemble tcp" - does not allow direction
324             scrub_options << "reassemble tcp";
325             scrub_rule_direction = "";
326         }
327     }
328 
329     if (options->getBool("pf_scrub_no_df"))  scrub_options << "no-df ";
330 
331     if (!scrub_options.empty())
332     {
333         file << "#" << endl;
334         file << "# Scrub rules" << endl;
335         file << "#" << endl;
336 
337         if (XMLTools::version_compare(fw->getStr("version"), "4.6")>=0)
338         {
339             file << "match "
340                  << scrub_rule_direction
341                  << "all scrub ("
342                  << scrub_options.join(" ").toStdString() << ")"
343                  << endl;
344         } else
345         {
346             file << "scrub "
347                  << scrub_rule_direction
348                  << "all "
349                  << scrub_options.join(" ").toStdString()
350                  << endl;
351         }
352     }
353 
354     scrub_options.clear();
355 
356     if (options->getBool("pf_scrub_random_id"))
357         scrub_options << "random-id";
358 
359     if (options->getBool("pf_scrub_use_minttl"))
360         scrub_options << "min-ttl " << options->getStr("pf_scrub_minttl").c_str();
361 
362     if (options->getBool("pf_scrub_use_maxmss"))
363         scrub_options << "max-mss " << options->getStr("pf_scrub_maxmss").c_str();
364 
365     if (!scrub_options.empty())
366     {
367         if (XMLTools::version_compare(fw->getStr("version"), "4.6")>=0)
368         {
369             file << "match out all scrub ("
370                  << scrub_options.join(" ").toStdString() << ")" << endl;
371         } else
372         {
373             file << "scrub out all "
374                  << scrub_options.join(" ").toStdString() << endl;
375         }
376     }
377 
378     file << endl;
379 
380     if (prolog_place == "pf_file_after_scrub")
381         printProlog(file, pre_hook);
382 
383     //file << table_factory->PrintTables();
384     //file << endl;
385 
386     //if (prolog_place == "pf_file_after_tables")
387     //    printProlog(file, pre_hook);
388 
389 }
390 
setToolPathVar(Firewall * fw,const string & os,const string & var_path_suffix,OSData::tools osdata_tool_type,Configlet * configlet)391 void CompilerDriver_pf::setToolPathVar(Firewall* fw,
392                                        const string &os,
393                                        const string &var_path_suffix,
394                                        OSData::tools osdata_tool_type,
395                                        Configlet *configlet)
396 {
397     OSData os_data;
398     FWOptions* options = fw->getOptionsObject();
399     string s;
400     string path;
401     s = options->getStr(os + "_" + var_path_suffix);
402     if (!s.empty()) path = s;
403     else            path = os_data.getPathForTool(os, osdata_tool_type);
404     configlet->setVariable(var_path_suffix.c_str(), path.c_str());
405 }
406 
printPathForAllTools(Firewall * fw,const string & os)407 QString CompilerDriver_pf::printPathForAllTools(Firewall* fw, const string &os)
408 {
409     Configlet tools = Configlet(fw, "bsd", "tools");
410     tools.removeComments();
411 
412     setToolPathVar(fw, os, "path_ifconfig", OSData::IFCONFIG, &tools);
413     setToolPathVar(fw, os, "path_ipf", OSData::IPF, &tools);
414     setToolPathVar(fw, os, "path_ipnat", OSData::IPNAT, &tools);
415     setToolPathVar(fw, os, "path_ipfw", OSData::IPFW, &tools);
416     setToolPathVar(fw, os, "path_pfctl", OSData::PFCTL, &tools);
417     setToolPathVar(fw, os, "path_sysctl", OSData::SYSCTL, &tools);
418     setToolPathVar(fw, os, "path_logger", OSData::LOGGER, &tools);
419     return tools.expand();
420 }
421 
422