summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--debug/valgrind-suppress24
-rwxr-xr-xdoval2
-rw-r--r--forward.c12
-rw-r--r--init.c2
-rw-r--r--misc.c10
-rw-r--r--misc.h2
-rw-r--r--multi.c14
-rw-r--r--openvpn-plugin.h23
-rw-r--r--plugin.c33
-rw-r--r--plugin/defer/README16
-rwxr-xr-xplugin/defer/build14
-rw-r--r--plugin/defer/simple.c138
-rwxr-xr-xplugin/defer/simple.def6
-rwxr-xr-xplugin/defer/winbuild18
-rw-r--r--push.c8
-rw-r--r--push.h2
-rw-r--r--socket.c2
-rw-r--r--ssl.c201
-rw-r--r--ssl.h30
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
}
diff --git a/doval b/doval
new file mode 100755
index 0000000..76e0811
--- /dev/null
+++ b/doval
@@ -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 $*
diff --git a/forward.c b/forward.c
index a38dd14..3e09c7f 100644
--- a/forward.c
+++ b/forward.c
@@ -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);
}
diff --git a/init.c b/init.c
index c7aa7be..dd1db5c 100644
--- a/init.c
+++ b/init.c
@@ -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");
}
diff --git a/misc.c b/misc.c
index f4edefa..5f0ec12 100644
--- a/misc.c
+++ b/misc.c
@@ -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);
}
diff --git a/misc.h b/misc.h
index f01695f..3ee0e27 100644
--- a/misc.h
+++ b/misc.h
@@ -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);
diff --git a/multi.c b/multi.c
index 2a74cda..54e1d76 100644
--- a/multi.c
+++ b/multi.c
@@ -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,
diff --git a/plugin.c b/plugin.c
index a337e46..bff9d49 100644
--- a/plugin.c
+++ b/plugin.c
@@ -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
diff --git a/push.c b/push.c
index d5a757a..db7ce89 100644
--- a/push.c
+++ b/push.c
@@ -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)
diff --git a/push.h b/push.h
index 78aadf2..bd73b3d 100644
--- a/push.h
+++ b/push.h
@@ -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
diff --git a/socket.c b/socket.c
index 78686ef..3338990 100644
--- a/socket.c
+++ b/socket.c
@@ -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");
}
diff --git a/ssl.c b/ssl.c
index 4cc6b76..2e0b3e3 100644
--- a/ssl.c
+++ b/ssl.c
@@ -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;
diff --git a/ssl.h b/ssl.h
index f80e083..a7876cb 100644
--- a/ssl.h
+++ b/ssl.h
@@ -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);
/*