Incremented version to 2.1_rc7d.

Support asynchronous authentication by plugins by allowing
OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY to return
OPENVPN_PLUGIN_FUNC_DEFERRED.  See comments in
openvpn-plugin.h for documentation.  Enabled by ENABLE_DEF_AUTH.

Added a simple packet filter functionality that can be driven by
a plugin.  See comments in openvpn-plugin.h for documentation.
Enabled by ENABLE_PF.

See openvpn/plugin/defer/simple.c for examples of ENABLE_DEF_AUTH
and ENABLE_PF.

"TLS Error: local/remote TLS keys are out of sync" is no longer a
fatal error for TCP-based sessions, since the error can arise
normally in the course of deferred authentication.  In a related
change, allow packet-id sequence to begin at some number n > 0 for
TCP sessions, rather than strictly requiring sequence to begin
at 1.

Added a test to configure.ac for LoadLibrary function on Windows.

Modified "make dist" function to include all files from
install-win32 so that ./domake-win can be run from a
tarball-expanded directory.

setenv and setenv-safe directives may now omit a value argument
which defaults to "".


git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@2978 e7ae566f-a301-0410-adde-c780ea21d3b5
This commit is contained in:
james 2008-06-04 05:16:44 +00:00
parent 7c51fe16b4
commit 47ae8457f9
26 changed files with 1215 additions and 113 deletions

View file

@ -112,6 +112,7 @@ openvpn_SOURCES = \
otime.c otime.h \
packet_id.c packet_id.h \
perf.c perf.h \
pf.c pf.h \
ping.c ping.h ping-inline.h \
plugin.c plugin.h \
pool.c pool.h \

View file

