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

Revision 531, 17.3 KB (checked in by aprilp, 8 years ago)

* Duplicated auth server list in NAT table to fix the issue

of using an auth server on port 80, since port 80 was being systematically
redirected to 2060 otherwise.

* Released 1.1.1

  • 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    iptables_do_command("-t nat -F " TABLE_WIFIDOG_AUTHSERVERS);
161}
162
163void
164iptables_fw_set_authservers(void)
165{
166    s_config *config;
167    t_auth_serv *auth_server;
168   
169    config = config_get_config();
170   
171    for (auth_server = config->auth_servers; auth_server != NULL; auth_server = auth_server->next) {
172            if (auth_server->last_ip && strcmp(auth_server->last_ip, "0.0.0.0") != 0) {
173                iptables_do_command("-t filter -A " TABLE_WIFIDOG_AUTHSERVERS " -d %s -j ACCEPT", auth_server->last_ip);
174                iptables_do_command("-t nat -A " TABLE_WIFIDOG_AUTHSERVERS " -d %s -j ACCEPT", auth_server->last_ip);
175            }
176    }
177
178}
179
180/** Initialize the firewall rules
181 */
182int
183iptables_fw_init(void)
184{
185    s_config *config;
186         char * gw_interface = NULL;
187         char * gw_address = NULL;
188         int gw_port = 0;
189   
190    fw_quiet = 0;
191
192         LOCK_CONFIG();
193    config = config_get_config();
194         gw_interface = safe_strdup(config->gw_interface);
195         gw_address = safe_strdup(config->gw_address);
196         gw_port = config->gw_port;
197         UNLOCK_CONFIG();
198   
199         /*
200          *
201          * Everything in the MANGLE table
202          *
203          */
204
205                        /* Create new chains */
206                        iptables_do_command("-t mangle -N " TABLE_WIFIDOG_OUTGOING);
207                        iptables_do_command("-t mangle -N " TABLE_WIFIDOG_INCOMING);
208
209                        /* Assign links and rules to these new chains */
210                        iptables_do_command("-t mangle -I PREROUTING 1 -i %s -j " TABLE_WIFIDOG_OUTGOING, gw_interface);
211                        iptables_do_command("-t mangle -I POSTROUTING 1 -o %s -j " TABLE_WIFIDOG_INCOMING, gw_interface);
212
213
214         /*
215          *
216          * Everything in the NAT table
217          *
218          */
219
220                        /* Create new chains */
221                        iptables_do_command("-t nat -N " TABLE_WIFIDOG_OUTGOING);
222                        iptables_do_command("-t nat -N " TABLE_WIFIDOG_WIFI_TO_ROUTER);
223                        iptables_do_command("-t nat -N " TABLE_WIFIDOG_WIFI_TO_INTERNET);
224                        iptables_do_command("-t nat -N " TABLE_WIFIDOG_UNKNOWN);
225                        iptables_do_command("-t nat -N " TABLE_WIFIDOG_AUTHSERVERS);
226
227                        /* Assign links and rules to these new chains */
228                        iptables_do_command("-t nat -I PREROUTING 1 -i %s -j " TABLE_WIFIDOG_OUTGOING, gw_interface);
229
230                        iptables_do_command("-t nat -A " TABLE_WIFIDOG_OUTGOING " -d %s -j " TABLE_WIFIDOG_WIFI_TO_ROUTER, gw_address);
231                        iptables_do_command("-t nat -A " TABLE_WIFIDOG_WIFI_TO_ROUTER " -j ACCEPT");
232
233                        iptables_do_command("-t nat -A " TABLE_WIFIDOG_OUTGOING " -j " TABLE_WIFIDOG_WIFI_TO_INTERNET);
234                        iptables_do_command("-t nat -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -m mark --mark 0x%u -j ACCEPT", FW_MARK_KNOWN);
235                        iptables_do_command("-t nat -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -m mark --mark 0x%u -j ACCEPT", FW_MARK_PROBATION);
236                        iptables_do_command("-t nat -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -j " TABLE_WIFIDOG_UNKNOWN);
237
238                        iptables_do_command("-t nat -A " TABLE_WIFIDOG_UNKNOWN " -j " TABLE_WIFIDOG_AUTHSERVERS);
239                        iptables_do_command("-t nat -A " TABLE_WIFIDOG_UNKNOWN " -p tcp --dport 80 -j REDIRECT --to-ports %d", gw_port);
240
241
242         /*
243          *
244          * Everything in the FILTER table
245          *
246          */
247
248                        /* Create new chains */
249                        iptables_do_command("-t filter -N " TABLE_WIFIDOG_WIFI_TO_INTERNET);
250                        iptables_do_command("-t filter -N " TABLE_WIFIDOG_AUTHSERVERS);
251                        iptables_do_command("-t filter -N " TABLE_WIFIDOG_LOCKED);
252                        iptables_do_command("-t filter -N " TABLE_WIFIDOG_GLOBAL);
253                        iptables_do_command("-t filter -N " TABLE_WIFIDOG_VALIDATE);
254                        iptables_do_command("-t filter -N " TABLE_WIFIDOG_KNOWN);
255                        iptables_do_command("-t filter -N " TABLE_WIFIDOG_UNKNOWN);
256
257                        /* Assign links and rules to these new chains */
258                        iptables_do_command("-t filter -I FORWARD 1 -i %s -j " TABLE_WIFIDOG_WIFI_TO_INTERNET, gw_interface);
259                        iptables_do_command("-t filter -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -j " TABLE_WIFIDOG_AUTHSERVERS);
260                        iptables_fw_set_authservers();
261
262                        iptables_do_command("-t filter -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -m mark --mark 0x%u -j " TABLE_WIFIDOG_LOCKED, FW_MARK_LOCKED);
263                        iptables_load_ruleset("locked-users", TABLE_WIFIDOG_LOCKED);
264
265                        iptables_do_command("-t filter -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -j " TABLE_WIFIDOG_GLOBAL);
266                        iptables_load_ruleset("global", TABLE_WIFIDOG_GLOBAL);
267
268                        iptables_do_command("-t filter -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -m mark --mark 0x%u -j " TABLE_WIFIDOG_VALIDATE, FW_MARK_PROBATION);
269                        iptables_load_ruleset("validating-users", TABLE_WIFIDOG_VALIDATE);
270
271                        iptables_do_command("-t filter -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -m mark --mark 0x%u -j " TABLE_WIFIDOG_KNOWN, FW_MARK_KNOWN);
272                        iptables_load_ruleset("known-users", TABLE_WIFIDOG_KNOWN);
273   
274                        iptables_do_command("-t filter -A " TABLE_WIFIDOG_WIFI_TO_INTERNET " -j " TABLE_WIFIDOG_UNKNOWN);
275                        iptables_load_ruleset("unknown-users", TABLE_WIFIDOG_UNKNOWN);
276                        iptables_do_command("-t filter -A " TABLE_WIFIDOG_UNKNOWN " -j REJECT --reject-with icmp-port-unreachable");
277
278        free(gw_interface);
279        free(gw_address);
280
281    return 1;
282}
283
284/** Remove the firewall rules
285 * This is used when we do a clean shutdown of WiFiDog and when it starts to make
286 * sure there are no rules left over
287 */
288int
289iptables_fw_destroy(void)
290{
291    fw_quiet = 1;
292
293         debug(LOG_DEBUG, "Destroying our iptables entries");
294
295         /*
296          *
297          * Everything in the MANGLE table
298          *
299          */
300         debug(LOG_DEBUG, "Destroying chains in the MANGLE table");
301         iptables_fw_destroy_mention("mangle", "PREROUTING", TABLE_WIFIDOG_OUTGOING);
302         iptables_fw_destroy_mention("mangle", "POSTROUTING", TABLE_WIFIDOG_INCOMING);
303    iptables_do_command("-t mangle -F " TABLE_WIFIDOG_OUTGOING);
304    iptables_do_command("-t mangle -F " TABLE_WIFIDOG_INCOMING);
305    iptables_do_command("-t mangle -X " TABLE_WIFIDOG_OUTGOING);
306    iptables_do_command("-t mangle -X " TABLE_WIFIDOG_INCOMING);
307
308        /*
309         *
310         * Everything in the NAT table
311         *
312         */
313        debug(LOG_DEBUG, "Destroying chains in the NAT table");
314        iptables_fw_destroy_mention("nat", "PREROUTING", TABLE_WIFIDOG_OUTGOING);
315        iptables_do_command("-t nat -F " TABLE_WIFIDOG_AUTHSERVERS);
316    iptables_do_command("-t nat -F " TABLE_WIFIDOG_OUTGOING);
317    iptables_do_command("-t nat -F " TABLE_WIFIDOG_WIFI_TO_ROUTER);
318    iptables_do_command("-t nat -F " TABLE_WIFIDOG_WIFI_TO_INTERNET);
319    iptables_do_command("-t nat -F " TABLE_WIFIDOG_UNKNOWN);
320        iptables_do_command("-t nat -X " TABLE_WIFIDOG_AUTHSERVERS);
321    iptables_do_command("-t nat -X " TABLE_WIFIDOG_OUTGOING);
322    iptables_do_command("-t nat -X " TABLE_WIFIDOG_WIFI_TO_ROUTER);
323    iptables_do_command("-t nat -X " TABLE_WIFIDOG_WIFI_TO_INTERNET);
324    iptables_do_command("-t nat -X " TABLE_WIFIDOG_UNKNOWN);
325
326         /*
327          *
328          * Everything in the FILTER table
329          *
330          */
331         debug(LOG_DEBUG, "Destroying chains in the FILTER table");
332         iptables_fw_destroy_mention("filter", "FORWARD", TABLE_WIFIDOG_WIFI_TO_INTERNET);
333         iptables_do_command("-t filter -F " TABLE_WIFIDOG_WIFI_TO_INTERNET);
334         iptables_do_command("-t filter -F " TABLE_WIFIDOG_AUTHSERVERS);
335         iptables_do_command("-t filter -F " TABLE_WIFIDOG_LOCKED);
336         iptables_do_command("-t filter -F " TABLE_WIFIDOG_GLOBAL);
337         iptables_do_command("-t filter -F " TABLE_WIFIDOG_VALIDATE);
338         iptables_do_command("-t filter -F " TABLE_WIFIDOG_KNOWN);
339         iptables_do_command("-t filter -F " TABLE_WIFIDOG_UNKNOWN);
340         iptables_do_command("-t filter -X " TABLE_WIFIDOG_WIFI_TO_INTERNET);
341         iptables_do_command("-t filter -X " TABLE_WIFIDOG_AUTHSERVERS);
342         iptables_do_command("-t filter -X " TABLE_WIFIDOG_LOCKED);
343         iptables_do_command("-t filter -X " TABLE_WIFIDOG_GLOBAL);
344         iptables_do_command("-t filter -X " TABLE_WIFIDOG_VALIDATE);
345         iptables_do_command("-t filter -X " TABLE_WIFIDOG_KNOWN);
346         iptables_do_command("-t filter -X " TABLE_WIFIDOG_UNKNOWN);
347
348    return 1;
349}
350
351/*
352 * Helper for iptables_fw_destroy
353 * @param table The table to search
354 * @param chain The chain in that table to search
355 * @param mention A word to find and delete in rules in the given table+chain
356 */
357int
358iptables_fw_destroy_mention(
359                char * table,
360                char * chain,
361                char * mention
362) {
363        FILE *p = NULL;
364        char *command = NULL;
365        char *command2 = NULL;
366        char line[MAX_BUF];
367        char rulenum[10];
368        int deleted = 0;
369
370        debug(LOG_DEBUG, "Attempting to destroy all mention of %s from %s.%s", mention, table, chain);
371
372        safe_asprintf(&command, "iptables -t %s -L %s -n --line-numbers -v", table, chain);
373
374        if ((p = popen(command, "r"))) {
375                /* Skip first 2 lines */
376                while (!feof(p) && fgetc(p) != '\n');
377                while (!feof(p) && fgetc(p) != '\n');
378                /* Loop over entries */
379                while (fgets(line, sizeof(line), p)) {
380                        /* Look for mention */
381                        if (strstr(line, mention)) {
382                                /* Found mention - Get the rule number into rulenum*/
383                                if (sscanf(line, "%9[0-9]", rulenum) == 1) {
384                                        /* Delete the rule: */
385                                        debug(LOG_DEBUG, "Deleting rule %s from %s.%s because it mentions %s", rulenum, table, chain, mention);
386                                        safe_asprintf(&command2, "-t %s -D %s %s", table, chain, rulenum);
387                                        iptables_do_command(command2);
388                                        free(command2);
389                                        deleted = 1;
390                                        /* Do not keep looping - the captured rulenums will no longer be accurate */
391                                        break;
392                                }
393                        }
394                }
395                pclose(p);
396        }
397
398        free(command);
399
400        if (deleted) {
401                /* Recurse just in case there are more in the same table+chain */
402                iptables_fw_destroy_mention(table, chain, mention);
403        }
404
405        return (deleted);
406}
407
408/** Set the firewall access for a specific client */
409int
410iptables_fw_access(fw_access_t type, char *ip, char *mac, int tag)
411{
412    int rc;
413
414    fw_quiet = 0;
415
416    switch(type) {
417        case FW_ACCESS_ALLOW:
418            iptables_do_command("-t mangle -A " TABLE_WIFIDOG_OUTGOING " -s %s -m mac --mac-source %s -j MARK --set-mark %d", ip, mac, tag);
419            rc = iptables_do_command("-t mangle -A " TABLE_WIFIDOG_INCOMING " -d %s -j ACCEPT", ip);
420            break;
421        case FW_ACCESS_DENY:
422            iptables_do_command("-t mangle -D " TABLE_WIFIDOG_OUTGOING " -s %s -m mac --mac-source %s -j MARK --set-mark %d", ip, mac, tag);
423            rc = iptables_do_command("-t mangle -D " TABLE_WIFIDOG_INCOMING " -d %s -j ACCEPT", ip);
424            break;
425        default:
426            rc = -1;
427            break;
428    }
429
430    return rc;
431}
432
433/** Update the counters of all the clients in the client list */
434int
435iptables_fw_counters_update(void)
436{
437    FILE *output;
438    char *script,
439        ip[16],
440        rc;
441    unsigned long long int counter;
442    t_client *p1;
443         struct in_addr tempaddr;
444
445    /* Look for outgoing traffic */
446    safe_asprintf(&script, "%s %s", "iptables", "-v -n -x -t mangle -L " TABLE_WIFIDOG_OUTGOING);
447    output = popen(script, "r");
448    free(script);
449    if (!output) {
450        debug(LOG_ERR, "popen(): %s", strerror(errno));
451        return -1;
452    }
453
454    /* skip the first two lines */
455    while (('\n' != fgetc(output)) && !feof(output))
456        ;
457    while (('\n' != fgetc(output)) && !feof(output))
458        ;
459    while (output && !(feof(output))) {
460        rc = fscanf(output, "%*s %llu %*s %*s %*s %*s %*s %15[0-9.] %*s %*s %*s %*s %*s 0x%*u", &counter, ip);
461        if (2 == rc && EOF != rc) {
462                          /* Sanity*/
463                          if (!inet_aton(ip, &tempaddr)) {
464                                  debug(LOG_WARNING, "I was supposed to read an IP address but instead got [%s] - ignoring it", ip);
465                                  continue;
466                          }
467            debug(LOG_DEBUG, "Outgoing %s Bytes=%llu", ip, counter);
468            LOCK_CLIENT_LIST();
469            if ((p1 = client_list_find_by_ip(ip))) {
470                if (p1->counters.outgoing < counter) {
471                    p1->counters.outgoing = counter;
472                    p1->counters.last_updated = time(NULL);
473                    debug(LOG_DEBUG, "%s - Updated counter.outgoing to %llu bytes", ip, counter);
474                }
475            } else {
476                debug(LOG_ERR, "Could not find %s in client list", ip);
477            }
478            UNLOCK_CLIENT_LIST();
479        }
480    }
481    pclose(output);
482
483    /* Look for incoming traffic */
484    safe_asprintf(&script, "%s %s", "iptables", "-v -n -x -t mangle -L " TABLE_WIFIDOG_INCOMING);
485    output = popen(script, "r");
486    free(script);
487    if (!output) {
488        debug(LOG_ERR, "popen(): %s", strerror(errno));
489        return -1;
490    }
491
492    /* skip the first two lines */
493    while (('\n' != fgetc(output)) && !feof(output))
494        ;
495    while (('\n' != fgetc(output)) && !feof(output))
496        ;
497    while (output && !(feof(output))) {
498        rc = fscanf(output, "%*s %llu %*s %*s %*s %*s %*s %*s %15[0-9.]", &counter, ip);
499        if (2 == rc && EOF != rc) {
500                          /* Sanity*/
501                          if (!inet_aton(ip, &tempaddr)) {
502                                  debug(LOG_WARNING, "I was supposed to read an IP address but instead got [%s] - ignoring it", ip);
503                                  continue;
504                          }
505            debug(LOG_DEBUG, "Incoming %s Bytes=%llu", ip, counter);
506            LOCK_CLIENT_LIST();
507            if ((p1 = client_list_find_by_ip(ip))) {
508                if (p1->counters.incoming < counter) {
509                    p1->counters.incoming = counter;
510                    debug(LOG_DEBUG, "%s - Updated counter.incoming to %llu bytes", ip, counter);
511                }
512            } else {
513                debug(LOG_ERR, "Could not find %s in client list", ip);
514            }
515            UNLOCK_CLIENT_LIST();
516        }
517    }
518    pclose(output);
519
520    return 1;
521}
Note: See TracBrowser for help on using the browser.