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

Revision 449, 16.3 KB (checked in by minaguib, 8 years ago)

* Stricter format rules for all *scan* functions hunting for IPs and MAC addresses
* Fix memory leak in arp_get

  • 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 "conf.h"
44#include "fw_iptables.h"
45#include "firewall.h"
46#include "debug.h"
47#include "util.h"
48#include "client_list.h"
49
50static int iptables_do_command(char *format, ...);
51static char *iptables_compile(char *, t_firewall_rule *);
52static void iptables_load_ruleset(char *, char *);
53
54extern pthread_mutex_t  client_list_mutex;
55extern pthread_mutex_t  config_mutex;
56
57/**
58Used to supress the error output of the firewall during destruction */ 
59static int fw_quiet = 0;
60
61/** @internal */
62static int
63iptables_do_command(char *format, ...)
64{
65    va_list vlist;
66    char *fmt_cmd,
67        *cmd;
68    int rc;
69
70    va_start(vlist, format);
71    vasprintf(&fmt_cmd, format, vlist);
72    asprintf(&cmd, "iptables %s", fmt_cmd);
73    debug(LOG_DEBUG, "Executing command: %s", cmd);
74       
75    rc = execute(cmd, fw_quiet);
76
77    free(fmt_cmd);
78    free(cmd);
79
80    return rc;
81}
82
83/**
84 * @internal
85 * Compiles a struct definition of a firewall rule into a valid iptables
86 * command.
87 * @arg chain Chain that the command will be (-A)ppended to.
88 * @arg rule Definition of a rule into a struct, from conf.c.
89 */
90static char *
91iptables_compile(char *chain, t_firewall_rule *rule)
92{
93    char        command[MAX_BUF],
94                *mode;
95   
96    memset(command, 0, MAX_BUF);
97   
98    if (rule->block_allow == 1) {
99        mode = strdup("ACCEPT");
100    } else {
101        mode = strdup("DROP");
102    }
103   
104    snprintf(command, sizeof(command),  "-t nat -A %s ", chain);
105    if (rule->mask != NULL) {
106        snprintf((command + strlen(command)), (sizeof(command) - 
107                strlen(command)), "-d %s ", rule->mask);
108    }
109    if (rule->protocol != NULL) {
110        snprintf((command + strlen(command)), (sizeof(command) -
111                strlen(command)), "-p %s ", rule->protocol);
112    }
113    if (rule->port != NULL) {
114        snprintf((command + strlen(command)), (sizeof(command) -
115                strlen(command)), "--dport %s ", rule->port);
116    }
117    snprintf((command + strlen(command)), (sizeof(command) - 
118            strlen(command)), "-j %s", mode);
119   
120    free(mode);
121
122    /* XXX The buffer command, an automatic variable, will get cleaned
123     * off of the stack when we return, so we strdup() it. */
124    return(strdup(command));
125}
126
127/**
128 * @internal
129 * Load all the rules in a rule set.
130 * @arg ruleset Name of the ruleset
131 * @arg chain IPTables chain the rules go into
132 */
133static void
134iptables_load_ruleset(char *ruleset, char *chain)
135{
136        t_firewall_rule         *rules;
137        char                    *cmd;
138
139        debug(LOG_DEBUG, "Load ruleset %s into chain %s", ruleset, chain);
140       
141        for (rules = get_ruleset(ruleset); rules != NULL; rules = rules->next) {
142                cmd = iptables_compile(chain, rules);
143                debug(LOG_DEBUG, "Loading rule \"%s\" into %s", cmd, chain);
144                iptables_do_command(cmd);
145                free(cmd);
146        }
147
148        debug(LOG_DEBUG, "Ruleset %s loaded into %s", ruleset, chain);
149}
150
151void
152iptables_fw_clear_authservers(void)
153{
154    iptables_do_command("-t nat -F " TABLE_WIFIDOG_AUTHSERVERS);
155}
156
157void
158iptables_fw_set_authservers(void)
159{
160    s_config *config;
161    t_auth_serv *auth_server;
162   
163    config = config_get_config();
164   
165    LOCK_CONFIG();
166   
167    for (auth_server = config->auth_servers; auth_server != NULL; auth_server = auth_server->next) {
168            if (auth_server->last_ip == NULL || strcmp(auth_server->last_ip, "0.0.0.0") == 0) {
169                iptables_do_command("-t nat -A " TABLE_WIFIDOG_AUTHSERVERS " -d %s -j ACCEPT", auth_server->authserv_hostname);
170            } else {
171                iptables_do_command("-t nat -A " TABLE_WIFIDOG_AUTHSERVERS " -d %s -j ACCEPT", auth_server->last_ip);
172            }
173    }
174
175    UNLOCK_CONFIG();
176}
177
178/** Initialize the firewall rules
179 */
180int
181iptables_fw_init(void)
182{
183    s_config *config;
184   
185    config = config_get_config();
186    fw_quiet = 0;
187   
188    /* Create authservers table here instead of in iptables_fw_set_authservers
189     * so we only have to flush it and not destroy/create every time */
190    iptables_do_command("-t nat -N " TABLE_WIFIDOG_AUTHSERVERS);
191
192    iptables_fw_set_authservers();
193
194    LOCK_CONFIG();
195   
196    iptables_do_command("-t nat -N " TABLE_WIFIDOG_VALIDATE);
197    iptables_do_command("-t nat -A " TABLE_WIFIDOG_VALIDATE " -j " TABLE_WIFIDOG_AUTHSERVERS);
198    iptables_do_command("-t nat -A " TABLE_WIFIDOG_VALIDATE " -d %s -j ACCEPT", config->gw_address);
199
200    UNLOCK_CONFIG();
201
202    /** Insert global rules BEFORE the "defaults" */
203    iptables_load_ruleset("global", TABLE_WIFIDOG_VALIDATE);
204    iptables_load_ruleset("validating-users", TABLE_WIFIDOG_VALIDATE);
205
206    LOCK_CONFIG();
207   
208    iptables_do_command("-t nat -N " TABLE_WIFIDOG_UNKNOWN);
209    iptables_do_command("-t nat -A " TABLE_WIFIDOG_UNKNOWN " -j " TABLE_WIFIDOG_AUTHSERVERS);
210    iptables_do_command("-t nat -A " TABLE_WIFIDOG_UNKNOWN " -d %s -j ACCEPT", config->gw_address);
211
212    UNLOCK_CONFIG();
213   
214    /** Insert global rules BEFORE the "defaults" */
215    iptables_load_ruleset("global", TABLE_WIFIDOG_UNKNOWN);
216    iptables_load_ruleset("unknown-users", TABLE_WIFIDOG_UNKNOWN);
217    LOCK_CONFIG();
218
219    /* XXX If there's a rule in global for port 80, it overrides this. */
220    iptables_do_command("-t nat -A " TABLE_WIFIDOG_UNKNOWN " -p tcp --dport 80 -j REDIRECT --to-ports %d", config->gw_port);
221    UNLOCK_CONFIG();
222    iptables_do_command("-t nat -A " TABLE_WIFIDOG_UNKNOWN " -j DROP");
223
224    iptables_do_command("-t nat -N " TABLE_WIFIDOG_KNOWN);
225    /* Insert global rules BEFORE the "defaults" */
226    iptables_load_ruleset("global", TABLE_WIFIDOG_KNOWN);
227    iptables_load_ruleset("known-users", TABLE_WIFIDOG_KNOWN);
228
229    iptables_do_command("-t nat -N " TABLE_WIFIDOG_LOCKED);
230    iptables_load_ruleset("locked-users", TABLE_WIFIDOG_KNOWN);
231   
232    iptables_do_command("-t nat -N " TABLE_WIFIDOG_CLASS);
233    LOCK_CONFIG();
234    iptables_do_command("-t nat -A " TABLE_WIFIDOG_CLASS " -i %s -m mark --mark 0x%u -j " TABLE_WIFIDOG_VALIDATE, config->gw_interface, FW_MARK_PROBATION);
235    iptables_do_command("-t nat -A " TABLE_WIFIDOG_CLASS " -i %s -m mark --mark 0x%u -j " TABLE_WIFIDOG_KNOWN, config->gw_interface, FW_MARK_KNOWN);
236    iptables_do_command("-t nat -A " TABLE_WIFIDOG_CLASS " -i %s -m mark --mark 0x%u -j " TABLE_WIFIDOG_LOCKED, config->gw_interface, FW_MARK_LOCKED);
237    iptables_do_command("-t nat -A " TABLE_WIFIDOG_CLASS " -i %s -j " TABLE_WIFIDOG_UNKNOWN, config->gw_interface);
238    iptables_do_command("-t nat -I PREROUTING 1 -i %s -j " TABLE_WIFIDOG_CLASS, config->gw_interface);
239
240    iptables_do_command("-t mangle -N " TABLE_WIFIDOG_OUTGOING);
241    iptables_do_command("-t mangle -I PREROUTING 1 -i %s -j " TABLE_WIFIDOG_OUTGOING, config->gw_interface);
242
243    iptables_do_command("-t mangle -N " TABLE_WIFIDOG_INCOMING);
244    if (config->external_interface) {
245        iptables_do_command("-t mangle -I FORWARD 1 -i %s -j " TABLE_WIFIDOG_INCOMING, config->external_interface);
246    } else {
247        iptables_do_command("-t mangle -I FORWARD 1 -j " TABLE_WIFIDOG_INCOMING);
248    }
249
250    iptables_do_command("-t filter -N " TABLE_WIFIDOG_WIFI_TO_GW);
251    iptables_do_command("-t filter -I INPUT 1 -i %s -j " TABLE_WIFIDOG_WIFI_TO_GW, config->gw_interface);
252
253    UNLOCK_CONFIG();
254   
255    return 1;
256}
257
258/** Remove the firewall rules
259 * This is used when we do a clean shutdown of WiFiDog and when it starts to make
260 * sure there are no rules left over
261 */
262int
263iptables_fw_destroy(void)
264{
265    int rc;
266    s_config *config = config_get_config();
267
268    fw_quiet = 1;
269    iptables_do_command("-t filter -F " TABLE_WIFIDOG_WIFI_TO_GW);
270    iptables_do_command("-t nat -F " TABLE_WIFIDOG_CLASS);
271    iptables_do_command("-t mangle -F " TABLE_WIFIDOG_OUTGOING);
272    iptables_do_command("-t mangle -F " TABLE_WIFIDOG_INCOMING);
273
274    iptables_do_command("-t nat -F " TABLE_WIFIDOG_AUTHSERVERS);
275    iptables_do_command("-t nat -F " TABLE_WIFIDOG_VALIDATE);
276    iptables_do_command("-t nat -F " TABLE_WIFIDOG_UNKNOWN);
277    iptables_do_command("-t nat -F " TABLE_WIFIDOG_KNOWN);
278    iptables_do_command("-t nat -F " TABLE_WIFIDOG_LOCKED);
279    iptables_do_command("-t nat -X " TABLE_WIFIDOG_AUTHSERVERS);
280    iptables_do_command("-t nat -X " TABLE_WIFIDOG_VALIDATE);
281    iptables_do_command("-t nat -X " TABLE_WIFIDOG_UNKNOWN);
282    iptables_do_command("-t nat -X " TABLE_WIFIDOG_KNOWN);
283    iptables_do_command("-t nat -X " TABLE_WIFIDOG_LOCKED);
284
285    /* We loop in case wifidog has crashed and left some unwanted rules,
286     * maybe we shouldn't loop forever, we'll try anyway
287     */
288    rc = 0;
289    while (rc == 0) {
290        rc = iptables_do_command("-t nat -D PREROUTING -i %s -j " TABLE_WIFIDOG_CLASS, config->gw_interface);
291    }
292    iptables_do_command("-t nat -X " TABLE_WIFIDOG_CLASS);
293
294    rc = 0;
295    while (rc == 0) {
296        rc = iptables_do_command("-t filter -D INPUT -i %s -j " TABLE_WIFIDOG_WIFI_TO_GW, config->gw_interface);
297    }
298    iptables_do_command("-t filter -X " TABLE_WIFIDOG_WIFI_TO_GW);
299
300    rc = 0;
301    while (rc == 0) {
302        rc = iptables_do_command("-t mangle -D PREROUTING -i %s -j " TABLE_WIFIDOG_OUTGOING, config->gw_interface);
303    }
304    iptables_do_command("-t mangle -X " TABLE_WIFIDOG_OUTGOING);
305
306    rc = 0;
307    while (rc == 0) {
308        if (config->external_interface) {
309            rc = iptables_do_command("-t mangle -D FORWARD -i %s -j " TABLE_WIFIDOG_INCOMING, config->external_interface);
310        } else {
311            rc = iptables_do_command("-t mangle -D FORWARD -j " TABLE_WIFIDOG_INCOMING);
312        }
313    }
314    iptables_do_command("-t mangle -X " TABLE_WIFIDOG_INCOMING);
315
316    return 1;
317}
318
319/** Set the firewall access for a specific client */
320int
321iptables_fw_access(fw_access_t type, char *ip, char *mac, int tag)
322{
323    int rc;
324
325    fw_quiet = 0;
326
327    switch(type) {
328        case FW_ACCESS_ALLOW:
329            iptables_do_command("-t filter -A " TABLE_WIFIDOG_WIFI_TO_GW " -s %s -j ACCEPT", ip);
330            iptables_do_command("-t mangle -A " TABLE_WIFIDOG_OUTGOING " -s %s -m mac --mac-source %s -j MARK --set-mark %d", ip, mac, tag);
331            rc = iptables_do_command("-t mangle -A " TABLE_WIFIDOG_INCOMING " -d %s -j ACCEPT", ip);
332            break;
333        case FW_ACCESS_DENY:
334            iptables_do_command("-t filter -D " TABLE_WIFIDOG_WIFI_TO_GW " -s %s -j ACCEPT", ip);
335            iptables_do_command("-t mangle -D " TABLE_WIFIDOG_OUTGOING " -s %s -m mac --mac-source %s -j MARK --set-mark %d", ip, mac, tag);
336            rc = iptables_do_command("-t mangle -D " TABLE_WIFIDOG_INCOMING " -d %s -j ACCEPT", ip);
337            break;
338        default:
339            rc = -1;
340            break;
341    }
342
343    return rc;
344}
345
346/** Update the counters of all the clients in the client list */
347int
348iptables_fw_counters_update(void)
349{
350    FILE *output;
351    char *script,
352        ip[16],
353        rc;
354    unsigned long int counter;
355    t_client *p1;
356         struct in_addr tempaddr;
357
358    /* Look for outgoing traffic */
359    asprintf(&script, "%s %s", "iptables", "-v -n -x -t mangle -L " TABLE_WIFIDOG_OUTGOING);
360    if (!(output = popen(script, "r"))) {
361        debug(LOG_ERR, "popen(): %s", strerror(errno));
362        return -1;
363    }
364    free(script);
365
366    /* skip the first two lines */
367    while (('\n' != fgetc(output)) && !feof(output))
368        ;
369    while (('\n' != fgetc(output)) && !feof(output))
370        ;
371    while (output && !(feof(output))) {
372        rc = fscanf(output, "%*s %lu %*s %*s %*s %*s %*s %15[0-9.] %*s %*s %*s %*s %*s 0x%*u", &counter, ip);
373        if (2 == rc && EOF != rc) {
374                          /* Sanity*/
375                          if (!inet_aton(ip, &tempaddr)) {
376                                  debug(LOG_WARNING, "I was supposed to read an IP address but instead got [%s] - ignoring it", ip);
377                                  continue;
378                          }
379            debug(LOG_DEBUG, "Outgoing %s Bytes=%ld", ip, counter);
380            LOCK_CLIENT_LIST();
381            if ((p1 = client_list_find_by_ip(ip))) {
382                if (p1->counters.outgoing < counter) {
383                    p1->counters.outgoing = counter;
384                    p1->counters.last_updated = time(NULL);
385                    debug(LOG_DEBUG, "%s - Updated outgoing counter to %ld bytes from outgoing chain", ip, counter);
386                }
387            } else {
388                debug(LOG_ERR, "Could not find %s in client list", ip);
389            }
390            UNLOCK_CLIENT_LIST();
391        }
392    }
393    pclose(output);
394
395    /* Look for wifi-to-firewall traffic */
396    asprintf(&script, "%s %s", "iptables", "-v -n -x -t filter -L " TABLE_WIFIDOG_WIFI_TO_GW);
397    if (!(output = popen(script, "r"))) {
398        debug(LOG_ERR, "popen(): %s", strerror(errno));
399        return -1;
400    }
401    free(script);
402
403    /* skip the first two lines */
404    while (('\n' != fgetc(output)) && !feof(output))
405        ;
406    while (('\n' != fgetc(output)) && !feof(output))
407        ;
408    while (output && !(feof(output))) {
409        rc = fscanf(output, "%*s %lu %*s %*s %*s %*s %*s %15[0-9.] %*s", &counter, ip);
410        if (2 == rc && EOF != rc) {
411                          /* Sanity*/
412                          if (!inet_aton(ip, &tempaddr)) {
413                                  debug(LOG_WARNING, "I was supposed to read an IP address but instead got [%s] - ignoring it", ip);
414                                  continue;
415                          }
416            debug(LOG_DEBUG, "WIFI2FW %s Bytes=%ld", ip, counter);
417            LOCK_CLIENT_LIST();
418            if ((p1 = client_list_find_by_ip(ip))) {
419                if (p1->counters.togateway < counter) {
420                    p1->counters.togateway = counter;
421                    p1->counters.last_updated = time(NULL);
422                    debug(LOG_DEBUG, "%s - Updated togateway counter to %ld bytes from wifi2fw chain", ip, counter);
423                }
424            } else {
425                debug(LOG_ERR, "Could not find %s in client list", ip);
426            }
427            UNLOCK_CLIENT_LIST();
428        }
429    }
430    pclose(output);
431
432    /* Look for incoming traffic */
433    asprintf(&script, "%s %s", "iptables", "-v -n -x -t mangle -L " TABLE_WIFIDOG_INCOMING);
434    if (!(output = popen(script, "r"))) {
435        debug(LOG_ERR, "popen(): %s", strerror(errno));
436        return -1;
437    }
438    free(script);
439
440    /* skip the first two lines */
441    while (('\n' != fgetc(output)) && !feof(output))
442        ;
443    while (('\n' != fgetc(output)) && !feof(output))
444        ;
445    while (output && !(feof(output))) {
446        rc = fscanf(output, "%*s %lu %*s %*s %*s %*s %*s %*s %15[0-9.]", &counter, ip);
447        if (2 == rc && EOF != rc) {
448                          /* Sanity*/
449                          if (!inet_aton(ip, &tempaddr)) {
450                                  debug(LOG_WARNING, "I was supposed to read an IP address but instead got [%s] - ignoring it", ip);
451                                  continue;
452                          }
453            debug(LOG_DEBUG, "Incoming %s Bytes=%ld", ip, counter);
454            LOCK_CLIENT_LIST();
455            if ((p1 = client_list_find_by_ip(ip))) {
456                if (p1->counters.incoming < counter) {
457                    p1->counters.incoming = counter;
458                    p1->counters.last_updated = time(NULL);
459                    debug(LOG_DEBUG, "%s - Updated counter to %ld bytes", ip, counter);
460                }
461            } else {
462                debug(LOG_ERR, "Could not find %s in client list", ip);
463            }
464            UNLOCK_CLIENT_LIST();
465        }
466    }
467    pclose(output);
468
469    return 1;
470}
Note: See TracBrowser for help on using the browser.