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

Generated on Sun Apr 13 21:55:00 2008 for WifiDog by  doxygen 1.5.3