8e114707ded470c163400f6d5eb79a1a73ea77dd
[fwknop.git] / server / fw_util_iptables.c
1 /*
2  *****************************************************************************
3  *
4  * File:    fw_util_iptables.c
5  *
6  * Author:  Damien S. Stuart
7  *
8  * Purpose: Fwknop routines for managing iptables firewall rules.
9  *
10  * Copyright 2010 Damien Stuart (dstuart@dstuart.org)
11  *
12  *  License (GNU 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 static void
48 zero_cmd_buffers(void)
49 {
50     memset(cmd_buf, 0x0, CMD_BUFSIZE);
51     memset(err_buf, 0x0, CMD_BUFSIZE);
52     memset(cmd_out, 0x0, STANDARD_CMD_OUT_BUFSIZE);
53 }
54
55 static int
56 add_jump_rule(const fko_srv_options_t *opts, const int chain_num)
57 {
58     int res = 0;
59
60     zero_cmd_buffers();
61
62     snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_ADD_JUMP_RULE_ARGS,
63         fwc.fw_command,
64         fwc.chain[chain_num].table,
65         fwc.chain[chain_num].from_chain,
66         fwc.chain[chain_num].jump_rule_pos,
67         fwc.chain[chain_num].to_chain
68     );
69
70     res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0);
71
72     if (opts->verbose)
73         log_msg(LOG_INFO, "add_jump_rule() CMD: '%s' (res: %d, err: %s)",
74             cmd_buf, res, err_buf);
75
76     if(EXTCMD_IS_SUCCESS(res))
77         log_msg(LOG_INFO, "Added jump rule from chain: %s to chain: %s",
78             fwc.chain[chain_num].from_chain,
79             fwc.chain[chain_num].to_chain);
80     else
81         log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf);
82
83     return res;
84 }
85
86 static int
87 jump_rule_exists(const int chain_num)
88 {
89     int     num, pos = 0;
90     char    cmd_buf[CMD_BUFSIZE] = {0};
91     char    target[CMD_BUFSIZE] = {0};
92     char    line_buf[CMD_BUFSIZE] = {0};
93     FILE   *ipt;
94
95     sprintf(cmd_buf, "%s " IPT_LIST_RULES_ARGS,
96         fwc.fw_command,
97         fwc.chain[chain_num].table,
98         fwc.chain[chain_num].from_chain
99     );
100
101     ipt = popen(cmd_buf, "r");
102
103     if(ipt == NULL)
104     {
105         log_msg(LOG_ERR,
106             "Got error %i trying to get rules list.\n", errno);
107         return(-1);
108     }
109
110     while((fgets(line_buf, CMD_BUFSIZE-1, ipt)) != NULL)
111     {
112         /* Get past comments and empty lines (note: we only look at the
113          * first character).
114         */
115         if(IS_EMPTY_LINE(line_buf[0]))
116             continue;
117
118         if(sscanf(line_buf, "%i %s ", &num, target) == 2)
119         {
120             if(strcmp(target, fwc.chain[chain_num].to_chain) == 0)
121             {
122                 pos = num;
123                 break;
124             }
125         }
126     }
127
128     pclose(ipt);
129
130     return(pos);
131 }
132
133 /* Print all firewall rules currently instantiated by the running fwknopd
134  * daemon to stdout.
135 */
136 int
137 fw_dump_rules(const fko_srv_options_t *opts)
138 {
139     int     i;
140     int     res, got_err = 0;
141
142     struct fw_chain *ch = opts->fw_config->chain;
143
144     if (opts->fw_list_all == 1)
145     {
146         fprintf(stdout, "Listing all iptables rules in applicable tables...\n");
147         fflush(stdout);
148
149         for(i=0; i<(NUM_FWKNOP_ACCESS_TYPES); i++)
150         {
151
152             if(fwc.chain[i].target[0] == '\0')
153                 continue;
154
155             zero_cmd_buffers();
156
157             /* Create the list command
158             */
159             snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_LIST_ALL_RULES_ARGS,
160                 opts->fw_config->fw_command,
161                 ch[i].table
162             );
163
164             res = system(cmd_buf);
165
166             if (opts->verbose)
167                 log_msg(LOG_INFO, "fw_dump_rules() CMD: '%s' (res: %d)",
168                     cmd_buf, res);
169
170             /* Expect full success on this */
171             if(! EXTCMD_IS_SUCCESS(res))
172             {
173                 log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); 
174                 got_err++;
175             }
176         }
177     }
178     else
179     {
180         fprintf(stdout, "Listing rules in fwknopd iptables chains...\n");
181         fflush(stdout);
182
183         for(i=0; i<(NUM_FWKNOP_ACCESS_TYPES); i++)
184         {
185
186             if(fwc.chain[i].target[0] == '\0')
187                 continue;
188
189             zero_cmd_buffers();
190
191             /* Create the list command
192             */
193             snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_LIST_RULES_ARGS,
194                 opts->fw_config->fw_command,
195                 ch[i].table,
196                 ch[i].to_chain
197             );
198
199             res = system(cmd_buf);
200
201             if (opts->verbose)
202                 log_msg(LOG_INFO, "fw_dump_rules() CMD: '%s' (res: %d)",
203                     cmd_buf, res);
204
205             /* Expect full success on this */
206             if(! EXTCMD_IS_SUCCESS(res))
207             {
208                 log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); 
209                 got_err++;
210             }
211         }
212     }
213
214     return(got_err);
215 }
216
217 /* Quietly flush and delete all fwknop custom chains.
218 */
219 static void
220 delete_all_chains(const fko_srv_options_t *opts)
221 {
222     int     i, res;
223     int     jump_rule_num;
224
225     for(i=0; i<(NUM_FWKNOP_ACCESS_TYPES); i++)
226     {
227         if(fwc.chain[i].target[0] == '\0')
228             continue;
229
230         /* First look for a jump rule to this chain and remove it if it
231          * is there.
232         */
233         if((jump_rule_num = jump_rule_exists(i)) > 0)
234         {
235             zero_cmd_buffers();
236
237             snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_DEL_RULE_ARGS,
238                 fwc.fw_command,
239                 fwc.chain[i].table,
240                 fwc.chain[i].from_chain,
241                 jump_rule_num
242             );
243
244             res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0);
245
246             if (opts->verbose)
247                 log_msg(LOG_INFO, "delete_all_chains() CMD: '%s' (res: %d, err: %s)",
248                     cmd_buf, res, err_buf);
249
250             /* Expect full success on this */
251             if(! EXTCMD_IS_SUCCESS(res))
252                 log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf);
253         }
254
255         zero_cmd_buffers();
256
257         /* Now flush and remove the chain.
258         */
259         snprintf(cmd_buf, CMD_BUFSIZE-1,
260             "(%s " IPT_FLUSH_CHAIN_ARGS "; %s " IPT_DEL_CHAIN_ARGS ")", // > /dev/null 2>&1",
261             fwc.fw_command,
262             fwc.chain[i].table,
263             fwc.chain[i].to_chain,
264             fwc.fw_command,
265             fwc.chain[i].table,
266             fwc.chain[i].to_chain
267         );
268
269         res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0);
270
271         if (opts->verbose)
272             log_msg(LOG_INFO, "delete_all_chains() CMD: '%s' (res: %d, err: %s)",
273                 cmd_buf, res, err_buf);
274
275         /* Expect full success on this */
276         if(! EXTCMD_IS_SUCCESS(res))
277             log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf);
278     }
279 }
280
281 /* Create the fwknop custom chains (at least those that are configured).
282 */
283 static int
284 create_fw_chains(const fko_srv_options_t *opts)
285 {
286     int     i;
287     int     res, got_err = 0;
288
289     for(i=0; i<(NUM_FWKNOP_ACCESS_TYPES); i++)
290     {
291         if(fwc.chain[i].target[0] == '\0')
292             continue;
293
294         zero_cmd_buffers();
295
296         /* Create the custom chain.
297         */
298         snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_NEW_CHAIN_ARGS,
299             fwc.fw_command,
300             fwc.chain[i].table,
301             fwc.chain[i].to_chain
302         );
303
304         res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0);
305
306         if (opts->verbose)
307             log_msg(LOG_INFO, "create_fw_chains() CMD: '%s' (res: %d, err: %s)",
308                 cmd_buf, res, err_buf);
309
310         /* Expect full success on this */
311         if(! EXTCMD_IS_SUCCESS(res))
312         {
313             log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf);
314             got_err++;
315         }
316
317         /* Then create the jump rule to that chain.
318         */
319         res = add_jump_rule(opts, i);
320
321         /* Expect full success on this */
322         if(! EXTCMD_IS_SUCCESS(res))
323             got_err++;
324     }
325
326     return(got_err);
327 }
328
329
330 static void
331 set_fw_chain_conf(const int type, char *conf_str)
332 {
333     int i, j;
334     char tbuf[1024]     = {0};
335     char *ndx           = conf_str;
336
337     char *chain_fields[FW_NUM_CHAIN_FIELDS];
338
339     struct fw_chain *chain = &(fwc.chain[type]);
340
341     chain->type = type;
342
343     if(ndx != NULL)
344         chain_fields[0] = tbuf;
345
346     i = 0;
347     j = 1;
348     while(*ndx != '\0')
349     {
350         if(*ndx != ' ')
351         {
352             if(*ndx == ',')
353             {
354                 tbuf[i] = '\0';
355                 chain_fields[j++] = &(tbuf[++i]);
356             }
357             else
358                 tbuf[i++] = *ndx;
359         }
360         ndx++;
361     }
362
363     /* Sanity check - j should be the number of chain fields
364      * (excluding the type).
365     */
366     if(j != FW_NUM_CHAIN_FIELDS)
367     {
368         fprintf(stderr, "[*] Custom Chain config parse error.\n"
369             "Wrong number of fields for chain type %i\n"
370             "Line: %s\n", type, conf_str);
371         exit(EXIT_FAILURE);
372     }
373
374     /* Pull and set Target */
375     strlcpy(chain->target, chain_fields[0], MAX_TARGET_NAME_LEN);
376
377     /* Pull and set Table */
378     strlcpy(chain->table, chain_fields[1], MAX_TABLE_NAME_LEN);
379
380     /* Pull and set From_chain */
381     strlcpy(chain->from_chain, chain_fields[2], MAX_CHAIN_NAME_LEN);
382
383     /* Pull and set Jump_rule_position */
384     chain->jump_rule_pos = atoi(chain_fields[3]);
385
386     /* Pull and set To_chain */
387     strlcpy(chain->to_chain, chain_fields[4], MAX_CHAIN_NAME_LEN);
388
389     /* Pull and set Jump_rule_position */
390     chain->rule_pos = atoi(chain_fields[5]);
391
392 }
393
394 void
395 fw_config_init(fko_srv_options_t *opts)
396 {
397
398     memset(&fwc, 0x0, sizeof(struct fw_config));
399
400     /* Set our firewall exe command path (iptables in most cases).
401     */
402     strlcpy(fwc.fw_command, opts->config[CONF_FIREWALL_EXE], MAX_PATH_LEN);
403
404     /* Pull the fwknop chain config info and setup our internal
405      * config struct.  The IPT_INPUT is the only one that is
406      * required. The rest are optional.
407     */
408     set_fw_chain_conf(IPT_INPUT_ACCESS, opts->config[CONF_IPT_INPUT_ACCESS]);
409
410     /* The FWKNOP_OUTPUT_ACCESS requires ENABLE_IPT_OUTPUT_ACCESS be Y
411     */
412     if(strncasecmp(opts->config[CONF_ENABLE_IPT_OUTPUT], "Y", 1)==0)
413         set_fw_chain_conf(IPT_OUTPUT_ACCESS, opts->config[CONF_IPT_OUTPUT_ACCESS]);
414
415     /* The remaining access chains require ENABLE_IPT_FORWARDING = Y
416     */
417     if(strncasecmp(opts->config[CONF_ENABLE_IPT_FORWARDING], "Y", 1)==0)
418     {
419
420         set_fw_chain_conf(IPT_FORWARD_ACCESS, opts->config[CONF_IPT_FORWARD_ACCESS]);
421         set_fw_chain_conf(IPT_DNAT_ACCESS, opts->config[CONF_IPT_DNAT_ACCESS]);
422
423         /* SNAT (whichever mode) requires ENABLE_IPT_SNAT = Y
424         */
425         if(strncasecmp(opts->config[CONF_ENABLE_IPT_SNAT], "Y", 1)==0)
426         {
427             /* If an SNAT_TRANSLATE_IP is specified use the SNAT_ACCESS mode.
428              * Otherwise, use MASQUERADE_ACCESS.
429              *
430              * XXX: --DSS: Not sure if using the TRANSLATE_IP parameter as
431              *             the determining factor is the best why to handle
432              *             this.
433              *
434             */
435             if(opts->config[CONF_SNAT_TRANSLATE_IP] != NULL
436               && strncasecmp(opts->config[CONF_SNAT_TRANSLATE_IP], "__CHANGEME__", 10)!=0)
437                 set_fw_chain_conf(IPT_SNAT_ACCESS, opts->config[CONF_IPT_SNAT_ACCESS]);
438             else
439                 set_fw_chain_conf(IPT_MASQUERADE_ACCESS, opts->config[CONF_IPT_MASQUERADE_ACCESS]);
440         }
441     }
442
443     /* Let us find it via our opts struct as well.
444     */
445     opts->fw_config = &fwc;
446
447     return;
448 }
449
450 void
451 fw_initialize(const fko_srv_options_t *opts)
452 {
453     int res;
454
455     /* Flush the chains (just in case) so we can start fresh.
456     */
457     if(strncasecmp(opts->config[CONF_FLUSH_IPT_AT_INIT], "Y", 1) == 0)
458         delete_all_chains(opts);
459
460     /* Now create any configured chains.
461     */
462     res = create_fw_chains(opts);
463
464     if(res != 0)
465     {
466         fprintf(stderr, "Warning: Errors detected during fwknop custom chain creation.\n");
467         exit(EXIT_FAILURE);
468     }
469 }
470
471 int
472 fw_cleanup(const fko_srv_options_t *opts)
473 {
474     if(strncasecmp(opts->config[CONF_FLUSH_IPT_AT_EXIT], "N", 1) == 0)
475         return(0);
476
477     delete_all_chains(opts);
478     return(0);
479 }
480
481 /****************************************************************************/
482
483 /* Rule Processing - Create an access request...
484 */
485 int
486 process_spa_request(const fko_srv_options_t *opts, const acc_stanza_t *acc, spa_data_t *spadat)
487 {
488     char             nat_ip[MAX_IPV4_STR_LEN] = {0};
489     char             snat_target[SNAT_TARGET_BUFSIZE] = {0};
490     char            *ndx;
491
492     unsigned int     nat_port = 0;
493
494     acc_port_list_t *port_list = NULL;
495     acc_port_list_t *ple;
496
497     unsigned int    fst_proto;
498     unsigned int    fst_port;
499
500     struct fw_chain *in_chain   = &(opts->fw_config->chain[IPT_INPUT_ACCESS]);
501     struct fw_chain *out_chain  = &(opts->fw_config->chain[IPT_OUTPUT_ACCESS]);
502     struct fw_chain *fwd_chain  = &(opts->fw_config->chain[IPT_FORWARD_ACCESS]);
503     struct fw_chain *dnat_chain = &(opts->fw_config->chain[IPT_DNAT_ACCESS]);
504     struct fw_chain *snat_chain; /* We assign this later (if we need to). */
505
506     int             res = 0;
507     time_t          now;
508     unsigned int    exp_ts;
509
510     /* Parse and expand our access message.
511     */
512     expand_acc_port_list(&port_list, spadat->spa_message_remain);
513
514     /* Start at the top of the proto-port list...
515     */
516     ple = port_list;
517
518     /* Remember the first proto/port combo in case we need them
519      * for NAT access requests.
520     */
521     fst_proto = ple->proto;
522     fst_port  = ple->port;
523
524     /* Set our expire time value.
525     */
526     time(&now);
527     exp_ts = now + spadat->fw_access_timeout;
528
529     /* For straight access requests, we currently support multiple proto/port
530      * request.
531     */
532     if((spadat->message_type == FKO_ACCESS_MSG
533       || spadat->message_type == FKO_CLIENT_TIMEOUT_ACCESS_MSG) && !acc->force_nat)
534     {
535
536         /* Check to make sure that the jump rules exist for each
537          * required chain
538         */
539         if(jump_rule_exists(IPT_INPUT_ACCESS) == 0)
540             add_jump_rule(opts, IPT_INPUT_ACCESS);
541
542         if(out_chain->to_chain != NULL && strlen(out_chain->to_chain))
543             if(jump_rule_exists(IPT_OUTPUT_ACCESS) == 0)
544                 add_jump_rule(opts, IPT_OUTPUT_ACCESS);
545
546         /* Create an access command for each proto/port for the source ip.
547         */
548         while(ple != NULL)
549         {
550             zero_cmd_buffers();
551
552             snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_ADD_RULE_ARGS,
553                 opts->fw_config->fw_command,
554                 in_chain->table,
555                 in_chain->to_chain,
556                 ple->proto,
557                 spadat->use_src_ip,
558                 ple->port,
559                 exp_ts,
560                 in_chain->target
561             );
562
563             res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0);
564
565             if (opts->verbose)
566                 log_msg(LOG_INFO, "process_spa_request() CMD: '%s' (res: %d, err: %s)",
567                     cmd_buf, res, err_buf);
568
569             if(EXTCMD_IS_SUCCESS(res))
570             {
571                 log_msg(LOG_INFO, "Added Rule to %s for %s, %s expires at %u",
572                     in_chain->to_chain, spadat->use_src_ip,
573                     spadat->spa_message_remain, exp_ts
574                 );
575
576                 in_chain->active_rules++;
577
578                 /* Reset the next expected expire time for this chain if it
579                 * is warranted.
580                 */
581                 if(in_chain->next_expire < now || exp_ts < in_chain->next_expire)
582                     in_chain->next_expire = exp_ts;
583             }
584             else
585                 log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf);
586
587             /* If we have to make an corresponding OUTPUT rule if out_chain target
588             * is not NULL.
589             */
590             if(out_chain->to_chain != NULL && strlen(out_chain->to_chain))
591             {
592                 zero_cmd_buffers();
593
594                 snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_ADD_OUT_RULE_ARGS,
595                     opts->fw_config->fw_command,
596                     out_chain->table,
597                     out_chain->to_chain,
598                     ple->proto,
599                     spadat->use_src_ip,
600                     ple->port,
601                     exp_ts,
602                     out_chain->target
603                 );
604
605                 res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0);
606
607                 if (opts->verbose)
608                     log_msg(LOG_INFO, "process_spa_request() CMD: '%s' (res: %d, err: %s)",
609                         cmd_buf, res, err_buf);
610
611                 if(EXTCMD_IS_SUCCESS(res))
612                 {
613                     log_msg(LOG_INFO, "Added OUTPUT Rule to %s for %s, %s expires at %u",
614                         out_chain->to_chain, spadat->use_src_ip,
615                         spadat->spa_message_remain, exp_ts
616                     );
617
618                     out_chain->active_rules++;
619
620                     /* Reset the next expected expire time for this chain if it
621                     * is warranted.
622                     */
623                     if(out_chain->next_expire < now || exp_ts < out_chain->next_expire)
624                         out_chain->next_expire = exp_ts;
625                 }
626                 else
627                     log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); 
628
629             }
630
631             ple = ple->next;
632         }
633     }
634     /* NAT requests... */
635     else if(spadat->message_type == FKO_LOCAL_NAT_ACCESS_MSG
636       || spadat->message_type == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG
637       || spadat->message_type == FKO_NAT_ACCESS_MSG
638       || spadat->message_type == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG
639       || acc->force_nat)
640     {
641         /* Parse out the NAT IP and Port components.
642         */
643         if(acc->force_nat)
644         {
645             strlcpy(nat_ip, acc->force_nat_ip, MAX_IPV4_STR_LEN);
646             nat_port = acc->force_nat_port;
647         }
648         else
649         {
650             ndx = strchr(spadat->nat_access, ',');
651             if(ndx != NULL)
652             {
653                 strlcpy(nat_ip, spadat->nat_access, (ndx-spadat->nat_access)+1);
654                 nat_port = atoi(ndx+1);
655             }
656         }
657
658         if(spadat->message_type == FKO_LOCAL_NAT_ACCESS_MSG)
659         {
660             /* Need to add an ACCEPT rule into the INPUT chain
661             */
662             zero_cmd_buffers();
663
664             snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_ADD_RULE_ARGS,
665                 opts->fw_config->fw_command,
666                 in_chain->table,
667                 in_chain->to_chain,
668                 fst_proto,
669                 spadat->use_src_ip,
670                 nat_port,
671                 exp_ts,
672                 in_chain->target
673             );
674
675             res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0);
676
677             if (opts->verbose)
678                 log_msg(LOG_INFO, "process_spa_request() CMD: '%s' (res: %d, err: %s)",
679                     cmd_buf, res, err_buf);
680
681             if(EXTCMD_IS_SUCCESS(res))
682             {
683                 log_msg(LOG_INFO, "Added Rule to %s for %s, %s expires at %u",
684                     in_chain->to_chain, spadat->use_src_ip,
685                     spadat->spa_message_remain, exp_ts
686                 );
687
688                 in_chain->active_rules++;
689
690                 /* Reset the next expected expire time for this chain if it
691                 * is warranted.
692                 */
693                 if(in_chain->next_expire < now || exp_ts < in_chain->next_expire)
694                     in_chain->next_expire = exp_ts;
695             }
696             else
697                 log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf);
698
699         }
700         else if(fwd_chain->to_chain != NULL && strlen(fwd_chain->to_chain))
701         {
702             /* Make our FORWARD and NAT rules, and make sure the
703              * required jump rule exists
704             */
705             if (jump_rule_exists(IPT_FORWARD_ACCESS) == 0)
706                 add_jump_rule(opts, IPT_FORWARD_ACCESS);
707
708             zero_cmd_buffers();
709
710             snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_ADD_FWD_RULE_ARGS,
711                 opts->fw_config->fw_command,
712                 fwd_chain->table,
713                 fwd_chain->to_chain,
714                 fst_proto,
715                 spadat->use_src_ip,
716                 nat_ip,
717                 nat_port,
718                 exp_ts,
719                 fwd_chain->target
720             );
721
722             res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0);
723
724             if (opts->verbose)
725                 log_msg(LOG_INFO, "process_spa_request() CMD: '%s' (res: %d, err: %s)",
726                     cmd_buf, res, err_buf);
727
728             if(EXTCMD_IS_SUCCESS(res))
729             {
730                 log_msg(LOG_INFO, "Added FORWARD Rule to %s for %s, %s expires at %u",
731                     fwd_chain->to_chain, spadat->use_src_ip,
732                     spadat->spa_message_remain, exp_ts
733                 );
734
735                 fwd_chain->active_rules++;
736
737                 /* Reset the next expected expire time for this chain if it
738                 * is warranted.
739                 */
740                 if(fwd_chain->next_expire < now || exp_ts < fwd_chain->next_expire)
741                     fwd_chain->next_expire = exp_ts;
742             }
743             else
744                 log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); 
745         }
746
747         if(dnat_chain->to_chain != NULL && strlen(dnat_chain->to_chain))
748         {
749
750             /* Make sure the required jump rule exists
751             */
752             if (jump_rule_exists(IPT_DNAT_ACCESS) == 0)
753                 add_jump_rule(opts, IPT_DNAT_ACCESS);
754
755             zero_cmd_buffers();
756
757             snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_ADD_DNAT_RULE_ARGS,
758                 opts->fw_config->fw_command,
759                 dnat_chain->table,
760                 dnat_chain->to_chain,
761                 fst_proto,
762                 spadat->use_src_ip,
763                 fst_port,
764                 exp_ts,
765                 dnat_chain->target,
766                 nat_ip,
767                 nat_port
768             );
769
770             res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0);
771
772             if (opts->verbose)
773                 log_msg(LOG_INFO, "process_spa_request() CMD: '%s' (res: %d, err: %s)",
774                     cmd_buf, res, err_buf);
775
776             if(EXTCMD_IS_SUCCESS(res))
777             {
778                 log_msg(LOG_INFO, "Added DNAT Rule to %s for %s, %s expires at %u",
779                     dnat_chain->to_chain, spadat->use_src_ip,
780                     spadat->spa_message_remain, exp_ts
781                 );
782
783                 dnat_chain->active_rules++;
784
785                 /* Reset the next expected expire time for this chain if it
786                 * is warranted.
787                 */
788                 if(dnat_chain->next_expire < now || exp_ts < dnat_chain->next_expire)
789                     dnat_chain->next_expire = exp_ts;
790             }
791             else
792                 log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); 
793         }
794
795         /* If SNAT (or MASQUERADE) is wanted, then we add those rules here as well.
796         */
797         if(strncasecmp(opts->config[CONF_ENABLE_IPT_SNAT], "Y", 1) == 0)
798         {
799             zero_cmd_buffers();
800
801             /* Setup some parameter depending on whether we are using SNAT
802              * or MASQUERADE.
803             */
804             if(strncasecmp(opts->config[CONF_SNAT_TRANSLATE_IP], "__CHANGEME__", 10)!=0)
805             {
806                 /* Using static SNAT */
807                 snat_chain = &(opts->fw_config->chain[IPT_SNAT_ACCESS]);
808                 snprintf(snat_target, SNAT_TARGET_BUFSIZE-1,
809                     "--to-source %s:%i", opts->config[CONF_SNAT_TRANSLATE_IP],
810                     fst_port);
811             }
812             else
813             {
814                 /* Using MASQUERADE */
815                 snat_chain = &(opts->fw_config->chain[IPT_MASQUERADE_ACCESS]);
816                 snprintf(snat_target, SNAT_TARGET_BUFSIZE-1,
817                     "--to-ports %i", fst_port);
818             }
819
820             snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_ADD_SNAT_RULE_ARGS,
821                 opts->fw_config->fw_command,
822                 snat_chain->table,
823                 snat_chain->to_chain,
824                 fst_proto,
825                 nat_ip,
826                 nat_port,
827                 exp_ts,
828                 snat_chain->target,
829                 snat_target
830             );
831
832             res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0);
833
834             if (opts->verbose)
835                 log_msg(LOG_INFO, "process_spa_request() CMD: '%s' (res: %d, err: %s)",
836                     cmd_buf, res, err_buf);
837
838             if(EXTCMD_IS_SUCCESS(res))
839             {
840                 log_msg(LOG_INFO, "Added Source NAT Rule to %s for %s, %s expires at %u",
841                     snat_chain->to_chain, spadat->use_src_ip,
842                     spadat->spa_message_remain, exp_ts
843                 );
844
845                 snat_chain->active_rules++;
846
847                 /* Reset the next expected expire time for this chain if it
848                 * is warranted.
849                 */
850             if(snat_chain->next_expire < now || exp_ts < snat_chain->next_expire)
851                     snat_chain->next_expire = exp_ts;
852             }
853             else
854                 log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf);
855         }
856     }
857
858     /* Done with the port list for access rules.
859     */
860     free_acc_port_list(port_list);
861
862     return(res);
863 }
864
865 /* Iterate over the configure firewall access chains and purge expired
866  * firewall rules.
867 */
868 void
869 check_firewall_rules(const fko_srv_options_t *opts)
870 {
871     char             exp_str[12];
872     char             rule_num_str[6];
873     char            *ndx, *rn_start, *rn_end, *tmp_mark;
874
875     int             i, res, rn_offset;
876     time_t          now, rule_exp, min_exp = 0;
877
878     struct fw_chain *ch = opts->fw_config->chain;
879
880     time(&now);
881
882     /* Iterate over each chain and look for active rules to delete.
883     */
884     for(i = 0; i < NUM_FWKNOP_ACCESS_TYPES; i++)
885     {
886         /* If there are no active rules or we have not yet
887          * reached our expected next expire time, continue.
888         */
889         if(ch[i].active_rules == 0 || ch[i].next_expire > now)
890             continue;
891
892         zero_cmd_buffers();
893
894         rn_offset = 0;
895
896         /* There should be a rule to delete.  Get the current list of
897          * rules for this chain and delete the ones that are expired.
898         */
899         snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_LIST_RULES_ARGS,
900             opts->fw_config->fw_command,
901             ch[i].table,
902             ch[i].to_chain
903         );
904
905         res = run_extcmd(cmd_buf, cmd_out, STANDARD_CMD_OUT_BUFSIZE, 0);
906
907         if (opts->verbose)
908             log_msg(LOG_INFO, "check_firewall_rules() CMD: '%s' (res: %d, err: %s)",
909                 cmd_buf, res, err_buf);
910
911         if(!EXTCMD_IS_SUCCESS(res))
912         {
913             log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, cmd_out);
914             continue;
915         }
916
917         if(opts->verbose > 1)
918             log_msg(LOG_INFO, "RES=%i, CMD_BUF: %s\nRULES LIST: %s", res, cmd_buf, cmd_out);
919
920         ndx = strstr(cmd_out, EXPIRE_COMMENT_PREFIX);
921         if(ndx == NULL)
922         {
923             /* we did not find an expected rule.
924             */
925             log_msg(LOG_ERR,
926                 "Did not find expire comment in rules list %i.\n", i);
927
928             if (ch[i].active_rules > 0)
929                 ch[i].active_rules--;
930
931             continue;
932         }
933
934         /* walk the list and process rules as needed.
935         */
936         while (ndx != NULL) {
937             /* Jump forward and extract the timestamp
938             */
939             ndx += strlen(EXPIRE_COMMENT_PREFIX);
940
941             /* remember this spot for when we look for the next
942              * rule.
943             */
944             tmp_mark = ndx;
945
946             strlcpy(exp_str, ndx, 11);
947             rule_exp = (time_t)atoll(exp_str);
948
949             if(rule_exp <= now)
950             {
951                 /* Backtrack and get the rule number and delete it.
952                 */
953                 rn_start = ndx;
954                 while(--rn_start > cmd_out)
955                 {
956                     if(*rn_start == '\n')
957                         break;
958                 }
959
960                 if(*rn_start != '\n')
961                 {
962                     /* This should not happen. But if it does, complain,
963                      * decrement the active rule value, and go on.
964                     */
965                     log_msg(LOG_ERR,
966                         "Rule parse error while finding rule line start in chain %i", i);
967
968                     if (ch[i].active_rules > 0)
969                         ch[i].active_rules--;
970
971                     break;
972                 }
973                 rn_start++;
974
975                 rn_end = strchr(rn_start, ' ');
976                 if(rn_end == NULL)
977                 {
978                     /* This should not happen. But if it does, complain,
979                      * decrement the active rule value, and go on.
980                     */
981                     log_msg(LOG_ERR,
982                         "Rule parse error while finding rule number in chain %i", i);
983
984                     if (ch[i].active_rules > 0)
985                         ch[i].active_rules--;
986
987                     break;
988                 }
989
990                 strlcpy(rule_num_str, rn_start, (rn_end - rn_start)+1);
991
992                 zero_cmd_buffers();
993
994                 snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_DEL_RULE_ARGS,
995                     opts->fw_config->fw_command,
996                     ch[i].table,
997                     ch[i].to_chain,
998                     atoi(rule_num_str) - rn_offset
999                 );
1000
1001
1002                 res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0);
1003
1004                 if (opts->verbose)
1005                     log_msg(LOG_INFO, "check_firewall_rules() CMD: '%s' (res: %d, err: %s)",
1006                         cmd_buf, res, err_buf);
1007
1008                 if(EXTCMD_IS_SUCCESS(res))
1009                 {
1010                     log_msg(LOG_INFO, "Removed rule %s from %s with expire time of %u.",
1011                         rule_num_str, ch[i].to_chain, rule_exp
1012                     );
1013
1014                     rn_offset++;
1015
1016                     if (ch[i].active_rules > 0)
1017                         ch[i].active_rules--;
1018                 }
1019                 else
1020                     log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); 
1021
1022             }
1023             else
1024             {
1025                 /* Track the minimum future rule expire time.
1026                 */
1027                 if(rule_exp > now)
1028                     min_exp = (min_exp < rule_exp) ? min_exp : rule_exp;
1029             }
1030
1031             /* Push our tracking index forward beyond (just processed) _exp_
1032              * string so we can continue to the next rule in the list.
1033             */
1034             ndx = strstr(tmp_mark, EXPIRE_COMMENT_PREFIX);
1035         }
1036
1037         /* Set the next pending expire time accordingly. 0 if there are no
1038          * more rules, or whatever the next expected (min_exp) time will be.
1039         */
1040         if(ch[i].active_rules < 1)
1041             ch[i].next_expire = 0;
1042         else if(min_exp)
1043             ch[i].next_expire = min_exp;
1044     }
1045 }
1046
1047 #endif /* FIREWALL_IPTABLES */
1048
1049 /***EOF***/