]> git.ipfire.org Git - thirdparty/openvpn.git/commitdiff
Support asynchronous/deferred authentication in
authorjames <james@e7ae566f-a301-0410-adde-c780ea21d3b5>
Sat, 24 May 2008 23:26:11 +0000 (23:26 +0000)
committerjames <james@e7ae566f-a301-0410-adde-c780ea21d3b5>
Sat, 24 May 2008 23:26:11 +0000 (23:26 +0000)
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

19 files changed:
debug/valgrind-suppress
doval [new file with mode: 0755]
forward.c
init.c
misc.c
misc.h
multi.c
openvpn-plugin.h
plugin.c
plugin/defer/README [new file with mode: 0644]
plugin/defer/build [new file with mode: 0755]
plugin/defer/simple.c [new file with mode: 0644]
plugin/defer/simple.def [new file with mode: 0755]
plugin/defer/winbuild [new file with mode: 0755]
push.c
push.h
socket.c
ssl.c
ssl.h

index fb8ebe689c3ddbd5772305f587ffabd968cbd9e1..69e2a3f5f083c5654dcafe5406f2dba535d8de78 100644 (file)
 }
 
 {
-   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 (executable)
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 $*
index a38dd14abc6544f0c00e7402cd2d4c214c173633..3e09c7f4fda5b6895e5cdc9f3059ae0696a3100f 100644 (file)
--- 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 c7aa7bed4e8c476e03166c5a3d750eaecee565c9..dd1db5cf5f9304a0fde2f9765b9f4db46f3e51c4 100644 (file)
--- 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 f4edefa34d4cec6da0957d0aac573c16ba193537..5f0ec12e00eb7a98b6c0afde28a42a69494ff056 100644 (file)
--- 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 f01695fb1059dd8a076cdca7bca7d58e1eec2d93..3ee0e2799cb9b8bf82c5738fb51ace0a429f1a4e 100644 (file)
--- 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 2a74cdaec43ef97fe84b23935db4f264b98f6b79..54e1d763c526b39948f6af51626c56344fd994fb 100644 (file)
--- 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);
 
index b33316881f893aaf195b56e53c99236b0c6c5388..cbcefa0ddcb4295c822132c58ee099a791a35ae8 100644 (file)
@@ -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,
index a337e46937bc9373b34a6aa7f2d8f733fb1e5a14..bff9d494113b80e1f4b5094f1d5c2150f5a8f1d2 100644 (file)
--- 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 (file)
index 0000000..d8990f8
--- /dev/null
@@ -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 (executable)
index 0000000..8b628a2
--- /dev/null
@@ -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 (file)
index 0000000..7311a3f
--- /dev/null
@@ -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 (executable)
index 0000000..a87507d
--- /dev/null
@@ -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 (executable)
index 0000000..97e724a
--- /dev/null
@@ -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 d5a757a2926e2978685b6e98bd448f4758ab508c..db7ce897d82429284984c2d27e8fe1431fd87e6b 100644 (file)
--- 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 78aadf26d72b7ead7219ba43d5503dccd5b7a4eb..bd73b3d8c1b3e2b8fac96818915f04c7e752c9d3 100644 (file)
--- 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
index 78686efcfaf4400839a01e371c6062c7121aad5e..3338990cbf4a3a64888702391837a3b64b33bb84 100644 (file)
--- 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 4cc6b768ca9f1b16a40be6e5128f58d183f9569e..2e0b3e359f2e7f4db2af064fc9e823a83ce0a47a 100644 (file)
--- 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 f80e083272ab88d08cb1eb8ad2ff740bb2dea4c6..a7876cb3114552f8cb6429d9812f1deba80a3faa 100644 (file)
--- a/ssl.h
+++ b/ssl.h
    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);
 
 /*