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