diff options
author | james <james@e7ae566f-a301-0410-adde-c780ea21d3b5> | 2008-05-24 23:26:11 +0000 |
---|---|---|
committer | james <james@e7ae566f-a301-0410-adde-c780ea21d3b5> | 2008-05-24 23:26:11 +0000 |
commit | 344ee9181734dcd5a922b8b2a7ebea4ce818a0b0 (patch) | |
tree | 8a4c3724971a0c81debc97d3bba62138aab3a247 | |
parent | Did: (diff) | |
download | openvpn-344ee9181734dcd5a922b8b2a7ebea4ce818a0b0.tar.xz |
Support asynchronous/deferred authentication in
OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY plugin handler.
See documentation in openvpn-plugin.h and example
usage in plugin/defer/simple.c.
git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@2969 e7ae566f-a301-0410-adde-c780ea21d3b5
-rw-r--r-- | debug/valgrind-suppress | 24 | ||||
-rwxr-xr-x | doval | 2 | ||||
-rw-r--r-- | forward.c | 12 | ||||
-rw-r--r-- | init.c | 2 | ||||
-rw-r--r-- | misc.c | 10 | ||||
-rw-r--r-- | misc.h | 2 | ||||
-rw-r--r-- | multi.c | 14 | ||||
-rw-r--r-- | openvpn-plugin.h | 23 | ||||
-rw-r--r-- | plugin.c | 33 | ||||
-rw-r--r-- | plugin/defer/README | 16 | ||||
-rwxr-xr-x | plugin/defer/build | 14 | ||||
-rw-r--r-- | plugin/defer/simple.c | 138 | ||||
-rwxr-xr-x | plugin/defer/simple.def | 6 | ||||
-rwxr-xr-x | plugin/defer/winbuild | 18 | ||||
-rw-r--r-- | push.c | 8 | ||||
-rw-r--r-- | push.h | 2 | ||||
-rw-r--r-- | socket.c | 2 | ||||
-rw-r--r-- | ssl.c | 201 | ||||
-rw-r--r-- | ssl.h | 30 |
19 files changed, 456 insertions, 101 deletions
diff --git a/debug/valgrind-suppress b/debug/valgrind-suppress index fb8ebe6..69e2a3f 100644 --- a/debug/valgrind-suppress +++ b/debug/valgrind-suppress @@ -104,19 +104,13 @@ } { - SSL_get_ex_new_index - Memcheck:Leak - fun:malloc - obj:/lib/libcrypto.so.* - fun:CRYPTO_malloc - fun:lh_new - obj:/lib/libcrypto.so.* - obj:/lib/libcrypto.so.* - obj:/lib/libcrypto.so.* - fun:CRYPTO_get_ex_new_index - fun:SSL_get_ex_new_index - fun:ssl_set_mydata_index - fun:init_ssl_lib - fun:init_static - fun:main + <insert a suppression name here> + Memcheck:Addr8 + obj:/lib/ld-2.5.so +} + +{ + <insert a suppression name here> + Memcheck:Cond + obj:/lib/ld-2.5.so } @@ -0,0 +1,2 @@ +#!/bin/bash +valgrind --tool=memcheck --error-limit=no --suppressions=debug/valgrind-suppress --gen-suppressions=all --leak-check=yes --num-callers=32 $* @@ -83,13 +83,19 @@ check_tls_dowork (struct context *c) if (interval_test (&c->c2.tmp_int)) { - if (tls_multi_process - (c->c2.tls_multi, &c->c2.to_link, &c->c2.to_link_addr, - get_link_socket_info (c), &wakeup)) + const int tmp_status = tls_multi_process + (c->c2.tls_multi, &c->c2.to_link, &c->c2.to_link_addr, + get_link_socket_info (c), &wakeup); + if (tmp_status == TLSMP_ACTIVE) { update_time (); interval_action (&c->c2.tmp_int); } + else if (tmp_status == TLSMP_KILL) + { + c->sig->signal_received = SIGTERM; + c->sig->signal_text = "auth-control-exit"; + } interval_future_trigger (&c->c2.tmp_int, wakeup); } @@ -728,7 +728,7 @@ do_route (const struct options *options, if (plugin_defined (plugins, OPENVPN_PLUGIN_ROUTE_UP)) { - if (plugin_call (plugins, OPENVPN_PLUGIN_ROUTE_UP, NULL, NULL, es)) + if (plugin_call (plugins, OPENVPN_PLUGIN_ROUTE_UP, NULL, NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS) msg (M_WARN, "WARNING: route-up plugin call failed"); } @@ -206,7 +206,7 @@ run_up_down (const char *command, ifconfig_local, ifconfig_remote, context); - if (plugin_call (plugins, plugin_type, BSTR (&cmd), NULL, es)) + if (plugin_call (plugins, plugin_type, BSTR (&cmd), NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS) msg (M_FATAL, "ERROR: up/down plugin call failed"); } @@ -1053,7 +1053,7 @@ test_file (const char *filename) /* create a temporary filename in directory */ const char * -create_temp_filename (const char *directory, struct gc_arena *gc) +create_temp_filename (const char *directory, const char *prefix, struct gc_arena *gc) { static unsigned int counter; struct buffer fname = alloc_buf_gc (256, gc); @@ -1062,9 +1062,11 @@ create_temp_filename (const char *directory, struct gc_arena *gc) ++counter; mutex_unlock_static (L_CREATE_TEMP); - buf_printf (&fname, PACKAGE "_%u_%u.tmp", + buf_printf (&fname, PACKAGE "_%s_%u_%u_%u.tmp", + prefix, openvpn_getpid (), - counter); + counter, + (unsigned int)now); return gen_path (directory, BSTR (&fname), gc); } @@ -206,7 +206,7 @@ long int get_random(void); bool test_file (const char *filename); /* create a temporary filename in directory */ -const char *create_temp_filename (const char *directory, struct gc_arena *gc); +const char *create_temp_filename (const char *directory, const char *prefix, struct gc_arena *gc); /* put a directory and filename together */ const char *gen_path (const char *directory, const char *filename, struct gc_arena *gc); @@ -82,7 +82,7 @@ learn_address_script (const struct multi_context *m, if (mi) buf_printf (&cmd, " \"%s\"", tls_common_name (mi->context.c2.tls_multi, false)); - if (plugin_call (plugins, OPENVPN_PLUGIN_LEARN_ADDRESS, BSTR (&cmd), NULL, es)) + if (plugin_call (plugins, OPENVPN_PLUGIN_LEARN_ADDRESS, BSTR (&cmd), NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS) { msg (M_WARN, "WARNING: learn-address plugin call failed"); ret = false; @@ -419,7 +419,7 @@ multi_client_disconnect_script (struct multi_context *m, if (plugin_defined (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_DISCONNECT)) { - if (plugin_call (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_DISCONNECT, NULL, NULL, mi->context.c2.es)) + if (plugin_call (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_DISCONNECT, NULL, NULL, mi->context.c2.es) != OPENVPN_PLUGIN_FUNC_SUCCESS) msg (M_WARN, "WARNING: client-disconnect plugin call failed"); } @@ -1310,7 +1310,7 @@ multi_client_connect_setenv (struct multi_context *m, static void multi_connection_established (struct multi_context *m, struct multi_instance *mi) { - if (tls_authenticated (mi->context.c2.tls_multi)) + if (tls_authentication_status (mi->context.c2.tls_multi, 0) == TLS_AUTHENTICATION_SUCCEEDED) { struct gc_arena gc = gc_new (); unsigned int option_types_found = 0; @@ -1400,11 +1400,11 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi /* deprecated callback, use a file for passing back return info */ if (plugin_defined (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT)) { - const char *dc_file = create_temp_filename (mi->context.options.tmp_dir, &gc); + const char *dc_file = create_temp_filename (mi->context.options.tmp_dir, "cc", &gc); delete_file (dc_file); - if (plugin_call (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT, dc_file, NULL, mi->context.c2.es)) + if (plugin_call (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT, dc_file, NULL, mi->context.c2.es) != OPENVPN_PLUGIN_FUNC_SUCCESS) { msg (M_WARN, "WARNING: client-connect plugin call failed"); cc_succeeded = false; @@ -1423,7 +1423,7 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi plugin_return_init (&pr); - if (plugin_call (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT_V2, NULL, &pr, mi->context.c2.es)) + if (plugin_call (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT_V2, NULL, &pr, mi->context.c2.es) != OPENVPN_PLUGIN_FUNC_SUCCESS) { msg (M_WARN, "WARNING: client-connect-v2 plugin call failed"); cc_succeeded = false; @@ -1448,7 +1448,7 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi setenv_str (mi->context.c2.es, "script_type", "client-connect"); - dc_file = create_temp_filename (mi->context.options.tmp_dir, &gc); + dc_file = create_temp_filename (mi->context.options.tmp_dir, "cc", &gc); delete_file (dc_file); diff --git a/openvpn-plugin.h b/openvpn-plugin.h index b333168..cbcefa0 100644 --- a/openvpn-plugin.h +++ b/openvpn-plugin.h @@ -57,6 +57,7 @@ typedef void *openvpn_plugin_handle_t; */ #define OPENVPN_PLUGIN_FUNC_SUCCESS 0 #define OPENVPN_PLUGIN_FUNC_ERROR 1 +#define OPENVPN_PLUGIN_FUNC_DEFERRED 2 /* * For Windows (needs to be modified for MSVC) @@ -202,6 +203,28 @@ OPENVPN_PLUGIN_DEF openvpn_plugin_handle_t OPENVPN_PLUGIN_FUNC(openvpn_plugin_op * RETURN VALUE * * OPENVPN_PLUGIN_FUNC_SUCCESS on success, OPENVPN_PLUGIN_FUNC_ERROR on failure + * + * In addition, OPENVPN_PLUGIN_FUNC_DEFERRED may be returned by + * OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY. This enables asynchronous + * authentication where the plugin (or one of its agents) may indicate + * authentication success/failure some number of seconds after the return + * of the OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY handler by writing a single + * char to the file named by auth_control_file in the environmental variable + * list (envp). + * + * first char of auth_control_file: + * '0' -- indicates auth failure + * '1' -- indicates auth success + * '2' -- indicates that the client should be immediately killed + * + * The auth_control file will be polled for the life of the key state + * it is associated with, and any change in the file will + * impact the client's current authentication state. + * + * OpenVPN will delete the auth_control_file after it goes out of scope. + * + * See plugin/defer/simple.c for an example on using asynchronous + * authentication. */ OPENVPN_PLUGIN_DEF int OPENVPN_PLUGIN_FUNC(openvpn_plugin_func_v2) (openvpn_plugin_handle_t handle, @@ -347,7 +347,7 @@ plugin_call_item (const struct plugin *p, plugin_type_name (type), status); - if (status != OPENVPN_PLUGIN_FUNC_SUCCESS) + if (status == OPENVPN_PLUGIN_FUNC_ERROR) msg (M_WARN, "PLUGIN_CALL: plugin function %s failed with status %d: %s", plugin_type_name (type), status, @@ -541,7 +541,8 @@ plugin_call (const struct plugin_list *pl, int i; const char **envp; const int n = plugin_n (pl); - int count = 0; + bool error = false; + bool deferred = false; mutex_lock_static (L_PLUGIN); @@ -550,13 +551,16 @@ plugin_call (const struct plugin_list *pl, for (i = 0; i < n; ++i) { - if (!plugin_call_item (&pl->common->plugins[i], - pl->per_client.per_client_context[i], - type, - args, - pr ? &pr->list[i] : NULL, - envp)) - ++count; + const int status = plugin_call_item (&pl->common->plugins[i], + pl->per_client.per_client_context[i], + type, + args, + pr ? &pr->list[i] : NULL, + envp); + if (status == OPENVPN_PLUGIN_FUNC_ERROR) + error = true; + else if (status == OPENVPN_PLUGIN_FUNC_DEFERRED) + deferred = true; } if (pr) @@ -566,12 +570,13 @@ plugin_call (const struct plugin_list *pl, gc_free (&gc); - return count == n ? 0 : 1; /* if any one plugin in the chain failed, return failure (1) */ - } - else - { - return 0; + if (error) + return OPENVPN_PLUGIN_FUNC_ERROR; + else if (deferred) + return OPENVPN_PLUGIN_FUNC_DEFERRED; } + + return OPENVPN_PLUGIN_FUNC_SUCCESS; } void diff --git a/plugin/defer/README b/plugin/defer/README new file mode 100644 index 0000000..d8990f8 --- /dev/null +++ b/plugin/defer/README @@ -0,0 +1,16 @@ +OpenVPN plugin examples. + +Examples provided: + +simple.c -- using the --auth-user-pass-verify callback, + test deferred authentication. + +To build: + + ./build simple (Linux/BSD/etc.) + ./winbuild simple (MinGW on Windows) + +To use in OpenVPN, add to config file: + + plugin simple.so (Linux/BSD/etc.) + plugin simple.dll (MinGW on Windows) diff --git a/plugin/defer/build b/plugin/defer/build new file mode 100755 index 0000000..8b628a2 --- /dev/null +++ b/plugin/defer/build @@ -0,0 +1,14 @@ +#!/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 +INCLUDE="-I../.." + +CC_FLAGS="-O2 -Wall" + +gcc $CC_FLAGS -fPIC -c $INCLUDE $1.c && \ +gcc -fPIC -shared -Wl,-soname,$1.so -o $1.so $1.o -lc diff --git a/plugin/defer/simple.c b/plugin/defer/simple.c new file mode 100644 index 0000000..7311a3f --- /dev/null +++ b/plugin/defer/simple.c @@ -0,0 +1,138 @@ +/* + * 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-2005 OpenVPN Solutions LLC <info@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. Will run on Windows or *nix. + * + * See the README file for build instructions. + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "openvpn-plugin.h" + +/* + * Our context, where we keep our state. + */ +struct plugin_context { + int dummy; +}; + +/* + * 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]"; +} + +OPENVPN_EXPORT openvpn_plugin_handle_t +openvpn_plugin_open_v1 (unsigned int *type_mask, const char *argv[], const char *envp[]) +{ + struct plugin_context *context; + + /* + * Allocate our context + */ + context = (struct plugin_context *) calloc (1, sizeof (struct plugin_context)); + + /* + * We are only interested in intercepting the + * --auth-user-pass-verify callback. + */ + *type_mask = OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY); + + return (openvpn_plugin_handle_t) context; +} + +OPENVPN_EXPORT int +openvpn_plugin_func_v1 (openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[]) +{ + /* struct plugin_context *context = (struct plugin_context *) handle; */ + + /* 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 10 seconds */ + if (auth_control_file) + { + char buf[256]; + snprintf (buf, sizeof(buf), "( sleep 10 ; echo AUTH %s ; echo 1 >%s ) &", + auth_control_file, + auth_control_file); + printf ("%s\n", buf); + system (buf); + return OPENVPN_PLUGIN_FUNC_DEFERRED; + } + else + { + return OPENVPN_PLUGIN_FUNC_ERROR; + } +} + +OPENVPN_EXPORT void +openvpn_plugin_close_v1 (openvpn_plugin_handle_t handle) +{ + struct plugin_context *context = (struct plugin_context *) handle; + free (context); +} diff --git a/plugin/defer/simple.def b/plugin/defer/simple.def new file mode 100755 index 0000000..a87507d --- /dev/null +++ b/plugin/defer/simple.def @@ -0,0 +1,6 @@ +LIBRARY OpenVPN_PLUGIN_SAMPLE +DESCRIPTION "Sample OpenVPN plug-in module." +EXPORTS + openvpn_plugin_open_v1 @1 + openvpn_plugin_func_v1 @2 + openvpn_plugin_close_v1 @3 diff --git a/plugin/defer/winbuild b/plugin/defer/winbuild new file mode 100755 index 0000000..97e724a --- /dev/null +++ b/plugin/defer/winbuild @@ -0,0 +1,18 @@ +# +# Build an OpenVPN plugin module on Windows/MinGW. +# 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 +INCLUDE="-I.." + +CC_FLAGS="-O2 -Wall" + +gcc -DBUILD_DLL $CC_FLAGS $INCLUDE -c $1.c +gcc --disable-stdcall-fixup -mdll -DBUILD_DLL -o junk.tmp -Wl,--base-file,base.tmp $1.o +rm junk.tmp +dlltool --dllname $1.dll --base-file base.tmp --output-exp temp.exp --input-def $1.def +rm base.tmp +gcc --enable-stdcall-fixup -mdll -DBUILD_DLL -o $1.dll $1.o -Wl,temp.exp +rm temp.exp @@ -70,10 +70,11 @@ receive_auth_failed (struct context *c, const struct buffer *buffer) /* * Send auth failed message from server to client. */ -bool +void send_auth_failed (struct context *c) { - return send_control_channel_string (c, "AUTH_FAILED", D_PUSH); + schedule_exit (c, c->options.scheduled_exit_interval); + send_control_channel_string (c, "AUTH_FAILED", D_PUSH); } #endif @@ -200,10 +201,9 @@ process_incoming_push_msg (struct context *c, #if P2MP_SERVER if (buf_string_compare_advance (&buf, "PUSH_REQUEST")) { - if (!tls_authenticated (c->c2.tls_multi) || c->c2.context_auth == CAS_FAILED) + if (tls_authentication_status (c->c2.tls_multi, 0) == TLS_AUTHENTICATION_FAILED || c->c2.context_auth == CAS_FAILED) { send_auth_failed (c); - schedule_exit (c, c->options.scheduled_exit_interval); ret = PUSH_MSG_AUTH_FAILURE; } else if (!c->c2.push_reply_deferred && c->c2.context_auth == CAS_SUCCEEDED) @@ -59,7 +59,7 @@ bool send_push_reply (struct context *c); void remove_iroutes_from_push_route_list (struct options *o); -bool send_auth_failed (struct context *c); +void send_auth_failed (struct context *c); #endif #endif @@ -1592,7 +1592,7 @@ link_socket_connection_initiated (const struct buffer *buf, if (plugin_defined (info->plugins, OPENVPN_PLUGIN_IPCHANGE)) { const char *addr_ascii = print_sockaddr_ex (&info->lsa->actual.dest, " ", PS_SHOW_PORT, &gc); - if (plugin_call (info->plugins, OPENVPN_PLUGIN_IPCHANGE, addr_ascii, NULL, es)) + if (plugin_call (info->plugins, OPENVPN_PLUGIN_IPCHANGE, addr_ascii, NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS) msg (M_WARN, "WARNING: ipchange plugin call failed"); } @@ -710,7 +710,7 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx) ret = plugin_call (opt->plugins, OPENVPN_PLUGIN_TLS_VERIFY, command, NULL, opt->es); - if (!ret) + if (ret == OPENVPN_PLUGIN_FUNC_SUCCESS) { msg (D_HANDSHAKE, "VERIFY PLUGIN OK: depth=%d, %s", ctx->error_depth, subject); @@ -847,33 +847,129 @@ tls_lock_common_name (struct tls_multi *multi) } /* - * Return true if at least one valid key state exists - * which has passed authentication. If we are using - * username/password authentication, and the authentication - * failed, we may have a live S_ACTIVE/S_NORMAL key state - * even though the 'authenticated' var might be false. - * - * This is so that we can return an AUTH_FAILED error - * message to the client over the TLS channel. - * - * If 'authenticated' is false, tunnel traffic forwarding - * is disabled but TLS channel data can still be sent - * or received. + * auth_control_file functions */ -bool -tls_authenticated (struct tls_multi *multi) + +static void +key_state_rm_auth_control_file (struct key_state *ks) +{ + if (ks && ks->auth_control_file) + { + delete_file (ks->auth_control_file); + free (ks->auth_control_file); + ks->auth_control_file = NULL; + } +} + +static void +key_state_gen_auth_control_file (struct key_state *ks, const struct tls_options *opt) { + struct gc_arena gc = gc_new (); + const char *acf; + + key_state_rm_auth_control_file (ks); + acf = create_temp_filename (opt->tmp_dir, "acf", &gc); + ks->auth_control_file = string_alloc (acf, NULL); + setenv_str (opt->es, "auth_control_file", ks->auth_control_file); + + gc_free (&gc); +} + +/* key_state_test_auth_control_file return values */ +#define ACF_SUCCEEDED 0 +#define ACF_FAILED 1 +#define ACF_KILL 2 +#define ACF_UNDEFINED 3 +#define ACF_DISABLED 4 +static int +key_state_test_auth_control_file (const struct key_state *ks) +{ + int ret = ACF_DISABLED; + if (ks && ks->auth_control_file) + { + ret = ACF_UNDEFINED; + FILE *fp = fopen (ks->auth_control_file, "r"); + if (fp) + { + int c = fgetc (fp); + if (c == '1') + ret = ACF_SUCCEEDED; + else if (c == '0') + ret = ACF_FAILED; + else if (c == '2') + ret = ACF_KILL; + fclose (fp); + } + } + return ret; +} + +/* + * Return current session authentication state. Return + * value is TLS_AUTHENTICATION_x. + */ + +int +tls_authentication_status (struct tls_multi *multi, const int latency) +{ + bool deferred = false; + bool success = false; + bool kill = false; + bool active = false; + + if (latency && multi->tas_last && multi->tas_last + latency >= now) + return TLS_AUTHENTICATION_UNDEFINED; + multi->tas_last = now; + if (multi) { int i; for (i = 0; i < KEY_SCAN_SIZE; ++i) { - const struct key_state *ks = multi->key_scan[i]; - if (DECRYPT_KEY_ENABLED (multi, ks) && ks->authenticated) - return true; + struct key_state *ks = multi->key_scan[i]; + if (DECRYPT_KEY_ENABLED (multi, ks)) + { + active = true; + if (ks->authenticated) + { + switch (key_state_test_auth_control_file (ks)) + { + case ACF_SUCCEEDED: + case ACF_DISABLED: + success = true; + ks->auth_deferred = false; + break; + case ACF_UNDEFINED: + if (now < ks->auth_deferred_expire) + deferred = true; + break; + case ACF_FAILED: + ks->authenticated = false; + break; + case ACF_KILL: + kill = true; + ks->authenticated = false; + break; + default: + ASSERT (0); + } + } + } } } - return false; + +#if 0 + dmsg (D_TLS_ERRORS, "TAS: a=%d k=%d s=%d d=%d", active, kill, success, deferred); +#endif + + if (kill) + return TLS_AUTHENTICATION_FAILED; + else if (success) + return TLS_AUTHENTICATION_SUCCEEDED; + else if (!active || deferred) + return TLS_AUTHENTICATION_DEFERRED; + else + return TLS_AUTHENTICATION_FAILED; } void @@ -1905,6 +2001,8 @@ key_state_free (struct key_state *ks, bool clear) packet_id_free (&ks->packet_id); + key_state_rm_auth_control_file (ks); + if (clear) CLEAR (*ks); } @@ -2747,7 +2845,7 @@ verify_user_pass_script (struct tls_session *session, const struct user_pass *up { struct status_output *so; - tmp_file = create_temp_filename (session->opt->tmp_dir, &gc); + tmp_file = create_temp_filename (session->opt->tmp_dir, "up", &gc); so = status_open (tmp_file, 0, -1, NULL, STATUS_OUTPUT_WRITE); status_printf (so, "%s", up->username); status_printf (so, "%s", up->password); @@ -2798,11 +2896,10 @@ verify_user_pass_script (struct tls_session *session, const struct user_pass *up return ret; } -static bool +static int verify_user_pass_plugin (struct tls_session *session, const struct user_pass *up, const char *raw_username) { - int retval; - bool ret = false; + int retval = OPENVPN_PLUGIN_FUNC_ERROR; /* Is username defined? */ if (strlen (up->username)) @@ -2817,11 +2914,15 @@ verify_user_pass_plugin (struct tls_session *session, const struct user_pass *up /* setenv client real IP address */ setenv_untrusted (session); + /* generate filename for deferred auth control file */ + key_state_gen_auth_control_file (ks, session->opt); + /* call command */ retval = plugin_call (session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY, NULL, NULL, session->opt->es); - if (!retval) - ret = true; + /* purge auth control filename (and file itself) for non-deferred returns */ + if (retval != OPENVPN_PLUGIN_FUNC_DEFERRED) + key_state_rm_auth_control_file (ks); setenv_del (session->opt->es, "password"); setenv_str (session->opt->es, "username", up->username); @@ -2831,7 +2932,7 @@ verify_user_pass_plugin (struct tls_session *session, const struct user_pass *up msg (D_TLS_ERRORS, "TLS Auth Error: peer provided a blank username"); } - return ret; + return retval; } /* @@ -3047,7 +3148,7 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi if (session->opt->auth_user_pass_verify_script || plugin_defined (session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY)) { - bool s1 = true; + int s1 = OPENVPN_PLUGIN_FUNC_SUCCESS; bool s2 = true; char *raw_username; @@ -3077,12 +3178,15 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi s2 = verify_user_pass_script (session, up); /* auth succeeded? */ - if (s1 && s2) + if ((s1 == OPENVPN_PLUGIN_FUNC_SUCCESS || s1 == OPENVPN_PLUGIN_FUNC_DEFERRED) && s2) { ks->authenticated = true; + if (s1 == OPENVPN_PLUGIN_FUNC_DEFERRED) + ks->auth_deferred = true; if (session->opt->username_as_common_name) set_common_name (session, up->username); - msg (D_HANDSHAKE, "TLS: Username/Password authentication succeeded for username '%s' %s", + msg (D_HANDSHAKE, "TLS: Username/Password authentication %s for username '%s' %s", + s1 == OPENVPN_PLUGIN_FUNC_SUCCESS ? "succeeded" : "deferred", up->username, session->opt->username_as_common_name ? "[CN SET]" : ""); } @@ -3155,7 +3259,7 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi */ if (ks->authenticated && plugin_defined (session->opt->plugins, OPENVPN_PLUGIN_TLS_FINAL)) { - if (plugin_call (session->opt->plugins, OPENVPN_PLUGIN_TLS_FINAL, NULL, NULL, session->opt->es)) + if (plugin_call (session->opt->plugins, OPENVPN_PLUGIN_TLS_FINAL, NULL, NULL, session->opt->es) != OPENVPN_PLUGIN_FUNC_SUCCESS) ks->authenticated = false; } @@ -3268,7 +3372,7 @@ tls_process (struct tls_multi *multi, buf = reliable_get_buf_output_sequenced (ks->send_reliable); if (buf) { - ks->must_negotiate = now + session->opt->handshake_window; + ks->auth_deferred_expire = ks->must_negotiate = now + session->opt->handshake_window; /* null buffer */ reliable_mark_active_outgoing (ks->send_reliable, buf, ks->initial_opcode); @@ -3593,7 +3697,7 @@ error: * the active or untrusted sessions. */ -bool +int tls_multi_process (struct tls_multi *multi, struct buffer *to_link, struct link_socket_actual **to_link_addr, @@ -3602,8 +3706,9 @@ tls_multi_process (struct tls_multi *multi, { struct gc_arena gc = gc_new (); int i; - bool active = false; + int active = TLSMP_INACTIVE; bool error = false; + int tas; perf_push (PERF_TLS_MULTI_PROCESS); @@ -3641,7 +3746,7 @@ tls_multi_process (struct tls_multi *multi, if (tls_process (multi, session, to_link, &tla, to_link_socket_info, wakeup)) - active = true; + active = TLSMP_ACTIVE; /* * If tls_process produced an outgoing packet, @@ -3680,6 +3785,8 @@ tls_multi_process (struct tls_multi *multi, update_time (); + tas = tls_authentication_status (multi, TLS_MULTI_AUTH_STATUS_INTERVAL); + /* * If lame duck session expires, kill it. */ @@ -3700,7 +3807,7 @@ tls_multi_process (struct tls_multi *multi, if (DECRYPT_KEY_ENABLED (multi, &multi->session[TM_UNTRUSTED].key[KS_PRIMARY])) { move_session (multi, TM_ACTIVE, TM_UNTRUSTED, true); msg (D_TLS_DEBUG_LOW, "TLS: tls_multi_process: untrusted session promoted to %strusted", - tls_authenticated (multi) ? "" : "semi-"); + tas == TLS_AUTHENTICATION_SUCCEEDED ? "" : "semi-"); } /* @@ -3738,7 +3845,8 @@ tls_multi_process (struct tls_multi *multi, perf_pop (); gc_free (&gc); - return active; + + return (tas == TLS_AUTHENTICATION_FAILED) ? TLSMP_KILL : active; } /* @@ -3815,6 +3923,7 @@ tls_pre_decrypt (struct tls_multi *multi, if (DECRYPT_KEY_ENABLED (multi, ks) && key_id == ks->key_id && ks->authenticated + && !ks->auth_deferred && link_socket_actual_match (from, &ks->remote_addr)) { /* return appropriate data channel decrypt key in opt */ @@ -3835,13 +3944,14 @@ tls_pre_decrypt (struct tls_multi *multi, #if 0 /* keys out of sync? */ else { - dmsg (D_TLS_DEBUG, "TLS_PRE_DECRYPT: [%d] dken=%d rkid=%d lkid=%d auth=%d match=%d", - i, - DECRYPT_KEY_ENABLED (multi, ks), - key_id, - ks->key_id, - ks->authenticated, - link_socket_actual_match (from, &ks->remote_addr)); + dmsg (D_TLS_ERRORS, "TLS_PRE_DECRYPT: [%d] dken=%d rkid=%d lkid=%d auth=%d def=%d match=%d", + i, + DECRYPT_KEY_ENABLED (multi, ks), + key_id, + ks->key_id, + ks->authenticated, + ks->auth_deferred, + link_socket_actual_match (from, &ks->remote_addr)); } #endif } @@ -4331,7 +4441,10 @@ tls_pre_encrypt (struct tls_multi *multi, for (i = 0; i < KEY_SCAN_SIZE; ++i) { struct key_state *ks = multi->key_scan[i]; - if (ks->state >= S_ACTIVE && ks->authenticated) + if (ks->state >= S_ACTIVE + && ks->authenticated + && !ks->auth_deferred + && (!ks->key_id || now >= ks->auth_deferred_expire)) { opt->key_ctx_bi = &ks->key; opt->packet_id = multi->opt.replay ? &ks->packet_id : NULL; @@ -271,6 +271,9 @@ communication pipe to the main thread to be ready to accept writes. */ #define TLS_MULTI_THREAD_SEND_TIMEOUT 5 +/* Interval that tls_multi_process should call tls_authentication_status */ +#define TLS_MULTI_AUTH_STATUS_INTERVAL 10 + /* * Buffer sizes (also see mtu.h). */ @@ -367,6 +370,11 @@ struct key_state * If bad username/password, TLS connection will come up but 'authenticated' will be false. */ bool authenticated; + + /* If auth_deferred is true, authentication is being deferred */ + char *auth_control_file; + bool auth_deferred; + time_t auth_deferred_expire; }; /* @@ -561,6 +569,9 @@ struct tls_multi */ char *locked_cn; + /* Time of last call to tls_authentication_status */ + time_t tas_last; + /* * Our session objects. */ @@ -599,11 +610,14 @@ void tls_multi_init_set_options(struct tls_multi* multi, const char *local, const char *remote); -bool tls_multi_process (struct tls_multi *multi, - struct buffer *to_link, - struct link_socket_actual **to_link_addr, - struct link_socket_info *to_link_socket_info, - interval_t *wakeup); +#define TLSMP_INACTIVE 0 +#define TLSMP_ACTIVE 1 +#define TLSMP_KILL 2 +int tls_multi_process (struct tls_multi *multi, + struct buffer *to_link, + struct link_socket_actual **to_link_addr, + struct link_socket_info *to_link_socket_info, + interval_t *wakeup); void tls_multi_free (struct tls_multi *multi, bool clear); @@ -647,7 +661,11 @@ const char *tls_common_name (struct tls_multi* multi, bool null); void tls_set_common_name (struct tls_multi *multi, const char *common_name); void tls_lock_common_name (struct tls_multi *multi); -bool tls_authenticated (struct tls_multi *multi); +#define TLS_AUTHENTICATION_SUCCEEDED 0 +#define TLS_AUTHENTICATION_FAILED 1 +#define TLS_AUTHENTICATION_DEFERRED 2 +#define TLS_AUTHENTICATION_UNDEFINED 3 +int tls_authentication_status (struct tls_multi *multi, const int latency); void tls_deauthenticate (struct tls_multi *multi); /* |