diff options
Diffstat (limited to 'plugin')
-rw-r--r-- | plugin/CMakeLists.txt | 43 | ||||
-rw-r--r-- | plugin/eurephia-auth.c | 179 | ||||
-rw-r--r-- | plugin/eurephia.c | 527 | ||||
-rw-r--r-- | plugin/eurephia.h | 36 | ||||
-rw-r--r-- | plugin/eurephia_struct.h | 98 | ||||
-rw-r--r-- | plugin/eurephiadb_session.c | 352 | ||||
-rw-r--r-- | plugin/eurephiadb_session.h | 44 | ||||
-rw-r--r-- | plugin/firewall/eurephiafw.c | 288 | ||||
-rw-r--r-- | plugin/firewall/eurephiafw.h | 34 | ||||
-rw-r--r-- | plugin/firewall/eurephiafw_helpers.c | 97 | ||||
-rw-r--r-- | plugin/firewall/eurephiafw_helpers.h | 36 | ||||
-rw-r--r-- | plugin/firewall/eurephiafw_intf.h | 30 | ||||
-rw-r--r-- | plugin/firewall/iptables/CMakeLists.txt | 12 | ||||
-rw-r--r-- | plugin/firewall/iptables/efw_iptables.c | 234 |
14 files changed, 2010 insertions, 0 deletions
diff --git a/plugin/CMakeLists.txt b/plugin/CMakeLists.txt new file mode 100644 index 0000000..d2b922f --- /dev/null +++ b/plugin/CMakeLists.txt @@ -0,0 +1,43 @@ +PROJECT(eurephia-auth C) +cmake_minimum_required(VERSION 2.6) +SET(eurephia_auth_SRC + ../common/certinfo.c + eurephia-auth.c + eurephia.c + ../database/eurephiadb.c + eurephiadb_session.c + firewall/eurephiafw.c + firewall/eurephiafw_helpers.c + ../common/eurephia_getsym.c + ../common/eurephia_log.c + ../common/eurephia_values.c + ../common/passwd.c + ../common/sha512.c +) +SET(subdirs "") +IF(FW_IPTABLES) + message(STATUS "Will build iptables firewall module") + SET(subdirs firewall/iptables) +ENDIF(FW_IPTABLES) + +CHECK_LIBRARY_EXISTS(rt sem_wait "" HAVE_RT_SEM_WAIT) +CHECK_LIBRARY_EXISTS(rt sem_post "" HAVE_RT_SEM_POST) +CHECK_LIBRARY_EXISTS(rt mq_open "" HAVE_RT_MQ_OPEN) +CHECK_LIBRARY_EXISTS(rt mq_close "" HAVE_RT_MQ_CLOSE) +CHECK_LIBRARY_EXISTS(rt mq_unlink "" HAVE_RT_MQ_UNLINK) +CHECK_LIBRARY_EXISTS(rt mq_send "" HAVE_RT_MQ_SEND) +CHECK_LIBRARY_EXISTS(rt mq_receive "" HAVE_RT_MQ_RECEIVE) +CHECK_LIBRARY_EXISTS(rt mq_getattr "" HAVE_RT_MQ_GETATTR) +IF(NOT HAVE_RT_SEM_WAIT OR NOT HAVE_RT_SEM_POST) + message(FATAL_ERROR "Missing proper pthread semaphore support") +ENDIF(NOT HAVE_RT_SEM_WAIT OR NOT HAVE_RT_SEM_POST) +IF(NOT HAVE_RT_MQ_OPEN OR NOT HAVE_RT_MQ_CLOSE OR NOT HAVE_RT_MQ_UNLINK OR NOT HAVE_RT_MQ_SEND OR NOT HAVE_RT_MQ_RECEIVE OR NOT HAVE_RT_MQ_GETATTR) + message(FATAL_ERROR "Missing proper pthread message queue support") +ENDIF(NOT HAVE_RT_MQ_OPEN OR NOT HAVE_RT_MQ_CLOSE OR NOT HAVE_RT_MQ_UNLINK OR NOT HAVE_RT_MQ_SEND OR NOT HAVE_RT_MQ_RECEIVE OR NOT HAVE_RT_MQ_GETATTR) + +INCLUDE_DIRECTORIES(../common ../database ./firewall .) +ADD_LIBRARY(eurephia-auth MODULE ${eurephia_auth_SRC}) +TARGET_LINK_LIBRARIES(eurephia-auth dl pthread rt crypto) +SET_TARGET_PROPERTIES(eurephia-auth PROPERTIES OUTPUT_NAME eurephia-auth PREFIX "") + +SUBDIRS(${subdirs}) diff --git a/plugin/eurephia-auth.c b/plugin/eurephia-auth.c new file mode 100644 index 0000000..98658c3 --- /dev/null +++ b/plugin/eurephia-auth.c @@ -0,0 +1,179 @@ +/* eurephia-auth.c -- Main OpenVPN plugin functions. The API level between OpenVPN and eurephia-auth + * + * GPLv2 - Copyright (C) 2008 David Sommerseth <dazo@users.sourceforge.net> + * + * 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 <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <sqlite3.h> + +#include "openvpn-plugin.h" +#include <eurephiadb.h> +#include <eurephia.h> + + +#ifdef ENABLE_DEBUG // To avoid compiler warnings when ENABLE_DEBUG is not defined +static const char *plugin_type_name(const int type) +{ + switch (type) + { + case OPENVPN_PLUGIN_UP: + return "PLUGIN_UP"; + case OPENVPN_PLUGIN_DOWN: + return "PLUGIN_DOWN"; + case OPENVPN_PLUGIN_ROUTE_UP: + return "PLUGIN_ROUTE_UP"; + case OPENVPN_PLUGIN_IPCHANGE: + return "PLUGIN_IPCHANGE"; + case OPENVPN_PLUGIN_TLS_VERIFY: + return "PLUGIN_TLS_VERIFY"; + case OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY: + return "PLUGIN_AUTH_USER_PASS_VERIFY"; + case OPENVPN_PLUGIN_CLIENT_CONNECT: + return "PLUGIN_CLIENT_CONNECT"; + case OPENVPN_PLUGIN_CLIENT_DISCONNECT: + return "PLUGIN_CLIENT_DISCONNECT"; + case OPENVPN_PLUGIN_LEARN_ADDRESS: + return "PLUGIN_LEARN_ADDRESS"; + default: + return "(UNKNOWN PLUGIN CODE)"; + } +} + +static void dump_env(FILE *f, const char *prefix, const char *envp[]) { + int i; + for (i = 0; envp[i]; i++) { +#ifdef SHOW_SECRETS + fprintf(f, "%s%s\n", prefix, envp[i]); +#else + fprintf(f, "%s%s\n", prefix , + (strncmp(envp[i], "password=", 9) == 0) ? "password=xxxxxxx" : envp[i]); +#endif // SHOW_SECRETS + } +} +#endif // ENABLE_DEBUG + + +// daemonize if "daemon" environment variable is set. +// preserves stderr access after being daemonized, but +// only if "daemon_log_direct" enviroment variable is set. +// +static void daemonize(const char *envp[]) +{ + const char *daemon_string = get_env(NULL, 0, envp, "daemon"); + if( daemon_string && daemon_string[0] == '1' ) { + const char *log_redirect = get_env(NULL, 0, envp, "daemon_log_redirect"); + int fd = -1; + if( log_redirect && log_redirect[0] == '1' ) { + fd = dup (2); + } + if( daemon(0, 0) < 0 ) { + fprintf(stderr, "eurephia-auth: daemonization failed\n"); + } else if( fd >= 3 ) { + dup2(fd, 2); + close(fd); + } + } +} + + +OPENVPN_EXPORT openvpn_plugin_handle_t openvpn_plugin_open_v1(unsigned int *type_mask, + const char *argv[], const char *envp[]) +{ + eurephiaCTX *context = NULL; + +#ifdef MEMWATCH + mwStatistics(3); +#warning MEMWATCH enabled +#endif + + // Define what will trigger eurephia + *type_mask = OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY) + | OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_TLS_VERIFY) + | OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_CLIENT_CONNECT) + | OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_CLIENT_DISCONNECT) + | OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_LEARN_ADDRESS); + + // Setup a eurephia context + context = eurephiaInit(argv); + // Daemonize if requested + daemonize(envp); + + return (openvpn_plugin_handle_t) context; +} + + +OPENVPN_EXPORT int openvpn_plugin_func_v1(openvpn_plugin_handle_t handle, + const int type, + const char *argv[], const char *envp[]) +{ + eurephiaCTX *ctx = (eurephiaCTX *) handle; + int result = 0; + + + if( (ctx == NULL) || (ctx->dbc == NULL) || (ctx->dbc->dbhandle == NULL) ) { + return OPENVPN_PLUGIN_FUNC_ERROR; + } + + DEBUG(ctx, 10, "openvpn_plugin_func_v1(ctx, %s, ...)", plugin_type_name(type)); + +#ifdef ENABLE_DEBUG + if( ctx->loglevel >= 30 ) { + dump_env(ctx->log, "ENV: ", envp); + dump_env(ctx->log, "ARG: ", argv); + } +#endif + + switch( type ) { + case OPENVPN_PLUGIN_TLS_VERIFY: + result = eurephia_tlsverify(ctx, envp, argv[1]); + break; + + case OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY: + result = eurephia_userauth(ctx, envp); + break; + + case OPENVPN_PLUGIN_CLIENT_CONNECT: + result = eurephia_connect(ctx, envp); + break; + + case OPENVPN_PLUGIN_CLIENT_DISCONNECT: + result = eurephia_disconnect(ctx, envp); + break; + + case OPENVPN_PLUGIN_LEARN_ADDRESS: + result = eurephia_learn_address(ctx, argv[1], argv[2], envp); + break; + + default: + eurephia_log(ctx, LOG_FATAL, 0, "Unknown OPENVPN_PLUGIN type: %i", type); + break; + } + return (result == 1 ? OPENVPN_PLUGIN_FUNC_SUCCESS : OPENVPN_PLUGIN_FUNC_ERROR); +} + + +OPENVPN_EXPORT void openvpn_plugin_close_v1(openvpn_plugin_handle_t handle) +{ + eurephiaCTX *ctx = (eurephiaCTX *) handle; + + eurephiaShutdown(ctx); +} + diff --git a/plugin/eurephia.c b/plugin/eurephia.c new file mode 100644 index 0000000..7454b1f --- /dev/null +++ b/plugin/eurephia.c @@ -0,0 +1,527 @@ +/* eurephia.c -- Main functions for the eurephia authentication module + * + * GPLv2 - Copyright (C) 2008 David Sommerseth <dazo@users.sourceforge.net> + * + * 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 <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> + +#include <eurephiadb.h> +#include <eurephiadb_driver.h> +#include <eurephiafw.h> +#include <eurephia_nullsafe.h> +#include <eurephia_values.h> +#include <certinfo.h> + +#define MAX_ARGUMENTS 64 + + +// Get value of a environment variable +const char *get_env(eurephiaCTX *ctx, int logmasking, const char *envp[], const char *fmt, ... ) +{ + if (envp) { + va_list ap; + char key[384]; + int keylen = 0; + int i; + + // Build up the key we are looking for + memset(&key, 0, 384); + va_start(ap, fmt); + vsnprintf(key, 382, fmt, ap); + + // Find the key + keylen = strlen (key); + for (i = 0; envp[i]; ++i) { + if (!strncmp (envp[i], key, keylen)) { + const char *cp = envp[i] + keylen; + if (*cp == '=') { +#ifdef ENABLE_DEBUG + int do_mask = 0; +#ifndef SHOW_SECRETS + do_mask = logmasking; +#endif + if( ctx != NULL ) { + DEBUG(ctx, 30, "Function call: get_env(envp, '%s') == '%s'", + key, (do_mask == 0 ? cp + 1 : "xxxxxxxxxxxxxx")); + } +#endif + + return cp + 1; + } + } + } + if( ctx != NULL ) { + DEBUG(ctx, 15, "Function call: get_env(envp, '%s') -- environment variable not found", + key); + } + va_end(ap); + } + return NULL; +} + + +// arguments: logfile loglevel eDB_driver [eurephiaDB arguments] +// 1 2 3 4..... +eurephiaCTX *eurephiaInit(const char **argv) +{ + static struct option eurephia_opts[] = { + {"log-destination", required_argument, 0, 'l'}, + {"log-level", required_argument, 0, 'L'}, + {"database-interface", required_argument, 0, 'i'}, + {0, 0, 0 ,0} + }; + int argc = 0, error = 0, loglvl = 0, dbargc = 0; + const char *dbargv[MAX_ARGUMENTS]; + const char *fwintf = NULL, *logfile = NULL, *dbi = NULL; + eurephiaCTX *ctx = NULL; + + // + // Parse input arguments + // + + // Count arguments + for( argc = 0; argv[argc] != NULL; argc++ ) {} + + while(1) { + int opt_idx = 0; + int c = 0; + + c = getopt_long(argc, (char **)argv, "l:L:i:", eurephia_opts, &opt_idx); + if( c == -1 ) { + break; + } + + switch( c ) { + case 'l': + logfile = optarg; + break; + + case 'L': + loglvl = atoi_nullsafe(optarg); + break; + + case 'i': + dbi = optarg; + break; + + default: + fprintf(stderr, "Error parsing eurephia-auth arguments.\n"); + return NULL; + break; + } + } + + // Put the rest of the arguments into an own array which will be the db module arguments + if( optind < argc ) { + // copy arguments, but make sure we do not exceed our limit + while( (optind < argc) && (dbargc < MAX_ARGUMENTS) ) { + dbargv[dbargc] = argv[optind++]; + dbargc++; + dbargv[dbargc] = NULL; + } + } + // End of argument parsing + + // Prepare a context area for eurephia-auth + ctx = (eurephiaCTX *) malloc(sizeof(eurephiaCTX)+2); + memset(ctx, 0, sizeof(eurephiaCTX)+2); + + // Open a log file + if( logfile != NULL ) { + if( strcmp(logfile, "openvpn:") == 0 ) { // Let openvpn do the logging + ctx->log = stderr; + } else if( strcmp(logfile, "none:") == 0 ) { // Do not perform any logging + ctx->log = NULL; + } else { // if no hit on these ones,open a file with given name + ctx->log = fopen(logfile, "aw"); + if( ctx->log == NULL ) { + fprintf(stderr, "Could not open eurephia log file: %s\n", argv[1]); + return NULL; + } + } + } else { + // If no logging is given ... log to openvpn: + ctx->log = stderr; + } + + // Set log verbosity + ctx->loglevel = loglvl; + + // Load the database driver + if( (error == 0) && eDBlink_init(ctx, dbi) ) { + // Connect to the database + if( !eDBconnect(ctx, dbargc, dbargv) ) { + eurephia_log(ctx, LOG_PANIC, 0, "Could not connect to the database"); + error = 1; + eDBlink_close(ctx); + } + } else { + eurephia_log(ctx, LOG_PANIC, 0, "Could not load the database driver"); + error = 1; + } + + if( error > 0 ) { + if( ctx->log != NULL ) { + eurephia_log(ctx, LOG_PANIC, 0, "eurephia-auth is not available"); + fclose(ctx->log); + } + free_nullsafe(ctx); + return NULL; + } + + // Check if firewall functionality is enabled, load module if given + fwintf = eGet_value(ctx->dbc->config, "firewall_interface"); + if( fwintf != NULL ) { + if( eFW_load(ctx, fwintf) ) { + eurephia_log(ctx, LOG_INFO, 0, "Loaded firewall interface: %s", fwintf); + eFW_StartFirewall(ctx); + } else { + eurephia_log(ctx, LOG_FATAL, 0, "Loading of firewall interface failed (%s)", fwintf); + ctx->eurephia_fw_intf = NULL; + } + } else { + ctx->eurephia_fw_intf = NULL; + } + + eurephia_log(ctx, LOG_INFO, 1, "eurehia-auth is initialised"); + return ctx; +} + + +int eurephiaShutdown(eurephiaCTX *ctx) +{ + if( ctx == NULL ) { + return 0; + } + + if( ctx->eurephia_fw_intf != NULL ) { + if( ctx->fwcfg != NULL ) { + eFW_StopFirewall(ctx); + } + eFW_unload(ctx); + } + + if( (ctx->dbc != NULL) && (ctx->dbc->dbhandle != NULL) ) { + eDBdisconnect(ctx); + } + + if( ctx->eurephia_driver != NULL ) { + eDBlink_close(ctx); + } + + if( ctx->log != NULL ) { + fflush(ctx->log); + + // Do not close log file if we're on stdout or stderr + if( (ctx->log != stderr) && (ctx->log != stdout) ) { + eurephia_log(ctx, LOG_INFO, 2, "Closing log file"); + fclose(ctx->log); + } + + ctx->log = NULL; + ctx->loglevel = 0; + } + free_nullsafe(ctx); + return 1; +} + + +int eurephia_tlsverify(eurephiaCTX *ctx, const char **env, const char *depth) +{ + int result = 0; + char *ipaddr; + char *tls_digest, *tls_id; + certinfo *ci = NULL; + + DEBUG(ctx, 10, "** Function call: eurephia_tlsverify(...)"); + + // Check if IP address is blacklisted + ipaddr = (char *) get_env(ctx, 0, env, "untrusted_ip"); + if( eDBblacklist_check(ctx, attempt_IPADDR, ipaddr) == 1 ) { + eDBregister_attempt(ctx, attempt_IPADDR, ATTEMPT_REGISTER, ipaddr); + return 0; + } + + // Check if certificate digest is blacklisted + tls_digest = (char *) get_env(ctx, 0, env, "tls_digest_%s", depth); + if( eDBblacklist_check(ctx, attempt_CERTIFICATE, tls_digest) == 1 ) { + eDBregister_attempt(ctx, attempt_IPADDR, ATTEMPT_REGISTER, ipaddr); + eDBregister_attempt(ctx, attempt_CERTIFICATE, ATTEMPT_REGISTER, tls_digest); + return 0; + } + + // Check if certificate is registered and allowed + tls_id = (char *) get_env(ctx, 0, env, "tls_id_%s", depth); + ci = parse_tlsid(tls_id); + result = eDBauth_TLS(ctx, ci->org, ci->common_name, ci->email, tls_digest, depth); + if( result < 1 ) { + eDBregister_attempt(ctx, attempt_IPADDR, ATTEMPT_REGISTER, ipaddr); + eDBregister_attempt(ctx, attempt_CERTIFICATE, ATTEMPT_REGISTER, tls_digest); + } + + if( result > 0 ) { + // Certificate is okay, result contains the certificate ID + eurephia_log(ctx, LOG_INFO, (depth == 0 ? 0 : 1), + "Found certid %i for user: %s/%s/%s", + result, ci->org, ci->common_name, ci->email); + + // Reset attempt counter for certificate if it is okey + eDBregister_attempt(ctx, attempt_CERTIFICATE, ATTEMPT_RESET, tls_digest); + } else { + eurephia_log(ctx, LOG_WARNING, 0, + "Unknown certificate for: %s/%s/%s (depth %s, digest: %s)", + ci->org, ci->common_name, ci->email, depth, tls_digest); + } + free_certinfo(ci); + DEBUG(ctx, 10, "** Function result: eurephia_tlsverify(...) == %i", result > 0); + + return (result > 0); +} + + +int eurephia_userauth(eurephiaCTX *ctx, const char **env) +{ + int result = 0, certid = 0; + char *ipaddr; + char *tls_digest, *tls_id, *username, *passwd; + certinfo *ci = NULL; + + DEBUG(ctx, 10, "** Function call: eurephia_userauth(...)"); + + + // Check if IP address is blacklisted + ipaddr = (char *) get_env(ctx, 0, env, "untrusted_ip"); + if( eDBblacklist_check(ctx, attempt_IPADDR, ipaddr) == 1 ) { + eDBregister_attempt(ctx, attempt_IPADDR, ATTEMPT_REGISTER, ipaddr); + return 0; + } + + // Check if certificate digest is blacklisted + tls_digest = (char *) get_env(ctx, 0, env, "tls_digest_0"); + if( eDBblacklist_check(ctx, attempt_CERTIFICATE, tls_digest) == 1 ) { + eDBregister_attempt(ctx, attempt_IPADDR, ATTEMPT_REGISTER, ipaddr); + eDBregister_attempt(ctx, attempt_CERTIFICATE, ATTEMPT_REGISTER, tls_digest); + return 0; + } + + // Check if username is blacklisted + username = (char *) get_env(ctx, 0, env, "username"); + if( eDBblacklist_check(ctx, attempt_USERNAME, username) == 1 ) { + eDBregister_attempt(ctx, attempt_IPADDR, ATTEMPT_REGISTER, ipaddr); + eDBregister_attempt(ctx, attempt_CERTIFICATE, ATTEMPT_REGISTER, tls_digest); + eDBregister_attempt(ctx, attempt_USERNAME, ATTEMPT_REGISTER, username); + return 0; + } + + // Get certificate ID + tls_id = (char *) get_env(ctx, 0, env, "tls_id_0"); + ci = parse_tlsid(tls_id); + certid = eDBauth_TLS(ctx, ci->org, ci->common_name, ci->email, tls_digest, "0"); + if( certid < 1 ) { + eDBregister_attempt(ctx, attempt_IPADDR, ATTEMPT_REGISTER, ipaddr); + eDBregister_attempt(ctx, attempt_CERTIFICATE, ATTEMPT_REGISTER, tls_digest); + eDBregister_attempt(ctx, attempt_USERNAME, ATTEMPT_REGISTER, username); + free_certinfo(ci); + return 0; + } + free_certinfo(ci); + + // Do username/password/certificate authentication + passwd = (char *)get_env(ctx, 1, env, "password"); + result = eDBauth_user(ctx, certid, username, passwd); + if( result < 1 ) { + eDBregister_attempt(ctx, attempt_IPADDR, ATTEMPT_REGISTER, ipaddr); + eDBregister_attempt(ctx, attempt_CERTIFICATE, ATTEMPT_REGISTER, tls_digest); + eDBregister_attempt(ctx, attempt_USERNAME, ATTEMPT_REGISTER, username); + } + + + if( result > 0 ) { + // If we have a valid result, reset all attempt counters. + eDBregister_attempt(ctx, attempt_IPADDR, ATTEMPT_RESET, ipaddr); + eDBregister_attempt(ctx, attempt_CERTIFICATE, ATTEMPT_RESET, tls_digest); + eDBregister_attempt(ctx, attempt_USERNAME, ATTEMPT_RESET, username); + + eurephia_log(ctx, LOG_INFO, 0, "User '%s' authenticated", username); + } + DEBUG(ctx, 10, "** Function result: eurephia_userauth(...) = %i", (result>0)); + return (result > 0); +} + + +int eurephia_connect(eurephiaCTX *ctx, const char **env) { + eurephiaSESSION *session = NULL; + const char *digest, *cname, *uname, *vpnipaddr, *vpnipmask, *remipaddr, *remport, *proto, *tlsid; + int certid = 0, uid = 0, ret = 0; + certinfo *ci = NULL; + + DEBUG(ctx, 10, "** Function call: eurephia_connect(...)"); + + // Fetch needed info + digest = get_env(ctx, 0, env, "tls_digest_0"); + tlsid = get_env(ctx, 0, env, "tls_id_0"); + cname = get_env(ctx, 0, env, "common_name"); + uname = get_env(ctx, 0, env, "username"); + vpnipaddr = get_env(ctx, 0, env, "ifconfig_pool_remote_ip"); + vpnipmask = get_env(ctx, 0, env, "ifconfig_pool_netmask"); + remipaddr = get_env(ctx, 0, env, "trusted_ip"); + remport = get_env(ctx, 0, env, "trusted_port"); + proto = get_env(ctx, 0, env, "proto_1"); + + // Get a session ticket + session = eDBopen_session_seed(ctx, digest, cname, uname, vpnipaddr, vpnipmask, remipaddr, remport); + if( session == NULL ) { + return 0; + } + + // Get certificate info + ci = parse_tlsid(tlsid); + if( ci == NULL ) { + eurephia_log(ctx, LOG_FATAL, 1, "Could not parse the TLS ID string"); + eDBfree_session(ctx, session); + return 0; + } + certid = eDBauth_TLS(ctx, ci->org, ci->common_name, ci->email, digest, "0"); + uid = eDBget_uid(ctx, certid, uname); + free_certinfo(ci); + + // Register the session login + ret = eDBregister_login(ctx, session, certid, uid, proto, remipaddr, remport, vpnipaddr, vpnipmask); + eDBfree_session(ctx, session); + + DEBUG(ctx, 10, "** Function result: eurephia_connect(...) = %i", ret); + return ret; +} + + +int eurephia_disconnect(eurephiaCTX *ctx, const char **env) { + eurephiaSESSION *session = NULL; + const char *digest, *cname, *uname, *vpnipaddr, *vpnipmask, *remipaddr, *remport; + const char *bytes_sent, *bytes_rec, *duration; + int ret = 0; + + DEBUG(ctx, 10, "** Function call: eurephia_disconnect(...)"); + + // Fetch needed info + digest = get_env(ctx, 0, env, "tls_digest_0"); + cname = get_env(ctx, 0, env, "common_name"); + uname = get_env(ctx, 0, env, "username"); + vpnipaddr = get_env(ctx, 0, env, "ifconfig_pool_remote_ip"); + vpnipmask = get_env(ctx, 0, env, "ifconfig_pool_netmask"); + remipaddr = get_env(ctx, 0, env, "trusted_ip"); + remport = get_env(ctx, 0, env, "trusted_port"); + bytes_sent= get_env(ctx, 0, env, "bytes_sent"); + bytes_rec = get_env(ctx, 0, env, "bytes_received"); + duration = get_env(ctx, 0, env, "time_duration"); + + // Get a session ticket + session = eDBopen_session_seed(ctx, digest, cname, uname, vpnipaddr, vpnipmask, remipaddr, remport); + if( session == NULL ) { + return 0; + } + + // 2. eDBregister_logout(ctx, session, env[bytes_sent], env[bytes_received]) + ret = eDBregister_logout(ctx, session, bytes_sent, bytes_rec, duration); + eDBfree_session(ctx, session); + + DEBUG(ctx, 10, "** Function result: eurephia_disconnect(...) = %i", ret); + return ret; +} + + +int eurephia_learn_address(eurephiaCTX *ctx, const char *mode, const char *macaddr, const char **env) { + eurephiaSESSION *session = NULL; + const char *digest, *cname, *uname, *vpnipaddr, *vpnipmask, *remipaddr, *remport; + char *fwprofile = NULL, *fwdest = NULL; + int ret = 0, fw_enabled = 0; + + DEBUG(ctx, 10, "** Function call: eurephia_learn_address(ctx, '%s', '%s', ...)", + mode, macaddr); + + // Get firewall information + fw_enabled = (eGet_value(ctx->dbc->config, "firewall_interface") != NULL); + fwdest = eGet_value(ctx->dbc->config, "firewall_destination"); + if( fw_enabled && (fwdest == NULL) ) { + eurephia_log(ctx, LOG_CRITICAL, 0, "No firewall destination defined in the config."); + } + + if( strncmp(mode, "add", 3) == 0 ) { + // Fetch needed info + digest = get_env(ctx, 0, env, "tls_digest_0"); + cname = get_env(ctx, 0, env, "common_name"); + uname = get_env(ctx, 0, env, "username"); + vpnipaddr = get_env(ctx, 0, env, "ifconfig_pool_remote_ip"); + vpnipmask = get_env(ctx, 0, env, "ifconfig_pool_netmask"); + remipaddr = get_env(ctx, 0, env, "trusted_ip"); + remport = get_env(ctx, 0, env, "trusted_port"); + + // Get a session ticket + session = eDBopen_session_seed(ctx, digest, cname, uname, vpnipaddr, vpnipmask, + remipaddr, remport); + if( session == NULL ) { + ret = 0; + goto exit; + } + + // Update openvpn_lastlog with the active MAC address, and save it as a session variable + ret = eDBregister_vpnmacaddr(ctx, session, macaddr); + + if( (fw_enabled) && (fwdest != NULL) ) { + // 1. Lookup firewall profile for user: eDBget_firewall_profile(ctx, session) + fwprofile = eDBget_firewall_profile(ctx, session); + // 2. Update firewall with eurephia_firewall(ctx, FWRULE_ADD, profileid) + if( fwprofile != NULL ) { + eFW_UpdateFirewall(ctx, FWRULE_ADD, macaddr, fwdest, fwprofile); + free_nullsafe(fwprofile); + } + } + eDBfree_session(ctx, session); + + } else if( strncmp(mode, "delete", 6) == 0 ) { + + // Load the session, based on MAC address + session = eDBopen_session_macaddr(ctx, macaddr); + if( session == NULL ) { + eurephia_log(ctx, LOG_WARNING, 0, + "Could not find any session connected to this MAC address: %s", + macaddr); + ret = 0; + goto exit; + } + + if( (fw_enabled) && (fwdest != NULL) ) { + fwprofile = eDBget_firewall_profile(ctx, session); + if( fwprofile != NULL ) { + // 1. Update firewall with eurephia_firewall(ctx, FWRULE_DELETE, macaddr) + eFW_UpdateFirewall(ctx, FWRULE_DELETE, macaddr, fwdest, fwprofile); + free_nullsafe(fwprofile); + } + } + ret = eDBdestroy_session(ctx, session); + } + + exit: + DEBUG(ctx, 10, "** Function result: eurephia_learn_address(ctx, '%s', '%s', ...) = %i", + mode, macaddr, ret); + + return ret; +} + diff --git a/plugin/eurephia.h b/plugin/eurephia.h new file mode 100644 index 0000000..5e2dfb9 --- /dev/null +++ b/plugin/eurephia.h @@ -0,0 +1,36 @@ +/* eurephia.h -- Main API for the eurephia authentication module + * + * GPLv2 - Copyright (C) 2008 David Sommerseth <dazo@users.sourceforge.net> + * + * 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. + * + */ + +#ifndef EUREPHIA_H_ +#define EUREPHIA_H_ + +#include <stdarg.h> + +const char *get_env(eurephiaCTX *ctx, const int mask, const char **envp, const char *fmt, ...); +eurephiaCTX *eurephiaInit(const char **argv); +int eurephiaShutdown(eurephiaCTX *ctx); + +int eurephia_tlsverify(eurephiaCTX *ctx, const char **argv, const char *depth); +int eurephia_userauth(eurephiaCTX *ctx, const char **env); +int eurephia_connect(eurephiaCTX *ctx, const char **env); +int eurephia_disconnect(eurephiaCTX *ctx, const char **env); +int eurephia_learn_address(eurephiaCTX *ctx, const char *mode, const char *macaddr, const char **env); + +#endif /* !EUREPHIA_H_ */ diff --git a/plugin/eurephia_struct.h b/plugin/eurephia_struct.h new file mode 100644 index 0000000..cdb8403 --- /dev/null +++ b/plugin/eurephia_struct.h @@ -0,0 +1,98 @@ +/* eurephia_struct.h -- Structures and defines used by the eurephia module + * + * GPLv2 - Copyright (C) 2008 David Sommerseth <dazo@users.sourceforge.net> + * + * 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. + * +*/ + + +/*** Structures used by the eurphia module ***/ + +#include <mqueue.h> +#include <semaphore.h> + +#ifndef EUREPHIASTRUCT_H_ +#define EUREPHIASTRUCT_H_ + +#define SESSION_NEW 1 +#define SESSION_EXISTING 2 +#define SESSION_REGISTERED 3 +#define SESSION_LOGGEDOUT 4 + +typedef struct __eurephiaVALUES { + unsigned int evgid; + unsigned int evid; + char *key; + char *val; + struct __eurephiaVALUES *next; +} eurephiaVALUES; + + +// +// Struct which contains session unique data +// +typedef struct { + char *sessionkey; + int sessionstatus; + eurephiaVALUES *sessvals; +} eurephiaSESSION; + + +// +// Struct containing connection to the database we are using +// +typedef struct { + void *dbhandle; + char *dbname; + eurephiaVALUES *config; +} eDBconn; + + + +// +// structs used by the firewall interface +// +typedef struct { + mqd_t msgq; + sem_t *semp_worker; + sem_t *semp_master; + char *fw_command; + void *ctx; +} efw_threaddata; + +typedef struct { + efw_threaddata thrdata; + pid_t fwproc_pid; +} eurephiaFWINTF; + + +// +// main structure for the eurephia module context +// - the same context structure is used for all OpenVPN sessions +// +typedef struct { + void *eurephia_driver; + void *eurephia_fw_intf; + eDBconn *dbc; + eurephiaFWINTF *fwcfg; + FILE *log; + int loglevel; + int fatal_error; +} eurephiaCTX; + + + +#endif diff --git a/plugin/eurephiadb_session.c b/plugin/eurephiadb_session.c new file mode 100644 index 0000000..cb314ee --- /dev/null +++ b/plugin/eurephiadb_session.c @@ -0,0 +1,352 @@ +/* eurephiadb_session.c -- Global API for handling eurephia sessions + * + * GPLv2 - Copyright (C) 2008 David Sommerseth <dazo@users.sourceforge.net> + * + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/time.h> +#include <time.h> +#include <openssl/rand.h> + +#include "eurephia_struct.h" +#include "eurephia_nullsafe.h" +#include "eurephia_log.h" +#include "eurephiadb_session.h" +#include "eurephia_values.h" +#include "sha512.h" + + +// Also defined in the eurephiadb_driver.h, but not as extern. +extern char *(*eDBget_sessionkey_seed) (eurephiaCTX *ctx, const char *sessionseed); +extern char *(*eDBget_sessionkey_macaddr) (eurephiaCTX *ctx, const char *macaddr); + +extern int (*eDBcheck_sessionkey_uniqueness) (eurephiaCTX *ctx, const char *seskey); + +extern int (*eDBregister_sessionkey) (eurephiaCTX *ctx, const char *seed, const char *seskey); + +extern eurephiaVALUES *(*eDBload_sessiondata) (eurephiaCTX *ctx, const char *sesskey); + +extern int eDBstore_session_value(eurephiaCTX *ctx, eurephiaSESSION *session, int mode, + const char *key, const char *val); + + +// Adds or updates a key in the eurephiaVALUES stack. Database is updated before the stack is updated. +// If database fails, the stack is not updated. +int eDBset_session_value(eurephiaCTX *ctx, eurephiaSESSION *session, const char *key, const char *val) { + eurephiaVALUES *svals = NULL; + + if( (session == NULL) || (key == NULL) ) { + return 0; + } + + DEBUG(ctx, 30, "Function call: eDBset_session_value(ctx, '%s','%s','%s')", + session->sessionkey, key, val); + + // Create a new session value buffer if it does not exist + if( session->sessvals == NULL ) { + session->sessvals = eCreate_value_space(ctx, 10); + if( session->sessvals == NULL ) { + eurephia_log(ctx, LOG_PANIC, 0, "Could not allocate memory for session values"); + return 0; + } + } + + // Check if the session value exists already. If it does update it, or else add it + svals = eGet_valuestruct(session->sessvals, key); + if( (svals == NULL) && (val != NULL) ) { + DEBUG(ctx, 32, "eDBset_session_value ... New session value: %s = '%s'", key, val); + // Add a new session value + if( eDBstore_session_value(ctx, session, SESSVAL_NEW, key, val) ) { + DEBUG(ctx, 32, "eDBset_session_value ... Adding value to value stack: %s = '%s'", + key, val); + // Add value to the stack + eAdd_value(ctx, session->sessvals, key, val); + + DEBUG(ctx, 32, "Registered session variable to session '%s': %s = %s", + session->sessionkey, key, val); + } + } else if( svals != NULL ) { + if( (val != NULL) && (strcmp(svals->val, val) == 0) ) { + DEBUG(ctx, 32, "Session value not changed('%s','%s','%s)", + session->sessionkey, key, val); + return 1; + } + // Update the value in the stack if database is updated without errors + if( eDBstore_session_value(ctx, session,(val != NULL ? SESSVAL_UPDATE : SESSVAL_DELETE), key,val)){ + free_nullsafe(svals->val); + svals->val = strdup_nullsafe(val); + DEBUG(ctx, 32, "Session variable updated in session '%s': %s = %s", + session->sessionkey, key, val); + } + } else if( (svals == NULL) && (val == NULL ) ) { + DEBUG(ctx, 32, "Ignoring saving new session value '%s' == NULL", key); + } + return 1; +} + + +// Generate some random data and return a string. +static int rand_init = 0; +int get_randstring(eurephiaCTX *ctx, char *rndstr, int len) { + int attempts = 0; + do { + if( !rand_init ) { + if( !RAND_load_file("/dev/urandom", 64) ) { + eurephia_log(ctx, LOG_FATAL, 0, "Could not load random data from /dev/urandom"); + return 0; + } + rand_init = 1; + } + + if( RAND_pseudo_bytes((unsigned char *) rndstr, len) ) { + return 1; + } + sleep(1); + rand_init = 0; + } while( attempts++ < 11 ); + eurephia_log(ctx, LOG_FATAL, 0, "RAND_pseudo_bytes() could not generate enough random data"); + return 0; +} + + + +// Generates a new session structure. Session key will be created if session seed (input params) are not known. +// If session seed is known, the already generated session key will be used. +eurephiaSESSION *eDBopen_session_seed(eurephiaCTX *ctx, const char *digest, + const char *cname, const char *username, + const char *vpnipaddr, const char *vpnipmask, + const char *remipaddr, const char *remport) +{ + eurephiaSESSION *new_session = NULL; + char *seeddata = NULL, *seed = NULL, *ptr = NULL; + SHA512Context sha; + uint8_t sha_res[SHA512_HASH_SIZE]; + int totlen = 0, i = 0; + + DEBUG(ctx, 12, "Function call: eDBopen_session_seed(ctx, '%s', '%s', '%s', '%s', '%s', '%s', '%s')", + digest, cname, username, vpnipaddr, vpnipmask, remipaddr, remport); + + new_session = (eurephiaSESSION *) malloc(sizeof(eurephiaSESSION) + 2); + if( new_session == NULL ) { + eurephia_log(ctx, LOG_PANIC, 0, "Could not allocate memory for a new session"); + return NULL; + } + memset(new_session, 0, sizeof(eurephiaSESSION) + 2); + + // Build up a string containing all elements for the session seed + totlen = strlen_nullsafe(digest) + strlen_nullsafe(cname) + strlen_nullsafe(username) + + strlen_nullsafe(vpnipaddr) + strlen_nullsafe(vpnipmask) + strlen_nullsafe(remipaddr) + + strlen_nullsafe(remport) + 5; // +5 == len(pid) + + seeddata = (char *) malloc((totlen * 2) + 4); + if( seeddata == NULL ) { + eurephia_log(ctx, LOG_PANIC, 0, "Could not allocate memory for a new session key (1)"); + free_nullsafe(new_session); + return NULL; + } + memset(seeddata, 0, (totlen * 2) + 4); + snprintf((char *)seeddata, totlen, + "%s%s%s%s%s%s%s%i", digest, cname, username, vpnipaddr, vpnipmask, remipaddr, remport,getpid()); + + // Generate a SHA512 version of session seed + memset(&sha, 0, sizeof(SHA512Context)); + memset(&sha_res, 0, sizeof(sha_res)); + SHA512Init(&sha); + SHA512Update(&sha, seeddata, totlen); + SHA512Final(&sha, sha_res); + + seed = (char *) malloc((SHA512_HASH_SIZE*2)+3); + if( seed == NULL ) { + eurephia_log(ctx, LOG_PANIC, 0, "Could not allocate memory for session seed"); + free_nullsafe(seeddata); + free_nullsafe(new_session); + return NULL; + } + memset(seed, 0, (SHA512_HASH_SIZE*2)+2); + ptr = seed; + + for( i = 0; i < SHA512_HASH_SIZE; i++ ) { + sprintf(ptr, "%02x", sha_res[i]); + ptr++; + } + memset(&sha, 0, sizeof(SHA512Context)); + memset(&sha_res, 0, sizeof(sha_res)); + + DEBUG(ctx, 13, "Using session seed '%s'", seed); + + // Try to retrieve the sessionkey from the database, based on the session seed + new_session->sessionkey = eDBget_sessionkey_seed(ctx, seed); + if( new_session->sessionkey == NULL ) { + // ... if we do not find a sessionkey ... lets generate one + int rndlen = 0; + char *rndstr = NULL; + char *skeydata = NULL; + int loop = 0, uniqcheck = 0; + + DEBUG(ctx, 13, "Unknown session seed, creating new session key"); + + // Loop until we get a unique sessionkey - don't loop more than 10 times + skeydata = (char *) malloc((totlen*2)+4); + if( skeydata == NULL ) { + eurephia_log(ctx, LOG_PANIC, 0, "Could not allocate memory for new session key data"); + free_nullsafe(new_session->sessionkey); + free_nullsafe(new_session); + free_nullsafe(seeddata); + free_nullsafe(seed); + return NULL; + } + do { + memset(skeydata, 0, (totlen*2)+4); + + // FIXME: Validate that we have enough random data for the session key + + // Append some random data to our session seed + rndstr = (char *) malloc((totlen * 2)); + if( rndstr == NULL ) { + eurephia_log(ctx, LOG_PANIC, 0, + "Could not allocate memory for new session key data (2)"); + free_nullsafe(new_session->sessionkey); + free_nullsafe(new_session); + free_nullsafe(seeddata); + free_nullsafe(seed); + return NULL; + } + memset(rndstr, 0, (totlen * 2)); + rndlen = ((totlen * 2) - strlen_nullsafe(seed) - 2); + + if( !get_randstring(ctx, rndstr, rndlen) ) { + eurephia_log(ctx, LOG_PANIC, 0, + "Could not generate enough random data for session key"); + free_nullsafe(new_session->sessionkey); + free_nullsafe(new_session); + free_nullsafe(seeddata); + free_nullsafe(seed); + return NULL; + } + + // Generate SHA512 version of the session data + SHA512Init(&sha); + SHA512Update(&sha, rndstr, rndlen); + SHA512Final(&sha, sha_res); + + free_nullsafe(new_session->sessionkey); + new_session->sessionkey = (char *) malloc((SHA512_HASH_SIZE*2)+3); + if( new_session->sessionkey == NULL ) { + eurephia_log(ctx, LOG_PANIC, 0, + "Could not allocate memory for new session key"); + free_nullsafe(new_session); + free_nullsafe(seeddata); + free_nullsafe(seed); + return NULL; + } + memset(new_session->sessionkey, 0, (SHA512_HASH_SIZE*2)+3); + + ptr = new_session->sessionkey; + for( i = 0; i < SHA512_HASH_SIZE; i++ ) { + sprintf(ptr, "%02x", sha_res[i]); + ptr++; + } + memset(&sha, 0, sizeof(SHA512Context)); + memset(&sha_res, 0, sizeof(sha_res)); + free_nullsafe(rndstr); + + loop++; + uniqcheck = eDBcheck_sessionkey_uniqueness(ctx, new_session->sessionkey); + } while( (uniqcheck == 0) && loop < 11 ); + free_nullsafe(skeydata); + + // If we did not manage to create a unique session key (random data collection must have failed!) + if( uniqcheck == 0 ) { + eurephia_log(ctx, LOG_FATAL, 0, + "Did not manage to create a unique sessionkey after %i attempts", loop-1); + free_nullsafe(new_session->sessionkey); + free_nullsafe(new_session); + free_nullsafe(seeddata); + free_nullsafe(seed); + return NULL; + } + + // Save this session key in the database and connect it to this session seed + if( eDBregister_sessionkey(ctx, seed, new_session->sessionkey) == 0) { + eurephia_log(ctx, LOG_FATAL, 0, "Could not register sessionkey"); + free_nullsafe(new_session->sessionkey); + free_nullsafe(new_session); + free_nullsafe(seeddata); + free_nullsafe(seed); + return NULL; + }; + new_session->sessionstatus = SESSION_NEW; + } else { + new_session->sessionstatus = SESSION_EXISTING; + DEBUG(ctx, 13, "Session seed found, using sessionkey '%s'", new_session->sessionkey); + } + free_nullsafe(seed); + free_nullsafe(seeddata); + + // Load session values from the database + new_session->sessvals = eDBload_sessiondata(ctx, new_session->sessionkey); + + // Return struct which contains session key and session variables + return new_session; +} + +// Open an existing session based on a MAC address +eurephiaSESSION *eDBopen_session_macaddr(eurephiaCTX *ctx, const char *macaddr) { + eurephiaSESSION *new_session = NULL; + + DEBUG(ctx, 12, "Function call: eDBopen_session_mac(ctx, '%s')", macaddr); + + new_session = (eurephiaSESSION *) malloc(sizeof(eurephiaSESSION) + 2); + if( new_session == NULL ) { + eurephia_log(ctx, LOG_PANIC, 0, "Could not allocate memory for a new session"); + return NULL; + } + memset(new_session, 0, sizeof(eurephiaSESSION) + 2); + + // Get the sessionkey from the database + new_session->sessionkey = eDBget_sessionkey_macaddr(ctx, macaddr); + if( new_session->sessionkey == NULL ) { + eurephia_log(ctx, LOG_CRITICAL, 0, "Could not find an active session for MAC address '%s'", + macaddr); + free_nullsafe(new_session); + return NULL; + } + DEBUG(ctx, 13, "Session seed found, using sessionkey '%s'", new_session->sessionkey); + + // Load session values from the database + new_session->sessvals = eDBload_sessiondata(ctx, new_session->sessionkey); + + // Return struct which contains the current session + return new_session; +} + + +// Free up the memory used by a session structure +void eDBfree_session_func(eurephiaCTX *ctx, eurephiaSESSION *session) { + if( session == NULL ) { + return; + } + DEBUG(ctx, 12, "Function call: eDBfree_session(ctx, '%s')", session->sessionkey); + eFree_values(ctx, session->sessvals); + free_nullsafe(session->sessionkey); + free_nullsafe(session); +} diff --git a/plugin/eurephiadb_session.h b/plugin/eurephiadb_session.h new file mode 100644 index 0000000..47f0049 --- /dev/null +++ b/plugin/eurephiadb_session.h @@ -0,0 +1,44 @@ +/* eurephia_session.h -- Internal API to provide storing of variables connected to a session + * + * GPLv2 - Copyright (C) 2008 David Sommerseth <dazo@users.sourceforge.net> + * + * 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. + * + */ + +#ifndef EUREPHIADB_SESSION_H_ +# define EUREPHIADB_SESSION_H_ + +#define SESSVAL_NEW 10 +#define SESSVAL_UPDATE 11 +#define SESSVAL_DELETE 12 + +eurephiaVALUES *eDBfind_session_value(eurephiaCTX *ctx, eurephiaSESSION *seskey, const char *key); + +int eDBset_session_value(eurephiaCTX *ctx, eurephiaSESSION *sesskey, const char *key, const char *val); + +char *eDBget_session_value(eurephiaCTX *ctx, eurephiaSESSION *sesskey, const char *key); + +eurephiaSESSION *eDBopen_session_seed(eurephiaCTX *ctx, const char *digest, + const char *cname, const char *username, + const char *vpnipaddr, const char *vpnipmask, + const char *remipaddr, const char *remport); + +eurephiaSESSION *eDBopen_session_macaddr(eurephiaCTX *ctx, const char *macaddr); + +#define eDBfree_session(c, s) { eDBfree_session_func(c, s); s = NULL;} +void eDBfree_session_func(eurephiaCTX *ctx, eurephiaSESSION *sk); + +#endif /* !EUREPHIADB_SESSION_H_ */ diff --git a/plugin/firewall/eurephiafw.c b/plugin/firewall/eurephiafw.c new file mode 100644 index 0000000..9fed02e --- /dev/null +++ b/plugin/firewall/eurephiafw.c @@ -0,0 +1,288 @@ +/* eurephiafw.c -- Firewall interface loader for the eurephia module + * + * GPLv2 - Copyright (C) 2008 David Sommerseth <dazo@users.sourceforge.net> + * + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <dlfcn.h> +#include <errno.h> +#include <signal.h> +#include <unistd.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/wait.h> + +#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); + } + + 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 *macaddr, 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", macaddr, fwdest, fwprofile); + snprintf(buf, 1024, "A %s %s %s", macaddr, 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", macaddr, fwdest, fwprofile); + snprintf(buf, 1024, "D %s %s %s", macaddr, fwdest, fwprofile); + 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)", + macaddr); + return 0; + } +} diff --git a/plugin/firewall/eurephiafw.h b/plugin/firewall/eurephiafw.h new file mode 100644 index 0000000..0c82b30 --- /dev/null +++ b/plugin/firewall/eurephiafw.h @@ -0,0 +1,34 @@ +/* eurephiafw.h -- Firewall interface loader for the eurephia module + * + * GPLv2 - Copyright (C) 2008 David Sommerseth <dazo@users.sourceforge.net> + * + * 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. + * + */ + +#ifndef EUREPHIAFW_H_ +#define EUREPHIAFW_H_ + +#define FWRULE_ADD 0x101 +#define FWRULE_DELETE 0x102 + +int eFW_load(eurephiaCTX *ctx, const char *intf); +int eFW_unload(eurephiaCTX *ctx); + +void eFW_StartFirewall(eurephiaCTX *ctx); +void eFW_StopFirewall(eurephiaCTX *ctx); +int eFW_UpdateFirewall(eurephiaCTX *ctx, int mode, const char *macaddr, const char *fwdest, const char *fwprofile); + +#endif /* !EUREPHIAFW_H_ */ diff --git a/plugin/firewall/eurephiafw_helpers.c b/plugin/firewall/eurephiafw_helpers.c new file mode 100644 index 0000000..ceb6d25 --- /dev/null +++ b/plugin/firewall/eurephiafw_helpers.c @@ -0,0 +1,97 @@ +/* eurephiafw_helpers.c -- Helper functions, shared between main module and + * firewall module. Setting up Posix MQ and semaphores + * + * GPLv2 - Copyright (C) 2008 David Sommerseth <dazo@users.sourceforge.net> + * + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <signal.h> +#include <unistd.h> +#include <semaphore.h> +#include <mqueue.h> + +#include "eurephia_struct.h" +#include "eurephia_log.h" +#include "eurephiafw.h" +#include "eurephiafw_helpers.h" + + +int efwSetupSemaphores(eurephiaCTX *ctx, efw_threaddata *cfg) { + // Initialise the main process' semaphore + cfg->semp_master = sem_open(SEMPH_MASTER, O_CREAT, 0666, 0); + if( cfg->semp_master == SEM_FAILED ) { + eurephia_log(ctx, LOG_FATAL, 0, "Could not setup semaphore for eFW master: %s", strerror(errno)); + return 0; + } + + // Initialise the worker process' semaphore + cfg->semp_worker = sem_open(SEMPH_WORKER, O_CREAT, 0666, 0); + if( cfg->semp_worker == SEM_FAILED ) { + eurephia_log(ctx, LOG_FATAL, 0, "Could not setup semaphore for eFW worker: %s", strerror(errno)); + return 0; + } + return 1; +} + +int efwRemoveSemaphores(eurephiaCTX *ctx, efw_threaddata *cfg) { + if( sem_close(cfg->semp_worker) != 0 ) { + eurephia_log(ctx, LOG_WARNING, 0, + "eFW: Could not destroy semaphore for worker: %s", strerror(errno)); + } + sem_unlink(SEMPH_WORKER); + + if( sem_close(cfg->semp_master) != 0 ) { + eurephia_log(ctx, LOG_WARNING, 0, + "eFW: Could not destroy semaphore for master: %s", strerror(errno)); + } + sem_unlink(SEMPH_MASTER); + return 1; +} + +int efwSetupMessageQueue(eurephiaCTX *ctx, efw_threaddata *cfg) { + struct mq_attr mqattr; + + // Prepare a POSIX Message Queue + mqattr.mq_maxmsg = 10; + mqattr.mq_msgsize = EFW_MSG_SIZE; + mqattr.mq_flags = 0; + cfg->msgq = mq_open(MQUEUE_NAME, O_RDWR | O_CREAT, 0600, &mqattr); + if( cfg->msgq < 0 ) { + eurephia_log(ctx, LOG_FATAL, 0, "Could not open message queue: %s", strerror(errno)); + return 0; + } + return 1; +} + +int efwRemoveMessageQueue(eurephiaCTX *ctx, efw_threaddata *cfg) { + // Close and remove the message queue used + if( mq_close((*cfg).msgq) != 0 ) { + eurephia_log(ctx, LOG_WARNING, 0, "Could not do close the message queue used for eFW: %s", + strerror(errno)); + } + + if( mq_unlink(MQUEUE_NAME) != 0 ) { + eurephia_log(ctx, LOG_WARNING, 0, "Could not do close the message queue used for eFW: %s", + strerror(errno)); + } + return 1; +} + diff --git a/plugin/firewall/eurephiafw_helpers.h b/plugin/firewall/eurephiafw_helpers.h new file mode 100644 index 0000000..9c21a2f --- /dev/null +++ b/plugin/firewall/eurephiafw_helpers.h @@ -0,0 +1,36 @@ +/* eurephiafw_helpers.h -- Helper functions, shared between main module and + * firewall module. Setting up Posix MQ and semaphores + * + * GPLv2 - Copyright (C) 2008 David Sommerseth <dazo@users.sourceforge.net> + * + * 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. + * + */ + +#ifndef EUREPHIAFW_HELPERS_H_ +#define EUREPHIAFW_HELPERS_H_ + +#define EFW_MSG_SIZE 1024 +#define MQUEUE_NAME "/eurephiaFW" +#define SEMPH_MASTER "eurephiafw_master" +#define SEMPH_WORKER "eurephiafw_worker" + +int efwSetupSemaphores(eurephiaCTX *, efw_threaddata *); +int efwRemoveSemaphores(eurephiaCTX *, efw_threaddata *); + +int efwSetupMessageQueue(eurephiaCTX *, efw_threaddata *); +int efwRemoveMessageQueue(eurephiaCTX *, efw_threaddata *); + +#endif /* !EUREPHIAFW_HELPERS_H_ */ diff --git a/plugin/firewall/eurephiafw_intf.h b/plugin/firewall/eurephiafw_intf.h new file mode 100644 index 0000000..0f18cc2 --- /dev/null +++ b/plugin/firewall/eurephiafw_intf.h @@ -0,0 +1,30 @@ +/* eurephiafw_intf.h -- + * + * GPLv2 - Copyright (C) 2008 David Sommerseth <dazo@users.sourceforge.net> + * + * 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. + * + */ + +#ifndef EUREPHIAFW_INTF_H_ +#define EUREPHIAFW_INTF_H_ + +// Mandatory functions for all FW interfaces +const char *(*eFWinterfaceVersion) (void); +int (*eFWinterfaceAPIversion) (void); + +void (*eFW_RunFirewall) (void *); + +#endif /* !EUREPHIAFW_INTF_H_ */ diff --git a/plugin/firewall/iptables/CMakeLists.txt b/plugin/firewall/iptables/CMakeLists.txt new file mode 100644 index 0000000..0f77a1b --- /dev/null +++ b/plugin/firewall/iptables/CMakeLists.txt @@ -0,0 +1,12 @@ +PROJECT(eurephiafw-iptables C) +cmake_minimum_required(VERSION 2.6) +SET(efw_ipt_SRC + efw_iptables.c + ../eurephiafw_helpers.c + ../../../common/eurephia_log.c +) + +INCLUDE_DIRECTORIES(BEFORE ../..) +ADD_LIBRARY(efw_iptables MODULE ${efw_ipt_SRC}) +TARGET_LINK_LIBRARIES(efw_iptables pthread rt gcc_s) +SET_TARGET_PROPERTIES(efw_iptables PROPERTIES PREFIX "") diff --git a/plugin/firewall/iptables/efw_iptables.c b/plugin/firewall/iptables/efw_iptables.c new file mode 100644 index 0000000..9e0aaa4 --- /dev/null +++ b/plugin/firewall/iptables/efw_iptables.c @@ -0,0 +1,234 @@ +/* efw_iptables.c -- iptables implementation - updates Linux iptables + * + * GPLv2 - Copyright (C) 2008 David Sommerseth <dazo@users.sourceforge.net> + * + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <pthread.h> +#include <sys/types.h> +#include <sys/wait.h> + +#include <eurephia_nullsafe.h> +#include <eurephia_log.h> +#include <eurephia_struct.h> +#include <eurephiafw_helpers.h> + +#define INTERFACEVER "1.0" +#define INTERFACEAPIVER 1 + + +const char *eFWinterfaceVersion() { + return "eFW-iptables (v"INTERFACEVER") David Sommerseth 2008 (C) GPLv2"; +} + +int eFWinterfaceAPIversion() { + return INTERFACEAPIVER; +} + + +int process_input(eurephiaCTX *ctx, const char *fwcmd, const char *msg); +int call_iptables(eurephiaCTX *ctx, const char *fwcmd, char **ipt_args); + +void eFW_RunFirewall(void *fwargs) { + efw_threaddata *cfg = (efw_threaddata *) fwargs; + eurephiaCTX *ctx = (eurephiaCTX *) cfg->ctx; + int quit = 0; + unsigned int prio; + char buf[EFW_MSG_SIZE+2]; + + DEBUG(ctx, 28, "eFW_RunFirewall: Waiting for eFW master to get ready"); + sem_wait(cfg->semp_master); + DEBUG(ctx, 28, "eFW_RunFirewall: Telling eFW master that the worker process is ready"); + sem_post(cfg->semp_worker); + + if( cfg->fw_command == NULL ) { + eurephia_log(ctx, LOG_FATAL, 0, + "eFW_RunFirewall: firewall_command is not configured. " + "iptables will not be updated."); + exit(3); + } + + // Main loop ... grab messages of the messague queue until shutdown command is sent, or a failure happens + while( quit == 0 ) { + memset(buf, 0, EFW_MSG_SIZE+2); + if( mq_receive(cfg->msgq, &buf[0], EFW_MSG_SIZE, &prio) == -1 ) { + eurephia_log(ctx, LOG_FATAL, 0, + "eFW_RunFirewall: Error while reading messages from queue: %s", + strerror(errno)); + exit(2); + } + quit = (strncmp(buf, "FWSHUTDOWN", 10) == 0 ); + if( !quit ) { + int res = 0; + + DEBUG(ctx, 20, "eFW_RunFirewall: Received '%s'", buf); + + res = process_input(ctx, cfg->fw_command, buf); + if( ! res ) { + quit = 1; + eurephia_log(ctx, LOG_FATAL, 0, + "eFW_RunFirewall: Failed updating iptables"); + } + } + } + + efwRemoveSemaphores(ctx, fwargs); + efwRemoveMessageQueue(ctx, fwargs); + exit(0); +} + + +int process_input(eurephiaCTX *ctx, const char *fwcmd, const char *input) { + char mode[3], *macaddr = NULL, *destchain = NULL, *jump = NULL; + char *msg = NULL, *orig_msg = NULL; + char *iptables_args[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; + int ret = 0; + + orig_msg = strdup_nullsafe(input); + msg = orig_msg; + DEBUG(ctx, 36, "eFW_RunFirewall::process_input(ctx, '%s')", msg); + + // + // Simple parsing of the input string + // + mode[0] = '-'; + mode[1] = *msg; + mode[2] = 0; + msg += 2; + + iptables_args[0] = (char *)fwcmd; + + switch( mode[1] ) { + case 'A': + case 'D': + iptables_args[1] = mode; + macaddr = msg; // start of string for macaddr + + // Search for end of macaddr and NULL terminate it + destchain = macaddr+1; + while( (*destchain != 0x20) || (*destchain == 0) ) { + destchain++; + } + if( *destchain == 0 ) { + return 0; + } + *destchain = 0; // end of string for macaddr + destchain++; // start of string for destchain + // Search for end of destchain and NULL terminate it + jump = destchain+1; + while( (*jump != 0x20) || (*jump == 0) ) { + jump++; + } + *jump = 0; // end of string for destchain + jump++; // start of string for jump + + // Prepare iptables arguments + iptables_args[2] = destchain; + iptables_args[3] = "-m\0"; + iptables_args[4] = "mac\0"; + iptables_args[5] = "--mac-source\0"; + iptables_args[6] = macaddr; + iptables_args[7] = "-m\0"; + iptables_args[8] = "state\0"; + iptables_args[9] = "--state\0"; + iptables_args[10] = "NEW\0"; + iptables_args[11] = "-j\0"; + iptables_args[12] = jump; + iptables_args[13] = NULL; + + eurephia_log(ctx, LOG_INFO, 3, "eFW_RunFirewall - updating iptables rules " + "==> mode: %s macaddr: '%s' destchain: '%s' jump: '%s'", + (mode[1] == 'A' ? "ADD":"DELETE"), macaddr, destchain, jump); + ret = call_iptables(ctx, fwcmd, iptables_args); + break; + + case 'F': + iptables_args[1] = mode; + destchain = msg; + iptables_args[2] = destchain; + iptables_args[3] = NULL; + eurephia_log(ctx, LOG_INFO, 3, "eFW_RunFirewall - updating iptables rules " + "==> mode: FLUSH destchain: '%s'", destchain); + ret = call_iptables(ctx, fwcmd, iptables_args); + break; + + case 'I': + // Init chain - flush it and then add needed rule for stateful inspection + destchain = msg; + + eurephia_log(ctx, LOG_INFO, 3, "eFW_RunFirewall - Initialising iptables chain '%s'", + destchain); + + // Flush phase + iptables_args[1] = "-F"; + destchain = msg; + iptables_args[2] = destchain; + iptables_args[3] = NULL; + ret = call_iptables(ctx, fwcmd, iptables_args); + + // Add stateful inspection + iptables_args[1] = "-I\0"; + iptables_args[2] = destchain; + iptables_args[3] = "-m\0"; + iptables_args[4] = "state\0"; + iptables_args[5] = "--state\0"; + iptables_args[6] = "ESTABLISHED,RELATED\0"; + iptables_args[7] = "-j\0"; + iptables_args[8] = "ACCEPT\0"; + ret &= call_iptables(ctx, fwcmd, iptables_args); + break; + + default: + eurephia_log(ctx, LOG_CRITICAL, 0, "eFW_RunFirewall::process_input: Malformed update request"); + ret = 1; + } + free_nullsafe(orig_msg); + return ret; +} + +int call_iptables(eurephiaCTX *ctx, const char *fwcmd, char **ipt_args) { + pid_t pid; + int cmdret = -1; + + // Fork out a child process which will run the iptables command. Since the execve replaces + // the current process, we need to do the forking first. + if( (pid = fork()) < 0) { + eurephia_log(ctx, LOG_FATAL, 0, + "eFW_RunFirewall::process_input: Failed to fork process for %s", fwcmd); + return 0; + } + + switch( pid ) { + case 0: // child process - execute the program and exit + execve(fwcmd, ipt_args, NULL); + exit(1); // execve should replace the process, but if it fails to do so, make sure we exit + + default: // parent process + if( waitpid(pid, &cmdret, 0) != pid ) { + eurephia_log(ctx, LOG_WARNING, 0, + "eFW_RunFirewall::process_input: Failed to wait for process for %s" + " to complete (%s)", fwcmd, strerror(errno)); + } + eurephia_log(ctx, LOG_INFO, 4, "eFW_RunFirewall - iptables exited with code: %i ", cmdret); + } + return 1; +} |