aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am2
-rw-r--r--README.ipv681
-rw-r--r--TODO.ipv630
-rw-r--r--acinclude.m46
-rw-r--r--buffer.c27
-rw-r--r--buffer.h7
-rw-r--r--clinat.c263
-rw-r--r--clinat.h65
-rw-r--r--common.h5
-rw-r--r--configure.ac31
-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.c113
-rwxr-xr-x[-rw-r--r--]install-win32/makeopenvpn62
-rw-r--r--lzo.c32
-rw-r--r--lzo.h29
-rw-r--r--manage.c85
-rw-r--r--manage.h14
-rw-r--r--misc.c44
-rw-r--r--misc.h4
-rw-r--r--mroute.c30
-rw-r--r--mtcp.c5
-rw-r--r--multi.c48
-rw-r--r--occ.c2
-rw-r--r--openvpn-plugin.h8
-rw-r--r--openvpn.8116
-rw-r--r--openvpn.h5
-rw-r--r--options.c243
-rw-r--r--options.h26
-rw-r--r--packet_id.c150
-rw-r--r--packet_id.h7
-rw-r--r--plugin/auth-pam/auth-pam.c6
-rw-r--r--proto.h37
-rw-r--r--ps.c124
-rw-r--r--ps.h4
-rw-r--r--push.c97
-rw-r--r--push.h5
-rw-r--r--route.c187
-rwxr-xr-xservice-win32/openvpnserv.c6
-rw-r--r--socket.c1096
-rw-r--r--socket.h245
-rw-r--r--socks.c18
-rw-r--r--ssl.c353
-rw-r--r--ssl.h25
-rw-r--r--syshead.h29
-rw-r--r--tun.c4
-rw-r--r--win/sign.py12
-rw-r--r--win32.h5
51 files changed, 3273 insertions, 606 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/README.ipv6 b/README.ipv6
new file mode 100644
index 0000000..4295f85
--- /dev/null
+++ b/README.ipv6
@@ -0,0 +1,81 @@
+[ Last updated: 25-Mar-2011. ]
+
+OpenVPN-2.1 over UDP6/TCP6 README for ipv6-0.4.x patch releases:
+( --udp6 and --tcp6-{client,server} )
+
+* Availability
+ Source code under GPLv2 from http://github.com/jjo/openvpn-ipv6
+
+ Distro ready repos/packages:
+ o Debian sid official repo, by Alberto Gonzalez Iniesta,
+ starting from openvpn_2.1~rc20-2
+ o Gentoo official portage tree, by Marcel Pennewiss:
+ - https://bugs.gentoo.org/show_bug.cgi?id=287896
+ o Ubuntu package, by Bernhard Schmidt:
+ - https://launchpad.net/~berni/+archive/ipv6/+packages
+ o Freetz.org, milestone freetz-1.2
+ - http://trac.freetz.org/milestone/freetz-1.2
+
+* Status:
+ o OK:
+ - upd6,tcp6: GNU/Linux, win32, openbsd-4.7, freebsd-8.1
+ - udp4->upd6,tcp4->tcp6 (ipv4/6 mapped): GNU/Linux
+ (gives a warning on local!=remote proto matching)
+ o NOT:
+ - win32: tcp4->tcp6 (ipv4/6 mapped) fails w/connection refused
+ o NOT tested:
+ - mgmt console
+
+* Build setup:
+ ./configure --enable-ipv6 (by default)
+
+* Usage:
+ For IPv6 just specify "-p upd6" an proper IPv6 hostnames, adapting the example
+ from man page ...
+
+ On may:
+ openvpn --proto udp6 --remote <june_IPv6_addr> --dev tun1 \
+ --ifconfig 10.4.0.1 10.4.0.2 --verb 5 --secret key
+
+ On june:
+ openvpn --proto udp6 --remote <may_IPv6_addr> --dev tun1 \
+ --ifconfig 10.4.0.2 10.4.0.1 --verb 5 --secret key
+
+ Same for --proto tcp6-client, tcp6-server.
+
+* Main code changes summary:
+ - socket.h: New struct openvpn_sockaddr type that holds sockaddrs and pktinfo,
+ (here I omitted #ifdef USE_PF_xxxx, see socket.h )
+
+ struct openvpn_sockaddr {
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in in;
+ struct sockaddr_in6 in6;
+ } addr;
+ };
+
+ struct link_socket_addr
+ {
+ struct openvpn_sockaddr local;
+ struct openvpn_sockaddr remote;
+ struct openvpn_sockaddr actual;
+ };
+
+ PRO: allows simple type overloading: local.addr.sa, local.addr.in, local.addr.in6 ... etc
+ (also local.pi.in and local.pi.in6)
+
+ - several function prototypes moved from sockaddr_in to openvpn_sockaddr
+ - several new sockaddr functions needed to "generalize" AF_xxxx operations:
+ addr_copy(), addr_zero(), ...etc
+ proto_is_udp(), proto_is_dgram(), proto_is_net()
+
+* TODO: See TODO.ipv6
+
+--
+JuanJo Ciarlante jjo () google () com ............................
+: :
+. Linux IP Aliasing author .
+. Modular algo (AES et all) support for FreeSWAN/OpenSWAN author .
+. OpenVPN over IPv6 support .
+:...... plus other scattered free software bits in the wild ...:
diff --git a/TODO.ipv6 b/TODO.ipv6
new file mode 100644
index 0000000..966af2d
--- /dev/null
+++ b/TODO.ipv6
@@ -0,0 +1,30 @@
+[ Last updated: 11-Nov-2009. ]
+
+* All platforms:
+ o mgmt console: as currently passes straight in_addr_t bits around
+
+ o make possible to get AF from getaddrinfo() answer, ie allow openvpn to
+ use ipv4/6 if DNS returns A/AAAA without specifying protocol.
+ Hard: requires deep changes in initialization/calling logic
+
+ o use AI_PASSIVE
+
+ o the getaddr()/getaddr6() interface is not prepared for handling socktype
+ "tagging", currently I abuse the sockflags bits for getting the ai_socktype
+ downstream.
+
+ o implement comparison for mapped addesses: server in dual stack
+ listening IPv6 must permit incoming streams from allowed IPv4 peer,
+ currently you need to pass eg: --remote ffff::1.2.3.4
+
+ o do something with multi mode learn routes, for now just ignoring
+ ipv6 addresses seems the most sensible thing to do, because there's
+ no support for intra-tunnel ipv6 stuff.
+
+* win32:
+ o find out about mapped addresses, as I can't make it work
+ with bound at ::1 and connect to 127.0.0.1
+
+* N/A:
+ o this is ipv6 *endpoint* support, so don't expect "ifconfig6"-like
+ support in this patch
diff --git a/acinclude.m4 b/acinclude.m4
index 185907f..acfc01d 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -123,5 +123,9 @@ AC_DEFUN([TYPE_SOCKLEN_T],
AC_DEFINE_UNQUOTED(socklen_t, $curl_cv_socklen_t_equiv,
[type to use in place of socklen_t if not defined])],
[#include <sys/types.h>
-#include <sys/socket.h>])
+#ifdef WIN32
+#include <ws2tcpip.h>
+#else
+#include <sys/socket.h>
+#endif])
])
diff --git a/buffer.c b/buffer.c
index 3fa0cd3..cff2616 100644
--- a/buffer.c
+++ b/buffer.c
@@ -214,26 +214,45 @@ buf_printf (struct buffer *buf, const char *format, ...)
return ret;
}
+bool
+buf_puts(struct buffer *buf, const char *str)
+{
+ int ret = false;
+ uint8_t *ptr = BEND (buf);
+ int cap = buf_forward_capacity (buf);
+ if (cap > 0)
+ {
+ strncpynt ((char *)ptr,str, cap);
+ *(buf->data + buf->capacity - 1) = 0; /* windows vsnprintf needs this */
+ buf->len += (int) strlen ((char *)ptr);
+ ret = true;
+ }
+ return ret;
+}
+
+
/*
* 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 3b24d09..1fe3c08 100644
--- a/buffer.h
+++ b/buffer.h
@@ -277,9 +277,14 @@ bool buf_printf (struct buffer *buf, const char *format, ...)
;
/*
+ * puts append to a buffer with overflow check
+ */
+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 e0847bc..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"],
@@ -146,6 +152,12 @@ AC_ARG_ENABLE(multihome,
[MULTIHOME="yes"]
)
+AC_ARG_ENABLE(ipv6,
+ [ --disable-ipv6 Disable UDP/IPv6 support],
+ [PF_INET6="$enableval"],
+ [PF_INET6="yes"]
+)
+
AC_ARG_ENABLE(port-share,
[ --disable-port-share Disable TCP server port-share support (--port-share)],
[PORT_SHARE="$enableval"],
@@ -566,6 +578,16 @@ LDFLAGS="$LDFLAGS -Wl,--fatal-warnings"
AC_CHECK_FUNC(epoll_create, AC_DEFINE(HAVE_EPOLL_CREATE, 1, [epoll_create function is defined]))
LDFLAGS="$OLDLDFLAGS"
+dnl ipv6 support
+if test "$PF_INET6" = "yes"; then
+ AC_CHECKING([for struct sockaddr_in6 for IPv6 support])
+ AC_CHECK_TYPE(
+ [struct sockaddr_in6],
+ [AC_DEFINE(USE_PF_INET6, 1, [struct sockaddr_in6 is needed for IPv6 peer support])],
+ [],
+ [#include "syshead.h"])
+fi
+
dnl
dnl check for valgrind tool
dnl
@@ -646,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,
@@ -676,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 6ca3c55..4a16fba 100644
--- a/init.c
+++ b/init.c
@@ -96,19 +96,12 @@ update_options_ce_post (struct options *options)
*/
if (options->pull
&& options->ping_rec_timeout_action == PING_UNDEF
- && options->ce.proto == PROTO_UDPv4)
+ && proto_is_dgram(options->ce.proto))
{
options->ping_rec_timeout = PRE_PULL_INITIAL_PING_RESTART;
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 ();
@@ -1191,7 +1207,12 @@ initialization_sequence_completed (struct context *c, const unsigned int flags)
const char *detail = "SUCCESS";
if (c->c1.tuntap)
tun_local = c->c1.tuntap->local;
- tun_remote = htonl (c->c1.link_socket_addr.actual.dest.sa.sin_addr.s_addr);
+ /* TODO(jjo): for ipv6 this will convert some 32bits in the ipv6 addr
+ * to a meaningless ipv4 address.
+ * In any case, is somewhat inconsistent to send local tunnel
+ * addr with remote _endpoint_ addr (?)
+ */
+ tun_remote = htonl (c->c1.link_socket_addr.actual.dest.addr.in4.sin_addr.s_addr);
if (flags & ISC_ERRORS)
detail = "ERROR";
management_set_state (management,
@@ -1218,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))
{
@@ -1436,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 */
@@ -1581,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
@@ -1595,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;
}
@@ -1620,7 +1650,7 @@ do_deferred_options (struct context *c, const unsigned int found)
#ifdef ENABLE_OCC
if (found & OPT_P_EXPLICIT_NOTIFY)
{
- if (c->options.ce.proto != PROTO_UDPv4 && c->options.explicit_exit_notification)
+ if (!proto_is_udp(c->options.ce.proto) && c->options.explicit_exit_notification)
{
msg (D_PUSH, "OPTIONS IMPORT: --explicit-exit-notify can only be used with --proto udp");
c->options.explicit_exit_notification = 0;
@@ -1715,13 +1745,22 @@ socket_restart_pause (struct context *c)
switch (c->options.ce.proto)
{
case PROTO_UDPv4:
+#ifdef USE_PF_INET6
+ case PROTO_UDPv6:
+#endif
if (proxy)
sec = c->options.ce.connect_retry_seconds;
break;
case PROTO_TCPv4_SERVER:
+#ifdef USE_PF_INET6
+ case PROTO_TCPv6_SERVER:
+#endif
sec = 1;
break;
case PROTO_TCPv4_CLIENT:
+#ifdef USE_PF_INET6
+ case PROTO_TCPv6_CLIENT:
+#endif
sec = c->options.ce.connect_retry_seconds;
break;
}
@@ -1735,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;
@@ -1854,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;
@@ -1951,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;
@@ -2003,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");
}
}
@@ -2056,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;
@@ -2080,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
@@ -2099,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)
{
@@ -2861,7 +2911,7 @@ do_setup_fast_io (struct context *c)
#ifdef WIN32
msg (M_INFO, "NOTE: --fast-io is disabled since we are running on Windows");
#else
- if (c->options.ce.proto != PROTO_UDPv4)
+ if (!proto_is_udp(c->options.ce.proto))
msg (M_INFO, "NOTE: --fast-io is disabled since we are not using UDP");
else
{
@@ -3137,7 +3187,11 @@ init_instance (struct context *c, const struct env_set *env, const unsigned int
/* link_socket_mode allows CM_CHILD_TCP
instances to inherit acceptable fds
from a top-level parent */
- if (c->options.ce.proto == PROTO_TCPv4_SERVER)
+ if (c->options.ce.proto == PROTO_TCPv4_SERVER
+#ifdef USE_PF_INET6
+ || c->options.ce.proto == PROTO_TCPv6_SERVER
+#endif
+ )
{
if (c->mode == CM_TOP)
link_socket_mode = LS_MODE_TCP_LISTEN;
@@ -3412,17 +3466,8 @@ inherit_context_child (struct context *dest,
{
CLEAR (*dest);
- switch (src->options.ce.proto)
- {
- case PROTO_UDPv4:
- dest->mode = CM_CHILD_UDP;
- break;
- case PROTO_TCPv4_SERVER:
- dest->mode = CM_CHILD_TCP;
- break;
- default:
- ASSERT (0);
- }
+ /* proto_is_dgram will ASSERT(0) if proto is invalid */
+ dest->mode = proto_is_dgram(src->options.ce.proto)? CM_CHILD_UDP : CM_CHILD_TCP;
dest->gc = gc_new ();
@@ -3528,7 +3573,7 @@ inherit_context_top (struct context *dest,
dest->c2.es_owned = false;
dest->c2.event_set = NULL;
- if (src->options.ce.proto == PROTO_UDPv4)
+ if (proto_is_dgram(src->options.ce.proto))
do_event_set_init (dest, false);
}
diff --git a/install-win32/makeopenvpn b/install-win32/makeopenvpn
index c1a805d..ced2a54 100644..100755
--- a/install-win32/makeopenvpn
+++ b/install-win32/makeopenvpn
@@ -2,35 +2,65 @@
H=`pwd`
-# get version.nsi definitions
-. autodefs/defs.sh
+case "`uname -o 2>/dev/null`" in
+ *inux)
-if gcc --version &>/dev/null && [ -d "$OPENSSL_DIR" ] && [ -d "$LZO_DIR" ] && [ -d "$PKCS11_HELPER_DIR" ]; then
+ # cross-compiling, make dude's life easier
+ XAUTOCONF="--host=i586-mingw32msvc --build=i386-linux $XAUTOCONF"
+ export CC=i586-mingw32msvc-gcc
+ export CXXCPP=i586-mingw32msvc-cpp
+ export CXX=i586-mingw32msvc-g++
+ # this requires the human to setup these environ vars:
+ # OPENSSL_DIR LZO_DIR PKCS11_HELPER_DIR
+ OPENSSL_INC_DIR=$OPENSSL_DIR/include
+ OPENSSL_LIB_DIR=$OPENSSL_DIR/out
+ LZO_INC_DIR=$LZO_DIR/include
+ LZO_LIB_DIR=$LZO_DIR/src/.libs
+ PKCS11_INC_DIR=$PKCS11_HELPER_DIR/include/pkcs11-helper-1.0/
+ PKCS11_LIB_DIR=$PKCS11_HELPER_DIR/lib/.libs/
+ ;;
+ *)
+ # get version.nsi definitions
+ . autodefs/defs.sh
+ XAUTOCONF=""
+ # default configuration creates relative-path environ vars:
+ OPENSSL_INC_DIR=$H/$OPENSSL_DIR/include
+ OPENSSL_LIB_DIR=$H/$OPENSSL_DIR/out
+ LZO_INC_DIR=$H/$LZO_DIR/include
+ LZO_LIB_DIR=$H/$LZO_DIR
+ PKCS11_INC_DIR=$H/$PKCS11_HELPER_DIR/usr/local/include
+ PKCS11_LIB_DIR=$H/$PKCS11_HELPER_DIR/usr/local/lib
+ ;;
+esac
+if $CC --version &>/dev/null && [ -d "$OPENSSL_DIR" ] && [ -d "$LZO_DIR" ] && [ -d "$PKCS11_HELPER_DIR" ]; then
# build OpenVPN binary
if ! [ -f Makefile ]; then
autoreconf -i -v \
- && ./configure \
+ && ./configure $XAUTOCONF \
--enable-strict \
--prefix=$H/windest \
MAN2HTML=true \
- --with-ssl-headers=$H/$OPENSSL_DIR/include \
- --with-ssl-lib=$H/$OPENSSL_DIR/out \
- --with-lzo-headers=$H/$LZO_DIR/include \
- --with-lzo-lib=$H/$LZO_DIR \
- --with-pkcs11-helper-headers=$H/$PKCS11_HELPER_DIR/usr/local/include \
- --with-pkcs11-helper-lib=$H/$PKCS11_HELPER_DIR/usr/local/lib
+ --with-ssl-headers=$OPENSSL_INC_DIR \
+ --with-ssl-lib=$OPENSSL_LIB_DIR \
+ --with-lzo-headers=$LZO_INC_DIR \
+ --with-lzo-lib=$LZO_LIB_DIR \
+ --with-pkcs11-helper-headers=$PKCS11_INC_DIR \
+ --with-pkcs11-helper-lib=$PKCS11_LIB_DIR \
+ || exit 1
fi
make -j $MAKE_JOBS && make install
+ if [ -n "$GENOOUT" ];then
# copy OpenVPN and service executables to GENOUT/bin
- mkdir -p $GENOUT/bin &>/dev/null
- cp windest/sbin/openvpn.exe $GENOUT/bin
- cp windest/sbin/openvpnserv.exe $GENOUT/bin
- if [ -z "$NO_STRIP" ]; then
- strip $GENOUT/bin/openvpn.exe
- strip $GENOUT/bin/openvpnserv.exe
+ mkdir -p $GENOUT/bin &>/dev/null
+ cp windest/sbin/openvpn.exe $GENOUT/bin
+ cp windest/sbin/openvpnserv.exe $GENOUT/bin
+ if [ -z "$NO_STRIP" ]; then
+ strip $GENOUT/bin/openvpn.exe
+ strip $GENOUT/bin/openvpnserv.exe
+ fi
fi
else
echo DID NOT BUILD openvpn.exe and openvpnserv.exe because one or more of gcc, OPENSSL_DIR, LZO_DIR, or PKCS11_HELPER_DIR directories were missing
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 310a70e..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);
@@ -1999,9 +1999,9 @@ man_settings_init (struct man_settings *ms,
/*
* Initialize socket address
*/
- ms->local.sa.sin_family = AF_INET;
- ms->local.sa.sin_addr.s_addr = 0;
- ms->local.sa.sin_port = htons (port);
+ ms->local.addr.in4.sin_family = AF_INET;
+ ms->local.addr.in4.sin_addr.s_addr = 0;
+ ms->local.addr.in4.sin_port = htons (port);
/*
* Run management over tunnel, or
@@ -2013,7 +2013,7 @@ man_settings_init (struct man_settings *ms,
}
else
{
- ms->local.sa.sin_addr.s_addr = getaddr
+ ms->local.addr.in4.sin_addr.s_addr = getaddr
(GETADDR_RESOLVE|GETADDR_WARN_ON_SIGNAL|GETADDR_FATAL, addr, 0, NULL, NULL);
}
}
@@ -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;
}
}
@@ -2472,7 +2493,7 @@ management_post_tunnel_open (struct management *man, const in_addr_t tun_local_i
&& man->connection.state == MS_INITIAL)
{
/* listen on our local TUN/TAP IP address */
- man->settings.local.sa.sin_addr.s_addr = htonl (tun_local_ip);
+ man->settings.local.addr.in4.sin_addr.s_addr = htonl (tun_local_ip);
man_connection_init (man);
}
@@ -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/mroute.c b/mroute.c
index 3182f65..c8e979a 100644
--- a/mroute.c
+++ b/mroute.c
@@ -266,25 +266,47 @@ bool mroute_extract_openvpn_sockaddr (struct mroute_addr *addr,
const struct openvpn_sockaddr *osaddr,
bool use_port)
{
- if (osaddr->sa.sin_family == AF_INET)
+ switch (osaddr->addr.sa.sa_family)
+ {
+ case AF_INET:
{
if (use_port)
{
addr->type = MR_ADDR_IPV4 | MR_WITH_PORT;
addr->netbits = 0;
addr->len = 6;
- memcpy (addr->addr, &osaddr->sa.sin_addr.s_addr, 4);
- memcpy (addr->addr + 4, &osaddr->sa.sin_port, 2);
+ memcpy (addr->addr, &osaddr->addr.in4.sin_addr.s_addr, 4);
+ memcpy (addr->addr + 4, &osaddr->addr.in4.sin_port, 2);
}
else
{
addr->type = MR_ADDR_IPV4;
addr->netbits = 0;
addr->len = 4;
- memcpy (addr->addr, &osaddr->sa.sin_addr.s_addr, 4);
+ memcpy (addr->addr, &osaddr->addr.in4.sin_addr.s_addr, 4);
}
return true;
}
+#ifdef USE_PF_INET6
+ case AF_INET6:
+ if (use_port)
+ {
+ addr->type = MR_ADDR_IPV6 | MR_WITH_PORT;
+ addr->netbits = 0;
+ addr->len = 18;
+ memcpy (addr->addr, &osaddr->addr.in6.sin6_addr, 16);
+ memcpy (addr->addr + 16, &osaddr->addr.in6.sin6_port, 2);
+ }
+ else
+ {
+ addr->type = MR_ADDR_IPV6;
+ addr->netbits = 0;
+ addr->len = 16;
+ memcpy (addr->addr, &osaddr->addr.in6.sin6_addr, 16);
+ }
+ return true;
+#endif
+ }
return false;
}
diff --git a/mtcp.c b/mtcp.c
index 314aa44..ade2cfb 100644
--- a/mtcp.c
+++ b/mtcp.c
@@ -150,6 +150,11 @@ multi_tcp_instance_specific_init (struct multi_context *m, struct multi_instance
ASSERT (mi->context.c2.link_socket);
ASSERT (mi->context.c2.link_socket->info.lsa);
ASSERT (mi->context.c2.link_socket->mode == LS_MODE_TCP_ACCEPT_FROM);
+ ASSERT (mi->context.c2.link_socket->info.lsa->actual.dest.addr.sa.sa_family == AF_INET
+#ifdef USE_PF_INET6
+ || mi->context.c2.link_socket->info.lsa->actual.dest.addr.sa.sa_family == AF_INET6
+#endif
+ );
if (!mroute_extract_openvpn_sockaddr (&mi->real, &mi->context.c2.link_socket->info.lsa->actual.dest, true))
{
msg (D_MULTI_ERRORS, "MULTI TCP: TCP client address is undefined");
diff --git a/multi.c b/multi.c
index 6c57423..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);
@@ -1055,8 +1055,8 @@ multi_learn_in_addr_t (struct multi_context *m,
struct mroute_addr addr;
CLEAR (remote_si);
- remote_si.sa.sin_family = AF_INET;
- remote_si.sa.sin_addr.s_addr = htonl (a);
+ remote_si.addr.in4.sin_family = AF_INET;
+ remote_si.addr.in4.sin_addr.s_addr = htonl (a);
ASSERT (mroute_extract_openvpn_sockaddr (&addr, &remote_si, false));
if (netbits >= 0)
@@ -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
@@ -2605,9 +2608,9 @@ management_callback_kill_by_addr (void *arg, const in_addr_t addr, const int por
int count = 0;
CLEAR (saddr);
- saddr.sa.sin_family = AF_INET;
- saddr.sa.sin_addr.s_addr = htonl (addr);
- saddr.sa.sin_port = htons (port);
+ saddr.addr.in4.sin_family = AF_INET;
+ saddr.addr.in4.sin_addr.s_addr = htonl (addr);
+ saddr.addr.in4.sin_port = htons (port);
if (mroute_extract_openvpn_sockaddr (&maddr, &saddr, true))
{
hash_iterator_init (m->iter, &hi);
@@ -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
@@ -2784,16 +2788,24 @@ tunnel_server (struct context *top)
{
ASSERT (top->options.mode == MODE_SERVER);
- switch (top->options.ce.proto) {
- case PROTO_UDPv4:
- tunnel_server_udp (top);
- break;
- case PROTO_TCPv4_SERVER:
- tunnel_server_tcp (top);
- break;
- default:
- ASSERT (0);
- }
+#ifdef USE_PF_INET6
+ if (proto_is_dgram(top->options.ce.proto))
+ tunnel_server_udp(top);
+ else
+ tunnel_server_tcp(top);
+#else
+ switch (top->options.ce.proto)
+ {
+ case PROTO_UDPv4:
+ tunnel_server_udp (top);
+ break;
+ case PROTO_TCPv4_SERVER:
+ tunnel_server_tcp (top);
+ break;
+ default:
+ ASSERT (0);
+ }
+#endif
}
#else
diff --git a/occ.c b/occ.c
index b84b18d..bcf91cc 100644
--- a/occ.c
+++ b/occ.c
@@ -369,7 +369,7 @@ process_received_occ_msg (struct context *c)
c->c2.max_send_size_remote,
c->c2.max_recv_size_local);
if (!c->options.fragment
- && c->options.ce.proto == PROTO_UDPv4
+ && (proto_is_dgram(c->options.ce.proto))
&& c->c2.max_send_size_local > TUN_MTU_MIN
&& (c->c2.max_recv_size_remote < c->c2.max_send_size_local
|| c->c2.max_recv_size_local < c->c2.max_send_size_remote))
diff --git a/openvpn-plugin.h b/openvpn-plugin.h
index 5c82daf..24aa36c 100644
--- a/openvpn-plugin.h
+++ b/openvpn-plugin.h
@@ -203,8 +203,8 @@ struct openvpn_plugin_string_list
struct openvpn_plugin_args_open_in
{
const int type_mask;
- const char const **argv;
- const char const **envp;
+ const char ** const argv;
+ const char ** const envp;
};
@@ -267,8 +267,8 @@ struct openvpn_plugin_args_open_return
struct openvpn_plugin_args_func_in
{
const int type;
- const char const **argv;
- const char const **envp;
+ const char ** const argv;
+ const char ** const envp;
openvpn_plugin_handle_t handle;
void *per_client_context;
int current_cert_depth;
diff --git a/openvpn.8 b/openvpn.8
index 11fd5ad..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:
.\"*********************************************************
@@ -5503,13 +5601,16 @@ or
script execution.
.\"*********************************************************
.TP
-.B trusted_ip
+.B trusted_ip (or trusted_ip6)
Actual IP address of connecting client or peer which has been authenticated.
Set prior to execution of
.B \-\-ipchange, \-\-client-connect,
and
.B \-\-client-disconnect
scripts.
+If using ipv6 endpoints (udp6, tcp6),
+.B trusted_ip6
+will be set instead.
.\"*********************************************************
.TP
.B trusted_port
@@ -5521,7 +5622,7 @@ and
scripts.
.\"*********************************************************
.TP
-.B untrusted_ip
+.B untrusted_ip (or untrusted_ip6)
Actual IP address of connecting client or peer which has not been authenticated
yet. Sometimes used to
.B nmap
@@ -5533,6 +5634,9 @@ Set prior to execution of
and
.B \-\-auth-user-pass-verify
scripts.
+If using ipv6 endpoints (udp6, tcp6),
+.B untrusted_ip6
+will be set instead.
.\"*********************************************************
.TP
.B untrusted_port
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 824492e..169a8e8 100644
--- a/options.c
+++ b/options.c
@@ -80,6 +80,12 @@ const char title_string[] =
#ifdef ENABLE_EUREPHIA
" [eurephia]"
#endif
+#if ENABLE_IP_PKTINFO
+ " [MH]"
+#endif
+#ifdef USE_PF_INET6
+ " [PF_INET6]"
+#endif
" [IPv6 payload 20110522-1 (2.2.0)]"
" built on " __DATE__
;
@@ -103,6 +109,9 @@ static const char usage_message[] =
"--proto p : Use protocol p for communicating with peer.\n"
" p = udp (default), tcp-server, or tcp-client\n"
"--proto-force p : only consider protocol p in list of connection profiles.\n"
+#ifdef USE_PF_INET6
+ " p = udp6, tcp6-server, or tcp6-client (ipv6)\n"
+#endif
"--connect-retry n : For --proto tcp-client, number of seconds to wait\n"
" between connection retries (default=%d).\n"
"--connect-timeout n : For --proto tcp-client, connection timeout (in seconds).\n"
@@ -214,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
@@ -350,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
@@ -439,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"
@@ -518,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"
@@ -525,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"
@@ -549,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"
@@ -563,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"
@@ -991,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
@@ -1124,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);
@@ -1214,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
@@ -1233,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)
@@ -1426,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);
@@ -1487,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);
@@ -1830,11 +1899,27 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
* Sanity check on TCP mode options
*/
- if (ce->connect_retry_defined && ce->proto != PROTO_TCPv4_CLIENT)
- msg (M_USAGE, "--connect-retry doesn't make sense unless also used with --proto tcp-client");
+ if (ce->connect_retry_defined && ce->proto != PROTO_TCPv4_CLIENT
+#ifdef USE_PF_INET6
+ && ce->proto != PROTO_TCPv6_CLIENT
+#endif
+ )
+ msg (M_USAGE, "--connect-retry doesn't make sense unless also used with --proto tcp-client"
+#ifdef USE_PF_INET6
+ " or tcp6-client"
+#endif
+ );
- if (ce->connect_timeout_defined && ce->proto != PROTO_TCPv4_CLIENT)
- msg (M_USAGE, "--connect-timeout doesn't make sense unless also used with --proto tcp-client");
+ if (ce->connect_timeout_defined && ce->proto != PROTO_TCPv4_CLIENT
+#ifdef USE_PF_INET6
+ && ce->proto != PROTO_TCPv6_CLIENT
+#endif
+ )
+ msg (M_USAGE, "--connect-timeout doesn't make sense unless also used with --proto tcp-client"
+#ifdef USE_PF_INET6
+ " or tcp6-client"
+#endif
+ );
/*
* Sanity check on MTU parameters
@@ -1843,7 +1928,7 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
msg (M_USAGE, "only one of --tun-mtu or --link-mtu may be defined (note that --ifconfig implies --link-mtu %d)", LINK_MTU_DEFAULT);
#ifdef ENABLE_OCC
- if (ce->proto != PROTO_UDPv4 && options->mtu_test)
+ if (!proto_is_udp(ce->proto) && options->mtu_test)
msg (M_USAGE, "--mtu-test only makes sense with --proto udp");
#endif
@@ -1856,7 +1941,8 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
* Sanity check on --local, --remote, and --ifconfig
*/
- if (string_defined_equal (ce->local, ce->remote)
+ if (proto_is_net(ce->proto)
+ && string_defined_equal (ce->local, ce->remote)
&& ce->local_port == ce->remote_port)
msg (M_USAGE, "--remote and --local addresses are the same");
@@ -1921,16 +2007,20 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
*/
#ifdef ENABLE_FRAGMENT
- if (ce->proto != PROTO_UDPv4 && options->fragment)
+ if (!proto_is_udp(ce->proto) && options->fragment)
msg (M_USAGE, "--fragment can only be used with --proto udp");
#endif
#ifdef ENABLE_OCC
- if (ce->proto != PROTO_UDPv4 && options->explicit_exit_notification)
+ if (!proto_is_udp(ce->proto) && options->explicit_exit_notification)
msg (M_USAGE, "--explicit-exit-notify can only be used with --proto udp");
#endif
- if (!ce->remote && ce->proto == PROTO_TCPv4_CLIENT)
+ if (!ce->remote && (ce->proto == PROTO_TCPv4_CLIENT
+#ifdef USE_PF_INET6
+ || ce->proto == PROTO_TCPv6_CLIENT
+#endif
+ ))
msg (M_USAGE, "--remote MUST be used in TCP Client mode");
#ifdef ENABLE_HTTP_PROXY
@@ -1948,7 +2038,12 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
msg (M_USAGE, "--socks-proxy can not be used in TCP Server mode");
#endif
- if (ce->proto == PROTO_TCPv4_SERVER && connection_list_defined (options))
+ if ((ce->proto == PROTO_TCPv4_SERVER
+#ifdef USE_PF_INET6
+ || ce->proto == PROTO_TCPv6_SERVER
+#endif
+ )
+ && connection_list_defined (options))
msg (M_USAGE, "TCP server mode allows at most one --remote address");
#if P2MP_SERVER
@@ -1962,11 +2057,28 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
msg (M_USAGE, "--mode server only works with --dev tun or --dev tap");
if (options->pull)
msg (M_USAGE, "--pull cannot be used with --mode server");
- if (!(ce->proto == PROTO_UDPv4 || ce->proto == PROTO_TCPv4_SERVER))
- msg (M_USAGE, "--mode server currently only supports --proto udp or --proto tcp-server");
+ if (!(proto_is_udp(ce->proto) || ce->proto == PROTO_TCPv4_SERVER
+#ifdef USE_PF_INET6
+ || ce->proto == PROTO_TCPv6_SERVER
+#endif
+ ))
+ msg (M_USAGE, "--mode server currently only supports --proto udp or --proto tcp-server"
+#ifdef USE_PF_INET6
+ " or proto tcp6-server"
+#endif
+ );
#if PORT_SHARE
- if ((options->port_share_host || options->port_share_port) && ce->proto != PROTO_TCPv4_SERVER)
- msg (M_USAGE, "--port-share only works in TCP server mode (--proto tcp-server)");
+ if ((options->port_share_host || options->port_share_port) &&
+ (ce->proto != PROTO_TCPv4_SERVER
+#ifdef USE_PF_INET6
+ && ce->proto != PROTO_TCPv6_SERVER
+#endif
+ ))
+ msg (M_USAGE, "--port-share only works in TCP server mode (--proto tcp-server"
+#ifdef USE_PF_INET6
+ " or tcp6-server"
+#endif
+ ")");
#endif
if (!options->tls_server)
msg (M_USAGE, "--mode server requires --tls-server");
@@ -1996,9 +2108,17 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
msg (M_USAGE, "--inetd cannot be used with --mode server");
if (options->ipchange)
msg (M_USAGE, "--ipchange cannot be used with --mode server (use --client-connect instead)");
- if (!(ce->proto == PROTO_UDPv4 || ce->proto == PROTO_TCPv4_SERVER))
- msg (M_USAGE, "--mode server currently only supports --proto udp or --proto tcp-server");
- if (ce->proto != PROTO_UDPv4 && (options->cf_max || options->cf_per))
+ if (!(proto_is_dgram(ce->proto) || ce->proto == PROTO_TCPv4_SERVER
+#ifdef USE_PF_INET6
+ || ce->proto == PROTO_TCPv6_SERVER
+#endif
+ ))
+ msg (M_USAGE, "--mode server currently only supports --proto udp or --proto tcp-server"
+#ifdef USE_PF_INET6
+ " or --proto tcp6-server"
+#endif
+ );
+ if (!proto_is_udp(ce->proto) && (options->cf_max || options->cf_per))
msg (M_USAGE, "--connect-freq only works with --mode server --proto udp. Try --max-clients instead.");
if (!(dev == DEV_TYPE_TAP || (dev == DEV_TYPE_TUN && options->topology == TOP_SUBNET)) && options->ifconfig_pool_netmask)
msg (M_USAGE, "The third parameter to --ifconfig-pool (netmask) is only valid in --dev tap mode");
@@ -2096,7 +2216,7 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
/*
* Check consistency of replay options
*/
- if ((ce->proto != PROTO_UDPv4)
+ if ((!proto_is_udp(ce->proto))
&& (options->replay_window != defaults.replay_window
|| options->replay_time != defaults.replay_time))
msg (M_USAGE, "--replay-window only makes sense with --proto udp");
@@ -2269,6 +2389,10 @@ options_postprocess_mutate_ce (struct options *o, struct connection_entry *ce)
{
if (ce->proto == PROTO_TCPv4)
ce->proto = PROTO_TCPv4_CLIENT;
+#ifdef USE_PF_INET6
+ else if (ce->proto == PROTO_TCPv6)
+ ce->proto = PROTO_TCPv6_CLIENT;
+#endif
}
#endif
@@ -2484,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
}
}
@@ -2505,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;
}
@@ -3801,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);
@@ -3822,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"))
{
@@ -4728,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);
@@ -4894,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])
{
@@ -5288,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"))
@@ -5328,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
{
@@ -5987,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])
{
@@ -6031,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);
@@ -6056,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..8a51502 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;
@@ -359,6 +369,8 @@ struct options
struct plugin_option_list *plugin_list;
#endif
+ const char *tmp_dir;
+
#if P2MP
#if P2MP_SERVER
@@ -399,7 +411,6 @@ struct options
const char *client_connect_script;
const char *client_disconnect_script;
const char *learn_address_script;
- const char *tmp_dir;
const char *client_config_dir;
bool ccd_exclusive;
bool disable;
@@ -410,6 +421,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 +440,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 +490,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 +502,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 +511,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 +569,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/plugin/auth-pam/auth-pam.c b/plugin/auth-pam/auth-pam.c
index a06a48e..e52f632 100644
--- a/plugin/auth-pam/auth-pam.c
+++ b/plugin/auth-pam/auth-pam.c
@@ -121,14 +121,14 @@ static void pam_server (int fd, const char *service, int verb, const struct name
static char *
searchandreplace(const char *tosearch, const char *searchfor, const char *replacewith)
{
- if (!tosearch || !searchfor || !replacewith) return 0;
- if (!strlen(tosearch) || !strlen(searchfor) || !strlen(replacewith)) return 0;
-
const char *searching=tosearch;
char *scratch;
char temp[strlen(tosearch)*10];
temp[0]=0;
+ if (!tosearch || !searchfor || !replacewith) return 0;
+ if (!strlen(tosearch) || !strlen(searchfor) || !strlen(replacewith)) return 0;
+
scratch = strstr(searching,searchfor);
if (!scratch) return strdup(tosearch);
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 ef48e36..98a20ad 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.addr.sa);
+ dlen = sizeof(to.addr.sa);
+ if (!getpeername (pc->sd, (struct sockaddr *) &from.addr.sa, &slen)
+ && !getsockname (cp->sd, (struct sockaddr *) &to.addr.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
@@ -320,9 +371,9 @@ sock_addr_set (struct openvpn_sockaddr *osaddr,
const int port)
{
CLEAR (*osaddr);
- osaddr->sa.sin_family = AF_INET;
- osaddr->sa.sin_addr.s_addr = htonl (addr);
- osaddr->sa.sin_port = htons (port);
+ osaddr->addr.in4.sin_family = AF_INET;
+ osaddr->addr.in4.sin_addr.s_addr = htonl (addr);
+ osaddr->addr.in4.sin_port = htons (port);
}
static inline 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
{
@@ -774,8 +860,10 @@ port_share_open (const char *host, const int port)
/* Let msg know that we forked */
msg_forked ();
+#ifdef ENABLE_MANAGEMENT
/* Don't interact with management interface */
management = NULL;
+#endif
/* close all parent fds except our socket back to parent */
close_fds_except (fd[1]);
@@ -787,7 +875,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 +960,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 7c81f75..96596cd 100644
--- a/route.c
+++ b/route.c
@@ -754,13 +754,23 @@ redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, u
if (!local)
{
/* route remote host to original default gateway */
- add_route3 (rl->spec.remote_host,
- ~0,
- rl->spec.net_gateway,
- tt,
- flags,
- es);
- rl->did_local = true;
+#ifdef USE_PF_INET6
+ /* if remote_host is not ipv4 (ie: ipv6), just skip
+ * adding this special /32 route */
+ if (rl->spec.remote_host != IPV4_INVALID_ADDR) {
+#endif
+ add_route3 (rl->spec.remote_host,
+ ~0,
+ rl->spec.net_gateway,
+ tt,
+ flags,
+ es);
+ rl->did_local = true;
+#ifdef USE_PF_INET6
+ } else {
+ dmsg (D_ROUTE, "ROUTE remote_host protocol differs from tunneled");
+ }
+#endif
}
/* route DHCP/DNS server traffic through original default gateway */
@@ -2324,70 +2334,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 +2367,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 +2411,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 +2443,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 +2463,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 +2884,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 3520aca..6b855c0 100644
--- a/socket.c
+++ b/socket.c
@@ -36,10 +36,16 @@
#include "memdbg.h"
const int proto_overhead[] = { /* indexed by PROTO_x */
- IPv4_UDP_HEADER_SIZE,
+ 0,
+ IPv4_UDP_HEADER_SIZE, /* IPv4 */
IPv4_TCP_HEADER_SIZE,
IPv4_TCP_HEADER_SIZE,
- IPv4_TCP_HEADER_SIZE
+#ifdef USE_PF_INET6
+ IPv6_UDP_HEADER_SIZE, /* IPv6 */
+ IPv6_TCP_HEADER_SIZE,
+ IPv6_TCP_HEADER_SIZE,
+ IPv6_TCP_HEADER_SIZE,
+#endif
};
/*
@@ -276,6 +282,201 @@ getaddr_multi (unsigned int flags,
return (flags & GETADDR_HOST_ORDER) ? ntohl (ia.s_addr) : ia.s_addr;
}
+#ifdef USE_PF_INET6
+/*
+ * Translate IPv6 addr or hostname into struct addrinfo
+ * If resolve error, try again for
+ * resolve_retry_seconds seconds.
+ */
+bool
+getaddr6 (unsigned int flags,
+ const char *hostname,
+ int resolve_retry_seconds,
+ volatile int *signal_received,
+ int *gai_err,
+ struct sockaddr_in6 *in6)
+{
+ bool success;
+ struct addrinfo hints, *ai;
+ int status;
+ int sigrec = 0;
+ int msglevel = (flags & GETADDR_FATAL) ? M_FATAL : D_RESOLVE_ERRORS;
+ struct gc_arena gc = gc_new ();
+
+ ASSERT(in6);
+
+ if (!hostname)
+ hostname = "::";
+
+ if (flags & GETADDR_RANDOMIZE)
+ hostname = hostname_randomize(hostname, &gc);
+
+ if (flags & GETADDR_MSG_VIRT_OUT)
+ msglevel |= M_MSG_VIRT_OUT;
+
+ CLEAR (ai);
+ success = false;
+
+ if ((flags & (GETADDR_FATAL_ON_SIGNAL|GETADDR_WARN_ON_SIGNAL))
+ && !signal_received)
+ signal_received = &sigrec;
+
+ /* try numeric ipv6 addr first */
+ CLEAR(hints);
+ hints.ai_family = AF_INET6;
+ hints.ai_flags = AI_NUMERICHOST;
+ if ((status = getaddrinfo(hostname, NULL, &hints, &ai))==0)
+ {
+ *in6 = *((struct sockaddr_in6 *)(ai->ai_addr));
+ freeaddrinfo(ai);
+ ai = NULL;
+ }
+ if (gai_err)
+ *gai_err = status;
+
+
+ if (status != 0) /* parse as IPv6 address failed? */
+ {
+ const int fail_wait_interval = 5; /* seconds */
+ int resolve_retries = (flags & GETADDR_TRY_ONCE) ? 1 : (resolve_retry_seconds / fail_wait_interval);
+ const char *fmt;
+ int level = 0;
+ int err;
+
+ ai = NULL;
+
+ fmt = "RESOLVE: Cannot resolve host address: %s: %s";
+ if ((flags & GETADDR_MENTION_RESOLVE_RETRY)
+ && !resolve_retry_seconds)
+ fmt = "RESOLVE: Cannot resolve host address: %s: %s (I would have retried this name query if you had specified the --resolv-retry option.)";
+
+ if (!(flags & GETADDR_RESOLVE) || status == EAI_FAIL)
+ {
+ msg (msglevel, "RESOLVE: Cannot parse IPv6 address: %s", hostname);
+ goto done;
+ }
+
+#ifdef ENABLE_MANAGEMENT
+ if (flags & GETADDR_UPDATE_MANAGEMENT_STATE)
+ {
+ if (management)
+ management_set_state (management,
+ OPENVPN_STATE_RESOLVE,
+ NULL,
+ (in_addr_t)0,
+ (in_addr_t)0);
+ }
+#endif
+
+ /*
+ * Resolve hostname
+ */
+ while (true)
+ {
+ /* try hostname lookup */
+ hints.ai_flags = 0;
+ hints.ai_socktype = dnsflags_to_socktype(flags);
+ dmsg (D_SOCKET_DEBUG, "GETADDR6 flags=0x%04x ai_family=%d ai_socktype=%d",
+ flags, hints.ai_family, hints.ai_socktype);
+ err = getaddrinfo(hostname, NULL, &hints, &ai);
+
+ if (gai_err)
+ *gai_err = err;
+
+ if (signal_received)
+ {
+ get_signal (signal_received);
+ if (*signal_received) /* were we interrupted by a signal? */
+ {
+ if (0 == err) {
+ ASSERT(ai);
+ freeaddrinfo(ai);
+ ai = NULL;
+ }
+ if (*signal_received == SIGUSR1) /* ignore SIGUSR1 */
+ {
+ msg (level, "RESOLVE: Ignored SIGUSR1 signal received during DNS resolution attempt");
+ *signal_received = 0;
+ }
+ else
+ goto done;
+ }
+ }
+
+ /* success? */
+ if (0 == err)
+ break;
+
+ /* resolve lookup failed, should we
+ continue or fail? */
+
+ level = msglevel;
+ if (resolve_retries > 0)
+ level = D_RESOLVE_ERRORS;
+
+ msg (level,
+ fmt,
+ hostname,
+ gai_strerror(err));
+
+ if (--resolve_retries <= 0)
+ goto done;
+
+ openvpn_sleep (fail_wait_interval);
+ }
+
+ ASSERT(ai);
+
+ if (!ai->ai_next)
+ *in6 = *((struct sockaddr_in6*)(ai->ai_addr));
+ else
+ /* more than one address returned */
+ {
+ struct addrinfo *ai_cursor;
+ int n = 0;
+ /* count address list */
+ for (ai_cursor = ai; ai_cursor; ai_cursor = ai_cursor->ai_next) n++;
+ ASSERT (n >= 2);
+
+ msg (D_RESOLVE_ERRORS, "RESOLVE: NOTE: %s resolves to %d ipv6 addresses, choosing one by random",
+ hostname,
+ n);
+
+ /* choose address randomly, for basic load-balancing capability */
+ n--;
+ n %= get_random();
+ for (ai_cursor = ai; n; ai_cursor = ai_cursor->ai_next) n--;
+ *in6 = *((struct sockaddr_in6*)(ai_cursor->ai_addr));
+ }
+
+ freeaddrinfo(ai);
+ ai = NULL;
+
+ /* hostname resolve succeeded */
+ success = true;
+ }
+ else
+ {
+ /* IP address parse succeeded */
+ success = true;
+ }
+
+ done:
+ if (signal_received && *signal_received)
+ {
+ int level = 0;
+ if (flags & GETADDR_FATAL_ON_SIGNAL)
+ level = M_FATAL;
+ else if (flags & GETADDR_WARN_ON_SIGNAL)
+ level = M_WARN;
+ msg (level, "RESOLVE: signal received during DNS resolution attempt");
+ }
+
+ gc_free (&gc);
+ return success;
+}
+#endif /* USE_PF_INET6 */
+
/*
* We do our own inet_aton because the glibc function
* isn't very good about error checking.
@@ -428,20 +629,53 @@ update_remote (const char* host,
bool *changed,
const unsigned int sockflags)
{
- if (host && addr)
+ switch(addr->addr.sa.sa_family)
{
- const in_addr_t new_addr = getaddr (
- sf2gaf(GETADDR_RESOLVE|GETADDR_UPDATE_MANAGEMENT_STATE, sockflags),
- host,
- 1,
- NULL,
- NULL);
- if (new_addr && addr->sa.sin_addr.s_addr != new_addr)
+ case AF_INET:
+ if (host && addr)
{
- addr->sa.sin_addr.s_addr = new_addr;
- *changed = true;
+ const in_addr_t new_addr = getaddr (
+ sf2gaf(GETADDR_RESOLVE|GETADDR_UPDATE_MANAGEMENT_STATE, sockflags),
+ host,
+ 1,
+ NULL,
+ NULL);
+ if (new_addr && addr->addr.in4.sin_addr.s_addr != new_addr)
+ {
+ addr->addr.in4.sin_addr.s_addr = new_addr;
+ *changed = true;
+ }
}
- }
+ break;
+#ifdef USE_PF_INET6
+ case AF_INET6:
+ if (host && addr)
+ {
+ struct sockaddr_in6 sin6;
+ CLEAR(sin6);
+ int success = getaddr6 (
+ sf2gaf(GETADDR_RESOLVE|GETADDR_UPDATE_MANAGEMENT_STATE, sockflags),
+ host,
+ 1,
+ NULL,
+ NULL,
+ &sin6);
+ if ( success )
+ {
+ if (!IN6_ARE_ADDR_EQUAL(&sin6.sin6_addr, &addr->addr.in6.sin6_addr))
+ {
+ int port = addr->addr.in6.sin6_port;
+ /* ipv6 requires also eg. sin6_scope_id => easier to fully copy and override port */
+ addr->addr.in6 = sin6;
+ addr->addr.in6.sin6_port = port;
+ }
+ }
+ }
+ break;
+#endif
+ default:
+ ASSERT(0);
+ }
}
static int
@@ -628,12 +862,62 @@ create_socket_udp (const unsigned int flags)
else if (flags & SF_USE_IP_PKTINFO)
{
int pad = 1;
- setsockopt (sd, SOL_IP, IP_PKTINFO, (void*)&pad, sizeof(pad));
+#ifdef IP_PKTINFO
+ if (setsockopt (sd, SOL_IP, IP_PKTINFO,
+ (void*)&pad, sizeof(pad)) < 0)
+ msg(M_SOCKERR, "UDP: failed setsockopt for IP_PKTINFO");
+#elif defined(IP_RECVDSTADDR)
+ if (setsockopt (sd, IPPROTO_IP, IP_RECVDSTADDR,
+ (void*)&pad, sizeof(pad)) < 0)
+ msg(M_SOCKERR, "UDP: failed setsockopt for IP_RECVDSTADDR");
+#else
+#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h)
+#endif
+ }
+#endif
+ return sd;
+}
+
+#ifdef USE_PF_INET6
+static socket_descriptor_t
+create_socket_udp6 (const unsigned int flags)
+{
+ socket_descriptor_t sd;
+
+ if ((sd = socket (PF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0)
+ msg (M_SOCKERR, "UDP: Cannot create UDP6 socket");
+#if ENABLE_IP_PKTINFO
+ else if (flags & SF_USE_IP_PKTINFO)
+ {
+ int pad = 1;
+ if (setsockopt (sd, IPPROTO_IPV6, IPV6_RECVPKTINFO,
+ (void*)&pad, sizeof(pad)) < 0)
+ msg(M_SOCKERR, "UDP: failed setsockopt for IPV6_RECVPKTINFO");
}
#endif
return sd;
}
+static socket_descriptor_t
+create_socket_tcp6 (void)
+{
+ socket_descriptor_t sd;
+
+ if ((sd = socket (PF_INET6, SOCK_STREAM, IPPROTO_TCP)) < 0)
+ msg (M_SOCKERR, "Cannot create TCP6 socket");
+
+ /* set SO_REUSEADDR on socket */
+ {
+ int on = 1;
+ if (setsockopt (sd, SOL_SOCKET, SO_REUSEADDR,
+ (void *) &on, sizeof (on)) < 0)
+ msg (M_SOCKERR, "TCP: Cannot setsockopt SO_REUSEADDR on TCP6 socket");
+ }
+
+ return sd;
+}
+
+#endif
static void
create_socket (struct link_socket *sock)
{
@@ -641,6 +925,7 @@ create_socket (struct link_socket *sock)
if (sock->info.proto == PROTO_UDPv4)
{
sock->sd = create_socket_udp (sock->sockflags);
+ sock->sockflags |= SF_GETADDRINFO_DGRAM;
#ifdef ENABLE_SOCKS
if (sock->socks_proxy)
@@ -652,6 +937,18 @@ create_socket (struct link_socket *sock)
{
sock->sd = create_socket_tcp ();
}
+#ifdef USE_PF_INET6
+ else if (sock->info.proto == PROTO_TCPv6_SERVER
+ || sock->info.proto == PROTO_TCPv6_CLIENT)
+ {
+ sock->sd = create_socket_tcp6 ();
+ }
+ else if (sock->info.proto == PROTO_UDPv6)
+ {
+ sock->sd = create_socket_udp6 (sock->sockflags);
+ sock->sockflags |= SF_GETADDRINFO_DGRAM;
+ }
+#endif
else
{
ASSERT (0);
@@ -689,7 +986,12 @@ socket_do_accept (socket_descriptor_t sd,
struct link_socket_actual *act,
const bool nowait)
{
- socklen_t remote_len = sizeof (act->dest.sa);
+ /* af_addr_size WILL return 0 in this case if AFs other than AF_INET
+ * are compiled because act is empty here.
+ * could use getsockname() to support later remote_len check
+ */
+ socklen_t remote_len_af = af_addr_size(act->dest.addr.sa.sa_family);
+ socklen_t remote_len = sizeof(act->dest.addr);
socket_descriptor_t new_sd = SOCKET_UNDEFINED;
CLEAR (*act);
@@ -697,7 +999,7 @@ socket_do_accept (socket_descriptor_t sd,
#ifdef HAVE_GETPEERNAME
if (nowait)
{
- new_sd = getpeername (sd, (struct sockaddr *) &act->dest.sa, &remote_len);
+ new_sd = getpeername (sd, &act->dest.addr.sa, &remote_len);
if (!socket_defined (new_sd))
msg (D_LINK_ERRORS | M_ERRNO_SOCK, "TCP: getpeername() failed");
@@ -710,7 +1012,7 @@ socket_do_accept (socket_descriptor_t sd,
#endif
else
{
- new_sd = accept (sd, (struct sockaddr *) &act->dest.sa, &remote_len);
+ new_sd = accept (sd, &act->dest.addr.sa, &remote_len);
}
#if 0 /* For debugging only, test the effect of accept() failures */
@@ -726,7 +1028,8 @@ socket_do_accept (socket_descriptor_t sd,
{
msg (D_LINK_ERRORS | M_ERRNO_SOCK, "TCP: accept(%d) failed", sd);
}
- else if (remote_len != sizeof (act->dest.sa))
+ /* only valid if we have remote_len_af!=0 */
+ else if (remote_len_af && remote_len != remote_len_af)
{
msg (D_LINK_ERRORS, "TCP: Received strange incoming connection with unknown address length=%d", remote_len);
openvpn_close_socket (new_sd);
@@ -827,7 +1130,7 @@ socket_bind (socket_descriptor_t sd,
{
struct gc_arena gc = gc_new ();
- if (bind (sd, (struct sockaddr *) &local->sa, sizeof (local->sa)))
+ if (bind (sd, &local->addr.sa, af_addr_size(local->addr.sa.sa_family)))
{
const int errnum = openvpn_errno_socket ();
msg (M_FATAL, "%s: Socket bind failed on local address %s: %s",
@@ -848,7 +1151,7 @@ openvpn_connect (socket_descriptor_t sd,
#ifdef CONNECT_NONBLOCK
set_nonblock (sd);
- status = connect (sd, (struct sockaddr *) &remote->sa, sizeof (remote->sa));
+ status = connect (sd, &remote->addr.sa, af_addr_size(remote->addr.sa.sa_family));
if (status)
status = openvpn_errno_socket ();
if (status == EINPROGRESS)
@@ -906,7 +1209,7 @@ openvpn_connect (socket_descriptor_t sd,
}
}
#else
- status = connect (sd, (struct sockaddr *) &remote->sa, sizeof (remote->sa));
+ status = connect (sd, &remote->addr.sa, af_addr_size(remote->addr.sa.sa_family));
if (status)
status = openvpn_errno_socket ();
#endif
@@ -984,7 +1287,20 @@ socket_connect (socket_descriptor_t *sd,
if (*signal_received)
goto done;
- *sd = create_socket_tcp ();
+#ifdef USE_PF_INET6
+ switch(local->addr.sa.sa_family)
+ {
+ case PF_INET6:
+ *sd = create_socket_tcp6 ();
+ break;
+ case PF_INET:
+#endif
+ *sd = create_socket_tcp ();
+#ifdef USE_PF_INET6
+ break;
+ }
+#endif
+
if (bind_local)
socket_bind (*sd, local, "TCP Client");
update_remote (remote_dynamic, remote, remote_changed, sockflags);
@@ -1049,15 +1365,54 @@ resolve_bind_local (struct link_socket *sock)
/* resolve local address if undefined */
if (!addr_defined (&sock->info.lsa->local))
{
- sock->info.lsa->local.sa.sin_family = AF_INET;
- sock->info.lsa->local.sa.sin_addr.s_addr =
- (sock->local_host ? getaddr (GETADDR_RESOLVE | GETADDR_WARN_ON_SIGNAL | GETADDR_FATAL,
+#ifdef USE_PF_INET6
+ /* may return AF_{INET|INET6} guessed from local_host */
+ switch(addr_guess_family(sock->info.proto, sock->local_host))
+ {
+ case AF_INET:
+#endif
+ sock->info.lsa->local.addr.in4.sin_family = AF_INET;
+ sock->info.lsa->local.addr.in4.sin_addr.s_addr =
+ (sock->local_host ? getaddr (GETADDR_RESOLVE | GETADDR_WARN_ON_SIGNAL | GETADDR_FATAL,
+ sock->local_host,
+ 0,
+ NULL,
+ NULL)
+ : htonl (INADDR_ANY));
+ sock->info.lsa->local.addr.in4.sin_port = htons (sock->local_port);
+#ifdef USE_PF_INET6
+ break;
+ case AF_INET6:
+ {
+ int success;
+ int err;
+ CLEAR(sock->info.lsa->local.addr.in6);
+ if (sock->local_host)
+ {
+ success = getaddr6(GETADDR_RESOLVE | GETADDR_WARN_ON_SIGNAL | GETADDR_FATAL,
sock->local_host,
0,
NULL,
- NULL)
- : htonl (INADDR_ANY));
- sock->info.lsa->local.sa.sin_port = htons (sock->local_port);
+ &err,
+ &sock->info.lsa->local.addr.in6);
+ }
+ else
+ {
+ sock->info.lsa->local.addr.in6.sin6_family = AF_INET6;
+ sock->info.lsa->local.addr.in6.sin6_addr = in6addr_any;
+ success = true;
+ }
+ if (!success)
+ {
+ msg (M_FATAL, "getaddr6() failed for local \"%s\": %s",
+ sock->local_host,
+ gai_strerror(err));
+ }
+ sock->info.lsa->local.addr.in6.sin6_port = htons (sock->local_port);
+ }
+ break;
+ }
+#endif /* USE_PF_INET6 */
}
/* bind to local address/port */
@@ -1080,14 +1435,32 @@ resolve_remote (struct link_socket *sock,
volatile int *signal_received)
{
struct gc_arena gc = gc_new ();
+#ifdef USE_PF_INET6
+ int af;
+#endif
if (!sock->did_resolve_remote)
{
/* resolve remote address if undefined */
if (!addr_defined (&sock->info.lsa->remote))
{
- sock->info.lsa->remote.sa.sin_family = AF_INET;
- sock->info.lsa->remote.sa.sin_addr.s_addr = 0;
+#ifdef USE_PF_INET6
+ af = addr_guess_family(sock->info.proto, sock->remote_host);
+ switch(af)
+ {
+ case AF_INET:
+#endif
+ sock->info.lsa->remote.addr.in4.sin_family = AF_INET;
+ sock->info.lsa->remote.addr.in4.sin_addr.s_addr = 0;
+#ifdef USE_PF_INET6
+ break;
+ case AF_INET6:
+ CLEAR(sock->info.lsa->remote.addr.in6);
+ sock->info.lsa->remote.addr.in6.sin6_family = AF_INET6;
+ sock->info.lsa->remote.addr.in6.sin6_addr = in6addr_any;
+ break;
+ }
+#endif
if (sock->remote_host)
{
@@ -1130,13 +1503,31 @@ resolve_remote (struct link_socket *sock,
ASSERT (0);
}
- sock->info.lsa->remote.sa.sin_addr.s_addr = getaddr (
- flags,
- sock->remote_host,
- retry,
- &status,
- signal_received);
-
+#ifdef USE_PF_INET6
+ switch(af)
+ {
+ case AF_INET:
+#endif
+ sock->info.lsa->remote.addr.in4.sin_addr.s_addr = getaddr (
+ flags,
+ sock->remote_host,
+ retry,
+ &status,
+ signal_received);
+#ifdef USE_PF_INET6
+ break;
+ case AF_INET6:
+ status = getaddr6 (
+ flags,
+ sock->remote_host,
+ retry,
+ signal_received,
+ NULL,
+ &sock->info.lsa->remote.addr.in6);
+ break;
+ }
+#endif
+
dmsg (D_SOCKET_DEBUG, "RESOLVE_REMOTE flags=0x%04x phase=%d rrs=%d sig=%d status=%d",
flags,
phase,
@@ -1156,8 +1547,19 @@ resolve_remote (struct link_socket *sock,
goto done;
}
}
-
- sock->info.lsa->remote.sa.sin_port = htons (sock->remote_port);
+#ifdef USE_PF_INET6
+ switch(af)
+ {
+ case AF_INET:
+#endif
+ sock->info.lsa->remote.addr.in4.sin_port = htons (sock->remote_port);
+#ifdef USE_PF_INET6
+ break;
+ case AF_INET6:
+ sock->info.lsa->remote.addr.in6.sin6_port = htons (sock->remote_port);
+ break;
+ }
+#endif
}
/* should we re-use previous active remote address? */
@@ -1274,7 +1676,11 @@ link_socket_init_phase1 (struct link_socket *sock,
if (mode == LS_MODE_TCP_ACCEPT_FROM)
{
ASSERT (accept_from);
- ASSERT (sock->info.proto == PROTO_TCPv4_SERVER);
+ ASSERT (sock->info.proto == PROTO_TCPv4_SERVER
+#ifdef USE_PF_INET6
+ || sock->info.proto == PROTO_TCPv6_SERVER
+#endif
+ );
ASSERT (!sock->inetd);
sock->sd = accept_from->sd;
}
@@ -1331,7 +1737,11 @@ link_socket_init_phase1 (struct link_socket *sock,
/* were we started by inetd or xinetd? */
if (sock->inetd)
{
- ASSERT (sock->info.proto != PROTO_TCPv4_CLIENT);
+ ASSERT (sock->info.proto != PROTO_TCPv4_CLIENT
+#ifdef USE_PF_INET6
+ && sock->info.proto != PROTO_TCPv6_CLIENT
+#endif
+ );
ASSERT (socket_defined (inetd_socket_descriptor));
sock->sd = inetd_socket_descriptor;
}
@@ -1380,7 +1790,34 @@ link_socket_init_phase2 (struct link_socket *sock,
/* were we started by inetd or xinetd? */
if (sock->inetd)
{
- if (sock->info.proto == PROTO_TCPv4_SERVER)
+ if (sock->info.proto == PROTO_TCPv4_SERVER
+#ifdef USE_PF_INET6
+ || sock->info.proto == PROTO_TCPv6_SERVER
+#endif
+ ) {
+ /* AF_INET as default (and fallback) for inetd */
+ sock->info.lsa->actual.dest.addr.sa.sa_family = AF_INET;
+#ifdef USE_PF_INET6
+#ifdef HAVE_GETSOCKNAME
+ {
+ /* inetd: hint family type for dest = local's */
+ struct openvpn_sockaddr local_addr;
+ socklen_t addrlen = sizeof(local_addr);
+ if (getsockname (sock->sd, (struct sockaddr *)&local_addr, &addrlen) == 0) {
+ sock->info.lsa->actual.dest.addr.sa.sa_family = local_addr.addr.sa.sa_family;
+ dmsg (D_SOCKET_DEBUG, "inetd(%s): using sa_family=%d from getsockname(%d)",
+ proto2ascii(sock->info.proto, false), local_addr.addr.sa.sa_family,
+ sock->sd);
+ } else
+ msg (M_WARN, "inetd(%s): getsockname(%d) failed, using AF_INET",
+ proto2ascii(sock->info.proto, false), sock->sd);
+ }
+#else
+ msg (M_WARN, "inetd(%s): this OS does not provide the getsockname() "
+ "function, using AF_INET",
+ proto2ascii(sock->info.proto, false));
+#endif
+#endif
sock->sd =
socket_listen_accept (sock->sd,
&sock->info.lsa->actual,
@@ -1390,6 +1827,7 @@ link_socket_init_phase2 (struct link_socket *sock,
false,
sock->inetd == INETD_NOWAIT,
signal_received);
+ }
ASSERT (!remote_changed);
if (*signal_received)
goto done;
@@ -1402,7 +1840,11 @@ link_socket_init_phase2 (struct link_socket *sock,
goto done;
/* TCP client/server */
- if (sock->info.proto == PROTO_TCPv4_SERVER)
+ if (sock->info.proto == PROTO_TCPv4_SERVER
+#ifdef USE_PF_INET6
+ ||sock->info.proto == PROTO_TCPv6_SERVER
+#endif
+ )
{
switch (sock->mode)
{
@@ -1437,7 +1879,11 @@ link_socket_init_phase2 (struct link_socket *sock,
ASSERT (0);
}
}
- else if (sock->info.proto == PROTO_TCPv4_CLIENT)
+ else if (sock->info.proto == PROTO_TCPv4_CLIENT
+#ifdef USE_PF_INET6
+ ||sock->info.proto == PROTO_TCPv6_CLIENT
+#endif
+ )
{
#ifdef GENERAL_PROXY_SUPPORT
@@ -1524,8 +1970,8 @@ link_socket_init_phase2 (struct link_socket *sock,
sock->remote_port = sock->proxy_dest_port;
sock->did_resolve_remote = false;
- sock->info.lsa->actual.dest.sa.sin_addr.s_addr = 0;
- sock->info.lsa->remote.sa.sin_addr.s_addr = 0;
+ addr_zero_host(&sock->info.lsa->actual.dest);
+ addr_zero_host(&sock->info.lsa->remote);
resolve_remote (sock, 1, NULL, signal_received);
@@ -1540,7 +1986,7 @@ link_socket_init_phase2 (struct link_socket *sock,
if (remote_changed)
{
msg (M_INFO, "TCP/UDP: Dynamic remote address changed during TCP connection establishment");
- sock->info.lsa->remote.sa.sin_addr.s_addr = sock->info.lsa->actual.dest.sa.sin_addr.s_addr;
+ addr_copy_host(&sock->info.lsa->remote, &sock->info.lsa->actual.dest);
}
}
@@ -1568,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)
@@ -1611,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");
}
@@ -1726,13 +2176,20 @@ link_socket_bad_incoming_addr (struct buffer *buf,
{
struct gc_arena gc = gc_new ();
- msg (D_LINK_ERRORS,
- "TCP/UDP: Incoming packet rejected from %s[%d], expected peer address: %s (allow this incoming source address/port by removing --remote or adding --float)",
- print_link_socket_actual (from_addr, &gc),
- (int)from_addr->dest.sa.sin_family,
- print_sockaddr (&info->lsa->remote, &gc));
+ switch(from_addr->dest.addr.sa.sa_family)
+ {
+ case AF_INET:
+#ifdef USE_PF_INET6
+ case AF_INET6:
+#endif
+ msg (D_LINK_ERRORS,
+ "TCP/UDP: Incoming packet rejected from %s[%d], expected peer address: %s (allow this incoming source address/port by removing --remote or adding --float)",
+ print_link_socket_actual (from_addr, &gc),
+ (int)from_addr->dest.addr.sa.sa_family,
+ print_sockaddr (&info->lsa->remote, &gc));
+ break;
+ }
buf->len = 0;
-
gc_free (&gc);
}
@@ -1747,10 +2204,25 @@ link_socket_current_remote (const struct link_socket_info *info)
{
const struct link_socket_addr *lsa = info->lsa;
+/*
+ * This logic supports "redirect-gateway" semantic, which
+ * makes sense only for PF_INET routes over PF_INET endpoints
+ *
+ * Maybe in the future consider PF_INET6 endpoints also ...
+ * by now just ignore it
+ *
+ */
+#ifdef USE_PF_INET6
+ if (lsa->actual.dest.addr.sa.sa_family != AF_INET)
+ return IPV4_INVALID_ADDR;
+#else
+ ASSERT (lsa->actual.dest.addr.sa.sa_family == AF_INET);
+#endif
+
if (link_socket_actual_defined (&lsa->actual))
- return ntohl (lsa->actual.dest.sa.sin_addr.s_addr);
+ return ntohl (lsa->actual.dest.addr.in4.sin_addr.s_addr);
else if (addr_defined (&lsa->remote))
- return ntohl (lsa->remote.sa.sin_addr.s_addr);
+ return ntohl (lsa->remote.addr.in4.sin_addr.s_addr);
else
return 0;
}
@@ -1977,26 +2449,61 @@ print_sockaddr_ex (const struct openvpn_sockaddr *addr,
const unsigned int flags,
struct gc_arena *gc)
{
- if (addr)
+ struct buffer out = alloc_buf_gc (128, gc);
+ bool addr_is_defined;
+ addr_is_defined = addr_defined (addr);
+ if (!addr_is_defined) {
+ return "[undef]";
+ }
+#ifdef USE_PF_INET6
+ switch(addr->addr.sa.sa_family)
{
- struct buffer out = alloc_buf_gc (64, gc);
- const int port = ntohs (addr->sa.sin_port);
+ case AF_INET:
+#endif
+ {
+ const int port= ntohs (addr->addr.in4.sin_port);
+ buf_puts (&out, "[AF_INET]");
+
+ if (!(flags & PS_DONT_SHOW_ADDR))
+ buf_printf (&out, "%s", (addr_defined (addr) ? inet_ntoa (addr->addr.in4.sin_addr) : "[undef]"));
- if (!(flags & PS_DONT_SHOW_ADDR))
- buf_printf (&out, "%s", (addr_defined (addr) ? inet_ntoa (addr->sa.sin_addr) : "[undef]"));
+ if (((flags & PS_SHOW_PORT) || (addr_defined (addr) && (flags & PS_SHOW_PORT_IF_DEFINED)))
+ && port)
+ {
+ if (separator)
+ buf_printf (&out, "%s", separator);
- if (((flags & PS_SHOW_PORT) || (addr_defined (addr) && (flags & PS_SHOW_PORT_IF_DEFINED)))
- && port)
+ buf_printf (&out, "%d", port);
+ }
+ }
+#ifdef USE_PF_INET6
+ break;
+ case AF_INET6:
{
- if (separator)
- buf_printf (&out, "%s", separator);
+ const int port= ntohs (addr->addr.in6.sin6_port);
+ char buf[INET6_ADDRSTRLEN] = "";
+ buf_puts (&out, "[AF_INET6]");
+ if (addr_is_defined)
+ {
+ getnameinfo(&addr->addr.sa, sizeof (struct sockaddr_in6),
+ buf, sizeof (buf), NULL, 0, NI_NUMERICHOST);
+ buf_puts (&out, buf);
+ }
+ if (((flags & PS_SHOW_PORT) || (addr_is_defined && (flags & PS_SHOW_PORT_IF_DEFINED)))
+ && port)
+ {
+ if (separator)
+ buf_puts (&out, separator);
- buf_printf (&out, "%d", port);
+ buf_printf (&out, "%d", port);
+ }
}
- return BSTR (&out);
+ break;
+ default:
+ ASSERT(0);
}
- else
- return "[NULL]";
+#endif
+ return BSTR (&out);
}
const char *
@@ -2005,6 +2512,10 @@ print_link_socket_actual (const struct link_socket_actual *act, struct gc_arena
return print_link_socket_actual_ex (act, ":", PS_SHOW_PORT|PS_SHOW_PKTINFO, gc);
}
+#ifndef IF_NAMESIZE
+#define IF_NAMESIZE 16
+#endif
+
const char *
print_link_socket_actual_ex (const struct link_socket_actual *act,
const char *separator,
@@ -2013,15 +2524,54 @@ print_link_socket_actual_ex (const struct link_socket_actual *act,
{
if (act)
{
+ char ifname[IF_NAMESIZE] = "[undef]";
struct buffer out = alloc_buf_gc (128, gc);
buf_printf (&out, "%s", print_sockaddr_ex (&act->dest, separator, flags, gc));
#if ENABLE_IP_PKTINFO
- if ((flags & PS_SHOW_PKTINFO) && act->pi.ipi_spec_dst.s_addr)
+ if ((flags & PS_SHOW_PKTINFO) && addr_defined_ipi(act))
{
- struct openvpn_sockaddr sa;
- CLEAR (sa);
- sa.sa.sin_addr = act->pi.ipi_spec_dst;
- buf_printf (&out, " (via %s)", print_sockaddr_ex (&sa, separator, 0, gc));
+#ifdef USE_PF_INET6
+ switch(act->dest.addr.sa.sa_family)
+ {
+ case AF_INET:
+#endif
+ {
+ struct openvpn_sockaddr sa;
+ CLEAR (sa);
+ sa.addr.in4.sin_family = AF_INET;
+#ifdef IP_PKTINFO
+ sa.addr.in4.sin_addr = act->pi.in4.ipi_spec_dst;
+ if_indextoname(act->pi.in4.ipi_ifindex, ifname);
+#elif defined(IP_RECVDSTADDR)
+ sa.addr.in4.sin_addr = act->pi.in4;
+ ifname[0]=0;
+#else
+#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h)
+#endif
+ buf_printf (&out, " (via %s%%%s)",
+ print_sockaddr_ex (&sa, separator, 0, gc),
+ ifname);
+ }
+#ifdef USE_PF_INET6
+ break;
+ case AF_INET6:
+ {
+ struct sockaddr_in6 sin6;
+ char buf[INET6_ADDRSTRLEN] = "[undef]";
+ CLEAR(sin6);
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_addr = act->pi.in6.ipi6_addr;
+ if_indextoname(act->pi.in6.ipi6_ifindex, ifname);
+ if (getnameinfo((struct sockaddr *)&sin6, sizeof (struct sockaddr_in6),
+ buf, sizeof (buf), NULL, 0, NI_NUMERICHOST) == 0)
+ buf_printf (&out, " (via %s%%%s)", buf, ifname);
+ else
+ buf_printf (&out, " (via [getnameinfo() err]%%%s)", ifname);
+ }
+ break;
+ }
+#endif /* USE_PF_INET6 */
+
}
#endif
return BSTR (&out);
@@ -2105,18 +2655,40 @@ setenv_sockaddr (struct env_set *es, const char *name_prefix, const struct openv
{
char name_buf[256];
- if (flags & SA_IP_PORT)
- openvpn_snprintf (name_buf, sizeof (name_buf), "%s_ip", name_prefix);
- else
- openvpn_snprintf (name_buf, sizeof (name_buf), "%s", name_prefix);
+#ifdef USE_PF_INET6
+ char buf[128];
+ switch(addr->addr.sa.sa_family)
+ {
+ case AF_INET:
+#endif
+ if (flags & SA_IP_PORT)
+ openvpn_snprintf (name_buf, sizeof (name_buf), "%s_ip", name_prefix);
+ else
+ openvpn_snprintf (name_buf, sizeof (name_buf), "%s", name_prefix);
- setenv_str (es, name_buf, inet_ntoa (addr->sa.sin_addr));
+ setenv_str (es, name_buf, inet_ntoa (addr->addr.in4.sin_addr));
- if ((flags & SA_IP_PORT) && addr->sa.sin_port)
- {
- openvpn_snprintf (name_buf, sizeof (name_buf), "%s_port", name_prefix);
- setenv_int (es, name_buf, ntohs (addr->sa.sin_port));
+ if ((flags & SA_IP_PORT) && addr->addr.in4.sin_port)
+ {
+ openvpn_snprintf (name_buf, sizeof (name_buf), "%s_port", name_prefix);
+ setenv_int (es, name_buf, ntohs (addr->addr.in4.sin_port));
+ }
+#ifdef USE_PF_INET6
+ break;
+ case AF_INET6:
+ openvpn_snprintf (name_buf, sizeof (name_buf), "%s_ip6", name_prefix);
+ getnameinfo(&addr->addr.sa, sizeof (struct sockaddr_in6),
+ buf, sizeof(buf), NULL, 0, NI_NUMERICHOST);
+ setenv_str (es, name_buf, buf);
+
+ if ((flags & SA_IP_PORT) && addr->addr.in6.sin6_port)
+ {
+ openvpn_snprintf (name_buf, sizeof (name_buf), "%s_port", name_prefix);
+ setenv_int (es, name_buf, ntohs (addr->addr.in6.sin6_port));
+ }
+ break;
}
+#endif
}
void
@@ -2126,7 +2698,8 @@ setenv_in_addr_t (struct env_set *es, const char *name_prefix, in_addr_t addr, c
{
struct openvpn_sockaddr si;
CLEAR (si);
- si.sa.sin_addr.s_addr = htonl (addr);
+ si.addr.in4.sin_family = AF_INET;
+ si.addr.in4.sin_addr.s_addr = htonl (addr);
setenv_sockaddr (es, name_prefix, &si, flags);
}
}
@@ -2147,16 +2720,63 @@ setenv_link_socket_actual (struct env_set *es,
struct proto_names {
const char *short_form;
const char *display_form;
+ bool is_dgram;
+ bool is_net;
+ unsigned short proto_af;
};
/* Indexed by PROTO_x */
-static const struct proto_names proto_names[] = {
- {"udp", "UDPv4"},
- {"tcp-server", "TCPv4_SERVER"},
- {"tcp-client", "TCPv4_CLIENT"},
- {"tcp", "TCPv4"}
+static const struct proto_names proto_names[PROTO_N] = {
+ {"proto-uninitialized", "proto-NONE",0,0, AF_UNSPEC},
+ {"udp", "UDPv4",1,1, AF_INET},
+ {"tcp-server", "TCPv4_SERVER",0,1, AF_INET},
+ {"tcp-client", "TCPv4_CLIENT",0,1, AF_INET},
+ {"tcp", "TCPv4",0,1, AF_INET},
+#ifdef USE_PF_INET6
+ {"udp6" ,"UDPv6",1,1, AF_INET6},
+ {"tcp6-server","TCPv6_SERVER",0,1, AF_INET6},
+ {"tcp6-client","TCPv6_CLIENT",0,1, AF_INET6},
+ {"tcp6" ,"TCPv6",0,1, AF_INET6},
+#endif
};
+bool
+proto_is_net(int proto)
+{
+ if (proto < 0 || proto >= PROTO_N)
+ ASSERT(0);
+ return proto_names[proto].is_net;
+}
+bool
+proto_is_dgram(int proto)
+{
+ if (proto < 0 || proto >= PROTO_N)
+ ASSERT(0);
+ return proto_names[proto].is_dgram;
+}
+bool
+proto_is_udp(int proto)
+{
+ if (proto < 0 || proto >= PROTO_N)
+ ASSERT(0);
+ return proto_names[proto].is_dgram&&proto_names[proto].is_net;
+}
+bool
+proto_is_tcp(int proto)
+{
+ if (proto < 0 || proto >= PROTO_N)
+ ASSERT(0);
+ return (!proto_names[proto].is_dgram)&&proto_names[proto].is_net;
+}
+
+unsigned short
+proto_sa_family(int proto)
+{
+ if (proto < 0 || proto >= PROTO_N)
+ ASSERT(0);
+ return proto_names[proto].proto_af;
+}
+
int
ascii2proto (const char* proto_name)
{
@@ -2196,6 +2816,45 @@ proto2ascii_all (struct gc_arena *gc)
return BSTR (&out);
}
+int
+addr_guess_family(int proto, const char *name)
+{
+#ifdef USE_PF_INET6
+ unsigned short ret;
+#endif
+ if (proto)
+ {
+ return proto_sa_family(proto); /* already stamped */
+ }
+#ifdef USE_PF_INET6
+ else
+ {
+ struct addrinfo hints , *ai;
+ int err;
+ CLEAR(hints);
+ hints.ai_flags = AI_NUMERICHOST;
+ err = getaddrinfo(name, NULL, &hints, &ai);
+ if ( 0 == err )
+ {
+ ret=ai->ai_family;
+ freeaddrinfo(ai);
+ return ret;
+ }
+ }
+#endif
+ return AF_INET; /* default */
+}
+const char *
+addr_family_name (int af)
+{
+ switch (af)
+ {
+ case AF_INET: return "AF_INET";
+ case AF_INET6: return "AF_INET6";
+ }
+ return "AF_UNSPEC";
+}
+
/*
* Given a local proto, return local proto
* if !remote, or compatible remote proto
@@ -2210,10 +2869,15 @@ proto_remote (int proto, bool remote)
ASSERT (proto >= 0 && proto < PROTO_N);
if (remote)
{
- if (proto == PROTO_TCPv4_SERVER)
- return PROTO_TCPv4_CLIENT;
- if (proto == PROTO_TCPv4_CLIENT)
- return PROTO_TCPv4_SERVER;
+ switch (proto)
+ {
+ case PROTO_TCPv4_SERVER: return PROTO_TCPv4_CLIENT;
+ case PROTO_TCPv4_CLIENT: return PROTO_TCPv4_SERVER;
+#ifdef USE_PF_INET6
+ case PROTO_TCPv6_SERVER: return PROTO_TCPv6_CLIENT;
+ case PROTO_TCPv6_CLIENT: return PROTO_TCPv6_SERVER;
+#endif
+ }
}
return proto;
}
@@ -2272,10 +2936,29 @@ link_socket_read_tcp (struct link_socket *sock,
#if ENABLE_IP_PKTINFO
#pragma pack(1) /* needed to keep structure size consistent for 32 vs. 64-bit architectures */
-struct openvpn_pktinfo
+struct openvpn_in4_pktinfo
{
struct cmsghdr cmsghdr;
- struct in_pktinfo in_pktinfo;
+#ifdef HAVE_IN_PKTINFO
+ struct in_pktinfo pi4;
+#endif
+#ifdef IP_RECVDSTADDR
+ struct in_addr pi4;
+#endif
+};
+#ifdef USE_PF_INET6
+struct openvpn_in6_pktinfo
+{
+ struct cmsghdr cmsghdr;
+ struct in6_pktinfo pi6;
+};
+#endif
+
+union openvpn_pktinfo {
+ struct openvpn_in4_pktinfo msgpi4;
+#ifdef USE_PF_INET6
+ struct openvpn_in6_pktinfo msgpi6;
+#endif
};
#pragma pack()
@@ -2286,18 +2969,18 @@ link_socket_read_udp_posix_recvmsg (struct link_socket *sock,
struct link_socket_actual *from)
{
struct iovec iov;
- struct openvpn_pktinfo opi;
+ union openvpn_pktinfo opi;
struct msghdr mesg;
- socklen_t fromlen = sizeof (from->dest.sa);
+ socklen_t fromlen = sizeof (from->dest.addr);
iov.iov_base = BPTR (buf);
iov.iov_len = maxsize;
mesg.msg_iov = &iov;
mesg.msg_iovlen = 1;
- mesg.msg_name = &from->dest.sa;
+ mesg.msg_name = &from->dest.addr;
mesg.msg_namelen = fromlen;
mesg.msg_control = &opi;
- mesg.msg_controllen = sizeof (opi);
+ mesg.msg_controllen = sizeof opi;
buf->len = recvmsg (sock->sd, &mesg, 0);
if (buf->len >= 0)
{
@@ -2306,14 +2989,39 @@ link_socket_read_udp_posix_recvmsg (struct link_socket *sock,
cmsg = CMSG_FIRSTHDR (&mesg);
if (cmsg != NULL
&& CMSG_NXTHDR (&mesg, cmsg) == NULL
+#ifdef IP_PKTINFO
&& cmsg->cmsg_level == SOL_IP
&& cmsg->cmsg_type == IP_PKTINFO
- && cmsg->cmsg_len >= sizeof (opi))
+#elif defined(IP_RECVDSTADDR)
+ && cmsg->cmsg_level == IPPROTO_IP
+ && cmsg->cmsg_type == IP_RECVDSTADDR
+#else
+#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h)
+#endif
+ && cmsg->cmsg_len >= sizeof (struct openvpn_in4_pktinfo))
{
+#ifdef IP_PKTINFO
struct in_pktinfo *pkti = (struct in_pktinfo *) CMSG_DATA (cmsg);
- from->pi.ipi_ifindex = pkti->ipi_ifindex;
- from->pi.ipi_spec_dst = pkti->ipi_spec_dst;
+ from->pi.in4.ipi_ifindex = pkti->ipi_ifindex;
+ from->pi.in4.ipi_spec_dst = pkti->ipi_spec_dst;
+#elif defined(IP_RECVDSTADDR)
+ from->pi.in4 = *(struct in_addr*) CMSG_DATA (cmsg);
+#else
+#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h)
+#endif
}
+#ifdef USE_PF_INET6
+ else if (cmsg != NULL
+ && CMSG_NXTHDR (&mesg, cmsg) == NULL
+ && cmsg->cmsg_level == IPPROTO_IPV6
+ && cmsg->cmsg_type == IPV6_PKTINFO
+ && cmsg->cmsg_len >= sizeof (struct openvpn_in6_pktinfo))
+ {
+ struct in6_pktinfo *pkti6 = (struct in6_pktinfo *) CMSG_DATA (cmsg);
+ from->pi.in6.ipi6_ifindex = pkti6->ipi6_ifindex;
+ from->pi.in6.ipi6_addr = pkti6->ipi6_addr;
+ }
+#endif
}
return fromlen;
}
@@ -2325,18 +3033,20 @@ link_socket_read_udp_posix (struct link_socket *sock,
int maxsize,
struct link_socket_actual *from)
{
- socklen_t fromlen = sizeof (from->dest.sa);
- from->dest.sa.sin_addr.s_addr = 0;
+ socklen_t fromlen = sizeof (from->dest.addr);
+ socklen_t expectedlen = af_addr_size(proto_sa_family(sock->info.proto));
+ addr_zero_host(&from->dest);
ASSERT (buf_safe (buf, maxsize));
#if ENABLE_IP_PKTINFO
- if (sock->sockflags & SF_USE_IP_PKTINFO)
+ /* Both PROTO_UDPv4 and PROTO_UDPv6 */
+ if (proto_is_udp(sock->info.proto) && sock->sockflags & SF_USE_IP_PKTINFO)
fromlen = link_socket_read_udp_posix_recvmsg (sock, buf, maxsize, from);
else
#endif
buf->len = recvfrom (sock->sd, BPTR (buf), maxsize, 0,
- (struct sockaddr *) &from->dest.sa, &fromlen);
- if (fromlen != sizeof (from->dest.sa))
- bad_address_length (fromlen, sizeof (from->dest.sa));
+ &from->dest.addr.sa, &fromlen);
+ if (buf->len >= 0 && expectedlen && fromlen != expectedlen)
+ bad_address_length (fromlen, expectedlen);
return buf->len;
}
@@ -2373,26 +3083,64 @@ link_socket_write_udp_posix_sendmsg (struct link_socket *sock,
struct iovec iov;
struct msghdr mesg;
struct cmsghdr *cmsg;
- struct in_pktinfo *pkti;
- struct openvpn_pktinfo opi;
iov.iov_base = BPTR (buf);
iov.iov_len = BLEN (buf);
mesg.msg_iov = &iov;
mesg.msg_iovlen = 1;
- mesg.msg_name = &to->dest.sa;
- mesg.msg_namelen = sizeof (to->dest.sa);
- mesg.msg_control = &opi;
- mesg.msg_controllen = sizeof (opi);
- mesg.msg_flags = 0;
- cmsg = CMSG_FIRSTHDR (&mesg);
- cmsg->cmsg_len = sizeof (opi);
- cmsg->cmsg_level = SOL_IP;
- cmsg->cmsg_type = IP_PKTINFO;
- pkti = (struct in_pktinfo *) CMSG_DATA (cmsg);
- pkti->ipi_ifindex = to->pi.ipi_ifindex;
- pkti->ipi_spec_dst = to->pi.ipi_spec_dst;
- pkti->ipi_addr.s_addr = 0;
+ switch (sock->info.lsa->remote.addr.sa.sa_family)
+ {
+ case AF_INET:
+ {
+ struct openvpn_in4_pktinfo msgpi4;
+ mesg.msg_name = &to->dest.addr.sa;
+ mesg.msg_namelen = sizeof (struct sockaddr_in);
+ mesg.msg_control = &msgpi4;
+ mesg.msg_controllen = sizeof msgpi4;
+ mesg.msg_flags = 0;
+ cmsg = CMSG_FIRSTHDR (&mesg);
+ cmsg->cmsg_len = sizeof (struct openvpn_in4_pktinfo);
+#ifdef HAVE_IN_PKTINFO
+ cmsg->cmsg_level = SOL_IP;
+ cmsg->cmsg_type = IP_PKTINFO;
+ {
+ struct in_pktinfo *pkti;
+ pkti = (struct in_pktinfo *) CMSG_DATA (cmsg);
+ pkti->ipi_ifindex = to->pi.in4.ipi_ifindex;
+ pkti->ipi_spec_dst = to->pi.in4.ipi_spec_dst;
+ pkti->ipi_addr.s_addr = 0;
+ }
+#elif defined(IP_RECVDSTADDR)
+ cmsg->cmsg_level = IPPROTO_IP;
+ cmsg->cmsg_type = IP_RECVDSTADDR;
+ *(struct in_addr *) CMSG_DATA (cmsg) = to->pi.in4;
+#else
+#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h)
+#endif
+ break;
+ }
+#ifdef USE_PF_INET6
+ case AF_INET6:
+ {
+ struct openvpn_in6_pktinfo msgpi6;
+ struct in6_pktinfo *pkti6;
+ mesg.msg_name = &to->dest.addr.sa;
+ mesg.msg_namelen = sizeof (struct sockaddr_in6);
+ mesg.msg_control = &msgpi6;
+ mesg.msg_controllen = sizeof msgpi6;
+ mesg.msg_flags = 0;
+ cmsg = CMSG_FIRSTHDR (&mesg);
+ cmsg->cmsg_len = sizeof (struct openvpn_in6_pktinfo);
+ cmsg->cmsg_level = IPPROTO_IPV6;
+ cmsg->cmsg_type = IPV6_PKTINFO;
+ pkti6 = (struct in6_pktinfo *) CMSG_DATA (cmsg);
+ pkti6->ipi6_ifindex = to->pi.in6.ipi6_ifindex;
+ pkti6->ipi6_addr = to->pi.in6.ipi6_addr;
+ break;
+ }
+#endif
+ default: ASSERT(0);
+ }
return sendmsg (sock->sd, &mesg, 0);
}
@@ -2465,11 +3213,11 @@ socket_recv_queue (struct link_socket *sock, int maxsize)
int status;
/* reset buf to its initial state */
- if (sock->info.proto == PROTO_UDPv4)
+ if (proto_is_udp(sock->info.proto))
{
sock->reads.buf = sock->reads.buf_init;
}
- else if (sock->info.proto == PROTO_TCPv4_CLIENT || sock->info.proto == PROTO_TCPv4_SERVER)
+ else if (proto_is_tcp(sock->info.proto))
{
stream_buf_get_next (&sock->stream_buf, &sock->reads.buf);
}
@@ -2489,10 +3237,15 @@ socket_recv_queue (struct link_socket *sock, int maxsize)
ASSERT (ResetEvent (sock->reads.overlapped.hEvent));
sock->reads.flags = 0;
- if (sock->info.proto == PROTO_UDPv4)
+ if (proto_is_udp(sock->info.proto))
{
sock->reads.addr_defined = true;
- sock->reads.addrlen = sizeof (sock->reads.addr);
+#ifdef USE_PF_INET6
+ if (sock->info.proto == PROTO_UDPv6)
+ sock->reads.addrlen = sizeof (sock->reads.addr6);
+ else
+#endif
+ sock->reads.addrlen = sizeof (sock->reads.addr);
status = WSARecvFrom(
sock->sd,
wsabuf,
@@ -2504,7 +3257,7 @@ socket_recv_queue (struct link_socket *sock, int maxsize)
&sock->reads.overlapped,
NULL);
}
- else if (sock->info.proto == PROTO_TCPv4_CLIENT || sock->info.proto == PROTO_TCPv4_SERVER)
+ else if (proto_is_tcp(sock->info.proto))
{
sock->reads.addr_defined = false;
status = WSARecv(
@@ -2524,8 +3277,14 @@ socket_recv_queue (struct link_socket *sock, int maxsize)
if (!status) /* operation completed immediately? */
{
+#ifdef USE_PF_INET6
+ int addrlen = af_addr_size(sock->info.lsa->local.addr.sa.sa_family);
+ if (sock->reads.addr_defined && sock->reads.addrlen != addrlen)
+ bad_address_length (sock->reads.addrlen, addrlen);
+#else
if (sock->reads.addr_defined && sock->reads.addrlen != sizeof (sock->reads.addr))
bad_address_length (sock->reads.addrlen, sizeof (sock->reads.addr));
+#endif
sock->reads.iostate = IOSTATE_IMMEDIATE_RETURN;
@@ -2584,12 +3343,22 @@ socket_send_queue (struct link_socket *sock, struct buffer *buf, const struct li
ASSERT (ResetEvent (sock->writes.overlapped.hEvent));
sock->writes.flags = 0;
- if (sock->info.proto == PROTO_UDPv4)
+ if (proto_is_udp(sock->info.proto))
{
/* set destination address for UDP writes */
sock->writes.addr_defined = true;
- sock->writes.addr = to->dest.sa;
- sock->writes.addrlen = sizeof (sock->writes.addr);
+#ifdef USE_PF_INET6
+ if (sock->info.proto == PROTO_UDPv6)
+ {
+ sock->writes.addr6 = to->dest.addr.in6;
+ sock->writes.addrlen = sizeof (sock->writes.addr6);
+ }
+ else
+#endif
+ {
+ sock->writes.addr = to->dest.addr.in4;
+ sock->writes.addrlen = sizeof (sock->writes.addr);
+ }
status = WSASendTo(
sock->sd,
@@ -2602,7 +3371,7 @@ socket_send_queue (struct link_socket *sock, struct buffer *buf, const struct li
&sock->writes.overlapped,
NULL);
}
- else if (sock->info.proto == PROTO_TCPv4_CLIENT || sock->info.proto == PROTO_TCPv4_SERVER)
+ else if (proto_is_tcp(sock->info.proto))
{
/* destination address for TCP writes was established on connection initiation */
sock->writes.addr_defined = false;
@@ -2741,13 +3510,44 @@ socket_finalize (SOCKET s,
if (from)
{
if (ret >= 0 && io->addr_defined)
+#ifdef USE_PF_INET6
+ {
+ /* TODO(jjo): streamline this mess */
+ /* in this func we dont have relevant info about the PF_ of this
+ * endpoint, as link_socket_actual will be zero for the 1st received packet
+ *
+ * Test for inets PF_ possible sizes
+ */
+ switch (io->addrlen)
+ {
+ case sizeof(struct sockaddr_in):
+ case sizeof(struct sockaddr_in6):
+ /* TODO(jjo): for some reason (?) I'm getting 24,28 for AF_INET6 */
+ case sizeof(struct sockaddr_in6)-4:
+ break;
+ default:
+ bad_address_length (io->addrlen, af_addr_size(io->addr.sin_family));
+ }
+
+ switch (io->addr.sin_family)
+ {
+ case AF_INET:
+ from->dest.addr.in4 = io->addr;
+ break;
+ case AF_INET6:
+ from->dest.addr.in6 = io->addr6;
+ break;
+ }
+ }
+#else
{
if (io->addrlen != sizeof (io->addr))
bad_address_length (io->addrlen, sizeof (io->addr));
- from->dest.sa = io->addr;
+ from->dest.addr.in4 = io->addr;
}
+#endif
else
- CLEAR (from->dest.sa);
+ CLEAR (from->dest.addr);
}
if (buf)
diff --git a/socket.h b/socket.h
index 17943e7..33da53c 100644
--- a/socket.h
+++ b/socket.h
@@ -70,7 +70,13 @@ typedef uint16_t packet_size_type;
struct openvpn_sockaddr
{
/*int dummy;*/ /* add offset to force a bug if sa not explicitly dereferenced */
- struct sockaddr_in sa;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in in4;
+#ifdef USE_PF_INET6
+ struct sockaddr_in6 in6;
+#endif
+ } addr;
};
/* actual address of remote, based on source address of received packets */
@@ -79,7 +85,17 @@ struct link_socket_actual
/*int dummy;*/ /* add offset to force a bug if dest not explicitly dereferenced */
struct openvpn_sockaddr dest;
#if ENABLE_IP_PKTINFO
- struct in_pktinfo pi;
+ union {
+#ifdef HAVE_IN_PKTINFO
+ struct in_pktinfo in4;
+#endif
+#ifdef IP_RECVDSTADDR
+ struct in_addr in4;
+#endif
+#ifdef USE_PF_INET6
+ struct in6_pktinfo in6;
+#endif
+ } pi;
#endif
};
@@ -199,6 +215,7 @@ struct link_socket
# define SF_TCP_NODELAY (1<<1)
# define SF_PORT_SHARE (1<<2)
# define SF_HOST_RANDOMIZE (1<<3)
+# define SF_GETADDRINFO_DGRAM (1<<4)
unsigned int sockflags;
/* for stream sockets */
@@ -373,6 +390,12 @@ void setenv_link_socket_actual (struct env_set *es,
void bad_address_length (int actual, int expected);
+#ifdef USE_PF_INET6
+/* IPV4_INVALID_ADDR: returned by link_socket_current_remote()
+ * to ease redirect-gateway logic for ipv4 tunnels on ipv6 endpoints
+ */
+#define IPV4_INVALID_ADDR 0xffffffff
+#endif
in_addr_t link_socket_current_remote (const struct link_socket_info *info);
void link_socket_connection_initiated (const struct buffer *buf,
@@ -413,6 +436,14 @@ socket_descriptor_t create_socket_tcp (void);
socket_descriptor_t socket_do_accept (socket_descriptor_t sd,
struct link_socket_actual *act,
const bool nowait);
+/*
+ * proto related
+ */
+bool proto_is_net(int proto);
+bool proto_is_dgram(int proto);
+bool proto_is_udp(int proto);
+bool proto_is_tcp(int proto);
+
#if UNIX_SOCK_SUPPORT
@@ -458,6 +489,11 @@ struct resolve_list {
#define GETADDR_UPDATE_MANAGEMENT_STATE (1<<8)
#define GETADDR_RANDOMIZE (1<<9)
+/* [ab]use flags bits to get socktype info downstream */
+/* TODO(jjo): resolve tradeoff between hackiness|args-overhead */
+#define GETADDR_DGRAM (1<<10)
+#define dnsflags_to_socktype(flags) ((flags & GETADDR_DGRAM) ? SOCK_DGRAM : SOCK_STREAM)
+
in_addr_t getaddr (unsigned int flags,
const char *hostname,
int resolve_retry_seconds,
@@ -475,23 +511,38 @@ in_addr_t getaddr_multi (unsigned int flags,
* Transport protocol naming and other details.
*/
-#define PROTO_UDPv4 0
-#define PROTO_TCPv4_SERVER 1
-#define PROTO_TCPv4_CLIENT 2
-#define PROTO_TCPv4 3
-#define PROTO_N 4
+/*
+ * Use enum's instead of #define to allow for easier
+ * optional proto support
+ */
+enum proto_num {
+ PROTO_NONE, /* catch for uninitialized */
+ PROTO_UDPv4,
+ PROTO_TCPv4_SERVER,
+ PROTO_TCPv4_CLIENT,
+ PROTO_TCPv4,
+#ifdef USE_PF_INET6
+ PROTO_UDPv6,
+ PROTO_TCPv6_SERVER,
+ PROTO_TCPv6_CLIENT,
+ PROTO_TCPv6,
+#endif
+ PROTO_N
+};
int ascii2proto (const char* proto_name);
const char *proto2ascii (int proto, bool display_form);
const char *proto2ascii_all (struct gc_arena *gc);
int proto_remote (int proto, bool remote);
+const char *addr_family_name(int af);
/*
* Overhead added to packets by various protocols.
*/
#define IPv4_UDP_HEADER_SIZE 28
#define IPv4_TCP_HEADER_SIZE 40
-#define IPv6_UDP_HEADER_SIZE 40
+#define IPv6_UDP_HEADER_SIZE 48
+#define IPv6_TCP_HEADER_SIZE 60
extern const int proto_overhead[];
@@ -521,7 +572,7 @@ is_proto_tcp(const int p)
static inline bool
link_socket_proto_connection_oriented (int proto)
{
- return proto == PROTO_TCPv4_SERVER || proto == PROTO_TCPv4_CLIENT;
+ return !proto_is_dgram(proto);
}
static inline bool
@@ -536,7 +587,36 @@ link_socket_connection_oriented (const struct link_socket *sock)
static inline bool
addr_defined (const struct openvpn_sockaddr *addr)
{
- return addr->sa.sin_addr.s_addr != 0;
+ if (!addr) return 0;
+ switch (addr->addr.sa.sa_family) {
+ case AF_INET: return addr->addr.in4.sin_addr.s_addr != 0;
+#ifdef USE_PF_INET6
+ case AF_INET6: return !IN6_IS_ADDR_UNSPECIFIED(&addr->addr.in6.sin6_addr);
+#endif
+ default: return 0;
+ }
+}
+static inline bool
+addr_defined_ipi (const struct link_socket_actual *lsa)
+{
+#if ENABLE_IP_PKTINFO
+ if (!lsa) return 0;
+ switch (lsa->dest.addr.sa.sa_family) {
+#ifdef HAVE_IN_PKTINFO
+ case AF_INET: return lsa->pi.in4.ipi_spec_dst.s_addr != 0;
+#endif
+#ifdef IP_RECVDSTADDR
+ case AF_INET: return lsa->pi.in4.s_addr != 0;
+#endif
+#ifdef USE_PF_INET6
+ case AF_INET6: return !IN6_IS_ADDR_UNSPECIFIED(&lsa->pi.in6.ipi6_addr);
+#endif
+ default: return 0;
+ }
+#else
+ ASSERT(0);
+#endif
+ return false;
}
static inline bool
@@ -548,20 +628,50 @@ link_socket_actual_defined (const struct link_socket_actual *act)
static inline bool
addr_match (const struct openvpn_sockaddr *a1, const struct openvpn_sockaddr *a2)
{
- return a1->sa.sin_addr.s_addr == a2->sa.sin_addr.s_addr;
+ switch(a1->addr.sa.sa_family) {
+ case AF_INET:
+ return a1->addr.in4.sin_addr.s_addr == a2->addr.in4.sin_addr.s_addr;
+#ifdef USE_PF_INET6
+ case AF_INET6:
+ return IN6_ARE_ADDR_EQUAL(&a1->addr.in6.sin6_addr, &a2->addr.in6.sin6_addr);
+#endif
+ }
+ ASSERT(0);
+ return false;
}
static inline in_addr_t
-addr_host (const struct openvpn_sockaddr *s)
+addr_host (const struct openvpn_sockaddr *addr)
{
- return ntohl (s->sa.sin_addr.s_addr);
+ /*
+ * "public" addr returned is checked against ifconfig for
+ * possible clash: non sense for now given
+ * that we do ifconfig only IPv4
+ */
+#if defined(USE_PF_INET6)
+ if(addr->addr.sa.sa_family != AF_INET)
+ return 0;
+#else
+ ASSERT(addr->addr.sa.sa_family == AF_INET);
+#endif
+ return ntohl (addr->addr.in4.sin_addr.s_addr);
}
static inline bool
addr_port_match (const struct openvpn_sockaddr *a1, const struct openvpn_sockaddr *a2)
{
- return a1->sa.sin_addr.s_addr == a2->sa.sin_addr.s_addr
- && a1->sa.sin_port == a2->sa.sin_port;
+ switch(a1->addr.sa.sa_family) {
+ case AF_INET:
+ return a1->addr.in4.sin_addr.s_addr == a2->addr.in4.sin_addr.s_addr
+ && a1->addr.in4.sin_port == a2->addr.in4.sin_port;
+#ifdef USE_PF_INET6
+ case AF_INET6:
+ return IN6_ARE_ADDR_EQUAL(&a1->addr.in6.sin6_addr, &a2->addr.in6.sin6_addr)
+ && a1->addr.in6.sin6_port == a2->addr.in6.sin6_port;
+#endif
+ }
+ ASSERT(0);
+ return false;
}
static inline bool
@@ -574,6 +684,74 @@ addr_match_proto (const struct openvpn_sockaddr *a1,
: addr_port_match (a1, a2);
}
+static inline void
+addr_zero_host(struct openvpn_sockaddr *addr)
+{
+ switch(addr->addr.sa.sa_family) {
+ case AF_INET:
+ addr->addr.in4.sin_addr.s_addr = 0;
+ break;
+#ifdef USE_PF_INET6
+ case AF_INET6:
+ memset(&addr->addr.in6.sin6_addr, 0, sizeof (struct in6_addr));
+ break;
+#endif
+ }
+}
+
+static inline void
+addr_copy_sa(struct openvpn_sockaddr *dst, const struct openvpn_sockaddr *src)
+{
+ dst->addr = src->addr;
+}
+
+static inline void
+addr_copy_host(struct openvpn_sockaddr *dst, const struct openvpn_sockaddr *src)
+{
+ switch(src->addr.sa.sa_family) {
+ case AF_INET:
+ dst->addr.in4.sin_addr.s_addr = src->addr.in4.sin_addr.s_addr;
+ break;
+#ifdef USE_PF_INET6
+ case AF_INET6:
+ dst->addr.in6.sin6_addr = src->addr.in6.sin6_addr;
+ break;
+#endif
+ }
+}
+
+static inline bool
+addr_inet4or6(struct sockaddr *addr)
+{
+ return addr->sa_family == AF_INET || addr->sa_family == AF_INET6;
+}
+
+int addr_guess_family(int proto, const char *name);
+static inline int
+af_addr_size(unsigned short af)
+{
+#if defined(USE_PF_INET6) || defined (USE_PF_UNIX)
+ switch(af) {
+ case AF_INET: return sizeof (struct sockaddr_in);
+#ifdef USE_PF_UNIX
+ case AF_UNIX: return sizeof (struct sockaddr_un);
+#endif
+#ifdef USE_PF_INET6
+ case AF_INET6: return sizeof (struct sockaddr_in6);
+#endif
+ default:
+#if 0
+ /* could be called from socket_do_accept() with empty addr */
+ msg (M_ERR, "Bad address family: %d\n", af);
+ ASSERT(0);
+#endif
+ return 0;
+ }
+#else /* only AF_INET */
+ return sizeof(struct sockaddr_in);
+#endif
+}
+
static inline bool
link_socket_actual_match (const struct link_socket_actual *a1, const struct link_socket_actual *a2)
{
@@ -630,14 +808,18 @@ link_socket_verify_incoming_addr (struct buffer *buf,
{
if (buf->len > 0)
{
- if (from_addr->dest.sa.sin_family != AF_INET)
- return false;
- if (!link_socket_actual_defined (from_addr))
- return false;
- if (info->remote_float || !addr_defined (&info->lsa->remote))
- return true;
- if (addr_match_proto (&from_addr->dest, &info->lsa->remote, info->proto))
- return true;
+ switch (from_addr->dest.addr.sa.sa_family) {
+#ifdef USE_PF_INET6
+ case AF_INET6:
+#endif
+ case AF_INET:
+ if (!link_socket_actual_defined (from_addr))
+ return false;
+ if (info->remote_float || !addr_defined (&info->lsa->remote))
+ return true;
+ if (addr_match_proto (&from_addr->dest, &info->lsa->remote, info->proto))
+ return true;
+ }
}
return false;
}
@@ -743,7 +925,7 @@ link_socket_read (struct link_socket *sock,
int maxsize,
struct link_socket_actual *from)
{
- if (sock->info.proto == PROTO_UDPv4)
+ if (proto_is_udp(sock->info.proto)) /* unified UDPv4 and UDPv6 */
{
int res;
@@ -754,10 +936,10 @@ link_socket_read (struct link_socket *sock,
#endif
return res;
}
- else if (sock->info.proto == PROTO_TCPv4_SERVER || sock->info.proto == PROTO_TCPv4_CLIENT)
+ else if (proto_is_tcp(sock->info.proto)) /* unified TCPv4 and TCPv6 */
{
/* from address was returned by accept */
- from->dest.sa = sock->info.lsa->actual.dest.sa;
+ addr_copy_sa(&from->dest, &sock->info.lsa->actual.dest);
return link_socket_read_tcp (sock, buf);
}
else
@@ -812,13 +994,14 @@ link_socket_write_udp_posix (struct link_socket *sock,
struct buffer *buf,
struct link_socket_actual *to);
- if (sock->sockflags & SF_USE_IP_PKTINFO)
+ if (proto_is_udp(sock->info.proto) && (sock->sockflags & SF_USE_IP_PKTINFO)
+ && addr_defined_ipi(to))
return link_socket_write_udp_posix_sendmsg (sock, buf, to);
else
#endif
return sendto (sock->sd, BPTR (buf), BLEN (buf), 0,
- (struct sockaddr *) &to->dest.sa,
- (socklen_t) sizeof (to->dest.sa));
+ (struct sockaddr *) &to->dest.addr.sa,
+ (socklen_t) af_addr_size(to->dest.addr.sa.sa_family));
}
static inline int
@@ -849,11 +1032,11 @@ link_socket_write (struct link_socket *sock,
struct buffer *buf,
struct link_socket_actual *to)
{
- if (sock->info.proto == PROTO_UDPv4)
+ if (proto_is_udp(sock->info.proto)) /* unified UDPv4 and UDPv6 */
{
return link_socket_write_udp (sock, buf, to);
}
- else if (sock->info.proto == PROTO_TCPv4_SERVER || sock->info.proto == PROTO_TCPv4_CLIENT)
+ else if (proto_is_tcp(sock->info.proto)) /* unified TCPv4 and TCPv6 */
{
return link_socket_write_tcp (sock, buf, to);
}
diff --git a/socks.c b/socks.c
index 8287274..949d256 100644
--- a/socks.c
+++ b/socks.c
@@ -299,9 +299,9 @@ recv_socks_reply (socket_descriptor_t sd,
if (addr != NULL)
{
- addr->sa.sin_family = AF_INET;
- addr->sa.sin_addr.s_addr = htonl (INADDR_ANY);
- addr->sa.sin_port = htons (0);
+ addr->addr.in4.sin_family = AF_INET;
+ addr->addr.in4.sin_addr.s_addr = htonl (INADDR_ANY);
+ addr->addr.in4.sin_port = htons (0);
}
while (len < 4 + alen + 2)
@@ -388,8 +388,8 @@ recv_socks_reply (socket_descriptor_t sd,
/* ATYP == 1 (IP V4 address) */
if (atyp == '\x01' && addr != NULL)
{
- memcpy (&addr->sa.sin_addr, buf + 4, sizeof (addr->sa.sin_addr));
- memcpy (&addr->sa.sin_port, buf + 8, sizeof (addr->sa.sin_port));
+ memcpy (&addr->addr.in4.sin_addr, buf + 4, sizeof (addr->addr.in4.sin_addr));
+ memcpy (&addr->addr.in4.sin_port, buf + 8, sizeof (addr->addr.in4.sin_port));
}
@@ -507,8 +507,8 @@ socks_process_incoming_udp (struct buffer *buf,
if (atyp != 1) /* ATYP == 1 (IP V4) */
goto error;
- buf_read (buf, &from->dest.sa.sin_addr, sizeof (from->dest.sa.sin_addr));
- buf_read (buf, &from->dest.sa.sin_port, sizeof (from->dest.sa.sin_port));
+ buf_read (buf, &from->dest.addr.in4.sin_addr, sizeof (from->dest.addr.in4.sin_addr));
+ buf_read (buf, &from->dest.addr.in4.sin_port, sizeof (from->dest.addr.in4.sin_port));
return;
@@ -540,8 +540,8 @@ socks_process_outgoing_udp (struct buffer *buf,
buf_write_u16 (&head, 0); /* RSV = 0 */
buf_write_u8 (&head, 0); /* FRAG = 0 */
buf_write_u8 (&head, '\x01'); /* ATYP = 1 (IP V4) */
- buf_write (&head, &to->dest.sa.sin_addr, sizeof (to->dest.sa.sin_addr));
- buf_write (&head, &to->dest.sa.sin_port, sizeof (to->dest.sa.sin_port));
+ buf_write (&head, &to->dest.addr.in4.sin_addr, sizeof (to->dest.addr.in4.sin_addr));
+ buf_write (&head, &to->dest.addr.in4.sin_port, sizeof (to->dest.addr.in4.sin_port));
return 10;
}
diff --git a/ssl.c b/ssl.c
index bd7ac94..a0493ff 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();
@@ -574,6 +586,99 @@ bool extract_x509_extension(X509 *cert, char *fieldname, char *out, int size)
}
return retval;
}
+#endif /* ENABLE_X509ALTUSERNAME */
+
+#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 +939,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 +960,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 +1029,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 +1064,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 +1214,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 +1360,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 +2131,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 +2243,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 +2271,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 +2285,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 +2296,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 +2858,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 +2965,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 +4039,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 d589531..5b86cad 100644
--- a/syshead.h
+++ b/syshead.h
@@ -390,9 +390,10 @@
#endif
/*
- * Does this platform support linux-style IP_PKTINFO?
+ * Does this platform support linux-style IP_PKTINFO
+ * or bsd-style IP_RECVDSTADDR ?
*/
-#if defined(ENABLE_MULTIHOME) && defined(HAVE_IN_PKTINFO) && defined(IP_PKTINFO) && defined(HAVE_MSGHDR) && defined(HAVE_CMSGHDR) && defined(HAVE_IOVEC) && defined(CMSG_FIRSTHDR) && defined(CMSG_NXTHDR) && defined(HAVE_RECVMSG) && defined(HAVE_SENDMSG)
+#if defined(ENABLE_MULTIHOME) && ((defined(HAVE_IN_PKTINFO)&&defined(IP_PKTINFO)) || defined(IP_RECVDSTADDR)) && defined(HAVE_MSGHDR) && defined(HAVE_CMSGHDR) && defined(HAVE_IOVEC) && defined(CMSG_FIRSTHDR) && defined(CMSG_NXTHDR) && defined(HAVE_RECVMSG) && defined(HAVE_SENDMSG)
#define ENABLE_IP_PKTINFO 1
#else
#define ENABLE_IP_PKTINFO 0
@@ -598,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)
@@ -675,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 cea1784..895409a 100644
--- a/tun.c
+++ b/tun.c
@@ -1003,7 +1003,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...
*/
@@ -1049,6 +1048,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;
@@ -1963,7 +1963,9 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tu
strerror(errno));
}
+#ifdef IFF_MULTICAST /* openbsd 4.x doesn't have this */
info.flags |= IFF_MULTICAST;
+#endif
if (ioctl (tt->fd, TUNSIFINFO, &info) < 0) {
msg (M_WARN | M_ERRNO, "Can't set interface info: %s",
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)
diff --git a/win32.h b/win32.h
index 829933f..d0ecc85 100644
--- a/win32.h
+++ b/win32.h
@@ -195,7 +195,10 @@ struct overlapped_io {
DWORD flags;
int status;
bool addr_defined;
- struct sockaddr_in addr;
+ union {
+ struct sockaddr_in addr;
+ struct sockaddr_in6 addr6;
+ };
int addrlen;
struct buffer buf_init;
struct buffer buf;