The defer-w-pf plug-in is a complete rewrite of the simple.c, which moves the plug-in over to the newer Plug-in v3 API. In addition, it improves the example by adding a lot more comments and code documentation. And it avoids using system() and rather more realistic approaches by using POSIX threads and also fork().
Also replaced the ./build script with a Makefile for the *nix family of users. Mostly as Makefiles are far more common to build projects than some script. It is _not_ tied into the autotools fold, though, as this plug-in may be packaged into development packages in OS distributions - which will not carry the rest of the OpenVPN code base. Signed-off-by: David Sommerseth <dav...@openvpn.net> --- v2 - Fixed doxygen syntax errors - Added even more doxygen comments on undocumented parts - Replaced atoi_null0() with str2uint() using strtol() with some results checking and logging parsing errors - Changed returns to breaks in a switch() - Rescoped FILE *authres - Use proper FSF address in the copyright --- sample/sample-plugins/defer/Makefile | 14 + sample/sample-plugins/defer/README | 56 +- sample/sample-plugins/defer/build | 15 - sample/sample-plugins/defer/defer-w-pf.c | 1062 ++++++++++++++++++++ .../defer/{simple.def => defer-w-pf.def} | 0 sample/sample-plugins/defer/simple.c | 340 ------- 6 files changed, 1122 insertions(+), 365 deletions(-) create mode 100644 sample/sample-plugins/defer/Makefile delete mode 100755 sample/sample-plugins/defer/build create mode 100644 sample/sample-plugins/defer/defer-w-pf.c rename sample/sample-plugins/defer/{simple.def => defer-w-pf.def} (100%) delete mode 100644 sample/sample-plugins/defer/simple.c diff --git a/sample/sample-plugins/defer/Makefile b/sample/sample-plugins/defer/Makefile new file mode 100644 index 00000000..abb315a7 --- /dev/null +++ b/sample/sample-plugins/defer/Makefile @@ -0,0 +1,14 @@ + +CFLAGS = -I../../../include -O2 -Wall -g -std=c99 -pthread -fPIC +LDFLAGS = -fPIC -pthread + +all : defer-w-pf.so + +defer-w-pf.so : defer-w-pf.o + $(CC) -shared $(LDFLAGS) -o $@ $^ + +.c.o : + $(CC) -c $(CFLAGS) $< + +clean : + rm -f *.o *.so diff --git a/sample/sample-plugins/defer/README b/sample/sample-plugins/defer/README index d8990f8b..69038740 100644 --- a/sample/sample-plugins/defer/README +++ b/sample/sample-plugins/defer/README @@ -1,16 +1,52 @@ -OpenVPN plugin examples. +OpenVPN plugin examples +======================= -Examples provided: +defer-w-pf.c +------------ -simple.c -- using the --auth-user-pass-verify callback, - test deferred authentication. +A (stupid and) simple plug-in which authenticates a password only. In +addition it supports enabling the Packet Filter (PF) in OpenVPN, by making +use of the username for selecting the PF profile to load for the session. -To build: +To test this plugin, add the following lines to an OpenVPN configuration file: - ./build simple (Linux/BSD/etc.) - ./winbuild simple (MinGW on Windows) + setenv test_deferred_auth 20 + setenv test_packet_filter 10 + plugin defer-w-pw.so + +This will enable deferred authentication to occur 20 seconds after the normal +TLS authentication process. To disable testing deferred authentication, remove +the first setenv line. + +NOTICE: The password authentication is EXTREMELY naĩve, and is hard coded +to be 'foobar' (without the quotes). Any username will be accepted. This +is by design, as this is an example plug-in NOT meant for production. + +The second setenv line will enable a packet filter file to be generated +10 seconds after the initial TLS negotiation, using {username}.pf as the +source. To disable the PF feature, remove the second setenv line. + +OpenVPN should respond to other connected clients without much added latency, +as both the authentication and PF generation features should run in a parallel +thread and not block the main OpenVPN thread. + +* Sample packet filter configuration: + + [CLIENTS DROP] + +otherclient + [SUBNETS DROP] + +10.0.0.0/8 + -10.10.0.8 + [END] + +* Building the plug-in + +On Linux/BSD: + + $ make defer-w-pf.so + +Building for Windows using mingw: + + $ ./winbuild defer-w-pf -To use in OpenVPN, add to config file: - plugin simple.so (Linux/BSD/etc.) - plugin simple.dll (MinGW on Windows) diff --git a/sample/sample-plugins/defer/build b/sample/sample-plugins/defer/build deleted file mode 100755 index ba41a39f..00000000 --- a/sample/sample-plugins/defer/build +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/sh - -# -# Build an OpenVPN plugin module on *nix. The argument should -# be the base name of the C source file (without the .c). -# - -# This directory is where we will look for openvpn-plugin.h -CPPFLAGS="${CPPFLAGS:--I../../../include}" - -CC="${CC:-gcc}" -CFLAGS="${CFLAGS:--O2 -Wall -g}" - -$CC $CPPFLAGS $CFLAGS -fPIC -c $1.c && \ -$CC $CFLAGS -fPIC -shared ${LDFLAGS} -Wl,-soname,$1.so -o $1.so $1.o -lc diff --git a/sample/sample-plugins/defer/defer-w-pf.c b/sample/sample-plugins/defer/defer-w-pf.c new file mode 100644 index 00000000..1a1a12e5 --- /dev/null +++ b/sample/sample-plugins/defer/defer-w-pf.c @@ -0,0 +1,1062 @@ +/* + * OpenVPN -- An application to securely tunnel IP networks + * over a single TCP/UDP port, with support for SSL/TLS-based + * session authentication and key exchange, + * packet encryption, packet authentication, and + * packet compression. + * + * Copyright (C) 2002-2017 OpenVPN Technologies, Inc. <sa...@openvpn.net> + * 2017 David Sommerseth <dav...@openvpn.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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. + */ + +/* + * This file implements a simple OpenVPN plugin module which + * will test deferred authentication and packet filtering. + * + * This plug-in is a complete rewrite of the older simple.c plug-in, + * making use of the newe Plugin v3 API. + * + * For more information, see the README file + * + */ + +#define PLUGIN_NAME "defer/defer-w-pf.c" /**< The name of this plug-in */ + +#define _POSIX_C_SOURCE 200809L /**< Needed to enable strdup() in C99 mode */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdbool.h> +#include <unistd.h> +#include <limits.h> +#include <pthread.h> +#include <sys/wait.h> +#include <errno.h> +#include "openvpn-plugin.h" + +/** + * Global context for this plug-in + */ +struct plugin_context { + int test_deferred_auth; /**< If > 0, deferred auth is tested */ + int test_packet_filter; /**< If > 0, PF integration is tested */ +}; + +/** + * Context used per connected session + */ +struct plugin_per_client_context { + bool pf_running; /**< Is the PF thread running? */ + pthread_t pfthread; /**< pthread object of running PF thread */ + int pf_delay; /**< PF thread sleep delay before acting */ + char *pf_file; /**< Destination file of the PF filter profile */ + bool generated_pf_file; /**< Have the PF file been generated? */ + + bool auth_running; /**< Is the auth thread running? */ + pthread_t auththread; /**< pthread object of the running auth thread */ + int auth_delay; /**< Auth thread sleep delay before acting */ + char *auth_control_file; /**< File name where to write auth status */ + char *auth_uname; /**< User name to use for authentication */ + char *auth_passw; /**< Password to use for authentication */ +}; + + +/* plug-in v3 API log functions */ +plugin_log_t ovpn_log = NULL; /**< Pointer to the OpenVPN log function. See plugin_log() */ +plugin_vlog_t ovpn_vlog = NULL; /**< Pointer to the OpenVPN vlog function. See plugin_vlog() */ + +/** + * Modules in this plug-in + */ +typedef enum { + MOD_AUTH, /**< Authentication module */ + MOD_PF /**< Packet Filter (PF) module */ +} module_type; + +/** + * Search the environment pointer for a specific env var name + * + * PLEASE NOTE! The result is not valid outside the local + * scope of the calling function. Once the calling function + * returns, any returned pointers are invalid. + * + * @param name String containing the env.var name to search for + * @param envp String array pointer to the environment variable + * + * @return Returns a pointer to the value in the environment variable + * table on successful match. Otherwise NULL is returned + * + */ +static const char * +get_env(const char *name, const char *envp[]) +{ + if (envp) + { + int i; + const int namelen = strlen(name); + for (i = 0; envp[i]; ++i) + { + if (!strncmp(envp[i], name, namelen)) + { + const char *cp = envp[i] + namelen; + if (*cp == '=') + { + return cp + 1; + } + } + } + } + return NULL; +} + +/** + * Simple function ensuring callers of np() won't be hit by a NULL + * pointer on strings pointers. + * + * @param str String to control + * + * @return Returns the pointer to str if str is not NULL. Otherwise + * it will point at the static string "[NULL]". + */ +static const char * +np(const char *str) +{ + return (str != NULL ? str : "[NULL]"); +} + +/** + * Safe processing of string to unsigned int conversion. + * + * @param str Pointer to the string buffer containing the value to + * be converted + * + * @return Returns an unsigned int. In case of critical errors, the result + * will be 0. Otherwise the value will always be between 0 and + * UINT_MAX. + */ +static unsigned int +str2uint(const char *str) +{ + if (str) + { + char *eptr = NULL; + long int ret = strtol(str, &eptr, 10); + + if (str == eptr) + { + ovpn_log(PLOG_WARN, PLUGIN_NAME, + "Failed to extract a number out of '%s'. " + "Result will be 0.", + str); + ret = 0; + } + else if (*eptr != 0) + { + ovpn_log(PLOG_WARN, PLUGIN_NAME, + "The string '%s' contains more than a number. " + "Using %ld for further processing.", + str, ret); + } + + /* Restrict the value range from 0 to UINT_MAX. + * + * It might in some cases make sense to check errno != ERANGE, + * but in this case, our scope is much smaller. Plus we don't + * do any really special error handling than printing some + * warnings to the log + * + */ + if (ret >= UINT_MAX) + { + ovpn_log(PLOG_WARN, PLUGIN_NAME, + "The string '%s' is a number higher than the maximum " + "value %u. Capping to maximum value.", + str, UINT_MAX); + ret = UINT_MAX; + } + else if (ret < 0) + { + ovpn_log(PLOG_WARN, PLUGIN_NAME, + "The string '%s' is a number lower than 0. " + "Result will be 0.", + str); + ret = 0; + } + + return (unsigned int) ret; + } + + /* If input string is NULL ... */ + return 0; +} + +/** + * Cancel a running authentication or pf thread + * + * @param module Thread module type to cancel, MOD_AUTH or MOD_PF + * @param pcc Pointer to the client specific context, with information + * about the thread to cancel + * + * @return Returns OPENVPN_PLUGIN_FUNC_SUCCESS on success, otherwise + * OPENVPN_PLUGIN_FUNC_ERROR + */ +static int +cancel_thread(module_type module, struct plugin_per_client_context *pcc) +{ + int s; + pthread_t modthread = NULL; + char *module_str = NULL; + bool *running = NULL; + + switch (module) + { + case MOD_AUTH: + modthread = pcc->auththread; + running = &pcc->auth_running; + module_str = "authentication"; + break; + + case MOD_PF: + modthread = pcc->pfthread; + running = &pcc->pf_running; + module_str = "pf"; + break; + } + + ovpn_log(PLOG_NOTE, PLUGIN_NAME, + "Another %s thread is detected. " + "Cancelling that thread and waiting until it " + "has stopped", module_str); + + s = pthread_cancel(modthread); + if (s) + { + ovpn_log(PLOG_ERR, PLUGIN_NAME, + "** ERROR ** Failed to cancel other " + "%s thread: %s", + module_str, strerror(s)); + + /* Bailing out, to avoid more problems */ + return OPENVPN_PLUGIN_FUNC_ERROR; + } + + s = pthread_join(modthread, NULL); + if (s) + { + ovpn_log(PLOG_ERR, PLUGIN_NAME, + "** ERROR ** Failed to wait for other " + "%s thread to complete: %s", + module_str, strerror(s)); + } + ovpn_log(PLOG_NOTE, PLUGIN_NAME, + "The other %s thread have " + "been cancelled", + module_str); + *running = false; + + return OPENVPN_PLUGIN_FUNC_SUCCESS; +} + + +/** + * Free memory used in the client specific context for the authentication or pf + * processing threads + * + * @param module Thread module type to release memory for, MOD_AUTH or MOD_PF + * @param pcc Pointer to the client specific context to use for this + * operation + * + */ +static void +free_thread_data(module_type module, struct plugin_per_client_context *pcc) +{ + if (!pcc) { + return; + } + + switch (module) + { + case MOD_AUTH: + if (pcc->auth_control_file) + { + free(pcc->auth_control_file); + pcc->auth_control_file = NULL; + } + if (pcc->auth_uname) + { + free(pcc->auth_uname); + pcc->auth_uname = NULL; + } + if (pcc->auth_passw) + { + free(pcc->auth_passw); + pcc->auth_uname = NULL; + } + break; + + case MOD_PF: + if (pcc->pf_file) + { + free(pcc->pf_file); + pcc->pf_file = NULL; + } + if (pcc->auth_uname) + { + free(pcc->auth_uname); + pcc->auth_uname = NULL; + } + break; + } +} + +/** + * Main worker thread for Password authentication + * + * @param thread_data Pointer to the client specific context with information for the + * authentication process + * + * @return This thread does not return any information, and always exits by calling + * pthread_exit(NULL); + */ +static void * +authentication_thread(void *thread_data) +{ + struct plugin_per_client_context *pcc = (struct plugin_per_client_context *) thread_data; + int s; + + if (!pcc || !pcc->auth_control_file || !pcc->auth_passw || !pcc->auth_uname) + { + ovpn_log(PLOG_ERR, PLUGIN_NAME, + "[child:Auth] ** ERROR ** No proper client context buffer received (Authentication)"); + pthread_exit(NULL); + } + + /* Make it possible to cancel this thread, if another authentication + * process is attempted in the same context. This can happen if the + * initial authentication request is aborted by the user and then + * starting a new one before this thread have completed. + */ + s = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + if (s) + { + ovpn_log(PLOG_ERR, PLUGIN_NAME, + "[child:Auth] ** ERROR ** Failed to set thread cancel state: %s", + strerror(s)); + /* For this demonstration plug-in, we ignore this error */ + } + s = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + if (s) + { + ovpn_log(PLOG_ERR, PLUGIN_NAME, + "[child:Auth] ** ERROR ** Failed to set thread cancel type: %s", + strerror(s)); + /* For this demonstration plug-in, we ignore this error */ + } + + /* + * Start the authentication phase + */ + + ovpn_log(PLOG_NOTE, PLUGIN_NAME, "[child:Auth] Sleeping %i seconds", + pcc->auth_delay); + pcc->auth_running = true; + sleep(pcc->auth_delay); + + /* This file needs to be created by the plug-in, to report back + * the authentication result + */ + FILE *authres = fopen(pcc->auth_control_file, "w"); + if (!authres) + { + ovpn_log(PLOG_ERR, PLUGIN_NAME, + "[child:Auth] ** ERROR ** Failed to open auth control file (%s)", + pcc->auth_control_file); + pthread_exit(NULL); + } + + /* WARNING: Extremely simple and stupid user authentication. + * + * This password check only checks if the password is 'foobar' or not. + * + * The reason we here don't check the username, is so the + * PF thread can be tested too, which will use the username when + * installing a PF filter. This makes it easier to test a variety + * of PF configurations without adding several hard coded usernames + * and passwords. + * + */ + int res = (pcc->auth_passw != NULL && 0 == strcmp(pcc->auth_passw, "foobar") ? 1 : 0); + + /* Report authentication result back to OpenVPN. + * Successful authentication == 1, failed == 0 + */ + fprintf(authres, "%i\n", res); + fclose(authres); + + /* WARNING: The line below LOGS PASSWORDS + * This is just for demonstration/debug purpose. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * !! DO NOT DO THIS IN A REAL AUTHENTICATION PLUGIN !! + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + ovpn_log(PLOG_NOTE, PLUGIN_NAME, + "[child:Auth]: AUTH completed - %s u: '%s' p: '%s' [%i]", + pcc->auth_control_file, pcc->auth_uname, pcc->auth_passw, res); + + + /* Wipe the password from memory + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * !! DO NOT be tempted to use memset() for this, many compilers !! + * !! may remove that call when optimizing the code result. !! + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * WARNING: This apprach here is SIMPLISTIC. See secure_memzero() + * in the OpenVPN source code (src/openvpn/buffer.h) for an + * even better implementation. + * + */ + if (pcc->auth_passw) + { + for (char *p = pcc->auth_passw + strlen(pcc->auth_passw); + p >= pcc->auth_passw; p-- ) + { + *p = 0; + } + free(pcc->auth_passw); + pcc->auth_passw = NULL; + } + + pcc->auth_running = false; + pthread_exit(NULL); +} + + +/** + * Main function for deferred password authentication + * + * @param context Pointer to the plug-in global context + * @param pcc Pointer from OpenVPN pointing at a client session + * specific context + * @param argv Pointer to the argument vectors for the plug-in + * configuration + * @param envp Pointer to the environment variable vectors prepared + * by OpenVPN. Contains session and configuration specific + * details. + * + * @return Returns OPENVPN_PLUGIN_FUNC_SUCCESS if the password authentication + * have not been configured (test_deferred_auth < 1) + * Returns OPENVPN_PLUGIN_FUNC_DEFERRED if a authentication thread + * have been successfully started. This thread will + * asynchronously return the authentication result directly + * to OpenVPN + * Otherwise OPENVPN_PLUGIN_FUNC_ERROR is returned in error situations. + */ +static int +auth_user_pass_verify(struct plugin_context *context, + struct plugin_per_client_context *pcc, + const char *argv[], const char *envp[]) +{ + if (context->test_deferred_auth < 1) + { + /* No authentication thread was requested. + * + * This is requested by adding --setenv test_deferred_auth $sec + * in the OpenVPN configuration, where $sec is number of seconds the + * authentication should be delayed + * + * In this case we must return with success, otherwise the client + * connection will be rejected. + */ + return OPENVPN_PLUGIN_FUNC_SUCCESS; + } + + /* get username/password/auth_control_file from envp string array */ + const char *username = get_env("username", envp); + const char *password = get_env("password", envp); + const char *auth_control_file = get_env("auth_control_file", envp); + + ovpn_log(PLOG_NOTE, PLUGIN_NAME, "DEFER u='%s' p='%s' acf='%s'", + np(username), + np(password), + np(auth_control_file)); + + /* Ensure we have the needed information */ + if (!username || !password || !auth_control_file) + { + ovpn_log(PLOG_ERR, PLUGIN_NAME, + "[parent:Auth] Missing username, password or auth_control_file " + "information needed for the authentication"); + return OPENVPN_PLUGIN_FUNC_ERROR; + } + + + /* To avoid back-clash between multiple auth-treads on the same client + * context, don't start a new one + * + */ + if (pcc->auth_running) + { + /* Cleaning up memory used by the other thread */ + free_thread_data(MOD_AUTH, pcc); + + if (cancel_thread(MOD_AUTH, pcc) == OPENVPN_PLUGIN_FUNC_ERROR) + { + return OPENVPN_PLUGIN_FUNC_ERROR; + } + } + + /* Information the authentication thread wil need. It is important that + * the memory buffers are created for each call, as once this function + * exits the environment variable pointers are no longer valid + */ + pcc->auth_delay = context->test_deferred_auth; + pcc->auth_control_file = strdup(auth_control_file); + pcc->auth_running = false; + pcc->auth_uname = strdup(username); + pcc->auth_passw = strdup(password); + + /* + * Start the authentication thread, which will be delayed + * test_deferred_auth seconds. + * + * WARNING: + * This implementation does not care how the authentication thread exists. + * A more proper and solid implementation would track this and report + * issues with these threads, outside of the authenticaion thread itself. + * This implementation _may_ result in a little memory leak due to this. + */ + if (pthread_create(&pcc->auththread, NULL, authentication_thread, pcc)) + { + ovpn_log(PLOG_ERR, PLUGIN_NAME, + "[parent:Auth] Failed to create authentication thread"); + return OPENVPN_PLUGIN_FUNC_ERROR; + } + + /* Signalize to OpenVPN that the authentication result is not yet ready */ + return OPENVPN_PLUGIN_FUNC_DEFERRED; +} + + +/** + * Main worker thread for PF initialization + * + * @param thread_data Pointer to the client specific context with information for the + * pf init process + * + * @return This thread does not return any information, and always exits by calling + * pthread_exit(NULL); + */ +static void * +pf_thread(void *thread_data) +{ + struct plugin_per_client_context *pcc = (struct plugin_per_client_context *) thread_data; + int s; + + if (!pcc || !pcc->pf_file || !pcc->auth_uname) + { + ovpn_log(PLOG_ERR, PLUGIN_NAME, + "[child:PF] ** ERROR ** No proper client context buffer received"); + pthread_exit(NULL); + } + + /* Make it possible to cancel this thread, if another authentication + * process is attempted in the same context. This can happen if the + * initial authentication request is aborted by the user and then + * starting a new one before this thread have completed. + */ + s = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + if (s) + { + ovpn_log(PLOG_ERR, PLUGIN_NAME, + "[child:PF] ** ERROR ** Failed to set thread cancel state: %s", + strerror(s)); + /* For this demonstration plug-in, we ignore this error */ + } + s = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + if (s) + { + ovpn_log(PLOG_ERR, PLUGIN_NAME, + "[chilld:PF] ** ERROR ** Failed to set thread cancel type: %s", + strerror(s)); + /* For this demonstration plug-in, we ignore this error */ + } + + /* + * Start the authentication phase + */ + + ovpn_log(PLOG_NOTE, PLUGIN_NAME, "[child:PF] Sleeping %i seconds", + pcc->pf_delay); + pcc->pf_running = true; + sleep(pcc->pf_delay); + + /* Put together the PF source file name */ + char srcf[130]; + snprintf(srcf, 128, "%s.pf", pcc->auth_uname); + + if (access(srcf, R_OK) != 0) + { + ovpn_log(PLOG_ERR, PLUGIN_NAME, + "[child:PF] ** ERROR ** Cannot read the PF profile '%s'", + srcf); + pcc->pf_running = false; + pthread_exit(NULL); + } + + /* Copy the PF profile to to the file OpenVPN have indicated it will + * read it from + */ + ovpn_log(PLOG_NOTE, PLUGIN_NAME, + "[child:PF] Copying %s to %s", srcf, pcc->pf_file); + + /* We need to fork out a child process, which will do the execve() call */ + pid_t pid = fork(); + if (pid == 0) + { + /* Child process */ + char *prg_args[] = { "/bin/cp", srcf, pcc->pf_file, NULL }; + execve(prg_args[0], prg_args, NULL); + exit(1); + } + else if (pid > 0) + { + /* parent process */ + int ret; + int s = waitpid(pid, &ret, 0); + if (s != pid) + { + pcc->generated_pf_file = false; /* /bin/cp could not be run */ + ovpn_log(PLOG_ERR, PLUGIN_NAME, + "[chilld:PF] Failed to copy file. Could not execute /bin/cp"); + } + + if (ret != 0) + { + /* Exit code was _not_ 0, presume failure */ + pcc->generated_pf_file = false; + ovpn_log(PLOG_ERR, PLUGIN_NAME, + "[chilld:PF] Failed to copy file. Unkown reason."); + } + else + { + /* Exit code was 0, presume success */ + pcc->generated_pf_file = true; + } + } + else + { + ovpn_log(PLOG_ERR, PLUGIN_NAME, + "[chilld:PF] Failed to fork file copy process"); + pcc->generated_pf_file = false; + } + + /* Exit the worker thread */ + pcc->pf_running = false; + pthread_exit(NULL); +} + + +/** + * Main function for adding a PF profile to a client session + * + * @param context Pointer to the plug-in global context + * @param pcc Pointer from OpenVPN pointing at a client session + * specific context + * @param argv Pointer to the argument vectors for the plug-in + * configuration + * @param envp Pointer to the environment variable vectors prepared + * by OpenVPN. Contains session and configuration specific + * details. + * + * @return Returns OPENVPN_PLUGIN_FUNC_SUCCESS if the worker thread was started + * successfully. This is also returned if the PF test is disabled + * (test_packet_filter < 1) + * + * Otherwise OPENVPN_PLUGIN_FUNC_ERROR is returned in error situations. + */ +static int +tls_final(struct plugin_context *context, + struct plugin_per_client_context *pcc, + const char *argv[], + const char *envp[]) +{ + + /* If PF is not enabled, consider this phase completed successfully */ + if (!context->test_packet_filter) + { + return OPENVPN_PLUGIN_FUNC_SUCCESS; + } + + /* If the PF file have already been copied to OpenVPN's import file */ + if (pcc->generated_pf_file) + { + return OPENVPN_PLUGIN_FUNC_ERROR; + } + + /* Get pf_file and username from envp string array */ + const char *pff = get_env("pf_file", envp); + const char *username = get_env("username", envp); + if (!pff || !username) + { + ovpn_log(PLOG_ERR, PLUGIN_NAME, + "[parent:PF] Missing pf_file or username information needed for " + "the pf initialization."); + return OPENVPN_PLUGIN_FUNC_ERROR; + } + + /* To avoid back-clash between multiple PF treads on the same client + * context, don't start a new one + */ + if (pcc->pf_running) + { + /* Cleaning up memory used by the other thread */ + free_thread_data(MOD_PF, pcc); + + if (cancel_thread(MOD_PF, pcc) == OPENVPN_PLUGIN_FUNC_ERROR) + { + return OPENVPN_PLUGIN_FUNC_ERROR; + } + } + + /* Information the pf thread wil need. It is important that + * the memory buffers are created for each call, as once this function + * exits the environment variable pointers are no longer valid + */ + pcc->pf_delay = context->test_packet_filter; + pcc->pf_file = strdup(pff); + pcc->pf_running = false; + pcc->generated_pf_file = false; + pcc->auth_uname = strdup(username); + + /* + * Start the pf worker thread + * + * WARNING: + * This implementation does not care how the authentication thread exists. + * A more proper and solid implementation would track this and report + * issues with these threads, outside of the authenticaion thread itself. + * This implementation _may_ result in a little memory leak due to this. + */ + if (pthread_create(&pcc->pfthread, NULL, pf_thread, pcc)) + { + ovpn_log(PLOG_ERR, PLUGIN_NAME, + "[parent:PF] Failed to create pf thread"); + return OPENVPN_PLUGIN_FUNC_ERROR; + } + + /* Signalize to OpenVPN that the pf init phase is done */ + return OPENVPN_PLUGIN_FUNC_SUCCESS; +} + + +/** + * This function is called when OpenVPN loads the plug-in. + * The purpose is to initialize the plug-in and tell OpenVPN + * which plug-in hooks this plug-in wants to be involved in + * + * For the arguments, see the include/openvpn-plugin.h file + * for details on the function parameters + * + * @param v3structver An integer containing the API version of + * the plug-in structs OpenVPN uses + * @param args A pointer to the argument struct for + * information and features provided by + * OpenVPN to the plug-in + * @param ret A pointer to the struct OpenVPN uses to + * receive information back from the plug-in + * + * @return Must return OPENVPN_PLUGIN_FUNC_SUCCESS when everything + * completed successfully. Otherwise it must be returned + * OPENVPN_PLUGIN_FUNC_ERROR, which will stop OpenVPN + * from running + * + */ +OPENVPN_EXPORT int +openvpn_plugin_open_v3(const int v3structver, + struct openvpn_plugin_args_open_in const *args, + struct openvpn_plugin_args_open_return *ret) +{ + struct plugin_context *context; + + /* Sanity check, to avoid issues when accessing plug-in API structs. + * This check is very strict, and requires an identical match. + * For some implementations, ensuring + * + * v3structver >= OPENVPN_PLUGINv3_STRUCTVER + * + * might be enough. But that depends on what kind of structs + * which are used by the plug-in + */ + if (v3structver != OPENVPN_PLUGINv3_STRUCTVER) + { + printf("defer/simple.c: ** ERROR ** Incompatible plug-in interface" + "between this plug-in and the OpenVPN plug-in API\n"); + return OPENVPN_PLUGIN_FUNC_ERROR; + } + + /* Hook into the log functions for plug-ins */ + ovpn_log = args->callbacks->plugin_log; + ovpn_vlog = args->callbacks->plugin_vlog; + + ovpn_log(PLOG_NOTE, PLUGIN_NAME, "FUNC: openvpn_plugin_open_v3"); + + /* Allocate our context and extract a few settings */ + context = (struct plugin_context *) calloc(1, sizeof(struct plugin_context)); + + /* The test_deferred_auth env variable defines if we will + * test password authentication + */ + context->test_deferred_auth = str2uint(get_env("test_deferred_auth", args->envp)); + ovpn_log(PLOG_NOTE, PLUGIN_NAME, "TEST_DEFERRED_AUTH %d", context->test_deferred_auth); + + /* The test_packet_filter env variable defines if we will + * test the OpenVPN packet filter (PF) feature + */ + context->test_packet_filter = str2uint(get_env("test_packet_filter", args->envp)); + ovpn_log(PLOG_NOTE, PLUGIN_NAME, "TEST_PACKET_FILTER %d", context->test_packet_filter); + + ret->handle = (void *) context; + + /* Which callbacks this plug-in will handle */ + ret->type_mask = OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_UP) + | OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_DOWN) + | OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_ROUTE_UP) + | OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_IPCHANGE) + | OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_TLS_VERIFY) + | OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY) + | OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_CLIENT_CONNECT_V2) + | OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_CLIENT_DISCONNECT) + | OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_LEARN_ADDRESS) + | OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_TLS_FINAL) + | OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_ENABLE_PF); + + return OPENVPN_PLUGIN_FUNC_SUCCESS; +} + + +/** + * This function is called by OpenVPN each time the OpenVPN reaches + * a point where plug-in calls should happen. It only happens for those + * plug-in hooks enabled in openvpn_plugin_open_v3(). + * + * For the arguments, see the include/openvpn-plugin.h file + * for details on the function parameters + * + * @param args Pointer to a struct with details about the plug-in + * call from the main OpenVPN process. + * @param returndata Pointer to a struct where the plug-in can provide + * information back to OpenVPN to be processed + * + * @returns Must return OPENVPN_PLUGIN_FUNC_SUCCESS or + * OPENVPN_PLUGIN_FUNC_DEFERRED on success. Otherwise it + * should return OPENVPN_FUNC_ERROR, which will stop and reject + * the client session from progressing. + * + */ +OPENVPN_EXPORT int +openvpn_plugin_func_v3(const int apiversion, + struct openvpn_plugin_args_func_in const *args, + struct openvpn_plugin_args_func_return *returndata) +{ + struct plugin_context *context = (struct plugin_context *) args->handle; + struct plugin_per_client_context *pcc = (struct plugin_per_client_context *) args->per_client_context; + + /* This plug-in uses both the generic plug-in context for the plug-in + * itself and a context per client session. So ensure these are not + * NULL. If your plug-in does not need this, those pointers can be + * ignored. + * + * These pointers are normally NULL and the needed buffers must be + * managed and maintained by the plug-in exclusively. OpenVPN itself + * does not touch these buffers at all. The per_client_context buffer + * must be allocated in the openvpn_plugin_client_constructor_v1() + * function and free()d in openvpn_plugin_client_destructor_v1(). + * + */ + if (!context || !pcc) + { + ovpn_log(PLOG_ERR, PLUGIN_NAME, "Needed context buffers are not available. Aborting."); + return OPENVPN_PLUGIN_FUNC_ERROR; + } + + /* Dispatch the various plug-in hooks to the appropriate handler. + * You only need to add a handler for those hooks you've enabled in + * openvpn_plugin_open_v3() + */ + switch (args->type) + { + case OPENVPN_PLUGIN_UP: + ovpn_log(PLOG_NOTE, PLUGIN_NAME, "OPENVPN_PLUGIN_UP"); + return OPENVPN_PLUGIN_FUNC_SUCCESS; + + case OPENVPN_PLUGIN_DOWN: + ovpn_log(PLOG_NOTE, PLUGIN_NAME, "OPENVPN_PLUGIN_DOWN"); + return OPENVPN_PLUGIN_FUNC_SUCCESS; + + case OPENVPN_PLUGIN_ROUTE_UP: + ovpn_log(PLOG_NOTE, PLUGIN_NAME, "OPENVPN_PLUGIN_ROUTE_UP"); + return OPENVPN_PLUGIN_FUNC_SUCCESS; + + case OPENVPN_PLUGIN_IPCHANGE: + ovpn_log(PLOG_NOTE, PLUGIN_NAME, "OPENVPN_PLUGIN_IPCHANGE"); + return OPENVPN_PLUGIN_FUNC_SUCCESS; + + case OPENVPN_PLUGIN_TLS_VERIFY: + ovpn_log(PLOG_NOTE, PLUGIN_NAME, "OPENVPN_PLUGIN_TLS_VERIFY"); + return OPENVPN_PLUGIN_FUNC_SUCCESS; + + case OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY: + ovpn_log(PLOG_NOTE, PLUGIN_NAME, "OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY"); + + /* Handle password authentication in its own function */ + return auth_user_pass_verify(context, pcc, args->argv, args->envp); + + case OPENVPN_PLUGIN_CLIENT_CONNECT_V2: + ovpn_log(PLOG_NOTE, PLUGIN_NAME, "OPENVPN_PLUGIN_CLIENT_CONNECT_V2"); + return OPENVPN_PLUGIN_FUNC_SUCCESS; + + case OPENVPN_PLUGIN_CLIENT_DISCONNECT: + ovpn_log(PLOG_NOTE, PLUGIN_NAME, "OPENVPN_PLUGIN_CLIENT_DISCONNECT"); + return OPENVPN_PLUGIN_FUNC_SUCCESS; + + case OPENVPN_PLUGIN_LEARN_ADDRESS: + ovpn_log(PLOG_NOTE, PLUGIN_NAME, "OPENVPN_PLUGIN_LEARN_ADDRESS"); + return OPENVPN_PLUGIN_FUNC_SUCCESS; + + case OPENVPN_PLUGIN_TLS_FINAL: + ovpn_log(PLOG_NOTE, PLUGIN_NAME, "OPENVPN_PLUGIN_TLS_FINAL"); + + /* This function prepares the Packet Filter (PF) in OpenVPN */ + return tls_final(context, pcc, args->argv, args->envp); + + case OPENVPN_PLUGIN_ENABLE_PF: + ovpn_log(PLOG_NOTE, PLUGIN_NAME, "OPENVPN_PLUGIN_ENABLE_PF"); + + /* Indicate if OpenVPN should enable PF for this session */ + if (context->test_packet_filter) + { + ovpn_log(PLOG_NOTE, PLUGIN_NAME, + "PF enabled"); + return OPENVPN_PLUGIN_FUNC_SUCCESS; + } + else + { + ovpn_log(PLOG_NOTE, PLUGIN_NAME, + "PF disabled"); + return OPENVPN_PLUGIN_FUNC_ERROR; + } + + default: + ovpn_log(PLOG_NOTE, PLUGIN_NAME, + "** ERROR ** Unknown OpenVPN plug-in function call (%i)", + args->type); + return OPENVPN_PLUGIN_FUNC_ERROR; + } +} + +/** + * This function's main responsibility is to allocate memory for the + * client session context, which is unique per client session. + * + * @param handle Pointer to a plug-in global context buffer + * + * @return Returns a pointer to the newly allocated client session context + */ +OPENVPN_EXPORT void * +openvpn_plugin_client_constructor_v1(openvpn_plugin_handle_t handle) +{ + struct plugin_per_client_context *pcc; + + ovpn_log(PLOG_NOTE, PLUGIN_NAME, "FUNC: openvpn_plugin_client_constructor_v1"); + pcc = calloc(1, sizeof(struct plugin_per_client_context)); + if (!pcc) + { + ovpn_log(PLOG_ERR, PLUGIN_NAME, + "** ERROR ** Failed to allocate memory a the client context"); + } + return pcc; +} + + +/** + * This function is responsible for releasing memory buffers used by + * the client session context and ensure all related running jobs have + * completed before this function cleans up and returns. + * + * @param handle Pointer to a plug-in global context buffer + * @param ppcp Pointer to the client session context buffer to be released + * + */ +OPENVPN_EXPORT void +openvpn_plugin_client_destructor_v1(openvpn_plugin_handle_t handle, void *ppcp) +{ + struct plugin_per_client_context *pcc = (struct plugin_per_client_context *) ppcp; + + ovpn_log(PLOG_NOTE, PLUGIN_NAME, "FUNC: openvpn_plugin_client_destructor_v1"); + + if (pcc->auth_running) + { + ovpn_log(PLOG_WARN, PLUGIN_NAME, + "** WARNING ** An authentication thread is still running, " + "waiting for it to complete"); + int s = pthread_join(pcc->auththread, NULL); + if (s) + { + ovpn_log(PLOG_ERR, PLUGIN_NAME, + "** ERROR ** Failed to wait for the authentication " + "thread to complete: %s", + strerror(s)); + } + } + + if (pcc->pf_running) + { + ovpn_log(PLOG_WARN, PLUGIN_NAME, + "** WARNING ** A PF thread is still running, " + "waiting for it to complete"); + int s = pthread_join(pcc->pfthread, NULL); + if (s) + { + ovpn_log(PLOG_ERR, PLUGIN_NAME, + "** ERROR ** Failed to wait for the PF thread " + "to complete: %s", + strerror(s)); + } + } + + free_thread_data(MOD_AUTH, pcc); + free_thread_data(MOD_PF, pcc); + free(pcc); +} + + +/** + * This cleans up the last part of the plug-in, allows it to + * shut down cleanly and release the plug-in global context buffer + * + * @param handle Pointer to the plug-in global context buffer, which + * need to be released by this function + */ +OPENVPN_EXPORT void +openvpn_plugin_close_v1(openvpn_plugin_handle_t handle) +{ + struct plugin_context *context = (struct plugin_context *) handle; + ovpn_log(PLOG_NOTE, PLUGIN_NAME, "FUNC: openvpn_plugin_close_v1"); + free(context); +} diff --git a/sample/sample-plugins/defer/simple.def b/sample/sample-plugins/defer/defer-w-pf.def similarity index 100% rename from sample/sample-plugins/defer/simple.def rename to sample/sample-plugins/defer/defer-w-pf.def diff --git a/sample/sample-plugins/defer/simple.c b/sample/sample-plugins/defer/simple.c deleted file mode 100644 index ad1bbb0f..00000000 --- a/sample/sample-plugins/defer/simple.c +++ /dev/null @@ -1,340 +0,0 @@ -/* - * OpenVPN -- An application to securely tunnel IP networks - * over a single TCP/UDP port, with support for SSL/TLS-based - * session authentication and key exchange, - * packet encryption, packet authentication, and - * packet compression. - * - * Copyright (C) 2002-2017 OpenVPN Technologies, Inc. <sa...@openvpn.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * 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 (see the file COPYING included with this - * distribution); if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -/* - * This file implements a simple OpenVPN plugin module which - * will test deferred authentication and packet filtering. - * - * Will run on Windows or *nix. - * - * Sample usage: - * - * setenv test_deferred_auth 20 - * setenv test_packet_filter 10 - * plugin plugin/defer/simple.so - * - * This will enable deferred authentication to occur 20 - * seconds after the normal TLS authentication process, - * and will cause a packet filter file to be generated 10 - * seconds after the initial TLS negotiation, using - * {common-name}.pf as the source. - * - * Sample packet filter configuration: - * - * [CLIENTS DROP] - * +otherclient - * [SUBNETS DROP] - * +10.0.0.0/8 - * -10.10.0.8 - * [END] - * - * See the README file for build instructions. - */ - -#include <stdio.h> -#include <string.h> -#include <stdlib.h> - -#include "openvpn-plugin.h" - -/* bool definitions */ -#define bool int -#define true 1 -#define false 0 - -/* - * Our context, where we keep our state. - */ - -struct plugin_context { - int test_deferred_auth; - int test_packet_filter; -}; - -struct plugin_per_client_context { - int n_calls; - bool generated_pf_file; -}; - -/* - * Given an environmental variable name, search - * the envp array for its value, returning it - * if found or NULL otherwise. - */ -static const char * -get_env(const char *name, const char *envp[]) -{ - if (envp) - { - int i; - const int namelen = strlen(name); - for (i = 0; envp[i]; ++i) - { - if (!strncmp(envp[i], name, namelen)) - { - const char *cp = envp[i] + namelen; - if (*cp == '=') - { - return cp + 1; - } - } - } - } - return NULL; -} - -/* used for safe printf of possible NULL strings */ -static const char * -np(const char *str) -{ - if (str) - { - return str; - } - else - { - return "[NULL]"; - } -} - -static int -atoi_null0(const char *str) -{ - if (str) - { - return atoi(str); - } - else - { - return 0; - } -} - -OPENVPN_EXPORT openvpn_plugin_handle_t -openvpn_plugin_open_v1(unsigned int *type_mask, const char *argv[], const char *envp[]) -{ - struct plugin_context *context; - - printf("FUNC: openvpn_plugin_open_v1\n"); - - /* - * Allocate our context - */ - context = (struct plugin_context *) calloc(1, sizeof(struct plugin_context)); - - context->test_deferred_auth = atoi_null0(get_env("test_deferred_auth", envp)); - printf("TEST_DEFERRED_AUTH %d\n", context->test_deferred_auth); - - context->test_packet_filter = atoi_null0(get_env("test_packet_filter", envp)); - printf("TEST_PACKET_FILTER %d\n", context->test_packet_filter); - - /* - * Which callbacks to intercept. - */ - *type_mask = - OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_UP) - |OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_DOWN) - |OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_ROUTE_UP) - |OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_IPCHANGE) - |OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_TLS_VERIFY) - |OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY) - |OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_CLIENT_CONNECT_V2) - |OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_CLIENT_DISCONNECT) - |OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_LEARN_ADDRESS) - |OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_TLS_FINAL) - |OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_ENABLE_PF); - - return (openvpn_plugin_handle_t) context; -} - -static int -auth_user_pass_verify(struct plugin_context *context, struct plugin_per_client_context *pcc, const char *argv[], const char *envp[]) -{ - if (context->test_deferred_auth) - { - /* get username/password from envp string array */ - const char *username = get_env("username", envp); - const char *password = get_env("password", envp); - - /* get auth_control_file filename from envp string array*/ - const char *auth_control_file = get_env("auth_control_file", envp); - - printf("DEFER u='%s' p='%s' acf='%s'\n", - np(username), - np(password), - np(auth_control_file)); - - /* Authenticate asynchronously in n seconds */ - if (auth_control_file) - { - char buf[256]; - int auth = 2; - sscanf(username, "%d", &auth); - snprintf(buf, sizeof(buf), "( sleep %d ; echo AUTH %s %d ; echo %d >%s ) &", - context->test_deferred_auth, - auth_control_file, - auth, - pcc->n_calls < auth, - auth_control_file); - printf("%s\n", buf); - system(buf); - pcc->n_calls++; - return OPENVPN_PLUGIN_FUNC_DEFERRED; - } - else - { - return OPENVPN_PLUGIN_FUNC_ERROR; - } - } - else - { - return OPENVPN_PLUGIN_FUNC_SUCCESS; - } -} - -static int -tls_final(struct plugin_context *context, struct plugin_per_client_context *pcc, const char *argv[], const char *envp[]) -{ - if (context->test_packet_filter) - { - if (!pcc->generated_pf_file) - { - const char *pff = get_env("pf_file", envp); - const char *cn = get_env("username", envp); - if (pff && cn) - { - char buf[256]; - snprintf(buf, sizeof(buf), "( sleep %d ; echo PF %s/%s ; cp \"%s.pf\" \"%s\" ) &", - context->test_packet_filter, cn, pff, cn, pff); - printf("%s\n", buf); - system(buf); - pcc->generated_pf_file = true; - return OPENVPN_PLUGIN_FUNC_SUCCESS; - } - else - { - return OPENVPN_PLUGIN_FUNC_ERROR; - } - } - else - { - return OPENVPN_PLUGIN_FUNC_ERROR; - } - } - else - { - return OPENVPN_PLUGIN_FUNC_SUCCESS; - } -} - -OPENVPN_EXPORT int -openvpn_plugin_func_v2(openvpn_plugin_handle_t handle, - const int type, - const char *argv[], - const char *envp[], - void *per_client_context, - struct openvpn_plugin_string_list **return_list) -{ - struct plugin_context *context = (struct plugin_context *) handle; - struct plugin_per_client_context *pcc = (struct plugin_per_client_context *) per_client_context; - switch (type) - { - case OPENVPN_PLUGIN_UP: - printf("OPENVPN_PLUGIN_UP\n"); - return OPENVPN_PLUGIN_FUNC_SUCCESS; - - case OPENVPN_PLUGIN_DOWN: - printf("OPENVPN_PLUGIN_DOWN\n"); - return OPENVPN_PLUGIN_FUNC_SUCCESS; - - case OPENVPN_PLUGIN_ROUTE_UP: - printf("OPENVPN_PLUGIN_ROUTE_UP\n"); - return OPENVPN_PLUGIN_FUNC_SUCCESS; - - case OPENVPN_PLUGIN_IPCHANGE: - printf("OPENVPN_PLUGIN_IPCHANGE\n"); - return OPENVPN_PLUGIN_FUNC_SUCCESS; - - case OPENVPN_PLUGIN_TLS_VERIFY: - printf("OPENVPN_PLUGIN_TLS_VERIFY\n"); - return OPENVPN_PLUGIN_FUNC_SUCCESS; - - case OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY: - printf("OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY\n"); - return auth_user_pass_verify(context, pcc, argv, envp); - - case OPENVPN_PLUGIN_CLIENT_CONNECT_V2: - printf("OPENVPN_PLUGIN_CLIENT_CONNECT_V2\n"); - return OPENVPN_PLUGIN_FUNC_SUCCESS; - - case OPENVPN_PLUGIN_CLIENT_DISCONNECT: - printf("OPENVPN_PLUGIN_CLIENT_DISCONNECT\n"); - return OPENVPN_PLUGIN_FUNC_SUCCESS; - - case OPENVPN_PLUGIN_LEARN_ADDRESS: - printf("OPENVPN_PLUGIN_LEARN_ADDRESS\n"); - return OPENVPN_PLUGIN_FUNC_SUCCESS; - - case OPENVPN_PLUGIN_TLS_FINAL: - printf("OPENVPN_PLUGIN_TLS_FINAL\n"); - return tls_final(context, pcc, argv, envp); - - case OPENVPN_PLUGIN_ENABLE_PF: - printf("OPENVPN_PLUGIN_ENABLE_PF\n"); - if (context->test_packet_filter) - { - return OPENVPN_PLUGIN_FUNC_SUCCESS; - } - else - { - return OPENVPN_PLUGIN_FUNC_ERROR; - } - - default: - printf("OPENVPN_PLUGIN_?\n"); - return OPENVPN_PLUGIN_FUNC_ERROR; - } -} - -OPENVPN_EXPORT void * -openvpn_plugin_client_constructor_v1(openvpn_plugin_handle_t handle) -{ - printf("FUNC: openvpn_plugin_client_constructor_v1\n"); - return calloc(1, sizeof(struct plugin_per_client_context)); -} - -OPENVPN_EXPORT void -openvpn_plugin_client_destructor_v1(openvpn_plugin_handle_t handle, void *per_client_context) -{ - printf("FUNC: openvpn_plugin_client_destructor_v1\n"); - free(per_client_context); -} - -OPENVPN_EXPORT void -openvpn_plugin_close_v1(openvpn_plugin_handle_t handle) -{ - struct plugin_context *context = (struct plugin_context *) handle; - printf("FUNC: openvpn_plugin_close_v1\n"); - free(context); -} -- 2.11.0 ------------------------------------------------------------------------------ Check out the vibrant tech community on one of the world's most engaging tech sites, Slashdot.org! http://sdm.link/slashdot _______________________________________________ Openvpn-devel mailing list Openvpn-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/openvpn-devel