root/trunk/wifidog/src/firewall.c @ 85

Revision 85, 10.6 KB (checked in by alexcv, 9 years ago)

Fixed sigterm handler running twice on Linux

  • 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 firewall.c
24  @brief Firewall update functions
25  @author Copyright (C) 2004 Philippe April <papril777@yahoo.com>
26 */
27
28#include "common.h"
29
30extern s_config config;
31
32pthread_mutex_t nodes_mutex;
33
34pthread_mutex_t sigterm_mutex = PTHREAD_MUTEX_INITIALIZER
35
36t_node *firstnode = NULL;
37
38/**
39 * @brief Allow a user through the firewall
40 *
41 * Add a rule in the firewall to tag the user's packets with its profile
42 * number by providing his IP and MAC address. This is done by
43 * executing the firewall script "fw.access" like this:
44 * fw.access allow <ip> <mac> <profile>
45 */
46int
47fw_allow(char *ip, char *mac, int profile)
48{
49        char s_profile[16];
50        char script[MAX_BUF];
51        struct stat st;
52        char *command[] = {script, "allow", ip, mac, s_profile, NULL};
53
54        sprintf(s_profile, "%-10d", profile);
55        sprintf(script, "%s/%s/%s", config.fwscripts_path, config.fwtype, 
56                SCRIPT_FWACCESS);
57
58        if (-1 == (stat(script, &st))) {
59                debug(D_LOG_ERR, "Could not find %s: %s", script,
60                        strerror(errno));
61                return(1);
62        }
63
64        return(execute(command));
65}
66
67/**
68 * @brief Deny a user through the firewall
69 *
70 * Remove the rule in the firewall that was tagging the user's traffic
71 * by executing the firewall script "fw.access" this way:
72 * fw.access deny <ip> <mac> <profile>
73 */
74int
75fw_deny(char *ip, char *mac, int profile)
76{
77        char s_profile[16];
78        char script[MAX_BUF];
79        struct stat st;
80        char *command[] = {script, "deny", ip, mac, s_profile, NULL};
81
82        sprintf(s_profile, "%-10d", profile);
83        sprintf(script, "%s/%s/%s", config.fwscripts_path, config.fwtype,
84                SCRIPT_FWACCESS);
85
86        if (-1 == (stat(script, &st))) {
87                debug(D_LOG_ERR, "Could not find %s: %s", script, 
88                        strerror(errno));
89                return(1);
90        }
91
92        return(execute(command));
93}
94
95/** @brief Execute a shell command
96 *
97 * Fork a child and execute a shell command, the parent
98 * process waits for the child to return and returns the child's exit()
99 * value.
100 */
101int
102execute(char **argv)
103{
104        int pid, status, rc;
105
106        debug(D_LOG_DEBUG, "Executing '%s'", argv[0]);
107
108        if ((pid = fork()) < 0) {     /* fork a child process           */
109                debug(D_LOG_ERR, "fork(): %s", strerror(errno));
110                exit(1);
111        } else if (pid == 0) {          /* for the child process:         */
112                if (execvp(*argv, argv) < 0) {     /* execute the command  */
113                        debug(D_LOG_ERR, "fork(): %s", strerror(errno));
114                        exit(1);
115                }
116        } else {                                  /* for the parent:      */
117                do {
118                        rc = wait(&status);
119                } while (rc != pid && rc != -1);        /* wait for completion  */
120        }
121
122        return(status);
123}
124
125/**
126 * @brief Get an IP's MAC address from the ARP cache.
127 *
128 * Go through all the entries in /proc/net/arp until we find the requested
129 * IP address and return the MAC address bound to it.
130 */
131/* TODO Make this function portable... Use shell scripts? */
132char *
133arp_get(char *req_ip)
134{
135        FILE *proc;
136        char ip[16], *mac;
137
138        if (!(proc = fopen("/proc/net/arp", "r"))) {
139                return NULL;
140        }
141
142        /* Skip first line */
143        fscanf(proc, "%*s %*s %*s %*s %*s %*s %*s %*s %*s");
144        mac = (char *)malloc(18);
145        while(!feof(proc)) {
146                fscanf(proc, "%15s %*s %*s %17s %*s %*s", ip, mac);
147                if (strcmp(ip, req_ip) == 0) {
148                        return mac;
149                }
150        }
151        fclose(proc);
152
153        free(mac);
154
155        return NULL;
156}
157
158/**
159 * @brief Initialize the firewall
160 *
161 * Initialize the firewall rules by executing the 'fw.init' script:
162 * fw.init <gw_interface> <gw_address> <port> <authserv_hostname>
163 */
164int
165fw_init(void)
166{
167        char port[16];
168        char script[MAX_BUF];
169        int rc;
170        struct stat st;
171        char *command[] = {script, config.gw_interface, config.gw_address, 
172                                port, config.authserv_hostname, NULL};
173
174        sprintf(port, "%-5d", config.gw_port);
175        sprintf(script, "%s/%s/%s", config.fwscripts_path, config.fwtype, 
176                SCRIPT_FWINIT);
177
178        if (-1 == (stat(script, &st))) {
179                debug(D_LOG_ERR, "Could not find %s: %s", script, 
180                        strerror(errno));
181                debug(D_LOG_ERR, "Exiting...");
182                exit(1);
183        }
184
185        debug(D_LOG_INFO, "Setting firewall rules");
186
187        if ((rc = execute(command)) != 0) {
188                debug(D_LOG_ERR, "Could not setup firewall, exiting...");
189                exit(1);
190        }
191
192        return(rc);
193}
194
195/**
196 * @brief Destroy the firewall
197 *
198 * Remove the firewall rules by executing the 'fw.destroy' script.
199 * This is used when we do a clean shutdown of WiFiDog.
200 */
201int
202fw_destroy(void)
203{
204        char script[MAX_BUF];
205        struct stat st;
206        char *command[] = {script, config.gw_interface, NULL };
207
208        sprintf(script, "%s/%s/%s", config.fwscripts_path, config.fwtype, 
209                SCRIPT_FWDESTROY);
210
211        if (-1 == (stat(script, &st))) {
212                debug(D_LOG_ERR, "Could not find %s: %s", script, 
213                        strerror(errno));
214                return(1);
215        }
216
217        debug(D_LOG_INFO, "Flushing firewall rules");
218
219        return(execute(command));
220}
221
222void
223fw_counter(void)
224{
225        FILE    *output;
226        long    int     counter;
227        int     profile,
228                rc;
229        char    ip[255],
230                mac[255],
231                script[MAX_BUF],
232                *token;
233        t_node *p1;
234
235        sprintf(script, "%s/%s/%s", config.fwscripts_path, config.fwtype, 
236                SCRIPT_FWCOUNTERS);
237
238        if (!(output = popen(script, "r"))) {
239                debug(D_LOG_ERR, "popen(): %s", strerror(errno));
240        } else {
241                while (!(feof(output)) && output) {
242                        rc = fscanf(output, "%ld %s %s %d", &counter, ip, 
243                                        mac, &profile);
244                        if (rc == 4 && rc != EOF) {
245
246                                pthread_mutex_lock(&nodes_mutex);
247
248                                p1 = node_find_by_ip(ip);
249
250                                if (p1->counter == counter) {
251                                        /* expire clients for inactivity */
252                                        debug(D_LOG_DEBUG, "Client %s was "
253                                                "inactive", ip);
254                                        fw_deny(p1->ip, p1->mac,
255                                                p1->rights->profile);
256                                        node_delete(p1);
257                                } else if (!(!(p1) || !(p1->active) ||
258                                        (p1->rights->last_checked +
259                                        (config.checkinterval *
260                                         config.clienttimeout)) > time(NULL))) {
261
262                                        p1->rights->last_checked = time(NULL);
263                                        p1->counter = counter;
264                                       
265                                        token = strdup(p1->token);
266                                       
267                                        pthread_mutex_unlock(&nodes_mutex);
268
269                                        profile = authenticate(ip, mac, token,
270                                                                counter);
271                                       
272                                        pthread_mutex_lock(&nodes_mutex);
273
274                                        free(token);
275                                       
276                                        /* may have changed while we held the
277                                         * mutex */
278                                        p1 = node_find_by_ip(ip);
279
280                                        if (p1 == NULL) {       
281                                                debug(D_LOG_DEBUG, "Node was "
282                                                        "freed while being "
283                                                        "re-validated!");
284                                        } else if (profile <= 0) {
285                                                /* failed */
286                                                debug(D_LOG_DEBUG, "Auth "
287                                                        "failed for client %s",
288                                                        ip);
289                                                fw_deny(p1->ip, p1->mac,
290                                                        p1->rights->profile);
291                                                node_delete(p1);
292                                        } else {
293                                                /* successful */
294                                                debug(D_LOG_DEBUG, "Updated "
295                                                        "client %s counter to "
296                                                        "%ld bytes", ip,
297                                                        counter);
298
299                                                if (!check_userrights(p1)) {
300                                                        fw_deny(p1->ip, p1->mac,
301                                                           p1->rights->profile);
302                                                        node_delete(p1);
303                                                }
304                                        }
305                                }
306                                pthread_mutex_unlock(&nodes_mutex);
307                        }
308                }
309                pclose(output);
310        }
311}
312
313/**
314 * @brief Initializes the list of connected clients (node)
315 */
316void
317node_init(void)
318{
319
320        firstnode = NULL;
321}
322
323/**
324 * @brief Adds a new node to the connections list
325 *
326 * Based on the parameters it receives, this function creates a new entry
327 * in the connections list. All the memory allocation is done here.
328 */
329t_node *
330node_add(char *ip, char *mac, char *token, long int counter, int active)
331{
332        t_node  *curnode,
333                *prevnode;
334
335        prevnode = NULL;
336        curnode = firstnode;
337
338        while (curnode != NULL) {
339                prevnode = curnode;
340                curnode = curnode->next;
341        }
342
343        curnode = (t_node *)malloc(sizeof(t_node));
344
345        if (curnode == NULL) {
346                debug(D_LOG_DEBUG, "Out of memory");
347                exit(-1);
348        }
349
350        memset(curnode, 0, sizeof(t_node));
351
352        curnode->ip = strdup(ip);
353        curnode->mac = strdup(mac);
354        curnode->token = strdup(token);
355        curnode->counter = counter;
356        curnode->active = active;
357
358        if (prevnode == NULL) {
359                firstnode = curnode;
360        } else {
361                prevnode->next = curnode;
362        }
363
364        debug(D_LOG_DEBUG, "Added a new node to linked list: IP: %s Token: %s",
365                ip, token);
366       
367        return curnode;
368}
369
370/**
371 * @brief Finds a specific node by its IP
372 */
373t_node *
374node_find_by_ip(char *ip)
375{
376        t_node *ptr;
377       
378        ptr = firstnode;
379        while (NULL != ptr) {
380                if (0 == strcmp(ptr->ip, ip))
381                        return ptr;
382                ptr = ptr->next;
383        }
384
385        return NULL;
386}
387
388/**
389 * @brief Finds a specific node by its token
390 */
391t_node *
392node_find_by_token(char *token)
393{
394        t_node *ptr;
395
396        ptr = firstnode;
397        while (NULL != ptr) {
398                if (0 == strcmp(ptr->token, token))
399                        return ptr;
400                ptr = ptr->next;
401        } 
402
403        return NULL;
404}
405
406/**
407 * @brief Frees the memory used by a t_node structure
408 *
409 * This function frees the memory used by the t_node structure in the
410 * proper order. It also calls the free_userrights() function to free
411 * the memory used by the rights structure for the node.
412 */
413void
414free_node(t_node *node)
415{
416
417        if (node->mac != NULL)
418                free(node->mac);
419
420        if (node->ip != NULL)
421                free(node->ip);
422
423        if (node->token != NULL)
424                free(node->token);
425
426        if (node->rights != NULL)
427                free_userrights(node->rights);
428
429        free(node);
430}
431
432/**
433 * @brief Deletes a node from the connections list
434 *
435 * Removes the specified node from the connections list and then calls
436 * the function to free the memory used by the node.
437 */
438void
439node_delete(t_node *node)
440{
441        t_node  *ptr;
442       
443        ptr = firstnode;
444
445        if (ptr == node) {
446                firstnode = ptr->next;
447                free_node(node);
448        } else {
449                while (ptr->next != NULL && ptr != node) {
450                        if (ptr->next == node) {
451                                ptr->next = node->next;
452                                free_node(node);
453                        }
454                }
455        }
456}
457
458/**
459 * @brief Check the rights for a client
460 *
461 * This function validates that a client hasn't met one of the conditions
462 * for the termination of his connection. Right now, we only check to see
463 * for a time-out. More checks could be added here.
464 */
465int
466check_userrights(t_node *node)
467{
468        if (node->rights->end_time <= time(NULL)) {
469                debug(D_LOG_DEBUG, "Connection %s has expired", node->ip);
470                return 0;
471        }
472
473        return 1;
474}
475
Note: See TracBrowser for help on using the browser.