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  * $Id: firewall.c 1243 2007-06-28 01:48:01Z benoitg $
00023  */
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 /* from commandline.c */
00073 extern pid_t restart_orig_pid;
00074 
00075 
00076 
00085 int
00086 fw_allow(char *ip, char *mac, int fw_connection_state)
00087 {
00088     debug(LOG_DEBUG, "Allowing %s %s with fw_connection_state %d", ip, mac, fw_connection_state);
00089 
00090     return iptables_fw_access(FW_ACCESS_ALLOW, ip, mac, fw_connection_state);
00091 }
00092 
00100 int
00101 fw_deny(char *ip, char *mac, int fw_connection_state)
00102 {
00103     debug(LOG_DEBUG, "Denying %s %s with fw_connection_state %d", ip, mac, fw_connection_state);
00104 
00105     return iptables_fw_access(FW_ACCESS_DENY, ip, mac, fw_connection_state);
00106 }
00107 
00114 char           *
00115 arp_get(char *req_ip)
00116 {
00117     FILE           *proc;
00118          char ip[16];
00119          char mac[18];
00120          char * reply = NULL;
00121 
00122     if (!(proc = fopen("/proc/net/arp", "r"))) {
00123         return NULL;
00124     }
00125 
00126     /* Skip first line */
00127          while (!feof(proc) && fgetc(proc) != '\n');
00128 
00129          /* Find ip, copy mac in reply */
00130          reply = NULL;
00131     while (!feof(proc) && (fscanf(proc, " %15[0-9.] %*s %*s %17[A-F0-9:] %*s %*s", ip, mac) == 2)) {
00132                   if (strcmp(ip, req_ip) == 0) {
00133                                 reply = safe_strdup(mac);
00134                                 break;
00135                   }
00136     }
00137 
00138     fclose(proc);
00139 
00140     return reply;
00141 }
00142 
00145 int
00146 fw_init(void)
00147 {
00148     int flags, oneopt = 1, zeroopt = 0;
00149          int result = 0;
00150          t_client * client = NULL;
00151 
00152     debug(LOG_INFO, "Creating ICMP socket");
00153     if ((icmp_fd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1 ||
00154             (flags = fcntl(icmp_fd, F_GETFL, 0)) == -1 ||
00155              fcntl(icmp_fd, F_SETFL, flags | O_NONBLOCK) == -1 ||
00156              setsockopt(icmp_fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) ||
00157              setsockopt(icmp_fd, SOL_SOCKET, SO_DONTROUTE, &zeroopt, sizeof(zeroopt)) == -1) {
00158         debug(LOG_ERR, "Cannot create ICMP raw socket.");
00159         return;
00160     }
00161 
00162     debug(LOG_INFO, "Initializing Firewall");
00163     result = iptables_fw_init();
00164 
00165          if (restart_orig_pid) {
00166                  debug(LOG_INFO, "Restoring firewall rules for clients inherited from parent");
00167                  LOCK_CLIENT_LIST();
00168                  client = client_get_first_client();
00169                  while (client) {
00170                          fw_allow(client->ip, client->mac, client->fw_connection_state);
00171                          client = client->next;
00172                  }
00173                  UNLOCK_CLIENT_LIST();
00174          }
00175 
00176          return result;
00177 }
00178 
00181 void
00182 fw_clear_authservers(void)
00183 {
00184         debug(LOG_INFO, "Clearing the authservers list");
00185         iptables_fw_clear_authservers();
00186 }
00187 
00190 void
00191 fw_set_authservers(void)
00192 {
00193         debug(LOG_INFO, "Setting the authservers list");
00194         iptables_fw_set_authservers();
00195 }
00196 
00201 int
00202 fw_destroy(void)
00203 {
00204     if (icmp_fd != 0) {
00205         debug(LOG_INFO, "Closing ICMP socket");
00206         close(icmp_fd);
00207     }
00208 
00209     debug(LOG_INFO, "Removing Firewall rules");
00210     return iptables_fw_destroy();
00211 }
00212 
00216 void
00217 fw_sync_with_authserver(void)
00218 {
00219     t_authresponse  authresponse;
00220     char            *token, *ip, *mac;
00221     t_client        *p1, *p2;
00222     unsigned long long      incoming, outgoing;
00223     s_config *config = config_get_config();
00224 
00225     if (-1 == iptables_fw_counters_update()) {
00226         debug(LOG_ERR, "Could not get counters from firewall!");
00227         return;
00228     }
00229 
00230     LOCK_CLIENT_LIST();
00231 
00232     for (p1 = p2 = client_get_first_client(); NULL != p1; p1 = p2) {
00233         p2 = p1->next;
00234 
00235         ip = safe_strdup(p1->ip);
00236         token = safe_strdup(p1->token);
00237         mac = safe_strdup(p1->mac);
00238             outgoing = p1->counters.outgoing;
00239             incoming = p1->counters.incoming;
00240 
00241             UNLOCK_CLIENT_LIST();
00242         /* Ping the client, if he responds it'll keep activity on the link.
00243          * However, if the firewall blocks it, it will not help.  The suggested
00244          * way to deal witht his is to keep the DHCP lease time extremely 
00245          * short:  Shorter than config->checkinterval * config->clienttimeout */
00246         icmp_ping(ip);
00247         /* Update the counters on the remote server only if we have an auth server */
00248         if (config->auth_servers != NULL) {
00249             auth_server_request(&authresponse, REQUEST_TYPE_COUNTERS, ip, mac, token, incoming, outgoing);
00250         }
00251             LOCK_CLIENT_LIST();
00252         
00253         if (!(p1 = client_list_find(ip, mac))) {
00254             debug(LOG_ERR, "Node %s was freed while being re-validated!", ip);
00255         } else {
00256                 time_t  current_time=time(NULL);
00257                 debug(LOG_INFO, "Checking client %s for timeout:  Last updated %ld (%ld seconds ago), timeout delay %ld seconds, current time %ld, ",
00258                         p1->ip, p1->counters.last_updated, current_time-p1->counters.last_updated, config->checkinterval * config->clienttimeout, current_time);
00259             if (p1->counters.last_updated +
00260                                 (config->checkinterval * config->clienttimeout)
00261                                 <= current_time) {
00262                 /* Timing out user */
00263                 debug(LOG_INFO, "%s - Inactive for more than %ld seconds, removing client and denying in firewall",
00264                         p1->ip, config->checkinterval * config->clienttimeout);
00265                 fw_deny(p1->ip, p1->mac, p1->fw_connection_state);
00266                 client_list_delete(p1);
00267 
00268                 /* Advertise the logout if we have an auth server */
00269                 if (config->auth_servers != NULL) {
00270                                         UNLOCK_CLIENT_LIST();
00271                                         auth_server_request(&authresponse, REQUEST_TYPE_LOGOUT, ip, mac, token, 0, 0);
00272                                         LOCK_CLIENT_LIST();
00273                 }
00274             } else {
00275                 /*
00276                  * This handles any change in
00277                  * the status this allows us
00278                  * to change the status of a
00279                  * user while he's connected
00280                  *
00281                  * Only run if we have an auth server
00282                  * configured!
00283                  */
00284                 if (config->auth_servers != NULL) {
00285                     switch (authresponse.authcode) {
00286                         case AUTH_DENIED:
00287                             debug(LOG_NOTICE, "%s - Denied. Removing client and firewall rules", p1->ip);
00288                             fw_deny(p1->ip, p1->mac, p1->fw_connection_state);
00289                             client_list_delete(p1);
00290                             break;
00291 
00292                         case AUTH_VALIDATION_FAILED:
00293                             debug(LOG_NOTICE, "%s - Validation timeout, now denied. Removing client and firewall rules", p1->ip);
00294                             fw_deny(p1->ip, p1->mac, p1->fw_connection_state);
00295                             client_list_delete(p1);
00296                             break;
00297 
00298                         case AUTH_ALLOWED:
00299                             if (p1->fw_connection_state != FW_MARK_KNOWN) {
00300                                 debug(LOG_INFO, "%s - Access has changed to allowed, refreshing firewall and clearing counters", p1->ip);
00301                                 //WHY did we deny, then allow!?!? benoitg 2007-06-21
00302                                 //fw_deny(p1->ip, p1->mac, p1->fw_connection_state);
00303 
00304                                 if (p1->fw_connection_state != FW_MARK_PROBATION) {
00305      p1->counters.incoming = p1->counters.outgoing = 0;
00306                                 }
00307                                 else {
00308                                         //We don't want to clear counters if the user was in validation, it probably already transmitted data..
00309                                     debug(LOG_INFO, "%s - Skipped clearing counters after all, the user was previously in validation", p1->ip);
00310                                 }
00311                                 p1->fw_connection_state = FW_MARK_KNOWN;
00312                                 fw_allow(p1->ip, p1->mac, p1->fw_connection_state);
00313                             }
00314                             break;
00315 
00316                         case AUTH_VALIDATION:
00317                             /*
00318                              * Do nothing, user
00319                              * is in validation
00320                              * period
00321                              */
00322                             debug(LOG_INFO, "%s - User in validation period", p1->ip);
00323                             break;
00324 
00325                               case AUTH_ERROR:
00326                                     debug(LOG_WARNING, "Error communicating with auth server - leaving %s as-is for now", p1->ip);
00327                                     break;
00328 
00329                         default:
00330                             debug(LOG_ERR, "I do not know about authentication code %d", authresponse.authcode);
00331                             break;
00332                     }
00333                 }
00334             }
00335         }
00336 
00337         free(token);
00338         free(ip);
00339         free(mac);
00340     }
00341     UNLOCK_CLIENT_LIST();
00342 }
00343 
00344 void icmp_ping(char *host) {
00345   struct sockaddr_in saddr;
00346 #ifdef __linux__
00347   struct { 
00348     struct ip ip;
00349     struct icmp icmp;
00350   } packet;
00351 #endif
00352   unsigned int i, j;
00353   int opt = 2000;
00354   unsigned short id = rand16();
00355 
00356   saddr.sin_family = AF_INET;
00357   saddr.sin_port = 0;
00358   inet_aton(host, &saddr.sin_addr);
00359 #ifdef HAVE_SOCKADDR_SA_LEN
00360   saddr.sin_len = sizeof(struct sockaddr_in);
00361 #endif
00362 
00363   memset(&(saddr.sin_zero), '\0', sizeof(saddr.sin_zero));
00364 
00365 #ifdef __linux__
00366   memset(&packet.icmp, 0, sizeof(packet.icmp));
00367   packet.icmp.icmp_type = ICMP_ECHO;
00368   packet.icmp.icmp_id = id;
00369   for (j = 0, i = 0; i < sizeof(struct icmp) / 2; i++)
00370     j += ((unsigned short *)&packet.icmp)[i];
00371   while (j>>16)
00372     j = (j & 0xffff) + (j >> 16);  
00373   packet.icmp.icmp_cksum = (j == 0xffff) ? j : ~j;
00374 
00375   if (setsockopt(icmp_fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) == -1) {
00376       debug(LOG_ERR, "setsockopt(): %s", strerror(errno));
00377   }
00378   if (sendto(icmp_fd, (char *)&packet.icmp, sizeof(struct icmp), 0, (struct sockaddr *)&saddr, sizeof(saddr)) == -1) {
00379       debug(LOG_ERR, "sendto(): %s", strerror(errno));
00380   }
00381   opt = 1;
00382   if (setsockopt(icmp_fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) == -1) {
00383       debug(LOG_ERR, "setsockopt(): %s", strerror(errno));
00384   }
00385 #endif
00386 
00387   return;
00388 }
00389 
00390 unsigned short rand16(void) {
00391   static int been_seeded = 0;
00392 
00393   if (!been_seeded) {
00394     int fd, n = 0;
00395     unsigned int c = 0, seed = 0;
00396     char sbuf[sizeof(seed)];
00397     char *s;
00398     struct timeval now;
00399 
00400     /* not a very good seed but what the heck, it needs to be quickly acquired */
00401     gettimeofday(&now, NULL);
00402     seed = now.tv_sec ^ now.tv_usec ^ (getpid() << 16);
00403 
00404     srand(seed);
00405     been_seeded = 1;
00406     }
00407 
00408     /* Some rand() implementations have less randomness in low bits
00409      * than in high bits, so we only pay attention to the high ones.
00410      * But most implementations don't touch the high bit, so we 
00411      * ignore that one.
00412      **/
00413       return( (unsigned short) (rand() >> 15) );
00414 }

Generated on Thu Oct 18 17:15:19 2007 for WifiDog by  doxygen 1.5.3