Branch data Line data Source code
1 : : /**
2 : : * \file server/fw_util_firewalld.c
3 : : *
4 : : * \brief Fwknop routines for managing firewalld firewall rules.
5 : : */
6 : :
7 : : /* Fwknop is developed primarily by the people listed in the file 'AUTHORS'.
8 : : * Copyright (C) 2009-2015 fwknop developers and contributors. For a full
9 : : * list of contributors, see the file 'CREDITS'.
10 : : *
11 : : * License (GNU General Public License):
12 : : *
13 : : * This program is free software; you can redistribute it and/or
14 : : * modify it under the terms of the GNU General Public License
15 : : * as published by the Free Software Foundation; either version 2
16 : : * of the License, or (at your option) any later version.
17 : : *
18 : : * This program is distributed in the hope that it will be useful,
19 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 : : * GNU General Public License for more details.
22 : : *
23 : : * You should have received a copy of the GNU General Public License
24 : : * along with this program; if not, write to the Free Software
25 : : * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
26 : : * USA
27 : : *
28 : : *****************************************************************************
29 : : */
30 : :
31 : : #include "fwknopd_common.h"
32 : :
33 : : #ifdef FIREWALL_FIREWALLD
34 : :
35 : : #include "fw_util.h"
36 : : #include "utils.h"
37 : : #include "log_msg.h"
38 : : #include "extcmd.h"
39 : : #include "access.h"
40 : :
41 : : static struct fw_config fwc;
42 : : static char cmd_buf[CMD_BUFSIZE];
43 : : static char err_buf[CMD_BUFSIZE];
44 : : static char cmd_out[STANDARD_CMD_OUT_BUFSIZE];
45 : :
46 : : /* assume 'firewall-cmd --direct --passthrough ipv4 -C' is offered
47 : : * (see firewd_chk_support()).
48 : : */
49 : : static int have_firewd_chk_support = 1;
50 : :
51 : : static void
52 : 18233 : zero_cmd_buffers(void)
53 : : {
54 : : memset(cmd_buf, 0x0, CMD_BUFSIZE);
55 : : memset(err_buf, 0x0, CMD_BUFSIZE);
56 : : memset(cmd_out, 0x0, STANDARD_CMD_OUT_BUFSIZE);
57 : 18233 : }
58 : :
59 : : static int pid_status = 0;
60 : :
61 : : static int
62 : 14 : rule_exists_no_chk_support(const fko_srv_options_t * const opts,
63 : : const struct fw_chain * const fwc,
64 : : const unsigned int proto,
65 : : const char * const srcip,
66 : : const char * const dstip,
67 : : const unsigned int port,
68 : : const char * const natip,
69 : : const unsigned int nat_port,
70 : : const unsigned int exp_ts)
71 : : {
72 : 14 : int rule_exists=0;
73 : 14 : char fw_line_buf[CMD_BUFSIZE] = {0};
74 : 14 : char target_search[CMD_BUFSIZE] = {0};
75 : 14 : char proto_search[CMD_BUFSIZE] = {0};
76 : 14 : char srcip_search[CMD_BUFSIZE] = {0};
77 : 14 : char dstip_search[CMD_BUFSIZE] = {0};
78 : 14 : char natip_search[CMD_BUFSIZE] = {0};
79 : 14 : char port_search[CMD_BUFSIZE] = {0};
80 : 14 : char nat_port_search[CMD_BUFSIZE] = {0};
81 : 14 : char exp_ts_search[CMD_BUFSIZE] = {0};
82 : 14 : char *ndx = NULL;
83 : :
84 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_LIST_RULES_ARGS,
85 : 14 : opts->fw_config->fw_command,
86 : 14 : fwc->table,
87 : 14 : fwc->to_chain
88 : : );
89 : :
90 : : #if CODE_COVERAGE
91 : : /* If we're maximizing code coverage, then exercise the run_extcmd_write()
92 : : * function which is normally only used for the PF firewall. This is to
93 : : * maximize code coverage in conjunction with the test suite, and is never
94 : : * compiled in for a production release of fwknop.
95 : : */
96 [ + - ]: 14 : if(run_extcmd_write("/bin/grep -v test", "/bin/echo test", &pid_status, opts) == 0)
97 : 14 : log_msg(LOG_WARNING, "[ignore] Code coverage: Executed command");
98 : : #endif
99 : :
100 [ + + ]: 14 : if(proto == IPPROTO_TCP)
101 : : snprintf(proto_search, CMD_BUFSIZE-1, " tcp ");
102 [ + - ]: 1 : else if(proto == IPPROTO_UDP)
103 : : snprintf(proto_search, CMD_BUFSIZE-1, " udp ");
104 [ # # ]: 0 : else if(proto == IPPROTO_ICMP)
105 : : snprintf(proto_search, CMD_BUFSIZE-1, " icmp ");
106 : : else
107 : : snprintf(proto_search, CMD_BUFSIZE-1, " %u ", proto);
108 : :
109 : : snprintf(port_search, CMD_BUFSIZE-1, "dpt:%u ", port);
110 : : snprintf(nat_port_search, CMD_BUFSIZE-1, ":%u", nat_port);
111 : 14 : snprintf(target_search, CMD_BUFSIZE-1, " %s ", fwc->target);
112 : :
113 [ + - ]: 14 : if (srcip != NULL)
114 : : snprintf(srcip_search, CMD_BUFSIZE-1, " %s ", srcip);
115 : :
116 [ - + ]: 14 : if (dstip != NULL)
117 : : snprintf(dstip_search, CMD_BUFSIZE-1, " %s ", dstip);
118 : :
119 [ + + ]: 14 : if (natip != NULL)
120 : : snprintf(natip_search, CMD_BUFSIZE-1, " to:%s", natip);
121 : :
122 : : snprintf(exp_ts_search, CMD_BUFSIZE-1, "%u ", exp_ts);
123 : :
124 : : /* search for each of the substrings - the rule expiration time is the
125 : : * primary search method
126 : : */
127 [ + + ]: 14 : if(search_extcmd_getline(cmd_buf, fw_line_buf,
128 : : CMD_BUFSIZE, NO_TIMEOUT, exp_ts_search, &pid_status, opts))
129 : : {
130 : 5 : chop_newline(fw_line_buf);
131 : : /* we have an iptables policy rule that matches the
132 : : * expiration time, so make sure this rule matches the
133 : : * other fields too. If not, then it is for different
134 : : * access requested by a separate SPA packet.
135 : : */
136 [ + - ]: 5 : if(((proto == ANY_PROTO) ? 1 : (strstr(fw_line_buf, proto_search) != NULL))
137 [ + - ][ + - ]: 5 : && ((srcip == NULL) ? 1 : (strstr(fw_line_buf, srcip_search) != NULL))
[ + - ]
138 [ - + ][ # # ]: 5 : && ((dstip == NULL) ? 1 : (strstr(fw_line_buf, dstip_search) != NULL))
139 [ + + ][ + - ]: 5 : && ((natip == NULL) ? 1 : (strstr(fw_line_buf, natip_search) != NULL))
140 [ + - ]: 5 : && (strstr(fw_line_buf, target_search) != NULL)
141 [ + - ][ + + ]: 5 : && ((port == ANY_PORT) ? 1 : (strstr(fw_line_buf, port_search) != NULL)))
142 : : {
143 : 4 : rule_exists = 1;
144 : : }
145 : : }
146 : :
147 : : /* If there is a nat port, we have to qualify it as part
148 : : * of the 'to:<ip>:<port>' portion of the rule (at the end)
149 : : */
150 [ + + ]: 14 : if(rule_exists && nat_port != NAT_ANY_PORT)
151 : : {
152 : 2 : ndx = strstr(fw_line_buf, " to:");
153 : : /* Make sure there isn't a duplicate " to:" string (i.e. if someone
154 : : * was trying to be tricky with the iptables comment match).
155 : : */
156 [ + - ][ + - ]: 2 : if(ndx != NULL && (strstr((ndx+strlen(" to:")), " to:") == NULL))
157 : : {
158 : 2 : ndx = strstr((ndx+strlen(" to:")), nat_port_search);
159 [ + - ]: 2 : if (ndx == NULL)
160 : : {
161 : : rule_exists = 0;
162 : : }
163 [ + + ]: 2 : else if((*(ndx+strlen(nat_port_search)) != '\0')
164 [ + - ]: 1 : && (*(ndx+strlen(nat_port_search)) != ' '))
165 : : {
166 : 1 : rule_exists = 0;
167 : : }
168 : : }
169 : : else
170 : : {
171 : : rule_exists = 0;
172 : : }
173 : : }
174 : :
175 [ + + ]: 14 : if(rule_exists)
176 : 3 : log_msg(LOG_DEBUG,
177 : : "rule_exists_no_chk_support() %s %u -> %s expires: %u rule already exists",
178 : : proto_search, port, srcip, exp_ts);
179 : : else
180 : 11 : log_msg(LOG_DEBUG,
181 : : "rule_exists_no_chk_support() %s %u -> %s expires: %u rule does not exist",
182 : : proto_search, port, srcip, exp_ts);
183 : :
184 : 14 : return(rule_exists);
185 : : }
186 : :
187 : : static int
188 : 2943 : rule_exists_chk_support(const fko_srv_options_t * const opts,
189 : : const char * const chain, const char * const rule)
190 : : {
191 : 2943 : int rule_exists = 0;
192 : 2943 : int res = 0;
193 : :
194 : 2943 : zero_cmd_buffers();
195 : :
196 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_CHK_RULE_ARGS,
197 : 2943 : opts->fw_config->fw_command, chain, rule);
198 : :
199 : 2943 : res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE,
200 : : WANT_STDERR, NO_TIMEOUT, &pid_status, opts);
201 : 2943 : chop_newline(err_buf);
202 : :
203 : 2943 : log_msg(LOG_DEBUG,
204 : : "rule_exists_chk_support() CMD: '%s' (res: %d, err: %s)",
205 : : cmd_buf, res, err_buf);
206 : :
207 [ + + ]: 2943 : if(strncmp(err_buf, "success", strlen("success")) == 0)
208 : : {
209 : 917 : rule_exists = 1;
210 : 917 : log_msg(LOG_DEBUG,
211 : : "rule_exists_chk_support() Rule : '%s' in %s already exists",
212 : : rule, chain);
213 : : }
214 : : else
215 : : {
216 : 2026 : log_msg(LOG_DEBUG,
217 : : "rule_exists_chk_support() Rule : '%s' in %s does not exist",
218 : : rule, chain);
219 : : }
220 : :
221 : 2943 : return(rule_exists);
222 : : }
223 : :
224 : : static int
225 : 382 : rule_exists(const fko_srv_options_t * const opts,
226 : : const struct fw_chain * const fwc,
227 : : const char * const rule,
228 : : const unsigned int proto,
229 : : const char * const srcip,
230 : : const char * const dstip,
231 : : const unsigned int port,
232 : : const char * const nat_ip,
233 : : const unsigned int nat_port,
234 : : const unsigned int exp_ts)
235 : : {
236 : 382 : int rule_exists = 0;
237 : :
238 [ + + ]: 382 : if(have_firewd_chk_support == 1)
239 : 368 : rule_exists = rule_exists_chk_support(opts, fwc->to_chain, rule);
240 : : else
241 [ - + ]: 14 : rule_exists = rule_exists_no_chk_support(opts, fwc, proto, srcip,
242 : 14 : (opts->fw_config->use_destination ? dstip : NULL), port,
243 : : nat_ip, nat_port, exp_ts);
244 : :
245 [ + + ]: 382 : if(rule_exists == 1)
246 : 7 : log_msg(LOG_DEBUG, "rule_exists() Rule : '%s' in %s already exists",
247 : 7 : rule, fwc->to_chain);
248 : : else
249 : 375 : log_msg(LOG_DEBUG, "rule_exists() Rule : '%s' in %s does not exist",
250 : 375 : rule, fwc->to_chain);
251 : :
252 : 382 : return(rule_exists);
253 : : }
254 : :
255 : : static void
256 : 435 : firewd_chk_support(const fko_srv_options_t * const opts)
257 : : {
258 : 435 : int res = 1;
259 : 435 : struct fw_chain *in_chain = &(opts->fw_config->chain[FIREWD_INPUT_ACCESS]);
260 : :
261 : 435 : zero_cmd_buffers();
262 : :
263 : : /* Add a harmless rule to the firewalld INPUT chain and see if firewalld
264 : : * supports '-C' to check for it. Set "have_firewd_chk_support" accordingly,
265 : : * delete the rule, and return.
266 : : */
267 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_TMP_CHK_RULE_ARGS,
268 : 435 : opts->fw_config->fw_command,
269 : 435 : in_chain->table,
270 : 435 : in_chain->from_chain,
271 : : 1, /* first rule */
272 : 435 : in_chain->target
273 : : );
274 : :
275 : 435 : res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE,
276 : : WANT_STDERR, NO_TIMEOUT, &pid_status, opts);
277 : 435 : chop_newline(err_buf);
278 : :
279 : 435 : log_msg(LOG_DEBUG, "firewd_chk_support() CMD: '%s' (res: %d, err: %s)",
280 : : cmd_buf, res, err_buf);
281 : :
282 : 435 : zero_cmd_buffers();
283 : :
284 : : /* Now see if '-C' works
285 : : */
286 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_TMP_VERIFY_CHK_ARGS,
287 : 435 : opts->fw_config->fw_command,
288 : : in_chain->table,
289 : : in_chain->from_chain,
290 : : in_chain->target
291 : : );
292 : :
293 : 435 : res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE,
294 : : WANT_STDERR, NO_TIMEOUT, &pid_status, opts);
295 : 435 : chop_newline(err_buf);
296 : :
297 : 435 : log_msg(LOG_DEBUG, "firewd_chk_support() CMD: '%s' (res: %d, err: %s)",
298 : : cmd_buf, res, err_buf);
299 : :
300 [ + + ]: 435 : if(strncmp(err_buf, "success", strlen("success")) == 0)
301 : : {
302 : 432 : log_msg(LOG_DEBUG, "firewd_chk_support() -C supported");
303 : 432 : have_firewd_chk_support = 1;
304 : : }
305 : : else
306 : : {
307 : 3 : log_msg(LOG_DEBUG, "firewd_chk_support() -C not supported");
308 : 3 : have_firewd_chk_support = 0;
309 : : }
310 : :
311 : : /* Delete the tmp rule
312 : : */
313 : 435 : zero_cmd_buffers();
314 : :
315 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_DEL_RULE_ARGS,
316 : 435 : opts->fw_config->fw_command,
317 : : in_chain->table,
318 : : in_chain->from_chain,
319 : : 1
320 : : );
321 : 435 : run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE,
322 : : WANT_STDERR, NO_TIMEOUT, &pid_status, opts);
323 : :
324 : 435 : return;
325 : : }
326 : :
327 : : static int
328 : 444 : comment_match_exists(const fko_srv_options_t * const opts)
329 : : {
330 : 444 : int res = 1;
331 : 444 : char *ndx = NULL;
332 : 444 : struct fw_chain *in_chain = &(opts->fw_config->chain[FIREWD_INPUT_ACCESS]);
333 : :
334 : 444 : zero_cmd_buffers();
335 : :
336 : : /* Add a harmless rule to the firewalld INPUT chain that uses the comment
337 : : * match and make sure it exists. If not, return zero. Otherwise, delete
338 : : * the rule and return true.
339 : : */
340 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_TMP_COMMENT_ARGS,
341 : 444 : opts->fw_config->fw_command,
342 : 444 : in_chain->table,
343 : 444 : in_chain->from_chain,
344 : : 1, /* first rule */
345 : 444 : in_chain->target
346 : : );
347 : :
348 : 444 : res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE,
349 : : WANT_STDERR, NO_TIMEOUT, &pid_status, opts);
350 : 444 : chop_newline(err_buf);
351 [ + - ][ + + ]: 444 : if((!EXTCMD_IS_SUCCESS(res)) || (pid_status != 0)) {
352 : 2 : log_msg(LOG_ERR, "comment_match_exists() Error %i from cmd:'%s': %s",
353 : : res, cmd_buf, cmd_out);
354 : 2 : return 0; /* Errored out*/
355 : : }
356 : :
357 : 442 : log_msg(LOG_DEBUG, "comment_match_exists() CMD: '%s' (res: %d, err: %s)",
358 : : cmd_buf, res, err_buf);
359 : :
360 : 442 : zero_cmd_buffers();
361 : :
362 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_LIST_RULES_ARGS,
363 : 442 : opts->fw_config->fw_command,
364 : : in_chain->table,
365 : : in_chain->from_chain
366 : : );
367 : :
368 : 442 : res = run_extcmd(cmd_buf, cmd_out, STANDARD_CMD_OUT_BUFSIZE,
369 : : WANT_STDERR, NO_TIMEOUT, &pid_status, opts);
370 : 442 : chop_newline(cmd_out);
371 : :
372 [ - + ]: 442 : if(!EXTCMD_IS_SUCCESS(res))
373 : 0 : log_msg(LOG_ERR, "comment_match_exists() Error %i from cmd:'%s': %s",
374 : : res, cmd_buf, cmd_out);
375 : :
376 : 442 : ndx = strstr(cmd_out, TMP_COMMENT);
377 [ + + ]: 442 : if(ndx == NULL)
378 : : res = 0; /* did not find the tmp comment */
379 : : else
380 : 441 : res = 1;
381 : :
382 [ + + ]: 442 : if(res == 1)
383 : : {
384 : : /* Delete the tmp comment rule
385 : : */
386 : 441 : zero_cmd_buffers();
387 : :
388 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_DEL_RULE_ARGS,
389 : 441 : opts->fw_config->fw_command,
390 : : in_chain->table,
391 : : in_chain->from_chain,
392 : : 1
393 : : );
394 : 441 : run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE,
395 : : WANT_STDERR, NO_TIMEOUT, &pid_status, opts);
396 : : }
397 : :
398 : 442 : return res;
399 : : }
400 : :
401 : : static int
402 : 575 : add_jump_rule(const fko_srv_options_t * const opts, const int chain_num)
403 : : {
404 : 575 : int res = 0, rv = 0;
405 : :
406 : 575 : zero_cmd_buffers();
407 : :
408 : 575 : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_ADD_JUMP_RULE_ARGS,
409 : : fwc.fw_command,
410 : 575 : fwc.chain[chain_num].table,
411 : 575 : fwc.chain[chain_num].from_chain,
412 : : fwc.chain[chain_num].jump_rule_pos,
413 : 575 : fwc.chain[chain_num].to_chain
414 : : );
415 : :
416 : 575 : res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE,
417 : : WANT_STDERR, NO_TIMEOUT, &pid_status, opts);
418 : :
419 : 575 : log_msg(LOG_DEBUG, "add_jump_rule() CMD: '%s' (res: %d, err: %s)",
420 : : cmd_buf, res, err_buf);
421 : :
422 [ + - ]: 575 : if(EXTCMD_IS_SUCCESS(res))
423 : : {
424 : 575 : log_msg(LOG_INFO, "Added jump rule from chain: %s to chain: %s",
425 : : fwc.chain[chain_num].from_chain,
426 : : fwc.chain[chain_num].to_chain);
427 : 575 : rv = 1;
428 : : }
429 : : else
430 : 0 : log_msg(LOG_ERR, "add_jump_rule() Error %i from cmd:'%s': %s",
431 : : res, cmd_buf, err_buf);
432 : :
433 : 575 : return rv;
434 : : }
435 : :
436 : : static int
437 : 945 : chain_exists(const fko_srv_options_t * const opts, const int chain_num)
438 : : {
439 : 945 : int res = 0, rv = 0;
440 : :
441 : 945 : zero_cmd_buffers();
442 : :
443 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_CHAIN_EXISTS_ARGS,
444 : : fwc.fw_command,
445 : 945 : fwc.chain[chain_num].table,
446 : 945 : fwc.chain[chain_num].to_chain
447 : : );
448 : :
449 : 945 : res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE,
450 : : WANT_STDERR, NO_TIMEOUT, &pid_status, opts);
451 : 945 : chop_newline(err_buf);
452 : :
453 : 945 : log_msg(LOG_DEBUG, "chain_exists() CMD: '%s' (res: %d, err: %s)",
454 : : cmd_buf, res, err_buf);
455 : :
456 [ + + ]: 945 : if(strstr(err_buf, FIREWD_CMD_FAIL_STR) == NULL)
457 : : {
458 : 372 : log_msg(LOG_DEBUG, "'%s' table '%s' chain exists",
459 : : fwc.chain[chain_num].table,
460 : : fwc.chain[chain_num].to_chain);
461 : 372 : rv = 1;
462 : : }
463 : : else
464 : 573 : log_msg(LOG_DEBUG,
465 : : "chain_exists() Error %i from cmd:'%s': %s",
466 : : res, cmd_buf, err_buf);
467 : :
468 : 945 : return rv;
469 : : }
470 : :
471 : : static int
472 : 2575 : jump_rule_exists_chk_support(const fko_srv_options_t * const opts, const int chain_num)
473 : : {
474 : 2575 : int exists = 0;
475 : 2575 : char rule_buf[CMD_BUFSIZE] = {0};
476 : :
477 : : snprintf(rule_buf, CMD_BUFSIZE-1, FIREWD_CHK_JUMP_RULE_ARGS,
478 : 2575 : fwc.chain[chain_num].table,
479 : 2575 : fwc.chain[chain_num].to_chain
480 : : );
481 : :
482 [ + + ]: 2575 : if(rule_exists_chk_support(opts, fwc.chain[chain_num].from_chain, rule_buf) == 1)
483 : : {
484 : 913 : log_msg(LOG_DEBUG, "jump_rule_exists_chk_support() jump rule found");
485 : 913 : exists = 1;
486 : : }
487 : : else
488 : 1662 : log_msg(LOG_DEBUG, "jump_rule_exists_chk_support() jump rule not found");
489 : :
490 : 2575 : return exists;
491 : : }
492 : :
493 : : static int
494 : 84 : jump_rule_exists_no_chk_support(const fko_srv_options_t * const opts,
495 : : const int chain_num)
496 : : {
497 : 84 : int exists = 0;
498 : 84 : char chain_search[CMD_BUFSIZE] = {0};
499 : :
500 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_LIST_RULES_ARGS,
501 : : fwc.fw_command,
502 : 84 : fwc.chain[chain_num].table,
503 : 84 : fwc.chain[chain_num].from_chain
504 : : );
505 : :
506 : : /* include spaces on either side as produced by 'firewalld -L' output
507 : : */
508 : : snprintf(chain_search, CMD_BUFSIZE-1, " %s ",
509 : 84 : fwc.chain[chain_num].to_chain);
510 : :
511 [ + + ]: 84 : if(search_extcmd(cmd_buf, WANT_STDERR,
512 : : NO_TIMEOUT, chain_search, &pid_status, opts) > 0)
513 : 30 : exists = 1;
514 : :
515 [ + + ]: 84 : if(exists)
516 : 30 : log_msg(LOG_DEBUG,
517 : : "jump_rule_exists_no_chk_support() jump rule found");
518 : : else
519 : 54 : log_msg(LOG_DEBUG,
520 : : "jump_rule_exists_no_chk_support() jump rule not found");
521 : :
522 : 84 : return(exists);
523 : : }
524 : :
525 : : static int
526 : 2659 : jump_rule_exists(const fko_srv_options_t * const opts, const int chain_num)
527 : : {
528 : 2659 : int exists = 0;
529 : :
530 [ + + ]: 2659 : if(have_firewd_chk_support == 1)
531 : 2575 : exists = jump_rule_exists_chk_support(opts, chain_num);
532 : : else
533 : 84 : exists = jump_rule_exists_no_chk_support(opts, chain_num);
534 : :
535 : 2659 : return exists;
536 : : }
537 : :
538 : : /* Print all firewall rules currently instantiated by the running fwknopd
539 : : * daemon to stdout.
540 : : */
541 : : int
542 : 2981 : fw_dump_rules(const fko_srv_options_t * const opts)
543 : : {
544 : : int i;
545 : 2981 : int res, got_err = 0;
546 : :
547 : 2981 : struct fw_chain *ch = opts->fw_config->chain;
548 : :
549 [ + + ]: 2981 : if (opts->fw_list_all == 1)
550 : : {
551 : 2 : fprintf(stdout, "Listing all firewalld rules in applicable tables...\n");
552 : 2 : fflush(stdout);
553 : :
554 [ + + ]: 14 : for(i=0; i < NUM_FWKNOP_ACCESS_TYPES; i++)
555 : : {
556 [ + + ]: 12 : if(fwc.chain[i].target[0] == '\0')
557 : 10 : continue;
558 : :
559 : 2 : zero_cmd_buffers();
560 : :
561 : : /* Create the list command
562 : : */
563 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_LIST_ALL_RULES_ARGS,
564 : 2 : opts->fw_config->fw_command,
565 : 2 : ch[i].table
566 : : );
567 : :
568 : 2 : res = run_extcmd(cmd_buf, NULL, 0, NO_STDERR,
569 : : NO_TIMEOUT, &pid_status, opts);
570 : :
571 : 2 : log_msg(LOG_DEBUG, "fw_dump_rules() CMD: '%s' (res: %d)",
572 : : cmd_buf, res);
573 : :
574 : : /* Expect full success on this */
575 [ - + ]: 2 : if(! EXTCMD_IS_SUCCESS(res))
576 : : {
577 : 0 : log_msg(LOG_ERR, "fw_dump_rules() Error %i from cmd:'%s': %s",
578 : : res, cmd_buf, err_buf);
579 : 0 : got_err++;
580 : : }
581 : : }
582 : : }
583 : : else
584 : : {
585 : 2979 : fprintf(stdout, "Listing rules in fwknopd firewalld chains...\n");
586 : 2979 : fflush(stdout);
587 : :
588 [ + + ]: 20853 : for(i=0; i < NUM_FWKNOP_ACCESS_TYPES; i++)
589 : : {
590 [ + + ]: 17874 : if(fwc.chain[i].target[0] == '\0')
591 : 14417 : continue;
592 : :
593 : 3457 : zero_cmd_buffers();
594 : :
595 : : /* Create the list command
596 : : */
597 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_LIST_RULES_ARGS,
598 : 3457 : opts->fw_config->fw_command,
599 : 3457 : ch[i].table,
600 : 3457 : ch[i].to_chain
601 : : );
602 : :
603 : 3457 : fprintf(stdout, "\n");
604 : 3457 : fflush(stdout);
605 : :
606 : 3457 : res = run_extcmd(cmd_buf, NULL, 0, NO_STDERR,
607 : : NO_TIMEOUT, &pid_status, opts);
608 : :
609 : 3457 : log_msg(LOG_DEBUG, "fw_dump_rules() CMD: '%s' (res: %d)",
610 : : cmd_buf, res);
611 : :
612 : : /* Expect full success on this */
613 [ - + ]: 3457 : if(! EXTCMD_IS_SUCCESS(res))
614 : : {
615 : 0 : log_msg(LOG_ERR, "fw_dump_rules() Error %i from cmd:'%s': %s",
616 : : res, cmd_buf, err_buf);
617 : 0 : got_err++;
618 : : }
619 : : }
620 : : }
621 : :
622 : 2981 : return(got_err);
623 : : }
624 : :
625 : : /* Quietly flush and delete all fwknop custom chains.
626 : : */
627 : : static void
628 : 889 : delete_all_chains(const fko_srv_options_t * const opts)
629 : : {
630 : 889 : int i, res, cmd_ctr = 0;
631 : :
632 [ + + ]: 6222 : for(i=0; i < NUM_FWKNOP_ACCESS_TYPES; i++)
633 : : {
634 [ + + ]: 5334 : if(fwc.chain[i].target[0] == '\0')
635 : 4193 : continue;
636 : :
637 : : /* First look for a jump rule to this chain and remove it if it
638 : : * is there.
639 : : */
640 : : cmd_ctr = 0;
641 [ + - ][ + + ]: 1714 : while(cmd_ctr < CMD_LOOP_TRIES && (jump_rule_exists(opts, i) == 1))
642 : : {
643 : 573 : zero_cmd_buffers();
644 : :
645 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_DEL_JUMP_RULE_ARGS,
646 : : fwc.fw_command,
647 : 573 : fwc.chain[i].table,
648 : 573 : fwc.chain[i].from_chain,
649 : 573 : fwc.chain[i].to_chain
650 : : );
651 : :
652 : 573 : res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE,
653 : : WANT_STDERR, NO_TIMEOUT, &pid_status, opts);
654 : 573 : chop_newline(err_buf);
655 : :
656 : 573 : log_msg(LOG_DEBUG, "delete_all_chains() CMD: '%s' (res: %d, err: %s)",
657 : : cmd_buf, res, err_buf);
658 : :
659 : : /* Expect full success on this */
660 [ - + ]: 573 : if(! EXTCMD_IS_SUCCESS(res))
661 : 0 : log_msg(LOG_ERR, "delete_all_chains() Error %i from cmd:'%s': %s",
662 : : res, cmd_buf, err_buf);
663 : :
664 : 573 : cmd_ctr++;
665 : : }
666 : :
667 : 1141 : zero_cmd_buffers();
668 : :
669 : : /* Now flush and remove the chain.
670 : : */
671 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_FLUSH_CHAIN_ARGS,
672 : : fwc.fw_command,
673 : 1141 : fwc.chain[i].table,
674 : 1141 : fwc.chain[i].to_chain
675 : : );
676 : :
677 : 1141 : res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, WANT_STDERR,
678 : : NO_TIMEOUT, &pid_status, opts);
679 : 1140 : chop_newline(err_buf);
680 : :
681 : 1140 : log_msg(LOG_DEBUG, "delete_all_chains() CMD: '%s' (res: %d, err: %s)",
682 : : cmd_buf, res, err_buf);
683 : :
684 : : /* Expect full success on this */
685 [ - + ]: 1140 : if(! EXTCMD_IS_SUCCESS(res))
686 : 0 : log_msg(LOG_ERR, "delete_all_chains() Error %i from cmd:'%s': %s",
687 : : res, cmd_buf, err_buf);
688 : :
689 : 1140 : zero_cmd_buffers();
690 : :
691 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_DEL_CHAIN_ARGS,
692 : : fwc.fw_command,
693 : 1140 : fwc.chain[i].table,
694 : : fwc.chain[i].to_chain
695 : : );
696 : :
697 : 1140 : res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, WANT_STDERR,
698 : : NO_TIMEOUT, &pid_status, opts);
699 : 1140 : chop_newline(err_buf);
700 : :
701 : 1140 : log_msg(LOG_DEBUG, "delete_all_chains() CMD: '%s' (res: %d, err: %s)",
702 : : cmd_buf, res, err_buf);
703 : :
704 : : /* Expect full success on this */
705 [ - + ]: 1140 : if(! EXTCMD_IS_SUCCESS(res))
706 : 0 : log_msg(LOG_ERR, "delete_all_chains() Error %i from cmd:'%s': %s",
707 : : res, cmd_buf, err_buf);
708 : :
709 : : #if USE_LIBNETFILTER_QUEUE
710 : : if(opts->enable_nfq_capture)
711 : : {
712 : : zero_cmd_buffers();
713 : :
714 : : /* Delete the rule to direct traffic to the NFQ chain.
715 : : */
716 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_DEL_RULE_ARGS,
717 : : fwc.fw_command,
718 : : opts->config[CONF_NFQ_TABLE],
719 : : "INPUT",
720 : : 1
721 : : );
722 : : res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, WANT_STDERR,
723 : : NO_TIMEOUT, &pid_status, opts);
724 : :
725 : : if (opts->verbose)
726 : : log_msg(LOG_INFO, "delete_all_chains() CMD: '%s' (res: %d, err: %s)",
727 : : cmd_buf, res, err_buf);
728 : :
729 : : /* Expect full success on this */
730 : : if(! EXTCMD_IS_SUCCESS(res))
731 : : log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf);
732 : :
733 : : zero_cmd_buffers();
734 : :
735 : : /* Flush the NFQ chain
736 : : */
737 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_FLUSH_CHAIN_ARGS,
738 : : fwc.fw_command,
739 : : opts->config[CONF_NFQ_TABLE],
740 : : opts->config[CONF_NFQ_CHAIN]
741 : : );
742 : : res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, WANT_STDERR,
743 : : NO_TIMEOUT, &pid_status, opts);
744 : :
745 : : if (opts->verbose)
746 : : log_msg(LOG_INFO, "delete_all_chains() CMD: '%s' (res: %d, err: %s)",
747 : : cmd_buf, res, err_buf);
748 : :
749 : : /* Expect full success on this */
750 : : if(! EXTCMD_IS_SUCCESS(res))
751 : : log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf);
752 : :
753 : : zero_cmd_buffers();
754 : :
755 : : /* Delete the NF_QUEUE chains and rules
756 : : */
757 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_DEL_CHAIN_ARGS,
758 : : fwc.fw_command,
759 : : opts->config[CONF_NFQ_TABLE],
760 : : opts->config[CONF_NFQ_CHAIN]
761 : : );
762 : : res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, WANT_STDERR,
763 : : NO_TIMEOUT, &pid_status, opts);
764 : :
765 : : if (opts->verbose)
766 : : log_msg(LOG_INFO, "delete_all_chains() CMD: '%s' (res: %d, err: %s)",
767 : : cmd_buf, res, err_buf);
768 : :
769 : : /* Expect full success on this */
770 : : if(! EXTCMD_IS_SUCCESS(res))
771 : : log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf);
772 : : }
773 : : #endif
774 : : }
775 : 888 : return;
776 : : }
777 : :
778 : : static int
779 : 573 : create_chain(const fko_srv_options_t * const opts, const int chain_num)
780 : : {
781 : 573 : int res = 0, rv = 0;
782 : :
783 : 573 : zero_cmd_buffers();
784 : :
785 : : /* Create the custom chain.
786 : : */
787 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_NEW_CHAIN_ARGS,
788 : : fwc.fw_command,
789 : 573 : fwc.chain[chain_num].table,
790 : 573 : fwc.chain[chain_num].to_chain
791 : : );
792 : :
793 : 573 : res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, WANT_STDERR,
794 : : NO_TIMEOUT, &pid_status, opts);
795 : 573 : chop_newline(err_buf);
796 : :
797 : 573 : log_msg(LOG_DEBUG, "create_chain() CMD: '%s' (res: %d, err: %s)",
798 : : cmd_buf, res, err_buf);
799 : :
800 : : /* Expect full success on this */
801 [ - + ]: 573 : if(EXTCMD_IS_SUCCESS(res))
802 : : rv = 1;
803 : : else
804 : 0 : log_msg(LOG_ERR, "create_chain() Error %i from cmd:'%s': %s",
805 : : res, cmd_buf, err_buf);
806 : :
807 : 573 : return rv;
808 : : }
809 : :
810 : : static int
811 : 945 : mk_chain(const fko_srv_options_t * const opts, const int chain_num)
812 : : {
813 : 945 : int err = 0;
814 : :
815 : : /* Make sure the required chain and jump rule exist
816 : : */
817 [ + + ]: 945 : if(! chain_exists(opts, chain_num))
818 [ - + ]: 573 : if(! create_chain(opts, chain_num))
819 : 0 : err++;
820 : :
821 [ + + ]: 945 : if (! jump_rule_exists(opts, chain_num))
822 [ - + ]: 575 : if(! add_jump_rule(opts, chain_num))
823 : 0 : err++;
824 : :
825 : 945 : return err;
826 : : }
827 : :
828 : : /* Create the fwknop custom chains (at least those that are configured).
829 : : */
830 : : static int
831 : 444 : create_fw_chains(const fko_srv_options_t * const opts)
832 : : {
833 : 444 : int i, got_err = 0;
834 : : #if USE_LIBNETFILTER_QUEUE
835 : : int res = 0;
836 : : #endif
837 : :
838 [ + + ]: 3108 : for(i=0; i < NUM_FWKNOP_ACCESS_TYPES; i++)
839 : : {
840 [ + + ]: 2664 : if(fwc.chain[i].target[0] == '\0')
841 : 2101 : continue;
842 : :
843 : 563 : got_err += mk_chain(opts, i);
844 : : }
845 : : #if USE_LIBNETFILTER_QUEUE
846 : : if(opts->enable_nfq_capture)
847 : : {
848 : : zero_cmd_buffers();
849 : :
850 : : /* Create the NF_QUEUE chains and rules
851 : : */
852 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_NEW_CHAIN_ARGS,
853 : : fwc.fw_command,
854 : : opts->config[CONF_NFQ_TABLE],
855 : : opts->config[CONF_NFQ_CHAIN]
856 : : );
857 : : res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, WANT_STDERR,
858 : : NO_TIMEOUT, &pid_status, opts);
859 : :
860 : : if (opts->verbose)
861 : : log_msg(LOG_INFO, "create_fw_chains() CMD: '%s' (res: %d, err: %s)",
862 : : cmd_buf, res, err_buf);
863 : :
864 : : /* Expect full success on this */
865 : : if(! EXTCMD_IS_SUCCESS(res))
866 : : {
867 : : log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf);
868 : : got_err++;
869 : : }
870 : :
871 : : zero_cmd_buffers();
872 : :
873 : : /* Create the rule to direct traffic to the NFQ chain.
874 : : */
875 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_ADD_JUMP_RULE_ARGS,
876 : : fwc.fw_command,
877 : : opts->config[CONF_NFQ_TABLE],
878 : : "INPUT",
879 : : 1,
880 : : opts->config[CONF_NFQ_CHAIN]
881 : : );
882 : : res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, WANT_STDERR,
883 : : NO_TIMEOUT, &pid_status, opts);
884 : :
885 : : if (opts->verbose)
886 : : log_msg(LOG_INFO, "create_fw_chains() CMD: '%s' (res: %d, err: %s)",
887 : : cmd_buf, res, err_buf);
888 : :
889 : : /* Expect full success on this */
890 : : if(! EXTCMD_IS_SUCCESS(res))
891 : : {
892 : : log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf);
893 : : got_err++;
894 : : }
895 : :
896 : : zero_cmd_buffers();
897 : :
898 : : /* Create the rule to direct SPA packets to the queue.
899 : : * If an interface is specified use the "_WITH_IF" version
900 : : * of the command.
901 : : */
902 : : if(strlen(opts->config[CONF_NFQ_INTERFACE]) > 0)
903 : : {
904 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_NFQ_ADD_ARGS_WITH_IF,
905 : : fwc.fw_command,
906 : : opts->config[CONF_NFQ_TABLE],
907 : : opts->config[CONF_NFQ_CHAIN],
908 : : opts->config[CONF_NFQ_INTERFACE],
909 : : opts->config[CONF_NFQ_PORT],
910 : : opts->config[CONF_NFQ_QUEUE_NUMBER]
911 : : );
912 : : }
913 : : else
914 : : {
915 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_NFQ_ADD_ARGS,
916 : : fwc.fw_command,
917 : : opts->config[CONF_NFQ_TABLE],
918 : : opts->config[CONF_NFQ_CHAIN],
919 : : opts->config[CONF_NFQ_PORT],
920 : : opts->config[CONF_NFQ_QUEUE_NUMBER]
921 : : );
922 : : }
923 : : res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, WANT_STDERR,
924 : : NO_TIMEOUT, &pid_status, opts);
925 : :
926 : : if (opts->verbose)
927 : : log_msg(LOG_INFO, "create_fw_chains() CMD: '%s' (res: %d, err: %s)",
928 : : cmd_buf, res, err_buf);
929 : :
930 : : /* Expect full success on this */
931 : : if(! EXTCMD_IS_SUCCESS(res))
932 : : {
933 : : log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf);
934 : : got_err++;
935 : : }
936 : : }
937 : : #endif
938 : 444 : return(got_err);
939 : : }
940 : :
941 : : static int
942 : 5309 : set_fw_chain_conf(const int type, const char * const conf_str)
943 : : {
944 : : int i, j, is_err;
945 : 5309 : char tbuf[MAX_LINE_LEN] = {0};
946 : 5309 : const char *ndx = conf_str;
947 : :
948 : : char *chain_fields[FW_NUM_CHAIN_FIELDS];
949 : :
950 : 5309 : struct fw_chain *chain = &(fwc.chain[type]);
951 : :
952 [ - + ]: 5309 : if(conf_str == NULL)
953 : : {
954 : 0 : log_msg(LOG_ERR, "[*] NULL conf_str");
955 : 0 : return 0;
956 : : }
957 : :
958 : 5309 : chain->type = type;
959 : :
960 [ + - ]: 5309 : if(ndx != NULL)
961 : 5309 : chain_fields[0] = tbuf;
962 : :
963 : : i = 0;
964 : : j = 1;
965 [ + + ]: 226505 : while(*ndx != '\0')
966 : : {
967 [ + + ]: 221196 : if(*ndx != ' ')
968 : : {
969 [ + + ]: 194651 : if(*ndx == ',')
970 : : {
971 : 26545 : tbuf[i] = '\0';
972 : 26545 : chain_fields[j++] = &(tbuf[++i]);
973 : : }
974 : : else
975 : 168106 : tbuf[i++] = *ndx;
976 : : }
977 [ + + ]: 221196 : if(*ndx != '\0'
978 : 221196 : && *ndx != ' '
979 [ + + ]: 194651 : && *ndx != ','
980 [ + + ]: 168106 : && *ndx != '_'
981 [ - + ]: 162779 : && isalnum(*ndx) == 0)
982 : : {
983 : 0 : log_msg(LOG_ERR, "[*] Custom chain config parse error: "
984 : : "invalid character '%c' for chain type %i, "
985 : 0 : "line: %s", *ndx, type, conf_str);
986 : 0 : return 0;
987 : : }
988 : 221196 : ndx++;
989 : : }
990 : :
991 : : /* Sanity check - j should be the number of chain fields
992 : : * (excluding the type).
993 : : */
994 [ - + ]: 5309 : if(j != FW_NUM_CHAIN_FIELDS)
995 : : {
996 : 0 : log_msg(LOG_ERR, "[*] Custom chain config parse error: "
997 : : "wrong number of fields for chain type %i, "
998 : : "line: %s", type, conf_str);
999 : 0 : return 0;
1000 : : }
1001 : :
1002 : : /* Pull and set Target */
1003 : 5309 : strlcpy(chain->target, chain_fields[0], sizeof(chain->target));
1004 : :
1005 : : /* Pull and set Table */
1006 : 5309 : strlcpy(chain->table, chain_fields[1], sizeof(chain->table));
1007 : :
1008 : : /* Pull and set From_chain */
1009 : 5309 : strlcpy(chain->from_chain, chain_fields[2], sizeof(chain->from_chain));
1010 : :
1011 : : /* Pull and set Jump_rule_position */
1012 : 5309 : chain->jump_rule_pos = strtol_wrapper(chain_fields[3],
1013 : : 0, RCHK_MAX_FIREWD_RULE_NUM, NO_EXIT_UPON_ERR, &is_err);
1014 [ + + ]: 5309 : if(is_err != FKO_SUCCESS)
1015 : : {
1016 : 1 : log_msg(LOG_ERR, "[*] invalid jump rule position in Line: %s",
1017 : : conf_str);
1018 : 1 : return 0;
1019 : : }
1020 : :
1021 : : /* Pull and set To_chain */
1022 : 5308 : strlcpy(chain->to_chain, chain_fields[4], sizeof(chain->to_chain));
1023 : :
1024 : : /* Pull and set to_chain rule position */
1025 : 5308 : chain->rule_pos = strtol_wrapper(chain_fields[5],
1026 : : 0, RCHK_MAX_FIREWD_RULE_NUM, NO_EXIT_UPON_ERR, &is_err);
1027 [ + + ]: 5308 : if(is_err != FKO_SUCCESS)
1028 : : {
1029 : 1 : log_msg(LOG_ERR, "[*] invalid to_chain rule position in Line: %s",
1030 : : conf_str);
1031 : 1 : return 0;
1032 : : }
1033 : : return 1;
1034 : : }
1035 : :
1036 : : int
1037 : 4673 : fw_config_init(fko_srv_options_t * const opts)
1038 : : {
1039 : : memset(&fwc, 0x0, sizeof(struct fw_config));
1040 : :
1041 : : /* Set our firewall exe command path (firewall-cmd or iptables in most cases).
1042 : : */
1043 : : #if FIREWALL_FIREWALLD
1044 : : char cmd_passthru[512];
1045 : 4673 : snprintf(cmd_passthru, sizeof cmd_passthru, "%s %s ",
1046 : : opts->config[CONF_FIREWALL_EXE], FIREWD_CMD_PREFIX);
1047 : 4673 : strlcpy(fwc.fw_command, cmd_passthru, sizeof(fwc.fw_command));
1048 : : #else
1049 : : strlcpy(fwc.fw_command, opts->config[CONF_FIREWALL_EXE], sizeof(fwc.fw_command));
1050 : : #endif
1051 : :
1052 : : #if HAVE_LIBFIU
1053 [ + + ]: 4673 : fiu_return_on("fw_config_init", 0);
1054 : : #endif
1055 : :
1056 : : /* Pull the fwknop chain config info and setup our internal
1057 : : * config struct. The FIREWD_INPUT is the only one that is
1058 : : * required. The rest are optional.
1059 : : */
1060 [ + + ]: 4672 : if(set_fw_chain_conf(FIREWD_INPUT_ACCESS, opts->config[CONF_FIREWD_INPUT_ACCESS]) != 1)
1061 : : return 0;
1062 : :
1063 : : /* The FWKNOP_OUTPUT_ACCESS requires ENABLE_FIREWD_OUTPUT_ACCESS == Y
1064 : : */
1065 [ + + ]: 4670 : if(strncasecmp(opts->config[CONF_ENABLE_FIREWD_OUTPUT], "Y", 1)==0)
1066 [ + - ]: 1 : if(set_fw_chain_conf(FIREWD_OUTPUT_ACCESS, opts->config[CONF_FIREWD_OUTPUT_ACCESS]) != 1)
1067 : : return 0;
1068 : :
1069 : : /* The remaining access chains require ENABLE_FIREWD_FORWARDING
1070 : : * or ENABLE_FIREWD_LOCAL_NAT
1071 : : */
1072 [ + + ]: 4670 : if(strncasecmp(opts->config[CONF_ENABLE_FIREWD_FORWARDING], "Y", 1)==0
1073 [ + + ]: 4466 : || strncasecmp(opts->config[CONF_ENABLE_FIREWD_LOCAL_NAT], "Y", 1)==0)
1074 : : {
1075 [ + - ]: 262 : if(set_fw_chain_conf(FIREWD_FORWARD_ACCESS, opts->config[CONF_FIREWD_FORWARD_ACCESS]) != 1)
1076 : : return 0;
1077 : :
1078 [ + - ]: 262 : if(set_fw_chain_conf(FIREWD_DNAT_ACCESS, opts->config[CONF_FIREWD_DNAT_ACCESS]) != 1)
1079 : : return 0;
1080 : :
1081 : : /* Requires ENABLE_FIREWD_SNAT = Y
1082 : : */
1083 [ + + ]: 262 : if(strncasecmp(opts->config[CONF_ENABLE_FIREWD_SNAT], "Y", 1)==0)
1084 : : {
1085 : : /* Support both SNAT and MASQUERADE - this will be controlled
1086 : : * via the access.conf configuration for individual rules
1087 : : */
1088 [ + - ]: 56 : if(set_fw_chain_conf(FIREWD_MASQUERADE_ACCESS,
1089 : 56 : opts->config[CONF_FIREWD_MASQUERADE_ACCESS]) != 1)
1090 : : return 0;
1091 : :
1092 [ + - ]: 56 : if(set_fw_chain_conf(FIREWD_SNAT_ACCESS,
1093 : 56 : opts->config[CONF_FIREWD_SNAT_ACCESS]) != 1)
1094 : : return 0;
1095 : : }
1096 : : }
1097 : :
1098 [ + + ]: 4670 : if(strncasecmp(opts->config[CONF_ENABLE_DESTINATION_RULE], "Y", 1)==0)
1099 : : {
1100 : 9 : fwc.use_destination = 1;
1101 : : }
1102 : :
1103 : : /* Let us find it via our opts struct as well.
1104 : : */
1105 : 4670 : opts->fw_config = &fwc;
1106 : :
1107 : 4670 : return 1;
1108 : : }
1109 : :
1110 : : int
1111 : 444 : fw_initialize(const fko_srv_options_t * const opts)
1112 : : {
1113 : 444 : int res = 1;
1114 : :
1115 : : /* See if firewalld offers the '-C' argument (older versions don't). If not,
1116 : : * then switch to parsing firewalld -L output to find rules.
1117 : : */
1118 [ + + ]: 444 : if(opts->firewd_disable_check_support)
1119 : 9 : have_firewd_chk_support = 0;
1120 : : else
1121 : 435 : firewd_chk_support(opts);
1122 : :
1123 : : /* Flush the chains (just in case) so we can start fresh.
1124 : : */
1125 [ + + ]: 444 : if(strncasecmp(opts->config[CONF_FLUSH_FIREWD_AT_INIT], "Y", 1) == 0)
1126 : 436 : delete_all_chains(opts);
1127 : :
1128 : : /* Now create any configured chains.
1129 : : */
1130 [ - + ]: 444 : if(create_fw_chains(opts) != 0)
1131 : : {
1132 : 0 : log_msg(LOG_WARNING,
1133 : : "fw_initialize() Warning: Errors detected during fwknop custom chain creation");
1134 : 0 : res = 0;
1135 : : }
1136 : :
1137 : : /* Make sure that the 'comment' match is available
1138 : : */
1139 [ + - ]: 444 : if(strncasecmp(opts->config[CONF_ENABLE_FIREWD_COMMENT_CHECK], "Y", 1) == 0)
1140 : : {
1141 [ + + ]: 444 : if(comment_match_exists(opts) == 1)
1142 : : {
1143 : 441 : log_msg(LOG_INFO, "firewalld 'comment' match is available");
1144 : : }
1145 : : else
1146 : : {
1147 : 3 : log_msg(LOG_WARNING, "Warning: Could not use the 'comment' match");
1148 : 3 : res = 0;
1149 : : }
1150 : : }
1151 : :
1152 : 444 : return(res);
1153 : : }
1154 : :
1155 : : int
1156 : 461 : fw_cleanup(const fko_srv_options_t * const opts)
1157 : : {
1158 [ + + ]: 461 : if(strncasecmp(opts->config[CONF_FLUSH_FIREWD_AT_EXIT], "N", 1) == 0
1159 [ - + ]: 8 : && opts->fw_flush == 0)
1160 : : return(0);
1161 : :
1162 : 453 : delete_all_chains(opts);
1163 : 452 : return(0);
1164 : : }
1165 : :
1166 : : static int
1167 : 375 : create_rule(const fko_srv_options_t * const opts,
1168 : : const char * const fw_chain, const char * const fw_rule)
1169 : : {
1170 : 375 : int res = 0;
1171 : :
1172 : 375 : zero_cmd_buffers();
1173 : :
1174 [ + + ]: 375 : if (strncasecmp(opts->config[CONF_ENABLE_RULE_PREPEND], "Y", 1) == 0) {
1175 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s -I %s %s",
1176 : 1 : opts->fw_config->fw_command, fw_chain, fw_rule);
1177 : : } else {
1178 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s -A %s %s",
1179 : 374 : opts->fw_config->fw_command, fw_chain, fw_rule);
1180 : : }
1181 : :
1182 : 375 : res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, WANT_STDERR,
1183 : : NO_TIMEOUT, &pid_status, opts);
1184 : 375 : chop_newline(err_buf);
1185 : :
1186 : 375 : log_msg(LOG_DEBUG, "create_rule() CMD: '%s' (res: %d, err: %s)",
1187 : : cmd_buf, res, err_buf);
1188 : :
1189 [ + - ]: 375 : if(EXTCMD_IS_SUCCESS(res))
1190 : : {
1191 : 375 : log_msg(LOG_DEBUG, "create_rule() Rule: '%s' added to %s", fw_rule, fw_chain);
1192 : 375 : res = 1;
1193 : : }
1194 : : else
1195 : 0 : log_msg(LOG_ERR, "create_rule() Error %i from cmd:'%s': %s",
1196 : : res, cmd_buf, err_buf);
1197 : :
1198 : 375 : return res;
1199 : : }
1200 : :
1201 : : static void
1202 : 382 : firewd_rule(const fko_srv_options_t * const opts,
1203 : : const char * const complete_rule_buf,
1204 : : const char * const fw_rule_macro,
1205 : : const char * const srcip,
1206 : : const char * const dstip,
1207 : : const unsigned int proto,
1208 : : const unsigned int port,
1209 : : const char * const nat_ip,
1210 : : const unsigned int nat_port,
1211 : : struct fw_chain * const chain,
1212 : : const unsigned int exp_ts,
1213 : : const time_t now,
1214 : : const char * const msg,
1215 : : const char * const access_msg)
1216 : : {
1217 : 382 : char rule_buf[CMD_BUFSIZE] = {0};
1218 : :
1219 [ + + ][ + - ]: 382 : if(complete_rule_buf != NULL && complete_rule_buf[0] != 0x0)
1220 : : {
1221 : 85 : strlcpy(rule_buf, complete_rule_buf, CMD_BUFSIZE-1);
1222 : : }
1223 : : else
1224 : : {
1225 : : memset(rule_buf, 0, CMD_BUFSIZE);
1226 : :
1227 : : snprintf(rule_buf, CMD_BUFSIZE-1, fw_rule_macro,
1228 : 297 : chain->table,
1229 : : proto,
1230 : : srcip,
1231 : : dstip,
1232 : : port,
1233 : : exp_ts,
1234 : 297 : chain->target
1235 : : );
1236 : : }
1237 : :
1238 : : /* Check to make sure that the chain and jump rule exists
1239 : : */
1240 : 382 : mk_chain(opts, chain->type);
1241 : :
1242 [ + + ]: 382 : if(rule_exists(opts, chain, rule_buf, proto, srcip,
1243 : : dstip, port, nat_ip, nat_port, exp_ts) == 0)
1244 : : {
1245 [ + - ]: 375 : if(create_rule(opts, chain->to_chain, rule_buf))
1246 : : {
1247 [ + + ]: 375 : log_msg(LOG_INFO, "Added %s rule to %s for %s -> %s %s, expires at %u",
1248 : : msg, chain->to_chain, srcip, (dstip == NULL) ? FIREWD_ANY_IP : dstip,
1249 : : access_msg, exp_ts
1250 : : );
1251 : :
1252 : 375 : chain->active_rules++;
1253 : :
1254 : : /* Reset the next expected expire time for this chain if it
1255 : : * is warranted.
1256 : : */
1257 [ + + ][ - + ]: 375 : if(chain->next_expire < now || exp_ts < chain->next_expire)
1258 : 355 : chain->next_expire = exp_ts;
1259 : : }
1260 : : }
1261 : :
1262 : 382 : return;
1263 : : }
1264 : :
1265 : 64 : static void forward_access_rule(const fko_srv_options_t * const opts,
1266 : : const acc_stanza_t * const acc,
1267 : : struct fw_chain * const fwd_chain,
1268 : : const char * const nat_ip,
1269 : : const unsigned int nat_port,
1270 : : const unsigned int fst_proto,
1271 : : const unsigned int fst_port,
1272 : : spa_data_t * const spadat,
1273 : : const unsigned int exp_ts,
1274 : : const time_t now)
1275 : : {
1276 : 32 : char rule_buf[CMD_BUFSIZE] = {0};
1277 : :
1278 : 32 : log_msg(LOG_DEBUG,
1279 : : "forward_access_rule() forward_all: %d, nat_ip: %s, nat_port: %d",
1280 : 32 : acc->forward_all, nat_ip, nat_port);
1281 : :
1282 : : memset(rule_buf, 0, CMD_BUFSIZE);
1283 [ + + ]: 32 : if(acc->forward_all)
1284 : : {
1285 : :
1286 : 5 : snprintf(rule_buf, CMD_BUFSIZE-1, FIREWD_FWD_ALL_RULE_ARGS,
1287 : 5 : fwd_chain->table,
1288 : : spadat->use_src_ip,
1289 : : exp_ts,
1290 : 5 : fwd_chain->target
1291 : : );
1292 : :
1293 : : /* Make a global ACCEPT rule for all ports/protocols
1294 : : */
1295 : 5 : firewd_rule(opts, rule_buf, NULL, spadat->use_src_ip,
1296 : : NULL, ANY_PROTO, ANY_PORT, NULL, NAT_ANY_PORT,
1297 : : fwd_chain, exp_ts, now, "FORWARD ALL", "*/*");
1298 : : }
1299 : : else
1300 : : {
1301 : : /* Make the FORWARD access rule
1302 : : */
1303 : 27 : snprintf(rule_buf, CMD_BUFSIZE-1, FIREWD_FWD_RULE_ARGS,
1304 : 27 : fwd_chain->table,
1305 : : fst_proto,
1306 : : spadat->use_src_ip,
1307 : : nat_port,
1308 : : exp_ts,
1309 : 27 : fwd_chain->target
1310 : : );
1311 : 27 : firewd_rule(opts, rule_buf, NULL, spadat->use_src_ip,
1312 : : NULL, fst_proto, nat_port, NULL, NAT_ANY_PORT,
1313 : 27 : fwd_chain, exp_ts, now, "FORWARD", spadat->spa_message_remain);
1314 : : }
1315 : 32 : return;
1316 : : }
1317 : :
1318 : 80 : static void dnat_rule(const fko_srv_options_t * const opts,
1319 : : const acc_stanza_t * const acc,
1320 : : struct fw_chain * const dnat_chain,
1321 : : const char * const nat_ip,
1322 : : const unsigned int nat_port,
1323 : : const unsigned int fst_proto,
1324 : : const unsigned int fst_port,
1325 : : spa_data_t * const spadat,
1326 : : const unsigned int exp_ts,
1327 : : const time_t now)
1328 : : {
1329 : 40 : char rule_buf[CMD_BUFSIZE] = {0};
1330 : :
1331 : 40 : log_msg(LOG_DEBUG, "dnat_rule() forward_all: %d, nat_ip: %s, nat_port: %d",
1332 : 40 : acc->forward_all, nat_ip, nat_port);
1333 : :
1334 [ + + ]: 40 : if(acc->forward_all)
1335 : : {
1336 : : memset(rule_buf, 0, CMD_BUFSIZE);
1337 : :
1338 [ + + ]: 3 : snprintf(rule_buf, CMD_BUFSIZE-1, FIREWD_DNAT_ALL_RULE_ARGS,
1339 : 3 : dnat_chain->table,
1340 : : spadat->use_src_ip,
1341 : 3 : (fwc.use_destination ? spadat->pkt_destination_ip : FIREWD_ANY_IP),
1342 : : exp_ts,
1343 : 3 : dnat_chain->target,
1344 : : nat_ip
1345 : : );
1346 : :
1347 : : /* Make a global DNAT rule for all ports/protocols
1348 : : */
1349 : 3 : firewd_rule(opts, rule_buf, NULL, spadat->use_src_ip,
1350 : : NULL, ANY_PROTO, ANY_PORT, NULL, NAT_ANY_PORT,
1351 : : dnat_chain, exp_ts, now, "DNAT ALL", "*/*");
1352 : : }
1353 : : else
1354 : : {
1355 : : memset(rule_buf, 0, CMD_BUFSIZE);
1356 : :
1357 [ - + ]: 37 : snprintf(rule_buf, CMD_BUFSIZE-1, FIREWD_DNAT_RULE_ARGS,
1358 : 37 : dnat_chain->table,
1359 : : fst_proto,
1360 : : spadat->use_src_ip,
1361 : 37 : (fwc.use_destination ? spadat->pkt_destination_ip : FIREWD_ANY_IP),
1362 : : fst_port,
1363 : : exp_ts,
1364 : 37 : dnat_chain->target,
1365 : : nat_ip,
1366 : : nat_port
1367 : : );
1368 : :
1369 [ - + ]: 37 : firewd_rule(opts, rule_buf, NULL, spadat->use_src_ip,
1370 : 37 : (fwc.use_destination ? spadat->pkt_destination_ip : FIREWD_ANY_IP),
1371 : : fst_proto, fst_port, nat_ip, nat_port, dnat_chain, exp_ts, now,
1372 : 37 : "DNAT", spadat->spa_message_remain);
1373 : : }
1374 : 40 : return;
1375 : : }
1376 : :
1377 : 13 : static void snat_rule(const fko_srv_options_t * const opts,
1378 : : const acc_stanza_t * const acc,
1379 : : const char * const nat_ip,
1380 : : const unsigned int nat_port,
1381 : : const unsigned int fst_proto,
1382 : : const unsigned int fst_port,
1383 : : spa_data_t * const spadat,
1384 : : const unsigned int exp_ts,
1385 : : const time_t now)
1386 : : {
1387 : 13 : char rule_buf[CMD_BUFSIZE] = {0};
1388 : 13 : char snat_target[SNAT_TARGET_BUFSIZE] = {0};
1389 : 13 : struct fw_chain *snat_chain = NULL;
1390 : :
1391 [ + + ]: 13 : log_msg(LOG_DEBUG,
1392 : : "snat_rule() forward_all: %d, nat_ip: %s, nat_port: %d, force_snat: %d, force_snat_ip: %s, force_masq: %d",
1393 : 26 : acc->forward_all, nat_ip, nat_port, acc->force_snat,
1394 : 13 : (acc->force_snat_ip == NULL) ? "(NONE)" : acc->force_snat_ip,
1395 : 13 : acc->force_masquerade);
1396 : :
1397 [ + + ]: 13 : if(acc->forward_all)
1398 : : {
1399 : : /* Default to MASQUERADE */
1400 : 5 : snat_chain = &(opts->fw_config->chain[FIREWD_MASQUERADE_ACCESS]);
1401 : : snprintf(snat_target, SNAT_TARGET_BUFSIZE-1, " ");
1402 : :
1403 : : /* Add SNAT or MASQUERADE rules.
1404 : : */
1405 [ + + ][ + + ]: 5 : if(acc->force_snat && acc->force_snat_ip != NULL && is_valid_ipv4_addr(acc->force_snat_ip, strlen(acc->force_snat_ip)))
[ + - ]
1406 : : {
1407 : : /* Using static SNAT */
1408 : 2 : snat_chain = &(opts->fw_config->chain[FIREWD_SNAT_ACCESS]);
1409 : 2 : snprintf(snat_target, SNAT_TARGET_BUFSIZE-1,
1410 : : "--to-source %s", acc->force_snat_ip);
1411 : : }
1412 [ + + ]: 3 : else if((opts->config[CONF_SNAT_TRANSLATE_IP] != NULL)
1413 [ + - ]: 1 : && is_valid_ipv4_addr(opts->config[CONF_SNAT_TRANSLATE_IP], strlen(opts->config[CONF_SNAT_TRANSLATE_IP])))
1414 : : {
1415 : : /* Using static SNAT */
1416 : 1 : snat_chain = &(opts->fw_config->chain[FIREWD_SNAT_ACCESS]);
1417 : 1 : snprintf(snat_target, SNAT_TARGET_BUFSIZE-1,
1418 : : "--to-source %s", opts->config[CONF_SNAT_TRANSLATE_IP]);
1419 : : }
1420 : :
1421 : : memset(rule_buf, 0, CMD_BUFSIZE);
1422 : :
1423 : 5 : snprintf(rule_buf, CMD_BUFSIZE-1, FIREWD_SNAT_ALL_RULE_ARGS,
1424 : 5 : snat_chain->table,
1425 : : spadat->use_src_ip,
1426 : : exp_ts,
1427 : 5 : snat_chain->target,
1428 : : snat_target
1429 : : );
1430 : :
1431 : 5 : firewd_rule(opts, rule_buf, NULL, spadat->use_src_ip,
1432 : : NULL, ANY_PROTO, ANY_PORT, NULL, NAT_ANY_PORT,
1433 : : snat_chain, exp_ts, now, "SNAT ALL", "*/*");
1434 : : }
1435 : : else
1436 : : {
1437 : : /* Add SNAT or MASQUERADE rules.
1438 : : */
1439 [ + + ][ + + ]: 8 : if(acc->force_snat && acc->force_snat_ip != NULL && is_valid_ipv4_addr(acc->force_snat_ip, strlen(acc->force_snat_ip)))
[ + - ]
1440 : : {
1441 : : /* Using static SNAT */
1442 : 2 : snat_chain = &(opts->fw_config->chain[FIREWD_SNAT_ACCESS]);
1443 : 2 : snprintf(snat_target, SNAT_TARGET_BUFSIZE-1,
1444 : : "--to-source %s", acc->force_snat_ip);
1445 : : }
1446 [ + + ][ + - ]: 6 : else if(acc->force_snat && acc->force_masquerade)
1447 : : {
1448 : : /* Using MASQUERADE */
1449 : 3 : snat_chain = &(opts->fw_config->chain[FIREWD_MASQUERADE_ACCESS]);
1450 : : snprintf(snat_target, SNAT_TARGET_BUFSIZE-1,
1451 : : "--to-ports %i", fst_port);
1452 : : }
1453 [ + + ]: 3 : else if((opts->config[CONF_SNAT_TRANSLATE_IP] != NULL)
1454 [ + - ]: 2 : && is_valid_ipv4_addr(opts->config[CONF_SNAT_TRANSLATE_IP], strlen(opts->config[CONF_SNAT_TRANSLATE_IP])))
1455 : : {
1456 : : /* Using static SNAT */
1457 : 2 : snat_chain = &(opts->fw_config->chain[FIREWD_SNAT_ACCESS]);
1458 : 2 : snprintf(snat_target, SNAT_TARGET_BUFSIZE-1,
1459 : : "--to-source %s", opts->config[CONF_SNAT_TRANSLATE_IP]);
1460 : : }
1461 : : else
1462 : : {
1463 : : /* Using MASQUERADE */
1464 : 1 : snat_chain = &(opts->fw_config->chain[FIREWD_MASQUERADE_ACCESS]);
1465 : : snprintf(snat_target, SNAT_TARGET_BUFSIZE-1,
1466 : : "--to-ports %i", fst_port);
1467 : : }
1468 : :
1469 : : memset(rule_buf, 0, CMD_BUFSIZE);
1470 : :
1471 : : snprintf(rule_buf, CMD_BUFSIZE-1, FIREWD_SNAT_RULE_ARGS,
1472 : 8 : snat_chain->table,
1473 : : fst_proto,
1474 : : nat_ip,
1475 : : nat_port,
1476 : : exp_ts,
1477 : 8 : snat_chain->target,
1478 : : snat_target
1479 : : );
1480 : :
1481 : 8 : firewd_rule(opts, rule_buf, NULL, spadat->use_src_ip,
1482 : : NULL, fst_proto, nat_port, nat_ip, nat_port,
1483 : : snat_chain, exp_ts, now, "SNAT",
1484 : 8 : spadat->spa_message_remain);
1485 : : }
1486 : 13 : return;
1487 : : }
1488 : :
1489 : : /****************************************************************************/
1490 : :
1491 : : /* Rule Processing - Create an access request...
1492 : : */
1493 : : int
1494 : 323 : process_spa_request(const fko_srv_options_t * const opts,
1495 : : const acc_stanza_t * const acc, spa_data_t * const spadat)
1496 : : {
1497 : 323 : char nat_ip[MAX_IPV4_STR_LEN] = {0};
1498 : 323 : char nat_dst[MAX_HOSTNAME_LEN] = {0};
1499 : 323 : unsigned int nat_port = 0;
1500 : : unsigned int fst_proto;
1501 : : unsigned int fst_port;
1502 : :
1503 : 323 : struct fw_chain * const in_chain = &(opts->fw_config->chain[FIREWD_INPUT_ACCESS]);
1504 : 323 : struct fw_chain * const out_chain = &(opts->fw_config->chain[FIREWD_OUTPUT_ACCESS]);
1505 : 323 : struct fw_chain * const fwd_chain = &(opts->fw_config->chain[FIREWD_FORWARD_ACCESS]);
1506 : 323 : struct fw_chain * const dnat_chain = &(opts->fw_config->chain[FIREWD_DNAT_ACCESS]);
1507 : :
1508 : 323 : acc_port_list_t *port_list = NULL;
1509 : 323 : acc_port_list_t *ple = NULL;
1510 : :
1511 : 323 : char *ndx = NULL;
1512 : 323 : int res = 0, is_err;
1513 : : int str_len;
1514 : : time_t now;
1515 : : unsigned int exp_ts;
1516 : :
1517 : : /* Parse and expand our access message.
1518 : : */
1519 [ - + ]: 323 : if(expand_acc_port_list(&port_list, spadat->spa_message_remain) != 1)
1520 : : {
1521 : : /* technically we would already have exited with an error if there were
1522 : : * any memory allocation errors (see the add_port_list() function), but
1523 : : * for completeness...
1524 : : */
1525 : 0 : free_acc_port_list(port_list);
1526 : 0 : return res;
1527 : : }
1528 : :
1529 : : /* Start at the top of the proto-port list...
1530 : : */
1531 : 323 : ple = port_list;
1532 : :
1533 : : /* Remember the first proto/port combo in case we need them
1534 : : * for NAT access requests.
1535 : : */
1536 : 323 : fst_proto = ple->proto;
1537 : 323 : fst_port = ple->port;
1538 : :
1539 : : /* Set our expire time value.
1540 : : */
1541 : 323 : time(&now);
1542 : 323 : exp_ts = now + spadat->fw_access_timeout;
1543 : :
1544 : : /* deal with SPA packets that themselves request a NAT operation
1545 : : */
1546 [ + + ]: 323 : if(spadat->message_type == FKO_LOCAL_NAT_ACCESS_MSG
1547 : : || spadat->message_type == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG
1548 : 323 : || spadat->message_type == FKO_NAT_ACCESS_MSG
1549 [ + - ]: 289 : || spadat->message_type == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG
1550 [ + + ]: 289 : || acc->force_nat)
1551 : : {
1552 [ + + ]: 46 : if(acc->force_nat)
1553 : : {
1554 : 17 : strlcpy(nat_ip, acc->force_nat_ip, sizeof(nat_ip));
1555 : 17 : nat_port = acc->force_nat_port;
1556 : : }
1557 : : else
1558 : : {
1559 : 29 : ndx = strchr(spadat->nat_access, ',');
1560 : 29 : str_len = strcspn(spadat->nat_access, ",");
1561 [ + - ]: 29 : if((ndx != NULL) && (str_len <= MAX_HOSTNAME_LEN))
1562 : : {
1563 : 29 : strlcpy(nat_dst, spadat->nat_access, str_len+1);
1564 [ + + ]: 29 : if(! is_valid_ipv4_addr(nat_dst, str_len))
1565 : : {
1566 [ + + ]: 4 : if(strncasecmp(opts->config[CONF_ENABLE_NAT_DNS], "Y", 1) == 0)
1567 : : {
1568 [ + + ]: 3 : if (!is_valid_hostname(nat_dst, str_len))
1569 : : {
1570 : 1 : log_msg(LOG_INFO, "Invalid Hostname in NAT SPA message");
1571 : 1 : free_acc_port_list(port_list);
1572 : 1 : return res;
1573 : : }
1574 [ + + ]: 2 : if (ipv4_resolve(nat_dst, nat_ip) == 0)
1575 : : {
1576 : 1 : log_msg(LOG_INFO, "Resolved NAT IP in SPA message");
1577 : : }
1578 : : else
1579 : : {
1580 : 1 : log_msg(LOG_INFO, "Unable to resolve Hostname in NAT SPA message");
1581 : 1 : free_acc_port_list(port_list);
1582 : 1 : return res;
1583 : : }
1584 : : }
1585 : : else
1586 : : {
1587 : 1 : log_msg(LOG_INFO, "Received Hostname in NAT SPA message, but hostname is disabled.");
1588 : 1 : free_acc_port_list(port_list);
1589 : 1 : return res;
1590 : :
1591 : : }
1592 : : }
1593 : : else
1594 : : {
1595 : 25 : strlcpy(nat_ip, nat_dst, MAX_IPV4_STR_LEN);
1596 : : }
1597 : :
1598 : 26 : nat_port = strtol_wrapper(ndx+1, 0, MAX_PORT,
1599 : : NO_EXIT_UPON_ERR, &is_err);
1600 [ - + ]: 26 : if(is_err != FKO_SUCCESS)
1601 : : {
1602 : 0 : log_msg(LOG_INFO, "Invalid NAT port in SPA message");
1603 : 0 : free_acc_port_list(port_list);
1604 : 0 : res = is_err;
1605 : 0 : return res;
1606 : : }
1607 : : }
1608 : : else
1609 : : {
1610 : 0 : log_msg(LOG_INFO, "Invalid NAT IP in SPA message");
1611 : 0 : free_acc_port_list(port_list);
1612 : 0 : return res;
1613 : : }
1614 : : }
1615 : :
1616 [ + + ]: 43 : if(spadat->message_type == FKO_LOCAL_NAT_ACCESS_MSG
1617 : 43 : || spadat->message_type == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG)
1618 : : {
1619 [ - + ]: 11 : firewd_rule(opts, NULL, FIREWD_RULE_ARGS, spadat->use_src_ip,
1620 : 11 : (fwc.use_destination ? spadat->pkt_destination_ip : FIREWD_ANY_IP),
1621 : : fst_proto, nat_port, nat_ip, nat_port, in_chain, exp_ts,
1622 : : now, "local NAT", spadat->spa_message_remain);
1623 : : }
1624 [ + - ]: 32 : else if(strlen(fwd_chain->to_chain))
1625 : : {
1626 : : /* FORWARD access rule
1627 : : */
1628 : 32 : forward_access_rule(opts, acc, fwd_chain, nat_ip,
1629 : : nat_port, fst_proto, fst_port, spadat, exp_ts, now);
1630 : : }
1631 : :
1632 : : /* DNAT rule
1633 : : */
1634 [ + - ][ + + ]: 43 : if(strlen(dnat_chain->to_chain) && !acc->disable_dnat)
1635 : 40 : dnat_rule(opts, acc, dnat_chain, nat_ip,
1636 : : nat_port, fst_proto, fst_port, spadat, exp_ts, now);
1637 : :
1638 : : /* SNAT rule
1639 : : */
1640 [ + + ][ + + ]: 43 : if(acc->force_snat || strncasecmp(opts->config[CONF_ENABLE_FIREWD_SNAT], "Y", 1) == 0)
1641 : 13 : snat_rule(opts, acc, nat_ip, nat_port,
1642 : : fst_proto, fst_port, spadat, exp_ts, now);
1643 : : }
1644 : : else /* Non-NAT request - this is the typical case. */
1645 : : {
1646 : : /* Create an access command for each proto/port for the source ip.
1647 : : */
1648 [ + + ]: 562 : while(ple != NULL)
1649 : : {
1650 [ + + ]: 285 : firewd_rule(opts, NULL, FIREWD_RULE_ARGS, spadat->use_src_ip,
1651 : 285 : (fwc.use_destination ? spadat->pkt_destination_ip : FIREWD_ANY_IP),
1652 : : ple->proto, ple->port, NULL, NAT_ANY_PORT,
1653 : : in_chain, exp_ts, now, "access", spadat->spa_message_remain);
1654 : :
1655 : : /* We need to make a corresponding OUTPUT rule if out_chain target
1656 : : * is not NULL.
1657 : : */
1658 [ + + ]: 285 : if(strlen(out_chain->to_chain))
1659 : : {
1660 [ - + ]: 1 : firewd_rule(opts, NULL, FIREWD_OUT_RULE_ARGS, spadat->use_src_ip,
1661 : 1 : (fwc.use_destination ? spadat->pkt_destination_ip : FIREWD_ANY_IP),
1662 : : ple->proto, ple->port, NULL, NAT_ANY_PORT,
1663 : : out_chain, exp_ts, now, "OUTPUT", spadat->spa_message_remain);
1664 : : }
1665 : 285 : ple = ple->next;
1666 : : }
1667 : : }
1668 : :
1669 : : /* Done with the port list for access rules.
1670 : : */
1671 : 320 : free_acc_port_list(port_list);
1672 : :
1673 : 320 : return(res);
1674 : : }
1675 : :
1676 : : static void
1677 : 884 : rm_expired_rules(const fko_srv_options_t * const opts,
1678 : : const char * const fw_output_buf,
1679 : : char *ndx, struct fw_chain *ch, int cpos, time_t now)
1680 : : {
1681 : 884 : char exp_str[12] = {0};
1682 : 884 : char rule_num_str[6] = {0};
1683 : : char *rn_start, *rn_end, *tmp_mark;
1684 : :
1685 : 884 : int res, is_err, rn_offset=0, rule_num;
1686 : 884 : time_t rule_exp, min_exp = 0;
1687 : :
1688 : : /* walk the list and process rules as needed.
1689 : : */
1690 [ + + ]: 5048 : while (ndx != NULL) {
1691 : : /* Jump forward and extract the timestamp
1692 : : */
1693 : 4164 : ndx += strlen(EXPIRE_COMMENT_PREFIX);
1694 : :
1695 : : /* remember this spot for when we look for the next
1696 : : * rule.
1697 : : */
1698 : 4164 : tmp_mark = ndx;
1699 : :
1700 : 4164 : strlcpy(exp_str, ndx, sizeof(exp_str));
1701 [ + + ]: 4164 : if (strchr(exp_str, '*') != NULL)
1702 : 697 : strchr(exp_str, '*')[0] = '\0';
1703 : :
1704 : 4164 : chop_spaces(exp_str);
1705 [ + + ]: 4164 : if(!is_digits(exp_str))
1706 : : {
1707 : : /* go to the next rule if it exists
1708 : : */
1709 : 696 : ndx = strstr(tmp_mark, EXPIRE_COMMENT_PREFIX);
1710 : 696 : continue;
1711 : : }
1712 : :
1713 : 3468 : rule_exp = (time_t)atoll(exp_str);
1714 : :
1715 [ + + ]: 3468 : if(rule_exp <= now)
1716 : : {
1717 : : /* Backtrack and get the rule number and delete it.
1718 : : */
1719 : : rn_start = ndx;
1720 [ + - ]: 38499 : while(--rn_start > fw_output_buf)
1721 : : {
1722 [ + + ]: 38942 : if(*rn_start == '\n')
1723 : : break;
1724 : : }
1725 : :
1726 [ - + ]: 443 : if(*rn_start != '\n')
1727 : : {
1728 : : /* This should not happen. But if it does, complain,
1729 : : * decrement the active rule value, and go on.
1730 : : */
1731 : 0 : log_msg(LOG_ERR,
1732 : : "Rule parse error while finding rule line start in chain %i",
1733 : : cpos);
1734 : :
1735 [ # # ]: 0 : if (ch[cpos].active_rules > 0)
1736 : 0 : ch[cpos].active_rules--;
1737 : :
1738 : : break;
1739 : : }
1740 : 443 : rn_start++;
1741 : :
1742 : 443 : rn_end = strchr(rn_start, ' ');
1743 [ - + ]: 443 : if(rn_end == NULL)
1744 : : {
1745 : : /* This should not happen. But if it does, complain,
1746 : : * decrement the active rule value, and go on.
1747 : : */
1748 : 0 : log_msg(LOG_ERR,
1749 : : "Rule parse error while finding rule number in chain %i",
1750 : : cpos);
1751 : :
1752 [ # # ]: 0 : if (ch[cpos].active_rules > 0)
1753 : 0 : ch[cpos].active_rules--;
1754 : :
1755 : : break;
1756 : : }
1757 : :
1758 : 443 : strlcpy(rule_num_str, rn_start, (rn_end - rn_start)+1);
1759 : :
1760 : 443 : rule_num = strtol_wrapper(rule_num_str, rn_offset, RCHK_MAX_FIREWD_RULE_NUM,
1761 : : NO_EXIT_UPON_ERR, &is_err);
1762 [ - + ]: 443 : if(is_err != FKO_SUCCESS)
1763 : : {
1764 : 0 : log_msg(LOG_ERR,
1765 : : "Rule parse error while finding rule number in chain %i",
1766 : : cpos);
1767 : :
1768 [ # # ]: 0 : if (ch[cpos].active_rules > 0)
1769 : 0 : ch[cpos].active_rules--;
1770 : :
1771 : : break;
1772 : : }
1773 : :
1774 : 443 : zero_cmd_buffers();
1775 : :
1776 : 443 : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_DEL_RULE_ARGS,
1777 : 443 : opts->fw_config->fw_command,
1778 : 443 : ch[cpos].table,
1779 : 443 : ch[cpos].to_chain,
1780 : : rule_num - rn_offset /* account for position of previously
1781 : : deleted rule with rn_offset */
1782 : : );
1783 : :
1784 : 443 : res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE,
1785 : : WANT_STDERR, NO_TIMEOUT, &pid_status, opts);
1786 : 443 : chop_newline(err_buf);
1787 : :
1788 : 443 : log_msg(LOG_DEBUG, "check_firewall_rules() CMD: '%s' (res: %d, err: %s)",
1789 : : cmd_buf, res, err_buf);
1790 : :
1791 [ + - ]: 443 : if(EXTCMD_IS_SUCCESS(res))
1792 : : {
1793 : 443 : log_msg(LOG_INFO, "Removed rule %s from %s with expire time of %u",
1794 : : rule_num_str, ch[cpos].to_chain, rule_exp
1795 : : );
1796 : :
1797 : 443 : rn_offset++;
1798 : :
1799 [ + + ]: 443 : if (ch[cpos].active_rules > 0)
1800 : 375 : ch[cpos].active_rules--;
1801 : : }
1802 : : else
1803 : 0 : log_msg(LOG_ERR, "rm_expired_rules() Error %i from cmd:'%s': %s",
1804 : : res, cmd_buf, err_buf);
1805 : :
1806 : : }
1807 : : else
1808 : : {
1809 : : /* Track the minimum future rule expire time.
1810 : : */
1811 : 3025 : min_exp = (min_exp < rule_exp) ? min_exp : rule_exp;
1812 : : }
1813 : :
1814 : : /* Push our tracking index forward beyond (just processed) _exp_
1815 : : * string so we can continue to the next rule in the list.
1816 : : */
1817 : 4164 : ndx = strstr(tmp_mark, EXPIRE_COMMENT_PREFIX);
1818 : : }
1819 : :
1820 : : /* Set the next pending expire time accordingly. 0 if there are no
1821 : : * more rules, or whatever the next expected (min_exp) time will be.
1822 : : */
1823 [ + + ]: 884 : if(ch[cpos].active_rules < 1)
1824 : 589 : ch[cpos].next_expire = 0;
1825 [ - + ]: 295 : else if(min_exp)
1826 : 0 : ch[cpos].next_expire = min_exp;
1827 : :
1828 : 884 : return;
1829 : : }
1830 : :
1831 : : /* Iterate over the configure firewall access chains and purge expired
1832 : : * firewall rules.
1833 : : */
1834 : : void
1835 : 56756 : check_firewall_rules(const fko_srv_options_t * const opts,
1836 : : const int chk_rm_all)
1837 : : {
1838 : : char *ndx;
1839 : 56756 : char fw_output_buf[STANDARD_CMD_OUT_BUFSIZE] = {0};
1840 : :
1841 : : int i, res;
1842 : : time_t now;
1843 : :
1844 : 56756 : struct fw_chain *ch = opts->fw_config->chain;
1845 : :
1846 : 56756 : time(&now);
1847 : :
1848 : : /* Iterate over each chain and look for active rules to delete.
1849 : : */
1850 [ + + ]: 397292 : for(i=0; i < NUM_FWKNOP_ACCESS_TYPES; i++)
1851 : : {
1852 : : /* If there are no active rules or we have not yet
1853 : : * reached our expected next expire time, continue.
1854 : : */
1855 [ + + ][ + + ]: 340536 : if(!chk_rm_all && (ch[i].active_rules == 0 || ch[i].next_expire > now))
[ + + ]
1856 : 324033 : continue;
1857 : :
1858 [ + + ][ - + ]: 16503 : if(ch[i].table[0] == '\0' || ch[i].to_chain[i] == '\0')
1859 : 13069 : continue;
1860 : :
1861 : 3434 : zero_cmd_buffers();
1862 : : memset(fw_output_buf, 0x0, STANDARD_CMD_OUT_BUFSIZE);
1863 : :
1864 : : /* Get the current list of rules for this chain and delete
1865 : : * any that have expired. Note that chk_rm_all puts us in
1866 : : * garbage collection mode, and allows any rules that have
1867 : : * been manually added (potentially by a program separate
1868 : : * from fwknopd) to take advantage of fwknopd's timeout
1869 : : * mechanism.
1870 : : */
1871 : : snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_LIST_RULES_ARGS,
1872 : 3434 : opts->fw_config->fw_command,
1873 : 3434 : ch[i].table,
1874 : 3434 : ch[i].to_chain
1875 : : );
1876 : :
1877 : 3434 : res = run_extcmd(cmd_buf, fw_output_buf, STANDARD_CMD_OUT_BUFSIZE,
1878 : : WANT_STDERR, NO_TIMEOUT, &pid_status, opts);
1879 : 3434 : chop_newline(fw_output_buf);
1880 : :
1881 : 3434 : log_msg(LOG_DEBUG,
1882 : : "check_firewall_rules() CMD: '%s' (res: %d, fw_output_buf: %s)",
1883 : : cmd_buf, res, fw_output_buf);
1884 : :
1885 [ - + ]: 3434 : if(!EXTCMD_IS_SUCCESS(res))
1886 : : {
1887 : 0 : log_msg(LOG_ERR,
1888 : : "check_firewall_rules() Error %i from cmd:'%s': %s",
1889 : : res, cmd_buf, fw_output_buf);
1890 : 0 : continue;
1891 : : }
1892 : :
1893 : 3434 : log_msg(LOG_DEBUG, "RES=%i, CMD_BUF: %s\nRULES LIST: %s",
1894 : : res, cmd_buf, fw_output_buf);
1895 : :
1896 : 3434 : ndx = strstr(fw_output_buf, EXPIRE_COMMENT_PREFIX);
1897 [ + + ]: 3434 : if(ndx == NULL)
1898 : : {
1899 : : /* we did not find a candidate rule to expire
1900 : : */
1901 : 2550 : log_msg(LOG_DEBUG,
1902 : : "Did not find expire comment in rules list %i", i);
1903 : :
1904 [ - + ]: 2550 : if (ch[i].active_rules > 0)
1905 : 0 : ch[i].active_rules--;
1906 : :
1907 : 2550 : continue;
1908 : : }
1909 : 884 : rm_expired_rules(opts, fw_output_buf, ndx, ch, i, now);
1910 : : }
1911 : :
1912 : 56756 : return;
1913 : : }
1914 : :
1915 : : int
1916 : 36929 : validate_firewd_chain_conf(const char * const chain_str)
1917 : : {
1918 : 36929 : int j, rv = 1;
1919 : 36929 : const char *ndx = chain_str;
1920 : :
1921 : 36929 : j = 1;
1922 [ + + ]: 1735086 : while(*ndx != '\0')
1923 : : {
1924 [ + + ]: 1698159 : if(*ndx == ',')
1925 : 184617 : j++;
1926 : :
1927 [ + + ]: 1698159 : if(*ndx != '\0'
1928 : 1698159 : && *ndx != ' '
1929 [ + + ]: 1513541 : && *ndx != ','
1930 [ + + ]: 1328924 : && *ndx != '_'
1931 [ + + ]: 1291981 : && isalnum(*ndx) == 0)
1932 : : {
1933 : : rv = 0;
1934 : : break;
1935 : : }
1936 : 1698157 : ndx++;
1937 : : }
1938 : :
1939 : : /* Sanity check - j should be the number of chain fields
1940 : : * (excluding the type).
1941 : : */
1942 [ + + ]: 36929 : if(j != FW_NUM_CHAIN_FIELDS)
1943 : 7 : rv = 0;
1944 : :
1945 : 36929 : return rv;
1946 : : }
1947 : :
1948 : : #endif /* FIREWALL_FIREWALLD */
1949 : :
1950 : : /***EOF***/
|