root/trunk/wifidog/src/fw_iptables.c @ 467

Revision 467, 15.9 KB (checked in by minaguib, 8 years ago)

* New safe.c with safe_malloc, safe_strdup, safe_asprintf and safe_vasprintf with propper logging and exit when error. Replaced all instances of original with safe versions in all files
* Fix memory leak in iptables_fw_counters_update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/********************************************************************\
2 * This program is free software; you can redistribute it and/or    *
3 * modify it under the terms of the GNU General Public License as   *
4 * published by the Free Software Foundation; either version 2 of   *
5 * the License, or (at your option) any later version.              *
6 *                                                                  *
7 * This program is distributed in the hope that it will be useful,  *
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
10 * GNU General Public License for more details.                     *
11 *                                                                  *
12 * You should have received a copy of the GNU General Public License*
13 * along with this program; if not, contact:                        *
14 *                                                                  *
15 * Free Software Foundation           Voice:  +1-617-542-5942       *
16 * 59 Temple Place - Suite 330        Fax:    +1-617-542-2652       *
17 * Boston, MA  02111-1307,  USA       gnu@gnu.org                   *
18 *                                                                  *
19 \********************************************************************/
20
21/* $Header$ */
22/** @internal
23  @file fw_iptables.c
24  @brief Firewall iptables functions
25  @author Copyright (C) 2004 Philippe April <papril777@yahoo.com>
26 */
27
28#define _GNU_SOURCE
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <stdarg.h>
33#include <syslog.h>
34#include <errno.h>
35#include <string.h>
36#include <pthread.h>
37#include <sys/socket.h>
38#include <netinet/in.h>
39#include <arpa/inet.h>
40
41#include "common.h"
42
43#include "safe.h"
44#include "conf.h"
45#include "fw_iptables.h"
46#include "firewall.h"
47#include "debug.h"
48#include "util.h"
49#include "client_list.h"
50
51static int iptables_do_command(char *format, ...);
52static char *iptables_compile(char *, t_firewall_rule *);
53static void iptables_load_ruleset(char *, char *);
54
55extern pthread_mutex_t  client_list_mutex;
56extern pthread_mutex_t  config_mutex;
57
58/**
59Used to supress the error output of the firewall during destruction */ 
60static int fw_quiet = 0;
61
62/** @internal */
63static int
64iptables_do_command(char *format, ...)
65{
66    va_list vlist;
67    char *fmt_cmd,
68        *cmd;
69    int rc;
70
71    va_start(vlist, format);
72    safe_vasprintf(&fmt_cmd, format, vlist);
73         va_end(vlist);
74
75    safe_asprintf(&cmd, "iptables %s", fmt_cmd);
76
77    free(fmt_cmd);
78
79    debug(LOG_DEBUG, "Executing command: %s", cmd);
80       
81    rc = execute(cmd, fw_quiet);
82
83    free(cmd);
84
85    return rc;
86}
87
88/**
89 * @internal
90 * Compiles a struct definition of a firewall rule into a valid iptables
91 * command.
92 * @arg chain Chain that the command will be (-A)ppended to.
93 * @arg rule Definition of a rule into a struct, from conf.c.
94 */
95static char *
96iptables_compile(char *chain, t_firewall_rule *rule)
97{
98    char        command[MAX_BUF],
99                *mode;
100   
101    memset(command, 0, MAX_BUF);
102   
103    if (rule->block_allow == 1) {
104        mode = safe_strdup("ACCEPT");
105    } else {
106        mode = safe_strdup("REJECT");
107    }
108   
109    snprintf(command, sizeof(command),  "-t filter -A %s ", chain);
110    if (rule->mask != NULL) {
111        snprintf((command + strlen(command)), (sizeof(command) - 
112                strlen(command)), "-d %s ", rule->mask);
113    }
114    if (rule->protocol != NULL) {
115        snprintf((command + strlen(command)), (sizeof(command) -
116                strlen(command)), "-p %s ", rule->protocol);
117    }
118    if (rule->port != NULL) {
119        snprintf((command + strlen(command)), (sizeof(command) -
120                strlen(command)), "--dport %s ", rule->port);
121    }
122    snprintf((command + strlen(command)), (sizeof(command) - 
123            strlen(command)), "-j %s", mode);
124   
125    free(mode);
126
127    /* XXX The buffer command, an automatic variable, will get cleaned
128     * off of the stack when we return, so we strdup() it. */
129    return(safe_strdup(command));
130}
131
132/**
133 * @internal
134 * Load all the rules in a rule set.
135 * @arg ruleset Name of the ruleset
136 * @arg chain IPTables chain the rules go into
137 */
138static void
139iptables_load_ruleset(char *ruleset, char *chain)
140{
141        t_firewall_rule         *rules;
142        char                    *cmd;
143
144        debug(LOG_DEBUG, "Load ruleset %s into chain %s", ruleset, chain);
145       
146        for (rules = get_ruleset(ruleset); rules != NULL; rules = rules->next) {
147                cmd = iptables_compile(chain, rules);
148                debug(LOG_DEBUG, "Loading rule \"%s\" into %s", cmd, chain);
149                iptables_do_command(cmd);
150                free(cmd);
151        }
152
153        debug(LOG_DEBUG, "Ruleset %s loaded into %s", ruleset, chain);
154}
155
156void
157iptables_fw_clear_authservers(void)
158{
159    iptables_do_command("-t filter -F " TABLE_WIFIDOG_AUTHSERVERS);
160}
161
162void
163iptables_fw_set_authservers(void)
164{
165    s_config *config;
166    t_auth_serv *auth_server;
167   
168    config = config_get_config();
169   
170    LOCK_CONFIG();
171   
172    for (auth_server = config->auth_servers; auth_server != NULL; auth_server = auth_server->next) {
173            if (auth_server->last_ip == NULL || strcmp(auth_server->last_ip, "0.0.0.0") == 0) {
174                iptables_do_command("-t filter -A " TABLE_WIFIDOG_AUTHSERVERS " -d %s -j ACCEPT", auth_server->authserv_hostname);
175            } else {
176                iptables_do_command("-t filter -A " TABLE_WIFIDOG_AUTHSERVERS " -d %s -j ACCEPT", auth_server->last_ip);
177            }
178    }
179
180    UNLOCK_CONFIG();
181}
182
183/** Initialize the firewall rules
184 */
185int
186iptables_fw_init(void)
187{
188    s_config *config;
189         char * gw_interface = NULL;
190         int gw_port = 0;
191   
192    fw_quiet = 0;
193
194    config = config_get_config();
195         LOCK_CONFIG();
196         gw_interface = safe_strdup(config->gw_interface);
197         gw_port = config->gw_port;
198         UNLOCK_CONFIG();
199   
200         /*
201          *
202          * Everything in the MANGLE table
203          *
204          */
205
206                        /* Create new chains */
207                        iptables_do_command("-t mangle -N " TABLE_WIFIDOG_OUTGOING);
208                        iptables_do_command("-t mangle -N " TABLE_WIFIDOG_INCOMING);
209
210                        /* Assign links and rules to these new chains */
211                        iptables_do_command("-t mangle -I PREROUTING 1 -i %s -j " TABLE_WIFIDOG_OUTGOING, gw_interface);
212                        iptables_do_command("-t mangle -I POSTROUTING 1 -o %s -j " TABLE_WIFIDOG_INCOMING, gw_interface);
213
214
215         /*
216          *
217          * Everything in the NAT table
218          *
219          */
220
221                        /* Create new chains */
222                        iptables_do_command("-t nat -N " TABLE_WIFIDOG_WIFI_TO_INTERNET);
223                        iptables_do_command("-t nat -N " TABLE_WIFIDOG_UNKNOWN);
224
225                        /* Assign links and rules to these new chains */
226                        iptables_do_command("-t nat -I PREROUTING 1 -i %s -j " TABLE_WIFIDOG_WIFI_TO_INTERNET, gw_interface);
227                        iptables_do_command("-t nat -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -m mark --mark 0x%u -j RETURN", FW_MARK_KNOWN);
228                        iptables_do_command("-t nat -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -m mark --mark 0x%u -j RETURN", FW_MARK_PROBATION);
229                        iptables_do_command("-t nat -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -j " TABLE_WIFIDOG_UNKNOWN);
230
231                        iptables_do_command("-t nat -A " TABLE_WIFIDOG_UNKNOWN " -p tcp --dport 80 -j REDIRECT --to-ports %d", gw_port);
232
233
234         /*
235          *
236          * Everything in the FILTER table
237          *
238          */
239
240                        /* Create new chains */
241                        iptables_do_command("-t filter -N " TABLE_WIFIDOG_WIFI_TO_INTERNET);
242                        iptables_do_command("-t filter -N " TABLE_WIFIDOG_AUTHSERVERS);
243                        iptables_do_command("-t filter -N " TABLE_WIFIDOG_LOCKED);
244                        iptables_do_command("-t filter -N " TABLE_WIFIDOG_GLOBAL);
245                        iptables_do_command("-t filter -N " TABLE_WIFIDOG_VALIDATE);
246                        iptables_do_command("-t filter -N " TABLE_WIFIDOG_KNOWN);
247                        iptables_do_command("-t filter -N " TABLE_WIFIDOG_UNKNOWN);
248
249                        /* Assign links and rules to these new chains */
250                        iptables_do_command("-t filter -I FORWARD 1 -i %s -j " TABLE_WIFIDOG_WIFI_TO_INTERNET, gw_interface);
251                        iptables_do_command("-t filter -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -j " TABLE_WIFIDOG_AUTHSERVERS);
252                        iptables_fw_set_authservers();
253
254                        iptables_do_command("-t filter -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -m mark --mark 0x%u -j " TABLE_WIFIDOG_LOCKED, FW_MARK_LOCKED);
255                        iptables_load_ruleset("locked-users", TABLE_WIFIDOG_LOCKED);
256
257                        iptables_do_command("-t filter -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -j " TABLE_WIFIDOG_GLOBAL);
258                        iptables_load_ruleset("global", TABLE_WIFIDOG_GLOBAL);
259
260                        iptables_do_command("-t filter -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -m mark --mark 0x%u -j " TABLE_WIFIDOG_VALIDATE, FW_MARK_PROBATION);
261                        iptables_load_ruleset("validating-users", TABLE_WIFIDOG_VALIDATE);
262
263                        iptables_do_command("-t filter -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -m mark --mark 0x%u -j " TABLE_WIFIDOG_KNOWN, FW_MARK_KNOWN);
264                        iptables_load_ruleset("known-users", TABLE_WIFIDOG_KNOWN);
265   
266                        iptables_do_command("-t filter -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -j " TABLE_WIFIDOG_UNKNOWN);
267                        iptables_load_ruleset("unknown-users", TABLE_WIFIDOG_UNKNOWN);
268                        iptables_do_command("-t filter -A " TABLE_WIFIDOG_UNKNOWN " -j REJECT --reject-with icmp-port-unreachable");
269
270        free(gw_interface);
271
272    return 1;
273}
274
275/** Remove the firewall rules
276 * This is used when we do a clean shutdown of WiFiDog and when it starts to make
277 * sure there are no rules left over
278 */
279int
280iptables_fw_destroy(void)
281{
282    fw_quiet = 1;
283
284         /*
285          *
286          * Everything in the MANGLE table
287          *
288          */
289         iptables_fw_destroy_mention("mangle", "PREROUTING", TABLE_WIFIDOG_OUTGOING);
290         iptables_fw_destroy_mention("mangle", "POSTROUTING", TABLE_WIFIDOG_INCOMING);
291    iptables_do_command("-t mangle -F " TABLE_WIFIDOG_OUTGOING);
292    iptables_do_command("-t mangle -F " TABLE_WIFIDOG_INCOMING);
293    iptables_do_command("-t mangle -X " TABLE_WIFIDOG_OUTGOING);
294    iptables_do_command("-t mangle -X " TABLE_WIFIDOG_INCOMING);
295
296         /*
297          *
298          * Everything in the NAT table
299          *
300          */
301         iptables_fw_destroy_mention("nat", "PREROUTING", TABLE_WIFIDOG_WIFI_TO_INTERNET);
302    iptables_do_command("-t nat -F " TABLE_WIFIDOG_WIFI_TO_INTERNET);
303    iptables_do_command("-t nat -F " TABLE_WIFIDOG_UNKNOWN);
304    iptables_do_command("-t nat -X " TABLE_WIFIDOG_WIFI_TO_INTERNET);
305    iptables_do_command("-t nat -X " TABLE_WIFIDOG_UNKNOWN);
306
307         /*
308          *
309          * Everything in the FILTER table
310          *
311          */
312         iptables_fw_destroy_mention("filter", "FORWARD", TABLE_WIFIDOG_WIFI_TO_INTERNET);
313         iptables_do_command("-t filter -F " TABLE_WIFIDOG_WIFI_TO_INTERNET);
314         iptables_do_command("-t filter -F " TABLE_WIFIDOG_AUTHSERVERS);
315         iptables_do_command("-t filter -F " TABLE_WIFIDOG_LOCKED);
316         iptables_do_command("-t filter -F " TABLE_WIFIDOG_GLOBAL);
317         iptables_do_command("-t filter -F " TABLE_WIFIDOG_VALIDATE);
318         iptables_do_command("-t filter -F " TABLE_WIFIDOG_KNOWN);
319         iptables_do_command("-t filter -F " TABLE_WIFIDOG_UNKNOWN);
320         iptables_do_command("-t filter -X " TABLE_WIFIDOG_WIFI_TO_INTERNET);
321         iptables_do_command("-t filter -X " TABLE_WIFIDOG_AUTHSERVERS);
322         iptables_do_command("-t filter -X " TABLE_WIFIDOG_LOCKED);
323         iptables_do_command("-t filter -X " TABLE_WIFIDOG_GLOBAL);
324         iptables_do_command("-t filter -X " TABLE_WIFIDOG_VALIDATE);
325         iptables_do_command("-t filter -X " TABLE_WIFIDOG_KNOWN);
326         iptables_do_command("-t filter -X " TABLE_WIFIDOG_UNKNOWN);
327
328    return 1;
329}
330
331/*
332 * Helper for iptables_fw_destroy
333 * @param table The table to search
334 * @param chain The chain in that table to search
335 * @param mention A word to find and delete in rules in the given table+chain
336 */
337int
338iptables_fw_destroy_mention(
339                char * table,
340                char * chain,
341                char * mention
342) {
343        FILE *p = NULL;
344        char *command = NULL;
345        char *command2 = NULL;
346        char line[MAX_BUF];
347        char rulenum[10];
348        int deleted = 0;
349
350        safe_asprintf(&command, "iptables -t %s -L %s -n --line-numbers -v", table, chain);
351
352        if ((p = popen(command, "r"))) {
353                /* Skip first 2 lines */
354                while (!feof(p) && fgetc(p) != '\n');
355                while (!feof(p) && fgetc(p) != '\n');
356                /* Loop over entries */
357                while (fgets(line, sizeof(line), p)) {
358                        /* Look for mention */
359                        if (strstr(line, mention)) {
360                                /* Found mention - Get the rule number into rulenum*/
361                                if (sscanf(line, "%9[0-9]", rulenum) == 1) {
362                                        /* Delete the rule: */
363                                        safe_asprintf(&command2, "-t %s -D %s %s", table, chain, rulenum);
364                                        iptables_do_command(command2);
365                                        free(command2);
366                                        deleted = 1;
367                                        /* Do not keep looping - the captures rulenums will no longer be accurate */
368                                        break;
369                                }
370                        }
371                }
372                pclose(p);
373        }
374
375        free(command);
376
377        if (deleted) {
378                /* Recurse just in case there are more in the same table+chain */
379                iptables_fw_destroy_mention(table, chain, mention);
380        }
381
382        return (deleted);
383}
384
385/** Set the firewall access for a specific client */
386int
387iptables_fw_access(fw_access_t type, char *ip, char *mac, int tag)
388{
389    int rc;
390
391    fw_quiet = 0;
392
393    switch(type) {
394        case FW_ACCESS_ALLOW:
395            iptables_do_command("-t mangle -A " TABLE_WIFIDOG_OUTGOING " -s %s -m mac --mac-source %s -j MARK --set-mark %d", ip, mac, tag);
396            rc = iptables_do_command("-t mangle -A " TABLE_WIFIDOG_INCOMING " -d %s -j ACCEPT", ip);
397            break;
398        case FW_ACCESS_DENY:
399            iptables_do_command("-t mangle -D " TABLE_WIFIDOG_OUTGOING " -s %s -m mac --mac-source %s -j MARK --set-mark %d", ip, mac, tag);
400            rc = iptables_do_command("-t mangle -D " TABLE_WIFIDOG_INCOMING " -d %s -j ACCEPT", ip);
401            break;
402        default:
403            rc = -1;
404            break;
405    }
406
407    return rc;
408}
409
410/** Update the counters of all the clients in the client list */
411int
412iptables_fw_counters_update(void)
413{
414    FILE *output;
415    char *script,
416        ip[16],
417        rc;
418    unsigned long int counter;
419    t_client *p1;
420         struct in_addr tempaddr;
421
422    /* Look for outgoing traffic */
423    safe_asprintf(&script, "%s %s", "iptables", "-v -n -x -t mangle -L " TABLE_WIFIDOG_OUTGOING);
424    output = popen(script, "r");
425    free(script);
426    if (!output) {
427        debug(LOG_ERR, "popen(): %s", strerror(errno));
428        return -1;
429    }
430
431    /* skip the first two lines */
432    while (('\n' != fgetc(output)) && !feof(output))
433        ;
434    while (('\n' != fgetc(output)) && !feof(output))
435        ;
436    while (output && !(feof(output))) {
437        rc = fscanf(output, "%*s %lu %*s %*s %*s %*s %*s %15[0-9.] %*s %*s %*s %*s %*s 0x%*u", &counter, ip);
438        if (2 == rc && EOF != rc) {
439                          /* Sanity*/
440                          if (!inet_aton(ip, &tempaddr)) {
441                                  debug(LOG_WARNING, "I was supposed to read an IP address but instead got [%s] - ignoring it", ip);
442                                  continue;
443                          }
444            debug(LOG_DEBUG, "Outgoing %s Bytes=%ld", ip, counter);
445            LOCK_CLIENT_LIST();
446            if ((p1 = client_list_find_by_ip(ip))) {
447                if (p1->counters.outgoing < counter) {
448                    p1->counters.outgoing = counter;
449                    p1->counters.last_updated = time(NULL);
450                    debug(LOG_DEBUG, "%s - Updated outgoing counter to %ld bytes from outgoing chain", ip, counter);
451                }
452            } else {
453                debug(LOG_ERR, "Could not find %s in client list", ip);
454            }
455            UNLOCK_CLIENT_LIST();
456        }
457    }
458    pclose(output);
459
460    /* Look for incoming traffic */
461    safe_asprintf(&script, "%s %s", "iptables", "-v -n -x -t mangle -L " TABLE_WIFIDOG_INCOMING);
462    output = popen(script, "r");
463    free(script);
464    if (!output) {
465        debug(LOG_ERR, "popen(): %s", strerror(errno));
466        return -1;
467    }
468
469    /* skip the first two lines */
470    while (('\n' != fgetc(output)) && !feof(output))
471        ;
472    while (('\n' != fgetc(output)) && !feof(output))
473        ;
474    while (output && !(feof(output))) {
475        rc = fscanf(output, "%*s %lu %*s %*s %*s %*s %*s %*s %15[0-9.]", &counter, ip);
476        if (2 == rc && EOF != rc) {
477                          /* Sanity*/
478                          if (!inet_aton(ip, &tempaddr)) {
479                                  debug(LOG_WARNING, "I was supposed to read an IP address but instead got [%s] - ignoring it", ip);
480                                  continue;
481                          }
482            debug(LOG_DEBUG, "Incoming %s Bytes=%ld", ip, counter);
483            LOCK_CLIENT_LIST();
484            if ((p1 = client_list_find_by_ip(ip))) {
485                if (p1->counters.incoming < counter) {
486                    p1->counters.incoming = counter;
487                    p1->counters.last_updated = time(NULL);
488                    debug(LOG_DEBUG, "%s - Updated counter to %ld bytes", ip, counter);
489                }
490            } else {
491                debug(LOG_ERR, "Could not find %s in client list", ip);
492            }
493            UNLOCK_CLIENT_LIST();
494        }
495    }
496    pclose(output);
497
498    return 1;
499}
Note: See TracBrowser for help on using the browser.