Main Page | Data Structures | Directories | File List | Data Fields

src/firewall.c

00001 /********************************************************************\
00002  * This program is free software; you can redistribute it and/or    *
00003  * modify it under the terms of the GNU General Public License as   *
00004  * published by the Free Software Foundation; either version 2 of   *
00005  * the License, or (at your option) any later version.              *
00006  *                                                                  *
00007  * This program is distributed in the hope that it will be useful,  *
00008  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00009  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00010  * GNU General Public License for more details.                     *
00011  *                                                                  *
00012  * You should have received a copy of the GNU General Public License*
00013  * along with this program; if not, contact:                        *
00014  *                                                                  *
00015  * Free Software Foundation           Voice:  +1-617-542-5942       *
00016  * 59 Temple Place - Suite 330        Fax:    +1-617-542-2652       *
00017  * Boston, MA  02111-1307,  USA       gnu@gnu.org                   *
00018  *                                                                  *
00019  \********************************************************************/
00020 
00021 /*
00022  * $Header: /cvsroot/wifidog/wifidog/src/firewall.c,v 1.32 2004/04/23
00023  * 11:37:43 aprilp Exp $
00024  */
00031 #define _GNU_SOURCE
00032 
00033 #include <stdio.h>
00034 #include <stdlib.h>
00035 #include <syslog.h>
00036 #include <errno.h>
00037 #include <pthread.h>
00038 #include <sys/wait.h>
00039 #include <sys/types.h>
00040 #include <sys/unistd.h>
00041 
00042 #include <string.h>
00043 
00044 #include <sys/socket.h>
00045 #include <netinet/in.h>
00046 #include <arpa/inet.h>
00047 #include <unistd.h>
00048 #include <sys/uio.h>
00049 #include <fcntl.h>
00050 #include <netdb.h>
00051 #include <sys/time.h>
00052 
00053 #ifdef __linux__
00054 #include <net/ethernet.h>
00055 #include <netinet/ip.h>
00056 #include <netinet/ip_icmp.h>
00057 #include <netpacket/packet.h>
00058 #endif
00059 
00060 #include "httpd.h"
00061 #include "safe.h"
00062 #include "debug.h"
00063 #include "conf.h"
00064 #include "firewall.h"
00065 #include "fw_iptables.h"
00066 #include "auth.h"
00067 #include "centralserver.h"
00068 #include "client_list.h"
00069 
00070 extern pthread_mutex_t client_list_mutex;
00071 
00072 int icmp_fd = 0;
00073 
00082 int
00083 fw_allow(char *ip, char *mac, int fw_connection_state)
00084 {
00085     debug(LOG_DEBUG, "Allowing %s %s with fw_connection_state %d", ip, mac, fw_connection_state);
00086 
00087     return iptables_fw_access(FW_ACCESS_ALLOW, ip, mac, fw_connection_state);
00088 }
00089 
00097 int
00098 fw_deny(char *ip, char *mac, int fw_connection_state)
00099 {
00100     debug(LOG_DEBUG, "Denying %s %s with fw_connection_state %d", ip, mac, fw_connection_state);
00101 
00102     return iptables_fw_access(FW_ACCESS_DENY, ip, mac, fw_connection_state);
00103 }
00104 
00111 char           *
00112 arp_get(char *req_ip)
00113 {
00114     FILE           *proc;
00115          char ip[16];
00116          char mac[18];
00117          char * reply = NULL;
00118 
00119     if (!(proc = fopen("/proc/net/arp", "r"))) {
00120         return NULL;
00121     }
00122 
00123     /* Skip first line */
00124          while (!feof(proc) && fgetc(proc) != '\n');
00125 
00126          /* Find ip, copy mac in reply */
00127          reply = NULL;
00128     while (!feof(proc) && (fscanf(proc, " %15[0-9.] %*s %*s %17[A-F0-9:] %*s %*s", ip, mac) == 2)) {
00129                   if (strcmp(ip, req_ip) == 0) {
00130                                 reply = safe_strdup(mac);
00131                                 break;
00132                   }
00133     }
00134 
00135     fclose(proc);
00136 
00137     return reply;
00138 }
00139 
00142 int
00143 fw_init(void)
00144 {
00145     int flags, oneopt = 1, zeroopt = 0;
00146 
00147     debug(LOG_INFO, "Creating ICMP socket");
00148     if ((icmp_fd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1 ||
00149             (flags = fcntl(icmp_fd, F_GETFL, 0)) == -1 ||
00150              fcntl(icmp_fd, F_SETFL, flags | O_NONBLOCK) == -1 ||
00151              setsockopt(icmp_fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) ||
00152              setsockopt(icmp_fd, SOL_SOCKET, SO_DONTROUTE, &zeroopt, sizeof(zeroopt)) == -1) {
00153         debug(LOG_ERR, "Cannot create ICMP raw socket.");
00154         return;
00155     }
00156 
00157     debug(LOG_INFO, "Initializing Firewall");
00158     return iptables_fw_init();
00159 }
00160 
00163 void
00164 fw_clear_authservers(void)
00165 {
00166         debug(LOG_INFO, "Clearing the authservers list");
00167         iptables_fw_clear_authservers();
00168 }
00169 
00172 void
00173 fw_set_authservers(void)
00174 {
00175         debug(LOG_INFO, "Setting the authservers list");
00176         iptables_fw_set_authservers();
00177 }
00178 
00183 int
00184 fw_destroy(void)
00185 {
00186     if (icmp_fd != 0) {
00187         debug(LOG_INFO, "Closing ICMP socket");
00188         close(icmp_fd);
00189     }
00190 
00191     debug(LOG_INFO, "Removing Firewall rules");
00192     return iptables_fw_destroy();
00193 }
00194 
00198 void
00199 fw_counter(void)
00200 {
00201     t_authresponse  authresponse;
00202     char            *token, *ip, *mac;
00203     t_client        *p1, *p2;
00204     unsigned long long      incoming, outgoing;
00205     s_config *config = config_get_config();
00206 
00207     if (-1 == iptables_fw_counters_update()) {
00208         debug(LOG_ERR, "Could not get counters from firewall!");
00209         return;
00210     }
00211 
00212     LOCK_CLIENT_LIST();
00213 
00214     for (p1 = p2 = client_get_first_client(); NULL != p1; p1 = p2) {
00215         p2 = p1->next;
00216 
00217         ip = safe_strdup(p1->ip);
00218         token = safe_strdup(p1->token);
00219         mac = safe_strdup(p1->mac);
00220             outgoing = p1->counters.outgoing;
00221             incoming = p1->counters.incoming;
00222 
00223             UNLOCK_CLIENT_LIST();
00224         /* Ping the client, if he responds it'll keep activity on the link */
00225         icmp_ping(ip);
00226         /* Update the counters on the remote server */
00227         auth_server_request(&authresponse, REQUEST_TYPE_COUNTERS, ip, mac, token, incoming, outgoing);
00228             LOCK_CLIENT_LIST();
00229         
00230         if (!(p1 = client_list_find(ip, mac))) {
00231             debug(LOG_ERR, "Node %s was freed while being re-validated!", ip);
00232         } else {
00233             if (p1->counters.last_updated +
00234                                 (config->checkinterval * config->clienttimeout)
00235                                 <= time(NULL)) {
00236                 /* Timing out user */
00237                 debug(LOG_INFO, "%s - Inactive for %ld seconds, removing client and denying in firewall", p1->ip, config->checkinterval * config->clienttimeout);
00238                 fw_deny(p1->ip, p1->mac, p1->fw_connection_state);
00239                 client_list_delete(p1);
00240 
00241                 /* Advertise the logout */
00242                                         UNLOCK_CLIENT_LIST();
00243                                         auth_server_request(&authresponse, REQUEST_TYPE_LOGOUT, ip, mac, token, 0, 0);
00244                                         LOCK_CLIENT_LIST();
00245             }
00246                                 else {
00247                 /*
00248                  * This handles any change in
00249                  * the status this allows us
00250                  * to change the status of a
00251                  * user while he's connected
00252                  */
00253                 switch (authresponse.authcode) {
00254                     case AUTH_DENIED:
00255                         debug(LOG_NOTICE, "%s - Denied. Removing client and firewall rules", p1->ip);
00256                         fw_deny(p1->ip, p1->mac, p1->fw_connection_state);
00257                         client_list_delete(p1);
00258                         break;
00259 
00260                     case AUTH_VALIDATION_FAILED:
00261                         debug(LOG_NOTICE, "%s - Validation timeout, now denied. Removing client and firewall rules", p1->ip);
00262                         fw_deny(p1->ip, p1->mac, p1->fw_connection_state);
00263                         client_list_delete(p1);
00264                         break;
00265 
00266                     case AUTH_ALLOWED:
00267                         if (p1->fw_connection_state != FW_MARK_KNOWN) {
00268                             debug(LOG_INFO, "%s - Access has changed to allowed, refreshing firewall and clearing counters", p1->ip);
00269                             fw_deny(p1->ip, p1->mac, p1->fw_connection_state);
00270                             p1->fw_connection_state = FW_MARK_KNOWN;
00271                             p1->counters.incoming = p1->counters.outgoing = 0;
00272                             fw_allow(p1->ip, p1->mac, p1->fw_connection_state);
00273                         }
00274                         break;
00275 
00276                     case AUTH_VALIDATION:
00277                         /*
00278                          * Do nothing, user
00279                          * is in validation
00280                          * period
00281                          */
00282                         debug(LOG_INFO, "%s - User in validation period", p1->ip);
00283                         break;
00284 
00285                                                   case AUTH_ERROR:
00286                                                                 debug(LOG_WARNING, "Error communicating with auth server - leaving %s as-is for now", p1->ip);
00287                                                                 break;
00288 
00289                     default:
00290                         debug(LOG_DEBUG, "I do not know about authentication code %d", authresponse.authcode);
00291                         break;
00292                 }
00293             }
00294         }
00295 
00296         free(token);
00297         free(ip);
00298         free(mac);
00299     }
00300     UNLOCK_CLIENT_LIST();
00301 }
00302 
00303 void icmp_ping(char *host) {
00304   struct sockaddr_in saddr;
00305 #ifdef __linux__
00306   struct { 
00307     struct ip ip;
00308     struct icmp icmp;
00309   } packet;
00310 #endif
00311   unsigned int i, j;
00312   int opt = 2000;
00313   unsigned short id = rand16();
00314 
00315   saddr.sin_family = AF_INET;
00316   saddr.sin_port = 0;
00317   inet_aton(host, &saddr.sin_addr);
00318 #ifdef HAVE_SOCKADDR_SA_LEN
00319   saddr.sin_len = sizeof(struct sockaddr_in);
00320 #endif
00321 
00322   memset(&(saddr.sin_zero), '\0', sizeof(saddr.sin_zero));
00323 
00324 #ifdef __linux__
00325   memset(&packet.icmp, 0, sizeof(packet.icmp));
00326   packet.icmp.icmp_type = ICMP_ECHO;
00327   packet.icmp.icmp_id = id;
00328   for (j = 0, i = 0; i < sizeof(struct icmp) / 2; i++)
00329     j += ((unsigned short *)&packet.icmp)[i];
00330   while (j>>16)
00331     j = (j & 0xffff) + (j >> 16);  
00332   packet.icmp.icmp_cksum = (j == 0xffff) ? j : ~j;
00333 
00334   if (setsockopt(icmp_fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) == -1) {
00335       debug(LOG_ERR, "setsockopt(): %s", strerror(errno));
00336   }
00337   if (sendto(icmp_fd, (char *)&packet.icmp, sizeof(struct icmp), 0, (struct sockaddr *)&saddr, sizeof(saddr)) == -1) {
00338       debug(LOG_ERR, "sendto(): %s", strerror(errno));
00339   }
00340   opt = 1;
00341   if (setsockopt(icmp_fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) == -1) {
00342       debug(LOG_ERR, "setsockopt(): %s", strerror(errno));
00343   }
00344 #endif
00345 
00346   return;
00347 }
00348 
00349 unsigned short rand16(void) {
00350   static int been_seeded = 0;
00351 
00352   if (!been_seeded) {
00353     int fd, n = 0;
00354     unsigned int c = 0, seed = 0;
00355     char sbuf[sizeof(seed)];
00356     char *s;
00357     struct timeval now;
00358 
00359     /* not a very good seed but what the heck, it needs to be quickly acquired */
00360     gettimeofday(&now, NULL);
00361     seed = now.tv_sec ^ now.tv_usec ^ (getpid() << 16);
00362 
00363     srand(seed);
00364     been_seeded = 1;
00365     }
00366 
00367     /* Some rand() implementations have less randomness in low bits
00368      * than in high bits, so we only pay attention to the high ones.
00369      * But most implementations don't touch the high bit, so we 
00370      * ignore that one.
00371      **/
00372       return( (unsigned short) (rand() >> 15) );
00373 }

Generated on Sun Apr 3 20:04:46 2005 for WifiDog by  doxygen 1.4.1