Hi, Looks like a great addition. Some minor comments below. (I'm going somewhat pedantic here, because of the 'example' character of this plugin.)
One high-level comment: shouldn't this be two separate plugins? Right now this is quite some code, which is partly caused because this implements two (quite different) plugin features. Multiple plugins are supported, right? On 09-05-17 20:49, David Sommerseth wrote: > 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> > > --- > v3 - Replace the too simplistic password wiper with the new > plugin_secure_memzero() > > 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 | 1057 > ++++++++++++++++++++ > .../defer/{simple.def => defer-w-pf.def} | 0 > sample/sample-plugins/defer/simple.c | 340 ------- > 6 files changed, 1117 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 > + Let's make these = be ?= instead, makes it easier to override for e.g. out-of-tree builds. > +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..c9b7479b > --- /dev/null > +++ b/sample/sample-plugins/defer/defer-w-pf.c > @@ -0,0 +1,1057 @@ > +/* > + * 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() */ > +plugin_secure_memzero_t ovpn_secure_memzero = NULL; /**< Pointer to the > OpenVPN secure_memzero() function */ > + > + > +/** > + * 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. This is not really true, right? The returned pointer points to data in envp[], to the lifetime is equal to that of envp (which may be on the stack of the calling function, but does not need to be). I think the text in @return already covers this nicely. > + * @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. > + */ Hm, 'critical' error sounds like we should stop processing all together. To me it seems this just returns 0 on 'error'. > +static unsigned int > +str2uint(const char *str) > +{ > + if (str) > + { > + char *eptr = NULL; > + long int ret = strtol(str, &eptr, 10); Why not just wrap strtoul(), instead of strtol()? > + > + 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; My compiler says: defer-w-pf.c:222:27: warning: initialization makes integer from pointer without a cast [-Wint-conversion] 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; s/auth_uname/auth_passwd/ Also might need a secure_memzero() ? > + } > + 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); Any reason for the "? 1 : 0" ? To me it feels like obfuscation ;-) > + > + /* 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. !! > + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > + * > + */ > + if (pcc->auth_passw) > + { > + ovpn_secure_memzero(pcc->auth_passw, strlen(pcc->auth_passw)); > + 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. > + */ s/have been/has been/ > +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 s/wil/will/ > + * 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. s/authenticaion/authentication/ > + * 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 */ > + } This code, including all the comments, are duplicated in both threads. Should probably go into a function. > + /* > + * 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]; = {0} ? > + snprintf(srcf, 128, "%s.pf", pcc->auth_uname); s/128/sizeof(srcf)/ > + > + 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; > + } Hm, wouldn't it be easier (and slightly more generic) to just open the two files and read/write the data (i.e. reimplement cp)? (But this is more your field of expertise than mine, so feel free to ignore me.) > + /* 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 s/wil/will/ > + * 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); Doesn't this leak memory if both the PF and auth parts are used? It seems that pcc->auth_uname then already points to memory that was allocated by the strdup() call in auth_user_pass_verify(). > + > + /* > + * 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. s/authenticaion/authentication/ > + * 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; I'm guessing that _DEFERRED is not allowed here? In that case this still needs a warning that the connection may be active before the PF profile is initialized. So this is only 'safe' if the default profile is less permissive than the connection profile. > +} > + > + > +/** > + * 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; Let's add an "= NULL" here to avoid future mistakes. > + /* 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_secure_memzero = args->callbacks->plugin_secure_memzero; > + > + ovpn_log(PLOG_NOTE, PLUGIN_NAME, "FUNC: openvpn_plugin_open_v3"); Using __func__ instead of writing the function name prevents inconsistencies. > + /* Allocate our context and extract a few settings */ > + context = (struct plugin_context *) calloc(1, sizeof(struct > plugin_context)); In these situations, it's usually better to use sizeof(*context), because that is always consistent with the actual object being allocated. (Also, calloc() return a void *, so the cast is not needed, right?) > + /* 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)); context->test_deferred_auth in an 'int', while str2uint() returns an unsigned int. Shouldn't both be unsigned? > + 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)); As above. > + 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); Does it really make sense to include all hooks here? That just increases the size of this example, and adds a list that is most likely not going to be updated when an extra plugin hook is added. > + 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) ppcp -> ppcc? > +{ > + 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); > -} > -Steffan ------------------------------------------------------------------------------ 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