/* eurephiafw.c -- Firewall interface loader for the eurephia module * * GPLv2 - Copyright (C) 2008 David Sommerseth * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; version 2 * of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include #include #include #include #include #include #include #include #include #include #include "eurephia_struct.h" #include "eurephia_log.h" #include "eurephiafw.h" #include "eurephiafw_intf.h" #include "eurephia_getsym.h" #include "eurephia_nullsafe.h" #include "eurephia_values.h" #include "eurephiafw_helpers.h" int eFW_unload(eurephiaCTX *ctx) { if( ctx == NULL ) { return 1; } if( ctx->eurephia_fw_intf != NULL ) { eurephia_log(ctx, LOG_INFO, 3, "Unloading eurephia firewall interface"); dlclose(ctx->eurephia_fw_intf); ctx->eurephia_fw_intf = NULL; return 0; } return 1; } int eFW_load(eurephiaCTX *ctx, const char *intf) { if( (intf == NULL) || (strlen(intf) == 0) ) { eurephia_log(ctx, LOG_FATAL, 0, "No valid eurephia firewall interface indicated"); return 0; } eurephia_log(ctx, LOG_INFO, 2, "Loading eurephia firewall interface: %s", intf); ctx->eurephia_fw_intf = dlopen(intf, RTLD_NOW); if( ctx->eurephia_fw_intf == NULL ) { eurephia_log(ctx, LOG_FATAL, 0, "Could not open the eurephia firewall interface (%s)", intf); eurephia_log(ctx, LOG_FATAL, 1, "dlopen error: %s", dlerror()); return 0; } // Mandatory functions eFWinterfaceVersion = eGetSym(ctx, ctx->eurephia_fw_intf, "eFWinterfaceVersion"); eFWinterfaceAPIversion = eGetSym(ctx, ctx->eurephia_fw_intf, "eFWinterfaceAPIversion"); eurephia_log(ctx, LOG_INFO, 1, "Firewall interface loaded: %s (API version %i)", eFWinterfaceVersion(), eFWinterfaceAPIversion()); // Configure firewall interface functions switch( eFWinterfaceAPIversion() ) { default: eurephia_log(ctx, LOG_WARNING, 0, "eurephia Firewall interface API is newer than what the running eurephia version is " "familiar with. Please consider to upgrade eurphia to take advantage of newer " "features in the eurephiaDB driver."); case 1: eFW_RunFirewall = eGetSym(ctx, ctx->eurephia_fw_intf, "eFW_RunFirewall"); break; } if( ctx->fatal_error > 0 ) { eurephia_log(ctx, LOG_FATAL, 0, "eurephia Firewall interface is not correctly initialised. " "eurephia authentication will not be available"); eFW_unload(ctx); return 0; } return 1; } void eFW_StartFirewall(eurephiaCTX *ctx) { struct mq_attr mqattr; eurephiaCTX *shadowctx = NULL; char buf[1026], *fwdest = NULL; unsigned int prio; ctx->fwcfg = (eurephiaFWINTF *) malloc(sizeof(eurephiaFWINTF)+2); memset(ctx->fwcfg, 0, sizeof(eurephiaFWINTF)+2); // Create a fake eurephia context, just for logging shadowctx = (eurephiaCTX *) malloc(sizeof(eurephiaCTX)+2); memset(shadowctx, 0, sizeof(eurephiaCTX)+2); shadowctx->loglevel = ctx->loglevel; shadowctx->log = ctx->log; (*ctx->fwcfg).thrdata.ctx = shadowctx; (*ctx->fwcfg).thrdata.fw_command = strdup_nullsafe(eGet_value(ctx->dbc->config, "firewall_command")); if( (*ctx->fwcfg).thrdata.fw_command == NULL) { eurephia_log(ctx, LOG_PANIC, 0, "Could not find firewall_command in configuration. " "Firewall updates will not be available."); return; } else { eurephia_log(ctx, LOG_INFO, 1, "Using %s to update the firewall rules.", (*ctx->fwcfg).thrdata.fw_command ); } fwdest = eGet_value(ctx->dbc->config, "firewall_destination"); if( fwdest == NULL ) { eurephia_log(ctx, LOG_PANIC, 0, "Could not find firewall_destination in configuration. " "Firewall updates will not be available."); return; } else { eurephia_log(ctx, LOG_INFO, 1, "Using '%s' as firewall rule for VPN accesses", fwdest); } ctx->fwcfg->fwblacklist = eGet_value(ctx->dbc->config, "firewall_blacklist_destination"); if( ctx->fwcfg->fwblacklist != NULL ) { eurephia_log(ctx, LOG_INFO, 1, "Blacklisted IP addresses will also be blocked in '%s'", ctx->fwcfg->fwblacklist); } eurephia_log(ctx, LOG_INFO, 3, "Starting eurephia firewall interface"); // Setup semaphores we need if( efwSetupSemaphores(ctx, &(*ctx->fwcfg).thrdata) == 0 ) { free_nullsafe(ctx->fwcfg->thrdata.fw_command); return; }; // Setup a message queue if( efwSetupMessageQueue(ctx, &(*ctx->fwcfg).thrdata) == 0 ) { free_nullsafe(ctx->fwcfg); return; } // Make sure that these variables are not available in the child madvise(ctx, sizeof(eurephiaCTX), MADV_DONTFORK); // Start a new process (should run with root permissions) - which will do the firewall work if( (ctx->fwcfg->fwproc_pid = fork()) < 0 ) { eurephia_log(ctx, LOG_PANIC, 0, "Could not fork out a child process for the firewall interface (%s)", strerror(errno)); return; } switch( ctx->fwcfg->fwproc_pid ) { case 0: // Child process eFW_RunFirewall(&(*ctx->fwcfg).thrdata); exit(-1); // If our child process exits abnormally. default: // Main process eurephia_log(ctx, LOG_INFO, 2, "Firewall updater process started (pid %i)", ctx->fwcfg->fwproc_pid); } // Flush the message queue for old messages if( mq_getattr((*ctx->fwcfg).thrdata.msgq, &mqattr) == 0 ) { long i; memset(&buf, 0, 1026); if( mqattr.mq_curmsgs > 0 ) { for( i = 0; i < mqattr.mq_curmsgs; i++ ) { if( mq_receive((*ctx->fwcfg).thrdata.msgq, &buf[0], 1024, &prio) == -1 ) { eurephia_log(ctx, LOG_CRITICAL, 0, "Error while emptying messages from queue: %s", strerror(errno)); } else { DEBUG(ctx, 28, "Removed message on queue: %s", buf); } } } eurephia_log(ctx, LOG_INFO, 3, "Message queue for firewall updates is ready"); } else { eurephia_log(ctx, LOG_FATAL, 0, "Could not retrieve message queue attributes (%s)", strerror(errno)); } // Indicate for the FW module that we are ready sem_post(ctx->fwcfg->thrdata.semp_master); // Waiting for the FW module to get ready DEBUG(ctx, 28, "eFW master is ready, waiting for the eFW worker to get ready"); sem_wait(ctx->fwcfg->thrdata.semp_worker); eurephia_log(ctx, LOG_INFO, 2, "eFW interface initialised."); // Initialise the chain memset(&buf, 0, 1026); snprintf(buf, 1024, "I %s", fwdest); if( mq_send((*ctx->fwcfg).thrdata.msgq, buf, strlen(buf)+1, 1) == -1 ) { eurephia_log(ctx, LOG_ERROR, 0, "Could not request firewall initialisation of the %s chain: %s", fwdest, strerror(errno)); }; } void eFW_StopFirewall(eurephiaCTX *ctx) { char buf[520], *fwdest = NULL; int childret = -1; if( ctx->fwcfg == NULL ) { return; } eurephia_log(ctx, LOG_INFO, 2, "Stopping eurephia firewall interface"); // Flush the firewall chain before shutting down, to make sure // we don't unintentionally some accesses open fwdest = eGet_value(ctx->dbc->config, "firewall_destination"); if( fwdest != NULL ) { memset(&buf, 0, 520); snprintf(buf, 512, "F %s", fwdest); if( mq_send((*ctx->fwcfg).thrdata.msgq, buf, strlen(buf)+1, 1) == -1 ) { eurephia_log(ctx, LOG_CRITICAL, 0, "Could not request firewall flushing of the %s chain: %s", fwdest, strerror(errno)); }; } else { eurephia_log(ctx, LOG_CRITICAL, 0, "firewall_destination not set in config. Will not flush " "firewall before shutting down the firewall interface."); } // Send shutdown message to the firewall module process memset(&buf, 0, 520); snprintf(buf, 512, "FWSHUTDOWN%c", 0); if( mq_send((*ctx->fwcfg).thrdata.msgq, buf, 11, 1) == -1 ) { eurephia_log(ctx, LOG_PANIC, 0, "Could not initiate shutdown on eFW module: %s", strerror(errno)); kill(ctx->fwcfg->fwproc_pid, SIGABRT); } // Wait for the firewall module process to finish if( waitpid(ctx->fwcfg->fwproc_pid, &childret, 0) != ctx->fwcfg->fwproc_pid ) { eurephia_log(ctx, LOG_PANIC, 0, "Failed to wait for eFW module process to quit: %s", strerror(errno)); kill(ctx->fwcfg->fwproc_pid, SIGABRT); } free_nullsafe((*ctx->fwcfg).thrdata.fw_command); free_nullsafe(ctx->fwcfg); eurephia_log(ctx, LOG_INFO, 2, "eurephia firewall interface is stopped"); } int eFW_UpdateFirewall(eurephiaCTX *ctx, int mode, const char *addr, const char *fwdest, const char *fwprofile) { char buf[1026]; if( (*ctx->fwcfg).thrdata.fw_command == NULL ) { eurephia_log(ctx, LOG_FATAL, 0, "Function call: eFW_UpdateFirewall() -- " "firewall_command is not configured. Firewall rules was not updated."); return 0; } memset(&buf, 0, 1026); switch( mode ) { case FWRULE_ADD: eurephia_log(ctx, LOG_INFO, 3, "Function call: eFW_UpdateFirewall(ctx, %s, '%s', '%s', '%s')", "ADD", addr, fwdest, fwprofile); snprintf(buf, 1024, "A %s %s %s", addr, fwdest, fwprofile); mq_send((*ctx->fwcfg).thrdata.msgq, buf, strlen(buf)+1, 1); return 1; case FWRULE_DELETE: eurephia_log(ctx, LOG_INFO, 3, "Function call: eFW_UpdateFirewall(ctx, %s, '%s', '%s', '%s')", "DELETE", addr, fwdest, fwprofile); snprintf(buf, 1024, "D %s %s %s", addr, fwdest, fwprofile); mq_send((*ctx->fwcfg).thrdata.msgq, buf, strlen(buf)+1, 1); return 1; case FWRULE_BLACKLIST: eurephia_log(ctx, LOG_INFO, 3, "Function call: eFW_UpdateFirewall(ctx, %s, '%s','%s', NULL)", "BLACKLIST", addr, fwdest); snprintf(buf, 1024, "B %s %s", addr, fwdest); mq_send((*ctx->fwcfg).thrdata.msgq, buf, strlen(buf)+1, 1); return 1; default: eurephia_log(ctx, LOG_CRITICAL, 0, "Function call: eFW_UpdateFirewall(ctx, %s, '%s') - UNKNOWN MODE", "(unknown)", addr); return 0; } }