Branch data Line data Source code
1 : : /*
2 : : *****************************************************************************
3 : : *
4 : : * File: fw_util_iptables.c
5 : : *
6 : : * Purpose: Fwknop routines for managing iptables firewall rules.
7 : : *
8 : : * Fwknop is developed primarily by the people listed in the file 'AUTHORS'.
9 : : * Copyright (C) 2009-2014 fwknop developers and contributors. For a full
10 : : * list of contributors, see the file 'CREDITS'.
11 : : *
12 : : * License (GNU General Public License):
13 : : *
14 : : * This program is free software; you can redistribute it and/or
15 : : * modify it under the terms of the GNU General Public License
16 : : * as published by the Free Software Foundation; either version 2
17 : : * of the License, or (at your option) any later version.
18 : : *
19 : : * This program is distributed in the hope that it will be useful,
20 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 : : * GNU General Public License for more details.
23 : : *
24 : : * You should have received a copy of the GNU General Public License
25 : : * along with this program; if not, write to the Free Software
26 : : * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
27 : : * USA
28 : : *
29 : : *****************************************************************************
30 : : */
31 : :
32 : : #include "fwknopd_common.h"
33 : :
34 : : #ifdef FIREWALL_IPTABLES
35 : :
36 : : #include "fw_util.h"
37 : : #include "utils.h"
38 : : #include "log_msg.h"
39 : : #include "extcmd.h"
40 : : #include "access.h"
41 : :
42 : : static struct fw_config fwc;
43 : : static char cmd_buf[CMD_BUFSIZE];
44 : : static char err_buf[CMD_BUFSIZE];
45 : : static char cmd_out[STANDARD_CMD_OUT_BUFSIZE];
46 : :
47 : : /* assume 'iptables -C' is offered since only older versions
48 : : * don't have this (see ipt_chk_support()).
49 : : */
50 : : static int have_ipt_chk_support = 1;
51 : :
52 : : static void
53 : 10823 : zero_cmd_buffers(void)
54 : : {
55 : : memset(cmd_buf, 0x0, CMD_BUFSIZE);
56 : : memset(err_buf, 0x0, CMD_BUFSIZE);
57 : : memset(cmd_out, 0x0, STANDARD_CMD_OUT_BUFSIZE);
58 : 10823 : }
59 : :
60 : : static int pid_status = 0;
61 : :
62 : : static void
63 : 8567 : chop_newline(char *str)
64 : : {
65 [ + + ][ + - ]: 8567 : if(str[0] != 0x0 && str[strlen(str)-1] == 0x0a)
66 : 4292 : str[strlen(str)-1] = 0x0;
67 : 8567 : return;
68 : : }
69 : :
70 : : static int
71 : 5 : rule_exists_no_chk_support(const fko_srv_options_t * const opts,
72 : : const struct fw_chain * const fwc, const unsigned int proto,
73 : : const char * const ip, const unsigned int port,
74 : : const unsigned int exp_ts)
75 : : {
76 : 5 : int rule_exists=0, rule_num=0, rtmp=0;
77 : 5 : char cmd_buf[CMD_BUFSIZE] = {0};
78 : 5 : char target_search[CMD_BUFSIZE] = {0};
79 : 5 : char proto_search[CMD_BUFSIZE] = {0};
80 : 5 : char ip_search[CMD_BUFSIZE] = {0};
81 : 5 : char port_search[CMD_BUFSIZE] = {0};
82 : 5 : char exp_ts_search[CMD_BUFSIZE] = {0};
83 : :
84 : : #if CODE_COVERAGE
85 : 5 : int pid_status = 0;
86 : : /* If we're maximizing code coverage, then exercise the run_extcmd_write()
87 : : * function which is normally only used for the PF firewall. This is to
88 : : * maximize code coverage in conjunction with the test suite, and is never
89 : : * compiled in for a production release of fwknop.
90 : : */
91 [ - + ]: 5 : if(run_extcmd_write("/bin/grep -v test", "/bin/echo test", &pid_status, opts) != 0)
92 : 0 : log_msg(LOG_WARNING, "Code coverage: Could not execute command");
93 : : #endif
94 : :
95 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_LIST_RULES_ARGS,
96 : 5 : opts->fw_config->fw_command,
97 : 5 : fwc->table,
98 : 5 : fwc->to_chain
99 : : );
100 : :
101 [ + + ]: 5 : if(proto == IPPROTO_TCP)
102 : : snprintf(proto_search, CMD_BUFSIZE-1, " tcp ");
103 [ + - ]: 1 : else if(proto == IPPROTO_UDP)
104 : : snprintf(proto_search, CMD_BUFSIZE-1, " udp ");
105 [ # # ]: 0 : else if(proto == IPPROTO_ICMP)
106 : : snprintf(proto_search, CMD_BUFSIZE-1, " icmp ");
107 : : else
108 : : snprintf(proto_search, CMD_BUFSIZE-1, " %u ", proto);
109 : :
110 : : snprintf(port_search, CMD_BUFSIZE-1, ":%u ", port);
111 : 5 : snprintf(target_search, CMD_BUFSIZE-1, " %s ", fwc->target);
112 : : snprintf(ip_search, CMD_BUFSIZE-1, " %s ", ip);
113 : : snprintf(exp_ts_search, CMD_BUFSIZE-1, "%u ", exp_ts);
114 : :
115 : : /* search for each of the substrings, and require the returned
116 : : * rule number to be the same across all searches to return true
117 : : */
118 : 5 : rtmp = search_extcmd(cmd_buf, WANT_STDERR,
119 : : NO_TIMEOUT, exp_ts_search, &pid_status, opts);
120 : :
121 [ - + ]: 5 : if(rtmp > 0)
122 : : {
123 : 0 : rule_num = rtmp;
124 : 0 : rtmp = search_extcmd(cmd_buf, WANT_STDERR,
125 : : NO_TIMEOUT, proto_search, &pid_status, opts);
126 [ # # ]: 0 : if(rtmp == rule_num)
127 : 0 : rtmp = search_extcmd(cmd_buf, WANT_STDERR,
128 : : NO_TIMEOUT, ip_search, &pid_status, opts);
129 [ # # ]: 0 : if(rtmp == rule_num)
130 : 0 : rtmp = search_extcmd(cmd_buf, WANT_STDERR,
131 : : NO_TIMEOUT, target_search, &pid_status, opts);
132 [ # # ]: 0 : if(rtmp == rule_num)
133 : 0 : rtmp = search_extcmd(cmd_buf, WANT_STDERR,
134 : : NO_TIMEOUT, port_search, &pid_status, opts);
135 [ # # ]: 0 : if(rtmp == rule_num)
136 : 0 : rule_exists = 1;
137 : : }
138 : :
139 [ - + ]: 5 : if(rule_exists)
140 : 0 : log_msg(LOG_DEBUG,
141 : : "rule_exists_no_chk_support() %s %u -> %s expires: %u rule (already exists",
142 : : proto_search, port, ip, exp_ts);
143 : : else
144 : 5 : log_msg(LOG_DEBUG,
145 : : "rule_exists_no_chk_support() %s %u -> %s expires: %u rule does not exist",
146 : : proto_search, port, ip, exp_ts);
147 : :
148 : 5 : return(rule_exists);
149 : : }
150 : :
151 : : static int
152 : 2381 : rule_exists_chk_support(const fko_srv_options_t * const opts,
153 : : const char * const chain, const char * const rule)
154 : : {
155 : 2381 : int rule_exists = 0;
156 : 2381 : int res = 0;
157 : :
158 : 2381 : zero_cmd_buffers();
159 : :
160 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_CHK_RULE_ARGS,
161 : 2381 : opts->fw_config->fw_command, chain, rule);
162 : :
163 : 2381 : res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE,
164 : : WANT_STDERR, NO_TIMEOUT, &pid_status, opts);
165 : 2381 : chop_newline(err_buf);
166 : :
167 : 2381 : log_msg(LOG_DEBUG, "rule_exists_chk_support() CMD: '%s' (res: %d, err: %s)",
168 : : cmd_buf, res, err_buf);
169 : :
170 [ + - ][ + + ]: 2381 : if(EXTCMD_IS_SUCCESS(res) && strlen(err_buf))
171 : : {
172 : 1644 : log_msg(LOG_DEBUG, "rule_exists_chk_support() Rule : '%s' in %s does not exist",
173 : : rule, chain);
174 : : }
175 : : else
176 : : {
177 : 737 : rule_exists = 1;
178 : 737 : log_msg(LOG_DEBUG, "rule_exists_chk_support() Rule : '%s' in %s already exists",
179 : : rule, chain);
180 : : }
181 : :
182 : 2381 : return(rule_exists);
183 : : }
184 : :
185 : : static int
186 : 303 : rule_exists(const fko_srv_options_t * const opts,
187 : : const struct fw_chain * const fwc, const char * const rule,
188 : : const unsigned int proto, const char * const ip,
189 : : const unsigned int port, const unsigned int exp_ts)
190 : : {
191 : 303 : int rule_exists = 0;
192 : :
193 [ + + ]: 303 : if(have_ipt_chk_support == 1)
194 : 298 : rule_exists = rule_exists_chk_support(opts, fwc->to_chain, rule);
195 : : else
196 : 5 : rule_exists = rule_exists_no_chk_support(opts, fwc, proto, ip, port, exp_ts);
197 : :
198 [ + + ]: 303 : if(rule_exists == 1)
199 : 2 : log_msg(LOG_DEBUG, "rule_exists() Rule : '%s' in %s already exists",
200 : 2 : rule, fwc->to_chain);
201 : : else
202 : 301 : log_msg(LOG_DEBUG, "rule_exists() Rule : '%s' in %s does not exist",
203 : 301 : rule, fwc->to_chain);
204 : :
205 : 303 : return(rule_exists);
206 : : }
207 : :
208 : : static void
209 : 371 : ipt_chk_support(const fko_srv_options_t * const opts)
210 : : {
211 : 371 : int res = 1;
212 : 371 : struct fw_chain *in_chain = &(opts->fw_config->chain[IPT_INPUT_ACCESS]);
213 : :
214 : 371 : zero_cmd_buffers();
215 : :
216 : : /* Add a harmless rule to the iptables INPUT chain and see if iptables
217 : : * supports '-C' to check for it. Set "have_ipt_chk_support" accordingly,
218 : : * delete the rule, and return.
219 : : */
220 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_TMP_CHK_RULE_ARGS,
221 : 371 : opts->fw_config->fw_command,
222 : 371 : in_chain->table,
223 : 371 : in_chain->from_chain,
224 : : 1, /* first rule */
225 : 371 : in_chain->target
226 : : );
227 : :
228 : 371 : res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE,
229 : : WANT_STDERR, NO_TIMEOUT, &pid_status, opts);
230 : 371 : chop_newline(err_buf);
231 : :
232 : 371 : log_msg(LOG_DEBUG, "ipt_chk_support() CMD: '%s' (res: %d, err: %s)",
233 : : cmd_buf, res, err_buf);
234 : :
235 : 371 : zero_cmd_buffers();
236 : :
237 : : /* Now see if '-C' works - any output indicates failure
238 : : */
239 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_TMP_VERIFY_CHK_ARGS,
240 : 371 : opts->fw_config->fw_command,
241 : : in_chain->table,
242 : : in_chain->from_chain,
243 : : in_chain->target
244 : : );
245 : :
246 : 371 : res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE,
247 : : WANT_STDERR, NO_TIMEOUT, &pid_status, opts);
248 : 371 : chop_newline(err_buf);
249 : :
250 : 371 : log_msg(LOG_DEBUG, "ipt_chk_support() CMD: '%s' (res: %d, err: %s)",
251 : : cmd_buf, res, err_buf);
252 : :
253 [ + - ][ + + ]: 371 : if(EXTCMD_IS_SUCCESS(res) && strlen(err_buf))
254 : : {
255 : 3 : log_msg(LOG_DEBUG, "ipt_chk_support() -C not supported");
256 : 3 : have_ipt_chk_support = 0;
257 : : }
258 : : else
259 : : {
260 : 368 : log_msg(LOG_DEBUG, "ipt_chk_support() -C supported");
261 : 368 : have_ipt_chk_support = 1;
262 : : }
263 : :
264 : : /* Delete the tmp rule
265 : : */
266 : 371 : zero_cmd_buffers();
267 : :
268 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_DEL_RULE_ARGS,
269 : 371 : opts->fw_config->fw_command,
270 : : in_chain->table,
271 : : in_chain->from_chain,
272 : : 1
273 : : );
274 : 371 : run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE,
275 : : WANT_STDERR, NO_TIMEOUT, &pid_status, opts);
276 : :
277 : 371 : return;
278 : : }
279 : :
280 : : static int
281 : 374 : comment_match_exists(const fko_srv_options_t * const opts)
282 : : {
283 : 374 : int res = 1;
284 : 374 : char *ndx = NULL;
285 : 374 : struct fw_chain *in_chain = &(opts->fw_config->chain[IPT_INPUT_ACCESS]);
286 : :
287 : 374 : zero_cmd_buffers();
288 : :
289 : : /* Add a harmless rule to the iptables INPUT chain that uses the comment
290 : : * match and make sure it exists. If not, return zero. Otherwise, delete
291 : : * the rule and return true.
292 : : */
293 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_TMP_COMMENT_ARGS,
294 : 374 : opts->fw_config->fw_command,
295 : 374 : in_chain->table,
296 : 374 : in_chain->from_chain,
297 : : 1, /* first rule */
298 : 374 : in_chain->target
299 : : );
300 : :
301 : 374 : res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE,
302 : : WANT_STDERR, NO_TIMEOUT, &pid_status, opts);
303 : 374 : chop_newline(err_buf);
304 : :
305 : 374 : log_msg(LOG_DEBUG, "comment_match_exists() CMD: '%s' (res: %d, err: %s)",
306 : : cmd_buf, res, err_buf);
307 : :
308 : 374 : zero_cmd_buffers();
309 : :
310 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_LIST_RULES_ARGS,
311 : 374 : opts->fw_config->fw_command,
312 : : in_chain->table,
313 : : in_chain->from_chain
314 : : );
315 : :
316 : 374 : res = run_extcmd(cmd_buf, cmd_out, STANDARD_CMD_OUT_BUFSIZE,
317 : : WANT_STDERR, NO_TIMEOUT, &pid_status, opts);
318 : 374 : chop_newline(cmd_out);
319 : :
320 [ - + ]: 374 : if(!EXTCMD_IS_SUCCESS(res))
321 : 0 : log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, cmd_out);
322 : :
323 : 374 : ndx = strstr(cmd_out, TMP_COMMENT);
324 [ + + ]: 374 : if(ndx == NULL)
325 : : res = 0; /* did not find the tmp comment */
326 : : else
327 : 371 : res = 1;
328 : :
329 [ + + ]: 374 : if(res == 1)
330 : : {
331 : : /* Delete the tmp comment rule
332 : : */
333 : 371 : zero_cmd_buffers();
334 : :
335 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_DEL_RULE_ARGS,
336 : 371 : opts->fw_config->fw_command,
337 : : in_chain->table,
338 : : in_chain->from_chain,
339 : : 1
340 : : );
341 : 371 : run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE,
342 : : WANT_STDERR, NO_TIMEOUT, &pid_status, opts);
343 : : }
344 : :
345 : 374 : return res;
346 : : }
347 : :
348 : : static int
349 : 459 : add_jump_rule(const fko_srv_options_t * const opts, const int chain_num)
350 : : {
351 : 459 : int res = 0;
352 : :
353 : 459 : zero_cmd_buffers();
354 : :
355 : 459 : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_ADD_JUMP_RULE_ARGS,
356 : : fwc.fw_command,
357 : 459 : fwc.chain[chain_num].table,
358 : 459 : fwc.chain[chain_num].from_chain,
359 : : fwc.chain[chain_num].jump_rule_pos,
360 : 459 : fwc.chain[chain_num].to_chain
361 : : );
362 : :
363 : 459 : res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE,
364 : : WANT_STDERR, NO_TIMEOUT, &pid_status, opts);
365 : :
366 : 459 : log_msg(LOG_DEBUG, "add_jump_rule() CMD: '%s' (res: %d, err: %s)",
367 : : cmd_buf, res, err_buf);
368 : :
369 [ + - ]: 459 : if(EXTCMD_IS_SUCCESS(res))
370 : 459 : log_msg(LOG_INFO, "Added jump rule from chain: %s to chain: %s",
371 : : fwc.chain[chain_num].from_chain,
372 : : fwc.chain[chain_num].to_chain);
373 : : else
374 : 0 : log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf);
375 : :
376 : 459 : return res;
377 : : }
378 : :
379 : : static int
380 : 746 : chain_exists(const fko_srv_options_t * const opts, const int chain_num)
381 : : {
382 : 746 : int res = 0;
383 : :
384 : 746 : zero_cmd_buffers();
385 : :
386 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_CHAIN_EXISTS_ARGS,
387 : : fwc.fw_command,
388 : 746 : fwc.chain[chain_num].table,
389 : 746 : fwc.chain[chain_num].to_chain
390 : : );
391 : :
392 : 746 : res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE,
393 : : WANT_STDERR, NO_TIMEOUT, &pid_status, opts);
394 : 746 : chop_newline(err_buf);
395 : :
396 : 746 : log_msg(LOG_DEBUG, "chain_exists() CMD: '%s' (res: %d, err: %s)",
397 : : cmd_buf, res, err_buf);
398 : :
399 [ + - ]: 746 : if(EXTCMD_IS_SUCCESS(res))
400 : 746 : log_msg(LOG_DEBUG, "'%s' table '%s' chain exists",
401 : : fwc.chain[chain_num].table,
402 : : fwc.chain[chain_num].to_chain);
403 : : else
404 : 0 : log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf);
405 : :
406 : 746 : return res;
407 : : }
408 : :
409 : : static int
410 : 2083 : jump_rule_exists_chk_support(const fko_srv_options_t * const opts, const int chain_num)
411 : : {
412 : 2083 : int exists = 0;
413 : 2083 : char rule_buf[CMD_BUFSIZE] = {0};
414 : :
415 : : snprintf(rule_buf, CMD_BUFSIZE-1, IPT_CHK_JUMP_RULE_ARGS,
416 : 2083 : fwc.chain[chain_num].table,
417 : 2083 : fwc.chain[chain_num].to_chain
418 : : );
419 : :
420 [ + + ]: 2083 : if(rule_exists_chk_support(opts, fwc.chain[chain_num].from_chain, rule_buf) == 1)
421 : : {
422 : 735 : log_msg(LOG_DEBUG, "jump_rule_exists_chk_support() jump rule found");
423 : 735 : exists = 1;
424 : : }
425 : : else
426 : 1348 : log_msg(LOG_DEBUG, "jump_rule_exists_chk_support() jump rule not found");
427 : :
428 : 2083 : return exists;
429 : : }
430 : :
431 : : static int
432 : 27 : jump_rule_exists_no_chk_support(const fko_srv_options_t * const opts, const int chain_num)
433 : : {
434 : 27 : int exists = 0;
435 : 27 : char cmd_buf[CMD_BUFSIZE] = {0};
436 : 27 : char chain_search[CMD_BUFSIZE] = {0};
437 : :
438 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_LIST_RULES_ARGS,
439 : : fwc.fw_command,
440 : 27 : fwc.chain[chain_num].table,
441 : 27 : fwc.chain[chain_num].from_chain
442 : : );
443 : :
444 : : /* include spaces on either side as produced by 'iptables -L' output
445 : : */
446 : : snprintf(chain_search, CMD_BUFSIZE-1, " %s ",
447 : 27 : fwc.chain[chain_num].to_chain);
448 : :
449 [ + + ]: 27 : if(search_extcmd(cmd_buf, WANT_STDERR,
450 : : NO_TIMEOUT, chain_search, &pid_status, opts) > 0)
451 : 9 : exists = 1;
452 : :
453 [ + + ]: 27 : if(exists)
454 : 9 : log_msg(LOG_DEBUG, "jump_rule_exists_no_chk_support() jump rule found");
455 : : else
456 : 18 : log_msg(LOG_DEBUG, "jump_rule_exists_no_chk_support() jump rule not found");
457 : :
458 : 27 : return(exists);
459 : : }
460 : :
461 : : static int
462 : 2110 : jump_rule_exists(const fko_srv_options_t * const opts, const int chain_num)
463 : : {
464 : 2110 : int exists = 0;
465 : :
466 [ + + ]: 2110 : if(have_ipt_chk_support == 1)
467 : 2083 : exists = jump_rule_exists_chk_support(opts, chain_num);
468 : : else
469 : 27 : exists = jump_rule_exists_no_chk_support(opts, chain_num);
470 : :
471 : 2110 : return exists;
472 : : }
473 : :
474 : : /* Print all firewall rules currently instantiated by the running fwknopd
475 : : * daemon to stdout.
476 : : */
477 : : int
478 : 891 : fw_dump_rules(const fko_srv_options_t * const opts)
479 : : {
480 : 891 : int i, res, got_err = 0;
481 : :
482 : 891 : struct fw_chain *ch = opts->fw_config->chain;
483 : :
484 [ + + ]: 891 : if (opts->fw_list_all == 1)
485 : : {
486 : 2 : fprintf(stdout, "Listing all iptables rules in applicable tables...\n");
487 : 2 : fflush(stdout);
488 : :
489 [ + + ]: 14 : for(i=0; i<(NUM_FWKNOP_ACCESS_TYPES); i++)
490 : : {
491 : :
492 [ + + ]: 12 : if(fwc.chain[i].target[0] == '\0')
493 : 10 : continue;
494 : :
495 : 2 : zero_cmd_buffers();
496 : :
497 : : /* Create the list command
498 : : */
499 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_LIST_ALL_RULES_ARGS,
500 : 2 : opts->fw_config->fw_command,
501 : 2 : ch[i].table
502 : : );
503 : :
504 : 2 : res = run_extcmd(cmd_buf, NULL, 0, NO_STDERR,
505 : : NO_TIMEOUT, &pid_status, opts);
506 : :
507 : 2 : log_msg(LOG_DEBUG, "fw_dump_rules() CMD: '%s' (res: %d)",
508 : : cmd_buf, res);
509 : :
510 : : /* Expect full success on this */
511 [ - + ]: 2 : if(! EXTCMD_IS_SUCCESS(res))
512 : : {
513 : 0 : log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf);
514 : 0 : got_err++;
515 : : }
516 : : }
517 : : }
518 : : else
519 : : {
520 : 889 : fprintf(stdout, "Listing rules in fwknopd iptables chains...\n");
521 : 889 : fflush(stdout);
522 : :
523 [ + + ]: 6223 : for(i=0; i<(NUM_FWKNOP_ACCESS_TYPES); i++)
524 : : {
525 : :
526 [ + + ]: 5334 : if(fwc.chain[i].target[0] == '\0')
527 : 4281 : continue;
528 : :
529 : 1053 : zero_cmd_buffers();
530 : :
531 : : /* Create the list command
532 : : */
533 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_LIST_RULES_ARGS,
534 : 1053 : opts->fw_config->fw_command,
535 : 1053 : ch[i].table,
536 : 1053 : ch[i].to_chain
537 : : );
538 : :
539 : 1053 : fprintf(stdout, "\n");
540 : 1053 : fflush(stdout);
541 : :
542 : 1053 : res = run_extcmd(cmd_buf, NULL, 0, NO_STDERR,
543 : : NO_TIMEOUT, &pid_status, opts);
544 : :
545 : 1053 : log_msg(LOG_DEBUG, "fw_dump_rules() CMD: '%s' (res: %d)",
546 : : cmd_buf, res);
547 : :
548 : : /* Expect full success on this */
549 [ - + ]: 1053 : if(! EXTCMD_IS_SUCCESS(res))
550 : : {
551 : 0 : log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf);
552 : 0 : got_err++;
553 : : }
554 : : }
555 : : }
556 : :
557 : 891 : return(got_err);
558 : : }
559 : :
560 : : /* Quietly flush and delete all fwknop custom chains.
561 : : */
562 : : static void
563 : 749 : delete_all_chains(const fko_srv_options_t * const opts)
564 : : {
565 : 749 : int i, res, cmd_ctr = 0;
566 : :
567 [ + + ]: 5243 : for(i=0; i<(NUM_FWKNOP_ACCESS_TYPES); i++)
568 : : {
569 [ + + ]: 4494 : if(fwc.chain[i].target[0] == '\0')
570 : 3587 : continue;
571 : :
572 : : /* First look for a jump rule to this chain and remove it if it
573 : : * is there.
574 : : */
575 : : cmd_ctr = 0;
576 [ + - ][ + + ]: 1364 : while(cmd_ctr < CMD_LOOP_TRIES && (jump_rule_exists(opts, i) == 1))
577 : : {
578 : 457 : zero_cmd_buffers();
579 : :
580 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_DEL_JUMP_RULE_ARGS,
581 : : fwc.fw_command,
582 : 457 : fwc.chain[i].table,
583 : 457 : fwc.chain[i].from_chain,
584 : 457 : fwc.chain[i].to_chain
585 : : );
586 : :
587 : 457 : res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE,
588 : : WANT_STDERR, NO_TIMEOUT, &pid_status, opts);
589 : 457 : chop_newline(err_buf);
590 : :
591 : 457 : log_msg(LOG_DEBUG, "delete_all_chains() CMD: '%s' (res: %d, err: %s)",
592 : : cmd_buf, res, err_buf);
593 : :
594 : : /* Expect full success on this */
595 [ - + ]: 457 : if(! EXTCMD_IS_SUCCESS(res))
596 : 0 : log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf);
597 : :
598 : 457 : cmd_ctr++;
599 : : }
600 : :
601 : 907 : zero_cmd_buffers();
602 : :
603 : : /* Now flush and remove the chain.
604 : : */
605 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_FLUSH_CHAIN_ARGS,
606 : : fwc.fw_command,
607 : 907 : fwc.chain[i].table,
608 : 907 : fwc.chain[i].to_chain
609 : : );
610 : :
611 : 907 : res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, WANT_STDERR,
612 : : NO_TIMEOUT, &pid_status, opts);
613 : 907 : chop_newline(err_buf);
614 : :
615 : 907 : log_msg(LOG_DEBUG, "delete_all_chains() CMD: '%s' (res: %d, err: %s)",
616 : : cmd_buf, res, err_buf);
617 : :
618 : : /* Expect full success on this */
619 [ - + ]: 907 : if(! EXTCMD_IS_SUCCESS(res))
620 : 0 : log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf);
621 : :
622 : 907 : zero_cmd_buffers();
623 : :
624 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_DEL_CHAIN_ARGS,
625 : : fwc.fw_command,
626 : 907 : fwc.chain[i].table,
627 : : fwc.chain[i].to_chain
628 : : );
629 : :
630 : 907 : res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, WANT_STDERR,
631 : : NO_TIMEOUT, &pid_status, opts);
632 : 907 : chop_newline(err_buf);
633 : :
634 : 907 : log_msg(LOG_DEBUG, "delete_all_chains() CMD: '%s' (res: %d, err: %s)",
635 : : cmd_buf, res, err_buf);
636 : :
637 : : /* Expect full success on this */
638 [ - + ]: 907 : if(! EXTCMD_IS_SUCCESS(res))
639 : 0 : log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf);
640 : : }
641 : 749 : return;
642 : : }
643 : :
644 : : static int
645 : 746 : create_chain(const fko_srv_options_t * const opts, const int chain_num)
646 : : {
647 : 746 : int res = 0;
648 : :
649 : 746 : zero_cmd_buffers();
650 : :
651 : : /* Create the custom chain.
652 : : */
653 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_NEW_CHAIN_ARGS,
654 : : fwc.fw_command,
655 : 746 : fwc.chain[chain_num].table,
656 : 746 : fwc.chain[chain_num].to_chain
657 : : );
658 : :
659 : 746 : res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, WANT_STDERR,
660 : : NO_TIMEOUT, &pid_status, opts);
661 : 746 : chop_newline(err_buf);
662 : :
663 : 746 : log_msg(LOG_DEBUG, "create_chain() CMD: '%s' (res: %d, err: %s)",
664 : : cmd_buf, res, err_buf);
665 : :
666 : : /* Expect full success on this */
667 [ - + ]: 746 : if(! EXTCMD_IS_SUCCESS(res))
668 : 0 : log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf);
669 : :
670 : 746 : return res;
671 : : }
672 : :
673 : : /* Create the fwknop custom chains (at least those that are configured).
674 : : */
675 : : static int
676 : 374 : create_fw_chains(const fko_srv_options_t * const opts)
677 : : {
678 : 374 : int i, got_err = 0;
679 : :
680 [ + + ]: 2618 : for(i=0; i<(NUM_FWKNOP_ACCESS_TYPES); i++)
681 : : {
682 [ + + ]: 2244 : if(fwc.chain[i].target[0] == '\0')
683 : 1797 : continue;
684 : :
685 [ + - ]: 447 : if(chain_exists(opts, i) == 0)
686 : : {
687 : :
688 : : /* Create the chain
689 : : */
690 [ - + ]: 447 : if(! EXTCMD_IS_SUCCESS(create_chain(opts, i)))
691 : 0 : got_err++;
692 : :
693 : : /* Then create the jump rule to that chain if it
694 : : * doesn't already exist (which is possible)
695 : : */
696 [ + - ]: 447 : if(jump_rule_exists(opts, i) == 0)
697 [ - + ]: 447 : if(! EXTCMD_IS_SUCCESS(add_jump_rule(opts, i)))
698 : 0 : got_err++;
699 : : }
700 : : }
701 : :
702 : 374 : return(got_err);
703 : : }
704 : :
705 : : static int
706 : 2844 : set_fw_chain_conf(const int type, const char * const conf_str)
707 : : {
708 : : int i, j, is_err;
709 : 2844 : char tbuf[MAX_LINE_LEN] = {0};
710 : 2844 : const char *ndx = conf_str;
711 : :
712 : : char *chain_fields[FW_NUM_CHAIN_FIELDS];
713 : :
714 : 2844 : struct fw_chain *chain = &(fwc.chain[type]);
715 : :
716 [ - + ]: 2844 : if(conf_str == NULL)
717 : : {
718 : 0 : log_msg(LOG_ERR, "[*] NULL conf_str");
719 : 0 : return 0;
720 : : }
721 : :
722 : 2844 : chain->type = type;
723 : :
724 [ + - ]: 2844 : if(ndx != NULL)
725 : 2844 : chain_fields[0] = tbuf;
726 : :
727 : : i = 0;
728 : : j = 1;
729 [ + + ]: 120892 : while(*ndx != '\0')
730 : : {
731 [ + + ]: 118048 : if(*ndx != ' ')
732 : : {
733 [ + + ]: 103828 : if(*ndx == ',')
734 : : {
735 : 14220 : tbuf[i] = '\0';
736 : 14220 : chain_fields[j++] = &(tbuf[++i]);
737 : : }
738 : : else
739 : 89608 : tbuf[i++] = *ndx;
740 : : }
741 [ + + ]: 118048 : if(*ndx != '\0'
742 : 118048 : && *ndx != ' '
743 [ + + ]: 103828 : && *ndx != ','
744 [ + + ]: 89608 : && *ndx != '_'
745 [ - + ]: 86747 : && isalnum(*ndx) == 0)
746 : : {
747 : 0 : log_msg(LOG_ERR, "[*] Custom chain config parse error: "
748 : : "invalid character '%c' for chain type %i, "
749 : : "line: %s", *ndx, type, conf_str);
750 : 0 : return 0;
751 : : }
752 : 118048 : ndx++;
753 : : }
754 : :
755 : : /* Sanity check - j should be the number of chain fields
756 : : * (excluding the type).
757 : : */
758 [ - + ]: 2844 : if(j != FW_NUM_CHAIN_FIELDS)
759 : : {
760 : 0 : log_msg(LOG_ERR, "[*] Custom chain config parse error: "
761 : : "wrong number of fields for chain type %i, "
762 : : "line: %s", type, conf_str);
763 : 0 : return 0;
764 : : }
765 : :
766 : : /* Pull and set Target */
767 : 2844 : strlcpy(chain->target, chain_fields[0], sizeof(chain->target));
768 : :
769 : : /* Pull and set Table */
770 : 2844 : strlcpy(chain->table, chain_fields[1], sizeof(chain->table));
771 : :
772 : : /* Pull and set From_chain */
773 : 2844 : strlcpy(chain->from_chain, chain_fields[2], sizeof(chain->from_chain));
774 : :
775 : : /* Pull and set Jump_rule_position */
776 : 2844 : chain->jump_rule_pos = strtol_wrapper(chain_fields[3],
777 : : 0, RCHK_MAX_IPT_RULE_NUM, NO_EXIT_UPON_ERR, &is_err);
778 [ + + ]: 2844 : if(is_err != FKO_SUCCESS)
779 : : {
780 : 1 : log_msg(LOG_ERR, "[*] invalid jump rule position in Line: %s",
781 : : conf_str);
782 : 1 : return 0;
783 : : }
784 : :
785 : : /* Pull and set To_chain */
786 : 2843 : strlcpy(chain->to_chain, chain_fields[4], sizeof(chain->to_chain));
787 : :
788 : : /* Pull and set to_chain rule position */
789 : 2843 : chain->rule_pos = strtol_wrapper(chain_fields[5],
790 : : 0, RCHK_MAX_IPT_RULE_NUM, NO_EXIT_UPON_ERR, &is_err);
791 [ + + ]: 2843 : if(is_err != FKO_SUCCESS)
792 : : {
793 : 1 : log_msg(LOG_ERR, "[*] invalid to_chain rule position in Line: %s",
794 : : conf_str);
795 : 1 : return 0;
796 : : }
797 : : return 1;
798 : : }
799 : :
800 : : int
801 : 2582 : fw_config_init(fko_srv_options_t * const opts)
802 : : {
803 : :
804 : : memset(&fwc, 0x0, sizeof(struct fw_config));
805 : :
806 : : /* Set our firewall exe command path (iptables in most cases).
807 : : */
808 : 2582 : strlcpy(fwc.fw_command, opts->config[CONF_FIREWALL_EXE], sizeof(fwc.fw_command));
809 : :
810 : : #if HAVE_LIBFIU
811 [ + + ]: 2582 : fiu_return_on("fw_config_init", 0);
812 : : #endif
813 : :
814 : : /* Pull the fwknop chain config info and setup our internal
815 : : * config struct. The IPT_INPUT is the only one that is
816 : : * required. The rest are optional.
817 : : */
818 [ + + ]: 2581 : if(set_fw_chain_conf(IPT_INPUT_ACCESS, opts->config[CONF_IPT_INPUT_ACCESS]) != 1)
819 : : return 0;
820 : :
821 : : /* The FWKNOP_OUTPUT_ACCESS requires ENABLE_IPT_OUTPUT_ACCESS be Y
822 : : */
823 [ + + ]: 2579 : if(strncasecmp(opts->config[CONF_ENABLE_IPT_OUTPUT], "Y", 1)==0)
824 [ + - ]: 1 : if(set_fw_chain_conf(IPT_OUTPUT_ACCESS, opts->config[CONF_IPT_OUTPUT_ACCESS]) != 1)
825 : : return 0;
826 : :
827 : : /* The remaining access chains require ENABLE_IPT_FORWARDING = Y
828 : : */
829 [ + + ]: 2579 : if(strncasecmp(opts->config[CONF_ENABLE_IPT_FORWARDING], "Y", 1)==0)
830 : : {
831 [ + - ]: 116 : if(set_fw_chain_conf(IPT_FORWARD_ACCESS, opts->config[CONF_IPT_FORWARD_ACCESS]) != 1)
832 : : return 0;
833 : :
834 [ + - ]: 116 : if(set_fw_chain_conf(IPT_DNAT_ACCESS, opts->config[CONF_IPT_DNAT_ACCESS]) != 1)
835 : : return 0;
836 : :
837 : : /* SNAT (whichever mode) requires ENABLE_IPT_SNAT = Y
838 : : */
839 [ + + ]: 116 : if(strncasecmp(opts->config[CONF_ENABLE_IPT_SNAT], "Y", 1)==0)
840 : : {
841 [ + + ]: 30 : if(opts->config[CONF_SNAT_TRANSLATE_IP] == NULL
842 [ - + ]: 15 : || ! is_valid_ipv4_addr(opts->config[CONF_SNAT_TRANSLATE_IP]))
843 : : {
844 : 15 : fwc.use_masquerade = 1;
845 [ + - ]: 15 : if(set_fw_chain_conf(IPT_MASQUERADE_ACCESS, opts->config[CONF_IPT_MASQUERADE_ACCESS]) != 1)
846 : : return 0;
847 : : }
848 : : else
849 : : {
850 [ + - ]: 15 : if(is_valid_ipv4_addr(opts->config[CONF_SNAT_TRANSLATE_IP]))
851 : : {
852 [ + - ]: 15 : if(set_fw_chain_conf(IPT_SNAT_ACCESS, opts->config[CONF_IPT_SNAT_ACCESS]) != 1)
853 : : return 0;
854 : : }
855 : : else
856 : : {
857 : : return 0;
858 : : }
859 : : }
860 : : }
861 : : }
862 : :
863 : : /* Let us find it via our opts struct as well.
864 : : */
865 : 2579 : opts->fw_config = &fwc;
866 : :
867 : 2579 : return 1;
868 : : }
869 : :
870 : : int
871 : 374 : fw_initialize(const fko_srv_options_t * const opts)
872 : : {
873 : 374 : int res = 1;
874 : :
875 : : /* See if iptables offers the '-C' argument (older versions don't). If not,
876 : : * then switch to parsing iptables -L output to find rules.
877 : : */
878 [ + + ]: 374 : if(opts->ipt_disable_check_support)
879 : 3 : have_ipt_chk_support = 0;
880 : : else
881 : 371 : ipt_chk_support(opts);
882 : :
883 : : /* Flush the chains (just in case) so we can start fresh.
884 : : */
885 [ + + ]: 374 : if(strncasecmp(opts->config[CONF_FLUSH_IPT_AT_INIT], "Y", 1) == 0)
886 : 366 : delete_all_chains(opts);
887 : :
888 : : /* Now create any configured chains.
889 : : */
890 [ - + ]: 374 : if(create_fw_chains(opts) != 0)
891 : : {
892 : 0 : log_msg(LOG_WARNING,
893 : : "Warning: Errors detected during fwknop custom chain creation");
894 : 0 : res = 0;
895 : : }
896 : :
897 : : /* Make sure that the 'comment' match is available
898 : : */
899 [ + - ]: 374 : if(strncasecmp(opts->config[CONF_ENABLE_IPT_COMMENT_CHECK], "Y", 1) == 0)
900 : : {
901 [ + + ]: 374 : if(comment_match_exists(opts) == 1)
902 : : {
903 : 371 : log_msg(LOG_INFO, "iptables 'comment' match is available");
904 : : }
905 : : else
906 : : {
907 : 3 : log_msg(LOG_WARNING, "Warning: Could not use the 'comment' match");
908 : 3 : res = 0;
909 : : }
910 : : }
911 : :
912 : 374 : return(res);
913 : : }
914 : :
915 : : int
916 : 391 : fw_cleanup(const fko_srv_options_t * const opts)
917 : : {
918 [ + + ]: 391 : if(strncasecmp(opts->config[CONF_FLUSH_IPT_AT_EXIT], "N", 1) == 0
919 [ - + ]: 8 : && opts->fw_flush == 0)
920 : : return(0);
921 : :
922 : 383 : delete_all_chains(opts);
923 : 383 : return(0);
924 : : }
925 : :
926 : : static int
927 : 301 : create_rule(const fko_srv_options_t * const opts,
928 : : const char * const fw_chain, const char * const fw_rule)
929 : : {
930 : 301 : int res = 0;
931 : :
932 : 301 : zero_cmd_buffers();
933 : :
934 : 301 : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s -A %s %s", opts->fw_config->fw_command, fw_chain, fw_rule);
935 : :
936 : 301 : res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, WANT_STDERR,
937 : : NO_TIMEOUT, &pid_status, opts);
938 : 301 : chop_newline(err_buf);
939 : :
940 : 301 : log_msg(LOG_DEBUG, "create_rule() CMD: '%s' (res: %d, err: %s)",
941 : : cmd_buf, res, err_buf);
942 : :
943 [ + - ]: 301 : if(EXTCMD_IS_SUCCESS(res))
944 : : {
945 : 301 : log_msg(LOG_DEBUG, "create_rule() Rule: '%s' added to %s", fw_rule, fw_chain);
946 : 301 : res = 1;
947 : : }
948 : : else
949 : 0 : log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf);
950 : :
951 : 301 : return res;
952 : : }
953 : :
954 : : /****************************************************************************/
955 : :
956 : : /* Rule Processing - Create an access request...
957 : : */
958 : : int
959 : 259 : process_spa_request(const fko_srv_options_t * const opts,
960 : : const acc_stanza_t * const acc, spa_data_t * const spadat)
961 : : {
962 : 259 : char nat_ip[MAX_IPV4_STR_LEN] = {0};
963 : 259 : char snat_target[SNAT_TARGET_BUFSIZE] = {0};
964 : 259 : char rule_buf[CMD_BUFSIZE] = {0};
965 : : char *ndx;
966 : :
967 : 259 : unsigned int nat_port = 0;
968 : :
969 : 259 : acc_port_list_t *port_list = NULL;
970 : 259 : acc_port_list_t *ple = NULL;
971 : :
972 : : unsigned int fst_proto;
973 : : unsigned int fst_port;
974 : :
975 : 259 : struct fw_chain * const in_chain = &(opts->fw_config->chain[IPT_INPUT_ACCESS]);
976 : 259 : struct fw_chain * const out_chain = &(opts->fw_config->chain[IPT_OUTPUT_ACCESS]);
977 : 259 : struct fw_chain * const fwd_chain = &(opts->fw_config->chain[IPT_FORWARD_ACCESS]);
978 : 259 : struct fw_chain * const dnat_chain = &(opts->fw_config->chain[IPT_DNAT_ACCESS]);
979 : : struct fw_chain *snat_chain; /* We assign this later (if we need to). */
980 : :
981 : 259 : int res = 0, is_err, snat_chain_num = 0;
982 : : time_t now;
983 : : unsigned int exp_ts;
984 : :
985 : : /* Parse and expand our access message.
986 : : */
987 [ - + ]: 259 : if(expand_acc_port_list(&port_list, spadat->spa_message_remain) != 1)
988 : : {
989 : : /* technically we would already have exited with an error if there were
990 : : * any memory allocation errors (see the add_port_list() function), but
991 : : * for completeness...
992 : : */
993 : 0 : free_acc_port_list(port_list);
994 : 0 : return res;
995 : : }
996 : :
997 : : /* Start at the top of the proto-port list...
998 : : */
999 : 259 : ple = port_list;
1000 : :
1001 : : /* Remember the first proto/port combo in case we need them
1002 : : * for NAT access requests.
1003 : : */
1004 : 259 : fst_proto = ple->proto;
1005 : 259 : fst_port = ple->port;
1006 : :
1007 : : /* Set our expire time value.
1008 : : */
1009 : 259 : time(&now);
1010 : 259 : exp_ts = now + spadat->fw_access_timeout;
1011 : :
1012 : : /* For straight access requests, we currently support multiple proto/port
1013 : : * request.
1014 : : */
1015 [ + + ]: 259 : if((spadat->message_type == FKO_ACCESS_MSG
1016 [ + + ]: 235 : || spadat->message_type == FKO_CLIENT_TIMEOUT_ACCESS_MSG) && !acc->force_nat)
1017 : : {
1018 : :
1019 : : /* Check to make sure that the jump rules exist for each
1020 : : * required chain
1021 : : */
1022 [ + - ]: 228 : if(chain_exists(opts, IPT_INPUT_ACCESS) == 0)
1023 : 228 : create_chain(opts, IPT_INPUT_ACCESS);
1024 : :
1025 [ - + ]: 228 : if(jump_rule_exists(opts, IPT_INPUT_ACCESS) == 0)
1026 : 0 : add_jump_rule(opts, IPT_INPUT_ACCESS);
1027 : :
1028 [ + + ]: 228 : if(strlen(out_chain->to_chain))
1029 : : {
1030 [ + - ]: 1 : if(chain_exists(opts, IPT_OUTPUT_ACCESS) == 0)
1031 : 1 : create_chain(opts, IPT_OUTPUT_ACCESS);
1032 : :
1033 [ - + ]: 1 : if(jump_rule_exists(opts, IPT_OUTPUT_ACCESS) == 0)
1034 : 228 : add_jump_rule(opts, IPT_OUTPUT_ACCESS);
1035 : : }
1036 : :
1037 : : /* Create an access command for each proto/port for the source ip.
1038 : : */
1039 [ + + ]: 460 : while(ple != NULL)
1040 : : {
1041 : : memset(rule_buf, 0, CMD_BUFSIZE);
1042 : :
1043 : 232 : snprintf(rule_buf, CMD_BUFSIZE-1, IPT_RULE_ARGS,
1044 : 232 : in_chain->table,
1045 : : ple->proto,
1046 : : spadat->use_src_ip,
1047 : : ple->port,
1048 : : exp_ts,
1049 : 232 : in_chain->target
1050 : : );
1051 : :
1052 [ + + ]: 232 : if(rule_exists(opts, in_chain, rule_buf,
1053 : 232 : ple->proto, spadat->use_src_ip, ple->port, exp_ts) == 0)
1054 : : {
1055 [ + - ]: 230 : if(create_rule(opts, in_chain->to_chain, rule_buf))
1056 : : {
1057 : 230 : log_msg(LOG_INFO, "Added Rule to %s for %s, %s expires at %u",
1058 : : in_chain->to_chain, spadat->use_src_ip,
1059 : : spadat->spa_message_remain, exp_ts
1060 : : );
1061 : :
1062 : 230 : in_chain->active_rules++;
1063 : :
1064 : : /* Reset the next expected expire time for this chain if it
1065 : : * is warranted.
1066 : : */
1067 [ + + ][ - + ]: 230 : if(in_chain->next_expire < now || exp_ts < in_chain->next_expire)
1068 : 224 : in_chain->next_expire = exp_ts;
1069 : : }
1070 : : }
1071 : :
1072 : : /* If we have to make an corresponding OUTPUT rule if out_chain target
1073 : : * is not NULL.
1074 : : */
1075 [ + + ]: 232 : if(strlen(out_chain->to_chain))
1076 : : {
1077 : : memset(rule_buf, 0, CMD_BUFSIZE);
1078 : :
1079 : 1 : snprintf(rule_buf, CMD_BUFSIZE-1, IPT_OUT_RULE_ARGS,
1080 : 1 : out_chain->table,
1081 : : ple->proto,
1082 : : spadat->use_src_ip,
1083 : : ple->port,
1084 : : exp_ts,
1085 : 1 : out_chain->target
1086 : : );
1087 : :
1088 [ + - ]: 1 : if(rule_exists(opts, out_chain, rule_buf,
1089 : 1 : ple->proto, spadat->use_src_ip, ple->port, exp_ts) == 0)
1090 : : {
1091 [ + - ]: 1 : if(create_rule(opts, out_chain->to_chain, rule_buf))
1092 : : {
1093 : 1 : log_msg(LOG_INFO, "Added Rule in %s for %s, %s expires at %u",
1094 : : out_chain->to_chain, spadat->use_src_ip,
1095 : : spadat->spa_message_remain, exp_ts
1096 : : );
1097 : :
1098 : 1 : out_chain->active_rules++;
1099 : :
1100 : : /* Reset the next expected expire time for this chain if it
1101 : : * is warranted. */
1102 [ - + ][ # # ]: 1 : if(out_chain->next_expire < now || exp_ts < out_chain->next_expire)
1103 : 1 : out_chain->next_expire = exp_ts;
1104 : : }
1105 : : }
1106 : : }
1107 : 232 : ple = ple->next;
1108 : : }
1109 : : }
1110 : : /* NAT requests... */
1111 [ + + ]: 31 : else if(spadat->message_type == FKO_LOCAL_NAT_ACCESS_MSG
1112 : : || spadat->message_type == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG
1113 : 31 : || spadat->message_type == FKO_NAT_ACCESS_MSG
1114 [ + - ]: 7 : || spadat->message_type == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG
1115 [ + - ]: 7 : || acc->force_nat)
1116 : : {
1117 : : /* Parse out the NAT IP and Port components.
1118 : : */
1119 [ + + ]: 31 : if(acc->force_nat)
1120 : : {
1121 : 11 : strlcpy(nat_ip, acc->force_nat_ip, sizeof(nat_ip));
1122 : 11 : nat_port = acc->force_nat_port;
1123 : : }
1124 : : else
1125 : : {
1126 : 20 : ndx = strchr(spadat->nat_access, ',');
1127 [ + - ]: 20 : if(ndx != NULL)
1128 : : {
1129 : 20 : strlcpy(nat_ip, spadat->nat_access, (ndx-spadat->nat_access)+1);
1130 [ - + ]: 20 : if (! is_valid_ipv4_addr(nat_ip))
1131 : : {
1132 : 0 : log_msg(LOG_INFO, "Invalid NAT IP in SPA message");
1133 : 0 : free_acc_port_list(port_list);
1134 : 0 : return res;
1135 : : }
1136 : :
1137 : 20 : nat_port = strtol_wrapper(ndx+1, 0, MAX_PORT, NO_EXIT_UPON_ERR, &is_err);
1138 [ - + ]: 20 : if(is_err != FKO_SUCCESS)
1139 : : {
1140 : 0 : log_msg(LOG_INFO, "Invalid NAT port in SPA message");
1141 : 0 : free_acc_port_list(port_list);
1142 : 0 : res = is_err;
1143 : 0 : return res;
1144 : : }
1145 : : }
1146 : : }
1147 : :
1148 [ + + ]: 31 : if(spadat->message_type == FKO_LOCAL_NAT_ACCESS_MSG)
1149 : : {
1150 : : memset(rule_buf, 0, CMD_BUFSIZE);
1151 : :
1152 : 9 : snprintf(rule_buf, CMD_BUFSIZE-1, IPT_RULE_ARGS,
1153 : 9 : in_chain->table,
1154 : : fst_proto,
1155 : : spadat->use_src_ip,
1156 : : nat_port,
1157 : : exp_ts,
1158 : 9 : in_chain->target
1159 : : );
1160 : :
1161 : : /* Check to make sure that the jump rules exist for each
1162 : : * required chain
1163 : : */
1164 [ + - ]: 9 : if(chain_exists(opts, IPT_INPUT_ACCESS) == 0)
1165 : 9 : create_chain(opts, IPT_INPUT_ACCESS);
1166 : :
1167 [ + + ]: 9 : if(jump_rule_exists(opts, IPT_INPUT_ACCESS) == 0)
1168 : 2 : add_jump_rule(opts, IPT_INPUT_ACCESS);
1169 : :
1170 [ + - ]: 9 : if(rule_exists(opts, in_chain, rule_buf,
1171 : 9 : fst_proto, spadat->use_src_ip, nat_port, exp_ts) == 0)
1172 : : {
1173 [ + - ]: 9 : if(create_rule(opts, in_chain->to_chain, rule_buf))
1174 : : {
1175 : 9 : log_msg(LOG_INFO, "Added Rule to %s for %s, %s expires at %u",
1176 : : in_chain->to_chain, spadat->use_src_ip,
1177 : : spadat->spa_message_remain, exp_ts
1178 : : );
1179 : :
1180 : 9 : in_chain->active_rules++;
1181 : :
1182 : : /* Reset the next expected expire time for this chain if it
1183 : : * is warranted.
1184 : : */
1185 [ - + ][ # # ]: 9 : if(in_chain->next_expire < now || exp_ts < in_chain->next_expire)
1186 : 9 : in_chain->next_expire = exp_ts;
1187 : : }
1188 : : }
1189 : : }
1190 [ + - ]: 22 : else if(strlen(fwd_chain->to_chain))
1191 : : {
1192 : : /* Make our FORWARD and NAT rules, and make sure the
1193 : : * required chain and jump rule exists
1194 : : */
1195 [ + - ]: 22 : if(chain_exists(opts, IPT_FORWARD_ACCESS) == 0)
1196 : 22 : create_chain(opts, IPT_FORWARD_ACCESS);
1197 : :
1198 [ + + ]: 22 : if (jump_rule_exists(opts, IPT_FORWARD_ACCESS) == 0)
1199 : 3 : add_jump_rule(opts, IPT_FORWARD_ACCESS);
1200 : :
1201 : : memset(rule_buf, 0, CMD_BUFSIZE);
1202 : :
1203 : 22 : snprintf(rule_buf, CMD_BUFSIZE-1, IPT_FWD_RULE_ARGS,
1204 : 22 : fwd_chain->table,
1205 : : fst_proto,
1206 : : spadat->use_src_ip,
1207 : : nat_ip,
1208 : : nat_port,
1209 : : exp_ts,
1210 : 22 : fwd_chain->target
1211 : : );
1212 : :
1213 [ + - ]: 22 : if(rule_exists(opts, fwd_chain, rule_buf, fst_proto,
1214 : 22 : spadat->use_src_ip, nat_port, exp_ts) == 0)
1215 : : {
1216 [ + - ]: 22 : if(create_rule(opts, fwd_chain->to_chain, rule_buf))
1217 : : {
1218 : 22 : log_msg(LOG_INFO, "Added FORWARD Rule to %s for %s, %s expires at %u",
1219 : : fwd_chain->to_chain, spadat->use_src_ip,
1220 : : spadat->spa_message_remain, exp_ts
1221 : : );
1222 : :
1223 : 22 : fwd_chain->active_rules++;
1224 : :
1225 : : /* Reset the next expected expire time for this chain if it
1226 : : * is warranted.
1227 : : */
1228 [ - + ][ # # ]: 22 : if(fwd_chain->next_expire < now || exp_ts < fwd_chain->next_expire)
1229 : 22 : fwd_chain->next_expire = exp_ts;
1230 : : }
1231 : : }
1232 : : }
1233 : :
1234 [ + - ]: 31 : if(strlen(dnat_chain->to_chain))
1235 : : {
1236 : : /* Make sure the required chain and jump rule exist
1237 : : */
1238 [ + - ]: 31 : if(chain_exists(opts, IPT_DNAT_ACCESS) == 0)
1239 : 31 : create_chain(opts, IPT_DNAT_ACCESS);
1240 : :
1241 [ + + ]: 31 : if (jump_rule_exists(opts, IPT_DNAT_ACCESS) == 0)
1242 : 5 : add_jump_rule(opts, IPT_DNAT_ACCESS);
1243 : :
1244 : : memset(rule_buf, 0, CMD_BUFSIZE);
1245 : :
1246 : 31 : snprintf(rule_buf, CMD_BUFSIZE-1, IPT_DNAT_RULE_ARGS,
1247 : 31 : dnat_chain->table,
1248 : : fst_proto,
1249 : : spadat->use_src_ip,
1250 : : fst_port,
1251 : : exp_ts,
1252 : 31 : dnat_chain->target,
1253 : : nat_ip,
1254 : : nat_port
1255 : : );
1256 : :
1257 [ + - ]: 31 : if(rule_exists(opts, dnat_chain, rule_buf, fst_proto,
1258 : 31 : spadat->use_src_ip, fst_port, exp_ts) == 0)
1259 : : {
1260 [ + - ]: 31 : if(create_rule(opts, dnat_chain->to_chain, rule_buf))
1261 : : {
1262 : 31 : log_msg(LOG_INFO, "Added DNAT Rule to %s for %s, %s expires at %u",
1263 : : dnat_chain->to_chain, spadat->use_src_ip,
1264 : : spadat->spa_message_remain, exp_ts
1265 : : );
1266 : :
1267 : 31 : dnat_chain->active_rules++;
1268 : :
1269 : : /* Reset the next expected expire time for this chain if it
1270 : : * is warranted.
1271 : : */
1272 [ - + ][ # # ]: 31 : if(dnat_chain->next_expire < now || exp_ts < dnat_chain->next_expire)
1273 : 31 : dnat_chain->next_expire = exp_ts;
1274 : : }
1275 : : }
1276 : : }
1277 : :
1278 : : /* If SNAT (or MASQUERADE) is wanted, then we add those rules here as well.
1279 : : */
1280 [ + + ][ + + ]: 31 : if(acc->force_snat || strncasecmp(opts->config[CONF_ENABLE_IPT_SNAT], "Y", 1) == 0)
1281 : : {
1282 : : /* Add SNAT or MASQUERADE rules.
1283 : : */
1284 [ + + ][ + + ]: 8 : if(acc->force_snat && is_valid_ipv4_addr(acc->force_snat_ip))
1285 : : {
1286 : : /* Using static SNAT */
1287 : 2 : snat_chain = &(opts->fw_config->chain[IPT_SNAT_ACCESS]);
1288 : 2 : snprintf(snat_target, SNAT_TARGET_BUFSIZE-1,
1289 : : "--to-source %s:%i", acc->force_snat_ip, fst_port);
1290 : 2 : snat_chain_num = IPT_SNAT_ACCESS;
1291 : : }
1292 [ + + ][ + - ]: 6 : else if(acc->force_snat && acc->force_masquerade)
1293 : : {
1294 : : /* Using MASQUERADE */
1295 : 2 : snat_chain = &(opts->fw_config->chain[IPT_MASQUERADE_ACCESS]);
1296 : : snprintf(snat_target, SNAT_TARGET_BUFSIZE-1,
1297 : : "--to-ports %i", fst_port);
1298 : 2 : snat_chain_num = IPT_MASQUERADE_ACCESS;
1299 : : }
1300 [ + + ]: 4 : else if((opts->config[CONF_SNAT_TRANSLATE_IP] != NULL)
1301 [ + - ]: 2 : && is_valid_ipv4_addr(opts->config[CONF_SNAT_TRANSLATE_IP]))
1302 : : {
1303 : : /* Using static SNAT */
1304 : 2 : snat_chain = &(opts->fw_config->chain[IPT_SNAT_ACCESS]);
1305 : 2 : snprintf(snat_target, SNAT_TARGET_BUFSIZE-1,
1306 : : "--to-source %s:%i", opts->config[CONF_SNAT_TRANSLATE_IP],
1307 : : fst_port);
1308 : 2 : snat_chain_num = IPT_SNAT_ACCESS;
1309 : : }
1310 : : else
1311 : : {
1312 : : /* Using MASQUERADE */
1313 : 2 : snat_chain = &(opts->fw_config->chain[IPT_MASQUERADE_ACCESS]);
1314 : : snprintf(snat_target, SNAT_TARGET_BUFSIZE-1,
1315 : : "--to-ports %i", fst_port);
1316 : 2 : snat_chain_num = IPT_MASQUERADE_ACCESS;
1317 : : }
1318 : :
1319 [ + - ]: 8 : if(chain_exists(opts, snat_chain_num) == 0)
1320 : 8 : create_chain(opts, snat_chain_num);
1321 : :
1322 [ + + ]: 8 : if(jump_rule_exists(opts, snat_chain_num) == 0)
1323 : 2 : add_jump_rule(opts, snat_chain_num);
1324 : :
1325 : : memset(rule_buf, 0, CMD_BUFSIZE);
1326 : :
1327 : : snprintf(rule_buf, CMD_BUFSIZE-1, IPT_SNAT_RULE_ARGS,
1328 : 8 : snat_chain->table,
1329 : : fst_proto,
1330 : : nat_ip,
1331 : : nat_port,
1332 : : exp_ts,
1333 : 8 : snat_chain->target,
1334 : : snat_target
1335 : : );
1336 : :
1337 [ + - ]: 8 : if(rule_exists(opts, snat_chain, rule_buf, fst_proto,
1338 : 8 : spadat->use_src_ip, nat_port, exp_ts) == 0)
1339 : : {
1340 [ + - ]: 8 : if(create_rule(opts, snat_chain->to_chain, rule_buf))
1341 : : {
1342 : 8 : log_msg(LOG_INFO, "Added SNAT Rule to %s for %s, %s expires at %u",
1343 : : snat_chain->to_chain, spadat->use_src_ip,
1344 : : spadat->spa_message_remain, exp_ts
1345 : : );
1346 : :
1347 : 8 : snat_chain->active_rules++;
1348 : :
1349 : : /* Reset the next expected expire time for this chain if it
1350 : : * is warranted.
1351 : : */
1352 [ - + ][ # # ]: 8 : if(snat_chain->next_expire < now || exp_ts < snat_chain->next_expire)
1353 : 8 : snat_chain->next_expire = exp_ts;
1354 : : }
1355 : : }
1356 : : }
1357 : : }
1358 : :
1359 : : /* Done with the port list for access rules.
1360 : : */
1361 : 259 : free_acc_port_list(port_list);
1362 : :
1363 : 259 : return(res);
1364 : : }
1365 : :
1366 : : /* Iterate over the configure firewall access chains and purge expired
1367 : : * firewall rules.
1368 : : */
1369 : : void
1370 : 19987 : check_firewall_rules(const fko_srv_options_t * const opts)
1371 : : {
1372 : 19987 : char exp_str[12] = {0};
1373 : 19987 : char rule_num_str[6] = {0};
1374 : : char *ndx, *rn_start, *rn_end, *tmp_mark;
1375 : :
1376 : : int i, res, rn_offset, rule_num, is_err;
1377 : 19987 : time_t now, rule_exp, min_exp = 0;
1378 : :
1379 : 19987 : struct fw_chain *ch = opts->fw_config->chain;
1380 : :
1381 : 19987 : time(&now);
1382 : :
1383 : : /* Iterate over each chain and look for active rules to delete.
1384 : : */
1385 [ + + ]: 139909 : for(i=0; i < NUM_FWKNOP_ACCESS_TYPES; i++)
1386 : : {
1387 : : /* If there are no active rules or we have not yet
1388 : : * reached our expected next expire time, continue.
1389 : : */
1390 [ + + ][ + + ]: 119922 : if(ch[i].active_rules == 0 || ch[i].next_expire > now)
1391 : 119591 : continue;
1392 : :
1393 : 331 : zero_cmd_buffers();
1394 : :
1395 : 331 : rn_offset = 0;
1396 : :
1397 : : /* There should be a rule to delete. Get the current list of
1398 : : * rules for this chain and delete the ones that are expired.
1399 : : */
1400 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_LIST_RULES_ARGS,
1401 : 331 : opts->fw_config->fw_command,
1402 : 331 : ch[i].table,
1403 : 331 : ch[i].to_chain
1404 : : );
1405 : :
1406 : 331 : res = run_extcmd(cmd_buf, cmd_out, STANDARD_CMD_OUT_BUFSIZE,
1407 : : WANT_STDERR, NO_TIMEOUT, &pid_status, opts);
1408 : 331 : chop_newline(cmd_out);
1409 : :
1410 : 331 : log_msg(LOG_DEBUG, "check_firewall_rules() CMD: '%s' (res: %d, cmd_out: %s)",
1411 : : cmd_buf, res, cmd_out);
1412 : :
1413 [ - + ]: 331 : if(!EXTCMD_IS_SUCCESS(res))
1414 : : {
1415 : 0 : log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, cmd_out);
1416 : 0 : continue;
1417 : : }
1418 : :
1419 : 331 : log_msg(LOG_DEBUG, "RES=%i, CMD_BUF: %s\nRULES LIST: %s", res, cmd_buf, cmd_out);
1420 : :
1421 : 331 : ndx = strstr(cmd_out, EXPIRE_COMMENT_PREFIX);
1422 [ + - ]: 331 : if(ndx == NULL)
1423 : : {
1424 : : /* we did not find an expected rule.
1425 : : */
1426 : 0 : log_msg(LOG_ERR,
1427 : : "Did not find expire comment in rules list %i", i);
1428 : :
1429 [ # # ]: 0 : if (ch[i].active_rules > 0)
1430 : 0 : ch[i].active_rules--;
1431 : :
1432 : 0 : continue;
1433 : : }
1434 : :
1435 : : /* walk the list and process rules as needed.
1436 : : */
1437 [ + + ]: 662 : while (ndx != NULL) {
1438 : : /* Jump forward and extract the timestamp
1439 : : */
1440 : 331 : ndx += strlen(EXPIRE_COMMENT_PREFIX);
1441 : :
1442 : : /* remember this spot for when we look for the next
1443 : : * rule.
1444 : : */
1445 : 331 : tmp_mark = ndx;
1446 : :
1447 : 331 : strlcpy(exp_str, ndx, sizeof(exp_str));
1448 : 331 : rule_exp = (time_t)atoll(exp_str);
1449 : :
1450 [ + + ]: 331 : if(rule_exp <= now)
1451 : : {
1452 : : /* Backtrack and get the rule number and delete it.
1453 : : */
1454 : : rn_start = ndx;
1455 [ + - ]: 26262 : while(--rn_start > cmd_out)
1456 : : {
1457 [ + + ]: 26563 : if(*rn_start == '\n')
1458 : : break;
1459 : : }
1460 : :
1461 [ - + ]: 301 : if(*rn_start != '\n')
1462 : : {
1463 : : /* This should not happen. But if it does, complain,
1464 : : * decrement the active rule value, and go on.
1465 : : */
1466 : 0 : log_msg(LOG_ERR,
1467 : : "Rule parse error while finding rule line start in chain %i", i);
1468 : :
1469 [ # # ]: 0 : if (ch[i].active_rules > 0)
1470 : 0 : ch[i].active_rules--;
1471 : :
1472 : : break;
1473 : : }
1474 : 301 : rn_start++;
1475 : :
1476 : 301 : rn_end = strchr(rn_start, ' ');
1477 [ - + ]: 301 : if(rn_end == NULL)
1478 : : {
1479 : : /* This should not happen. But if it does, complain,
1480 : : * decrement the active rule value, and go on.
1481 : : */
1482 : 0 : log_msg(LOG_ERR,
1483 : : "Rule parse error while finding rule number in chain %i", i);
1484 : :
1485 [ # # ]: 0 : if (ch[i].active_rules > 0)
1486 : 0 : ch[i].active_rules--;
1487 : :
1488 : : break;
1489 : : }
1490 : :
1491 : 301 : strlcpy(rule_num_str, rn_start, (rn_end - rn_start)+1);
1492 : :
1493 : 301 : rule_num = strtol_wrapper(rule_num_str, rn_offset, RCHK_MAX_IPT_RULE_NUM,
1494 : : NO_EXIT_UPON_ERR, &is_err);
1495 [ - + ]: 301 : if(is_err != FKO_SUCCESS)
1496 : : {
1497 : 0 : log_msg(LOG_ERR,
1498 : : "Rule parse error while finding rule number in chain %i", i);
1499 : :
1500 [ # # ]: 0 : if (ch[i].active_rules > 0)
1501 : 0 : ch[i].active_rules--;
1502 : :
1503 : : break;
1504 : : }
1505 : :
1506 : 301 : zero_cmd_buffers();
1507 : :
1508 : 301 : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_DEL_RULE_ARGS,
1509 : 301 : opts->fw_config->fw_command,
1510 : : ch[i].table,
1511 : : ch[i].to_chain,
1512 : : rule_num - rn_offset
1513 : : );
1514 : :
1515 : 301 : res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE,
1516 : : WANT_STDERR, NO_TIMEOUT, &pid_status, opts);
1517 : 301 : chop_newline(err_buf);
1518 : :
1519 : 301 : log_msg(LOG_DEBUG, "check_firewall_rules() CMD: '%s' (res: %d, err: %s)",
1520 : : cmd_buf, res, err_buf);
1521 : :
1522 [ + - ]: 301 : if(EXTCMD_IS_SUCCESS(res))
1523 : : {
1524 : 301 : log_msg(LOG_INFO, "Removed rule %s from %s with expire time of %u",
1525 : : rule_num_str, ch[i].to_chain, rule_exp
1526 : : );
1527 : :
1528 : 301 : rn_offset++;
1529 : :
1530 [ + - ]: 301 : if (ch[i].active_rules > 0)
1531 : 301 : ch[i].active_rules--;
1532 : : }
1533 : : else
1534 : 0 : log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf);
1535 : :
1536 : : }
1537 : : else
1538 : : {
1539 : : /* Track the minimum future rule expire time.
1540 : : */
1541 [ + - ]: 30 : if(rule_exp > now)
1542 : 30 : min_exp = (min_exp < rule_exp) ? min_exp : rule_exp;
1543 : : }
1544 : :
1545 : : /* Push our tracking index forward beyond (just processed) _exp_
1546 : : * string so we can continue to the next rule in the list.
1547 : : */
1548 : 331 : ndx = strstr(tmp_mark, EXPIRE_COMMENT_PREFIX);
1549 : : }
1550 : :
1551 : : /* Set the next pending expire time accordingly. 0 if there are no
1552 : : * more rules, or whatever the next expected (min_exp) time will be.
1553 : : */
1554 [ + + ]: 331 : if(ch[i].active_rules < 1)
1555 : 295 : ch[i].next_expire = 0;
1556 [ - + ]: 36 : else if(min_exp)
1557 : 0 : ch[i].next_expire = min_exp;
1558 : : }
1559 : 19987 : }
1560 : :
1561 : : int
1562 : 23644 : validate_ipt_chain_conf(const char * const chain_str)
1563 : : {
1564 : 23644 : int j, rv = 1;
1565 : 23644 : const char *ndx = chain_str;
1566 : :
1567 : 23644 : j = 1;
1568 [ + + ]: 1114128 : while(*ndx != '\0')
1569 : : {
1570 [ + + ]: 1090486 : if(*ndx == ',')
1571 : 118192 : j++;
1572 : :
1573 [ + + ]: 1090486 : if(*ndx != '\0'
1574 : 1090486 : && *ndx != ' '
1575 [ + + ]: 972293 : && *ndx != ','
1576 [ + + ]: 854101 : && *ndx != '_'
1577 [ + + ]: 830444 : && isalnum(*ndx) == 0)
1578 : : {
1579 : : rv = 0;
1580 : : break;
1581 : : }
1582 : 1090484 : ndx++;
1583 : : }
1584 : :
1585 : : /* Sanity check - j should be the number of chain fields
1586 : : * (excluding the type).
1587 : : */
1588 [ + + ]: 23644 : if(j != FW_NUM_CHAIN_FIELDS)
1589 : 7 : rv = 0;
1590 : :
1591 : 23644 : return rv;
1592 : : }
1593 : :
1594 : : #endif /* FIREWALL_IPTABLES */
1595 : :
1596 : : /***EOF***/
|