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