eurephiafw.c

Go to the documentation of this file.
00001 /* eurephiafw.c  --  Firewall interface loader for the eurephia module
00002  *
00003  *  GPLv2 only - Copyright (C) 2008 - 2010
00004  *               David Sommerseth <dazo@users.sourceforge.net>
00005  *
00006  *  This program is free software; you can redistribute it and/or
00007  *  modify it under the terms of the GNU General Public License
00008  *  as published by the Free Software Foundation; version 2
00009  *  of the License.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00019  *
00020  */
00021 
00032 #include <stdio.h>
00033 #include <stdlib.h>
00034 #include <string.h>
00035 #include <dlfcn.h>
00036 #include <errno.h>
00037 #include <unistd.h>
00038 #include <sys/mman.h>
00039 #include <sys/wait.h>
00040 #include <time.h>
00041 #include <assert.h>
00042 
00043 #define EUREPHIA_FWINTF
00044 #include <eurephiafw_struct.h>
00045 #include <eurephia_context.h>
00046 #include "eurephia_log.h"
00047 #include "eurephiafw.h"
00048 #include "eurephiafw_intf.h"
00049 #include "eurephia_getsym.h"
00050 #include "eurephia_nullsafe.h"
00051 #include "eurephia_values.h"
00052 #include "eurephiafw_helpers.h"
00053 #include "eurephiadb_driver.h"
00054 
00055 
00063 int eFW_unload(eurephiaCTX *ctx) {
00064         if( ctx == NULL ) {
00065                 return 1;
00066         }
00067 
00068         if( ctx->eurephia_fw_intf != NULL ) {
00069                 eurephia_log(ctx, LOG_INFO, 3, "Unloading eurephia firewall interface");
00070                 dlclose(ctx->eurephia_fw_intf);
00071                 ctx->eurephia_fw_intf = NULL;
00072                 return 0;
00073         }
00074         return 1;
00075 }
00076 
00077 
00086 int eFW_load(eurephiaCTX *ctx, const char *intf) {
00087         if( (intf == NULL) || (strlen(intf) == 0) ) {
00088                 eurephia_log(ctx, LOG_FATAL, 0, "No valid eurephia firewall interface indicated");
00089                 return 0;
00090         }
00091         eurephia_log(ctx, LOG_INFO, 2, "Loading eurephia firewall interface: %s", intf);
00092 
00093         ctx->eurephia_fw_intf = dlopen(intf, RTLD_NOW);
00094         if( ctx->eurephia_fw_intf == NULL ) {
00095                 eurephia_log(ctx, LOG_FATAL, 0, "Could not open the eurephia firewall interface (%s)", intf);
00096                 eurephia_log(ctx, LOG_FATAL, 1, "dlopen error: %s", dlerror());
00097                 return 0;
00098         }
00099 
00100         // Mandatory functions
00101         eFWinterfaceVersion = eGetSym(ctx, ctx->eurephia_fw_intf, "eFWinterfaceVersion");
00102         eFWinterfaceAPIversion = eGetSym(ctx, ctx->eurephia_fw_intf, "eFWinterfaceAPIversion");
00103 
00104         eurephia_log(ctx, LOG_INFO, 1, "Firewall interface loaded: %s (API version %i)",
00105                      eFWinterfaceVersion(), eFWinterfaceAPIversion());
00106 
00107         // Configure firewall interface functions
00108         switch( eFWinterfaceAPIversion() ) {
00109         default:
00110                 eurephia_log(ctx, LOG_WARNING, 0,
00111                              "eurephia Firewall interface API is newer than what the running eurephia version is "
00112                              "familiar with.  Please consider to upgrade eurephia to take advantage of newer "
00113                              "features in the eurephiaDB driver.");
00114 
00115         case 1:
00116                 eFW_RunFirewall = eGetSym(ctx, ctx->eurephia_fw_intf, "eFW_RunFirewall");
00117                 break;
00118 
00119         }
00120 
00121         if( ctx->fatal_error > 0 ) {
00122                 eurephia_log(ctx, LOG_FATAL, 0, "eurephia Firewall interface is not correctly initialised.  "
00123                              "eurephia authentication will not be available");
00124                 eFW_unload(ctx);
00125                 return 0;
00126         }
00127         return 1;
00128 }
00129 
00130 
00137 void eFW_StartFirewall(eurephiaCTX *ctx) {
00138         struct mq_attr mqattr;
00139         eurephiaCTX *shadowctx = NULL;
00140         char buf[1026], *fwdest = NULL;
00141         unsigned int prio;
00142 
00143         ctx->fwcfg = (eurephiaFWINTF *) malloc_nullsafe(ctx, sizeof(eurephiaFWINTF)+2);
00144 
00145         // Create a fake eurephia context, just for logging
00146         shadowctx = (eurephiaCTX *) malloc_nullsafe(ctx, sizeof(eurephiaCTX)+2);
00147         assert( shadowctx != NULL );
00148         if( mlock(shadowctx, sizeof(eurephiaCTX)+2) < 0 ) {
00149                 eurephia_log(ctx, LOG_CRITICAL, 0, "Could not mlock() firewall context: %s",
00150                              strerror(errno));
00151         };
00152         shadowctx->context_type = ECTX_NO_PRIVILEGES;
00153         shadowctx->log = ctx->log;
00154         (*ctx->fwcfg).thrdata.ctx = shadowctx;
00155 
00156         (*ctx->fwcfg).thrdata.fw_command = strdup_nullsafe(eGet_value(ctx->dbc->config, "firewall_command"));
00157         if( (*ctx->fwcfg).thrdata.fw_command == NULL) {
00158                 eurephia_log(ctx, LOG_PANIC, 0, "Could not find firewall_command in configuration.  "
00159                              "Firewall updates will not be available.");
00160                 return;
00161         } else {
00162                 eurephia_log(ctx, LOG_INFO, 1, "Using %s to update the firewall rules.",
00163                              (*ctx->fwcfg).thrdata.fw_command );
00164         }
00165 
00166         fwdest = eGet_value(ctx->dbc->config, "firewall_destination");
00167         if( fwdest == NULL ) {
00168                 eurephia_log(ctx, LOG_PANIC, 0, "Could not find firewall_destination in configuration.  "
00169                              "Firewall updates will not be available.");
00170                 return;
00171         } else {
00172                 eurephia_log(ctx, LOG_INFO, 1, "Using '%s' as firewall rule for VPN accesses", fwdest);
00173         }
00174 
00175         ctx->fwcfg->fwblacklist = eGet_value(ctx->dbc->config, "firewall_blacklist_destination");
00176         if( ctx->fwcfg->fwblacklist != NULL ) {
00177                 eurephia_log(ctx, LOG_INFO, 1,
00178                              "Blacklisted IP addresses will also be blocked in '%s'",
00179                              ctx->fwcfg->fwblacklist);
00180 
00181                 // Create value space for blacklisted IP addresses
00182                 ctx->fwcfg->blacklisted = eCreate_value_space(ctx, 20);
00183 
00184                 // Setup where to send the blacklisted IP addresses - default is to drop them.
00185                 ctx->fwcfg->fwblacklist_sendto = eGet_value(ctx->dbc->config, "firewall_blacklist_send_to");
00186                 if( ctx->fwcfg->fwblacklist_sendto == NULL ) {
00187                         ctx->fwcfg->fwblacklist_sendto = strdup("DROP\0");
00188                         eurephia_log(ctx, LOG_INFO, 2,"Blacklisted IP addresses will be dropped immediately");
00189                 } else {
00190                         eurephia_log(ctx, LOG_INFO, 2,"Blacklisted IP addresses will be sent to '%s'",
00191                                      ctx->fwcfg->fwblacklist_sendto);
00192                 }
00193         }
00194 
00195         eurephia_log(ctx, LOG_INFO, 3, "Starting eurephia firewall interface");
00196 
00197         // Setup semaphores we need
00198         if( efwSetupSemaphores(ctx, &(*ctx->fwcfg).thrdata) == 0 ) {
00199                 free_nullsafe(ctx, ctx->fwcfg->thrdata.fw_command);
00200                 return;
00201         };
00202 
00203         // Setup a message queue
00204         if( efwSetupMessageQueue(ctx, &(*ctx->fwcfg).thrdata) == 0 ) {
00205                 free_nullsafe(ctx, ctx->fwcfg);
00206                 return;
00207         }
00208 
00209         // Make sure that these variables are not available in the child
00210         madvise(ctx, sizeof(eurephiaCTX), MADV_DONTFORK);
00211 
00212         // Start a new process (should run with root permissions) - which will do the firewall work
00213         if( (ctx->fwcfg->fwproc_pid = fork()) < 0 ) {
00214                 eurephia_log(ctx, LOG_PANIC, 0,
00215                              "Could not fork out a child process for the firewall interface (%s)",
00216                              strerror(errno));
00217                 return;
00218         }
00219         switch( ctx->fwcfg->fwproc_pid ) {
00220         case 0: // Child process
00221                 eDBdisconnect(ctx);
00222                 eFW_RunFirewall(&(*ctx->fwcfg).thrdata);
00223                 exit(-1); // If our child process exits abnormally.
00224 
00225         default: // Main process
00226                 eurephia_log(ctx, LOG_INFO, 2, "Firewall updater process started (pid %i)",
00227                              ctx->fwcfg->fwproc_pid);
00228         }
00229 
00230         // Flush the message queue for old messages
00231         if( mq_getattr((*ctx->fwcfg).thrdata.msgq, &mqattr) == 0 ) {
00232                 long i;
00233 
00234                 memset(&buf, 0, 1026);
00235                 if( mqattr.mq_curmsgs > 0 ) {
00236                         for( i = 0; i < mqattr.mq_curmsgs; i++ ) {
00237                                 if( mq_receive((*ctx->fwcfg).thrdata.msgq, &buf[0], 1024, &prio) == -1 ) {
00238                                         eurephia_log(ctx, LOG_CRITICAL, 0,
00239                                                      "Error while emptying messages from queue: %s",
00240                                                      strerror(errno));
00241                                 } else {
00242                                         DEBUG(ctx, 28, "Removed message on queue: %s", buf);
00243                                 }
00244                         }
00245                 }
00246                 eurephia_log(ctx, LOG_INFO, 3, "Message queue for firewall updates is ready");
00247         } else {
00248                 eurephia_log(ctx, LOG_FATAL, 0, "Could not retrieve message queue attributes (%s)",
00249                              strerror(errno));
00250         }
00251 
00252         // Indicate for the FW module that we are ready
00253         sem_post(ctx->fwcfg->thrdata.semp_master);
00254 
00255         // Waiting for the FW module to get ready
00256         DEBUG(ctx, 28, "eFW master is ready, waiting for the eFW worker to get ready");
00257         sem_wait(ctx->fwcfg->thrdata.semp_worker);
00258         eurephia_log(ctx, LOG_INFO, 2, "eFW interface initialised.");
00259 
00260         // Initialise the chain
00261         memset(&buf, 0, 1026);
00262         snprintf(buf, 1024, "I %s", fwdest);
00263         if( mq_send((*ctx->fwcfg).thrdata.msgq, buf, strlen(buf)+1, 1) == -1 ) {
00264                 eurephia_log(ctx, LOG_ERROR, 0, "Could not request firewall initialisation of the %s chain: %s",
00265                              fwdest, strerror(errno));
00266         };
00267 
00268         if( ctx->fwcfg->fwblacklist != NULL ) {
00269                 eurephiaVALUES *blacklisted = NULL, *p = NULL;
00270                 // Flushing firewall blacklist chain
00271                 snprintf(buf, 1024, "F %s", ctx->fwcfg->fwblacklist);
00272                 if( mq_send((*ctx->fwcfg).thrdata.msgq, buf, strlen(buf)+1, 1) == -1 ) {
00273                         eurephia_log(ctx, LOG_ERROR, 0, "Could not request flushing of the %s chain: %s",
00274                                      ctx->fwcfg->fwblacklist, strerror(errno));
00275                 };
00276 
00277                 // Registering already blacklisted IP addresses into the proper firewall chain
00278                 blacklisted = eDBget_blacklisted_ip(ctx);
00279                 p = blacklisted;
00280                 while( p != NULL ) {
00281                         if( p->val != NULL ) {
00282                                 eFW_UpdateFirewall(ctx, FWRULE_BLACKLIST, p->val, ctx->fwcfg->fwblacklist, NULL);
00283                         }
00284                         p = p->next;
00285                 }
00286                 eFree_values(ctx, blacklisted);
00287         }
00288 }
00289 
00290 
00296 void eFW_StopFirewall(eurephiaCTX *ctx) {
00297         char buf[520], *fwdest = NULL;
00298         struct timespec tsp;
00299 
00300         if( ctx->fwcfg == NULL ) {
00301                 return;
00302         }
00303 
00304         eurephia_log(ctx, LOG_INFO, 2, "Stopping eurephia firewall interface");
00305 
00306         // Flush the firewall chain before shutting down, to make sure
00307         // we don't unintentionally some accesses open
00308         fwdest = eGet_value(ctx->dbc->config, "firewall_destination");
00309         if( fwdest != NULL ) {
00310                 memset(&buf, 0, 520);
00311                 snprintf(buf, 512, "F %s", fwdest);
00312                 if( mq_send((*ctx->fwcfg).thrdata.msgq, buf, strlen(buf)+1, 1) == -1 ) {
00313                         eurephia_log(ctx, LOG_CRITICAL, 0,
00314                                      "Could not request firewall flushing of the %s chain: %s",
00315                                      fwdest, strerror(errno));
00316                 };
00317         } else {
00318                 eurephia_log(ctx, LOG_CRITICAL, 0, "firewall_destination not set in config.  Will not flush "
00319                             "firewall before shutting down the firewall interface.");
00320         }
00321 
00322 
00323         // Send shutdown message to the firewall module process
00324         memset(&buf, 0, 520);
00325         snprintf(buf, 512, "FWSHUTDOWN%c", 0);
00326         if( mq_send((*ctx->fwcfg).thrdata.msgq, buf, 11, 1) == -1 ) {
00327                 eurephia_log(ctx, LOG_PANIC, 0, "Could not initiate shutdown on eFW module: %s", strerror(errno));
00328                 kill(ctx->fwcfg->fwproc_pid, SIGABRT);
00329         }
00330 
00331         //
00332         // Wait for the firewall module process to finish
00333         //
00334 
00335         // prepare a timeout - 30 seconder's should be enough
00336         if( clock_gettime(CLOCK_REALTIME, &tsp) == -1 ) {
00337                 eurephia_log(ctx, LOG_FATAL, 0, "Could not prepare timeout for firewall shutdown: %s",
00338                              strerror(errno));
00339                 sleep(3);
00340                 kill(ctx->fwcfg->fwproc_pid, SIGABRT);
00341         }
00342         tsp.tv_sec += 30;
00343 
00344         // Wait for shutdown accepted signal
00345         if( sem_timedwait(ctx->fwcfg->thrdata.semp_worker, &tsp) == -1 ) {
00346                 eurephia_log(ctx, LOG_PANIC, 0, "Failed to wait for eFW module process to quit: %s",
00347                              strerror(errno));
00348                 sleep(3);
00349                 kill(ctx->fwcfg->fwproc_pid, SIGABRT);
00350         }
00351 
00352         // Send acknowledge back
00353         sem_post(ctx->fwcfg->thrdata.semp_master);
00354 
00355         // Clean up and exit
00356         munlock(ctx->fwcfg->thrdata.ctx, sizeof(eurephiaCTX)+2);
00357         free_nullsafe(ctx, ctx->fwcfg->thrdata.ctx);
00358         free_nullsafe(ctx, ctx->fwcfg->fwblacklist_sendto);
00359         eFree_values(ctx, ctx->fwcfg->blacklisted);
00360         free_nullsafe(ctx, (*ctx->fwcfg).thrdata.fw_command);
00361         free_nullsafe(ctx, ctx->fwcfg);
00362         eurephia_log(ctx, LOG_INFO, 2, "eurephia firewall interface is stopped");
00363 }
00364 
00365 
00377 int eFW_UpdateFirewall(eurephiaCTX *ctx, int mode,
00378                        const char *addr, const char *fwdest, const char *fwprofile) {
00379         char buf[1026];
00380         char *blchk = NULL;
00381 
00382         if( (*ctx->fwcfg).thrdata.fw_command == NULL ) {
00383                 eurephia_log(ctx, LOG_FATAL, 0, "Function call: eFW_UpdateFirewall() -- "
00384                              "firewall_command is not configured. Firewall rules was not updated.");
00385                 return 0;
00386         }
00387 
00388         memset(&buf, 0, 1026);
00389         switch( mode ) {
00390         case FWRULE_ADD:
00391                 eurephia_log(ctx, LOG_INFO, 3,
00392                              "Function call: eFW_UpdateFirewall(ctx, %s, '%.18s', '%s', '%s')",
00393                              "ADD", addr, fwdest, fwprofile);
00394                 snprintf(buf, 1024, "A %.18s %s %s", addr, fwdest, fwprofile);
00395                 mq_send((*ctx->fwcfg).thrdata.msgq, buf, strlen(buf)+1, 1);
00396                 return 1;
00397 
00398         case FWRULE_DELETE:
00399                 eurephia_log(ctx, LOG_INFO, 3,
00400                              "Function call: eFW_UpdateFirewall(ctx, %s, '%.18s', '%s', '%s')",
00401                              "DELETE", addr, fwdest, fwprofile);
00402                 snprintf(buf, 1024, "D %.18s %s %s", addr, fwdest, fwprofile);
00403                 mq_send((*ctx->fwcfg).thrdata.msgq, buf, strlen(buf)+1, 1);
00404                 return 1;
00405 
00406         case FWRULE_BLACKLIST:
00407                 eurephia_log(ctx, LOG_INFO, 3,
00408                              "Function call: eFW_UpdateFirewall(ctx, %s, '%.34s','%s', NULL)",
00409                              "BLACKLIST", addr, fwdest);
00410 
00411                 // Check if IP address is already registered as blacklisted
00412                 if( (blchk = eGet_value(ctx->fwcfg->blacklisted, addr)) == NULL ) {
00413                         snprintf(buf, 1024, "B %.34s %s %s", addr, fwdest, ctx->fwcfg->fwblacklist_sendto);
00414                         mq_send((*ctx->fwcfg).thrdata.msgq, buf, strlen(buf)+1, 1);
00415                         eAdd_value(ctx, ctx->fwcfg->blacklisted, addr, fwdest);
00416                 } else {
00417                         eurephia_log(ctx, LOG_INFO, 5, "IP address already blacklisted in '%s'", blchk);
00418                 }
00419                 return 1;
00420 
00421         default:
00422                 eurephia_log(ctx, LOG_CRITICAL, 0,
00423                              "Function call: eFW_UpdateFirewall(ctx, %s, '%s') - UNKNOWN MODE", "(unknown)",
00424                              addr);
00425                 return 0;
00426         }
00427 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines