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
This commit is contained in:
james 2008-05-24 23:26:11 +00:00
parent 4da783f3a5
commit 344ee91817
19 changed files with 456 additions and 101 deletions

View file

@ -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
}

2
doval Executable file
View file

@ -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 $*

View file

@ -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);
}

2
init.c
View file

@ -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");
}

10
misc.c
View file

@ -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);
}

2
misc.h
View file

@ -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);

14
multi.c
View file

@ -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);

View 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,

View file

@ -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

16
plugin/defer/README Normal file
View file

@ -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)

14
plugin/defer/build Executable file
View file

@ -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

138
plugin/defer/simple.c Normal file
View file

@ -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);
}

6
plugin/defer/simple.def Executable file
View file

@ -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

18
plugin/defer/winbuild Executable file
View file

@ -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

8
push.c
View file

@ -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)

2
push.h
View file

@ -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

View file

@ -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");
}

201
ssl.c
View file

@ -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;

30
ssl.h
View file

@ -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);
/*