summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjames <james@e7ae566f-a301-0410-adde-c780ea21d3b5>2008-06-04 05:16:44 +0000
committerjames <james@e7ae566f-a301-0410-adde-c780ea21d3b5>2008-06-04 05:16:44 +0000
commit47ae8457f9e9c2bb0f5c1e8f28822e1bbc16c196 (patch)
tree0f47ea714dda8312ee85fe7530ee231c59b91221
parent7c51fe16b435712423dd00145008ab58a95fdc5e (diff)
downloadopenvpn-47ae8457f9e9c2bb0f5c1e8f28822e1bbc16c196.tar.gz
openvpn-47ae8457f9e9c2bb0f5c1e8f28822e1bbc16c196.tar.xz
openvpn-47ae8457f9e9c2bb0f5c1e8f28822e1bbc16c196.zip
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
-rw-r--r--Makefile.am1
-rw-r--r--buffer.c11
-rw-r--r--buffer.h8
-rw-r--r--configure.ac20
-rw-r--r--domake-win3
-rw-r--r--errlevel.h1
-rw-r--r--forward.c4
-rw-r--r--init.c9
-rw-r--r--install-win32/Makefile.am26
-rw-r--r--list.c7
-rw-r--r--list.h3
-rw-r--r--mroute.h9
-rw-r--r--multi.c51
-rw-r--r--openvpn-plugin.h65
-rw-r--r--openvpn.h5
-rw-r--r--options.c8
-rw-r--r--packet_id.c4
-rw-r--r--pf.c627
-rw-r--r--pf.h103
-rw-r--r--plugin.c23
-rw-r--r--plugin/defer/simple.c162
-rwxr-xr-xsources2
-rw-r--r--ssl.c102
-rw-r--r--ssl.h48
-rw-r--r--syshead.h14
-rw-r--r--version.m42
26 files changed, 1210 insertions, 108 deletions
diff --git a/Makefile.am b/Makefile.am
index f8ce6f5..6b94ccd 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -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 \
diff --git a/buffer.c b/buffer.c
index 93c7930..6ceaacf 100644
--- a/buffer.c
+++ b/buffer.c
@@ -424,6 +424,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 {
const int len = strlen (str);
@@ -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;
diff --git a/buffer.h b/buffer.h
index 8d04103..eb37794 100644
--- a/buffer.h
+++ b/buffer.h
@@ -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);
diff --git a/configure.ac b/configure.ac
index ee8ae0b..c009566 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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
@@ -609,6 +609,24 @@ if test "${WIN32}" != "yes"; then
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
diff --git a/domake-win b/domake-win
index a6fb194..55b02de 100644
--- a/domake-win
+++ b/domake-win
@@ -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
diff --git a/errlevel.h b/errlevel.h
index 689d23a..652bf39 100644
--- a/errlevel.h
+++ b/errlevel.h
@@ -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 */
diff --git a/forward.c b/forward.c
index 3e09c7f..b7c8b3b 100644
--- a/forward.c
+++ b/forward.c
@@ -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);
diff --git a/init.c b/init.c
index dd1db5c..9d80d1a 100644
--- a/init.c
+++ b/init.c
@@ -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);
diff --git a/install-win32/Makefile.am b/install-win32/Makefile.am
index 80fd4be..559544c 100644
--- a/install-win32/Makefile.am
+++ b/install-win32/Makefile.am
@@ -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
diff --git a/list.c b/list.c
index 8849957..4fe7479 100644
--- a/list.c
+++ b/list.c
@@ -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);
diff --git a/list.h b/list.h
index 84b989d..39307fb 100644
--- a/list.h
+++ b/list.h
@@ -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));
diff --git a/mroute.h b/mroute.h
index fb7bee7..16d2add 100644
--- a/mroute.h
+++ b/mroute.h
@@ -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 */
diff --git a/multi.c b/multi.c
index 54e1d76..431ad95 100644
--- a/multi.c
+++ b/multi.c
@@ -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);
diff --git a/openvpn-plugin.h b/openvpn-plugin.h
index ceca186..81070f3 100644
--- a/openvpn-plugin.h
+++ b/openvpn-plugin.h
@@ -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,
diff --git a/openvpn.h b/openvpn.h
index 0cdab62..f637cd5 100644
--- a/openvpn.h
+++ b/openvpn.h
@@ -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
};
diff --git a/options.c b/options.c
index 1acfd6b..e355e16 100644
--- a/options.c
+++ b/options.c
@@ -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"))
{
diff --git a/packet_id.c b/packet_id.c
index 08e5974..b1e1caa 100644
--- a/packet_id.c
+++ b/packet_id.c
@@ -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 */
diff --git a/pf.c b/pf.c
new file mode 100644
index 0000000..87d7c3b
--- /dev/null
+++ b/pf.c
@@ -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
diff --git a/pf.h b/pf.h
new file mode 100644
index 0000000..754db8a
--- /dev/null
+++ b/pf.h
@@ -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
diff --git a/plugin.c b/plugin.c
index 1c1b545..508b12e 100644
--- a/plugin.c
+++ b/plugin.c
@@ -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;
diff --git a/plugin/defer/simple.c b/plugin/defer/simple.c
index 2dcf9f2..8be9300 100644
--- a/plugin/defer/simple.c
+++ b/plugin/defer/simple.c
@@ -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);
+ if (context->test_deferred_auth)
+ {
+ /* 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);
+ /* 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));
+ 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_SUCCESS;
+}
- /* Authenticate asynchronously in 10 seconds */
- if (auth_control_file)
+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)
{
- 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;
+ 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_ERROR;
+ 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
diff --git a/sources b/sources
new file mode 100755
index 0000000..dbfa018
--- /dev/null
+++ b/sources
@@ -0,0 +1,2 @@
+ls -1 *.[ch]
+ls -1 configure.ac Makefile.am
diff --git a/ssl.c b/ssl.c
index 2e0b3e3..9b3c4b5 100644
--- a/ssl.c
+++ b/ssl.c
@@ -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;
diff --git a/ssl.h b/ssl.h
index a7876cb..2f8095f 100644
--- a/ssl.h
+++ b/ssl.h
@@ -370,11 +370,15 @@ struct key_state
* If bad username/password, TLS connection will come up but 'authenticated' will be false.
*/
bool authenticated;
+ time_t auth_deferred_expire;
+#ifdef ENABLE_DEF_AUTH
/* If auth_deferred is true, authentication is being deferred */
- char *auth_control_file;
bool auth_deferred;
- time_t auth_deferred_expire;
+ 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
*/
diff --git a/syshead.h b/syshead.h
index 58e59c6..bb6a62d 100644
--- a/syshead.h
+++ b/syshead.h
@@ -471,6 +471,20 @@ socket_defined (const socket_descriptor_t sd)
#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?
*/
#ifdef USE_PTHREAD
diff --git a/version.m4 b/version.m4
index 4a1335c..3817318 100644
--- a/version.m4
+++ b/version.m4
@@ -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])