From 6440a3a4592a8c0f0f8fd50cd551b41c1e066efe Mon Sep 17 00:00:00 2001 From: David Sommerseth Date: Tue, 14 Dec 2010 18:47:58 +0100 Subject: Implemented proper TUN support in eurephia Now eurephia will support both TUN and TAP configurations in OpenVPN. Thanks to Tavis Paquette and Matthew Gyurgyik for their willingness to test out this patch. Signed-off-by: David Sommerseth --- CREDITS.txt | 3 +++ common/eurephia_context.h | 1 + plugin/eurephia-auth.c | 8 ++++++ plugin/eurephia.c | 58 ++++++++++++++++++++++++++++++++++++++------ plugin/eurephiadb_session.c | 57 +++++++++++++++++++++++++++++++------------ plugin/eurephiadb_session.h | 1 + plugin/firewall/eurephiafw.c | 2 +- 7 files changed, 107 insertions(+), 23 deletions(-) diff --git a/CREDITS.txt b/CREDITS.txt index 3f12c2f..878a5fb 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -44,3 +44,6 @@ or indirectly, will be credited here. * Tavis Paquette - Helped out solving FreeBSD building issues - Also helped testing out the new TUN support for eurephia + +* Matthew Gyurgyik + - Helped out testing the TUN support in eurephia on Linux with iptables support diff --git a/common/eurephia_context.h b/common/eurephia_context.h index d7c1bae..56b1ed7 100644 --- a/common/eurephia_context.h +++ b/common/eurephia_context.h @@ -70,6 +70,7 @@ typedef struct { eurephiaLOG *log; /**< Log context, used by eurephia_log() */ int fatal_error; /**< If this flag is set, the execution should stop immediately */ int context_type; /**< Defines the context type */ + eurephiaVALUES *disconnected; /**< List over disconnected clients which has not yet been removed */ } eurephiaCTX; #endif diff --git a/plugin/eurephia-auth.c b/plugin/eurephia-auth.c index 71969e5..9245a40 100644 --- a/plugin/eurephia-auth.c +++ b/plugin/eurephia-auth.c @@ -270,6 +270,14 @@ OPENVPN_EXPORT int openvpn_plugin_func_v1(openvpn_plugin_handle_t handle, case OPENVPN_PLUGIN_CLIENT_DISCONNECT: // Register logout result = eurephia_disconnect(ctx, envp); + if( (result == 1) && (ctx->tuntype == tuntype_TUN) ) { + // If running in TUN mode, OPENVPN_PLUGIN_LEARN_ADDRESS will not be called, + // so we fake this call here instead. + char *vpnipaddr = GETENV_POOLIPADDR(ctx, envp); + + result = (vpnipaddr ? eurephia_learn_address(ctx, "delete", vpnipaddr, envp) : 0); + free_nullsafe(ctx, vpnipaddr); + } break; case OPENVPN_PLUGIN_LEARN_ADDRESS: // Log IP address, MAC address and update firewall diff --git a/plugin/eurephia.c b/plugin/eurephia.c index 71181f5..eda014f 100644 --- a/plugin/eurephia.c +++ b/plugin/eurephia.c @@ -204,6 +204,12 @@ eurephiaCTX *eurephiaInit(const char const **argv, const char const **envp) ctx->eurephia_fw_intf = NULL; } + // Prepare an empty disconnected list. + // This one is used to track all clients IP addresses and their corresponding eurephia session ID + // when they disconnect. This is especially needed in TUN mode, the eurephia_learn_address() + // delete call with otherwise have too little information to identify the session. + ctx->disconnected = eCreate_value_space(ctx, 12); + eurephia_log(ctx, LOG_INFO, 1, "eurephia-auth is initialised"); return ctx; } @@ -223,6 +229,11 @@ int eurephiaShutdown(eurephiaCTX *ctx) return 0; } + if( ctx->disconnected != NULL ) { + eFree_values(ctx, ctx->disconnected); + ctx->disconnected = NULL; + } + if( ctx->eurephia_fw_intf != NULL ) { if( ctx->fwcfg != NULL ) { eFW_StopFirewall(ctx); @@ -599,6 +610,12 @@ int eurephia_disconnect(eurephiaCTX *ctx, const char **env) { return 0; } + if( ctx->tuntype == tuntype_TUN ) { + // Put the clients IP address into the disconnected list, to be used and freed + // by eurephia_learn_address() afterwards + eAdd_value(ctx, ctx->disconnected, vpnipaddr, session->sessionkey); + } + ret = eDBregister_logout(ctx, session, bytes_sent, bytes_rec, duration); eDBfree_session(ctx, session); eurephia_log(ctx, LOG_INFO, 1, "User '%s' logged out", uname); @@ -646,7 +663,7 @@ int eurephia_learn_address(eurephiaCTX *ctx, const char *mode, const char *clien char *fwprofile = NULL, *fwdest = NULL; int ret = 0, fw_enabled = 0; - DEBUG(ctx, 10, "** Function call: eurephia_learn_address(ctx, '%.10s', '%.18s', ...)", + DEBUG(ctx, 10, "** Function call: eurephia_learn_address(ctx, '%.10s', '%.64s', ...)", mode, clientaddr); // Get firewall information @@ -656,7 +673,15 @@ int eurephia_learn_address(eurephiaCTX *ctx, const char *mode, const char *clien eurephia_log(ctx, LOG_CRITICAL, 0, "No firewall destination defined in the config."); } - if( strncmp(mode, "add", 3) == 0 ) { + // In TUN mode, if a client reconnects quickly after a disconnect, + // OpenVPN re-uses the session for this and just sends an update + // call to LEARN_ADDRESS. As eurephia in TUN mode treats disconnects + // with an explicit 'delete' (see eurephia-auth.c for more info) and + // don't wait for a separate LEARN_ADDRESS delete call (which never + // seems to come), we treat 'update' as an ordinary 'add' in TUN mode. + if( (strncmp(mode, "add", 3) == 0) + || ((ctx->tuntype == tuntype_TUN) && (strncmp(mode, "update", 6) == 0)) ) { + // Fetch needed info digest = GETENV_TLSDIGEST(ctx, env, 0); cname = GETENV_CNAME(ctx, env); @@ -674,8 +699,12 @@ int eurephia_learn_address(eurephiaCTX *ctx, const char *mode, const char *clien goto exit; } - // Update openvpn_lastlog with the active MAC address, and save it as a session variable - ret = eDBregister_vpnmacaddr(ctx, session, clientaddr); + if( ctx->tuntype == tuntype_TAP ) { + // Update openvpn_lastlog with the active MAC address, and save it as a session variable + ret = eDBregister_vpnmacaddr(ctx, session, clientaddr); + } else { + ret = 1; + } if( (fw_enabled) && (fwdest != NULL) ) { fwprofile = eDBget_firewall_profile(ctx, session); @@ -696,11 +725,26 @@ int eurephia_learn_address(eurephiaCTX *ctx, const char *mode, const char *clien } else if( strncmp(mode, "delete", 6) == 0 ) { - // Load the session, based on MAC address - session = eDBopen_session_macaddr(ctx, clientaddr); + // Load the session + if( ctx->tuntype == tuntype_TAP ) { + // in TAP mode - use the MAC address + session = eDBopen_session_macaddr(ctx, clientaddr); + } else { + // in TUN mode - use the disconnected list to find the session key + eurephiaVALUES *sessval = eGet_valuestruct(ctx->disconnected, clientaddr); + if( sessval ) { + session = eDBsession_load(ctx, sessval->val, stSESSION); + if( ctx->disconnected->next != NULL ) { + ctx->disconnected = eRemove_value(ctx, ctx->disconnected, + sessval->evgid, sessval->evid); + } else { + eClear_key_value(ctx->disconnected); + } + } + } if( session == NULL ) { eurephia_log(ctx, LOG_WARNING, 0, - "Could not find any session connected to this MAC address: %.18s", + "Could not find any session connected to the client address %s", clientaddr); ret = 0; goto exit; diff --git a/plugin/eurephiadb_session.c b/plugin/eurephiadb_session.c index f8fb97a..f6c7faa 100644 --- a/plugin/eurephiadb_session.c +++ b/plugin/eurephiadb_session.c @@ -242,37 +242,34 @@ eurephiaSESSION *eDBopen_session_seed(eurephiaCTX *ctx, const char *digest, /** - * Open an existing eurephia session based on a MAC address. This function is only used - * when there is not enough information to generate a session seed and when the MAC address - * is available. Usually this only happens when the client has disconnected and the session - * is about to be marked as closed. + * Load a session based on a known session key * - * @param ctx eurephiaCTX - * @param macaddr MAC address of the client + * @param ctx eurephiaCTX + * @param sesskey A string containing the session key + * @param sesstype What kind of type the session data should be opend as (sessionType) * - * @return returns a eurephiaSESSION pointer on success, otherwise NULL. + * @return Returns a pointer to the corresponding eurephiaSESSION struct on success, otherwise NULL. */ -eurephiaSESSION *eDBopen_session_macaddr(eurephiaCTX *ctx, const char *macaddr) { + +eurephiaSESSION *eDBsession_load(eurephiaCTX *ctx, const char *sesskey, sessionType sesstype) { eurephiaSESSION *new_session = NULL; - DEBUG(ctx, 12, "Function call: eDBopen_session_mac(ctx, '%s')", macaddr); + DEBUG(ctx, 12, "Function call: eDBsession_load(ctx, '%s')", sesskey); new_session = (eurephiaSESSION *) malloc_nullsafe(ctx, sizeof(eurephiaSESSION) + 2); if( new_session == NULL ) { return NULL; } - - new_session->type = stSESSION; // When we have macaddr - this is a stSESSION type of session + new_session->type = sesstype; // Get the sessionkey from the database - new_session->sessionkey = eDBget_sessionkey_macaddr(ctx, macaddr); + new_session->sessionkey = strdup_nullsafe(sesskey); if( new_session->sessionkey == NULL ) { - eurephia_log(ctx, LOG_CRITICAL, 0, "Could not find an active session for MAC address '%s'", - macaddr); + eurephia_log(ctx, LOG_CRITICAL, 0, "Failed to set the session key to '%s'", + sesskey); free_nullsafe(ctx, 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); @@ -280,3 +277,33 @@ eurephiaSESSION *eDBopen_session_macaddr(eurephiaCTX *ctx, const char *macaddr) // Return struct which contains the current session return new_session; } + + +/** + * Open an existing eurephia session based on a MAC address. This function is only used + * when there is not enough information to generate a session seed and when the MAC address + * is available. Usually this only happens when the client has disconnected and the session + * is about to be marked as closed. + * + * @param ctx eurephiaCTX + * @param macaddr MAC address of the client + * + * @return returns a eurephiaSESSION pointer on success, otherwise NULL. + */ +eurephiaSESSION *eDBopen_session_macaddr(eurephiaCTX *ctx, const char *macaddr) { + char *sesskey = NULL; + + DEBUG(ctx, 12, "Function call: eDBopen_session_mac(ctx, '%s')", macaddr); + + // Get the sessionkey from the database + sesskey = eDBget_sessionkey_macaddr(ctx, macaddr); + if( sesskey == NULL ) { + eurephia_log(ctx, LOG_CRITICAL, 0, "Could not find an active session for MAC address '%s'", + macaddr); + return NULL; + } + DEBUG(ctx, 13, "Session seed found, using sessionkey '%s'", sesskey); + + // Open and load the session from the database + return eDBsession_load(ctx, sesskey, stSESSION); +} diff --git a/plugin/eurephiadb_session.h b/plugin/eurephiadb_session.h index 5af2ce2..a3e159a 100644 --- a/plugin/eurephiadb_session.h +++ b/plugin/eurephiadb_session.h @@ -38,6 +38,7 @@ eurephiaSESSION *eDBopen_session_seed(eurephiaCTX *ctx, const char *digest, const char *vpnipaddr, const char *vpnipmask, const char *remipaddr, const char *remport); +eurephiaSESSION *eDBsession_load(eurephiaCTX *ctx, const char *sesskey, sessionType sesstype); eurephiaSESSION *eDBopen_session_macaddr(eurephiaCTX *ctx, const char *macaddr); #endif /* !EUREPHIADB_SESSION_H_ */ diff --git a/plugin/firewall/eurephiafw.c b/plugin/firewall/eurephiafw.c index d443e76..4ec2980 100644 --- a/plugin/firewall/eurephiafw.c +++ b/plugin/firewall/eurephiafw.c @@ -472,7 +472,7 @@ int eFW_UpdateFirewall(eurephiaCTX *ctx, eFWupdateRequest *request ) { eurephia_log(ctx, LOG_INFO, 3, "Function call: eFW_UpdateFirewall(ctx, %s, '%.18s', '%s', '%s')", (request->mode == fwADD ? "ADD" : "DELETE"), - request->macaddress, + (ctx->tuntype == tuntype_TAP ? request->macaddress : request->ipaddress), request->rule_destination, request->goto_destination); errno = 0; -- cgit