/* eurephia-auth.c -- Main OpenVPN plug-in functions. * The API level between OpenVPN and eurephia-auth * * GPLv2 only - Copyright (C) 2008 - 2010 * 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. * */ /** * @file eurephia-auth.c * @author David Sommerseth * @date 2008-08-06 * * @brief Implements the API which the OpenVPN plug-in interface requires * */ #include #include #include #include "openvpn-plugin.h" #define EUREPHIA_FWINTF #include #include #include #include #include #include #include #ifdef ENABLE_DEBUG /* To avoid compiler warnings when ENABLE_DEBUG is not defined */ /** * Simple "converter" from OPENVPN_PLUGIN_* type IDs to string * * @param type int value, corresponding to an OPENVPN_PLUGIN_* type * * @return String containing the string version of the type */ 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)"; } } /** * Dumps the contents of the environmental table to the given FILE. This function is only available * if DEBUG is defined during compilation. If SHOW_SECRETS is not defined, it will mask the contents * of the password field, if found. * * @param f FILE * where the contents will be dumped * @param prefix Adds a fixed prefix to each of the lines * @param envp openvpn environmental table */ 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 /** * Simple function for converting a device name/type string to * a OVPN_tunnelType constant */ static OVPN_tunnelType conv_str2tuntype(const char *str) { if( str ) { if( strncmp(str, "tun", 3) == 0 ) { return tuntype_TUN; } else if( strncmp(str, "tap", 3) == 0 ) { return tuntype_TAP; } } return tuntype_UNKN; } static inline int detect_tunnel_type(eurephiaCTX *ctx, const char const *env[]) { char *devname = NULL; char *devtype = NULL; OVPN_tunnelType tuntype = tuntype_UNKN; int forced = 0; devname = GETENV_DEVNAME(ctx, env); // Check if eurephia is configured to override the device type devtype = eGet_value(ctx->dbc->config, "openvpn_devtype"); if( devtype ) { tuntype = conv_str2tuntype(devtype); if( tuntype == tuntype_UNKN ) { eurephia_log(ctx, LOG_ERR, 0, "Unkown openvpn_devtype configuration value: '%s'. " "Will try to auto-detect the type for the %s device.", devtype, devname); free_nullsafe(ctx, devtype); } else { ctx->tuntype = tuntype; forced = 1; goto success; } } // Check if we got the 'dev_type' environment variable devtype = GETENV_DEVTYPE(ctx, env); tuntype = conv_str2tuntype(devtype); if( tuntype != tuntype_UNKN ) { ctx->tuntype = tuntype; goto success; } // If no 'dev_type', try to guess the dev-type based on the dev name tuntype = conv_str2tuntype(devname); if( tuntype != tuntype_UNKN ) { ctx->tuntype = tuntype; goto success; } // If we're here, it's an unknown device type eurephia_log(ctx, LOG_FATAL, 0, "Could not detect automatically which device type the %s device is. " "You need to force the tunnel device type setting the 'openvpn_devtype' " "configuration value.", devname); return 0; success: eurephia_log(ctx, LOG_INFO, 1, "OpenVPN device type is %s %s on the %s device.", (forced ? "forced to" : "detected as"), (tuntype == tuntype_TUN ? "TUN" : "TAP"), devname); free_nullsafe(ctx, devtype); free_nullsafe(ctx, devname); return 1; } /** * Prepares a eurephiaCTX (context) for the openvpn process and tells openvpn which hooks eurephia * will make use of. * * @param type_mask int pointer, containing the hooks eurephia will make use of * @param argv arguments from the openvpn --plugin configuration option. * @param envp openvpn environmental table * * @return Returns a pointer to the eurephiaCTX. */ 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_UP) | 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, envp); if( context ) { // Make sure tunnel type is unknown context->tuntype = tuntype_UNKN; } return (openvpn_plugin_handle_t) context; } /** * On each hook defined in openvpn_plugin_open_v1(), this function will be called when * openvpn reaches that phase. * * @param handle Contains a pointer to the eurephiaCTX * @param type What kind of event is openvpn processing now * @param argv openvpn arguments for the current event * @param envp openvpn environmental table * * @return Returns OPENVPN_PLUGIN_FUNC_SUCCESS on success, otherwise OPENVPN_PLUGIN_FUNC_ERROR */ 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->log->loglevel >= 30) && (ctx->log->logfile != NULL) ) { dump_env(ctx->log->logfile, "ENV: ", envp); dump_env(ctx->log->logfile, "ARG: ", argv); } #endif switch( type ) { case OPENVPN_PLUGIN_UP: result = detect_tunnel_type(ctx, envp); // Figure out what kind of tunnel type we got. if( result == 0 ) { // If we cannot identify the tunnel type, we will anyway reject // OpenVPN from starting up. In that case, shutdown eurephia properly eurephiaShutdown(ctx); } break; case OPENVPN_PLUGIN_TLS_VERIFY: // Validate certificates result = eurephia_tlsverify(ctx, envp, argv[1]); break; case OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY: // Validate user name and password result = eurephia_userauth(ctx, envp); break; case OPENVPN_PLUGIN_CLIENT_CONNECT: // Register login result = eurephia_connect(ctx, envp); break; case OPENVPN_PLUGIN_CLIENT_DISCONNECT: // Register logout result = eurephia_disconnect(ctx, envp); break; case OPENVPN_PLUGIN_LEARN_ADDRESS: // Log IP address, MAC address and update firewall result = eurephia_learn_address(ctx, argv[1], argv[2], envp); break; default: // This should normally not be reached at all eurephia_log(ctx, LOG_FATAL, 0, "Unknown OPENVPN_PLUGIN type: %i", type); break; } return (result == 1 ? OPENVPN_PLUGIN_FUNC_SUCCESS : OPENVPN_PLUGIN_FUNC_ERROR); } /** * Called when openvpn is shutting down. This makes sure that eurephia disconnects, * unloads drivers and frees the memory it has been using. * * @param handle Contains a pointer to the eurephiaCTX */ OPENVPN_EXPORT void openvpn_plugin_close_v1(openvpn_plugin_handle_t handle) { eurephiaCTX *ctx = (eurephiaCTX *) handle; eurephiaShutdown(ctx); }