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

Revision 449, 11.4 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
RevLine 
[9]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 *                                                                  *
[34]19 \********************************************************************/
[9]20
[100]21/*
22 * $Header: /cvsroot/wifidog/wifidog/src/firewall.c,v 1.32 2004/04/23
23 * 11:37:43 aprilp Exp $
24 */
[9]25/** @internal
[34]26  @file firewall.c
27  @brief Firewall update functions
28  @author Copyright (C) 2004 Philippe April <papril777@yahoo.com>
29 */
[9]30
[119]31#define _GNU_SOURCE
[9]32
[119]33#include <stdio.h>
34#include <stdlib.h>
35#include <syslog.h>
36#include <errno.h>
37#include <pthread.h>
38#include <sys/wait.h>
39#include <sys/types.h>
40#include <sys/unistd.h>
41
42#include <string.h>
43
[422]44#include <sys/socket.h>
45#include <netinet/in.h>
46#include <arpa/inet.h>
47#include <unistd.h>
48#include <net/ethernet.h>
49#include <netinet/ip.h>
50#include <netinet/ip_icmp.h>
51#include <netpacket/packet.h>
52#include <sys/uio.h>
53#include <fcntl.h>
54#include <netdb.h>
55#include <sys/time.h>
56
[274]57#include "httpd.h"
58
[119]59#include "debug.h"
60#include "conf.h"
61#include "firewall.h"
[170]62#include "fw_iptables.h"
[119]63#include "auth.h"
64#include "centralserver.h"
[135]65#include "client_list.h"
[119]66
[135]67extern pthread_mutex_t client_list_mutex;
[72]68
[422]69int icmp_fd = 0;
70
[75]71/**
[136]72 * Allow a client access through the firewall by adding a rule in the firewall to MARK the user's packets with the proper
[109]73 * rule by providing his IP and MAC address
[95]74 * @param ip IP address to allow
75 * @param mac MAC address to allow
[135]76 * @param fw_connection_state fw_connection_state Tag
[95]77 * @return Return code of the command
[75]78 */
[35]79int
[135]80fw_allow(char *ip, char *mac, int fw_connection_state)
[9]81{
[135]82    debug(LOG_DEBUG, "Allowing %s %s with fw_connection_state %d", ip, mac, fw_connection_state);
[107]83
[135]84    return iptables_fw_access(FW_ACCESS_ALLOW, ip, mac, fw_connection_state);
[9]85}
86
[75]87/**
[136]88 * @brief Deny a client access through the firewall by removing the rule in the firewall that was fw_connection_stateging the user's traffic
[95]89 * @param ip IP address to deny
90 * @param mac MAC address to deny
[135]91 * @param fw_connection_state fw_connection_state Tag
[95]92 * @return Return code of the command
[75]93 */
[35]94int
[135]95fw_deny(char *ip, char *mac, int fw_connection_state)
[9]96{
[135]97    debug(LOG_DEBUG, "Denying %s %s with fw_connection_state %d", ip, mac, fw_connection_state);
[107]98
[135]99    return iptables_fw_access(FW_ACCESS_DENY, ip, mac, fw_connection_state);
[9]100}
101
[75]102/**
[136]103 * Get an IP's MAC address from the ARP cache.
[75]104 * Go through all the entries in /proc/net/arp until we find the requested
105 * IP address and return the MAC address bound to it.
[95]106 * @todo Make this function portable (using shell scripts?)
[75]107 */
[100]108char           *
[9]109arp_get(char *req_ip)
110{
[100]111    FILE           *proc;
[116]112    char            *ip, *mac;
[449]113         char * reply;
[9]114
[100]115    if (!(proc = fopen("/proc/net/arp", "r"))) {
116        return NULL;
117    }
[449]118    if (!(ip = malloc(16))) {
119                 debug(LOG_CRIT, "Cannot allocate 16 bytes of memory for IP, Banzai!");
120                 exit(1);
121         }
122    if (!(mac = malloc(18))) {
123                 debug(LOG_CRIT, "Cannot allocate 18 bytes of memory for MAC, Banzai!");
124                 exit(1);
125         }
126
[100]127    /* Skip first line */
[449]128         while (!feof(proc) && fgetc(proc) != '\n');
129
130         /* Find ip, put mac in reply */
131         reply = NULL;
132    while (!feof(proc) && (fscanf(proc, " %15[0-9.] %*s %*s %17[A-F0-9:] %*s %*s", ip, mac) == 2)) {
133                  if (strcmp(ip, req_ip) == 0) {
134                                reply = mac;
135                                break;
136                  }
[100]137    }
[28]138
[116]139    free(ip);
[449]140         if (!reply)
141                 free(mac);
[9]142
[449]143    fclose(proc);
144
145    return reply;
[9]146}
147
[136]148/** Initialize the firewall rules
[75]149 */
[35]150int
[9]151fw_init(void)
152{
[422]153    int flags, oneopt = 1, zeroopt = 0;
154
155    debug(LOG_INFO, "Creating ICMP socket");
156    if ((icmp_fd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1 ||
157            (flags = fcntl(icmp_fd, F_GETFL, 0)) == -1 ||
158             fcntl(icmp_fd, F_SETFL, flags | O_NONBLOCK) == -1 ||
159             setsockopt(icmp_fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) ||
160             setsockopt(icmp_fd, SOL_SOCKET, SO_DONTROUTE, &zeroopt, sizeof(zeroopt)) == -1) {
161        debug(LOG_ERR, "Cannot create ICMP raw socket.");
162        return;
163    }
164
[101]165    debug(LOG_INFO, "Initializing Firewall");
[135]166    return iptables_fw_init();
[9]167}
168
[277]169/** Clear the authserver rules
170 */
171void
172fw_clear_authservers(void)
173{
174        debug(LOG_INFO, "Clearing the authservers list");
175        iptables_fw_clear_authservers();
176}
177
178/** Set the authservers rules
179 */
180void
181fw_set_authservers(void)
182{
183        debug(LOG_INFO, "Setting the authservers list");
184        iptables_fw_set_authservers();
185}
186
[136]187/** Remove the firewall rules
[75]188 * This is used when we do a clean shutdown of WiFiDog.
[95]189 * @return Return code of the fw.destroy script
[75]190 */
[35]191int
[9]192fw_destroy(void)
193{
[422]194    if (icmp_fd != 0) {
195        debug(LOG_INFO, "Closing ICMP socket");
196        close(icmp_fd);
197    }
198
[101]199    debug(LOG_INFO, "Removing Firewall rules");
[135]200    return iptables_fw_destroy();
[9]201}
202
[136]203/**Probably a misnomer, this function actually refreshes the entire client list's traffic counter, re-authenticates every client with the central server and update's the central servers traffic counters and notifies it if a client has logged-out.
[95]204 * @todo Make this function smaller and use sub-fonctions
205 */
[35]206void
[9]207fw_counter(void)
208{
[100]209    t_authresponse  authresponse;
[126]210    char            *token, *ip, *mac;
[135]211    t_client        *p1, *p2;
212    long long       incoming, outgoing;
[136]213    s_config *config = config_get_config();
[9]214
[135]215    if (-1 == iptables_fw_counters_update()) {
[121]216        debug(LOG_ERR, "Could not get counters from firewall!");
[113]217        return;
218    }
[18]219
[256]220    LOCK_CLIENT_LIST();
[136]221
222    for (p1 = p2 = client_get_first_client(); NULL != p1; p1 = p2) {
[121]223        p2 = p1->next;
[126]224
[121]225        ip = strdup(p1->ip);
226        token = strdup(p1->token);
[126]227        mac = strdup(p1->mac);
[422]228            outgoing = p1->counters.outgoing;
229            incoming = p1->counters.incoming;
[126]230
[422]231            UNLOCK_CLIENT_LIST();
232        /* Ping the client, if he responds it'll keep activity on the link */
233        icmp_ping(ip);
234        /* Update the counters on the remote server */
[135]235        auth_server_request(&authresponse, REQUEST_TYPE_COUNTERS, ip, mac, token, incoming, outgoing);
[422]236            LOCK_CLIENT_LIST();
[256]237       
[135]238        if (!(p1 = client_list_find(ip, mac))) {
[121]239            debug(LOG_ERR, "Node %s was freed while being re-validated!", ip);
240        } else {
241            if (p1->counters.last_updated +
[136]242                                (config->checkinterval * config->clienttimeout)
[135]243                                <= time(NULL)) {
[121]244                /* Timing out user */
[136]245                debug(LOG_INFO, "%s - Inactive for %ld seconds, removing client and denying in firewall", p1->ip, config->checkinterval * config->clienttimeout);
[135]246                fw_deny(p1->ip, p1->mac, p1->fw_connection_state);
247                client_list_delete(p1);
[126]248
249                /* Advertise the logout */
[256]250                UNLOCK_CLIENT_LIST();
[135]251                auth_server_request(&authresponse, REQUEST_TYPE_LOGOUT, ip, mac, token, 0, 0);
[256]252                LOCK_CLIENT_LIST();
[121]253            } else {
254                /*
255                 * This handles any change in
256                 * the status this allows us
257                 * to change the status of a
258                 * user while he's connected
259                 */
260                switch (authresponse.authcode) {
261                    case AUTH_DENIED:
[72]262
[121]263                    case AUTH_VALIDATION_FAILED:
[135]264                        debug(LOG_NOTICE, "%s - Validation timeout, now denied. Removing client and firewall rules", p1->ip);
265                        fw_deny(p1->ip, p1->mac, p1->fw_connection_state);
266                        client_list_delete(p1);
[121]267                        break;
[95]268
[121]269                    case AUTH_ALLOWED:
[135]270                        if (p1->fw_connection_state != FW_MARK_KNOWN) {
[121]271                            debug(LOG_INFO, "%s - Access has changed, refreshing firewall and clearing counters", p1->ip);
[135]272                            fw_deny(p1->ip, p1->mac, p1->fw_connection_state);
273                            p1->fw_connection_state = FW_MARK_KNOWN;
[422]274                            p1->counters.togateway = p1->counters.incoming = p1->counters.outgoing = 0;
[135]275                            fw_allow(p1->ip, p1->mac, p1->fw_connection_state);
[121]276                        }
277                        break;
[113]278
[121]279                    case AUTH_VALIDATION:
280                        /*
281                         * Do nothing, user
282                         * is in validation
283                         * period
284                         */
285                        debug(LOG_INFO, "%s - User in validation period", p1->ip);
286                        break;
[113]287
[121]288                    default:
289                        debug(LOG_DEBUG, "I do not know about authentication code %d", authresponse.authcode);
290                        break;
[113]291                }
[121]292            }
293        }
[113]294
[121]295        free(token);
296        free(ip);
[127]297        free(mac);
[121]298    }
[256]299    UNLOCK_CLIENT_LIST();
[9]300}
[422]301
302void icmp_ping(char *host) {
303  struct sockaddr_in saddr;
304  struct { 
305    struct ip ip;
306    struct icmp icmp;
307  } packet;
308  unsigned int i, j;
309  int opt = 2000;
310  unsigned short id = rand16();
311
312  saddr.sin_family = AF_INET;
313  saddr.sin_port = 0;
314  inet_aton(host, &saddr.sin_addr);
315#ifdef HAVE_SOCKADDR_SA_LEN
316  saddr.sin_len = sizeof(struct sockaddr_in);
317#endif
318
319  memset(&(saddr.sin_zero), '\0', sizeof(saddr.sin_zero));
320 
321  memset(&packet.icmp, 0, sizeof(packet.icmp));
322  packet.icmp.icmp_type = ICMP_ECHO;
323  packet.icmp.icmp_id = id;
324  for (j = 0, i = 0; i < sizeof(struct icmp) / 2; i++)
325    j += ((unsigned short *)&packet.icmp)[i];
326  while (j>>16)
327    j = (j & 0xffff) + (j >> 16); 
328  packet.icmp.icmp_cksum = (j == 0xffff) ? j : ~j;
329
330  if (setsockopt(icmp_fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) == -1) {
331      debug(LOG_ERR, "setsockopt(): %s", strerror(errno));
332  }
333  if (sendto(icmp_fd, (char *)&packet.icmp, sizeof(struct icmp), 0, (struct sockaddr *)&saddr, sizeof(saddr)) == -1) {
334      debug(LOG_ERR, "sendto(): %s", strerror(errno));
335  }
336  opt = 1;
337  if (setsockopt(icmp_fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) == -1) {
338      debug(LOG_ERR, "setsockopt(): %s", strerror(errno));
339  }
340
341  return;
342}
343
344unsigned short rand16(void) {
345  static int been_seeded = 0;
346
347  if (!been_seeded) {
348    int fd, n = 0;
349    unsigned int c = 0, seed = 0;
350    char sbuf[sizeof(seed)];
351    char *s;
352    struct timeval now;
353
354    /* not a very good seed but what the heck, it needs to be quickly acquired */
355    gettimeofday(&now, NULL);
356    seed = now.tv_sec ^ now.tv_usec ^ (getpid() << 16);
357
358    srand(seed);
359    been_seeded = 1;
360    }
361
362    /* Some rand() implementations have less randomness in low bits
363     * than in high bits, so we only pay attention to the high ones.
364     * But most implementations don't touch the high bit, so we
365     * ignore that one.
366     **/
367      return( (unsigned short) (rand() >> 15) );
368}
Note: See TracBrowser for help on using the browser.