@ -423,6 +423,15 @@ string_null_terminate (char *str, int len, int capacity)
*/
void
chomp (char *str)
{
rm_trailing_chars (str, "\r\n");
}
/*
* Remove trailing chars
*/
void
rm_trailing_chars (char *str, const char *what_to_delete)
{
bool modified;
do {
@ -431,7 +440,7 @@ chomp (char *str)
if (len > 0)
{
char *cp = str + (len - 1);
if (*cp == '\n' || *cp == '\r')
if (strchr (what_to_delete, *cp) != NULL)
{
*cp = '\0';
modified = true;

View file

@ -137,6 +137,13 @@ buf_reset (struct buffer *buf)
buf->data = NULL;
}
static inline void
buf_reset_len (struct buffer *buf)
{
buf->len = 0;
buf->offset = 0;
}
static inline bool
buf_init_dowork (struct buffer *buf, int offset)
{
@ -224,6 +231,7 @@ void buf_rmtail (struct buffer *buf, uint8_t remove);
* non-buffer string functions
*/
void chomp (char *str);
void rm_trailing_chars (char *str, const char *what_to_delete);
const char *skip_leading_whitespace (const char *str);
void string_null_terminate (char *str, int len, int capacity);

View file

@ -520,7 +520,7 @@ dnl Checking for a working epoll
AC_CHECKING([for working epoll implementation])
OLDLDFLAGS="$LDFLAGS"
LDFLAGS="$LDFLAGS -Wl,--fatal-warnings"
AC_CHECK_FUNCS(epoll_create, AC_DEFINE([HAVE_EPOLL_CREATE], 1, []))
AC_CHECK_FUNC(epoll_create, AC_DEFINE(HAVE_EPOLL_CREATE, 1, [epoll_create function is defined]))
LDFLAGS="$OLDLDFLAGS"
dnl
@ -608,6 +608,24 @@ if test "${WIN32}" != "yes"; then
fi
fi
dnl
dnl Check if LoadLibrary exists on Windows
dnl
if test "${WIN32}" == "yes"; then
if test "$PLUGINS" = "yes"; then
AC_TRY_LINK([
#include <windows.h>
], [
LoadLibrary (NULL);
], [
AC_MSG_RESULT([LoadLibrary DEFINED])
AC_DEFINE(USE_LOAD_LIBRARY, 1, [Use LoadLibrary to load DLLs on Windows])
], [
AC_MSG_RESULT([LoadLibrary UNDEFINED])
])
fi
fi
dnl
dnl check for LZO library
dnl

View file

@ -11,6 +11,9 @@
# and openvpnserv.exe) you can use the
# provided autoconf/automake build environment.
#
# If you are building from an expanded .tar.gz file,
# make sure to run "./doclean" before "./domake-win".
#
# See top-level build configuration and settings in:
#
# version.m4

View file

@ -94,6 +94,7 @@
#define D_ROUTE_QUOTA LOGLEV(3, 43, 0) /* show route quota exceeded messages */
#define D_OSBUF LOGLEV(3, 44, 0) /* show socket/tun/tap buffer sizes */
#define D_PS_PROXY LOGLEV(3, 45, 0) /* messages related to --port-share option */
#define D_PF LOGLEV(3, 46, 0) /* messages related to packet filter */
#define D_SHOW_PARMS LOGLEV(4, 50, 0) /* show all parameters on program initiation */
#define D_SHOW_OCC LOGLEV(4, 51, 0) /* show options compatibility string */

View file

@ -492,6 +492,10 @@ process_coarse_timers (struct context *c)
check_push_request (c);
#endif
#ifdef ENABLE_PF
pf_check_reload (c);
#endif
/* process --route options */
check_add_routes (c);

9
init.c
View file

@ -2737,6 +2737,11 @@ init_instance (struct context *c, const struct env_set *env, const unsigned int
init_port_share (c);
#endif
#ifdef ENABLE_PF
if (child)
pf_init_context (c);
#endif
/* Check for signals */
if (IS_SIG (c))
goto sig;
@ -2787,6 +2792,10 @@ close_instance (struct context *c)
/* close TUN/TAP device */
do_close_tun (c, false);
#ifdef ENABLE_PF
pf_destroy_context (&c->c2.pf);
#endif
#ifdef ENABLE_PLUGIN
/* call plugin close functions and unload */
do_close_plugins (c);

View file

@ -25,8 +25,32 @@
MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
dist_noinst_DATA = \
GetWindowsVersion.nsi \
build-pkcs11-helper.sh \
buildinstaller \
buildopensslpath.bat \
ddk-common \
doclean \
dosname.pl \
getgui \
getopenssl \
getpkcs11helper \
getprebuilt \
getxgui \
ifdef.pl \
m4todef.pl \
macro.pl \
makeopenvpn \
maketap \
maketapinstall \
maketext \
openssl.patch \
openvpn.nsi \
setpath.nsi
setpath.nsi \
settings.in \
trans.pl \
u2d.c \
winconfig
if WIN32

7
list.c
View file

@ -33,6 +33,7 @@
struct hash *
hash_init (const int n_buckets,
const uint32_t iv,
uint32_t (*hash_function)(const void *key, uint32_t iv),
bool (*compare_function)(const void *key1, const void *key2))
{
@ -45,7 +46,7 @@ hash_init (const int n_buckets,
h->mask = h->n_buckets - 1;
h->hash_function = hash_function;
h->compare_function = compare_function;
h->iv = get_random ();
h->iv = iv;
ALLOC_ARRAY (h->buckets, struct hash_bucket, h->n_buckets);
for (i = 0; i < h->n_buckets; ++i)
{
@ -398,8 +399,8 @@ list_test (void)
{
struct gc_arena gc = gc_new ();
struct hash *hash = hash_init (10000, word_hash_function, word_compare_function);
struct hash *nhash = hash_init (256, word_hash_function, word_compare_function);
struct hash *hash = hash_init (10000, get_random (), word_hash_function, word_compare_function);
struct hash *nhash = hash_init (256, get_random (), word_hash_function, word_compare_function);
printf ("hash_init n_buckets=%d mask=0x%08x\n", hash->n_buckets, hash->mask);

3
list.h
View file

@ -57,7 +57,7 @@ struct hash_element
struct hash_bucket
{
MUTEX_DEFINE (mutex);
struct hash_element * volatile list;
struct hash_element *list;
};
struct hash
@ -72,6 +72,7 @@ struct hash
};
struct hash *hash_init (const int n_buckets,
const uint32_t iv,
uint32_t (*hash_function)(const void *key, uint32_t iv),
bool (*compare_function)(const void *key1, const void *key2));

View file

@ -163,5 +163,14 @@ mroute_extract_in_addr_t (struct mroute_addr *dest, const in_addr_t src)
*(in_addr_t*)dest->addr = htonl (src);
}
static inline in_addr_t
in_addr_t_from_mroute_addr (const struct mroute_addr *addr)
{
if (addr->type == MR_ADDR_IPV4 && addr->netbits == 0 && addr->len == 4)
return ntohl(*(in_addr_t*)addr->addr);
else
return 0;
}
#endif /* P2MP_SERVER */
#endif /* MROUTE_H */

51
multi.c
View file

@ -229,6 +229,7 @@ multi_init (struct multi_context *m, struct context *t, bool tcp_mode, int threa
* which is seen on the TCP/UDP socket.
*/
m->hash = hash_init (t->options.real_hash_size,
get_random (),
mroute_addr_hash_function,
mroute_addr_compare_function);
@ -237,6 +238,7 @@ multi_init (struct multi_context *m, struct context *t, bool tcp_mode, int threa
* which client to route a packet to.
*/
m->vhash = hash_init (t->options.virtual_hash_size,
get_random (),
mroute_addr_hash_function,
mroute_addr_compare_function);
@ -246,6 +248,7 @@ multi_init (struct multi_context *m, struct context *t, bool tcp_mode, int threa
* for fast iteration through the list.
*/
m->iter = hash_init (1,
get_random (),
mroute_addr_hash_function,
mroute_addr_compare_function);
@ -1818,12 +1821,29 @@ multi_process_incoming_link (struct multi_context *m, struct multi_instance *ins
/* if dest addr is a known client, route to it */
if (mi)
{
multi_unicast (m, &c->c2.to_tun, mi);
register_activity (c, BLEN(&c->c2.to_tun));
#ifdef ENABLE_PF
if (!pf_c2c_test (c, &mi->context))
{
msg (D_PF, "PF: client -> [%s] packet dropped by packet filter",
np (mi->msg_prefix));
}
else
#endif
{
multi_unicast (m, &c->c2.to_tun, mi);
register_activity (c, BLEN(&c->c2.to_tun));
}
c->c2.to_tun.len = 0;
}
}
}
#ifdef ENABLE_PF
else if (!pf_addr_test (c, &dest))
{
msg (D_PF, "PF: client -> [%s] packet dropped by packet filter",
mroute_addr_print (&dest, &gc));
}
#endif
}
else if (TUNNEL_TYPE (m->top.c1.tuntap) == DEV_TYPE_TAP)
{
@ -1936,17 +1956,28 @@ multi_process_incoming_tun (struct multi_context *m, const unsigned int mpp_flag
set_prefix (m->pending);
if (multi_output_queue_ready (m, m->pending))
#ifdef ENABLE_PF
if (!pf_addr_test (c, &src))
{
/* transfer packet pointer from top-level context buffer to instance */
c->c2.buf = m->top.c2.buf;
msg (D_PF, "PF: [%s] -> client packet dropped by packet filter",
mroute_addr_print (&src, &gc));
buf_reset_len (&c->c2.buf);
}
else
{
/* drop packet */
msg (D_MULTI_DROPPED, "MULTI: packet dropped due to output saturation (multi_process_incoming_tun)");
buf_clear (&c->c2.buf);
}
#endif
{
if (multi_output_queue_ready (m, m->pending))
{
/* transfer packet pointer from top-level context buffer to instance */
c->c2.buf = m->top.c2.buf;
}
else
{
/* drop packet */
msg (D_MULTI_DROPPED, "MULTI: packet dropped due to output saturation (multi_process_incoming_tun)");
buf_reset_len (&c->c2.buf);
}
}
/* encrypt in instance context */
process_incoming_tun (c);

View file

@ -41,13 +41,13 @@
* New Client Connection:
*
* FUNC: openvpn_plugin_client_constructor_v1
* FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_VERIFY (called once for every cert
* FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_TLS_VERIFY (called once for every cert
* in the server chain)
* FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_AUTH_USER_PASS_TLS_VERIFY
* FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY
* FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_TLS_FINAL
* FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_IPCHANGE
*
* [If OPENVPN_PLUGIN_AUTH_USER_PASS_TLS_VERIFY returned OPENVPN_PLUGIN_FUNC_DEFERRED,
* [If OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY returned OPENVPN_PLUGIN_FUNC_DEFERRED,
* we don't proceed until authentication is verified via auth_control_file]
*
* FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_CLIENT_CONNECT_V2
@ -57,12 +57,14 @@
*
* For each "TLS soft reset", according to reneg-sec option (or similar):
*
* FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_VERIFY (called once for every cert
* FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_ENABLE_PF
*
* FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_TLS_VERIFY (called once for every cert
* in the server chain)
* FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_AUTH_USER_PASS_TLS_VERIFY
* FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY
* FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_TLS_FINAL
*
* [If OPENVPN_PLUGIN_AUTH_USER_PASS_TLS_VERIFY returned OPENVPN_PLUGIN_FUNC_DEFERRED,
* [If OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY returned OPENVPN_PLUGIN_FUNC_DEFERRED,
* we expect that authentication is verified via auth_control_file within
* the number of seconds defined by the "hand-window" option. Data channel traffic
* will continue to flow uninterrupted during this period.]
@ -94,7 +96,8 @@
#define OPENVPN_PLUGIN_LEARN_ADDRESS 8
#define OPENVPN_PLUGIN_CLIENT_CONNECT_V2 9
#define OPENVPN_PLUGIN_TLS_FINAL 10
#define OPENVPN_PLUGIN_N 11
#define OPENVPN_PLUGIN_ENABLE_PF 11
#define OPENVPN_PLUGIN_N 12
/*
* Build a mask out of a set of plug-in types.
@ -270,16 +273,52 @@ OPENVPN_PLUGIN_DEF openvpn_plugin_handle_t OPENVPN_PLUGIN_FUNC(openvpn_plugin_op
* 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.
*
* If an OPENVPN_PLUGIN_ENABLE_PF handler is defined and returns success
* for a particular client instance, packet filtering will be enabled for that
* instance. OpenVPN will then attempt to read the packet filter configuration
* from the temporary file named by the environmental variable pf_file. This
* file may be generated asynchronously and may be dynamically updated during the
* client session, however the client will be blocked from sending or receiving
* VPN tunnel packets until the packet filter file has been generated. OpenVPN
* will periodically test the packet filter file over the life of the client
* instance and reload when modified. OpenVPN will delete the packet filter file
* when the client instance goes out of scope.
*
* Packet filter file grammar:
*
* [CLIENTS DROP|ACCEPT]
* {+|-}common_name1
* {+|-}common_name2
* . . .
* [SUBNETS DROP|ACCEPT]
* {+|-}subnet1
* {+|-}subnet2
* . . .
* [END]
*
* Subnet: IP-ADDRESS | IP-ADDRESS/NUM_NETWORK_BITS
*
* CLIENTS refers to the set of clients (by their common-name) which
* this instance is allowed ('+') to connect to, or is excluded ('-')
* from connecting to. Note that in the case of client-to-client
* connections, such communication must be allowed by the packet filter
* configuration files of both clients.
*
* SUBNETS refers to IP addresses or IP address subnets which this
* instance may connect to ('+') or is excluded ('-') from connecting
* to.
*
* DROP or ACCEPT defines default policy when there is no explicit match
* for a common-name or subnet. The [END] tag must exist. A special
* purpose tag called [KILL] will immediately kill the client instance.
* A given client or subnet rule applies to both incoming and outgoing
* packets.
*
* See plugin/defer/simple.c for an example on using asynchronous
* authentication.
* authentication and client-specific packet filtering.
*/
OPENVPN_PLUGIN_DEF int OPENVPN_PLUGIN_FUNC(openvpn_plugin_func_v2)
(openvpn_plugin_handle_t handle,

View file

@ -46,6 +46,7 @@
#include "pool.h"
#include "plugin.h"
#include "manage.h"
#include "pf.h"
/*
* Our global key schedules, packaged thusly
@ -430,7 +431,11 @@ struct context_2
const char *pulled_options_string;
struct event_timeout scheduled_exit;
#endif
/* packet filter */
#ifdef ENABLE_PF
struct pf_context pf;
#endif
};

View file

@ -4023,15 +4023,15 @@ add_option (struct options *options,
}
options->routes->flags |= RG_ENABLE;
}
else if (streq (p[0], "setenv") && p[1] && p[2])
else if (streq (p[0], "setenv") && p[1])
{
VERIFY_PERMISSION (OPT_P_GENERAL);
setenv_str (es, p[1], p[2]);
setenv_str (es, p[1], p[2] ? p[2] : "");
}
else if (streq (p[0], "setenv-safe") && p[1] && p[2])
else if (streq (p[0], "setenv-safe") && p[1])
{
VERIFY_PERMISSION (OPT_P_SETENV);
setenv_str_safe (es, p[1], p[2]);
setenv_str_safe (es, p[1], p[2] ? p[2] : "");
}
else if (streq (p[0], "mssfix"))
{

View file

@ -209,12 +209,12 @@ packet_id_test (const struct packet_id_rec *p,
{
/*
* In non-backtrack mode, all sequence number series must
* begin at 1 and must increment linearly without gaps.
* begin at some number n > 0 and must increment linearly without gaps.
*
* This mode is used with TCP.
*/
if (pin->time == p->time)
return pin->id == p->id + 1;
return !p->id || pin->id == p->id + 1;
else if (pin->time < p->time) /* if time goes back, reject */
return false;
else /* time moved forward */

627
pf.c Normal file
View file

@ -0,0 +1,627 @@
/*
* 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
*/
/* packet filter functions */
#include "syshead.h"
#if defined(ENABLE_PF)
#include "init.h"
#include "memdbg.h"
static void
pf_destroy (struct pf_set *pfs)
{
if (pfs)
{
if (pfs->cns.hash_table)
hash_free (pfs->cns.hash_table);
{
struct pf_cn_elem *l = pfs->cns.list;
while (l)
{
struct pf_cn_elem *next = l->next;
free (l->rule.cn);
free (l);
l = next;
}
}
{
struct pf_subnet *l = pfs->sns.list;
while (l)
{
struct pf_subnet *next = l->next;
free (l);
l = next;
}
}
free (pfs);
}
}
static bool
add_client (const char *line, const char *fn, const int line_num, struct pf_cn_elem ***next, const bool exclude)
{
struct pf_cn_elem *e;
ALLOC_OBJ_CLEAR (e, struct pf_cn_elem);
e->rule.exclude = exclude;
e->rule.cn = string_alloc (line, NULL);
**next = e;
*next = &e->next;
return true;
}
static bool
add_subnet (const char *line, const char *fn, const int line_num, struct pf_subnet ***next, const bool exclude)
{
struct in_addr network;
in_addr_t netmask = 0;
int netbits = 32;
char *div = strchr (line, '/');
if (div)
{
*div++ = '\0';
if (sscanf (div, "%d", &netbits) != 1)
{
msg (D_PF, "PF: %s/%d: bad '/n' subnet specifier: '%s'", fn, line_num, div);
return false;
}
if (netbits < 0 || netbits > 32)
{
msg (D_PF, "PF: %s/%d: bad '/n' subnet specifier: must be between 0 and 32: '%s'", fn, line_num, div);
return false;
}
}
if (openvpn_inet_aton (line, &network) != OIA_IP)
{
msg (D_PF, "PF: %s/%d: bad network address: '%s'", fn, line_num, line);
return false;
}
netmask = netbits_to_netmask (netbits);
{
struct pf_subnet *e;
ALLOC_OBJ_CLEAR (e, struct pf_subnet);
e->rule.exclude = exclude;
e->rule.network = ntohl (network.s_addr);
e->rule.netmask = netmask;
**next = e;
*next = &e->next;
return true;
}
}
static uint32_t
cn_hash_function (const void *key, uint32_t iv)
{
return hash_func ((uint8_t *)key, strlen ((char *)key) + 1, iv);
}
static bool
cn_compare_function (const void *key1, const void *key2)
{
return !strcmp((const char *)key1, (const char *)key2);
}
static bool
genhash (struct pf_cn_set *cns, const char *fn, const int n_clients)
{
struct pf_cn_elem *e;
bool status = true;
int n_buckets = n_clients;
if (n_buckets < 16)
n_buckets = 16;
cns->hash_table = hash_init (n_buckets, 0, cn_hash_function, cn_compare_function);
for (e = cns->list; e != NULL; e = e->next)
{
if (!hash_add (cns->hash_table, e->rule.cn, &e->rule, false))
{
msg (D_PF, "PF: %s: duplicate common name in [clients] section: '%s'", fn, e->rule.cn);
status = false;
}
}
return status;
}
static struct pf_set *
pf_init (const char *fn)
{
# define MODE_UNDEF 0
# define MODE_CLIENTS 1
# define MODE_SUBNETS 2
int mode = MODE_UNDEF;
int line_num = 0;
int n_clients = 0;
int n_subnets = 0;
int n_errors = 0;
struct pf_set *pfs = NULL;
char line[256];
ALLOC_OBJ_CLEAR (pfs, struct pf_set);
FILE *fp = fopen (fn, "r");
if (fp)
{
struct pf_cn_elem **cl = &pfs->cns.list;
struct pf_subnet **sl = &pfs->sns.list;
while (fgets (line, sizeof (line), fp) != NULL)
{
++line_num;
rm_trailing_chars (line, "\r\n\t ");
if (line[0] == '\0' || line[0] == '#')
;
else if (line[0] == '+' || line[0] == '-')
{
bool exclude = (line[0] == '-');
if (line[1] =='\0')
{
msg (D_PF, "PF: %s/%d: no data after +/-: '%s'", fn, line_num, line);
++n_errors;
}
else if (mode == MODE_CLIENTS)
{
if (add_client (&line[1], fn, line_num, &cl, exclude))
++n_clients;
else
++n_errors;
}
else if (mode == MODE_SUBNETS)
{
if (add_subnet (&line[1], fn, line_num, &sl, exclude))
++n_subnets;
else
++n_errors;
}
else if (mode == MODE_UNDEF)
;
else
{
ASSERT (0);
}
}
else if (line[0] == '[')
{
if (!strcasecmp (line, "[clients accept]"))
{
mode = MODE_CLIENTS;
pfs->cns.default_allow = true;
}
else if (!strcasecmp (line, "[clients drop]"))
{
mode = MODE_CLIENTS;
pfs->cns.default_allow = false;
}
else if (!strcasecmp (line, "[subnets accept]"))
{
mode = MODE_SUBNETS;
pfs->sns.default_allow = true;
}
else if (!strcasecmp (line, "[subnets drop]"))
{
mode = MODE_SUBNETS;
pfs->sns.default_allow = false;
}
else if (!strcasecmp (line, "[end]"))
goto done;
else if (!strcasecmp (line, "[kill]"))
goto kill;
else
{
mode = MODE_UNDEF;
msg (D_PF, "PF: %s/%d unknown tag: '%s'", fn, line_num, line);
++n_errors;
}
}
else
{
msg (D_PF, "PF: %s/%d line must begin with '+', '-', or '[' : '%s'", fn, line_num, line);
++n_errors;
}
}
++n_errors;
msg (D_PF, "PF: %s: missing [end]", fn);
}
else
{
msg (D_PF|M_ERRNO, "PF: %s: cannot open", fn);
++n_errors;
}
done:
if (fp)
{
fclose (fp);
if (!n_errors)
{
if (!genhash (&pfs->cns, fn, n_clients))
++n_errors;
}
if (n_errors)
msg (D_PF, "PF: %s rejected due to %d error(s)", fn, n_errors);
}
if (n_errors)
{
pf_destroy (pfs);
pfs = NULL;
}
return pfs;
kill:
if (fp)
fclose (fp);
pf_destroy (pfs);
ALLOC_OBJ_CLEAR (pfs, struct pf_set);
pfs->kill = true;
return pfs;
}
#if PF_DEBUG >= 1
static const char *
drop_accept (const bool accept)
{
return accept ? "ACCEPT" : "DROP";
}
#endif
#if PF_DEBUG >= 2
static void
pf_cn_test_print (const char *prefix,
const char *cn,
const bool allow,
const struct pf_cn *rule)
{
if (rule)
{
msg (D_PF, "PF: %s %s %s rule=[%s %s]",
prefix, cn, drop_accept (allow),
rule->cn, drop_accept (!rule->exclude));
}
else
{
msg (D_PF, "PF: %s %s %s",
prefix, cn, drop_accept (allow));
}
}
static void
pf_addr_test_print (const char *prefix,
const struct context *src,
const struct mroute_addr *dest,
const bool allow,
const struct ipv4_subnet *rule)
{
struct gc_arena gc = gc_new ();
if (rule)
{
msg (D_PF, "PF: %s %s %s %s rule=[%s/%s %s]",
prefix,
tls_common_name (src->c2.tls_multi, false),
mroute_addr_print (dest, &gc),
drop_accept (allow),
print_in_addr_t (rule->network, 0, &gc),
print_in_addr_t (rule->netmask, 0, &gc),
drop_accept (!rule->exclude));
}
else
{
msg (D_PF, "PF: %s %s %s %s",
prefix,
tls_common_name (src->c2.tls_multi, false),
mroute_addr_print (dest, &gc),
drop_accept (allow));
}
gc_free (&gc);
}
#endif
static inline struct pf_cn *
lookup_cn_rule (struct hash *h, const char *cn, const uint32_t cn_hash)
{
struct hash_element *he = hash_lookup_fast (h, hash_bucket (h, cn_hash), cn, cn_hash);
if (he)
return (struct pf_cn *) he->value;
else
return NULL;
}
static inline bool
cn_test (struct pf_set *pfs, const struct tls_multi *tm)
{
if (!pfs->kill)
{
const char *cn;
uint32_t cn_hash;
if (tls_common_name_hash (tm, &cn, &cn_hash))
{
const struct pf_cn *rule = lookup_cn_rule (pfs->cns.hash_table, cn, cn_hash);
if (rule)
{
#if PF_DEBUG >= 2
pf_cn_test_print ("PF_CN_MATCH", cn, !rule->exclude, rule);
#endif
if (!rule->exclude)
return true;
else
return false;
}
else
{
#if PF_DEBUG >= 2
pf_cn_test_print ("PF_CN_DEFAULT", cn, pfs->cns.default_allow, NULL);
#endif
if (pfs->cns.default_allow)
return true;
else
return false;
}
}
}
#if PF_DEBUG >= 2
pf_cn_test_print ("PF_CN_FAULT", tls_common_name (tm, false), false, NULL);
#endif
return false;
}
bool
pf_c2c_test (const struct context *src, const struct context *dest)
{
return (!src->c2.pf.filename || cn_test (src->c2.pf.pfs, dest->c2.tls_multi))
&& (!dest->c2.pf.filename || cn_test (dest->c2.pf.pfs, src->c2.tls_multi));
}
bool
pf_addr_test (const struct context *src, const struct mroute_addr *dest)
{
if (src->c2.pf.filename)
{
struct pf_set *pfs = src->c2.pf.pfs;
if (pfs && !pfs->kill)
{
const in_addr_t addr = in_addr_t_from_mroute_addr (dest);
const struct pf_subnet *se = pfs->sns.list;
while (se)
{
if ((addr & se->rule.netmask) == se->rule.network)
{
#if PF_DEBUG >= 2
pf_addr_test_print ("PF_ADDR_MATCH", src, dest, !se->rule.exclude, &se->rule);
#endif
return !se->rule.exclude;
}
se = se->next;
}
#if PF_DEBUG >= 2
pf_addr_test_print ("PF_ADDR_DEFAULT", src, dest, pfs->sns.default_allow, NULL);
#endif
return pfs->sns.default_allow;
}
else
{
#if PF_DEBUG >= 2
pf_addr_test_print ("PF_ADDR_FAULT", src, dest, false, NULL);
#endif
return false;
}
}
else
{
return true;
}
}
void
pf_check_reload (struct context *c)
{
const int slow_wakeup = 15;
const int fast_wakeup = 1;
const int wakeup_transition = 60;
bool reloaded = false;
if (c->c2.pf.filename && event_timeout_trigger (&c->c2.pf.reload, &c->c2.timeval, ETT_DEFAULT))
{
struct stat s;
if (!stat (c->c2.pf.filename, &s))
{
if (s.st_mtime > c->c2.pf.file_last_mod)
{
struct pf_set *pfs = pf_init (c->c2.pf.filename);
if (pfs)
{
if (c->c2.pf.pfs)
pf_destroy (c->c2.pf.pfs);
c->c2.pf.pfs = pfs;
reloaded = true;
if (pf_kill_test (pfs))
{
c->sig->signal_received = SIGTERM;
c->sig->signal_text = "pf-kill";
}
}
c->c2.pf.file_last_mod = s.st_mtime;
}
}
{
int wakeup = slow_wakeup;
if (!c->c2.pf.pfs && c->c2.pf.n_check_reload < wakeup_transition)
wakeup = fast_wakeup;
event_timeout_init (&c->c2.pf.reload, wakeup, now);
reset_coarse_timers (c);
c->c2.pf.n_check_reload++;
}
}
#if PF_DEBUG >= 1
if (reloaded)
pf_context_print (&c->c2.pf, "pf_check_reload", M_INFO);
#endif
}
void
pf_init_context (struct context *c)
{
struct gc_arena gc = gc_new ();
if (plugin_defined (c->plugins, OPENVPN_PLUGIN_ENABLE_PF))
{
const char *pf_file = create_temp_filename (c->options.tmp_dir, "pf", &gc);
delete_file (pf_file);
setenv_str (c->c2.es, "pf_file", pf_file);
if (plugin_call (c->plugins, OPENVPN_PLUGIN_ENABLE_PF, NULL, NULL, c->c2.es) == OPENVPN_PLUGIN_FUNC_SUCCESS)
{
event_timeout_init (&c->c2.pf.reload, 1, now);
c->c2.pf.filename = string_alloc (pf_file, NULL);
#if PF_DEBUG >= 1
pf_context_print (&c->c2.pf, "pf_init_context", M_INFO);
#endif
}
else
{
msg (M_WARN, "WARNING: OPENVPN_PLUGIN_ENABLE_PF disabled");
}
}
gc_free (&gc);
}
void
pf_destroy_context (struct pf_context *pfc)
{
if (pfc->filename)
{
delete_file (pfc->filename);
free (pfc->filename);
}
if (pfc->pfs)
pf_destroy (pfc->pfs);
}
#if PF_DEBUG >= 1
static void
pf_subnet_set_print (const struct pf_subnet_set *s, const int lev)
{
struct gc_arena gc = gc_new ();
if (s)
{
struct pf_subnet *e;
msg (lev, " ----- struct pf_subnet_set -----");
msg (lev, " default_allow=%s", drop_accept (s->default_allow));
for (e = s->list; e != NULL; e = e->next)
{
msg (lev, " %s/%s %s",
print_in_addr_t (e->rule.network, 0, &gc),
print_in_addr_t (e->rule.netmask, 0, &gc),
drop_accept (!e->rule.exclude));
}
}
gc_free (&gc);
}
static void
pf_cn_set_print (const struct pf_cn_set *s, const int lev)
{
if (s)
{
struct hash_iterator hi;
struct hash_element *he;
msg (lev, " ----- struct pf_cn_set -----");
msg (lev, " default_allow=%s", drop_accept (s->default_allow));
if (s->hash_table)
{
hash_iterator_init (s->hash_table, &hi, false);
while ((he = hash_iterator_next (&hi)))
{
struct pf_cn *e = (struct pf_cn *)he->value;
msg (lev, " %s %s",
e->cn,
drop_accept (!e->exclude));
}
msg (lev, " ----------");
{
struct pf_cn_elem *ce;
for (ce = s->list; ce != NULL; ce = ce->next)
{
struct pf_cn *e = lookup_cn_rule (s->hash_table, ce->rule.cn, cn_hash_function (ce->rule.cn, 0));
if (e)
{
msg (lev, " %s %s",
e->cn,
drop_accept (!e->exclude));
}
else
{
msg (lev, " %s LOOKUP FAILED", ce->rule.cn);
}
}
}
}
}
}
static void
pf_set_print (const struct pf_set *pfs, const int lev)
{
if (pfs)
{
msg (lev, " ----- struct pf_set -----");
msg (lev, " kill=%d", pfs->kill);
pf_subnet_set_print (&pfs->sns, lev);
pf_cn_set_print (&pfs->cns, lev);
}
}
void
pf_context_print (const struct pf_context *pfc, const char *prefix, const int lev)
{
msg (lev, "----- %s : struct pf_context -----", prefix);
if (pfc)
{
msg (lev, "filename='%s'", np(pfc->filename));
msg (lev, "file_last_mod=%u", (unsigned int)pfc->file_last_mod);
msg (lev, "n_check_reload=%u", pfc->n_check_reload);
msg (lev, "reload=[%d,%u,%u]", pfc->reload.defined, pfc->reload.n, (unsigned int)pfc->reload.last);
pf_set_print (pfc->pfs, lev);
}
msg (lev, "--------------------");
}
#endif
#endif

103
pf.h Normal file
View file

@ -0,0 +1,103 @@
/*
* 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
*/
/* packet filter functions */
#if defined(ENABLE_PF) && !defined(OPENVPN_PF_H)
#define OPENVPN_PF_H
#include "list.h"
#include "mroute.h"
#define PF_DEBUG 0
struct context;
struct ipv4_subnet {
bool exclude;
in_addr_t network;
in_addr_t netmask;
};
struct pf_subnet {
struct pf_subnet *next;
struct ipv4_subnet rule;
};
struct pf_subnet_set {
bool default_allow;
struct pf_subnet *list;
};
struct pf_cn {
bool exclude;
char *cn;
};
struct pf_cn_elem {
struct pf_cn_elem *next;
struct pf_cn rule;
};
struct pf_cn_set {
bool default_allow;
struct pf_cn_elem *list;
struct hash *hash_table;
};
struct pf_set {
bool kill;
struct pf_subnet_set sns;
struct pf_cn_set cns;
};
struct pf_context {
char *filename;
time_t file_last_mod;
unsigned int n_check_reload;
struct event_timeout reload;
struct pf_set *pfs;
};
void pf_init_context (struct context *c);
void pf_destroy_context (struct pf_context *pfc);
void pf_check_reload (struct context *c);
bool pf_c2c_test (const struct context *src, const struct context *dest);
bool pf_addr_test (const struct context *src, const struct mroute_addr *dest);
static inline bool
pf_kill_test (const struct pf_set *pfs)
{
return pfs->kill;
}
#if PF_DEBUG >= 1
void pf_context_print (const struct pf_context *pfc, const char *prefix, const int lev);
#endif
#endif

View file

@ -83,6 +83,8 @@ plugin_type_name (const int type)
return "PLUGIN_LEARN_ADDRESS";
case OPENVPN_PLUGIN_TLS_FINAL:
return "PLUGIN_TLS_FINAL";
case OPENVPN_PLUGIN_ENABLE_PF:
return "OPENVPN_PLUGIN_ENABLE_PF";
default:
return "PLUGIN_???";
}
@ -540,6 +542,7 @@ plugin_call (const struct plugin_list *pl,
int i;
const char **envp;
const int n = plugin_n (pl);
bool success = false;
bool error = false;
bool deferred = false;
@ -556,10 +559,18 @@ plugin_call (const struct plugin_list *pl,
args,
pr ? &pr->list[i] : NULL,
envp);
if (status == OPENVPN_PLUGIN_FUNC_ERROR)
error = true;
else if (status == OPENVPN_PLUGIN_FUNC_DEFERRED)
deferred = true;
switch (status)
{
case OPENVPN_PLUGIN_FUNC_SUCCESS:
success = true;
break;
case OPENVPN_PLUGIN_FUNC_DEFERRED:
deferred = true;
break;
default:
error = true;
break;
}
}
if (pr)
@ -569,7 +580,9 @@ plugin_call (const struct plugin_list *pl,
gc_free (&gc);
if (error)
if (type == OPENVPN_PLUGIN_ENABLE_PF && success)
return OPENVPN_PLUGIN_FUNC_SUCCESS;
else if (error)
return OPENVPN_PLUGIN_FUNC_ERROR;
else if (deferred)
return OPENVPN_PLUGIN_FUNC_DEFERRED;

View file

@ -24,7 +24,30 @@
/*
* This file implements a simple OpenVPN plugin module which
* will test deferred authentication. Will run on Windows or *nix.
* will test deferred authentication and packet filtering.
*
* Will run on Windows or *nix.
*
* Sample usage:
*
* setenv test_deferred_auth 20
* setenv test_packet_filter 10
* plugin plugin/defer/simple.so
*
* This will enable deferred authentication to occur 20
* seconds after the normal TLS authentication process,
* and will cause a packet filter file to be generated 10
* seconds after the initial TLS negotiation, using
* {common-name}.pf as the source.
*
* Sample packet filter configuration:
*
* [CLIENTS DROP]
* +otherclient
* [SUBNETS DROP]
* +10.0.0.0/8
* -10.10.0.8
* [END]
*
* See the README file for build instructions.
*/
@ -35,11 +58,23 @@
#include "openvpn-plugin.h"
/* bool definitions */
#define bool int
#define true 1
#define false 0
/*
* Our context, where we keep our state.
*/
struct plugin_context {
int dummy;
int test_deferred_auth;
int test_packet_filter;
};
struct plugin_per_client_context {
int n_calls;
bool generated_pf_file;
};
/*
@ -77,6 +112,15 @@ np (const char *str)
return "[NULL]";
}
static int
atoi_null0 (const char *str)
{
if (str)
return atoi (str);
else
return 0;
}
OPENVPN_EXPORT openvpn_plugin_handle_t
openvpn_plugin_open_v1 (unsigned int *type_mask, const char *argv[], const char *envp[])
{
@ -89,11 +133,14 @@ openvpn_plugin_open_v1 (unsigned int *type_mask, const char *argv[], const char
*/
context = (struct plugin_context *) calloc (1, sizeof (struct plugin_context));
context->test_deferred_auth = atoi_null0 (get_env ("test_deferred_auth", envp));
printf ("TEST_DEFERRED_AUTH %d\n", context->test_deferred_auth);
context->test_packet_filter = atoi_null0 (get_env ("test_packet_filter", envp));
printf ("TEST_PACKET_FILTER %d\n", context->test_packet_filter);
/*
* Which callbacks to intercept. We are only interested in
* OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY, but we intercept all
* the callbacks for illustration purposes, so we can show
* the calling sequence via debug output.
* Which callbacks to intercept.
*/
*type_mask =
OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_UP) |
@ -105,45 +152,92 @@ openvpn_plugin_open_v1 (unsigned int *type_mask, const char *argv[], const char
OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_CLIENT_CONNECT_V2) |
OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_CLIENT_DISCONNECT) |
OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_LEARN_ADDRESS) |
OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_TLS_FINAL);
OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_TLS_FINAL) |
OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_ENABLE_PF);
return (openvpn_plugin_handle_t) context;
}
static int
auth_user_pass_verify (struct plugin_context *context, const char *argv[], const char *envp[])
auth_user_pass_verify (struct plugin_context *context, struct plugin_per_client_context *pcc, const char *argv[], const char *envp[])
{
/* 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)
if (context->test_deferred_auth)
{
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;
/* get username/password from envp string array */
const char *username = get_env ("username", envp);
const char *password = get_env ("password", envp);
/* get auth_control_file filename from envp string array*/
const char *auth_control_file = get_env ("auth_control_file", envp);
printf ("DEFER u='%s' p='%s' acf='%s'\n",
np(username),
np(password),
np(auth_control_file));
/* Authenticate asynchronously in n seconds */
if (auth_control_file)
{
char buf[256];
int auth = 2;
sscanf (username, "%d", &auth);
snprintf (buf, sizeof(buf), "( sleep %d ; echo AUTH %s %d ; echo %d >%s ) &",
context->test_deferred_auth,
auth_control_file,
auth,
pcc->n_calls < auth,
auth_control_file);
printf ("%s\n", buf);
system (buf);
pcc->n_calls++;
return OPENVPN_PLUGIN_FUNC_DEFERRED;
}
else
return OPENVPN_PLUGIN_FUNC_ERROR;
}
else
return OPENVPN_PLUGIN_FUNC_ERROR;
return OPENVPN_PLUGIN_FUNC_SUCCESS;
}
static int
tls_final (struct plugin_context *context, struct plugin_per_client_context *pcc, const char *argv[], const char *envp[])
{
if (context->test_packet_filter)
{
if (!pcc->generated_pf_file)
{
const char *pff = get_env ("pf_file", envp);
const char *cn = get_env ("username", envp);
if (pff && cn)
{
char buf[256];
snprintf (buf, sizeof(buf), "( sleep %d ; echo PF %s/%s ; cp \"%s.pf\" \"%s\" ) &",
context->test_packet_filter, cn, pff, cn, pff);
printf ("%s\n", buf);
system (buf);
pcc->generated_pf_file = true;
return OPENVPN_PLUGIN_FUNC_SUCCESS;
}
else
return OPENVPN_PLUGIN_FUNC_ERROR;
}
else
return OPENVPN_PLUGIN_FUNC_ERROR;
}
else
return OPENVPN_PLUGIN_FUNC_SUCCESS;
}
OPENVPN_EXPORT int
openvpn_plugin_func_v1 (openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[])
openvpn_plugin_func_v2 (openvpn_plugin_handle_t handle,
const int type,
const char *argv[],
const char *envp[],
void *per_client_context,
struct openvpn_plugin_string_list **return_list)
{
struct plugin_context *context = (struct plugin_context *) handle;
struct plugin_per_client_context *pcc = (struct plugin_per_client_context *) per_client_context;
switch (type)
{
case OPENVPN_PLUGIN_UP:
@ -163,7 +257,7 @@ openvpn_plugin_func_v1 (openvpn_plugin_handle_t handle, const int type, const ch
return OPENVPN_PLUGIN_FUNC_SUCCESS;
case OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY:
printf ("OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY\n");
return auth_user_pass_verify (context, argv, envp);
return auth_user_pass_verify (context, pcc, argv, envp);
case OPENVPN_PLUGIN_CLIENT_CONNECT_V2:
printf ("OPENVPN_PLUGIN_CLIENT_CONNECT_V2\n");
return OPENVPN_PLUGIN_FUNC_SUCCESS;
@ -175,7 +269,13 @@ openvpn_plugin_func_v1 (openvpn_plugin_handle_t handle, const int type, const ch
return OPENVPN_PLUGIN_FUNC_SUCCESS;
case OPENVPN_PLUGIN_TLS_FINAL:
printf ("OPENVPN_PLUGIN_TLS_FINAL\n");
return OPENVPN_PLUGIN_FUNC_SUCCESS;
return tls_final (context, pcc, argv, envp);
case OPENVPN_PLUGIN_ENABLE_PF:
printf ("OPENVPN_PLUGIN_ENABLE_PF\n");
if (context->test_packet_filter)
return OPENVPN_PLUGIN_FUNC_SUCCESS;
else
return OPENVPN_PLUGIN_FUNC_ERROR;
default:
printf ("OPENVPN_PLUGIN_?\n");
return OPENVPN_PLUGIN_FUNC_ERROR;
@ -186,7 +286,7 @@ OPENVPN_EXPORT void *
openvpn_plugin_client_constructor_v1 (openvpn_plugin_handle_t handle)
{
printf ("FUNC: openvpn_plugin_client_constructor_v1\n");
return malloc(1);
return calloc (1, sizeof (struct plugin_per_client_context));
}
OPENVPN_EXPORT void

2
sources Executable file
View file

@ -0,0 +1,2 @@
ls -1 *.[ch]
ls -1 configure.ac Makefile.am

102
ssl.c
View file

@ -47,6 +47,7 @@
#include "status.h"
#include "gremlin.h"
#include "pkcs11.h"
#include "list.h"
#ifdef WIN32
#include "cryptoapi.h"
@ -436,10 +437,22 @@ set_common_name (struct tls_session *session, const char *common_name)
{
free (session->common_name);
session->common_name = NULL;
#ifdef ENABLE_PF
session->common_name_hashval = 0;
#endif
}
if (common_name)
{
session->common_name = string_alloc (common_name, NULL);
#ifdef ENABLE_PF
{
const uint32_t len = (uint32_t) strlen (common_name);
if (len)
session->common_name_hashval = hash_func ((const uint8_t*)common_name, len+1, 0);
else
session->common_name_hashval = 0;
}
#endif
}
}
@ -825,7 +838,7 @@ tls_set_common_name (struct tls_multi *multi, const char *common_name)
}
const char *
tls_common_name (struct tls_multi *multi, bool null)
tls_common_name (const struct tls_multi *multi, const bool null)
{
const char *ret = NULL;
if (multi)
@ -846,6 +859,8 @@ tls_lock_common_name (struct tls_multi *multi)
multi->locked_cn = string_alloc (cn, NULL);
}
#ifdef ENABLE_DEF_AUTH
/*
* auth_control_file functions
*/
@ -876,34 +891,37 @@ key_state_gen_auth_control_file (struct key_state *ks, const struct tls_options
}
/* 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
#define ACF_UNDEFINED 0
#define ACF_SUCCEEDED 1
#define ACF_DISABLED 2
#define ACF_FAILED 3
static int
key_state_test_auth_control_file (const struct key_state *ks)
key_state_test_auth_control_file (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 ret = ks->auth_control_status;
if (ret == ACF_UNDEFINED)
{
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);
FILE *fp = fopen (ks->auth_control_file, "r");
if (fp)
{
const int c = fgetc (fp);
if (c == '1')
ret = ACF_SUCCEEDED;
else if (c == '0')
ret = ACF_FAILED;
fclose (fp);
ks->auth_control_status = ret;
}
}
return ret;
}
return ret;
return ACF_DISABLED;
}
#endif
/*
* Return current session authentication state. Return
* value is TLS_AUTHENTICATION_x.
@ -914,12 +932,13 @@ tls_authentication_status (struct tls_multi *multi, const int latency)
{
bool deferred = false;
bool success = false;
bool kill = false;
bool active = false;
#ifdef ENABLE_DEF_AUTH
if (latency && multi->tas_last && multi->tas_last + latency >= now)
return TLS_AUTHENTICATION_UNDEFINED;
multi->tas_last = now;
#endif
if (multi)
{
@ -932,6 +951,7 @@ tls_authentication_status (struct tls_multi *multi, const int latency)
active = true;
if (ks->authenticated)
{
#ifdef ENABLE_DEF_AUTH
switch (key_state_test_auth_control_file (ks))
{
case ACF_SUCCEEDED:
@ -946,25 +966,22 @@ tls_authentication_status (struct tls_multi *multi, const int latency)
case ACF_FAILED:
ks->authenticated = false;
break;
case ACF_KILL:
kill = true;
ks->authenticated = false;
break;
default:
ASSERT (0);
}
#else
success = true;
#endif
}
}
}
}
#if 0
dmsg (D_TLS_ERRORS, "TAS: a=%d k=%d s=%d d=%d", active, kill, success, deferred);
dmsg (D_TLS_ERRORS, "TAS: a=%d s=%d d=%d", active, success, deferred);
#endif
if (kill)
return TLS_AUTHENTICATION_FAILED;
else if (success)
if (success)
return TLS_AUTHENTICATION_SUCCEEDED;
else if (!active || deferred)
return TLS_AUTHENTICATION_DEFERRED;
@ -2001,7 +2018,9 @@ key_state_free (struct key_state *ks, bool clear)
packet_id_free (&ks->packet_id);
#ifdef ENABLE_DEF_AUTH
key_state_rm_auth_control_file (ks);
#endif
if (clear)
CLEAR (*ks);
@ -2914,15 +2933,19 @@ verify_user_pass_plugin (struct tls_session *session, const struct user_pass *up
/* setenv client real IP address */
setenv_untrusted (session);
#ifdef ENABLE_DEF_AUTH
/* generate filename for deferred auth control file */
key_state_gen_auth_control_file (ks, session->opt);
#endif
/* call command */
retval = plugin_call (session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY, NULL, NULL, session->opt->es);
#ifdef ENABLE_DEF_AUTH
/* purge auth control filename (and file itself) for non-deferred returns */
if (retval != OPENVPN_PLUGIN_FUNC_DEFERRED)
key_state_rm_auth_control_file (ks);
#endif
setenv_del (session->opt->es, "password");
setenv_str (session->opt->es, "username", up->username);
@ -3178,11 +3201,17 @@ 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 == OPENVPN_PLUGIN_FUNC_SUCCESS || s1 == OPENVPN_PLUGIN_FUNC_DEFERRED) && s2)
if ((s1 == OPENVPN_PLUGIN_FUNC_SUCCESS
#ifdef ENABLE_DEF_AUTH
|| s1 == OPENVPN_PLUGIN_FUNC_DEFERRED
#endif
) && s2)
{
ks->authenticated = true;
#ifdef ENABLE_DEF_AUTH
if (s1 == OPENVPN_PLUGIN_FUNC_DEFERRED)
ks->auth_deferred = true;
#endif
if (session->opt->username_as_common_name)
set_common_name (session, up->username);
msg (D_HANDSHAKE, "TLS: Username/Password authentication %s for username '%s' %s",
@ -3923,7 +3952,9 @@ tls_pre_decrypt (struct tls_multi *multi,
if (DECRYPT_KEY_ENABLED (multi, ks)
&& key_id == ks->key_id
&& ks->authenticated
#ifdef ENABLE_DEF_AUTH
&& !ks->auth_deferred
#endif
&& link_socket_actual_match (from, &ks->remote_addr))
{
/* return appropriate data channel decrypt key in opt */
@ -3950,7 +3981,11 @@ tls_pre_decrypt (struct tls_multi *multi,
key_id,
ks->key_id,
ks->authenticated,
#ifdef ENABLE_DEF_AUTH
ks->auth_deferred,
#else
-1,
#endif
link_socket_actual_match (from, &ks->remote_addr));
}
#endif
@ -3959,7 +3994,7 @@ tls_pre_decrypt (struct tls_multi *multi,
msg (D_TLS_ERRORS,
"TLS Error: local/remote TLS keys are out of sync: %s [%d]",
print_link_socket_actual (from, &gc), key_id);
goto error;
goto error_lite;
}
else /* control channel packet */
{
@ -4312,8 +4347,9 @@ tls_pre_decrypt (struct tls_multi *multi,
return ret;
error:
ERR_clear_error ();
++multi->n_soft_errors;
error_lite:
ERR_clear_error ();
goto done;
}
@ -4443,7 +4479,9 @@ tls_pre_encrypt (struct tls_multi *multi,
struct key_state *ks = multi->key_scan[i];
if (ks->state >= S_ACTIVE
&& ks->authenticated
#ifdef ENABLE_DEF_AUTH
&& !ks->auth_deferred
#endif
&& (!ks->key_id || now >= ks->auth_deferred_expire))
{
opt->key_ctx_bi = &ks->key;

52
ssl.h
View file

@ -370,11 +370,15 @@ 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;
#ifdef ENABLE_DEF_AUTH
/* If auth_deferred is true, authentication is being deferred */
bool auth_deferred;
time_t acf_last_mod;
char *auth_control_file;
int auth_control_status;
#endif
};
/*
@ -498,6 +502,11 @@ struct tls_session
int verify_maxlevel;
char *common_name;
#ifdef ENABLE_PF
uint32_t common_name_hashval;
#endif
bool verified; /* true if peer certificate was verified against CA */
/* not-yet-authenticated incoming client */
@ -569,8 +578,10 @@ struct tls_multi
*/
char *locked_cn;
#ifdef ENABLE_DEF_AUTH
/* Time of last call to tls_authentication_status */
time_t tas_last;
#endif
/*
* Our session objects.
@ -657,7 +668,7 @@ bool tls_send_payload (struct tls_multi *multi,
bool tls_rec_payload (struct tls_multi *multi,
struct buffer *buf);
const char *tls_common_name (struct tls_multi* multi, bool null);
const char *tls_common_name (const struct tls_multi* multi, const bool null);
void tls_set_common_name (struct tls_multi *multi, const char *common_name);
void tls_lock_common_name (struct tls_multi *multi);
@ -672,6 +683,17 @@ void tls_deauthenticate (struct tls_multi *multi);
* inline functions
*/
static inline bool
tls_test_auth_deferred_interval (const struct tls_multi *multi)
{
if (multi)
{
const struct key_state *ks = &multi->session[TM_ACTIVE].key[KS_PRIMARY];
return now < ks->auth_deferred_expire;
}
return false;
}
static inline int
tls_test_payload_len (const struct tls_multi *multi)
{
@ -691,6 +713,26 @@ tls_set_single_session (struct tls_multi *multi)
multi->opt.single_session = true;
}
#ifdef ENABLE_PF
static inline bool
tls_common_name_hash (const struct tls_multi *multi, const char **cn, uint32_t *cn_hash)
{
if (multi)
{
const struct tls_session *s = &multi->session[TM_ACTIVE];
if (s->common_name && s->common_name[0] != '\0')
{
*cn = s->common_name;
*cn_hash = s->common_name_hashval;
return true;
}
}
return false;
}
#endif
/*
* protocol_dump() flags
*/

View file

@ -470,6 +470,20 @@ socket_defined (const socket_descriptor_t sd)
#define ENABLE_PLUGIN
#endif
/*
* Enable deferred authentication
*/
#if defined(ENABLE_PLUGIN) && P2MP_SERVER
#define ENABLE_DEF_AUTH
#endif
/*
* Enable packet filter
*/
#if defined(ENABLE_PLUGIN) && P2MP_SERVER && defined(HAVE_STAT)
#define ENABLE_PF
#endif
/*
* Do we have pthread capability?
*/

View file

@ -1,5 +1,5 @@
dnl define the OpenVPN version
define(PRODUCT_VERSION,[2.1_rc7c])
define(PRODUCT_VERSION,[2.1_rc7d])
dnl define the TAP version
define(PRODUCT_TAP_ID,[tap0901])
define(PRODUCT_TAP_WIN32_MIN_MAJOR,[9])