summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Sommerseth <dazo@users.sourceforge.net>2011-04-26 23:04:18 +0200
committerDavid Sommerseth <dazo@users.sourceforge.net>2011-04-26 23:04:18 +0200
commit20b18fd799e2ea9d0651f3ef913dd9ce2e481471 (patch)
tree86867a1128f35087f37d4b49ecf241b7b8e6b38e
parentc5f7d08b8c3d4287dd40bbdf52525add8f5cee20 (diff)
parente4359af463463097dd80e679836905bcd8ad7a13 (diff)
downloadopenvpn-20b18fd799e2ea9d0651f3ef913dd9ce2e481471.tar.gz
openvpn-20b18fd799e2ea9d0651f3ef913dd9ce2e481471.tar.xz
openvpn-20b18fd799e2ea9d0651f3ef913dd9ce2e481471.zip
Merge branch 'svn-branch-2.1' into merge
Pulling in changes from James' 2.1/openvpn branch in SVN. Conflicts: buffer.c init.c manage.h multi.c openvpn.8 options.c ssl.c version.m4 win/sign.py Signed-off-by: David Sommerseth <dazo@users.sourceforge.net>
-rw-r--r--Makefile.am2
-rw-r--r--buffer.c10
-rw-r--r--buffer.h2
-rw-r--r--clinat.c263
-rw-r--r--clinat.h65
-rw-r--r--common.h5
-rw-r--r--configure.ac15
-rw-r--r--errlevel.h20
-rw-r--r--error.c29
-rw-r--r--event.c4
-rw-r--r--forward.c32
-rw-r--r--forward.h1
-rw-r--r--init.c70
-rw-r--r--lzo.c32
-rw-r--r--lzo.h29
-rw-r--r--manage.c75
-rw-r--r--manage.h14
-rw-r--r--misc.c44
-rw-r--r--misc.h4
-rw-r--r--multi.c10
-rw-r--r--openvpn.8106
-rw-r--r--openvpn.h5
-rw-r--r--options.c143
-rw-r--r--options.h23
-rw-r--r--packet_id.c150
-rw-r--r--packet_id.h7
-rw-r--r--proto.h37
-rw-r--r--ps.c116
-rw-r--r--ps.h4
-rw-r--r--push.c97
-rw-r--r--push.h5
-rw-r--r--route.c163
-rwxr-xr-xservice-win32/openvpnserv.c6
-rw-r--r--socket.c34
-rw-r--r--ssl.c350
-rw-r--r--ssl.h25
-rw-r--r--syshead.h24
-rw-r--r--tun.c2
-rw-r--r--win/sign.py12
39 files changed, 1702 insertions, 333 deletions
diff --git a/Makefile.am b/Makefile.am
index f311837..32b40bb 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -82,7 +82,9 @@ openvpn_SOURCES = \
basic.h \
buffer.c buffer.h \
circ_list.h \
+ clinat.c clinat.h \
common.h \
+ config-win32.h \
crypto.c crypto.h \
dhcp.c dhcp.h \
errlevel.h \
diff --git a/buffer.c b/buffer.c
index 51cf7c1..cff2616 100644
--- a/buffer.c
+++ b/buffer.c
@@ -235,22 +235,24 @@ buf_puts(struct buffer *buf, const char *str)
* This is necessary due to certain buggy implementations of snprintf,
* that don't guarantee null termination for size > 0.
*
+ * Return false on overflow.
+ *
* This function is duplicated into service-win32/openvpnserv.c
* Any modifications here should be done to the other place as well.
*/
-int openvpn_snprintf(char *str, size_t size, const char *format, ...)
+bool openvpn_snprintf(char *str, size_t size, const char *format, ...)
{
va_list arglist;
- int ret = 0;
+ int len = -1;
if (size > 0)
{
va_start (arglist, format);
- ret = vsnprintf (str, size, format, arglist);
+ len = vsnprintf (str, size, format, arglist);
va_end (arglist);
str[size - 1] = 0;
}
- return ret;
+ return (len >= 0 && len < size);
}
/*
diff --git a/buffer.h b/buffer.h
index 0a394a7..1fe3c08 100644
--- a/buffer.h
+++ b/buffer.h
@@ -284,7 +284,7 @@ bool buf_puts (struct buffer *buf, const char *str);
/*
* Like snprintf but guarantees null termination for size > 0
*/
-int openvpn_snprintf(char *str, size_t size, const char *format, ...)
+bool openvpn_snprintf(char *str, size_t size, const char *format, ...)
#ifdef __GNUC__
__attribute__ ((format (printf, 3, 4)))
#endif
diff --git a/clinat.c b/clinat.c
new file mode 100644
index 0000000..8e85e22
--- /dev/null
+++ b/clinat.c
@@ -0,0 +1,263 @@
+/*
+ * 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-2010 OpenVPN Technologies, Inc. <sales@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
+ */
+
+#include "syshead.h"
+
+#if defined(ENABLE_CLIENT_NAT)
+
+#include "clinat.h"
+#include "proto.h"
+#include "socket.h"
+#include "memdbg.h"
+
+static bool
+add_entry(struct client_nat_option_list *dest,
+ const struct client_nat_entry *e)
+{
+ if (dest->n >= MAX_CLIENT_NAT)
+ {
+ msg (M_WARN, "WARNING: client-nat table overflow (max %d entries)", MAX_CLIENT_NAT);
+ return false;
+ }
+ else
+ {
+ dest->entries[dest->n++] = *e;
+ return true;
+ }
+}
+
+void
+print_client_nat_list(const struct client_nat_option_list *list, int msglevel)
+{
+ struct gc_arena gc = gc_new ();
+ int i;
+
+ msg (msglevel, "*** CNAT list");
+ if (list)
+ {
+ for (i = 0; i < list->n; ++i)
+ {
+ const struct client_nat_entry *e = &list->entries[i];
+ msg (msglevel, " CNAT[%d] t=%d %s/%s/%s",
+ i,
+ e->type,
+ print_in_addr_t (e->network, IA_NET_ORDER, &gc),
+ print_in_addr_t (e->netmask, IA_NET_ORDER, &gc),
+ print_in_addr_t (e->foreign_network, IA_NET_ORDER, &gc));
+ }
+ }
+ gc_free (&gc);
+}
+
+struct client_nat_option_list *
+new_client_nat_list (struct gc_arena *gc)
+{
+ struct client_nat_option_list *ret;
+ ALLOC_OBJ_CLEAR_GC (ret, struct client_nat_option_list, gc);
+ return ret;
+}
+
+struct client_nat_option_list *
+clone_client_nat_option_list (const struct client_nat_option_list *src, struct gc_arena *gc)
+{
+ struct client_nat_option_list *ret;
+ ALLOC_OBJ_GC (ret, struct client_nat_option_list, gc);
+ *ret = *src;
+ return ret;
+}
+
+void
+copy_client_nat_option_list (struct client_nat_option_list *dest,
+ const struct client_nat_option_list *src)
+{
+ int i;
+ for (i = 0; i < src->n; ++i)
+ {
+ if (!add_entry(dest, &src->entries[i]))
+ break;
+ }
+}
+
+void
+add_client_nat_to_option_list (struct client_nat_option_list *dest,
+ const char *type,
+ const char *network,
+ const char *netmask,
+ const char *foreign_network,
+ int msglevel)
+{
+ struct client_nat_entry e;
+ bool ok;
+
+ if (!strcmp(type, "snat"))
+ e.type = CN_SNAT;
+ else if (!strcmp(type, "dnat"))
+ e.type = CN_DNAT;
+ else
+ {
+ msg(msglevel, "client-nat: type must be 'snat' or 'dnat'");
+ return;
+ }
+
+ e.network = getaddr(0, network, 0, &ok, NULL);
+ if (!ok)
+ {
+ msg(msglevel, "client-nat: bad network: %s", network);
+ return;
+ }
+ e.netmask = getaddr(0, netmask, 0, &ok, NULL);
+ if (!ok)
+ {
+ msg(msglevel, "client-nat: bad netmask: %s", netmask);
+ return;
+ }
+ e.foreign_network = getaddr(0, foreign_network, 0, &ok, NULL);
+ if (!ok)
+ {
+ msg(msglevel, "client-nat: bad foreign network: %s", foreign_network);
+ return;
+ }
+
+ add_entry(dest, &e);
+}
+
+#if 0
+static void
+print_checksum (struct openvpn_iphdr *iph, const char *prefix)
+{
+ uint16_t *sptr;
+ unsigned int sum = 0;
+ int i = 0;
+ for (sptr = (uint16_t *)iph; (uint8_t *)sptr < (uint8_t *)iph + sizeof(struct openvpn_iphdr); sptr++)
+ {
+ i += 1;
+ sum += *sptr;
+ }
+ msg (M_INFO, "** CKSUM[%d] %s %08x", i, prefix, sum);
+}
+#endif
+
+static void
+print_pkt (struct openvpn_iphdr *iph, const char *prefix, const int direction, const int msglevel)
+{
+ struct gc_arena gc = gc_new ();
+
+ char *dirstr = "???";
+ if (direction == CN_OUTGOING)
+ dirstr = "OUT";
+ else if (direction == CN_INCOMING)
+ dirstr = "IN";
+
+ msg(msglevel, "** CNAT %s %s %s -> %s",
+ dirstr,
+ prefix,
+ print_in_addr_t (iph->saddr, IA_NET_ORDER, &gc),
+ print_in_addr_t (iph->daddr, IA_NET_ORDER, &gc));
+
+ gc_free (&gc);
+}
+
+void
+client_nat_transform (const struct client_nat_option_list *list,
+ struct buffer *ipbuf,
+ const int direction)
+{
+ struct ip_tcp_udp_hdr *h = (struct ip_tcp_udp_hdr *) BPTR (ipbuf);
+ int i;
+ uint32_t addr, *addr_ptr;
+ const uint32_t *from, *to;
+ int accumulate = 0;
+ unsigned int amask;
+ unsigned int alog = 0;
+
+ if (check_debug_level (D_CLIENT_NAT))
+ print_pkt (&h->ip, "BEFORE", direction, D_CLIENT_NAT);
+
+ for (i = 0; i < list->n; ++i)
+ {
+ const struct client_nat_entry *e = &list->entries[i]; /* current NAT rule */
+ if (e->type ^ direction)
+ {
+ addr = *(addr_ptr = &h->ip.daddr);
+ amask = 2;
+ }
+ else
+ {
+ addr = *(addr_ptr = &h->ip.saddr);
+ amask = 1;
+ }
+ if (direction)
+ {
+ from = &e->foreign_network;
+ to = &e->network;
+ }
+ else
+ {
+ from = &e->network;
+ to = &e->foreign_network;
+ }
+
+ if (((addr & e->netmask) == *from) && !(amask & alog))
+ {
+ /* pre-adjust IP checksum */
+ ADD_CHECKSUM_32(accumulate, addr);
+
+ /* do NAT transform */
+ addr = (addr & ~e->netmask) | *to;
+
+ /* post-adjust IP checksum */
+ SUB_CHECKSUM_32(accumulate, addr);
+
+ /* write the modified address to packet */
+ *addr_ptr = addr;
+
+ /* mark as modified */
+ alog |= amask;
+ }
+ }
+ if (alog)
+ {
+ if (check_debug_level (D_CLIENT_NAT))
+ print_pkt (&h->ip, "AFTER", direction, D_CLIENT_NAT);
+
+ ADJUST_CHECKSUM(accumulate, h->ip.check);
+
+ if (h->ip.protocol == OPENVPN_IPPROTO_TCP)
+ {
+ if (BLEN(ipbuf) >= sizeof(struct openvpn_iphdr) + sizeof(struct openvpn_tcphdr))
+ {
+ ADJUST_CHECKSUM(accumulate, h->u.tcp.check);
+ }
+ }
+ else if (h->ip.protocol == OPENVPN_IPPROTO_UDP)
+ {
+ if (BLEN(ipbuf) >= sizeof(struct openvpn_iphdr) + sizeof(struct openvpn_udphdr))
+ {
+ ADJUST_CHECKSUM(accumulate, h->u.udp.check);
+ }
+ }
+ }
+}
+
+#endif
diff --git a/clinat.h b/clinat.h
new file mode 100644
index 0000000..d55a727
--- /dev/null
+++ b/clinat.h
@@ -0,0 +1,65 @@
+/*
+ * 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-2010 OpenVPN Technologies, Inc. <sales@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
+ */
+
+#if !defined(CLINAT_H) && defined(ENABLE_CLIENT_NAT)
+#define CLINAT_H
+
+#include "buffer.h"
+
+#define MAX_CLIENT_NAT 64
+
+#define CN_OUTGOING 0
+#define CN_INCOMING 1
+
+struct client_nat_entry {
+# define CN_SNAT 0
+# define CN_DNAT 1
+ int type;
+ in_addr_t network;
+ in_addr_t netmask;
+ in_addr_t foreign_network;
+};
+
+struct client_nat_option_list {
+ int n;
+ struct client_nat_entry entries[MAX_CLIENT_NAT];
+};
+
+struct client_nat_option_list *new_client_nat_list (struct gc_arena *gc);
+struct client_nat_option_list *clone_client_nat_option_list (const struct client_nat_option_list *src, struct gc_arena *gc);
+void copy_client_nat_option_list (struct client_nat_option_list *dest, const struct client_nat_option_list *src);
+void print_client_nat_list(const struct client_nat_option_list *list, int msglevel);
+
+void add_client_nat_to_option_list (struct client_nat_option_list *dest,
+ const char *type,
+ const char *network,
+ const char *netmask,
+ const char *foreign_network,
+ int msglevel);
+
+void client_nat_transform (const struct client_nat_option_list *list,
+ struct buffer *ipbuf,
+ const int direction);
+
+#endif
diff --git a/common.h b/common.h
index ff3a0d5..de2d609 100644
--- a/common.h
+++ b/common.h
@@ -87,6 +87,11 @@ typedef unsigned long ptr_type;
#define PUSH_BUNDLE_SIZE 1024
/*
+ * In how many seconds does client re-send PUSH_REQUEST if we haven't yet received a reply
+ */
+#define PUSH_REQUEST_INTERVAL 5
+
+/*
* A sort of pseudo-filename for data provided inline within
* the configuration file.
*/
diff --git a/configure.ac b/configure.ac
index c3bd8e8..a3789d9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -68,6 +68,12 @@ AC_ARG_ENABLE(lzo,
[LZO="yes"]
)
+AC_ARG_ENABLE(lzo-stub,
+ [ --enable-lzo-stub Don't compile LZO compression support but still allow limited interoperability with LZO-enabled peers],
+ [LZO_STUB="$enableval"],
+ [LZO_STUB="no"]
+)
+
AC_ARG_ENABLE(crypto,
[ --disable-crypto Disable OpenSSL crypto support],
[CRYPTO="$enableval"],
@@ -662,7 +668,7 @@ dnl
dnl check for LZO library
dnl
-if test "$LZO" = "yes"; then
+if test "$LZO" = "yes" && test "$LZO_STUB" = "no"; then
LZO_H=""
AC_CHECKING([for LZO Library and Header files])
AC_CHECK_HEADER(lzo/lzo1x.h,
@@ -692,10 +698,15 @@ if test "$LZO" = "yes"; then
else
AC_MSG_RESULT([LZO headers were not found])
AC_MSG_RESULT([LZO library available from http://www.oberhumer.com/opensource/lzo/])
- AC_MSG_ERROR([Or try ./configure --disable-lzo])
+ AC_MSG_ERROR([Or try ./configure --disable-lzo OR ./configure --enable-lzo-stub])
fi
fi
+dnl enable multi-client mode
+if test "$LZO_STUB" = "yes"; then
+ AC_DEFINE(LZO_STUB, 1, [Enable LZO stub capability])
+fi
+
dnl
dnl check for OpenSSL-crypto library
dnl
diff --git a/errlevel.h b/errlevel.h
index 75c3194..be1ef5e 100644
--- a/errlevel.h
+++ b/errlevel.h
@@ -71,9 +71,7 @@
#define D_ALIGN_ERRORS LOGLEV(1, 14, M_NONFATAL) /* show bad struct alignments */
#define D_HANDSHAKE LOGLEV(2, 20, 0) /* show data & control channel handshakes */
-#define D_MTU_INFO LOGLEV(2, 21, 0) /* show terse MTU info */
#define D_CLOSE LOGLEV(2, 22, 0) /* show socket and TUN/TAP close */
-#define D_SHOW_OCC_HASH LOGLEV(2, 23, 0) /* show MD5 hash of option compatibility string */
#define D_PROXY LOGLEV(2, 24, 0) /* show http proxy control packets */
#define D_ARGV LOGLEV(2, 25, 0) /* show struct argv errors */
@@ -85,7 +83,7 @@
#define D_RESTART LOGLEV(3, 33, 0) /* show certain restart messages */
#define D_PUSH LOGLEV(3, 34, 0) /* show push/pull info */
#define D_IFCONFIG_POOL LOGLEV(3, 35, 0) /* show ifconfig pool info */
-#define D_BACKTRACK LOGLEV(3, 36, 0) /* show replay backtracks */
+#define D_PID_DEBUG_LOW LOGLEV(3, 36, 0) /* show low-freq packet-id debugging info */
#define D_AUTH LOGLEV(3, 37, 0) /* show user/pass auth info */
#define D_MULTI_LOW LOGLEV(3, 38, 0) /* show point-to-multipoint low-freq debug info */
#define D_PLUGIN LOGLEV(3, 39, 0) /* show plugin calls */
@@ -104,12 +102,19 @@
#define D_PACKET_TRUNC_ERR LOGLEV(4, 55, 0) /* PACKET_TRUNCATION_CHECK */
#define D_PF_DROPPED LOGLEV(4, 56, 0) /* packet filter dropped a packet */
#define D_MULTI_DROPPED LOGLEV(4, 57, 0) /* show point-to-multipoint packet drops */
+#define D_MULTI_MEDIUM LOGLEV(4, 58, 0) /* show medium frequency multi messages */
+#define D_X509_ATTR LOGLEV(4, 59, 0) /* show x509-track attributes on connection */
+#define D_INIT_MEDIUM LOGLEV(4, 60, 0) /* show medium frequency init messages */
+#define D_MTU_INFO LOGLEV(4, 61, 0) /* show terse MTU info */
+#define D_SHOW_OCC_HASH LOGLEV(4, 62, 0) /* show MD5 hash of option compatibility string */
+#define D_PID_DEBUG_MEDIUM LOGLEV(4, 63, 0) /* show medium-freq packet-id debugging info */
#define D_LOG_RW LOGLEV(5, 0, 0) /* Print 'R' or 'W' to stdout for read/write */
-#define D_LINK_RW LOGLEV(6, 60, M_DEBUG) /* show TCP/UDP reads/writes (terse) */
-#define D_TUN_RW LOGLEV(6, 60, M_DEBUG) /* show TUN/TAP reads/writes */
-#define D_TAP_WIN32_DEBUG LOGLEV(6, 60, M_DEBUG) /* show TAP-Win32 driver debug info */
+#define D_LINK_RW LOGLEV(6, 69, M_DEBUG) /* show TCP/UDP reads/writes (terse) */
+#define D_TUN_RW LOGLEV(6, 69, M_DEBUG) /* show TUN/TAP reads/writes */
+#define D_TAP_WIN32_DEBUG LOGLEV(6, 69, M_DEBUG) /* show TAP-Win32 driver debug info */
+#define D_CLIENT_NAT LOGLEV(6, 69, M_DEBUG) /* show client NAT debug info */
#define D_SHOW_KEYS LOGLEV(7, 70, M_DEBUG) /* show data channel encryption keys */
#define D_SHOW_KEY_SOURCE LOGLEV(7, 70, M_DEBUG) /* show data channel key source entropy */
@@ -117,7 +122,6 @@
#define D_FRAG_DEBUG LOGLEV(7, 70, M_DEBUG) /* show fragment debugging info */
#define D_WIN32_IO_LOW LOGLEV(7, 70, M_DEBUG) /* low freq win32 I/O debugging info */
#define D_MTU_DEBUG LOGLEV(7, 70, M_DEBUG) /* show MTU debugging info */
-#define D_PID_DEBUG_LOW LOGLEV(7, 70, M_DEBUG) /* show low-freq packet-id debugging info */
#define D_MULTI_DEBUG LOGLEV(7, 70, M_DEBUG) /* show medium-freq multi debugging info */
#define D_MSS LOGLEV(7, 70, M_DEBUG) /* show MSS adjustments */
#define D_COMP_LOW LOGLEV(7, 70, M_DEBUG) /* show adaptive compression state changes */
@@ -141,6 +145,7 @@
#define D_TLS_KEYSELECT LOGLEV(7, 70, M_DEBUG) /* show information on key selection for data channel */
#define D_ARGV_PARSE_CMD LOGLEV(7, 70, M_DEBUG) /* show parse_line() errors in argv_printf %sc */
#define D_CRYPTO_DEBUG LOGLEV(7, 70, M_DEBUG) /* show detailed info from crypto.c routines */
+#define D_PID_DEBUG LOGLEV(7, 70, M_DEBUG) /* show packet-id debugging info */
#define D_PF_DROPPED_BCAST LOGLEV(7, 71, M_DEBUG) /* packet filter dropped a broadcast packet */
#define D_PF_DEBUG LOGLEV(7, 72, M_DEBUG) /* packet filter debugging, must also define PF_DEBUG in pf.h */
@@ -158,7 +163,6 @@
#define D_READ_WRITE LOGLEV(9, 70, M_DEBUG) /* show all tun/tcp/udp reads/writes/opens */
#define D_PACKET_CONTENT LOGLEV(9, 70, M_DEBUG) /* show before/after encryption packet content */
#define D_TLS_NO_SEND_KEY LOGLEV(9, 70, M_DEBUG) /* show when no data channel send-key exists */
-#define D_PID_DEBUG LOGLEV(9, 70, M_DEBUG) /* show packet-id debugging info */
#define D_PID_PERSIST_DEBUG LOGLEV(9, 70, M_DEBUG) /* show packet-id persist debugging info */
#define D_LINK_RW_VERBOSE LOGLEV(9, 70, M_DEBUG) /* show link reads/writes with greater verbosity */
#define D_STREAM_DEBUG LOGLEV(9, 70, M_DEBUG) /* show TCP stream debug info */
diff --git a/error.c b/error.c
index 9754464..2310f96 100644
--- a/error.c
+++ b/error.c
@@ -340,7 +340,7 @@ void x_msg (const unsigned int flags, const char *format, ...)
}
if (flags & M_FATAL)
- msg (M_INFO, "Exiting");
+ msg (M_INFO, "Exiting due to fatal error");
if (flags & M_FATAL)
openvpn_exit (OPENVPN_EXIT_STATUS_ERROR); /* exit point */
@@ -651,35 +651,38 @@ const struct virtual_output *x_msg_virtual_output; /* GLOBAL */
void
openvpn_exit (const int status)
{
- void tun_abort();
+ if (!forked)
+ {
+ void tun_abort();
#ifdef ENABLE_PLUGIN
- void plugin_abort (void);
+ void plugin_abort (void);
#endif
- tun_abort();
+ tun_abort();
#ifdef WIN32
- uninit_win32 ();
+ uninit_win32 ();
#endif
- close_syslog ();
+ close_syslog ();
#ifdef ENABLE_PLUGIN
- plugin_abort ();
+ plugin_abort ();
#endif
#if PORT_SHARE
- if (port_share)
- port_share_abort (port_share);
+ if (port_share)
+ port_share_abort (port_share);
#endif
#ifdef ABORT_ON_ERROR
- if (status == OPENVPN_EXIT_STATUS_ERROR)
- abort ();
+ if (status == OPENVPN_EXIT_STATUS_ERROR)
+ abort ();
#endif
- if (status == OPENVPN_EXIT_STATUS_GOOD)
- perf_output_results ();
+ if (status == OPENVPN_EXIT_STATUS_GOOD)
+ perf_output_results ();
+ }
exit (status);
}
diff --git a/event.c b/event.c
index 6a9161b..51b17b3 100644
--- a/event.c
+++ b/event.c
@@ -522,10 +522,10 @@ ep_ctl (struct event_set *es, event_t event, unsigned int rwflags, void *arg)
if (errno == ENOENT)
{
if (epoll_ctl (eps->epfd, EPOLL_CTL_ADD, event, &ev) < 0)
- msg (M_ERR, "EVENT: epoll_ctl EPOLL_CTL_ADD failed");
+ msg (M_ERR, "EVENT: epoll_ctl EPOLL_CTL_ADD failed, sd=%d", (int)event);
}
else
- msg (M_ERR, "EVENT: epoll_ctl EPOLL_CTL_MOD failed");
+ msg (M_ERR, "EVENT: epoll_ctl EPOLL_CTL_MOD failed, sd=%d", (int)event);
}
}
diff --git a/forward.c b/forward.c
index 7fede27..b43c1c0 100644
--- a/forward.c
+++ b/forward.c
@@ -155,7 +155,9 @@ check_incoming_control_channel_dowork (struct context *c)
else if (buf_string_match_head_str (&buf, "PUSH_"))
incoming_push_message (c, &buf);
else if (buf_string_match_head_str (&buf, "RESTART"))
- server_pushed_restart (c, &buf);
+ server_pushed_signal (c, &buf, true, 7);
+ else if (buf_string_match_head_str (&buf, "HALT"))
+ server_pushed_signal (c, &buf, false, 4);
else
msg (D_PUSH_ERRORS, "WARNING: Received unknown control message: %s", BSTR (&buf));
}
@@ -176,8 +178,8 @@ check_push_request_dowork (struct context *c)
{
send_push_request (c);
- /* if no response to first push_request, retry at 5 second intervals */
- event_timeout_modify_wakeup (&c->c2.push_request_interval, 5);
+ /* if no response to first push_request, retry at PUSH_REQUEST_INTERVAL second intervals */
+ event_timeout_modify_wakeup (&c->c2.push_request_interval, PUSH_REQUEST_INTERVAL);
}
#endif /* P2MP */
@@ -230,22 +232,28 @@ bool
send_control_channel_string (struct context *c, const char *str, int msglevel)
{
#if defined(USE_CRYPTO) && defined(USE_SSL)
-
if (c->c2.tls_multi) {
+ struct gc_arena gc = gc_new ();
bool stat;
/* buffered cleartext write onto TLS control channel */
stat = tls_send_payload (c->c2.tls_multi, (uint8_t*) str, strlen (str) + 1);
- /* reschedule tls_multi_process */
+ /*
+ * Reschedule tls_multi_process.
+ * NOTE: in multi-client mode, usually the below two statements are
+ * insufficient to reschedule the client instance object unless
+ * multi_schedule_context_wakeup(m, mi) is also called.
+ */
interval_action (&c->c2.tmp_int);
context_immediate_reschedule (c); /* ZERO-TIMEOUT */
msg (msglevel, "SENT CONTROL [%s]: '%s' (status=%d)",
tls_common_name (c->c2.tls_multi, false),
- str,
+ sanitize_control_message (str, &gc),
(int) stat);
+ gc_free (&gc);
return stat;
}
#endif
@@ -968,7 +976,7 @@ process_incoming_tun (struct context *c)
* The --passtos and --mssfix options require
* us to examine the IPv4 header.
*/
- process_ipv4_header (c, PIPV4_PASSTOS|PIPV4_MSSFIX, &c->c2.buf);
+ process_ipv4_header (c, PIPV4_PASSTOS|PIPV4_MSSFIX|PIPV4_CLIENT_NAT, &c->c2.buf);
#ifdef PACKET_TRUNCATION_CHECK
/* if (c->c2.buf.len > 1) --c->c2.buf.len; */
@@ -1026,6 +1034,14 @@ process_ipv4_header (struct context *c, unsigned int flags, struct buffer *buf)
if (flags & PIPV4_MSSFIX)
mss_fixup (&ipbuf, MTU_TO_MSS (TUN_MTU_SIZE_DYNAMIC (&c->c2.frame)));
+#ifdef ENABLE_CLIENT_NAT
+ /* possibly do NAT on packet */
+ if ((flags & PIPV4_CLIENT_NAT) && c->options.client_nat)
+ {
+ const int direction = (flags & PIPV4_OUTGOING) ? CN_INCOMING : CN_OUTGOING;
+ client_nat_transform (c->options.client_nat, &ipbuf, direction);
+ }
+#endif
/* possibly extract a DHCP router message */
if (flags & PIPV4_EXTRACT_DHCP_ROUTER)
{
@@ -1188,7 +1204,7 @@ process_outgoing_tun (struct context *c)
* The --mssfix option requires
* us to examine the IPv4 header.
*/
- process_ipv4_header (c, PIPV4_MSSFIX|PIPV4_EXTRACT_DHCP_ROUTER|PIPV4_OUTGOING, &c->c2.to_tun);
+ process_ipv4_header (c, PIPV4_MSSFIX|PIPV4_EXTRACT_DHCP_ROUTER|PIPV4_CLIENT_NAT|PIPV4_OUTGOING, &c->c2.to_tun);
if (c->c2.to_tun.len <= MAX_RW_SIZE_TUN (&c->c2.frame))
{
diff --git a/forward.h b/forward.h
index 17cc928..76d8b9e 100644
--- a/forward.h
+++ b/forward.h
@@ -76,6 +76,7 @@ bool send_control_channel_string (struct context *c, const char *str, int msglev
#define PIPV4_MSSFIX (1<<1)
#define PIPV4_OUTGOING (1<<2)
#define PIPV4_EXTRACT_DHCP_ROUTER (1<<3)
+#define PIPV4_CLIENT_NAT (1<<4)
void process_ipv4_header (struct context *c, unsigned int flags, struct buffer *buf);
diff --git a/init.c b/init.c
index 6a9f1b7..4a16fba 100644
--- a/init.c
+++ b/init.c
@@ -102,13 +102,6 @@ update_options_ce_post (struct options *options)
options->ping_rec_timeout_action = PING_RESTART;
}
#endif
-#ifdef USE_CRYPTO
- /*
- * Don't use replay window for TCP mode (i.e. require that packets be strictly in sequence).
- */
- if (link_socket_proto_connection_oriented (options->ce.proto))
- options->replay_window = options->replay_time = 0;
-#endif
}
#if HTTP_PROXY_FALLBACK
@@ -521,7 +514,9 @@ init_port_share (struct context *c)
if (!port_share && (c->options.port_share_host && c->options.port_share_port))
{
port_share = port_share_open (c->options.port_share_host,
- c->options.port_share_port);
+ c->options.port_share_port,
+ MAX_RW_SIZE_LINK (&c->c2.frame),
+ c->options.port_share_journal_dir);
if (port_share == NULL)
msg (M_FATAL, "Fatal error: Port sharing failed");
}
@@ -601,6 +596,27 @@ init_static (void)
return false;
#endif
+#ifdef TEST_GET_DEFAULT_GATEWAY
+ {
+ struct gc_arena gc = gc_new ();
+ in_addr_t addr;
+ char macaddr[6];
+
+ if (get_default_gateway(&addr, NULL))
+ msg (M_INFO, "GW %s", print_in_addr_t(addr, 0, &gc));
+ else
+ msg (M_INFO, "GDG ERROR");
+
+ if (get_default_gateway_mac_addr(macaddr))
+ msg (M_INFO, "MAC %s", format_hex_ex (macaddr, 6, 0, 1, ":", &gc));
+ else
+ msg (M_INFO, "GDGMA ERROR");
+
+ gc_free (&gc);
+ return false;
+ }
+#endif
+
#ifdef GEN_PATH_TEST
{
struct gc_arena gc = gc_new ();
@@ -1223,7 +1239,14 @@ do_route (const struct options *options,
struct env_set *es)
{
if (!options->route_noexec && ( route_list || route_ipv6_list ) )
- add_routes (route_list, route_ipv6_list, tt, ROUTE_OPTION_FLAGS (options), es);
+ {
+ add_routes (route_list, route_ipv6_list, tt, ROUTE_OPTION_FLAGS (options), es);
+ setenv_int (es, "redirect_gateway", route_list->did_redirect_default_gateway);
+ }
+#ifdef ENABLE_MANAGEMENT
+ if (management)
+ management_up_down (management, "UP", es);
+#endif
if (plugin_defined (plugins, OPENVPN_PLUGIN_ROUTE_UP))
{
@@ -1441,7 +1464,10 @@ do_close_tun (struct context *c, bool force)
#ifdef ENABLE_MANAGEMENT
/* tell management layer we are about to close the TUN/TAP device */
if (management)
- management_pre_tunnel_close (management);
+ {
+ management_pre_tunnel_close (management);
+ management_up_down (management, "DOWN", c->c2.es);
+ }
#endif
/* delete any routes we added */
@@ -1586,7 +1612,6 @@ pull_permission_mask (const struct context *c)
unsigned int flags =
OPT_P_UP
| OPT_P_ROUTE_EXTRAS
- | OPT_P_IPWIN32
| OPT_P_SOCKBUF
| OPT_P_SOCKFLAGS
| OPT_P_SETENV
@@ -1600,7 +1625,7 @@ pull_permission_mask (const struct context *c)
| OPT_P_PULL_MODE;
if (!c->options.route_nopull)
- flags |= OPT_P_ROUTE;
+ flags |= (OPT_P_ROUTE | OPT_P_IPWIN32);
return flags;
}
@@ -1749,9 +1774,11 @@ socket_restart_pause (struct context *c)
if (auth_retry_get () == AR_NOINTERACT)
sec = 10;
+#if 0 /* not really needed because of c->persist.restart_sleep_seconds */
if (c->options.server_poll_timeout && sec > 1)
sec = 1;
#endif
+#endif
if (c->persist.restart_sleep_seconds > 0 && c->persist.restart_sleep_seconds > sec)
sec = c->persist.restart_sleep_seconds;
@@ -1868,8 +1895,11 @@ do_init_crypto_static (struct context *c, const unsigned int flags)
/* Initialize packet ID tracking */
if (options->replay)
{
- packet_id_init (&c->c2.packet_id, options->replay_window,
- options->replay_time);
+ packet_id_init (&c->c2.packet_id,
+ link_socket_proto_connection_oriented (options->ce.proto),
+ options->replay_window,
+ options->replay_time,
+ "STATIC", 0);
c->c2.crypto_options.packet_id = &c->c2.packet_id;
c->c2.crypto_options.pid_persist = &c->c1.pid_persist;
c->c2.crypto_options.flags |= CO_PACKET_ID_LONG_FORM;
@@ -1965,7 +1995,7 @@ do_init_crypto_tls_c1 (struct context *c)
msg (M_FATAL, "Error: private key password verification failed");
break;
case AR_INTERACT:
- ssl_purge_auth ();
+ ssl_purge_auth (false);
case AR_NOINTERACT:
c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- Password failure error */
break;
@@ -2017,7 +2047,7 @@ do_init_crypto_tls_c1 (struct context *c)
}
else
{
- msg (M_INFO, "Re-using SSL/TLS context");
+ msg (D_INIT_MEDIUM, "Re-using SSL/TLS context");
}
}
@@ -2070,6 +2100,7 @@ do_init_crypto_tls (struct context *c, const unsigned int flags)
to.replay = options->replay;
to.replay_window = options->replay_window;
to.replay_time = options->replay_time;
+ to.tcp_mode = link_socket_proto_connection_oriented (options->ce.proto);
to.transition_window = options->transition_window;
to.handshake_window = options->handshake_window;
to.packet_timeout = options->tls_timeout;
@@ -2094,9 +2125,11 @@ do_init_crypto_tls (struct context *c, const unsigned int flags)
to.verify_export_cert = options->tls_export_cert;
to.verify_x509name = options->tls_remote;
to.crl_file = options->crl_file;
+ to.ssl_flags = options->ssl_flags;
to.ns_cert_type = options->ns_cert_type;
memmove (to.remote_cert_ku, options->remote_cert_ku, sizeof (to.remote_cert_ku));
to.remote_cert_eku = options->remote_cert_eku;
+ to.verify_hash = options->verify_hash;
to.es = c->c2.es;
#ifdef ENABLE_DEBUG
@@ -2113,11 +2146,14 @@ do_init_crypto_tls (struct context *c, const unsigned int flags)
to.auth_user_pass_verify_script = options->auth_user_pass_verify_script;
to.auth_user_pass_verify_script_via_file = options->auth_user_pass_verify_script_via_file;
to.tmp_dir = options->tmp_dir;
- to.ssl_flags = options->ssl_flags;
if (options->ccd_exclusive)
to.client_config_dir_exclusive = options->client_config_dir;
#endif
+#ifdef ENABLE_X509_TRACK
+ to.x509_track = options->x509_track;
+#endif
+
/* TLS handshake authentication (--tls-auth) */
if (options->tls_auth_file)
{
diff --git a/lzo.c b/lzo.c
index 6ea0d03..3d6aa5a 100644
--- a/lzo.c
+++ b/lzo.c
@@ -32,6 +32,8 @@
#include "memdbg.h"
+#ifndef LZO_STUB
+
static bool
lzo_adaptive_compress_test (struct lzo_adaptive_compress *ac)
{
@@ -79,6 +81,8 @@ lzo_adaptive_compress_data (struct lzo_adaptive_compress *ac, int n_total, int n
ac->n_comp += n_comp;
}
+#endif /* LZO_STUB */
+
void lzo_adjust_frame_parameters (struct frame *frame)
{
/* Leave room for our one-byte compressed/didn't-compress prefix byte. */
@@ -94,14 +98,18 @@ lzo_compress_init (struct lzo_compress_workspace *lzowork, unsigned int flags)
{
CLEAR (*lzowork);
- lzowork->wmem_size = LZO_WORKSPACE;
lzowork->flags = flags;
+#ifndef LZO_STUB
+ lzowork->wmem_size = LZO_WORKSPACE;
if (lzo_init () != LZO_E_OK)
msg (M_FATAL, "Cannot initialize LZO compression library");
lzowork->wmem = (lzo_voidp) lzo_malloc (lzowork->wmem_size);
check_malloc_return (lzowork->wmem);
- msg (M_INFO, "LZO compression initialized");
+ msg (D_INIT_MEDIUM, "LZO compression initialized");
+#else
+ msg (D_INIT_MEDIUM, "LZO stub compression initialized");
+#endif
lzowork->defined = true;
}
@@ -111,8 +119,10 @@ lzo_compress_uninit (struct lzo_compress_workspace *lzowork)
if (lzowork)
{
ASSERT (lzowork->defined);
+#ifndef LZO_STUB
lzo_free (lzowork->wmem);
lzowork->wmem = NULL;
+#endif
lzowork->defined = false;
}
}
@@ -120,6 +130,7 @@ lzo_compress_uninit (struct lzo_compress_workspace *lzowork)
static inline bool
lzo_compression_enabled (struct lzo_compress_workspace *lzowork)
{
+#ifndef LZO_STUB
if ((lzowork->flags & (LZO_SELECTED|LZO_ON)) == (LZO_SELECTED|LZO_ON))
{
if (lzowork->flags & LZO_ADAPTIVE)
@@ -127,6 +138,7 @@ lzo_compression_enabled (struct lzo_compress_workspace *lzowork)
else
return true;
}
+#endif
return false;
}
@@ -139,15 +151,18 @@ lzo_compress (struct buffer *buf, struct buffer work,
struct lzo_compress_workspace *lzowork,
const struct frame* frame)
{
+#ifndef LZO_STUB
lzo_uint zlen = 0;
int err;
bool compressed = false;
+#endif
ASSERT (lzowork->defined);
if (buf->len <= 0)
return;
+#ifndef LZO_STUB
/*
* In order to attempt compression, length must be at least COMPRESS_THRESHOLD,
* and our adaptive level must give the OK.
@@ -193,6 +208,7 @@ lzo_compress (struct buffer *buf, struct buffer work,
*buf = work;
}
else
+#endif
{
uint8_t *header = buf_prepend (buf, 1);
*header = NO_COMPRESS;
@@ -204,9 +220,11 @@ lzo_decompress (struct buffer *buf, struct buffer work,
struct lzo_compress_workspace *lzowork,
const struct frame* frame)
{
+#ifndef LZO_STUB
lzo_uint zlen = EXPANDED_SIZE (frame);
- uint8_t c; /* flag indicating whether or not our peer compressed */
int err;
+#endif
+ uint8_t c; /* flag indicating whether or not our peer compressed */
ASSERT (lzowork->defined);
@@ -220,6 +238,7 @@ lzo_decompress (struct buffer *buf, struct buffer work,
if (c == YES_COMPRESS) /* packet was compressed */
{
+#ifndef LZO_STUB
ASSERT (buf_safe (&work, zlen));
err = LZO_DECOMPRESS (BPTR (buf), BLEN (buf), BPTR (&work), &zlen,
lzowork->wmem);
@@ -238,6 +257,11 @@ lzo_decompress (struct buffer *buf, struct buffer work,
lzowork->post_decompress += work.len;
*buf = work;
+#else
+ dmsg (D_COMP_ERRORS, "LZO decompression error: LZO capability not compiled");
+ buf->len = 0;
+ return;
+#endif
}
else if (c == NO_COMPRESS) /* packet was not compressed */
{
@@ -264,10 +288,12 @@ void lzo_print_stats (const struct lzo_compress_workspace *lzo_compwork, struct
{
ASSERT (lzo_compwork->defined);
+#ifndef LZO_STUB
status_printf (so, "pre-compress bytes," counter_format, lzo_compwork->pre_compress);
status_printf (so, "post-compress bytes," counter_format, lzo_compwork->post_compress);
status_printf (so, "pre-decompress bytes," counter_format, lzo_compwork->pre_decompress);
status_printf (so, "post-decompress bytes," counter_format, lzo_compwork->post_decompress);
+#endif
}
#else
diff --git a/lzo.h b/lzo.h
index bb15753..831f204 100644
--- a/lzo.h
+++ b/lzo.h
@@ -27,6 +27,7 @@
#ifdef USE_LZO
+#ifndef LZO_STUB
#ifdef LZO_HEADER_DIR
#include "lzo/lzoutil.h"
#include "lzo/lzo1x.h"
@@ -34,6 +35,7 @@
#include "lzoutil.h"
#include "lzo1x.h"
#endif
+#endif
#include "buffer.h"
#include "mtu.h"
@@ -46,6 +48,18 @@
#define LZO_ADAPTIVE (1<<2)
/*
+ * Length of prepended prefix on LZO packets
+ */
+#define LZO_PREFIX_LEN 1
+
+/*
+ * LZO 2.0 worst case size expansion
+ */
+#define LZO_EXTRA_BUFFER(len) ((len)/8 + 128 + 3)
+
+#ifndef LZO_STUB
+
+/*
* Use LZO compress routine lzo1x_1_15_compress which is described
* as faster but needs a bit more memory than the standard routine.
* Use safe decompress (i.e. check for buffer overflows).
@@ -58,19 +72,12 @@
#define LZO_WORKSPACE LZO1X_1_15_MEM_COMPRESS
#define LZO_DECOMPRESS lzo1x_decompress_safe
-#define LZO_EXTRA_BUFFER(len) ((len)/8 + 128 + 3) /* LZO 2.0 worst case size expansion. */
-
/*
* Don't try to compress any packet smaller than this.
*/
#define COMPRESS_THRESHOLD 100
/*
- * Length of prepended prefix on LZO packets
- */
-#define LZO_PREFIX_LEN 1
-
-/*
* Adaptive compress parameters
*/
#define AC_SAMP_SEC 2 /* number of seconds in sample period */
@@ -88,23 +95,27 @@ struct lzo_adaptive_compress {
int n_comp;
};
+#endif /* LZO_STUB */
+
/*
* Compress and Uncompress routines.
*/
struct lzo_compress_workspace
{
+ bool defined;
+ unsigned int flags;
+#ifndef LZO_STUB
lzo_voidp wmem;
int wmem_size;
struct lzo_adaptive_compress ac;
- unsigned int flags;
- bool defined;
/* statistics */
counter_type pre_decompress;
counter_type post_decompress;
counter_type pre_compress;
counter_type post_compress;
+#endif
};
void lzo_adjust_frame_parameters(struct frame *frame);
diff --git a/manage.c b/manage.c
index a4fd258..ab425e7 100644
--- a/manage.c
+++ b/manage.c
@@ -96,7 +96,7 @@ man_help ()
msg (M_CLIENT, "client-auth-nt CID KID : Authenticate client-id/key-id CID/KID");
msg (M_CLIENT, "client-deny CID KID R [CR] : Deny auth client-id/key-id CID/KID with log reason");
msg (M_CLIENT, " text R and optional client reason text CR");
- msg (M_CLIENT, "client-kill CID : Kill client instance CID");
+ msg (M_CLIENT, "client-kill CID [M] : Kill client instance CID with message M (def=RESTART)");
msg (M_CLIENT, "env-filter [level] : Set env-var filter level");
#ifdef MANAGEMENT_PF
msg (M_CLIENT, "client-pf CID : Define packet filter for client CID (MULTILINE)");
@@ -698,7 +698,7 @@ static void
man_forget_passwords (struct management *man)
{
#if defined(USE_CRYPTO) && defined(USE_SSL)
- ssl_purge_auth ();
+ ssl_purge_auth (false);
msg (M_CLIENT, "SUCCESS: Passwords were forgotten");
#endif
}
@@ -947,14 +947,14 @@ man_client_deny (struct management *man, const char *cid_str, const char *kid_st
}
static void
-man_client_kill (struct management *man, const char *cid_str)
+man_client_kill (struct management *man, const char *cid_str, const char *kill_msg)
{
unsigned long cid = 0;
if (parse_cid (cid_str, &cid))
{
if (man->persist.callback.kill_by_cid)
{
- const bool status = (*man->persist.callback.kill_by_cid) (man->persist.callback.arg, cid);
+ const bool status = (*man->persist.callback.kill_by_cid) (man->persist.callback.arg, cid, kill_msg);
if (status)
{
msg (M_CLIENT, "SUCCESS: client-kill command succeeded");
@@ -1265,8 +1265,8 @@ man_dispatch_command (struct management *man, struct status_output *so, const ch
#ifdef MANAGEMENT_DEF_AUTH
else if (streq (p[0], "client-kill"))
{
- if (man_need (man, p, 1, 0))
- man_client_kill (man, p[1]);
+ if (man_need (man, p, 1, MN_AT_LEAST))
+ man_client_kill (man, p[1], p[2]);
}
else if (streq (p[0], "client-deny"))
{
@@ -1682,7 +1682,7 @@ man_reset_client_socket (struct management *man, const bool exiting)
{
#if defined(USE_CRYPTO) && defined(USE_SSL)
if (man->settings.flags & MF_FORGET_DISCONNECT)
- ssl_purge_auth ();
+ ssl_purge_auth (false);
#endif
if (man->settings.flags & MF_SIGNAL) {
int mysig = man_mod_signal (man, SIGUSR1);
@@ -2190,6 +2190,7 @@ management_open (struct management *man,
void
management_close (struct management *man)
{
+ man_output_list_push_finalize (man); /* flush output queue */
man_connection_close (man);
man_settings_close (&man->settings);
man_persist_close (&man->persist);
@@ -2252,8 +2253,6 @@ management_set_state (struct management *man,
}
}
-#ifdef MANAGEMENT_DEF_AUTH
-
static bool
env_filter_match (const char *env_str, const int env_filter_level)
{
@@ -2261,7 +2260,7 @@ env_filter_match (const char *env_str, const int env_filter_level)
"username=",
"password=",
"X509_0_CN=",
- "tls_serial_0=",
+ "tls_serial_",
"untrusted_ip=",
"ifconfig_local=",
"ifconfig_netmask=",
@@ -2274,24 +2273,28 @@ env_filter_match (const char *env_str, const int env_filter_level)
"bytes_sent=",
"bytes_received="
};
- if (env_filter_level >= 1)
+
+ if (env_filter_level == 0)
+ return true;
+ else if (env_filter_level <= 1 && !strncmp(env_str, "X509_", 5))
+ return true;
+ else if (env_filter_level <= 2)
{
size_t i;
for (i = 0; i < SIZE(env_names); ++i)
{
const char *en = env_names[i];
const size_t len = strlen(en);
- if (strncmp(env_str, en, len) == 0)
+ if (!strncmp(env_str, en, len))
return true;
}
return false;
}
- else
- return true;
+ return false;
}
static void
-man_output_env (const struct env_set *es, const bool tail, const int env_filter_level)
+man_output_env (const struct env_set *es, const bool tail, const int env_filter_level, const char *prefix)
{
if (es)
{
@@ -2299,15 +2302,15 @@ man_output_env (const struct env_set *es, const bool tail, const int env_filter_
for (e = es->list; e != NULL; e = e->next)
{
if (e->string && (!env_filter_level || env_filter_match(e->string, env_filter_level)))
- msg (M_CLIENT, ">CLIENT:ENV,%s", e->string);
+ msg (M_CLIENT, ">%s:ENV,%s", prefix, e->string);
}
}
if (tail)
- msg (M_CLIENT, ">CLIENT:ENV,END");
+ msg (M_CLIENT, ">%s:ENV,END", prefix);
}
static void
-man_output_extra_env (struct management *man)
+man_output_extra_env (struct management *man, const char *prefix)
{
struct gc_arena gc = gc_new ();
struct env_set *es = env_set_create (&gc);
@@ -2316,10 +2319,28 @@ man_output_extra_env (struct management *man)
const int nclients = (*man->persist.callback.n_clients) (man->persist.callback.arg);
setenv_int (es, "n_clients", nclients);
}
- man_output_env (es, false, man->connection.env_filter_level);
+ man_output_env (es, false, man->connection.env_filter_level, prefix);
gc_free (&gc);
}
+void
+management_up_down(struct management *man, const char *updown, const struct env_set *es)
+{
+ if (man->settings.flags & MF_UP_DOWN)
+ {
+ msg (M_CLIENT, ">UPDOWN:%s", updown);
+ man_output_env (es, true, 0, "UPDOWN");
+ }
+}
+
+void
+management_notify(struct management *man, const char *severity, const char *type, const char *text)
+{
+ msg (M_CLIENT, ">NOTIFY:%s,%s,%s", severity, type, text);
+}
+
+#ifdef MANAGEMENT_DEF_AUTH
+
static bool
validate_peer_info_line(const char *line)
{
@@ -2384,9 +2405,9 @@ management_notify_client_needing_auth (struct management *management,
if (mdac->flags & DAF_CONNECTION_ESTABLISHED)
mode = "REAUTH";
msg (M_CLIENT, ">CLIENT:%s,%lu,%u", mode, mdac->cid, mda_key_id);
- man_output_extra_env (management);
+ man_output_extra_env (management, "CLIENT");
man_output_peer_info_env(management, mdac);
- man_output_env (es, true, management->connection.env_filter_level);
+ man_output_env (es, true, management->connection.env_filter_level, "CLIENT");
mdac->flags |= DAF_INITIAL_AUTH;
}
}
@@ -2398,8 +2419,8 @@ management_connection_established (struct management *management,
{
mdac->flags |= DAF_CONNECTION_ESTABLISHED;
msg (M_CLIENT, ">CLIENT:ESTABLISHED,%lu", mdac->cid);
- man_output_extra_env (management);
- man_output_env (es, true, management->connection.env_filter_level);
+ man_output_extra_env (management, "CLIENT");
+ man_output_env (es, true, management->connection.env_filter_level, "CLIENT");
}
void
@@ -2410,7 +2431,7 @@ management_notify_client_close (struct management *management,
if ((mdac->flags & DAF_INITIAL_AUTH) && !(mdac->flags & DAF_CONNECTION_CLOSED))
{
msg (M_CLIENT, ">CLIENT:DISCONNECT,%lu", mdac->cid);
- man_output_env (es, true, management->connection.env_filter_level);
+ man_output_env (es, true, management->connection.env_filter_level, "CLIENT");
mdac->flags |= DAF_CONNECTION_CLOSED;
}
}
@@ -2494,6 +2515,12 @@ management_auth_failure (struct management *man, const char *type, const char *r
msg (M_CLIENT, ">PASSWORD:Verification Failed: '%s'", type);
}
+void
+management_auth_token (struct management *man, const char *token)
+{
+ msg (M_CLIENT, ">PASSWORD:Auth-Token:%s", token);
+}
+
static inline bool
man_persist_state (unsigned int *persistent, const int n)
{
diff --git a/manage.h b/manage.h
index a8f971b..c6ce31e 100644
--- a/manage.h
+++ b/manage.h
@@ -156,7 +156,7 @@ struct management_callback
void (*delete_event) (void *arg, event_t event);
int (*n_clients) (void *arg);
#ifdef MANAGEMENT_DEF_AUTH
- bool (*kill_by_cid) (void *arg, const unsigned long cid);
+ bool (*kill_by_cid) (void *arg, const unsigned long cid, const char *kill_msg);
bool (*client_auth) (void *arg,
const unsigned long cid,
const unsigned int mda_key_id,
@@ -274,7 +274,6 @@ struct man_connection {
#ifdef MANAGEMENT_DEF_AUTH
unsigned long in_extra_cid;
unsigned int in_extra_kid;
- int env_filter_level;
#endif
#ifdef MANAGMENT_EXTERNAL_KEY
# define EKS_UNDEF 0
@@ -286,6 +285,7 @@ struct man_connection {
#endif
#endif
struct event_set *es;
+ int env_filter_level;
bool state_realtime;
bool log_realtime;
@@ -332,6 +332,7 @@ struct management *management_init (void);
#ifdef MANAGMENT_EXTERNAL_KEY
# define MF_EXTERNAL_KEY (1<<9)
#endif
+#define MF_UP_DOWN (1<<10)
bool management_open (struct management *man,
const char *addr,
@@ -372,6 +373,10 @@ bool management_hold (struct management *man);
void management_event_loop_n_seconds (struct management *man, int sec);
+void management_up_down(struct management *man, const char *updown, const struct env_set *es);
+
+void management_notify(struct management *man, const char *severity, const char *type, const char *text);
+
#ifdef MANAGEMENT_DEF_AUTH
void management_notify_client_needing_auth (struct management *management,
const unsigned int auth_id,
@@ -467,6 +472,11 @@ void management_echo (struct management *man, const char *string, const bool pul
void management_auth_failure (struct management *man, const char *type, const char *reason);
/*
+ * Echo an authentication token to management interface
+ */
+void management_auth_token (struct management *man, const char *token);
+
+/*
* These functions drive the bytecount in/out counters.
*/
diff --git a/misc.c b/misc.c
index d499009..2d6c6f3 100644
--- a/misc.c
+++ b/misc.c
@@ -1713,6 +1713,16 @@ purge_user_pass (struct user_pass *up, const bool force)
}
}
+void
+set_auth_token (struct user_pass *up, const char *token)
+{
+ if (token && strlen(token) && up && up->defined && !up->nocache)
+ {
+ CLEAR (up->password);
+ strncpynt (up->password, token, USER_PASS_LEN);
+ }
+}
+
/*
* Process string received by untrusted peer before
* printing to console or log file.
@@ -2381,3 +2391,37 @@ openvpn_basename (const char *path)
}
return NULL;
}
+
+/*
+ * Remove SESS_ID_x strings (i.e. auth tokens) from control message
+ * strings so that they will not be output to log file.
+ */
+const char *
+sanitize_control_message(const char *str, struct gc_arena *gc)
+{
+ char *ret = gc_malloc (strlen(str)+1, false, gc);
+ char *cp = ret;
+ bool redact = false;
+
+ strcpy(ret, str);
+ for (;;)
+ {
+ const char c = *cp;
+ if (c == '\0')
+ break;
+ if (c == 'S' && !strncmp(cp, "SESS_ID_", 8))
+ {
+ cp += 7;
+ redact = true;
+ }
+ else
+ {
+ if (c == ',') /* end of session id? */
+ redact = false;
+ if (redact)
+ *cp = '_';
+ }
+ ++cp;
+ }
+ return ret;
+}
diff --git a/misc.h b/misc.h
index dbd206a..7681c00 100644
--- a/misc.h
+++ b/misc.h
@@ -316,6 +316,8 @@ void fail_user_pass (const char *prefix,
void purge_user_pass (struct user_pass *up, const bool force);
+void set_auth_token (struct user_pass *up, const char *token);
+
/*
* Process string received by untrusted peer before
* printing to console or log file.
@@ -337,6 +339,8 @@ void openvpn_sleep (const int n);
void configure_path (void);
+const char *sanitize_control_message(const char *str, struct gc_arena *gc);
+
#if AUTO_USERID
void get_user_pass_auto_userid (struct user_pass *up, const char *tag);
#endif
diff --git a/multi.c b/multi.c
index 12fb5cb..15bea22 100644
--- a/multi.c
+++ b/multi.c
@@ -626,7 +626,7 @@ multi_create_instance (struct multi_context *m, const struct mroute_addr *real)
perf_push (PERF_MULTI_CREATE_INSTANCE);
- msg (D_MULTI_LOW, "MULTI: multi_create_instance called");
+ msg (D_MULTI_MEDIUM, "MULTI: multi_create_instance called");
ALLOC_OBJ_CLEAR (mi, struct multi_instance);
@@ -1237,6 +1237,9 @@ multi_select_virtual_addr (struct multi_context *m, struct multi_instance *mi)
mi->context.c2.push_ifconfig_defined = true;
mi->context.c2.push_ifconfig_local = mi->context.options.push_ifconfig_local;
mi->context.c2.push_ifconfig_remote_netmask = mi->context.options.push_ifconfig_remote_netmask;
+#ifdef ENABLE_CLIENT_NAT
+ mi->context.c2.push_ifconfig_local_alias = mi->context.options.push_ifconfig_local_alias;
+#endif
/* the current implementation does not allow "static IPv4, pool IPv6",
* (see below) so issue a warning if that happens - don't break the
@@ -2650,13 +2653,14 @@ lookup_by_cid (struct multi_context *m, const unsigned long cid)
}
static bool
-management_kill_by_cid (void *arg, const unsigned long cid)
+management_kill_by_cid (void *arg, const unsigned long cid, const char *kill_msg)
{
struct multi_context *m = (struct multi_context *) arg;
struct multi_instance *mi = lookup_by_cid (m, cid);
if (mi)
{
- send_restart (&mi->context); /* was: multi_signal_instance (m, mi, SIGTERM); */
+ send_restart (&mi->context, kill_msg); /* was: multi_signal_instance (m, mi, SIGTERM); */
+ multi_schedule_context_wakeup(m, mi);
return true;
}
else
diff --git a/openvpn.8 b/openvpn.8
index 95d060d..5cbf4c5 100644
--- a/openvpn.8
+++ b/openvpn.8
@@ -1083,6 +1083,31 @@ and
.B \-\-route-gateway.
.\"*********************************************************
.TP
+.B \-\-client-nat snat|dnat network netmask alias
+This pushable client option sets up a stateless one-to-one NAT
+rule on packet addresses (not ports), and is useful in cases
+where routes or ifconfig settings pushed to the client would
+create an IP numbering conflict.
+
+.B network/netmask
+(for example 192.168.0.0/255.255.0.0)
+defines the local view of a resource from the client perspective, while
+.B alias/netmask
+(for example 10.64.0.0/255.255.0.0)
+defines the remote view from the server perspective.
+
+Use
+.B snat
+(source NAT) for resources owned by the client and
+.B dnat
+(destination NAT) for remote resources.
+
+Set
+.B \-\-verb 6
+for debugging info showing the transformation of src/dest
+addresses in packets.
+.\"*********************************************************
+.TP
.B \-\-redirect-gateway flags...
(Experimental) Automatically execute routing commands to cause all outgoing IP traffic
to be redirected over the VPN.
@@ -2406,6 +2431,11 @@ lines of log file history for usage
by the management channel.
.\"*********************************************************
.TP
+.B \-\-management-up-down
+Report tunnel up/down events to management interface.
+.B
+.\"*********************************************************
+.TP
.B \-\-management-client-auth
Gives management interface client the responsibility
to authenticate clients after their client certificate
@@ -2740,7 +2770,7 @@ This option is deprecated, and should be replaced with
which is functionally equivalent.
.\"*********************************************************
.TP
-.B \-\-ifconfig-push local remote-netmask
+.B \-\-ifconfig-push local remote-netmask [alias]
Push virtual IP endpoints for client tunnel,
overriding the \-\-ifconfig-pool dynamic allocation.
@@ -2759,6 +2789,15 @@ are from the perspective of the client, not the server. They may be
DNS names rather than IP addresses, in which case they will be resolved
on the server at the time of client connection.
+The optional
+.B alias
+parameter may be used in cases where NAT causes the client view
+of its local endpoint to differ from the server view. In this case
+.B local/remote-netmask
+will refer to the server view while
+.B alias/remote-netmask
+will refer to the client view.
+
This option must be associated with a specific client instance,
which means that it must be specified either in a client
instance config file using
@@ -3259,7 +3298,7 @@ disable the remapping feature. Don't use this option unless you
know what you are doing!
.\"*********************************************************
.TP
-.B \-\-port-share host port
+.B \-\-port-share host port [dir]
When run in TCP server mode, share the OpenVPN port with
another application, such as an HTTPS server. If OpenVPN
senses a connection to its port which is using a non-OpenVPN
@@ -3269,6 +3308,16 @@ Currently only designed to work with HTTP/HTTPS,
though it would be theoretically possible to extend to
other protocols such as ssh.
+.B dir
+specifies an optional directory where a temporary file with name N
+containing content C will be dynamically generated for each proxy
+connection, where N is the source IP:port of the client connection
+and C is the source IP:port of the connection to the proxy
+receiver. This directory can be used as a dictionary by
+the proxy receiver to determine the origin of the connection.
+Each generated file will be automatically deleted when the proxied
+connection is torn down.
+
Not implemented on Windows.
.\"*********************************************************
.SS Client Mode
@@ -3902,6 +3951,22 @@ that for certificate authority functions, you must set up the files
).
.\"*********************************************************
.TP
+.B \-\-extra-certs file
+Specify a
+.B file
+containing one or more PEM certs (concatenated together)
+that complete the
+local certificate chain.
+
+This option is useful for "split" CAs, where the CA for server
+certs is different than the CA for client certs. Putting certs
+in this file allows them to be used to complete the local
+certificate chain without trusting them to verify the peer-submitted
+certificate, as would be the case if the certs were placed in the
+.B ca
+file.
+.\"*********************************************************
+.TP
.B \-\-key file
Local peer's private key in .pem format. Use the private key which was generated
when you built your peer's certificate (see
@@ -3918,6 +3983,17 @@ and
.B \-\-key.
.\"*********************************************************
.TP
+.B \-\-verify-hash hash
+Specify SHA1 fingerprint for level-1 cert. The level-1 cert is the
+CA (or intermediate cert) that signs the leaf certificate, and is
+one removed from the leaf certificate in the direction of the root.
+When accepting a connection from a peer, the level-1 cert
+fingerprint must match
+.B hash
+or certificate verification will fail. Hash is specified
+as XX:XX:... For example: AD:B0:95:D8:09:C8:36:45:12:A9:89:C8:90:09:CB:13:72:A6:AD:16
+.\"*********************************************************
+.TP
.B \-\-pkcs11-cert-private [0|1]...
Set if access to certificate object should be performed after login.
Every provider has its own setting.
@@ -4347,7 +4423,7 @@ additional parameters passed as environmental variables.
.TP
.B \-\-tls-export-cert directory
Store the certificates the clients uses upon connection to this
-directory. This will be done before --tls-verify is called. The
+directory. This will be done before \-\-tls-verify is called. The
certificates will use a temporary name and will be deleted when
the tls-verify script returns. The file name used for the certificate
is available via the peer_cert environment variable.
@@ -4387,6 +4463,18 @@ works in a
environment too.
.\"*********************************************************
.TP
+.B \-\-x509-track attribute
+Save peer X509
+.B attribute
+value in environment for use by plugins and management interface.
+Prepend a '+' to
+.B attribute
+to save values from full cert chain. Values will be encoded
+as X509_<depth>_<attribute>=<value>. Multiple
+.B \-\-x509-track
+options can be defined to track multiple attributes.
+.\"*********************************************************
+.TP
.B \-\-ns-cert-type client|server
Require that peer certificate was signed with an explicit
.B nsCertType
@@ -4472,7 +4560,7 @@ or
.B \-\-tls-verify.
.\"*********************************************************
.TP
-.B \-\-crl-verify crl
+.B \-\-crl-verify crl ['dir']
Check peer certificate against the file
.B crl
in PEM format.
@@ -4488,6 +4576,16 @@ overall integrity of the PKI.
The only time when it would be necessary to rebuild the entire PKI from scratch would be
if the root certificate key itself was compromised.
+
+If the optional
+.B dir
+flag is specified, enable a different mode where
+.B crl
+is a directory containing files named as revoked serial numbers
+(the files may be empty, the contents are never read). If a client
+requests a connection, where the client certificate serial number
+(decimal string) is the name of a file present in the directory,
+it will be rejected.
.\"*********************************************************
.SS SSL Library information:
.\"*********************************************************
diff --git a/openvpn.h b/openvpn.h
index e5e6e58..33661df 100644
--- a/openvpn.h
+++ b/openvpn.h
@@ -417,8 +417,12 @@ struct context_2
/* --ifconfig endpoints to be pushed to client */
bool push_reply_deferred;
bool push_ifconfig_defined;
+ bool sent_push_reply;
in_addr_t push_ifconfig_local;
in_addr_t push_ifconfig_remote_netmask;
+#ifdef ENABLE_CLIENT_NAT
+ in_addr_t push_ifconfig_local_alias;
+#endif
bool push_ifconfig_ipv6_defined;
struct in6_addr push_ifconfig_ipv6_local;
@@ -435,6 +439,7 @@ struct context_2
#endif
struct event_timeout push_request_interval;
+ int n_sent_push_requests;
bool did_pre_pull_restore;
/* hash of pulled options, so we can compare when options change */
diff --git a/options.c b/options.c
index 8d1645d..f643acc 100644
--- a/options.c
+++ b/options.c
@@ -223,6 +223,9 @@ static const char usage_message[] =
" Add 'bypass-dns' flag to similarly bypass tunnel for DNS.\n"
"--redirect-private [flags]: Like --redirect-gateway, but omit actually changing\n"
" the default gateway. Useful when pushing private subnets.\n"
+#ifdef ENABLE_CLIENT_NAT
+ "--client-nat snat|dnat network netmask alias : on client add 1-to-1 NAT rule.\n"
+#endif
#ifdef ENABLE_PUSH_PEER_INFO
"--push-peer-info : (client only) push client info to server.\n"
#endif
@@ -359,6 +362,7 @@ static const char usage_message[] =
"--management-signal : Issue SIGUSR1 when management disconnect event occurs.\n"
"--management-forget-disconnect : Forget passwords when management disconnect\n"
" event occurs.\n"
+ "--management-up-down : Report tunnel up/down events to management interface.\n"
"--management-log-cache n : Cache n lines of log file history for usage\n"
" by the management channel.\n"
#if UNIX_SOCK_SUPPORT
@@ -448,8 +452,9 @@ static const char usage_message[] =
"--max-clients n : Allow a maximum of n simultaneously connected clients.\n"
"--max-routes-per-client n : Allow a maximum of n internal routes per client.\n"
#if PORT_SHARE
- "--port-share host port : When run in TCP mode, proxy incoming HTTPS sessions\n"
- " to a web server at host:port.\n"
+ "--port-share host port [dir] : When run in TCP mode, proxy incoming HTTPS\n"
+ " sessions to a web server at host:port. dir specifies an\n"
+ " optional directory to write origin IP:port data.\n"
#endif
#endif
"\n"
@@ -527,6 +532,7 @@ static const char usage_message[] =
" Use \"openssl dhparam -out dh1024.pem 1024\" to generate.\n"
"--cert file : Local certificate in .pem format -- must be signed\n"
" by a Certificate Authority in --ca file.\n"
+ "--extra-certs file : one or more PEM certs that complete the cert chain.\n"
"--key file : Local private key in .pem format.\n"
"--pkcs12 file : PKCS#12 file containing local private key, local certificate\n"
" and optionally the root CA certificate.\n"
@@ -534,6 +540,7 @@ static const char usage_message[] =
"--x509-username-field : Field used in x509 certificat to be username.\n"
" Default is CN.\n"
#endif
+ "--verify-hash : Specify SHA1 fingerprint for level-1 cert.\n"
#ifdef WIN32
"--cryptoapicert select-string : Load the certificate and private key from the\n"
" Windows Certificate System Store.\n"
@@ -558,7 +565,7 @@ static const char usage_message[] =
" see --secret option for more info.\n"
"--askpass [file]: Get PEM password from controlling tty before we daemonize.\n"
"--auth-nocache : Don't cache --askpass or --auth-user-pass passwords.\n"
- "--crl-verify crl: Check peer certificate against a CRL.\n"
+ "--crl-verify crl ['dir']: Check peer certificate against a CRL.\n"
"--tls-verify cmd: Execute shell command cmd to verify the X509 name of a\n"
" pending TLS connection that has otherwise passed all other\n"
" tests of certification. cmd should return 0 to allow\n"
@@ -572,6 +579,8 @@ static const char usage_message[] =
" of verification.\n"
"--ns-cert-type t: Require that peer certificate was signed with an explicit\n"
" nsCertType designation t = 'client' | 'server'.\n"
+ "--x509-track x : Save peer X509 attribute x in environment for use by\n"
+ " plugins and management interface.\n"
#if OPENSSL_VERSION_NUMBER >= 0x00907000L
"--remote-cert-ku v ... : Require that the peer certificate was signed with\n"
" explicit key usage, you can specify more than one value.\n"
@@ -1000,6 +1009,40 @@ is_stateful_restart (const struct options *o)
return is_persist_option (o) || connection_list_defined (o);
}
+#ifdef USE_SSL
+static uint8_t *
+parse_hash_fingerprint(const char *str, int nbytes, int msglevel, struct gc_arena *gc)
+{
+ int i;
+ const char *cp = str;
+ uint8_t *ret = (uint8_t *) gc_malloc (nbytes, true, gc);
+ char term = 1;
+ int byte;
+ char bs[3];
+
+ for (i = 0; i < nbytes; ++i)
+ {
+ if (strlen(cp) < 2)
+ msg (msglevel, "format error in hash fingerprint: %s", str);
+ bs[0] = *cp++;
+ bs[1] = *cp++;
+ bs[2] = 0;
+ byte = 0;
+ if (sscanf(bs, "%x", &byte) != 1)
+ msg (msglevel, "format error in hash fingerprint hex byte: %s", str);
+ ret[i] = (uint8_t)byte;
+ term = *cp++;
+ if (term != ':' && term != 0)
+ msg (msglevel, "format error in hash fingerprint delimiter: %s", str);
+ if (term == 0)
+ break;
+ }
+ if (term != 0 || i != nbytes-1)
+ msg (msglevel, "hash fingerprint is different length than expected (%d bytes): %s", nbytes, str);
+ return ret;
+}
+#endif
+
#ifdef WIN32
#ifdef ENABLE_DEBUG
@@ -1133,7 +1176,6 @@ show_p2mp_parms (const struct options *o)
SHOW_INT (max_routes_per_client);
SHOW_STR (auth_user_pass_verify_script);
SHOW_BOOL (auth_user_pass_verify_script_via_file);
- SHOW_INT (ssl_flags);
#if PORT_SHARE
SHOW_STR (port_share_host);
SHOW_INT (port_share_port);
@@ -1223,6 +1265,9 @@ options_detach (struct options *o)
{
gc_detach (&o->gc);
o->routes = NULL;
+#ifdef ENABLE_CLIENT_NAT
+ o->client_nat = NULL;
+#endif
#if P2MP_SERVER
clone_push_list(o);
#endif
@@ -1242,6 +1287,15 @@ rol6_check_alloc (struct options *options)
options->routes_ipv6 = new_route_ipv6_option_list (options->max_routes, &options->gc);
}
+#ifdef ENABLE_CLIENT_NAT
+static void
+cnol_check_alloc (struct options *options)
+{
+ if (!options->client_nat)
+ options->client_nat = new_client_nat_list (&options->gc);
+}
+#endif
+
#ifdef ENABLE_DEBUG
static void
show_connection_entry (const struct connection_entry *o)
@@ -1435,6 +1489,11 @@ show_settings (const struct options *o)
SHOW_BOOL (allow_pull_fqdn);
if (o->routes)
print_route_options (o->routes, D_SHOW_PARMS);
+
+#ifdef ENABLE_CLIENT_NAT
+ if (o->client_nat)
+ print_client_nat_list(o->client_nat, D_SHOW_PARMS);
+#endif
#ifdef ENABLE_MANAGEMENT
SHOW_STR (management_addr);
@@ -1496,6 +1555,7 @@ show_settings (const struct options *o)
SHOW_INT (remote_cert_ku[i]);
}
SHOW_STR (remote_cert_eku);
+ SHOW_INT (ssl_flags);
SHOW_INT (tls_timeout);
@@ -2548,6 +2608,13 @@ pre_pull_save (struct options *o)
o->pre_pull->routes = clone_route_option_list(o->routes, &o->gc);
o->pre_pull->routes_defined = true;
}
+#ifdef ENABLE_CLIENT_NAT
+ if (o->client_nat)
+ {
+ o->pre_pull->client_nat = clone_client_nat_option_list(o->client_nat, &o->gc);
+ o->pre_pull->client_nat_defined = true;
+ }
+#endif
}
}
@@ -2569,6 +2636,16 @@ pre_pull_restore (struct options *o)
else
o->routes = NULL;
+#ifdef ENABLE_CLIENT_NAT
+ if (pp->client_nat_defined)
+ {
+ cnol_check_alloc (o);
+ copy_client_nat_option_list (o->client_nat, pp->client_nat);
+ }
+ else
+ o->client_nat = NULL;
+#endif
+
o->foreign_option_index = pp->foreign_option_index;
}
@@ -3865,6 +3942,11 @@ add_option (struct options *options,
VERIFY_PERMISSION (OPT_P_GENERAL);
options->management_flags |= MF_FORGET_DISCONNECT;
}
+ else if (streq (p[0], "management-up-down"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->management_flags |= MF_UP_DOWN;
+ }
else if (streq (p[0], "management-client"))
{
VERIFY_PERMISSION (OPT_P_GENERAL);
@@ -3886,6 +3968,13 @@ add_option (struct options *options,
options->management_flags |= MF_CLIENT_AUTH;
}
#endif
+#ifdef ENABLE_X509_TRACK
+ else if (streq (p[0], "x509-track") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ x509_track_add (&options->x509_track, p[1], msglevel, &options->gc);
+ }
+#endif
#ifdef MANAGEMENT_PF
else if (streq (p[0], "management-client-pf"))
{
@@ -4792,6 +4881,14 @@ add_option (struct options *options,
VERIFY_PERMISSION (OPT_P_PERSIST_IP);
options->persist_remote_ip = true;
}
+#ifdef ENABLE_CLIENT_NAT
+ else if (streq (p[0], "client-nat") && p[1] && p[2] && p[3] && p[4])
+ {
+ VERIFY_PERMISSION (OPT_P_ROUTE);
+ cnol_check_alloc (options);
+ add_client_nat_to_option_list(options->client_nat, p[1], p[2], p[3], p[4], msglevel);
+ }
+#endif
else if (streq (p[0], "route") && p[1])
{
VERIFY_PERMISSION (OPT_P_ROUTE);
@@ -4958,6 +5055,12 @@ add_option (struct options *options,
msg (msglevel, "this is a generic configuration and cannot directly be used");
goto err;
}
+#ifdef ENABLE_PUSH_PEER_INFO
+ else if (streq (p[1], "PUSH_PEER_INFO"))
+ {
+ options->push_peer_info = true;
+ }
+#endif
#if P2MP
else if (streq (p[1], "SERVER_POLL_TIMEOUT") && p[2])
{
@@ -5352,6 +5455,7 @@ add_option (struct options *options,
options->port_share_host = p[1];
options->port_share_port = port;
+ options->port_share_journal_dir = p[3];
}
#endif
else if (streq (p[0], "client-to-client"))
@@ -5392,6 +5496,10 @@ add_option (struct options *options,
options->push_ifconfig_defined = true;
options->push_ifconfig_local = local;
options->push_ifconfig_remote_netmask = remote_netmask;
+#ifdef ENABLE_CLIENT_NAT
+ if (p[3])
+ options->push_ifconfig_local_alias = getaddr (GETADDR_HOST_ORDER|GETADDR_RESOLVE, p[3], 0, NULL, NULL);
+#endif
}
else
{
@@ -6051,6 +6159,22 @@ add_option (struct options *options,
}
#endif
}
+ else if (streq (p[0], "extra-certs") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->extra_certs_file = p[1];
+#if ENABLE_INLINE_FILES
+ if (streq (p[1], INLINE_FILE_TAG) && p[2])
+ {
+ options->extra_certs_file_inline = p[2];
+ }
+#endif
+ }
+ else if (streq (p[0], "verify-hash") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->verify_hash = parse_hash_fingerprint(p[1], SHA_DIGEST_LENGTH, msglevel, &options->gc);
+ }
#ifdef WIN32
else if (streq (p[0], "cryptoapicert") && p[1])
{
@@ -6095,6 +6219,15 @@ add_option (struct options *options,
VERIFY_PERMISSION (OPT_P_GENERAL);
ssl_set_auth_nocache ();
}
+ else if (streq (p[0], "auth-token") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_ECHO);
+ ssl_set_auth_token(p[1]);
+#ifdef ENABLE_MANAGEMENT
+ if (management)
+ management_auth_token (management, p[1]);
+#endif
+ }
else if (streq (p[0], "single-session"))
{
VERIFY_PERMISSION (OPT_P_GENERAL);
@@ -6120,6 +6253,8 @@ add_option (struct options *options,
else if (streq (p[0], "crl-verify") && p[1])
{
VERIFY_PERMISSION (OPT_P_GENERAL);
+ if (p[2] && streq(p[2], "dir"))
+ options->ssl_flags |= SSLF_CRL_VERIFY_DIR;
options->crl_file = p[1];
}
else if (streq (p[0], "tls-verify") && p[1])
diff --git a/options.h b/options.h
index 120ff57..a9c6a94 100644
--- a/options.h
+++ b/options.h
@@ -41,6 +41,7 @@
#include "proxy.h"
#include "lzo.h"
#include "pushlist.h"
+#include "clinat.h"
/*
* Maximum number of parameters associated with an option,
@@ -67,6 +68,11 @@ struct options_pre_pull
bool routes_defined;
struct route_option_list *routes;
+#ifdef ENABLE_CLIENT_NAT
+ bool client_nat_defined;
+ struct client_nat_option_list *client_nat;
+#endif
+
int foreign_option_index;
};
@@ -334,6 +340,10 @@ struct options
bool route_gateway_via_dhcp;
bool allow_pull_fqdn; /* as a client, allow server to push a FQDN for certain parameters */
+#ifdef ENABLE_CLIENT_NAT
+ struct client_nat_option_list *client_nat;
+#endif
+
#ifdef ENABLE_OCC
/* Enable options consistency check between peers */
bool occ;
@@ -410,6 +420,9 @@ struct options
bool push_ifconfig_defined;
in_addr_t push_ifconfig_local;
in_addr_t push_ifconfig_remote_netmask;
+#ifdef ENABLE_CLIENT_NAT
+ in_addr_t push_ifconfig_local_alias;
+#endif
bool push_ifconfig_constraint_defined;
in_addr_t push_ifconfig_constraint_network;
in_addr_t push_ifconfig_constraint_netmask;
@@ -426,10 +439,10 @@ struct options
const char *auth_user_pass_verify_script;
bool auth_user_pass_verify_script_via_file;
- unsigned int ssl_flags; /* set to SSLF_x flags from ssl.h */
#if PORT_SHARE
char *port_share_host;
int port_share_port;
+ const char *port_share_journal_dir;
#endif
#endif
@@ -476,6 +489,7 @@ struct options
const char *ca_path;
const char *dh_file;
const char *cert_file;
+ const char *extra_certs_file;
const char *priv_key_file;
const char *pkcs12_file;
const char *cipher_list;
@@ -487,6 +501,7 @@ struct options
#if ENABLE_INLINE_FILES
const char *ca_file_inline;
const char *cert_file_inline;
+ const char *extra_certs_file_inline;
char *priv_key_file_inline;
const char *dh_file_inline;
const char *pkcs12_file_inline; /* contains the base64 encoding of pkcs12 file */
@@ -495,6 +510,8 @@ struct options
int ns_cert_type; /* set to 0, NS_SSL_SERVER, or NS_SSL_CLIENT */
unsigned remote_cert_ku[MAX_PARMS];
const char *remote_cert_eku;
+ uint8_t *verify_hash;
+ unsigned int ssl_flags; /* set to SSLF_x flags from ssl.h */
#ifdef ENABLE_PKCS11
const char *pkcs11_providers[MAX_PARMS];
@@ -551,6 +568,10 @@ struct options
#endif /* USE_SSL */
#endif /* USE_CRYPTO */
+#ifdef ENABLE_X509_TRACK
+ const struct x509_track *x509_track;
+#endif
+
/* special state parms */
int foreign_option_index;
diff --git a/packet_id.c b/packet_id.c
index b11e71f..9bbfbf3 100644
--- a/packet_id.c
+++ b/packet_id.c
@@ -41,6 +41,8 @@
#include "memdbg.h"
+/* #define PID_SIMULATE_BACKTRACK */
+
/*
* Special time_t value that indicates that
* sequence number has expired.
@@ -48,17 +50,39 @@
#define SEQ_UNSEEN ((time_t)0)
#define SEQ_EXPIRED ((time_t)1)
+static void packet_id_debug_print (int msglevel,
+ const struct packet_id_rec *p,
+ const struct packet_id_net *pin,
+ const char *message,
+ int value);
+
+static inline void
+packet_id_debug (int msglevel,
+ const struct packet_id_rec *p,
+ const struct packet_id_net *pin,
+ const char *message,
+ int value)
+{
+#ifdef ENABLE_DEBUG
+ if (unlikely(check_debug_level(msglevel)))
+ packet_id_debug_print (msglevel, p, pin, message, value);
+#endif
+}
+
void
-packet_id_init (struct packet_id *p, int seq_backtrack, int time_backtrack)
+packet_id_init (struct packet_id *p, bool tcp_mode, int seq_backtrack, int time_backtrack, const char *name, int unit)
{
- dmsg (D_PID_DEBUG_LOW, "PID packet_id_init seq_backtrack=%d time_backtrack=%d",
- seq_backtrack,
- time_backtrack);
+ dmsg (D_PID_DEBUG, "PID packet_id_init tcp_mode=%d seq_backtrack=%d time_backtrack=%d",
+ tcp_mode,
+ seq_backtrack,
+ time_backtrack);
ASSERT (p);
CLEAR (*p);
- if (seq_backtrack)
+ p->rec.name = name;
+ p->rec.unit = unit;
+ if (seq_backtrack && !tcp_mode)
{
ASSERT (MIN_SEQ_BACKTRACK <= seq_backtrack && seq_backtrack <= MAX_SEQ_BACKTRACK);
ASSERT (MIN_TIME_BACKTRACK <= time_backtrack && time_backtrack <= MAX_TIME_BACKTRACK);
@@ -74,7 +98,7 @@ packet_id_free (struct packet_id *p)
{
if (p)
{
- dmsg (D_PID_DEBUG_LOW, "PID packet_id_free");
+ dmsg (D_PID_DEBUG, "PID packet_id_free");
if (p->rec.seq_list)
free (p->rec.seq_list);
CLEAR (*p);
@@ -105,7 +129,11 @@ packet_id_add (struct packet_id_rec *p, const struct packet_id_net *pin)
CIRC_LIST_RESET (p->seq_list);
}
- while (p->id < pin->id)
+ while (p->id < pin->id
+#ifdef PID_SIMULATE_BACKTRACK
+ || (get_random() % 64) < 31
+#endif
+ )
{
CIRC_LIST_PUSH (p->seq_list, SEQ_UNSEEN);
++p->id;
@@ -155,17 +183,13 @@ packet_id_reap (struct packet_id_rec *p)
* it is a replay.
*/
bool
-packet_id_test (const struct packet_id_rec *p,
+packet_id_test (struct packet_id_rec *p,
const struct packet_id_net *pin)
{
- static int max_backtrack_stat;
packet_id_type diff;
- dmsg (D_PID_DEBUG,
- "PID TEST " time_format ":" packet_id_format " " time_format ":" packet_id_format "",
- (time_type)p->time, (packet_id_print_type)p->id, (time_type)pin->time,
- (packet_id_print_type)pin->id);
-
+ packet_id_debug (D_PID_DEBUG, p, pin, "PID_TEST", 0);
+
ASSERT (p->initialized);
if (!pin->id)
@@ -189,19 +213,35 @@ packet_id_test (const struct packet_id_rec *p,
diff = p->id - pin->id;
/* keep track of maximum backtrack seen for debugging purposes */
- if ((int)diff > max_backtrack_stat)
+ if ((int)diff > p->max_backtrack_stat)
{
- max_backtrack_stat = (int)diff;
- msg (D_BACKTRACK, "Replay-window backtrack occurred [%d]", max_backtrack_stat);
+ p->max_backtrack_stat = (int)diff;
+ packet_id_debug (D_PID_DEBUG_LOW, p, pin, "PID_ERR replay-window backtrack occurred", p->max_backtrack_stat);
}
if (diff >= (packet_id_type) CIRC_LIST_SIZE (p->seq_list))
- return false;
+ {
+ packet_id_debug (D_PID_DEBUG_LOW, p, pin, "PID_ERR large diff", diff);
+ return false;
+ }
- return CIRC_LIST_ITEM (p->seq_list, diff) == 0;
+ {
+ const time_t v = CIRC_LIST_ITEM (p->seq_list, diff);
+ if (v == 0)
+ return true;
+ else
+ {
+ /* might want to increase this to D_PID_DEBUG_MEDIUM (or even D_PID_DEBUG) in the future */
+ packet_id_debug (D_PID_DEBUG_LOW, p, pin, "PID_ERR replay", diff);
+ return false;
+ }
+ }
}
else if (pin->time < p->time) /* if time goes back, reject */
- return false;
+ {
+ packet_id_debug (D_PID_DEBUG_LOW, p, pin, "PID_ERR time backtrack", 0);
+ return false;
+ }
else /* time moved forward */
return true;
}
@@ -434,6 +474,76 @@ packet_id_persist_print (const struct packet_id_persist *p, struct gc_arena *gc)
return (char *)out.data;
}
+#ifdef ENABLE_DEBUG
+
+static void
+packet_id_debug_print (int msglevel,
+ const struct packet_id_rec *p,
+ const struct packet_id_net *pin,
+ const char *message,
+ int value)
+{
+ struct gc_arena gc = gc_new ();
+ struct buffer out = alloc_buf_gc (256, &gc);
+ struct timeval tv;
+ const time_t prev_now = now;
+ const struct seq_list *sl = p->seq_list;
+ int i;
+
+ CLEAR (tv);
+ gettimeofday (&tv, NULL);
+
+ buf_printf (&out, "%s [%d]", message, value);
+ buf_printf (&out, " [%s-%d] [", p->name, p->unit);
+ for (i = 0; i < sl->x_size; ++i)
+ {
+ char c;
+ time_t v;
+ int diff;
+
+ v = CIRC_LIST_ITEM(sl, i);
+ if (v == SEQ_UNSEEN)
+ c = '_';
+ else if (v == SEQ_EXPIRED)
+ c = 'E';
+ else
+ {
+ diff = (int) prev_now - v;
+ if (diff < 0)
+ c = 'N';
+ else if (diff < 10)
+ c = '0' + diff;
+ else
+ c = '>';
+ }
+ buf_printf(&out, "%c", c);
+ }
+ buf_printf (&out, "] " time_format ":" packet_id_format, (time_type)p->time, (packet_id_print_type)p->id);
+ if (pin)
+ buf_printf (&out, " " time_format ":" packet_id_format, (time_type)pin->time, (packet_id_print_type)pin->id);
+
+ buf_printf (&out, " t=" time_format "[%d]",
+ (time_type)prev_now,
+ (int)(prev_now - tv.tv_sec));
+
+ buf_printf (&out, " r=[%d,%d,%d,%d,%d]",
+ (int)(p->last_reap - tv.tv_sec),
+ p->seq_backtrack,
+ p->time_backtrack,
+ p->max_backtrack_stat,
+ (int)p->initialized);
+ buf_printf (&out, " sl=[%d,%d,%d,%d]",
+ sl->x_head,
+ sl->x_size,
+ sl->x_cap,
+ sl->x_sizeof);
+
+ msg (msglevel, "%s", BSTR(&out));
+ gc_free (&gc);
+}
+
+#endif
+
#ifdef PID_TEST
void
diff --git a/packet_id.h b/packet_id.h
index 12c1df3..7f4be8a 100644
--- a/packet_id.h
+++ b/packet_id.h
@@ -138,8 +138,11 @@ struct packet_id_rec
packet_id_type id; /* highest sequence number received */
int seq_backtrack; /* set from --replay-window */
int time_backtrack; /* set from --replay-window */
+ int max_backtrack_stat; /* maximum backtrack seen so far */
bool initialized; /* true if packet_id_init was called */
struct seq_list *seq_list; /* packet-id "memory" */
+ const char *name;
+ int unit;
};
/*
@@ -207,11 +210,11 @@ struct packet_id
struct packet_id_rec rec;
};
-void packet_id_init (struct packet_id *p, int seq_backtrack, int time_backtrack);
+void packet_id_init (struct packet_id *p, bool tcp_mode, int seq_backtrack, int time_backtrack, const char *name, int unit);
void packet_id_free (struct packet_id *p);
/* should we accept an incoming packet id ? */
-bool packet_id_test (const struct packet_id_rec *p,
+bool packet_id_test (struct packet_id_rec *p,
const struct packet_id_net *pin);
/* change our current state to reflect an accepted packet id */
diff --git a/proto.h b/proto.h
index b8e8997..8cd4ede 100644
--- a/proto.h
+++ b/proto.h
@@ -164,6 +164,14 @@ struct openvpn_tcphdr {
#define OPENVPN_TCPOPT_MAXSEG 2
#define OPENVPN_TCPOLEN_MAXSEG 4
+struct ip_tcp_udp_hdr {
+ struct openvpn_iphdr ip;
+ union {
+ struct openvpn_tcphdr tcp;
+ struct openvpn_udphdr udp;
+ } u;
+};
+
#pragma pack()
/*
@@ -175,19 +183,30 @@ struct openvpn_tcphdr {
* is the checksum value to be updated.
*/
#define ADJUST_CHECKSUM(acc, cksum) { \
- (acc) += (cksum); \
- if ((acc) < 0) { \
- (acc) = -(acc); \
- (acc) = ((acc) >> 16) + ((acc) & 0xffff); \
- (acc) += (acc) >> 16; \
- (cksum) = (uint16_t) ~(acc); \
+ int _acc = acc; \
+ _acc += (cksum); \
+ if (_acc < 0) { \
+ _acc = -_acc; \
+ _acc = (_acc >> 16) + (_acc & 0xffff); \
+ _acc += _acc >> 16; \
+ (cksum) = (uint16_t) ~_acc; \
} else { \
- (acc) = ((acc) >> 16) + ((acc) & 0xffff); \
- (acc) += (acc) >> 16; \
- (cksum) = (uint16_t) (acc); \
+ _acc = (_acc >> 16) + (_acc & 0xffff); \
+ _acc += _acc >> 16; \
+ (cksum) = (uint16_t) _acc; \
} \
}
+#define ADD_CHECKSUM_32(acc, u32) { \
+ acc += (u32) & 0xffff; \
+ acc += (u32) >> 16; \
+}
+
+#define SUB_CHECKSUM_32(acc, u32) { \
+ acc -= (u32) & 0xffff; \
+ acc -= (u32) >> 16; \
+}
+
/*
* We are in a "liberal" position with respect to MSS,
* i.e. we assume that MSS can be calculated from MTU
diff --git a/ps.c b/ps.c
index e07078f..d5be31e 100644
--- a/ps.c
+++ b/ps.c
@@ -69,6 +69,7 @@ struct proxy_connection {
bool buffer_initial;
int rwflags;
int sd;
+ char *jfn;
};
#if 0
@@ -226,7 +227,9 @@ port_share_sendmsg (const socket_descriptor_t sd,
status = sendmsg (sd, &mesg, MSG_NOSIGNAL);
if (status == -1)
- msg (M_WARN, "PORT SHARE: sendmsg failed (unable to communicate with background process)");
+ msg (M_WARN|M_ERRNO_SOCK, "PORT SHARE: sendmsg failed -- unable to communicate with background process (%d,%d,%d,%d)",
+ sd, sd_send, sd_null[0], sd_null[1]
+ );
close_socket_if_defined (sd_null[0]);
close_socket_if_defined (sd_null[1]);
@@ -261,6 +264,12 @@ proxy_entry_mark_for_close (struct proxy_connection *pc, struct event_set *es)
pc->buffer_initial = false;
pc->rwflags = 0;
pc->defined = false;
+ if (pc->jfn)
+ {
+ unlink (pc->jfn);
+ free (pc->jfn);
+ pc->jfn = NULL;
+ }
if (cp && cp->defined && cp->counterpart == pc)
proxy_entry_mark_for_close (cp, es);
}
@@ -297,6 +306,48 @@ proxy_list_housekeeping (struct proxy_connection **list)
}
/*
+ * Record IP/port of client in filesystem, so that server receiving
+ * the proxy can determine true client origin.
+ */
+static void
+journal_add (const char *journal_dir, struct proxy_connection *pc, struct proxy_connection *cp)
+{
+ struct gc_arena gc = gc_new ();
+ struct openvpn_sockaddr from, to;
+ socklen_t slen, dlen;
+ int fnlen;
+ char *jfn;
+ int fd;
+
+ slen = sizeof(from.sa);
+ dlen = sizeof(to.sa);
+ if (!getpeername (pc->sd, (struct sockaddr *) &from.sa, &slen)
+ && !getsockname (cp->sd, (struct sockaddr *) &to.sa, &dlen))
+ {
+ const char *f = print_sockaddr_ex (&from, ":", PS_SHOW_PORT, &gc);
+ const char *t = print_sockaddr_ex (&to, ":", PS_SHOW_PORT, &gc);
+ fnlen = strlen(journal_dir) + strlen(t) + 2;
+ jfn = (char *) malloc(fnlen);
+ check_malloc_return (jfn);
+ openvpn_snprintf (jfn, fnlen, "%s/%s", journal_dir, t);
+ dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: client origin %s -> %s", jfn, f);
+ fd = open (jfn, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP);
+ if (fd != -1)
+ {
+ write(fd, f, strlen(f));
+ close (fd);
+ cp->jfn = jfn;
+ }
+ else
+ {
+ msg (M_WARN|M_ERRNO, "PORT SHARE: unable to write journal file in %s", jfn);
+ free (jfn);
+ }
+ }
+ gc_free (&gc);
+}
+
+/*
* Cleanup function, on proxy process exit.
*/
static void
@@ -349,7 +400,8 @@ proxy_entry_new (struct proxy_connection **list,
const in_addr_t server_addr,
const int server_port,
const socket_descriptor_t sd_client,
- struct buffer *initial_data)
+ struct buffer *initial_data,
+ const char *journal_dir)
{
struct openvpn_sockaddr osaddr;
socket_descriptor_t sd_server;
@@ -359,7 +411,11 @@ proxy_entry_new (struct proxy_connection **list,
/* connect to port share server */
sock_addr_set (&osaddr, server_addr, server_port);
- sd_server = create_socket_tcp ();
+ if ((sd_server = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
+ {
+ msg (M_WARN|M_ERRNO_SOCK, "PORT SHARE PROXY: cannot create socket");
+ return false;
+ }
status = openvpn_connect (sd_server, &osaddr, 5, NULL);
if (status)
{
@@ -396,6 +452,10 @@ proxy_entry_new (struct proxy_connection **list,
/* add to list */
*list = pc;
+
+ /* add journal entry */
+ if (journal_dir)
+ journal_add (journal_dir, pc, cp);
dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: NEW CONNECTION [c=%d s=%d]", (int)sd_client, (int)sd_server);
@@ -417,9 +477,14 @@ control_message_from_parent (const socket_descriptor_t sd_control,
struct proxy_connection **list,
struct event_set *es,
const in_addr_t server_addr,
- const int server_port)
+ const int server_port,
+ const int max_initial_buf,
+ const char *journal_dir)
{
- struct buffer buf = alloc_buf (PROXY_CONNECTION_BUFFER_SIZE);
+ /* this buffer needs to be large enough to handle the largest buffer
+ that might be returned by the link_socket_read call in read_incoming_link. */
+ struct buffer buf = alloc_buf (max_initial_buf);
+
struct msghdr mesg;
struct cmsghdr* h;
struct iovec iov[2];
@@ -455,7 +520,7 @@ control_message_from_parent (const socket_descriptor_t sd_control,
|| h->cmsg_level != SOL_SOCKET
|| h->cmsg_type != SCM_RIGHTS )
{
- ret = false;
+ msg (M_WARN, "PORT SHARE PROXY: received unknown message");
}
else
{
@@ -470,7 +535,8 @@ control_message_from_parent (const socket_descriptor_t sd_control,
server_addr,
server_port,
received_fd,
- &buf))
+ &buf,
+ journal_dir))
{
CLEAR (buf); /* we gave the buffer to proxy_entry_new */
}
@@ -505,6 +571,7 @@ proxy_connection_io_recv (struct proxy_connection *pc)
{
if (!status)
return IOSTAT_READ_ERROR;
+ dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: read[%d] %d", (int)pc->sd, status);
pc->buf.len = status;
}
return IOSTAT_GOOD;
@@ -532,7 +599,7 @@ proxy_connection_io_send (struct proxy_connection *pc, int *bytes_sent)
}
else
{
- /*dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: wrote[%d] %d", (int)sd, status);*/
+ dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: wrote[%d] %d", (int)sd, status);
pc->buf.len = 0;
pc->buf.offset = 0;
}
@@ -615,6 +682,8 @@ proxy_connection_io_dispatch (struct proxy_connection *pc,
int rwflags_pc = pc->rwflags;
int rwflags_cp = cp->rwflags;
+ ASSERT(pc->defined && cp->defined && cp->counterpart == pc);
+
if (rwflags & EVENT_READ)
{
const int status = proxy_connection_io_xfer (pc, max_transfer_per_iteration);
@@ -641,7 +710,11 @@ proxy_connection_io_dispatch (struct proxy_connection *pc,
* This is the main function for the port share proxy background process.
*/
static void
-port_share_proxy (const in_addr_t hostaddr, const int port, const socket_descriptor_t sd_control)
+port_share_proxy (const in_addr_t hostaddr,
+ const int port,
+ const socket_descriptor_t sd_control,
+ const int max_initial_buf,
+ const char *journal_dir)
{
if (send_control (sd_control, RESPONSE_INIT_SUCCEEDED) >= 0)
{
@@ -675,7 +748,7 @@ port_share_proxy (const in_addr_t hostaddr, const int port, const socket_descrip
const struct event_set_return *e = &esr[i];
if (e->arg == sd_control_marker)
{
- if (!control_message_from_parent (sd_control, &list, es, hostaddr, port))
+ if (!control_message_from_parent (sd_control, &list, es, hostaddr, port, max_initial_buf, journal_dir))
goto done;
}
else
@@ -701,7 +774,7 @@ port_share_proxy (const in_addr_t hostaddr, const int port, const socket_descrip
proxy_list_close (&list);
event_free (es);
}
- msg (D_PS_PROXY, "PORT SHARE PROXY: proxy exiting");
+ msg (M_INFO, "PORT SHARE PROXY: proxy exiting");
}
/*
@@ -709,7 +782,10 @@ port_share_proxy (const in_addr_t hostaddr, const int port, const socket_descrip
* share proxy.
*/
struct port_share *
-port_share_open (const char *host, const int port)
+port_share_open (const char *host,
+ const int port,
+ const int max_initial_buf,
+ const char *journal_dir)
{
pid_t pid;
socket_descriptor_t fd[2];
@@ -717,6 +793,8 @@ port_share_open (const char *host, const int port)
struct port_share *ps;
ALLOC_OBJ_CLEAR (ps, struct port_share);
+ ps->foreground_fd = -1;
+ ps->background_pid = -1;
/*
* Get host's IP address
@@ -758,9 +836,17 @@ port_share_open (const char *host, const int port)
status = recv_control (fd[0]);
if (status == RESPONSE_INIT_SUCCEEDED)
{
+ /* note that this will cause possible EAGAIN when writing to
+ control socket if proxy process is backlogged */
+ set_nonblock (fd[0]);
+
ps->foreground_fd = fd[0];
return ps;
}
+ else
+ {
+ msg (M_SOCKERR, "PORT SHARE: unexpected init recv_control status=%d", status);
+ }
}
else
{
@@ -787,7 +873,7 @@ port_share_open (const char *host, const int port)
prng_init (NULL, 0);
/* execute the event loop */
- port_share_proxy (hostaddr, port, fd[1]);
+ port_share_proxy (hostaddr, port, fd[1], max_initial_buf, journal_dir);
openvpn_close_socket (fd[1]);
@@ -872,7 +958,9 @@ void
port_share_redirect (struct port_share *ps, const struct buffer *head, socket_descriptor_t sd)
{
if (ps)
- port_share_sendmsg (ps->foreground_fd, COMMAND_REDIRECT, head, sd);
+ {
+ port_share_sendmsg (ps->foreground_fd, COMMAND_REDIRECT, head, sd);
+ }
}
#endif
diff --git a/ps.h b/ps.h
index d488a72..4280635 100644
--- a/ps.h
+++ b/ps.h
@@ -44,7 +44,9 @@ struct port_share {
extern struct port_share *port_share;
struct port_share *port_share_open (const char *host,
- const int port);
+ const int port,
+ const int max_initial_buf,
+ const char *journal_dir);
void port_share_close (struct port_share *ps);
void port_share_abort (struct port_share *ps);
diff --git a/push.c b/push.c
index 1fd8bea..a8ce356 100644
--- a/push.c
+++ b/push.c
@@ -42,7 +42,7 @@
void
receive_auth_failed (struct context *c, const struct buffer *buffer)
{
- msg (M_VERB0, "AUTH: Received AUTH_FAILED control message");
+ msg (M_VERB0, "AUTH: Received control message: %s", BSTR(buffer));
connection_list_set_no_advance(&c->options);
if (c->options.pull)
{
@@ -52,7 +52,7 @@ receive_auth_failed (struct context *c, const struct buffer *buffer)
c->sig->signal_received = SIGTERM; /* SOFT-SIGTERM -- Auth failure error */
break;
case AR_INTERACT:
- ssl_purge_auth ();
+ ssl_purge_auth (false);
case AR_NOINTERACT:
c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- Auth failure error */
break;
@@ -87,13 +87,48 @@ receive_auth_failed (struct context *c, const struct buffer *buffer)
* Act on received restart message from server
*/
void
-server_pushed_restart (struct context *c, const struct buffer *buffer)
+server_pushed_signal (struct context *c, const struct buffer *buffer, const bool restart, const int adv)
{
if (c->options.pull)
{
- msg (D_STREAM_ERRORS, "Connection reset command was pushed by server");
- c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- server-pushed connection reset */
- c->sig->signal_text = "server-pushed-connection-reset";
+ struct buffer buf = *buffer;
+ const char *m = "";
+ if (buf_advance (&buf, adv) && buf_read_u8 (&buf) == ',' && BLEN (&buf))
+ m = BSTR (&buf);
+
+ /* preserve cached passwords? */
+ {
+ bool purge = true;
+
+ if (m[0] == '[')
+ {
+ int i;
+ for (i = 1; m[i] != '\0' && m[i] != ']'; ++i)
+ {
+ if (m[i] == 'P')
+ purge = false;
+ }
+ }
+ if (purge)
+ ssl_purge_auth (true);
+ }
+
+ if (restart)
+ {
+ msg (D_STREAM_ERRORS, "Connection reset command was pushed by server ('%s')", m);
+ c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- server-pushed connection reset */
+ c->sig->signal_text = "server-pushed-connection-reset";
+ }
+ else
+ {
+ msg (D_STREAM_ERRORS, "Halt command was pushed by server ('%s')", m);
+ c->sig->signal_received = SIGTERM; /* SOFT-SIGTERM -- server-pushed halt */
+ c->sig->signal_text = "server-pushed-halt";
+ }
+#ifdef ENABLE_MANAGEMENT
+ if (management)
+ management_notify (management, "info", c->sig->signal_text, m);
+#endif
}
}
@@ -130,10 +165,10 @@ send_auth_failed (struct context *c, const char *client_reason)
* Send restart message from server to client.
*/
void
-send_restart (struct context *c)
+send_restart (struct context *c, const char *kill_msg)
{
schedule_exit (c, c->options.scheduled_exit_interval, SIGTERM);
- send_control_channel_string (c, "RESTART", D_PUSH);
+ send_control_channel_string (c, kill_msg ? kill_msg : "RESTART", D_PUSH);
}
#endif
@@ -149,7 +184,7 @@ incoming_push_message (struct context *c, const struct buffer *buffer)
unsigned int option_types_found = 0;
int status;
- msg (D_PUSH, "PUSH: Received control message: '%s'", BSTR (buffer));
+ msg (D_PUSH, "PUSH: Received control message: '%s'", sanitize_control_message(BSTR(buffer), &gc));
status = process_incoming_push_msg (c,
buffer,
@@ -158,7 +193,7 @@ incoming_push_message (struct context *c, const struct buffer *buffer)
&option_types_found);
if (status == PUSH_MSG_ERROR)
- msg (D_PUSH_ERRORS, "WARNING: Received bad push/pull message: %s", BSTR (buffer));
+ msg (D_PUSH_ERRORS, "WARNING: Received bad push/pull message: %s", sanitize_control_message(BSTR(buffer), &gc));
else if (status == PUSH_MSG_REPLY || status == PUSH_MSG_CONTINUATION)
{
if (status == PUSH_MSG_REPLY)
@@ -172,7 +207,18 @@ incoming_push_message (struct context *c, const struct buffer *buffer)
bool
send_push_request (struct context *c)
{
- return send_control_channel_string (c, "PUSH_REQUEST", D_PUSH);
+ const int max_push_requests = c->options.handshake_window / PUSH_REQUEST_INTERVAL;
+ if (++c->c2.n_sent_push_requests <= max_push_requests)
+ {
+ return send_control_channel_string (c, "PUSH_REQUEST", D_PUSH);
+ }
+ else
+ {
+ msg (D_STREAM_ERRORS, "No reply from server after sending %d push requests", max_push_requests);
+ c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- server-pushed connection reset */
+ c->sig->signal_text = "no-push-reply";
+ return false;
+ }
}
#if P2MP_SERVER
@@ -185,7 +231,7 @@ send_push_reply (struct context *c)
struct push_entry *e = c->options.push_list.head;
bool multi_push = false;
static char cmd[] = "PUSH_REPLY";
- const int extra = 64; /* extra space for possible trailing ifconfig and push-continuation */
+ const int extra = 84; /* extra space for possible trailing ifconfig and push-continuation */
const int safe_cap = BCAP (&buf) - extra;
bool push_sent = false;
@@ -238,9 +284,16 @@ send_push_reply (struct context *c)
}
if (c->c2.push_ifconfig_defined && c->c2.push_ifconfig_local && c->c2.push_ifconfig_remote_netmask)
- buf_printf (&buf, ",ifconfig %s %s",
- print_in_addr_t (c->c2.push_ifconfig_local, 0, &gc),
- print_in_addr_t (c->c2.push_ifconfig_remote_netmask, 0, &gc));
+ {
+ in_addr_t ifconfig_local = c->c2.push_ifconfig_local;
+#ifdef ENABLE_CLIENT_NAT
+ if (c->c2.push_ifconfig_local_alias)
+ ifconfig_local = c->c2.push_ifconfig_local_alias;
+#endif
+ buf_printf (&buf, ",ifconfig %s %s",
+ print_in_addr_t (ifconfig_local, 0, &gc),
+ print_in_addr_t (c->c2.push_ifconfig_remote_netmask, 0, &gc));
+ }
if (multi_push)
buf_printf (&buf, ",push-continuation 1");
@@ -359,8 +412,18 @@ process_incoming_push_msg (struct context *c,
}
else if (!c->c2.push_reply_deferred && c->c2.context_auth == CAS_SUCCEEDED)
{
- if (send_push_reply (c))
- ret = PUSH_MSG_REQUEST;
+ if (c->c2.sent_push_reply)
+ {
+ ret = PUSH_MSG_ALREADY_REPLIED;
+ }
+ else
+ {
+ if (send_push_reply (c))
+ {
+ ret = PUSH_MSG_REQUEST;
+ c->c2.sent_push_reply = true;
+ }
+ }
}
else
{
diff --git a/push.h b/push.h
index 089cf45..8c3f157 100644
--- a/push.h
+++ b/push.h
@@ -35,6 +35,7 @@
#define PUSH_MSG_REQUEST_DEFERRED 3
#define PUSH_MSG_AUTH_FAILURE 4
#define PUSH_MSG_CONTINUATION 5
+#define PUSH_MSG_ALREADY_REPLIED 6
void incoming_push_message (struct context *c,
const struct buffer *buffer);
@@ -49,7 +50,7 @@ bool send_push_request (struct context *c);
void receive_auth_failed (struct context *c, const struct buffer *buffer);
-void server_pushed_restart (struct context *c, const struct buffer *buffer);
+void server_pushed_signal (struct context *c, const struct buffer *buffer, const bool restart, const int adv);
#if P2MP_SERVER
@@ -66,7 +67,7 @@ void remove_iroutes_from_push_route_list (struct options *o);
void send_auth_failed (struct context *c, const char *client_reason);
-void send_restart (struct context *c);
+void send_restart (struct context *c, const char *kill_msg);
#endif
#endif
diff --git a/route.c b/route.c
index 0c40253..cb685c3 100644
--- a/route.c
+++ b/route.c
@@ -2324,70 +2324,26 @@ get_default_gateway (in_addr_t *ret, in_addr_t *netmask)
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
+#include <net/route.h>
+#include <net/if_dl.h>
-/* all of this is taken from <net/route.h> in Darwin */
-#define RTA_DST 0x1
-#define RTA_GATEWAY 0x2
-#define RTA_NETMASK 0x4
-
-#define RTM_GET 0x4
-#define RTM_VERSION 5
-
-#define RTF_UP 0x1
-#define RTF_GATEWAY 0x2
-
-/*
- * These numbers are used by reliable protocols for determining
- * retransmission behavior and are included in the routing structure.
- */
-struct rt_metrics {
- u_long rmx_locks; /* Kernel must leave these values alone */
- u_long rmx_mtu; /* MTU for this path */
- u_long rmx_hopcount; /* max hops expected */
- u_long rmx_expire; /* lifetime for route, e.g. redirect */
- u_long rmx_recvpipe; /* inbound delay-bandwidth product */
- u_long rmx_sendpipe; /* outbound delay-bandwidth product */
- u_long rmx_ssthresh; /* outbound gateway buffer limit */
- u_long rmx_rtt; /* estimated round trip time */
- u_long rmx_rttvar; /* estimated rtt variance */
- u_long rmx_pksent; /* packets sent using this route */
- u_long rmx_filler[4]; /* will be used for T/TCP later */
-};
-
-/*
- * Structures for routing messages.
- */
-struct rt_msghdr {
- u_short rtm_msglen; /* to skip over non-understood messages */
- u_char rtm_version; /* future binary compatibility */
- u_char rtm_type; /* message type */
- u_short rtm_index; /* index for associated ifp */
- int rtm_flags; /* flags, incl. kern & message, e.g. DONE */
- int rtm_addrs; /* bitmask identifying sockaddrs in msg */
- pid_t rtm_pid; /* identify sender */
- int rtm_seq; /* for sender to identify action */
- int rtm_errno; /* why failed */
- int rtm_use; /* from rtentry */
- u_long rtm_inits; /* which metrics we are initializing */
- struct rt_metrics rtm_rmx; /* metrics themselves */
-};
-
-struct {
+struct rtmsg {
struct rt_msghdr m_rtm;
char m_space[512];
-} m_rtmsg;
+};
#define ROUNDUP(a) \
- ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(uint32_t) - 1))) : sizeof(uint32_t))
-bool
-get_default_gateway (in_addr_t *ret, in_addr_t *netmask)
+static bool
+get_default_gateway_ex (in_addr_t *ret, in_addr_t *netmask, char **ifname)
{
struct gc_arena gc = gc_new ();
+ struct rtmsg m_rtmsg;
int s, seq, l, pid, rtm_addrs, i;
struct sockaddr so_dst, so_mask;
char *cp = m_rtmsg.m_space;
- struct sockaddr *gate = NULL, *sa;
+ struct sockaddr *gate = NULL, *ifp = NULL, *sa;
struct rt_msghdr *rtm_aux;
#define NEXTADDR(w, u) \
@@ -2401,8 +2357,9 @@ get_default_gateway (in_addr_t *ret, in_addr_t *netmask)
pid = getpid();
seq = 0;
- rtm_addrs = RTA_DST | RTA_NETMASK;
+ rtm_addrs = RTA_DST | RTA_NETMASK | RTA_IFP;
+ bzero(&m_rtmsg, sizeof(m_rtmsg));
bzero(&so_dst, sizeof(so_dst));
bzero(&so_mask, sizeof(so_mask));
bzero(&rtm, sizeof(struct rt_msghdr));
@@ -2444,11 +2401,16 @@ get_default_gateway (in_addr_t *ret, in_addr_t *netmask)
cp = ((char *)(rtm_aux + 1));
if (rtm_aux->rtm_addrs) {
for (i = 1; i; i <<= 1)
- if (i & rtm_aux->rtm_addrs) {
- sa = (struct sockaddr *)cp;
- if (i == RTA_GATEWAY )
- gate = sa;
- ADVANCE(cp, sa);
+ {
+ if (i & rtm_aux->rtm_addrs)
+ {
+ sa = (struct sockaddr *)cp;
+ if (i == RTA_GATEWAY )
+ gate = sa;
+ else if (i == RTA_IFP)
+ ifp = sa;
+ ADVANCE(cp, sa);
+ }
}
}
else
@@ -2471,6 +2433,16 @@ get_default_gateway (in_addr_t *ret, in_addr_t *netmask)
*netmask = 0xFFFFFF00; // FIXME -- get the real netmask of the adapter containing the default gateway
}
+ if (ifp && ifname)
+ {
+ struct sockaddr_dl *adl = (struct sockaddr_dl *) ifp;
+ char *name = malloc(adl->sdl_nlen+1);
+ check_malloc_return(name);
+ memcpy(name, adl->sdl_data, adl->sdl_nlen);
+ name[adl->sdl_nlen] = '\0';
+ *ifname = name;
+ }
+
gc_free (&gc);
return true;
}
@@ -2481,6 +2453,12 @@ get_default_gateway (in_addr_t *ret, in_addr_t *netmask)
}
}
+bool
+get_default_gateway (in_addr_t *ret, in_addr_t *netmask)
+{
+ return get_default_gateway_ex(ret, netmask, NULL);
+}
+
#elif defined(TARGET_OPENBSD) || defined(TARGET_NETBSD)
#include <sys/types.h>
@@ -2896,6 +2874,73 @@ get_default_gateway_mac_addr (unsigned char *macaddr)
return false;
}
+#elif defined(TARGET_DARWIN)
+
+bool
+get_default_gateway_mac_addr (unsigned char *macaddr)
+{
+# define max(a,b) ((a) > (b) ? (a) : (b))
+ struct gc_arena gc = gc_new ();
+ struct ifconf ifc;
+ struct ifreq *ifr;
+ char *buffer, *cp;
+ bool status = false;
+ in_addr_t gw = 0;
+ char *ifname = NULL;
+ int sockfd = -1;
+ const int bufsize = 4096;
+
+ if (!get_default_gateway_ex (&gw, NULL, &ifname)) /* get interface name of default gateway */
+ {
+ msg (M_WARN, "GDGMA: get_default_gateway_ex failed");
+ goto done;
+ }
+
+ if (!ifname)
+ {
+ msg (M_WARN, "GDGMA: cannot get default gateway ifname");
+ goto done;
+ }
+
+ buffer = (char *) gc_malloc (bufsize, true, &gc);
+
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd < 0)
+ {
+ msg (M_WARN, "GDGMA: socket failed");
+ goto done;
+ }
+
+ ifc.ifc_len = bufsize;
+ ifc.ifc_buf = buffer;
+
+ if (ioctl(sockfd, SIOCGIFCONF, (char *)&ifc) < 0)
+ {
+ msg (M_WARN, "GDGMA: ioctl failed");
+ goto done;
+ }
+
+ for (cp = buffer; cp <= buffer + bufsize - sizeof(struct ifreq); )
+ {
+ ifr = (struct ifreq *)cp;
+ if (ifr->ifr_addr.sa_family == AF_LINK && !strncmp(ifr->ifr_name, ifname, IFNAMSIZ))
+ {
+ struct sockaddr_dl *sdl = (struct sockaddr_dl *)&ifr->ifr_addr;
+ memcpy(macaddr, LLADDR(sdl), 6);
+ status = true;
+ }
+ cp += sizeof(ifr->ifr_name) + max(sizeof(ifr->ifr_addr), ifr->ifr_addr.sa_len);
+ }
+
+ done:
+ if (sockfd >= 0)
+ close (sockfd);
+ free (ifname);
+ gc_free (&gc);
+ return status;
+# undef max
+}
+
#else
bool
diff --git a/service-win32/openvpnserv.c b/service-win32/openvpnserv.c
index 5b0eb6e..0993064 100755
--- a/service-win32/openvpnserv.c
+++ b/service-win32/openvpnserv.c
@@ -133,15 +133,15 @@ static HANDLE exit_event = NULL;
int openvpn_snprintf(char *str, size_t size, const char *format, ...)
{
va_list arglist;
- int ret = 0;
+ int len = -1;
if (size > 0)
{
va_start (arglist, format);
- ret = vsnprintf (str, size, format, arglist);
+ len = vsnprintf (str, size, format, arglist);
va_end (arglist);
str[size - 1] = 0;
}
- return ret;
+ return (len >= 0 && len < size);
}
diff --git a/socket.c b/socket.c
index 59821fd..6b855c0 100644
--- a/socket.c
+++ b/socket.c
@@ -2014,21 +2014,25 @@ link_socket_init_phase2 (struct link_socket *sock,
#endif
/* print local address */
- if (sock->inetd)
- msg (M_INFO, "%s link local: [inetd]", proto2ascii (sock->info.proto, true));
- else
- msg (M_INFO, "%s link local%s: %s",
+ {
+ const int msglevel = (sock->mode == LS_MODE_TCP_ACCEPT_FROM) ? D_INIT_MEDIUM : M_INFO;
+
+ if (sock->inetd)
+ msg (msglevel, "%s link local: [inetd]", proto2ascii (sock->info.proto, true));
+ else
+ msg (msglevel, "%s link local%s: %s",
+ proto2ascii (sock->info.proto, true),
+ (sock->bind_local ? " (bound)" : ""),
+ print_sockaddr_ex (&sock->info.lsa->local, ":", sock->bind_local ? PS_SHOW_PORT : 0, &gc));
+
+ /* print active remote address */
+ msg (msglevel, "%s link remote: %s",
proto2ascii (sock->info.proto, true),
- (sock->bind_local ? " (bound)" : ""),
- print_sockaddr_ex (&sock->info.lsa->local, ":", sock->bind_local ? PS_SHOW_PORT : 0, &gc));
-
- /* print active remote address */
- msg (M_INFO, "%s link remote: %s",
- proto2ascii (sock->info.proto, true),
- print_link_socket_actual_ex (&sock->info.lsa->actual,
- ":",
- PS_SHOW_PORT_IF_DEFINED,
- &gc));
+ print_link_socket_actual_ex (&sock->info.lsa->actual,
+ ":",
+ PS_SHOW_PORT_IF_DEFINED,
+ &gc));
+ }
done:
if (sig_save && signal_received)
@@ -2057,7 +2061,7 @@ link_socket_close (struct link_socket *sock)
#endif
if (!gremlin)
{
- msg (D_CLOSE, "TCP/UDP: Closing socket");
+ msg (D_LOW, "TCP/UDP: Closing socket");
if (openvpn_close_socket (sock->sd))
msg (M_WARN | M_ERRNO_SOCK, "TCP/UDP: Close Socket failed");
}
diff --git a/ssl.c b/ssl.c
index bd7ac94..a2b5b18 100644
--- a/ssl.c
+++ b/ssl.c
@@ -321,15 +321,27 @@ ssl_set_auth_nocache (void)
}
/*
+ * Set an authentication token
+ */
+void
+ssl_set_auth_token (const char *token)
+{
+ set_auth_token (&auth_user_pass, token);
+}
+
+/*
* Forget private key password AND auth-user-pass username/password.
*/
void
-ssl_purge_auth (void)
+ssl_purge_auth (const bool auth_user_pass_only)
{
+ if (!auth_user_pass_only)
+ {
#ifdef USE_PKCS11
- pkcs11_logout ();
+ pkcs11_logout ();
#endif
- purge_user_pass (&passbuf, true);
+ purge_user_pass (&passbuf, true);
+ }
purge_user_pass (&auth_user_pass, true);
#ifdef ENABLE_CLIENT_CR
ssl_purge_auth_challenge();
@@ -573,6 +585,96 @@ bool extract_x509_extension(X509 *cert, char *fieldname, char *out, int size)
sk_GENERAL_NAME_free (extensions);
}
return retval;
+#ifdef ENABLE_X509_TRACK
+/*
+ * setenv_x509_track function -- save X509 fields to environment,
+ * using the naming convention:
+ *
+ * X509_{cert_depth}_{name}={value}
+ *
+ * This function differs from setenv_x509 below in the following ways:
+ *
+ * (1) Only explicitly named attributes in xt are saved, per usage
+ * of --x509-track program options.
+ * (2) Only the level 0 cert info is saved unless the XT_FULL_CHAIN
+ * flag is set in xt->flags (corresponds with prepending a '+'
+ * to the name when specified by --x509-track program option).
+ * (3) This function supports both X509 subject name fields as
+ * well as X509 V3 extensions.
+ */
+
+/* worker method for setenv_x509_track */
+static void
+do_setenv_x509 (struct env_set *es, const char *name, char *value, int depth)
+{
+ char *name_expand;
+ size_t name_expand_size;
+
+ string_mod (value, CC_ANY, CC_CRLF, '?');
+ msg (D_X509_ATTR, "X509 ATTRIBUTE name='%s' value='%s' depth=%d", name, value, depth);
+ name_expand_size = 64 + strlen (name);
+ name_expand = (char *) malloc (name_expand_size);
+ check_malloc_return (name_expand);
+ openvpn_snprintf (name_expand, name_expand_size, "X509_%d_%s", depth, name);
+ setenv_str (es, name_expand, value);
+ free (name_expand);
+}
+
+static void
+setenv_x509_track (const struct x509_track *xt, struct env_set *es, const int depth, X509 *x509)
+{
+ X509_NAME *x509_name = X509_get_subject_name (x509);
+ const char nullc = '\0';
+ int i;
+
+ while (xt)
+ {
+ if (depth == 0 || (xt->flags & XT_FULL_CHAIN))
+ {
+ i = X509_NAME_get_index_by_NID(x509_name, xt->nid, -1);
+ if (i >= 0)
+ {
+ X509_NAME_ENTRY *ent = X509_NAME_get_entry(x509_name, i);
+ if (ent)
+ {
+ ASN1_STRING *val = X509_NAME_ENTRY_get_data (ent);
+ unsigned char *buf;
+ buf = (unsigned char *)1; /* bug in OpenSSL 0.9.6b ASN1_STRING_to_UTF8 requires this workaround */
+ if (ASN1_STRING_to_UTF8 (&buf, val) > 0)
+ {
+ do_setenv_x509(es, xt->name, (char *)buf, depth);
+ OPENSSL_free (buf);
+ }
+ }
+ }
+ else
+ {
+ i = X509_get_ext_by_NID(x509, xt->nid, -1);
+ if (i >= 0)
+ {
+ X509_EXTENSION *ext = X509_get_ext(x509, i);
+ if (ext)
+ {
+ BIO *bio = BIO_new(BIO_s_mem());
+ if (bio)
+ {
+ if (X509V3_EXT_print(bio, ext, 0, 0))
+ {
+ if (BIO_write(bio, &nullc, 1) == 1)
+ {
+ char *str;
+ BIO_get_mem_data(bio, &str);
+ do_setenv_x509(es, xt->name, str, depth);
+ }
+ }
+ BIO_free(bio);
+ }
+ }
+ }
+ }
+ }
+ xt = xt->next;
+ }
}
#endif
@@ -834,6 +936,7 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
const struct tls_options *opt;
const int max_depth = MAX_CERT_DEPTH;
struct argv argv = argv_new ();
+ char *serial = NULL;
/* get the tls_session pointer */
ssl = X509_STORE_CTX_get_ex_data (ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
@@ -854,7 +957,12 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
}
/* Save X509 fields in environment */
- setenv_x509 (opt->es, ctx->error_depth, X509_get_subject_name (ctx->current_cert));
+#ifdef ENABLE_X509_TRACK
+ if (opt->x509_track)
+ setenv_x509_track (opt->x509_track, opt->es, ctx->error_depth, ctx->current_cert);
+ else
+#endif
+ setenv_x509 (opt->es, ctx->error_depth, X509_get_subject_name (ctx->current_cert));
/* enforce character class restrictions in X509 name */
string_mod_sslname (subject, X509_NAME_CHAR_CLASS, opt->ssl_flags);
@@ -918,6 +1026,16 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
goto err; /* Reject connection */
}
+ /* verify level 1 cert, i.e. the CA that signed our leaf cert */
+ if (ctx->error_depth == 1 && opt->verify_hash)
+ {
+ if (memcmp (ctx->current_cert->sha1_hash, opt->verify_hash, SHA_DIGEST_LENGTH))
+ {
+ msg (D_TLS_ERRORS, "TLS Error: level-1 certificate hash verification failed");
+ goto err;
+ }
+ }
+
/* save common name in session object */
if (ctx->error_depth == 0)
set_common_name (session, common_name);
@@ -943,32 +1061,17 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
setenv_str (opt->es, envname, common_name);
#endif
- /* export serial number as environmental variable */
+ /* export serial number as environmental variable,
+ use bignum in case serial number is large */
{
- BIO *bio = NULL;
- char serial[100];
- int n1, n2;
-
- CLEAR (serial);
- if ((bio = BIO_new (BIO_s_mem ())) == NULL)
- {
- msg (M_WARN, "CALLBACK: Cannot create BIO (for tls_serial_%d)", ctx->error_depth);
- }
- else
- {
- /* "prints" the serial number onto the BIO and read it back */
- if ( ! ( ( (n1 = i2a_ASN1_INTEGER(bio, X509_get_serialNumber (ctx->current_cert))) >= 0 ) &&
- ( (n2 = BIO_read (bio, serial, sizeof (serial)-1)) >= 0 ) &&
- ( n1 == n2 ) ) )
- {
- msg (M_WARN, "CALLBACK: Error reading/writing BIO (for tls_serial_%d)", ctx->error_depth);
- CLEAR (serial); /* empty string */
- }
-
- openvpn_snprintf (envname, sizeof(envname), "tls_serial_%d", ctx->error_depth);
- setenv_str (opt->es, envname, serial);
- BIO_free(bio);
- }
+ ASN1_INTEGER *asn1_i;
+ BIGNUM *bignum;
+ asn1_i = X509_get_serialNumber(ctx->current_cert);
+ bignum = ASN1_INTEGER_to_BN(asn1_i, NULL);
+ serial = BN_bn2dec(bignum);
+ openvpn_snprintf (envname, sizeof(envname), "tls_serial_%d", ctx->error_depth);
+ setenv_str (opt->es, envname, serial);
+ BN_free(bignum);
}
/* export current untrusted IP */
@@ -1108,67 +1211,89 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
/* check peer cert against CRL */
if (opt->crl_file)
{
- X509_CRL *crl=NULL;
- X509_REVOKED *revoked;
- BIO *in=NULL;
- int n,i,retval = 0;
+ if (opt->ssl_flags & SSLF_CRL_VERIFY_DIR)
+ {
+ char fn[256];
+ int fd;
+ if (!openvpn_snprintf(fn, sizeof(fn), "%s%c%s", opt->crl_file, OS_SPECIFIC_DIRSEP, serial))
+ {
+ msg (D_HANDSHAKE, "VERIFY CRL: filename overflow");
+ goto err;
+ }
+ fd = open (fn, O_RDONLY);
+ if (fd >= 0)
+ {
+ msg (D_HANDSHAKE, "VERIFY CRL: certificate serial number %s is revoked", serial);
+ close(fd);
+ goto err;
+ }
+ }
+ else
+ {
+ X509_CRL *crl=NULL;
+ X509_REVOKED *revoked;
+ BIO *in=NULL;
+ int n,i,retval = 0;
- in=BIO_new(BIO_s_file());
+ in=BIO_new(BIO_s_file());
- if (in == NULL) {
- msg (M_ERR, "CRL: BIO err");
- goto end;
- }
- if (BIO_read_filename(in, opt->crl_file) <= 0) {
- msg (M_ERR, "CRL: cannot read: %s", opt->crl_file);
- goto end;
- }
- crl=PEM_read_bio_X509_CRL(in,NULL,NULL,NULL);
- if (crl == NULL) {
- msg (M_ERR, "CRL: cannot read CRL from file %s", opt->crl_file);
- goto end;
- }
+ if (in == NULL) {
+ msg (M_ERR, "CRL: BIO err");
+ goto end;
+ }
+ if (BIO_read_filename(in, opt->crl_file) <= 0) {
+ msg (M_ERR, "CRL: cannot read: %s", opt->crl_file);
+ goto end;
+ }
+ crl=PEM_read_bio_X509_CRL(in,NULL,NULL,NULL);
+ if (crl == NULL) {
+ msg (M_ERR, "CRL: cannot read CRL from file %s", opt->crl_file);
+ goto end;
+ }
- if (X509_NAME_cmp(X509_CRL_get_issuer(crl), X509_get_issuer_name(ctx->current_cert)) != 0) {
- msg (M_WARN, "CRL: CRL %s is from a different issuer than the issuer of certificate %s", opt->crl_file, subject);
- retval = 1;
- goto end;
- }
+ if (X509_NAME_cmp(X509_CRL_get_issuer(crl), X509_get_issuer_name(ctx->current_cert)) != 0) {
+ msg (M_WARN, "CRL: CRL %s is from a different issuer than the issuer of certificate %s", opt->crl_file, subject);
+ retval = 1;
+ goto end;
+ }
- n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
+ n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
- for (i = 0; i < n; i++) {
- revoked = (X509_REVOKED *)sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
- if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(ctx->current_cert)) == 0) {
- msg (D_HANDSHAKE, "CRL CHECK FAILED: %s is REVOKED",subject);
- goto end;
- }
- }
+ for (i = 0; i < n; i++) {
+ revoked = (X509_REVOKED *)sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
+ if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(ctx->current_cert)) == 0) {
+ msg (D_HANDSHAKE, "CRL CHECK FAILED: %s is REVOKED",subject);
+ goto end;
+ }
+ }
- retval = 1;
- msg (D_HANDSHAKE, "CRL CHECK OK: %s",subject);
+ retval = 1;
+ msg (D_HANDSHAKE, "CRL CHECK OK: %s",subject);
- end:
+ end:
- BIO_free(in);
- if (crl)
- X509_CRL_free (crl);
- if (!retval)
- goto err;
+ BIO_free(in);
+ if (crl)
+ X509_CRL_free (crl);
+ if (!retval)
+ goto err;
+ }
}
msg (D_HANDSHAKE, "VERIFY OK: depth=%d, %s", ctx->error_depth, subject);
-
session->verified = true;
+
+ done:
OPENSSL_free (subject);
+ if (serial)
+ OPENSSL_free(serial);
argv_reset (&argv);
- return 1; /* Accept connection */
+ return (session->verified == true) ? 1 : 0;
err:
ERR_clear_error ();
- OPENSSL_free (subject);
- argv_reset (&argv);
- return 0; /* Reject connection */
+ session->verified = false;
+ goto done;
}
void
@@ -1232,6 +1357,31 @@ tls_lock_username (struct tls_multi *multi, const char *username)
return true;
}
+#ifdef ENABLE_X509_TRACK
+
+void
+x509_track_add (const struct x509_track **ll_head, const char *name, int msglevel, struct gc_arena *gc)
+{
+ struct x509_track *xt;
+ ALLOC_OBJ_CLEAR_GC (xt, struct x509_track, gc);
+ if (*name == '+')
+ {
+ xt->flags |= XT_FULL_CHAIN;
+ ++name;
+ }
+ xt->name = name;
+ xt->nid = OBJ_txt2nid(name);
+ if (xt->nid != NID_undef)
+ {
+ xt->next = *ll_head;
+ *ll_head = xt;
+ }
+ else
+ msg(msglevel, "x509_track: no such attribute '%s'", name);
+}
+
+#endif
+
#ifdef ENABLE_DEF_AUTH
/* key_state_test_auth_control_file return values,
NOTE: acf_merge indexing depends on these values */
@@ -1978,7 +2128,7 @@ init_ssl (const struct options *options)
{
if (!X509_STORE_add_cert(ctx->cert_store,sk_X509_value(ca, i)))
msg (M_SSLERR, "Cannot add certificate to certificate chain (X509_STORE_add_cert)");
- if (!SSL_CTX_add_client_CA(ctx, sk_X509_value(ca, i)))
+ if (options->tls_server && !SSL_CTX_add_client_CA(ctx, sk_X509_value(ca, i)))
msg (M_SSLERR, "Cannot add certificate to client CA list (SSL_CTX_add_client_CA)");
}
}
@@ -2090,11 +2240,11 @@ init_ssl (const struct options *options)
#endif
{
/* Load CA file for verifying peer supplied certificate */
- status = SSL_CTX_load_verify_locations (ctx, options->ca_file, options->ca_path);
+ status = SSL_CTX_load_verify_locations (ctx, options->ca_file, NULL);
}
if (!status)
- msg (M_SSLERR, "Cannot load CA certificate file %s path %s (SSL_CTX_load_verify_locations)", options->ca_file, options->ca_path);
+ msg (M_SSLERR, "Cannot load CA certificate file %s path %s (SSL_CTX_load_verify_locations)", np(options->ca_file), np(options->ca_path));
/* Set a store for certs (CA & CRL) with a lookup on the "capath" hash directory */
if (options->ca_path) {
@@ -2118,7 +2268,7 @@ init_ssl (const struct options *options)
}
/* Load names of CAs from file and use it as a client CA list */
- if (options->ca_file) {
+ if (options->ca_file && options->tls_server) {
STACK_OF(X509_NAME) *cert_names = NULL;
#if ENABLE_INLINE_FILES
if (!strcmp (options->ca_file, INLINE_FILE_TAG) && options->ca_file_inline)
@@ -2132,7 +2282,7 @@ init_ssl (const struct options *options)
}
if (!cert_names)
msg (M_SSLERR, "Cannot load CA certificate file %s (SSL_load_client_CA_file)", options->ca_file);
- SSL_CTX_set_client_CA_list (ctx, cert_names);
+ SSL_CTX_set_client_CA_list (ctx, cert_names);
}
}
@@ -2143,6 +2293,37 @@ init_ssl (const struct options *options)
msg (M_SSLERR, "Cannot load certificate chain file %s (SSL_use_certificate_chain_file)", options->cert_file);
}
+ /* Load extra certificates that are part of our own certificate
+ chain but shouldn't be included in the verify chain */
+ if (options->extra_certs_file || options->extra_certs_file_inline)
+ {
+ BIO *bio;
+ X509 *cert;
+#if ENABLE_INLINE_FILES
+ if (!strcmp (options->extra_certs_file, INLINE_FILE_TAG) && options->extra_certs_file_inline)
+ {
+ bio = BIO_new_mem_buf ((char *)options->extra_certs_file_inline, -1);
+ }
+ else
+#endif
+ {
+ bio = BIO_new(BIO_s_file());
+ if (BIO_read_filename(bio, options->extra_certs_file) <= 0)
+ msg (M_SSLERR, "Cannot load extra-certs file: %s", options->extra_certs_file);
+ }
+ for (;;)
+ {
+ cert = NULL;
+ if (!PEM_read_bio_X509 (bio, &cert, 0, NULL)) /* takes ownership of cert */
+ break;
+ if (!cert)
+ msg (M_SSLERR, "Error reading extra-certs certificate");
+ if (SSL_CTX_add_extra_chain_cert(ctx, cert) != 1)
+ msg (M_SSLERR, "Error adding extra-certs certificate");
+ }
+ BIO_free (bio);
+ }
+
/* Require peer certificate verification */
#if P2MP_SERVER
if (options->ssl_flags & SSLF_CLIENT_CERT_NOT_REQUIRED)
@@ -2674,8 +2855,10 @@ key_state_init (struct tls_session *session, struct key_state *ks)
/* init packet ID tracker */
packet_id_init (&ks->packet_id,
+ session->opt->tcp_mode,
session->opt->replay_window,
- session->opt->replay_time);
+ session->opt->replay_time,
+ "SSL", ks->key_id);
#ifdef MANAGEMENT_DEF_AUTH
ks->mda_key_id = session->opt->mda_context->mda_key_id_counter++;
@@ -2779,8 +2962,10 @@ tls_session_init (struct tls_multi *multi, struct tls_session *session)
/* initialize packet ID replay window for --tls-auth */
packet_id_init (session->tls_auth.packet_id,
+ session->opt->tcp_mode,
session->opt->replay_window,
- session->opt->replay_time);
+ session->opt->replay_time,
+ "TLS_AUTH", session->key_id);
/* load most recent packet-id to replay protect on --tls-auth */
packet_id_persist_load_obj (session->tls_auth.pid_persist, session->tls_auth.packet_id);
@@ -3851,6 +4036,11 @@ push_peer_info(struct buffer *buf, struct tls_session *session)
buf_printf (&out, "IV_HWADDR=%s\n", format_hex_ex (macaddr, 6, 0, 1, ":", &gc));
}
+ /* push LZO status */
+#ifdef LZO_STUB
+ buf_printf (&out, "IV_LZO_STUB=1\n");
+#endif
+
/* push env vars that begin with UV_ */
for (e=es->list; e != NULL; e=e->next)
{
diff --git a/ssl.h b/ssl.h
index 8464492..790a57e 100644
--- a/ssl.h
+++ b/ssl.h
@@ -404,6 +404,21 @@ struct key_state
#endif
};
+#ifdef ENABLE_X509_TRACK
+
+struct x509_track
+{
+ const struct x509_track *next;
+ const char *name;
+# define XT_FULL_CHAIN (1<<0)
+ unsigned int flags;
+ int nid;
+};
+
+void x509_track_add (const struct x509_track **ll_head, const char *name, int msglevel, struct gc_arena *gc);
+
+#endif
+
/*
* Our const options, obtained directly or derived from
* command line options.
@@ -454,6 +469,7 @@ struct tls_options
int ns_cert_type;
unsigned remote_cert_ku[MAX_PARMS];
const char *remote_cert_eku;
+ uint8_t *verify_hash;
/* allow openvpn config info to be
passed over control channel */
@@ -465,6 +481,7 @@ struct tls_options
int replay_window; /* --replay-window parm */
int replay_time; /* --replay-window parm */
+ bool tcp_mode;
/* packet authentication for TLS handshake */
struct crypto_options tls_auth;
@@ -491,12 +508,17 @@ struct tls_options
# define SSLF_AUTH_USER_PASS_OPTIONAL (1<<2)
# define SSLF_NO_NAME_REMAPPING (1<<3)
# define SSLF_OPT_VERIFY (1<<4)
+# define SSLF_CRL_VERIFY_DIR (1<<5)
unsigned int ssl_flags;
#ifdef MANAGEMENT_DEF_AUTH
struct man_def_auth_context *mda_context;
#endif
+#ifdef ENABLE_X509_TRACK
+ const struct x509_track *x509_track;
+#endif
+
/* --gremlin bits */
int gremlin;
};
@@ -703,7 +725,8 @@ void pem_password_setup (const char *auth_file);
int pem_password_callback (char *buf, int size, int rwflag, void *u);
void auth_user_pass_setup (const char *auth_file);
void ssl_set_auth_nocache (void);
-void ssl_purge_auth (void);
+void ssl_set_auth_token (const char *token);
+void ssl_purge_auth (const bool auth_user_pass_only);
#ifdef ENABLE_CLIENT_CR
diff --git a/syshead.h b/syshead.h
index 9dbb501..5b86cad 100644
--- a/syshead.h
+++ b/syshead.h
@@ -599,6 +599,13 @@ socket_defined (const socket_descriptor_t sd)
#endif
/*
+ * Enable x509-track feature?
+ */
+#if defined(USE_CRYPTO) && defined(USE_SSL)
+#define ENABLE_X509_TRACK
+#endif
+
+/*
* Is poll available on this platform?
*/
#if defined(HAVE_POLL) && defined(HAVE_SYS_POLL_H)
@@ -676,4 +683,21 @@ socket_defined (const socket_descriptor_t sd)
*/
#define ENABLE_PUSH_PEER_INFO
+/*
+ * Do we support internal client-side NAT?
+ */
+#define ENABLE_CLIENT_NAT
+
+/*
+ * Support LZO as a stub in client? (LZO lib not included, but we
+ * we still support LZO protocol changes that allow us to
+ * communicate with an LZO-enabled server)
+ */
+#ifdef LZO_STUB
+#undef USE_LZO
+#undef LZO_VERSION_NUM
+#define USE_LZO 1
+#define LZO_VERSION_NUM "STUB"
+#endif
+
#endif
diff --git a/tun.c b/tun.c
index d844260..5a41cdf 100644
--- a/tun.c
+++ b/tun.c
@@ -991,7 +991,6 @@ do_ifconfig (struct tuntap *tt,
tt->did_ifconfig = true;
#elif defined(TARGET_DARWIN)
-
/*
* Darwin (i.e. Mac OS X) seems to exhibit similar behaviour to OpenBSD...
*/
@@ -1037,6 +1036,7 @@ do_ifconfig (struct tuntap *tt,
tun_mtu
);
}
+
argv_msg (M_INFO, &argv);
openvpn_execve_check (&argv, es, S_FATAL, "Mac OS X ifconfig failed");
tt->did_ifconfig = true;
diff --git a/win/sign.py b/win/sign.py
index 67d1cbc..a80bf98 100644
--- a/win/sign.py
+++ b/win/sign.py
@@ -4,16 +4,20 @@ from wb import config, choose_arch, home_fn
if 'SIGNTOOL' in config:
sys.path.append(home_fn(config['SIGNTOOL']))
-def main(conf, arch):
+def main(conf, arch, tap_dir):
from signtool import SignTool
- st = SignTool(conf)
+ st = SignTool(conf, tap_dir)
for x64 in choose_arch(arch):
st.sign_verify(x64=x64)
# if we are run directly, and not loaded as a module
if __name__ == "__main__":
if len(sys.argv) >= 2:
- main(config, sys.argv[1])
+ if len(sys.argv) >= 3:
+ tap_dir = home_fn(sys.argv[2])
+ else:
+ tap_dir = None
+ main(config, sys.argv[1], tap_dir)
else:
- print "usage: sign <x64|x86|all>"
+ print "usage: sign <x64|x86|all> [tap-dir]"
sys.exit(2)